294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
1 |
# IVLE - Informatics Virtual Learning Environment
|
2 |
# Copyright (C) 2007-2008 The University of Melbourne
|
|
3 |
#
|
|
4 |
# This program is free software; you can redistribute it and/or modify
|
|
5 |
# it under the terms of the GNU General Public License as published by
|
|
6 |
# the Free Software Foundation; either version 2 of the License, or
|
|
7 |
# (at your option) any later version.
|
|
8 |
#
|
|
9 |
# This program is distributed in the hope that it will be useful,
|
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12 |
# GNU General Public License for more details.
|
|
13 |
#
|
|
14 |
# You should have received a copy of the GNU General Public License
|
|
15 |
# along with this program; if not, write to the Free Software
|
|
16 |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
17 |
||
18 |
# Module: TestFramework
|
|
19 |
# Author: Dilshan Angampitiya
|
|
507
by stevenbird
Extended code test framework to support tests on the code string, rather |
20 |
# Steven Bird (revisions)
|
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
21 |
# Date: 24/1/2008
|
22 |
||
1394.1.3
by William Grant
Add a nice introductory docstring. |
23 |
"""Test framework for verifying student exercise solutions.
|
24 |
||
25 |
With the ability to run flexible user-specified tests over student
|
|
26 |
exercise submissions, this is the core of the automated testing mechanism.
|
|
27 |
||
28 |
Note that this has three classes and another concept with the same names as
|
|
29 |
IVLE database classes, but corresponding to something different:
|
|
30 |
||
31 |
TestFramework | IVLE
|
|
32 |
----------------------------------
|
|
33 |
TestSuite | Exercise (sort of)
|
|
34 |
TestCase | TestSuite
|
|
35 |
TestCasePart | TestCase
|
|
36 |
test | TestCasePart
|
|
37 |
||
38 |
The test framework uses the IVLE console subsystem to execute student code
|
|
39 |
in a safe environment.
|
|
40 |
"""
|
|
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
41 |
|
1034
by dcoles
Console: Refactored the code to support supplying of stdin to the console. |
42 |
import sys, copy |
507
by stevenbird
Extended code test framework to support tests on the code string, rather |
43 |
import types |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
44 |
|
1079
by William Grant
Merge setup-refactor branch. This completely breaks existing installations; |
45 |
from ivle import testfilespace |
1029
by dcoles
Tutorial Service: Ported the tutorial service to the console so that all |
46 |
|
1264
by William Grant
Add __test__ = False to ivle.webapp.tutorial.test, to keep nose away. |
47 |
# Don't let nose into here, as it has lots of stuff named Test* without being
|
48 |
# tests.
|
|
49 |
__test__ = False |
|
50 |
||
306
by dilshan_a
Consolidated SolutionError and AttemptError into ScriptExecutionError. |
51 |
# student error or author error
|
52 |
# errors in student code get handled internally
|
|
53 |
# errors in solution code get passed up
|
|
54 |
class ScriptExecutionError(Exception): |
|
55 |
"""Runtime error in the student code or solution code"""
|
|
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
56 |
def __init__(self, exc_info): |
57 |
cla, exc, trbk = exc_info |
|
58 |
self._name = cla.__name__ |
|
59 |
self._detail = str(exc) |
|
310
by dilshan_a
Add lineno to exception info. |
60 |
self._trbk = trbk |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
61 |
|
62 |
def is_critical(self): |
|
63 |
if ( self._name == 'FunctionNotFoundError' |
|
64 |
or self._name == 'SyntaxError' |
|
65 |
or self._name == 'IndentationError'): |
|
66 |
return True |
|
67 |
else: |
|
68 |
return False |
|
69 |
||
70 |
def to_dict(self): |
|
310
by dilshan_a
Add lineno to exception info. |
71 |
import traceback |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
72 |
return {'name': self._name, |
73 |
'detail': self._detail, |
|
311
by dilshan_a
Last commit was buggy. |
74 |
'critical': self.is_critical(), |
310
by dilshan_a
Add lineno to exception info. |
75 |
'lineno': traceback.tb_lineno(self._trbk) |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
76 |
}
|
77 |
||
78 |
def __str__(self): |
|
79 |
return self._name + " - " + str(self._detail) |
|
80 |
||
306
by dilshan_a
Consolidated SolutionError and AttemptError into ScriptExecutionError. |
81 |
# author error
|
82 |
class TestCreationError(Exception): |
|
83 |
"""An error occured while creating the test suite or one of its components"""
|
|
84 |
def __init__(self, reason): |
|
85 |
self._reason = reason |
|
86 |
||
87 |
def __str__(self): |
|
88 |
return self._reason |
|
89 |
||
90 |
# author error
|
|
91 |
class TestError(Exception): |
|
92 |
"""Runtime error in the testing framework outside of the provided or student code"""
|
|
93 |
def __init__(self, exc_info): |
|
94 |
cla, exc, trbk = exc_info |
|
95 |
self._name = cla.__name__ |
|
96 |
self._detail = str(exc) |
|
97 |
self._exc_info = exc_info |
|
98 |
||
99 |
def exc_info(self): |
|
100 |
return self._exc_info |
|
101 |
||
102 |
def __str__(self): |
|
103 |
return "Error testing solution against attempt: %s - %s" %(self._name, self._detail) |
|
104 |
||
105 |
# author error
|
|
106 |
# raised when expected file not found in solution output
|
|
107 |
# Always gets caught and passed up as a TestError
|
|
108 |
class FileNotFoundError(Exception): |
|
109 |
def __init__(self, filename): |
|
110 |
self._filename = filename |
|
111 |
||
112 |
def __str__(self): |
|
113 |
return "File %s not found in output" %(self._filename) |
|
114 |
||
115 |
||
116 |
# Error encountered when executing solution or attempt code
|
|
117 |
# Always gets caught and passed up in a ScriptExecutionError
|
|
299
by dilshan_a
Test framework now handles exceptions as valid outputs for scripts. |
118 |
class FunctionNotFoundError(Exception): |
119 |
"""This error is returned when a function was expected in a
|
|
120 |
test case but was not found"""
|
|
121 |
def __init__(self, function_name): |
|
122 |
self.function_name = function_name |
|
123 |
||
124 |
def __str__(self): |
|
125 |
return "Function " + self.function_name + " not found" |
|
126 |
||
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
127 |
class TestCasePart: |
128 |
"""
|
|
129 |
A part of a test case which compares a subset of the input files or file streams.
|
|
502
by stevenbird
www/apps/tutorialservice/__init__.py |
130 |
This can be done either with a comparison function, or by comparing directly, after
|
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
131 |
applying normalisations.
|
132 |
"""
|
|
495
by stevenbird
Fixes to permit content authors to produce nicer diagnostic responses as a result |
133 |
|
1410
by William Grant
A class method takes the class as an argument. We want static methods in testframework. |
134 |
ident = staticmethod(lambda x: x) |
135 |
ignore = staticmethod(lambda x: None) |
|
136 |
match = staticmethod(lambda x,y: x==y) |
|
137 |
always_match = staticmethod(lambda x,y: True) |
|
138 |
true = staticmethod(lambda *x: True) |
|
139 |
false = staticmethod(lambda *x: False) |
|
495
by stevenbird
Fixes to permit content authors to produce nicer diagnostic responses as a result |
140 |
|
1099.1.141
by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file. |
141 |
def __init__(self, test_case): |
1394.1.2
by William Grant
Rewrite TestFramework class docstrings to indicate the corresponding DB classes. |
142 |
"""Create a testable TestCasePart from an IVLE database TestCase.
|
143 |
||
144 |
The name mismatch is unfortunately not a typo. A database TestCase
|
|
145 |
represents a TestFramework TestCasePart.
|
|
146 |
||
147 |
Initialise with descriptions (pass,fail) and a default behavior for output
|
|
1412
by William Grant
Disable support for testcases with a default other than 'ignore'. |
148 |
If default is match, values without tests are matched exactly
|
149 |
If default is ignore, values without tests are ignored
|
|
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
150 |
The default default is match.
|
151 |
"""
|
|
1099.1.141
by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file. |
152 |
self._pass_msg = test_case.passmsg |
153 |
self._fail_msg = test_case.failmsg |
|
154 |
self._default = test_case.test_default |
|
155 |
if self._default == 'ignore': |
|
495
by stevenbird
Fixes to permit content authors to produce nicer diagnostic responses as a result |
156 |
self._default_func = self.true |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
157 |
else: |
1412
by William Grant
Disable support for testcases with a default other than 'ignore'. |
158 |
raise TestCreationError( |
159 |
"Only 'ignore' defaults are supported at this time.") |
|
495
by stevenbird
Fixes to permit content authors to produce nicer diagnostic responses as a result |
160 |
self._default_func = self.match |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
161 |
|
162 |
self._file_tests = {} |
|
163 |
self._stdout_test = ('check', self._default_func) |
|
164 |
self._stderr_test = ('check', self._default_func) |
|
299
by dilshan_a
Test framework now handles exceptions as valid outputs for scripts. |
165 |
self._exception_test = ('check', self._default_func) |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
166 |
self._result_test = ('check', self._default_func) |
502
by stevenbird
www/apps/tutorialservice/__init__.py |
167 |
self._code_test = ('check', self._default_func) |
1099.1.141
by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file. |
168 |
|
169 |
for part in test_case.parts: |
|
170 |
if part.part_type =="file": |
|
1394.1.4
by William Grant
Reject attempts to run file tests through TestFramework -- the console backend does not yet have support. |
171 |
raise AssertionError( |
172 |
"Files not supported by the console - see bug #492437.") |
|
1099.1.150
by Nick Chadwick
Modified worksheets to properly link attempts to worksheets and |
173 |
self.add_file_test(part) |
1099.1.141
by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file. |
174 |
elif part.part_type =="stdout": |
1099.1.150
by Nick Chadwick
Modified worksheets to properly link attempts to worksheets and |
175 |
self.add_stdout_test(part) |
1099.1.141
by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file. |
176 |
elif part.part_type =="stderr": |
1099.1.150
by Nick Chadwick
Modified worksheets to properly link attempts to worksheets and |
177 |
self.add_stderr_test(part) |
1099.1.141
by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file. |
178 |
elif part.part_type =="result": |
1099.1.150
by Nick Chadwick
Modified worksheets to properly link attempts to worksheets and |
179 |
self.add_result_test(part) |
1099.1.141
by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file. |
180 |
elif part.part_type =="exception": |
1099.1.150
by Nick Chadwick
Modified worksheets to properly link attempts to worksheets and |
181 |
self.add_exception_test(part) |
1099.1.141
by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file. |
182 |
elif part.part_type =="code": |
1099.1.150
by Nick Chadwick
Modified worksheets to properly link attempts to worksheets and |
183 |
self.add_code_test(part) |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
184 |
|
185 |
def _set_default_function(self, function, test_type): |
|
186 |
""""Ensure test type is valid and set function to a default
|
|
1394.1.1
by William Grant
Add note about the _check_code string->function eval hack. |
187 |
if not specified.
|
188 |
|
|
189 |
The function may be a string containing the code, in which case
|
|
190 |
it will be evaluated by a hack in _check_code.
|
|
191 |
"""
|
|
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
192 |
|
1426
by William Grant
Add exact match support to the TestFramework backend. |
193 |
if test_type not in ['norm', 'check', 'match']: |
1677
by David Coles
exercise: Make invalid test case type error slightly more explicit |
194 |
raise TestCreationError( |
195 |
"Invalid test type '%s' in Test Case '%s'"% |
|
196 |
(test_type, self._pass_msg)) |
|
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
197 |
|
198 |
if function == '': |
|
495
by stevenbird
Fixes to permit content authors to produce nicer diagnostic responses as a result |
199 |
if test_type == 'norm': function = self.ident |
200 |
else: function = self.match |
|
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
201 |
|
202 |
return function |
|
203 |
||
204 |
def _validate_function(self, function, included_code): |
|
205 |
"""Create a function object from the given string.
|
|
502
by stevenbird
www/apps/tutorialservice/__init__.py |
206 |
If a valid function object cannot be created, raise an error.
|
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
207 |
"""
|
208 |
if not callable(function): |
|
209 |
try: |
|
210 |
exec "__f__ = %s" %function in included_code |
|
211 |
except: |
|
507
by stevenbird
Extended code test framework to support tests on the code string, rather |
212 |
raise TestCreationError("Invalid function %s" % function) |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
213 |
|
214 |
f = included_code['__f__'] |
|
215 |
||
216 |
if not callable(f): |
|
507
by stevenbird
Extended code test framework to support tests on the code string, rather |
217 |
raise TestCreationError("Invalid function %s" % function) |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
218 |
else: |
219 |
f = function |
|
220 |
||
221 |
return f |
|
222 |
||
223 |
def validate_functions(self, included_code): |
|
224 |
"""Ensure all functions used by the test cases exist and are callable.
|
|
507
by stevenbird
Extended code test framework to support tests on the code string, rather |
225 |
Also convert their string representations to function objects.
|
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
226 |
This can only be done once all the include code has been specified.
|
227 |
"""
|
|
228 |
(test_type, function) = self._stdout_test |
|
229 |
self._stdout_test = (test_type, self._validate_function(function, included_code)) |
|
230 |
||
231 |
(test_type, function) = self._stderr_test |
|
232 |
self._stderr_test = (test_type, self._validate_function(function, included_code)) |
|
716
by mattgiuca
Test Framework: Numerous bug fixes. |
233 |
|
234 |
(test_type, function) = self._result_test |
|
235 |
self._result_test = (test_type, self._validate_function(function, included_code)) |
|
236 |
||
237 |
(test_type, function) = self._exception_test |
|
238 |
self._exception_test = (test_type, self._validate_function(function, included_code)) |
|
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
239 |
|
240 |
for filename, (test_type, function) in self._file_tests.items(): |
|
241 |
self._file_tests[filename] = (test_type, self._validate_function(function, included_code)) |
|
242 |
||
1099.1.141
by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file. |
243 |
def add_result_test(self, part): |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
244 |
"Test part that compares function return values"
|
1099.1.141
by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file. |
245 |
function = self._set_default_function(part.data, part.test_type) |
1099.1.150
by Nick Chadwick
Modified worksheets to properly link attempts to worksheets and |
246 |
self._result_test = (part.test_type, function) |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
247 |
|
1099.1.141
by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file. |
248 |
def add_stdout_test(self, part): |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
249 |
"Test part that compares stdout"
|
1099.1.141
by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file. |
250 |
function = self._set_default_function(part.data, part.test_type) |
1099.1.150
by Nick Chadwick
Modified worksheets to properly link attempts to worksheets and |
251 |
self._stdout_test = (part.test_type, function) |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
252 |
|
1099.1.141
by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file. |
253 |
def add_stderr_test(self, part): |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
254 |
"Test part that compares stderr"
|
1099.1.141
by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file. |
255 |
function = self._set_default_function(part.data, part.test_type) |
1099.1.150
by Nick Chadwick
Modified worksheets to properly link attempts to worksheets and |
256 |
self._stderr_test = (part.test_type, function) |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
257 |
|
1099.1.141
by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file. |
258 |
def add_exception_test(self, part): |
299
by dilshan_a
Test framework now handles exceptions as valid outputs for scripts. |
259 |
"Test part that compares stderr"
|
1099.1.141
by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file. |
260 |
function = self._set_default_function(part.data, part.test_type) |
1099.1.150
by Nick Chadwick
Modified worksheets to properly link attempts to worksheets and |
261 |
self._exception_test = (part.test_type, function) |
299
by dilshan_a
Test framework now handles exceptions as valid outputs for scripts. |
262 |
|
1099.1.141
by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file. |
263 |
def add_file_test(self, part): |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
264 |
"Test part that compares the contents of a specified file"
|
1099.1.141
by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file. |
265 |
function = self._set_default_function(part.data, part.test_type) |
1099.1.150
by Nick Chadwick
Modified worksheets to properly link attempts to worksheets and |
266 |
self._file_tests[part.filename] = (part.test_type, function) |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
267 |
|
1099.1.141
by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file. |
268 |
def add_code_test(self, part): |
502
by stevenbird
www/apps/tutorialservice/__init__.py |
269 |
"Test part that examines the supplied code"
|
1099.1.141
by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file. |
270 |
function = self._set_default_function(part.data, part.test_type) |
1099.1.150
by Nick Chadwick
Modified worksheets to properly link attempts to worksheets and |
271 |
self._code_test = (part.test_type, function) |
502
by stevenbird
www/apps/tutorialservice/__init__.py |
272 |
|
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
273 |
def _check_output(self, solution_output, attempt_output, test_type, f): |
274 |
"""Compare solution output and attempt output using the
|
|
502
by stevenbird
www/apps/tutorialservice/__init__.py |
275 |
specified comparison function.
|
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
276 |
"""
|
507
by stevenbird
Extended code test framework to support tests on the code string, rather |
277 |
solution_output = str(solution_output) |
278 |
attempt_output = str(attempt_output) |
|
1426
by William Grant
Add exact match support to the TestFramework backend. |
279 |
|
280 |
if test_type == 'match': |
|
281 |
return solution_output == attempt_output |
|
282 |
elif test_type == 'norm': |
|
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
283 |
return f(solution_output) == f(attempt_output) |
284 |
else: |
|
285 |
return f(solution_output, attempt_output) |
|
286 |
||
519
by stevenbird
Fixed bug in test framework. Code given in the <include> section was |
287 |
def _check_code(self, solution, attempt, test_type, f, include_space): |
502
by stevenbird
www/apps/tutorialservice/__init__.py |
288 |
"""Compare solution code and attempt code using the
|
289 |
specified comparison function.
|
|
290 |
"""
|
|
1394.1.1
by William Grant
Add note about the _check_code string->function eval hack. |
291 |
# XXX: Horrible kludge. We get a string from the DB, but we need
|
292 |
# an actual callable object.
|
|
293 |
if type(f) in types.StringTypes: |
|
519
by stevenbird
Fixed bug in test framework. Code given in the <include> section was |
294 |
f = eval(str(f), include_space) |
1426
by William Grant
Add exact match support to the TestFramework backend. |
295 |
if test_type == 'match': |
296 |
return solution == attempt |
|
297 |
elif test_type == 'norm': |
|
502
by stevenbird
www/apps/tutorialservice/__init__.py |
298 |
return f(solution) == f(attempt) |
299 |
else: |
|
300 |
return f(solution, attempt) |
|
301 |
||
519
by stevenbird
Fixed bug in test framework. Code given in the <include> section was |
302 |
def run(self, solution_data, attempt_data, include_space): |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
303 |
"""Run the tests to compare the solution and attempt data
|
328
by mattgiuca
console: Renamed HTML element IDs to prefix "console_". |
304 |
Returns the empty string if the test passes, or else an error message.
|
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
305 |
"""
|
306 |
||
502
by stevenbird
www/apps/tutorialservice/__init__.py |
307 |
# check source code itself
|
308 |
(test_type, f) = self._code_test |
|
519
by stevenbird
Fixed bug in test framework. Code given in the <include> section was |
309 |
if not self._check_code(solution_data['code'], attempt_data['code'], test_type, f, include_space): |
502
by stevenbird
www/apps/tutorialservice/__init__.py |
310 |
return 'Unexpected code' |
311 |
||
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
312 |
# check function return value (None for scripts)
|
313 |
(test_type, f) = self._result_test |
|
507
by stevenbird
Extended code test framework to support tests on the code string, rather |
314 |
if not self._check_output(solution_data['result'], attempt_data['result'], test_type, f): |
502
by stevenbird
www/apps/tutorialservice/__init__.py |
315 |
return 'Unexpected function return value' |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
316 |
|
317 |
# check stdout
|
|
318 |
(test_type, f) = self._stdout_test |
|
507
by stevenbird
Extended code test framework to support tests on the code string, rather |
319 |
if not self._check_output(solution_data['stdout'], attempt_data['stdout'], test_type, f): |
502
by stevenbird
www/apps/tutorialservice/__init__.py |
320 |
return 'Unexpected output' |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
321 |
|
322 |
#check stderr
|
|
323 |
(test_type, f) = self._stderr_test |
|
507
by stevenbird
Extended code test framework to support tests on the code string, rather |
324 |
if not self._check_output(solution_data['stderr'], attempt_data['stderr'], test_type, f): |
502
by stevenbird
www/apps/tutorialservice/__init__.py |
325 |
return 'Unexpected error output' |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
326 |
|
299
by dilshan_a
Test framework now handles exceptions as valid outputs for scripts. |
327 |
#check exception
|
328 |
(test_type, f) = self._exception_test |
|
507
by stevenbird
Extended code test framework to support tests on the code string, rather |
329 |
if not self._check_output(solution_data['exception'], attempt_data['exception'], test_type, f): |
502
by stevenbird
www/apps/tutorialservice/__init__.py |
330 |
return 'Unexpected exception' |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
331 |
|
332 |
solution_files = solution_data['modified_files'] |
|
333 |
attempt_files = attempt_data['modified_files'] |
|
334 |
||
335 |
# check files indicated by test
|
|
336 |
for (filename, (test_type, f)) in self._file_tests.items(): |
|
337 |
if filename not in solution_files: |
|
306
by dilshan_a
Consolidated SolutionError and AttemptError into ScriptExecutionError. |
338 |
raise FileNotFoundError(filename) |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
339 |
elif filename not in attempt_files: |
340 |
return filename + ' not found' |
|
341 |
elif not self._check_output(solution_files[filename], attempt_files[filename], test_type, f): |
|
342 |
return filename + ' does not match' |
|
343 |
||
344 |
if self._default == 'ignore': |
|
345 |
return '' |
|
346 |
||
347 |
# check files found in solution, but not indicated by test
|
|
348 |
for filename in [f for f in solution_files if f not in self._file_tests]: |
|
349 |
if filename not in attempt_files: |
|
350 |
return filename + ' not found' |
|
495
by stevenbird
Fixes to permit content authors to produce nicer diagnostic responses as a result |
351 |
elif not self._check_output(solution_files[filename], attempt_files[filename], 'match', self.match): |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
352 |
return filename + ' does not match' |
353 |
||
354 |
# check if attempt has any extra files
|
|
355 |
for filename in [f for f in attempt_files if f not in solution_files]: |
|
356 |
return "Unexpected file found: " + filename |
|
357 |
||
358 |
# Everything passed with no problems
|
|
359 |
return '' |
|
360 |
||
361 |
class TestCase: |
|
362 |
"""
|
|
363 |
A set of tests with a common inputs
|
|
364 |
"""
|
|
1099.1.141
by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file. |
365 |
def __init__(self, console, suite): |
1394.1.2
by William Grant
Rewrite TestFramework class docstrings to indicate the corresponding DB classes. |
366 |
"""Create a testable TestCase from an IVLE database TestSuite.
|
367 |
||
368 |
The name mismatch is unfortunately not a typo. A database TestSuite
|
|
369 |
represents a TestFramework TestCase.
|
|
370 |
||
371 |
'console' should be an ivle.console.Console, in which to execute
|
|
372 |
the student code.
|
|
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
373 |
"""
|
1029
by dcoles
Tutorial Service: Ported the tutorial service to the console so that all |
374 |
self._console = console |
1099.1.141
by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file. |
375 |
self._name = suite.description |
376 |
||
377 |
function = suite.function |
|
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
378 |
if function == '': function = None |
379 |
self._function = function |
|
380 |
self._list_args = [] |
|
381 |
self._keyword_args = {} |
|
382 |
||
1099.1.141
by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file. |
383 |
self.set_stdin(suite.stdin) |
384 |
self._filespace = testfilespace.TestFilespace(None) |
|
385 |
self._global_space = {} |
|
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
386 |
self._parts = [] |
299
by dilshan_a
Test framework now handles exceptions as valid outputs for scripts. |
387 |
self._allowed_exceptions = set() |
1099.1.141
by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file. |
388 |
|
389 |
for var in suite.variables: |
|
390 |
if var.var_type == "file": |
|
391 |
self.add_file(var) |
|
392 |
elif var.var_type == "var": |
|
393 |
self.add_variable(var) |
|
394 |
elif var.var_type == "arg": |
|
1411
by William Grant
Unbreak positional arguments in testframework. |
395 |
self.add_arg(var) |
1099.1.141
by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file. |
396 |
elif var.var_type == "exception": |
397 |
self.add_exception(var) |
|
398 |
||
399 |
for test_case in suite.test_cases: |
|
400 |
self.add_part(TestCasePart(test_case)) |
|
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
401 |
|
402 |
def set_stdin(self, stdin): |
|
403 |
""" Set the given string as the stdin for this test case"""
|
|
1035
by dcoles
Tutorial: Added in stdin support for exercises (sets them up in console) |
404 |
# stdin must have a newline at the end for raw_input to work properly
|
1099.1.141
by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file. |
405 |
if stdin is not None: |
406 |
if stdin[-1:] != '\n': |
|
407 |
stdin += '\n' |
|
408 |
else: |
|
409 |
stdin = "" |
|
1036
by dcoles
TutorialService: Fix up bug in setting of stdin (would crash scripts) |
410 |
self._stdin = stdin |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
411 |
|
1099.1.141
by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file. |
412 |
def add_file(self, filevar): |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
413 |
""" Insert the given filename-data pair into the filespace for this test case"""
|
1029
by dcoles
Tutorial Service: Ported the tutorial service to the console so that all |
414 |
# TODO: Add the file to the console
|
1394.1.4
by William Grant
Reject attempts to run file tests through TestFramework -- the console backend does not yet have support. |
415 |
raise AssertionError( |
416 |
"Files not supported by the console - see bug #492437.") |
|
1099.1.141
by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file. |
417 |
self._filespace.add_file(filevar.var_name, "") |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
418 |
|
1099.1.141
by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file. |
419 |
def add_variable(self, var): |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
420 |
""" Add the given varibale-value pair to the initial global environment
|
1029
by dcoles
Tutorial Service: Ported the tutorial service to the console so that all |
421 |
for this test case. The value is the string repr() of an actual value.
|
422 |
Throw an exception if the value cannot be paresed.
|
|
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
423 |
"""
|
424 |
||
425 |
try: |
|
1099.1.141
by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file. |
426 |
self._global_space[var.var_name] = eval(var.var_value) |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
427 |
except: |
1099.1.141
by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file. |
428 |
raise TestCreationError("Invalid value for variable %s: %s" |
429 |
%(var.var_name, var.var_value)) |
|
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
430 |
|
1099.1.141
by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file. |
431 |
def add_arg(self, var): |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
432 |
""" Add a value to the argument list. This only applies when testing functions.
|
433 |
By default arguments are not named, but if they are, they become keyword arguments.
|
|
434 |
"""
|
|
435 |
try: |
|
1099.1.141
by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file. |
436 |
if var.var_name == None or var.var_name == '': |
437 |
self._list_args.append(eval(var.var_value)) |
|
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
438 |
else: |
1417
by William Grant
Ensure that TestFramework keyword argument names are strs, not unicodes. |
439 |
# XXX: keyword argument names must be strs, not unicode,
|
440 |
# but they are stored in the DB as unicodes for
|
|
441 |
# reasons that I cannot fathom.
|
|
442 |
var_name_str = str(var.var_name) |
|
443 |
self._keyword_args[var_name_str] = var.var_value |
|
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
444 |
except: |
1099.1.141
by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file. |
445 |
raise TestCreationError("Invalid value for function argument: %s" %var.var_value) |
299
by dilshan_a
Test framework now handles exceptions as valid outputs for scripts. |
446 |
|
1691.1.1
by David Coles
Fix exception varibles of TestSuites so exercise writers can specify allowed exceptions |
447 |
def add_exception(self, var): |
1099.1.141
by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file. |
448 |
self._allowed_exceptions.add(var.var_name) |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
449 |
|
450 |
def add_part(self, test_part): |
|
451 |
""" Add a TestPart to this test case"""
|
|
452 |
self._parts.append(test_part) |
|
453 |
||
454 |
def validate_functions(self, included_code): |
|
455 |
""" Validate all the functions in each part in this test case
|
|
456 |
This can only be done once all the include code has been specified.
|
|
457 |
"""
|
|
458 |
for part in self._parts: |
|
459 |
part.validate_functions(included_code) |
|
460 |
||
461 |
def get_name(self): |
|
462 |
""" Get the name of the test case """
|
|
463 |
return self._name |
|
464 |
||
519
by stevenbird
Fixed bug in test framework. Code given in the <include> section was |
465 |
def run(self, solution, attempt_code, include_space, stop_on_fail=True): |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
466 |
""" Run the solution and the attempt with the inputs specified for this test case.
|
467 |
Then pass the outputs to each test part and collate the results.
|
|
468 |
"""
|
|
469 |
case_dict = {} |
|
470 |
case_dict['name'] = self._name |
|
471 |
||
472 |
# Run solution
|
|
473 |
try: |
|
474 |
global_space_copy = copy.deepcopy(self._global_space) |
|
475 |
solution_data = self._execstring(solution, global_space_copy) |
|
1035
by dcoles
Tutorial: Added in stdin support for exercises (sets them up in console) |
476 |
self._console.stdin.truncate(0) |
477 |
self._console.stdin.write(self._stdin) |
|
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
478 |
|
479 |
# if we are just testing a function
|
|
480 |
if not self._function == None: |
|
1029
by dcoles
Tutorial Service: Ported the tutorial service to the console so that all |
481 |
if self._function not in solution_data['globals']: |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
482 |
raise FunctionNotFoundError(self._function) |
1029
by dcoles
Tutorial Service: Ported the tutorial service to the console so that all |
483 |
solution_data = self._run_function(self._function, |
484 |
self._list_args, self._keyword_args, solution) |
|
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
485 |
|
1099.1.141
by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file. |
486 |
except Exception, e: |
1434
by William Grant
Exceptions that occur in the provided solution code should be raised as TestErrors, so authors can see them. |
487 |
raise TestError(sys.exc_info()) |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
488 |
|
489 |
# Run student attempt
|
|
490 |
try: |
|
491 |
global_space_copy = copy.deepcopy(self._global_space) |
|
302
by dilshan_a
Updated test framework so that student's code is passed in as a string. |
492 |
attempt_data = self._execstring(attempt_code, global_space_copy) |
1035
by dcoles
Tutorial: Added in stdin support for exercises (sets them up in console) |
493 |
self._console.stdin.truncate(0) |
494 |
self._console.stdin.write(self._stdin) |
|
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
495 |
|
496 |
# if we are just testing a function
|
|
497 |
if not self._function == None: |
|
1029
by dcoles
Tutorial Service: Ported the tutorial service to the console so that all |
498 |
if self._function not in attempt_data['globals']: |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
499 |
raise FunctionNotFoundError(self._function) |
1029
by dcoles
Tutorial Service: Ported the tutorial service to the console so that all |
500 |
attempt_data = self._run_function(self._function, |
501 |
self._list_args, self._keyword_args, attempt_code) |
|
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
502 |
except: |
306
by dilshan_a
Consolidated SolutionError and AttemptError into ScriptExecutionError. |
503 |
case_dict['exception'] = ScriptExecutionError(sys.exc_info()).to_dict() |
304
by dilshan_a
Added documentation of output of TestSuite. |
504 |
case_dict['passed'] = False |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
505 |
return case_dict |
506 |
||
507 |
results = [] |
|
304
by dilshan_a
Added documentation of output of TestSuite. |
508 |
passed = True |
509 |
||
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
510 |
# generate results
|
511 |
for test_part in self._parts: |
|
306
by dilshan_a
Consolidated SolutionError and AttemptError into ScriptExecutionError. |
512 |
try: |
519
by stevenbird
Fixed bug in test framework. Code given in the <include> section was |
513 |
result = test_part.run(solution_data, attempt_data, include_space) |
306
by dilshan_a
Consolidated SolutionError and AttemptError into ScriptExecutionError. |
514 |
except: |
515 |
raise TestError(sys.exc_info()) |
|
516 |
||
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
517 |
result_dict = {} |
502
by stevenbird
www/apps/tutorialservice/__init__.py |
518 |
result_dict['description'] = test_part._pass_msg |
495
by stevenbird
Fixes to permit content authors to produce nicer diagnostic responses as a result |
519 |
result_dict['passed'] = (result == '') |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
520 |
if result_dict['passed'] == False: |
521 |
result_dict['error_message'] = result |
|
502
by stevenbird
www/apps/tutorialservice/__init__.py |
522 |
result_dict['description'] = test_part._fail_msg |
304
by dilshan_a
Added documentation of output of TestSuite. |
523 |
passed = False |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
524 |
|
525 |
results.append(result_dict) |
|
526 |
||
514
by stevenbird
More flexible control of test_case_parts via optional flag |
527 |
# Do we continue the test_parts after one of them has failed?
|
528 |
if not passed and stop_on_fail: |
|
529 |
break; |
|
530 |
||
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
531 |
case_dict['parts'] = results |
304
by dilshan_a
Added documentation of output of TestSuite. |
532 |
case_dict['passed'] = passed |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
533 |
|
534 |
return case_dict |
|
535 |
||
536 |
def _execfile(self, filename, global_space): |
|
537 |
""" Execute the file given by 'filename' in global_space, and return the outputs. """
|
|
538 |
self._initialise_global_space(global_space) |
|
716
by mattgiuca
Test Framework: Numerous bug fixes. |
539 |
data = self._run_function(lambda: execfile(filename, global_space), |
540 |
code = open(filename).read()) |
|
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
541 |
return data |
542 |
||
543 |
def _execstring(self, string, global_space): |
|
1029
by dcoles
Tutorial Service: Ported the tutorial service to the console so that all |
544 |
""" Execute the given string in global_space, and return the outputs.
|
545 |
"""
|
|
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
546 |
self._initialise_global_space(global_space) |
305
by dilshan_a
Neated up execution of strings in TestCase. |
547 |
|
1034
by dcoles
Console: Refactored the code to support supplying of stdin to the console. |
548 |
inspection = self._console.execute(string) |
1029
by dcoles
Tutorial Service: Ported the tutorial service to the console so that all |
549 |
|
550 |
exception_name = None |
|
551 |
if 'exception' in inspection: |
|
1397.1.4
by William Grant
Unbreak TestFramework's console exception handling. |
552 |
exception = inspection['exception'] |
1029
by dcoles
Tutorial Service: Ported the tutorial service to the console so that all |
553 |
exception_name = type(exception).__name__ |
1691.1.1
by David Coles
Fix exception varibles of TestSuites so exercise writers can specify allowed exceptions |
554 |
if exception_name not in self._allowed_exceptions: |
555 |
raise(exception) |
|
1029
by dcoles
Tutorial Service: Ported the tutorial service to the console so that all |
556 |
|
557 |
return {'code': string, |
|
558 |
'result': None, |
|
1034
by dcoles
Console: Refactored the code to support supplying of stdin to the console. |
559 |
'globals': self._console.globals(), |
1691.1.1
by David Coles
Fix exception varibles of TestSuites so exercise writers can specify allowed exceptions |
560 |
'exception': exception_name, |
1034
by dcoles
Console: Refactored the code to support supplying of stdin to the console. |
561 |
'stdout': self._console.stdout.read(), |
562 |
'stderr': self._console.stderr.read(), |
|
1029
by dcoles
Tutorial Service: Ported the tutorial service to the console so that all |
563 |
'modified_files': None} |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
564 |
|
565 |
def _initialise_global_space(self, global_space): |
|
566 |
""" Modify the provided global_space so that file, open and raw_input are redefined
|
|
567 |
to use our methods instead.
|
|
568 |
"""
|
|
1034
by dcoles
Console: Refactored the code to support supplying of stdin to the console. |
569 |
self._console.globals(global_space) |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
570 |
self._current_filespace_copy = self._filespace.copy() |
571 |
global_space['file'] = lambda filename, mode='r', bufsize=-1: self._current_filespace_copy.openfile(filename, mode) |
|
572 |
global_space['open'] = global_space['file'] |
|
573 |
global_space['raw_input'] = lambda x=None: raw_input() |
|
574 |
return global_space |
|
575 |
||
1029
by dcoles
Tutorial Service: Ported the tutorial service to the console so that all |
576 |
def _run_function(self, function, args, kwargs, code): |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
577 |
""" Run the provided function with the provided stdin, capturing stdout and stderr
|
578 |
and the return value.
|
|
579 |
Return all the output data.
|
|
716
by mattgiuca
Test Framework: Numerous bug fixes. |
580 |
code: The full text of the code, which needs to be stored as part of
|
581 |
the returned dictionary.
|
|
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
582 |
"""
|
1029
by dcoles
Tutorial Service: Ported the tutorial service to the console so that all |
583 |
s_args = map(repr, args) |
584 |
s_kwargs = dict(zip(kwargs.keys(), map(repr, kwargs.values()))) |
|
585 |
call = self._console.call(function, *s_args, **s_kwargs) |
|
586 |
||
299
by dilshan_a
Test framework now handles exceptions as valid outputs for scripts. |
587 |
exception_name = None |
1029
by dcoles
Tutorial Service: Ported the tutorial service to the console so that all |
588 |
if 'exception' in call: |
1416
by William Grant
Correctly handle exceptions when a console function is called by TestFramework. |
589 |
exception = call['exception']['except'] |
1029
by dcoles
Tutorial Service: Ported the tutorial service to the console so that all |
590 |
exception_name = type(exception).__name__ |
591 |
raise(exception) |
|
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
592 |
|
716
by mattgiuca
Test Framework: Numerous bug fixes. |
593 |
return {'code': code, |
1029
by dcoles
Tutorial Service: Ported the tutorial service to the console so that all |
594 |
'result': call['result'], |
299
by dilshan_a
Test framework now handles exceptions as valid outputs for scripts. |
595 |
'exception': exception_name, |
1036
by dcoles
TutorialService: Fix up bug in setting of stdin (would crash scripts) |
596 |
'stdout': self._console.stdout.read(), |
597 |
'stderr': self._console.stderr.read(), |
|
1029
by dcoles
Tutorial Service: Ported the tutorial service to the console so that all |
598 |
'modified_files': None} |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
599 |
|
600 |
class TestSuite: |
|
601 |
"""
|
|
513
by stevenbird
test/test_framework/*, exercises/sample/* |
602 |
The complete collection of test cases for a given exercise
|
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
603 |
"""
|
1099.1.141
by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file. |
604 |
def __init__(self, exercise, console): |
1394.1.2
by William Grant
Rewrite TestFramework class docstrings to indicate the corresponding DB classes. |
605 |
"""Create a testable TestSuite from an IVLE database Exercise.
|
606 |
||
607 |
This is not to be confused with the TestFramework object derived
|
|
608 |
from a database TestSuite, which is in fact a TestFramework TestCase.
|
|
609 |
||
610 |
'console' should be an ivle.console.Console, in which to execute
|
|
611 |
the student code.
|
|
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
612 |
"""
|
1099.1.141
by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file. |
613 |
self._solution = exercise.solution |
614 |
self._name = exercise.id |
|
615 |
self._exercise = exercise |
|
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
616 |
self._tests = [] |
1029
by dcoles
Tutorial Service: Ported the tutorial service to the console so that all |
617 |
self._console = console |
1099.1.141
by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file. |
618 |
self.add_include_code(exercise.include) |
619 |
||
620 |
for test_case in exercise.test_suites: |
|
621 |
new_case = TestCase(console, test_case) |
|
622 |
self.add_case(new_case) |
|
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
623 |
|
624 |
def has_solution(self): |
|
523
by stevenbird
Adding ReStructured Text preprocessing of exercise descriptions, |
625 |
" Returns true if a solution has been provided "
|
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
626 |
return self._solution != None |
627 |
||
628 |
def add_include_code(self, include_code = ''): |
|
629 |
""" Add include code that may be used by the test cases during
|
|
630 |
comparison of outputs.
|
|
631 |
"""
|
|
632 |
||
633 |
# if empty, make sure it can still be executed
|
|
1175
by William Grant
Treat a NULL exercise.include the same as an empty one. Don't crash. |
634 |
if include_code == "" or include_code is None: |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
635 |
include_code = "pass" |
1099.1.221
by Nick Chadwick
added in extra parts to the exercise edit view. Now almost all |
636 |
self._include_code = include_code |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
637 |
|
638 |
include_space = {} |
|
639 |
try: |
|
640 |
exec self._include_code in include_space |
|
641 |
except: |
|
1099.1.221
by Nick Chadwick
added in extra parts to the exercise edit view. Now almost all |
642 |
raise TestCreationError("-= Bad include code =-\n" + include_code) |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
643 |
|
644 |
self._include_space = include_space |
|
1099.1.150
by Nick Chadwick
Modified worksheets to properly link attempts to worksheets and |
645 |
|
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
646 |
def add_case(self, test_case): |
647 |
""" Add a TestCase, then validate all functions inside test case
|
|
648 |
now that the include code is known
|
|
649 |
"""
|
|
650 |
self._tests.append(test_case) |
|
651 |
test_case.validate_functions(self._include_space) |
|
652 |
||
502
by stevenbird
www/apps/tutorialservice/__init__.py |
653 |
def run_tests(self, attempt_code, stop_on_fail=False): |
1029
by dcoles
Tutorial Service: Ported the tutorial service to the console so that all |
654 |
" Run all test cases on the specified console and collate the results "
|
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
655 |
|
513
by stevenbird
test/test_framework/*, exercises/sample/* |
656 |
exercise_dict = {} |
657 |
exercise_dict['name'] = self._name |
|
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
658 |
|
659 |
test_case_results = [] |
|
309
by dilshan_a
Added a passed key to return value of problem suite. |
660 |
passed = True |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
661 |
for test in self._tests: |
519
by stevenbird
Fixed bug in test framework. Code given in the <include> section was |
662 |
result_dict = test.run(self._solution, attempt_code, self._include_space) |
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
663 |
if 'exception' in result_dict and result_dict['exception']['critical']: |
664 |
# critical error occured, running more cases is useless
|
|
665 |
# FunctionNotFound, Syntax, Indentation
|
|
513
by stevenbird
test/test_framework/*, exercises/sample/* |
666 |
exercise_dict['critical_error'] = result_dict['exception'] |
667 |
exercise_dict['passed'] = False |
|
668 |
return exercise_dict |
|
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
669 |
|
670 |
test_case_results.append(result_dict) |
|
304
by dilshan_a
Added documentation of output of TestSuite. |
671 |
|
309
by dilshan_a
Added a passed key to return value of problem suite. |
672 |
if not result_dict['passed']: |
673 |
passed = False |
|
674 |
if stop_on_fail: |
|
675 |
break
|
|
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
676 |
|
513
by stevenbird
test/test_framework/*, exercises/sample/* |
677 |
exercise_dict['cases'] = test_case_results |
678 |
exercise_dict['passed'] = passed |
|
679 |
return exercise_dict |
|
294
by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for |
680 |
|
681 |
def get_name(self): |
|
1099.1.141
by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file. |
682 |
return self._names |