1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 """Test classes for tests written in Python."""
17
18
19
20
21
22 import qm
23 import qm.fields
24 import qm.test.base
25 from qm.test.result import *
26 from qm.test.test import *
27 import string
28 import sys
29 import types
30
31
32
33
34
36 """Check that a Python expression evaluates to true.
37
38 An 'ExecTest' test consists of Python source code together with
39 an (optional) Python expression. First the Python code is
40 executed. If it throws an (uncaught) exception the test fails.
41 If the optional expression is present, it is then evaluated. If it
42 evaluates to false, the test fails. Otherwise, the test passes."""
43
44 arguments = [
45 qm.fields.TextField(
46 name="source",
47 title="Python Source Code",
48 description="""The source code.
49
50 This code may contain class definitions, function
51 definitions, statements, and so forth. If this code
52 throws an uncaught exception, the test will fail.""",
53 verbatim="true",
54 multiline="true",
55 default_value="pass"
56 ),
57
58 qm.fields.TextField(
59 name="expression",
60 title="Python Expression",
61 description="""The expression to evaluate.
62
63 If the expression evaluates to true, the test will pass,
64 unless the source code above throws an uncaught exception.
65
66 If this field is left blank, it is treated as an expression
67 that is always true.""",
68 verbatim="true",
69 multiline="true",
70 default_value="1"
71 )
72 ]
73
74
75 - def Run(self, context, result):
76
77
78
79
80 if not self.source:
81 self.source = "\n"
82 elif self.source[-1] != "\n":
83 self.source += "\n"
84 global_namespace, local_namespace = make_namespaces(context)
85
86 try:
87 exec self.source in global_namespace, local_namespace
88 except:
89
90
91 result.NoteException(cause="Exception executing source.")
92 else:
93
94
95 if self.expression is not None:
96
97 try:
98 value = eval(self.expression,
99 global_namespace, local_namespace)
100 except:
101
102
103 result.NoteException(cause=
104 "Exception evaluating expression.")
105 else:
106
107
108 if not value:
109 result.Fail("Expression evaluates to false.",
110 { "ExecTest.expr" : self.expression,
111 "ExecTest.value" : repr(value) })
112 else:
113
114
115 pass
116
117
119 """Base class for tests of exceptions."""
120
121 arguments = [
122 qm.fields.TextField(
123 name="source",
124 title="Python Source Code",
125 description="""The source code.
126
127 This code may contain class definitions, function
128 definitions, statements, and so forth.""",
129 verbatim="true",
130 multiline="true",
131 default_value="pass"
132 ),
133
134 qm.fields.TextField(
135 name="exception_argument",
136 title="Exception Argument",
137 description="""The expected value of the exception.
138
139 This value is a Python expression which should evaluate
140 to the same value as the exception raised.
141
142 If this field is left blank, the value of the exception is
143 ignored.""",
144 default_value=""
145 )
146 ]
147
148
149 - def Run(self, context, result):
150
151
152 if string.strip(self.exception_argument) != "":
153 self.exception_argument = eval(self.exception_argument, {}, {})
154 self.has_exception_argument = 1
155 else:
156 self.has_exception_argument = 0
157
158 global_namespace, local_namespace = make_namespaces(context)
159 try:
160
161 exec self.source in global_namespace, local_namespace
162 except:
163 exc_info = sys.exc_info()
164
165 self.CheckArgument(exc_info, result)
166 if result.GetOutcome() != Result.PASS:
167 return
168
169 self.MakeResult(exc_info, result)
170 else:
171
172 result.Fail(qm.message("test did not raise"))
173
174
176 """Check that the exception argument matches expectations.
177
178 'result' -- The result object for this test."""
179
180
181 if self.has_exception_argument:
182
183 argument = exc_info[1]
184 if cmp(argument, self.exception_argument):
185 cause = qm.message("test raised wrong argument")
186 result.Fail(cause,
187 { "BaseExceptionTest.type" :
188 str(exc_info[0]),
189 "BaseExceptionTest.argument" :
190 repr(argument) })
191
192
194 """Check the exception in 'exc_info' and construct the result.
195
196 'result' -- The result object for this test."""
197
198 pass
199
200
201
203 """Check that the specified Python code raises an exception.
204
205 An 'ExceptionTest' checks that the specified Python code raises a
206 particular exception. The test passes if the exception is an
207 instance of the expected class and (optionally) if its value matches
208 the expected value. If the code fails to raise an exception, the
209 test fails."""
210
211 arguments = [
212 qm.fields.TextField(
213 name="exception_class",
214 title="Exception Class",
215 description="""The expected type of the exception.
216
217 This value is the name of a Python class. If the
218 exception raised is not an instance of this class, the
219 test fails.""",
220 default_value="Exception"
221 )
222 ]
223
224
226
227 if not type(exc_info[0]) in [types.ClassType, type]:
228 result.Fail(qm.message("test raised non-object",
229 exc_type=str(type(exc_info[0]))))
230
231 exception_class_name = exc_info[0].__name__
232 if exception_class_name != self.exception_class:
233 cause = qm.message("test raised wrong class",
234 class_name=exception_class_name)
235 result.Fail(cause=cause)
236
237
239 """Check that the exception argument matches expectations.
240
241 'result' -- The result object for this test."""
242
243
244 if self.has_exception_argument:
245
246 try:
247 argument = exc_info[1].args
248 except:
249
250
251
252 result.Fail("Exception object does not provide access "
253 "to arguments provided to 'raise'",
254 { "ExceptionTest.type" : str(exc_info[0]) })
255 return
256
257
258 expected_argument = self.exception_argument
259
260
261
262 if expected_argument is None:
263 expected_argument = ()
264
265 elif type(expected_argument) is types.TupleType:
266 pass
267
268 else:
269 expected_argument = (expected_argument, )
270
271
272 if cmp(expected_argument, argument) != 0:
273
274 cause = qm.message("test raised wrong argument")
275 result.Fail(cause,
276 { "ExceptionTest.type" : str(exc_info[0]),
277 "ExceptionTest.argument" : repr(argument) })
278
279
280
282 """Check that the specified Python code raises a string exception.
283
284 A 'StringExceptionTest' checks that the specified code throws
285 an exception. The exception must be a string and must have
286 the expected value."""
287
288 arguments = [
289 qm.fields.TextField(
290 name="exception_text",
291 title="Exception Text",
292 description="The expected exception string.",
293 default_value="exception"
294 )
295 ]
296
297
299
300 if not type(exc_info[0]) is types.StringType:
301 result.Fail(qm.message("test raised non-string",
302 exc_type=str(type(exc_info[0]))))
303
304 if exc_info[0] != self.exception_text:
305 result.Fail(qm.message("test raised wrong string",
306 text=exc_info[0]))
307
308
309
310
311
312
313
315 """Construct namespaces for eval/exec of Python test code.
316
317 'context' -- The test context.
318
319 returns -- A pair '(global_namespace, local_namespace)' of maps."""
320
321
322 global_namespace = {
323 "context": context,
324 }
325
326 local_namespace = {
327 }
328
329
330
331
332
333
334
335
336 return global_namespace, global_namespace
337
338
339
340
341
342
343
344
345