blob: c07a4a0245beaf34367e5319dc399f10663b5075 [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 +020043AOSP_ROOT = join(utils.REPO_ROOT, 'build/aosp')
44
45AOSP_MANIFEST_URL = 'https://android.googlesource.com/platform/manifest'
46AOSP_PRESET = 'aosp_x86-eng'
47
48AOSP_OUT = join(AOSP_ROOT, 'out')
49OUT_IMG = join(AOSP_ROOT, 'out_img') # output dir for android img build
50OUT_CTS = join(AOSP_ROOT, 'out_cts') # output dir for CTS build
51RESULTS_DIR_BASE = join(OUT_CTS, 'host/linux-x86/cts/android-cts/results')
52CTS_TRADEFED = join(OUT_CTS,
53 'host/linux-x86/cts/android-cts/tools/cts-tradefed')
54
Søren Gjesseadfde762017-10-13 09:51:38 +020055J_DEFAULT = 8
56J_OPTION = '-j' + str(J_DEFAULT)
Tamas Kenez971eec62017-05-24 11:08:40 +020057
58EXIT_FAILURE = 1
59
60def parse_arguments():
61 parser = argparse.ArgumentParser(
62 description = 'Download the AOSP source tree, build an Android image'
63 ' and the CTS targets and run CTS with the emulator on the image.')
64 parser.add_argument('--tool',
Søren Gjesseadfde762017-10-13 09:51:38 +020065 choices = ['dx', 'd8'],
Tamas Kenez971eec62017-05-24 11:08:40 +020066 default = 'd8',
67 help='compiler tool to use')
Tamas Kenez507481b2017-06-06 15:01:33 +020068 parser.add_argument('--save-result',
69 metavar = 'FILE',
70 help = 'Save final test_result.xml to the specified file.')
71 parser.add_argument('--no-baseline',
72 action = 'store_true',
73 help = "Don't compare results to baseline hence don't return failure if"
74 ' they differ.')
75 parser.add_argument('--clean-dex',
76 action = 'store_true',
77 help = 'Remove AOSP/dex files always, before the build. By default they'
78 " are removed only if '--tool=d8' and they're older then the D8 tool")
Tamas Kenez971eec62017-05-24 11:08:40 +020079 return parser.parse_args()
80
81# return False on error
82def remove_aosp_out():
83 if os.path.exists(AOSP_OUT):
84 if os.path.islink(AOSP_OUT):
85 os.remove(AOSP_OUT)
86 else:
87 print("The AOSP out directory ('" + AOSP_OUT + "') is expected"
88 " to be a symlink", file = sys.stderr)
89 return False
90 return True
91
Tamas Kenez8a4f0772017-06-13 09:16:30 +020092# Return list of fully qualified names of tests passing in
93# all the files.
94def consistently_passing_tests_from_test_results(filenames):
Tamas Kenez971eec62017-05-24 11:08:40 +020095 tree = {}
96 module = None
97 testcase = None
Tamas Kenez8a4f0772017-06-13 09:16:30 +020098 # Build a tree with leaves True|False|None for passing, failing and flaky
99 # tests.
100 for f in filenames:
101 for x in utils.read_cts_test_result(f):
102 if type(x) is utils.CtsModule:
103 module = tree.setdefault(x.name, {})
104 elif type(x) is utils.CtsTestCase:
105 testcase = module.setdefault(x.name, {})
106 else:
107 outcome = testcase.setdefault(x.name, x.outcome)
108 if outcome is not None and outcome != x.outcome:
109 testcase[x.name] = None
Tamas Kenez971eec62017-05-24 11:08:40 +0200110
Tamas Kenez8a4f0772017-06-13 09:16:30 +0200111 result = []
112 for module_name, module in tree.iteritems():
113 for test_case_name, test_case in module.iteritems():
114 result.extend(['{}/{}/{}'.format(module_name, test_case_name, test_name)
115 for test_name, test in test_case.iteritems()
116 if test])
Tamas Kenez971eec62017-05-24 11:08:40 +0200117
Tamas Kenez8a4f0772017-06-13 09:16:30 +0200118 return result
Tamas Kenez971eec62017-05-24 11:08:40 +0200119
Tamas Kenez507481b2017-06-06 15:01:33 +0200120def setup_and_clean(tool_is_d8, clean_dex):
Tamas Kenez971eec62017-05-24 11:08:40 +0200121 # Two output dirs, one for the android image and one for cts tests.
122 # The output is compiled with d8 and jack, respectively.
123 utils.makedirs_if_needed(AOSP_ROOT)
124 utils.makedirs_if_needed(OUT_IMG)
125 utils.makedirs_if_needed(OUT_CTS)
126
127 # remove dex files older than the current d8 tool
Tamas Kenez507481b2017-06-06 15:01:33 +0200128 counter = 0
129 if tool_is_d8 or clean_dex:
130 if not clean_dex:
Rico Windb4621c12017-08-28 12:48:53 +0200131 d8jar_mtime = os.path.getmtime(utils.D8_JAR)
Tamas Kenez507481b2017-06-06 15:01:33 +0200132 dex_files = (chain.from_iterable(glob(join(x[0], '*.dex'))
133 for x in os.walk(OUT_IMG)))
134 for f in dex_files:
135 if clean_dex or os.path.getmtime(f) <= d8jar_mtime:
136 os.remove(f)
137 counter += 1
138 if counter > 0:
139 print('Removed {} dex files.'.format(counter))
Tamas Kenez971eec62017-05-24 11:08:40 +0200140
Tamas Kenez971eec62017-05-24 11:08:40 +0200141def Main():
142 args = parse_arguments()
143
Søren Gjesseadfde762017-10-13 09:51:38 +0200144 assert args.tool in ['dx', 'd8']
Tamas Kenez971eec62017-05-24 11:08:40 +0200145
Søren Gjesseadfde762017-10-13 09:51:38 +0200146 use_d8 = 'USE_D8=false'
Tamas Kenez971eec62017-05-24 11:08:40 +0200147 if args.tool == 'd8':
Søren Gjesseadfde762017-10-13 09:51:38 +0200148 use_d8 = 'USE_D8=true'
Tamas Kenez971eec62017-05-24 11:08:40 +0200149
Søren Gjesseadfde762017-10-13 09:51:38 +0200150 gradle.RunGradle(['d8', 'compatdx'])
Tamas Kenez971eec62017-05-24 11:08:40 +0200151
Tamas Kenez507481b2017-06-06 15:01:33 +0200152 setup_and_clean(args.tool == 'd8', args.clean_dex)
Tamas Kenez971eec62017-05-24 11:08:40 +0200153
Søren Gjesseadfde762017-10-13 09:51:38 +0200154 checkout_aosp.checkout_aosp("test", AOSP_MANIFEST_URL, None,
155 AOSP_MANIFEST_XML, str(J_DEFAULT), True)
Tamas Kenez971eec62017-05-24 11:08:40 +0200156
157 # activate OUT_CTS and build Android CTS
158 # AOSP has no clean way to set the output directory.
159 # In order to do incremental builds we apply the following symlink-based
160 # workaround.
161 # Note: this does not work on windows, but the AOSP
162 # doesn't build, either
163
164 if not remove_aosp_out():
165 return EXIT_FAILURE
Tamas Kenez507481b2017-06-06 15:01:33 +0200166 print("-- Building CTS with 'make {} cts'.".format(J_OPTION))
Tamas Kenez971eec62017-05-24 11:08:40 +0200167 os.symlink(OUT_CTS, AOSP_OUT)
168 check_call([AOSP_HELPER_SH, AOSP_PRESET, 'make', J_OPTION, 'cts'],
169 cwd = AOSP_ROOT)
170
171 # activate OUT_IMG and build the Android image
172 if not remove_aosp_out():
173 return EXIT_FAILURE
Søren Gjesseadfde762017-10-13 09:51:38 +0200174 print("-- Building Android image with 'make {} {}'." \
175 .format(J_OPTION, use_d8))
Tamas Kenez971eec62017-05-24 11:08:40 +0200176 os.symlink(OUT_IMG, AOSP_OUT)
Søren Gjesseadfde762017-10-13 09:51:38 +0200177 check_call([AOSP_HELPER_SH, AOSP_PRESET, 'make', J_OPTION,
178 use_d8_option], cwd = AOSP_ROOT)
Tamas Kenez971eec62017-05-24 11:08:40 +0200179
180 emulator_proc = Popen([AOSP_HELPER_SH, AOSP_PRESET,
181 'emulator', '-partition-size', '4096', '-wipe-data'], cwd = AOSP_ROOT)
182
183 if emulator_proc.poll() is not None:
184 print("Can't start Android Emulator.", file = sys.stderr)
185
186 check_call([AOSP_HELPER_SH, AOSP_PRESET, 'run-cts',
187 CTS_TRADEFED, 'run', 'cts'], cwd = AOSP_ROOT)
188
189 emulator_proc.terminate()
Tamas Kenez0cd1e1e2017-06-06 14:50:52 +0200190 time.sleep(6) # aosp_helper waits to be killed in looping 'sleep 5'
Tamas Kenez971eec62017-05-24 11:08:40 +0200191
192 # find the newest test_result.xml
193 result_dirs = \
194 [f for f in glob(join(RESULTS_DIR_BASE, '*')) if os.path.isdir(f)]
195 if len(result_dirs) == 0:
196 print("Can't find result directories in ", RESULTS_DIR_BASE)
197 return EXIT_FAILURE
198 result_dirs.sort(key = os.path.getmtime)
199 results_xml = join(result_dirs[-1], 'test_result.xml')
200
201 # print summaries
202 re_summary = re.compile('<Summary ')
Tamas Kenez507481b2017-06-06 15:01:33 +0200203
204 summaries = [('Summary from current test results: ', results_xml)]
Tamas Kenez507481b2017-06-06 15:01:33 +0200205
206 for (title, result_file) in summaries:
Tamas Kenez971eec62017-05-24 11:08:40 +0200207 print(title, result_file)
208 with open(result_file) as f:
209 for line in f:
210 if re_summary.search(line):
211 print(line)
212 break
213
Tamas Kenez507481b2017-06-06 15:01:33 +0200214 if args.no_baseline:
215 r = 0
216 else:
217 print('Comparing test results to baseline:\n')
Tamas Kenez971eec62017-05-24 11:08:40 +0200218
Tamas Kenez8a4f0772017-06-13 09:16:30 +0200219 passing_tests = consistently_passing_tests_from_test_results([results_xml])
Tamas Kenez02bff032017-07-18 12:13:58 +0200220 baseline_results = glob(join(CTS_BASELINE_FILES_DIR, '*.xml'))
Tamas Kenez8a4f0772017-06-13 09:16:30 +0200221 assert len(baseline_results) != 0
Tamas Kenez971eec62017-05-24 11:08:40 +0200222
Tamas Kenez8a4f0772017-06-13 09:16:30 +0200223 passing_tests_in_baseline = \
224 consistently_passing_tests_from_test_results(baseline_results)
225
226 missing_or_failing_tests = \
227 set(passing_tests_in_baseline) - set(passing_tests)
228
229 num_tests = len(missing_or_failing_tests)
230 if num_tests != 0:
231 if num_tests > 1:
232 text = '{} tests that consistently pass in the baseline' \
233 ' are missing or failing in the current test:'.format(num_tests)
234 else:
235 text = '1 test that consistently passes in the baseline' \
236 ' is missing or failing in the current test:'
237 print(text)
238 for t in missing_or_failing_tests:
239 print(t)
240 r = EXIT_FAILURE
241 else:
242 r = 0
Tamas Kenez507481b2017-06-06 15:01:33 +0200243
244 if args.save_result:
245 copy2(results_xml, args.save_result)
246
247 return r
Tamas Kenez971eec62017-05-24 11:08:40 +0200248
249if __name__ == '__main__':
250 sys.exit(Main())