1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712
|
#!/usr/bin/python
# Copyright (c) 2012 The Native Client Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Simple testing harness for running commands and checking expected output.
This harness is used instead of shell scripts to ensure windows compatibility
"""
# python imports
import getopt
import os
import pipes
import re
import sys
# local imports
import test_lib
GlobalPlatform=None # for pychecker, initialized in ProcessOptions
GlobalSettings = {}
def Print(message):
print message
def Banner(message):
Print('=' * 70)
print(message)
print('=' * 70)
def DifferentFromGolden(actual, golden, output_type):
"""Compares actual output against golden output.
If there are any differences, output an error message (to stdout) with
appropriate banners.
Args:
actual: actual output from the program under test, as a single
string.
golden: expected output from the program under test, as a single
string.
output_type: the name / title for the output type being compared.
Used in banner output.
Returns:
True when there is a difference, False otherwise.
"""
diff = list(test_lib.DiffStringsIgnoringWhiteSpace(golden, actual))
diff = '\n'.join(diff)
if diff:
Banner('Error %s diff found' % output_type)
Print(diff)
Banner('Potential New Golden Output')
Print(actual)
return True
return False
def ResetGlobalSettings():
global GlobalSettings
GlobalSettings = {
'exit_status': 0,
'using_nacl_signal_handler': False,
# When declares_exit_status is set, we read the expected exit
# status from stderr. We look for a line of the form
# "** intended_exit_status=X". This allows crash tests to
# declare their expected exit status close to where the crash is
# generated, rather than in a Scons file. It reduces the risk
# that the test passes accidentally by crashing during setup.
'declares_exit_status': False,
# List of environment variables to set.
'osenv': '',
'arch': None,
'subarch': None,
# An environment description that should include all factors that may
# affect tracked performance. Used to compare different environments.
'perf_env_description': None,
'name': None,
'output_stamp': None,
'stdin': None,
'log_file': None,
'stdout_golden': None,
'stderr_golden': None,
'log_golden': None,
# This option must be '1' for the output to be captured, for checking
# against golden files, special exit_status signals, etc.
# When this option is '0', stdout and stderr will be streamed out.
'capture_output': '1',
# This option must be '1' for the stderr to be captured with stdout
# (it's ignored if capture_output == 0). If this option is '0' only
# stdout will be captured and stderr will be streamed out.
'capture_stderr': '1',
'filter_regex': None,
'filter_inverse': False,
'filter_group_only': False,
# Number of times a test is run.
# This is useful for getting multiple samples for time perf tests.
'num_runs': 1,
# Scripts for processing output along with its arguments.
# This script is given the output of a single run.
'process_output_single': None,
# This script is given the concatenated output of all |num_runs|, after
# having been filtered by |process_output_single| for individual runs.
'process_output_combined': None,
'time_warning': 0,
'time_error': 0,
'run_under': None,
}
def StringifyList(lst):
return ','.join(lst)
def DestringifyList(lst):
# BUG(robertm): , is a legitimate character for an environment variable
# value.
return lst.split(',')
# The following messages match gtest's formatting. This improves log
# greppability for people who primarily work on Chrome. It also allows
# gtest-specific hooks on the buildbots to fire.
# The buildbots expect test names in the format "suite_name.test_name", so we
# prefix the test name with a bogus suite name (nacl).
def RunMessage():
return '[ RUN ] %s' % (GlobalSettings['name'],)
def FailureMessage(total_time):
return '[ FAILED ] %s (%d ms)' % (GlobalSettings['name'],
total_time * 1000.0)
def SuccessMessage(total_time):
return '[ OK ] %s (%d ms)' % (GlobalSettings['name'],
total_time * 1000.0)
def LogPerfResult(graph_name, trace_name, value, units):
# NOTE: This RESULT message is parsed by Chrome's perf graph generator.
Print('RESULT %s: %s= %s %s' %
(graph_name, trace_name, value, units))
# On POSIX systems, exit() codes are 8-bit. You cannot use exit() to
# make it look like the process was killed by a signal. Instead,
# NaCl's signal handler encodes the signal number into the exit() code
# by returning with exit(-signum) or equivalently, exit((-signum) & 0xff).
def IndirectSignal(signum):
return (-signum) & 0xff
# Windows exit codes that indicate unhandled exceptions.
STATUS_ACCESS_VIOLATION = 0xc0000005
STATUS_ILLEGAL_INSTRUCTION = 0xc000001d
STATUS_PRIVILEGED_INSTRUCTION = 0xc0000096
STATUS_FLOAT_DIVIDE_BY_ZERO = 0xc000008e
STATUS_INTEGER_DIVIDE_BY_ZERO = 0xc0000094
# Python's wrapper for GetExitCodeProcess() treats the STATUS_* values
# as negative, although the unsigned values are used in headers and
# are more widely recognised.
def MungeWindowsErrorExit(num):
return num - 0x100000000
# If a crash occurs in x86-32 untrusted code on Windows, the kernel
# apparently gets confused about the cause. It fails to take %cs into
# account when examining the faulting instruction, so it looks at the
# wrong instruction, so we could get either of the errors below.
# See http://code.google.com/p/nativeclient/issues/detail?id=1689
win32_untrusted_crash_exit = [
MungeWindowsErrorExit(STATUS_ACCESS_VIOLATION),
MungeWindowsErrorExit(STATUS_PRIVILEGED_INSTRUCTION)]
win32_sigfpe = [
MungeWindowsErrorExit(STATUS_FLOAT_DIVIDE_BY_ZERO),
MungeWindowsErrorExit(STATUS_INTEGER_DIVIDE_BY_ZERO),
]
# We patch Windows' KiUserExceptionDispatcher on x86-64 to terminate
# the process safely when untrusted code crashes. We get the exit
# code associated with the HLT instruction.
win64_exit_via_ntdll_patch = [
MungeWindowsErrorExit(STATUS_PRIVILEGED_INSTRUCTION)]
# Mach exception code for Mac OS X.
EXC_BAD_ACCESS = 1
# 32-bit processes on Mac OS X return SIGBUS in most of the cases where Linux
# returns SIGSEGV, except for actual x86 segmentation violations. 64-bit
# processes on Mac OS X behave differently.
status_map = {
'sigtrap' : {
'linux2': [-5], # SIGTRAP
'darwin': [-5], # SIGTRAP
},
'trusted_sigabrt' : {
'linux2': [-6], # SIGABRT
'mac32': [-6], # SIGABRT
'mac64': [-6], # SIGABRT
# On Windows, NaClAbort() exits using the HLT instruction.
'win32': [MungeWindowsErrorExit(STATUS_PRIVILEGED_INSTRUCTION)],
'win64': [MungeWindowsErrorExit(STATUS_PRIVILEGED_INSTRUCTION)],
},
'naclabort_coverage' : {
# This case is here because NaClAbort() behaves differently when
# code coverage is enabled.
# This is not used on Windows.
'linux2': [IndirectSignal(6)], # SIGABRT
'mac32': [IndirectSignal(6)], # SIGABRT
'mac64': [IndirectSignal(6)], # SIGABRT
},
'sigpipe': {
# This is not used on Windows because Windows does not have an
# equivalent of SIGPIPE.
'linux2': [-13], # SIGPIPE
'mac32': [-13], # SIGPIPE
'mac64': [-13], # SIGPIPE
},
'untrusted_sigsegv': {
'linux2': [-11], # SIGSEGV
'mac32': [-11], # SIGSEGV
'mac64': [-11], # SIGSEGV
'win32': win32_untrusted_crash_exit,
'win64': win64_exit_via_ntdll_patch,
},
'untrusted_sigill' : {
'linux2': [-4], # SIGILL
'mac32': [-4], # SIGILL
'mac64': [-4], # SIGILL
'win32': win32_untrusted_crash_exit,
'win64': win64_exit_via_ntdll_patch,
},
'untrusted_sigfpe' : {
'linux2': [-8], # SIGFPE
'mac32': [-8], # SIGFPE
'mac64': [-8], # SIGFPE
'win32': win32_sigfpe,
'win64': win64_exit_via_ntdll_patch,
},
'untrusted_segfault': {
'linux2': [-11], # SIGSEGV
'mac32': [-10], # SIGBUS
'mac64': [-10], # SIGBUS
'mach_exception': EXC_BAD_ACCESS,
'win32': win32_untrusted_crash_exit,
'win64': win64_exit_via_ntdll_patch,
},
'untrusted_sigsegv_or_equivalent': {
'linux2': [-11], # SIGSEGV
'mac32': [-11], # SIGSEGV
'mac64': [-10], # SIGBUS
'win32': win32_untrusted_crash_exit,
'win64': win64_exit_via_ntdll_patch,
},
'trusted_segfault': {
'linux2': [-11], # SIGSEGV
'mac32': [-10], # SIGBUS
'mac64': [-11], # SIGSEGV
'mach_exception': EXC_BAD_ACCESS,
'win32': [MungeWindowsErrorExit(STATUS_ACCESS_VIOLATION)],
'win64': [MungeWindowsErrorExit(STATUS_ACCESS_VIOLATION)],
},
'trusted_sigsegv_or_equivalent': {
'linux2': [-11], # SIGSEGV
'mac32': [-11], # SIGSEGV
'mac64': [-11], # SIGSEGV
'win32': [],
'win64': [],
},
# This is like 'untrusted_segfault', but without the 'untrusted_'
# prefix which marks the status type as expecting a
# gracefully-printed exit message from nacl_signal_common.c. This
# is a special case because we use different methods for writing
# the exception stack frame on different platforms. On Mac and
# Windows, NaCl uses a system call which will detect unwritable
# pages, so the exit status appears as an unhandled fault from
# untrusted code. On Linux, NaCl's signal handler writes the
# frame directly, so the exit status comes from getting a SIGSEGV
# inside the SIGSEGV handler.
'unwritable_exception_stack': {
'linux2': [-11], # SIGSEGV
'mac32': [-10], # SIGBUS
'mac64': [-10], # SIGBUS
'win32': win32_untrusted_crash_exit,
'win64': win64_exit_via_ntdll_patch,
},
# Expectations for __builtin_trap(), which is compiled to different
# instructions by the GCC and LLVM toolchains. The exact exit status
# does not matter too much, as long as it's a crash and not a graceful
# exit via exit() or _exit(). We want __builtin_trap() to trigger the
# debugger or a crash reporter.
'untrusted_builtin_trap': {
'linux2': [-4, -5, -11],
'mac32': [-4, -10, -11],
'mac64': [-4, -10, -11],
'win32': win32_untrusted_crash_exit +
[MungeWindowsErrorExit(STATUS_ILLEGAL_INSTRUCTION)],
'win64': win64_exit_via_ntdll_patch,
},
}
def ProcessOptions(argv):
global GlobalPlatform
"""Process command line options and return the unprocessed left overs."""
ResetGlobalSettings()
try:
opts, args = getopt.getopt(argv, '', [x + '=' for x in GlobalSettings])
except getopt.GetoptError, err:
Print(str(err)) # will print something like 'option -a not recognized'
sys.exit(1)
for o, a in opts:
# strip the leading '--'
option = o[2:]
assert option in GlobalSettings
if option == 'exit_status':
GlobalSettings[option] = a
elif type(GlobalSettings[option]) == int:
GlobalSettings[option] = int(a)
else:
GlobalSettings[option] = a
if (sys.platform == 'win32') and (GlobalSettings['subarch'] == '64'):
GlobalPlatform = 'win64'
elif (sys.platform == 'darwin'):
# mac32, mac64
GlobalPlatform = 'mac' + GlobalSettings['subarch']
else:
GlobalPlatform = sys.platform
# return the unprocessed options, i.e. the command
return args
# Parse output for signal type and number
#
# The '** Signal' output is from the nacl signal handler code.
#
# Since it is possible for there to be an output race with another
# thread, or additional output due to atexit functions, we scan the
# output in reverse order for the signal signature.
def GetNaClSignalInfoFromStderr(stderr):
lines = stderr.splitlines()
# Scan for signal msg in reverse order
for curline in reversed(lines):
match = re.match('\*\* (Signal|Mach exception) (\d+) from '
'(trusted|untrusted) code', curline)
if match is not None:
return match.group(0)
return None
def GetQemuSignalFromStderr(stderr, default):
for line in reversed(stderr.splitlines()):
# Look for 'qemu: uncaught target signal XXX'.
words = line.split()
if (len(words) > 4 and
words[0] == 'qemu:' and words[1] == 'uncaught' and
words[2] == 'target' and words[3] == 'signal'):
return -int(words[4])
return default
def FormatExitStatus(number):
# Include the hex version because it makes the Windows error exit
# statuses (STATUS_*) more recognisable.
return '%i (0x%x)' % (number, number & 0xffffffff)
def FormatResult((exit_status, printed_status)):
return 'exit status %s and signal info %r' % (
FormatExitStatus(exit_status), printed_status)
def PrintStdStreams(stdout, stderr):
if stderr is not None:
Banner('Stdout for %s:' % os.path.basename(GlobalSettings['name']))
Print(stdout)
Banner('Stderr for %s:' % os.path.basename(GlobalSettings['name']))
Print(stderr)
def GetIntendedExitStatuses(stderr):
statuses = []
for line in stderr.splitlines():
match = re.match(r'\*\* intended_exit_status=(.*)$', line)
if match is not None:
statuses.append(match.group(1))
return statuses
def CheckExitStatus(failed, req_status, using_nacl_signal_handler,
exit_status, stdout, stderr):
if GlobalSettings['declares_exit_status']:
assert req_status == 0
intended_statuses = GetIntendedExitStatuses(stderr)
if len(intended_statuses) == 0:
Print('\nERROR: Command returned exit status %s but did not output an '
'intended_exit_status line to stderr - did it exit too early?'
% FormatExitStatus(exit_status))
return False
elif len(intended_statuses) != 1:
Print('\nERROR: Command returned exit status %s but produced '
'multiple intended_exit_status lines (%s)'
% (FormatExitStatus(exit_status), ', '.join(intended_statuses)))
return False
else:
req_status = intended_statuses[0]
expected_sigtype = 'normal'
if req_status in status_map:
expected_statuses = status_map[req_status][GlobalPlatform]
if using_nacl_signal_handler:
if req_status.startswith('trusted_'):
expected_sigtype = 'trusted'
elif req_status.startswith('untrusted_'):
expected_sigtype = 'untrusted'
else:
expected_statuses = [int(req_status)]
expected_printed_status = None
if expected_sigtype == 'normal':
expected_results = [(status, None) for status in expected_statuses]
else:
if sys.platform == 'darwin':
# Mac OS X
default = '<mach_exception field missing for %r>' % req_status
expected_printed_status = '** Mach exception %s from %s code' % (
status_map.get(req_status, {}).get('mach_exception', default),
expected_sigtype)
expected_results = [(status, expected_printed_status)
for status in expected_statuses]
else:
# Linux
assert sys.platform != 'win32'
def MapStatus(status):
# Expected value should be a signal number, negated.
assert status < 0, status
expected_printed_signum = -status
expected_printed_status = '** Signal %d from %s code' % (
expected_printed_signum,
expected_sigtype)
return (IndirectSignal(expected_printed_signum),
expected_printed_status)
expected_results = [MapStatus(status) for status in expected_statuses]
# If an uncaught signal occurs under QEMU (on ARM), the exit status
# contains the signal number, mangled as per IndirectSignal(). We
# extract the unadulterated signal number from QEMU's log message in
# stderr instead. If we are not using QEMU, or no signal is raised
# under QEMU, this is a no-op.
if stderr is not None:
exit_status = GetQemuSignalFromStderr(stderr, exit_status)
if using_nacl_signal_handler and stderr is not None:
actual_printed_status = GetNaClSignalInfoFromStderr(stderr)
else:
actual_printed_status = None
actual_result = (exit_status, actual_printed_status)
msg = '\nERROR: Command returned: %s\n' % FormatResult(actual_result)
msg += 'but we expected: %s' % '\n or: '.join(FormatResult(r)
for r in expected_results)
if actual_result not in expected_results:
Print(msg)
failed = True
return not failed
def CheckTimeBounds(total_time):
if GlobalSettings['time_error']:
if total_time > GlobalSettings['time_error']:
Print('ERROR: should have taken less than %f secs' %
(GlobalSettings['time_error']))
return False
if GlobalSettings['time_warning']:
if total_time > GlobalSettings['time_warning']:
Print('WARNING: should have taken less than %f secs' %
(GlobalSettings['time_warning']))
return True
def CheckGoldenOutput(stdout, stderr):
for (stream, getter) in [
('stdout', lambda: stdout),
('stderr', lambda: stderr),
('log', lambda: open(GlobalSettings['log_file']).read()),
]:
golden = stream + '_golden'
if GlobalSettings[golden]:
golden_data = open(GlobalSettings[golden]).read()
actual = getter()
if GlobalSettings['filter_regex']:
actual = test_lib.RegexpFilterLines(GlobalSettings['filter_regex'],
GlobalSettings['filter_inverse'],
GlobalSettings['filter_group_only'],
actual)
if DifferentFromGolden(actual, golden_data, stream):
return False
return True
def ProcessLogOutputSingle(stdout, stderr):
output_processor = GlobalSettings['process_output_single']
if output_processor is None:
return (True, stdout, stderr)
else:
output_processor_cmd = DestringifyList(output_processor)
# Also, get the output from log_file to get NaClLog output in Windows.
log_output = open(GlobalSettings['log_file']).read()
# Assume the log processor does not care about the order of the lines.
all_output = log_output + stdout + stderr
_, retcode, failed, new_stdout, new_stderr = \
test_lib.RunTestWithInputOutput(
output_processor_cmd, all_output,
timeout=GlobalSettings['time_error'])
# Print the result, since we have done some processing and we need
# to have the processed data. However, if we intend to process it some
# more later via process_output_combined, do not duplicate the data here.
# Only print out the final result!
if not GlobalSettings['process_output_combined']:
PrintStdStreams(new_stdout, new_stderr)
if retcode != 0 or failed:
return (False, new_stdout, new_stderr)
else:
return (True, new_stdout, new_stderr)
def ProcessLogOutputCombined(stdout, stderr):
output_processor = GlobalSettings['process_output_combined']
if output_processor is None:
return True
else:
output_processor_cmd = DestringifyList(output_processor)
all_output = stdout + stderr
_, retcode, failed, new_stdout, new_stderr = \
test_lib.RunTestWithInputOutput(
output_processor_cmd, all_output,
timeout=GlobalSettings['time_error'])
# Print the result, since we have done some processing.
PrintStdStreams(new_stdout, new_stderr)
if retcode != 0 or failed:
return False
else:
return True
def DoRun(command, stdin_data):
"""
Run the command, given stdin_data. Returns a return code (0 is good)
and optionally a captured version of stdout, stderr from the run
(if the global setting capture_output is true).
"""
# Initialize stdout, stderr to indicate we have not captured
# any of stdout or stderr.
stdout = ''
stderr = ''
if not int(GlobalSettings['capture_output']):
# We are only blurting out the stdout and stderr, not capturing it
# for comparison, etc.
assert (not GlobalSettings['stdout_golden']
and not GlobalSettings['stderr_golden']
and not GlobalSettings['log_golden']
and not GlobalSettings['filter_regex']
and not GlobalSettings['filter_inverse']
and not GlobalSettings['filter_group_only']
and not GlobalSettings['process_output_single']
and not GlobalSettings['process_output_combined']
)
# If python ever changes popen.stdout.read() to not risk deadlock,
# we could stream and capture, and use RunTestWithInputOutput instead.
(total_time, exit_status, failed) = test_lib.RunTestWithInput(
command, stdin_data,
timeout=GlobalSettings['time_error'])
if not CheckExitStatus(failed,
GlobalSettings['exit_status'],
GlobalSettings['using_nacl_signal_handler'],
exit_status, None, None):
Print(FailureMessage(total_time))
return (1, stdout, stderr)
else:
(total_time, exit_status,
failed, stdout, stderr) = test_lib.RunTestWithInputOutput(
command, stdin_data, int(GlobalSettings['capture_stderr']),
timeout=GlobalSettings['time_error'])
# CheckExitStatus may spew stdout/stderr when there is an error.
# Otherwise, we do not spew stdout/stderr in this case (capture_output).
if not CheckExitStatus(failed,
GlobalSettings['exit_status'],
GlobalSettings['using_nacl_signal_handler'],
exit_status, stdout, stderr):
PrintStdStreams(stdout, stderr)
Print(FailureMessage(total_time))
return (1, stdout, stderr)
if not CheckGoldenOutput(stdout, stderr):
Print(FailureMessage(total_time))
return (1, stdout, stderr)
success, stdout, stderr = ProcessLogOutputSingle(stdout, stderr)
if not success:
Print(FailureMessage(total_time) + ' ProcessLogOutputSingle failed!')
return (1, stdout, stderr)
if not CheckTimeBounds(total_time):
Print(FailureMessage(total_time))
return (1, stdout, stderr)
Print(SuccessMessage(total_time))
return (0, stdout, stderr)
def DisableCrashDialog():
"""
Disable Windows' crash dialog box, which pops up when a process exits with
an unhandled fault. This causes the process to hang on the Buildbots. We
duplicate this function from SConstruct because ErrorMode flags are
overwritten in scons due to race conditions. See bug
https://code.google.com/p/nativeclient/issues/detail?id=2968
"""
if sys.platform == 'win32':
import win32api
import win32con
# The double call is to preserve existing flags, as discussed at
# http://blogs.msdn.com/oldnewthing/archive/2004/07/27/198410.aspx
new_flags = win32con.SEM_NOGPFAULTERRORBOX
existing_flags = win32api.SetErrorMode(new_flags)
win32api.SetErrorMode(existing_flags | new_flags)
def Main(argv):
DisableCrashDialog()
command = ProcessOptions(argv)
if not GlobalSettings['name']:
GlobalSettings['name'] = command[0]
GlobalSettings['name'] = os.path.basename(GlobalSettings['name'])
Print(RunMessage())
num_runs = GlobalSettings['num_runs']
if num_runs > 1:
Print(' (running %d times)' % num_runs)
if GlobalSettings['osenv']:
Banner('setting environment')
env_vars = DestringifyList(GlobalSettings['osenv'])
else:
env_vars = []
for env_var in env_vars:
key, val = env_var.split('=', 1)
Print('[%s] = [%s]' % (key, val))
os.environ[key] = val
stdin_data = ''
if GlobalSettings['stdin']:
stdin_data = open(GlobalSettings['stdin'])
run_under = GlobalSettings['run_under']
if run_under:
command = run_under.split(',') + command
# print the command in copy-and-pastable fashion
print ' '.join(pipes.quote(arg) for arg in env_vars + command)
# Concatenate output when running multiple times (e.g., for timing).
combined_stdout = ''
combined_stderr = ''
cur_runs = 0
num_runs = GlobalSettings['num_runs']
while cur_runs < num_runs:
cur_runs += 1
# Clear out previous log_file.
if GlobalSettings['log_file']:
try:
os.unlink(GlobalSettings['log_file']) # might not pre-exist
except OSError:
pass
ret_code, stdout, stderr = DoRun(command, stdin_data)
if ret_code != 0:
return ret_code
combined_stdout += stdout
combined_stderr += stderr
# Process the log output after all the runs.
success = ProcessLogOutputCombined(combined_stdout, combined_stderr)
if not success:
# Bogus time, since only ProcessLogOutputCombined failed.
Print(FailureMessage(0.0) + ' ProcessLogOutputCombined failed!')
return 1
if GlobalSettings['output_stamp'] is not None:
# Create an empty stamp file to indicate success.
fh = open(GlobalSettings['output_stamp'], 'w')
fh.close()
return 0
if __name__ == '__main__':
retval = Main(sys.argv[1:])
# Add some whitepsace to make the logs easier to read.
sys.stdout.write('\n\n')
sys.exit(retval)
|