1#! /usr/bin/env vpython3 2# 3# Copyright 2023 The ANGLE Project Authors. All rights reserved. 4# Use of this source code is governed by a BSD-style license that can be 5# found in the LICENSE file. 6# 7 8import argparse 9import contextlib 10import difflib 11import json 12import logging 13import os 14import pathlib 15import shutil 16import subprocess 17import sys 18import tempfile 19import time 20 21SCRIPT_DIR = str(pathlib.Path(__file__).resolve().parent) 22PY_UTILS = str(pathlib.Path(SCRIPT_DIR) / '..' / 'py_utils') 23if PY_UTILS not in sys.path: 24 os.stat(PY_UTILS) and sys.path.insert(0, PY_UTILS) 25import angle_test_util 26 27 28@contextlib.contextmanager 29def temporary_dir(prefix=''): 30 path = tempfile.mkdtemp(prefix=prefix) 31 try: 32 yield path 33 finally: 34 logging.info("Removing temporary directory: %s" % path) 35 shutil.rmtree(path) 36 37 38def file_content(path): 39 with open(path, 'rb') as f: 40 content = f.read() 41 42 if path.endswith('.json'): 43 info = json.loads(content) 44 info['TraceMetadata']['CaptureRevision'] = '<ignored>' 45 return json.dumps(info, indent=2).encode() 46 47 return content 48 49 50def diff_files(path, expected_path): 51 content = file_content(path) 52 expected_content = file_content(expected_path) 53 fn = os.path.basename(path) 54 55 if content == expected_content: 56 return False 57 58 if fn.endswith('.angledata'): 59 logging.error('Checks failed. Binary file contents mismatch: %s', fn) 60 return True 61 62 # Captured files are expected to have LF line endings. 63 # Note that git's EOL conversion for these files is disabled via .gitattributes 64 assert b'\r\n' not in content 65 assert b'\r\n' not in expected_content 66 67 diff = list( 68 difflib.unified_diff( 69 expected_content.decode().splitlines(), 70 content.decode().splitlines(), 71 fromfile=fn, 72 tofile=fn, 73 )) 74 75 logging.error('Checks failed. Found diff in %s:\n%s\n', fn, '\n'.join(diff)) 76 return True 77 78 79def run_test(test_name, overwrite_expected): 80 with temporary_dir() as temp_dir: 81 if angle_test_util.IsAndroid(): 82 cmd = [ 83 '../../src/tests/angle_android_test_runner.py', 'gtest', 84 '--suite=angle_end2end_tests', '--output-directory=.' 85 ] 86 else: 87 cmd = [angle_test_util.ExecutablePathInCurrentDir('angle_end2end_tests')] 88 89 test_args = ['--gtest_filter=%s' % test_name, '--angle-per-test-capture-label'] 90 extra_env = { 91 'ANGLE_CAPTURE_ENABLED': '1', 92 'ANGLE_CAPTURE_FRAME_START': '2', 93 'ANGLE_CAPTURE_FRAME_END': '5', 94 'ANGLE_CAPTURE_OUT_DIR': temp_dir, 95 'ANGLE_CAPTURE_COMPRESSION': '0', 96 } 97 subprocess.check_call(cmd + test_args, env={**os.environ.copy(), **extra_env}) 98 logging.info('Capture finished, comparing files') 99 files = sorted(fn for fn in os.listdir(temp_dir)) 100 expected_dir = os.path.join(SCRIPT_DIR, 'expected') 101 expected_files = sorted(fn for fn in os.listdir(expected_dir) if not fn.startswith('.')) 102 103 if overwrite_expected: 104 for f in expected_files: 105 os.remove(os.path.join(expected_dir, f)) 106 shutil.copytree(temp_dir, expected_dir, dirs_exist_ok=True) 107 return True 108 109 if files != expected_files: 110 logging.error( 111 'Checks failed. Capture produced a different set of files: %s\nDiff:\n%s\n', files, 112 '\n'.join(difflib.unified_diff(expected_files, files))) 113 return False 114 115 has_diffs = False 116 for fn in files: 117 has_diffs |= diff_files(os.path.join(temp_dir, fn), os.path.join(expected_dir, fn)) 118 119 return not has_diffs 120 121 122def main(): 123 parser = argparse.ArgumentParser() 124 parser.add_argument('--isolated-script-test-output', type=str) 125 parser.add_argument('--log', help='Logging level.', default='info') 126 parser.add_argument( 127 '--overwrite-expected', help='Overwrite contents of expected/', action='store_true') 128 args, extra_flags = parser.parse_known_args() 129 130 logging.basicConfig(level=args.log.upper()) 131 132 angle_test_util.Initialize('angle_end2end_tests') 133 134 test_name = 'CapturedTest.MultiFrame/ES3_Vulkan' 135 had_error = False 136 try: 137 if not run_test(test_name, args.overwrite_expected): 138 had_error = True 139 logging.error( 140 'Found capture diffs. If diffs are expected, build angle_end2end_tests and run ' 141 '(cd out/<build>; ../../src/tests/capture_tests/capture_tests.py --overwrite-expected)' 142 ) 143 except Exception as e: 144 logging.exception(e) 145 had_error = True 146 147 if args.isolated_script_test_output: 148 results = { 149 'tests': { 150 'capture_test': {} 151 }, 152 'interrupted': False, 153 'seconds_since_epoch': time.time(), 154 'path_delimiter': '.', 155 'version': 3, 156 'num_failures_by_type': { 157 'FAIL': 0, 158 'PASS': 0, 159 'SKIP': 0, 160 }, 161 } 162 result = 'FAIL' if had_error else 'PASS' 163 results['tests']['capture_test'][test_name] = {'expected': 'PASS', 'actual': result} 164 results['num_failures_by_type'][result] += 1 165 166 with open(args.isolated_script_test_output, 'w') as f: 167 f.write(json.dumps(results, indent=2)) 168 169 return 1 if had_error else 0 170 171 172if __name__ == '__main__': 173 sys.exit(main()) 174