Package qm :: Package test :: Package classes :: Module dejagnu_test
[hide private]
[frames] | no frames]

Source Code for Module qm.test.classes.dejagnu_test

  1  ######################################################################## 
  2  # 
  3  # File:   dejagnu_test.py 
  4  # Author: Mark Mitchell 
  5  # Date:   04/16/2003 
  6  # 
  7  # Contents: 
  8  #   DejaGNUTest 
  9  # 
 10  # Copyright (c) 2003 by CodeSourcery, LLC.  All rights reserved.  
 11  # 
 12  # For license terms see the file COPYING. 
 13  # 
 14  ######################################################################## 
 15   
 16  ######################################################################## 
 17  # Imports 
 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  # Classes 
 30  ######################################################################## 
 31   
32 -class DejaGNUTest(Test, DejaGNUBase):
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
89 - class BuildExecutable(RedirectedExecutable):
90 """A 'BuildExecutable' runs on the build machine. 91 92 Classes derived from 'DejaGNUTest' may provide derived 93 versions of this class.""" 94
95 - def _StderrPipe(self):
96 97 # Combine stdout/stderr into a single stream. 98 return None
99 100
101 - def _GetTargetEnvironment(self, context):
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
114 - def _RunBuildExecutable(self, context, result, file, args = [], 115 dir = None):
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
142 - def _RunTargetExecutable(self, context, result, file):
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 # Figure out whether the execution was successful. 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
167 - def _RecordDejaGNUOutcome(self, result, outcome, message, 168 expectation = None):
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 # If the test was expected to fail, transform PASS or FAIL 181 # into XPASS or XFAIL, respectively. 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 # Create an annotation corresponding to the DejaGNU outcome. 189 key = "%s%d" % (self.RESULT_PREFIX, self.__next_result) 190 self.__next_result += 1 191 result[key] = outcome + ": " + message 192 # If the test was passing until now, give it a new outcome. 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
201 - def _Unresolved(self, result, message):
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
211 - def _Error(self, message):
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
224 - def _GetSourcePath(self):
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
233 - def _GetBuild(self, context):
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
244 - def _GetTarget(self, context):
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
256 - def _IsNative(self, context):
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
267 - def _SetUp(self, context):
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 # The next DejaGNU result will be the first. 277 self.__next_result = 1
278 279
280 - def _ParseTclWords(self, s, variables = {}):
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 # There are no words yet. 301 words = [] 302 # There is no current word. 303 word = None 304 # We are not processing a double-quoted string. 305 in_double_quoted_string = 0 306 # Nor are we processing a brace-quoted string. 307 in_brace_quoted_string = 0 308 # Iterate through all of the characters in s. 309 n = 0 310 while n < len(s): 311 # See what the next character is. 312 c = s[n] 313 # A "$" indicates variable substitution. 314 if c == "$" and not in_brace_quoted_string: 315 k = n + 1 316 if s[k] == "{": 317 # The name of the variable is enclosed in braces. 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 # The following letters, numbers, and underscores make 325 # up the variable name. 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 # A "[" indicates command substitution. 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 # A double-quote indicates the beginning of a double-quoted 347 # string. 348 elif c == '"' and not in_brace_quoted_string: 349 # We are now entering a new double-quoted string, or 350 # leaving the old one. 351 in_double_quoted_string = not in_double_quoted_string 352 # Skip the quote. 353 n += 1 354 # The quote starts the word. 355 if word is None: 356 word = "" 357 # A "{" indicates the beginning of a brace-quoted string. 358 elif c == '{' and not in_double_quoted_string: 359 # If that's not the opening quote, add it to the 360 # string. 361 if in_brace_quoted_string: 362 if word is not None: 363 word = word + "{" 364 else: 365 word = "{" 366 # The quote starts the word. 367 if word is None: 368 word = "" 369 # We are entering a brace-quoted string. 370 in_brace_quoted_string += 1 371 # Skip the brace. 372 n += 1 373 elif c == '}' and in_brace_quoted_string: 374 # Leave the brace quoted string. 375 in_brace_quoted_string -= 1 376 # Skip the brace. 377 n += 1 378 # If that's not the closing quote, add it to the 379 # string. 380 if in_brace_quoted_string: 381 if word is not None: 382 word = word + "}" 383 else: 384 word = "}" 385 # A backslash-newline is translated into a space. 386 elif c == '\\' and len(s) > 1 and s[1] == '\n': 387 # Skip the backslash and the newline. 388 n += 2 389 # Now, skip tabs and spaces. 390 while n < len(s) and (s[n] == ' ' or s[n] == '\t'): 391 n += 1 392 # Now prepend one space. 393 n -= 1 394 s[n] = " " 395 # A backslash indicates backslash-substitution. 396 elif c == '\\' and not in_brace_quoted_string: 397 # There should be a character following the backslash. 398 if len(s) == 1: 399 raise QMException, "Invalid Tcl string." 400 # Skip the backslash. 401 n += 1 402 # See what the next character is. 403 c = s[n] 404 # If it's a control character, use the same character 405 # in Python. 406 if c in ["a", "b", "f", "n", "r", "t", "v"]: 407 c = eval('"\%s"' % c) 408 n += 1 409 # "\x" indicates a hex literal. 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 # "\d" where "d" is a digit indicates an octal literal. 418 elif c.isdigit(): 419 raise QMException, "Unsupported Tcl escape." 420 # Any other character just indicates the character 421 # itself. 422 else: 423 n += 1 424 # Add it to the current word. 425 if word is not None: 426 word = word + c 427 else: 428 word = c 429 # A space or tab indicates a word separator. 430 elif ((c == ' ' or c == '\t') 431 and not in_double_quoted_string 432 and not in_brace_quoted_string): 433 # Add the current word to the list of words. 434 if word is not None: 435 words.append(word) 436 # Skip over the space. 437 n += 1 438 # Keep skipping while the leading character of s is 439 # a space or tab. 440 while n < len(s) and (s[n] == ' ' or s[n] == '\t'): 441 n += 1 442 # Start the next word. 443 word = None 444 # Any other character is just added to the current word. 445 else: 446 if word is not None: 447 word = word + c 448 else: 449 word = c 450 n += 1 451 452 # If we were working on a word when we reached the end of 453 # the string, add it to the list. 454 if word is not None: 455 words.append(word) 456 457 return words
458