blob: cf56c9ef8293f8bb90c7c88e2accc220bcd28c52 [file] [log] [blame]
// 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.internal;
import static com.android.tools.r8.shaking.ProguardConfigurationParser.UNSUPPORTED_FLAG_OPTIONS;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.CompilationException;
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.D8Command;
import com.android.tools.r8.R8Command;
import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
import com.android.tools.r8.Resource;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.shaking.ProguardConfigurationSourceFile;
import com.android.tools.r8.shaking.ProguardConfigurationSourceStrings;
import com.android.tools.r8.shaking.ProguardRuleParserException;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.ArtErrorParser;
import com.android.tools.r8.utils.ArtErrorParser.ArtErrorInfo;
import com.android.tools.r8.utils.ArtErrorParser.ArtErrorParserException;
import com.android.tools.r8.utils.DexInspector;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.OutputMode;
import com.google.common.collect.ImmutableList;
import com.google.common.io.ByteStreams;
import com.google.common.io.Closer;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
import java.util.function.Predicate;
import org.junit.ComparisonFailure;
import org.junit.Rule;
import org.junit.rules.TemporaryFolder;
public abstract class CompilationTestBase {
private static final Predicate<String> BYPASSING_OPTIONS_FOR_TESTING =
line -> UNSUPPORTED_FLAG_OPTIONS.stream().noneMatch(line::contains);
@Rule
public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
public AndroidApp runAndCheckVerification(
CompilerUnderTest compiler,
CompilationMode mode,
String referenceApk,
String pgMap,
String pgConf,
String... inputs)
throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
return runAndCheckVerification(
compiler, mode, referenceApk, pgMap, pgConf, null, Arrays.asList(inputs));
}
public AndroidApp runAndCheckVerification(D8Command command, String referenceApk)
throws IOException, ExecutionException, CompilationException {
return checkVerification(ToolHelper.runD8(command), referenceApk);
}
public AndroidApp runAndCheckVerification(
CompilerUnderTest compiler,
CompilationMode mode,
String referenceApk,
String pgMap,
String pgConf,
Consumer<InternalOptions> optionsConsumer,
List<String> inputs)
throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
assertTrue(referenceApk == null || new File(referenceApk).exists());
AndroidApp outputApp;
if (compiler == CompilerUnderTest.R8) {
R8Command.Builder builder =
R8Command.builder()
.addProgramFiles(ListUtils.map(inputs, Paths::get))
.setMode(mode)
.setMinApiLevel(AndroidApiLevel.L.getLevel());
if (pgMap != null) {
builder.setProguardMapFile(Paths.get(pgMap));
}
if (pgConf != null) {
ProguardConfigurationSourceFile pgFile =
new ProguardConfigurationSourceFile(Paths.get(pgConf));
builder.addProguardConfiguration(
new ProguardConfigurationSourceStrings(
Paths.get(pgConf).getParent(),
Arrays.stream(pgFile.get().split(System.lineSeparator()))
.filter(BYPASSING_OPTIONS_FOR_TESTING)
.collect(ImmutableList.toImmutableList())));
}
builder.addProguardConfigurationConsumer(b -> {
b.setPrintSeeds(false);
});
outputApp = ToolHelper.runR8(builder.build(), optionsConsumer);
} else {
assert compiler == CompilerUnderTest.D8;
outputApp =
ToolHelper.runD8(
D8Command.builder()
.addProgramFiles(ListUtils.map(inputs, Paths::get))
.setMode(mode)
.setMinApiLevel(AndroidApiLevel.L.getLevel())
.build());
}
return checkVerification(outputApp, referenceApk);
}
public AndroidApp checkVerification(AndroidApp outputApp, String referenceApk)
throws IOException, ExecutionException {
Path out = temp.getRoot().toPath().resolve("all.zip");
Path oatFile = temp.getRoot().toPath().resolve("all.oat");
outputApp.writeToZip(out, OutputMode.Indexed);
try {
ToolHelper.runDex2Oat(out, oatFile);
return outputApp;
} catch (AssertionError e) {
if (referenceApk == null) {
throw e;
}
DexInspector theirs = new DexInspector(Paths.get(referenceApk));
DexInspector ours = new DexInspector(out);
List<ArtErrorInfo> errors;
try {
errors = ArtErrorParser.parse(e.getMessage());
} catch (ArtErrorParserException parserException) {
System.err.println(parserException.toString());
throw e;
}
if (errors.isEmpty()) {
throw e;
}
for (ArtErrorInfo error : errors.subList(0, errors.size() - 1)) {
System.err.println(new ComparisonFailure(error.getMessage(),
"REFERENCE\n" + error.dump(theirs, false) + "\nEND REFERENCE",
"PROCESSED\n" + error.dump(ours, true) + "\nEND PROCESSED").toString());
}
ArtErrorInfo error = errors.get(errors.size() - 1);
throw new ComparisonFailure(error.getMessage(),
"REFERENCE\n" + error.dump(theirs, false) + "\nEND REFERENCE",
"PROCESSED\n" + error.dump(ours, true) + "\nEND PROCESSED");
}
}
public int applicationSize(AndroidApp app) throws IOException {
int bytes = 0;
try (Closer closer = Closer.create()) {
for (Resource dex : app.getDexProgramResources()) {
bytes += ByteStreams.toByteArray(closer.register(dex.getStream())).length;
}
}
return bytes;
}
public void assertIdenticalApplications(AndroidApp app1, AndroidApp app2) throws IOException {
assertIdenticalApplications(app1, app2, false);
}
public void assertIdenticalApplications(AndroidApp app1, AndroidApp app2, boolean write)
throws IOException {
try (Closer closer = Closer.create()) {
if (write) {
app1.writeToDirectory(temp.newFolder("app1").toPath(), OutputMode.Indexed);
app2.writeToDirectory(temp.newFolder("app2").toPath(), OutputMode.Indexed);
}
List<Resource> files1 = app1.getDexProgramResources();
List<Resource> files2 = app2.getDexProgramResources();
assertEquals(files1.size(), files2.size());
for (int index = 0; index < files1.size(); index++) {
InputStream file1 = closer.register(files1.get(index).getStream());
InputStream file2 = closer.register(files2.get(index).getStream());
byte[] bytes1 = ByteStreams.toByteArray(file1);
byte[] bytes2 = ByteStreams.toByteArray(file2);
assertArrayEquals("File index " + index, bytes1, bytes2);
}
}
}
}