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

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: xxx 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',
Nick Bray 2011/11/07 23:39:09 Make this an integer. The option parser will do t
jvoung - send to chromium... 2011/11/09 00:50:54 Done. Leaving capture_output, track_cmdtime, alone
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': '',
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 num_runs = int(GlobalSettings['num_runs'])
140 if num_runs > 1:
141 run_message_extra = ' (run %d times)' % num_runs
142 else:
143 run_message_extra = ''
144 return '[ RUN ] nacl.%s%s' % (GlobalSettings['name'], run_message_extra)
Nick Bray 2011/11/07 23:39:09 It might be clearer to produce the base string and
jvoung - send to chromium... 2011/11/09 00:50:54 Done.
132 145
133 def FailureMessage(total_time): 146 def FailureMessage(total_time):
134 return '[ FAILED ] nacl.%s (%d ms)' % (GlobalSettings['name'], 147 return '[ FAILED ] nacl.%s (%d ms)' % (GlobalSettings['name'],
135 total_time * 1000.0) 148 total_time * 1000.0)
136 149
137 def SuccessMessage(total_time): 150 def SuccessMessage(total_time):
138 return '[ OK ] nacl.%s (%d ms)' % (GlobalSettings['name'], 151 return '[ OK ] nacl.%s (%d ms)' % (GlobalSettings['name'],
139 total_time * 1000.0) 152 total_time * 1000.0)
140 153
141 def LogPerfResult(graph_name, trace_name, value, units): 154 def LogPerfResult(graph_name, trace_name, value, units):
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after
252 except getopt.GetoptError, err: 265 except getopt.GetoptError, err:
253 Print(str(err)) # will print something like 'option -a not recognized' 266 Print(str(err)) # will print something like 'option -a not recognized'
254 sys.exit(-1) 267 sys.exit(-1)
255 268
256 for o, a in opts: 269 for o, a in opts:
257 # strip the leading '--' 270 # strip the leading '--'
258 option = o[2:] 271 option = o[2:]
259 assert option in GlobalSettings 272 assert option in GlobalSettings
260 if option == 'exit_status': 273 if option == 'exit_status':
261 GlobalSettings[option] = a 274 GlobalSettings[option] = a
262 elif type(GlobalSettings[option]) == int: 275 elif type(GlobalSettings[option]) == int:
Nick Bray 2011/11/07 23:39:09 Check this out!
jvoung - send to chromium... 2011/11/09 00:50:54 Done =P
263 GlobalSettings[option] = int(a) 276 GlobalSettings[option] = int(a)
264 else: 277 else:
265 GlobalSettings[option] = a 278 GlobalSettings[option] = a
266 279
267 if (sys.platform == 'win32') and (GlobalSettings['subarch'] == '64'): 280 if (sys.platform == 'win32') and (GlobalSettings['subarch'] == '64'):
268 GlobalPlatform = 'win64' 281 GlobalPlatform = 'win64'
269 else: 282 else:
270 GlobalPlatform = sys.platform 283 GlobalPlatform = sys.platform
271 284
272 # return the unprocessed options, i.e. the command 285 # return the unprocessed options, i.e. the command
(...skipping 30 matching lines...) Expand all
303 words[0] == 'qemu:' and words[1] == 'uncaught' and 316 words[0] == 'qemu:' and words[1] == 'uncaught' and
304 words[2] == 'target' and words[3] == 'signal'): 317 words[2] == 'target' and words[3] == 'signal'):
305 return -int(words[4]) 318 return -int(words[4])
306 return default 319 return default
307 320
308 def FormatExitStatus(number): 321 def FormatExitStatus(number):
309 # Include the hex version because it makes the Windows error exit 322 # Include the hex version because it makes the Windows error exit
310 # statuses (STATUS_*) more recognisable. 323 # statuses (STATUS_*) more recognisable.
311 return '%i (0x%x)' % (number, number & 0xffffffff) 324 return '%i (0x%x)' % (number, number & 0xffffffff)
312 325
326 def PrintStdStreams(stdout, stderr):
327 if stdout:
Nick Bray 2011/11/07 23:39:09 The case in which stdout is printed has changed.
jvoung - send to chromium... 2011/11/09 00:50:54 Leaving this the same as before for now, as noted
328 Banner('Stdout for %s:' % os.path.basename(GlobalSettings['name']))
329 Print(stdout)
330 if stderr:
Nick Bray 2011/11/07 23:39:09 is not None? != ''? It's hard to tell what your in
jvoung - send to chromium... 2011/11/09 00:50:54 Hmm... I don't remember why I did that change anym
331 Banner('Stderr for %s:' % os.path.basename(GlobalSettings['name']))
332 Print(stderr)
333
313 def CheckExitStatus(failed, req_status, using_nacl_signal_handler, 334 def CheckExitStatus(failed, req_status, using_nacl_signal_handler,
314 exit_status, stdout, stderr): 335 exit_status, stdout, stderr):
315 expected_sigtype = 'normal' 336 expected_sigtype = 'normal'
316 if req_status in status_map: 337 if req_status in status_map:
317 expected_statuses = status_map[req_status][GlobalPlatform] 338 expected_statuses = status_map[req_status][GlobalPlatform]
318 if using_nacl_signal_handler: 339 if using_nacl_signal_handler:
319 if req_status.startswith('trusted_'): 340 if req_status.startswith('trusted_'):
320 expected_sigtype = 'trusted' 341 expected_sigtype = 'trusted'
321 elif req_status.startswith('untrusted_'): 342 elif req_status.startswith('untrusted_'):
322 expected_sigtype = 'untrusted' 343 expected_sigtype = 'untrusted'
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
361 expected_printed = (expected_printed_signum, expected_sigtype) 382 expected_printed = (expected_printed_signum, expected_sigtype)
362 actual_printed = GetNaClSignalInfoFromStderr(stderr) 383 actual_printed = GetNaClSignalInfoFromStderr(stderr)
363 msg = ('\nERROR: Command printed the signal info %s to stderr ' 384 msg = ('\nERROR: Command printed the signal info %s to stderr '
364 'but we expected %s' % 385 'but we expected %s' %
365 (actual_printed, expected_printed)) 386 (actual_printed, expected_printed))
366 if actual_printed != expected_printed: 387 if actual_printed != expected_printed:
367 Print(msg) 388 Print(msg)
368 failed = True 389 failed = True
369 390
370 if failed: 391 if failed:
371 if stderr is not None: 392 PrintStdStreams(stdout, stderr)
372 Banner('Stdout')
373 Print(stdout)
374 Banner('Stderr')
375 Print(stderr)
376 return not failed 393 return not failed
377 394
378 def CheckTimeBounds(total_time): 395 def CheckTimeBounds(total_time):
379 if GlobalSettings['time_error']: 396 if GlobalSettings['time_error']:
380 if total_time > GlobalSettings['time_error']: 397 if total_time > GlobalSettings['time_error']:
381 Print('ERROR: should have taken less than %f secs' % 398 Print('ERROR: should have taken less than %f secs' %
382 (GlobalSettings['time_error'])) 399 (GlobalSettings['time_error']))
383 return False 400 return False
384 401
385 if GlobalSettings['time_warning']: 402 if GlobalSettings['time_warning']:
(...skipping 14 matching lines...) Expand all
400 actual = getter() 417 actual = getter()
401 if GlobalSettings['filter_regex']: 418 if GlobalSettings['filter_regex']:
402 actual = test_lib.RegexpFilterLines(GlobalSettings['filter_regex'], 419 actual = test_lib.RegexpFilterLines(GlobalSettings['filter_regex'],
403 GlobalSettings['filter_inverse'], 420 GlobalSettings['filter_inverse'],
404 GlobalSettings['filter_group_only'], 421 GlobalSettings['filter_group_only'],
405 actual) 422 actual)
406 if DifferentFromGolden(actual, golden_data, stream): 423 if DifferentFromGolden(actual, golden_data, stream):
407 return False 424 return False
408 return True 425 return True
409 426
410 def ProcessLogOutput(stdout, stderr): 427 def ProcessLogOutputSingle(stdout, stderr):
411 output_processor = GlobalSettings['process_output'] 428 output_processor = GlobalSettings['process_output_single']
412 if output_processor: 429 if not output_processor:
430 return (True, stdout, stderr)
431 else:
413 output_processor_cmd = DestringifyList(output_processor) 432 output_processor_cmd = DestringifyList(output_processor)
414 # Also, get the output from logout (to get NaClLog output in Windows). 433 # Also, get the output from log_file to get NaClLog output in Windows.
415 log_output = open(GlobalSettings['log_file']).read() 434 log_output = open(GlobalSettings['log_file']).read()
416 # Assume the log processor does not care about the order of the lines. 435 # Assume the log processor does not care about the order of the lines.
417 all_output = log_output + stdout + stderr 436 all_output = log_output + stdout + stderr
418 if not test_lib.RunCmdWithInput(output_processor_cmd, all_output): 437 _, retcode, failed, new_stdout, new_stderr = \
438 test_lib.RunTestWithInputOutput(output_processor_cmd, all_output)
439 # Print the result, since we have done some processing and we need
440 # to have the processed data. However, if we intend to process it some
441 # more later via process_output_combined, do not duplicate the data here.
442 # Only print out the final result!
443 if not GlobalSettings['process_output_combined']:
444 PrintStdStreams(new_stdout, new_stderr)
445 if retcode != 0 or failed:
446 return (False, new_stdout, new_stderr)
447 else:
448 return (True, new_stdout, new_stderr)
449
450 def ProcessLogOutputCombined(stdout, stderr):
451 output_processor = GlobalSettings['process_output_combined']
452 if not output_processor:
453 return True
454 else:
455 output_processor_cmd = DestringifyList(output_processor)
456 all_output = stdout + stderr
457 _, retcode, failed, new_stdout, new_stderr = \
458 test_lib.RunTestWithInputOutput(output_processor_cmd, all_output)
459 # Print the result, since we have done some processing.
460 PrintStdStreams(new_stdout, new_stderr)
461 if retcode != 0 or failed:
419 return False 462 return False
420 return True 463 else:
464 return True
421 465
422 def main(argv): 466 def DoRun(command, stdin_data):
423 global GlobalPlatform 467 """
424 global GlobalReportStream 468 Run the command, given stdin_data. Returns a return code (0 is good)
425 command = ProcessOptions(argv) 469 and optionally a captured version of stdout, stderr from the run
426 470 (if the global setting capture_output is true).
427 if GlobalSettings['report']: 471 """
428 GlobalReportStream.append(open(GlobalSettings['report'], 'w')) 472 # Initialize stdout, stderr to indicate we have not captured
429 473 # any of stdout or stderr.
430 if not GlobalSettings['name']: 474 stdout = ''
431 GlobalSettings['name'] = command[0] 475 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']): 476 if not int(GlobalSettings['capture_output']):
465 # We are only blurting out the stdout and stderr, not capturing it 477 # We are only blurting out the stdout and stderr, not capturing it
466 # for comparison, etc. 478 # for comparison, etc.
467 assert (not GlobalSettings['stdout_golden'] 479 assert (not GlobalSettings['stdout_golden']
468 and not GlobalSettings['stderr_golden'] 480 and not GlobalSettings['stderr_golden']
469 and not GlobalSettings['log_golden'] 481 and not GlobalSettings['log_golden']
470 and not GlobalSettings['filter_regex'] 482 and not GlobalSettings['filter_regex']
471 and not GlobalSettings['filter_inverse'] 483 and not GlobalSettings['filter_inverse']
472 and not GlobalSettings['filter_group_only'] 484 and not GlobalSettings['filter_group_only']
473 and not GlobalSettings['process_output'] 485 and not GlobalSettings['process_output_single']
486 and not GlobalSettings['process_output_combined']
474 ) 487 )
475 # If python ever changes popen.stdout.read() to not risk deadlock, 488 # If python ever changes popen.stdout.read() to not risk deadlock,
476 # we could stream and capture, and use RunTestWithInputOutput instead. 489 # we could stream and capture, and use RunTestWithInputOutput instead.
477 (total_time, exit_status, failed) = test_lib.RunTestWithInput(command, 490 (total_time, exit_status, failed) = test_lib.RunTestWithInput(command,
478 stdin_data) 491 stdin_data)
479 PrintTotalTime(total_time) 492 PrintTotalTime(total_time)
480 if not CheckExitStatus(failed, 493 if not CheckExitStatus(failed,
481 GlobalSettings['exit_status'], 494 GlobalSettings['exit_status'],
482 GlobalSettings['using_nacl_signal_handler'], 495 GlobalSettings['using_nacl_signal_handler'],
483 exit_status, None, None): 496 exit_status, None, None):
484 Print(FailureMessage(total_time)) 497 Print(FailureMessage(total_time))
485 return -1 498 return (-1, stdout, stderr)
486 else: 499 else:
487 (total_time, exit_status, 500 (total_time, exit_status,
488 failed, stdout, stderr) = test_lib.RunTestWithInputOutput( 501 failed, stdout, stderr) = test_lib.RunTestWithInputOutput(
489 command, stdin_data) 502 command, stdin_data)
490 PrintTotalTime(total_time) 503 PrintTotalTime(total_time)
504 # CheckExitStatus may spew stdout/stderr when there is an error.
505 # Otherwise, we do not spew stdout/stderr in this case (capture_output).
491 if not CheckExitStatus(failed, 506 if not CheckExitStatus(failed,
492 GlobalSettings['exit_status'], 507 GlobalSettings['exit_status'],
493 GlobalSettings['using_nacl_signal_handler'], 508 GlobalSettings['using_nacl_signal_handler'],
494 exit_status, stdout, stderr): 509 exit_status, stdout, stderr):
495 Print(FailureMessage(total_time)) 510 Print(FailureMessage(total_time))
496 return -1 511 return (-1, stdout, stderr)
497 if not CheckGoldenOutput(stdout, stderr): 512 if not CheckGoldenOutput(stdout, stderr):
498 Print(FailureMessage(total_time)) 513 Print(FailureMessage(total_time))
499 return -1 514 return (-1, stdout, stderr)
500 if not ProcessLogOutput(stdout, stderr): 515 success, stdout, stderr = ProcessLogOutputSingle(stdout, stderr)
501 Print(FailureMessage(total_time)) 516 if not success:
502 return -1 517 Print(FailureMessage(total_time) + ' ProcessLogOutputSingle failed!')
518 return (-1, stdout, stderr)
503 519
504 if not CheckTimeBounds(total_time): 520 if not CheckTimeBounds(total_time):
505 Print(FailureMessage(total_time)) 521 Print(FailureMessage(total_time))
506 return -1 522 return (-1, stdout, stderr)
507 523
508 Print(SuccessMessage(total_time)) 524 Print(SuccessMessage(total_time))
525 return (0, stdout, stderr)
526
527
528 def main(argv):
Nick Bray 2011/11/07 23:39:09 Capitalize
jvoung - send to chromium... 2011/11/09 00:50:54 capitalize Main? ok.
529 global GlobalPlatform
Nick Bray 2011/11/07 23:39:09 Do not declare globals unless you assign directly
jvoung - send to chromium... 2011/11/09 00:50:54 Done.
530 global GlobalReportStream
531 command = ProcessOptions(argv)
532
533 if GlobalSettings['report']:
534 GlobalReportStream.append(open(GlobalSettings['report'], 'w'))
535
536 if not GlobalSettings['name']:
537 GlobalSettings['name'] = command[0]
538 GlobalSettings['name'] = os.path.basename(GlobalSettings['name'])
539
540 Print(RunMessage())
541
542 if GlobalSettings['osenv']:
543 Banner('setting environment')
544 env_vars = DestringifyList(GlobalSettings['osenv'])
545 else:
546 env_vars = []
547 for env_var in env_vars:
548 key, val = env_var.split('=', 1)
549 Print('[%s] = [%s]' % (key, val))
550 os.putenv(key, val)
Nick Bray 2011/11/07 23:39:09 putenv considered harmful. Assign to os.environ?
jvoung - send to chromium... 2011/11/09 00:50:54 Don't know the specifics (does it sometimes not wo
551
552 stdin_data = ''
553 if GlobalSettings['stdin']:
554 stdin_data = open(GlobalSettings['stdin'])
555
556 run_under = GlobalSettings['run_under']
557 if run_under:
558 command = run_under.split(',') + command
559
560 Banner('running %s' % str(command))
561 # print the command in copy-and-pastable fashion
562 print " ".join(env_vars + command)
563
564 # Concatenate output when running multiple times (e.g., for timing).
565 combined_stdout = ''
566 combined_stderr = ''
567 cur_runs = 0
568 num_runs = int(GlobalSettings['num_runs'])
569 while cur_runs < num_runs:
570 cur_runs += 1
571 # Clear out previous log_file.
572 if GlobalSettings['log_file']:
573 try:
574 os.unlink(GlobalSettings['log_file']) # might not pre-exist
575 except OSError:
576 pass
577 ret_code, stdout, stderr = DoRun(command, stdin_data)
578 if ret_code != 0:
579 return ret_code
580 combined_stdout += stdout
581 combined_stderr += stderr
582 # Process the log output after all the runs.
583 success = ProcessLogOutputCombined(combined_stdout, combined_stderr)
584 if not success:
585 # Bogus time, since only ProcessLogOutputCombined failed.
586 Print(FailureMessage(0.0) + ' ProcessLogOutputCombined failed!')
587 return -1
509 return 0 588 return 0
510 589
511
512 if __name__ == '__main__': 590 if __name__ == '__main__':
513 retval = main(sys.argv[1:]) 591 retval = main(sys.argv[1:])
514 # Add some whitepsace to make the logs easier to read. 592 # Add some whitepsace to make the logs easier to read.
515 sys.stdout.write('\n\n') 593 sys.stdout.write('\n\n')
516 sys.exit(retval) 594 sys.exit(retval)
517
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698