Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(772)

Side by Side Diff: tools/command_tester.py

Issue 8439032: Add option to command tester for running a test multiple times. Use that (Closed) Base URL: svn://svn.chromium.org/native_client/trunk/src/native_client/
Patch Set: one more try Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 #!/usr/bin/python 1 #!/usr/bin/python
2 # Copyright (c) 2011 The Native Client Authors. All rights reserved. 2 # Copyright (c) 2011 The Native Client Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 6
7 """Simple testing harness for running commands and checking expected output. 7 """Simple testing harness for running commands and checking expected output.
8 8
9 This harness is used instead of shell scripts to ensure windows compatibility 9 This harness is used instead of shell scripts to ensure windows compatibility
10 10
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after
98 98
99 # This option must be '1' for the output to be captured, for checking 99 # This option must be '1' for the output to be captured, for checking
100 # against golden files, special exit_status signals, etc. 100 # against golden files, special exit_status signals, etc.
101 # When this option is '0', stdout and stderr will be streamed out. 101 # When this option is '0', stdout and stderr will be streamed out.
102 'capture_output': '1', 102 'capture_output': '1',
103 103
104 'filter_regex': None, 104 'filter_regex': None,
105 'filter_inverse': False, 105 'filter_inverse': False,
106 'filter_group_only': False, 106 'filter_group_only': False,
107 107
108 # Script for processing output along with its arguments. 108 # Number of times a test is run.
109 'process_output': '', 109 # This is useful for getting multiple samples for time perf tests.
110 'num_runs': 1,
111
112 # Scripts for processing output along with its arguments.
113 # This script is given the output of a single run.
114 'process_output_single': '',
Nick Bray 2011/11/11 22:42:39 Would None be a better default? (See other uses.)
jvoung - send to chromium... 2011/11/11 23:28:27 I'll Dones it.
115 # This script is given the concatenated output of all |num_runs|, after
116 # having been filtered by |process_output_single| for individual runs.
117 'process_output_combined': '',
110 118
111 'time_warning': 0, 119 'time_warning': 0,
112 'time_error': 0, 120 'time_error': 0,
113 121
114 'run_under': None, 122 'run_under': None,
115 } 123 }
116 124
117 def StringifyList(lst): 125 def StringifyList(lst):
118 return ','.join(lst) 126 return ','.join(lst)
119 127
120 def DestringifyList(lst): 128 def DestringifyList(lst):
121 # BUG(robertm): , is a legitimate character for an environment variable 129 # BUG(robertm): , is a legitimate character for an environment variable
122 # value. 130 # value.
123 return lst.split(',') 131 return lst.split(',')
124 132
125 # The following messages match gtest's formatting. This improves log 133 # The following messages match gtest's formatting. This improves log
126 # greppability for people who primarily work on Chrome. It also allows 134 # greppability for people who primarily work on Chrome. It also allows
127 # gtest-specific hooks on the buildbots to fire. 135 # gtest-specific hooks on the buildbots to fire.
128 # The buildbots expect test names in the format "suite_name.test_name", so we 136 # The buildbots expect test names in the format "suite_name.test_name", so we
129 # prefix the test name with a bogus suite name (nacl). 137 # prefix the test name with a bogus suite name (nacl).
130 def RunMessage(): 138 def RunMessage():
131 return '[ RUN ] nacl.%s' % GlobalSettings['name'] 139 msg = '[ RUN ] nacl.%s' % GlobalSettings['name']
140 num_runs = GlobalSettings['num_runs']
141 if num_runs > 1:
142 msg += ' (run %d times)' % num_runs
Nick Bray 2011/11/11 22:42:39 Actually... you might want to put this info on its
jvoung - send to chromium... 2011/11/11 23:28:27 Done.
143 return msg
132 144
133 def FailureMessage(total_time): 145 def FailureMessage(total_time):
134 return '[ FAILED ] nacl.%s (%d ms)' % (GlobalSettings['name'], 146 return '[ FAILED ] nacl.%s (%d ms)' % (GlobalSettings['name'],
135 total_time * 1000.0) 147 total_time * 1000.0)
136 148
137 def SuccessMessage(total_time): 149 def SuccessMessage(total_time):
138 return '[ OK ] nacl.%s (%d ms)' % (GlobalSettings['name'], 150 return '[ OK ] nacl.%s (%d ms)' % (GlobalSettings['name'],
139 total_time * 1000.0) 151 total_time * 1000.0)
140 152
141 def LogPerfResult(graph_name, trace_name, value, units): 153 def LogPerfResult(graph_name, trace_name, value, units):
(...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after
303 words[0] == 'qemu:' and words[1] == 'uncaught' and 315 words[0] == 'qemu:' and words[1] == 'uncaught' and
304 words[2] == 'target' and words[3] == 'signal'): 316 words[2] == 'target' and words[3] == 'signal'):
305 return -int(words[4]) 317 return -int(words[4])
306 return default 318 return default
307 319
308 def FormatExitStatus(number): 320 def FormatExitStatus(number):
309 # Include the hex version because it makes the Windows error exit 321 # Include the hex version because it makes the Windows error exit
310 # statuses (STATUS_*) more recognisable. 322 # statuses (STATUS_*) more recognisable.
311 return '%i (0x%x)' % (number, number & 0xffffffff) 323 return '%i (0x%x)' % (number, number & 0xffffffff)
312 324
325 def PrintStdStreams(stdout, stderr):
326 if stderr is not None:
327 Banner('Stdout for %s:' % os.path.basename(GlobalSettings['name']))
328 Print(stdout)
329 Banner('Stderr for %s:' % os.path.basename(GlobalSettings['name']))
330 Print(stderr)
331
313 def CheckExitStatus(failed, req_status, using_nacl_signal_handler, 332 def CheckExitStatus(failed, req_status, using_nacl_signal_handler,
314 exit_status, stdout, stderr): 333 exit_status, stdout, stderr):
315 expected_sigtype = 'normal' 334 expected_sigtype = 'normal'
316 if req_status in status_map: 335 if req_status in status_map:
317 expected_statuses = status_map[req_status][GlobalPlatform] 336 expected_statuses = status_map[req_status][GlobalPlatform]
318 if using_nacl_signal_handler: 337 if using_nacl_signal_handler:
319 if req_status.startswith('trusted_'): 338 if req_status.startswith('trusted_'):
320 expected_sigtype = 'trusted' 339 expected_sigtype = 'trusted'
321 elif req_status.startswith('untrusted_'): 340 elif req_status.startswith('untrusted_'):
322 expected_sigtype = 'untrusted' 341 expected_sigtype = 'untrusted'
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
361 expected_printed = (expected_printed_signum, expected_sigtype) 380 expected_printed = (expected_printed_signum, expected_sigtype)
362 actual_printed = GetNaClSignalInfoFromStderr(stderr) 381 actual_printed = GetNaClSignalInfoFromStderr(stderr)
363 msg = ('\nERROR: Command printed the signal info %s to stderr ' 382 msg = ('\nERROR: Command printed the signal info %s to stderr '
364 'but we expected %s' % 383 'but we expected %s' %
365 (actual_printed, expected_printed)) 384 (actual_printed, expected_printed))
366 if actual_printed != expected_printed: 385 if actual_printed != expected_printed:
367 Print(msg) 386 Print(msg)
368 failed = True 387 failed = True
369 388
370 if failed: 389 if failed:
371 if stderr is not None: 390 PrintStdStreams(stdout, stderr)
372 Banner('Stdout')
373 Print(stdout)
374 Banner('Stderr')
375 Print(stderr)
376 return not failed 391 return not failed
377 392
378 def CheckTimeBounds(total_time): 393 def CheckTimeBounds(total_time):
379 if GlobalSettings['time_error']: 394 if GlobalSettings['time_error']:
380 if total_time > GlobalSettings['time_error']: 395 if total_time > GlobalSettings['time_error']:
381 Print('ERROR: should have taken less than %f secs' % 396 Print('ERROR: should have taken less than %f secs' %
382 (GlobalSettings['time_error'])) 397 (GlobalSettings['time_error']))
383 return False 398 return False
384 399
385 if GlobalSettings['time_warning']: 400 if GlobalSettings['time_warning']:
(...skipping 14 matching lines...) Expand all
400 actual = getter() 415 actual = getter()
401 if GlobalSettings['filter_regex']: 416 if GlobalSettings['filter_regex']:
402 actual = test_lib.RegexpFilterLines(GlobalSettings['filter_regex'], 417 actual = test_lib.RegexpFilterLines(GlobalSettings['filter_regex'],
403 GlobalSettings['filter_inverse'], 418 GlobalSettings['filter_inverse'],
404 GlobalSettings['filter_group_only'], 419 GlobalSettings['filter_group_only'],
405 actual) 420 actual)
406 if DifferentFromGolden(actual, golden_data, stream): 421 if DifferentFromGolden(actual, golden_data, stream):
407 return False 422 return False
408 return True 423 return True
409 424
410 def ProcessLogOutput(stdout, stderr): 425 def ProcessLogOutputSingle(stdout, stderr):
411 output_processor = GlobalSettings['process_output'] 426 output_processor = GlobalSettings['process_output_single']
412 if output_processor: 427 if not output_processor:
428 return (True, stdout, stderr)
429 else:
413 output_processor_cmd = DestringifyList(output_processor) 430 output_processor_cmd = DestringifyList(output_processor)
414 # Also, get the output from logout (to get NaClLog output in Windows). 431 # Also, get the output from log_file to get NaClLog output in Windows.
415 log_output = open(GlobalSettings['log_file']).read() 432 log_output = open(GlobalSettings['log_file']).read()
416 # Assume the log processor does not care about the order of the lines. 433 # Assume the log processor does not care about the order of the lines.
417 all_output = log_output + stdout + stderr 434 all_output = log_output + stdout + stderr
418 if not test_lib.RunCmdWithInput(output_processor_cmd, all_output): 435 _, retcode, failed, new_stdout, new_stderr = \
436 test_lib.RunTestWithInputOutput(output_processor_cmd, all_output)
437 # Print the result, since we have done some processing and we need
438 # to have the processed data. However, if we intend to process it some
439 # more later via process_output_combined, do not duplicate the data here.
440 # Only print out the final result!
441 if not GlobalSettings['process_output_combined']:
442 PrintStdStreams(new_stdout, new_stderr)
443 if retcode != 0 or failed:
444 return (False, new_stdout, new_stderr)
445 else:
446 return (True, new_stdout, new_stderr)
447
448 def ProcessLogOutputCombined(stdout, stderr):
449 output_processor = GlobalSettings['process_output_combined']
450 if not output_processor:
451 return True
452 else:
453 output_processor_cmd = DestringifyList(output_processor)
454 all_output = stdout + stderr
455 _, retcode, failed, new_stdout, new_stderr = \
456 test_lib.RunTestWithInputOutput(output_processor_cmd, all_output)
457 # Print the result, since we have done some processing.
458 PrintStdStreams(new_stdout, new_stderr)
459 if retcode != 0 or failed:
419 return False 460 return False
420 return True 461 else:
462 return True
421 463
422 def main(argv): 464 def DoRun(command, stdin_data):
423 global GlobalPlatform 465 """
424 global GlobalReportStream 466 Run the command, given stdin_data. Returns a return code (0 is good)
425 command = ProcessOptions(argv) 467 and optionally a captured version of stdout, stderr from the run
426 468 (if the global setting capture_output is true).
427 if GlobalSettings['report']: 469 """
428 GlobalReportStream.append(open(GlobalSettings['report'], 'w')) 470 # Initialize stdout, stderr to indicate we have not captured
429 471 # any of stdout or stderr.
430 if not GlobalSettings['name']: 472 stdout = ''
431 GlobalSettings['name'] = command[0] 473 stderr = ''
432 GlobalSettings['name'] = os.path.basename(GlobalSettings['name'])
433
434 Print(RunMessage())
435
436 if GlobalSettings['osenv']:
437 Banner('setting environment')
438 env_vars = DestringifyList(GlobalSettings['osenv'])
439 else:
440 env_vars = []
441 for env_var in env_vars:
442 key, val = env_var.split('=', 1)
443 Print('[%s] = [%s]' % (key, val))
444 os.putenv(key, val)
445
446 stdin_data = ''
447 if GlobalSettings['stdin']:
448 stdin_data = open(GlobalSettings['stdin'])
449
450 if GlobalSettings['log_file']:
451 try:
452 os.unlink(GlobalSettings['log_file']) # might not pre-exist
453 except OSError:
454 pass
455
456 run_under = GlobalSettings['run_under']
457 if run_under:
458 command = run_under.split(',') + command
459
460 Banner('running %s' % str(command))
461 # print the command in copy-and-pastable fashion
462 print " ".join(env_vars + command)
463
464 if not int(GlobalSettings['capture_output']): 474 if not int(GlobalSettings['capture_output']):
465 # We are only blurting out the stdout and stderr, not capturing it 475 # We are only blurting out the stdout and stderr, not capturing it
466 # for comparison, etc. 476 # for comparison, etc.
467 assert (not GlobalSettings['stdout_golden'] 477 assert (not GlobalSettings['stdout_golden']
468 and not GlobalSettings['stderr_golden'] 478 and not GlobalSettings['stderr_golden']
469 and not GlobalSettings['log_golden'] 479 and not GlobalSettings['log_golden']
470 and not GlobalSettings['filter_regex'] 480 and not GlobalSettings['filter_regex']
471 and not GlobalSettings['filter_inverse'] 481 and not GlobalSettings['filter_inverse']
472 and not GlobalSettings['filter_group_only'] 482 and not GlobalSettings['filter_group_only']
473 and not GlobalSettings['process_output'] 483 and not GlobalSettings['process_output_single']
484 and not GlobalSettings['process_output_combined']
474 ) 485 )
475 # If python ever changes popen.stdout.read() to not risk deadlock, 486 # If python ever changes popen.stdout.read() to not risk deadlock,
476 # we could stream and capture, and use RunTestWithInputOutput instead. 487 # we could stream and capture, and use RunTestWithInputOutput instead.
477 (total_time, exit_status, failed) = test_lib.RunTestWithInput(command, 488 (total_time, exit_status, failed) = test_lib.RunTestWithInput(command,
478 stdin_data) 489 stdin_data)
479 PrintTotalTime(total_time) 490 PrintTotalTime(total_time)
480 if not CheckExitStatus(failed, 491 if not CheckExitStatus(failed,
481 GlobalSettings['exit_status'], 492 GlobalSettings['exit_status'],
482 GlobalSettings['using_nacl_signal_handler'], 493 GlobalSettings['using_nacl_signal_handler'],
483 exit_status, None, None): 494 exit_status, None, None):
484 Print(FailureMessage(total_time)) 495 Print(FailureMessage(total_time))
485 return -1 496 return (-1, stdout, stderr)
486 else: 497 else:
487 (total_time, exit_status, 498 (total_time, exit_status,
488 failed, stdout, stderr) = test_lib.RunTestWithInputOutput( 499 failed, stdout, stderr) = test_lib.RunTestWithInputOutput(
489 command, stdin_data) 500 command, stdin_data)
490 PrintTotalTime(total_time) 501 PrintTotalTime(total_time)
502 # CheckExitStatus may spew stdout/stderr when there is an error.
503 # Otherwise, we do not spew stdout/stderr in this case (capture_output).
491 if not CheckExitStatus(failed, 504 if not CheckExitStatus(failed,
492 GlobalSettings['exit_status'], 505 GlobalSettings['exit_status'],
493 GlobalSettings['using_nacl_signal_handler'], 506 GlobalSettings['using_nacl_signal_handler'],
494 exit_status, stdout, stderr): 507 exit_status, stdout, stderr):
495 Print(FailureMessage(total_time)) 508 Print(FailureMessage(total_time))
496 return -1 509 return (-1, stdout, stderr)
497 if not CheckGoldenOutput(stdout, stderr): 510 if not CheckGoldenOutput(stdout, stderr):
498 Print(FailureMessage(total_time)) 511 Print(FailureMessage(total_time))
499 return -1 512 return (-1, stdout, stderr)
500 if not ProcessLogOutput(stdout, stderr): 513 success, stdout, stderr = ProcessLogOutputSingle(stdout, stderr)
501 Print(FailureMessage(total_time)) 514 if not success:
502 return -1 515 Print(FailureMessage(total_time) + ' ProcessLogOutputSingle failed!')
516 return (-1, stdout, stderr)
503 517
504 if not CheckTimeBounds(total_time): 518 if not CheckTimeBounds(total_time):
505 Print(FailureMessage(total_time)) 519 Print(FailureMessage(total_time))
506 return -1 520 return (-1, stdout, stderr)
507 521
508 Print(SuccessMessage(total_time)) 522 Print(SuccessMessage(total_time))
523 return (0, stdout, stderr)
524
525
526 def Main(argv):
527 command = ProcessOptions(argv)
528
529 if GlobalSettings['report']:
530 GlobalReportStream.append(open(GlobalSettings['report'], 'w'))
531
532 if not GlobalSettings['name']:
533 GlobalSettings['name'] = command[0]
534 GlobalSettings['name'] = os.path.basename(GlobalSettings['name'])
535
536 Print(RunMessage())
537
538 if GlobalSettings['osenv']:
539 Banner('setting environment')
540 env_vars = DestringifyList(GlobalSettings['osenv'])
541 else:
542 env_vars = []
543 for env_var in env_vars:
544 key, val = env_var.split('=', 1)
545 Print('[%s] = [%s]' % (key, val))
546 os.environ[key] = val
547
548 stdin_data = ''
549 if GlobalSettings['stdin']:
550 stdin_data = open(GlobalSettings['stdin'])
551
552 run_under = GlobalSettings['run_under']
553 if run_under:
554 command = run_under.split(',') + command
555
556 Banner('running %s' % str(command))
557 # print the command in copy-and-pastable fashion
558 print " ".join(env_vars + command)
559
560 # Concatenate output when running multiple times (e.g., for timing).
561 combined_stdout = ''
562 combined_stderr = ''
563 cur_runs = 0
564 num_runs = GlobalSettings['num_runs']
565 while cur_runs < num_runs:
566 cur_runs += 1
567 # Clear out previous log_file.
568 if GlobalSettings['log_file']:
569 try:
570 os.unlink(GlobalSettings['log_file']) # might not pre-exist
571 except OSError:
572 pass
573 ret_code, stdout, stderr = DoRun(command, stdin_data)
574 if ret_code != 0:
575 return ret_code
576 combined_stdout += stdout
577 combined_stderr += stderr
578 # Process the log output after all the runs.
579 success = ProcessLogOutputCombined(combined_stdout, combined_stderr)
580 if not success:
581 # Bogus time, since only ProcessLogOutputCombined failed.
582 Print(FailureMessage(0.0) + ' ProcessLogOutputCombined failed!')
583 return -1
509 return 0 584 return 0
510 585
511
512 if __name__ == '__main__': 586 if __name__ == '__main__':
513 retval = main(sys.argv[1:]) 587 retval = Main(sys.argv[1:])
514 # Add some whitepsace to make the logs easier to read. 588 # Add some whitepsace to make the logs easier to read.
515 sys.stdout.write('\n\n') 589 sys.stdout.write('\n\n')
516 sys.exit(retval) 590 sys.exit(retval)
517
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698