blob: 0c84e7cacc4652545a632d2acfb7185a00a9f127 [file] [log] [blame]
// 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 com.android.tools.r8.TestBase.Backend.DEX;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.fail;
import com.android.tools.r8.TestBase.Backend;
import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.debug.CfDebugTestConfig;
import com.android.tools.r8.debug.DebugTestConfig;
import com.android.tools.r8.debug.DexDebugTestConfig;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import org.hamcrest.Matcher;
public abstract class TestCompileResult<
CR extends TestCompileResult<CR, RR>, RR extends TestRunResult> {
final TestState state;
public final AndroidApp app;
final List<Path> additionalRunClassPath = new ArrayList<>();
TestCompileResult(TestState state, AndroidApp app) {
this.state = state;
this.app = app;
}
public abstract CR self();
public abstract Backend getBackend();
public abstract TestDiagnosticMessages getDiagnosticMessages();
protected abstract RR createRunResult(ProcessResult result);
public RR run(Class<?> mainClass) throws IOException {
return run(mainClass.getTypeName());
}
public RR run(String mainClass) throws IOException {
switch (getBackend()) {
case DEX:
return runArt(additionalRunClassPath, mainClass);
case CF:
return runJava(additionalRunClassPath, mainClass);
default:
throw new Unreachable();
}
}
public CR addRunClasspath(List<Path> classpath) {
additionalRunClassPath.addAll(classpath);
return self();
}
public CR writeToZip(Path file) throws IOException {
app.writeToZip(file, getBackend() == DEX ? OutputMode.DexIndexed : OutputMode.ClassFile);
return self();
}
public CodeInspector inspector() throws IOException, ExecutionException {
return new CodeInspector(app);
}
public CR inspect(Consumer<CodeInspector> consumer) throws IOException, ExecutionException {
consumer.accept(inspector());
return self();
}
public CR assertNoMessages() {
assertEquals(0, getDiagnosticMessages().getInfos().size());
assertEquals(0, getDiagnosticMessages().getWarnings().size());
assertEquals(0, getDiagnosticMessages().getErrors().size());
return self();
}
public CR assertOnlyInfos() {
assertNotEquals(0, getDiagnosticMessages().getInfos().size());
assertEquals(0, getDiagnosticMessages().getWarnings().size());
assertEquals(0, getDiagnosticMessages().getErrors().size());
return self();
}
public CR assertOnlyWarnings() {
assertEquals(0, getDiagnosticMessages().getInfos().size());
assertNotEquals(0, getDiagnosticMessages().getWarnings().size());
assertEquals(0, getDiagnosticMessages().getErrors().size());
return self();
}
public CR assertWarningMessageThatMatches(Matcher<String> matcher) {
assertNotEquals(0, getDiagnosticMessages().getWarnings().size());
for (int i = 0; i < getDiagnosticMessages().getWarnings().size(); i++) {
if (matcher.matches(getDiagnosticMessages().getWarnings().get(i).getDiagnosticMessage())) {
return self();
}
}
StringBuilder builder = new StringBuilder("No warning matches " + matcher.toString());
builder.append(System.lineSeparator());
if (getDiagnosticMessages().getWarnings().size() == 0) {
builder.append("There where no warnings.");
} else {
builder.append("There where " + getDiagnosticMessages().getWarnings().size() + " warnings:");
builder.append(System.lineSeparator());
for (int i = 0; i < getDiagnosticMessages().getWarnings().size(); i++) {
builder.append(getDiagnosticMessages().getWarnings().get(i).getDiagnosticMessage());
builder.append(System.lineSeparator());
}
}
fail(builder.toString());
return self();
}
public CR assertNoWarningMessageThatMatches(Matcher<String> matcher) {
assertNotEquals(0, getDiagnosticMessages().getWarnings().size());
for (int i = 0; i < getDiagnosticMessages().getWarnings().size(); i++) {
String message = getDiagnosticMessages().getWarnings().get(i).getDiagnosticMessage();
if (matcher.matches(message)) {
fail("The warning: \"" + message + "\" + matches " + matcher + ".");
}
}
return self();
}
public CR disassemble(PrintStream ps) throws IOException, ExecutionException {
ToolHelper.disassemble(app, ps);
return self();
}
public CR disassemble() throws IOException, ExecutionException {
return disassemble(System.out);
}
public DebugTestConfig debugConfig() {
// Rethrow exceptions since debug config is usually used in a delayed wrapper which
// does not declare exceptions.
try {
Path out = state.getNewTempFolder().resolve("out.zip");
switch (getBackend()) {
case CF:
{
app.writeToZip(out, OutputMode.ClassFile);
return new CfDebugTestConfig().addPaths(out);
}
case DEX:
{
app.writeToZip(out, OutputMode.DexIndexed);
return new DexDebugTestConfig().addPaths(out);
}
default:
throw new Unreachable();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private RR runJava(List<Path> additionalClassPath, String mainClass) throws IOException {
Path out = state.getNewTempFolder().resolve("out.zip");
app.writeToZip(out, OutputMode.ClassFile);
List<Path> classPath = ImmutableList.<Path>builder()
.addAll(additionalClassPath)
.add(out)
.build();
ProcessResult result = ToolHelper.runJava(classPath, mainClass);
return createRunResult(result);
}
private RR runArt(List<Path> additionalClassPath, String mainClass) throws IOException {
Path out = state.getNewTempFolder().resolve("out.zip");
app.writeToZip(out, OutputMode.DexIndexed);
List<String> classPath = ImmutableList.<String>builder()
.addAll(additionalClassPath.stream().map(Path::toString).collect(Collectors.toList()))
.add(out.toString())
.build();
ProcessResult result = ToolHelper.runArtRaw(classPath, mainClass, dummy -> {});
return createRunResult(result);
}
public Dex2OatTestRunResult runDex2Oat() throws IOException {
return runDex2Oat(ToolHelper.getDexVm());
}
public Dex2OatTestRunResult runDex2Oat(DexVm vm) throws IOException {
assert getBackend() == DEX;
Path tmp = state.getNewTempFolder();
Path jarFile = tmp.resolve("out.jar");
Path oatFile = tmp.resolve("out.oat");
app.writeToZip(jarFile, OutputMode.DexIndexed);
return new Dex2OatTestRunResult(app, ToolHelper.runDex2OatRaw(jarFile, oatFile, vm));
}
}