blob: ccb762b1d3e05a38b061789ab51d15f2ec43b4e3 [file] [log] [blame]
Tamas Kenez971eec62017-05-24 11:08:40 +02001#!/usr/bin/env python
2# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
3# for details. All rights reserved. Use of this source code is governed by a
4# BSD-style license that can be found in the LICENSE file.
5
6# Clone and build AOSP, using D8 instead of JACK or DX,
7# then run the Android-CTS on the emulator and compare results
8# to a baseline.
9#
10# This script uses the repo manifest file 'third_party/aosp_manifest.xml'
11# which is a snapshot of the aosp repo set.
12# The manifest file can be updated with the following commands:
13#
14# cd build/aosp
15# repo manifest -o ../../third_party/aosp_manifest.xml -r
16#
Tamas Kenez8a4f0772017-06-13 09:16:30 +020017# The baseline is a set of `test_result.xml` files in
18# third_party/android_cts_baseline/jack. The current test considered a success
19# if all tests pass that consistently pass in the baseline.
Tamas Kenez971eec62017-05-24 11:08:40 +020020
21from __future__ import print_function
22from glob import glob
23from itertools import chain
24from os.path import join
25from shutil import copy2
26from subprocess import check_call, Popen
27import argparse
28import os
29import re
30import sys
Tamas Kenez82efeb52017-06-12 13:56:22 +020031import time
Tamas Kenez971eec62017-05-24 11:08:40 +020032
Søren Gjesse298759e2017-09-08 08:22:41 +020033import checkout_aosp
Tamas Kenez971eec62017-05-24 11:08:40 +020034import gradle
35import utils
36
Tamas Kenez8a4f0772017-06-13 09:16:30 +020037CTS_BASELINE_FILES_DIR = join(utils.REPO_ROOT,
38 'third_party/android_cts_baseline/jack')
Tamas Kenez971eec62017-05-24 11:08:40 +020039AOSP_MANIFEST_XML = join(utils.REPO_ROOT, 'third_party',
40 'aosp_manifest.xml')
41AOSP_HELPER_SH = join(utils.REPO_ROOT, 'scripts', 'aosp_helper.sh')
42
Tamas Kenez971eec62017-05-24 11:08:40 +020043D8LOGGER_JAR = join(utils.REPO_ROOT, 'build/libs/d8logger.jar')
44
45AOSP_ROOT = join(utils.REPO_ROOT, 'build/aosp')
46
47AOSP_MANIFEST_URL = 'https://android.googlesource.com/platform/manifest'
48AOSP_PRESET = 'aosp_x86-eng'
49
50AOSP_OUT = join(AOSP_ROOT, 'out')
51OUT_IMG = join(AOSP_ROOT, 'out_img') # output dir for android img build
52OUT_CTS = join(AOSP_ROOT, 'out_cts') # output dir for CTS build
53RESULTS_DIR_BASE = join(OUT_CTS, 'host/linux-x86/cts/android-cts/results')
54CTS_TRADEFED = join(OUT_CTS,
55 'host/linux-x86/cts/android-cts/tools/cts-tradefed')
56
Søren Gjesse298759e2017-09-08 08:22:41 +020057J_DEFAULT = '8'
58J_OPTION = '-j' + J_DEFAULT
Tamas Kenez971eec62017-05-24 11:08:40 +020059
60EXIT_FAILURE = 1
61
62def parse_arguments():
63 parser = argparse.ArgumentParser(
64 description = 'Download the AOSP source tree, build an Android image'
65 ' and the CTS targets and run CTS with the emulator on the image.')
66 parser.add_argument('--tool',
67 choices = ['jack', 'dx', 'd8'],
68 default = 'd8',
69 help='compiler tool to use')
70 parser.add_argument('--d8log',
71 metavar = 'FILE',
72 help = 'Enable logging d8 (compatdx) calls to the specified file. Works'
73 ' only with --tool=d8')
Tamas Kenez507481b2017-06-06 15:01:33 +020074 parser.add_argument('--save-result',
75 metavar = 'FILE',
76 help = 'Save final test_result.xml to the specified file.')
77 parser.add_argument('--no-baseline',
78 action = 'store_true',
79 help = "Don't compare results to baseline hence don't return failure if"
80 ' they differ.')
81 parser.add_argument('--clean-dex',
82 action = 'store_true',
83 help = 'Remove AOSP/dex files always, before the build. By default they'
84 " are removed only if '--tool=d8' and they're older then the D8 tool")
Tamas Kenez971eec62017-05-24 11:08:40 +020085 return parser.parse_args()
86
87# return False on error
88def remove_aosp_out():
89 if os.path.exists(AOSP_OUT):
90 if os.path.islink(AOSP_OUT):
91 os.remove(AOSP_OUT)
92 else:
93 print("The AOSP out directory ('" + AOSP_OUT + "') is expected"
94 " to be a symlink", file = sys.stderr)
95 return False
96 return True
97
Tamas Kenez8a4f0772017-06-13 09:16:30 +020098# Return list of fully qualified names of tests passing in
99# all the files.
100def consistently_passing_tests_from_test_results(filenames):
Tamas Kenez971eec62017-05-24 11:08:40 +0200101 tree = {}
102 module = None
103 testcase = None
Tamas Kenez8a4f0772017-06-13 09:16:30 +0200104 # Build a tree with leaves True|False|None for passing, failing and flaky
105 # tests.
106 for f in filenames:
107 for x in utils.read_cts_test_result(f):
108 if type(x) is utils.CtsModule:
109 module = tree.setdefault(x.name, {})
110 elif type(x) is utils.CtsTestCase:
111 testcase = module.setdefault(x.name, {})
112 else:
113 outcome = testcase.setdefault(x.name, x.outcome)
114 if outcome is not None and outcome != x.outcome:
115 testcase[x.name] = None
Tamas Kenez971eec62017-05-24 11:08:40 +0200116
Tamas Kenez8a4f0772017-06-13 09:16:30 +0200117 result = []
118 for module_name, module in tree.iteritems():
119 for test_case_name, test_case in module.iteritems():
120 result.extend(['{}/{}/{}'.format(module_name, test_case_name, test_name)
121 for test_name, test in test_case.iteritems()
122 if test])
Tamas Kenez971eec62017-05-24 11:08:40 +0200123
Tamas Kenez8a4f0772017-06-13 09:16:30 +0200124 return result
Tamas Kenez971eec62017-05-24 11:08:40 +0200125
Tamas Kenez507481b2017-06-06 15:01:33 +0200126def setup_and_clean(tool_is_d8, clean_dex):
Tamas Kenez971eec62017-05-24 11:08:40 +0200127 # Two output dirs, one for the android image and one for cts tests.
128 # The output is compiled with d8 and jack, respectively.
129 utils.makedirs_if_needed(AOSP_ROOT)
130 utils.makedirs_if_needed(OUT_IMG)
131 utils.makedirs_if_needed(OUT_CTS)
132
133 # remove dex files older than the current d8 tool
Tamas Kenez507481b2017-06-06 15:01:33 +0200134 counter = 0
135 if tool_is_d8 or clean_dex:
136 if not clean_dex:
Rico Windb4621c12017-08-28 12:48:53 +0200137 d8jar_mtime = os.path.getmtime(utils.D8_JAR)
Tamas Kenez507481b2017-06-06 15:01:33 +0200138 dex_files = (chain.from_iterable(glob(join(x[0], '*.dex'))
139 for x in os.walk(OUT_IMG)))
140 for f in dex_files:
141 if clean_dex or os.path.getmtime(f) <= d8jar_mtime:
142 os.remove(f)
143 counter += 1
144 if counter > 0:
145 print('Removed {} dex files.'.format(counter))
Tamas Kenez971eec62017-05-24 11:08:40 +0200146
Tamas Kenez971eec62017-05-24 11:08:40 +0200147def Main():
148 args = parse_arguments()
149
150 if args.d8log and args.tool != 'd8':
151 print("The '--d8log' option works only with '--tool=d8'.",
152 file = sys.stderr)
153 return EXIT_FAILURE
154
155 assert args.tool in ['jack', 'dx', 'd8']
156
157 jack_option = 'ANDROID_COMPILE_WITH_JACK=' \
Tamas Kenezed123be2017-05-31 01:20:42 +0200158 + ('true' if args.tool == 'jack' else 'false')
Tamas Kenez971eec62017-05-24 11:08:40 +0200159
Tamas Kenez507481b2017-06-06 15:01:33 +0200160 # DX_ALT_JAR need to be cleared if not set, for 'make' to work properly
161 alt_jar_option = 'DX_ALT_JAR='
Tamas Kenez971eec62017-05-24 11:08:40 +0200162 if args.tool == 'd8':
163 if args.d8log:
Tamas Kenez507481b2017-06-06 15:01:33 +0200164 alt_jar_option += D8LOGGER_JAR
Tamas Kenez971eec62017-05-24 11:08:40 +0200165 os.environ['D8LOGGER_OUTPUT'] = args.d8log
166 else:
Tamas Kenez0cad51c2017-08-21 14:42:01 +0200167 alt_jar_option += utils.COMPATDX_JAR
Tamas Kenez971eec62017-05-24 11:08:40 +0200168
169 gradle.RunGradle(['d8','d8logger', 'compatdx'])
170
Tamas Kenez507481b2017-06-06 15:01:33 +0200171 setup_and_clean(args.tool == 'd8', args.clean_dex)
Tamas Kenez971eec62017-05-24 11:08:40 +0200172
Søren Gjesse298759e2017-09-08 08:22:41 +0200173 checkout_aosp.checkout_aosp(AOSP_ROOT, AOSP_MANIFEST_XML, J_DEFAULT)
Tamas Kenez971eec62017-05-24 11:08:40 +0200174
175 # activate OUT_CTS and build Android CTS
176 # AOSP has no clean way to set the output directory.
177 # In order to do incremental builds we apply the following symlink-based
178 # workaround.
179 # Note: this does not work on windows, but the AOSP
180 # doesn't build, either
181
182 if not remove_aosp_out():
183 return EXIT_FAILURE
Tamas Kenez507481b2017-06-06 15:01:33 +0200184 print("-- Building CTS with 'make {} cts'.".format(J_OPTION))
Tamas Kenez971eec62017-05-24 11:08:40 +0200185 os.symlink(OUT_CTS, AOSP_OUT)
186 check_call([AOSP_HELPER_SH, AOSP_PRESET, 'make', J_OPTION, 'cts'],
187 cwd = AOSP_ROOT)
188
189 # activate OUT_IMG and build the Android image
190 if not remove_aosp_out():
191 return EXIT_FAILURE
Tamas Kenez507481b2017-06-06 15:01:33 +0200192 print("-- Building Android image with 'make {} {} {}'." \
193 .format(J_OPTION, jack_option, alt_jar_option))
Tamas Kenez971eec62017-05-24 11:08:40 +0200194 os.symlink(OUT_IMG, AOSP_OUT)
195 check_call([AOSP_HELPER_SH, AOSP_PRESET, 'make', J_OPTION, jack_option,
196 alt_jar_option], cwd = AOSP_ROOT)
197
198 emulator_proc = Popen([AOSP_HELPER_SH, AOSP_PRESET,
199 'emulator', '-partition-size', '4096', '-wipe-data'], cwd = AOSP_ROOT)
200
201 if emulator_proc.poll() is not None:
202 print("Can't start Android Emulator.", file = sys.stderr)
203
204 check_call([AOSP_HELPER_SH, AOSP_PRESET, 'run-cts',
205 CTS_TRADEFED, 'run', 'cts'], cwd = AOSP_ROOT)
206
207 emulator_proc.terminate()
Tamas Kenez0cd1e1e2017-06-06 14:50:52 +0200208 time.sleep(6) # aosp_helper waits to be killed in looping 'sleep 5'
Tamas Kenez971eec62017-05-24 11:08:40 +0200209
210 # find the newest test_result.xml
211 result_dirs = \
212 [f for f in glob(join(RESULTS_DIR_BASE, '*')) if os.path.isdir(f)]
213 if len(result_dirs) == 0:
214 print("Can't find result directories in ", RESULTS_DIR_BASE)
215 return EXIT_FAILURE
216 result_dirs.sort(key = os.path.getmtime)
217 results_xml = join(result_dirs[-1], 'test_result.xml')
218
219 # print summaries
220 re_summary = re.compile('<Summary ')
Tamas Kenez507481b2017-06-06 15:01:33 +0200221
222 summaries = [('Summary from current test results: ', results_xml)]
Tamas Kenez507481b2017-06-06 15:01:33 +0200223
224 for (title, result_file) in summaries:
Tamas Kenez971eec62017-05-24 11:08:40 +0200225 print(title, result_file)
226 with open(result_file) as f:
227 for line in f:
228 if re_summary.search(line):
229 print(line)
230 break
231
Tamas Kenez507481b2017-06-06 15:01:33 +0200232 if args.no_baseline:
233 r = 0
234 else:
235 print('Comparing test results to baseline:\n')
Tamas Kenez971eec62017-05-24 11:08:40 +0200236
Tamas Kenez8a4f0772017-06-13 09:16:30 +0200237 passing_tests = consistently_passing_tests_from_test_results([results_xml])
Tamas Kenez02bff032017-07-18 12:13:58 +0200238 baseline_results = glob(join(CTS_BASELINE_FILES_DIR, '*.xml'))
Tamas Kenez8a4f0772017-06-13 09:16:30 +0200239 assert len(baseline_results) != 0
Tamas Kenez971eec62017-05-24 11:08:40 +0200240
Tamas Kenez8a4f0772017-06-13 09:16:30 +0200241 passing_tests_in_baseline = \
242 consistently_passing_tests_from_test_results(baseline_results)
243
244 missing_or_failing_tests = \
245 set(passing_tests_in_baseline) - set(passing_tests)
246
247 num_tests = len(missing_or_failing_tests)
248 if num_tests != 0:
249 if num_tests > 1:
250 text = '{} tests that consistently pass in the baseline' \
251 ' are missing or failing in the current test:'.format(num_tests)
252 else:
253 text = '1 test that consistently passes in the baseline' \
254 ' is missing or failing in the current test:'
255 print(text)
256 for t in missing_or_failing_tests:
257 print(t)
258 r = EXIT_FAILURE
259 else:
260 r = 0
Tamas Kenez507481b2017-06-06 15:01:33 +0200261
262 if args.save_result:
263 copy2(results_xml, args.save_result)
264
265 return r
Tamas Kenez971eec62017-05-24 11:08:40 +0200266
267if __name__ == '__main__':
268 sys.exit(Main())