#!/usr/bin/env python3 __doc__ = """ This file scans tests directory and generates a cmake file containing all *.asy tests, excluding wce. The resulting file is supposed to be checked in and imported by the main cmake build scripts. This script has 2 modes - list generation and verification. Verification does not write to any file, but raises an error if the file output does not match the contents of the test list cmake file (that is, could be missing a test). To run in verification mode, pass in "--verify-no-missing-tests" arguments. """ import io import os import pathlib import sys import textwrap REPO_ROOT = pathlib.Path(__file__).parent TESTS_DIR = REPO_ROOT / "tests" GENERATED_TESTS_LIST_CMAKE_FILE_PATH = ( REPO_ROOT / "cmake-scripts/generated/asy-tests-list.cmake" ) TESTS_REQUIRING_CMAKE_FEATURES = {"gsl": "ENABLE_GSL", "gc": "ENABLE_GC"} TESTS_NOT_PART_OF_CORE_CHECKS = {"gc", "gs"} TESTS_WITH_ARTIFACTS = {"gc": [".eps"], "output": ["circle.eps", "line.eps"]} EXCLUDED_TESTS = {"bench"} GENERATED_CMAKE_FILE_COMMENT_HEADER = textwrap.dedent( f""" # This file is automatically generated. Do not modify manually. # This file is checked in as part of the repo. It is not meant to be ignored. # # If more tests are added, run {pathlib.Path(__file__).name} to re-generate # the test list. """ ) def generate_tests_list_per_directory(test_dir: os.DirEntry): test_name = test_dir.name if test_name in EXCLUDED_TESTS: return "" with os.scandir(test_dir) as scanner_it: tests = sorted( entry.name[:-4] # removing .asy extension for entry in scanner_it if entry.is_file() and entry.name.endswith(".asy") ) if not tests: return "" if test_name in TESTS_WITH_ARTIFACTS: artifacts_text = f"TEST_ARTIFACTS {' '.join(TESTS_WITH_ARTIFACTS[test_name])}" else: artifacts_text = "" test_not_check_str = ( "TEST_NOT_PART_OF_CHECK_TEST true" if test_name in TESTS_NOT_PART_OF_CORE_CHECKS else "" ) with io.StringIO() as text_writer: if test_name in TESTS_REQUIRING_CMAKE_FEATURES: text_writer.write(f"if ({TESTS_REQUIRING_CMAKE_FEATURES[test_name]})\n") text_writer.write( textwrap.dedent( f""" add_asy_tests( TEST_DIR {test_name} TESTS {' '.join(tests)} {artifacts_text} {test_not_check_str} ) """ ) ) text_writer.write("\n") if test_name in TESTS_REQUIRING_CMAKE_FEATURES: text_writer.write("endif()\n") return text_writer.getvalue() def write_cmake_lists_data_to_file(test_dirs, out_file): out_file.write(GENERATED_CMAKE_FILE_COMMENT_HEADER) out_file.write("\n") for entry in test_dirs: cmake_text = generate_tests_list_per_directory(entry) out_file.write(cmake_text) out_file.write("\n") def get_test_dirs(): with os.scandir(TESTS_DIR) as scanner_it: return sorted( (entry for entry in scanner_it if entry.is_dir()), key=lambda entry: entry.name, ) def main(): # sort to make output deterministic args = sys.argv[1:] test_dirs = get_test_dirs() if "--verify-no-missing-tests" in args: with io.StringIO() as text_writer: write_cmake_lists_data_to_file(test_dirs, text_writer) expected_file_contents = text_writer.getvalue().strip() with open(GENERATED_TESTS_LIST_CMAKE_FILE_PATH, "r", encoding="utf-8") as in_f: actual_file_contents = in_f.read().strip() if expected_file_contents != actual_file_contents: print( "asy-tests-list.cmake has not been updated properly. " + "It may be missing some unit tests. " + f"\nPlease run {pathlib.Path(__file__).name} to " + "regenerate asy-tests-list.cmake." ) raise RuntimeError("File contents do not match") print("test file has no missing tests!") else: with open(GENERATED_TESTS_LIST_CMAKE_FILE_PATH, "w", encoding="utf-8") as out_f: write_cmake_lists_data_to_file(test_dirs, out_f) if __name__ == "__main__": main()