Merge "Make running test possible with CMD.exe"
diff --git a/.gitignore b/.gitignore
index a599785..bc26830 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,38 +22,24 @@
third_party/android_jar/lib.tar.gz
third_party/android_jar/lib-v[0-9][0-9]
third_party/android_jar/lib-v[0-9][0-9].tar.gz
-third_party/gmscore/v4
-third_party/gmscore/v4.tar.gz
-third_party/gmscore/v5
-third_party/gmscore/v5.tar.gz
-third_party/gmscore/v6
-third_party/gmscore/v6.tar.gz
-third_party/gmscore/v7
-third_party/gmscore/v7.tar.gz
-third_party/gmscore/v8
-third_party/gmscore/v8.tar.gz
-third_party/gmscore/gmscore_v9
-third_party/gmscore/gmscore_v9.tar.gz
-third_party/gmscore/gmscore_v10
-third_party/gmscore/gmscore_v10.tar.gz
-third_party/gmscore/latest
-third_party/gmscore/latest.tar.gz
+third_party/gmail/*
+!third_party/gmail/*.sha1
+third_party/gmscore/*
+!third_party/gmscore/*.sha1
third_party/gradle/gradle.tar.gz
third_party/gradle/gradle
third_party/jasmin.tar.gz
third_party/jasmin
third_party/jdwp-tests.tar.gz
third_party/jdwp-tests
+third_party/photos/*
+!third_party/photos/*.sha1
third_party/proguard/proguard5.2.1.tar.gz
third_party/proguard/proguard5.2.1
third_party/proguardsettings.tar.gz
third_party/proguardsettings/
-third_party/youtube/youtube.android_12.10
-third_party/youtube/youtube.android_12.10.tar.gz
-third_party/youtube/youtube.android_12.17/
-third_party/youtube/youtube.android_12.17.tar.gz
-third_party/youtube/youtube.android_12.22/
-third_party/youtube/youtube.android_12.22.tar.gz
+third_party/youtube/*
+!third_party/youtube/*sha1
third_party/jctf
third_party/jctf.tar.gz
third_party/android_cts_baseline
diff --git a/build.gradle b/build.gradle
index 6feb7a8..0a512f2 100644
--- a/build.gradle
+++ b/build.gradle
@@ -151,6 +151,7 @@
def x20Dependencies = [
"third_party": [
+ "gmail/gmail_android_170604.16.tar.gz",
"gmscore/v4.tar.gz",
"gmscore/v5.tar.gz",
"gmscore/v6.tar.gz",
@@ -160,9 +161,9 @@
"gmscore/gmscore_v10.tar.gz",
"gmscore/latest.tar.gz",
"photos/2017-06-06.tar.gz",
- "youtube/youtube.android_11.47.tar.gz",
"youtube/youtube.android_12.10.tar.gz",
"youtube/youtube.android_12.17.tar.gz",
+ "youtube/youtube.android_12.22.tar.gz",
"proguardsettings.tar.gz",
],
]
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 0a0c976..5a8ff11 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.dex.ApplicationWriter;
+import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.ir.conversion.IRConverter;
@@ -162,7 +163,7 @@
app = optimize(app, appInfo, options);
// If a method filter is present don't produce output since the application is likely partial.
- if (!options.methodsFilter.isEmpty()) {
+ if (options.hasMethodsFilter()) {
System.out.println("Finished compilation with method filter: ");
options.methodsFilter.forEach((m) -> System.out.println(" - " + m));
return null;
@@ -178,13 +179,17 @@
options.printWarnings();
return output;
} catch (ExecutionException e) {
- throw new RuntimeException(e.getMessage(), e.getCause());
+ if (e.getCause() instanceof CompilationError) {
+ throw (CompilationError) e.getCause();
+ } else {
+ throw new RuntimeException(e.getMessage(), e.getCause());
+ }
}
}
private static DexApplication optimize(
DexApplication application, AppInfo appInfo, InternalOptions options)
- throws IOException, ExecutionException {
+ throws IOException {
final CfgPrinter printer = options.printCfg ? new CfgPrinter() : null;
IRConverter converter = new IRConverter(application, appInfo, options, printer);
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index caf7f46..2116981 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -173,7 +173,7 @@
}
static CompilationResult runForTesting(AndroidApp app, InternalOptions options)
- throws ProguardRuleParserException, ExecutionException, IOException {
+ throws ProguardRuleParserException, IOException {
ExecutorService executor = ThreadUtils.getExecutorService(options);
try {
return runForTesting(app, options, executor);
@@ -186,12 +186,12 @@
AndroidApp app,
InternalOptions options,
ExecutorService executor)
- throws ProguardRuleParserException, ExecutionException, IOException {
+ throws ProguardRuleParserException, IOException {
return new R8(options).run(app, executor);
}
private CompilationResult run(AndroidApp inputApp, ExecutorService executorService)
- throws IOException, ExecutionException, ProguardRuleParserException {
+ throws IOException, ProguardRuleParserException {
if (options.quiet) {
System.setOut(new PrintStream(ByteStreams.nullOutputStream()));
}
@@ -324,7 +324,7 @@
timing.end();
// If a method filter is present don't produce output since the application is likely partial.
- if (!options.methodsFilter.isEmpty()) {
+ if (options.hasMethodsFilter()) {
System.out.println("Finished compilation with method filter: ");
options.methodsFilter.forEach((m) -> System.out.println(" - " + m));
return null;
@@ -363,6 +363,12 @@
}
options.printWarnings();
return new CompilationResult(androidApp, application, appInfo);
+ } catch (ExecutionException e) {
+ if (e.getCause() instanceof CompilationError) {
+ throw (CompilationError) e.getCause();
+ } else {
+ throw new RuntimeException(e.getMessage(), e.getCause());
+ }
} finally {
// Dump timings.
if (options.printTimes) {
@@ -381,7 +387,7 @@
* @return the compilation result.
*/
public static AndroidApp run(R8Command command)
- throws IOException, CompilationException, ExecutionException, ProguardRuleParserException {
+ throws IOException, CompilationException, ProguardRuleParserException {
AndroidApp outputApp =
runForTesting(command.getInputApp(), command.getInternalOptions()).androidApp;
if (command.getOutputPath() != null) {
@@ -401,7 +407,7 @@
* @return the compilation result.
*/
public static AndroidApp run(R8Command command, ExecutorService executor)
- throws IOException, CompilationException, ExecutionException, ProguardRuleParserException {
+ throws IOException, CompilationException, ProguardRuleParserException {
AndroidApp outputApp =
runForTesting(command.getInputApp(), command.getInternalOptions(), executor).androidApp;
if (command.getOutputPath() != null) {
@@ -411,7 +417,7 @@
}
private static void run(String[] args)
- throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+ throws IOException, ProguardRuleParserException, CompilationException {
R8Command.Builder builder = R8Command.parse(args);
if (builder.getOutputPath() == null) {
builder.setOutputPath(Paths.get("."));
@@ -442,7 +448,7 @@
} catch (ProguardRuleParserException e) {
System.err.println("Failed parsing proguard keep rules: " + e.getMessage());
System.exit(1);
- } catch (RuntimeException | ExecutionException e) {
+ } catch (RuntimeException e) {
System.err.println("Compilation failed with an internal error.");
Throwable cause = e.getCause() == null ? e : e.getCause();
cause.printStackTrace();
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
index 208d0f8..e7bf9db 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -70,13 +70,6 @@
readClassSources(builder, closer);
initializeLazyClassCollection(builder);
ThreadUtils.awaitFutures(futures);
- } catch (ExecutionException e) {
- // If the reading failed with a valid compilation error, rethrow the unwrapped exception.
- Throwable cause = e.getCause();
- if (cause != null && cause instanceof CompilationError) {
- throw (CompilationError) cause;
- }
- throw e;
} finally {
timing.end();
}
diff --git a/src/main/java/com/android/tools/r8/dex/VirtualFile.java b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
index 07fce3a..db8adab 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -575,6 +575,7 @@
this.originalNames = originalNames;
}
+ @Override
public List<DexProgramClass> call() {
String currentPrefix = null;
int currentFileId = -1;
@@ -598,9 +599,9 @@
inserted.add(clazz);
}
if (file.isFull()) {
- throw new RuntimeException(
+ throw new CompilationError(
"Cannot fit package " + currentPrefix
- + " in requested dex file, consider to remove mapping.");
+ + " in requested dex file, consider removing mapping.");
}
}
file.commitTransaction();
@@ -695,6 +696,7 @@
return originalNames != null ? originalNames.get(clazz) : clazz.toString();
}
+ @Override
public Map<String, Integer> call() throws IOException {
Iterator<VirtualFile> allFilesCyclic = Iterators.cycle(files.values());
Iterator<VirtualFile> activeFiles = Iterators.limit(allFilesCyclic, files.size());
@@ -797,7 +799,7 @@
}
private void addNonPackageClasses(Iterator<VirtualFile> activeFiles,
- List<DexProgramClass> nonPackageClasses) throws IOException {
+ List<DexProgramClass> nonPackageClasses) {
VirtualFile current;
current = activeFiles.next();
for (DexProgramClass clazz : nonPackageClasses) {
@@ -820,7 +822,7 @@
}
}
- private VirtualFile getVirtualFile(Iterator<VirtualFile> activeFiles) throws IOException {
+ private VirtualFile getVirtualFile(Iterator<VirtualFile> activeFiles) {
VirtualFile current = null;
while (activeFiles.hasNext() && (current = activeFiles.next()).isFilledEnough(options)) {}
if (current == null || current.isFilledEnough(options)) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexApplication.java b/src/main/java/com/android/tools/r8/graph/DexApplication.java
index 6ae0b16..af586db 100644
--- a/src/main/java/com/android/tools/r8/graph/DexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DexApplication.java
@@ -212,15 +212,38 @@
ps.append(builder.toString());
}
- /** Write smali source for the application code on the provided PrintStream. */
+ private void writeClassFooter(DexClass clazz, PrintStream ps) {
+ StringBuilder builder = new StringBuilder();
+ builder.append("# End of class ");
+ builder.append(clazz.type.toSmaliString());
+ builder.append("\n");
+ ps.append(builder.toString());
+ }
+
+ /**
+ * Write smali source for the application code on the provided PrintStream.
+ */
public void smali(InternalOptions options, PrintStream ps) {
List<DexProgramClass> classes = (List<DexProgramClass>) classes();
classes.sort(Comparator.comparing(DexProgramClass::toSourceString));
+ boolean firstClass = true;
for (DexClass clazz : classes) {
boolean classHeaderWritten = false;
+ if (!options.hasMethodsFilter()) {
+ if (!firstClass) {
+ ps.append("\n");
+ firstClass = false;
+ }
+ writeClassHeader(clazz, ps);
+ classHeaderWritten = true;
+ }
for (DexEncodedMethod method : clazz.virtualMethods()) {
if (options.methodMatchesFilter(method)) {
if (!classHeaderWritten) {
+ if (!firstClass) {
+ ps.append("\n");
+ firstClass = false;
+ }
writeClassHeader(clazz, ps);
classHeaderWritten = true;
}
@@ -231,6 +254,10 @@
for (DexEncodedMethod method : clazz.directMethods()) {
if (options.methodMatchesFilter(method)) {
if (!classHeaderWritten) {
+ if (!firstClass) {
+ ps.append("\n");
+ firstClass = false;
+ }
writeClassHeader(clazz, ps);
classHeaderWritten = true;
}
@@ -238,6 +265,10 @@
ps.append(method.toSmaliString(getProguardMap()));
}
}
+ if (classHeaderWritten) {
+ ps.append("\n");
+ writeClassFooter(clazz, ps);
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index 860fc45..d760b93 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -41,7 +41,7 @@
"optimizationpasses",
"target");
private static final List<String> ignoredOptionalSingleArgOptions = ImmutableList
- .of("keepdirectories");
+ .of("keepdirectories", "runtype", "laststageoutput");
private static final List<String> ignoredFlagOptions = ImmutableList
.of("forceprocessing", "dontusemixedcaseclassnames",
"dontpreverify", "experimentalshrinkunusedprotofields",
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index bb8bc7c..15fc89c 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -98,9 +98,13 @@
return printed;
}
+ public boolean hasMethodsFilter() {
+ return methodsFilter.size() > 0;
+ }
+
public boolean methodMatchesFilter(DexEncodedMethod method) {
// Not specifying a filter matches all methods.
- if (methodsFilter.size() == 0) {
+ if (!hasMethodsFilter()) {
return true;
}
// Currently the filter is simple string equality on the qualified name.
diff --git a/src/test/java/com/android/tools/r8/JctfTestSpecifications.java b/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
index b1934dd..f7cbee8 100644
--- a/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
+++ b/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
@@ -4686,8 +4686,7 @@
// 1) t02
// java.lang.AssertionError: java.lang.AssertionError: expected:<5> but was:<4>
- .put("lang.Thread.interrupt.Thread_interrupt_A04",
- match(D8_COMPILER, runtimes(DexVm.ART_6_0_1)))
+ .put("lang.Thread.interrupt.Thread_interrupt_A04", any())
// Been running fine for a while then this happened on a bot:
// 1) t01
// java.lang.AssertionError: expected:<BLOCKED> but was:<RUNNABLE>
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
index 52bbda6..4923a27 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -379,4 +379,15 @@
);
parser.parse(proguardConfig);
}
+
+ @Test
+ public void parseCustomFlags() throws Exception {
+ ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+ // Custom Proguard flags -runtype and -laststageoutput are ignored.
+ Path proguardConfig = writeTextToTempFile(
+ "-runtype FINAL ",
+ "-laststageoutput /some/file/name "
+ );
+ parser.parse(proguardConfig);
+ }
}
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliDisassembleTest.java b/src/test/java/com/android/tools/r8/smali/SmaliDisassembleTest.java
index 0c1d13d..66d0304 100644
--- a/src/test/java/com/android/tools/r8/smali/SmaliDisassembleTest.java
+++ b/src/test/java/com/android/tools/r8/smali/SmaliDisassembleTest.java
@@ -68,7 +68,9 @@
" mul-int/2addr v2, v1\n" +
" div-int/2addr v3, v2\n" +
" return v3\n" +
- ".end method\n";
+ ".end method\n" +
+ "\n" +
+ "# End of class LTest;\n";
assertEquals(expected, application.smali(new InternalOptions()));
@@ -122,7 +124,9 @@
" 0x00000001 -> :label_5 # 1\n" +
" 0x00000002 -> :label_7 # 2\n" +
" .end sparse-switch\n" +
- ".end method\n";
+ ".end method\n" +
+ "\n" +
+ "# End of class LTest;\n";
assertEquals(expected, application.smali(new InternalOptions()));
@@ -176,7 +180,9 @@
" :label_5\n" +
" :label_7\n" +
" .end packed-switch\n" +
- ".end method\n";
+ ".end method\n" +
+ "\n" +
+ "# End of class LTest;\n";
assertEquals(expected, application.smali(new InternalOptions()));
@@ -217,7 +223,9 @@
" 0x02 # 2\n" +
" 0xff # 255\n" +
" .end array-data\n" +
- ".end method\n";
+ ".end method\n" +
+ "\n" +
+ "# End of class LTest;\n";
assertEquals(expected, application.smali(new InternalOptions()));
@@ -258,7 +266,9 @@
" 0x0002 # 2\n" +
" 0xffff # 65535\n" +
" .end array-data\n" +
- ".end method\n";
+ ".end method\n" +
+ "\n" +
+ "# End of class LTest;\n";
assertEquals(expected, application.smali(new InternalOptions()));
@@ -299,7 +309,9 @@
" 0x00000002 # 2\n" +
" 0xffffffff # 4294967295\n" +
" .end array-data\n" +
- ".end method\n";
+ ".end method\n" +
+ "\n" +
+ "# End of class LTest;\n";
assertEquals(expected, application.smali(new InternalOptions()));
@@ -340,7 +352,9 @@
" 0x0000000000000002 # 2\n" +
" 0xffffffffffffffff # -1\n" +
" .end array-data\n" +
- ".end method\n";
+ ".end method\n" +
+ "\n" +
+ "# End of class LTest;\n";
assertEquals(expected, application.smali(new InternalOptions()));
@@ -361,7 +375,9 @@
".super Ljava/lang/Object;\n" +
"\n" +
".method public abstract test()I\n" +
- ".end method\n";
+ ".end method\n" +
+ "\n" +
+ "# End of class LTest;\n";
assertEquals(expected, application.smali(new InternalOptions()));
@@ -383,7 +399,9 @@
".implements Ljava/util/List;\n" +
"\n" +
".method public abstract test()I\n" +
- ".end method\n";
+ ".end method\n" +
+ "\n" +
+ "# End of class LTest;\n";
assertEquals(expected, application.smali(new InternalOptions()));
diff --git a/third_party/android_cts_baseline.tar.gz.sha1 b/third_party/android_cts_baseline.tar.gz.sha1
index 7126671..5efd8e5 100644
--- a/third_party/android_cts_baseline.tar.gz.sha1
+++ b/third_party/android_cts_baseline.tar.gz.sha1
@@ -1 +1 @@
-3914f251fc8b8669b2ec92766696267c1a9ac7ff
\ No newline at end of file
+499d86b9b9eba837261c2d10e5b76f3a3a9a9952
\ No newline at end of file
diff --git a/third_party/gmail/gmail_android_170604.16.tar.gz.sha1 b/third_party/gmail/gmail_android_170604.16.tar.gz.sha1
new file mode 100644
index 0000000..f57ba90
--- /dev/null
+++ b/third_party/gmail/gmail_android_170604.16.tar.gz.sha1
@@ -0,0 +1 @@
+161c569821a5c9b4cb8e99de764f3449191af084
\ No newline at end of file
diff --git a/tools/compare_cts_results.py b/tools/compare_cts_results.py
index 95e29d1..00901ec 100755
--- a/tools/compare_cts_results.py
+++ b/tools/compare_cts_results.py
@@ -9,9 +9,10 @@
from os.path import basename
import argparse
import os
-import re
import sys
+import utils
+
class Module:
def __init__(self):
self.test_cases = {}
@@ -90,35 +91,19 @@
# Read CTS test_result.xml from file and merge into result_tree
def add_to_result_tree(result_tree, file_xml, file_idx):
- re_module = re.compile('<Module name="([^"]*)"')
- re_test_case = re.compile('<TestCase name="([^"]*)"')
- re_test = re.compile('<Test result="(pass|fail)" name="([^"]*)"')
module = None
test_case = None
- with open(file_xml) as f:
- for line in f:
- m = re_module.search(line)
- if m:
- module_name = m.groups()[0]
- module = result_tree.setdefault(module_name, Module())
- module.set_file_index_present(file_idx)
- continue
-
- m = re_test_case.search(line)
- if m:
- test_case_name = m.groups()[0]
- test_case = module.get_test_case_maybe_create(test_case_name)
- test_case.set_file_index_present(file_idx)
- continue
-
- m = re_test.search(line)
- if m:
- outcome = m.groups()[0]
- test_name = m.groups()[1]
- assert outcome in ["fail", "pass"]
-
- v = test_case.get_test_maybe_create(test_name)
- v.set_file_index_outcome(outcome == 'pass', file_idx)
+ for x in utils.read_cts_test_result(file_xml):
+ if type(x) is utils.CtsModule:
+ module = result_tree.setdefault(x.name, Module())
+ module.set_file_index_present(file_idx)
+ elif type(x) is utils.CtsTestCase:
+ test_case = module.get_test_case_maybe_create(x.name)
+ test_case.set_file_index_present(file_idx)
+ else:
+ assert(type(x) is utils.CtsTest)
+ v = test_case.get_test_maybe_create(x.name)
+ v.set_file_index_outcome(x.outcome, file_idx)
# main tree_report function
def tree_report(result_tree, files, diff_only):
diff --git a/tools/gmail_data.py b/tools/gmail_data.py
new file mode 100644
index 0000000..d09a3e8
--- /dev/null
+++ b/tools/gmail_data.py
@@ -0,0 +1,35 @@
+# 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.
+
+import glob
+import os
+import utils
+
+THIRD_PARTY = os.path.join(utils.REPO_ROOT, 'third_party')
+BASE = os.path.join(THIRD_PARTY, 'gmail')
+
+V170604_16_BASE = os.path.join(BASE, 'gmail_android_170604.16')
+V170604_16_PREFIX = os.path.join(V170604_16_BASE, 'Gmail_release_unstripped')
+
+# NOTE: We always use android.jar for SDK v25 for now.
+ANDROID_JAR = os.path.join(THIRD_PARTY, 'android_jar', 'lib-v25', 'android.jar')
+
+VERSIONS = {
+ '170604.16': {
+ 'dex' : {
+ 'inputs': [os.path.join(V170604_16_BASE, 'Gmail_release_unsigned.apk')],
+ 'pgmap': '%s_proguard.map' % V170604_16_PREFIX,
+ 'libraries' : [ANDROID_JAR],
+ 'r8-flags': '--ignore-missing-classes',
+ },
+ 'deploy' : {
+ 'inputs': ['%s_deploy.jar' % V170604_16_PREFIX],
+ 'pgconf': ['%s_proguard.config' % V170604_16_PREFIX],
+ },
+ 'proguarded' : {
+ 'inputs': ['%s_proguard.jar' % V170604_16_PREFIX],
+ 'pgmap': '%s_proguard.map' % V170604_16_PREFIX,
+ }
+ },
+}
diff --git a/tools/run-r8-on-gmscore.py b/tools/run-r8-on-gmscore.py
index c00a58d..6d09760 100755
--- a/tools/run-r8-on-gmscore.py
+++ b/tools/run-r8-on-gmscore.py
@@ -5,7 +5,8 @@
import sys
-import run_r8_on_app
+import run_on_app
if __name__ == '__main__':
- sys.exit(run_r8_on_app.main())
+ # Default compiler is R8.
+ sys.exit(run_on_app.main())
diff --git a/tools/run_r8_on_app.py b/tools/run_on_app.py
similarity index 73%
rename from tools/run_r8_on_app.py
rename to tools/run_on_app.py
index 804a27e..966f786 100755
--- a/tools/run_r8_on_app.py
+++ b/tools/run_on_app.py
@@ -6,16 +6,22 @@
import optparse
import os
import r8
+import d8
import sys
import gmscore_data
import youtube_data
+import gmail_data
TYPES = ['dex', 'deploy', 'proguarded']
-APPS = ['gmscore', 'youtube']
+APPS = ['gmscore', 'youtube', 'gmail']
def ParseOptions():
result = optparse.OptionParser()
+ result.add_option('--compiler',
+ help='',
+ default='r8',
+ choices=['d8', 'r8'])
result.add_option('--app',
help='',
default='gmscore',
@@ -69,6 +75,9 @@
elif options.app == 'youtube':
options.version = options.version or '12.22'
data = youtube_data
+ elif options.app == 'gmail':
+ options.version = options.version or '170604.16'
+ data = gmail_data
else:
raise 'Unexpected'
@@ -85,18 +94,21 @@
return 1
values = version[options.type]
inputs = None
- # For 'deploy' the JAR is located using the Proguard configuration -injars option.
- if 'inputs' in values and options.type != 'deploy':
+ # For R8 'deploy' the JAR is located using the Proguard configuration -injars option.
+ if 'inputs' in values and (options.compiler != 'r8' or options.type != 'deploy'):
inputs = values['inputs']
args.extend(['--output', outdir])
- if 'pgmap' in values:
- args.extend(['--pg-map', values['pgmap']])
- if 'pgconf' in values and not options.k:
- for pgconf in values['pgconf']:
- args.extend(['--pg-conf', pgconf])
- if options.k:
- args.extend(['--pg-conf', options.k])
+
+ if options.compiler == 'r8':
+ if 'pgmap' in values:
+ args.extend(['--pg-map', values['pgmap']])
+ if 'pgconf' in values and not options.k:
+ for pgconf in values['pgconf']:
+ args.extend(['--pg-conf', pgconf])
+ if options.k:
+ args.extend(['--pg-conf', options.k])
+
if not options.no_libraries and 'libraries' in values:
for lib in values['libraries']:
args.extend(['--lib', lib])
@@ -104,10 +116,11 @@
if not outdir.endswith('.zip') and not outdir.endswith('.jar') and not os.path.exists(outdir):
os.makedirs(outdir)
- if 'r8-flags' in values:
- args.extend(values['r8-flags'].split(' '))
- if options.r8_flags:
- args.extend(options.r8_flags.split(' '))
+ if options.compiler == 'r8':
+ if 'r8-flags' in values:
+ args.extend(values['r8-flags'].split(' '))
+ if options.r8_flags:
+ args.extend(options.r8_flags.split(' '))
if inputs:
args.extend(inputs)
@@ -116,8 +129,12 @@
with open(options.dump_args_file, 'w') as args_file:
args_file.writelines([arg + os.linesep for arg in args])
else:
- r8.run(args, not options.no_build, not options.no_debug, options.profile,
- options.track_memory_to_file)
+ if options.compiler == 'd8':
+ d8.run(args, not options.no_build, not options.no_debug, options.profile,
+ options.track_memory_to_file)
+ else:
+ r8.run(args, not options.no_build, not options.no_debug, options.profile,
+ options.track_memory_to_file)
if __name__ == '__main__':
sys.exit(main())
diff --git a/tools/test_android_cts.py b/tools/test_android_cts.py
index 384ce70..606611d 100755
--- a/tools/test_android_cts.py
+++ b/tools/test_android_cts.py
@@ -14,11 +14,9 @@
# cd build/aosp
# repo manifest -o ../../third_party/aosp_manifest.xml -r
#
-# The baseline is the `test_result.xml` file which is created with an AOSP
-# build which uses the default (JACK) toolset.
-#
-# Use this script, with '--tool=jack' to reproduce the baseline results
-#
+# The baseline is a set of `test_result.xml` files in
+# third_party/android_cts_baseline/jack. The current test considered a success
+# if all tests pass that consistently pass in the baseline.
from __future__ import print_function
from glob import glob
@@ -30,12 +28,13 @@
import os
import re
import sys
+import time
import gradle
import utils
-CTS_BASELINE = join(utils.REPO_ROOT,
- 'third_party/android_cts_baseline/test_result.xml')
+CTS_BASELINE_FILES_DIR = join(utils.REPO_ROOT,
+ 'third_party/android_cts_baseline/jack')
AOSP_MANIFEST_XML = join(utils.REPO_ROOT, 'third_party',
'aosp_manifest.xml')
AOSP_HELPER_SH = join(utils.REPO_ROOT, 'scripts', 'aosp_helper.sh')
@@ -96,97 +95,33 @@
return False
return True
-# Read the xml test result file into an in-memory tree:
-# Extract only the Module/TestCase/Test names and outcome (True|False for
-# PASS|FAIL):
-#
-# tree[module_name][testcase_name][test_name] = True|False
-#
-def read_test_result_into_tree(filename):
- re_module = re.compile('<Module name="([^"]*)"')
- re_testcase = re.compile('<TestCase name="([^"]*)"')
- re_test = re.compile('<Test result="(pass|fail)" name="([^"]*)"')
+# Return list of fully qualified names of tests passing in
+# all the files.
+def consistently_passing_tests_from_test_results(filenames):
tree = {}
module = None
testcase = None
- with open(filename) as f:
- for line in f:
- m = re_module.search(line)
- if m:
- module_name = m.groups()[0]
- tree[module_name] = {}
- module = tree[module_name]
- continue
+ # Build a tree with leaves True|False|None for passing, failing and flaky
+ # tests.
+ for f in filenames:
+ for x in utils.read_cts_test_result(f):
+ if type(x) is utils.CtsModule:
+ module = tree.setdefault(x.name, {})
+ elif type(x) is utils.CtsTestCase:
+ testcase = module.setdefault(x.name, {})
+ else:
+ outcome = testcase.setdefault(x.name, x.outcome)
+ if outcome is not None and outcome != x.outcome:
+ testcase[x.name] = None
- m = re_testcase.search(line)
- if m:
- testcase_name = m.groups()[0]
- module[testcase_name] = {}
- testcase = module[testcase_name]
- continue
+ result = []
+ for module_name, module in tree.iteritems():
+ for test_case_name, test_case in module.iteritems():
+ result.extend(['{}/{}/{}'.format(module_name, test_case_name, test_name)
+ for test_name, test in test_case.iteritems()
+ if test])
- m = re_test.search(line)
- if m:
- outcome = m.groups()[0]
- test_name = m.groups()[1]
- assert outcome in ["fail", "pass"]
- testcase[test_name] = outcome == "pass"
- return tree
-
-# Report the items with the title
-def report_key_diff(title, items, prefix = ''):
- if len(items) > 0:
- print(title, ":")
- for x in items:
- print("- {}{}".format(prefix, x))
- print()
-
-
-def diff_sets(base_minus_result_title, result_minus_base_title,
- base_set, result_set, prefix = ''):
- base_minus_result = base_set - result_set
- result_minus_base = result_set - base_set
- report_key_diff(base_minus_result_title, base_minus_result, prefix)
- report_key_diff(result_minus_base_title, result_minus_base, prefix)
- return len(base_minus_result) > 0 or len(result_minus_base) > 0
-
-def diff_tree_report(baseline_tree, result_tree):
- baseline_modules = set(baseline_tree.keys())
- result_modules = set(result_tree.keys())
- differ = diff_sets('Modules missing from current result',
- 'New modules appeared in current result',
- baseline_modules, result_modules)
- for module in (result_modules & baseline_modules):
- baseline_module = baseline_tree[module]
- result_module = result_tree[module]
- baseline_testcases = set(baseline_module.keys())
- result_testcases = set(result_module.keys())
- differ = diff_sets('Test cases missing from current result',
- 'New test cases appeared in current result',
- baseline_testcases, result_testcases, module + '/') \
- or differ
- for testcase in (result_testcases & baseline_testcases):
- baseline_testcase = baseline_module[testcase]
- result_testcase = result_module[testcase]
- baseline_tests = set(baseline_testcase.keys())
- result_tests = set(result_testcase.keys())
- differ = diff_sets('Tests missing from current result',
- 'New tests appeared in current result',
- baseline_tests, result_tests, module + '/' + testcase + '/') \
- or differ
- need_newline_at_end = False
- for test in (result_tests & baseline_tests):
- baseline_outcome = baseline_testcase[test]
- result_outcome = result_testcase[test]
- if baseline_outcome != result_outcome:
- differ = True
- print('Test: {}/{}/{}, change: {}'.format(
- module, testcase, test,
- 'PASS -> FAIL' if baseline_outcome else 'FAIL -> PASS'))
- need_newline_at_end = True
- if need_newline_at_end:
- print()
- return differ
+ return result
def setup_and_clean(tool_is_d8, clean_dex):
# Two output dirs, one for the android image and one for cts tests.
@@ -296,8 +231,6 @@
re_summary = re.compile('<Summary ')
summaries = [('Summary from current test results: ', results_xml)]
- if not args.no_baseline:
- summaries.append(('Summary from baseline: ', CTS_BASELINE))
for (title, result_file) in summaries:
print(title, result_file)
@@ -312,10 +245,31 @@
else:
print('Comparing test results to baseline:\n')
- result_tree = read_test_result_into_tree(results_xml)
- baseline_tree = read_test_result_into_tree(CTS_BASELINE)
+ passing_tests = consistently_passing_tests_from_test_results([results_xml])
+ baseline_results = \
+ [f for f in glob(join(CTS_BASELINE_FILES_DIR, '*.xml'))]
+ assert len(baseline_results) != 0
- r = EXIT_FAILURE if diff_tree_report(baseline_tree, result_tree) else 0
+ passing_tests_in_baseline = \
+ consistently_passing_tests_from_test_results(baseline_results)
+
+ missing_or_failing_tests = \
+ set(passing_tests_in_baseline) - set(passing_tests)
+
+ num_tests = len(missing_or_failing_tests)
+ if num_tests != 0:
+ if num_tests > 1:
+ text = '{} tests that consistently pass in the baseline' \
+ ' are missing or failing in the current test:'.format(num_tests)
+ else:
+ text = '1 test that consistently passes in the baseline' \
+ ' is missing or failing in the current test:'
+ print(text)
+ for t in missing_or_failing_tests:
+ print(t)
+ r = EXIT_FAILURE
+ else:
+ r = 0
if args.save_result:
copy2(results_xml, args.save_result)
diff --git a/tools/utils.py b/tools/utils.py
index 67cac46..7379e16 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -6,6 +6,7 @@
import hashlib
import os
+import re
import shutil
import subprocess
import sys
@@ -69,3 +70,40 @@
def __exit__(self, *_):
print "Enter directory = ", self._old_cwd
os.chdir(self._old_cwd)
+
+# Reading Android CTS test_result.xml
+
+class CtsModule(object):
+ def __init__(self, module_name):
+ self.name = module_name
+
+class CtsTestCase(object):
+ def __init__(self, test_case_name):
+ self.name = test_case_name
+
+class CtsTest(object):
+ def __init__(self, test_name, outcome):
+ self.name = test_name
+ self.outcome = outcome
+
+# Generator yielding CtsModule, CtsTestCase or CtsTest from
+# reading through a CTS test_result.xml file.
+def read_cts_test_result(file_xml):
+ re_module = re.compile('<Module name="([^"]*)"')
+ re_test_case = re.compile('<TestCase name="([^"]*)"')
+ re_test = re.compile('<Test result="(pass|fail)" name="([^"]*)"')
+ with open(file_xml) as f:
+ for line in f:
+ m = re_module.search(line)
+ if m:
+ yield CtsModule(m.groups()[0])
+ continue
+ m = re_test_case.search(line)
+ if m:
+ yield CtsTestCase(m.groups()[0])
+ continue
+ m = re_test.search(line)
+ if m:
+ outcome = m.groups()[0]
+ assert outcome in ["fail", "pass"]
+ yield CtsTest(m.groups()[1], outcome == 'pass')