package com.tngtech.archunit.core.importer;
import java.io.IOException;
import java.io.PrintStream;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarFile;
import com.google.common.base.Predicate;
import com.google.common.base.Suppliers;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.tngtech.archunit.ArchConfiguration;
import com.tngtech.archunit.base.DescribedPredicate;
import com.tngtech.archunit.base.Optional;
import com.tngtech.archunit.core.domain.AccessTarget;
import com.tngtech.archunit.core.domain.AccessTarget.ConstructorCallTarget;
import com.tngtech.archunit.core.domain.AccessTarget.FieldAccessTarget;
import com.tngtech.archunit.core.domain.AccessTarget.MethodCallTarget;
import com.tngtech.archunit.core.domain.Dependency;
import com.tngtech.archunit.core.domain.JavaAccess;
import com.tngtech.archunit.core.domain.JavaAnnotation;
import com.tngtech.archunit.core.domain.JavaClass;
import com.tngtech.archunit.core.domain.JavaClassList;
import com.tngtech.archunit.core.domain.JavaClasses;
import com.tngtech.archunit.core.domain.JavaCodeUnit;
import com.tngtech.archunit.core.domain.JavaConstructor;
import com.tngtech.archunit.core.domain.JavaConstructorCall;
import com.tngtech.archunit.core.domain.JavaEnumConstant;
import com.tngtech.archunit.core.domain.JavaField;
import com.tngtech.archunit.core.domain.JavaFieldAccess;
import com.tngtech.archunit.core.domain.JavaFieldAccess.AccessType;
import com.tngtech.archunit.core.domain.JavaMethod;
import com.tngtech.archunit.core.domain.JavaMethodCall;
import com.tngtech.archunit.core.domain.JavaModifier;
import com.tngtech.archunit.core.domain.Source;
import com.tngtech.archunit.core.domain.properties.HasName;
import com.tngtech.archunit.core.domain.properties.HasOwner;
import com.tngtech.archunit.core.importer.DomainBuilders.FieldAccessTargetBuilder;
import com.tngtech.archunit.core.importer.DomainBuilders.MethodCallTargetBuilder;
import com.tngtech.archunit.core.importer.testexamples.SomeAnnotation;
import com.tngtech.archunit.core.importer.testexamples.annotatedclassimport.ClassAnnotationWithArrays;
import com.tngtech.archunit.core.importer.testexamples.annotatedclassimport.ClassWithAnnotationWithEmptyArrays;
import com.tngtech.archunit.core.importer.testexamples.annotatedclassimport.ClassWithComplexAnnotations;
import com.tngtech.archunit.core.importer.testexamples.annotatedclassimport.ClassWithOneAnnotation;
import com.tngtech.archunit.core.importer.testexamples.annotatedclassimport.ClassWithUnimportedAnnotation;
import com.tngtech.archunit.core.importer.testexamples.annotatedclassimport.SimpleAnnotation;
import com.tngtech.archunit.core.importer.testexamples.annotatedclassimport.TypeAnnotationWithEnumAndArrayValue;
import com.tngtech.archunit.core.importer.testexamples.annotationfieldimport.ClassWithAnnotatedFields;
import com.tngtech.archunit.core.importer.testexamples.annotationfieldimport.ClassWithAnnotatedFields.FieldAnnotationWithEnumClassAndArrayValue;
import com.tngtech.archunit.core.importer.testexamples.annotationfieldimport.ClassWithAnnotatedFields.FieldAnnotationWithIntValue;
import com.tngtech.archunit.core.importer.testexamples.annotationfieldimport.ClassWithAnnotatedFields.FieldAnnotationWithStringValue;
import com.tngtech.archunit.core.importer.testexamples.annotationfieldimport.FieldAnnotationWithArrays;
import com.tngtech.archunit.core.importer.testexamples.annotationmethodimport.ClassWithAnnotatedMethods;
import com.tngtech.archunit.core.importer.testexamples.annotationmethodimport.ClassWithAnnotatedMethods.MethodAnnotationWithEnumAndArrayValue;
import com.tngtech.archunit.core.importer.testexamples.annotationmethodimport.ClassWithAnnotatedMethods.MethodAnnotationWithIntValue;
import com.tngtech.archunit.core.importer.testexamples.annotationmethodimport.ClassWithAnnotatedMethods.MethodAnnotationWithStringValue;
import com.tngtech.archunit.core.importer.testexamples.annotationmethodimport.MethodAnnotationWithArrays;
import com.tngtech.archunit.core.importer.testexamples.callimport.CallsExternalMethod;
import com.tngtech.archunit.core.importer.testexamples.callimport.CallsMethodReturningArray;
import com.tngtech.archunit.core.importer.testexamples.callimport.CallsMethodReturningArray.SomeEnum;
import com.tngtech.archunit.core.importer.testexamples.callimport.CallsOtherConstructor;
import com.tngtech.archunit.core.importer.testexamples.callimport.CallsOtherMethod;
import com.tngtech.archunit.core.importer.testexamples.callimport.CallsOwnConstructor;
import com.tngtech.archunit.core.importer.testexamples.callimport.CallsOwnMethod;
import com.tngtech.archunit.core.importer.testexamples.callimport.ExternalInterfaceMethodCall;
import com.tngtech.archunit.core.importer.testexamples.callimport.ExternalOverriddenMethodCall;
import com.tngtech.archunit.core.importer.testexamples.callimport.ExternalSubTypeConstructorCall;
import com.tngtech.archunit.core.importer.testexamples.classhierarchyimport.BaseClass;
import com.tngtech.archunit.core.importer.testexamples.classhierarchyimport.CollectionInterface;
import com.tngtech.archunit.core.importer.testexamples.classhierarchyimport.GrandParentInterface;
import com.tngtech.archunit.core.importer.testexamples.classhierarchyimport.OtherInterface;
import com.tngtech.archunit.core.importer.testexamples.classhierarchyimport.OtherSubClass;
import com.tngtech.archunit.core.importer.testexamples.classhierarchyimport.ParentInterface;
import com.tngtech.archunit.core.importer.testexamples.classhierarchyimport.SomeCollection;
import com.tngtech.archunit.core.importer.testexamples.classhierarchyimport.SubClass;
import com.tngtech.archunit.core.importer.testexamples.classhierarchyimport.SubInterface;
import com.tngtech.archunit.core.importer.testexamples.classhierarchyimport.SubSubClass;
import com.tngtech.archunit.core.importer.testexamples.complexexternal.ChildClass;
import com.tngtech.archunit.core.importer.testexamples.complexexternal.ParentClass;
import com.tngtech.archunit.core.importer.testexamples.complexmethodimport.ClassWithComplexMethod;
import com.tngtech.archunit.core.importer.testexamples.constructorimport.ClassWithComplexConstructor;
import com.tngtech.archunit.core.importer.testexamples.constructorimport.ClassWithSimpleConstructors;
import com.tngtech.archunit.core.importer.testexamples.dependents.ClassDependingOnParentThroughChild;
import com.tngtech.archunit.core.importer.testexamples.dependents.ClassHoldingDependencies;
import com.tngtech.archunit.core.importer.testexamples.dependents.FirstClassWithDependency;
import com.tngtech.archunit.core.importer.testexamples.dependents.ParentClassHoldingDependencies;
import com.tngtech.archunit.core.importer.testexamples.dependents.SecondClassWithDependency;
import com.tngtech.archunit.core.importer.testexamples.dependents.SubClassHoldingDependencies;
import com.tngtech.archunit.core.importer.testexamples.diamond.ClassCallingDiamond;
import com.tngtech.archunit.core.importer.testexamples.diamond.ClassImplementingD;
import com.tngtech.archunit.core.importer.testexamples.diamond.InterfaceB;
import com.tngtech.archunit.core.importer.testexamples.diamond.InterfaceC;
import com.tngtech.archunit.core.importer.testexamples.diamond.InterfaceD;
import com.tngtech.archunit.core.importer.testexamples.fieldaccessimport.ExternalFieldAccess;
import com.tngtech.archunit.core.importer.testexamples.fieldaccessimport.ExternalShadowedFieldAccess;
import com.tngtech.archunit.core.importer.testexamples.fieldaccessimport.ForeignFieldAccess;
import com.tngtech.archunit.core.importer.testexamples.fieldaccessimport.ForeignFieldAccessFromConstructor;
import com.tngtech.archunit.core.importer.testexamples.fieldaccessimport.ForeignFieldAccessFromStaticInitializer;
import com.tngtech.archunit.core.importer.testexamples.fieldaccessimport.ForeignStaticFieldAccess;
import com.tngtech.archunit.core.importer.testexamples.fieldaccessimport.MultipleFieldAccessInSameMethod;
import com.tngtech.archunit.core.importer.testexamples.fieldaccessimport.OwnFieldAccess;
import com.tngtech.archunit.core.importer.testexamples.fieldaccessimport.OwnStaticFieldAccess;
import com.tngtech.archunit.core.importer.testexamples.fieldaccesstointerfaces.ClassAccessingInterfaceFields;
import com.tngtech.archunit.core.importer.testexamples.fieldaccesstointerfaces.InterfaceWithFields;
import com.tngtech.archunit.core.importer.testexamples.fieldaccesstointerfaces.OtherInterfaceWithFields;
import com.tngtech.archunit.core.importer.testexamples.fieldaccesstointerfaces.ParentInterfaceWithFields;
import com.tngtech.archunit.core.importer.testexamples.fieldimport.ClassWithIntAndObjectFields;
import com.tngtech.archunit.core.importer.testexamples.fieldimport.ClassWithStringField;
import com.tngtech.archunit.core.importer.testexamples.hierarchicalfieldaccess.AccessToSuperAndSubClassField;
import com.tngtech.archunit.core.importer.testexamples.hierarchicalfieldaccess.SubClassWithAccessedField;
import com.tngtech.archunit.core.importer.testexamples.hierarchicalfieldaccess.SuperClassWithAccessedField;
import com.tngtech.archunit.core.importer.testexamples.hierarchicalmethodcall.CallOfSuperAndSubClassMethod;
import com.tngtech.archunit.core.importer.testexamples.hierarchicalmethodcall.SubClassWithCalledMethod;
import com.tngtech.archunit.core.importer.testexamples.hierarchicalmethodcall.SuperClassWithCalledMethod;
import com.tngtech.archunit.core.importer.testexamples.innerclassimport.CalledClass;
import com.tngtech.archunit.core.importer.testexamples.innerclassimport.ClassWithInnerClass;
import com.tngtech.archunit.core.importer.testexamples.integration.ClassA;
import com.tngtech.archunit.core.importer.testexamples.integration.ClassBDependingOnClassA;
import com.tngtech.archunit.core.importer.testexamples.integration.ClassCDependingOnClassB_SuperClassOfX;
import com.tngtech.archunit.core.importer.testexamples.integration.ClassD;
import com.tngtech.archunit.core.importer.testexamples.integration.ClassXDependingOnClassesABCD;
import com.tngtech.archunit.core.importer.testexamples.integration.InterfaceOfClassX;
import com.tngtech.archunit.core.importer.testexamples.methodimport.ClassWithObjectVoidAndIntIntSerializableMethod;
import com.tngtech.archunit.core.importer.testexamples.methodimport.ClassWithStringStringMethod;
import com.tngtech.archunit.core.importer.testexamples.nestedimport.ClassWithNestedClass;
import com.tngtech.archunit.core.importer.testexamples.simpleimport.ClassToImportOne;
import com.tngtech.archunit.core.importer.testexamples.simpleimport.ClassToImportTwo;
import com.tngtech.archunit.core.importer.testexamples.simpleimport.EnumToImport;
import com.tngtech.archunit.core.importer.testexamples.simpleimport.InterfaceToImport;
import com.tngtech.archunit.core.importer.testexamples.specialtargets.ClassCallingSpecialTarget;
import com.tngtech.archunit.testutil.OutsideOfClassPathRule;
import org.assertj.core.api.Condition;
import org.junit.After;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Predicates.containsPattern;
import static com.google.common.base.Predicates.not;
import static com.google.common.collect.Iterables.getFirst;
import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.common.collect.Sets.newHashSet;
import static com.tngtech.archunit.core.domain.JavaClass.Predicates.type;
import static com.tngtech.archunit.core.domain.JavaConstructor.CONSTRUCTOR_NAME;
import static com.tngtech.archunit.core.domain.JavaFieldAccess.AccessType.GET;
import static com.tngtech.archunit.core.domain.JavaFieldAccess.AccessType.SET;
import static com.tngtech.archunit.core.domain.JavaModifier.FINAL;
import static com.tngtech.archunit.core.domain.JavaModifier.PRIVATE;
import static com.tngtech.archunit.core.domain.JavaModifier.PROTECTED;
import static com.tngtech.archunit.core.domain.JavaModifier.PUBLIC;
import static com.tngtech.archunit.core.domain.JavaModifier.STATIC;
import static com.tngtech.archunit.core.domain.JavaModifier.TRANSIENT;
import static com.tngtech.archunit.core.domain.JavaModifier.VOLATILE;
import static com.tngtech.archunit.core.domain.JavaStaticInitializer.STATIC_INITIALIZER_NAME;
import static com.tngtech.archunit.core.domain.SourceTest.bytesAt;
import static com.tngtech.archunit.core.domain.SourceTest.urlOf;
import static com.tngtech.archunit.core.domain.TestUtils.MD5_SUM_DISABLED;
import static com.tngtech.archunit.core.domain.TestUtils.asClasses;
import static com.tngtech.archunit.core.domain.TestUtils.md5sumOf;
import static com.tngtech.archunit.core.domain.TestUtils.resolvedTargetFrom;
import static com.tngtech.archunit.core.domain.TestUtils.targetFrom;
import static com.tngtech.archunit.core.importer.testexamples.SomeEnum.OTHER_VALUE;
import static com.tngtech.archunit.core.importer.testexamples.SomeEnum.SOME_VALUE;
import static com.tngtech.archunit.core.importer.testexamples.annotationmethodimport.ClassWithAnnotatedMethods.enumAndArrayAnnotatedMethod;
import static com.tngtech.archunit.core.importer.testexamples.annotationmethodimport.ClassWithAnnotatedMethods.methodAnnotatedWithAnnotationFromParentPackage;
import static com.tngtech.archunit.core.importer.testexamples.annotationmethodimport.ClassWithAnnotatedMethods.methodAnnotatedWithEmptyArrays;
import static com.tngtech.archunit.core.importer.testexamples.annotationmethodimport.ClassWithAnnotatedMethods.stringAndIntAnnotatedMethod;
import static com.tngtech.archunit.core.importer.testexamples.annotationmethodimport.ClassWithAnnotatedMethods.stringAnnotatedMethod;
import static com.tngtech.archunit.testutil.Assertions.assertThat;
import static com.tngtech.archunit.testutil.Assertions.assertThatClasses;
import static com.tngtech.archunit.testutil.ReflectionTestUtils.constructor;
import static com.tngtech.archunit.testutil.ReflectionTestUtils.field;
import static com.tngtech.archunit.testutil.ReflectionTestUtils.method;
import static org.junit.Assume.assumeTrue;
public class ClassFileImporterTest {
@Rule
public final OutsideOfClassPathRule outsideOfClassPath = new OutsideOfClassPathRule();
@After
public void tearDown() {
ArchConfiguration.get().reset();
}
@Test
public void imports_simple_package() throws Exception {
Set<String> expectedClassNames = Sets.newHashSet(
ClassToImportOne.class.getName(), ClassToImportTwo.class.getName(), InterfaceToImport.class.getName(), EnumToImport.class.getName());
Iterable<JavaClass> classes = classesIn("testexamples/simpleimport");
assertThat(namesOf(classes)).isEqualTo(expectedClassNames);
}
@Test
public void imports_simple_class_details() throws Exception {
ImportedClasses classes = classesIn("testexamples/simpleimport");
JavaClass javaClass = classes.get(ClassToImportOne.class);
assertThat(javaClass.getName()).as("full name").isEqualTo(ClassToImportOne.class.getName());
assertThat(javaClass.getSimpleName()).as("simple name").isEqualTo(ClassToImportOne.class.getSimpleName());
assertThat(javaClass.getPackage()).as("package").isEqualTo(ClassToImportOne.class.getPackage().getName());
assertThat(javaClass.getModifiers()).as("modifiers").containsOnly(JavaModifier.PUBLIC);
assertThat(javaClass.getSuperClass().get()).as("super class").matches(Object.class);
assertThat(javaClass.getInterfaces()).as("interfaces").isEmpty();
assertThat(javaClass.isInterface()).as("is interface").isFalse();
assertThat(javaClass.isEnum()).as("is enum").isFalse();
assertThat(classes.get(ClassToImportTwo.class).getModifiers()).containsOnly(JavaModifier.PUBLIC, JavaModifier.FINAL);
}
@Test
public void imports_simple_enum() throws Exception {
JavaClass javaClass = classesIn("testexamples/simpleimport").get(EnumToImport.class);
assertThat(javaClass.getName()).as("full name").isEqualTo(EnumToImport.class.getName());
assertThat(javaClass.getSimpleName()).as("simple name").isEqualTo(EnumToImport.class.getSimpleName());
assertThat(javaClass.getPackage()).as("package").isEqualTo(EnumToImport.class.getPackage().getName());
assertThat(javaClass.getModifiers()).as("modifiers").containsOnly(JavaModifier.PUBLIC, JavaModifier.FINAL);
assertThat(javaClass.getSuperClass().get()).as("super class").matches(Enum.class);
assertThat(javaClass.getInterfaces()).as("interfaces").isEmpty();
assertThatClasses(javaClass.getAllInterfaces()).matchInAnyOrder(Serializable.class, Comparable.class);
assertThat(javaClass.isInterface()).as("is interface").isFalse();
assertThat(javaClass.isEnum()).as("is enum").isTrue();
}
@Test
public void imports_interfaces() throws Exception {
JavaClass simpleInterface = classesIn("testexamples/simpleimport").get(InterfaceToImport.class);
assertThat(simpleInterface.getName()).as("full name").isEqualTo(InterfaceToImport.class.getName());
assertThat(simpleInterface.getSimpleName()).as("simple name").isEqualTo(InterfaceToImport.class.getSimpleName());
assertThat(simpleInterface.getPackage()).as("package").isEqualTo(InterfaceToImport.class.getPackage().getName());
assertThat(simpleInterface.getSuperClass()).as("super class").isAbsent();
assertThat(simpleInterface.getInterfaces()).as("interfaces").isEmpty();
assertThat(simpleInterface.isInterface()).as("is interface").isTrue();
assertThat(simpleInterface.isEnum()).as("is enum").isFalse();
}
@Test
public void imports_nested_classes() throws Exception {
JavaClasses classes = classesIn("testexamples/nestedimport").classes;
assertThatClasses(classes).matchInAnyOrder(
ClassWithNestedClass.class,
ClassWithNestedClass.NestedClass.class,
ClassWithNestedClass.StaticNestedClass.class,
Class.forName(ClassWithNestedClass.class.getName() + "$PrivateNestedClass"));
}
@Test
public void imports_fields() throws Exception {
Set<JavaField> fields = classesIn("testexamples/fieldimport").getFields();
assertThat(namesOf(fields)).containsOnly("stringField", "serializableField", "objectField");
assertThat(findAnyByName(fields, "stringField"))
.isEquivalentTo(field(ClassWithStringField.class, "stringField"));
assertThat(findAnyByName(fields, "serializableField"))
.isEquivalentTo(field(ClassWithIntAndObjectFields.class, "serializableField"));
assertThat(findAnyByName(fields, "objectField"))
.isEquivalentTo(field(ClassWithIntAndObjectFields.class, "objectField"));
}
@Test
public void imports_primitive_fields() throws Exception {
Set<JavaField> fields = classesIn("testexamples/primitivefieldimport").getFields();
assertThat(findAnyByName(fields, "aBoolean").getType()).matches(boolean.class);
assertThat(findAnyByName(fields, "anInt").getType()).matches(int.class);
assertThat(findAnyByName(fields, "aByte").getType()).matches(byte.class);
assertThat(findAnyByName(fields, "aChar").getType()).matches(char.class);
assertThat(findAnyByName(fields, "aShort").getType()).matches(short.class);
assertThat(findAnyByName(fields, "aLong").getType()).matches(long.class);
assertThat(findAnyByName(fields, "aFloat").getType()).matches(float.class);
assertThat(findAnyByName(fields, "aDouble").getType()).matches(double.class);
}
// NOTE: This provokes the scenario where the target type can't be determined uniquely due to a diamond
// scenario and thus a fallback to (primitive and array) type names by ASM descriptors occurs.
// Unfortunately those ASM type names for example are the canonical name instead of the class name.
@Test
public void imports_special_target_parameters() throws Exception {
ImportedClasses classes = classesIn("testexamples/specialtargets");
Set<JavaMethodCall> calls = classes.get(ClassCallingSpecialTarget.class).getMethodCallsFromSelf();
assertThat(targetParametersOf(calls, "primitiveArgs")).matches(byte.class, long.class);
assertThat(returnTypeOf(calls, "primitiveReturnType")).matches(byte.class);
assertThat(targetParametersOf(calls, "arrayArgs")).matches(byte[].class, Object[].class);
assertThat(returnTypeOf(calls, "primitiveArrayReturnType")).matches(short[].class);
assertThat(returnTypeOf(calls, "objectArrayReturnType")).matches(String[].class);
assertThat(targetParametersOf(calls, "twoDimArrayArgs")).matches(float[][].class, Object[][].class);
assertThat(returnTypeOf(calls, "primitiveTwoDimArrayReturnType")).matches(double[][].class);
assertThat(returnTypeOf(calls, "objectTwoDimArrayReturnType")).matches(String[][].class);
}
@Test
public void attaches_correct_owner_to_fields() throws Exception {
Iterable<JavaClass> classes = classesIn("testexamples/fieldimport");
for (JavaClass clazz : classes) {
for (JavaField field : clazz.getFields()) {
assertThat(field.getOwner()).isSameAs(clazz);
}
}
}
@Test
public void imports_fields_with_correct_modifiers() throws Exception {
Set<JavaField> fields = classesIn("testexamples/modifierfieldimport").getFields();
assertThat(findAnyByName(fields, "privateField").getModifiers()).containsOnly(PRIVATE);
assertThat(findAnyByName(fields, "defaultField").getModifiers()).isEmpty();
assertThat(findAnyByName(fields, "privateFinalField").getModifiers()).containsOnly(PRIVATE, FINAL);
assertThat(findAnyByName(fields, "privateStaticField").getModifiers()).containsOnly(PRIVATE, STATIC);
assertThat(findAnyByName(fields, "privateStaticFinalField").getModifiers()).containsOnly(PRIVATE, STATIC, FINAL);
assertThat(findAnyByName(fields, "staticDefaultField").getModifiers()).containsOnly(STATIC);
assertThat(findAnyByName(fields, "protectedField").getModifiers()).containsOnly(PROTECTED);
assertThat(findAnyByName(fields, "protectedFinalField").getModifiers()).containsOnly(PROTECTED, FINAL);
assertThat(findAnyByName(fields, "publicField").getModifiers()).containsOnly(PUBLIC);
assertThat(findAnyByName(fields, "publicStaticFinalField").getModifiers()).containsOnly(PUBLIC, STATIC, FINAL);
assertThat(findAnyByName(fields, "volatileField").getModifiers()).containsOnly(VOLATILE);
assertThat(findAnyByName(fields, "synchronizedField").getModifiers()).containsOnly(TRANSIENT);
}
@Test
public void imports_annotation_defaults() throws Exception {
ImportedClasses classes = classesIn("testexamples/annotatedclassimport");
JavaClass annotationType = classes.get(TypeAnnotationWithEnumAndArrayValue.class);
assertThat((JavaEnumConstant) annotationType.getMethod("valueWithDefault")
.getDefaultValue().get())
.as("default of valueWithDefault()").isEquivalentTo(SOME_VALUE);
assertThat(((JavaEnumConstant[]) annotationType.getMethod("enumArrayWithDefault")
.getDefaultValue().get()))
.as("default of enumArrayWithDefault()").matches(OTHER_VALUE);
assertThat(((JavaAnnotation) annotationType.getMethod("subAnnotationWithDefault")
.getDefaultValue().get()).get("value").get())
.as("default of subAnnotationWithDefault()").isEqualTo("default");
assertThat(((JavaAnnotation[]) annotationType.getMethod("subAnnotationArrayWithDefault")
.getDefaultValue().get())[0].get("value").get())
.as("default of subAnnotationArrayWithDefault()").isEqualTo("first");
assertThat((JavaClass) annotationType.getMethod("clazzWithDefault")
.getDefaultValue().get())
.as("default of clazzWithDefault()").matches(String.class);
assertThat((JavaClass[]) annotationType.getMethod("classesWithDefault")
.getDefaultValue().get())
.as("default of clazzWithDefault()").matchExactly(Serializable.class, List.class);
}
@Test
public void imports_fields_with_one_annotation_correctly() throws Exception {
ImportedClasses classes = classesIn("testexamples/annotationfieldimport");
JavaField field = findAnyByName(classes.getFields(), "stringAnnotatedField");
JavaAnnotation annotation = field.getAnnotationOfType(FieldAnnotationWithStringValue.class.getName());
assertThat(annotation.getType()).isEqualTo(classes.get(FieldAnnotationWithStringValue.class));
assertThat(annotation.get("value").get()).isEqualTo("something");
assertThat(field).isEquivalentTo(field.getOwner().reflect().getDeclaredField("stringAnnotatedField"));
}
@Test
public void fields_handle_optional_annotation_correctly() throws Exception {
Set<JavaField> fields = classesIn("testexamples/annotationfieldimport").getFields();
JavaField field = findAnyByName(fields, "stringAnnotatedField");
assertThat(field.tryGetAnnotationOfType(FieldAnnotationWithStringValue.class)).isPresent();
assertThat(field.tryGetAnnotationOfType(FieldAnnotationWithEnumClassAndArrayValue.class)).isAbsent();
}
@Test
public void imports_fields_with_two_annotations_correctly() throws Exception {
Set<JavaField> fields = classesIn("testexamples/annotationfieldimport").getFields();
JavaField field = findAnyByName(fields, "stringAndIntAnnotatedField");
assertThat(field.getAnnotations()).hasSize(2);
JavaAnnotation annotationWithString = field.getAnnotationOfType(FieldAnnotationWithStringValue.class.getName());
assertThat(annotationWithString.get("value").get()).isEqualTo("otherThing");
JavaAnnotation annotationWithInt = field.getAnnotationOfType(FieldAnnotationWithIntValue.class.getName());
assertThat(annotationWithInt.get("intValue").get()).as("Annotation value with default").isEqualTo(0);
assertThat(annotationWithInt.get("otherValue").get()).isEqualTo("overridden");
assertThat(field).isEquivalentTo(field.getOwner().reflect().getDeclaredField("stringAndIntAnnotatedField"));
}
@Test
public void imports_fields_with_complex_annotations_correctly() throws Exception {
ImportedClasses classes = classesIn("testexamples/annotationfieldimport");
JavaField field = findAnyByName(classes.getFields(), "enumAndArrayAnnotatedField");
JavaAnnotation annotation = field.getAnnotationOfType(FieldAnnotationWithEnumClassAndArrayValue.class.getName());
assertThat((JavaEnumConstant) annotation.get("value").get()).isEquivalentTo(OTHER_VALUE);
assertThat((JavaEnumConstant) annotation.get("valueWithDefault").get()).isEquivalentTo(SOME_VALUE);
assertThat(((JavaEnumConstant[]) annotation.get("enumArray").get())).matches(SOME_VALUE, OTHER_VALUE);
assertThat(((JavaEnumConstant[]) annotation.get("enumArrayWithDefault").get())).matches(OTHER_VALUE);
assertThat(((JavaAnnotation) annotation.get("subAnnotation").get()).get("value").get()).isEqualTo("changed");
assertThat(((JavaAnnotation) annotation.get("subAnnotationWithDefault").get()).get("value").get())
.isEqualTo("default");
assertThat(((JavaAnnotation[]) annotation.get("subAnnotationArray").get())[0].get("value").get())
.isEqualTo("another");
assertThat(((JavaAnnotation[]) annotation.get("subAnnotationArrayWithDefault").get())[0].get("value").get())
.isEqualTo("first");
assertThat((JavaClass) annotation.get("clazz").get()).matches(Map.class);
assertThat((JavaClass) annotation.get("clazzWithDefault").get()).matches(String.class);
assertThat((JavaClass[]) annotation.get("classes").get()).matchExactly(Object.class, Serializable.class);
assertThat((JavaClass[]) annotation.get("classesWithDefault").get()).matchExactly(Serializable.class, List.class);
assertThat(field).isEquivalentTo(field.getOwner().reflect().getDeclaredField("enumAndArrayAnnotatedField"));
}
@Test
public void imports_fields_with_annotation_with_empty_array() throws Exception {
JavaClass clazz = classesIn("testexamples/annotationfieldimport").get(ClassWithAnnotatedFields.class);
JavaAnnotation annotation = clazz.getField("fieldAnnotatedWithEmptyArrays")
.getAnnotationOfType(FieldAnnotationWithArrays.class.getName());
assertThat(Array.getLength(annotation.get("primitives").get())).isZero();
assertThat(Array.getLength(annotation.get("objects").get())).isZero();
assertThat(Array.getLength(annotation.get("enums").get())).isZero();
assertThat(Array.getLength(annotation.get("classes").get())).isZero();
assertThat(Array.getLength(annotation.get("annotations").get())).isZero();
FieldAnnotationWithArrays reflected = annotation.as(FieldAnnotationWithArrays.class);
assertThat(reflected.primitives()).isEmpty();
assertThat(reflected.objects()).isEmpty();
assertThat(reflected.enums()).isEmpty();
assertThat(reflected.classes()).isEmpty();
assertThat(reflected.annotations()).isEmpty();
}
@Test
public void imports_fields_annotated_with_unimported_annotation() throws Exception {
JavaClass clazz = classesIn("testexamples/annotationfieldimport").get(ClassWithAnnotatedFields.class);
JavaAnnotation annotation = clazz.getField("fieldAnnotatedWithAnnotationFromParentPackage")
.getAnnotationOfType(SomeAnnotation.class.getName());
assertThat(annotation.get("mandatory")).contains("mandatory");
// NOTE: If we haven't imported the annotation itself, the import can't determine default values
assertThat(annotation.get("optional")).isAbsent();
SomeAnnotation reflected = annotation.as(SomeAnnotation.class);
assertThat(reflected.mandatory()).isEqualTo("mandatory");
assertThat(reflected.optional()).isEqualTo("optional");
}
@Test
public void imports_simple_methods_with_correct_parameters() throws Exception {
Set<JavaMethod> methods = classesIn("testexamples/methodimport").getMethods();
assertThat(methods).extractingResultOf("getDefaultValue").containsOnly(Optional.absent());
assertThat(findAnyByName(methods, "createString")).isEquivalentTo(
ClassWithStringStringMethod.class.getDeclaredMethod("createString", String.class));
assertThat(findAnyByName(methods, "consume")).isEquivalentTo(
ClassWithObjectVoidAndIntIntSerializableMethod.class.getDeclaredMethod("consume", Object.class));
assertThat(findAnyByName(methods, "createSerializable")).isEquivalentTo(
ClassWithObjectVoidAndIntIntSerializableMethod.class
.getDeclaredMethod("createSerializable", int.class, int.class));
}
@Test
public void imports_complex_method_with_correct_parameters() throws Exception {
JavaClass clazz = classesIn("testexamples/complexmethodimport").get(ClassWithComplexMethod.class);
assertThat(clazz.getMethods()).as("Methods of %s", ClassWithComplexMethod.class.getSimpleName()).hasSize(1);
assertThat(clazz.getMethod("complex", String.class, long.class, long.class, Serializable.class, Serializable.class))
.isEquivalentTo(ClassWithComplexMethod.class.getDeclaredMethod(
"complex", String.class, long.class, long.class, Serializable.class, Serializable.class));
}
@Test
public void imports_methods_with_correct_return_types() throws Exception {
Set<JavaCodeUnit> methods = classesIn("testexamples/methodimport").getCodeUnits();
assertThat(findAnyByName(methods, "createString").getReturnType())
.as("Return type of method 'createString'").matches(String.class);
assertThat(findAnyByName(methods, "consume").getReturnType())
.as("Return type of method 'consume'").matches(void.class);
assertThat(findAnyByName(methods, "createSerializable").getReturnType())
.as("Return type of method 'createSerializable'").matches(Serializable.class);
}
@Test
public void imports_methods_with_one_annotation_correctly() throws Exception {
JavaClass clazz = classesIn("testexamples/annotationmethodimport").get(ClassWithAnnotatedMethods.class);
JavaMethod method = findAnyByName(clazz.getMethods(), stringAnnotatedMethod);
JavaAnnotation annotation = method.getAnnotationOfType(MethodAnnotationWithStringValue.class.getName());
assertThat(annotation.getType()).matches(MethodAnnotationWithStringValue.class);
JavaAnnotation annotationByName = method.getAnnotationOfType(MethodAnnotationWithStringValue.class.getName());
assertThat(annotationByName).isEqualTo(annotation);
JavaAnnotation rawAnnotation = method.getAnnotationOfType(MethodAnnotationWithStringValue.class.getName());
assertThat(rawAnnotation.get("value").get()).isEqualTo("something");
assertThat(method).isEquivalentTo(ClassWithAnnotatedMethods.class.getMethod(stringAnnotatedMethod));
}
@Test
public void methods_handle_optional_annotation_correctly() throws Exception {
Set<JavaCodeUnit> methods = classesIn("testexamples/annotationmethodimport").getCodeUnits();
JavaCodeUnit method = findAnyByName(methods, "stringAnnotatedMethod");
assertThat(method.tryGetAnnotationOfType(MethodAnnotationWithStringValue.class)).isPresent();
assertThat(method.tryGetAnnotationOfType(MethodAnnotationWithEnumAndArrayValue.class)).isAbsent();
}
@Test
public void imports_methods_with_two_annotations_correctly() throws Exception {
JavaClass clazz = classesIn("testexamples/annotationmethodimport").get(ClassWithAnnotatedMethods.class);
JavaMethod method = findAnyByName(clazz.getMethods(), stringAndIntAnnotatedMethod);
assertThat(method.getAnnotations()).hasSize(2);
JavaAnnotation annotationWithString = method.getAnnotationOfType(MethodAnnotationWithStringValue.class.getName());
assertThat(annotationWithString.get("value").get()).isEqualTo("otherThing");
JavaAnnotation annotationWithInt = method.getAnnotationOfType(MethodAnnotationWithIntValue.class.getName());
assertThat(annotationWithInt.get("otherValue").get()).isEqualTo("overridden");
assertThat(method).isEquivalentTo(ClassWithAnnotatedMethods.class.getMethod(stringAndIntAnnotatedMethod));
}
@Test
public void imports_methods_with_complex_annotations_correctly() throws Exception {
JavaClass clazz = classesIn("testexamples/annotationmethodimport").get(ClassWithAnnotatedMethods.class);
JavaMethod method = findAnyByName(clazz.getMethods(), enumAndArrayAnnotatedMethod);
JavaAnnotation annotation = method.getAnnotationOfType(MethodAnnotationWithEnumAndArrayValue.class.getName());
assertThat((JavaEnumConstant) annotation.get("value").get()).isEquivalentTo(OTHER_VALUE);
assertThat((JavaEnumConstant) annotation.get("valueWithDefault").get()).isEquivalentTo(SOME_VALUE);
assertThat(((JavaEnumConstant[]) annotation.get("enumArray").get())).matches(SOME_VALUE, OTHER_VALUE);
assertThat(((JavaEnumConstant[]) annotation.get("enumArrayWithDefault").get())).matches(OTHER_VALUE);
assertThat(((JavaAnnotation) annotation.get("subAnnotation").get()).get("value").get()).isEqualTo("changed");
assertThat(((JavaAnnotation) annotation.get("subAnnotationWithDefault").get()).get("value").get())
.isEqualTo("default");
assertThat(((JavaAnnotation[]) annotation.get("subAnnotationArray").get())[0].get("value").get())
.isEqualTo("another");
assertThat(((JavaAnnotation[]) annotation.get("subAnnotationArrayWithDefault").get())[0].get("value").get())
.isEqualTo("first");
assertThat((JavaClass) annotation.get("clazz").get()).matches(Map.class);
assertThat((JavaClass) annotation.get("clazzWithDefault").get()).matches(String.class);
assertThat((JavaClass[]) annotation.get("classes").get()).matchExactly(Object.class, Serializable.class);
assertThat((JavaClass[]) annotation.get("classesWithDefault").get()).matchExactly(Serializable.class, List.class);
assertThat(method).isEquivalentTo(ClassWithAnnotatedMethods.class.getMethod(enumAndArrayAnnotatedMethod));
}
@Test
public void imports_method_with_annotation_with_empty_array() throws Exception {
JavaClass clazz = classesIn("testexamples/annotationmethodimport").get(ClassWithAnnotatedMethods.class);
JavaAnnotation annotation = clazz.getMethod(methodAnnotatedWithEmptyArrays)
.getAnnotationOfType(MethodAnnotationWithArrays.class.getName());
assertThat(Array.getLength(annotation.get("primitives").get())).isZero();
assertThat(Array.getLength(annotation.get("objects").get())).isZero();
assertThat(Array.getLength(annotation.get("enums").get())).isZero();
assertThat(Array.getLength(annotation.get("classes").get())).isZero();
assertThat(Array.getLength(annotation.get("annotations").get())).isZero();
MethodAnnotationWithArrays reflected = annotation.as(MethodAnnotationWithArrays.class);
assertThat(reflected.primitives()).isEmpty();
assertThat(reflected.objects()).isEmpty();
assertThat(reflected.enums()).isEmpty();
assertThat(reflected.classes()).isEmpty();
assertThat(reflected.annotations()).isEmpty();
}
@Test
public void imports_methods_annotated_with_unimported_annotation() throws Exception {
JavaClass clazz = classesIn("testexamples/annotationmethodimport").get(ClassWithAnnotatedMethods.class);
JavaAnnotation annotation = clazz.getMethod(methodAnnotatedWithAnnotationFromParentPackage)
.getAnnotationOfType(SomeAnnotation.class.getName());
assertThat(annotation.get("mandatory")).contains("mandatory");
// NOTE: If we haven't imported the annotation itself, the import can't determine default values
assertThat(annotation.get("optional")).isAbsent();
SomeAnnotation reflected = annotation.as(SomeAnnotation.class);
assertThat(reflected.mandatory()).isEqualTo("mandatory");
assertThat(reflected.optional()).isEqualTo("optional");
}
@Test
public void imports_class_with_one_annotation_correctly() throws Exception {
JavaClass clazz = classesIn("testexamples/annotatedclassimport").get(ClassWithOneAnnotation.class);
JavaAnnotation annotation = clazz.getAnnotationOfType(SimpleAnnotation.class.getName());
assertThat(annotation.getType()).matches(SimpleAnnotation.class);
JavaAnnotation annotationByName = clazz.getAnnotationOfType(SimpleAnnotation.class.getName());
assertThat(annotationByName).isEqualTo(annotation);
assertThat(annotation.get("value").get()).isEqualTo("test");
assertThat(clazz).matches(ClassWithOneAnnotation.class);
}
@Test
public void class_handles_optional_annotation_correctly() throws Exception {
JavaClass clazz = classesIn("testexamples/annotatedclassimport").get(ClassWithOneAnnotation.class);
assertThat(clazz.tryGetAnnotationOfType(SimpleAnnotation.class)).isPresent();
assertThat(clazz.tryGetAnnotationOfType(Deprecated.class)).isAbsent();
}
@Test
public void imports_class_with_complex_annotations_correctly() throws Exception {
JavaClass clazz = classesIn("testexamples/annotatedclassimport").get(ClassWithComplexAnnotations.class);
assertThat(clazz.getAnnotations()).as("annotations of " + clazz.getSimpleName()).hasSize(2);
JavaAnnotation annotation = clazz.getAnnotationOfType(TypeAnnotationWithEnumAndArrayValue.class.getName());
assertThat((JavaEnumConstant) annotation.get("value").get()).isEquivalentTo(OTHER_VALUE);
assertThat((JavaEnumConstant) annotation.get("valueWithDefault").get()).isEquivalentTo(SOME_VALUE);
assertThat(((JavaEnumConstant[]) annotation.get("enumArray").get())).matches(SOME_VALUE, OTHER_VALUE);
assertThat(((JavaEnumConstant[]) annotation.get("enumArrayWithDefault").get())).matches(OTHER_VALUE);
assertThat(((JavaAnnotation) annotation.get("subAnnotation").get()).get("value").get()).isEqualTo("sub");
assertThat(((JavaAnnotation) annotation.get("subAnnotationWithDefault").get()).get("value").get())
.isEqualTo("default");
assertThat(((JavaAnnotation[]) annotation.get("subAnnotationArray").get())[0].get("value").get())
.isEqualTo("otherFirst");
assertThat(((JavaAnnotation[]) annotation.get("subAnnotationArrayWithDefault").get())[0].get("value").get())
.isEqualTo("first");
assertThat((JavaClass) annotation.get("clazz").get()).matches(Serializable.class);
assertThat((JavaClass) annotation.get("clazzWithDefault").get()).matches(String.class);
assertThat((JavaClass[]) annotation.get("classes").get()).matchExactly(Serializable.class, String.class);
assertThat((JavaClass[]) annotation.get("classesWithDefault").get()).matchExactly(Serializable.class, List.class);
assertThat(clazz).matches(ClassWithComplexAnnotations.class);
}
@Test
public void imports_class_with_annotation_with_empty_array() throws Exception {
JavaClass clazz = classesIn("testexamples/annotatedclassimport").get(ClassWithAnnotationWithEmptyArrays.class);
JavaAnnotation annotation = clazz.getAnnotationOfType(ClassAnnotationWithArrays.class.getName());
assertThat(Array.getLength(annotation.get("primitives").get())).isZero();
assertThat(Array.getLength(annotation.get("objects").get())).isZero();
assertThat(Array.getLength(annotation.get("enums").get())).isZero();
assertThat(Array.getLength(annotation.get("classes").get())).isZero();
assertThat(Array.getLength(annotation.get("annotations").get())).isZero();
ClassAnnotationWithArrays reflected = clazz.getAnnotationOfType(ClassAnnotationWithArrays.class);
assertThat(reflected.primitives()).isEmpty();
assertThat(reflected.objects()).isEmpty();
assertThat(reflected.enums()).isEmpty();
assertThat(reflected.classes()).isEmpty();
assertThat(reflected.annotations()).isEmpty();
}
@Test
public void imports_class_annotated_with_unimported_annotation() throws Exception {
JavaClass clazz = classesIn("testexamples/annotatedclassimport").get(ClassWithUnimportedAnnotation.class);
JavaAnnotation annotation = clazz.getAnnotationOfType(SomeAnnotation.class.getName());
assertThat(annotation.get("mandatory")).contains("mandatory");
// NOTE: If we haven't imported the annotation itself, the import can't determine default values
assertThat(annotation.get("optional")).isAbsent();
assertThat((JavaEnumConstant) annotation.get("mandatoryEnum").get()).isEquivalentTo(SOME_VALUE);
assertThat(annotation.get("optionalEnum")).isAbsent();
SomeAnnotation reflected = clazz.getAnnotationOfType(SomeAnnotation.class);
assertThat(reflected.mandatory()).isEqualTo("mandatory");
assertThat(reflected.optional()).isEqualTo("optional");
assertThat(reflected.mandatoryEnum()).isEqualTo(SOME_VALUE);
assertThat(reflected.optionalEnum()).isEqualTo(OTHER_VALUE);
}
@Test
public void imports_simple_constructors_with_correct_parameters() throws Exception {
JavaClass clazz = classesIn("testexamples/constructorimport").get(ClassWithSimpleConstructors.class);
assertThat(clazz.getConstructors()).as("Constructors").hasSize(3);
assertThat(clazz.getConstructor()).isEquivalentTo(ClassWithSimpleConstructors.class.getDeclaredConstructor());
assertThat(clazz.getConstructor(Object.class))
.isEquivalentTo(ClassWithSimpleConstructors.class.getDeclaredConstructor(Object.class));
assertThat(clazz.getConstructor(int.class, int.class))
.isEquivalentTo(ClassWithSimpleConstructors.class.getDeclaredConstructor(int.class, int.class));
}
@Test
public void imports_complex_constructor_with_correct_parameters() throws Exception {
JavaClass clazz = classesIn("testexamples/constructorimport").get(ClassWithComplexConstructor.class);
assertThat(clazz.getConstructors()).as("Constructors").hasSize(1);
assertThat(clazz.getConstructor(String.class, long.class, long.class, Serializable.class, Serializable.class))
.isEquivalentTo(ClassWithComplexConstructor.class.getDeclaredConstructor(
String.class, long.class, long.class, Serializable.class, Serializable.class));
}
@Test
public void imports_constructors_with_complex_annotations_correctly() throws Exception {
JavaConstructor constructor = classesIn("testexamples/annotationmethodimport").get(ClassWithAnnotatedMethods.class)
.getConstructor();
JavaAnnotation annotation = constructor.getAnnotationOfType(MethodAnnotationWithEnumAndArrayValue.class.getName());
assertThat((Object[]) annotation.get("classes").get()).extracting("name")
.containsExactly(Object.class.getName(), Serializable.class.getName());
assertThat(constructor).isEquivalentTo(ClassWithAnnotatedMethods.class.getConstructor());
}
@Test
public void imports_interfaces_and_classes() throws Exception {
ImportedClasses classes = classesIn("testexamples/classhierarchyimport");
JavaClass baseClass = classes.get(BaseClass.class);
JavaClass parentInterface = classes.get(ParentInterface.class);
assertThat(baseClass.isInterface()).as(BaseClass.class.getSimpleName() + " is interface").isFalse();
assertThat(parentInterface.isInterface()).as(ParentInterface.class.getSimpleName() + " is interface").isTrue();
}
@Test
public void imports_base_class_in_class_hierarchy_correctly() throws Exception {
JavaClass baseClass = classesIn("testexamples/classhierarchyimport").get(BaseClass.class);
assertThat(baseClass.getConstructors()).as("Constructors of " + BaseClass.class.getSimpleName()).hasSize(2);
assertThat(baseClass.getFields()).as("Fields of " + BaseClass.class.getSimpleName()).hasSize(1);
assertThat(baseClass.getMethods()).as("Methods of " + BaseClass.class.getSimpleName()).hasSize(2);
assertThat(baseClass.getStaticInitializer().get().getMethodCallsFromSelf().size())
.as("Calls from %s.<clinit>()", BaseClass.class.getSimpleName()).isGreaterThan(0);
}
@Test
public void imports_sub_class_in_class_hierarchy_correctly() throws Exception {
JavaClass subClass = classesIn("testexamples/classhierarchyimport").get(SubClass.class);
assertThat(subClass.getConstructors()).hasSize(3);
assertThat(subClass.getFields()).hasSize(1);
assertThat(subClass.getMethods()).hasSize(3);
assertThat(subClass.getStaticInitializer().get().getMethodCallsFromSelf().size()).isGreaterThan(0);
}
@Test
public void creates_relations_between_super_and_sub_classes() throws Exception {
ImportedClasses classes = classesIn("testexamples/classhierarchyimport");
JavaClass baseClass = classes.get(BaseClass.class);
JavaClass subClass = classes.get(SubClass.class);
JavaClass otherSubClass = classes.get(OtherSubClass.class);
JavaClass subSubClass = classes.get(SubSubClass.class);
assertThat(baseClass.getSuperClass().get().reflect()).isEqualTo(Object.class);
assertThat(baseClass.getSubClasses()).containsOnly(subClass, otherSubClass);
assertThat(baseClass.getAllSubClasses()).containsOnly(subClass, otherSubClass, subSubClass);
assertThat(subClass.getSuperClass()).contains(baseClass);
assertThat(subClass.getAllSubClasses()).containsOnly(subSubClass);
assertThat(subSubClass.getSuperClass()).contains(subClass);
}
@Test
public void creates_relations_between_classes_and_interfaces() throws Exception {
ImportedClasses classes = classesIn("testexamples/classhierarchyimport");
JavaClass baseClass = classes.get(BaseClass.class);
JavaClass otherInterface = classes.get(OtherInterface.class);
JavaClass subClass = classes.get(SubClass.class);
JavaClass subInterface = classes.get(SubInterface.class);
JavaClass otherSubClass = classes.get(OtherSubClass.class);
JavaClass parentInterface = classes.get(ParentInterface.class);
JavaClass grandParentInterface = classes.get(GrandParentInterface.class);
JavaClass someCollection = classes.get(SomeCollection.class);
JavaClass collectionInterface = classes.get(CollectionInterface.class);
assertThat(baseClass.getInterfaces()).containsOnly(otherInterface);
assertThat(baseClass.getAllInterfaces()).containsOnly(otherInterface, grandParentInterface);
assertThat(subClass.getInterfaces()).containsOnly(subInterface);
assertThat(subClass.getAllInterfaces()).containsOnly(
subInterface, otherInterface, parentInterface, grandParentInterface);
assertThat(otherSubClass.getInterfaces()).containsOnly(parentInterface);
assertThat(otherSubClass.getAllInterfaces()).containsOnly(parentInterface, grandParentInterface, otherInterface);
assertThat(someCollection.getInterfaces()).containsOnly(collectionInterface, otherInterface, subInterface);
assertThat(someCollection.getAllInterfaces()).extractingResultOf("reflect").containsOnly(
CollectionInterface.class, OtherInterface.class, SubInterface.class, ParentInterface.class,
GrandParentInterface.class, Collection.class, Iterable.class);
}
@Test
public void creates_relations_between_interfaces_and_interfaces() throws Exception {
ImportedClasses classes = classesIn("testexamples/classhierarchyimport");
JavaClass subInterface = classes.get(SubInterface.class);
JavaClass parentInterface = classes.get(ParentInterface.class);
JavaClass grandParentInterface = classes.get(GrandParentInterface.class);
JavaClass collectionInterface = classes.get(CollectionInterface.class);
assertThat(grandParentInterface.getAllInterfaces()).isEmpty();
assertThat(parentInterface.getInterfaces()).containsOnly(grandParentInterface);
assertThat(parentInterface.getAllInterfaces()).containsOnly(grandParentInterface);
assertThat(subInterface.getInterfaces()).containsOnly(parentInterface);
assertThat(subInterface.getAllInterfaces()).containsOnly(parentInterface, grandParentInterface);
assertThat(collectionInterface.getInterfaces()).extractingResultOf("reflect").containsOnly(Collection.class);
}
@Test
public void creates_relations_between_interfaces_and_sub_classes() throws Exception {
ImportedClasses classes = classesIn("testexamples/classhierarchyimport");
JavaClass baseClass = classes.get(BaseClass.class);
JavaClass otherInterface = classes.get(OtherInterface.class);
JavaClass subClass = classes.get(SubClass.class);
JavaClass subSubClass = classes.get(SubSubClass.class);
JavaClass subInterface = classes.get(SubInterface.class);
JavaClass otherSubClass = classes.get(OtherSubClass.class);
JavaClass parentInterface = classes.get(ParentInterface.class);
JavaClass grandParentInterface = classes.get(GrandParentInterface.class);
JavaClass someCollection = classes.get(SomeCollection.class);
JavaClass collectionInterface = classes.get(CollectionInterface.class);
assertThat(grandParentInterface.getSubClasses()).containsOnly(parentInterface, otherInterface);
assertThat(grandParentInterface.getAllSubClasses()).containsOnly(
parentInterface, subInterface, otherInterface,
baseClass, subClass, otherSubClass, subSubClass, someCollection
);
assertThat(parentInterface.getSubClasses()).containsOnly(subInterface, otherSubClass);
assertThat(parentInterface.getAllSubClasses()).containsOnly(
subInterface, subClass, subSubClass, someCollection, otherSubClass);
JavaClass collection = getOnlyElement(collectionInterface.getInterfaces());
assertThat(collection.getAllSubClasses()).containsOnly(collectionInterface, someCollection);
}
@Test
public void imports_enclosing_classes() throws Exception {
ImportedClasses classes = classesIn("testexamples/innerclassimport");
JavaClass classWithInnerClass = classes.get(ClassWithInnerClass.class);
JavaClass innerClass = classes.get(ClassWithInnerClass.Inner.class);
JavaClass anonymousClass = classes.get(ClassWithInnerClass.class.getName() + "$1");
JavaMethod calledTarget = getOnlyElement(classes.get(CalledClass.class).getMethods());
assertThat(classWithInnerClass.isAnonymous()).as("class is anonymous").isFalse();
assertThat(innerClass.isAnonymous()).as("class is anonymous").isFalse();
assertThat(innerClass.getEnclosingClass()).contains(classWithInnerClass);
assertThat(innerClass).matches(ClassWithInnerClass.Inner.class);
assertThat(anonymousClass.getEnclosingClass()).contains(classWithInnerClass);
assertThat(anonymousClass.getName()).isEqualTo(ClassWithInnerClass.class.getName() + "$1");
assertThat(anonymousClass.isAnonymous()).as("class is anonymous").isTrue();
assertThat(anonymousClass.getSimpleName()).isEmpty();
assertThat(anonymousClass.getPackage()).isEqualTo(ClassWithInnerClass.class.getPackage().getName());
JavaMethodCall call = getOnlyElement(innerClass.getCodeUnitWithParameterTypes("call").getMethodCallsFromSelf());
assertThatCall(call).isFrom("call").isTo(calledTarget).inLineNumber(20);
call = getOnlyElement(anonymousClass.getCodeUnitWithParameterTypes("call").getMethodCallsFromSelf());
assertThatCall(call).isFrom("call").isTo(calledTarget).inLineNumber(10);
}
@Test
public void imports_overridden_methods_correctly() throws Exception {
ImportedClasses classes = classesIn("testexamples/classhierarchyimport");
JavaClass baseClass = classes.get(BaseClass.class);
JavaClass subClass = classes.get(SubClass.class);
assertThat(baseClass.getCodeUnitWithParameterTypes("getSomeField").getModifiers()).containsOnly(PROTECTED);
assertThat(subClass.getCodeUnitWithParameterTypes("getSomeField").getModifiers()).containsOnly(PUBLIC);
}
@Test
public void imports_own_get_field_access() throws Exception {
JavaClass classWithOwnFieldAccess = classesIn("testexamples/fieldaccessimport").get(OwnFieldAccess.class);
JavaMethod getStringValue = classWithOwnFieldAccess.getMethod("getStringValue");
JavaFieldAccess access = getOnlyElement(getStringValue.getFieldAccesses());
assertThatAccess(access)
.isOfType(GET)
.isFrom(getStringValue)
.isTo("stringValue")
.inLineNumber(8);
}
@Test
public void imports_own_set_field_access() throws Exception {
JavaClass classWithOwnFieldAccess = classesIn("testexamples/fieldaccessimport").get(OwnFieldAccess.class);
JavaMethod setStringValue = classWithOwnFieldAccess.getMethod("setStringValue", String.class);
JavaFieldAccess access = getOnlyElement(setStringValue.getFieldAccesses());
assertThatAccess(access)
.isOfType(SET)
.isFrom(setStringValue)
.isTo(classWithOwnFieldAccess.getField("stringValue"))
.inLineNumber(12);
}
@Test
public void imports_multiple_own_accesses() throws Exception {
JavaClass classWithOwnFieldAccess = classesIn("testexamples/fieldaccessimport").get(OwnFieldAccess.class);
Set<JavaFieldAccess> fieldAccesses = classWithOwnFieldAccess.getFieldAccessesFromSelf();
assertThat(fieldAccesses).hasSize(4);
assertThat(getOnly(fieldAccesses, "stringValue", GET).getLineNumber())
.as("Line number of get stringValue").isEqualTo(8);
assertThat(getOnly(fieldAccesses, "stringValue", SET).getLineNumber())
.as("Line number of set stringValue").isEqualTo(12);
assertThat(getOnly(fieldAccesses, "intValue", GET).getLineNumber())
.as("Line number of get intValue").isEqualTo(16);
assertThat(getOnly(fieldAccesses, "intValue", SET).getLineNumber())
.as("Line number of set intValue").isEqualTo(20);
}
@Test
public void imports_own_static_field_accesses() throws Exception {
JavaClass classWithOwnFieldAccess = classesIn("testexamples/fieldaccessimport").get(OwnStaticFieldAccess.class);
Set<JavaFieldAccess> accesses = classWithOwnFieldAccess.getFieldAccessesFromSelf();
assertThat(accesses).hasSize(2);
JavaFieldAccess getAccess = getOnly(accesses, "staticStringValue", GET);
assertThatAccess(getAccess)
.isFrom("getStaticStringValue")
.isTo("staticStringValue")
.inLineNumber(7);
JavaFieldAccess setAccess = getOnly(accesses, "staticStringValue", SET);
assertThatAccess(setAccess)
.isFrom("setStaticStringValue", String.class)
.isTo("staticStringValue")
.inLineNumber(11);
}
@Test
public void imports_other_field_accesses() throws Exception {
ImportedClasses classes = classesIn("testexamples/fieldaccessimport");
JavaClass classWithOwnFieldAccess = classes.get(OwnFieldAccess.class);
JavaClass classWithForeignFieldAccess = classes.get(ForeignFieldAccess.class);
Set<JavaFieldAccess> accesses = classWithForeignFieldAccess.getFieldAccessesFromSelf();
assertThat(accesses).hasSize(4);
assertThatAccess(getOnly(accesses, "stringValue", GET))
.isFrom(classWithForeignFieldAccess.getCodeUnitWithParameterTypes("getStringFromOther"))
.isTo(classWithOwnFieldAccess.getField("stringValue"))
.inLineNumber(5);
assertThatAccess(getOnly(accesses, "stringValue", SET))
.isFrom(classWithForeignFieldAccess.getCodeUnitWithParameterTypes("setStringFromOther"))
.isTo(classWithOwnFieldAccess.getField("stringValue"))
.inLineNumber(9);
assertThatAccess(getOnly(accesses, "intValue", GET))
.isFrom(classWithForeignFieldAccess.getCodeUnitWithParameterTypes("getIntFromOther"))
.isTo(classWithOwnFieldAccess.getField("intValue"))
.inLineNumber(13);
assertThatAccess(getOnly(accesses, "intValue", SET))
.isFrom(classWithForeignFieldAccess.getCodeUnitWithParameterTypes("setIntFromOther"))
.isTo(classWithOwnFieldAccess.getField("intValue"))
.inLineNumber(17);
}
@Test
public void imports_other_static_field_accesses() throws Exception {
ImportedClasses classes = classesIn("testexamples/fieldaccessimport");
JavaClass classWithOwnFieldAccess = classes.get(OwnStaticFieldAccess.class);
JavaClass classWithForeignFieldAccess = classes.get(ForeignStaticFieldAccess.class);
Set<JavaFieldAccess> accesses = classWithForeignFieldAccess.getFieldAccessesFromSelf();
assertThat(accesses).as("Number of field accesses from " + classWithForeignFieldAccess.getName()).hasSize(2);
assertThatAccess(getOnly(accesses, "staticStringValue", GET))
.isFrom(classWithForeignFieldAccess.getCodeUnitWithParameterTypes("getStaticStringFromOther"))
.isTo(classWithOwnFieldAccess.getField("staticStringValue"))
.inLineNumber(5);
assertThatAccess(getOnly(accesses, "staticStringValue", SET))
.isFrom(classWithForeignFieldAccess.getCodeUnitWithParameterTypes("setStaticStringFromOther"))
.isTo(classWithOwnFieldAccess.getField("staticStringValue"))
.inLineNumber(9);
}
@Test
public void imports_multiple_accesses_from_same_method() throws Exception {
ImportedClasses classes = classesIn("testexamples/fieldaccessimport");
JavaClass classWithOwnFieldAccess = classes.get(OwnFieldAccess.class);
JavaClass multipleFieldAccesses = classes.get(MultipleFieldAccessInSameMethod.class);
Set<JavaFieldAccess> accesses = multipleFieldAccesses.getFieldAccessesFromSelf();
assertThat(accesses).as("Number of field accesses from " + multipleFieldAccesses.getName()).hasSize(5);
Set<JavaFieldAccess> setStringValues = getByNameAndAccessType(accesses, "stringValue", SET);
assertThat(setStringValues).hasSize(2);
assertThat(targetsOf(setStringValues)).containsOnly(targetFrom(classWithOwnFieldAccess.getField("stringValue")));
assertThat(lineNumbersOf(setStringValues)).containsOnly(6, 8);
assertThatAccess(getOnly(accesses, "stringValue", GET))
.isTo(classWithOwnFieldAccess.getField("stringValue"))
.inLineNumber(7);
assertThatAccess(getOnly(accesses, "intValue", GET))
.isTo(classWithOwnFieldAccess.getField("intValue"))
.inLineNumber(10);
assertThatAccess(getOnly(accesses, "intValue", SET))
.isTo(classWithOwnFieldAccess.getField("intValue"))
.inLineNumber(11);
}
@Test
public void imports_other_field_accesses_from_constructor() throws Exception {
ImportedClasses classes = classesIn("testexamples/fieldaccessimport");
JavaClass classWithOwnFieldAccess = classes.get(OwnFieldAccess.class);
JavaClass fieldAccessFromConstructor = classes.get(ForeignFieldAccessFromConstructor.class);
Set<JavaFieldAccess> accesses = fieldAccessFromConstructor.getFieldAccessesFromSelf();
assertThat(accesses).as("Number of field accesses from " + fieldAccessFromConstructor.getName()).hasSize(2);
assertThatAccess(getOnly(accesses, "stringValue", GET))
.isFrom(fieldAccessFromConstructor.getCodeUnitWithParameterTypes(CONSTRUCTOR_NAME))
.isTo(classWithOwnFieldAccess.getField("stringValue"))
.inLineNumber(5);
assertThatAccess(getOnly(accesses, "intValue", SET))
.isFrom(fieldAccessFromConstructor.getCodeUnitWithParameterTypes(CONSTRUCTOR_NAME))
.isTo(classWithOwnFieldAccess.getField("intValue"))
.inLineNumber(6);
}
@Test
public void imports_other_field_accesses_from_static_initializer() throws Exception {
ImportedClasses classes = classesIn("testexamples/fieldaccessimport");
JavaClass classWithOwnFieldAccess = classes.get(OwnFieldAccess.class);
JavaClass fieldAccessFromInitializer = classes.get(ForeignFieldAccessFromStaticInitializer.class);
Set<JavaFieldAccess> accesses = fieldAccessFromInitializer.getFieldAccessesFromSelf();
assertThat(accesses).as("Number of field accesses from " + fieldAccessFromInitializer.getName()).hasSize(2);
assertThatAccess(getOnly(accesses, "stringValue", GET))
.isFrom(fieldAccessFromInitializer.getCodeUnitWithParameterTypes(STATIC_INITIALIZER_NAME))
.isTo(classWithOwnFieldAccess.getField("stringValue"))
.inLineNumber(5);
assertThatAccess(getOnly(accesses, "intValue", SET))
.isFrom(fieldAccessFromInitializer.getCodeUnitWithParameterTypes(STATIC_INITIALIZER_NAME))
.isTo(classWithOwnFieldAccess.getField("intValue"))
.inLineNumber(6);
}
@Test
public void imports_external_field_access() throws Exception {
JavaClass classWithExternalFieldAccess = classesIn("testexamples/fieldaccessimport").get(ExternalFieldAccess.class);
JavaFieldAccess access = getOnlyElement(classWithExternalFieldAccess.getMethod("access").getFieldAccesses());
assertThatAccess(access)
.isFrom(classWithExternalFieldAccess.getCodeUnitWithParameterTypes("access"))
.inLineNumber(8);
assertThat(access.getTarget()).isEquivalentTo(field(ClassWithIntAndObjectFields.class, "objectField"));
access = getOnlyElement(classWithExternalFieldAccess.getMethod("accessInheritedExternalField").getFieldAccesses());
assertThatAccess(access)
.isFrom(classWithExternalFieldAccess.getCodeUnitWithParameterTypes("accessInheritedExternalField"))
.inLineNumber(12);
assertThat(access.getTarget()).isEquivalentTo(field(ParentClass.class, "someParentField"));
}
@Test
public void imports_external_field_access_with_shadowed_field() throws Exception {
JavaClass classWithExternalFieldAccess = classesIn("testexamples/fieldaccessimport").get(ExternalShadowedFieldAccess.class);
JavaFieldAccess access = getOnlyElement(classWithExternalFieldAccess.getFieldAccessesFromSelf());
assertThatAccess(access)
.isFrom(classWithExternalFieldAccess.getCodeUnitWithParameterTypes("accessField"))
.inLineNumber(7);
assertThat(access.getTarget()).isEquivalentTo(field(ChildClass.class, "someField"));
}
@Test
public void imports_shadowed_and_superclass_field_access() throws Exception {
ImportedClasses classes = classesIn("testexamples/hierarchicalfieldaccess");
JavaClass classThatAccessesFieldOfSuperClass = classes.get(AccessToSuperAndSubClassField.class);
JavaClass superClassWithAccessedField = classes.get(SuperClassWithAccessedField.class);
JavaClass subClassWithAccessedField = classes.get(SubClassWithAccessedField.class);
Set<JavaFieldAccess> accesses = classThatAccessesFieldOfSuperClass.getFieldAccessesFromSelf();
assertThat(accesses).hasSize(2);
JavaField field = superClassWithAccessedField.getField("field");
FieldAccessTarget expectedSuperClassFieldAccess = new FieldAccessTargetBuilder()
.withOwner(subClassWithAccessedField)
.withName(field.getName())
.withType(field.getType())
.withField(Suppliers.ofInstance(Optional.of(field)))
.build();
assertThatAccess(getOnly(accesses, "field", GET))
.isFrom("accessSuperClassField")
.isTo(expectedSuperClassFieldAccess)
.inLineNumber(5);
assertThatAccess(getOnly(accesses, "maskedField", GET))
.isFrom("accessSubClassField")
.isTo(subClassWithAccessedField.getField("maskedField"))
.inLineNumber(9);
}
@Test
public void imports_field_accesses_to_fields_from_interfaces() throws Exception {
Set<JavaFieldAccess> accesses = classesIn("testexamples/fieldaccesstointerfaces")
.get(ClassAccessingInterfaceFields.class).getFieldAccessesFromSelf();
assertThat(findAnyByName(accesses, "" + InterfaceWithFields.objectFieldOne).getTarget().resolveField().get())
.isEquivalentTo(field(InterfaceWithFields.class, "" + InterfaceWithFields.objectFieldOne));
assertThat(findAnyByName(accesses, "" + InterfaceWithFields.objectFieldTwo).getTarget().resolveField().get())
.isEquivalentTo(field(InterfaceWithFields.class, "" + InterfaceWithFields.objectFieldTwo));
assertThat(findAnyByName(accesses, "" + OtherInterfaceWithFields.otherObjectFieldOne).getTarget().resolveField().get())
.isEquivalentTo(field(OtherInterfaceWithFields.class, "" + OtherInterfaceWithFields.otherObjectFieldOne));
assertThat(findAnyByName(accesses, "" + OtherInterfaceWithFields.otherObjectFieldTwo).getTarget().resolveField().get())
.isEquivalentTo(field(OtherInterfaceWithFields.class, "" + OtherInterfaceWithFields.otherObjectFieldTwo));
assertThat(findAnyByName(accesses, "" + ParentInterfaceWithFields.parentObjectFieldOne).getTarget().resolveField().get())
.isEquivalentTo(field(ParentInterfaceWithFields.class, "" + ParentInterfaceWithFields.parentObjectFieldOne));
assertThat(findAnyByName(accesses, "" + ParentInterfaceWithFields.parentObjectFieldTwo).getTarget().resolveField().get())
.isEquivalentTo(field(ParentInterfaceWithFields.class, "" + ParentInterfaceWithFields.parentObjectFieldTwo));
}
@Test
public void imports_shadowed_and_superclass_method_calls() throws Exception {
ImportedClasses classes = classesIn("testexamples/hierarchicalmethodcall");
JavaClass classThatCallsMethodOfSuperClass = classes.get(CallOfSuperAndSubClassMethod.class);
JavaClass superClassWithCalledMethod = classes.get(SuperClassWithCalledMethod.class);
JavaClass subClassWithCalledMethod = classes.get(SubClassWithCalledMethod.class);
Set<JavaMethodCall> calls = classThatCallsMethodOfSuperClass.getMethodCallsFromSelf();
assertThat(calls).hasSize(2);
JavaCodeUnit callSuperClassMethod = classThatCallsMethodOfSuperClass
.getCodeUnitWithParameterTypes(CallOfSuperAndSubClassMethod.callSuperClassMethod);
JavaMethod expectedSuperClassMethod = superClassWithCalledMethod.getMethod(SuperClassWithCalledMethod.method);
MethodCallTarget expectedSuperClassCall = new MethodCallTargetBuilder()
.withOwner(subClassWithCalledMethod)
.withName(expectedSuperClassMethod.getName())
.withParameters(expectedSuperClassMethod.getParameters())
.withReturnType(expectedSuperClassMethod.getReturnType())
.withMethods(Suppliers.ofInstance(Collections.singleton(expectedSuperClassMethod)))
.build();
assertThatCall(getOnlyByCaller(calls, callSuperClassMethod))
.isFrom(callSuperClassMethod)
.isTo(expectedSuperClassCall)
.inLineNumber(CallOfSuperAndSubClassMethod.callSuperClassLineNumber);
JavaCodeUnit callSubClassMethod = classThatCallsMethodOfSuperClass
.getCodeUnitWithParameterTypes(CallOfSuperAndSubClassMethod.callSubClassMethod);
assertThatCall(getOnlyByCaller(calls, callSubClassMethod))
.isFrom(callSubClassMethod)
.isTo(subClassWithCalledMethod.getMethod(SubClassWithCalledMethod.maskedMethod))
.inLineNumber(CallOfSuperAndSubClassMethod.callSubClassLineNumber);
}
@Test
public void imports_constructor_calls_on_self() throws Exception {
JavaClass classThatCallsOwnConstructor = classesIn("testexamples/callimport").get(CallsOwnConstructor.class);
JavaCodeUnit caller = classThatCallsOwnConstructor.getCodeUnitWithParameterTypes("copy");
Set<JavaConstructorCall> calls = classThatCallsOwnConstructor.getConstructorCallsFromSelf();
assertThatCall(getOnlyByCaller(calls, caller))
.isFrom(caller)
.isTo(classThatCallsOwnConstructor.getConstructor(String.class))
.inLineNumber(8);
}
@Test
public void imports_method_calls_on_self() throws Exception {
JavaClass classThatCallsOwnMethod = classesIn("testexamples/callimport").get(CallsOwnMethod.class);
JavaMethodCall call = getOnlyElement(classThatCallsOwnMethod.getMethodCallsFromSelf());
assertThatCall(call)
.isFrom(classThatCallsOwnMethod.getCodeUnitWithParameterTypes("getString"))
.isTo(classThatCallsOwnMethod.getMethod("string"))
.inLineNumber(6);
}
@Test
public void imports_constructor_calls_on_other() throws Exception {
ImportedClasses classes = classesIn("testexamples/callimport");
JavaClass classThatCallsOtherConstructor = classes.get(CallsOtherConstructor.class);
JavaClass otherClass = classes.get(CallsOwnConstructor.class);
JavaCodeUnit caller = classThatCallsOtherConstructor.getCodeUnitWithParameterTypes("createOther");
Set<JavaConstructorCall> calls = classThatCallsOtherConstructor.getConstructorCallsFromSelf();
assertThatCall(getOnlyByCaller(calls, caller))
.isFrom(caller)
.isTo(otherClass.getConstructor(String.class))
.inLineNumber(5);
}
@Test
public void imports_method_calls_on_other() throws Exception {
ImportedClasses classes = classesIn("testexamples/callimport");
JavaClass classThatCallsOtherMethod = classes.get(CallsOtherMethod.class);
JavaClass other = classes.get(CallsOwnMethod.class);
JavaMethodCall call = getOnlyElement(classThatCallsOtherMethod.getMethodCallsFromSelf());
assertThatCall(call)
.isFrom(classThatCallsOtherMethod.getCodeUnitWithParameterTypes("getFromOther"))
.isTo(other.getMethod("getString"))
.inLineNumber(7);
}
@Test
public void imports_constructor_calls_on_external_class() throws Exception {
JavaClass classThatCallsOwnConstructor = classesIn("testexamples/callimport").get(CallsOwnConstructor.class);
JavaCodeUnit constructorCallingObjectInit = classThatCallsOwnConstructor.getConstructor(String.class);
JavaConstructorCall objectInitCall = getOnlyElement(constructorCallingObjectInit.getConstructorCallsFromSelf());
assertThatCall(objectInitCall)
.isFrom(constructorCallingObjectInit)
.inLineNumber(4);
ConstructorCallTarget target = objectInitCall.getTarget();
assertThat(target.getFullName()).isEqualTo(Object.class.getName() + ".<init>()");
assertThat(reflect(target)).isEqualTo(Object.class.getConstructor());
}
@Test
public void imports_constructor_calls_to_sub_type_constructor_on_external_class() throws Exception {
JavaClass classWithExternalConstructorCall = classesIn("testexamples/callimport").get(ExternalSubTypeConstructorCall.class);
assertConstructorCall(classWithExternalConstructorCall.getCodeUnitWithParameterTypes("call"), ChildClass.class, 9);
assertConstructorCall(classWithExternalConstructorCall.getCodeUnitWithParameterTypes("newHashMap"), HashMap.class, 13);
}
private void assertConstructorCall(JavaCodeUnit call, Class<?> constructorOwner, int lineNumber) {
JavaConstructorCall callToExternalClass =
getOnlyElement(getByTargetOwner(call.getConstructorCallsFromSelf(), constructorOwner));
assertThatCall(callToExternalClass)
.isFrom(call)
.inLineNumber(lineNumber);
ConstructorCallTarget target = callToExternalClass.getTarget();
assertThat(target.getFullName()).isEqualTo(constructorOwner.getName() + ".<init>()");
assertThat(reflect(target)).isEqualTo(constructor(constructorOwner));
}
@Test
public void imports_method_calls_on_external_class() throws Exception {
JavaClass classThatCallsExternalMethod = classesIn("testexamples/callimport").get(CallsExternalMethod.class);
JavaMethodCall call = getOnlyElement(classThatCallsExternalMethod.getMethodCallsFromSelf());
assertThatCall(call)
.isFrom(classThatCallsExternalMethod.getCodeUnitWithParameterTypes("getString"))
.inLineNumber(7);
MethodCallTarget target = call.getTarget();
assertThat(target.getOwner().reflect()).isEqualTo(ArrayList.class);
assertThat(target.getFullName()).isEqualTo(ArrayList.class.getName() + ".toString()");
}
@Test
public void imports_method_calls_on_overridden_external_class() throws Exception {
JavaClass classThatCallsExternalMethod = classesIn("testexamples/callimport").get(ExternalOverriddenMethodCall.class);
JavaMethodCall call = getOnlyElement(classThatCallsExternalMethod.getMethodCallsFromSelf());
assertThatCall(call)
.isFrom(classThatCallsExternalMethod.getCodeUnitWithParameterTypes("call"))
.inLineNumber(9);
MethodCallTarget target = call.getTarget();
assertThat(target.getFullName()).isEqualTo(ChildClass.class.getName() + ".overrideMe()");
assertThat(getOnlyElement(target.resolve()).getFullName()).isEqualTo(ChildClass.class.getName() + ".overrideMe()");
assertThat(reflect(target)).isEqualTo(method(ChildClass.class, "overrideMe"));
}
@Test
public void imports_method_calls_on_external_interface_hierarchies() throws Exception {
JavaClass classThatCallsExternalMethod = classesIn("testexamples/callimport").get(ExternalInterfaceMethodCall.class);
JavaMethodCall call = getOnlyElement(classThatCallsExternalMethod.getMethodCallsFromSelf());
assertThatCall(call)
.isFrom(classThatCallsExternalMethod.getCodeUnitWithParameterTypes("call"))
.inLineNumber(9);
MethodCallTarget target = call.getTarget();
assertThat(reflect(target)).isEqualTo(method(Map.class, "put", Object.class, Object.class));
}
@Test
public void imports_non_unique_targets_for_diamond_scenarios() throws Exception {
ImportedClasses diamondScenario = classesIn("testexamples/diamond");
JavaClass classCallingDiamond = diamondScenario.get(ClassCallingDiamond.class);
JavaClass diamondLeftInterface = diamondScenario.get(InterfaceB.class);
JavaClass diamondRightInterface = diamondScenario.get(InterfaceC.class);
JavaClass diamondPeakInterface = diamondScenario.get(InterfaceD.class);
JavaClass diamondPeakClass = diamondScenario.get(ClassImplementingD.class);
Set<JavaMethodCall> calls = classCallingDiamond.getMethodCallsFromSelf();
assertThat(calls).hasSize(2);
JavaCodeUnit callInterface = classCallingDiamond
.getCodeUnitWithParameterTypes(ClassCallingDiamond.callInterface);
JavaMethodCall callToInterface = getOnlyByCaller(calls, callInterface);
assertThatCall(callToInterface)
.isFrom(callInterface)
.inLineNumber(ClassCallingDiamond.callInterfaceLineNumber);
// NOTE: There is no java.lang.reflect.Method InterfaceD.implementMe(), because the method is inherited
assertThat(callToInterface.getTarget().getName()).isEqualTo(InterfaceD.implementMe);
assertThat(callToInterface.getTarget().getOwner()).isEqualTo(diamondPeakInterface);
assertThat(callToInterface.getTarget().getParameters()).isEmpty();
assertThat(callToInterface.getTarget().resolve()).extracting("fullName")
.containsOnly(
diamondLeftInterface.getMethod(InterfaceB.implementMe).getFullName(),
diamondRightInterface.getMethod(InterfaceB.implementMe).getFullName());
JavaCodeUnit callImplementation = classCallingDiamond
.getCodeUnitWithParameterTypes(ClassCallingDiamond.callImplementation);
assertThatCall(getOnlyByCaller(calls, callImplementation))
.isFrom(callImplementation)
.isTo(diamondPeakClass.getMethod(InterfaceD.implementMe))
.inLineNumber(ClassCallingDiamond.callImplementationLineNumber);
}
@Test
public void imports_method_calls_that_return_Arrays() throws Exception {
JavaClass classThatCallsMethodReturningArray = classesIn("testexamples/callimport").get(CallsMethodReturningArray.class);
MethodCallTarget target = getOnlyElement(classThatCallsMethodReturningArray.getMethodCallsFromSelf()).getTarget();
assertThat(target.getOwner()).matches(SomeEnum.class);
assertThat(target.getReturnType()).matches(SomeEnum[].class);
}
@Test
public void dependency_target_classes_are_derived_correctly() throws Exception {
ImportedClasses classes = classesIn("testexamples/integration");
JavaClass javaClass = classes.get(ClassXDependingOnClassesABCD.class);
Set<JavaClass> expectedTargetClasses = ImmutableSet.of(
classes.get(ClassA.class),
classes.get(ClassBDependingOnClassA.class),
classes.get(ClassCDependingOnClassB_SuperClassOfX.class),
classes.get(ClassD.class),
classes.get(InterfaceOfClassX.class)
);
Set<JavaClass> targetClasses = new HashSet<>();
for (Dependency dependency : javaClass.getDirectDependencies()) {
targetClasses.add(dependency.getTargetClass());
}
assertThat(targetClasses).isEqualTo(expectedTargetClasses);
}
@Test
public void getDirectDependencies_does_not_return_transitive_dependencies() throws Exception {
ImportedClasses classes = classesIn("testexamples/integration");
JavaClass javaClass = classes.get(ClassCDependingOnClassB_SuperClassOfX.class);
JavaClass expectedTargetClass = classes.get(ClassBDependingOnClassA.class);
Set<JavaClass> targetClasses = new HashSet<>();
for (Dependency dependency : javaClass.getDirectDependencies()) {
if (dependency.getTargetClass().getPackage().contains("testexamples")) {
targetClasses.add(dependency.getTargetClass());
}
}
assertThat(targetClasses).containsOnly(expectedTargetClass);
}
@Test
public void fields_know_their_accesses() throws Exception {
ImportedClasses classes = classesIn("testexamples/dependents");
JavaClass classHoldingDependencies = classes.get(ClassHoldingDependencies.class);
JavaClass firstClassWithDependency = classes.get(FirstClassWithDependency.class);
JavaClass secondClassWithDependency = classes.get(SecondClassWithDependency.class);
Set<JavaFieldAccess> accesses = classHoldingDependencies.getField("someInt").getAccessesToSelf();
Set<JavaFieldAccess> expected = ImmutableSet.<JavaFieldAccess>builder()
.addAll(getByName(classHoldingDependencies.getFieldAccessesFromSelf(), "someInt"))
.addAll(getByName(firstClassWithDependency.getFieldAccessesFromSelf(), "someInt"))
.addAll(getByName(secondClassWithDependency.getFieldAccessesFromSelf(), "someInt"))
.build();
assertThat(accesses).as("Field Accesses to someInt").isEqualTo(expected);
}
@Test
public void classes_know_the_field_accesses_to_them() throws Exception {
ImportedClasses classes = classesIn("testexamples/dependents");
JavaClass classHoldingDependencies = classes.get(ClassHoldingDependencies.class);
JavaClass firstClassWithDependency = classes.get(FirstClassWithDependency.class);
JavaClass secondClassWithDependency = classes.get(SecondClassWithDependency.class);
Set<JavaFieldAccess> accesses = classHoldingDependencies.getFieldAccessesToSelf();
Set<JavaFieldAccess> expected = ImmutableSet.<JavaFieldAccess>builder()
.addAll(classHoldingDependencies.getFieldAccessesFromSelf())
.addAll(firstClassWithDependency.getFieldAccessesFromSelf())
.addAll(secondClassWithDependency.getFieldAccessesFromSelf())
.build();
assertThat(accesses).as("Field Accesses to class").isEqualTo(expected);
}
@Test
public void methods_know_callers() throws Exception {
ImportedClasses classes = classesIn("testexamples/dependents");
JavaClass classHoldingDependencies = classes.get(ClassHoldingDependencies.class);
JavaClass firstClassWithDependency = classes.get(FirstClassWithDependency.class);
JavaClass secondClassWithDependency = classes.get(SecondClassWithDependency.class);
Set<JavaMethodCall> calls = classHoldingDependencies.getMethod("setSomeInt", int.class).getCallsOfSelf();
Set<JavaMethodCall> expected = ImmutableSet.<JavaMethodCall>builder()
.addAll(getByName(classHoldingDependencies.getMethodCallsFromSelf(), "setSomeInt"))
.addAll(getByName(firstClassWithDependency.getMethodCallsFromSelf(), "setSomeInt"))
.addAll(getByName(secondClassWithDependency.getMethodCallsFromSelf(), "setSomeInt"))
.build();
assertThat(calls).as("Method calls to setSomeInt").isEqualTo(expected);
}
@Test
public void classes_know_method_calls_to_themselves() throws Exception {
ImportedClasses classes = classesIn("testexamples/dependents");
JavaClass classHoldingDependencies = classes.get(ClassHoldingDependencies.class);
JavaClass firstClassWithDependency = classes.get(FirstClassWithDependency.class);
JavaClass secondClassWithDependency = classes.get(SecondClassWithDependency.class);
Set<JavaMethodCall> calls = classHoldingDependencies.getMethodCallsToSelf();
Set<JavaMethodCall> expected = ImmutableSet.<JavaMethodCall>builder()
.addAll(classHoldingDependencies.getMethodCallsFromSelf())
.addAll(getByTargetOwner(firstClassWithDependency.getMethodCallsFromSelf(), classHoldingDependencies))
.addAll(getByTargetOwner(secondClassWithDependency.getMethodCallsFromSelf(), classHoldingDependencies))
.build();
assertThat(calls).as("Method calls to class").isEqualTo(expected);
}
@Test
public void constructors_know_callers() throws Exception {
ImportedClasses classes = classesIn("testexamples/dependents");
JavaClass classHoldingDependencies = classes.get(ClassHoldingDependencies.class);
JavaClass firstClassWithDependency = classes.get(FirstClassWithDependency.class);
JavaClass secondClassWithDependency = classes.get(SecondClassWithDependency.class);
JavaConstructor targetConstructur = classHoldingDependencies.getConstructor();
Set<JavaConstructorCall> calls = targetConstructur.getCallsOfSelf();
Set<JavaConstructorCall> expected = ImmutableSet.<JavaConstructorCall>builder()
.addAll(getByTarget(classHoldingDependencies.getConstructorCallsFromSelf(), targetConstructur))
.addAll(getByTarget(firstClassWithDependency.getConstructorCallsFromSelf(), targetConstructur))
.addAll(getByTarget(secondClassWithDependency.getConstructorCallsFromSelf(), targetConstructur))
.build();
assertThat(calls).as("Default Constructor calls to ClassWithDependents").isEqualTo(expected);
}
@Test
public void classes_know_constructor_calls_to_themselves() throws Exception {
ImportedClasses classes = classesIn("testexamples/dependents");
JavaClass classHoldingDependencies = classes.get(ClassHoldingDependencies.class);
JavaClass firstClassWithDependency = classes.get(FirstClassWithDependency.class);
JavaClass secondClassWithDependency = classes.get(SecondClassWithDependency.class);
Set<JavaConstructorCall> calls = classHoldingDependencies.getConstructorCallsToSelf();
Set<JavaConstructorCall> expected = ImmutableSet.<JavaConstructorCall>builder()
.addAll(getByTargetOwner(classHoldingDependencies.getConstructorCallsFromSelf(), classHoldingDependencies))
.addAll(getByTargetOwner(firstClassWithDependency.getConstructorCallsFromSelf(), classHoldingDependencies))
.addAll(getByTargetOwner(secondClassWithDependency.getConstructorCallsFromSelf(), classHoldingDependencies))
.build();
assertThat(calls).as("Constructor calls to ClassWithDependents").isEqualTo(expected);
}
@Test
public void classes_know_accesses_to_themselves() throws Exception {
ImportedClasses classes = classesIn("testexamples/dependents");
JavaClass classHoldingDependencies = classes.get(ClassHoldingDependencies.class);
JavaClass firstClassWithDependency = classes.get(FirstClassWithDependency.class);
JavaClass secondClassWithDependency = classes.get(SecondClassWithDependency.class);
Set<JavaAccess<?>> accesses = classHoldingDependencies.getAccessesToSelf();
Set<JavaAccess<?>> expected = ImmutableSet.<JavaAccess<?>>builder()
.addAll(getByTargetOwner(classHoldingDependencies.getAccessesFromSelf(), classHoldingDependencies))
.addAll(getByTargetOwner(firstClassWithDependency.getAccessesFromSelf(), classHoldingDependencies))
.addAll(getByTargetOwner(secondClassWithDependency.getAccessesFromSelf(), classHoldingDependencies))
.build();
assertThat(accesses).as("Accesses to ClassWithDependents").isEqualTo(expected);
}
@Test
public void inherited_field_accesses_and_method_calls_are_resolved() throws Exception {
ImportedClasses classes = classesIn("testexamples/dependents");
JavaClass classHoldingDependencies = classes.get(ParentClassHoldingDependencies.class);
JavaClass subClassHoldingDependencies = classes.get(SubClassHoldingDependencies.class);
JavaClass dependentClass = classes.get(ClassDependingOnParentThroughChild.class);
Set<JavaFieldAccess> fieldAccessesToSelf = classHoldingDependencies.getFieldAccessesToSelf();
Set<JavaFieldAccess> expectedFieldAccesses =
getByTargetNot(dependentClass.getFieldAccessesFromSelf(), dependentClass);
assertThat(fieldAccessesToSelf).as("Field accesses to class").isEqualTo(expectedFieldAccesses);
Set<JavaMethodCall> methodCalls = classHoldingDependencies.getMethodCallsToSelf();
Set<JavaMethodCall> expectedMethodCalls =
getByTargetNot(dependentClass.getMethodCallsFromSelf(), dependentClass);
assertThat(methodCalls).as("Method calls to class").isEqualTo(expectedMethodCalls);
// NOTE: For constructors it's impossible to be accessed via a subclass,
// since the byte code always holds an explicitly declared constructor
Set<JavaConstructorCall> constructorCalls = classHoldingDependencies.getConstructorCallsToSelf();
Set<JavaConstructorCall> expectedConstructorCalls =
getByTargetOwner(subClassHoldingDependencies.getConstructorCallsFromSelf(), classHoldingDependencies.getName());
assertThat(constructorCalls).as("Constructor calls to class").isEqualTo(expectedConstructorCalls);
constructorCalls = subClassHoldingDependencies.getConstructorCallsToSelf();
expectedConstructorCalls =
getByTargetOwner(dependentClass.getConstructorCallsFromSelf(), subClassHoldingDependencies.getName());
assertThat(constructorCalls).as("Constructor calls to class").isEqualTo(expectedConstructorCalls);
}
@Test
public void reflect_works() throws Exception {
ImportedClasses classes = classesIn("testexamples/innerclassimport");
JavaClass calledClass = classes.get(CalledClass.class);
assertThat(calledClass.reflect()).isEqualTo(CalledClass.class);
assertThat(calledClass.getField("someString").reflect()).isEqualTo(field(CalledClass.class, "someString"));
assertThat(calledClass.getConstructor().reflect()).isEqualTo(constructor(CalledClass.class));
assertThat(calledClass.getConstructor(String.class).reflect()).isEqualTo(constructor(CalledClass.class, String.class));
assertThat(calledClass.getCodeUnitWithParameterTypes(CONSTRUCTOR_NAME, String.class).reflect())
.isEqualTo(constructor(CalledClass.class, String.class));
JavaClass innerClass = classes.get(ClassWithInnerClass.Inner.class);
assertThat(innerClass.reflect()).isEqualTo(ClassWithInnerClass.Inner.class);
assertThat(innerClass.getMethod("call").reflect())
.isEqualTo(method(ClassWithInnerClass.Inner.class, "call"));
}
@Test
public void imports_urls_of_files() {
Set<URL> urls = newHashSet(urlOf(ClassToImportOne.class), urlOf(ClassWithNestedClass.class));
Set<JavaClass> classesFoundAtUrls = new HashSet<>();
for (JavaClass javaClass : new ClassFileImporter().importUrls(urls)) {
if (!Object.class.getName().equals(javaClass.getName())) {
classesFoundAtUrls.add(javaClass);
}
}
assertThat(classesFoundAtUrls).as("Number of classes at the given URLs").hasSize(2);
}
@Test
public void imports_urls_of_jars() throws IOException {
Set<URL> urls = newHashSet(urlOf(Test.class), urlOf(RunWith.class));
assumeTrue("We can't completely ensure, that this will always be taken from a JAR file, though it's very likely",
"jar".equals(urls.iterator().next().getProtocol()));
JavaClasses classes = new ClassFileImporter().importUrls(urls)
.that(DescribedPredicate.not(type(Annotation.class))); // NOTE @Test and @RunWith implement Annotation.class
assertThat(classes).as("Number of classes at the given URLs").hasSize(2);
}
@Test
public void imports_classes_outside_of_the_classpath() throws IOException {
Path targetDir = outsideOfClassPath
.onlyKeep(not(containsPattern("^Missing.*")))
.setUp(getClass().getResource("testexamples/outsideofclasspath"));
JavaClasses classes = new ClassFileImporter().importPath(targetDir);
assertThat(classes).hasSize(5);
assertThat(classes).extracting("name").containsOnly(
"com.tngtech.archunit.core.importer.testexamples.outsideofclasspath.ChildClass",
"com.tngtech.archunit.core.importer.testexamples.outsideofclasspath.MiddleClass",
"com.tngtech.archunit.core.importer.testexamples.outsideofclasspath.ExistingDependency",
"com.tngtech.archunit.core.importer.testexamples.outsideofclasspath.ChildClass$MySeed",
"com.tngtech.archunit.core.importer.testexamples.outsideofclasspath.ExistingDependency$GimmeADescription"
);
JavaClass middleClass = findAnyByName(classes,
"com.tngtech.archunit.core.importer.testexamples.outsideofclasspath.MiddleClass");
assertThat(middleClass.getSimpleName()).as("simple name").isEqualTo("MiddleClass");
assertThat(middleClass.isInterface()).as("is interface").isFalse();
assertThatCall(findAnyByName(middleClass.getMethodCallsFromSelf(), "println"))
.isFrom(middleClass.getMethod("overrideMe"))
.isTo(targetWithFullName(String.format("%s.println(%s)", PrintStream.class.getName(), String.class.getName())))
.inLineNumber(12);
assertThatCall(findAnyByName(middleClass.getMethodCallsFromSelf(), "getSomeString"))
.isFrom(middleClass.getMethod("overrideMe"))
.isTo(targetWithFullName(
"com.tngtech.archunit.core.importer.testexamples.outsideofclasspath.MissingDependency.getSomeString()"))
.inLineNumber(12);
JavaClass gimmeADescription = findAnyByName(classes,
"com.tngtech.archunit.core.importer.testexamples.outsideofclasspath.ExistingDependency$GimmeADescription");
assertThat(gimmeADescription.getSimpleName()).as("simple name").isEqualTo("GimmeADescription");
assertThat(gimmeADescription.isInterface()).as("is interface").isTrue();
}
@Test
public void resolve_missing_dependencies_from_classpath_can_be_toogled() throws Exception {
ArchConfiguration.get().unsetClassResolver();
ArchConfiguration.get().setResolveMissingDependenciesFromClassPath(true);
JavaClass clazz = classesIn("testexamples/simpleimport").get(ClassToImportOne.class);
assertThat(clazz.getSuperClass().get().getMethods()).isNotEmpty();
ArchConfiguration.get().setResolveMissingDependenciesFromClassPath(false);
clazz = classesIn("testexamples/simpleimport").get(ClassToImportOne.class);
assertThat(clazz.getSuperClass().get().getMethods()).isEmpty();
}
@Test
public void class_has_source_of_import() throws Exception {
ArchConfiguration.get().setMd5InClassSourcesEnabled(true);
JavaClass clazzFromFile = new ClassFileImporter().importClass(ClassToImportOne.class);
Source source = clazzFromFile.getSource().get();
assertThat(source.getUri()).isEqualTo(urlOf(ClassToImportOne.class).toURI());
assertThat(source.getMd5sum()).isEqualTo(md5sumOf(bytesAt(urlOf(ClassToImportOne.class))));
JavaClass clazzFromJar = new ClassFileImporter().importClass(Rule.class);
source = clazzFromJar.getSource().get();
assertThat(source.getUri()).isEqualTo(urlOf(Rule.class).toURI());
assertThat(source.getMd5sum()).isEqualTo(md5sumOf(bytesAt(urlOf(Rule.class))));
ArchConfiguration.get().setMd5InClassSourcesEnabled(false);
source = new ClassFileImporter().importClass(ClassToImportOne.class).getSource().get();
assertThat(source.getMd5sum()).isEqualTo(MD5_SUM_DISABLED);
}
@Test
public void imports_class_objects() throws Exception {
JavaClasses classes = new ClassFileImporter().importClasses(ClassToImportOne.class, ClassToImportTwo.class);
assertThatClasses(classes).matchInAnyOrder(ClassToImportOne.class, ClassToImportTwo.class);
}
@Test
public void imports_packages() {
Set<Class<?>> expectedClasses = ImmutableSet.of(getClass(), Rule.class);
Set<String> packages = packagesOf(expectedClasses);
JavaClasses classes = new ClassFileImporter().importPackages(packages);
assertThatClasses(classes).contain(expectedClasses);
}
@Test
public void imports_jars() throws Exception {
JavaClasses classes = new ClassFileImporter().importJar(jarFileOf(Rule.class));
assertThatClasses(classes).contain(Rule.class);
assertThatClasses(classes).dontContain(Object.class, ImmutableList.class);
classes = new ClassFileImporter().importJars(jarFileOf(Rule.class), jarFileOf(ImmutableList.class));
assertThatClasses(classes).contain(Rule.class, ImmutableList.class);
assertThatClasses(classes).dontContain(Object.class);
classes = new ClassFileImporter().importJars(ImmutableList.of(
jarFileOf(Rule.class), jarFileOf(ImmutableList.class)));
assertThatClasses(classes).contain(Rule.class, ImmutableList.class);
assertThatClasses(classes).dontContain(Object.class);
}
@Test
public void ImportOptions_are_respected() throws Exception {
ClassFileImporter importer = new ClassFileImporter().withImportOption(importNothing());
assertThat(importer.importPath(Paths.get(urlOf(getClass()).toURI()))).isEmpty();
assertThat(importer.importUrl(urlOf(getClass()))).isEmpty();
assertThat(importer.importJar(jarFileOf(Rule.class))).isEmpty();
}
private Set<String> packagesOf(Set<Class<?>> classes) {
Set<String> result = new HashSet<>();
for (Class<?> c : classes) {
result.add(c.getPackage().getName());
}
return result;
}
private JarFile jarFileOf(Class<?> clazzInJar) throws IOException {
String fileName = urlOf(clazzInJar).getFile();
checkArgument(fileName.contains(".jar!/"), "Class %s is not contained in a JAR", clazzInJar.getName());
return new JarFile(fileName.replaceAll(".*:", "").replaceAll("!/.*", ""));
}
private ImportOption importNothing() {
return new ImportOption() {
@Override
public boolean includes(Location location) {
return false;
}
};
}
private Condition<MethodCallTarget> targetWithFullName(final String name) {
return new Condition<MethodCallTarget>(String.format("target with name '%s'", name)) {
@Override
public boolean matches(MethodCallTarget value) {
return value.getFullName().equals(name);
}
};
}
private Constructor<?> reflect(ConstructorCallTarget target) {
return reflect(target.resolveConstructor().get());
}
private Constructor<?> reflect(JavaConstructor javaConstructor) {
try {
return javaConstructor.getOwner().reflect().getConstructor(asClasses(javaConstructor.getParameters()));
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
private Method reflect(MethodCallTarget target) {
return reflect(getOnlyElement(target.resolve()));
}
private Method reflect(JavaMethod javaMethod) {
try {
return javaMethod.getOwner().reflect().getMethod(javaMethod.getName(), asClasses(javaMethod.getParameters()));
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
private JavaClassList targetParametersOf(Set<JavaMethodCall> calls, String name) {
return findAnyByName(calls, name).getTarget().getParameters();
}
private JavaClass returnTypeOf(Set<JavaMethodCall> calls, String name) {
return findAnyByName(calls, name).getTarget().getReturnType();
}
private JavaFieldAccess getOnly(Set<JavaFieldAccess> fieldAccesses, String name, AccessType accessType) {
return getOnlyElement(getByNameAndAccessType(fieldAccesses, name, accessType));
}
private Set<JavaFieldAccess> getByNameAndAccessType(Set<JavaFieldAccess> fieldAccesses, String name, AccessType accessType) {
Set<JavaFieldAccess> result = new HashSet<>();
for (JavaFieldAccess access : fieldAccesses) {
if (name.equals(access.getName()) && access.getAccessType() == accessType) {
result.add(access);
}
}
return result;
}
private <T extends HasOwner<JavaCodeUnit>> T getOnlyByCaller(Set<T> calls, JavaCodeUnit caller) {
return getOnlyElement(getByCaller(calls, caller));
}
private <T extends JavaAccess<?>> Set<T> getByTarget(Set<T> calls, final JavaConstructor target) {
return getBy(calls, new Predicate<JavaAccess<?>>() {
@Override
public boolean apply(JavaAccess<?> input) {
return targetFrom(target).getFullName().equals(input.getTarget().getFullName());
}
});
}
private <T extends JavaAccess<?>> Set<T> getByTargetNot(Set<T> accesses, JavaClass target) {
return getBy(accesses, not(targetOwnerNameEquals(target.getName())));
}
private <T extends JavaAccess<?>> Set<T> getByTargetOwner(Set<T> calls, Class<?> targetOwner) {
return getByTargetOwner(calls, targetOwner.getName());
}
private <T extends JavaAccess<?>> Set<T> getByTargetOwner(Set<T> calls, final String targetOwnerName) {
return getBy(calls, targetOwnerNameEquals(targetOwnerName));
}
private Predicate<JavaAccess<?>> targetOwnerNameEquals(final String targetFqn) {
return new Predicate<JavaAccess<?>>() {
@Override
public boolean apply(JavaAccess<?> input) {
return targetFqn.equals(input.getTarget().getOwner().getName());
}
};
}
private <T extends JavaAccess<?>> Set<T> getByTargetOwner(Set<T> calls, final JavaClass targetOwner) {
return getBy(calls, new Predicate<T>() {
@Override
public boolean apply(T input) {
return targetOwner.equals(input.getTarget().getOwner());
}
});
}
private <T extends HasOwner<JavaCodeUnit>> Set<T> getByCaller(Set<T> calls, final JavaCodeUnit caller) {
return getBy(calls, new Predicate<T>() {
@Override
public boolean apply(T input) {
return caller.equals(input.getOwner());
}
});
}
private <T extends HasOwner<JavaCodeUnit>> Set<T> getBy(Set<T> calls, Predicate<? super T> predicate) {
return FluentIterable.from(calls).filter(predicate).toSet();
}
private Set<FieldAccessTarget> targetsOf(Set<JavaFieldAccess> fieldAccesses) {
Set<FieldAccessTarget> result = new HashSet<>();
for (JavaFieldAccess access : fieldAccesses) {
result.add(access.getTarget());
}
return result;
}
private Set<Integer> lineNumbersOf(Set<JavaFieldAccess> fieldAccesses) {
Set<Integer> result = new HashSet<>();
for (JavaFieldAccess access : fieldAccesses) {
result.add(access.getLineNumber());
}
return result;
}
private Set<String> namesOf(Iterable<? extends HasName> thingsWithNames) {
Set<String> result = new HashSet<>();
for (HasName hasName : thingsWithNames) {
result.add(hasName.getName());
}
return result;
}
private <T extends HasName> Set<T> getByName(Iterable<T> thingsWithName, String name) {
Set<T> result = new HashSet<>();
for (T hasName : thingsWithName) {
if (name.equals(hasName.getName())) {
result.add(hasName);
}
}
return result;
}
private <T extends HasName> T findAnyByName(Iterable<T> thingsWithName, String name) {
T result = getFirst(getByName(thingsWithName, name), null);
return checkNotNull(result, "No object with name '" + name + "' is present in " + thingsWithName);
}
private ImportedClasses classesIn(String path) throws Exception {
return new ImportedClasses(path);
}
private class ImportedClasses implements Iterable<JavaClass> {
private final ClassFileImporter importer = new ClassFileImporter();
private final JavaClasses classes;
private ImportedClasses(String path) throws Exception {
classes = importer.importPath(Paths.get(ClassFileImporterTest.this.getClass().getResource(path).toURI()));
}
JavaClass get(Class<?> clazz) {
return get(clazz.getName());
}
private JavaClass get(String className) {
return findAnyByName(classes, className);
}
@Override
public Iterator<JavaClass> iterator() {
return classes.iterator();
}
Set<JavaCodeUnit> getCodeUnits() {
Set<JavaCodeUnit> codeUnits = new HashSet<>();
for (JavaClass clazz : classes) {
codeUnits.addAll(clazz.getCodeUnits());
}
return codeUnits;
}
Set<JavaMethod> getMethods() {
Set<JavaMethod> methods = new HashSet<>();
for (JavaClass clazz : classes) {
methods.addAll(clazz.getMethods());
}
return methods;
}
Set<JavaField> getFields() {
Set<JavaField> fields = new HashSet<>();
for (JavaClass clazz : classes) {
fields.addAll(clazz.getFields());
}
return fields;
}
}
private static AccessToFieldAssertion assertThatAccess(JavaFieldAccess access) {
return new AccessToFieldAssertion(access);
}
private static MethodCallAssertion assertThatCall(JavaMethodCall call) {
return new MethodCallAssertion(call);
}
private static ConstructorCallAssertion assertThatCall(JavaConstructorCall call) {
return new ConstructorCallAssertion(call);
}
protected abstract static class BaseAccessAssertion<
SELF extends BaseAccessAssertion<SELF, ACCESS, TARGET>,
ACCESS extends JavaAccess<TARGET>,
TARGET extends AccessTarget> {
ACCESS access;
BaseAccessAssertion(ACCESS access) {
this.access = access;
}
SELF isFrom(String name, Class<?>... parameterTypes) {
return isFrom(access.getOrigin().getOwner().getCodeUnitWithParameterTypes(name, parameterTypes));
}
SELF isFrom(JavaCodeUnit codeUnit) {
assertThat(access.getOrigin()).as("Origin of field access").isEqualTo(codeUnit);
return newAssertion(access);
}
SELF isTo(TARGET target) {
assertThat(access.getTarget()).as("Target of " + access.getName()).isEqualTo(target);
return newAssertion(access);
}
SELF isTo(Condition<TARGET> target) {
assertThat(access.getTarget()).as("Target of " + access.getName()).is(target);
return newAssertion(access);
}
void inLineNumber(int number) {
assertThat(access.getLineNumber())
.as("Line number of access to " + access.getName())
.isEqualTo(number);
}
protected abstract SELF newAssertion(ACCESS access);
}
private static class AccessToFieldAssertion extends BaseAccessAssertion<AccessToFieldAssertion, JavaFieldAccess, FieldAccessTarget> {
private AccessToFieldAssertion(JavaFieldAccess access) {
super(access);
}
@Override
protected AccessToFieldAssertion newAssertion(JavaFieldAccess access) {
return new AccessToFieldAssertion(access);
}
private AccessToFieldAssertion isTo(String name) {
return isTo(access.getOrigin().getOwner().getField(name));
}
private AccessToFieldAssertion isTo(JavaField field) {
return isTo(targetFrom(field));
}
private AccessToFieldAssertion isOfType(AccessType type) {
assertThat(access.getAccessType()).isEqualTo(type);
return newAssertion(access);
}
}
private static class MethodCallAssertion extends BaseAccessAssertion<MethodCallAssertion, JavaMethodCall, MethodCallTarget> {
private MethodCallAssertion(JavaMethodCall call) {
super(call);
}
MethodCallAssertion isTo(JavaMethod target) {
return isTo(resolvedTargetFrom(target));
}
@Override
protected MethodCallAssertion newAssertion(JavaMethodCall call) {
return new MethodCallAssertion(call);
}
}
private static class ConstructorCallAssertion extends BaseAccessAssertion<ConstructorCallAssertion, JavaConstructorCall, ConstructorCallTarget> {
private ConstructorCallAssertion(JavaConstructorCall call) {
super(call);
}
ConstructorCallAssertion isTo(JavaConstructor target) {
return isTo(targetFrom(target));
}
@Override
protected ConstructorCallAssertion newAssertion(JavaConstructorCall call) {
return new ConstructorCallAssertion(call);
}
}
}