11#!/usr/bin/env python3
22import os
3- import shutil
43import subprocess
54import sys
65import glob
1918
2019_RE_COMBINE_WHITESPACE = re .compile (r"\s+" )
2120
21+
2222def normalize_file_lines (file_path ):
2323 """Yield normalized lines from a file, supporting both regular and XZ-compressed files."""
24- open_func = lzma .open if file_path .endswith (' .xz' ) else open
25- with open_func (file_path , 'rt' , encoding = ' utf-8' ) as f :
24+ open_func = lzma .open if file_path .endswith (" .xz" ) else open
25+ with open_func (file_path , "rt" , encoding = " utf-8" ) as f :
2626 for line in f :
27- line = _RE_COMBINE_WHITESPACE .sub (" " , line ).strip () # Normalize whitespace, and tabs
27+ line = _RE_COMBINE_WHITESPACE .sub (
28+ " " , line
29+ ).strip () # Normalize whitespace, and tabs
2830 if line : # Ignore blank lines
29- yield line .lower () # Normalize case
31+ yield line .lower () # Normalize case
32+
3033
3134# This function is adapted from https://github.com/python/cpython/blob/main/Lib/doctest.py
3235# Released to the public domain 16-Jan-2001, by Tim Peters (tim@python.org).
3336def ellipsis_match (want , got ):
34- """"Compares ``want`` to ``got`` ignoring differences where ``...`` appears in ``want``.
35- """
36- ELLIPSIS_MARKER = '...'
37+ """ "Compares ``want`` to ``got`` ignoring differences where ``...`` appears in ``want``."""
38+ ELLIPSIS_MARKER = "..."
3739 if ELLIPSIS_MARKER not in want :
3840 return want == got
3941
@@ -44,14 +46,14 @@ def ellipsis_match(want, got):
4446 # Deal with exact matches possibly needed at one or both ends.
4547 startpos , endpos = 0 , len (got )
4648 w = ws [0 ]
47- if w : # starts with exact match
49+ if w : # starts with exact match
4850 if got .startswith (w ):
4951 startpos = len (w )
5052 del ws [0 ]
5153 else :
5254 return False
5355 w = ws [- 1 ]
54- if w : # ends with exact match
56+ if w : # ends with exact match
5557 if got .endswith (w ):
5658 endpos -= len (w )
5759 del ws [- 1 ]
@@ -77,6 +79,7 @@ def ellipsis_match(want, got):
7779
7880 return True
7981
82+
8083def generate_unified_diff (file1 , file2 ):
8184 """Generate a unified diff between two files with normalization."""
8285
@@ -86,36 +89,35 @@ def generate_unified_diff(file1, file2):
8689
8790 for want , got in zip_longest (lines1 , lines2 , fillvalue = "" ):
8891 # We do not allow the ellipsis to span multiple lines.
89- if not ellipsis_match (want = want , got = got ):
92+ if not ellipsis_match (want = want , got = got ):
9093 equal = False
9194 break
9295
9396 if equal :
9497 return ""
9598
9699 diff = difflib .unified_diff (
97- lines1 ,
98- lines2 ,
99- fromfile = file1 ,
100- tofile = file2 ,
101- lineterm = ''
100+ lines1 , lines2 , fromfile = file1 , tofile = file2 , lineterm = ""
102101 )
103- return '\n ' .join (diff )
102+ return "\n " .join (diff )
103+
104104
105105def truncate_lines (output : str , max_lines : int ) -> str :
106106 lines = output .splitlines ()
107107 if len (lines ) <= max_lines :
108- return output # Return unmodified
109- return '\n ' .join (lines [:max_lines ] + ['...truncated' ])
108+ return output # Return unmodified
109+ return "\n " .join (lines [:max_lines ] + ["...truncated" ])
110+
110111
111- def runcmd (command , cwd = None , env = {}, outfile = subprocess .DEVNULL ):
112- env [' LC_ALL' ] = "C" # To avoid problems with sorting.
112+ def runcmd (command , cwd = None , env = {}, outfile = subprocess .DEVNULL ):
113+ env [" LC_ALL" ] = "C" # To avoid problems with sorting.
113114 stdout = outfile if outfile == subprocess .DEVNULL else subprocess .PIPE
114- result = subprocess .run (command , shell = True , env = env , cwd = cwd ,
115- stdout = stdout , stderr = subprocess .STDOUT )
115+ result = subprocess .run (
116+ command , shell = True , env = env , cwd = cwd , stdout = stdout , stderr = subprocess .STDOUT
117+ )
116118
117119 if stdout == subprocess .PIPE :
118- open_func = lzma .open if outfile .endswith (' .xz' ) else open
120+ open_func = lzma .open if outfile .endswith (" .xz" ) else open
119121 with open_func (outfile , "wb" ) as fh :
120122 fh .write (result .stdout )
121123
@@ -130,6 +132,7 @@ def is_exe(fpath):
130132 and os .path .getsize (fpath ) > 0
131133 )
132134
135+
133136def run_test (test , program ):
134137 testdirname = os .path .dirname (test )
135138 if testdirname == "" :
@@ -146,10 +149,12 @@ def run_test(test, program):
146149 start_time = time .time ()
147150 # FIXME: How can we avoid using '.' to read the test?
148151 # Using 'source' only works in bash.
149- runcmd (f". ./{ testbasename } " , cwd = testdirname ,
150- env = dict (PROGRAM = program ,
151- TESTNAME = testbasename .replace (".test" , "" )),
152- outfile = outfile )
152+ runcmd (
153+ f". ./{ testbasename } " ,
154+ cwd = testdirname ,
155+ env = dict (PROGRAM = program , TESTNAME = testbasename .replace (".test" , "" )),
156+ outfile = outfile ,
157+ )
153158 elapsed_time = time .time () - start_time
154159
155160 diff_output = generate_unified_diff (expfile , outfile )
@@ -159,7 +164,7 @@ def run_test(test, program):
159164 return True
160165 else :
161166 print_rich (f"[bold red]FAILED![/] { elapsed_time :6.2f} " )
162- print (truncate_lines (diff_output , max_lines = 20 ))
167+ print (truncate_lines (diff_output , max_lines = 20 ))
163168 if debug :
164169 diff = "xzdiff" if expfile .endswith (".xz" ) else "diff"
165170 print (subprocess .getoutput (f"{ diff } -uiEBw -- { expfile } { outfile } " ))
@@ -178,7 +183,11 @@ def main():
178183 print (f"error: '{ program } ' not found or not executable!" )
179184 sys .exit (1 )
180185
181- tests = sorted (glob .glob ("**/*.test" , recursive = True )) if len (sys .argv ) == 2 else sys .argv [2 :]
186+ tests = (
187+ sorted (glob .glob ("**/*.test" , recursive = True ))
188+ if len (sys .argv ) == 2
189+ else sys .argv [2 :]
190+ )
182191 for test in tests :
183192 if not test .endswith (".test" ):
184193 print (test , "is not a test file" )
@@ -187,12 +196,9 @@ def main():
187196 print (test , "not found or not readable" )
188197 sys .exit (1 )
189198
190-
191199 ntotal = len (tests )
192200 elapsed_time = time .time ()
193- ok = Parallel (n_jobs = - 2 )(
194- delayed (run_test )(test , program = program ) for test in tests
195- )
201+ ok = Parallel (n_jobs = - 2 )(delayed (run_test )(test , program = program ) for test in tests )
196202 elapsed_time = time .time () - elapsed_time
197203 npassed = sum (ok )
198204 nfailed = ntotal - npassed
0 commit comments