Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds theClass(..), noClass(..), should().be(..) and should().notBe(..) #80

Merged
merged 4 commits into from
Jun 19, 2018
Merged
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
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