blob: 5252f088a92f687491902b64873cf759fccca81a [file] [log] [blame]
// Copyright (c) 2017, 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.bisect;
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.bisect.BisectOptions.Result;
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.smali.SmaliBuilder;
import com.android.tools.r8.smali.SmaliBuilder.MethodSignature;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.ImmutableList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
public class BisectTest {
private final String[] CLASSES = {"A", "B", "C", "D", "E", "F", "G", "H"};
private final String ERRONEOUS_CLASS = "F";
private final String ERRONEOUS_METHOD = "foo";
private final String VALID_METHOD = "bar";
// Set during build to more easily inspect later.
private MethodSignature erroneousMethodSignature = null;
@Rule
public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
@Test
public void bisect() throws Exception {
InternalOptions options = new InternalOptions();
Timing timing = new Timing("bisect-test");
// Build "good" application with no method in "F".
SmaliBuilder builderGood = new SmaliBuilder();
for (String clazz : CLASSES) {
builderGood.addClass(clazz);
builderGood.addStaticMethod(
"void", VALID_METHOD, ImmutableList.of(), 0, "return-void");
}
AndroidApp inputGood =
AndroidApp.builder().addDexProgramData(builderGood.compile(), Origin.unknown()).build();
DexApplication appGood = new ApplicationReader(inputGood, options, timing).read();
// Build "bad" application with a method "foo" in "F".
SmaliBuilder builderBad = new SmaliBuilder();
for (String clazz : CLASSES) {
builderBad.addClass(clazz);
if (clazz.equals(ERRONEOUS_CLASS)) {
erroneousMethodSignature = builderBad.addStaticMethod(
"void", ERRONEOUS_METHOD, ImmutableList.of(), 0, "return-void");
} else {
builderBad.addStaticMethod(
"void", VALID_METHOD, ImmutableList.of(), 0, "return-void");
}
}
AndroidApp inputBad =
AndroidApp.builder().addDexProgramData(builderBad.compile(), Origin.unknown()).build();
DexApplication appBad = new ApplicationReader(inputBad, options, timing).read();
ExecutorService executor = Executors.newWorkStealingPool();
try {
BisectState state = new BisectState(appGood, appBad, null);
DexProgramClass clazz = Bisect.run(state, this::command, temp.newFolder().toPath(), executor);
System.out.println("Found bad class: " + clazz);
assertEquals(clazz.type.toString(), ERRONEOUS_CLASS);
} finally {
executor.shutdown();
}
}
private Result command(DexApplication application) {
CodeInspector inspector = new CodeInspector(application);
if (inspector
.clazz(ERRONEOUS_CLASS)
.method(erroneousMethodSignature.returnType,
erroneousMethodSignature.name,
erroneousMethodSignature.parameterTypes)
.isPresent()) {
return Result.BAD;
}
return Result.GOOD;
}
}