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