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