diff --git a/scripts/result_listing/compare_results.py b/scripts/result_listing/compare_results.py new file mode 100644 index 0000000000..6371746dd5 --- /dev/null +++ b/scripts/result_listing/compare_results.py @@ -0,0 +1,55 @@ +# ------------------------------------------------------------------------- +# +# Part of the CodeChecker project, under the Apache License v2.0 with +# LLVM Exceptions. See LICENSE for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +# ------------------------------------------------------------------------- + +import argparse +import json +import sys + +def __sorted_results(result_file): + return json.load(result_file).sort( + key=lambda report: report['bugHash'] + str(report['reportId'])) + +def __print_missing(needles, haystack, haystack_name): + for needle in needles: + if needle not in haystack: + print(f"Report not found in {haystack_name}:") + print(json.dumps(needle, indent=4)) + return True + return False + +def main(): + parser = argparse.ArgumentParser( + description="Compares two CodeChecker results. The results should be " + "generated by 'CodeChecker cmd results' command.") + + parser.add_argument( + "result1", + type=argparse.FileType("r"), + help="Path of the first result.") + + parser.add_argument( + "result2", + type=argparse.FileType("r"), + help="Path of the second result.") + + args = parser.parse_args() + + result1 = __sorted_results(args.result1) + result2 = __sorted_results(args.result2) + + found_missing = False + found_missing |= __print_missing(result1, result2, "result2") + found_missing |= __print_missing(result2, result1, "result1") + + args.result1.close() + args.result2.close() + + return 1 if found_missing else 0 + +if __name__ == '__main__': + sys.exit(main()) diff --git a/scripts/result_listing/query_all_reports.py b/scripts/result_listing/query_all_reports.py new file mode 100644 index 0000000000..deb6298415 --- /dev/null +++ b/scripts/result_listing/query_all_reports.py @@ -0,0 +1,128 @@ +# ------------------------------------------------------------------------- +# +# Part of the CodeChecker project, under the Apache License v2.0 with +# LLVM Exceptions. See LICENSE for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +# ------------------------------------------------------------------------- +import argparse +import functools +import json +import os +import subprocess +import sys +from multiprocess import Pool +from pathlib import Path +from typing import List, Tuple + + +def parse_args(): + """ + Initialize global variables based on command line arguments. These + variables are global because get_results() uses them. That function is used + as a callback which doesn't get this info as parameters. + """ + parser = argparse.ArgumentParser( + description="Fetch all reports of products") + + parser.add_argument( + '--url', + default='localhost:8001', + help='URL of a CodeChecker server.') + + parser.add_argument( + '--output', + required=True, + help='Output folder for generated JSON files.') + + parser.add_argument( + '-j', '--jobs', + default=1, + type=int, + help='Get reports in this many parallel jobs.') + + parser.add_argument( + '--products', + nargs='+', + help='List of products to fetch reports for. By default, all products ' + 'are fetched.') + + return parser.parse_args() + + +def __get_keys_from_list(out) -> List[str]: + """ + Get all keys from a JSON list. + """ + return map(lambda prod: next(iter(prod.keys())), json.loads(out)) + + +def result_getter(args: argparse.Namespace): + def get_results(product_run: Tuple[str, str]): + product, run = product_run + print(product, run) + + out, _ = subprocess.Popen([ + 'CodeChecker', 'cmd', 'results', run, + '-o', 'json', + '--url', f'{args.url}/{product}'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE).communicate() + + reports = sorted( + json.loads(out), key=lambda report: report['reportId']) + + run = run.replace('/', '_') + with open(Path(args.output) / f'{product}_{run}.json', 'w', + encoding='utf-8') as f: + json.dump(reports, f) + + return get_results + + +def get_all_products(url: str) -> List[str]: + """ + Get all products from a CodeChecker server. + + :param url: URL of a CodeChecker server. + :return: List of product names. + """ + out, _ = subprocess.Popen( + ['CodeChecker', 'cmd', 'products', 'list', '-o', 'json', '--url', url], + stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL).communicate() + + return __get_keys_from_list(out) + + +def dump_products(args: argparse.Namespace): + for product in args.products: + f = functools.partial(lambda p, r: (p, r), product) + with subprocess.Popen([ + 'CodeChecker', 'cmd', 'runs', + '--url', f'{args.url}/{product}', + '-o', 'json'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE + ) as proc: + runs = list(__get_keys_from_list(proc.stdout.read())) + + with Pool(args.jobs) as p: + p.map(result_getter(args), map(f, runs)) + + +def main(): + args = parse_args() + + os.makedirs(args.output, exist_ok=True) + + if not args.products: + args.products = get_all_products(args.url) + + dump_products(args) + + return 0 + + +if __name__ == '__main__': + sys.exit(main())