55import sys
66import time
77import json
8- import subprocess
8+ import importlib
9+ from collections import abc
10+ import multiprocessing as mp
911
1012from collections import namedtuple as nt
1113
@@ -26,34 +28,63 @@ def print_red(text):
2628 print (f"{ bcolors .FAIL } { text } { bcolors .ENDC } " )
2729
2830
29- Test = nt ("Test" , "process output expected name duration" )
31+ Test = nt ("Test" , "solution result test_case name duration" )
32+ lock = mp .Lock ()
3033
3134
3235def check_result (test ):
33- out = test .output .decode ("ascii" ).strip ()
34- if test .expected == out :
36+ out = test .solution .format_output (* listify (test .result ))
37+ lock .acquire ()
38+ if test .test_case ["output" ] == out :
3539 print_green (f"Test: { test .name } is [OK]\t duration: { test .duration } " )
3640 print_green (out )
41+ print ("-" * 10 )
3742 else :
3843 print_red (f"Test: { test .name } is [FAIL]\t duration: { test .duration } " )
39- print_green (test .expected )
44+ print_red (f"Input:\n { test .test_case ['input' ]} \n ---" )
45+ print_green (test .test_case ["output" ])
4046 print_red (out )
4147 print ("-" * 10 )
48+ lock .release ()
49+
50+
51+ def listify (a ):
52+ if isinstance (a , str ):
53+ return [a .strip ()]
54+ if isinstance (a , abc .Iterable ):
55+ return a
56+ return [a ]
4257
4358
4459def run_test (program_path , test_path ):
4560 with open (test_path , "r" ) as test :
4661 test_case = json .load (test )
47- started = time .time ()
48- p = subprocess .Popen (["python3" , program_path + "/" + program_path .split ("/" )[- 1 ] + ".py" ], stdin = subprocess .PIPE , stdout = subprocess .PIPE )
62+ solution_module_name = "." .join (program_path .split ("/" )) + "." + program_path .split ("/" )[- 1 ]
63+ solution = importlib .import_module (solution_module_name )
64+ try :
65+ # each solution should have main() function which returns the result solution
66+ # and get_input() function which accepts the input as str, returns the input in prepared for main format
67+ # and format_output() function which returns string to compare
68+ inp = solution .get_input (test_case ["input" ])
69+ started = time .time ()
70+ res = solution .main (* listify (inp ))
71+ except Exception as ex :
72+ lock .acquire ()
73+ print_red ("{} [FAIL]" .format (test_path ))
74+ lock .release ()
75+ raise ex
4976 finished = time .time ()
50- p = subprocess .Popen (["python3" , program_path + "/" + program_path .split ("/" )[- 1 ] + ".py" ], stdin = subprocess .PIPE , stdout = subprocess .PIPE )
51- outp , errs = p .communicate (str (test_case ["input" ]).encode ("ascii" ))
52- if errs :
53- print_red ("[FAIL]" )
54- print (errs )
55- return
56- check_result (Test (p , outp , test_case ["output" ], test_path .split ("/" )[- 1 ], finished - started ))
77+ check_result (Test (solution , res , test_case , test_path .split ("/" )[- 1 ], finished - started ))
78+
79+
80+ SYSTEM_CORES_AMOUNT = 16
81+
82+
83+ def flush_processes (procs ):
84+ for p in procs :
85+ p .start ()
86+ for p in procs :
87+ p .join ()
5788
5889
5990def main (argv ):
@@ -63,10 +94,15 @@ def main(argv):
6394 """
6495 cwd = os .getcwd ()
6596 program_path = os .path .join (cwd , argv [1 ])
97+ test_processes = []
6698 for test_path in os .listdir (os .path .join (program_path , "test" )):
6799 if not re .match (test_path , "_res$" ) and not re .match (test_path , "_test_res$" ):
68100 full_path = os .path .join (program_path , "test" , test_path )
69- run_test (program_path , full_path )
101+ test_processes .append (mp .Process (target = run_test , args = (argv [1 ], full_path )))
102+ if len (test_processes ) == SYSTEM_CORES_AMOUNT :
103+ flush_processes (test_processes )
104+ test_processes = []
105+ flush_processes (test_processes )
70106
71107
72108if __name__ == "__main__" :
0 commit comments