| #!/usr/bin/env python3 | |
| # Copyright 2022 The IREE Authors | |
| # | |
| # Licensed under the Apache License v2.0 with LLVM Exceptions. | |
| # See https://llvm.org/LICENSE.txt for license information. | |
| # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| """Parses a list of tests from ctest into a format for the web test runner. | |
| Example usage: | |
| ctest --test-dir build-emscripten --show-only=json-v1 > /tmp/ctest.json | |
| python3 parse_test_list.py \ | |
| --ctest_dump=/tmp/ctest.json \ | |
| --build_dir=build-emscripten \ | |
| --output_format=html \ | |
| -o /tmp/parsed.html | |
| """ | |
| import argparse | |
| import json | |
| import os | |
| def parse_arguments(): | |
| """Parses command line arguments.""" | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument( | |
| "--ctest_dump", | |
| type=str, | |
| required=True, | |
| help="Path to the output of `ctest --show-only=json-v1`", | |
| ) | |
| parser.add_argument( | |
| "--build_dir", | |
| type=str, | |
| required=True, | |
| help="Path to the CMake build directory (absolute or relative)", | |
| ) | |
| parser.add_argument( | |
| "--output_format", | |
| type=str, | |
| choices=("html", "json"), | |
| default="html", | |
| help="Output format, either 'html' for the test runner or 'json' for a list of JSON objects", | |
| ) | |
| parser.add_argument( | |
| "-o", "--output", type=str, required=True, help="Output file path" | |
| ) | |
| return parser.parse_args() | |
| def get_normalized_relative_path(absolute_path, root_directory): | |
| # Strip the root directory prefix and get a relative path. | |
| relative_path = os.path.relpath(absolute_path, root_directory) | |
| # Replace the path separator (such as '\' on Windows) with web-style '/'. | |
| normalized_path = relative_path.replace(os.sep, "/") | |
| return normalized_path | |
| def parse_ctest_dump(ctest_dump_path, build_dir): | |
| parsed_tests = [] | |
| # Open the ctest dump JSON file and parse each test. | |
| # https://cmake.org/cmake/help/latest/manual/ctest.1.html#show-as-json-object-model | |
| with open(ctest_dump_path, "rt") as f: | |
| data = json.load(f) | |
| for test in data["tests"]: | |
| parsed_test = { | |
| "testName": test["name"], | |
| "requiredFiles": [], | |
| "args": [], | |
| } | |
| # Parse the 'command' list into the source file and its arguments. | |
| # /path/to/test_runner.js # such as iree-check-module.js or test.js | |
| # arg 1 # such as --device=local-task | |
| # arg 2 # such as check_vmvx_op.mlir_module.vmfb | |
| test_source_absolute_path = test["command"][0] | |
| parsed_test["sourceFile"] = get_normalized_relative_path( | |
| test_source_absolute_path, build_dir | |
| ) | |
| parsed_test["args"] = test["command"][1:] | |
| # Parse the test "properties". | |
| # Note: required file paths are relative to the working directory. | |
| for property in test["properties"]: | |
| if property["name"] == "REQUIRED_FILES": | |
| parsed_test["requiredFiles"] = property["value"] | |
| elif property["name"] == "WORKING_DIRECTORY": | |
| working_directory_absolute_path = property["value"] | |
| parsed_test["workingDirectory"] = get_normalized_relative_path( | |
| working_directory_absolute_path, build_dir | |
| ) | |
| parsed_tests.append(parsed_test) | |
| print("Parsed {} tests from '{}'".format(len(parsed_tests), ctest_dump_path)) | |
| return parsed_tests | |
| def print_parsed_tests(parsed_tests, output_path, output_format): | |
| with open(output_path, "wt") as f: | |
| if output_format == "html": | |
| print("Outputting parsed tests as HTML to '" + output_path + "'") | |
| for test in parsed_tests: | |
| f.write( | |
| '<li><a href="test-runner.html?testName={testName}&sourceFile={sourceFile}&workingDirectory={workingDirectory}&requiredFiles={requiredFiles}&args={args}" target=testRunner>{testName}</a></li>\n'.format( | |
| testName=test["testName"], | |
| sourceFile=test["sourceFile"], | |
| workingDirectory=test["workingDirectory"], | |
| requiredFiles="[" + ",".join(test["requiredFiles"]) + "]", | |
| args="[" + ",".join(test["args"]) + "]", | |
| ) | |
| ) | |
| elif output_format == "json": | |
| print("Outputting parsed tests as JSON to '" + output_path + "'") | |
| f.write(json.dumps(parsed_tests, indent=2)) | |
| else: | |
| raise Exception("Unknown output format: '" + output_format + "'") | |
| def main(args): | |
| # Refine the provided build directory path to a normalized, absolute path. | |
| build_dir = args.build_dir | |
| if not os.path.isabs(build_dir): | |
| build_dir = os.path.join(os.getcwd(), build_dir) | |
| build_dir = os.path.normpath(build_dir) | |
| # Create the output directory as needed (relative paths are fine here). | |
| output_dir = os.path.dirname(args.output) | |
| if output_dir and not os.path.isdir(output_dir): | |
| os.makedirs(output_dir) | |
| parsed_tests = parse_ctest_dump(args.ctest_dump, build_dir) | |
| parsed_tests.sort(key=lambda test: test["testName"]) | |
| print_parsed_tests(parsed_tests, args.output, args.output_format) | |
| if __name__ == "__main__": | |
| main(parse_arguments()) |