Skip to content

Commit

Permalink
TNG#257 also consider the component type for dependencies from access
Browse files Browse the repository at this point in the history
Signed-off-by: Johannes Wengert <johannes.wengert@googlemail.com>
  • Loading branch information
jjank committed Nov 23, 2020
1 parent 4590cd0 commit 2b51e4f
Show file tree
Hide file tree
Showing 7 changed files with 58 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ Method <com.tngtech.archunit.example.layers.service.ComplexServiceAnnotation.sim
Method <com.tngtech.archunit.example.layers.service.ServiceType.values()> calls method <[Lcom.tngtech.archunit.example.layers.service.ServiceType;.clone()> in (ServiceType.java:3)
Method <com.tngtech.archunit.example.layers.service.ServiceType.values()> has return type <[Lcom.tngtech.archunit.example.layers.service.ServiceType;> in (ServiceType.java:0)
Method <com.tngtech.archunit.example.layers.service.ServiceType.values()> depends on component type <com.tngtech.archunit.example.layers.service.ServiceType> in (ServiceType.java:0)
Method <com.tngtech.archunit.example.layers.service.ServiceType.values()> depends on component type <com.tngtech.archunit.example.layers.service.ServiceType> in (ServiceType.java:3)
Method <com.tngtech.archunit.example.layers.service.ServiceViolatingDaoRules.illegallyUseEntityManager()> calls method <com.tngtech.archunit.example.layers.service.ServiceViolatingDaoRules$MyEntityManager.persist(java.lang.Object)> in (ServiceViolatingDaoRules.java:27)
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import com.tngtech.archunit.base.ChainableFunction;
import com.tngtech.archunit.base.DescribedPredicate;
import com.tngtech.archunit.base.HasDescription;
import com.tngtech.archunit.base.Optional;
import com.tngtech.archunit.core.domain.properties.HasName;
import com.tngtech.archunit.core.domain.properties.HasSourceCodeLocation;

Expand Down Expand Up @@ -63,11 +62,20 @@ private Dependency(JavaClass originClass, JavaClass targetClass, int lineNumber,
this.sourceCodeLocation = SourceCodeLocation.of(originClass, lineNumber);
}

static Optional<Dependency> tryCreateFromAccess(JavaAccess<?> access) {
if (access.getOriginOwner().equals(access.getTargetOwner()) || access.getTargetOwner().isPrimitive()) {
return Optional.absent();
static Set<Dependency> tryCreateFromAccess(JavaAccess<?> access) {
JavaClass originOwner = access.getOriginOwner();
JavaClass targetOwner = access.getTargetOwner();
if (originOwner.equals(targetOwner) || targetOwner.isPrimitive()) {
return Collections.emptySet();
}
Set<Dependency> dependencies = new HashSet<>();
if (targetOwner.isArray()) {
String componentDescription = String.format("%s depends on component type %s in %s",
access.getOrigin().getDescription(), bracketFormat(targetOwner.getComponentType().getName()), access.getSourceCodeLocation());
dependencies.add(new Dependency(originOwner, targetOwner.getComponentType(), access.getLineNumber(), componentDescription));
}
return Optional.of(new Dependency(access.getOriginOwner(), access.getTargetOwner(), access.getLineNumber(), access.getDescription()));
dependencies.add(new Dependency(originOwner, targetOwner, access.getLineNumber(), access.getDescription()));
return dependencies;
}

static Dependency fromInheritance(JavaClass origin, JavaClass targetSuperType) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ Set<InstanceofCheck> getInstanceofChecksWithTypeOfClass() {
private Set<Dependency> dependenciesFromAccesses(Set<JavaAccess<?>> accesses) {
ImmutableSet.Builder<Dependency> result = ImmutableSet.builder();
for (JavaAccess<?> access : accesses) {
result.addAll(Dependency.tryCreateFromAccess(access).asSet());
result.addAll(Dependency.tryCreateFromAccess(access));
}
return result.build();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.tngtech.archunit.core.domain;

import java.io.File;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
Expand Down Expand Up @@ -57,12 +58,30 @@ public void Dependencies_from_field_with_component_type() {
public void Dependency_from_access() {
JavaMethodCall call = simulateCall().from(getClass(), "toString").to(Object.class, "toString");

Dependency dependency = Dependency.tryCreateFromAccess(call).get();
Dependency dependency = getOnlyElement(Dependency.tryCreateFromAccess(call));
assertThatType(dependency.getTargetClass()).as("target class").isEqualTo(call.getTargetOwner());
assertThat(dependency.getDescription())
.as("description").isEqualTo(call.getDescription());
}

@Test
public void Dependency_from_access_with_component_type() {
JavaMethod method = importClassWithContext(ClassWithArrayMember.class).getMethod("internal");
JavaMethodCall call = getOnlyElement(method.getMethodCallsFromSelf());

Set<Dependency> dependencies = Dependency.tryCreateFromAccess(call);

assertThatDependencies(dependencies)
.containOnly(
from(ClassWithArrayMember.class).to(File[].class)
.withDescriptionContaining("Method <%s> calls method <%s>", ".internal()", ".clone()")
.inLocation(DependencyTest.class, 355)

.from(ClassWithArrayMember.class).to(File.class)
.withDescriptionContaining("Method <%s> depends on component type <%s>", ".internal()", File.class.getName())
.inLocation(DependencyTest.class, 355));
}

@Test
public void Dependency_from_origin_and_target() {
JavaClass origin = importClassWithContext(getClass());
Expand Down Expand Up @@ -330,5 +349,10 @@ void annotatedMethod() {

private static class ClassWithArrayMember {
private String[] objects;

private void internal() {
File[] files = new File[0];
files.clone();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.tngtech.archunit.core.domain;

import java.io.File;
import java.io.Serializable;
import java.lang.annotation.Retention;
import java.util.AbstractList;
Expand Down Expand Up @@ -93,6 +94,15 @@ public void finds_array_component_types_as_dependencies_from_self() {

// Assert the presence of both the array type and the component type of the array for all dependencies
assertThat(javaClass.getDirectDependenciesFromSelf())
.areAtLeastOne(callDependency()
.from(ArrayComponentTypeDependencies.class)
.to(File[].class)
.inLineNumber(13))
.areAtLeastOne((componentTypeDependency()
.from(ArrayComponentTypeDependencies.class)
.to(File.class)
.inLineNumber(13)))

.areAtLeastOne(methodReturnTypeDependency()
.from(ArrayComponentTypeDependencies.class)
.to(Object[].class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ public static ConstructorCallTarget targetFrom(JavaConstructor constructor) {
}

public static Dependency dependencyFrom(JavaAccess<?> access) {
return Dependency.tryCreateFromAccess(access).get();
return getOnlyElement(Dependency.tryCreateFromAccess(access));
}

public static class AccessesSimulator {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
package com.tngtech.archunit.core.domain.testobjects;

import java.io.File;

public class ArrayComponentTypeDependencies {
private String[] strings;

public ArrayComponentTypeDependencies(Integer[] ints) {
}

private void internal() {
File[] files = new File[0];
files.clone();
}

public Object[] objects() {
return null;
}
Expand Down

0 comments on commit 2b51e4f

Please sign in to comment.