diff options
Diffstat (limited to 'src/test/java/com/google/devtools/build/android/desugar/DefaultMethodClassFixerTest.java')
-rw-r--r-- | src/test/java/com/google/devtools/build/android/desugar/DefaultMethodClassFixerTest.java | 234 |
1 files changed, 234 insertions, 0 deletions
diff --git a/src/test/java/com/google/devtools/build/android/desugar/DefaultMethodClassFixerTest.java b/src/test/java/com/google/devtools/build/android/desugar/DefaultMethodClassFixerTest.java new file mode 100644 index 0000000000..cdc32639b3 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/DefaultMethodClassFixerTest.java @@ -0,0 +1,234 @@ +// Copyright 2017 The Bazel Authors. All rights reserved. +// +// 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.google.devtools.build.android.desugar; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.truth.Truth.assertThat; +import static com.google.devtools.build.android.desugar.DefaultMethodClassFixer.InterfaceComparator.INSTANCE; + +import com.google.common.collect.ImmutableList; +import com.google.common.io.Closer; +import com.google.devtools.build.android.desugar.Desugar.ThrowingClassLoader; +import java.io.File; +import java.io.IOException; +import java.io.Serializable; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.List; +import java.util.TreeSet; +import java.util.concurrent.Callable; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.InsnNode; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.MethodNode; + +/** Unit Test for {@link DefaultMethodClassFixer} */ +@RunWith(JUnit4.class) +public class DefaultMethodClassFixerTest { + + private ClassReaderFactory classpathReader; + private ClassReaderFactory bootclassPath; + private ClassLoader classLoader; + private Closer closer; + + @Before + public void setup() throws IOException { + closer = Closer.create(); + CoreLibraryRewriter rewriter = new CoreLibraryRewriter(""); + + IndexedInputs indexedInputs = + toIndexedInputs(closer, System.getProperty("DefaultMethodClassFixerTest.input")); + IndexedInputs indexedClasspath = + toIndexedInputs(closer, System.getProperty("DefaultMethodClassFixerTest.classpath")); + IndexedInputs indexedBootclasspath = + toIndexedInputs(closer, System.getProperty("DefaultMethodClassFixerTest.bootclasspath")); + + bootclassPath = new ClassReaderFactory(indexedBootclasspath, rewriter); + IndexedInputs indexedClasspathAndInputFiles = indexedClasspath.withParent(indexedInputs); + classpathReader = new ClassReaderFactory(indexedClasspathAndInputFiles, rewriter); + ClassLoader bootclassloader = + new HeaderClassLoader(indexedBootclasspath, rewriter, new ThrowingClassLoader()); + classLoader = new HeaderClassLoader(indexedClasspathAndInputFiles, rewriter, bootclassloader); + } + + @After + public void teardown() throws IOException { + closer.close(); + } + + private static IndexedInputs toIndexedInputs(Closer closer, String stringPathList) + throws IOException { + final List<Path> pathList = readPathListFromString(stringPathList); + return new IndexedInputs(Desugar.toRegisteredInputFileProvider(closer, pathList)); + } + + private static List<Path> readPathListFromString(String pathList) { + return Arrays.stream(checkNotNull(pathList).split(File.pathSeparator)) + .map(Paths::get) + .collect(ImmutableList.toImmutableList()); + } + + private byte[] desugar(String classname) { + ClassReader reader = classpathReader.readIfKnown(classname); + return desugar(reader); + } + + private byte[] desugar(ClassReader reader) { + ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS); + DefaultMethodClassFixer fixer = + new DefaultMethodClassFixer(writer, classpathReader, bootclassPath, classLoader); + reader.accept(fixer, 0); + return writer.toByteArray(); + } + + private byte[] desugar(byte[] classContent) { + ClassReader reader = new ClassReader(classContent); + return desugar(reader); + } + + @Test + public void testDesugaringDirectImplementation() { + byte[] desugaredClass = + desugar( + ("com.google.devtools.build.android.desugar.testdata.java8." + + "DefaultInterfaceMethodWithStaticInitializer$TestInterfaceSetOne$C") + .replace('.', '/')); + checkClinitForDefaultInterfaceMethodWithStaticInitializerTestInterfaceSetOneC(desugaredClass); + + byte[] desugaredClassAgain = desugar(desugaredClass); + checkClinitForDefaultInterfaceMethodWithStaticInitializerTestInterfaceSetOneC( + desugaredClassAgain); + + desugaredClassAgain = desugar(desugaredClassAgain); + checkClinitForDefaultInterfaceMethodWithStaticInitializerTestInterfaceSetOneC( + desugar(desugaredClassAgain)); + } + + private void checkClinitForDefaultInterfaceMethodWithStaticInitializerTestInterfaceSetOneC( + byte[] classContent) { + ClassReader reader = new ClassReader(classContent); + reader.accept( + new ClassVisitor(Opcodes.ASM5) { + + class ClinitMethod extends MethodNode { + + public ClinitMethod( + int access, String name, String desc, String signature, String[] exceptions) { + super(Opcodes.ASM5, access, name, desc, signature, exceptions); + } + } + + private ClinitMethod clinit; + + @Override + public MethodVisitor visitMethod( + int access, String name, String desc, String signature, String[] exceptions) { + if ("<clinit>".equals(name)) { + assertThat(clinit).isNull(); + clinit = new ClinitMethod(access, name, desc, signature, exceptions); + return clinit; + } + return super.visitMethod(access, name, desc, signature, exceptions); + } + + @Override + public void visitEnd() { + assertThat(clinit).isNotNull(); + assertThat(clinit.instructions.size()).isEqualTo(3); + AbstractInsnNode instruction = clinit.instructions.getFirst(); + { + assertThat(instruction).isInstanceOf(MethodInsnNode.class); + MethodInsnNode field = (MethodInsnNode) instruction; + assertThat(field.owner) + .isEqualTo( + "com/google/devtools/build/android/desugar/testdata/java8/" + + "DefaultInterfaceMethodWithStaticInitializer" + + "$TestInterfaceSetOne$I1$$CC"); + assertThat(field.name) + .isEqualTo(InterfaceDesugaring.COMPANION_METHOD_TO_TRIGGER_INTERFACE_CLINIT_NAME); + assertThat(field.desc) + .isEqualTo(InterfaceDesugaring.COMPANION_METHOD_TO_TRIGGER_INTERFACE_CLINIT_DESC); + } + { + instruction = instruction.getNext(); + assertThat(instruction).isInstanceOf(MethodInsnNode.class); + MethodInsnNode field = (MethodInsnNode) instruction; + assertThat(field.owner) + .isEqualTo( + "com/google/devtools/build/android/desugar/testdata/java8/" + + "DefaultInterfaceMethodWithStaticInitializer" + + "$TestInterfaceSetOne$I2$$CC"); + assertThat(field.name) + .isEqualTo(InterfaceDesugaring.COMPANION_METHOD_TO_TRIGGER_INTERFACE_CLINIT_NAME); + assertThat(field.desc) + .isEqualTo(InterfaceDesugaring.COMPANION_METHOD_TO_TRIGGER_INTERFACE_CLINIT_DESC); + } + { + instruction = instruction.getNext(); + assertThat(instruction).isInstanceOf(InsnNode.class); + assertThat(instruction.getOpcode()).isEqualTo(Opcodes.RETURN); + } + } + }, + 0); + } + + @Test + public void testInterfaceComparator() { + assertThat(INSTANCE.compare(Runnable.class, Runnable.class)).isEqualTo(0); + assertThat(INSTANCE.compare(Runnable.class, MyRunnable1.class)).isEqualTo(1); + assertThat(INSTANCE.compare(MyRunnable2.class, Runnable.class)).isEqualTo(-1); + assertThat(INSTANCE.compare(MyRunnable3.class, Runnable.class)).isEqualTo(-1); + assertThat(INSTANCE.compare(MyRunnable1.class, MyRunnable3.class)).isEqualTo(1); + assertThat(INSTANCE.compare(MyRunnable3.class, MyRunnable2.class)).isEqualTo(-1); + assertThat(INSTANCE.compare(MyRunnable2.class, MyRunnable1.class)).isGreaterThan(0); + assertThat(INSTANCE.compare(Runnable.class, Serializable.class)).isGreaterThan(0); + assertThat(INSTANCE.compare(Serializable.class, Runnable.class)).isLessThan(0); + + TreeSet<Class<?>> orderedSet = new TreeSet<>(INSTANCE); + orderedSet.add(Serializable.class); + orderedSet.add(Runnable.class); + orderedSet.add(MyRunnable2.class); + orderedSet.add(Callable.class); + orderedSet.add(Serializable.class); + orderedSet.add(MyRunnable1.class); + orderedSet.add(MyRunnable3.class); + assertThat(orderedSet) + .containsExactly( + MyRunnable3.class, // subtype before supertype(s) + MyRunnable1.class, + MyRunnable2.class, + Serializable.class, // java... comes textually after com.google... + Runnable.class, + Callable.class) + .inOrder(); + } + + private static interface MyRunnable1 extends Runnable {} + + private static interface MyRunnable2 extends Runnable {} + + private static interface MyRunnable3 extends MyRunnable1, MyRunnable2 {} +} |