2 # -*- Mode: Python; indent-tabs-mode: nil; tab-width: 4 -*-
3 # vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5)
5 # ***** BEGIN LICENSE BLOCK *****
6 # Version: MPL 1.1/GPL 2.0/LGPL 2.1
8 # The contents of this file are subject to the Mozilla Public License Version
9 # 1.1 (the "License"); you may not use this file except in compliance with
10 # the License. You may obtain a copy of the License at
11 # http://www.mozilla.org/MPL/
13 # Software distributed under the License is distributed on an "AS IS" basis,
14 # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
15 # for the specific language governing rights and limitations under the
18 # The Original Code is [Open Source Virtual Machine.].
20 # The Initial Developer of the Original Code is
21 # Adobe System Incorporated.
22 # Portions created by the Initial Developer are Copyright (C) 2005-2009
23 # the Initial Developer. All Rights Reserved.
26 # christine@netscape.com
29 # Alternatively, the contents of this file may be used under the terms of
30 # either the GNU General Public License Version 2 or later (the "GPL"), or
31 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
32 # in which case the provisions of the GPL or the LGPL are applicable instead
33 # of those above. If you wish to allow use of your version of this file only
34 # under the terms of either the GPL or the LGPL, and not to allow others to
35 # use your version of this file under the terms of the MPL, indicate your
36 # decision by deleting the provisions above and replace them with the notice
37 # and other provisions required by the GPL or the LGPL. If you do not delete
38 # the provisions above, a recipient may use your version of this file under
39 # the terms of any one of the MPL, the GPL or the LGPL.
41 # ***** END LICENSE BLOCK ***** */
44 import os
, sys
, getopt
, datetime
, pipes
, glob
, itertools
, tempfile
, string
45 import re
, platform
, threading
, time
47 from os
import getcwd
,environ
,walk
48 from datetime
import datetime
50 from sys
import argv
, exit
51 from getopt
import getopt
52 from itertools
import count
55 # add parent dir to python module search path
57 from util
.runtestBase
import RuntestBase
58 # runtestUtils must be imported after "from os.path import *" as walk is overridden
59 from util
.runtestUtils
import *
61 class AcceptanceRuntest(RuntestBase
):
63 escbin
= '../../esc/bin/'
64 androidthreads
= False
67 swfversions
= [9,10,11,12]
70 # Set threads to # of available cpus/cores
71 self
.threads
= detectCPUs()
72 RuntestBase
.__init
__(self
)
74 def setEnvironVars(self
):
75 RuntestBase
.setEnvironVars(self
)
76 if 'ESCBIN' in environ
:
77 self
.escbin
= environ
['ESCBIN'].strip()
80 RuntestBase
.usage(self
, c
)
81 print(' --esc run esc instead of avm')
82 print(' --escbin location of esc/bin directory - defaults to ../../esc/bin')
83 print(' --eval use run-time compiler')
84 print(' --ext set the testfile extension (defaults to .as)')
85 print(' --ats generate ats swfs instead of running tests')
86 print(' --atsdir base output directory for ats swfs - defaults to ATS_SWFS')
87 print(' --threads number of threads to run (default=# of cpu/cores), set to 1 to have tests finish sequentially')
88 print(' --androidthreads assign a thread for each android device connected.')
89 print(' --verify run a verify pass instead of running abcs')
90 print(' --verifyonly run a -Dverifyonly pass: only checks test exitcode')
91 print(' --remoteip IP/DNS address of the machine to run the tests on.')
92 print(' --remoteuser user name to use to connect to the remote machine.')
96 RuntestBase
.setOptions(self
)
97 self
.longOptions
.extend(['ext=','esc','escbin=','eval','threads=','ats',
98 'atsdir=','verify','verifyonly','androidthreads'])
100 def parseOptions(self
):
101 opts
= RuntestBase
.parseOptions(self
)
105 elif o
in ('--esc',):
107 elif o
in ('--escbin',):
109 elif o
in ('--eval',):
111 elif o
in ('--androidthreads',):
112 self
.androidthreads
=True
114 elif o
in ('--threads',):
118 print('Incorrect threads value: %s\n' % v
)
120 elif o
in ('--ats',):
121 self
.genAtsSwfs
= True
122 self
.rebuildtests
= True
123 # Need to run single threaded since we create a temp file for
124 # every test and this file can collide when using multiple threads
126 elif o
in ('--atsdir',):
128 elif o
in ('--verify',):
130 self
.vmargs
= '-Dverifyall -Dverbose=verify'
131 elif o
in ('--verifyonly',):
132 self
.verifyonly
= True
133 if '-Dverifyonly' not in self
.vmargs
:
134 self
.vmargs
+= ' -Dverifyonly'
135 elif o
in ('--remoteip',):
137 elif o
in ('--remoteuser',):
141 self
.setEnvironVars()
142 self
.loadPropertiesFile()
148 self
.determineConfig()
149 if self
.verifyonly
and 'debugger' not in self
.vmtype
:
150 exit('You must be running a debugger build in order to use the -Dverifyonly option')
151 if self
.rebuildtests
==False and (re
.search('arm-winmobile-emulator',self
.config
)!=None or self
.osName
=='winmobile'):
152 if re
.search('^arm-winmobile-emulator',self
.config
)==None:
153 print('ERROR: to use windows mobile build set --config arm-winmobile-emulator-tvm-release or install cygwin utility /usr/bin/file.exe')
155 self
.setupCEEmulators()
156 if self
.htmlOutput
and not self
.rebuildtests
:
157 self
.createOutputFile()
158 # extension lists must be tuples
159 self
.otherTestExtensions
= (self
.abcasmExt
,)
160 self
.executableExtensions
= (self
.abcOnlyExt
,)
161 # test configuration is contained in two files: failconfig & testconfig)
162 self
.settings
= self
.parseTestConfig(self
.testconfig
) & self
.parsTestConfig(self
.failconfig
)
163 self
.tests
= self
.getTestsList(self
.args
)
164 # Load root .asc_args and .java_args files
165 self
.parseRootConfigFiles()
166 self
.preProcessTests()
167 if self
.rebuildtests
:
170 self
.runTests(self
.tests
)
173 def preProcessTests(self
):
174 RuntestBase
.preProcessTests(self
)
175 # Are we running esc - depends on a valid avm
177 self
.runSource
= True
178 # generate the executable cmd for esc
179 #escAbcs = [f for f in os.listdir(self.escbin) if f.endswith('.abc')] #not all abcs are used for esc
180 escAbcs
= ['debug','util','bytes-tamarin','util-tamarin','lex-char','lex-token',
181 'lex-scan','ast','ast-decode','parse','asm','abc','emit','cogen',
182 'cogen-stmt','cogen-expr','esc-core','eval-support','esc-env','main']
183 if not self
.escbin
.endswith('/'):
186 self
.avm
+= ' %s%s.es.abc' % (self
.escbin
, f
)
188 self
.avm
+= ' %s../test/spidermonkey-prefix.es' % self
.escbin
#needed to run shell harness
189 if self
.androidthreads
:
190 p
=subprocess
.Popen('adb devices',shell
=True,stdout
=subprocess
.PIPE
,stderr
=subprocess
.PIPE
)
191 (out
,err
)=p
.communicate()
192 for line
in out
.split('\n'):
194 if len(items
)==2 and items
[1]=='device':
195 for i
in range(self
.threads
):
196 self
.androiddevices
.append(items
[0])
197 if len(self
.androiddevices
)==0:
198 print("error: adb did not detect any attached devices")
199 print("adb devices stdout: %s stderr: %s" % (out
,err
))
201 print("detected %d android devices" % (len(self
.androiddevices
)/self
.threads
))
202 self
.threads
=len(self
.androiddevices
)
204 def runTestPrep(self
, testAndNum
):
206 testnum
= testAndNum
[1]
207 outputCalls
= [] #queue all output calls so that output is written in a block
211 dir = os
.path
.split(ast
)[0]
212 root
,ext
= splitext(ast
)
213 if self
.runSource
or self
.eval or ext
in self
.executableExtensions
:
216 testName
= root
+ '.abc'
218 includes
= self
.includes
#list
220 settings
= self
.getLocalSettings(root
)
222 # skip tests that can't be verified
223 if self
.verify
and '.*' in settings
and 'verify_skip' in settings
['.*']:
224 outputCalls
.append((self
.js_print
,('%d running %s' % (testnum
, ast
), '<b>', '</b><br/>')));
225 outputCalls
.append((self
.js_print
,(' skipping... reason: %s' % settings
['.*']['verify_skip'],)))
229 # skip entire test if specified
230 if '.*' in settings
and 'skip' in settings
['.*'] and not 'include' in settings
['.*']:
231 outputCalls
.append((self
.js_print
,(' skipping... reason: %s' % settings
['.*']['skip'],)))
233 outputCalls
.insert(0,(self
.js_print
,('%d running %s' % (testnum
, ast
), '<b>', '</b><br/>')));
236 # Check for a timezone file
237 if isfile('%s.tz' % ast
):
238 if not self
.valid_time_zone(ast
, testnum
, outputCalls
):
241 # delete abc if forcerebuild
242 if self
.forcerebuild
and isfile(testName
) and ext
not in self
.executableExtensions
:
244 if isfile(testName
) and getmtime(ast
)>getmtime(testName
) and self
.timestampcheck
:
245 outputCalls
.append((self
.verbose_print
, ("%s has been modified, recompiling" % ast
,)))
248 # process support dir
249 if exists(root
+self
.supportFolderExt
):
250 self
.compile_support_files(root
+self
.supportFolderExt
, outputCalls
)
252 # compile file if needed
253 if not isfile(testName
):
254 compileOutput
= self
.compile_test(ast
, outputCalls
=outputCalls
)
255 if not isfile(testName
):
256 if ast
.endswith(self
.abcasmExt
):
257 # file didn't compile, compare compile output
258 flines
= self
.compareAbcAsmOutput(ast
, compileOutput
)
261 outputCalls
.append((self
.fail
,(testName
, 'FAILED! :\nExpected:\n'+''.join(flines
)+'\nGOT:\n'+''.join(compileOutput
), self
.failmsgs
)))
262 outputCalls
.append((self
.js_print
, (' FAILED passes: 0 fails: 1 unexpected passes: 0 expected failures: 0', '', '<br/>')))
265 outputCalls
.append((self
.verbose_print
, (' PASSED passes: 1 fails: 0 unexpected passes: 0 expected failures: 0', '', '<br/>')))
266 outputCalls
.insert(0,(self
.js_print
,('%d running %s' % (testnum
, ast
), '<b>', '</b><br/>')));
270 outputCalls
.append((self
.js_print
, ('%s' % '\n'.join(compileOutput
),)))
271 outputCalls
.append((self
.fail
,(testName
, 'FAILED! file did not compile: %s' %
272 testName
, self
.failmsgs
)))
275 if self
.runSource
or self
.eval:
276 incfiles
=self
.build_incfiles(testName
)
277 incfiles
.append("shell" + self
.sourceExt
)
278 for incfile
in incfiles
:
279 testName
=incfile
+" "+testName
281 # read any extra avm arguments, each line will execute the avm with those args
282 if isfile('%s.avm_args' % ast
):
283 avm_args_file
= open('%s.avm_args' % ast
,'r')
285 uses_swfversion
= re
.compile('uses_swfversion', re
.IGNORECASE
)
286 for line
in avm_args_file
:
288 if line
.startswith('#'):
292 if uses_swfversion
.search(line
):
293 # run avm with all bugcompat versions
294 for swf_ver
in self
.swfversions
:
295 line
= uses_swfversion
.sub('', line
)
296 line
, extraVmArgs
, abcargs
= self
.process_avm_args_line(line
, dir)
297 extraVmArgs
+= ' -swfversion %s ' % swf_ver
298 outputCalls
.extend(self
.runTest(
299 ast
, root
, testName
, '%s.%s' % (testnum
, index
),
300 settings
, extraVmArgs
, abcargs
))
303 line
, extraVmArgs
, abcargs
= self
.process_avm_args_line(line
, dir)
304 outputCalls
.extend(self
.runTest(ast
, root
, testName
, '%s.%s' % (testnum
, index
), settings
, extraVmArgs
, abcargs
))
306 outputCalls
.extend(self
.runTest(ast
, root
, testName
, testnum
, settings
))
310 def valid_time_zone(self
, testname
, testnum
, outputCalls
):
311 '''Read in the associated time zone (testname.as.tz) file and
312 check if we match that timezone. Returns a boolean indicating
313 whether we are in the right timezone'''
314 tz_file
= open('%s.tz' % testname
)
318 if not line
or line
.startswith('#'):
320 valid_timezones
.append(line
)
322 # windows python time.tzname returns time zone strings instead of
323 # abbreviations. Take the string and condense it so that it matches
324 # the unix tzname values. Not ideal, but works for most time zones.
325 # e.g.: computer set to Pacific Standard Time
326 # windows tzname = ('Pacific Standard Time', 'Pacific Daylight Time')
327 # cygwin tzname = ('PST', 'PDT')
328 if self
.osName
== 'win' and not self
.cygwin
:
329 print('win tz: ', time
.tzname
)
330 # extract only the upper case letters from the windows time zone
331 tzname
= tuple([''.join(re
.findall('[A-Z]',tz
)) for tz
in time
.tzname
])
334 print('tzname = ', tzname
)
335 # check if current tz is in the list
336 if str(tzname
) not in valid_timezones
:
339 (' skipping... reason: Current timezone: %s not in %s.tz timezones:\n' \
341 % (tzname
, testname
, ','.join(valid_timezones
)),)))
342 outputCalls
.insert(0,(self
.js_print
,('%d running %s' % (testnum
, testname
), '<b>', '</b><br/>')));
347 def compile_support_files(self
, support_dir
, outputCalls
):
348 for p
, dirs
, files
in walk(support_dir
):
350 if f
.endswith(self
.sourceExt
):
352 binFile
= splitext(f
)[0]+'.abc'
353 if exists(binFile
) and (self
.forcerebuild
or (self
.timestampcheck
and getmtime(f
)>getmtime(binFile
))):
355 if not isfile(binFile
):
356 compileOutput
= self
.compile_test(f
, outputCalls
=outputCalls
)
357 if not isfile(binFile
):
358 outputCalls
.append((self
.js_print
,(' Error compiling support file: %s' % f
,)))
359 outputCalls
.append((self
.verbose_print
, (' compile output: %s' % compileOutput
,)))
361 def process_avm_args_line(self
, line
, dir):
363 line
= line
.replace("$DIR", dir)
364 if line
.find('--') != -1:
365 (extraVmArgs
, abcargs
) = line
.split('--')
368 return line
, extraVmArgs
, abcargs
370 def runTest(self
, ast
, root
, testName
, testnum
, settings
, extraVmArgs
='', abcargs
=''):
371 if self
.androidthreads
:
373 if threading
.currentThread().getName()=='MainThread':
376 n
=int(threading
.currentThread().getName()[7:])-1
377 if n
<len(self
.androiddevices
):
378 extraVmArgs
+=" --threadid=%d --androidid=%s %s" % (n
,self
.androiddevices
[n
],extraVmArgs
)
380 print(sys
.exc_info())
388 starttime
=time
.time()
390 if self
.aotsdk
and self
.aotout
:
392 if isfile("%s.avm_args" % ast
):
393 avm_args
= open("%s.avm_args" % ast
).readline()
394 if avm_args
.find("mops") >= 0:
396 progname
= testName
.replace(".abc", "")
397 progname
= progname
.replace("/", ".")
398 progpath
= os
.path
.join(self
.aotout
, progname
)
401 while retryCount
> 0:
402 # copy file to remote device
403 cmd
= "scp %s %s@%s:" % (progpath
, self
.remoteuser
, self
.remoteip
)
404 self
.run_pipe(cmd
, outputCalls
=outputCalls
)
406 cmd
= "ssh %s@%s ./%s %s" % (self
.remoteuser
, self
.remoteip
, progname
, avm_args
)
407 (f
,err
,exitcode
) = self
.run_pipe(cmd
, outputCalls
=outputCalls
)
414 cmd
= "ssh %s@%s rm %s" % (self
.remoteuser
, self
.remoteip
, progname
)
415 self
.run_pipe(cmd
, outputCalls
=outputCalls
)
417 cmd
= "%s %s" % (progpath
, avm_args
)
418 # print("about to execute: " + cmd)
419 (f
,err
,exitcode
) = self
.run_pipe(cmd
, outputCalls
=outputCalls
)
420 elif ast
.endswith(self
.abcasmExt
):
421 # make sure util file has been compiled
422 if not exists(self
.abcasmShell
+'.abc'): # compile abcasmShell with no additional args
423 self
.run_pipe('"%s" -jar %s %s' % (self
.java
, self
.asc
, self
.abcasmShell
+'.as'), outputCalls
=outputCalls
)
424 (f
,err
,exitcode
) = self
.run_pipe('%s %s %s %s %s' % (self
.avm
, self
.vmargs
, extraVmArgs
, self
.abcasmShell
+'.abc', testName
), outputCalls
=outputCalls
)
426 # get the abcdump for the file
427 (f
,err
,exitcode
) = self
.run_pipe('%s %s -- %s' % (self
.avm
, self
.abcdump
+'.abc', testName
), outputCalls
=outputCalls
)
428 abcdumpFunctions
= [line
.strip() for line
in f
if line
.startswith('var function')]
430 outputCalls
.append((self
.js_print
,(abcdumpFunctions
,)))
431 # get -Dverifyall -Dverbose=verify output
432 (f
,err
,exitcode
) = self
.run_pipe('%s %s %s' % (self
.avm
, self
.vmargs
, testName
), outputCalls
=outputCalls
)
433 verifyFunctions
= [line
.strip() for line
in f
if line
.startswith('verify Function/')]
435 outputCalls
.append((self
.js_print
,(verifyFunctions
,)))
437 # we can't compare actual function names since abcdump treats function names and var names the same
438 # we instead just compare that the # of functions verified == the # of functions listed out by abcdump
439 if len(abcdumpFunctions
) != len(verifyFunctions
):
441 outputCalls
.append((self
.fail
,(testName
, 'FAILED! :\nExpected (from abcdump): %s functions verified\nGOT (-Dverifyall -Dverbose=verify): %s functions verified' %
442 (len(abcdumpFunctions
),len(verifyFunctions
)), self
.failmsgs
)))
447 (f
,err
,exitcode
) = self
.run_pipe('%s %s %s %s -- %s' % (self
.avm
, self
.vmargs
, extraVmArgs
, testName
, abcargs
), outputCalls
=outputCalls
)
449 (f
,err
,exitcode
) = self
.run_pipe('%s %s %s %s' % (self
.avm
, self
.vmargs
, extraVmArgs
, testName
), outputCalls
=outputCalls
)
451 # Test has been run, handle output
453 # only check the exit code when running a verifyonly pass
454 ec_lfail
, ec_lexpfail
, expectedExitcode
= self
.checkExitCode(exitcode
, root
, testName
, f
, err
, settings
, outputCalls
)
455 if ec_lfail
or ec_lexpfail
:
457 lexpfail
+= ec_lexpfail
460 elif not self
.verify
:
463 if isfile(root
+'.out'):
464 # override standard runtests behavior, just compare the .out file with stdout+stderr
465 actual
= [line
.strip() for line
in f
+err
if line
.strip()]
467 outfile
= open(root
+'.out', 'r')
468 expectedOut
= [line
.strip() for line
in outfile
.readlines() if line
.strip()]
470 outputCalls
.append((self
.verbose_print
,('%s.out file (expected):' % root
,)))
471 outputCalls
.append((self
.verbose_print
,(expectedOut
,)))
472 outputCalls
.append((self
.verbose_print
,('\nactual output:',)))
473 outputCalls
.append((self
.verbose_print
,(actual
,)))
474 # check settings if this should be an expected failure
475 expectedfail
= dict_match(settings
,'','expectedfail')
476 # .out files can contain regex but must be prefaced with REGEXP as the first line in the file
478 if expectedOut
[0] == 'REGEXP':
479 expectedOut
= expectedOut
[1:]
480 if len(actual
) < len(expectedOut
):
481 # pad actual output w/ empty lines
482 for i
in range(len(expectedOut
)-len(actual
)):
484 for i
in range(len(actual
)):
485 if not re
.search(expectedOut
[i
], actual
[i
]):
488 if actual
!= expectedOut
:
490 # test passed - check to make sure its not an expected failure
492 outputCalls
.append((self
.fail
,(
493 testName
, 'unexpected .out file pass. ' +
494 ' reason: '+expectedfail
, self
.unpassmsgs
)))
501 outputCalls
.append((self
.fail
,(
503 'expected failure: .out file does not match stdout+stderr. ' +
504 ' reason: '+expectedfail
, self
.expfailmsgs
)))
507 outputCalls
.append((self
.fail
,(testName
,
508 '.out file does not match output:\n%s.out file (expected):\n%s\nactual output:\n%s' % (root
, expectedOut
,actual
),
512 outputLines
.append((self
.js_print
,('Error opening %s.out' % root
,)))
516 outputLines
.append(line
)
517 outputCalls
.append((self
.verbose_print
,(' %s' % line
.strip(),)))
518 if 'Assertion failed:' in line
:
520 outputCalls
.append((self
.fail
,(testName
+extraVmArgs
, line
, self
.assertmsgs
)))
523 testcase
=line
.strip()
524 skipTestDesc
= dict_match(settings
,testcase
,'skip')
525 includeTestDesc
= dict_match(settings
, testcase
, 'include')
526 if skipTestDesc
and not includeTestDesc
:
527 outputCalls
.append((self
.js_print
,(' skipping "%s" ... reason: %s' % (line
.strip(),skipTestDesc
),)))
530 if 'PASSED!' in line
:
531 res
=dict_match(settings
,testcase
,'expectedfail')
533 outputCalls
.append((self
.fail
,(testName
, 'unexpected pass: ' + line
.strip() + ' reason: '+res
, self
.unpassmsgs
)))
537 if 'FAILED!' in line
:
538 res
=dict_match(settings
,testcase
,'expectedfail')
540 outputCalls
.append((self
.fail
,(testName
, 'expected failure: ' + line
.strip() + ' reason: '+res
, self
.expfailmsgs
)))
544 outputCalls
.append((self
.fail
,(testName
+extraVmArgs
, line
, self
.failmsgs
)))
546 print('exception running avm')
550 ec_lfail
, ec_lexpfail
, expectedExitcode
= self
.checkExitCode(exitcode
, root
, testName
, f
, err
, settings
, outputCalls
)
551 if ec_lfail
or ec_lexpfail
:
553 lexpfail
+= ec_lexpfail
555 # TODO: When needed, add support for expected stderr output - see https://bugzilla.mozilla.org/show_bug.cgi?id=561892
556 outputCalls
.append((self
.fail
,(testName
, "unexpected stderr expected:'%s' actual:'%s'" % ('',err
), self
.failmsgs
)))
557 lfail
+= 1 # any messages to stderr automatically fail the test
558 elif lpass
== 0 and lfail
== 0 and lunpass
==0 and lexpfail
==0:
559 res
=dict_match(settings
,'*','expectedfail')
561 outputCalls
.append((self
.fail
,(testName
, 'expected failure: FAILED contained no testcase messages reason: %s' % res
,self
.expfailmsgs
)))
564 # test may not have any output but is still passing if a non-standard exitcode was expected (e.g. VerifyErrors)
565 if expectedExitcode
!= 0 and exitcode
==expectedExitcode
:
566 #outputCalls.append((self.js_print,(' Expected Exit Code: %s Actual Exit Code: %s PASSED' % (expectedExitcode, exitcode),)))
571 for l
in outputLines
:
572 outputmsg
+=l
.strip()+'|'
573 outputCalls
.append((self
.fail
,(testName
, ' FAILED contained no testcase messages - reason: %s' % outputmsg
, self
.failmsgs
)))
575 self
.allfails
+= lfail
576 self
.allpasses
+= lpass
577 self
.allexpfails
+= lexpfail
578 self
.allunpass
+= lunpass
579 self
.alltimeouts
+= ltimeout
580 self
.allasserts
+= lassert
582 outputCalls
.append((self
.js_print
, (' FAILED passes:%d fails:%d unexpected passes: %d expected failures: %d\n' % (lpass
,lfail
,lunpass
,lexpfail
), '', '<br/>')))
584 outputCalls
.append((self
.verbose_print
, (' PASSED passes:%d fails:%d unexpected passes: %d expected failures: %d\n' % (lpass
,lfail
,lunpass
,lexpfail
), '', '<br/>')))
586 outputCalls
.insert(0,(self
.js_print
,('%s running %s %s %s time %.1f' % (testnum
, ast
, extraVmArgs
, abcargs
, time
.time()-starttime
), '<b>', '</b><br/>')));
588 outputCalls
.insert(0,(self
.js_print
,('%s running %s %s %s' % (testnum
, ast
, extraVmArgs
, abcargs
), '<b>', '</b><br/>')));
593 def checkExitCode(self
, exitcode
, root
, testName
, f
, err
, settings
, outputCalls
):
594 '''Check the exitcode for a test against any expected non-zero exitcodes
595 Return the fail and the expected exitcode if non-zero
600 if isfile(root
+'.err'):
601 # .err file holds both the expected (non-catchable) error (usually a VerifyError) and the expected exitcode
602 expectedErr
,expectedExitcode
= self
.loadExpectedErr(root
+'.err')
603 # check the expectedErr - error is always the last (non-empty) line of output
605 for line
in reversed(f
):
606 # When running in --verifyonly mode, output will be VERIFY FAILED instead of VerifyError
607 line
= line
.replace('VERIFY FAILED', 'VerifyError')
608 # make sure we have an error
609 if re
.search('.*Error:.*', line
):
610 actualErr
= self
.getError(line
.strip())
612 if actualErr
!= expectedErr
:
613 outputCalls
.append((self
.fail
,(testName
, 'unexpected error message. expected: %s actual: %s'
614 % (expectedErr
, actualErr
), self
.failmsgs
)))
616 elif isfile(root
+".exitcode"):
618 expectedExitcode
=int(open(root
+".exitcode").read())
620 print("ERROR: reading exit code file '%s' should contain an integer")
621 res
=dict_match(settings
,'exitcode','expectedfail')
623 if exitcode
!=expectedExitcode
:
624 res2
=dict_match(settings
,'exitcode','skip')
625 if res2
==None and res
:
626 outputCalls
.append((self
.js_print
,(testName
, 'expected failure: exitcode reason: %s'
627 % res
,self
.expfailmsgs
)))
630 outputCalls
.append((self
.fail
,(testName
, 'unexpected exit code expected:%d actual:%d Signal Name: %s FAILED!'
631 % (expectedExitcode
,exitcode
,getSignalName(abs(exitcode
))),
635 outputmsg
+=l
.strip()+'|'
636 outputCalls
.append((self
.fail
,(testName
, 'captured output: %s'
637 % outputmsg
,self
.failmsgs
)))
639 return lfail
, lexpfail
, expectedExitcode
641 def loadExpectedErr(self
, file):
648 if line
and not line
.startswith('#'):
649 if line
.lower().startswith('exitcode:'):
651 expectedExitcode
= int(line
.split(':')[1].strip())
655 # check to see if line is a number - if so set expectedExitcode
657 expectedExitcode
= int(line
)
660 return expectedErr
, expectedExitcode
662 return 'Error reading .err file: %s' % file, 0
664 def getError(self
, line
):
665 # Parse out the error type and #, but strip the description
667 return ':'.join(line
.split(':')[0:2])
671 runtest
= AcceptanceRuntest()