| #!/usr/bin/env python3 | 
 | # 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. | 
 |  | 
 | # Compare multiple CTS test_result.xml files | 
 |  | 
 | from __future__ import print_function | 
 | from os.path import basename | 
 | import argparse | 
 | import os | 
 | import sys | 
 |  | 
 | import utils | 
 |  | 
 | class Module: | 
 |   def __init__(self): | 
 |     self.test_cases = {} | 
 |     self.bf_covered_in_file = 0 # bitfield, one bit per file | 
 |  | 
 |   def get_test_case_maybe_create(self, test_case_name): | 
 |     return self.test_cases.setdefault(test_case_name, TestCase()) | 
 |  | 
 |   def set_file_index_present(self, file_idx): | 
 |     self.bf_covered_in_file |= (1 << file_idx) | 
 |  | 
 |   def report(self, module_name, files, diff_only): | 
 |     bf_all_files = self.bf_covered_in_file | 
 |     for test_case_name, test_case in self.test_cases.iteritems(): | 
 |       if test_case.bf_covered_in_file != bf_all_files: | 
 |         report_missing_thing('test_case', module_name + '/' + test_case_name, | 
 |             test_case.bf_covered_in_file, files) | 
 |     for test_case_name, test_case in self.test_cases.iteritems(): | 
 |       test_case.report(module_name, test_case_name, files, diff_only) | 
 |  | 
 | class TestCase: | 
 |   def __init__(self): | 
 |     self.tests = {} | 
 |     self.bf_covered_in_file = 0 # bitfield, one bit per file | 
 |  | 
 |   def get_test_maybe_create(self, test_name): | 
 |     return self.tests.setdefault(test_name, Test()) | 
 |  | 
 |   def set_file_index_present(self, file_idx): | 
 |     self.bf_covered_in_file |= (1 << file_idx) | 
 |  | 
 |   def report(self, module_name, test_case_name, files, diff_only): | 
 |     bf_all_files = self.bf_covered_in_file | 
 |     for test_name, test in self.tests.iteritems(): | 
 |       do_report = test.bf_passing_in_file != bf_all_files | 
 |       if diff_only: | 
 |         do_report = do_report and test.bf_failing_in_file != bf_all_files | 
 |       if do_report: | 
 |         test.report(module_name, test_case_name, test_name, files) | 
 |  | 
 | class Test: | 
 |   def __init__(self): | 
 |     self.bf_failing_in_file = 0 # bitfields, one bit per file | 
 |     self.bf_passing_in_file = 0 | 
 |  | 
 |   def set_file_index_outcome(self, outcome_is_passed, file_idx): | 
 |     bf_value = (1 << file_idx) | 
 |     if outcome_is_passed: | 
 |       self.bf_passing_in_file |= bf_value | 
 |     else: | 
 |       self.bf_failing_in_file |= bf_value | 
 |  | 
 |   # Report test's status in all files: pass/fail/missing | 
 |   def report(self, module_name, test_case_name, test_name, files): | 
 |     print('Test: {}/{}/{}:'.format(module_name, test_case_name, test_name)) | 
 |     for file_idx, f in enumerate(files): | 
 |       bf_value = 1 << file_idx | 
 |       print('\t- {:20}'.format(basename(f)), end = '') | 
 |       if self.bf_passing_in_file & bf_value: | 
 |         print('PASS') | 
 |       elif self.bf_failing_in_file & bf_value: | 
 |         print('     FAIL') | 
 |       else: | 
 |         print(' --   --  (missing)') | 
 |  | 
 | def parse_arguments(): | 
 |   parser = argparse.ArgumentParser( | 
 |       description = 'Compare multiple Android CTS test_result.xml files.') | 
 |   parser.add_argument('files', nargs = '+', | 
 |       help = 'List of (possibly renamed) test_result.xml files') | 
 |   parser.add_argument('--diff-only', | 
 |       action = 'store_true', | 
 |       help = "Don't list tests that consistently fail in all result files," | 
 |       " list only differences.") | 
 |   return parser.parse_args() | 
 |  | 
 | # Read CTS test_result.xml from file and merge into result_tree | 
 | def add_to_result_tree(result_tree, file_xml, file_idx): | 
 |   module = None | 
 |   test_case = None | 
 |   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): | 
 |   bf_all_files = (1 << len(files)) - 1 | 
 |   for module_name, module in result_tree.iteritems(): | 
 |     if module.bf_covered_in_file != bf_all_files: | 
 |       report_missing_thing('module', module_name, module.bf_covered_in_file, | 
 |           files) | 
 |   for module_name, module in result_tree.iteritems(): | 
 |     module.report(module_name, files, diff_only) | 
 |  | 
 | def report_missing_thing(thing_type, thing_name, bf_covered_in_file, files): | 
 |   print('Missing {}: {}, from:'.format(thing_type, thing_name)) | 
 |   for file_idx, f in enumerate(files): | 
 |     if not (bf_covered_in_file & (1 << file_idx)): | 
 |       print('\t- ' + f) | 
 |  | 
 | def Main(): | 
 |   m = Module() | 
 |   m.get_test_case_maybe_create('qwe') | 
 |  | 
 |   args = parse_arguments() | 
 |  | 
 |   result_tree = {} | 
 |   for file_idx, f in enumerate(args.files): | 
 |     add_to_result_tree(result_tree, f, file_idx) | 
 |  | 
 |   tree_report(result_tree, args.files, args.diff_only) | 
 |  | 
 |   return 0 | 
 |  | 
 | if __name__ == '__main__': | 
 |   sys.exit(Main()) |