diff --git a/libcpuid/check-consistency.py b/libcpuid/check-consistency.py index 2cb9c6c..7e52fe1 100755 --- a/libcpuid/check-consistency.py +++ b/libcpuid/check-consistency.py @@ -1,14 +1,15 @@ #!/usr/bin/env python3 -import os, sys, re, glob +import argparse, os, sys, re, glob +from pathlib import Path -if len(sys.argv) != 2: - print("Usage: check-consistency ") - sys.exit(0) + +### Constants: +git_root_dir = os.popen("git rev-parse --show-toplevel").read().splitlines() err = 0 def getEnumElements(enumName): - f = open("%s/libcpuid.h" % sys.argv[1], "r") + f = open("%s/libcpuid.h" % args.root_dir, "r") l = [] on = False rexp = re.compile(r'^\s*([A-Z0-9_]+)(\s*=\s*[A-Z0-9_]+)?\s*,.*$') @@ -27,7 +28,7 @@ def getEnumElements(enumName): return [] def getConstant(constantName): - f = open("%s/libcpuid_constants.h" % sys.argv[1], "r") + f = open("%s/libcpuid_constants.h" % args.root_dir, "r") value = 0 for line in f: items = line.strip().split() @@ -49,13 +50,27 @@ def checkEnumSize(enumName, constantName): else: print("OK") +def check_type_directory(directory): + if not Path(directory).is_dir(): + raise argparse.ArgumentTypeError(f"{directory} is not a directory") + return directory + +# Parse arguments +parser = argparse.ArgumentParser(description="Check library consistency.") +parser.add_argument("root_dir", + nargs='?', + type=check_type_directory, + default=f"{git_root_dir[-1]}/libcpuid" if len(git_root_dir) > 0 else "", + help="path to the libcpuid sub-directory") +args = parser.parse_args() + checkEnumSize("cpu_feature_t", "CPU_FLAGS_MAX") checkEnumSize("cpu_hint_t", "CPU_HINTS_MAX") checkEnumSize("cpu_sgx_feature_t", "SGX_FLAGS_MAX") rexp = re.compile('.*{ CPU_FEATURE_([^,]+), "([^"]+)".*}.*') print("Finding features:") -for fn in glob.glob("%s/*.c" % sys.argv[1]): +for fn in glob.glob("%s/*.c" % args.root_dir): f = open(fn, "rt") line = 1 nfeat = 0 @@ -75,7 +90,7 @@ for fn in glob.glob("%s/*.c" % sys.argv[1]): # Check whether all features are converted by cpu_feature_str(): allf = [] -f = open("%s/libcpuid.h" % sys.argv[1], "rt") +f = open("%s/libcpuid.h" % args.root_dir, "rt") rexp = re.compile('\t(CPU_FEATURE_[^, ]+).*') for s in f.readlines(): if rexp.match(s): @@ -85,7 +100,7 @@ f.close() impf = [] rexp = re.compile('\t+{ (CPU_FEATURE_[^,]+).*') -f = open("%s/cpuid_main.c" % sys.argv[1], "rt") +f = open("%s/cpuid_main.c" % args.root_dir, "rt") for s in f.readlines(): if rexp.match(s): entry = rexp.findall(s)[0] @@ -112,7 +127,7 @@ rexp1 = re.compile(r'.*\[(CPU_FEATURE_[^ \]]+)\]\s*=\s*{.*') # e.g. "[CPU_FEATUR rexp2 = re.compile(r'.*(CPU_FEATURE_[^ ,]+),\s*FEATURE_LEVEL_ARM_.*') # e.g. "set_feature_status(data, ext_status, (mte_frac == 0b0000), CPU_FEATURE_MTE_ASYNC, FEATURE_LEVEL_ARM_V8_5_A, -1);" rexp3 = re.compile(r'.*(CPU_FEATURE_[^ }]+).*') # e.g. "{ 28, CPU_FEATURE_HT }," -for fn in glob.glob("%s/*.c" % sys.argv[1]): +for fn in glob.glob("%s/*.c" % args.root_dir): f = open(fn, "rt") files_code[fn] = [] for s in f.readlines(): @@ -163,7 +178,7 @@ definitions = 0 match_entry_fields = 11 # this number needs to change if the definition of match_entry_t ever changes codename_str_max = 64-1 # this number needs to change if the value of CODENAME_STR_MAX ever changes common_cache_sizes = ["8", "16", "32", "64", "128", "256", "512", "1024", "2048", "3072", "4096", "6144", "8192", "12288", "16384"] -for fn in glob.glob("%s/*.c" % sys.argv[1]): +for fn in glob.glob("%s/*.c" % args.root_dir): bfn = os.path.basename(fn) nline = 0 f = open(fn, "rt") diff --git a/tests/create_test.py b/tests/create_test.py index e36c609..1326f1a 100755 --- a/tests/create_test.py +++ b/tests/create_test.py @@ -1,17 +1,14 @@ #!/usr/bin/env python3 -import os, sys, re +import argparse, sys, re -args = sys.argv -if len(args) != 3: - print("Usage: create_test.py ") - print("The .test file is written to stdout.") - sys.exit(1) +### Constants: +delimiter = "-" * 80 def readRawFile(): rawdata = [] - for line in open(args[1], "rt").readlines(): + for line in args.raw_file.readlines(): lookfor = [ "Logical CPU", "CPUID", "CPU#", # common "basic_cpuid", "ext_cpuid", "intel_fn4", "intel_fn11", "amd_fn8000001dh", # x86 @@ -36,7 +33,7 @@ def readRawFile(): def readResultFile(): repdata = [] rexp = re.compile('(-?[0-9]+).*') - for line in open(args[2], "rt").readlines(): + for line in args.report_file.readlines(): s = line.strip() if s.find(":") == -1: continue @@ -80,6 +77,25 @@ def readResultFile(): repdata.append(value) return repdata -delimiter = "-" * 80 +# Parse arguments +parser = argparse.ArgumentParser(description="Create a new test file by using cpuid_tool raw and report files.") +parser.add_argument("raw_file", + nargs='?', + type=argparse.FileType('r'), + default="raw.txt", + help="an existing raw data file") +parser.add_argument("report_file", + nargs='?', + type=argparse.FileType('r'), + default="report.txt", + help="an existing report file") +parser.add_argument("test_file", + nargs='?', + type=argparse.FileType('w'), + default=sys.stdout, + help="test file to create (default is standard output)") +args = parser.parse_args() + +# Create test file lines = readRawFile() + readResultFile() -sys.stdout.writelines([s + "\n" for s in lines]) +args.test_file.writelines([s + "\n" for s in lines]) diff --git a/tests/run_tests.py b/tests/run_tests.py index 8f303ea..671274b 100755 --- a/tests/run_tests.py +++ b/tests/run_tests.py @@ -1,8 +1,8 @@ #!/usr/bin/env python3 -import os, sys, re, random, lzma -from pathlib import Path +import argparse, textwrap, os, sys, re, random, lzma +from pathlib import Path, PurePath ### Constants: @@ -20,42 +20,6 @@ fields_arm = [ "architecture", "feature-level", "purpose", "cores", "logical", "codename", "flags" ] -args = sys.argv -fix = False -show_test_fast_warning = False - -if len(args) < 3: - print(""" -Usage: run_tests.py [test file/dir ...] [OPTIONS] - -If a test file is given, it is tested by itself. -If a directory is given, process all *.test files there, subdirectories included. - -If the --fix option is given, the behaviour of the cpuid_tool binary is deemed correct -and any failing tests are updated. -""") - sys.exit(1) - -filelist = [] -cpuid_tool = args[1] -for arg in args[2:]: - if arg == "--fix": - fix = True - continue - if arg == "--show-test-fast-warning": - show_test_fast_warning = True - continue - if os.path.isdir(arg): - # gather all *.test files from subdirs amd and intel: - for dirpath, dirnames, filenames in os.walk(arg): - filelist += [os.path.join(dirpath, fn) for fn in filenames if Path(fn).suffixes[0] == ".test"] - else: - filelist.append(arg) - -#f = open(args[1], "rt") -#lines = f.readlines() -#f.close() - # One would usually use os.tempnam, but libc gives off hell a lot of # warnings when you attempt to use that :( def make_tempname(prefix): @@ -109,7 +73,7 @@ def do_test(inp, expected_out, binary, test_file_name, num_cpu_type): except IOError: return "Exception" if len(real_out) != len(expected_out) or len(real_out) != len(fields) * num_cpu_type: - if fix: + if args.fix: fixFile(test_file_name, inp, real_out_delim) return "Number of records, fixed." else: @@ -121,12 +85,69 @@ def do_test(inp, expected_out, binary, test_file_name, num_cpu_type): if not err_fields: return "OK" else: - if fix: + if args.fix: fixFile(test_file_name, inp, real_out_delim) return "Mismatch, fixed." else: return "Mismatch in fields:\n%s" % "\n".join([fmt_error(err) for err in err_fields]) +def is_regular_file(filename): + try: + with open(filename, 'r') as fd: + fd.read() + return True + except: + return False + +def check_type_binary_file(filename): + if not Path(filename).is_file(): + raise argparse.ArgumentTypeError(f"{filename} is not a file") + if is_regular_file(filename): + raise argparse.ArgumentTypeError(f"{filename} is not a binary file") + return filename + +# Parse arguments +parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, + description=textwrap.dedent(""" + +Run test files. + +If a test file is given, it is tested by itself. +If a directory is given, process all *.test files there, subdirectories included. + +If the --fix option is given, the behaviour of the cpuid_tool binary is deemed correct and any failing tests are updated. +""")) +parser.add_argument("cpuid_tool", + nargs='?', + type=check_type_binary_file, + default="./build/cpuid_tool/cpuid_tool", + help="path to the cpuid_tool binary") +parser.add_argument("input_test_files", + nargs='+', + default=["./tests"], + help="test file or directory containing test files") +parser.add_argument("--fix", + action=argparse.BooleanOptionalAction, + default=False, + help="update failing tests (default is false)") +parser.add_argument("--show-test-fast-warning", + dest="show_test_fast_warning", + action=argparse.BooleanOptionalAction, + default=False, + help="show a warning on errors (default if false)") +args = parser.parse_args() + +# Create test files list +filelist = [] +for input_test_file in args.input_test_files: + if Path(input_test_file).is_dir(): + # gather all *.test files from subdirs amd, intel and cie: + for dirpath, dirnames, filenames in Path(input_test_file).walk(): + filelist += [PurePath(dirpath).joinpath(fn) for fn in filenames if Path(fn).suffixes[0] == ".test"] + else: + filelist.append(input_test_file) + +# Run tests errors = False print("Testing...") for test_file_name_raw in filelist: @@ -158,14 +179,14 @@ for test_file_name_raw in filelist: current_input.append(line) f.close() #codename = current_output[len(current_output) - 2] - result = do_test(current_input, current_output, cpuid_tool, test_file_name, num_cpu_type) + result = do_test(current_input, current_output, args.cpuid_tool, test_file_name, num_cpu_type) print("Test [%s]: %s" % (test_file_name.name, result)) if result != "OK": errors = True build_output = False if errors: - if show_test_fast_warning: + if args.show_test_fast_warning: print(""" You're running tests in fast mode; before taking any action on the errors above, please confirm that the slow mode ('make test-old') also fails.""")