1111import json
1212import os
1313import shutil
14+ from subprocess import Popen , PIPE
1415import sys
16+ import threading
1517import typing
1618import tqdm
1719from tqdm .contrib import DummyTqdmFile
2022
2123def parse_arguments ():
2224 parser = argparse .ArgumentParser (
23- prog = 'UtBot Python test' ,
24- description = 'Generate tests for example files'
25+ prog = "UtBot Python test" , description = "Generate tests for example files"
2526 )
2627 subparsers = parser .add_subparsers (dest = "command" )
27- parser_generate = subparsers .add_parser (' generate' , help = ' Generate tests' )
28- parser_generate .add_argument (' java' )
29- parser_generate .add_argument (' jar' )
30- parser_generate .add_argument (' path_to_test_dir' )
31- parser_generate .add_argument ('-c' , ' --config_file' , required = True )
32- parser_generate .add_argument ('-p' , ' --python_path' , required = True )
33- parser_generate .add_argument ('-o' , ' --output_dir' , required = True )
34- parser_generate .add_argument ('-i' , ' --coverage_output_dir' , required = True )
35- parser_run = subparsers .add_parser (' run' , help = ' Run tests' )
36- parser_run .add_argument ('-p' , ' --python_path' , required = True )
37- parser_run .add_argument ('-t' , ' --test_directory' , required = True )
38- parser_run .add_argument ('-c' , ' --code_directory' , required = True )
39- parser_coverage = subparsers .add_parser (' check_coverage' , help = ' Check coverage' )
40- parser_coverage .add_argument ('-i' , ' --coverage_output_dir' , required = True )
41- parser_coverage .add_argument ('-c' , ' --config_file' , required = True )
28+ parser_generate = subparsers .add_parser (" generate" , help = " Generate tests" )
29+ parser_generate .add_argument (" java" )
30+ parser_generate .add_argument (" jar" )
31+ parser_generate .add_argument (" path_to_test_dir" )
32+ parser_generate .add_argument ("-c" , " --config_file" , required = True )
33+ parser_generate .add_argument ("-p" , " --python_path" , required = True )
34+ parser_generate .add_argument ("-o" , " --output_dir" , required = True )
35+ parser_generate .add_argument ("-i" , " --coverage_output_dir" , required = True )
36+ parser_run = subparsers .add_parser (" run" , help = " Run tests" )
37+ parser_run .add_argument ("-p" , " --python_path" , required = True )
38+ parser_run .add_argument ("-t" , " --test_directory" , required = True )
39+ parser_run .add_argument ("-c" , " --code_directory" , required = True )
40+ parser_coverage = subparsers .add_parser (" check_coverage" , help = " Check coverage" )
41+ parser_coverage .add_argument ("-i" , " --coverage_output_dir" , required = True )
42+ parser_coverage .add_argument ("-c" , " --config_file" , required = True )
4243 return parser .parse_args ()
4344
4445
45- def inner_zip (collection : dict [str , typing .Iterable ], keys : list [str ]) -> typing .Iterator [list [typing .Any ]]:
46- key , inner_keys = keys [0 ], keys [1 :]
47- if len (inner_keys ) == 0 :
48- yield [collection [key ]]
49- return
50- for inner_collection in collection [key ]:
51- for group in inner_zip (inner_collection , inner_keys ):
52- yield [collection [key ]] + group
53-
54-
55- def test_inner_zip ():
56- data = {"1" : [{"2" : [1 , 2 , 3 ]}, {"2" : [4 , 5 , 6 ]}]}
57- actual = inner_zip (data , ["1" , "2" ])
58- assert list (actual ) == [[data ["1" ], data ["1" ][0 ]["2" ]], [data ["1" ], data ["1" ][1 ]["2" ]]]
59-
60-
6146def parse_config (config_path : str ):
6247 with open (config_path , "r" ) as fin :
6348 return json .loads (fin .read ())
@@ -78,31 +63,37 @@ def std_out_err_redirect_tqdm():
7863
7964
8065def generate_tests (
81- java : str ,
82- jar_path : str ,
83- sys_paths : list [str ],
84- python_path : str ,
85- file_under_test : str ,
86- timeout : int ,
87- output : str ,
88- coverage_output : str ,
89- class_names : typing .Optional [list [str ]] = None ,
90- method_names : typing .Optional [list [str ]] = None
91- ):
66+ java : str ,
67+ jar_path : str ,
68+ sys_paths : list [str ],
69+ python_path : str ,
70+ file_under_test : str ,
71+ timeout : int ,
72+ output : str ,
73+ coverage_output : str ,
74+ class_names : typing .Optional [list [str ]] = None ,
75+ method_names : typing .Optional [list [str ]] = None ,
76+ ):
9277 command = f"{ java } -jar { jar_path } generate_python { file_under_test } .py -p { python_path } -o { output } -s { ' ' .join (sys_paths )} --timeout { timeout * 1000 } --install-requirements --runtime-exception-behaviour PASS --coverage={ coverage_output } "
9378 if class_names is not None :
9479 command += f" -c { ',' .join (class_names )} "
9580 if method_names is not None :
9681 command += f" -m { ',' .join (method_names )} "
97- tqdm .tqdm .write (command )
98- code = os .system (command )
99- return code
82+ tqdm .tqdm .write ("\n " + command )
83+
84+ def stdout_printer (p ):
85+ for line in p .stdout :
86+ tqdm .tqdm .write (line .rstrip ().decode ())
87+
88+ p = Popen (command .split (), stdout = PIPE )
89+ t = threading .Thread (target = stdout_printer , args = (p ,))
90+ t .run ()
10091
10192
10293def run_tests (
103- python_path : str ,
104- tests_dir : str ,
105- samples_dir : str ,
94+ python_path : str ,
95+ tests_dir : str ,
96+ samples_dir : str ,
10697):
10798 command = f'{ python_path } -m coverage run --source={ samples_dir } -m unittest discover -p "utbot_*" { tests_dir } '
10899 tqdm .tqdm .write (command )
@@ -111,28 +102,38 @@ def run_tests(
111102
112103
113104def check_coverage (
114- config_file : str ,
115- coverage_output_dir : str ,
105+ config_file : str ,
106+ coverage_output_dir : str ,
116107):
117108 config = parse_config (config_file )
118109 report : typing .Dict [str , bool ] = {}
119110 coverage : typing .Dict [str , typing .Tuple [float , float ]] = {}
120- for part in config [' parts' ]:
121- for file in part [' files' ]:
122- for group in file [' groups' ]:
123- expected_coverage = group .get (' coverage' , 0 )
111+ for part in config [" parts" ]:
112+ for file in part [" files" ]:
113+ for group in file [" groups" ]:
114+ expected_coverage = group .get (" coverage" , 0 )
124115
125116 file_suffix = f"{ part ['path' ].replace ('/' , '_' )} _{ file ['name' ]} "
126- coverage_output_file = pathlib .Path (coverage_output_dir , f"coverage_{ file_suffix } .json" )
117+ coverage_output_file = pathlib .Path (
118+ coverage_output_dir , f"coverage_{ file_suffix } .json"
119+ )
127120 if coverage_output_file .exists ():
128121 with open (coverage_output_file , "rt" ) as fin :
129122 actual_coverage_json = json .loads (fin .readline ())
130- actual_covered = sum (lines ['end' ] - lines ['start' ] + 1 for lines in actual_coverage_json ['covered' ])
131- actual_not_covered = sum (lines ['end' ] - lines ['start' ] + 1 for lines in actual_coverage_json ['notCovered' ])
123+ actual_covered = sum (
124+ lines ["end" ] - lines ["start" ] + 1
125+ for lines in actual_coverage_json ["covered" ]
126+ )
127+ actual_not_covered = sum (
128+ lines ["end" ] - lines ["start" ] + 1
129+ for lines in actual_coverage_json ["notCovered" ]
130+ )
132131 if actual_covered + actual_not_covered == 0 :
133132 actual_coverage = 0
134133 else :
135- actual_coverage = round (actual_covered / (actual_not_covered + actual_covered ) * 100 )
134+ actual_coverage = round (
135+ actual_covered / (actual_not_covered + actual_covered ) * 100
136+ )
136137 else :
137138 actual_coverage = 0
138139
@@ -154,32 +155,45 @@ def main_test_generation(args):
154155 if pathlib .Path (args .coverage_output_dir ).exists ():
155156 shutil .rmtree (args .coverage_output_dir )
156157 with std_out_err_redirect_tqdm () as orig_stdout :
157- # for (part, file, group) in tqdm.tqdm(inner_zip(config, ["parts", "files", "groups"]), file=orig_stdout, dynamic_ncols=True):
158- for part in tqdm .tqdm (config ["parts" ], file = orig_stdout , dynamic_ncols = True ):
159- for file in tqdm .tqdm (part ["files" ], file = orig_stdout , dynamic_ncols = True ):
160- for group in tqdm .tqdm (file ["groups" ], file = orig_stdout , dynamic_ncols = True ):
161- full_name = pathlib .PurePath (args .path_to_test_dir , part ['path' ], file ['name' ])
162- output_file = pathlib .PurePath (args .output_dir , f"utbot_tests_{ part ['path' ].replace ('/' , '_' )} _{ file ['name' ]} .py" )
163- coverage_output_file = pathlib .PurePath (args .coverage_output_dir , f"coverage_{ part ['path' ].replace ('/' , '_' )} _{ file ['name' ]} .json" )
158+ for part in tqdm .tqdm (
159+ config ["parts" ], file = orig_stdout , dynamic_ncols = True , desc = "Progress"
160+ ):
161+ for file in tqdm .tqdm (
162+ part ["files" ], file = orig_stdout , dynamic_ncols = True , desc = part ["path" ]
163+ ):
164+ for group in file ["groups" ]:
165+ full_name = pathlib .PurePath (
166+ args .path_to_test_dir , part ["path" ], file ["name" ]
167+ )
168+ output_file = pathlib .PurePath (
169+ args .output_dir ,
170+ f"utbot_tests_{ part ['path' ].replace ('/' , '_' )} _{ file ['name' ]} .py" ,
171+ )
172+ coverage_output_file = pathlib .PurePath (
173+ args .coverage_output_dir ,
174+ f"coverage_{ part ['path' ].replace ('/' , '_' )} _{ file ['name' ]} .json" ,
175+ )
164176 generate_tests (
165177 args .java ,
166178 args .jar ,
167179 [args .path_to_test_dir ],
168180 args .python_path ,
169181 str (full_name ),
170- group [' timeout' ],
182+ group [" timeout" ],
171183 str (output_file ),
172184 str (coverage_output_file ),
173- group [' classes' ],
174- group [' methods' ]
185+ group [" classes" ],
186+ group [" methods" ],
175187 )
176-
177188
178- if __name__ == '__main__' :
189+
190+ if __name__ == "__main__" :
179191 arguments = parse_arguments ()
180- if arguments .command == ' generate' :
192+ if arguments .command == " generate" :
181193 main_test_generation (arguments )
182- elif arguments .command == 'run' :
183- run_tests (arguments .python_path , arguments .test_directory , arguments .code_directory )
184- elif arguments .command == 'check_coverage' :
194+ elif arguments .command == "run" :
195+ run_tests (
196+ arguments .python_path , arguments .test_directory , arguments .code_directory
197+ )
198+ elif arguments .command == "check_coverage" :
185199 check_coverage (arguments .config_file , arguments .coverage_output_dir )
0 commit comments