[PDNCF] Python 3.12 compatibility
[platform/framework/web/chromium-efl.git] / tools / perry.py
1 #!/usr/bin/env python
2 # Copyright 2017 The Chromium Authors
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 """Runs all permutations of pairs of tests in a gtest binary to attempt to
7 detect state leakage between tests.
8
9 Example invocation:
10
11 gn gen out/asan --args='is_asan=true enable_nacl=false is_debug=false'
12 ninja -C out/asan base_unittests
13 tools/perry.py out/asan/base_unittests > perry.log &
14 tail -f perry.log
15
16 You might want to run it in `screen` as it'll take a while.
17 """
18
19 from __future__ import print_function
20
21 import argparse
22 import os
23 import multiprocessing
24 import subprocess
25 import sys
26
27
28 def _GetTestList(path_to_binary):
29   """Returns a set of full test names.
30
31   Each test will be of the form "Case.Test". There will be a separate line
32   for each combination of Case/Test (there are often multiple tests in each
33   case).
34   """
35   raw_output = subprocess.check_output([path_to_binary, "--gtest_list_tests"])
36   input_lines = raw_output.splitlines()
37
38   # The format of the gtest_list_tests output is:
39   # "Case1."
40   # "  Test1  # <Optional extra stuff>"
41   # "  Test2"
42   # "Case2."
43   # "  Test1"
44   case_name = ''  # Includes trailing dot.
45   test_set = set()
46   for line in input_lines:
47     if len(line) > 1:
48       if '#' in line:
49         line = line[:line.find('#')]
50       if line[0] == ' ':
51         # Indented means a test in previous case.
52         test_set.add(case_name + line.strip())
53       else:
54         # New test case.
55         case_name = line.strip()
56
57   return test_set
58
59
60 def _CheckForFailure(data):
61   test_binary, pair0, pair1 = data
62   p = subprocess.Popen(
63       [test_binary, '--gtest_repeat=5', '--gtest_shuffle',
64        '--gtest_filter=' + pair0 + ':' + pair1],
65       stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
66   out, _ = p.communicate()
67   if p.returncode != 0:
68     return (pair0, pair1, out)
69   return None
70
71
72 def _PrintStatus(i, total, failed):
73   status = '%d of %d tested (%d failures)' % (i+1, total, failed)
74   print('\r%s%s' % (status, '\x1B[K'), end=' ')
75   sys.stdout.flush()
76
77
78 def main():
79   parser = argparse.ArgumentParser(description="Find failing pairs of tests.")
80   parser.add_argument('binary', help='Path to gtest binary or wrapper script.')
81   args = parser.parse_args()
82   print('Getting test list...')
83   all_tests = _GetTestList(args.binary)
84   permuted = [(args.binary, x, y) for x in all_tests for y in all_tests]
85
86   failed = []
87   pool = multiprocessing.Pool()
88   total_count = len(permuted)
89   for i, result in enumerate(pool.imap_unordered(
90       _CheckForFailure, permuted, 1)):
91     if result:
92       print('\n--gtest_filter=%s:%s failed\n\n%s\n\n' % (result[0], result[1],
93                                                          result[2]))
94       failed.append(result)
95     _PrintStatus(i, total_count, len(failed))
96
97   pool.terminate()
98   pool.join()
99
100   if failed:
101     print('Failed pairs:')
102     for f in failed:
103       print(f[0], f[1])
104
105   return 0
106
107
108 if __name__ == '__main__':
109   sys.exit(main())