Mercurial > ~astiob > upreckon > hgweb
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) |
