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