From 6154464050171e74133bafe5d94597053d687037 Mon Sep 17 00:00:00 2001 From: Mikhael Bogdanov Date: Fri, 11 Oct 2013 13:41:08 +0400 Subject: [PATCH] Test framework fix: Load kotlin test classes in separate class loader --- .../jet/codegen/AnnotationGenTest.java | 6 ++- .../jet/codegen/ClassLoaderIsolationUtil.java | 46 +++++++++++++++++++ .../ClassPathInParentClassLoaderTest.java | 2 +- .../jet/codegen/CodegenTestCase.java | 19 +++++++- .../jet/codegen/JUnitUsageGenTest.java | 8 ++-- .../JetValueParameterAnnotationTest.java | 6 +-- .../codegen/KotlinClassAnnotationTest.java | 8 ++-- .../codegen/KotlinPackageAnnotationTest.java | 16 ++++--- .../KotlinPackageFragmentAnnotationTest.java | 15 ++++-- .../jet/codegen/NamespaceGenTest.java | 9 ++-- .../jetbrains/jet/codegen/TestlibTest.java | 13 +++++- 11 files changed, 118 insertions(+), 30 deletions(-) create mode 100644 compiler/tests/org/jetbrains/jet/codegen/ClassLoaderIsolationUtil.java diff --git a/compiler/tests/org/jetbrains/jet/codegen/AnnotationGenTest.java b/compiler/tests/org/jetbrains/jet/codegen/AnnotationGenTest.java index 7cc96e1788b..cb4025a17a0 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/AnnotationGenTest.java +++ b/compiler/tests/org/jetbrains/jet/codegen/AnnotationGenTest.java @@ -229,8 +229,10 @@ public class AnnotationGenTest extends CodegenTestCase { Class[] interfaces = aClass.getInterfaces(); assertEquals(2, interfaces.length); assertEquals(0, aClass.getDeclaredMethods().length); - assertTrue(Annotation.class == interfaces[0] || Annotation.class == interfaces[1]); - assertTrue(JetObject.class == interfaces[0] || JetObject.class == interfaces[1]); + Class annotationClass = getCorrespondingClass(Annotation.class); + Class jetObjectClass = getCorrespondingClass(JetObject.class); + assertTrue(annotationClass == interfaces[0] || annotationClass == interfaces[1]); + assertTrue(jetObjectClass == interfaces[0] || jetObjectClass == interfaces[1]); assertTrue(aClass.isAnnotation()); } diff --git a/compiler/tests/org/jetbrains/jet/codegen/ClassLoaderIsolationUtil.java b/compiler/tests/org/jetbrains/jet/codegen/ClassLoaderIsolationUtil.java new file mode 100644 index 00000000000..2a3afca794b --- /dev/null +++ b/compiler/tests/org/jetbrains/jet/codegen/ClassLoaderIsolationUtil.java @@ -0,0 +1,46 @@ +package org.jetbrains.jet.codegen; + +import junit.framework.Assert; +import org.jetbrains.annotations.NotNull; + +import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationTargetException; + +import static junit.framework.Assert.*; + +public class ClassLoaderIsolationUtil { + + public static void assertEquals(Class expected, Class actual) { + if (actual == null) { + assertNull(expected); + } + else Assert.assertEquals(getClassFromClassLoader(expected, actual.getClassLoader()), actual); + } + + public static Class getAnnotationClass(Class annotationClass, ClassLoader classLoader) { + return (Class) getClassFromClassLoader(annotationClass, classLoader); + } + + public static Object getAnnotationAttribute(Object annotation, String name) { + try { + return annotation.getClass().getMethod(name).invoke(annotation); + } + catch (Exception e) { + e.printStackTrace(); + fail(e.getMessage()); + } + return null; + } + + @NotNull + public static Class getClassFromClassLoader(Class classForName, ClassLoader classLoader) { + try { + return classLoader.loadClass(classForName.getName()); + } + catch (ClassNotFoundException e) { + e.printStackTrace(); + fail(e.getMessage()); + } + return null; + } +} diff --git a/compiler/tests/org/jetbrains/jet/codegen/ClassPathInParentClassLoaderTest.java b/compiler/tests/org/jetbrains/jet/codegen/ClassPathInParentClassLoaderTest.java index 13620e48b2b..0b3fee88869 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/ClassPathInParentClassLoaderTest.java +++ b/compiler/tests/org/jetbrains/jet/codegen/ClassPathInParentClassLoaderTest.java @@ -32,7 +32,7 @@ public class ClassPathInParentClassLoaderTest extends CodegenTestCase { @NotNull @Override protected GeneratedClassLoader createClassLoader(@NotNull ClassFileFactory factory) { - ClassLoader parentClassLoader = new URLClassLoader(getClassPathURLs(), CodegenTestCase.class.getClassLoader()); + ClassLoader parentClassLoader = new URLClassLoader(getClassPathURLs(), null); initializedClassLoader = new GeneratedClassLoader(factory, parentClassLoader); return initializedClassLoader; } diff --git a/compiler/tests/org/jetbrains/jet/codegen/CodegenTestCase.java b/compiler/tests/org/jetbrains/jet/codegen/CodegenTestCase.java index 5042df1d4ab..74de63c119e 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/CodegenTestCase.java +++ b/compiler/tests/org/jetbrains/jet/codegen/CodegenTestCase.java @@ -25,12 +25,14 @@ import org.jetbrains.jet.JetTestCaseBuilder; import org.jetbrains.jet.JetTestUtils; import org.jetbrains.jet.cli.jvm.JVMConfigurationKeys; import org.jetbrains.jet.cli.jvm.compiler.JetCoreEnvironment; +import org.jetbrains.jet.codegen.forTestCompile.ForTestCompileRuntime; import org.jetbrains.jet.lang.psi.JetPsiUtil; import org.jetbrains.jet.lang.resolve.name.FqName; import org.jetbrains.jet.utils.ExceptionUtils; import java.io.File; import java.io.IOException; +import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URL; @@ -109,7 +111,7 @@ public abstract class CodegenTestCase extends UsefulTestCase { fail("Double initialization of class loader in same test"); } - initializedClassLoader = new GeneratedClassLoader(factory, CodegenTestCase.class.getClassLoader(), getClassPathURLs()); + initializedClassLoader = new GeneratedClassLoader(factory, null, getClassPathURLs()); return initializedClassLoader; } @@ -123,6 +125,13 @@ public abstract class CodegenTestCase extends UsefulTestCase { throw new RuntimeException(e); } } + try { + //add runtime library + urls.add(ForTestCompileRuntime.runtimeJarForTests().toURI().toURL()); + } + catch (MalformedURLException e) { + throw new RuntimeException(e); + } return urls.toArray(new URL[urls.size()]); } @@ -201,4 +210,12 @@ public abstract class CodegenTestCase extends UsefulTestCase { } return method; } + + public Class getCorrespondingAnnotationClass(Class classForName) { + return ClassLoaderIsolationUtil.getAnnotationClass(classForName, initializedClassLoader); + } + + public Class getCorrespondingClass(Class classForName) { + return ClassLoaderIsolationUtil.getClassFromClassLoader(classForName, initializedClassLoader); + } } diff --git a/compiler/tests/org/jetbrains/jet/codegen/JUnitUsageGenTest.java b/compiler/tests/org/jetbrains/jet/codegen/JUnitUsageGenTest.java index ea8d756f496..60c2f88e6be 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/JUnitUsageGenTest.java +++ b/compiler/tests/org/jetbrains/jet/codegen/JUnitUsageGenTest.java @@ -23,6 +23,7 @@ import org.jetbrains.jet.cli.jvm.compiler.JetCoreEnvironment; import org.junit.Test; import java.io.File; +import java.lang.annotation.Annotation; import java.lang.reflect.Method; public class JUnitUsageGenTest extends CodegenTestCase { @@ -49,8 +50,9 @@ public class JUnitUsageGenTest extends CodegenTestCase { Class namespaceClass = generateNamespaceClass(); Method method = namespaceClass.getMethod("foo", Method.class); method.setAccessible(true); - Test annotation = method.getAnnotation(Test.class); - assertEquals(annotation.timeout(), 0l); - assertEquals(annotation.expected(), Test.None.class); + Annotation annotation = method.getAnnotation(getCorrespondingAnnotationClass(Test.class)); + assertEquals(ClassLoaderIsolationUtil.getAnnotationAttribute(annotation, "timeout"), 0l); + ClassLoaderIsolationUtil.assertEquals(Test.None.class, (Class) ClassLoaderIsolationUtil.getAnnotationAttribute(annotation, + "expected")); } } diff --git a/compiler/tests/org/jetbrains/jet/codegen/JetValueParameterAnnotationTest.java b/compiler/tests/org/jetbrains/jet/codegen/JetValueParameterAnnotationTest.java index 29e8434dcbf..4552ed17388 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/JetValueParameterAnnotationTest.java +++ b/compiler/tests/org/jetbrains/jet/codegen/JetValueParameterAnnotationTest.java @@ -104,9 +104,9 @@ public class JetValueParameterAnnotationTest extends CodegenTestCase { for (int i = 0, length = annotations.length; i < length; i++) { assertSize(1, annotations[i]); Annotation annotation = annotations[i][0]; - assertEquals(JetValueParameter.class, annotation.annotationType()); - assertEquals(expected[i].name(), ((JetValueParameter) annotation).name()); - assertEquals(expected[i].type(), ((JetValueParameter) annotation).type()); + ClassLoaderIsolationUtil.assertEquals(JetValueParameter.class, annotation.annotationType()); + assertEquals(expected[i].name(), ClassLoaderIsolationUtil.getAnnotationAttribute(annotation, "name")); + assertEquals(expected[i].type(), ClassLoaderIsolationUtil.getAnnotationAttribute(annotation, "type")); } } } diff --git a/compiler/tests/org/jetbrains/jet/codegen/KotlinClassAnnotationTest.java b/compiler/tests/org/jetbrains/jet/codegen/KotlinClassAnnotationTest.java index 024477483b8..da5c08c04f9 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/KotlinClassAnnotationTest.java +++ b/compiler/tests/org/jetbrains/jet/codegen/KotlinClassAnnotationTest.java @@ -23,6 +23,7 @@ import org.jetbrains.jet.descriptors.serialization.JavaProtoBufUtil; import org.jetbrains.jet.lang.resolve.name.FqName; import org.jetbrains.jet.lang.resolve.name.FqNameUnsafe; +import java.lang.annotation.Annotation; import java.util.Arrays; import java.util.Set; @@ -47,10 +48,11 @@ public class KotlinClassAnnotationTest extends CodegenTestCase { "}\n"); Class aClass = generateClass(NAMESPACE_NAME + "." + CLASS_NAME); - assertTrue(aClass.isAnnotationPresent(KotlinClass.class)); - KotlinClass kotlinClass = (KotlinClass) aClass.getAnnotation(KotlinClass.class); + Class annotationClass = getCorrespondingAnnotationClass(KotlinClass.class); + assertTrue(aClass.isAnnotationPresent(annotationClass)); + Annotation kotlinClass = aClass.getAnnotation(annotationClass); - ClassData data = JavaProtoBufUtil.readClassDataFrom(kotlinClass.data()); + ClassData data = JavaProtoBufUtil.readClassDataFrom((String[]) ClassLoaderIsolationUtil.getAnnotationAttribute(kotlinClass, "data")); Set callableNames = collectCallableNames(data.getClassProto().getMemberList(), data.getNameResolver()); assertSameElements(Arrays.asList("foo", "bar"), callableNames); diff --git a/compiler/tests/org/jetbrains/jet/codegen/KotlinPackageAnnotationTest.java b/compiler/tests/org/jetbrains/jet/codegen/KotlinPackageAnnotationTest.java index 2a8e78ec8f6..cb077dcba81 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/KotlinPackageAnnotationTest.java +++ b/compiler/tests/org/jetbrains/jet/codegen/KotlinPackageAnnotationTest.java @@ -19,13 +19,11 @@ package org.jetbrains.jet.codegen; import jet.KotlinPackage; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.ConfigurationKind; -import org.jetbrains.jet.descriptors.serialization.JavaProtoBufUtil; -import org.jetbrains.jet.descriptors.serialization.NameResolver; -import org.jetbrains.jet.descriptors.serialization.PackageData; -import org.jetbrains.jet.descriptors.serialization.ProtoBuf; +import org.jetbrains.jet.descriptors.serialization.*; import org.jetbrains.jet.lang.resolve.java.PackageClassUtils; import org.jetbrains.jet.lang.resolve.name.FqName; +import java.lang.annotation.Annotation; import java.util.Arrays; import java.util.HashSet; import java.util.List; @@ -51,10 +49,14 @@ public class KotlinPackageAnnotationTest extends CodegenTestCase { "object C\n"); Class aClass = generateClass(PackageClassUtils.getPackageClassFqName(NAMESPACE_NAME).asString()); - assertTrue(aClass.isAnnotationPresent(KotlinPackage.class)); - KotlinPackage kotlinPackage = (KotlinPackage) aClass.getAnnotation(KotlinPackage.class); + Class annotationClass = getCorrespondingAnnotationClass(KotlinPackage.class); + assertTrue(aClass.isAnnotationPresent(annotationClass)); + assertTrue(aClass.isAnnotationPresent(annotationClass)); - PackageData data = JavaProtoBufUtil.readPackageDataFrom(kotlinPackage.data()); + Annotation kotlinPackage = aClass.getAnnotation(annotationClass); + + PackageData data = JavaProtoBufUtil.readPackageDataFrom((String[]) ClassLoaderIsolationUtil.getAnnotationAttribute(kotlinPackage, + "data")); Set callableNames = collectCallableNames(data.getPackageProto().getMemberList(), data.getNameResolver()); assertSameElements(Arrays.asList("foo", "bar", "C"), callableNames); diff --git a/compiler/tests/org/jetbrains/jet/codegen/KotlinPackageFragmentAnnotationTest.java b/compiler/tests/org/jetbrains/jet/codegen/KotlinPackageFragmentAnnotationTest.java index 370c03a6a12..00182119d5b 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/KotlinPackageFragmentAnnotationTest.java +++ b/compiler/tests/org/jetbrains/jet/codegen/KotlinPackageFragmentAnnotationTest.java @@ -23,6 +23,8 @@ import org.jetbrains.jet.lang.resolve.java.JvmClassName; import org.jetbrains.jet.lang.resolve.java.PackageClassUtils; import org.jetbrains.jet.lang.resolve.name.FqName; +import java.lang.annotation.Annotation; + public class KotlinPackageFragmentAnnotationTest extends CodegenTestCase { public static final FqName NAMESPACE_NAME = new FqName("test"); @@ -42,12 +44,17 @@ public class KotlinPackageFragmentAnnotationTest extends CodegenTestCase { // The file which is not a facade is a package fragment String fqName = fileName.substring(0, fileName.length() - ".class".length()).replace('/', '.'); Class aClass = generateClass(fqName); - assertTrue("No KotlinPackageFragment annotation on a package fragment", - aClass.isAnnotationPresent(KotlinPackageFragment.class)); - KotlinPackageFragment annotation = (KotlinPackageFragment) aClass.getAnnotation(KotlinPackageFragment.class); + Class annotationClass = getCorrespondingAnnotationClass(KotlinPackageFragment.class); + + assertTrue("No KotlinPackageFragment annotation on a package fragment", + aClass.isAnnotationPresent(annotationClass)); + + Annotation kotlinPackageFragment = aClass.getAnnotation(annotationClass); + assertTrue("KotlinPackageFragment annotation is written with an unsupported format", - AbiVersionUtil.isAbiVersionCompatible(annotation.abiVersion())); + AbiVersionUtil.isAbiVersionCompatible( + (Integer) ClassLoaderIsolationUtil.getAnnotationAttribute(kotlinPackageFragment, "abiVersion"))); return; } diff --git a/compiler/tests/org/jetbrains/jet/codegen/NamespaceGenTest.java b/compiler/tests/org/jetbrains/jet/codegen/NamespaceGenTest.java index add094fc43e..f15730b75bf 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/NamespaceGenTest.java +++ b/compiler/tests/org/jetbrains/jet/codegen/NamespaceGenTest.java @@ -414,10 +414,11 @@ public class NamespaceGenTest extends CodegenTestCase { public void testIntRange() throws Exception { loadText("fun foo() = 1..10"); Method main = generateFunction(); - IntRange result = (IntRange) main.invoke(null); - assertTrue(result.contains(1)); - assertTrue(result.contains(10)); - assertFalse(result.contains(11)); + Object result = main.invoke(null); + Method contains = result.getClass().getMethod("contains", Integer.TYPE); + assertTrue((Boolean) contains.invoke(result, 1)); + assertTrue((Boolean) contains.invoke(result, 10)); + assertFalse((Boolean) contains.invoke(result, 11)); } public void testSubstituteJavaMethodTypeParameters() throws Exception { diff --git a/compiler/tests/org/jetbrains/jet/codegen/TestlibTest.java b/compiler/tests/org/jetbrains/jet/codegen/TestlibTest.java index 1eda52d96b4..4cf4022e161 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/TestlibTest.java +++ b/compiler/tests/org/jetbrains/jet/codegen/TestlibTest.java @@ -108,8 +108,17 @@ public class TestlibTest extends UsefulTestCase { ClassFileFactory classFileFactory = generationState.getFactory(); classLoader = new GeneratedClassLoader(classFileFactory, - new URLClassLoader(new URL[] {ForTestCompileRuntime.runtimeJarForTests().toURI().toURL(), junitJar.toURI().toURL()}, - TestCase.class.getClassLoader())); + new URLClassLoader(new URL[] {ForTestCompileRuntime.runtimeJarForTests().toURI().toURL()}, + null)) { + @Override + public Class loadClass(String name) throws ClassNotFoundException { + if (name.startsWith("junit.") || name.startsWith("org.junit.")) { + //In other way we don't find any test cause will have two different TestCase classes! + return TestlibTest.class.getClassLoader().loadClass(name); + } + return super.loadClass(name); + } + }; typeMapper = generationState.getTypeMapper();