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