| // Copyright (c) 2018, 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 org.hamcrest.CoreMatchers.containsString; | 
 | import static org.hamcrest.MatcherAssert.assertThat; | 
 | import static org.junit.Assert.assertEquals; | 
 | import static org.junit.Assert.assertNotEquals; | 
 | import static org.junit.Assert.assertNotNull; | 
 |  | 
 | import com.android.tools.r8.ToolHelper.ProcessResult; | 
 | import com.android.tools.r8.naming.retrace.StackTrace; | 
 | import com.android.tools.r8.utils.AndroidApp; | 
 | import com.android.tools.r8.utils.StringUtils; | 
 | import com.android.tools.r8.utils.ThrowingConsumer; | 
 | import com.android.tools.r8.utils.codeinspector.CodeInspector; | 
 | import java.io.IOException; | 
 | import java.io.PrintStream; | 
 | import java.util.concurrent.ExecutionException; | 
 | import java.util.function.Consumer; | 
 | import java.util.function.Function; | 
 | import org.hamcrest.Matcher; | 
 |  | 
 | public abstract class TestRunResult<RR extends TestRunResult<RR>> { | 
 |   protected final AndroidApp app; | 
 |   private final TestRuntime runtime; | 
 |   private final ProcessResult result; | 
 |  | 
 |   public TestRunResult(AndroidApp app, TestRuntime runtime, ProcessResult result) { | 
 |     this.app = app; | 
 |     this.runtime = runtime; | 
 |     this.result = result; | 
 |   } | 
 |  | 
 |   abstract RR self(); | 
 |  | 
 |   public <S> S map(Function<RR, S> fn) { | 
 |     return fn.apply(self()); | 
 |   } | 
 |  | 
 |   public RR apply(Consumer<RR> fn) { | 
 |     fn.accept(self()); | 
 |     return self(); | 
 |   } | 
 |  | 
 |   public AndroidApp app() { | 
 |     return app; | 
 |   } | 
 |  | 
 |   public String getStdOut() { | 
 |     return result.stdout; | 
 |   } | 
 |  | 
 |   public String getStdErr() { | 
 |     return result.stderr; | 
 |   } | 
 |  | 
 |   public StackTrace getStackTrace() { | 
 |     if (runtime.isDex()) { | 
 |       return StackTrace.extractFromArt(getStdErr(), runtime.asDex().getVm()); | 
 |     } else { | 
 |       return StackTrace.extractFromJvm(getStdErr()); | 
 |     } | 
 |   } | 
 |  | 
 |   public int getExitCode() { | 
 |     return result.exitCode; | 
 |   } | 
 |  | 
 |   public RR assertSuccess() { | 
 |     assertEquals(errorMessage("Expected run to succeed."), 0, result.exitCode); | 
 |     return self(); | 
 |   } | 
 |  | 
 |   public RR assertFailure() { | 
 |     assertNotEquals(errorMessage("Expected run to fail."), 0, result.exitCode); | 
 |     return self(); | 
 |   } | 
 |  | 
 |   public RR assertFailureWithOutput(String expected) { | 
 |     assertFailure(); | 
 |     assertEquals(errorMessage("Run stdout incorrect.", expected), expected, result.stdout); | 
 |     return self(); | 
 |   } | 
 |  | 
 |   public RR assertFailureWithErrorThatMatches(Matcher<String> matcher) { | 
 |     assertFailure(); | 
 |     assertThat( | 
 |         errorMessage("Run stderr incorrect.", matcher.toString()), result.stderr, matcher); | 
 |     return self(); | 
 |   } | 
 |  | 
 |   public RR assertFailureWithErrorThatThrows(Class<? extends Throwable> expectedError) { | 
 |     assertFailure(); | 
 |     assertThat( | 
 |         errorMessage("Run stderr incorrect.", expectedError.getName()), | 
 |         result.stderr, | 
 |         containsString(expectedError.getName())); | 
 |     return self(); | 
 |   } | 
 |  | 
 |   public RR assertStderrMatches(Matcher<String> matcher) { | 
 |     assertThat(errorMessage("Run stderr incorrect.", matcher.toString()), result.stderr, matcher); | 
 |     return self(); | 
 |   } | 
 |  | 
 |   public RR assertSuccessWithOutput(String expected) { | 
 |     assertSuccess(); | 
 |     assertEquals(errorMessage("Run stdout incorrect.", expected), expected, result.stdout); | 
 |     return self(); | 
 |   } | 
 |  | 
 |   public RR assertSuccessWithOutputLines(String... expected) { | 
 |     return assertSuccessWithOutput(StringUtils.lines(expected)); | 
 |   } | 
 |  | 
 |   public RR assertSuccessWithOutputThatMatches(Matcher<String> matcher) { | 
 |     assertSuccess(); | 
 |     assertThat(errorMessage("Run stdout incorrect.", matcher.toString()), result.stdout, matcher); | 
 |     return self(); | 
 |   } | 
 |  | 
 |   public CodeInspector inspector() throws IOException, ExecutionException { | 
 |     // Inspection post run implies success. If inspection of an invalid program is needed it should | 
 |     // be done on the compilation result or on the input. | 
 |     assertSuccess(); | 
 |     assertNotNull(app); | 
 |     return new CodeInspector(app); | 
 |   } | 
 |  | 
 |   public RR inspect(ThrowingConsumer<CodeInspector, NoSuchMethodException> consumer) | 
 |       throws IOException, ExecutionException { | 
 |     CodeInspector inspector = inspector(); | 
 |     try { | 
 |       consumer.accept(inspector); | 
 |     } catch (NoSuchMethodException exception) { | 
 |       throw new RuntimeException(exception); | 
 |     } | 
 |     return self(); | 
 |   } | 
 |  | 
 |   public <E extends Throwable> RR inspectStackTrace(ThrowingConsumer<StackTrace, E> consumer) | 
 |       throws E { | 
 |     consumer.accept(getStackTrace()); | 
 |     return self(); | 
 |   } | 
 |  | 
 |   public RR disassemble(PrintStream ps) throws IOException, ExecutionException { | 
 |     ToolHelper.disassemble(app, ps); | 
 |     return self(); | 
 |   } | 
 |  | 
 |   public RR disassemble() throws IOException, ExecutionException { | 
 |     return disassemble(System.out); | 
 |   } | 
 |  | 
 |   @Override | 
 |   public String toString() { | 
 |     StringBuilder builder = new StringBuilder(); | 
 |     appendInfo(builder); | 
 |     return builder.toString(); | 
 |   } | 
 |  | 
 |   String errorMessage(String message) { | 
 |     return errorMessage(message, null); | 
 |   } | 
 |  | 
 |   String errorMessage(String message, String expected) { | 
 |     StringBuilder builder = new StringBuilder(message).append('\n'); | 
 |     if (expected != null) { | 
 |       if (expected.contains(System.lineSeparator())) { | 
 |         builder.append("EXPECTED:").append(System.lineSeparator()).append(expected); | 
 |       } else { | 
 |         builder.append("EXPECTED: ").append(expected); | 
 |       } | 
 |       builder.append(System.lineSeparator()); | 
 |     } | 
 |     appendInfo(builder); | 
 |     return builder.toString(); | 
 |   } | 
 |  | 
 |   private void appendInfo(StringBuilder builder) { | 
 |     builder.append("APPLICATION: "); | 
 |     appendApplication(builder); | 
 |     builder.append('\n'); | 
 |     appendProcessResult(builder); | 
 |   } | 
 |  | 
 |   private void appendApplication(StringBuilder builder) { | 
 |     builder.append(app == null ? "<default>" : app.toString()); | 
 |   } | 
 |  | 
 |   private void appendProcessResult(StringBuilder builder) { | 
 |     builder.append("COMMAND: ").append(result.command).append('\n').append(result); | 
 |   } | 
 |  | 
 |   public RR writeProcessResult(PrintStream ps) { | 
 |     StringBuilder sb = new StringBuilder(); | 
 |     appendProcessResult(sb); | 
 |     ps.println(sb.toString()); | 
 |     return self(); | 
 |   } | 
 | } |