]> mj.ucw.cz Git - moe.git/blob - t/moe/testutils.py
Merge branch 'python-newpipe' into python
[moe.git] / t / moe / testutils.py
1 #!/usr/bin/env python
2
3 import shutil
4 import traceback
5 import re
6 import os.path
7
8 import moe
9 import moe.config
10 import moe.eval
11 import moe.logs
12 from moe.logs import *
13
14
15 # Allowed test names
16 testname_regexp = re.compile('\A[\w]+\Z')
17
18 def hook_run_tests(e):
19     """Hook that runs the test pipeline for each test in `TESTS`.
20     Opens per-test log `TEST_LOG`.
21     `TEST` is set, checked for invalid characters and fixed in each iteration.
22
23     .. todo :: Different log-level for per-test log?
24     """ 
25     log.info('Running test pipeline for each test')
26     e.config.fix('TESTS')
27     tests = e['TESTS'].split()
28     log.debug('TESTS: %r', tests)
29     for t in tests:
30         if not testname_regexp.match(t):
31             raise MoeError("Invalid test name %r", t)
32         userlog.info('TEST %s ...' % t)
33         with e.config.parse("TEST='"+t+"'", level=70, source='<hook_run_tests>'):
34             try:
35                 e.config.fix('TEST')
36                 moe.logs.open_test_log(e['TEST_LOG'], log.level) 
37                 log.info(' *** Test case %s *** ' % t)
38                 e.debug_dump_config()
39                 e.debug_dump_pipe(e.test_pipe)
40                 e.test_pipe.run(e=e)
41             except:
42                 (testlog or log).exception("Exception falls through hook_run_tests()")
43                 raise
44             finally:
45                 e.config.unfix('TEST')
46                 moe.logs.close_test_log()
47
48 def configure_test(e, test):
49     test_cf = os.path.join(e["PDIR"], test + ".config")
50     if os.path.exists(test_cf):
51         cfg = moe.config.MoeConfig(name=test_cf, type="test")
52         e.cfgs.push(cfg)
53
54     e.log_config(2, "for the test")
55
56     e.test_stat = moe.status.MoeStatus()
57     e.test_stat["id"] = test
58     e.stat.get_list("tests").append(e.test_stat)
59
60 def collect_status(e):
61     sf = os.path.join(e["TDIR"], e["TESTCASE_STATUS"])
62     if os.path.exists(sf):
63         e.log.verbose("Reading status from %s\n" % sf)
64         stat = moe.status.MoeStatus()
65         stat.read(name=sf)
66         if e.log.verbosity > 1:
67             stat.write_nested(e.log.log_file, 1)
68         for k in stat.keys():
69             e.test_stat[k] = stat[k]
70         os.unlink(sf)
71     else:
72         e.log.verbose("No status file present\n")
73
74 def tmpname(e):
75     return os.path.join(e["TDIR"], e["TEST"] + ".tmp")
76
77 def collect_tmp_line(tmp_file):
78     if os.path.exists(tmp_file):
79         f = open(tmp_file, "r")
80         v = f.readline().rstrip("\n")
81         f.close()
82         os.unlink(tmp_file)
83     else:
84         v = ""
85     return v
86
87 def collect_verdict(e, verdict_file):
88     v = collect_tmp_line(verdict_file)
89     if len(v) >= 4 and v[0].isalnum and v[1].isalnum and v[2] == ":" and v[3] == " ":
90         e.test_stat["status"] = v[0:2]
91         e.log.verbose("Judge's status: %s\n" % v[0:2])
92         v = v[4:]
93     v.strip()
94     if v != "":
95         e.test_stat["message"] = v
96         e.log.verbose("Judge's verdict: %s\n" % v)
97
98 def collect_points(e):
99     p = collect_tmp_line(os.path.join(e["TDIR"], e["TESTCASE_PTS"]))
100     if p != "":
101         e.log.verbose("Judge has supplied points: %s\n" % p)
102         e.test_stat["points"] = p
103
104 def setup(e):
105     pdir = e["PDIR"]
106     tdir = e["TDIR"]
107     inn = e["TESTCASE_IN"]
108     out = e["TESTCASE_OUT"]
109     ok = e["TESTCASE_OK"]
110
111     if os.path.exists(os.path.join(pdir, inn)):
112         moe.util.link_or_copy(os.path.join(pdir, inn), os.path.join(tdir, inn))
113     if os.path.exists(os.path.join(pdir, out)):
114         moe.util.link_or_copy(os.path.join(pdir, out), os.path.join(tdir, ok))
115
116 def judge(e):
117     cmd = e["OUTPUT_CHECK"]
118     if cmd == "":
119         return
120     verdict_file = tmpname(e)
121     cmd = "exec 2>%s ; %s" % (verdict_file,cmd)
122
123     e.log.progress("<check> ")
124     e.log.verbose("Checking output: %s\n" % cmd)
125     e.log.flush()
126     rc = os.system(cmd)
127     collect_verdict(e, verdict_file)
128     collect_points(e)
129     collect_status(e)
130     if os.WIFEXITED(rc):
131         if os.WEXITSTATUS(rc) == 0:
132             return
133         elif os.WEXITSTATUS(rc) == 1:
134             raise moe.TestError("Wrong answer", "WA")
135     raise moe.MoeError("Judge failure")
136
137 def points(e):
138     if e.test_stat["points"] is None:
139         e.test_stat["points"] = e["POINTS_PER_TEST"]
140
141 def filter(e):
142     cmd = e["OUTPUT_FILTER"]
143     if cmd == "":
144         return
145
146     os.rename(os.path.join(e["TDIR"], e["TESTCASE_OUT"]), os.path.join(e["TDIR"], e["TESTCASE_RAW"]))
147     e.log.progress("<filter> ")
148     e.log.verbose("Filtering output: %s\n" % cmd)
149     e.log.flush()
150     rc = os.system(cmd)
151     if os.WIFEXITED(rc) and os.WEXITSTATUS(rc) == 0:
152         if not os.path.exists(os.path.join(e["TDIR"], e["TESTCASE_OUT"])):
153             raise moe.MoeError("Filter has generated no output")
154     else:
155         raise moe.MoeError("Filter failure")
156
157 def syntax(e):
158     cmd = e["SYNTAX_CHECK"]
159     if cmd == "":
160         return
161     verdict_file = tmpname(e)
162     cmd = "exec 2>%s ; %s" % (verdict_file,cmd)
163
164     e.log.progress("<syntax> ")
165     e.log.verbose("Checking syntax: %s\n" % cmd)
166     e.log.flush()
167     rc = os.system(cmd)
168     collect_verdict(e, verdict_file)
169     collect_status(e)
170     if os.WIFEXITED(rc):
171         if os.WEXITSTATUS(rc) == 0:
172             return
173         elif os.WEXITSTATUS(rc) == 1:
174             raise moe.TestError("Wrong syntax", "SY")
175     raise moe.MoeError("Syntax checker failure")
176
177 def run_test(e, test):
178     configure_test(e, test)
179
180     ## FIXME: interactive tasks
181     e.test_pipe.configure(e["TESTCASE_HOOKS"])
182     if e.log.verbosity >= 2:
183         e.test_pipe.dump(e.log.log_file, prefix="\t")
184     e.test_pipe.run(e)
185
186 def wrap_run_test(e, test):
187     try:
188         run_test(e, test)
189     except moe.MoeError, err:
190         raise moe.TestError(err, "XX")
191     except Exception:
192         if e["DEBUG"]:
193             moe.log.fatal_exception()
194         traceback.print_exc(file = e.log.log_file)
195         raise moe.TestError("Internal exception", "XX")
196
197 def conclude_test(e):
198     stat = e.test_stat
199     if not stat["points"]:
200         stat["points"] = 0
201
202     if e.log.verbosity > 1:
203         e.log.verbose("Final status:\n")
204         stat.write_nested(e.log.log_file, 1)
205
206     if stat["status"]:
207         msg = "%s: %s" % (stat["status"], e.test_stat["message"])
208     else:
209         msg = "OK"
210     msg += " (%s points" % stat["points"]
211     if stat["time"]:
212         msg += ", %s s" % stat["time"]
213     if stat["mem"]:
214         msg += ", %d MB" % ((int(stat["mem"]) + 524288) // 1048576)
215     msg += ")\n"
216     e.log.progress(msg)
217     e.log.say(msg)
218
219 def run_tests(e):
220     e.test_pipe.insert(0, "setup", setup)
221     e.test_pipe.insert(400, "filter", filter)
222     e.test_pipe.insert(500, "syntax", syntax)
223     e.test_pipe.insert(600, "judge", judge)
224     e.test_pipe.insert(700, "points", points)
225
226     for test in e["TESTS"].split():
227         e.log.progress("Test %s: " % test)
228         old_cfgs = e.cfgs
229         old_log = e.log
230
231         try:
232             wrap_run_test(e, test)
233         except moe.TestError, err:
234             if not e.test_stat["status"]:
235                 e.test_stat["status"] = err.stat_code
236             if not e.test_stat["message"]:
237                 e.test_stat["message"] = err.message
238
239         conclude_test(e)
240         
241         e.cfgs = old_cfgs
242         e.log = old_log
243         moe.log.default = old_log