Mercurial > ~astiob > upreckon > hgweb
comparison test-svn.py @ 11:231e3317477d
More version reporting
test.py -> test-svn.py
publish.py now generates test.py rather than test-pub.py
actually added $Rev$ to test-svn.py's properties
author | Oleg Oshmyan <chortos@inbox.lv> |
---|---|
date | Fri, 12 Mar 2010 23:59:26 +0000 |
parents | test.py@c87ec78f1fae |
children | 7c6f02865bf6 |
comparison
equal
deleted
inserted
replaced
10:c87ec78f1fae | 11:231e3317477d |
---|---|
1 #! /usr/bin/python | |
2 # Copyright (c) 2009-2010 Chortos-2 <chortos@inbox.lv> | |
3 | |
4 import os, sys, shutil, time, subprocess, filecmp, optparse, signal, tempfile, tarfile, zipfile | |
5 | |
6 # $Rev: 11$ | |
7 version = '1.21.0 (SVN r$$REV$$)' | |
8 parser = optparse.OptionParser(version='test.py '+version, usage='usage: %prog [options] [problem names] [[path' + os.path.sep + 'to' + os.path.sep + ']solution-app] [test case numbers]\n\nTest case numbers can be specified in plain text or as a Python expression\nif there is only one positional argument.\n\nOnly problem names listed in testconf.py are recognized.') | |
9 parser.add_option('-u', '--update', dest='update', action='store_true', default=False, help='check for an updated version of test.py') | |
10 parser.add_option('-e', '--exclude', dest='exclude', action='append', help='test case number(s) to exclude, as a Python expression; multiple -e options can be supplied') | |
11 parser.add_option('-c', '--cleanup', dest='clean', action='store_true', default=False, help='delete the copies of input/output files and exit') | |
12 parser.add_option('-s', '--save-io', dest='erase', action='store_false', default=True, help='do not delete the copies of input/output files after the last test case; create copies of input files and store output in files even if the solution uses standard I/O; delete the stored input/output files if the solution uses standard I/O and the -c/--cleanup option is specified') | |
13 parser.add_option('-m', '--copy-io', dest='copyonly', action='store_true', default=False, help='only create a copy of the input/output files of the last test case for manual testing; to delete them, use options -cs or -cm') | |
14 parser.add_option('-x', '--auto-exit', dest='pause', action='store_false', default=True, help='do not wait for a key to be pressed when finished testing') | |
15 parser.add_option('-p', '--python', action='store_true', default=False, help='always parse all positional arguments as a single Python expression (including the first argument even if it names an executable file)') | |
16 parser.add_option('-t', '--detect-time', dest='autotime', action='store_true', default=False, help='spend a second detecting the most precise time measurement function') | |
17 | |
18 options, args = parser.parse_args() | |
19 parser.destroy() | |
20 del parser | |
21 | |
22 def update(): | |
23 import urllib | |
24 homepage = urllib.urlopen('http://chortos.selfip.net/~astiob/test.py/').read() | |
25 i = homepage.find("The latest version of test.py is <span style='font-weight: bold; font-size: larger'>") | |
26 i += len("The latest version of test.py is <span style='font-weight: bold; font-size: larger'>") | |
27 j = homepage.find(".</span>", i) | |
28 latest = homepage[i:j].split('.') | |
29 installed = version.split('.') | |
30 update = '' | |
31 if latest[0] > installed[0]: | |
32 update = 'major' | |
33 elif latest[0] == installed[0]: | |
34 if latest[1] > installed[1]: | |
35 update = 'feature' | |
36 elif latest[1] == installed[1]: | |
37 if latest[2] > installed[2]: | |
38 update = 'bug-fixing' | |
39 elif latest[2] == installed[2]: | |
40 print 'You are using the latest publicly available version of test.py.' | |
41 return | |
42 if update == '': | |
43 print 'Your copy of test.py is newer than the publicly available version.' | |
44 return | |
45 print 'A ' + update + ' update to test.py is available. Downloading...' | |
46 urllib.urlretrieve('http://chortos.selfip.net/~astiob/test.py/test.py', 'test.py') | |
47 print 'Downloaded and installed. Now you are using test.py ' + homepage[i:j] + '.' | |
48 | |
49 if options.update: | |
50 update() | |
51 sys.exit() | |
52 | |
53 globals1 = set(globals()) | |
54 | |
55 # Initialize some configuration variables with default values | |
56 tasknames = (os.path.curdir,) | |
57 maxtime = 0 | |
58 tests = () | |
59 dummies = () | |
60 testsexcluded = () | |
61 padwithzeroestolength = 0 | |
62 taskweight = 100 | |
63 pointmap = {} | |
64 stdio = False | |
65 dummyinname = '' | |
66 dummyoutname = '' | |
67 tester = '' | |
68 maxexitcode = 0 | |
69 | |
70 def exectestconf_helper(name): | |
71 if os.path.isfile('tests.tar'): | |
72 f = tarfile.open('tests.tar') | |
73 try: | |
74 exec f.extractfile(name).read() in globals() | |
75 f.close() | |
76 return True | |
77 except KeyError: | |
78 f.close() | |
79 if os.path.isfile('tests.zip'): | |
80 f = zipfile.ZipFile('tests.zip') | |
81 try: | |
82 exec f.open(name, 'rU').read() in globals() | |
83 f.close() | |
84 return True | |
85 except KeyError: | |
86 f.close() | |
87 if os.path.isfile('tests.tgz'): | |
88 f = tarfile.open('tests.tgz') | |
89 try: | |
90 exec f.extractfile(name).read() in globals() | |
91 f.close() | |
92 return True | |
93 except KeyError: | |
94 f.close() | |
95 if os.path.isfile('tests.tar.gz'): | |
96 f = tarfile.open('tests.tar.gz') | |
97 try: | |
98 exec f.extractfile(name).read() in globals() | |
99 f.close() | |
100 return True | |
101 except KeyError: | |
102 f.close() | |
103 if os.path.isfile('tests.tbz2'): | |
104 f = tarfile.open('tests.tbz2') | |
105 try: | |
106 exec f.extractfile(name).read() in globals() | |
107 f.close() | |
108 return True | |
109 except KeyError: | |
110 f.close() | |
111 if os.path.isfile('tests.tar.bz2'): | |
112 f = tarfile.open('tests.tar.bz2') | |
113 try: | |
114 exec f.extractfile(name).read() in globals() | |
115 f.close() | |
116 return True | |
117 except KeyError: | |
118 f.close() | |
119 return False | |
120 | |
121 try: | |
122 execfile('testconf.py') | |
123 except IOError, error: | |
124 exc_info = sys.exc_info()[2] | |
125 try: | |
126 execfile(os.path.join('tests', 'testconf.py')) | |
127 except IOError: | |
128 if not exectestconf_helper('testconf.py'): | |
129 raise IOError, (error.errno, 'The configuration file is missing', error.filename), exc_info | |
130 del exc_info | |
131 | |
132 globals2 = set(globals()) | |
133 globals2.remove('globals1') | |
134 globals2 -= globals1 | |
135 del globals1 | |
136 | |
137 shared = {} | |
138 g = globals() | |
139 for k in globals2: | |
140 shared[k] = g[k] | |
141 | |
142 newtasknames = [] | |
143 while len(args) and args[0] in tasknames: | |
144 newtasknames.append(args[0]) | |
145 del args[0] | |
146 if len(newtasknames): | |
147 tasknames = newtasknames | |
148 | |
149 scoresumoveralltasks = 0 | |
150 scoremaxoveralltasks = 0 | |
151 ntasks = 0 | |
152 nfulltasks = 0 | |
153 cwd = '' # At any time this is either '' or taskname | |
154 | |
155 if options.autotime: | |
156 c = time.clock() | |
157 time.sleep(1) | |
158 c = time.clock() - c | |
159 if int(c + .99999) == 1: | |
160 clock = time.clock | |
161 else: | |
162 clock = time.time | |
163 elif os.name == 'nt': | |
164 clock = time.clock | |
165 else: | |
166 clock = time.time | |
167 | |
168 if options.copyonly: | |
169 options.erase = False | |
170 | |
171 def existstestcase_helper(name): | |
172 if os.path.isfile('tests.tar'): | |
173 f = tarfile.open('tests.tar') | |
174 try: | |
175 f.getmember(name) | |
176 f.close() | |
177 return True | |
178 except KeyError: | |
179 f.close() | |
180 if os.path.isfile('tests.zip'): | |
181 f = zipfile.ZipFile('tests.zip') | |
182 try: | |
183 f.getinfo(name) | |
184 f.close() | |
185 return True | |
186 except KeyError: | |
187 f.close() | |
188 if os.path.isfile('tests.tgz'): | |
189 f = tarfile.open('tests.tgz') | |
190 try: | |
191 f.getmember(name) | |
192 f.close() | |
193 return True | |
194 except KeyError: | |
195 f.close() | |
196 if os.path.isfile('tests.tar.gz'): | |
197 f = tarfile.open('tests.tar.gz') | |
198 try: | |
199 f.getmember(name) | |
200 f.close() | |
201 return True | |
202 except KeyError: | |
203 f.close() | |
204 if os.path.isfile('tests.tbz2'): | |
205 f = tarfile.open('tests.tbz2') | |
206 try: | |
207 f.getmember(name) | |
208 f.close() | |
209 return True | |
210 except KeyError: | |
211 f.close() | |
212 if os.path.isfile('tests.tar.bz2'): | |
213 f = tarfile.open('tests.tar.bz2') | |
214 try: | |
215 f.getmember(name) | |
216 f.close() | |
217 return True | |
218 except KeyError: | |
219 f.close() | |
220 return False | |
221 | |
222 def existstestcase(name): | |
223 if os.path.isfile(os.path.join('tests', taskname, name)) or os.path.isfile(os.path.join('tests', name)): | |
224 return True | |
225 if cwd and (os.path.isfile(os.path.join(oldcwd, 'tests', cwd, name)) or os.path.isfile(os.path.join(oldcwd, 'tests', name))): | |
226 return True | |
227 if existstestcase_helper(os.path.join(taskname, name)) or existstestcase_helper(name): | |
228 return True | |
229 if cwd: | |
230 os.chdir(oldcwd) | |
231 if existstestcase_helper(os.path.join(cwd, name)) or existstestcase_helper(name): | |
232 os.chdir(cwd) | |
233 return True | |
234 os.chdir(cwd) | |
235 return False | |
236 | |
237 def opentestcase_helper(name): | |
238 if os.path.isfile('tests.tar'): | |
239 f = tarfile.open('tests.tar') | |
240 try: | |
241 c = f.extractfile(name) | |
242 return c | |
243 except KeyError: | |
244 f.close() | |
245 if os.path.isfile('tests.zip'): | |
246 f = zipfile.ZipFile('tests.zip') | |
247 try: | |
248 c = f.open(name, 'rU') | |
249 f.close() | |
250 return c | |
251 except KeyError: | |
252 f.close() | |
253 if os.path.isfile('tests.tgz'): | |
254 f = tarfile.open('tests.tgz') | |
255 try: | |
256 c = f.extractfile(name) | |
257 return c | |
258 except KeyError: | |
259 f.close() | |
260 if os.path.isfile('tests.tar.gz'): | |
261 f = tarfile.open('tests.tar.gz') | |
262 try: | |
263 c = f.extractfile(name) | |
264 return c | |
265 except KeyError: | |
266 f.close() | |
267 if os.path.isfile('tests.tbz2'): | |
268 f = tarfile.open('tests.tbz2') | |
269 try: | |
270 c = f.extractfile(name) | |
271 return c | |
272 except KeyError: | |
273 f.close() | |
274 if os.path.isfile('tests.tar.bz2'): | |
275 f = tarfile.open('tests.tar.bz2') | |
276 try: | |
277 c = f.extractfile(name) | |
278 return c | |
279 except KeyError: | |
280 f.close() | |
281 return None | |
282 | |
283 def opentestcase(name): | |
284 if os.path.isfile(os.path.join('tests', taskname, name)): | |
285 return open(os.path.join('tests', taskname, name), 'rU') | |
286 elif os.path.isfile(os.path.join('tests', name)): | |
287 return open(os.path.join('tests', name), 'rU') | |
288 f = opentestcase_helper(os.path.join(taskname, name)) | |
289 if not f: | |
290 f = opentestcase_helper(name) | |
291 if f: | |
292 return f | |
293 if cwd: | |
294 if os.path.isfile(os.path.join(oldcwd, 'tests', cwd, name)): | |
295 return open(os.path.join(oldcwd, 'tests', cwd, name), 'rU') | |
296 elif os.path.isfile(os.path.join(oldcwd, 'tests', name)): | |
297 return open(os.path.join(oldcwd, 'tests', name), 'rU') | |
298 os.chdir(oldcwd) | |
299 f = opentestcase_helper(os.path.join(cwd, name)) | |
300 if not f: | |
301 f = opentestcase_helper(name) | |
302 os.chdir(cwd) | |
303 if f: | |
304 return f | |
305 raise KeyError, 'The test-case-defining file \'' + name + '\' cannot be found' | |
306 | |
307 def copytestcase_helper(name, target): | |
308 if os.path.isfile('tests.tar'): | |
309 f = tarfile.open('tests.tar') | |
310 try: | |
311 m = f.getmember(name) | |
312 m.name = target | |
313 f.extract(m) | |
314 f.close() | |
315 return True | |
316 except KeyError: | |
317 f.close() | |
318 if os.path.isfile('tests.zip'): | |
319 if not os.path.isabs(target): | |
320 f = zipfile.ZipFile('tests.zip') | |
321 m = f.getinfo(name) | |
322 try: | |
323 m.filename = target | |
324 f.extract(m) | |
325 f.close() | |
326 return True | |
327 except KeyError: | |
328 f.close() | |
329 else: | |
330 oldcwd = os.getcwdu() | |
331 os.chdir('/') # FIXME: portability? | |
332 f = zipfile.ZipFile(os.path.join(oldcwd, 'tests.zip')) | |
333 try: | |
334 m = f.getinfo(name) | |
335 m.filename = target[1:] | |
336 f.extract(m) | |
337 f.close() | |
338 os.chdir(oldcwd) | |
339 return True | |
340 except KeyError: | |
341 f.close() | |
342 os.chdir(oldwcd) | |
343 if os.path.isfile('tests.tgz'): | |
344 f = tarfile.open('tests.tgz') | |
345 try: | |
346 m = f.getmember(name) | |
347 m.name = target | |
348 f.extract(m) | |
349 f.close() | |
350 return True | |
351 except KeyError: | |
352 f.close() | |
353 if os.path.isfile('tests.tar.gz'): | |
354 f = tarfile.open('tests.tar.gz') | |
355 try: | |
356 m = f.getmember(name) | |
357 m.name = target | |
358 f.extract(m) | |
359 f.close() | |
360 return True | |
361 except KeyError: | |
362 f.close() | |
363 if os.path.isfile('tests.tbz2'): | |
364 f = tarfile.open('tests.tbz2') | |
365 try: | |
366 m = f.getmember(name) | |
367 m.name = target | |
368 f.extract(m) | |
369 f.close() | |
370 return True | |
371 except KeyError: | |
372 f.close() | |
373 if os.path.isfile('tests.tar.bz2'): | |
374 f = tarfile.open('tests.tar.bz2') | |
375 try: | |
376 m = f.getmember(name) | |
377 m.name = target | |
378 f.extract(m) | |
379 f.close() | |
380 return True | |
381 except KeyError: | |
382 f.close() | |
383 return False | |
384 | |
385 def copytestcase(name, target): | |
386 if os.path.isfile(os.path.join('tests', taskname, name)): | |
387 shutil.copyfile(os.path.join('tests', taskname, name), target) | |
388 return | |
389 elif os.path.isfile(os.path.join('tests', name)): | |
390 shutil.copyfile(os.path.join('tests', name), target) | |
391 return | |
392 if copytestcase_helper(os.path.join(taskname, name), target) or copytestcase_helper(name, target): | |
393 return | |
394 if cwd: | |
395 if os.path.isfile(os.path.join(oldcwd, 'tests', cwd, name)): | |
396 shutil.copyfile(os.path.join(oldcwd, 'tests', cwd, name), target) | |
397 return | |
398 elif os.path.isfile(os.path.join(oldcwd, 'tests', name)): | |
399 shutil.copyfile(os.path.join(oldcwd, 'tests', name), target) | |
400 return | |
401 os.chdir(oldcwd) | |
402 if copytestcase_helper(os.path.join(cwd, name), target) or copytestcase_helper(name, target): | |
403 os.chdir(cwd) | |
404 return | |
405 os.chdir(cwd) | |
406 raise KeyError, 'The test-case-defining file \'' + name + '\' cannot be found' | |
407 | |
408 # Always chdir if the directory exists but use any existing config | |
409 def chdir_and_exec_testconf(): | |
410 global cwd | |
411 cwd = '' | |
412 if os.path.isdir(taskname): | |
413 os.chdir(taskname) | |
414 if taskname != os.path.curdir: | |
415 cwd = taskname | |
416 try: | |
417 execfile('testconf.py', globals()) | |
418 return | |
419 except IOError: | |
420 pass | |
421 if not cwd: | |
422 if os.path.isfile(os.path.join('tests', taskname, 'testconf.py')): | |
423 execfile(os.path.join('tests', taskname, 'testconf.py'), globals()) | |
424 return | |
425 if os.path.isfile(os.path.join('tests', 'testconf.py')): | |
426 execfile(os.path.join('tests', 'testconf.py'), globals()) | |
427 return | |
428 if exectestconf_helper(os.path.join(taskname, 'testconf.py')) or exectestconf_helper('testconf.py'): | |
429 return | |
430 if cwd: | |
431 os.chdir(oldcwd) | |
432 if os.path.isfile(os.path.join('tests', cwd, 'testconf.py')): | |
433 execfile(os.path.join('tests', cwd, 'testconf.py'), globals()) | |
434 os.chdir(cwd) | |
435 return | |
436 if os.path.isfile(os.path.join('tests', 'testconf.py')): | |
437 execfile(os.path.join('tests', 'testconf.py'), globals()) | |
438 os.chdir(cwd) | |
439 return | |
440 if exectestconf_helper(os.path.join(cwd, 'testconf.py')) or exectestconf_helper('testconf.py'): | |
441 os.chdir(cwd) | |
442 return | |
443 if os.path.isfile('testconf.py'): | |
444 execfile('testconf.py', globals()) | |
445 os.chdir(cwd) | |
446 return | |
447 os.chdir(cwd) | |
448 elif os.path.isfile('testconf.py'): | |
449 execfile('testconf.py', globals()) | |
450 return | |
451 raise KeyError, 'The configuration file for task ' + taskname + ' is missing' | |
452 | |
453 try: | |
454 name | |
455 namedefined = True | |
456 except Exception: | |
457 namedefined = False | |
458 | |
459 for taskname in tasknames: | |
460 if ntasks: | |
461 print | |
462 | |
463 try: | |
464 if len(tasknames) > 1: | |
465 print taskname | |
466 except Exception: | |
467 if taskname != os.path.curdir or ntasks: | |
468 print taskname | |
469 | |
470 try: del inname | |
471 except NameError: pass | |
472 try: del outname | |
473 except NameError: pass | |
474 try: del ansname | |
475 except NameError: pass | |
476 | |
477 if not namedefined and taskname != os.path.curdir: | |
478 name = os.path.join(os.path.curdir, taskname) | |
479 for k in shared: | |
480 g[k] = shared[k] | |
481 | |
482 oldcwd = os.getcwdu() | |
483 chdir_and_exec_testconf() | |
484 | |
485 if options.clean: | |
486 try: | |
487 if not stdio or tester: | |
488 if not tester: | |
489 inname | |
490 outname | |
491 if tester: | |
492 ansname | |
493 except NameError, error: | |
494 raise NameError, 'configuration ' + str(error).replace('name ', 'variable ', 1), sys.exc_info()[2] | |
495 if not options.erase: | |
496 try: | |
497 inname = inname.replace('%', taskname) | |
498 except NameError: | |
499 inname = taskname + '.in' | |
500 try: | |
501 outname = outname.replace('%', taskname) | |
502 except NameError: | |
503 outname = taskname + '.out' | |
504 try: | |
505 ansname = ansname.replace('%', taskname) | |
506 except NameError: | |
507 ansname = taskname + '.ans' | |
508 elif not stdio or tester or not options.erase: | |
509 inname = inname.replace('%', taskname) | |
510 outname = outname.replace('%', taskname) | |
511 if tester: | |
512 ansname = ansname.replace('%', taskname) | |
513 if not stdio or tester or not options.erase: | |
514 if os.path.exists(inname): os.remove(inname) | |
515 if os.path.exists(outname): os.remove(outname) | |
516 if (tester or not options.erase) and ansname: | |
517 if os.path.exists(ansname): os.remove(ansname) | |
518 continue | |
519 | |
520 try: | |
521 name | |
522 except NameError, error: | |
523 if str(error).count('name') == 1: | |
524 raise NameError, 'configuration ' + str(error), sys.exc_info()[2] | |
525 else: | |
526 raise NameError, 'configuration ' + str(error).replace('name ', 'variable ', 1), sys.exc_info()[2] | |
527 | |
528 try: | |
529 if not stdio: | |
530 inname | |
531 outname | |
532 testcaseinname | |
533 if tester: | |
534 outname | |
535 if ansname: | |
536 testcaseoutname | |
537 else: | |
538 testcaseoutname | |
539 except NameError, error: | |
540 raise NameError, 'configuration ' + str(error).replace('name ', 'variable ', 1), sys.exc_info()[2] | |
541 | |
542 if not options.erase: | |
543 try: | |
544 inname | |
545 except NameError: | |
546 inname = taskname + '.in' | |
547 try: | |
548 outname | |
549 except NameError: | |
550 outname = taskname + '.out' | |
551 try: | |
552 ansname | |
553 except NameError: | |
554 ansname = taskname + '.ans' | |
555 | |
556 if options.pause: | |
557 try: | |
558 pause | |
559 except NameError, error: | |
560 if os.name == 'posix': | |
561 pause = 'read -s -n 1' | |
562 print 'Configuration ' + str(error).replace('name ', 'variable ') + '; it was devised automatically but the choice might be incorrect, so test.py might exit immediately after the testing is completed.' | |
563 elif os.name == 'nt': | |
564 pause = 'pause' | |
565 else: | |
566 raise NameError, 'configuration ' + str(error).replace('name ', 'variable ') + ' and cannot be devised automatically', sys.exc_info()[2] | |
567 | |
568 if not dummyinname: | |
569 dummyinname = testcaseinname | |
570 if not dummyoutname and (not tester or ansname): | |
571 dummyoutname = testcaseoutname | |
572 | |
573 dummyinname = dummyinname.replace('%', taskname) | |
574 dummyoutname = dummyoutname.replace('%', taskname) | |
575 testcaseinname = testcaseinname.replace('%', taskname) | |
576 if not stdio or not options.erase: | |
577 inname = inname.replace('%', taskname) | |
578 outname = outname.replace('%', taskname) | |
579 try: | |
580 ansname = ansname.replace('%', taskname) | |
581 except NameError: | |
582 pass | |
583 if tester: | |
584 try: inname = inname.replace('%', taskname) | |
585 except NameError: pass | |
586 outname = outname.replace('%', taskname) | |
587 if ansname: | |
588 ansname = ansname.replace('%', taskname) | |
589 testcaseoutname = testcaseoutname.replace('%', taskname) | |
590 else: | |
591 testcaseoutname = testcaseoutname.replace('%', taskname) | |
592 | |
593 if isinstance(padwithzeroestolength, tuple): | |
594 padwithzeroestolength, paddummieswithzeroestolength = padwithzeroestolength | |
595 else: | |
596 paddummieswithzeroestolength = padwithzeroestolength | |
597 | |
598 if options.python: | |
599 dummies = () | |
600 s = ' '.join(args) | |
601 tests = eval(s) | |
602 try: | |
603 tests.__iter__ | |
604 except AttributeError: | |
605 tests = (tests,) | |
606 elif len(args): | |
607 if os.path.exists(args[0]): | |
608 name = args[0] | |
609 del args[0] | |
610 if len(args) > 1: | |
611 dummies = () | |
612 tests = args | |
613 elif len(args): | |
614 dummies = () | |
615 s = args[0] | |
616 if len(s) < padwithzeroestolength: | |
617 s = s.zfill(padwithzeroestolength) | |
618 if existstestcase(testcaseinname.replace('$', s)): | |
619 tests = (s,) | |
620 else: | |
621 try: | |
622 tests = eval(args[0]) | |
623 try: | |
624 tests.__iter__ | |
625 except AttributeError: | |
626 tests = (tests,) | |
627 except Exception: | |
628 tests = (s,) | |
629 | |
630 if options.exclude: | |
631 testsexcluded = [] | |
632 for i in options.exclude: | |
633 v = eval(i) | |
634 try: | |
635 testsexcluded.extend(v) | |
636 except TypeError: | |
637 testsexcluded.append(v) | |
638 | |
639 # Windows doesn't like paths beginning with .\ and not ending with an extension | |
640 name = os.path.normcase(name) | |
641 if os.name == 'nt' and name.startswith('.\\'): | |
642 name = name[2:] | |
643 | |
644 newpointmap = {} | |
645 | |
646 for i in pointmap: | |
647 try: | |
648 for j in i: | |
649 newpointmap[j] = pointmap[i] | |
650 except TypeError: | |
651 newpointmap[i] = pointmap[i] | |
652 | |
653 pointmap = newpointmap | |
654 | |
655 if not tester: | |
656 maxexitcode = 0 | |
657 | |
658 if maxtime > 0: | |
659 strmaxtime = '/%.3f' % maxtime | |
660 else: | |
661 strmaxtime = '' | |
662 | |
663 padoutputtolength = 0 | |
664 ntests = [] | |
665 | |
666 for j in dummies: | |
667 try: | |
668 j.__iter__ | |
669 except AttributeError: | |
670 j = (j,) | |
671 ntests.append((j, True)) | |
672 for i in j: | |
673 s = str(i) | |
674 if len(s) < paddummieswithzeroestolength: | |
675 s = s.zfill(paddummieswithzeroestolength) | |
676 s = 'sample ' + s | |
677 if padoutputtolength < len(s): | |
678 padoutputtolength = len(s) | |
679 | |
680 for j in tests: | |
681 try: | |
682 j.__iter__ | |
683 except AttributeError: | |
684 j = (j,) | |
685 ntests.append((j, False)) | |
686 for i in j: | |
687 s = str(i) | |
688 if len(s) < padwithzeroestolength: | |
689 s = s.zfill(padwithzeroestolength) | |
690 if padoutputtolength < len(s): | |
691 padoutputtolength = len(s) | |
692 | |
693 tests = ntests | |
694 score = maxpoints = ncorrect = ntotal = ncorrectvalued = nvalued = 0 | |
695 | |
696 if options.copyonly: | |
697 j, isdummy = tests[-1] | |
698 if isdummy: | |
699 realinname = dummyinname | |
700 realoutname = dummyoutname | |
701 else: | |
702 realinname = testcaseinname | |
703 realoutname = testcaseoutname | |
704 for i in j: | |
705 if i in testsexcluded and not isdummy: | |
706 continue | |
707 s = str(i) | |
708 if isdummy: | |
709 if len(s) < paddummieswithzeroestolength: | |
710 s = s.zfill(paddummieswithzeroestolength) | |
711 else: | |
712 if len(s) < padwithzeroestolength: | |
713 s = s.zfill(padwithzeroestolength) | |
714 copytestcase(realinname.replace('$', s), inname) | |
715 if ansname: | |
716 copytestcase(realoutname.replace('$', s), ansname) | |
717 continue | |
718 | |
719 for j, isdummy in tests: | |
720 ncorrectgrp = 0 | |
721 ntotalgrp = 0 | |
722 scoregrp = 0 | |
723 maxpointsgrp = 0 | |
724 if isdummy: | |
725 realinname = dummyinname | |
726 realoutname = dummyoutname | |
727 else: | |
728 realinname = testcaseinname | |
729 realoutname = testcaseoutname | |
730 for i in j: | |
731 if i in testsexcluded and not isdummy: | |
732 continue | |
733 ntotalgrp += 1 | |
734 s = str(i) | |
735 if isdummy: | |
736 npoints = 0 | |
737 if len(s) < paddummieswithzeroestolength: | |
738 s = s.zfill(paddummieswithzeroestolength) | |
739 spref = 'sample ' | |
740 else: | |
741 npoints = pointmap.get(None, maxexitcode if maxexitcode and isinstance(maxexitcode, int) else 1) | |
742 npoints = pointmap.get(i, npoints) | |
743 maxpointsgrp += npoints | |
744 if npoints: | |
745 nvalued += 1 | |
746 if len(s) < padwithzeroestolength: | |
747 s = s.zfill(padwithzeroestolength) | |
748 spref = '' | |
749 print ' ' * (padoutputtolength - len(spref + s)) + spref + s + ':', | |
750 sys.stdout.flush() | |
751 outputdata = open(os.devnull, 'w') | |
752 if stdio: | |
753 f = tempfile.NamedTemporaryFile(delete=False) | |
754 inputdatafname = f.name | |
755 f.close() | |
756 copytestcase(realinname.replace('$', s), inputdatafname) | |
757 inputdata = open(inputdatafname, 'rU') | |
758 if options.erase: | |
759 tempoutput = tempfile.TemporaryFile('w+') | |
760 else: | |
761 tempoutput = open(outname, 'w+') | |
762 try: | |
763 proc = subprocess.Popen(name, stdin=inputdata, stdout=tempoutput, stderr=outputdata, universal_newlines=True) | |
764 except OSError, error: | |
765 raise OSError, 'The program to be tested cannot be launched: ' + str(error), sys.exc_info()[2] | |
766 else: | |
767 if os.path.exists(outname): | |
768 os.remove(outname) | |
769 copytestcase(realinname.replace('$', s), inname) | |
770 try: | |
771 proc = subprocess.Popen(name, stdin=outputdata, stdout=outputdata, stderr=outputdata, universal_newlines=True) | |
772 except OSError, error: | |
773 raise OSError, 'The program to be tested cannot be launched: ' + str(error), sys.exc_info()[2] | |
774 cl = clock() | |
775 if maxtime > 0: | |
776 while 1: | |
777 proc.poll() | |
778 elapsed = clock() - cl | |
779 if proc.returncode == None: | |
780 if elapsed >= maxtime: | |
781 print '%.3f%s s, 0/%d, time limit exceeded' % (elapsed, strmaxtime, npoints) | |
782 sys.stdout.flush() | |
783 while proc.returncode == None: | |
784 try: | |
785 proc.terminate() | |
786 except OSError: | |
787 pass | |
788 except AttributeError: | |
789 try: | |
790 os.kill(proc.pid, signal.SIGTERM) | |
791 except Exception: | |
792 pass | |
793 proc.poll() | |
794 outputdata.close() | |
795 if stdio: | |
796 tempoutput.close() | |
797 break | |
798 else: | |
799 print '%.3f%s s,' % (elapsed, strmaxtime), | |
800 sys.stdout.flush() | |
801 elapsed = 0 | |
802 if stdio: | |
803 tempoutput.seek(0) | |
804 lines = tempoutput.readlines() | |
805 tempoutput.close() | |
806 break | |
807 if elapsed >= maxtime: | |
808 continue | |
809 else: | |
810 data = proc.communicate() | |
811 elapsed = clock() - cl | |
812 print '%.3f%s s,' % (elapsed, strmaxtime), | |
813 sys.stdout.flush() | |
814 if stdio: | |
815 tempoutput.seek(0) | |
816 lines = tempoutput.readlines() | |
817 tempoutput.close() | |
818 outputdata.close() | |
819 if stdio: | |
820 inputdata.close() | |
821 try: | |
822 os.unlink(inputdatafname) | |
823 except Exception: | |
824 pass | |
825 if proc.returncode > 0: | |
826 print '0/%d, non-zero return code %d' % (npoints, proc.returncode) | |
827 sys.stdout.flush() | |
828 elif proc.returncode < 0: | |
829 print '0/%d, terminated by signal %d' % (npoints, -proc.returncode) | |
830 sys.stdout.flush() | |
831 else: | |
832 if not tester: | |
833 if stdio: | |
834 outputdata = opentestcase(realoutname.replace('$', s)) | |
835 r = 0 | |
836 data = outputdata.read().splitlines(True) | |
837 if len(lines) != len(data): | |
838 r = 1 | |
839 else: | |
840 for i in zip(lines, data): | |
841 if i[0] != i[1]: | |
842 r = 1 | |
843 break | |
844 outputdata.close() | |
845 else: | |
846 try: | |
847 inputdata = open(outname, 'rU') | |
848 except IOError: | |
849 print '0/%g, output file not created or not readable' % npoints | |
850 sys.stdout.flush() | |
851 r = None | |
852 else: | |
853 outputdata = opentestcase(realoutname.replace('$', s)) | |
854 r = 0 | |
855 lines = inputdata.readlines() | |
856 data = outputdata.read().splitlines(True) | |
857 if len(lines) != len(data): | |
858 r = 1 | |
859 else: | |
860 for i in zip(lines, data): | |
861 if i[0] != i[1]: | |
862 r = 1 | |
863 break | |
864 inputdata.close() | |
865 outputdata.close() | |
866 else: | |
867 if ansname: | |
868 copytestcase(realoutname.replace('$', s), ansname) | |
869 if stdio: | |
870 try: copytestcase(realinname.replace('$', s), inname) | |
871 except NameError: pass | |
872 outputdata = open(outname, 'w') | |
873 outputdata.writelines(lines) | |
874 outputdata.close() | |
875 try: | |
876 proc = subprocess.Popen(tester, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) | |
877 except OSError, error: | |
878 raise OSError, 'The tester application cannot be launched: ' + str(error), sys.exc_info()[2] | |
879 data = proc.communicate() | |
880 r = proc.returncode | |
881 if tester and data[0]: | |
882 data = ''.join((' (', data[0].strip(), ')')) | |
883 else: | |
884 data = '' | |
885 if not maxexitcode and r or maxexitcode and not r: | |
886 print '0/%g, wrong answer%s' % (npoints, data) | |
887 sys.stdout.flush() | |
888 elif not maxexitcode and r == 0 or maxexitcode and r >= maxexitcode: | |
889 print '%g/%g, OK%s' % (npoints, npoints, data) | |
890 sys.stdout.flush() | |
891 scoregrp += npoints | |
892 ncorrectgrp += 1 | |
893 if npoints: | |
894 ncorrectvalued += 1 | |
895 elif maxexitcode and r != None: | |
896 actualpoints = npoints*r/maxexitcode if not npoints*r%maxexitcode else float(npoints*r)/maxexitcode | |
897 print '%g/%g, partly OK%s' % (actualpoints, npoints, data) | |
898 sys.stdout.flush() | |
899 scoregrp += actualpoints | |
900 ncorrectgrp += 1 | |
901 if npoints: | |
902 ncorrectvalued += 1 | |
903 if ntotalgrp: | |
904 if ncorrectgrp < ntotalgrp: | |
905 scoregrp = 0 | |
906 if ntotalgrp > 1: | |
907 print 'Group total: %d/%d tests; %g/%g points' % (ncorrectgrp, ntotalgrp, scoregrp, maxpointsgrp) | |
908 sys.stdout.flush() | |
909 ncorrect += ncorrectgrp | |
910 ntotal += ntotalgrp | |
911 score += scoregrp | |
912 maxpoints += maxpointsgrp | |
913 | |
914 if options.erase: | |
915 if not stdio or tester: | |
916 if os.path.exists(inname): os.remove(inname) | |
917 if os.path.exists(outname): os.remove(outname) | |
918 if tester and ansname: | |
919 if os.path.exists(ansname): os.remove(ansname) | |
920 elif stdio: | |
921 copytestcase(realinname.replace('$', s), inname) | |
922 copytestcase(realoutname.replace('$', s), ansname) | |
923 actualpoints = (score*taskweight/maxpoints if not score*taskweight%maxpoints else float(score*taskweight)/maxpoints) if maxpoints else 0 | |
924 if nvalued != ntotal: | |
925 print 'Grand total: %d/%d tests (%d/%d valued); %g/%g points; weighted score: %g/%g' % (ncorrect, ntotal, ncorrectvalued, nvalued, score, maxpoints, actualpoints, taskweight) | |
926 else: | |
927 print 'Grand total: %d/%d tests; %g/%g points; weighted score: %g/%g' % (ncorrect, ntotal, score, maxpoints, actualpoints, taskweight) | |
928 | |
929 scoresumoveralltasks += actualpoints | |
930 scoremaxoveralltasks += taskweight | |
931 ntasks += 1 | |
932 nfulltasks += int((score == maxpoints) if maxpoints else (taskweight == 0)) | |
933 | |
934 os.chdir(oldcwd) | |
935 | |
936 if options.clean or options.copyonly: | |
937 sys.exit() | |
938 | |
939 if ntasks != 1: | |
940 print | |
941 print 'Grand grand total: %g/%g weighted points; %d/%d problems solved fully' % (scoresumoveralltasks, scoremaxoveralltasks, nfulltasks, ntasks) | |
942 | |
943 if options.pause: | |
944 print 'Press any key to exit... ', | |
945 sys.stdout.flush() | |
946 os.system(pause + ' >' + os.devnull) |