Compare commits

...

2 Commits

Author SHA1 Message Date
Jens Schauder
171f6641c7 archunit-dependency-tests - Prepare branch 2023-03-03 13:02:05 +01:00
Jens Schauder
4f8c29f48a Migrates DependencyTests to ArchUnit.
The tests are currently failing due to various undesired dependencies.

The tests start with something like:

		JavaClasses importedClasses = new ClassFileImporter()
		        ...
				.that(onlySpringData())

By adding

				.that(ignorePackage(org.springframework.data.aot.hint))

or

				.that(ignore(SomeClass.class));

the given classes or packages can be ignored for the analysis.
Of course the goal should be to fix the cycles.
2023-03-03 13:01:24 +01:00
5 changed files with 138 additions and 44 deletions

View File

@@ -5,7 +5,7 @@
<groupId>org.springframework.data</groupId> <groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId> <artifactId>spring-data-mongodb-parent</artifactId>
<version>4.1.0-SNAPSHOT</version> <version>4.1.0-archunit-dependency-tests-SNAPSHOT</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<name>Spring Data MongoDB</name> <name>Spring Data MongoDB</name>

View File

@@ -7,7 +7,7 @@
<parent> <parent>
<groupId>org.springframework.data</groupId> <groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId> <artifactId>spring-data-mongodb-parent</artifactId>
<version>4.1.0-SNAPSHOT</version> <version>4.1.0-archunit-dependency-tests-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>

View File

@@ -15,7 +15,7 @@
<parent> <parent>
<groupId>org.springframework.data</groupId> <groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId> <artifactId>spring-data-mongodb-parent</artifactId>
<version>4.1.0-SNAPSHOT</version> <version>4.1.0-archunit-dependency-tests-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>

View File

@@ -13,7 +13,7 @@
<parent> <parent>
<groupId>org.springframework.data</groupId> <groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId> <artifactId>spring-data-mongodb-parent</artifactId>
<version>4.1.0-SNAPSHOT</version> <version>4.1.0-archunit-dependency-tests-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
@@ -23,6 +23,7 @@
<java-module-name>spring.data.mongodb</java-module-name> <java-module-name>spring.data.mongodb</java-module-name>
<project.root>${basedir}/..</project.root> <project.root>${basedir}/..</project.root>
<multithreadedtc>1.01</multithreadedtc> <multithreadedtc>1.01</multithreadedtc>
<archunit.version>1.0.1</archunit.version>
</properties> </properties>
<dependencies> <dependencies>
@@ -250,9 +251,9 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>de.schauderhaft.degraph</groupId> <groupId>com.tngtech.archunit</groupId>
<artifactId>degraph-check</artifactId> <artifactId>archunit</artifactId>
<version>0.1.4</version> <version>${archunit.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>

View File

@@ -15,12 +15,16 @@
*/ */
package org.springframework.data.mongodb; package org.springframework.data.mongodb;
import static de.schauderhaft.degraph.check.JCheck.*; import com.tngtech.archunit.base.DescribedPredicate;
import static org.hamcrest.MatcherAssert.*; import com.tngtech.archunit.core.domain.JavaClass;
import com.tngtech.archunit.core.domain.JavaClasses;
import de.schauderhaft.degraph.configuration.NamedPattern; import com.tngtech.archunit.core.importer.ClassFileImporter;
import com.tngtech.archunit.core.importer.ImportOption;
import org.junit.jupiter.api.Disabled; 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;
import org.assertj.core.api.SoftAssertions;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
/** /**
@@ -29,49 +33,138 @@ import org.junit.jupiter.api.Test;
* @author Jens Schauder * @author Jens Schauder
* @author Oliver Gierke * @author Oliver Gierke
*/ */
@Disabled("Needs to be tansitioned to ArchUnit")
class DependencyTests { class DependencyTests {
@Test @Test
void noInternalPackageCycles() { void cycleFree() {
assertThat(classpath() // JavaClasses importedClasses = new ClassFileImporter() //
.noJars() // .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS) //
.including("org.springframework.data.mongodb.**") // .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_JARS) // we just analyze the code of this module.
.filterClasspath("*target/classes") // .importPackages("org.springframework.data.mongodb").that( //
.printOnFailure("degraph.graphml"), // onlySpringData() //
violationFree() // );
);
ArchRule rule = SlicesRuleDefinition.slices() //
.matching("org.springframework.data.mongodb.(**)") //
.should() //
.beFreeOfCycles();
rule.check(importedClasses);
} }
@Test @Test
void onlyConfigMayUseRepository() { void acrossModules() {
assertThat(classpath() // JavaClasses importedClasses = new ClassFileImporter().withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS)
.including("org.springframework.data.**") // .importPackages( //
.filterClasspath("*target/classes") // "org.springframework.data.mongodb", // Spring Data Relational
.printOnFailure("onlyConfigMayUseRepository.graphml") // "org.springframework.data" // Spring Data Commons
.withSlicing("slices", // ).that(onlySpringData());
"**.(config).**", //
new NamedPattern("**.cdi.**", "config"), // ArchRule rule = SlicesRuleDefinition.slices() //
"**.(repository).**", // .assignedFrom(subModuleSlicing()) //
new NamedPattern("**", "other")) .should().beFreeOfCycles();
.allow("config", "repository", "other"), //
violationFree() // rule.check(importedClasses);
);
} }
@Test @Test
void commonsInternaly() { // GH-1058
void testGetFirstPackagePart() {
assertThat(classpath() // SoftAssertions.assertSoftly(softly -> {
.noJars() // softly.assertThat(getFirstPackagePart("a.b.c")).isEqualTo("a");
.including("org.springframework.data.**") // softly.assertThat(getFirstPackagePart("a")).isEqualTo("a");
.excluding("org.springframework.data.mongodb.**") // });
.filterClasspath("*target/classes") //
.printTo("commons.graphml"), //
violationFree() //
);
} }
@Test
// GH-1058
void testSubModule() {
SoftAssertions.assertSoftly(softly -> {
softly.assertThat(subModule("a.b", "a.b.c.d")).isEqualTo("c");
softly.assertThat(subModule("a.b", "a.b.c")).isEqualTo("c");
softly.assertThat(subModule("a.b", "a.b")).isEqualTo("");
});
}
private DescribedPredicate<JavaClass> onlySpringData() {
return new DescribedPredicate<>("Spring Data Classes") {
@Override
public boolean test(JavaClass input) {
return input.getPackageName().startsWith("org.springframework.data");
}
};
}
private DescribedPredicate<JavaClass> ignore(Class<?> type) {
return new DescribedPredicate<>("ignored class " + type.getName()) {
@Override
public boolean test(JavaClass input) {
return !input.getFullName().startsWith(type.getName());
}
};
}
private DescribedPredicate<JavaClass> 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.mongodb", 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";
}
};
}
} }