comparison test-vcs.py @ 45:5afefe51dcdc

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