You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2021/07/01 08:46:49 UTC

[isis] branch ISIS-2781 updated (413ebae -> 48137cd)

This is an automated email from the ASF dual-hosted git repository.

danhaywood pushed a change to branch ISIS-2781
in repository https://gitbox.apache.org/repos/asf/isis.git.


 discard 413ebae  ISIS-2781: adds first-cut of the arch tests
     add c9c292d  ISIS-1720: properly handle synthetic methods
     add f606728  ISIS-1720: fixes param naming for 'synthetic' actions
     add 654f0fd  ISIS-1720: adds another WrapperInteraction test
     add c5ba498  ISIS-1720: cleaning up experiments; polishing synthetic to regular method lookup
     add a2e76f1  ISIS-2740: changing bootstrap growl z-index 1031 -> 999
     add 1f49dde  ISIS-1720: optimization: CssClassFaFacetOnMemberFromConfiguredRegex reuse MemberNamedFacet as CssClassFaFactory if provides static names
     new 48137cd  ISIS-2781: adds first-cut of the arch tests

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (413ebae)
            \
             N -- N -- N   refs/heads/ISIS-2781 (48137cd)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../isis/commons/internal/reflection/_Reflect.java | 26 +++++++++++++++
 .../core/metamodel/facets/ImperativeFacet.java     |  9 ++++++
 ...ctionInvocationFacetForDomainEventAbstract.java |  2 +-
 .../method/ActionDefaultsFacetViaMethod.java       |  2 +-
 .../ActionParameterValidationFacetViaMethod.java   |  2 +-
 .../method/ActionValidationFacetViaMethod.java     |  2 +-
 .../CollectionAccessorFacetViaAccessor.java        |  2 +-
 ...CssClassFaFacetOnMemberFromConfiguredRegex.java | 10 ++++--
 .../method/DisableForContextFacetViaMethod.java    |  2 +-
 .../method/HideForContextFacetViaMethod.java       |  2 +-
 .../object/callbacks/CallbackFacetAbstract.java    |  7 ++--
 .../method/DisabledObjectFacetViaMethod.java       |  2 +-
 .../TitleFacetInferredFromToStringMethod.java      |  2 +-
 .../title/methods/TitleFacetViaTitleMethod.java    |  2 +-
 .../method/ValidateObjectFacetMethod.java          |  2 +-
 .../ActionParameterAutoCompleteFacetViaMethod.java |  2 +-
 .../method/ActionChoicesFacetViaMethod.java        |  2 +-
 .../ActionParameterChoicesFacetViaMethod.java      |  2 +-
 .../ActionParameterDefaultsFacetViaMethod.java     |  2 +-
 .../ActionParameterDisabledFacetViaMethod.java     |  2 +-
 .../ActionParameterHiddenFacetViaMethod.java       |  2 +-
 .../ParameterNameFacetFactoryUsingReflection.java  |  2 +-
 .../ActionParameterValidationFacetViaMethod.java   |  2 +-
 .../accessor/PropertyAccessorFacetViaAccessor.java |  2 +-
 .../method/PropertyAutoCompleteFacetMethod.java    |  2 +-
 .../method/PropertyChoicesFacetViaMethod.java      |  2 +-
 .../method/PropertyDefaultFacetViaMethod.java      |  2 +-
 .../clear/PropertyClearFacetViaClearMethod.java    |  2 +-
 .../clear/PropertyClearFacetViaSetterMethod.java   |  2 +-
 ...PropertyInitializationFacetViaSetterMethod.java |  2 +-
 .../modify/PropertySetterFacetViaSetterMethod.java |  2 +-
 .../specloader/specimpl/FacetedMethodsBuilder.java | 13 ++++++--
 .../specimpl/dflt/ObjectSpecificationDefault.java  | 33 +++++++++++++++++--
 .../interact/WrapperInteractionTest3.java          |  3 +-
 ...tionTest3.java => WrapperInteractionTest4.java} | 37 +++++++++-------------
 .../viewer/wicket/ui/errors/js/bootstrap-growl.js  |  2 +-
 .../wicket/ui/errors/js/bootstrap-growl.min.js     |  2 +-
 37 files changed, 134 insertions(+), 62 deletions(-)
 copy regressiontests/stable-interact/src/test/java/org/apache/isis/testdomain/interact/{WrapperInteractionTest3.java => WrapperInteractionTest4.java} (85%)

[isis] 01/01: ISIS-2781: adds first-cut of the arch tests

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

danhaywood pushed a commit to branch ISIS-2781
in repository https://gitbox.apache.org/repos/asf/isis.git

commit 48137cd8ac239757cedf115ec47233347077fee5
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Thu Jul 1 07:40:34 2021 +0100

    ISIS-2781: adds first-cut of the arch tests
---
 antora/playbooks/site-testing.yml                  |   3 +
 antora/playbooks/site.yml                          |   3 +
 core/pom.xml                                       |  19 ++-
 mavendeps/{unittests => archtests}/pom.xml         |  13 +-
 mavendeps/pom.xml                                  |   7 +
 mavendeps/unittests/pom.xml                        |   2 +-
 .../adoc/modules/ROOT/partials/component-nav.adoc  |   1 +
 testing/archtestsupport/adoc/antora.yml            |  19 +++
 .../adoc/modules/archtestsupport/nav.adoc          |   5 +
 .../adoc/modules/archtestsupport/pages/about.adoc  | 132 ++++++++++++++++++
 .../archtestsupport/partials/module-nav.adoc       |   6 +
 testing/archtestsupport/applib/pom.xml             | 108 +++++++++++++++
 .../IsisModuleTestingArchTestSupportApplib.java    |  28 ++++
 .../applib/classrules/ArchitectureClassRules.java  |  35 +++++
 .../modulerules/ArchitectureModuleRules.java       | 154 +++++++++++++++++++++
 .../packagerules/ArchitecturePackageRules.java     |  33 +++++
 .../applib/packagerules/Subpackage.java            |  20 +++
 .../applib/packagerules/SubpackageEnum.java        |  69 +++++++++
 .../applib/packagerules/SubpackageType.java        |  22 +++
 testing/archtestsupport/pom.xml                    |  34 +++++
 testing/pom.xml                                    |   1 +
 21 files changed, 703 insertions(+), 11 deletions(-)

diff --git a/antora/playbooks/site-testing.yml b/antora/playbooks/site-testing.yml
index eb384e4..2fa3dc1 100644
--- a/antora/playbooks/site-testing.yml
+++ b/antora/playbooks/site-testing.yml
@@ -47,6 +47,9 @@ content:
       start_path: testing/adoc # testing
       branches: HEAD
     - url: .
+      start_path: testing/archtestsupport/adoc # testing
+      branches: HEAD
+    - url: .
       start_path: testing/fakedata/adoc # testing
       branches: HEAD
     - url: .
diff --git a/antora/playbooks/site.yml b/antora/playbooks/site.yml
index 23032e8..51e5ca0 100644
--- a/antora/playbooks/site.yml
+++ b/antora/playbooks/site.yml
@@ -259,6 +259,9 @@ content:
       start_path: testing/adoc # testing
       branches: HEAD
     - url: .
+      start_path: testing/archtestsupport/adoc # testing
+      branches: HEAD
+    - url: .
       start_path: testing/fakedata/adoc # testing
       branches: HEAD
     - url: .
diff --git a/core/pom.xml b/core/pom.xml
index 74c71c8..9960e5b 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -85,7 +85,7 @@
 
 
 		<!-- LIBRARY DEPENDENCIES -->
-
+		<archunit.version>0.14.1</archunit.version>
 		<approvaltests.version>11.7.0</approvaltests.version>
 
 		<assertj-guava.version>3.4.0</assertj-guava.version>
@@ -854,6 +854,12 @@
 			</dependency>
 			<dependency>
 				<groupId>org.apache.isis.mavendeps</groupId>
+				<artifactId>isis-mavendeps-archtests</artifactId>
+				<version>2.0.0-SNAPSHOT</version>
+				<type>pom</type>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.isis.mavendeps</groupId>
 				<artifactId>isis-mavendeps-integtests</artifactId>
 				<version>2.0.0-SNAPSHOT</version>
 				<type>pom</type>
@@ -965,6 +971,17 @@
 			</dependency>
 
 			<dependency>
+				<groupId>com.tngtech.archunit</groupId>
+				<artifactId>archunit-junit5-api</artifactId>
+				<version>${archunit.version}</version>
+			</dependency>
+			<dependency>
+				<groupId>com.tngtech.archunit</groupId>
+				<artifactId>archunit-junit5-engine</artifactId>
+				<version>${archunit.version}</version>
+			</dependency>
+
+			<dependency>
 				<groupId>com.ullink.slack</groupId>
 				<artifactId>simpleslackapi</artifactId>
 				<version>${simpleslackapi.version}</version>
diff --git a/mavendeps/unittests/pom.xml b/mavendeps/archtests/pom.xml
similarity index 85%
copy from mavendeps/unittests/pom.xml
copy to mavendeps/archtests/pom.xml
index 049ab40..77e13ad 100644
--- a/mavendeps/unittests/pom.xml
+++ b/mavendeps/archtests/pom.xml
@@ -21,19 +21,19 @@
 		<version>2.0.0-SNAPSHOT</version>
 	</parent>
 
-	<artifactId>isis-mavendeps-unittests</artifactId>
+	<artifactId>isis-mavendeps-archtests</artifactId>
 
 	<packaging>pom</packaging>
 
-	<name>Apache Isis Maven Deps - Testing</name>
+	<name>Apache Isis Maven Deps - Arch Testing</name>
 	<description>
-        Defines a module that can be used as a single dependency for a set of common testing libraries.
+        Defines a module that can be used as a single dependency for architecture testing.
     </description>
 
 	<dependencies>
 		<dependency>
 			<groupId>org.apache.isis.testing</groupId>
-			<artifactId>isis-testing-unittestsupport-applib</artifactId>
+			<artifactId>isis-testing-archtestsupport-applib</artifactId>
 			<!--
             excluded to make consistent with JDK11 where these have been removed.
             -->
@@ -57,11 +57,6 @@
 			</exclusions>
 		</dependency>
 
-		<dependency>
-			<groupId>org.apache.isis.testing</groupId>
-			<artifactId>isis-testing-fakedata-applib</artifactId>
-		</dependency>
-
 	</dependencies>
 
 </project>
diff --git a/mavendeps/pom.xml b/mavendeps/pom.xml
index ef7339b..f316689 100644
--- a/mavendeps/pom.xml
+++ b/mavendeps/pom.xml
@@ -94,6 +94,12 @@
 
 			<dependency>
 				<groupId>org.apache.isis.mavendeps</groupId>
+				<artifactId>isis-mavendeps-archtests</artifactId>
+				<version>2.0.0-SNAPSHOT</version>
+			</dependency>
+
+			<dependency>
+				<groupId>org.apache.isis.mavendeps</groupId>
 				<artifactId>isis-mavendeps-integtests</artifactId>
 				<version>2.0.0-SNAPSHOT</version>
 			</dependency>
@@ -150,6 +156,7 @@
 
 	<modules>
 		<module>jdk11</module>
+		<module>archtests</module>
 		<module>unittests</module>
 		<module>integtests</module>
 		<module>integspecs</module>
diff --git a/mavendeps/unittests/pom.xml b/mavendeps/unittests/pom.xml
index 049ab40..6291db3 100644
--- a/mavendeps/unittests/pom.xml
+++ b/mavendeps/unittests/pom.xml
@@ -25,7 +25,7 @@
 
 	<packaging>pom</packaging>
 
-	<name>Apache Isis Maven Deps - Testing</name>
+	<name>Apache Isis Maven Deps - Unit Testing</name>
 	<description>
         Defines a module that can be used as a single dependency for a set of common testing libraries.
     </description>
diff --git a/testing/adoc/modules/ROOT/partials/component-nav.adoc b/testing/adoc/modules/ROOT/partials/component-nav.adoc
index 06ee362..bae43df 100644
--- a/testing/adoc/modules/ROOT/partials/component-nav.adoc
+++ b/testing/adoc/modules/ROOT/partials/component-nav.adoc
@@ -8,6 +8,7 @@ include::testing:integtestsupport:partial$module-nav.adoc[]
 include::testing:specsupport:partial$module-nav.adoc[]
 include::testing:fixtures:partial$module-nav.adoc[]
 include::testing:fakedata:partial$module-nav.adoc[]
+include::testing:archtestsupport:partial$module-nav.adoc[]
 
 include::testing:h2console:partial$module-nav.adoc[]
 include::testing:hsqldbmgr:partial$module-nav.adoc[]
diff --git a/testing/archtestsupport/adoc/antora.yml b/testing/archtestsupport/adoc/antora.yml
new file mode 100644
index 0000000..3c95843
--- /dev/null
+++ b/testing/archtestsupport/adoc/antora.yml
@@ -0,0 +1,19 @@
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing,
+#  software distributed under the License is distributed on an
+#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+#  specific language governing permissions and limitations
+#  under the License.
+
+name: testing
+version: latest
diff --git a/testing/archtestsupport/adoc/modules/archtestsupport/nav.adoc b/testing/archtestsupport/adoc/modules/archtestsupport/nav.adoc
new file mode 100644
index 0000000..650d66a
--- /dev/null
+++ b/testing/archtestsupport/adoc/modules/archtestsupport/nav.adoc
@@ -0,0 +1,5 @@
+
+:Notice: Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at. http://www.apache.org/licenses/LICENSE-2.0 . Unless required by applicable law or ag [...]
+
+include::testing:ROOT:partial$component-nav.adoc[]
+
diff --git a/testing/archtestsupport/adoc/modules/archtestsupport/pages/about.adoc b/testing/archtestsupport/adoc/modules/archtestsupport/pages/about.adoc
new file mode 100644
index 0000000..502bbeb
--- /dev/null
+++ b/testing/archtestsupport/adoc/modules/archtestsupport/pages/about.adoc
@@ -0,0 +1,132 @@
+= Architecture Test Support
+
+:Notice: Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at. http://www.apache.org/licenses/LICENSE-2.0 . Unless required by applicable law or ag [...]
+
+Apache Isis provides a library of link:https://www.archunit.org/[ArchUnit] tests to verify the structure of your domain applications.
+
+
+
+== Setup
+
+
+TODO: explain using similar format to elsewhere.
+
+=== Maven Configuration
+
+CAUTION: TODO
+
+=== Recommended Naming Convention
+
+Architecture tests run quickly, so it generally makes sense to name them as xref:testing:unittestsupport:about.adoc[unit tests] (with a `Test` suffix, to be picked up by surefire).
+
+== Module Tests
+
+CAUTION: TODO - to flesh out
+
+[source,java]
+.ModuleTests.java
+----
+@AnalyzeClasses(
+        packagesOf = {
+                CustomerModule.class                                                // <.>
+                , OrderModule.class
+                , ProductModule.class
+        },
+        importOptions = { ImportOption.DoNotIncludeTests.class }
+)
+public class ModuleTests {
+
+    @ArchTest
+    public static ArchRule code_dependencies_follow_module_Imports =
+        ArchitectureModuleRules.code_dependencies_follow_module_Imports(            // <.>
+            ArchitectureModuleRules.analyzeClasses_packagesOf(ModuleTests.class));  // <.>
+}
+----
+
+<.> the modules of the application to be scanned
+<.> invoke the rule
+<.> just obtains the list of modules
+
+
+== Package Tests
+
+CAUTION: TODO - to flesh out
+
+[source,java]
+.Subpackage
+----
+public interface Subpackage {
+    String getName();
+
+    SubpackageType getSubpackageType();
+
+    String packageIdentifierWithin(Class<?> moduleClass);
+
+    default void defineLayer(Architectures.LayeredArchitecture layeredArchitecture, Class<?> moduleClass) {
+        val layerDefinition = getSubpackageType().defineLayer(this, moduleClass, layeredArchitecture);
+        layerDefinition.definedBy(packageIdentifierWithin(moduleClass));
+    }
+
+    boolean canReference(Subpackage referenced);
+}
+----
+
+The `SubpackageEnum` provides an off the shelf implementation.
+
+[source,java]
+.PackageTestsForCustomerModule.java
+----
+@AnalyzeClasses(
+    packagesOf = {CustomerModule.class},                                            // <.>
+    importOptions = {ImportOption.DoNotIncludeTests.class}
+)
+public class PackageTestsForCustomerModule {
+
+    @ArchTest public static ArchRule code_dependencies_follow_module_subpackages =
+      ArchitecturePackageRules.code_dependencies_follow_module_subpackages(         // <.>
+        CustomerModule.class,                                                       // <1>
+        Arrays.asList(SubpackageEnum.values()));                                    // <.>
+
+}
+----
+
+<.> the module to be analyzed.
+A similar test should be created for each and every module in the app.
+<.> xxx
+<.> xxx
+
+
+== Class Tests
+
+CAUTION: TODO - to flesh out
+
+[source,java]
+.ClassTests.java
+----
+@AnalyzeClasses(
+        packagesOf = {
+                CustomerModule.class                                                // <.>
+                , OrderModule.class
+                , ProductModule.class
+        },
+        importOptions = { ImportOption.DoNotIncludeTests.class }
+)
+public class ClassTests {
+
+    @ArchTest
+    static ArchRule classes_annotated_with_Entity_are_also_annotated_with_DomainObject =
+      ArchitectureClassRules.classes_annotated_with_Entity_must_also_be_annotated_with_DomainObject();  // <.>
+
+    @ArchTest
+    static ArchRule classes_annotated_with_Entity_are_also_annotated_with_XmlJavaTypeAdapter =
+      ArchitectureClassRules.classes_annotated_with_Entity_must_also_be_annotated_with_XmlJavaAdapter(); // <.>
+
+    @ArchTest
+    static ArchRule classes_annotated_with_DomainObject_are_also_annotated_with_DomainObjectLayout =
+      ArchitectureClassRules.classes_annotated_with_DomainObject_must_also_be_annotated_with_DomainObjectLayout(); // <.>
+----
+
+<.> the modules of the application to be scanned
+<.> xxx
+<.> xxx
+<.> xxx
diff --git a/testing/archtestsupport/adoc/modules/archtestsupport/partials/module-nav.adoc b/testing/archtestsupport/adoc/modules/archtestsupport/partials/module-nav.adoc
new file mode 100644
index 0000000..58c4fbc
--- /dev/null
+++ b/testing/archtestsupport/adoc/modules/archtestsupport/partials/module-nav.adoc
@@ -0,0 +1,6 @@
+
+
+
+* xref:testing:archtestsupport:about.adoc[Architecture Test Support]
+
+
diff --git a/testing/archtestsupport/applib/pom.xml b/testing/archtestsupport/applib/pom.xml
new file mode 100644
index 0000000..1ce9cd1
--- /dev/null
+++ b/testing/archtestsupport/applib/pom.xml
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor
+	license agreements. See the NOTICE file distributed with this work for additional
+	information regarding copyright ownership. The ASF licenses this file to
+	you under the Apache License, Version 2.0 (the "License"); you may not use
+	this file except in compliance with the License. You may obtain a copy of
+	the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required
+	by applicable law or agreed to in writing, software distributed under the
+	License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
+	OF ANY KIND, either express or implied. See the License for the specific
+	language governing permissions and limitations under the License. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.isis.testing</groupId>
+        <artifactId>isis-testing-archtestsupport</artifactId>
+        <version>2.0.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>isis-testing-archtestsupport-applib</artifactId>
+    <name>Apache Isis Tst - Architecture Test Support (applib)</name>
+
+    <properties>
+        <jar-plugin.automaticModuleName>org.apache.isis.testing.archtestsupport.applib</jar-plugin.automaticModuleName>
+        <git-plugin.propertiesDir>org/apache/isis/testing/archtestsupport/applib</git-plugin.propertiesDir>
+    </properties>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>org.apache.isis.core</groupId>
+            <artifactId>isis-applib</artifactId>
+        </dependency>
+
+        <!-- SPRING -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.ow2.asm</groupId>
+                    <artifactId>asm</artifactId>
+                </exclusion>
+                <exclusion>
+                    <!-- when spring-boot release lags behind spring-core release explicitly
+                        spring-test added below -->
+                    <groupId>org.springframework</groupId>
+                    <artifactId>spring-test</artifactId>
+                </exclusion>
+                <exclusion>
+                    <!-- when spring-boot release lags behind spring-core release explicitly
+                        spring-test added below -->
+                    <groupId>org.springframework</groupId>
+                    <artifactId>spring-core</artifactId>
+                </exclusion>
+                <exclusion>
+                    <!-- we use log4j-2 instead -->
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-logging</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.slf4j</groupId>
+                    <artifactId>slf4j-api</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-test</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.tngtech.archunit</groupId>
+            <artifactId>archunit-junit5-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.tngtech.archunit</groupId>
+            <artifactId>archunit-junit5-engine</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+
+    </dependencies>
+
+    <profiles>
+
+        <profile>
+            <id>jdk11-supplemental</id>
+            <activation>
+                <jdk>[11,)</jdk>
+            </activation>
+			<!-- These dependencies are required in order to build on JDK11+ -->
+			<dependencies>
+				<dependency>
+					<groupId>org.apache.isis.core</groupId>
+					<artifactId>isis-jdk-supplemental</artifactId>
+					<type>pom</type>
+				</dependency>
+			</dependencies>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/IsisModuleTestingArchTestSupportApplib.java b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/IsisModuleTestingArchTestSupportApplib.java
new file mode 100644
index 0000000..8a234cd
--- /dev/null
+++ b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/IsisModuleTestingArchTestSupportApplib.java
@@ -0,0 +1,28 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.testing.archtestsupport.applib;
+
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @since 2.0 {@index}
+ */
+@Configuration
+public class IsisModuleTestingArchTestSupportApplib {
+}
diff --git a/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/ArchitectureClassRules.java b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/ArchitectureClassRules.java
new file mode 100644
index 0000000..eb8eba2
--- /dev/null
+++ b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/ArchitectureClassRules.java
@@ -0,0 +1,35 @@
+package org.apache.isis.testing.archtestsupport.applib.classrules;
+
+import javax.persistence.Entity;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+
+import com.tngtech.archunit.lang.ArchRule;
+
+import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
+
+import org.apache.isis.applib.annotation.DomainObject;
+import org.apache.isis.applib.annotation.DomainObjectLayout;
+
+import lombok.experimental.UtilityClass;
+
+@UtilityClass
+public class ArchitectureClassRules {
+
+  public static ArchRule classes_annotated_with_Entity_must_also_be_annotated_with_DomainObject() {
+    return classes()
+          .that().areAnnotatedWith(Entity.class)
+          .should().beAnnotatedWith(DomainObject.class);
+  }
+
+  public static ArchRule classes_annotated_with_Entity_must_also_be_annotated_with_XmlJavaAdapter() {
+    return classes()
+      .that().areAnnotatedWith(Entity.class)
+      .should().beAnnotatedWith(XmlJavaTypeAdapter.class);
+  }
+
+  public static ArchRule classes_annotated_with_DomainObject_must_also_be_annotated_with_DomainObjectLayout() {
+    return classes()
+      .that().areAnnotatedWith(DomainObject.class)
+      .should().beAnnotatedWith(DomainObjectLayout.class);
+  }
+}
diff --git a/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/modulerules/ArchitectureModuleRules.java b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/modulerules/ArchitectureModuleRules.java
new file mode 100644
index 0000000..76520ea
--- /dev/null
+++ b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/modulerules/ArchitectureModuleRules.java
@@ -0,0 +1,154 @@
+package org.apache.isis.testing.archtestsupport.applib.modulerules;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import com.tngtech.archunit.junit.AnalyzeClasses;
+import com.tngtech.archunit.library.Architectures;
+
+import org.springframework.context.annotation.Import;
+import org.springframework.core.annotation.AnnotationUtils;
+
+import lombok.val;
+import lombok.experimental.UtilityClass;
+
+
+@UtilityClass
+public class ArchitectureModuleRules {
+
+  /**
+   * Utility method.
+   *
+   * @param clazz
+   * @return
+   */
+  public static List<Class<?>> analyzeClasses_packagesOf(Class<?> clazz) {
+    val analyzeClassesAnnot = AnnotationUtils.findAnnotation(clazz, AnalyzeClasses.class);
+    return Arrays.stream(analyzeClassesAnnot.packagesOf())
+      .filter(x -> x.getSimpleName().endsWith("Module"))
+      .collect(Collectors.toList());
+  }
+
+  /**
+   * Ensures that the actual dependencies between classes within modules honour the module dependency graph inferred
+   * from the {@link Import} statements of each module.
+   */
+  public static Architectures.LayeredArchitecture code_dependencies_follow_module_Imports(List<Class<?>> moduleClasses) {
+    val layeredArchitecture = Architectures.layeredArchitecture();
+
+    defineLayers(moduleClasses, layeredArchitecture);
+
+    val directDependenciesByImported = new HashMap<Class<?>, Set<Class<?>>>();
+    val directDependenciesByImporting = new HashMap<Class<?>, Set<Class<?>>>();
+    computeDirectDependencies(moduleClasses, directDependenciesByImported, directDependenciesByImporting);
+
+    val transitiveDependenciesByImporting = new HashMap<Class<?>, Set<Class<?>>>();
+    computeTransitiveDependencies(moduleClasses, directDependenciesByImporting, transitiveDependenciesByImporting);
+
+    val transitiveDependenciesByImported = invert(transitiveDependenciesByImporting);
+    checkLayerAccess(layeredArchitecture, transitiveDependenciesByImported);
+
+    val importingClassesNotImported = new LinkedHashSet<Class<?>>(transitiveDependenciesByImporting.keySet());
+    importingClassesNotImported.removeAll(transitiveDependenciesByImported.keySet());
+
+    checkNoAccessToTopmostLayers(layeredArchitecture, importingClassesNotImported);
+    return layeredArchitecture;
+  }
+
+  private static void defineLayers(List<Class<?>> moduleClasses, Architectures.LayeredArchitecture layeredArchitecture) {
+    moduleClasses.forEach(x -> {
+      final String moduleName = nameOf(x);
+      final String s = packageIdentifierFor(x);
+      layeredArchitecture.layer(moduleName).definedBy(s);
+    });
+  }
+
+  private static void computeDirectDependencies(List<Class<?>> moduleClasses, Map<Class<?>, Set<Class<?>>> directDependenciesByImported, Map<Class<?>, Set<Class<?>>> directDependenciesByImporting) {
+    moduleClasses.forEach(
+      moduleClass -> {
+        final Import importAnnotation = AnnotationUtils.findAnnotation(moduleClass, Import.class);
+        if (importAnnotation != null) {
+          val importedClassesFromAnnot = importAnnotation.value();
+          val importedClasses = new LinkedHashSet<>(Arrays.asList(importedClassesFromAnnot));
+          directDependenciesByImporting.put(moduleClass, importedClasses);
+          importedClasses.forEach(
+            importedClass -> {
+              val importingClasses = directDependenciesByImported.computeIfAbsent(importedClass, k -> new LinkedHashSet<>());
+              importingClasses.add(moduleClass);
+            }
+          );
+        }
+      }
+    );
+  }
+
+  private static void computeTransitiveDependencies(List<Class<?>> moduleClasses, Map<Class<?>, Set<Class<?>>> directDependenciesByImporting, Map<Class<?>, Set<Class<?>>> transitiveDependenciesByImporting) {
+    moduleClasses.forEach((moduleClass) -> {
+      val transitiveDependencies = new LinkedHashSet<Class<?>>();
+      accumulateTransitiveDependencies(moduleClass, directDependenciesByImporting, transitiveDependencies);
+      transitiveDependenciesByImporting.put(moduleClass, transitiveDependencies);
+    });
+  }
+
+  private static void checkLayerAccess(Architectures.LayeredArchitecture layeredArchitecture, Map<Class<?>, Set<Class<?>>> transitiveDependenciesByImported) {
+    transitiveDependenciesByImported.forEach((importedClass, importingClasses) -> {
+      final String importedModuleName = nameOf(importedClass);
+      final String[] importingModuleNames = namesOf(importingClasses);
+      layeredArchitecture
+        .whereLayer(importedModuleName)
+        .mayOnlyBeAccessedByLayers(importingModuleNames);
+    });
+  }
+
+  private static void checkNoAccessToTopmostLayers(Architectures.LayeredArchitecture layeredArchitecture, Set<Class<?>> importingClassesNotImported) {
+    importingClassesNotImported.forEach(importingClass -> {
+      final String importingModuleName = nameOf(importingClass);
+      layeredArchitecture
+        .whereLayer(importingModuleName)
+        .mayNotBeAccessedByAnyLayer();
+    });
+  }
+
+  static String nameOf(Class<?> moduleClass) {
+    return moduleClass.getSimpleName();
+  }
+
+  static String packageIdentifierFor(Class<?> moduleClass) {
+    return moduleClass.getPackage().getName() + "..";
+  }
+
+  static String[] namesOf(Set<Class<?>> importingClasses) {
+    return importingClasses.stream()
+      .map(ArchitectureModuleRules::nameOf)
+      .collect(Collectors.toList())
+      .toArray(new String[]{});
+  }
+
+  static <T> Map<T, Set<T>> invert(Map<T, Set<T>> valueSetByKey) {
+    val inverted = new LinkedHashMap<T, Set<T>>();
+    valueSetByKey.forEach((key, values) ->
+      values.forEach(value -> {
+        val keySet = inverted.computeIfAbsent(value, k -> new LinkedHashSet<>());
+        keySet.add(key);
+      }));
+    return inverted;
+  }
+
+  static void accumulateTransitiveDependencies(
+    final Class<?> referringClass
+    , final Map<Class<?>, Set<Class<?>>> directDependenciesByReferringClass
+    , final Set<Class<?>> transitiveDependenciesOfReferringClass) {
+    val directDependencies = directDependenciesByReferringClass.getOrDefault(referringClass, Collections.emptySet());
+    transitiveDependenciesOfReferringClass.addAll(directDependencies);
+    directDependencies.forEach(directDependency ->
+      accumulateTransitiveDependencies(directDependency, directDependenciesByReferringClass, transitiveDependenciesOfReferringClass));
+  }
+
+}
diff --git a/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/packagerules/ArchitecturePackageRules.java b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/packagerules/ArchitecturePackageRules.java
new file mode 100644
index 0000000..bdd6091
--- /dev/null
+++ b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/packagerules/ArchitecturePackageRules.java
@@ -0,0 +1,33 @@
+package org.apache.isis.testing.archtestsupport.applib.packagerules;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import com.tngtech.archunit.lang.ArchRule;
+import com.tngtech.archunit.library.Architectures;
+
+import lombok.val;
+import lombok.experimental.UtilityClass;
+
+@UtilityClass
+public class ArchitecturePackageRules {
+
+    public static ArchRule code_dependencies_follow_module_subpackages(Class<?> moduleClass, List<Subpackage> subpackages) {
+        val layeredArchitecture = Architectures.layeredArchitecture();
+        defineAndCheckSubpackageDependencies(moduleClass, layeredArchitecture, subpackages);
+        return layeredArchitecture;
+    }
+
+    void defineAndCheckSubpackageDependencies(Class<?> moduleClass, Architectures.LayeredArchitecture layeredArchitecture, List<Subpackage> subpackages) {
+        subpackages.forEach(subpackage -> subpackage.defineLayer(layeredArchitecture, moduleClass));
+
+        subpackages.forEach(referenced -> {
+            final String[] referencingSubpackageNames =
+                    subpackages.stream().filter(subpackage -> subpackage.canReference(referenced))
+                            .map(Subpackage::getName)
+                            .collect(Collectors.toList()).toArray(new String[]{});
+            layeredArchitecture.whereLayer(referenced.getName()).mayOnlyBeAccessedByLayers(referencingSubpackageNames);
+        });
+    }
+
+}
diff --git a/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/packagerules/Subpackage.java b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/packagerules/Subpackage.java
new file mode 100644
index 0000000..b609683
--- /dev/null
+++ b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/packagerules/Subpackage.java
@@ -0,0 +1,20 @@
+package org.apache.isis.testing.archtestsupport.applib.packagerules;
+
+import com.tngtech.archunit.library.Architectures;
+
+import lombok.val;
+
+public interface Subpackage {
+    String getName();
+
+    SubpackageType getSubpackageType();
+
+    String packageIdentifierWithin(Class<?> moduleClass);
+
+    default void defineLayer(Architectures.LayeredArchitecture layeredArchitecture, Class<?> moduleClass) {
+        val layerDefinition = getSubpackageType().defineLayer(this, moduleClass, layeredArchitecture);
+        layerDefinition.definedBy(packageIdentifierWithin(moduleClass));
+    }
+
+    boolean canReference(Subpackage referenced);
+}
diff --git a/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/packagerules/SubpackageEnum.java b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/packagerules/SubpackageEnum.java
new file mode 100644
index 0000000..cd2c614
--- /dev/null
+++ b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/packagerules/SubpackageEnum.java
@@ -0,0 +1,69 @@
+package org.apache.isis.testing.archtestsupport.applib.packagerules;
+
+import java.util.List;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.emptyList;
+import static java.util.Collections.singletonList;
+
+import static org.apache.isis.testing.archtestsupport.applib.packagerules.SubpackageType.MANDATORY;
+import static org.apache.isis.testing.archtestsupport.applib.packagerules.SubpackageType.OPTIONAL;
+
+import lombok.Getter;
+
+
+public enum SubpackageEnum implements Subpackage {
+
+    parent(MANDATORY, emptyList(), "fixtures") {
+        @Override
+        public String packageIdentifierWithin(Class<?> moduleClass) {
+            return moduleClass.getPackage().getName() + "..";
+        }
+    },
+
+    dom(OPTIONAL, emptyList(), "fixtures"), // allow access to personas
+
+    app(OPTIONAL, singletonList(dom)),
+
+    menu(OPTIONAL, singletonList(dom)),
+
+    contributions(OPTIONAL, singletonList(dom)),
+
+    subscriptions(OPTIONAL, singletonList(dom)),
+
+    restapi(OPTIONAL, singletonList(dom)),
+
+    spi(OPTIONAL, singletonList(dom)),
+
+    spiimpl(OPTIONAL, singletonList(dom)),
+
+    fixtures(OPTIONAL, asList(dom, menu, contributions)),
+
+    seed(OPTIONAL, asList(dom, fixtures)),
+
+    integtests(OPTIONAL, asList(dom, fixtures, app, menu, contributions, subscriptions, restapi, spi, spiimpl, seed)),
+    ;
+
+    @Getter
+    final SubpackageType subpackageType;
+    final List<Subpackage> references;
+    final List<String> softReferences;
+
+    SubpackageEnum(SubpackageType subpackageType, List<Subpackage> references, String... softReferences) {
+        this.subpackageType = subpackageType;
+        this.references = references;
+        this.softReferences = asList(softReferences);
+    }
+
+    public String getName() {
+        return name();
+    }
+
+    public String packageIdentifierWithin(Class<?> moduleClass) {
+        return moduleClass.getPackage().getName() + "." + name() + "..";
+    }
+
+    public boolean canReference(Subpackage subpackage) {
+        return references.contains(subpackage) || softReferences.contains(subpackage.getName());
+    }
+}
diff --git a/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/packagerules/SubpackageType.java b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/packagerules/SubpackageType.java
new file mode 100644
index 0000000..cfc7a90
--- /dev/null
+++ b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/packagerules/SubpackageType.java
@@ -0,0 +1,22 @@
+package org.apache.isis.testing.archtestsupport.applib.packagerules;
+
+import com.tngtech.archunit.library.Architectures;
+
+public enum SubpackageType {
+    MANDATORY {
+        @Override
+        Architectures.LayeredArchitecture.LayerDefinition defineLayer(
+                Subpackage subpackage, Class<?> moduleClass, Architectures.LayeredArchitecture layeredArchitecture) {
+            return layeredArchitecture.layer(subpackage.getName());
+        }
+    },
+    OPTIONAL {
+        @Override
+        Architectures.LayeredArchitecture.LayerDefinition defineLayer(
+                Subpackage subpackage, Class<?> moduleClass, Architectures.LayeredArchitecture layeredArchitecture) {
+            return layeredArchitecture.optionalLayer(subpackage.getName());
+        }
+    };
+
+    abstract Architectures.LayeredArchitecture.LayerDefinition defineLayer(Subpackage subpackage, Class<?> moduleClass, Architectures.LayeredArchitecture layeredArchitecture);
+}
diff --git a/testing/archtestsupport/pom.xml b/testing/archtestsupport/pom.xml
new file mode 100644
index 0000000..19868df
--- /dev/null
+++ b/testing/archtestsupport/pom.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor
+	license agreements. See the NOTICE file distributed with this work for additional
+	information regarding copyright ownership. The ASF licenses this file to
+	you under the Apache License, Version 2.0 (the "License"); you may not use
+	this file except in compliance with the License. You may obtain a copy of
+	the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required
+	by applicable law or agreed to in writing, software distributed under the
+	License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
+	OF ANY KIND, either express or implied. See the License for the specific
+	language governing permissions and limitations under the License. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.isis.testing</groupId>
+        <artifactId>isis-testing</artifactId>
+        <version>2.0.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>isis-testing-archtestsupport</artifactId>
+
+    <name>Apache Isis Tst - Architecture Test Support (parent)</name>
+    <description>
+        A module providing a library of architecture tests
+    </description>
+
+    <packaging>pom</packaging>
+
+    <modules>
+        <module>applib</module>
+    </modules>
+
+</project>
diff --git a/testing/pom.xml b/testing/pom.xml
index 1314304..efe36d8 100644
--- a/testing/pom.xml
+++ b/testing/pom.xml
@@ -127,6 +127,7 @@
 
 
 	<modules>
+		<module>archtestsupport</module>
 		<module>fakedata</module>
 		<module>fixtures</module>
 		<module>h2console</module>