package org.jnode.ant.taskdefs; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.TreeSet; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.experimental.theories.DataPoints; import org.junit.experimental.theories.Theories; import org.junit.experimental.theories.Theory; import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.objectweb.asm.Attribute; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.attrs.Annotation; import org.objectweb.asm.attrs.RuntimeVisibleAnnotations; import org.objectweb.asm.util.TraceClassVisitor; import org.objectweb.asm.util.attrs.ASMRuntimeVisibleAnnotations; import static org.junit.Assert.assertArrayEquals; @RunWith(Theories.class) public class AnnotatorTest { @Rule public TemporaryFolder tempFolder = new TemporaryFolder(); @DataPoints public static final String[] ANNOTATIONS = new String[]{"SharedStatics", "MagicPermission"}; private Annotator annotator; private InputStream inputStreamClass; private File outputClassFile; @Before public void setUp() throws IOException { annotator = new Annotator(); outputClassFile = tempFolder.newFile("output.class"); } @Test public void testAddAnnotation_singleAnnotationWithNoise() throws Exception { Collection<String> annotations = Collections.singletonList(ANNOTATIONS[0]); inputStreamClass = getInputStream(ClassToAnnotateWithNoise.class); annotator.addAnnotations(inputStreamClass, outputClassFile, annotations); assertClassHasAnnotations(outputClassFile, annotations); } @Theory public void testAddAnnotation_singleAnnotation(String annotation) throws Exception { Collection<String> annotations = Collections.singletonList(annotation); inputStreamClass = getInputStream(ClassToAnnotate.class); annotator.addAnnotations(inputStreamClass, outputClassFile, annotations); assertClassHasAnnotations(outputClassFile, annotations); } @Test public void testAddAnnotation_multipleAnnotations() throws Exception { Collection<String> annotations = Arrays.asList(ANNOTATIONS); inputStreamClass = getInputStream(ClassToAnnotate.class); annotator.addAnnotations(inputStreamClass, outputClassFile, annotations); assertClassHasAnnotations(outputClassFile, annotations); } private static InputStream getInputStream(Class<?> classToAnnotate) throws java.io.IOException { final String resourceName = "/" + classToAnnotate.getName().replace(".", "/") + ".class"; return classToAnnotate.getResource(resourceName).openStream(); } private static void assertClassHasAnnotations(File outputClassFile, Collection<String> expectedAnnotations) throws Exception { final List<String> actualAnnotations = new ArrayList<String>(); FileInputStream fis = null; try { fis = new FileInputStream(outputClassFile); ClassReader cr = new ClassReader(fis); ClassVisitor adapter = new TraceClassVisitor(null, new PrintWriter(new StringWriter())) { public void visitAttribute(Attribute var1) { if (var1 instanceof RuntimeVisibleAnnotations) { for (Object annotation : ((RuntimeVisibleAnnotations) var1).annotations) { String className = ((Annotation) annotation).type; actualAnnotations.add(className.substring(1, className.length() - 1)); } } } }; cr.accept(adapter, new Attribute[]{new ASMRuntimeVisibleAnnotations()}, true); } finally { if (fis != null) { fis.close(); } } Collections.sort(actualAnnotations); TreeSet<String> expectAnnotations = new TreeSet<String>(); for (String simpleName : expectedAnnotations) { expectAnnotations.add("org/jnode/annotation/" + simpleName); } assertArrayEquals(expectAnnotations.toArray(), actualAnnotations.toArray()); } public static @interface NoiseAnnotation { } public static class ClassToAnnotate { } @NoiseAnnotation public static class ClassToAnnotateWithNoise { } }