Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion archunit-example/example-junit4/build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
plugins {
id 'archunit.java-examples-conventions'
id 'archunit.self-test-conventions'
}

ext.moduleName = 'com.tngtech.archunit.example.junit4'
Expand All @@ -15,4 +16,4 @@ test {
excludeCategories 'com.tngtech.archunit.exampletest.junit4.Example'
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.tngtech.archunit.exampletest.junit4;

import com.tngtech.archunit.ArchUnitExampleArchitectureRules;
import com.tngtech.archunit.junit.AnalyzeClasses;
import com.tngtech.archunit.junit.ArchTest;
import com.tngtech.archunit.junit.ArchTests;
import com.tngtech.archunit.junit.ArchUnitRunner;
import org.junit.runner.RunWith;

@RunWith(ArchUnitRunner.class)
@AnalyzeClasses(packages = "com.tngtech.archunit.exampletest.junit4")
public class ArchUnitArchitectureTest {
private ArchUnitArchitectureTest() {
}

@ArchTest
public static final ArchTests example_rules = ArchTests.in(ArchUnitExampleArchitectureRules.class);
}
1 change: 1 addition & 0 deletions archunit-example/example-junit5/build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
plugins {
id 'archunit.java-examples-conventions'
id 'archunit.self-test-conventions'
}

ext.moduleName = 'com.tngtech.archunit.example.junit5'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.tngtech.archunit.exampletest.junit5;

import com.tngtech.archunit.ArchUnitExampleArchitectureRules;
import com.tngtech.archunit.junit.AnalyzeClasses;
import com.tngtech.archunit.junit.ArchTest;
import com.tngtech.archunit.junit.ArchTests;

@AnalyzeClasses(packages = "com.tngtech.archunit.exampletest.junit5")
public class ArchUnitArchitectureTest {
private ArchUnitArchitectureTest() {
}

@ArchTest
public static final ArchTests example_rules = ArchTests.in(ArchUnitExampleArchitectureRules.class);
}
20 changes: 20 additions & 0 deletions archunit-example/example-junit6/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
plugins {
id 'archunit.java-examples-conventions'
id 'archunit.self-test-conventions'
}

ext.moduleName = 'com.tngtech.archunit.example.junit6'
ext.minimumJavaVersion = JavaVersion.VERSION_17

dependencies {
testImplementation project(path: ':archunit-junit6')
testImplementation project(path: ':archunit-example:example-plain')
}

test {
useJUnitPlatform {
if (!project.hasProperty('example')) {
excludeTags 'example'
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.tngtech.archunit.exampletest.junit6;

import com.tngtech.archunit.ArchUnitExampleArchitectureRules;
import com.tngtech.archunit.junit.AnalyzeClasses;
import com.tngtech.archunit.junit.ArchTest;
import com.tngtech.archunit.junit.ArchTests;

@AnalyzeClasses(packages = "com.tngtech.archunit.exampletest.junit6")
public class ArchUnitArchitectureTest {
private ArchUnitArchitectureTest() {
}

@ArchTest
public static final ArchTests example_rules = ArchTests.in(ArchUnitExampleArchitectureRules.class);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.tngtech.archunit.exampletest.junit6;

import java.util.logging.Logger;

import com.tngtech.archunit.core.domain.JavaClasses;
import com.tngtech.archunit.junit.AnalyzeClasses;
import com.tngtech.archunit.junit.ArchTag;
import com.tngtech.archunit.junit.ArchTest;
import com.tngtech.archunit.lang.ArchRule;
import com.tngtech.archunit.lang.CompositeArchRule;

import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.fields;
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;
import static com.tngtech.archunit.library.GeneralCodingRules.ACCESS_STANDARD_STREAMS;
import static com.tngtech.archunit.library.GeneralCodingRules.NO_CLASSES_SHOULD_ACCESS_STANDARD_STREAMS;
import static com.tngtech.archunit.library.GeneralCodingRules.NO_CLASSES_SHOULD_THROW_GENERIC_EXCEPTIONS;
import static com.tngtech.archunit.library.GeneralCodingRules.NO_CLASSES_SHOULD_USE_FIELD_INJECTION;
import static com.tngtech.archunit.library.GeneralCodingRules.NO_CLASSES_SHOULD_USE_JAVA_UTIL_LOGGING;
import static com.tngtech.archunit.library.GeneralCodingRules.NO_CLASSES_SHOULD_USE_JODATIME;

@ArchTag("example")
@AnalyzeClasses(packages = "com.tngtech.archunit.example.layers")
public class CodingRulesTest {

@ArchTest
private final ArchRule no_access_to_standard_streams = NO_CLASSES_SHOULD_ACCESS_STANDARD_STREAMS;

@ArchTest
private void no_access_to_standard_streams_as_method(JavaClasses classes) {
noClasses().should(ACCESS_STANDARD_STREAMS).check(classes);
}

@ArchTest
private final ArchRule no_generic_exceptions = NO_CLASSES_SHOULD_THROW_GENERIC_EXCEPTIONS;

@ArchTest
private final ArchRule no_java_util_logging = NO_CLASSES_SHOULD_USE_JAVA_UTIL_LOGGING;

@ArchTest
private final ArchRule loggers_should_be_private_static_final =
fields().that().haveRawType(Logger.class)
.should().bePrivate()
.andShould().beStatic()
.andShould().beFinal()
.because("we agreed on this convention");

@ArchTest
private final ArchRule no_jodatime = NO_CLASSES_SHOULD_USE_JODATIME;

@ArchTest
private final ArchRule no_field_injection = NO_CLASSES_SHOULD_USE_FIELD_INJECTION;

@ArchTest
static final ArchRule no_classes_should_access_standard_streams_or_throw_generic_exceptions =
CompositeArchRule.of(NO_CLASSES_SHOULD_ACCESS_STANDARD_STREAMS)
.and(NO_CLASSES_SHOULD_THROW_GENERIC_EXCEPTIONS);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.tngtech.archunit.exampletest.junit6;

import com.tngtech.archunit.base.DescribedPredicate;
import com.tngtech.archunit.core.domain.JavaClass;
import com.tngtech.archunit.core.domain.JavaMember;
import com.tngtech.archunit.core.domain.PackageMatchers;
import com.tngtech.archunit.example.layers.security.Secured;
import com.tngtech.archunit.junit.AnalyzeClasses;
import com.tngtech.archunit.junit.ArchTag;
import com.tngtech.archunit.junit.ArchTest;
import com.tngtech.archunit.lang.ArchRule;

import static com.tngtech.archunit.core.domain.JavaClass.Functions.GET_PACKAGE_NAME;
import static com.tngtech.archunit.core.domain.JavaMember.Predicates.declaredIn;
import static com.tngtech.archunit.core.domain.properties.CanBeAnnotated.Predicates.annotatedWith;
import static com.tngtech.archunit.lang.conditions.ArchPredicates.are;
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;

@ArchTag("example")
@AnalyzeClasses(packages = "com.tngtech.archunit.example.layers")
public class ControllerRulesTest {

@ArchTest
static final ArchRule controllers_should_only_call_secured_methods =
classes().that().resideInAPackage("..controller..")
.should().onlyCallMethodsThat(areDeclaredInController().or(are(annotatedWith(Secured.class))));

@ArchTest
static final ArchRule controllers_should_only_call_secured_constructors =
classes()
.that().resideInAPackage("..controller..")
.should().onlyCallConstructorsThat(areDeclaredInController().or(are(annotatedWith(Secured.class))));

@ArchTest
static final ArchRule controllers_should_only_call_secured_code_units =
classes()
.that().resideInAPackage("..controller..")
.should().onlyCallCodeUnitsThat(areDeclaredInController().or(are(annotatedWith(Secured.class))));

@ArchTest
static final ArchRule controllers_should_only_access_secured_fields =
classes()
.that().resideInAPackage("..controller..")
.should().onlyAccessFieldsThat(areDeclaredInController().or(are(annotatedWith(Secured.class))));

@ArchTest
static final ArchRule controllers_should_only_access_secured_members =
classes()
.that().resideInAPackage("..controller..")
.should().onlyAccessMembersThat(areDeclaredInController().or(are(annotatedWith(Secured.class))));

private static DescribedPredicate<JavaMember> areDeclaredInController() {
DescribedPredicate<JavaClass> aPackageController = GET_PACKAGE_NAME.is(PackageMatchers.of("..controller..", "java.."))
.as("a package '..controller..'");
return are(declaredIn(aPackageController));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package com.tngtech.archunit.exampletest.junit6;

import com.tngtech.archunit.core.domain.JavaClass;
import com.tngtech.archunit.example.cycles.complexcycles.slice1.SliceOneCallingConstructorInSliceTwoAndMethodInSliceThree;
import com.tngtech.archunit.example.cycles.complexcycles.slice3.ClassCallingConstructorInSliceFive;
import com.tngtech.archunit.junit.AnalyzeClasses;
import com.tngtech.archunit.junit.ArchTag;
import com.tngtech.archunit.junit.ArchTest;
import com.tngtech.archunit.lang.ArchRule;
import com.tngtech.archunit.library.dependencies.SliceAssignment;
import com.tngtech.archunit.library.dependencies.SliceIdentifier;

import static com.tngtech.archunit.base.DescribedPredicate.alwaysTrue;
import static com.tngtech.archunit.core.domain.JavaClass.Predicates.resideInAPackage;
import static com.tngtech.archunit.library.dependencies.SlicesRuleDefinition.slices;

@ArchTag("example")
@AnalyzeClasses(packages = "com.tngtech.archunit.example.cycles")
public class CyclicDependencyRulesTest {

@ArchTest
static final ArchRule no_cycles_by_method_calls_between_slices =
slices().matching("..(simplecycle).(*)..").namingSlices("$2 of $1").should().beFreeOfCycles();

@ArchTest
static final ArchRule no_cycles_by_constructor_calls_between_slices =
slices().matching("..(constructorcycle).(*)..").namingSlices("$2 of $1").should().beFreeOfCycles();

@ArchTest
static final ArchRule no_cycles_by_inheritance_between_slices =
slices().matching("..(inheritancecycle).(*)..").namingSlices("$2 of $1").should().beFreeOfCycles();

@ArchTest
static final ArchRule no_cycles_by_field_access_between_slices =
slices().matching("..(fieldaccesscycle).(*)..").namingSlices("$2 of $1").should().beFreeOfCycles();

@ArchTest
static final ArchRule no_cycles_by_member_dependencies_between_slices =
slices().matching("..(membercycle).(*)..").namingSlices("$2 of $1").should().beFreeOfCycles();

@ArchTest
static final ArchRule no_cycles_in_simple_scenario =
slices().matching("..simplescenario.(*)..").namingSlices("$1").should().beFreeOfCycles();

@ArchTest
static final ArchRule no_cycles_in_complex_scenario =
slices().matching("..(complexcycles).(*)..").namingSlices("$2 of $1").should().beFreeOfCycles();

@ArchTest
static final ArchRule no_cycles_in_complex_scenario_with_custom_ignore =
slices().matching("..(complexcycles).(*)..").namingSlices("$2 of $1")
.as("Slices of complex scenario ignoring some violations")
.should().beFreeOfCycles()
.ignoreDependency(SliceOneCallingConstructorInSliceTwoAndMethodInSliceThree.class, ClassCallingConstructorInSliceFive.class)
.ignoreDependency(resideInAPackage("..slice4.."), alwaysTrue());

@ArchTest
static final ArchRule no_cycles_in_freely_customized_slices =
slices().assignedFrom(inComplexSliceOneOrTwo())
.namingSlices("$1[$2]")
.should().beFreeOfCycles();

private static SliceAssignment inComplexSliceOneOrTwo() {
return new SliceAssignment() {
@Override
public String getDescription() {
return "complex slice one or two";
}

@Override
public SliceIdentifier getIdentifierOf(JavaClass javaClass) {
if (javaClass.getPackageName().contains("complexcycles.slice1")) {
return SliceIdentifier.of("Complex-Cycle", "One");
}
if (javaClass.getPackageName().contains("complexcycles.slice2")) {
return SliceIdentifier.of("Complex-Cycle", "Two");
}
return SliceIdentifier.ignore();
}
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.tngtech.archunit.exampletest.junit6;

import java.sql.SQLException;

import javax.persistence.Entity;
import javax.persistence.EntityManager;

import com.tngtech.archunit.junit.AnalyzeClasses;
import com.tngtech.archunit.junit.ArchTag;
import com.tngtech.archunit.junit.ArchTest;
import com.tngtech.archunit.lang.ArchRule;

import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noMethods;

@ArchTag("example")
@AnalyzeClasses(packages = "com.tngtech.archunit.example.layers")
public class DaoRulesTest {
@ArchTest
static final ArchRule DAOs_must_reside_in_a_dao_package =
classes().that().haveNameMatching(".*Dao").should().resideInAPackage("..dao..")
.as("DAOs should reside in a package '..dao..'");

@ArchTest
static final ArchRule entities_must_reside_in_a_domain_package =
classes().that().areAnnotatedWith(Entity.class).should().resideInAPackage("..domain..")
.as("Entities should reside in a package '..domain..'");

@ArchTest
static final ArchRule only_DAOs_may_use_the_EntityManager =
noClasses().that().resideOutsideOfPackage("..dao..")
.should().accessClassesThat().areAssignableTo(EntityManager.class)
.as("Only DAOs may use the " + EntityManager.class.getSimpleName());


@ArchTest
static final ArchRule DAOs_must_not_throw_SQLException =
noMethods().that().areDeclaredInClassesThat().haveNameMatching(".*Dao")
.should().declareThrowableOfType(SQLException.class);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.tngtech.archunit.exampletest.junit6;

import com.tngtech.archunit.example.layers.ClassViolatingCodingRules;
import com.tngtech.archunit.junit.AnalyzeClasses;
import com.tngtech.archunit.junit.ArchTag;
import com.tngtech.archunit.junit.ArchTest;
import com.tngtech.archunit.lang.ArchRule;

import static com.tngtech.archunit.library.DependencyRules.NO_CLASSES_SHOULD_DEPEND_UPPER_PACKAGES;

@ArchTag("example")
@AnalyzeClasses(packagesOf = ClassViolatingCodingRules.class)
public class DependencyRulesTest {

@ArchTest
static final ArchRule no_accesses_to_upper_package = NO_CLASSES_SHOULD_DEPEND_UPPER_PACKAGES;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.tngtech.archunit.exampletest.junit6;

import javax.persistence.EntityManager;

import com.tngtech.archunit.ArchConfiguration;
import com.tngtech.archunit.junit.AnalyzeClasses;
import com.tngtech.archunit.junit.ArchTag;
import com.tngtech.archunit.junit.ArchTest;
import com.tngtech.archunit.lang.ArchRule;
import com.tngtech.archunit.library.freeze.FreezingArchRule;
import com.tngtech.archunit.library.freeze.ViolationLineMatcher;
import com.tngtech.archunit.library.freeze.ViolationStore;

import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;
import static com.tngtech.archunit.library.freeze.FreezingArchRule.freeze;

/**
* This test demonstrates the use of {@link FreezingArchRule} with 'default' configuration.
* While both rules shown have numerous violations, most of those violations have been 'frozen', i.e. at some point in time all violations
* were recorded as accepted for the moment. Only violations added afterwards will be reported.<br>
* You can see how the default text based {@link ViolationStore} stores the results under {@code src/test/resources/frozen} configured
* via {@value ArchConfiguration#ARCHUNIT_PROPERTIES_RESOURCE_NAME}. You can also
* observe that if you fix an old violation, this store will automatically be adjusted to not allow any regression.<br>
* Furthermore you can observe how the default {@link ViolationLineMatcher} will ignore changes in line numbers of recorded violations,
* i.e. if you only change the line numbers of frozen violations, the test will still pass.
*/
@ArchTag("example")
@AnalyzeClasses(packages = "com.tngtech.archunit.example.layers")
public class FrozenRulesTest {

@ArchTest
static final ArchRule no_classes_should_depend_on_service =
freeze(noClasses().should().dependOnClassesThat().resideInAPackage("..service.."));

@ArchTest
static final ArchRule no_classes_should_use_the_EntityManager =
freeze(noClasses().should().dependOnClassesThat().areAssignableTo(EntityManager.class));
}

Loading
Loading