Skip to content

Commit

Permalink
Merge pull request #80 from msherman32/SingleEquivalence
Browse files Browse the repository at this point in the history
Adds theClass(..), noClass(..), should().be(..) and should().notBe(..)

Resolves: #60
  • Loading branch information
codecholeric committed Jun 19, 2018
2 parents e6de5bc + f9656c9 commit 84088e9
Show file tree
Hide file tree
Showing 23 changed files with 1,954 additions and 49 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.tngtech.archunit.example;

import com.tngtech.archunit.example.core.VeryCentralCore;

@SuppressWarnings("unused")
public class EvilCoreAccessor {
void iShouldNotAccessCore() {
new VeryCentralCore().doCoreStuff();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.tngtech.archunit.example;

public interface SomeOtherBusinessInterface {
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.tngtech.archunit.example.controller;

import com.tngtech.archunit.example.MyController;
import com.tngtech.archunit.example.core.HighSecurity;

@HighSecurity
@MyController
public class WronglyAnnotated {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.tngtech.archunit.example.core;

@SuppressWarnings("unused")
public class AnotherGoodCoreSatellite implements CoreSatellite {
VeryCentralCore centralCore;

void iAlsoMayAccessCore() {
centralCore.doCoreStuff();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.tngtech.archunit.example.core;

public interface CoreSatellite {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.tngtech.archunit.example.core;

@SuppressWarnings("unused")
public class GoodCoreSatellite implements CoreSatellite {
VeryCentralCore centralCore;

void iMayAccessCore() {
centralCore.doCoreStuff();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.tngtech.archunit.example.core;

import java.lang.annotation.Retention;

import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Retention(RUNTIME)
public @interface HighSecurity {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.tngtech.archunit.example.core;

import com.tngtech.archunit.example.SomeOtherBusinessInterface;
import com.tngtech.archunit.example.web.AnnotatedController;

@HighSecurity
@SuppressWarnings("unused")
public class VeryCentralCore implements SomeOtherBusinessInterface {
public static final String DO_CORE_STUFF_METHOD_NAME = "doCoreStuff";

public void doCoreStuff() {
}

void coreDoingIllegalStuff() {
new AnnotatedController();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.tngtech.archunit.exampletest;

import com.tngtech.archunit.core.domain.JavaClasses;
import com.tngtech.archunit.core.importer.ClassFileImporter;
import com.tngtech.archunit.example.ClassViolatingCodingRules;
import com.tngtech.archunit.example.SomeOtherBusinessInterface;
import com.tngtech.archunit.example.core.CoreSatellite;
import com.tngtech.archunit.example.core.HighSecurity;
import com.tngtech.archunit.example.core.VeryCentralCore;
import org.junit.Test;
import org.junit.experimental.categories.Category;

import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClass;
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.theClass;

@Category(Example.class)
public class SingleClassTest {
private static final JavaClasses classes = new ClassFileImporter().importPackagesOf(ClassViolatingCodingRules.class);

@Test
public void core_should_only_be_accessed_by_satellites() {
theClass(VeryCentralCore.class)
.should().onlyBeAccessed().byClassesThat().implement(CoreSatellite.class)
.check(classes);
}

@Test
public void core_should_only_access_classes_in_core_itself() {
noClass(VeryCentralCore.class)
.should().accessClassesThat().resideOutsideOfPackages("..core..", "java..")
.check(classes);
}

@Test
public void the_only_class_with_high_security_is_central_core() {
classes()
.that().areAnnotatedWith(HighSecurity.class)
.should().be(VeryCentralCore.class)
.check(classes);
}

@Test
public void central_core_should_not_implement_some_business_interface() {
classes()
.that().implement(SomeOtherBusinessInterface.class)
.should().notBe(VeryCentralCore.class)
.check(classes);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.tngtech.archunit.integration;

import com.tngtech.archunit.example.EvilCoreAccessor;
import com.tngtech.archunit.example.SomeOtherBusinessInterface;
import com.tngtech.archunit.example.controller.WronglyAnnotated;
import com.tngtech.archunit.example.core.CoreSatellite;
import com.tngtech.archunit.example.core.HighSecurity;
import com.tngtech.archunit.example.core.VeryCentralCore;
import com.tngtech.archunit.example.web.AnnotatedController;
import com.tngtech.archunit.exampletest.SingleClassTest;
import com.tngtech.archunit.junit.ExpectedViolation;
import org.junit.Rule;

import static com.tngtech.archunit.example.core.VeryCentralCore.DO_CORE_STUFF_METHOD_NAME;
import static com.tngtech.archunit.junit.ExpectedAccess.callFrom;
import static com.tngtech.archunit.junit.ExpectedClass.javaClass;

public class SingleClassIntegrationTest extends SingleClassTest {
@Rule
public final ExpectedViolation expectedViolation = ExpectedViolation.none();

@Override
public void core_should_only_be_accessed_by_satellites() {
expectedViolation
.ofRule(String.format("the class %s should only be accessed by classes that implement %s",
VeryCentralCore.class.getName(), CoreSatellite.class.getName()))
.by(callFrom(EvilCoreAccessor.class, "iShouldNotAccessCore")
.toConstructor(VeryCentralCore.class)
.inLine(8))
.by(callFrom(EvilCoreAccessor.class, "iShouldNotAccessCore")
.toMethod(VeryCentralCore.class, DO_CORE_STUFF_METHOD_NAME)
.inLine(8));

super.core_should_only_be_accessed_by_satellites();
}

@Override
public void core_should_only_access_classes_in_core_itself() {
expectedViolation
.ofRule(String.format("no class %s should access classes that reside outside of packages ['..core..', 'java..']",
VeryCentralCore.class.getName()))
.by(callFrom(VeryCentralCore.class, "coreDoingIllegalStuff")
.toConstructor(AnnotatedController.class)
.inLine(15));

super.core_should_only_access_classes_in_core_itself();
}

@Override
public void the_only_class_with_high_security_is_central_core() {
expectedViolation
.ofRule(String.format("classes that are annotated with @%s should be %s",
HighSecurity.class.getSimpleName(), VeryCentralCore.class.getName()))
.by(javaClass(WronglyAnnotated.class).notBeing(VeryCentralCore.class));

super.the_only_class_with_high_security_is_central_core();
}

@Override
public void central_core_should_not_implement_some_business_interface() {
expectedViolation
.ofRule(String.format("classes that implement %s should not be %s",
SomeOtherBusinessInterface.class.getName(), VeryCentralCore.class.getName()))
.by(javaClass(VeryCentralCore.class).being(VeryCentralCore.class));

super.central_core_should_not_implement_some_business_interface();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.tngtech.archunit.junit;

public class ExpectedClass {
public static Creator javaClass(Class<?> clazz) {
return new Creator(clazz);
}

public static class Creator {
private final Class<?> clazz;

private Creator(Class<?> clazz) {
this.clazz = clazz;
}

public ExpectedMessage notBeing(Class<?> desiredClass) {
String expectedMessage = String.format(
"class %s is not %s in (%s.java:0)",
clazz.getName(), desiredClass.getName(), clazz.getSimpleName());
return new ExpectedMessage(expectedMessage);
}

public ExpectedMessage being(Class<?> desiredClass) {
String expectedMessage = String.format(
"class %s is %s in (%s.java:0)",
clazz.getName(), desiredClass.getName(), clazz.getSimpleName());
return new ExpectedMessage(expectedMessage);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ private Creator(Class<?> clazz) {

public ExpectedMessage notResidingIn(String packageIdentifier) {
String expectedMessage = String.format(
"Class %s doesn't reside in a package '%s' in (%s.java:0)",
"class %s doesn't reside in a package '%s' in (%s.java:0)",
clazz.getName(), packageIdentifier, clazz.getSimpleName());
return new ExpectedMessage(expectedMessage);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ private PackageAssertionCreator(Class<?> clazz) {
}

public MessageAssertionChain.Link notMatching(String packageIdentifier) {
return containsLine("Class %s doesn't reside in a package '%s' in (%s.java:0)",
return containsLine("class %s doesn't reside in a package '%s' in (%s.java:0)",
clazz.getName(), packageIdentifier, clazz.getSimpleName());
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@
import static com.tngtech.archunit.core.domain.properties.HasName.Predicates.nameMatching;
import static com.tngtech.archunit.core.domain.properties.HasOwner.Predicates.With.owner;
import static com.tngtech.archunit.core.domain.properties.HasParameterTypes.Predicates.parameterTypes;
import static com.tngtech.archunit.lang.conditions.ArchPredicates.be;
import static com.tngtech.archunit.lang.conditions.ArchPredicates.have;
import static java.util.Arrays.asList;

Expand Down Expand Up @@ -276,6 +275,37 @@ private static DescribedPredicate<? super JavaFieldAccess> ownerAndNameAre(Strin
.as(ownerName + "." + fieldName);
}

@PublicAPI(usage = ACCESS)
public static ArchCondition<JavaClass> be(final Class<?> clazz) {
return be(clazz.getName());
}

@PublicAPI(usage = ACCESS)
public static ArchCondition<JavaClass> notBe(final Class<?> clazz) {
return not(be(clazz));
}

@PublicAPI(usage = ACCESS)
public static ArchCondition<JavaClass> be(final String className) {
return new ArchCondition<JavaClass>("be " + className) {
@Override
public void check(JavaClass javaClass, ConditionEvents events) {
boolean itemEquivalentToClazz = javaClass.getName().equals(className);
String message = String.format("class %s %s %s in %s",
javaClass.getName(),
itemEquivalentToClazz ? "is" : "is not",
className,
formatLocation(javaClass, 0));
events.add(new SimpleConditionEvent(javaClass, itemEquivalentToClazz, message));
}
};
}

@PublicAPI(usage = ACCESS)
public static ArchCondition<JavaClass> notBe(final String className) {
return not(be(className));
}

@PublicAPI(usage = ACCESS)
public static ArchCondition<JavaClass> haveFullyQualifiedName(final String name) {
final DescribedPredicate<HasName> haveFullyQualifiedName = have(fullyQualifiedName(name));
Expand Down Expand Up @@ -442,7 +472,7 @@ private static ArchCondition<JavaClass> residesConditionForPredicate(final Descr
@Override
public void check(JavaClass item, ConditionEvents events) {
boolean satisfied = resideInAPackage.apply(item);
String message = String.format("Class %s %s %s in %s",
String message = String.format("class %s %s %s in %s",
item.getName(),
satisfied ? "does" : "doesn't",
resideInAPackage.getDescription(),
Expand Down Expand Up @@ -578,7 +608,7 @@ public static ArchCondition<JavaClass> notBeMetaAnnotatedWith(DescribedPredicate
}

private static ArchCondition<JavaClass> createAnnotatedCondition(final DescribedPredicate<CanBeAnnotated> annotatedWith) {
return new ArchCondition<JavaClass>(be(annotatedWith).getDescription()) {
return new ArchCondition<JavaClass>(ArchPredicates.be(annotatedWith).getDescription()) {
@Override
public void check(JavaClass item, ConditionEvents events) {
boolean satisfied = annotatedWith.apply(item);
Expand Down Expand Up @@ -720,7 +750,7 @@ public static ArchCondition<JavaClass> notBeInterfaces() {
}

private static ArchCondition<JavaClass> createAssignableCondition(final DescribedPredicate<JavaClass> assignable) {
return new ArchCondition<JavaClass>(be(assignable).getDescription()) {
return new ArchCondition<JavaClass>(ArchPredicates.be(assignable).getDescription()) {
@Override
public void check(JavaClass item, ConditionEvents events) {
boolean satisfied = assignable.apply(item);
Expand Down
Loading

0 comments on commit 84088e9

Please sign in to comment.