1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 from dejagnu_base import DejaGNUBase
21 import os
22 import qm
23 from qm.common import QMException
24 from qm.executable import RedirectedExecutable
25 from qm.test.test import Test
26 from qm.test.result import Result
27
28
29
30
31
33 """A 'DejaGNUTest' emulates a DejaGNU test.
34
35 See 'framework.exp' in the DejaGNU distribution for more
36 information."""
37
38 arguments = [
39 qm.fields.AttachmentField(
40 name="source_file",
41 title="Source File",
42 description="""The source file."""),
43 ]
44
45 PASS = "PASS"
46 FAIL = "FAIL"
47 KFAIL = "KFAIL"
48 KPASS = "KPASS"
49 XPASS = "XPASS"
50 XFAIL = "XFAIL"
51 WARNING = "WARNING"
52 ERROR = "ERROR"
53 UNTESTED = "UNTESTED"
54 UNRESOLVED = "UNRESOLVED"
55 UNSUPPORTED = "UNSUPPORTED"
56
57 dejagnu_outcomes = (
58 PASS, FAIL, XPASS, XFAIL, WARNING, ERROR, UNTESTED,
59 UNRESOLVED, UNSUPPORTED
60 )
61 """The DejaGNU test outcomes."""
62
63 outcome_map = {
64 PASS : Result.PASS,
65 FAIL : Result.FAIL,
66 KFAIL : Result.FAIL,
67 KPASS : Result.PASS,
68 XPASS : Result.PASS,
69 XFAIL : Result.FAIL,
70 WARNING : Result.PASS,
71 ERROR : Result.ERROR,
72 UNTESTED : Result.UNTESTED,
73 UNRESOLVED : Result.UNTESTED,
74 UNSUPPORTED : Result.UNTESTED
75 }
76 """A map from DejaGNU outcomes to QMTest outcomes."""
77
78 executable_timeout = 300
79 """The number of seconds a program is permitted to run on the target."""
80
81 RESULT_PREFIX = "DejaGNUTest.result_"
82 """The prefix for DejaGNU result annotations.
83
84 All results that would be generated by DejaGNU are inserted into
85 the QMTest result as annotations beginning with this prefix. The
86 prefix is followed by an 1-indexed integer; earlier results are
87 inserted with lower numbers."""
88
90 """A 'BuildExecutable' runs on the build machine.
91
92 Classes derived from 'DejaGNUTest' may provide derived
93 versions of this class."""
94
99
100
102 """Return additional environment variables to set on the target.
103
104 'context' -- The 'Context' in which this test is running.
105
106 returns -- A map from strings (environment variable names) to
107 strings (values for those variables). These new variables are
108 added to the environment when a program executes on the
109 target."""
110
111 return {}
112
113
116 """Run 'file' on the target.
117
118 'context' -- The 'Context' in which this test is running.
119
120 'result' -- The 'Result' of this test.
121
122 'file' -- The path to the executable file.
123
124 'args' -- The arguments to the 'file'.
125
126 'dir' -- The directory in which the program should execute.
127
128 returns -- A pair '(status, output)'. The 'status' is the
129 exit status from the command; the 'output' is the combined
130 results of the standard output and standard error streams."""
131
132 executable = self.BuildExecutable(self.executable_timeout)
133 command = [file] + args
134 index = self._RecordCommand(result, command)
135 status = executable.Run(command, None, dir)
136 output = executable.stdout
137 self._RecordCommandOutput(result, index, status, output)
138
139 return status, output
140
141
143 """Run 'file' on the target.
144
145 'context' -- The 'Context' in which this test is running.
146
147 'result' -- The 'Result' of this test.
148
149 'file' -- The path to the executable file.
150
151 returns -- One of the 'dejagnu_outcomes'."""
152
153 host = context['CompilerTable.target']
154 index = self._RecordCommand(result, [file])
155 environment = self._GetTargetEnvironment(context)
156 status, output = host.Run(file, [], environment)
157 self._RecordCommandOutput(result, index, status, output)
158
159 if os.WIFEXITED(status) and os.WEXITSTATUS(status) == 0:
160 outcome = self.PASS
161 else:
162 outcome = self.FAIL
163
164 return outcome
165
166
169 """Record a DejaGNU outcome.
170
171 'result' -- A 'Result' object.
172
173 'outcome' -- One of the 'dejagnu_outcomes'.
174
175 'message' -- A string, explaining the outcome.
176
177 'expectation' -- If not 'None, the DejaGNU outcome that was
178 expected."""
179
180
181
182 if expectation == self.FAIL:
183 if outcome == self.PASS:
184 outcome = self.XPASS
185 elif outcome == self.FAIL:
186 outcome = self.XFAIL
187
188
189 key = "%s%d" % (self.RESULT_PREFIX, self.__next_result)
190 self.__next_result += 1
191 result[key] = outcome + ": " + message
192
193 new_outcome = self.outcome_map[outcome]
194 if (new_outcome
195 and new_outcome != Result.PASS
196 and result.GetOutcome() == Result.PASS):
197 result.SetOutcome(new_outcome)
198 result[Result.CAUSE] = message
199
200
202 """Record an 'unresolved' DejaGNU outcome.
203
204 This function is identical to 'RecordDejaGNUOutcome', except
205 that the 'outcome' is always 'UNRESOLVED'."""
206
207 self._RecordDejaGNUOutcome(result, self.UNRESOLVED, message)
208
209
210
212 """Raise an exception indicating an error in the test.
213
214 'message' -- A description of the problem.
215
216 This function is used when the original Tcl code in DejaGNU
217 would have used the Tcl 'error' primitive. These situations
218 indicate problems with the test itself, such as incorrect
219 usage of special test commands."""
220
221 raise DejaGNUError, message
222
223
225 """Return the path to the primary source file.
226
227 returns -- A string giving the path to the primary source
228 file."""
229
230 return self.source_file.GetDataFile()
231
232
234 """Return the GNU triplet corresponding to the build machine.
235
236 'context' -- The 'Context' in which the test is running.
237
238 returns -- The GNU triplet corresponding to the target
239 machine, i.e,. the machine on which the compiler will run."""
240
241 return context.get("DejaGNUTest.build") or self._GetTarget(context)
242
243
245 """Return the GNU triplet corresponding to the target machine.
246
247 'context' -- The 'Context' in which the test is running.
248
249 returns -- The GNU triplet corresponding to the target
250 machine, i.e,. the machine on which the programs generated by
251 the compiler will run."""
252
253 return context["DejaGNUTest.target"]
254
255
257 """Returns true if the build and target machines are the same.
258
259 'context' -- The 'Context' in which this test is running.
260
261 returns -- True if this test is runing "natively", i.e., if
262 the build and target machines are the same."""
263
264 return self._GetTarget(context) == self._GetBuild(context)
265
266
268 """Prepare to run a test.
269
270 'context' -- The 'Context' in which this test will run.
271
272 This method may be overridden by derived classes, but they
273 must call this version."""
274
275 super(DejaGNUTest, self)._SetUp(context)
276
277 self.__next_result = 1
278
279
281 """Separate 's' into words, in the same way that Tcl would.
282
283 's' -- A string.
284
285 'variables' -- A map from variable names to values. If Tcl
286 variable substitutions are encountered in 's', the
287 corresponding value from 'variables' will be used.
288
289 returns -- A sequence of strings, each of which is a Tcl
290 word.
291
292 Command substitution is not supported and results in an
293 exceptions. Invalid inputs (like the string consisting of a
294 single quote) also result in exceptions.
295
296 See 'Tcl and the Tk Toolkit', by John K. Ousterhout, copyright
297 1994 by Addison-Wesley Publishing Company, Inc. for details
298 about the syntax of Tcl."""
299
300
301 words = []
302
303 word = None
304
305 in_double_quoted_string = 0
306
307 in_brace_quoted_string = 0
308
309 n = 0
310 while n < len(s):
311
312 c = s[n]
313
314 if c == "$" and not in_brace_quoted_string:
315 k = n + 1
316 if s[k] == "{":
317
318 start = k + 1
319 finish = s.index("}", start)
320 n = finish + 1
321 var = s[start:finish]
322 v = variables[var]
323 else:
324
325
326 start = k
327 while (k < len(s)
328 and (s[k].isalnum() or s[k] == "_")):
329 k += 1
330 n = k
331 finish = k
332 if start < finish:
333 var = s[start:finish]
334 v = variables[var]
335 else:
336 v = "$"
337 if word is None:
338 word = v
339 else:
340 word += v
341 continue
342
343 elif (c == "[" and not in_brace_quoted_string
344 and n < len(s) + 1 and s[n + 1] != "]"):
345 raise QMException, "Tcl command substitution is unsupported."
346
347
348 elif c == '"' and not in_brace_quoted_string:
349
350
351 in_double_quoted_string = not in_double_quoted_string
352
353 n += 1
354
355 if word is None:
356 word = ""
357
358 elif c == '{' and not in_double_quoted_string:
359
360
361 if in_brace_quoted_string:
362 if word is not None:
363 word = word + "{"
364 else:
365 word = "{"
366
367 if word is None:
368 word = ""
369
370 in_brace_quoted_string += 1
371
372 n += 1
373 elif c == '}' and in_brace_quoted_string:
374
375 in_brace_quoted_string -= 1
376
377 n += 1
378
379
380 if in_brace_quoted_string:
381 if word is not None:
382 word = word + "}"
383 else:
384 word = "}"
385
386 elif c == '\\' and len(s) > 1 and s[1] == '\n':
387
388 n += 2
389
390 while n < len(s) and (s[n] == ' ' or s[n] == '\t'):
391 n += 1
392
393 n -= 1
394 s[n] = " "
395
396 elif c == '\\' and not in_brace_quoted_string:
397
398 if len(s) == 1:
399 raise QMException, "Invalid Tcl string."
400
401 n += 1
402
403 c = s[n]
404
405
406 if c in ["a", "b", "f", "n", "r", "t", "v"]:
407 c = eval('"\%s"' % c)
408 n += 1
409
410 elif c == "x":
411 if (n < len(s)
412 and s[n + 1] in ["0", "1", "2", "3", "4", "5",
413 "6", "7", "8", "9", "a", "b",
414 "c", "d", "e", "f"]):
415 raise QMException, "Unsupported Tcl escape."
416 n += 1
417
418 elif c.isdigit():
419 raise QMException, "Unsupported Tcl escape."
420
421
422 else:
423 n += 1
424
425 if word is not None:
426 word = word + c
427 else:
428 word = c
429
430 elif ((c == ' ' or c == '\t')
431 and not in_double_quoted_string
432 and not in_brace_quoted_string):
433
434 if word is not None:
435 words.append(word)
436
437 n += 1
438
439
440 while n < len(s) and (s[n] == ' ' or s[n] == '\t'):
441 n += 1
442
443 word = None
444
445 else:
446 if word is not None:
447 word = word + c
448 else:
449 word = c
450 n += 1
451
452
453
454 if word is not None:
455 words.append(word)
456
457 return words
458