/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.tools.idea.rendering; import com.google.common.collect.Lists; import junit.framework.TestCase; import org.jetbrains.annotations.NotNull; import java.net.URL; import static com.android.tools.idea.rendering.ClassConverter.*; public class ClassConverterTest extends TestCase { public void testClassVersionToJdk() { assertEquals("1.5", classVersionToJdk(49)); assertEquals("1.6", classVersionToJdk(50)); assertEquals("1.7", classVersionToJdk(51)); assertEquals("1.8", classVersionToJdk(52)); assertEquals("1.4", classVersionToJdk(48)); assertEquals("1.3", classVersionToJdk(47)); assertEquals("1.2", classVersionToJdk(46)); assertEquals("1.1", classVersionToJdk(45)); } public void testJdkToClassVersion() { assertEquals(-1, jdkToClassVersion("?")); assertEquals(49, jdkToClassVersion("1.5")); assertEquals(49, jdkToClassVersion("1.5 ")); assertEquals(49, jdkToClassVersion("1.5.0")); assertEquals(49, jdkToClassVersion("1.5.0_50")); assertEquals(50, jdkToClassVersion("1.6")); assertEquals(50, jdkToClassVersion("1.6.1")); assertEquals(51, jdkToClassVersion("1.7.0")); assertEquals(45, jdkToClassVersion("1.1")); } public void testFindHighestMajorVersion() { InconvertibleClassError v1 = new InconvertibleClassError(null, "foo1", 49, 0); InconvertibleClassError v2 = new InconvertibleClassError(null, "foo2", 51, 0); InconvertibleClassError v3 = new InconvertibleClassError(null, "foo3", 49, 0); assertEquals(51, findHighestMajorVersion(Lists.<Throwable>newArrayList(v1, v2, v3))); } public void testGetCurrentClassVersion() { assertTrue(ClassConverter.getCurrentClassVersion() >= 50); } public void testGetCurrentJdkVersion() { assertTrue(ClassConverter.getCurrentJdkVersion().startsWith("1.")); } public void testMangling() throws Exception { // Compile // public class Test { public static int test() { return 42; } } // then compile with javac Test.java and take the binary contents of Test.class byte[] data = new byte[] { (byte)202, (byte)254, (byte)186, (byte)190, (byte)0, (byte)0, (byte)0, (byte)50, (byte)0, (byte)15, (byte)10, (byte)0, (byte)3, (byte)0, (byte)12, (byte)7, (byte)0, (byte)13, (byte)7, (byte)0, (byte)14, (byte)1, (byte)0, (byte)6, (byte)60, (byte)105, (byte)110, (byte)105, (byte)116, (byte)62, (byte)1, (byte)0, (byte)3, (byte)40, (byte)41, (byte)86, (byte)1, (byte)0, (byte)4, (byte)67, (byte)111, (byte)100, (byte)101, (byte)1, (byte)0, (byte)15, (byte)76, (byte)105, (byte)110, (byte)101, (byte)78, (byte)117, (byte)109, (byte)98, (byte)101, (byte)114, (byte)84, (byte)97, (byte)98, (byte)108, (byte)101, (byte)1, (byte)0, (byte)4, (byte)116, (byte)101, (byte)115, (byte)116, (byte)1, (byte)0, (byte)3, (byte)40, (byte)41, (byte)73, (byte)1, (byte)0, (byte)10, (byte)83, (byte)111, (byte)117, (byte)114, (byte)99, (byte)101, (byte)70, (byte)105, (byte)108, (byte)101, (byte)1, (byte)0, (byte)9, (byte)84, (byte)101, (byte)115, (byte)116, (byte)46, (byte)106, (byte)97, (byte)118, (byte)97, (byte)12, (byte)0, (byte)4, (byte)0, (byte)5, (byte)1, (byte)0, (byte)4, (byte)84, (byte)101, (byte)115, (byte)116, (byte)1, (byte)0, (byte)16, (byte)106, (byte)97, (byte)118, (byte)97, (byte)47, (byte)108, (byte)97, (byte)110, (byte)103, (byte)47, (byte)79, (byte)98, (byte)106, (byte)101, (byte)99, (byte)116, (byte)0, (byte)33, (byte)0, (byte)2, (byte)0, (byte)3, (byte)0, (byte)0, (byte)0, (byte)0, (byte)0, (byte)2, (byte)0, (byte)1, (byte)0, (byte)4, (byte)0, (byte)5, (byte)0, (byte)1, (byte)0, (byte)6, (byte)0, (byte)0, (byte)0, (byte)29, (byte)0, (byte)1, (byte)0, (byte)1, (byte)0, (byte)0, (byte)0, (byte)5, (byte)42, (byte)183, (byte)0, (byte)1, (byte)177, (byte)0, (byte)0, (byte)0, (byte)1, (byte)0, (byte)7, (byte)0, (byte)0, (byte)0, (byte)6, (byte)0, (byte)1, (byte)0, (byte)0, (byte)0, (byte)1, (byte)0, (byte)9, (byte)0, (byte)8, (byte)0, (byte)9, (byte)0, (byte)1, (byte)0, (byte)6, (byte)0, (byte)0, (byte)0, (byte)27, (byte)0, (byte)1, (byte)0, (byte)0, (byte)0, (byte)0, (byte)0, (byte)3, (byte)16, (byte)42, (byte)172, (byte)0, (byte)0, (byte)0, (byte)1, (byte)0, (byte)7, (byte)0, (byte)0, (byte)0, (byte)6, (byte)0, (byte)1, (byte)0, (byte)0, (byte)0, (byte)1, (byte)0, (byte)1, (byte)0, (byte)10, (byte)0, (byte)0, (byte)0, (byte)2, (byte)0, (byte)11 }; assertTrue(ClassConverter.isValidClassFile(data)); assertFalse(ClassConverter.isValidClassFile(new byte[100])); assertEquals(50, ClassConverter.getMajorVersion(data)); assertEquals(0, ClassConverter.getMinorVersion(data)); assertEquals(0xCAFEBABE, ClassConverter.getMagic(data)); ClassLoader classLoader = new TestClassLoader(data); Class<?> clz = classLoader.loadClass("Test"); assertNotNull(clz); Object result = clz.getMethod("test").invoke(null); assertEquals(Integer.valueOf(42), result); Class<?> oldClz = clz; data = ClassConverter.rewriteClass(data, 48, Integer.MIN_VALUE); assertEquals(48, ClassConverter.getMajorVersion(data)); classLoader = new TestClassLoader(data); clz = classLoader.loadClass("Test"); assertNotNull(clz); assertNotSame(clz, oldClz); result = clz.getMethod("test").invoke(null); assertEquals(Integer.valueOf(42), result); data = ClassConverter.rewriteClass(data, Integer.MAX_VALUE, 52); // latest known assertEquals(52, ClassConverter.getMajorVersion(data)); classLoader = new TestClassLoader(data); clz = classLoader.loadClass("Test"); assertNotNull(clz); result = clz.getMethod("test").invoke(null); assertEquals(Integer.valueOf(42), result); // Make sure that that class cannot actually be loaded in the regular way try { final byte[] finalData = data; ClassLoader cl = new ClassLoader() { @Override protected Class<?> findClass(String s) throws ClassNotFoundException { assertEquals("Test", s); return defineClass(null, finalData, 0, finalData.length); } }; cl.loadClass("Test"); fail("Expected class loading error"); } catch (UnsupportedClassVersionError e) { // pass } catch (Throwable t) { fail("Expected class loading error"); } } private static class TestClassLoader extends RenderClassLoader { final byte[] myData; public TestClassLoader(byte[] data) { super(null); myData = data; } @Override protected URL[] getExternalJars() { return new URL[0]; } @NotNull @Override protected Class<?> load(String name) throws ClassNotFoundException { assertEquals("Test", name); Class<?> clz = loadClass(name, myData); assertNotNull(clz); return clz; } } }