mirror of
https://github.com/anrieff/libcpuid
synced 2025-06-07 00:51:40 +00:00
198 lines
6.2 KiB
Python
Executable file
198 lines
6.2 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
|
|
|
|
import argparse, textwrap, os, sys, re, random, lzma
|
|
from pathlib import Path, PurePath
|
|
|
|
|
|
### Constants:
|
|
os.environ["LIBCPUID_NO_WARN"] = "1"
|
|
delimiter = "-" * 80
|
|
fields_x86 = [ "architecture", "feature-level", "purpose", "family", "model", "stepping",
|
|
"extfamily", "extmodel", "cores", "logical",
|
|
"l1d-cache", "l1i-cache", "l2-cache", "l3-cache", "l4-cache",
|
|
"l1d-assoc", "l1i-assoc", "l2-assoc", "l3-assoc", "l4-assoc",
|
|
"l1d-cacheline", "l1i-cacheline", "l2-cacheline", "l3-cacheline", "l4-cacheline",
|
|
"l1d-instances", "l1i-instances", "l2-instances", "l3-instances", "l4-instances",
|
|
"sse-size", "codename", "technology", "flags" ]
|
|
fields_arm = [ "architecture", "feature-level", "purpose",
|
|
"implementer", "variant", "part-num", "revision",
|
|
"cores", "logical",
|
|
"codename", "technology", "flags" ]
|
|
|
|
# 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):
|
|
chars = ""
|
|
for i in range(26):
|
|
chars += chr(97+i)
|
|
chars += chr(65+i)
|
|
for i in range(10):
|
|
chars += chr(48+i)
|
|
for i in range(6):
|
|
prefix += random.choice(chars)
|
|
return prefix
|
|
|
|
def fmt_error(err):
|
|
pfix = f" {err[0]}: "
|
|
return "{} expected `{}'\n{} got `{}'".format(pfix, err[1], ' '*len(pfix), err[2])
|
|
|
|
def fixFile(filename, input_lines, output_lines):
|
|
pfilename = Path(filename)
|
|
if pfilename.suffixes[-1] == ".xz":
|
|
# XZ compressed file
|
|
f = lzma.open(pfilename, "wt")
|
|
else:
|
|
# Plain text file
|
|
f = open(pfilename, "wt")
|
|
f.writelines([s + "\n" for s in input_lines])
|
|
f.write(delimiter + "\n")
|
|
f.writelines([s + "\n" for s in output_lines])
|
|
f.close()
|
|
|
|
def do_test(inp, expected_out, binary, test_file_name, num_cpu_type):
|
|
fninp = make_tempname("cpuidin")
|
|
fnoutp = make_tempname("cpuidout")
|
|
f = open(fninp, "wt")
|
|
f.writelines([s + "\n" for s in inp])
|
|
f.close()
|
|
architecture = os.popen(f"{binary} --load={fninp} --architecture").read().splitlines()[-1]
|
|
if architecture == "x86":
|
|
fields = fields_x86
|
|
elif architecture == "ARM":
|
|
fields = fields_arm
|
|
else:
|
|
fields = []
|
|
cmd = f"{binary} --load={fninp} --outfile={fnoutp} {' '.join(['--' + s for s in fields])}"
|
|
os.system(cmd)
|
|
os.unlink(fninp)
|
|
real_out = []
|
|
real_out_delim = [] # when fixing the file, delimiters are required
|
|
try:
|
|
f = open(fnoutp, "rt")
|
|
for s in f.readlines():
|
|
real_out_delim.append(s.strip())
|
|
if delimiter not in s:
|
|
real_out.append(s.strip())
|
|
f.close()
|
|
os.unlink(fnoutp)
|
|
except IOError:
|
|
return "Exception"
|
|
if len(real_out) != len(expected_out) or len(real_out) != len(fields) * num_cpu_type:
|
|
if args.fix:
|
|
fixFile(test_file_name, inp, real_out_delim)
|
|
return "Number of records, fixed."
|
|
else:
|
|
return "Unexpected number of records returned\n - expected length {}\n - real length {}\n - {} fields".format(len(expected_out), len(real_out), len(fields) * num_cpu_type)
|
|
err_fields = []
|
|
for i in range(len(real_out)):
|
|
if real_out[i] != expected_out[i]:
|
|
err_fields.append((fields[i % len(fields)], expected_out[i], real_out[i]))
|
|
if not err_fields:
|
|
return "OK"
|
|
else:
|
|
if args.fix:
|
|
fixFile(test_file_name, inp, real_out_delim)
|
|
return "Mismatch, fixed."
|
|
else:
|
|
return "Mismatch in fields:\n{}".format('\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 os.walk(input_test_file):
|
|
filelist += [PurePath(dirpath).joinpath(fn) for fn in filenames if ".test" in Path(fn).suffixes]
|
|
else:
|
|
filelist.append(input_test_file)
|
|
|
|
# Run tests
|
|
errors = False
|
|
print("Testing...")
|
|
for test_file_name_raw in filelist:
|
|
test_file_name = Path(test_file_name_raw)
|
|
num_cpu_type = 0
|
|
current_input = []
|
|
current_output = []
|
|
build_output = False
|
|
if test_file_name.suffixes[-1] == ".xz":
|
|
# XZ compressed file
|
|
f = lzma.open(test_file_name, "rt")
|
|
else:
|
|
# Plain text file
|
|
f = open(test_file_name, "rt")
|
|
# Read file line by line
|
|
for line in f.readlines():
|
|
line = line.strip()
|
|
if line == delimiter:
|
|
build_output = True
|
|
num_cpu_type += 1
|
|
else:
|
|
if build_output:
|
|
current_output.append(line)
|
|
else:
|
|
current_input.append(line)
|
|
f.close()
|
|
#codename = current_output[len(current_output) - 2]
|
|
result = do_test(current_input, current_output, args.cpuid_tool, test_file_name, num_cpu_type)
|
|
print(f"Test [{test_file_name.name}]: {result}")
|
|
if result != "OK":
|
|
errors = True
|
|
build_output = False
|
|
|
|
if errors:
|
|
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.""")
|
|
sys.exit(1)
|
|
else:
|
|
print("All successfull!")
|
|
sys.exit(0)
|