Browse Source

Dependency tests are now based on ArchUnit.

Replaced ignored tests with exclusion of packages and classes.
Upgraded ArchUnit dependency to 1.0.0.

Closes #1354
pull/1355/head
Jens Schauder 3 years ago
parent
commit
58bf5566b6
  1. 2
      pom.xml
  2. 26
      spring-data-jdbc/src/test/java/org/springframework/data/jdbc/DependencyTests.java
  3. 6
      spring-data-r2dbc/pom.xml
  4. 164
      spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/DependencyTests.java
  5. 21
      spring-data-relational/src/test/java/org/springframework/data/relational/DependencyTests.java

2
pom.xml

@ -41,7 +41,7 @@
<!-- test utilities--> <!-- test utilities-->
<awaitility.version>4.2.0</awaitility.version> <awaitility.version>4.2.0</awaitility.version>
<archunit.version>0.23.1</archunit.version> <archunit.version>1.0.0</archunit.version>
</properties> </properties>
<inceptionYear>2017</inceptionYear> <inceptionYear>2017</inceptionYear>

26
spring-data-jdbc/src/test/java/org/springframework/data/jdbc/DependencyTests.java

@ -16,7 +16,6 @@
package org.springframework.data.jdbc; package org.springframework.data.jdbc;
import org.assertj.core.api.SoftAssertions; import org.assertj.core.api.SoftAssertions;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.data.auditing.config.AuditingHandlerBeanDefinitionParser; import org.springframework.data.auditing.config.AuditingHandlerBeanDefinitionParser;
@ -43,8 +42,7 @@ public class DependencyTests {
JavaClasses importedClasses = new ClassFileImporter() // JavaClasses importedClasses = new ClassFileImporter() //
.withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS) // .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS) //
.withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_JARS) // we just analyze the code of this module. .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_JARS) // we just analyze the code of this module.
.importPackages("org.springframework.data.jdbc") .importPackages("org.springframework.data.jdbc").that( //
.that( //
onlySpringData() // onlySpringData() //
); );
@ -57,17 +55,17 @@ public class DependencyTests {
} }
@Test @Test
@Disabled("Cycle in Spring Data Commons")
void acrossModules() { void acrossModules() {
JavaClasses importedClasses = new ClassFileImporter() JavaClasses importedClasses = new ClassFileImporter().withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS)
.withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS)
.importPackages( // .importPackages( //
"org.springframework.data.jdbc", // Spring Data Relational "org.springframework.data.jdbc", // Spring Data Relational
"org.springframework.data.relational", // Spring Data Relational "org.springframework.data.relational", // Spring Data Relational
"org.springframework.data" // Spring Data Commons "org.springframework.data" // Spring Data Commons
).that(onlySpringData()) // ).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() // ArchRule rule = SlicesRuleDefinition.slices() //
.assignedFrom(subModuleSlicing()) // .assignedFrom(subModuleSlicing()) //
@ -99,7 +97,7 @@ public class DependencyTests {
return new DescribedPredicate<>("Spring Data Classes") { return new DescribedPredicate<>("Spring Data Classes") {
@Override @Override
public boolean apply(JavaClass input) { public boolean test(JavaClass input) {
return input.getPackageName().startsWith("org.springframework.data"); return input.getPackageName().startsWith("org.springframework.data");
} }
}; };
@ -109,12 +107,22 @@ public class DependencyTests {
return new DescribedPredicate<>("ignored class " + type.getName()) { return new DescribedPredicate<>("ignored class " + type.getName()) {
@Override @Override
public boolean apply(JavaClass input) { public boolean test(JavaClass input) {
return !input.getFullName().startsWith(type.getName()); 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) { private String getFirstPackagePart(String subpackage) {
int index = subpackage.indexOf("."); int index = subpackage.indexOf(".");

6
spring-data-r2dbc/pom.xml

@ -306,6 +306,12 @@
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>com.tngtech.archunit</groupId>
<artifactId>archunit</artifactId>
<version>${archunit.version}</version>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>

164
spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/DependencyTests.java

@ -15,17 +15,20 @@
*/ */
package org.springframework.data.r2dbc; package org.springframework.data.r2dbc;
import static de.schauderhaft.degraph.check.JCheck.*; import org.assertj.core.api.SoftAssertions;
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.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test; 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. * Test package dependencies for violations.
* *
@ -35,40 +38,131 @@ import org.junit.jupiter.api.Test;
public class DependencyTests { public class DependencyTests {
@Test // DATAJDBC-114 @Test // DATAJDBC-114
public void cycleFree() { void cycleFree() {
Assume.assumeThat( // JavaClasses importedClasses = new ClassFileImporter() //
classpath() // .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS) //
.noJars() // .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_JARS) // we just analyze the code of this module.
.including("org.springframework.data.jdbc.**") // .importPackages("org.springframework.data.r2dbc").that( //
.including("org.springframework.data.relational.**") // onlySpringData() //
.including("org.springframework.data.r2dbc.**") // );
.filterClasspath("*target/classes") // exclude test code
.withSlicing("modules", "org.springframework.data.(*).**").printOnFailure("degraph.graphml"), ArchRule rule = SlicesRuleDefinition.slices() //
JCheck.violationFree()); .matching("org.springframework.data.r2dbc.(**)") //
.should() //
.beFreeOfCycles();
rule.check(importedClasses);
} }
@Test // DATAJDBC-220 @Test // DATAJDBC-220
public void acrossModules() { 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");
});
}
assertThat( // private DescribedPredicate<JavaClass> onlySpringData() {
classpath() //
// include only Spring Data related classes (for example no JDK code) return new DescribedPredicate<>("Spring Data Classes") {
.including("org.springframework.data.**") // @Override
.filterClasspath(new AbstractFunction1<String, Object>() { 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 @Override
public Object apply(String s) { // public boolean test(JavaClass input) {
// only the current module + commons return !input.getFullName().startsWith(type.getName());
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());
} }
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.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";
}
};
}
} }

21
spring-data-relational/src/test/java/org/springframework/data/relational/DependencyTests.java

@ -16,7 +16,6 @@
package org.springframework.data.relational; package org.springframework.data.relational;
import org.assertj.core.api.SoftAssertions; import org.assertj.core.api.SoftAssertions;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.data.relational.core.dialect.RenderContextFactory; import org.springframework.data.relational.core.dialect.RenderContextFactory;
import org.springframework.data.relational.core.sql.render.SelectRenderContext; import org.springframework.data.relational.core.sql.render.SelectRenderContext;
@ -59,7 +58,6 @@ public class DependencyTests {
} }
@Test @Test
@Disabled("Cycle in Spring Data Commons")
void acrossModules() { void acrossModules() {
JavaClasses importedClasses = new ClassFileImporter() // JavaClasses importedClasses = new ClassFileImporter() //
@ -67,8 +65,9 @@ public class DependencyTests {
.importPackages( // .importPackages( //
"org.springframework.data.relational", // Spring Data Relational "org.springframework.data.relational", // Spring Data Relational
"org.springframework.data" // Spring Data Commons "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() // ArchRule rule = SlicesRuleDefinition.slices() //
.assignedFrom(subModuleSlicing()) // .assignedFrom(subModuleSlicing()) //
@ -100,7 +99,7 @@ public class DependencyTests {
return new DescribedPredicate<>("Spring Data Classes") { return new DescribedPredicate<>("Spring Data Classes") {
@Override @Override
public boolean apply(JavaClass input) { public boolean test(JavaClass input) {
return input.getPackageName().startsWith("org.springframework.data"); return input.getPackageName().startsWith("org.springframework.data");
} }
}; };
@ -110,12 +109,22 @@ public class DependencyTests {
return new DescribedPredicate<>("ignored class " + type.getName()) { return new DescribedPredicate<>("ignored class " + type.getName()) {
@Override @Override
public boolean apply(JavaClass input) { public boolean test(JavaClass input) {
return !input.getFullName().startsWith(type.getName()); 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) { private String getFirstPackagePart(String subpackage) {
int index = subpackage.indexOf("."); int index = subpackage.indexOf(".");

Loading…
Cancel
Save