1#!/usr/bin/env python3 2# 3# Copyright 2024 The Chromium Authors 4# Use of this source code is governed by a BSD-style license that can be 5# found in the LICENSE file. 6"""List the fuzzers within a fuzztest and confirm they match what we expect. 7 8Invoked by GN from fuzzer_test.gni. 9""" 10 11import argparse 12import os 13import re 14import subprocess 15import sys 16 17# Set up path to be able to import action_helpers 18sys.path.append( 19 os.path.join( 20 os.path.dirname(os.path.abspath(__file__)), 21 os.pardir, 22 os.pardir, 23 'build', 24 ) 25) 26import action_helpers 27 28 29def CreateArgumentParser(): 30 """Creates an argparse.ArgumentParser instance.""" 31 parser = argparse.ArgumentParser(description='Generate fuzztest fuzzers.') 32 parser.add_argument( 33 '--executable', help='Executable to interrogate for present fuzztests.' 34 ) 35 parser.add_argument( 36 '--output', 37 help='Path to the output file (which will be intentionally blank).', 38 ) 39 parser.add_argument( 40 '--fuzztests', nargs='+', help='List of fuzztests we expect to find.' 41 ) 42 return parser 43 44 45def main(): 46 parser = CreateArgumentParser() 47 args = parser.parse_args() 48 49 expected_tests = set(args.fuzztests) 50 51 env = os.environ 52 env['ASAN_OPTIONS'] = 'detect_odr_violation=0' 53 process_result = subprocess.run( 54 [args.executable, '--list_fuzz_tests'], 55 env=env, stdout=subprocess.PIPE, check=False 56 ) 57 58 if process_result.returncode == 0: 59 test_list = process_result.stdout.decode('utf-8') 60 61 actual_tests = set(re.findall('Fuzz test: (.*)', test_list)) 62 63 if expected_tests != actual_tests: 64 print( 65 'Unexpected fuzztests found in this binary.\nFuzztest binary: ' 66 + args.executable 67 + '\n' 68 + 'Expected fuzztests (as declared by the gn "fuzztests" variable on' 69 ' this target): ' 70 + str(expected_tests) 71 + '\nActual tests (as found in the binary): ' 72 + str(actual_tests) 73 + '\nYou probably need to update the gn variable to match the tests' 74 ' actually in the binary.' 75 ) 76 sys.exit(-1) 77 78 # If we couldn't run the fuzztest binary itself, we'll 79 # regard that as fine. This is a best-efforts check that the 80 # gn 'fuzztests' variable is correct, and sometimes fuzzers don't 81 # run on LUCI e.g. due to lack of dependencies. 82 83 with action_helpers.atomic_output(args.output) as output: 84 output.write(''.encode('utf-8')) 85 86 87if __name__ == '__main__': 88 main() 89