xref: /aosp_15_r20/cts/tools/incremental-cts/incremental_deqp.py (revision b7c941bb3fa97aba169d73cee0bed2de8ac964bf)
1*b7c941bbSAndroid Build Coastguard Worker#!/usr/bin/env python3
2*b7c941bbSAndroid Build Coastguard Worker#
3*b7c941bbSAndroid Build Coastguard Worker#   Copyright 2021 - The Android Open Source Project
4*b7c941bbSAndroid Build Coastguard Worker#
5*b7c941bbSAndroid Build Coastguard Worker#   Licensed under the Apache License, Version 2.0 (the "License");
6*b7c941bbSAndroid Build Coastguard Worker#   you may not use this file except in compliance with the License.
7*b7c941bbSAndroid Build Coastguard Worker#   You may obtain a copy of the License at
8*b7c941bbSAndroid Build Coastguard Worker#
9*b7c941bbSAndroid Build Coastguard Worker#       http://www.apache.org/licenses/LICENSE-2.0
10*b7c941bbSAndroid Build Coastguard Worker#
11*b7c941bbSAndroid Build Coastguard Worker#   Unless required by applicable law or agreed to in writing, software
12*b7c941bbSAndroid Build Coastguard Worker#   distributed under the License is distributed on an "AS IS" BASIS,
13*b7c941bbSAndroid Build Coastguard Worker#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*b7c941bbSAndroid Build Coastguard Worker#   See the License for the specific language governing permissions and
15*b7c941bbSAndroid Build Coastguard Worker#   limitations under the License.
16*b7c941bbSAndroid Build Coastguard Worker
17*b7c941bbSAndroid Build Coastguard Worker"""Incremental dEQP
18*b7c941bbSAndroid Build Coastguard Worker
19*b7c941bbSAndroid Build Coastguard WorkerThis script will run a subset of dEQP test on device to get dEQP dependency.
20*b7c941bbSAndroid Build Coastguard Worker
21*b7c941bbSAndroid Build Coastguard WorkerUsage 1: Compare with a base build to check if any dEQP dependency has
22*b7c941bbSAndroid Build Coastguard Workerchanged. Output a decision if dEQP could be skipped, and a cts-tradefed
23*b7c941bbSAndroid Build Coastguard Workercommand to be used based on the decision.
24*b7c941bbSAndroid Build Coastguard Worker
25*b7c941bbSAndroid Build Coastguard Workerpython3 incremental_deqp.py -s [device serial] -t [test directory] -b
26*b7c941bbSAndroid Build Coastguard Worker[base build target file] -c [current build target file]
27*b7c941bbSAndroid Build Coastguard Worker
28*b7c941bbSAndroid Build Coastguard WorkerUsage 2: Generate a file containing a list of dEQP dependencies for the
29*b7c941bbSAndroid Build Coastguard Workerbuild on device.
30*b7c941bbSAndroid Build Coastguard Worker
31*b7c941bbSAndroid Build Coastguard Workerpython3 incremental_deqp.py -s [device serial] -t [test directory]
32*b7c941bbSAndroid Build Coastguard Worker--generate_deps_only
33*b7c941bbSAndroid Build Coastguard Worker
34*b7c941bbSAndroid Build Coastguard Worker"""
35*b7c941bbSAndroid Build Coastguard Workerimport argparse
36*b7c941bbSAndroid Build Coastguard Workerimport importlib
37*b7c941bbSAndroid Build Coastguard Workerimport json
38*b7c941bbSAndroid Build Coastguard Workerimport logging
39*b7c941bbSAndroid Build Coastguard Workerimport os
40*b7c941bbSAndroid Build Coastguard Workerimport pkgutil
41*b7c941bbSAndroid Build Coastguard Workerimport re
42*b7c941bbSAndroid Build Coastguard Workerimport subprocess
43*b7c941bbSAndroid Build Coastguard Workerimport tempfile
44*b7c941bbSAndroid Build Coastguard Workerimport time
45*b7c941bbSAndroid Build Coastguard Workerimport uuid
46*b7c941bbSAndroid Build Coastguard Workerfrom target_file_handler import TargetFileHandler
47*b7c941bbSAndroid Build Coastguard Workerfrom custom_build_file_handler import CustomBuildFileHandler
48*b7c941bbSAndroid Build Coastguard Workerfrom zipfile import ZipFile
49*b7c941bbSAndroid Build Coastguard Worker
50*b7c941bbSAndroid Build Coastguard Worker
51*b7c941bbSAndroid Build Coastguard WorkerDEFAULT_CTS_XML = ('<?xml version="1.0" encoding="utf-8"?>\n'
52*b7c941bbSAndroid Build Coastguard Worker                   '<configuration description="Runs CTS from a pre-existing CTS installation">\n'
53*b7c941bbSAndroid Build Coastguard Worker                   '   <include name="cts-common" />\n'
54*b7c941bbSAndroid Build Coastguard Worker                   '   <include name="cts-exclude" />\n'
55*b7c941bbSAndroid Build Coastguard Worker                   '   <include name="cts-exclude-instant" />\n'
56*b7c941bbSAndroid Build Coastguard Worker                   '   <option name="enable-token-sharding" '
57*b7c941bbSAndroid Build Coastguard Worker                   'value="true" />\n'
58*b7c941bbSAndroid Build Coastguard Worker                   '   <option name="plan" value="cts" />\n'
59*b7c941bbSAndroid Build Coastguard Worker                   '</configuration>\n')
60*b7c941bbSAndroid Build Coastguard Worker
61*b7c941bbSAndroid Build Coastguard WorkerINCREMENTAL_DEQP_XML = ('<?xml version="1.0" encoding="utf-8"?>\n'
62*b7c941bbSAndroid Build Coastguard Worker                        '<configuration description="Runs CTS with incremental dEQP">\n'
63*b7c941bbSAndroid Build Coastguard Worker                        '   <include name="cts-common" />\n'
64*b7c941bbSAndroid Build Coastguard Worker                        '   <include name="cts-exclude" />\n'
65*b7c941bbSAndroid Build Coastguard Worker                        '   <include name="cts-exclude-instant" />\n'
66*b7c941bbSAndroid Build Coastguard Worker                        '   <option name="enable-token-sharding" '
67*b7c941bbSAndroid Build Coastguard Worker                        'value="true" />\n'
68*b7c941bbSAndroid Build Coastguard Worker                        '   <option name="compatibility:exclude-filter" '
69*b7c941bbSAndroid Build Coastguard Worker                        'value="CtsDeqpTestCases" />\n'
70*b7c941bbSAndroid Build Coastguard Worker                        '   <option name="plan" value="cts" />\n'
71*b7c941bbSAndroid Build Coastguard Worker                        '</configuration>\n')
72*b7c941bbSAndroid Build Coastguard Worker
73*b7c941bbSAndroid Build Coastguard WorkerREPORT_FILENAME = 'incremental_dEQP_report.json'
74*b7c941bbSAndroid Build Coastguard Worker
75*b7c941bbSAndroid Build Coastguard Workerlogger = logging.getLogger()
76*b7c941bbSAndroid Build Coastguard Worker
77*b7c941bbSAndroid Build Coastguard Worker
78*b7c941bbSAndroid Build Coastguard Workerclass AtsError(Exception):
79*b7c941bbSAndroid Build Coastguard Worker  """Error when running incremental dEQP with Android Test Station"""
80*b7c941bbSAndroid Build Coastguard Worker  pass
81*b7c941bbSAndroid Build Coastguard Worker
82*b7c941bbSAndroid Build Coastguard Workerclass AdbError(Exception):
83*b7c941bbSAndroid Build Coastguard Worker  """Error when running adb command."""
84*b7c941bbSAndroid Build Coastguard Worker  pass
85*b7c941bbSAndroid Build Coastguard Worker
86*b7c941bbSAndroid Build Coastguard Workerclass TestError(Exception):
87*b7c941bbSAndroid Build Coastguard Worker  """Error when running dEQP test."""
88*b7c941bbSAndroid Build Coastguard Worker  pass
89*b7c941bbSAndroid Build Coastguard Worker
90*b7c941bbSAndroid Build Coastguard Workerclass TestResourceError(Exception):
91*b7c941bbSAndroid Build Coastguard Worker  """Error with test resource. """
92*b7c941bbSAndroid Build Coastguard Worker  pass
93*b7c941bbSAndroid Build Coastguard Worker
94*b7c941bbSAndroid Build Coastguard Workerclass BuildHelper(object):
95*b7c941bbSAndroid Build Coastguard Worker  """Helper class for analyzing build."""
96*b7c941bbSAndroid Build Coastguard Worker
97*b7c941bbSAndroid Build Coastguard Worker  def __init__(self, custom_handler=False):
98*b7c941bbSAndroid Build Coastguard Worker    """Init BuildHelper.
99*b7c941bbSAndroid Build Coastguard Worker
100*b7c941bbSAndroid Build Coastguard Worker    Args:
101*b7c941bbSAndroid Build Coastguard Worker      custom_handler: use custom build handler.
102*b7c941bbSAndroid Build Coastguard Worker    """
103*b7c941bbSAndroid Build Coastguard Worker    self._build_file_handler = TargetFileHandler
104*b7c941bbSAndroid Build Coastguard Worker    if custom_handler:
105*b7c941bbSAndroid Build Coastguard Worker      self._build_file_handler = CustomBuildFileHandler
106*b7c941bbSAndroid Build Coastguard Worker
107*b7c941bbSAndroid Build Coastguard Worker
108*b7c941bbSAndroid Build Coastguard Worker  def compare_base_build_with_current_build(self, deqp_deps, current_build_file,
109*b7c941bbSAndroid Build Coastguard Worker                                            base_build_file):
110*b7c941bbSAndroid Build Coastguard Worker    """Compare the difference of current build and base build with dEQP dependency.
111*b7c941bbSAndroid Build Coastguard Worker
112*b7c941bbSAndroid Build Coastguard Worker    If the difference doesn't involve dEQP dependency, current build could skip dEQP test if
113*b7c941bbSAndroid Build Coastguard Worker    base build has passed test.
114*b7c941bbSAndroid Build Coastguard Worker
115*b7c941bbSAndroid Build Coastguard Worker    Args:
116*b7c941bbSAndroid Build Coastguard Worker      deqp_deps: a set of dEQP dependency.
117*b7c941bbSAndroid Build Coastguard Worker      current_build_file: current build's file name.
118*b7c941bbSAndroid Build Coastguard Worker      base_build_file: base build's file name.
119*b7c941bbSAndroid Build Coastguard Worker    Returns:
120*b7c941bbSAndroid Build Coastguard Worker      True if current build could skip dEQP, otherwise False.
121*b7c941bbSAndroid Build Coastguard Worker      Dictionary of changed dependencies and their details.
122*b7c941bbSAndroid Build Coastguard Worker    """
123*b7c941bbSAndroid Build Coastguard Worker    print('Comparing base build and current build...')
124*b7c941bbSAndroid Build Coastguard Worker    current_build_handler = self._build_file_handler(current_build_file)
125*b7c941bbSAndroid Build Coastguard Worker    current_build_hash = current_build_handler.get_file_hash(deqp_deps)
126*b7c941bbSAndroid Build Coastguard Worker
127*b7c941bbSAndroid Build Coastguard Worker    base_build_handler = self._build_file_handler(base_build_file)
128*b7c941bbSAndroid Build Coastguard Worker    base_build_hash = base_build_handler.get_file_hash(deqp_deps)
129*b7c941bbSAndroid Build Coastguard Worker
130*b7c941bbSAndroid Build Coastguard Worker    return self._compare_build_hash(current_build_hash, base_build_hash)
131*b7c941bbSAndroid Build Coastguard Worker
132*b7c941bbSAndroid Build Coastguard Worker
133*b7c941bbSAndroid Build Coastguard Worker  def compare_base_build_with_device_files(self, deqp_deps, adb, base_build_file):
134*b7c941bbSAndroid Build Coastguard Worker    """Compare the difference of files on device and base build with dEQP dependency.
135*b7c941bbSAndroid Build Coastguard Worker
136*b7c941bbSAndroid Build Coastguard Worker    If the difference doesn't involve dEQP dependency, current build could skip dEQP test if
137*b7c941bbSAndroid Build Coastguard Worker    base build has passed test.
138*b7c941bbSAndroid Build Coastguard Worker
139*b7c941bbSAndroid Build Coastguard Worker    Args:
140*b7c941bbSAndroid Build Coastguard Worker      deqp_deps: a set of dEQP dependency.
141*b7c941bbSAndroid Build Coastguard Worker      adb: an instance of AdbHelper for current device under test.
142*b7c941bbSAndroid Build Coastguard Worker      base_build_file: base build file name.
143*b7c941bbSAndroid Build Coastguard Worker    Returns:
144*b7c941bbSAndroid Build Coastguard Worker      True if current build could skip dEQP, otherwise False.
145*b7c941bbSAndroid Build Coastguard Worker      Dictionary of changed dependencies and their details.
146*b7c941bbSAndroid Build Coastguard Worker    """
147*b7c941bbSAndroid Build Coastguard Worker    print('Comparing base build and current build on the device...')
148*b7c941bbSAndroid Build Coastguard Worker    # Get current build's hash.
149*b7c941bbSAndroid Build Coastguard Worker    current_build_hash = dict()
150*b7c941bbSAndroid Build Coastguard Worker    for dep in deqp_deps:
151*b7c941bbSAndroid Build Coastguard Worker      content = adb.run_shell_command('cat ' + dep)
152*b7c941bbSAndroid Build Coastguard Worker      current_build_hash[dep] = hash(content)
153*b7c941bbSAndroid Build Coastguard Worker
154*b7c941bbSAndroid Build Coastguard Worker    base_build_handler = self._build_file_handler(base_build_file)
155*b7c941bbSAndroid Build Coastguard Worker    base_build_hash = base_build_handler.get_file_hash(deqp_deps)
156*b7c941bbSAndroid Build Coastguard Worker
157*b7c941bbSAndroid Build Coastguard Worker    return self._compare_build_hash(current_build_hash, base_build_hash)
158*b7c941bbSAndroid Build Coastguard Worker
159*b7c941bbSAndroid Build Coastguard Worker  def get_system_fingerprint(self, build_file):
160*b7c941bbSAndroid Build Coastguard Worker    """Get build fingerprint in SYSTEM partition.
161*b7c941bbSAndroid Build Coastguard Worker
162*b7c941bbSAndroid Build Coastguard Worker    Returns:
163*b7c941bbSAndroid Build Coastguard Worker      String of build fingerprint.
164*b7c941bbSAndroid Build Coastguard Worker    """
165*b7c941bbSAndroid Build Coastguard Worker    return self._build_file_handler(build_file).get_system_fingerprint()
166*b7c941bbSAndroid Build Coastguard Worker
167*b7c941bbSAndroid Build Coastguard Worker
168*b7c941bbSAndroid Build Coastguard Worker  def _compare_build_hash(self, current_build_hash, base_build_hash):
169*b7c941bbSAndroid Build Coastguard Worker    """Compare the hash value of current build and base build.
170*b7c941bbSAndroid Build Coastguard Worker
171*b7c941bbSAndroid Build Coastguard Worker    Args:
172*b7c941bbSAndroid Build Coastguard Worker      current_build_hash: map of current build where key is file name, and value is content hash.
173*b7c941bbSAndroid Build Coastguard Worker      base_build_hash: map of base build where key is file name and value is content hash.
174*b7c941bbSAndroid Build Coastguard Worker    Returns:
175*b7c941bbSAndroid Build Coastguard Worker      Boolean about if two builds' hash is the same.
176*b7c941bbSAndroid Build Coastguard Worker      Dictionary of changed dependencies and their details.
177*b7c941bbSAndroid Build Coastguard Worker    """
178*b7c941bbSAndroid Build Coastguard Worker    changes = {}
179*b7c941bbSAndroid Build Coastguard Worker    if current_build_hash == base_build_hash:
180*b7c941bbSAndroid Build Coastguard Worker      print('Done!')
181*b7c941bbSAndroid Build Coastguard Worker      return True, changes
182*b7c941bbSAndroid Build Coastguard Worker
183*b7c941bbSAndroid Build Coastguard Worker    for key, val in current_build_hash.items():
184*b7c941bbSAndroid Build Coastguard Worker      if key not in base_build_hash:
185*b7c941bbSAndroid Build Coastguard Worker        detail = 'File:{build_file} was not found in base build'.format(build_file=key)
186*b7c941bbSAndroid Build Coastguard Worker        changes[key] = detail
187*b7c941bbSAndroid Build Coastguard Worker        logger.info(detail)
188*b7c941bbSAndroid Build Coastguard Worker      elif base_build_hash[key] != val:
189*b7c941bbSAndroid Build Coastguard Worker        detail = ('Detected dEQP dependency file difference:{deps}. Base build hash:{base}, '
190*b7c941bbSAndroid Build Coastguard Worker                  'current build hash:{current}'.format(deps=key, base=base_build_hash[key],
191*b7c941bbSAndroid Build Coastguard Worker                                                        current=val))
192*b7c941bbSAndroid Build Coastguard Worker        changes[key] = detail
193*b7c941bbSAndroid Build Coastguard Worker        logger.info(detail)
194*b7c941bbSAndroid Build Coastguard Worker
195*b7c941bbSAndroid Build Coastguard Worker    print('Done!')
196*b7c941bbSAndroid Build Coastguard Worker    return False, changes
197*b7c941bbSAndroid Build Coastguard Worker
198*b7c941bbSAndroid Build Coastguard Worker
199*b7c941bbSAndroid Build Coastguard Workerclass AdbHelper(object):
200*b7c941bbSAndroid Build Coastguard Worker  """Helper class for running adb."""
201*b7c941bbSAndroid Build Coastguard Worker
202*b7c941bbSAndroid Build Coastguard Worker  def __init__(self, device_serial=None):
203*b7c941bbSAndroid Build Coastguard Worker    """Initialize AdbHelper.
204*b7c941bbSAndroid Build Coastguard Worker
205*b7c941bbSAndroid Build Coastguard Worker    Args:
206*b7c941bbSAndroid Build Coastguard Worker      device_serial: A string of device serial number, optional.
207*b7c941bbSAndroid Build Coastguard Worker    """
208*b7c941bbSAndroid Build Coastguard Worker    self._device_serial = device_serial
209*b7c941bbSAndroid Build Coastguard Worker
210*b7c941bbSAndroid Build Coastguard Worker  def _run_adb_command(self, *args):
211*b7c941bbSAndroid Build Coastguard Worker    """Run adb command."""
212*b7c941bbSAndroid Build Coastguard Worker    adb_cmd = ['adb']
213*b7c941bbSAndroid Build Coastguard Worker    if self._device_serial:
214*b7c941bbSAndroid Build Coastguard Worker      adb_cmd.extend(['-s', self._device_serial])
215*b7c941bbSAndroid Build Coastguard Worker    adb_cmd.extend(args)
216*b7c941bbSAndroid Build Coastguard Worker    adb_cmd = ' '.join(adb_cmd)
217*b7c941bbSAndroid Build Coastguard Worker    completed = subprocess.run(adb_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
218*b7c941bbSAndroid Build Coastguard Worker    if completed.returncode != 0:
219*b7c941bbSAndroid Build Coastguard Worker      raise AdbError('adb command: {cmd} failed with error: {error}'
220*b7c941bbSAndroid Build Coastguard Worker                     .format(cmd=adb_cmd, error=completed.stderr))
221*b7c941bbSAndroid Build Coastguard Worker
222*b7c941bbSAndroid Build Coastguard Worker    return completed.stdout
223*b7c941bbSAndroid Build Coastguard Worker
224*b7c941bbSAndroid Build Coastguard Worker  def push_file(self, source_file, destination_file):
225*b7c941bbSAndroid Build Coastguard Worker    """Push a file from device to host.
226*b7c941bbSAndroid Build Coastguard Worker
227*b7c941bbSAndroid Build Coastguard Worker    Args:
228*b7c941bbSAndroid Build Coastguard Worker      source_file: A string representing file to push.
229*b7c941bbSAndroid Build Coastguard Worker      destination_file: A string representing target on device to push to.
230*b7c941bbSAndroid Build Coastguard Worker    """
231*b7c941bbSAndroid Build Coastguard Worker    return self._run_adb_command('push', source_file, destination_file)
232*b7c941bbSAndroid Build Coastguard Worker
233*b7c941bbSAndroid Build Coastguard Worker  def pull_file(self, source_file, destination_file):
234*b7c941bbSAndroid Build Coastguard Worker    """Pull a file to device.
235*b7c941bbSAndroid Build Coastguard Worker
236*b7c941bbSAndroid Build Coastguard Worker    Args:
237*b7c941bbSAndroid Build Coastguard Worker      source_file: A string representing file on device to pull.
238*b7c941bbSAndroid Build Coastguard Worker      destination_file: A string representing target on host to pull to.
239*b7c941bbSAndroid Build Coastguard Worker    """
240*b7c941bbSAndroid Build Coastguard Worker    return self._run_adb_command('pull', source_file, destination_file)
241*b7c941bbSAndroid Build Coastguard Worker
242*b7c941bbSAndroid Build Coastguard Worker  def get_fingerprint(self):
243*b7c941bbSAndroid Build Coastguard Worker    """Get fingerprint of the device."""
244*b7c941bbSAndroid Build Coastguard Worker    return self._run_adb_command('shell', 'getprop ro.build.fingerprint').decode("utf-8").strip()
245*b7c941bbSAndroid Build Coastguard Worker
246*b7c941bbSAndroid Build Coastguard Worker  def run_shell_command(self, command):
247*b7c941bbSAndroid Build Coastguard Worker    """Run a adb shell command.
248*b7c941bbSAndroid Build Coastguard Worker
249*b7c941bbSAndroid Build Coastguard Worker    Args:
250*b7c941bbSAndroid Build Coastguard Worker      command: A string of command to run, executed through 'adb shell'
251*b7c941bbSAndroid Build Coastguard Worker    """
252*b7c941bbSAndroid Build Coastguard Worker    return self._run_adb_command('shell', command)
253*b7c941bbSAndroid Build Coastguard Worker
254*b7c941bbSAndroid Build Coastguard Worker
255*b7c941bbSAndroid Build Coastguard Workerclass DeqpDependencyCollector(object):
256*b7c941bbSAndroid Build Coastguard Worker  """Collect dEQP dependency from device under test."""
257*b7c941bbSAndroid Build Coastguard Worker
258*b7c941bbSAndroid Build Coastguard Worker  def __init__(self, work_dir, test_dir, adb):
259*b7c941bbSAndroid Build Coastguard Worker    """Init DeqpDependencyCollector.
260*b7c941bbSAndroid Build Coastguard Worker
261*b7c941bbSAndroid Build Coastguard Worker    Args:
262*b7c941bbSAndroid Build Coastguard Worker      work_dir: path of directory for saving script result and logs.
263*b7c941bbSAndroid Build Coastguard Worker      test_dir: path of directory for incremental dEQP test file.
264*b7c941bbSAndroid Build Coastguard Worker      adb: an instance of AdbHelper.
265*b7c941bbSAndroid Build Coastguard Worker    """
266*b7c941bbSAndroid Build Coastguard Worker    self._work_dir = work_dir
267*b7c941bbSAndroid Build Coastguard Worker    self._test_dir = test_dir
268*b7c941bbSAndroid Build Coastguard Worker    self._adb = adb
269*b7c941bbSAndroid Build Coastguard Worker    # dEQP dependency with pattern below are not an actual file:
270*b7c941bbSAndroid Build Coastguard Worker    # files has prefix /data/ are not system files, e.g. intermediate files.
271*b7c941bbSAndroid Build Coastguard Worker    # [vdso] is virtual dynamic shared object.
272*b7c941bbSAndroid Build Coastguard Worker    # /dmabuf is a temporary file.
273*b7c941bbSAndroid Build Coastguard Worker    self._exclude_deqp_pattern = re.compile('(^/data/|^\[vdso\]|^/dmabuf)')
274*b7c941bbSAndroid Build Coastguard Worker
275*b7c941bbSAndroid Build Coastguard Worker  def check_test_log(self, test_file, log_file):
276*b7c941bbSAndroid Build Coastguard Worker    """Check test's log to see if all tests are executed.
277*b7c941bbSAndroid Build Coastguard Worker
278*b7c941bbSAndroid Build Coastguard Worker    Args:
279*b7c941bbSAndroid Build Coastguard Worker      test_file: Name of test .txt file.
280*b7c941bbSAndroid Build Coastguard Worker      log_content: Name of log file.
281*b7c941bbSAndroid Build Coastguard Worker    Returns:
282*b7c941bbSAndroid Build Coastguard Worker      True if all tests are executed, otherwise False.
283*b7c941bbSAndroid Build Coastguard Worker    """
284*b7c941bbSAndroid Build Coastguard Worker    test_cnt = 0
285*b7c941bbSAndroid Build Coastguard Worker    with open(test_file, 'r') as f:
286*b7c941bbSAndroid Build Coastguard Worker      for _ in f:
287*b7c941bbSAndroid Build Coastguard Worker        test_cnt += 1
288*b7c941bbSAndroid Build Coastguard Worker
289*b7c941bbSAndroid Build Coastguard Worker    executed_test_cnt = 0
290*b7c941bbSAndroid Build Coastguard Worker
291*b7c941bbSAndroid Build Coastguard Worker    with open(log_file, 'r') as f:
292*b7c941bbSAndroid Build Coastguard Worker      for line in f:
293*b7c941bbSAndroid Build Coastguard Worker        # 'NotSupported' status means test is not supported in device.
294*b7c941bbSAndroid Build Coastguard Worker        # TODO(yichunli): Check with graphics team if failed test is allowed.
295*b7c941bbSAndroid Build Coastguard Worker        if ('StatusCode="Pass"' in line or 'StatusCode="NotSupported"' in line or
296*b7c941bbSAndroid Build Coastguard Worker            'StatusCode="Fail"' in line):
297*b7c941bbSAndroid Build Coastguard Worker          executed_test_cnt += 1
298*b7c941bbSAndroid Build Coastguard Worker    return executed_test_cnt == test_cnt
299*b7c941bbSAndroid Build Coastguard Worker
300*b7c941bbSAndroid Build Coastguard Worker  def update_dependency(self, deps, dump):
301*b7c941bbSAndroid Build Coastguard Worker    """Parse perf dump file and update dEQP dependency.
302*b7c941bbSAndroid Build Coastguard Worker
303*b7c941bbSAndroid Build Coastguard Worker     Below is an example of how dump file looks like:
304*b7c941bbSAndroid Build Coastguard Worker     630 record comm: type 3, misc 0, size 64
305*b7c941bbSAndroid Build Coastguard Worker     631   pid 23365, tid 23365, comm simpleperf
306*b7c941bbSAndroid Build Coastguard Worker     632   sample_id: pid 0, tid 0
307*b7c941bbSAndroid Build Coastguard Worker     633   sample_id: time 0
308*b7c941bbSAndroid Build Coastguard Worker     634   sample_id: id 23804
309*b7c941bbSAndroid Build Coastguard Worker     635   sample_id: cpu 0, res 0
310*b7c941bbSAndroid Build Coastguard Worker  .......
311*b7c941bbSAndroid Build Coastguard Worker     684 record comm: type 3, misc 8192, size 64
312*b7c941bbSAndroid Build Coastguard Worker     685   pid 23365, tid 23365, comm deqp-binary64
313*b7c941bbSAndroid Build Coastguard Worker     686   sample_id: pid 23365, tid 23365
314*b7c941bbSAndroid Build Coastguard Worker     687   sample_id: time 595063921159958
315*b7c941bbSAndroid Build Coastguard Worker     688   sample_id: id 23808
316*b7c941bbSAndroid Build Coastguard Worker     689   sample_id: cpu 4, res 0
317*b7c941bbSAndroid Build Coastguard Worker  .......
318*b7c941bbSAndroid Build Coastguard Worker     698 record mmap2: type 10, misc 8194, size 136
319*b7c941bbSAndroid Build Coastguard Worker     699   pid 23365, tid 23365, addr 0x58b817b000, len 0x3228000
320*b7c941bbSAndroid Build Coastguard Worker     700   pgoff 0x0, maj 253, min 9, ino 14709, ino_generation 2575019956
321*b7c941bbSAndroid Build Coastguard Worker     701   prot 1, flags 6146, filename /data/local/tmp/deqp-binary64
322*b7c941bbSAndroid Build Coastguard Worker     702   sample_id: pid 23365, tid 23365
323*b7c941bbSAndroid Build Coastguard Worker     703   sample_id: time 595063921188552
324*b7c941bbSAndroid Build Coastguard Worker     704   sample_id: id 23808
325*b7c941bbSAndroid Build Coastguard Worker     705   sample_id: cpu 4, res 0
326*b7c941bbSAndroid Build Coastguard Worker
327*b7c941bbSAndroid Build Coastguard Worker    Args:
328*b7c941bbSAndroid Build Coastguard Worker      deps: a set of string containing dEQP dependency.
329*b7c941bbSAndroid Build Coastguard Worker      dump: perf dump file's name.
330*b7c941bbSAndroid Build Coastguard Worker    """
331*b7c941bbSAndroid Build Coastguard Worker    binary_executed = False
332*b7c941bbSAndroid Build Coastguard Worker    correct_mmap = False
333*b7c941bbSAndroid Build Coastguard Worker    with open(dump, 'r') as f:
334*b7c941bbSAndroid Build Coastguard Worker      for line in f:
335*b7c941bbSAndroid Build Coastguard Worker        # It means dEQP binary starts to be executed.
336*b7c941bbSAndroid Build Coastguard Worker        if re.search(' comm .*deqp-binary', line):
337*b7c941bbSAndroid Build Coastguard Worker          binary_executed = True
338*b7c941bbSAndroid Build Coastguard Worker        if not binary_executed:
339*b7c941bbSAndroid Build Coastguard Worker          continue
340*b7c941bbSAndroid Build Coastguard Worker        # We get a new perf event
341*b7c941bbSAndroid Build Coastguard Worker        if not line.startswith(' '):
342*b7c941bbSAndroid Build Coastguard Worker          # mmap with misc 1 is not for deqp binary.
343*b7c941bbSAndroid Build Coastguard Worker          correct_mmap = line.startswith('record mmap') and 'misc 1,' not in line
344*b7c941bbSAndroid Build Coastguard Worker        # Get file name in memory map.
345*b7c941bbSAndroid Build Coastguard Worker        if 'filename' in line and correct_mmap:
346*b7c941bbSAndroid Build Coastguard Worker          deps_file = line[line.find('filename') + 9:].strip()
347*b7c941bbSAndroid Build Coastguard Worker          if not re.search(self._exclude_deqp_pattern, deps_file):
348*b7c941bbSAndroid Build Coastguard Worker            deps.add(deps_file)
349*b7c941bbSAndroid Build Coastguard Worker
350*b7c941bbSAndroid Build Coastguard Worker
351*b7c941bbSAndroid Build Coastguard Worker  def get_test_binary_name(self, test_name):
352*b7c941bbSAndroid Build Coastguard Worker    """Get dEQP binary's name based on test name.
353*b7c941bbSAndroid Build Coastguard Worker
354*b7c941bbSAndroid Build Coastguard Worker    Args:
355*b7c941bbSAndroid Build Coastguard Worker      test_name: name of test.
356*b7c941bbSAndroid Build Coastguard Worker    Returns:
357*b7c941bbSAndroid Build Coastguard Worker      dEQP binary's name.
358*b7c941bbSAndroid Build Coastguard Worker    """
359*b7c941bbSAndroid Build Coastguard Worker    if test_name.endswith('32'):
360*b7c941bbSAndroid Build Coastguard Worker      return 'deqp-binary'
361*b7c941bbSAndroid Build Coastguard Worker    elif test_name.endswith('64'):
362*b7c941bbSAndroid Build Coastguard Worker      return 'deqp-binary64'
363*b7c941bbSAndroid Build Coastguard Worker    else:
364*b7c941bbSAndroid Build Coastguard Worker      raise TestError('Fail to get dEQP binary due to unknonw test name: ' + test_name)
365*b7c941bbSAndroid Build Coastguard Worker
366*b7c941bbSAndroid Build Coastguard Worker  def get_test_log_name(self, test_name):
367*b7c941bbSAndroid Build Coastguard Worker    """Get test log's name based on test name.
368*b7c941bbSAndroid Build Coastguard Worker
369*b7c941bbSAndroid Build Coastguard Worker    Args:
370*b7c941bbSAndroid Build Coastguard Worker      test_name: name of test.
371*b7c941bbSAndroid Build Coastguard Worker    Returns:
372*b7c941bbSAndroid Build Coastguard Worker      test log's name when running dEQP test.
373*b7c941bbSAndroid Build Coastguard Worker    """
374*b7c941bbSAndroid Build Coastguard Worker    return test_name + '.qpa'
375*b7c941bbSAndroid Build Coastguard Worker
376*b7c941bbSAndroid Build Coastguard Worker  def get_test_perf_name(self, test_name):
377*b7c941bbSAndroid Build Coastguard Worker    """Get perf file's name based on test name.
378*b7c941bbSAndroid Build Coastguard Worker
379*b7c941bbSAndroid Build Coastguard Worker    Args:
380*b7c941bbSAndroid Build Coastguard Worker      test_name: name of test.
381*b7c941bbSAndroid Build Coastguard Worker    Returns:
382*b7c941bbSAndroid Build Coastguard Worker      perf file's name.
383*b7c941bbSAndroid Build Coastguard Worker    """
384*b7c941bbSAndroid Build Coastguard Worker    return test_name + '.data'
385*b7c941bbSAndroid Build Coastguard Worker
386*b7c941bbSAndroid Build Coastguard Worker  def get_perf_dump_name(self, test_name):
387*b7c941bbSAndroid Build Coastguard Worker    """Get perf dump file's name based on test name.
388*b7c941bbSAndroid Build Coastguard Worker
389*b7c941bbSAndroid Build Coastguard Worker    Args:
390*b7c941bbSAndroid Build Coastguard Worker      test_name: name of test.
391*b7c941bbSAndroid Build Coastguard Worker    Returns:
392*b7c941bbSAndroid Build Coastguard Worker      perf dump file's name.
393*b7c941bbSAndroid Build Coastguard Worker    """
394*b7c941bbSAndroid Build Coastguard Worker    return test_name + '-perf-dump.txt'
395*b7c941bbSAndroid Build Coastguard Worker
396*b7c941bbSAndroid Build Coastguard Worker  def get_test_list_name(self, test_name):
397*b7c941bbSAndroid Build Coastguard Worker    """Get test list file's name based on test name.
398*b7c941bbSAndroid Build Coastguard Worker
399*b7c941bbSAndroid Build Coastguard Worker    test list file is used to run dEQP test.
400*b7c941bbSAndroid Build Coastguard Worker
401*b7c941bbSAndroid Build Coastguard Worker    Args:
402*b7c941bbSAndroid Build Coastguard Worker      test_name: name of test.
403*b7c941bbSAndroid Build Coastguard Worker    Returns:
404*b7c941bbSAndroid Build Coastguard Worker      test list file's name.
405*b7c941bbSAndroid Build Coastguard Worker    """
406*b7c941bbSAndroid Build Coastguard Worker    if test_name.startswith('vk'):
407*b7c941bbSAndroid Build Coastguard Worker      return 'vk-master-subset.txt'
408*b7c941bbSAndroid Build Coastguard Worker    elif test_name.startswith('gles3'):
409*b7c941bbSAndroid Build Coastguard Worker      return 'gles3-master-subset.txt'
410*b7c941bbSAndroid Build Coastguard Worker    else:
411*b7c941bbSAndroid Build Coastguard Worker      raise TestError('Fail to get test list due to unknown test name: ' + test_name)
412*b7c941bbSAndroid Build Coastguard Worker
413*b7c941bbSAndroid Build Coastguard Worker  def get_deqp_dependency(self):
414*b7c941bbSAndroid Build Coastguard Worker    """Get dEQP dependency.
415*b7c941bbSAndroid Build Coastguard Worker
416*b7c941bbSAndroid Build Coastguard Worker    Returns:
417*b7c941bbSAndroid Build Coastguard Worker      A set of dEQP dependency.
418*b7c941bbSAndroid Build Coastguard Worker    """
419*b7c941bbSAndroid Build Coastguard Worker    device_deqp_dir = '/data/local/tmp'
420*b7c941bbSAndroid Build Coastguard Worker    device_deqp_out_dir = '/data/local/tmp/out'
421*b7c941bbSAndroid Build Coastguard Worker    test_list = ['vk-32', 'vk-64', 'gles3-32', 'gles3-64']
422*b7c941bbSAndroid Build Coastguard Worker
423*b7c941bbSAndroid Build Coastguard Worker    # Clean up the device.
424*b7c941bbSAndroid Build Coastguard Worker    self._adb.run_shell_command('mkdir -p ' + device_deqp_out_dir)
425*b7c941bbSAndroid Build Coastguard Worker
426*b7c941bbSAndroid Build Coastguard Worker    # Copy test resources to device.
427*b7c941bbSAndroid Build Coastguard Worker    logger.info(self._adb.push_file(self._test_dir + '/*', device_deqp_dir))
428*b7c941bbSAndroid Build Coastguard Worker
429*b7c941bbSAndroid Build Coastguard Worker    # Run the dEQP binary with simpleperf
430*b7c941bbSAndroid Build Coastguard Worker    print('Running a subset of dEQP tests as binary on the device...')
431*b7c941bbSAndroid Build Coastguard Worker    deqp_deps = set()
432*b7c941bbSAndroid Build Coastguard Worker    for test in test_list:
433*b7c941bbSAndroid Build Coastguard Worker      test_file = os.path.join(device_deqp_dir, self.get_test_list_name(test))
434*b7c941bbSAndroid Build Coastguard Worker      log_file = os.path.join(device_deqp_out_dir, self.get_test_log_name(test))
435*b7c941bbSAndroid Build Coastguard Worker      perf_file = os.path.join(device_deqp_out_dir, self.get_test_perf_name(test))
436*b7c941bbSAndroid Build Coastguard Worker      deqp_binary = os.path.join(device_deqp_dir, self.get_test_binary_name(test))
437*b7c941bbSAndroid Build Coastguard Worker      simpleperf_command = ('"cd {device_deqp_dir} && simpleperf record -o {perf_file} {binary} '
438*b7c941bbSAndroid Build Coastguard Worker                            '--deqp-caselist-file={test_list} --deqp-log-images=disable '
439*b7c941bbSAndroid Build Coastguard Worker                            '--deqp-log-shader-sources=disable --deqp-log-filename={log_file} '
440*b7c941bbSAndroid Build Coastguard Worker                            '--deqp-surface-type=fbo --deqp-surface-width=2048 '
441*b7c941bbSAndroid Build Coastguard Worker                            '--deqp-surface-height=2048"')
442*b7c941bbSAndroid Build Coastguard Worker      self._adb.run_shell_command(
443*b7c941bbSAndroid Build Coastguard Worker          simpleperf_command.format(device_deqp_dir=device_deqp_dir, binary=deqp_binary,
444*b7c941bbSAndroid Build Coastguard Worker                                    perf_file=perf_file, test_list=test_file, log_file=log_file))
445*b7c941bbSAndroid Build Coastguard Worker
446*b7c941bbSAndroid Build Coastguard Worker      # Check test log.
447*b7c941bbSAndroid Build Coastguard Worker      host_log_file = os.path.join(self._work_dir, self.get_test_log_name(test))
448*b7c941bbSAndroid Build Coastguard Worker      self._adb.pull_file(log_file, host_log_file )
449*b7c941bbSAndroid Build Coastguard Worker      if not self.check_test_log(os.path.join(self._test_dir, self.get_test_list_name(test)),
450*b7c941bbSAndroid Build Coastguard Worker                                 host_log_file):
451*b7c941bbSAndroid Build Coastguard Worker        error_msg = ('Fail to run incremental dEQP because of crashed test. Check test'
452*b7c941bbSAndroid Build Coastguard Worker                     'log {} for more detail.').format(host_log_file)
453*b7c941bbSAndroid Build Coastguard Worker        logger.error(error_msg)
454*b7c941bbSAndroid Build Coastguard Worker        raise TestError(error_msg)
455*b7c941bbSAndroid Build Coastguard Worker    print('Tests are all passed!')
456*b7c941bbSAndroid Build Coastguard Worker
457*b7c941bbSAndroid Build Coastguard Worker    # Parse perf dump result to get dependency.
458*b7c941bbSAndroid Build Coastguard Worker    print('Analyzing dEQP dependency...')
459*b7c941bbSAndroid Build Coastguard Worker    for test in test_list:
460*b7c941bbSAndroid Build Coastguard Worker      perf_file = os.path.join(device_deqp_out_dir, self.get_test_perf_name(test))
461*b7c941bbSAndroid Build Coastguard Worker      dump_file = os.path.join(self._work_dir, self.get_perf_dump_name(test))
462*b7c941bbSAndroid Build Coastguard Worker      self._adb.run_shell_command('simpleperf dump {perf_file} > {dump_file}'
463*b7c941bbSAndroid Build Coastguard Worker                                  .format(perf_file=perf_file, dump_file=dump_file))
464*b7c941bbSAndroid Build Coastguard Worker      self.update_dependency(deqp_deps, dump_file)
465*b7c941bbSAndroid Build Coastguard Worker    print('Done!')
466*b7c941bbSAndroid Build Coastguard Worker    return deqp_deps
467*b7c941bbSAndroid Build Coastguard Worker
468*b7c941bbSAndroid Build Coastguard Workerdef _is_deqp_dependency(dependency_name):
469*b7c941bbSAndroid Build Coastguard Worker  """Check if dependency is related to dEQP."""
470*b7c941bbSAndroid Build Coastguard Worker  # dEQP dependency with pattern below will not be used to compare build:
471*b7c941bbSAndroid Build Coastguard Worker  # files has /apex/ prefix are not related to dEQP.
472*b7c941bbSAndroid Build Coastguard Worker  return not re.search(re.compile('^/apex/'), dependency_name)
473*b7c941bbSAndroid Build Coastguard Worker
474*b7c941bbSAndroid Build Coastguard Workerdef _get_parser():
475*b7c941bbSAndroid Build Coastguard Worker  parser = argparse.ArgumentParser(description='Run incremental dEQP on devices.')
476*b7c941bbSAndroid Build Coastguard Worker  parser.add_argument('-s', '--serial', help='Optional. Use device with given serial.')
477*b7c941bbSAndroid Build Coastguard Worker  parser.add_argument('-t', '--test', help=('Optional. Directory of incremental deqp test file. '
478*b7c941bbSAndroid Build Coastguard Worker                                            'This directory should have test resources and dEQP '
479*b7c941bbSAndroid Build Coastguard Worker                                            'binaries.'))
480*b7c941bbSAndroid Build Coastguard Worker  parser.add_argument('-b', '--base_build', help=('Target file of base build that has passed dEQP '
481*b7c941bbSAndroid Build Coastguard Worker                                                  'test, e.g. flame-target_files-6935423.zip.'))
482*b7c941bbSAndroid Build Coastguard Worker  parser.add_argument('-c', '--current_build',
483*b7c941bbSAndroid Build Coastguard Worker                      help=('Optional. When empty, the script will read files in the build from '
484*b7c941bbSAndroid Build Coastguard Worker                            'the device via adb. When set, the script will read build files from '
485*b7c941bbSAndroid Build Coastguard Worker                            'the file provided by this argument. And this file should be the '
486*b7c941bbSAndroid Build Coastguard Worker                            'current build that is flashed to device, such as a target file '
487*b7c941bbSAndroid Build Coastguard Worker                            'like flame-target_files-6935424.zip. This argument can be used when '
488*b7c941bbSAndroid Build Coastguard Worker                            'some dependencies files are not accessible via adb.'))
489*b7c941bbSAndroid Build Coastguard Worker  parser.add_argument('--generate_deps_only', action='store_true',
490*b7c941bbSAndroid Build Coastguard Worker                      help=('Run test and generate dEQP dependency list only '
491*b7c941bbSAndroid Build Coastguard Worker                            'without comparing build.'))
492*b7c941bbSAndroid Build Coastguard Worker  parser.add_argument('--custom_handler', action='store_true',
493*b7c941bbSAndroid Build Coastguard Worker                      help='Use custome build file handler')
494*b7c941bbSAndroid Build Coastguard Worker  parser.add_argument('--ats_mode', action='store_true',
495*b7c941bbSAndroid Build Coastguard Worker                      help=('Run incremental dEQP with Android Test Station.'))
496*b7c941bbSAndroid Build Coastguard Worker  parser.add_argument('--userdebug_build', action='store_true',
497*b7c941bbSAndroid Build Coastguard Worker                      help=('ATS mode option. Current build on device is userdebug.'))
498*b7c941bbSAndroid Build Coastguard Worker  return parser
499*b7c941bbSAndroid Build Coastguard Worker
500*b7c941bbSAndroid Build Coastguard Workerdef _create_logger(log_file_name):
501*b7c941bbSAndroid Build Coastguard Worker  """Create logger.
502*b7c941bbSAndroid Build Coastguard Worker
503*b7c941bbSAndroid Build Coastguard Worker  Args:
504*b7c941bbSAndroid Build Coastguard Worker    log_file_name: absolute path of the log file.
505*b7c941bbSAndroid Build Coastguard Worker  Returns:
506*b7c941bbSAndroid Build Coastguard Worker    a logging.Logger
507*b7c941bbSAndroid Build Coastguard Worker  """
508*b7c941bbSAndroid Build Coastguard Worker  logging.basicConfig(filename=log_file_name)
509*b7c941bbSAndroid Build Coastguard Worker  logger = logging.getLogger()
510*b7c941bbSAndroid Build Coastguard Worker  logger.setLevel(level=logging.NOTSET)
511*b7c941bbSAndroid Build Coastguard Worker  return logger
512*b7c941bbSAndroid Build Coastguard Worker
513*b7c941bbSAndroid Build Coastguard Workerdef _save_deqp_deps(deqp_deps, file_name):
514*b7c941bbSAndroid Build Coastguard Worker  """Save dEQP dependency to file.
515*b7c941bbSAndroid Build Coastguard Worker
516*b7c941bbSAndroid Build Coastguard Worker  Args:
517*b7c941bbSAndroid Build Coastguard Worker    deqp_deps: a set of dEQP dependency.
518*b7c941bbSAndroid Build Coastguard Worker    file_name: name of the file to save dEQP dependency.
519*b7c941bbSAndroid Build Coastguard Worker  Returns:
520*b7c941bbSAndroid Build Coastguard Worker    name of the file that saves dEQP dependency.
521*b7c941bbSAndroid Build Coastguard Worker  """
522*b7c941bbSAndroid Build Coastguard Worker  with open(file_name, 'w') as f:
523*b7c941bbSAndroid Build Coastguard Worker    for dep in sorted(deqp_deps):
524*b7c941bbSAndroid Build Coastguard Worker      f.write(dep+'\n')
525*b7c941bbSAndroid Build Coastguard Worker  return file_name
526*b7c941bbSAndroid Build Coastguard Worker
527*b7c941bbSAndroid Build Coastguard Workerdef _generate_report(
528*b7c941bbSAndroid Build Coastguard Worker    report_name,
529*b7c941bbSAndroid Build Coastguard Worker    base_build_fingerprint,
530*b7c941bbSAndroid Build Coastguard Worker    current_build_fingerprint,
531*b7c941bbSAndroid Build Coastguard Worker    deqp_deps,
532*b7c941bbSAndroid Build Coastguard Worker    extra_deqp_deps,
533*b7c941bbSAndroid Build Coastguard Worker    deqp_deps_changes):
534*b7c941bbSAndroid Build Coastguard Worker  """Generate a json report.
535*b7c941bbSAndroid Build Coastguard Worker
536*b7c941bbSAndroid Build Coastguard Worker  Args:
537*b7c941bbSAndroid Build Coastguard Worker    report_name: absolute file name of report.
538*b7c941bbSAndroid Build Coastguard Worker    base_build_fingerprint: fingerprint of the base build.
539*b7c941bbSAndroid Build Coastguard Worker    current_build_fingerprint: fingerprint of the current build.
540*b7c941bbSAndroid Build Coastguard Worker    deqp_deps: list of dEQP dependencies generated by the tool.
541*b7c941bbSAndroid Build Coastguard Worker    extra_deqp_deps: list of extra dEQP dependencies.
542*b7c941bbSAndroid Build Coastguard Worker    deqp_deps_changes: dictionary of dependency changes.
543*b7c941bbSAndroid Build Coastguard Worker  """
544*b7c941bbSAndroid Build Coastguard Worker  data = {}
545*b7c941bbSAndroid Build Coastguard Worker  data['base_build_fingerprint'] = base_build_fingerprint
546*b7c941bbSAndroid Build Coastguard Worker  data['current_build_fingerprint'] = current_build_fingerprint
547*b7c941bbSAndroid Build Coastguard Worker  data['deqp_deps'] = sorted(list(deqp_deps))
548*b7c941bbSAndroid Build Coastguard Worker  data['extra_deqp_deps'] = sorted(list(extra_deqp_deps))
549*b7c941bbSAndroid Build Coastguard Worker  data['deqp_deps_changes'] = deqp_deps_changes
550*b7c941bbSAndroid Build Coastguard Worker
551*b7c941bbSAndroid Build Coastguard Worker  with open(report_name, 'w') as f:
552*b7c941bbSAndroid Build Coastguard Worker    json.dump(data, f, indent=4)
553*b7c941bbSAndroid Build Coastguard Worker
554*b7c941bbSAndroid Build Coastguard Worker  print('Incremental dEQP report is generated at: ' + report_name)
555*b7c941bbSAndroid Build Coastguard Worker
556*b7c941bbSAndroid Build Coastguard Worker
557*b7c941bbSAndroid Build Coastguard Workerdef _local_run(args, work_dir):
558*b7c941bbSAndroid Build Coastguard Worker  """Run incremental dEQP locally.
559*b7c941bbSAndroid Build Coastguard Worker
560*b7c941bbSAndroid Build Coastguard Worker  Args:
561*b7c941bbSAndroid Build Coastguard Worker    args: return of parser.parse_args().
562*b7c941bbSAndroid Build Coastguard Worker    work_dir: path of directory for saving script result and logs.
563*b7c941bbSAndroid Build Coastguard Worker  """
564*b7c941bbSAndroid Build Coastguard Worker  print('Logs and simpleperf results will be copied to: ' + work_dir)
565*b7c941bbSAndroid Build Coastguard Worker  if args.test:
566*b7c941bbSAndroid Build Coastguard Worker    test_dir = args.test
567*b7c941bbSAndroid Build Coastguard Worker  else:
568*b7c941bbSAndroid Build Coastguard Worker    test_dir = os.path.dirname(os.path.abspath(__file__))
569*b7c941bbSAndroid Build Coastguard Worker  # Extra dEQP dependencies are the files can't be loaded to memory such as firmware.
570*b7c941bbSAndroid Build Coastguard Worker  extra_deqp_deps = set()
571*b7c941bbSAndroid Build Coastguard Worker  extra_deqp_deps_file = os.path.join(test_dir, 'extra_deqp_dependency.txt')
572*b7c941bbSAndroid Build Coastguard Worker  if not os.path.exists(extra_deqp_deps_file):
573*b7c941bbSAndroid Build Coastguard Worker    if not args.generate_deps_only:
574*b7c941bbSAndroid Build Coastguard Worker      raise TestResourceError('{test_resource} doesn\'t exist'
575*b7c941bbSAndroid Build Coastguard Worker                             .format(test_resource=extra_deqp_deps_file))
576*b7c941bbSAndroid Build Coastguard Worker  else:
577*b7c941bbSAndroid Build Coastguard Worker    with open(extra_deqp_deps_file, 'r') as f:
578*b7c941bbSAndroid Build Coastguard Worker      for line in f:
579*b7c941bbSAndroid Build Coastguard Worker        extra_deqp_deps.add(line.strip())
580*b7c941bbSAndroid Build Coastguard Worker
581*b7c941bbSAndroid Build Coastguard Worker  if args.serial:
582*b7c941bbSAndroid Build Coastguard Worker    adb = AdbHelper(args.serial)
583*b7c941bbSAndroid Build Coastguard Worker  else:
584*b7c941bbSAndroid Build Coastguard Worker    adb = AdbHelper()
585*b7c941bbSAndroid Build Coastguard Worker
586*b7c941bbSAndroid Build Coastguard Worker  dependency_collector = DeqpDependencyCollector(work_dir, test_dir, adb)
587*b7c941bbSAndroid Build Coastguard Worker  deqp_deps = dependency_collector.get_deqp_dependency()
588*b7c941bbSAndroid Build Coastguard Worker  aggregated_deqp_deps = deqp_deps.union(extra_deqp_deps)
589*b7c941bbSAndroid Build Coastguard Worker
590*b7c941bbSAndroid Build Coastguard Worker  deqp_deps_file_name = _save_deqp_deps(aggregated_deqp_deps,
591*b7c941bbSAndroid Build Coastguard Worker                                        os.path.join(work_dir, 'dEQP-dependency.txt'))
592*b7c941bbSAndroid Build Coastguard Worker  print('dEQP dependency list has been generated in: ' + deqp_deps_file_name)
593*b7c941bbSAndroid Build Coastguard Worker
594*b7c941bbSAndroid Build Coastguard Worker  if args.generate_deps_only:
595*b7c941bbSAndroid Build Coastguard Worker    return
596*b7c941bbSAndroid Build Coastguard Worker
597*b7c941bbSAndroid Build Coastguard Worker  # Compare the build difference with dEQP dependency
598*b7c941bbSAndroid Build Coastguard Worker  valid_deqp_deps = [dep for dep in aggregated_deqp_deps if _is_deqp_dependency(dep)]
599*b7c941bbSAndroid Build Coastguard Worker  build_helper = BuildHelper(args.custom_handler)
600*b7c941bbSAndroid Build Coastguard Worker  if args.current_build:
601*b7c941bbSAndroid Build Coastguard Worker    skip_dEQP, changes = build_helper.compare_base_build_with_current_build(
602*b7c941bbSAndroid Build Coastguard Worker        valid_deqp_deps, args.current_build, args.base_build)
603*b7c941bbSAndroid Build Coastguard Worker  else:
604*b7c941bbSAndroid Build Coastguard Worker    skip_dEQP, changes = build_helper.compare_base_build_with_device_files(
605*b7c941bbSAndroid Build Coastguard Worker        valid_deqp_deps, adb, args.base_build)
606*b7c941bbSAndroid Build Coastguard Worker  if skip_dEQP:
607*b7c941bbSAndroid Build Coastguard Worker    print('Congratulations, current build could skip dEQP test.\n'
608*b7c941bbSAndroid Build Coastguard Worker          'If you run CTS through suite, you could pass filter like '
609*b7c941bbSAndroid Build Coastguard Worker          '\'--exclude-filter CtsDeqpTestCases\'.')
610*b7c941bbSAndroid Build Coastguard Worker  else:
611*b7c941bbSAndroid Build Coastguard Worker    print('Sorry, current build can\'t skip dEQP test because dEQP dependency has been '
612*b7c941bbSAndroid Build Coastguard Worker          'changed.\nPlease check logs for more details.')
613*b7c941bbSAndroid Build Coastguard Worker
614*b7c941bbSAndroid Build Coastguard Worker  _generate_report(os.path.join(work_dir, REPORT_FILENAME),
615*b7c941bbSAndroid Build Coastguard Worker                   build_helper.get_system_fingerprint(args.base_build),
616*b7c941bbSAndroid Build Coastguard Worker                   adb.get_fingerprint(),
617*b7c941bbSAndroid Build Coastguard Worker                   deqp_deps,
618*b7c941bbSAndroid Build Coastguard Worker                   extra_deqp_deps,
619*b7c941bbSAndroid Build Coastguard Worker                   changes)
620*b7c941bbSAndroid Build Coastguard Worker
621*b7c941bbSAndroid Build Coastguard Workerdef _generate_cts_xml(out_dir, content):
622*b7c941bbSAndroid Build Coastguard Worker  """Generate cts configuration for Android Test Station.
623*b7c941bbSAndroid Build Coastguard Worker
624*b7c941bbSAndroid Build Coastguard Worker  Args:
625*b7c941bbSAndroid Build Coastguard Worker   out_dir: output directory for cts confiugration.
626*b7c941bbSAndroid Build Coastguard Worker   content: configuration content.
627*b7c941bbSAndroid Build Coastguard Worker  """
628*b7c941bbSAndroid Build Coastguard Worker  with open(os.path.join(out_dir, 'incremental_deqp.xml'), 'w') as f:
629*b7c941bbSAndroid Build Coastguard Worker    f.write(content)
630*b7c941bbSAndroid Build Coastguard Worker
631*b7c941bbSAndroid Build Coastguard Worker
632*b7c941bbSAndroid Build Coastguard Workerdef _ats_run(args, work_dir):
633*b7c941bbSAndroid Build Coastguard Worker  """Run incremental dEQP with Android Test Station.
634*b7c941bbSAndroid Build Coastguard Worker
635*b7c941bbSAndroid Build Coastguard Worker  Args:
636*b7c941bbSAndroid Build Coastguard Worker    args: return of parser.parse_args().
637*b7c941bbSAndroid Build Coastguard Worker    work_dir: path of directory for saving script result and logs.
638*b7c941bbSAndroid Build Coastguard Worker  """
639*b7c941bbSAndroid Build Coastguard Worker  # Extra dEQP dependencies are the files can't be loaded to memory such as firmware.
640*b7c941bbSAndroid Build Coastguard Worker  extra_deqp_deps = set()
641*b7c941bbSAndroid Build Coastguard Worker  with open(os.path.join(work_dir, 'extra_deqp_dependency.txt'), 'r') as f:
642*b7c941bbSAndroid Build Coastguard Worker    for line in f:
643*b7c941bbSAndroid Build Coastguard Worker      if line.strip():
644*b7c941bbSAndroid Build Coastguard Worker        extra_deqp_deps.add(line.strip())
645*b7c941bbSAndroid Build Coastguard Worker
646*b7c941bbSAndroid Build Coastguard Worker  android_serials = os.getenv('ANDROID_SERIALS')
647*b7c941bbSAndroid Build Coastguard Worker  if not android_serials:
648*b7c941bbSAndroid Build Coastguard Worker    raise AtsError('Fail to read environment variable ANDROID_SERIALS.')
649*b7c941bbSAndroid Build Coastguard Worker  first_device_serial = android_serials.split(',')[0]
650*b7c941bbSAndroid Build Coastguard Worker  adb = AdbHelper(first_device_serial)
651*b7c941bbSAndroid Build Coastguard Worker
652*b7c941bbSAndroid Build Coastguard Worker  dependency_collector = DeqpDependencyCollector(work_dir,
653*b7c941bbSAndroid Build Coastguard Worker                                                 os.path.join(work_dir, 'test_resources'), adb)
654*b7c941bbSAndroid Build Coastguard Worker  deqp_deps = dependency_collector.get_deqp_dependency()
655*b7c941bbSAndroid Build Coastguard Worker  aggregated_deqp_deps = deqp_deps.union(extra_deqp_deps)
656*b7c941bbSAndroid Build Coastguard Worker
657*b7c941bbSAndroid Build Coastguard Worker  deqp_deps_file_name = _save_deqp_deps(aggregated_deqp_deps,
658*b7c941bbSAndroid Build Coastguard Worker                                        os.path.join(work_dir, 'dEQP-dependency.txt'))
659*b7c941bbSAndroid Build Coastguard Worker
660*b7c941bbSAndroid Build Coastguard Worker  if args.generate_deps_only:
661*b7c941bbSAndroid Build Coastguard Worker    _generate_cts_xml(work_dir, DEFAULT_CTS_XML)
662*b7c941bbSAndroid Build Coastguard Worker    return
663*b7c941bbSAndroid Build Coastguard Worker
664*b7c941bbSAndroid Build Coastguard Worker  # Compare the build difference with dEQP dependency
665*b7c941bbSAndroid Build Coastguard Worker  valid_deqp_deps = [dep for dep in aggregated_deqp_deps if _is_deqp_dependency(dep)]
666*b7c941bbSAndroid Build Coastguard Worker
667*b7c941bbSAndroid Build Coastguard Worker  # base build target file is from test resources.
668*b7c941bbSAndroid Build Coastguard Worker  base_build_target = os.path.join(work_dir, 'base_build_target_files')
669*b7c941bbSAndroid Build Coastguard Worker  build_helper = BuildHelper(args.custom_handler)
670*b7c941bbSAndroid Build Coastguard Worker  if args.userdebug_build:
671*b7c941bbSAndroid Build Coastguard Worker    current_build_fingerprint = adb.get_fingerprint()
672*b7c941bbSAndroid Build Coastguard Worker    skip_dEQP, changes = build_helper.compare_base_build_with_device_files(
673*b7c941bbSAndroid Build Coastguard Worker        valid_deqp_deps, adb, base_build_target)
674*b7c941bbSAndroid Build Coastguard Worker  else:
675*b7c941bbSAndroid Build Coastguard Worker    current_build_target = os.path.join(work_dir, 'current_build_target_files')
676*b7c941bbSAndroid Build Coastguard Worker    current_build_fingerprint = build_helper.get_system_fingerprint(current_build_target)
677*b7c941bbSAndroid Build Coastguard Worker    skip_dEQP, changes = build_helper.compare_base_build_with_current_build(
678*b7c941bbSAndroid Build Coastguard Worker        valid_deqp_deps, current_build_target, base_build_target)
679*b7c941bbSAndroid Build Coastguard Worker  if skip_dEQP:
680*b7c941bbSAndroid Build Coastguard Worker    _generate_cts_xml(work_dir, INCREMENTAL_DEQP_XML)
681*b7c941bbSAndroid Build Coastguard Worker  else:
682*b7c941bbSAndroid Build Coastguard Worker    _generate_cts_xml(work_dir, DEFAULT_CTS_XML)
683*b7c941bbSAndroid Build Coastguard Worker
684*b7c941bbSAndroid Build Coastguard Worker  _generate_report(os.path.join(*[work_dir, 'logs', REPORT_FILENAME]),
685*b7c941bbSAndroid Build Coastguard Worker                   build_helper.get_system_fingerprint(base_build_target),
686*b7c941bbSAndroid Build Coastguard Worker                   current_build_fingerprint,
687*b7c941bbSAndroid Build Coastguard Worker                   deqp_deps,
688*b7c941bbSAndroid Build Coastguard Worker                   extra_deqp_deps,
689*b7c941bbSAndroid Build Coastguard Worker                   changes)
690*b7c941bbSAndroid Build Coastguard Worker
691*b7c941bbSAndroid Build Coastguard Workerdef main():
692*b7c941bbSAndroid Build Coastguard Worker  parser = _get_parser()
693*b7c941bbSAndroid Build Coastguard Worker  args = parser.parse_args()
694*b7c941bbSAndroid Build Coastguard Worker  if not args.generate_deps_only and not args.base_build and not args.ats_mode:
695*b7c941bbSAndroid Build Coastguard Worker    parser.error('Base build argument: \'-b [file] or --base_build [file]\' '
696*b7c941bbSAndroid Build Coastguard Worker                 'is required to compare build.')
697*b7c941bbSAndroid Build Coastguard Worker
698*b7c941bbSAndroid Build Coastguard Worker  work_dir = ''
699*b7c941bbSAndroid Build Coastguard Worker  log_file_name = ''
700*b7c941bbSAndroid Build Coastguard Worker  if args.ats_mode:
701*b7c941bbSAndroid Build Coastguard Worker    work_dir = os.getenv('TF_WORK_DIR')
702*b7c941bbSAndroid Build Coastguard Worker    log_file_name = os.path.join(*[work_dir, 'logs', 'incremental-deqp-log-'+str(uuid.uuid4())])
703*b7c941bbSAndroid Build Coastguard Worker  else:
704*b7c941bbSAndroid Build Coastguard Worker    work_dir = tempfile.mkdtemp(prefix='incremental-deqp-'
705*b7c941bbSAndroid Build Coastguard Worker                                + time.strftime("%Y%m%d-%H%M%S"))
706*b7c941bbSAndroid Build Coastguard Worker    log_file_name = os.path.join(work_dir, 'incremental-deqp-log')
707*b7c941bbSAndroid Build Coastguard Worker  global logger
708*b7c941bbSAndroid Build Coastguard Worker  logger = _create_logger(log_file_name)
709*b7c941bbSAndroid Build Coastguard Worker
710*b7c941bbSAndroid Build Coastguard Worker  if args.ats_mode:
711*b7c941bbSAndroid Build Coastguard Worker    _ats_run(args, work_dir)
712*b7c941bbSAndroid Build Coastguard Worker  else:
713*b7c941bbSAndroid Build Coastguard Worker    _local_run(args, work_dir)
714*b7c941bbSAndroid Build Coastguard Worker
715*b7c941bbSAndroid Build Coastguard Workerif __name__ == '__main__':
716*b7c941bbSAndroid Build Coastguard Worker  main()
717*b7c941bbSAndroid Build Coastguard Worker
718