Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Expand test coverage for compatibility changes related to annotations #396

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 106 additions & 2 deletions japicmp/src/test/java/japicmp/compat/CompatibilityChangesTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -2615,18 +2615,22 @@ public void testAnnotationOnClassModified() throws Exception {
public List<CtClass> createOldClasses(ClassPool classPool) throws Exception {
CtClass anAnnotation = CtAnnotationBuilder.create().name("japicmp.MyAnnotation").addToClassPool(classPool);
CtMethodBuilder.create().name("foo").returnType(CtClass.intType).publicAccess().addToClass(anAnnotation);
return Collections.singletonList(anAnnotation);
CtClass aClass = CtClassBuilder.create().name("japicmp.Test").withAnnotation("japicmp.MyAnnotation", new CtElement("foo", 1000)).addToClassPool(classPool);
return Arrays.asList(aClass, anAnnotation);
}

@Override
public List<CtClass> createNewClasses(ClassPool classPool) {
CtClass anAnnotation = CtAnnotationBuilder.create().name("japicmp.MyAnnotation").addToClassPool(classPool);
return Arrays.asList(anAnnotation);
CtClass aClass = CtClassBuilder.create().name("japicmp.Test").withAnnotation("japicmp.MyAnnotation", new CtElement("foo", 999)).addToClassPool(classPool);
return Arrays.asList(aClass, anAnnotation);
}
});
JApiClass jApiClass = getJApiClass(jApiClasses, "japicmp.MyAnnotation");
JApiMethod jApiMethod = getJApiMethod(jApiClass.getMethods(), "foo");
assertThat(jApiMethod.getCompatibilityChanges(), hasItem(new JApiCompatibilityChange(JApiCompatibilityChangeType.METHOD_REMOVED)));
JApiClass jApiClassWithAnnotation = getJApiClass(jApiClasses, "japicmp.Test");
assertThat(jApiClassWithAnnotation.getCompatibilityChanges(), hasItem(new JApiCompatibilityChange(JApiCompatibilityChangeType.ANNOTATION_MODIFIED)));
}

@Test
Expand Down Expand Up @@ -2676,4 +2680,104 @@ public List<CtClass> createNewClasses(ClassPool classPool) throws Exception {
JApiMethod jApiMethod = getJApiMethod(jApiClass.getMethods(), "method");
assertThat(jApiMethod.getCompatibilityChanges(), hasItem(new JApiCompatibilityChange(JApiCompatibilityChangeType.ANNOTATION_REMOVED)));
}

@Test
public void testAnnotationOnMethodModified() throws Exception {
JarArchiveComparatorOptions options = new JarArchiveComparatorOptions();
List<JApiClass> jApiClasses = ClassesHelper.compareClasses(options, new ClassesHelper.ClassesGenerator() {
@Override
public List<CtClass> createOldClasses(ClassPool classPool) throws Exception {
CtClass anAnnotation = CtAnnotationBuilder.create().name("japicmp.MyAnnotation").addToClassPool(classPool);
CtMethodBuilder.create().name("foo").returnType(CtClass.intType).publicAccess().addToClass(anAnnotation);
CtClass aClass = CtClassBuilder.create().name("japicmp.Test").addToClassPool(classPool);
CtMethodBuilder.create().publicAccess().name("method").withAnnotation("japicmp.MyAnnotation", new CtElement("foo", 1000)).addToClass(aClass);
return Arrays.asList(aClass, anAnnotation);
}

@Override
public List<CtClass> createNewClasses(ClassPool classPool) throws Exception {
CtClass anAnnotation = CtAnnotationBuilder.create().name("japicmp.MyAnnotation").addToClassPool(classPool);
CtClass aClass = CtClassBuilder.create().name("japicmp.Test").addToClassPool(classPool);
CtMethodBuilder.create().publicAccess().name("method").withAnnotation("japicmp.MyAnnotation", new CtElement("foo", 999)).addToClass(aClass);
return Arrays.asList(aClass, anAnnotation);
}
});
JApiClass jApiClass = getJApiClass(jApiClasses, "japicmp.Test");
JApiMethod jApiMethod = getJApiMethod(jApiClass.getMethods(), "method");
assertThat(jApiMethod.getCompatibilityChanges(), hasItem(new JApiCompatibilityChange(JApiCompatibilityChangeType.ANNOTATION_MODIFIED)));
}

@Test
public void testAnnotationAddedToField() throws Exception {
JarArchiveComparatorOptions options = new JarArchiveComparatorOptions();
List<JApiClass> jApiClasses = ClassesHelper.compareClasses(options, new ClassesHelper.ClassesGenerator() {
@Override
public List<CtClass> createOldClasses(ClassPool classPool) throws Exception {
CtClass aClass = CtClassBuilder.create().name("japicmp.Test").addToClassPool(classPool);
CtFieldBuilder.create().name("field").addToClass(aClass);
return Collections.singletonList(aClass);
}

@Override
public List<CtClass> createNewClasses(ClassPool classPool) throws Exception {
CtClass anAnnotation = CtAnnotationBuilder.create().name("japicmp.MyAnnotation").addToClassPool(classPool);
CtClass aClass = CtClassBuilder.create().name("japicmp.Test").addToClassPool(classPool);
CtFieldBuilder.create().withAnnotation("japicmp.MyAnnotation").name("field").addToClass(aClass);
return Arrays.asList(aClass, anAnnotation);
}
});
JApiClass jApiClass = getJApiClass(jApiClasses, "japicmp.Test");
JApiField jApiField = getJApiField(jApiClass.getFields(), "field");
assertThat(jApiField.getCompatibilityChanges(), hasItem(new JApiCompatibilityChange(JApiCompatibilityChangeType.ANNOTATION_ADDED)));
}

@Test
public void testAnnotationRemovedFromField() throws Exception {
JarArchiveComparatorOptions options = new JarArchiveComparatorOptions();
List<JApiClass> jApiClasses = ClassesHelper.compareClasses(options, new ClassesHelper.ClassesGenerator() {
@Override
public List<CtClass> createOldClasses(ClassPool classPool) throws Exception {
CtClass anAnnotation = CtAnnotationBuilder.create().name("japicmp.MyAnnotation").addToClassPool(classPool);
CtClass aClass = CtClassBuilder.create().name("japicmp.Test").addToClassPool(classPool);
CtFieldBuilder.create().name("field").withAnnotation("japicmp.MyAnnotation").addToClass(aClass);
return Arrays.asList(aClass, anAnnotation);
}

@Override
public List<CtClass> createNewClasses(ClassPool classPool) throws Exception {
CtClass aClass = CtClassBuilder.create().name("japicmp.Test").addToClassPool(classPool);
CtFieldBuilder.create().name("field").addToClass(aClass);
return Collections.singletonList(aClass);
}
});
JApiClass jApiClass = getJApiClass(jApiClasses, "japicmp.Test");
JApiField jApiField = getJApiField(jApiClass.getFields(), "field");
assertThat(jApiField.getCompatibilityChanges(), hasItem(new JApiCompatibilityChange(JApiCompatibilityChangeType.ANNOTATION_REMOVED)));
}

@Test
public void testAnnotationOnFieldModified() throws Exception {
JarArchiveComparatorOptions options = new JarArchiveComparatorOptions();
List<JApiClass> jApiClasses = ClassesHelper.compareClasses(options, new ClassesHelper.ClassesGenerator() {
@Override
public List<CtClass> createOldClasses(ClassPool classPool) throws Exception {
CtClass anAnnotation = CtAnnotationBuilder.create().name("japicmp.MyAnnotation").addToClassPool(classPool);
CtMethodBuilder.create().name("foo").returnType(CtClass.intType).publicAccess().addToClass(anAnnotation);
CtClass aClass = CtClassBuilder.create().name("japicmp.Test").addToClassPool(classPool);
CtFieldBuilder.create().name("field").withAnnotation("japicmp.MyAnnotation", new CtElement("foo", 1000)).addToClass(aClass);
return Arrays.asList(aClass, anAnnotation);
}

@Override
public List<CtClass> createNewClasses(ClassPool classPool) throws Exception {
CtClass anAnnotation = CtAnnotationBuilder.create().name("japicmp.MyAnnotation").addToClassPool(classPool);
CtClass aClass = CtClassBuilder.create().name("japicmp.Test").addToClassPool(classPool);
CtFieldBuilder.create().name("field").withAnnotation("japicmp.MyAnnotation", new CtElement("foo", 999)).addToClass(aClass);
return Arrays.asList(aClass, anAnnotation);
}
});
JApiClass jApiClass = getJApiClass(jApiClasses, "japicmp.Test");
JApiField jApiField = getJApiField(jApiClass.getFields(), "field");
assertThat(jApiField.getCompatibilityChanges(), hasItem(new JApiCompatibilityChange(JApiCompatibilityChangeType.ANNOTATION_MODIFIED)));
}
}
13 changes: 9 additions & 4 deletions japicmp/src/test/java/japicmp/util/CtClassBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@
import javassist.bytecode.annotation.Annotation;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class CtClassBuilder {
public static final String DEFAULT_CLASS_NAME = "japicmp.Test";
private String name = DEFAULT_CLASS_NAME;
private int modifier = Modifier.PUBLIC;
private final List<String> annotations = new ArrayList<>();
private final Map<String, CtElement[]> annotations = new HashMap<>();
private Optional<CtClass> superclass = Optional.absent();
private final List<CtClass> interfaces = new ArrayList<>();

Expand All @@ -30,8 +32,8 @@ public CtClassBuilder syntheticModifier() {
return this;
}

public CtClassBuilder withAnnotation(String annotation) {
this.annotations.add(annotation);
public CtClassBuilder withAnnotation(String annotation, CtElement... elements) {
this.annotations.put(annotation, elements);
return this;
}

Expand Down Expand Up @@ -69,11 +71,14 @@ public CtClass addToClassPool(ClassPool classPool) {
ctClass = classPool.makeClass(this.name);
}
ctClass.setModifiers(this.modifier);
for (String annotation : annotations) {
for (String annotation : annotations.keySet()) {
ClassFile classFile = ctClass.getClassFile();
ConstPool constPool = classFile.getConstPool();
AnnotationsAttribute attr = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
Annotation annot = new Annotation(annotation, constPool);
for (CtElement element : annotations.get(annotation)) {
annot.addMemberValue(element.name, element.value.apply(constPool));
}
attr.setAnnotation(annot);
ctClass.getClassFile2().addAttribute(attr);
}
Expand Down
66 changes: 66 additions & 0 deletions japicmp/src/test/java/japicmp/util/CtElement.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package japicmp.util;

import java.util.function.Function;
import javassist.bytecode.ConstPool;
import javassist.bytecode.annotation.BooleanMemberValue;
import javassist.bytecode.annotation.ByteMemberValue;
import javassist.bytecode.annotation.CharMemberValue;
import javassist.bytecode.annotation.ClassMemberValue;
import javassist.bytecode.annotation.DoubleMemberValue;
import javassist.bytecode.annotation.FloatMemberValue;
import javassist.bytecode.annotation.IntegerMemberValue;
import javassist.bytecode.annotation.LongMemberValue;
import javassist.bytecode.annotation.MemberValue;
import javassist.bytecode.annotation.ShortMemberValue;
import javassist.bytecode.annotation.StringMemberValue;

public class CtElement {

public final String name;
public final Function<ConstPool, MemberValue> value;

private CtElement(String name, Function<ConstPool, MemberValue> value) {
this.name = name;
this.value = value;
}

public CtElement(String name, boolean value) {
this(name, cp -> new BooleanMemberValue(value, cp));
}

public CtElement(String name, byte value) {
this(name, cp -> new ByteMemberValue(value, cp));
}

public CtElement(String name, char value) {
this(name, cp -> new CharMemberValue(value, cp));
}

public CtElement(String name, short value) {
this(name, cp -> new ShortMemberValue(value, cp));
}

public CtElement(String name, int value) {
this(name, cp -> new IntegerMemberValue(cp, value));
}

public CtElement(String name, long value) {
this(name, cp -> new LongMemberValue(value, cp));
}

public CtElement(String name, float value) {
this(name, cp -> new FloatMemberValue(value, cp));
}

public CtElement(String name, double value) {
this(name, cp -> new DoubleMemberValue(value, cp));
}

public CtElement(String name, Class<?> value) {
this(name, cp -> new ClassMemberValue(value.getName(), cp));
}

public CtElement(String name, String value) {
this(name, cp -> new StringMemberValue(value, cp));
}
}
15 changes: 9 additions & 6 deletions japicmp/src/test/java/japicmp/util/CtFieldBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@
import javassist.bytecode.ConstPool;
import javassist.bytecode.annotation.Annotation;

import java.util.ArrayList;
import java.util.List;
import java.util.HashMap;
import java.util.Map;

public class CtFieldBuilder {
public static final String DEFAULT_FIELD_NAME = "field";
private CtClass type = CtClass.intType;
private String name = DEFAULT_FIELD_NAME;
private int modifier = Modifier.PUBLIC;
private final List<String> annotations = new ArrayList<>();
private final Map<String, CtElement[]> annotations = new HashMap<>();
private Object constantValue = null;

public CtFieldBuilder type(CtClass ctClass) {
Expand Down Expand Up @@ -53,11 +53,14 @@ public CtField addToClass(CtClass ctClass) throws CannotCompileException {
} else {
ctClass.addField(ctField);
}
for (String annotation : annotations) {
for (String annotation : annotations.keySet()) {
ClassFile classFile = ctClass.getClassFile();
ConstPool constPool = classFile.getConstPool();
AnnotationsAttribute attr = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
Annotation annot = new Annotation(annotation, constPool);
for (CtElement element : annotations.get(annotation)) {
annot.addMemberValue(element.name, element.value.apply(constPool));
}
attr.setAnnotation(annot);
ctField.getFieldInfo().addAttribute(attr);
}
Expand All @@ -68,8 +71,8 @@ public static CtFieldBuilder create() {
return new CtFieldBuilder();
}

public CtFieldBuilder withAnnotation(String annotation) {
this.annotations.add(annotation);
public CtFieldBuilder withAnnotation(String annotation, CtElement... elements) {
this.annotations.put(annotation, elements);
return this;
}

Expand Down
15 changes: 9 additions & 6 deletions japicmp/src/test/java/japicmp/util/CtMethodBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@
import javassist.bytecode.SignatureAttribute;
import javassist.bytecode.annotation.Annotation;

import java.util.ArrayList;
import java.util.List;
import java.util.HashMap;
import java.util.Map;

public class CtMethodBuilder extends CtBehaviorBuilder {
private static final String DEFAULT_METHOD_NAME = "method";
private String body = "return null;";
private String name = DEFAULT_METHOD_NAME;
private CtClass returnType;
private final List<String> annotations = new ArrayList<>();
private final Map<String, CtElement[]> annotations = new HashMap<>();

public CtMethodBuilder name(String name) {
this.name = name;
Expand Down Expand Up @@ -80,8 +80,8 @@ public CtMethodBuilder finalMethod() {
return (CtMethodBuilder) super.finalMethod();
}

public CtMethodBuilder withAnnotation(String annotation) {
this.annotations.add(annotation);
public CtMethodBuilder withAnnotation(String annotation, CtElement... elements) {
this.annotations.put(annotation, elements);
return this;
}

Expand All @@ -97,10 +97,13 @@ public CtMethod addToClass(CtClass declaringClass) throws CannotCompileException
CtMethod ctMethod = CtNewMethod.make(this.modifier, this.returnType, this.name, this.parameters, this.exceptions, this.body, declaringClass);
ctMethod.setModifiers(this.modifier);
declaringClass.addMethod(ctMethod);
for (String annotation : annotations) {
for (String annotation : annotations.keySet()) {
ConstPool constPool = declaringClass.getClassFile().getConstPool();
AnnotationsAttribute attr = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
Annotation annot = new Annotation(annotation, constPool);
for (CtElement element : annotations.get(annotation)) {
annot.addMemberValue(element.name, element.value.apply(constPool));
}
attr.setAnnotation(annot);
ctMethod.getMethodInfo().addAttribute(attr);
}
Expand Down
Loading