Skip to content

Commit

Permalink
add referenced class object dependencies from and to self
Browse files Browse the repository at this point in the history
Signed-off-by: Peter Gafert <[email protected]>
  • Loading branch information
codecholeric committed Jan 31, 2021
1 parent fe72d92 commit 1263ae5
Show file tree
Hide file tree
Showing 8 changed files with 152 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -972,6 +972,9 @@ Stream<DynamicTest> OnionArchitectureTest() {
.by(field(ShoppingService.class, "productRepository").ofType(ProductRepository.class))
.by(field(ShoppingService.class, "shoppingCartRepository").ofType(ShoppingCartRepository.class))

.by(method(AdministrationCLI.class, "handle")
.referencingClassObject(ProductRepository.class)
.inLine(16))
.by(callFromMethod(AdministrationCLI.class, "handle", String[].class, AdministrationPort.class)
.toMethod(ProductRepository.class, "getTotalCount")
.inLine(17).asDependency())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,11 @@ public ExpectedDependency withAnnotationType(Class<?> annotationType) {
}

public AddsLineNumber checkingInstanceOf(Class<?> target) {
return new AddsLineNumber(owner, getOriginName(), target);
return new AddsLineNumber(owner, getOriginName(), "checks instanceof", target);
}

public AddsLineNumber referencingClassObject(Class<?> target) {
return new AddsLineNumber(owner, getOriginName(), "references class object", target);
}

private String getOriginName() {
Expand All @@ -201,15 +205,17 @@ public static class AddsLineNumber {
private final Class<?> owner;
private final String origin;
private final Class<?> target;
private final String dependencyType;

private AddsLineNumber(Class<?> owner, String origin, Class<?> target) {
private AddsLineNumber(Class<?> owner, String origin, String dependencyType, Class<?> target) {
this.owner = owner;
this.origin = origin;
this.target = target;
this.dependencyType = dependencyType;
}

public ExpectedDependency inLine(int lineNumber) {
String dependencyPattern = getDependencyPattern(origin, "checks instanceof", target.getName(), lineNumber);
String dependencyPattern = getDependencyPattern(origin, dependencyType, target.getName(), lineNumber);
return new ExpectedDependency(owner, target, dependencyPattern);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,12 @@ static Set<Dependency> tryCreateFromInstanceofCheck(InstanceofCheck instanceofCh
instanceofCheck.getRawType(), instanceofCheck.getSourceCodeLocation());
}

static Set<Dependency> tryCreateFromReferencedClassObject(ReferencedClassObject referencedClassObject) {
return tryCreateDependency(
referencedClassObject.getOwner(), "references class object",
referencedClassObject.getRawType(), referencedClassObject.getSourceCodeLocation());
}

static Set<Dependency> tryCreateFromAnnotation(JavaAnnotation<?> target) {
Origin origin = findSuitableOrigin(target, target.getAnnotatedElement());
return tryCreateDependency(origin, "is annotated with", target.getRawType());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ public Set<Dependency> get() {
result.addAll(constructorParameterDependenciesFromSelf());
result.addAll(annotationDependenciesFromSelf());
result.addAll(instanceofCheckDependenciesFromSelf());
result.addAll(referencedClassObjectDependenciesFromSelf());
result.addAll(typeParameterDependenciesFromSelf());
return result.build();
}
Expand Down Expand Up @@ -158,6 +159,14 @@ private Set<Dependency> instanceofCheckDependenciesFromSelf() {
return result.build();
}

private Set<Dependency> referencedClassObjectDependenciesFromSelf() {
ImmutableSet.Builder<Dependency> result = ImmutableSet.builder();
for (ReferencedClassObject referencedClassObject : javaClass.getReferencedClassObjects()) {
result.addAll(Dependency.tryCreateFromReferencedClassObject(referencedClassObject));
}
return result.build();
}

private Set<Dependency> typeParameterDependenciesFromSelf() {
ImmutableSet.Builder<Dependency> result = ImmutableSet.builder();
for (JavaTypeVariable<?> typeVariable : javaClass.getTypeParameters()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.file.FileSystem;
import java.util.Set;

import com.google.common.base.MoreObjects;
import com.google.common.collect.FluentIterable;
import com.tngtech.archunit.base.DescribedPredicate;
import com.tngtech.archunit.core.domain.testobjects.ClassWithArrayDependencies;
import com.tngtech.archunit.core.domain.testobjects.ClassWithDependencyOnInstanceofCheck;
import com.tngtech.archunit.core.domain.testobjects.ClassWithDependencyOnInstanceofCheck.InstanceOfCheckTarget;
import com.tngtech.archunit.core.domain.testobjects.DependenciesOnClassObjects;
import com.tngtech.archunit.core.importer.ClassFileImporter;
import com.tngtech.archunit.testutil.Assertions;
import com.tngtech.archunit.testutil.assertion.DependenciesAssertion;
Expand All @@ -24,6 +27,7 @@
import org.junit.runner.RunWith;

import static com.google.common.collect.Iterables.getOnlyElement;
import static com.tngtech.archunit.base.Guava.toGuava;
import static com.tngtech.archunit.core.domain.Dependency.Functions.GET_ORIGIN_CLASS;
import static com.tngtech.archunit.core.domain.Dependency.Functions.GET_TARGET_CLASS;
import static com.tngtech.archunit.core.domain.Dependency.Predicates.dependency;
Expand All @@ -32,6 +36,7 @@
import static com.tngtech.archunit.core.domain.TestUtils.importClassWithContext;
import static com.tngtech.archunit.core.domain.TestUtils.importClassesWithContext;
import static com.tngtech.archunit.core.domain.TestUtils.simulateCall;
import static com.tngtech.archunit.core.domain.properties.HasType.Predicates.rawType;
import static com.tngtech.archunit.testutil.Assertions.assertThat;
import static com.tngtech.archunit.testutil.Assertions.assertThatDependencies;
import static com.tngtech.archunit.testutil.Assertions.assertThatType;
Expand Down Expand Up @@ -289,6 +294,24 @@ class ClassWithTypeParameters extends Base<String> {
ClassWithTypeParameters.class.getName(), Base.class.getName(), String.class.getName(), getClass().getSimpleName()));
}

@Test
public void Dependency_from_referenced_class_object() {
JavaMethod origin = new ClassFileImporter()
.importClass(DependenciesOnClassObjects.class)
.getMethod("referencedClassObjectsInMethod");
ReferencedClassObject referencedClassObject = getOnlyElement(FluentIterable.from(origin.getReferencedClassObjects())
.filter(toGuava(rawType(FileSystem.class)))
.toSet());

Dependency dependency = getOnlyElement(Dependency.tryCreateFromReferencedClassObject(referencedClassObject));

assertThatType(dependency.getOriginClass()).matches(DependenciesOnClassObjects.class);
assertThatType(dependency.getTargetClass()).matches(FileSystem.class);
assertThat(dependency.getDescription()).as("description")
.contains(String.format("Method <%s> references class object <%s> in (%s.java:%d)",
origin.getFullName(), FileSystem.class.getName(), DependenciesOnClassObjects.class.getSimpleName(), 22));
}

@Test
public void origin_predicates_match() {
assertThatDependency(Origin.class, Target.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FilterInputStream;
import java.io.Serializable;
import java.lang.annotation.Retention;
import java.nio.Buffer;
import java.nio.charset.Charset;
import java.nio.file.FileSystem;
import java.nio.file.Path;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collection;
Expand All @@ -25,6 +30,7 @@
import com.tngtech.archunit.core.domain.testobjects.ArrayComponentTypeDependencies;
import com.tngtech.archunit.core.domain.testobjects.B;
import com.tngtech.archunit.core.domain.testobjects.ComponentTypeDependency;
import com.tngtech.archunit.core.domain.testobjects.DependenciesOnClassObjects;
import com.tngtech.archunit.core.domain.testobjects.InterfaceForA;
import com.tngtech.archunit.core.domain.testobjects.SuperA;
import com.tngtech.archunit.core.importer.ClassFileImporter;
Expand Down Expand Up @@ -639,6 +645,31 @@ class ClassWithTypeParameters<
);
}

@Test
public void direct_dependencies_from_self_by_referenced_class_objects() {
JavaClass javaClass = new ClassFileImporter().importClass(DependenciesOnClassObjects.class);

assertThatDependencies(javaClass.getDirectDependenciesFromSelf())
.contain(from(DependenciesOnClassObjects.class).to(FilterInputStream.class).inLocation(DependenciesOnClassObjects.class, 16)
.withDescriptionContaining("references class object")

.from(DependenciesOnClassObjects.class).to(Buffer.class).inLocation(DependenciesOnClassObjects.class, 16)
.withDescriptionContaining("references class object")

.from(DependenciesOnClassObjects.class).to(File.class).inLocation(DependenciesOnClassObjects.class, 19)
.withDescriptionContaining("references class object")

.from(DependenciesOnClassObjects.class).to(Path.class).inLocation(DependenciesOnClassObjects.class, 19)
.withDescriptionContaining("references class object")

.from(DependenciesOnClassObjects.class).to(FileSystem.class).inLocation(DependenciesOnClassObjects.class, 22)
.withDescriptionContaining("references class object")

.from(DependenciesOnClassObjects.class).to(Charset.class).inLocation(DependenciesOnClassObjects.class, 22)
.withDescriptionContaining("references class object")
);
}

@Test
public void direct_dependencies_from_self_finds_correct_set_of_target_types() {
JavaClass javaClass = importPackagesOf(getClass()).get(ClassWithAnnotationDependencies.class);
Expand Down Expand Up @@ -872,24 +903,39 @@ class SecondDependingOnOtherThroughTypeParameter<
);
}

@Test
public void direct_dependencies_to_self_by_referenced_class_objects() {
JavaClass javaClass = new ClassFileImporter()
.importClasses(DependenciesOnClassObjects.class, DependenciesOnClassObjects.MoreDependencies.class, Charset.class)
.get(Charset.class);

assertThatDependencies(javaClass.getDirectDependenciesToSelf())
.contain(from(DependenciesOnClassObjects.class).to(Charset.class).inLocation(DependenciesOnClassObjects.class, 22)
.withDescriptionContaining("references class object")

.from(DependenciesOnClassObjects.MoreDependencies.class).to(Charset.class).inLocation(DependenciesOnClassObjects.class, 27)
.withDescriptionContaining("references class object")
);
}

@Test
public void direct_dependencies_to_self_finds_correct_set_of_origin_types() {
JavaClasses classes = importPackagesOf(getClass());

Set<JavaClass> origins = getOriginsOfDependenciesTo(classes.get(WithType.class));

assertThatTypes(origins).matchInAnyOrder(
ClassWithAnnotationDependencies.class, ClassWithSelfReferences.class, WithNestedAnnotations.class, OnMethodParam.class);
getClass(), ClassWithAnnotationDependencies.class, ClassWithSelfReferences.class, WithNestedAnnotations.class, OnMethodParam.class);

origins = getOriginsOfDependenciesTo(classes.get(B.class));

assertThatTypes(origins).matchInAnyOrder(
ClassWithAnnotationDependencies.class, OnMethodParam.class, AAccessingB.class, AhavingMembersOfTypeB.class);
getClass(), ClassWithAnnotationDependencies.class, OnMethodParam.class, AAccessingB.class, AhavingMembersOfTypeB.class);

origins = getOriginsOfDependenciesTo(classes.get(SomeEnumAsNestedAnnotationParameter.class));

assertThatTypes(origins).matchInAnyOrder(
ClassWithAnnotationDependencies.class, WithEnum.class);
getClass(), ClassWithAnnotationDependencies.class, WithEnum.class);
}

private Set<JavaClass> getOriginsOfDependenciesTo(JavaClass withType) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.tngtech.archunit.core.domain.testobjects;

import java.io.File;
import java.io.FilterInputStream;
import java.nio.Buffer;
import java.nio.charset.Charset;
import java.nio.file.FileSystem;
import java.nio.file.Path;
import java.util.List;

import com.google.common.collect.ImmutableList;

@SuppressWarnings({"FieldMayBeFinal", "unused"})
public class DependenciesOnClassObjects {
static {
List<Class<?>> referencedClassObjectsInStaticInitializer = ImmutableList.of(FilterInputStream.class, Buffer.class);
}

List<Class<?>> referencedClassObjectsInConstructor = ImmutableList.<Class<?>>of(File.class, Path.class);

List<Class<?>> referencedClassObjectsInMethod() {
return ImmutableList.of(FileSystem.class, Charset.class);
}

public static class MoreDependencies {
Class<?> moreReferencedClassObjectsInMethod() {
return Charset.class;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -934,7 +934,7 @@ public void areNotInnerClasses_predicate(ClassesThat<ClassesShouldConjunction> c
public void areAnonymousClasses_predicate(ClassesThat<ClassesShouldConjunction> classesShouldOnlyBeBy) {
Set<JavaClass> classes = filterClassesAppearingInFailureReport(
classesShouldOnlyBeBy.areAnonymousClasses())
.on(ClassAccessingAnonymousClass.class, anonymousClassBeingAccessed.getClass(),
.on(ClassAccessingAnonymousClass_Reference, anonymousClassBeingAccessed.getClass(),
ClassAccessingOtherClass.class, ClassBeingAccessedByOtherClass.class);

assertThatTypes(classes).matchInAnyOrder(ClassAccessingOtherClass.class, ClassBeingAccessedByOtherClass.class);
Expand All @@ -945,18 +945,18 @@ public void areAnonymousClasses_predicate(ClassesThat<ClassesShouldConjunction>
public void areNotAnonymousClasses_predicate(ClassesThat<ClassesShouldConjunction> classesShouldOnlyBeBy) {
Set<JavaClass> classes = filterClassesAppearingInFailureReport(
classesShouldOnlyBeBy.areNotAnonymousClasses())
.on(ClassAccessingAnonymousClass.class, anonymousClassBeingAccessed.getClass(),
.on(ClassAccessingAnonymousClass_Reference, anonymousClassBeingAccessed.getClass(),
ClassAccessingOtherClass.class, ClassBeingAccessedByOtherClass.class);

assertThatTypes(classes).matchInAnyOrder(ClassAccessingAnonymousClass.class, anonymousClassBeingAccessed.getClass());
assertThatTypes(classes).matchInAnyOrder(ClassAccessingAnonymousClass_Reference, anonymousClassBeingAccessed.getClass());
}

@Test
@UseDataProvider("should_only_be_by_rule_starts")
public void areLocalClasses_predicate(ClassesThat<ClassesShouldConjunction> classesShouldOnlyBeBy) {
Set<JavaClass> classes = filterClassesAppearingInFailureReport(
classesShouldOnlyBeBy.areLocalClasses())
.on(ClassBeingAccessedByLocalClass.class, ClassBeingAccessedByLocalClass.getLocalClass(),
.on(ClassAccessingLocalClass_Reference, ClassBeingAccessedByLocalClass.getLocalClass(),
ClassAccessingOtherClass.class, ClassBeingAccessedByOtherClass.class);

assertThatTypes(classes).matchInAnyOrder(ClassAccessingOtherClass.class, ClassBeingAccessedByOtherClass.class);
Expand All @@ -967,10 +967,10 @@ public void areLocalClasses_predicate(ClassesThat<ClassesShouldConjunction> clas
public void areNotLocalClasses_predicate(ClassesThat<ClassesShouldConjunction> classesShouldOnlyBeBy) {
Set<JavaClass> classes = filterClassesAppearingInFailureReport(
classesShouldOnlyBeBy.areNotLocalClasses())
.on(ClassBeingAccessedByLocalClass.class, ClassBeingAccessedByLocalClass.getLocalClass(),
.on(ClassAccessingLocalClass_Reference, ClassBeingAccessedByLocalClass.getLocalClass(),
ClassAccessingOtherClass.class, ClassBeingAccessedByOtherClass.class);

assertThatTypes(classes).matchInAnyOrder(ClassBeingAccessedByLocalClass.class, ClassBeingAccessedByLocalClass.getLocalClass());
assertThatTypes(classes).matchInAnyOrder(ClassAccessingLocalClass_Reference, ClassBeingAccessedByLocalClass.getLocalClass());
}

@Test
Expand Down Expand Up @@ -1050,6 +1050,14 @@ private static DescribedPredicate<HasName> classWithNameOf(Class<?> type) {
return GET_NAME.is(equalTo(type.getName()));
}

private static Class<?> classForName(String className) {
try {
return Class.forName(className);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}

private static class ClassAccessedByFoo {
void method() {
}
Expand Down Expand Up @@ -1306,6 +1314,8 @@ void access() {
private class InnerMemberClassBeingAccessed {
}

// This must be loaded via Reflection, otherwise the test will be tainted by the dependency on the class object
private static final Class<?> ClassAccessingAnonymousClass_Reference = classForName("com.tngtech.archunit.lang.syntax.elements.ShouldOnlyByClassesThatTest$ClassAccessingAnonymousClass");
private static class ClassAccessingAnonymousClass {
@SuppressWarnings("unused")
void access() {
Expand All @@ -1320,15 +1330,20 @@ public void run() {
}
};

// This must be loaded via Reflection, otherwise the test will be tainted by the dependency on the class object
private static final Class<?> ClassAccessingLocalClass_Reference = classForName("com.tngtech.archunit.lang.syntax.elements.ShouldOnlyByClassesThatTest$ClassBeingAccessedByLocalClass");

private static class ClassBeingAccessedByLocalClass {
static Class<?> getLocalClass() {
@SuppressWarnings({"unused", "InstantiationOfUtilityClass"})
class LocalClass {
@SuppressWarnings("unused")
void access() {
new ClassBeingAccessedByLocalClass();
}
}
return LocalClass.class;

// This must be loaded via Reflection, otherwise the test will be tainted by the dependency on the class object
return classForName("com.tngtech.archunit.lang.syntax.elements.ShouldOnlyByClassesThatTest$ClassBeingAccessedByLocalClass$1LocalClass");
}
}
}

0 comments on commit 1263ae5

Please sign in to comment.