123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109 |
- #!/usr/bin/env python
- # Copyright 2017 The Chromium Authors. All rights reserved.
- # Use of this source code is governed by a BSD-style license that can be
- # found in the LICENSE file.
- """Runs all permutations of pairs of tests in a gtest binary to attempt to
- detect state leakage between tests.
- Example invocation:
- gn gen out/asan --args='is_asan=true enable_nacl=false is_debug=false'
- ninja -C out/asan base_unittests
- tools/perry.py out/asan/base_unittests > perry.log &
- tail -f perry.log
- You might want to run it in `screen` as it'll take a while.
- """
- from __future__ import print_function
- import argparse
- import os
- import multiprocessing
- import subprocess
- import sys
- def _GetTestList(path_to_binary):
- """Returns a set of full test names.
- Each test will be of the form "Case.Test". There will be a separate line
- for each combination of Case/Test (there are often multiple tests in each
- case).
- """
- raw_output = subprocess.check_output([path_to_binary, "--gtest_list_tests"])
- input_lines = raw_output.splitlines()
- # The format of the gtest_list_tests output is:
- # "Case1."
- # " Test1 # <Optional extra stuff>"
- # " Test2"
- # "Case2."
- # " Test1"
- case_name = '' # Includes trailing dot.
- test_set = set()
- for line in input_lines:
- if len(line) > 1:
- if '#' in line:
- line = line[:line.find('#')]
- if line[0] == ' ':
- # Indented means a test in previous case.
- test_set.add(case_name + line.strip())
- else:
- # New test case.
- case_name = line.strip()
- return test_set
- def _CheckForFailure(data):
- test_binary, pair0, pair1 = data
- p = subprocess.Popen(
- [test_binary, '--gtest_repeat=5', '--gtest_shuffle',
- '--gtest_filter=' + pair0 + ':' + pair1],
- stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
- out, _ = p.communicate()
- if p.returncode != 0:
- return (pair0, pair1, out)
- return None
- def _PrintStatus(i, total, failed):
- status = '%d of %d tested (%d failures)' % (i+1, total, failed)
- print('\r%s%s' % (status, '\x1B[K'), end=' ')
- sys.stdout.flush()
- def main():
- parser = argparse.ArgumentParser(description="Find failing pairs of tests.")
- parser.add_argument('binary', help='Path to gtest binary or wrapper script.')
- args = parser.parse_args()
- print('Getting test list...')
- all_tests = _GetTestList(args.binary)
- permuted = [(args.binary, x, y) for x in all_tests for y in all_tests]
- failed = []
- pool = multiprocessing.Pool()
- total_count = len(permuted)
- for i, result in enumerate(pool.imap_unordered(
- _CheckForFailure, permuted, 1)):
- if result:
- print('\n--gtest_filter=%s:%s failed\n\n%s\n\n' % (result[0], result[1],
- result[2]))
- failed.append(result)
- _PrintStatus(i, total_count, len(failed))
- pool.terminate()
- pool.join()
- if failed:
- print('Failed pairs:')
- for f in failed:
- print(f[0], f[1])
- return 0
- if __name__ == '__main__':
- sys.exit(main())
|