diff --git a/pom.xml b/pom.xml index b2d4b1d1a..1399a7fc4 100644 --- a/pom.xml +++ b/pom.xml @@ -41,7 +41,7 @@ 4.2.0 - 0.23.1 + 1.0.0 2017 diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/DependencyTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/DependencyTests.java index e5e0c5625..cdd296a69 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/DependencyTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/DependencyTests.java @@ -16,7 +16,6 @@ package org.springframework.data.jdbc; import org.assertj.core.api.SoftAssertions; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.data.auditing.config.AuditingHandlerBeanDefinitionParser; @@ -43,10 +42,9 @@ public class DependencyTests { JavaClasses importedClasses = new ClassFileImporter() // .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS) // .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_JARS) // we just analyze the code of this module. - .importPackages("org.springframework.data.jdbc") - .that( // - onlySpringData() // - ); + .importPackages("org.springframework.data.jdbc").that( // + onlySpringData() // + ); ArchRule rule = SlicesRuleDefinition.slices() // .matching("org.springframework.data.jdbc.(**)") // @@ -57,17 +55,17 @@ public class DependencyTests { } @Test - @Disabled("Cycle in Spring Data Commons") void acrossModules() { - JavaClasses importedClasses = new ClassFileImporter() - .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS) + JavaClasses importedClasses = new ClassFileImporter().withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS) .importPackages( // "org.springframework.data.jdbc", // Spring Data Relational "org.springframework.data.relational", // Spring Data Relational "org.springframework.data" // Spring Data Commons ).that(onlySpringData()) // - .that(ignore(AuditingHandlerBeanDefinitionParser.class)); + .that(ignore(AuditingHandlerBeanDefinitionParser.class)) // + .that(ignorePackage("org.springframework.data.aot.hint")) // ignoring aot, since it causes cycles in commons + .that(ignorePackage("org.springframework.data.aot")); // ignoring aot, since it causes cycles in commons ArchRule rule = SlicesRuleDefinition.slices() // .assignedFrom(subModuleSlicing()) // @@ -99,7 +97,7 @@ public class DependencyTests { return new DescribedPredicate<>("Spring Data Classes") { @Override - public boolean apply(JavaClass input) { + public boolean test(JavaClass input) { return input.getPackageName().startsWith("org.springframework.data"); } }; @@ -109,12 +107,22 @@ public class DependencyTests { return new DescribedPredicate<>("ignored class " + type.getName()) { @Override - public boolean apply(JavaClass input) { + public boolean test(JavaClass input) { return !input.getFullName().startsWith(type.getName()); } }; } + private DescribedPredicate ignorePackage(String type) { + + return new DescribedPredicate<>("ignored class " + type) { + @Override + public boolean test(JavaClass input) { + return !input.getPackageName().equals(type); + } + }; + } + private String getFirstPackagePart(String subpackage) { int index = subpackage.indexOf("."); diff --git a/spring-data-r2dbc/pom.xml b/spring-data-r2dbc/pom.xml index 14d7909d7..9bd821ef3 100644 --- a/spring-data-r2dbc/pom.xml +++ b/spring-data-r2dbc/pom.xml @@ -306,6 +306,12 @@ test + + com.tngtech.archunit + archunit + ${archunit.version} + test + diff --git a/spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/DependencyTests.java b/spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/DependencyTests.java index 51a0006e2..aaa399fd7 100644 --- a/spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/DependencyTests.java +++ b/spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/DependencyTests.java @@ -15,17 +15,20 @@ */ package org.springframework.data.r2dbc; -import static de.schauderhaft.degraph.check.JCheck.*; -import static org.junit.Assert.*; - -import de.schauderhaft.degraph.check.JCheck; -import de.schauderhaft.degraph.configuration.NamedPattern; -import scala.runtime.AbstractFunction1; - -import org.junit.Assume; +import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import com.tngtech.archunit.base.DescribedPredicate; +import com.tngtech.archunit.core.domain.JavaClass; +import com.tngtech.archunit.core.domain.JavaClasses; +import com.tngtech.archunit.core.importer.ClassFileImporter; +import com.tngtech.archunit.core.importer.ImportOption; +import com.tngtech.archunit.lang.ArchRule; +import com.tngtech.archunit.library.dependencies.SliceAssignment; +import com.tngtech.archunit.library.dependencies.SliceIdentifier; +import com.tngtech.archunit.library.dependencies.SlicesRuleDefinition; + /** * Test package dependencies for violations. * @@ -35,40 +38,131 @@ import org.junit.jupiter.api.Test; public class DependencyTests { @Test // DATAJDBC-114 - public void cycleFree() { - - Assume.assumeThat( // - classpath() // - .noJars() // - .including("org.springframework.data.jdbc.**") // - .including("org.springframework.data.relational.**") // - .including("org.springframework.data.r2dbc.**") // - .filterClasspath("*target/classes") // exclude test code - .withSlicing("modules", "org.springframework.data.(*).**").printOnFailure("degraph.graphml"), - JCheck.violationFree()); + void cycleFree() { + + JavaClasses importedClasses = new ClassFileImporter() // + .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS) // + .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_JARS) // we just analyze the code of this module. + .importPackages("org.springframework.data.r2dbc").that( // + onlySpringData() // + ); + + ArchRule rule = SlicesRuleDefinition.slices() // + .matching("org.springframework.data.r2dbc.(**)") // + .should() // + .beFreeOfCycles(); + + rule.check(importedClasses); } @Test // DATAJDBC-220 - public void acrossModules() { - - assertThat( // - classpath() // - // include only Spring Data related classes (for example no JDK code) - .including("org.springframework.data.**") // - .filterClasspath(new AbstractFunction1() { - @Override - public Object apply(String s) { // - // only the current module + commons - return s.endsWith("target/classes") || s.contains("spring-data-commons"); - } - }) // exclude test code - .withSlicing("sub-modules", // sub-modules are defined by any of the following pattern. - "org.springframework.data.jdbc.(**).*", // - "org.springframework.data.relational.(**).*", // - new NamedPattern("org.springframework.data.r2dbc.**", "repository.reactive"), // - "org.springframework.data.(**).*") // - .printTo("degraph-across-modules.graphml"), // writes a graphml to this location - JCheck.violationFree()); + void acrossModules() { + + JavaClasses importedClasses = new ClassFileImporter().withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS) + .importPackages( // + "org.springframework.data.r2dbc", // Spring Data Relational + "org.springframework.data.relational", // Spring Data Relational + "org.springframework.data" // Spring Data Commons + ).that(onlySpringData()) // + .that(ignorePackage("org.springframework.data.aot.hint")) // ignoring aot, since it causes cycles in commons + .that(ignorePackage("org.springframework.data.aot")); // ignoring aot, since it causes cycles in commons + + ArchRule rule = SlicesRuleDefinition.slices() // + .assignedFrom(subModuleSlicing()) // + .should().beFreeOfCycles(); + + rule.check(importedClasses); + } + + @Test // GH-1058 + void testGetFirstPackagePart() { + + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(getFirstPackagePart("a.b.c")).isEqualTo("a"); + softly.assertThat(getFirstPackagePart("a")).isEqualTo("a"); + }); + } + + private DescribedPredicate onlySpringData() { + + return new DescribedPredicate<>("Spring Data Classes") { + @Override + public boolean test(JavaClass input) { + return input.getPackageName().startsWith("org.springframework.data"); + } + }; + } + + private DescribedPredicate ignore(Class type) { + + return new DescribedPredicate<>("ignored class " + type.getName()) { + @Override + public boolean test(JavaClass input) { + return !input.getFullName().startsWith(type.getName()); + } + }; } + private DescribedPredicate ignorePackage(String type) { + + return new DescribedPredicate<>("ignored class " + type) { + @Override + public boolean test(JavaClass input) { + return !input.getPackageName().equals(type); + } + }; + } + + private String getFirstPackagePart(String subpackage) { + + int index = subpackage.indexOf("."); + if (index < 0) { + return subpackage; + } + return subpackage.substring(0, index); + } + + private String subModule(String basePackage, String packageName) { + + if (packageName.startsWith(basePackage) && packageName.length() > basePackage.length()) { + + final int index = basePackage.length() + 1; + String subpackage = packageName.substring(index); + return getFirstPackagePart(subpackage); + } + return ""; + } + + private SliceAssignment subModuleSlicing() { + return new SliceAssignment() { + + @Override + public SliceIdentifier getIdentifierOf(JavaClass javaClass) { + + String packageName = javaClass.getPackageName(); + + String subModule = subModule("org.springframework.data.jdbc", packageName); + if (!subModule.isEmpty()) { + return SliceIdentifier.of(subModule); + } + + subModule = subModule("org.springframework.data.relational", packageName); + if (!subModule.isEmpty()) { + return SliceIdentifier.of(subModule); + } + + subModule = subModule("org.springframework.data", packageName); + if (!subModule.isEmpty()) { + return SliceIdentifier.of(subModule); + } + + return SliceIdentifier.ignore(); + } + + @Override + public String getDescription() { + return "Submodule"; + } + }; + } } diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/DependencyTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/DependencyTests.java index a98b24423..716d1a5eb 100644 --- a/spring-data-relational/src/test/java/org/springframework/data/relational/DependencyTests.java +++ b/spring-data-relational/src/test/java/org/springframework/data/relational/DependencyTests.java @@ -16,7 +16,6 @@ package org.springframework.data.relational; import org.assertj.core.api.SoftAssertions; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.data.relational.core.dialect.RenderContextFactory; import org.springframework.data.relational.core.sql.render.SelectRenderContext; @@ -59,7 +58,6 @@ public class DependencyTests { } @Test - @Disabled("Cycle in Spring Data Commons") void acrossModules() { JavaClasses importedClasses = new ClassFileImporter() // @@ -67,8 +65,9 @@ public class DependencyTests { .importPackages( // "org.springframework.data.relational", // Spring Data Relational "org.springframework.data" // Spring Data Commons - ).that(onlySpringData()); - + ).that(onlySpringData()) // + .that(ignorePackage("org.springframework.data.aot.hint")) // ignoring aot, since it causes cycles in commons + .that(ignorePackage("org.springframework.data.aot")); // ignoring aot, since it causes cycles in commons; ArchRule rule = SlicesRuleDefinition.slices() // .assignedFrom(subModuleSlicing()) // @@ -100,7 +99,7 @@ public class DependencyTests { return new DescribedPredicate<>("Spring Data Classes") { @Override - public boolean apply(JavaClass input) { + public boolean test(JavaClass input) { return input.getPackageName().startsWith("org.springframework.data"); } }; @@ -110,12 +109,22 @@ public class DependencyTests { return new DescribedPredicate<>("ignored class " + type.getName()) { @Override - public boolean apply(JavaClass input) { + public boolean test(JavaClass input) { return !input.getFullName().startsWith(type.getName()); } }; } + private DescribedPredicate ignorePackage(String type) { + + return new DescribedPredicate<>("ignored class " + type) { + @Override + public boolean test(JavaClass input) { + return !input.getPackageName().equals(type); + } + }; + } + private String getFirstPackagePart(String subpackage) { int index = subpackage.indexOf(".");