| // Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file | 
 | // for details. All rights reserved. Use of this source code is governed by a | 
 | // BSD-style license that can be found in the LICENSE file. | 
 | package com.android.tools.r8; | 
 |  | 
 | import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION; | 
 | import static org.junit.Assert.assertEquals; | 
 | import static org.junit.Assert.assertTrue; | 
 | import static org.junit.Assert.fail; | 
 |  | 
 | import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest; | 
 | import com.android.tools.r8.R8RunArtTestsTest.DexTool; | 
 | import com.android.tools.r8.ToolHelper.DexVm; | 
 | import com.android.tools.r8.errors.Unreachable; | 
 | import com.android.tools.r8.origin.Origin; | 
 | import com.android.tools.r8.utils.InternalOptions; | 
 | import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization; | 
 | import com.android.tools.r8.utils.TestDescriptionWatcher; | 
 | import com.google.common.collect.ImmutableList; | 
 | import java.io.IOException; | 
 | import java.nio.file.Path; | 
 | import java.nio.file.Paths; | 
 | import java.util.Map; | 
 | import java.util.Set; | 
 | import org.junit.Assume; | 
 | import org.junit.Before; | 
 | import org.junit.Rule; | 
 | import org.junit.Test; | 
 | import org.junit.rules.ExpectedException; | 
 | import org.junit.rules.TemporaryFolder; | 
 |  | 
 | public abstract class R8RunExamplesCommon { | 
 |  | 
 |   protected enum Input { | 
 |     JAVAC, | 
 |     JAVAC_ALL, | 
 |     JAVAC_NONE | 
 |   } | 
 |  | 
 |   protected enum Output { | 
 |     DEX, | 
 |     CF | 
 |   } | 
 |  | 
 |   protected static String[] makeTest( | 
 |       Input input, CompilerUnderTest compiler, CompilationMode mode, String clazz) { | 
 |     return makeTest(input, compiler, mode, clazz, Output.DEX); | 
 |   } | 
 |  | 
 |   protected static String[] makeTest( | 
 |       Input input, | 
 |       CompilerUnderTest compiler, | 
 |       CompilationMode mode, | 
 |       String clazz, | 
 |       Output output) { | 
 |     String pkg = clazz.substring(0, clazz.lastIndexOf('.')); | 
 |     return new String[] {pkg, input.name(), compiler.name(), mode.name(), clazz, output.name()}; | 
 |   } | 
 |  | 
 |   @Rule | 
 |   public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest(); | 
 |  | 
 |   @Rule | 
 |   public TestDescriptionWatcher watcher = new TestDescriptionWatcher(); | 
 |  | 
 |   private final Input input; | 
 |   private final CompilerUnderTest compiler; | 
 |   private final CompilationMode mode; | 
 |   private final String pkg; | 
 |   private final String mainClass; | 
 |   protected final Output output; | 
 |  | 
 |   public R8RunExamplesCommon( | 
 |       String pkg, | 
 |       String input, | 
 |       String compiler, | 
 |       String mode, | 
 |       String mainClass, | 
 |       String output) { | 
 |     this.pkg = pkg; | 
 |     this.input = Input.valueOf(input); | 
 |     this.compiler = CompilerUnderTest.valueOf(compiler); | 
 |     this.mode = CompilationMode.valueOf(mode); | 
 |     this.mainClass = mainClass; | 
 |     this.output = Output.valueOf(output); | 
 |   } | 
 |  | 
 |   private Path getOutputFile() { | 
 |     return temp.getRoot().toPath().resolve("out.jar"); | 
 |   } | 
 |  | 
 |   private Path getInputFile() { | 
 |     switch(input) { | 
 |       case JAVAC: | 
 |         return getOriginalJarFile(""); | 
 |       case JAVAC_ALL: | 
 |         return getOriginalJarFile("_debuginfo_all"); | 
 |       case JAVAC_NONE: | 
 |         return getOriginalJarFile("_debuginfo_none"); | 
 |       default: | 
 |         throw new Unreachable(); | 
 |     } | 
 |   } | 
 |  | 
 |   public R8Command.Builder addInputFile(R8Command.Builder builder) { | 
 |     builder.addProgramFiles(getInputFile()); | 
 |     return builder; | 
 |   } | 
 |  | 
 |   public Path getOriginalJarFile(String postFix) { | 
 |     return Paths.get(getExampleDir(), pkg + postFix + JAR_EXTENSION); | 
 |   } | 
 |  | 
 |   private Path getOriginalDexFile() { | 
 |     return Paths.get(getExampleDir(), pkg, ToolHelper.DEFAULT_DEX_FILENAME); | 
 |   } | 
 |  | 
 |   @Rule | 
 |   public ExpectedException thrown = ExpectedException.none(); | 
 |  | 
 |   @Before | 
 |   public void compile() throws Exception { | 
 |     if (shouldCompileFail()) { | 
 |       thrown.expect(Throwable.class); | 
 |     } | 
 |     OutputMode outputMode = output == Output.CF ? OutputMode.ClassFile : OutputMode.DexIndexed; | 
 |     switch (compiler) { | 
 |       case D8: { | 
 |         assertTrue(output == Output.DEX); | 
 |         D8.run( | 
 |             D8Command.builder() | 
 |                 .addProgramFiles(getInputFile()) | 
 |                 .setOutput(getOutputFile(), outputMode) | 
 |                 .setMode(mode) | 
 |                 .build()); | 
 |         break; | 
 |       } | 
 |       case R8: { | 
 |           R8Command command = | 
 |               addInputFile(R8Command.builder()) | 
 |                   .addLibraryFiles( | 
 |                       output == Output.CF | 
 |                           ? ToolHelper.getJava8RuntimeJar() | 
 |                           : ToolHelper.getMostRecentAndroidJar()) | 
 |                   .setOutput(getOutputFile(), outputMode) | 
 |                   .setMode(mode) | 
 |                   .setDisableTreeShaking(true) | 
 |                   .setDisableMinification(true) | 
 |                   .addProguardConfiguration( | 
 |                       ImmutableList.of("-keepattributes *", "-dontoptimize"), Origin.unknown()) | 
 |                   .build(); | 
 |           ToolHelper.runR8(command, this::configure); | 
 |         break; | 
 |       } | 
 |       default: | 
 |         throw new Unreachable(); | 
 |     } | 
 |   } | 
 |  | 
 |   protected void configure(InternalOptions options) { | 
 |     options.lineNumberOptimization = LineNumberOptimization.OFF; | 
 |   } | 
 |  | 
 |   private boolean shouldCompileFail() { | 
 |     if (output == Output.CF && getFailingCompileCf().contains(mainClass)) { | 
 |       return true; | 
 |     } | 
 |     if (output == Output.DEX && getFailingCompileCfToDex().contains(mainClass)) { | 
 |       return true; | 
 |     } | 
 |     return false; | 
 |   } | 
 |  | 
 |   @Test | 
 |   public void outputIsIdentical() throws IOException { | 
 |     if (shouldCompileFail()) { | 
 |       // We expected an exception, but got none. | 
 |       // Return to ensure that this test fails due to the missing exception. | 
 |       return; | 
 |     } | 
 |     Assume.assumeTrue(ToolHelper.artSupported() || ToolHelper.compareAgaintsGoldenFiles()); | 
 |  | 
 |     DexVm vm = ToolHelper.getDexVm(); | 
 |     Assume.assumeFalse("Triage (b/144966342)", vm.isNewerThan(DexVm.ART_9_0_0_HOST)); | 
 |  | 
 |     if (shouldSkipVm(vm.getVersion())) { | 
 |       return; | 
 |     } | 
 |  | 
 |     String original = getOriginalDexFile().toString(); | 
 |     Path generated = getOutputFile(); | 
 |  | 
 |     ToolHelper.ProcessResult javaResult = ToolHelper.runJava(getOriginalJarFile(""), mainClass); | 
 |     if (javaResult.exitCode != 0) { | 
 |       System.out.println(javaResult.stdout); | 
 |       System.err.println(javaResult.stderr); | 
 |       fail("JVM failed for: " + mainClass); | 
 |     } | 
 |  | 
 |     TestCondition condition = | 
 |         output == Output.CF ? getFailingRunCf().get(mainClass) : getFailingRun().get(mainClass); | 
 |     if (condition != null && condition.test(DexTool.NONE, compiler, vm.getVersion(), mode)) { | 
 |       thrown.expect(Throwable.class); | 
 |     } | 
 |  | 
 |     if (output == Output.DEX && getFailingRunCfToDex().contains(mainClass)) { | 
 |       thrown.expect(Throwable.class); | 
 |     } | 
 |  | 
 |     if (output == Output.CF) { | 
 |       ToolHelper.ProcessResult result = ToolHelper.runJava(generated, mainClass); | 
 |       if (result.exitCode != 0) { | 
 |         System.err.println(result.stderr); | 
 |         fail("JVM failed on compiled output for: " + mainClass); | 
 |       } | 
 |       if (!getFailingOutputCf().contains(mainClass)) { | 
 |         assertEquals( | 
 |             "JavaC/JVM and " + compiler.name() + "/JVM output differ", | 
 |             javaResult.stdout, | 
 |             result.stdout); | 
 |       } | 
 |       return; | 
 |     } | 
 |  | 
 |     // Check output against Art output on original dex file. | 
 |     String output = | 
 |         ToolHelper.checkArtOutputIdentical(original, generated.toString(), mainClass, vm); | 
 |  | 
 |     // Check output against JVM output. | 
 |     if (shouldMatchJVMOutput(vm.getVersion())) { | 
 |       String javaOutput = javaResult.stdout; | 
 |       assertEquals("JVM and Art output differ", javaOutput, output); | 
 |     } | 
 |   } | 
 |  | 
 |   private boolean shouldMatchJVMOutput(DexVm.Version version) { | 
 |     TestCondition condition = getOutputNotIdenticalToJVMOutput().get(mainClass); | 
 |     return condition == null || !condition.test(DexTool.NONE, compiler, version, mode); | 
 |   } | 
 |  | 
 |   private boolean shouldSkipVm(DexVm.Version version) { | 
 |     TestCondition condition = getSkip().get(mainClass); | 
 |     return condition != null && condition.test(DexTool.NONE, compiler, version, mode); | 
 |   } | 
 |  | 
 |   protected abstract String getExampleDir(); | 
 |  | 
 |   protected abstract Map<String, TestCondition> getFailingRun(); | 
 |  | 
 |   protected abstract Map<String, TestCondition> getFailingRunCf(); | 
 |  | 
 |   protected abstract Set<String> getFailingCompileCfToDex(); | 
 |  | 
 |   // TODO(mathiasr): Add CompilerSet for CfToDex so we can fold this into getFailingRun(). | 
 |   protected abstract Set<String> getFailingRunCfToDex(); | 
 |  | 
 |   protected abstract Set<String> getFailingCompileCf(); | 
 |  | 
 |   protected abstract Set<String> getFailingOutputCf(); | 
 |  | 
 |   protected abstract Map<String, TestCondition> getOutputNotIdenticalToJVMOutput(); | 
 |  | 
 |   protected abstract Map<String, TestCondition> getSkip(); | 
 | } |