OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright 2014 the V8 project authors. All rights reserved. | 2 # Copyright 2014 the V8 project 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 Performance runner for d8. | 7 Performance runner for d8. |
8 | 8 |
9 Call e.g. with tools/run-perf.py --arch ia32 some_suite.json | 9 Call e.g. with tools/run-perf.py --arch ia32 some_suite.json |
10 | 10 |
(...skipping 197 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
208 | 208 |
209 def GetResults(self): | 209 def GetResults(self): |
210 return Results([{ | 210 return Results([{ |
211 "graphs": self.graphs, | 211 "graphs": self.graphs, |
212 "units": self.units, | 212 "units": self.units, |
213 "results": self.results, | 213 "results": self.results, |
214 "stddev": self.stddev, | 214 "stddev": self.stddev, |
215 }], self.errors) | 215 }], self.errors) |
216 | 216 |
217 | 217 |
218 def AccumulateResults(graph_names, trace_configs, iter_output, calc_total): | 218 class NullMeasurement(object): |
| 219 """Null object to avoid having extra logic for configurations that didn't |
| 220 run like running without patch on trybots. |
| 221 """ |
| 222 def ConsumeOutput(self, stdout): |
| 223 pass |
| 224 |
| 225 def GetResults(self): |
| 226 return Results() |
| 227 |
| 228 |
| 229 def Unzip(iterable): |
| 230 left = [] |
| 231 right = [] |
| 232 for l, r in iterable: |
| 233 left.append(l) |
| 234 right.append(r) |
| 235 return lambda: iter(left), lambda: iter(right) |
| 236 |
| 237 |
| 238 def AccumulateResults( |
| 239 graph_names, trace_configs, iter_output, trybot, no_patch, calc_total): |
219 """Iterates over the output of multiple benchmark reruns and accumulates | 240 """Iterates over the output of multiple benchmark reruns and accumulates |
220 results for a configured list of traces. | 241 results for a configured list of traces. |
221 | 242 |
222 Args: | 243 Args: |
223 graph_names: List of names that configure the base path of the traces. E.g. | 244 graph_names: List of names that configure the base path of the traces. E.g. |
224 ['v8', 'Octane']. | 245 ['v8', 'Octane']. |
225 trace_configs: List of "TraceConfig" instances. Each trace config defines | 246 trace_configs: List of "TraceConfig" instances. Each trace config defines |
226 how to perform a measurement. | 247 how to perform a measurement. |
227 iter_output: Iterator over the standard output of each test run. | 248 iter_output: Iterator over the standard output of each test run. |
| 249 trybot: Indicates that this is run in trybot mode, i.e. run twice, once |
| 250 with once without patch. |
| 251 no_patch: Indicates weather this is a trybot run without patch. |
228 calc_total: Boolean flag to speficy the calculation of a summary trace. | 252 calc_total: Boolean flag to speficy the calculation of a summary trace. |
229 Returns: A "Results" object. | 253 Returns: A "Results" object. |
230 """ | 254 """ |
231 measurements = [trace.CreateMeasurement() for trace in trace_configs] | 255 measurements = [ |
| 256 trace.CreateMeasurement(trybot, no_patch) for trace in trace_configs] |
232 for stdout in iter_output(): | 257 for stdout in iter_output(): |
233 for measurement in measurements: | 258 for measurement in measurements: |
234 measurement.ConsumeOutput(stdout) | 259 measurement.ConsumeOutput(stdout) |
235 | 260 |
236 res = reduce(lambda r, m: r + m.GetResults(), measurements, Results()) | 261 res = reduce(lambda r, m: r + m.GetResults(), measurements, Results()) |
237 | 262 |
238 if not res.traces or not calc_total: | 263 if not res.traces or not calc_total: |
239 return res | 264 return res |
240 | 265 |
241 # Assume all traces have the same structure. | 266 # Assume all traces have the same structure. |
(...skipping 22 matching lines...) Expand all Loading... |
264 | 289 |
265 Args: | 290 Args: |
266 graph_names: List of names that configure the base path of the traces. E.g. | 291 graph_names: List of names that configure the base path of the traces. E.g. |
267 ['v8', 'Octane']. | 292 ['v8', 'Octane']. |
268 suite_units: Measurement default units as defined by the benchmark suite. | 293 suite_units: Measurement default units as defined by the benchmark suite. |
269 iter_output: Iterator over the standard output of each test run. | 294 iter_output: Iterator over the standard output of each test run. |
270 Returns: A "Results" object. | 295 Returns: A "Results" object. |
271 """ | 296 """ |
272 traces = OrderedDict() | 297 traces = OrderedDict() |
273 for stdout in iter_output(): | 298 for stdout in iter_output(): |
| 299 if stdout is None: |
| 300 # The None value is used as a null object to simplify logic. |
| 301 continue |
274 for line in stdout.strip().splitlines(): | 302 for line in stdout.strip().splitlines(): |
275 match = GENERIC_RESULTS_RE.match(line) | 303 match = GENERIC_RESULTS_RE.match(line) |
276 if match: | 304 if match: |
277 stddev = "" | 305 stddev = "" |
278 graph = match.group(1) | 306 graph = match.group(1) |
279 trace = match.group(2) | 307 trace = match.group(2) |
280 body = match.group(3) | 308 body = match.group(3) |
281 units = match.group(4) | 309 units = match.group(4) |
282 match_stddev = RESULT_STDDEV_RE.match(body) | 310 match_stddev = RESULT_STDDEV_RE.match(body) |
283 match_list = RESULT_LIST_RE.match(body) | 311 match_list = RESULT_LIST_RE.match(body) |
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
387 stddev_default = None | 415 stddev_default = None |
388 self.stddev_regexp = suite.get("stddev_regexp", stddev_default) | 416 self.stddev_regexp = suite.get("stddev_regexp", stddev_default) |
389 | 417 |
390 | 418 |
391 class TraceConfig(GraphConfig): | 419 class TraceConfig(GraphConfig): |
392 """Represents a leaf in the suite tree structure.""" | 420 """Represents a leaf in the suite tree structure.""" |
393 def __init__(self, suite, parent, arch): | 421 def __init__(self, suite, parent, arch): |
394 super(TraceConfig, self).__init__(suite, parent, arch) | 422 super(TraceConfig, self).__init__(suite, parent, arch) |
395 assert self.results_regexp | 423 assert self.results_regexp |
396 | 424 |
397 def CreateMeasurement(self): | 425 def CreateMeasurement(self, trybot, no_patch): |
| 426 if not trybot and no_patch: |
| 427 # Use null object for no-patch logic if this is not a trybot run. |
| 428 return NullMeasurement() |
| 429 |
398 return Measurement( | 430 return Measurement( |
399 self.graphs, | 431 self.graphs, |
400 self.units, | 432 self.units, |
401 self.results_regexp, | 433 self.results_regexp, |
402 self.stddev_regexp, | 434 self.stddev_regexp, |
403 ) | 435 ) |
404 | 436 |
405 | 437 |
406 class RunnableConfig(GraphConfig): | 438 class RunnableConfig(GraphConfig): |
407 """Represents a runnable suite definition (i.e. has a main file). | 439 """Represents a runnable suite definition (i.e. has a main file). |
(...skipping 13 matching lines...) Expand all Loading... |
421 | 453 |
422 def GetCommandFlags(self, extra_flags=None): | 454 def GetCommandFlags(self, extra_flags=None): |
423 suffix = ["--"] + self.test_flags if self.test_flags else [] | 455 suffix = ["--"] + self.test_flags if self.test_flags else [] |
424 return self.flags + (extra_flags or []) + [self.main] + suffix | 456 return self.flags + (extra_flags or []) + [self.main] + suffix |
425 | 457 |
426 def GetCommand(self, shell_dir, extra_flags=None): | 458 def GetCommand(self, shell_dir, extra_flags=None): |
427 # TODO(machenbach): This requires +.exe if run on windows. | 459 # TODO(machenbach): This requires +.exe if run on windows. |
428 cmd = [os.path.join(shell_dir, self.binary)] | 460 cmd = [os.path.join(shell_dir, self.binary)] |
429 return cmd + self.GetCommandFlags(extra_flags=extra_flags) | 461 return cmd + self.GetCommandFlags(extra_flags=extra_flags) |
430 | 462 |
431 def Run(self, runner): | 463 def Run(self, runner, trybot): |
432 """Iterates over several runs and handles the output for all traces.""" | 464 """Iterates over several runs and handles the output for all traces.""" |
433 return AccumulateResults(self.graphs, self._children, runner, self.total) | 465 stdout_with_patch, stdout_no_patch = Unzip(runner()) |
| 466 return ( |
| 467 AccumulateResults( |
| 468 self.graphs, |
| 469 self._children, |
| 470 iter_output=stdout_with_patch, |
| 471 trybot=trybot, |
| 472 no_patch=False, |
| 473 calc_total=self.total, |
| 474 ), |
| 475 AccumulateResults( |
| 476 self.graphs, |
| 477 self._children, |
| 478 iter_output=stdout_no_patch, |
| 479 trybot=trybot, |
| 480 no_patch=True, |
| 481 calc_total=self.total, |
| 482 ), |
| 483 ) |
434 | 484 |
435 | 485 |
436 class RunnableTraceConfig(TraceConfig, RunnableConfig): | 486 class RunnableTraceConfig(TraceConfig, RunnableConfig): |
437 """Represents a runnable suite definition that is a leaf.""" | 487 """Represents a runnable suite definition that is a leaf.""" |
438 def __init__(self, suite, parent, arch): | 488 def __init__(self, suite, parent, arch): |
439 super(RunnableTraceConfig, self).__init__(suite, parent, arch) | 489 super(RunnableTraceConfig, self).__init__(suite, parent, arch) |
440 | 490 |
441 def Run(self, runner): | 491 def Run(self, runner, trybot): |
442 """Iterates over several runs and handles the output.""" | 492 """Iterates over several runs and handles the output.""" |
443 measurement = self.CreateMeasurement() | 493 measurement_with_patch = self.CreateMeasurement(trybot, False) |
444 for stdout in runner(): | 494 measurement_no_patch = self.CreateMeasurement(trybot, True) |
445 measurement.ConsumeOutput(stdout) | 495 for stdout_with_patch, stdout_no_patch in runner(): |
446 return measurement.GetResults() | 496 measurement_with_patch.ConsumeOutput(stdout_with_patch) |
| 497 measurement_no_patch.ConsumeOutput(stdout_no_patch) |
| 498 return ( |
| 499 measurement_with_patch.GetResults(), |
| 500 measurement_no_patch.GetResults(), |
| 501 ) |
447 | 502 |
448 | 503 |
449 class RunnableGenericConfig(RunnableConfig): | 504 class RunnableGenericConfig(RunnableConfig): |
450 """Represents a runnable suite definition with generic traces.""" | 505 """Represents a runnable suite definition with generic traces.""" |
451 def __init__(self, suite, parent, arch): | 506 def __init__(self, suite, parent, arch): |
452 super(RunnableGenericConfig, self).__init__(suite, parent, arch) | 507 super(RunnableGenericConfig, self).__init__(suite, parent, arch) |
453 | 508 |
454 def Run(self, runner): | 509 def Run(self, runner, trybot): |
455 return AccumulateGenericResults(self.graphs, self.units, runner) | 510 stdout_with_patch, stdout_no_patch = Unzip(runner()) |
| 511 return ( |
| 512 AccumulateGenericResults(self.graphs, self.units, stdout_with_patch), |
| 513 AccumulateGenericResults(self.graphs, self.units, stdout_no_patch), |
| 514 ) |
456 | 515 |
457 | 516 |
458 def MakeGraphConfig(suite, arch, parent): | 517 def MakeGraphConfig(suite, arch, parent): |
459 """Factory method for making graph configuration objects.""" | 518 """Factory method for making graph configuration objects.""" |
460 if isinstance(parent, RunnableConfig): | 519 if isinstance(parent, RunnableConfig): |
461 # Below a runnable can only be traces. | 520 # Below a runnable can only be traces. |
462 return TraceConfig(suite, parent, arch) | 521 return TraceConfig(suite, parent, arch) |
463 elif suite.get("main") is not None: | 522 elif suite.get("main") is not None: |
464 # A main file makes this graph runnable. Empty strings are accepted. | 523 # A main file makes this graph runnable. Empty strings are accepted. |
465 if suite.get("tests"): | 524 if suite.get("tests"): |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
507 for child in node._children: | 566 for child in node._children: |
508 for result in FlattenRunnables(child, node_cb): | 567 for result in FlattenRunnables(child, node_cb): |
509 yield result | 568 yield result |
510 else: # pragma: no cover | 569 else: # pragma: no cover |
511 raise Exception("Invalid suite configuration.") | 570 raise Exception("Invalid suite configuration.") |
512 | 571 |
513 | 572 |
514 class Platform(object): | 573 class Platform(object): |
515 def __init__(self, options): | 574 def __init__(self, options): |
516 self.shell_dir = options.shell_dir | 575 self.shell_dir = options.shell_dir |
| 576 self.shell_dir_no_patch = options.shell_dir_no_patch |
517 self.extra_flags = options.extra_flags.split() | 577 self.extra_flags = options.extra_flags.split() |
518 | 578 |
519 @staticmethod | 579 @staticmethod |
520 def GetPlatform(options): | 580 def GetPlatform(options): |
521 if options.android_build_tools: | 581 if options.android_build_tools: |
522 return AndroidPlatform(options) | 582 return AndroidPlatform(options) |
523 else: | 583 else: |
524 return DesktopPlatform(options) | 584 return DesktopPlatform(options) |
525 | 585 |
| 586 def _Run(self, runnable, count, no_patch=False): |
| 587 raise NotImplementedError() # pragma: no cover |
| 588 |
| 589 def Run(self, runnable, count): |
| 590 """Execute the benchmark's main file. |
| 591 |
| 592 If options.shell_dir_no_patch is specified, the benchmark is run once with |
| 593 and once without patch. |
| 594 Args: |
| 595 runnable: A Runnable benchmark instance. |
| 596 count: The number of this (repeated) run. |
| 597 Returns: A tuple with the benchmark outputs with and without patch. The |
| 598 latter will be None if options.shell_dir_no_patch was not |
| 599 specified. |
| 600 """ |
| 601 stdout = self._Run(runnable, count, no_patch=False) |
| 602 if self.shell_dir_no_patch: |
| 603 return stdout, self._Run(runnable, count, no_patch=True) |
| 604 else: |
| 605 return stdout, None |
| 606 |
526 | 607 |
527 class DesktopPlatform(Platform): | 608 class DesktopPlatform(Platform): |
528 def __init__(self, options): | 609 def __init__(self, options): |
529 super(DesktopPlatform, self).__init__(options) | 610 super(DesktopPlatform, self).__init__(options) |
530 | 611 |
531 def PreExecution(self): | 612 def PreExecution(self): |
532 pass | 613 pass |
533 | 614 |
534 def PostExecution(self): | 615 def PostExecution(self): |
535 pass | 616 pass |
536 | 617 |
537 def PreTests(self, node, path): | 618 def PreTests(self, node, path): |
538 if isinstance(node, RunnableConfig): | 619 if isinstance(node, RunnableConfig): |
539 node.ChangeCWD(path) | 620 node.ChangeCWD(path) |
540 | 621 |
541 def Run(self, runnable, count): | 622 def _Run(self, runnable, count, no_patch=False): |
| 623 suffix = ' - without patch' if no_patch else '' |
| 624 shell_dir = self.shell_dir_no_patch if no_patch else self.shell_dir |
| 625 title = ">>> %%s (#%d)%s:" % ((count + 1), suffix) |
542 try: | 626 try: |
543 output = commands.Execute( | 627 output = commands.Execute( |
544 runnable.GetCommand(self.shell_dir, self.extra_flags), | 628 runnable.GetCommand(shell_dir, self.extra_flags), |
545 timeout=runnable.timeout, | 629 timeout=runnable.timeout, |
546 ) | 630 ) |
547 except OSError as e: | 631 except OSError as e: # pragma: no cover |
548 print ">>> OSError (#%d):" % (count + 1) | 632 print title % "OSError" |
549 print e | 633 print e |
550 return "" | 634 return "" |
551 print ">>> Stdout (#%d):" % (count + 1) | 635 print title % "Stdout" |
552 print output.stdout | 636 print output.stdout |
553 if output.stderr: # pragma: no cover | 637 if output.stderr: # pragma: no cover |
554 # Print stderr for debugging. | 638 # Print stderr for debugging. |
555 print ">>> Stderr (#%d):" % (count + 1) | 639 print title % "Stderr" |
556 print output.stderr | 640 print output.stderr |
557 if output.timed_out: | 641 if output.timed_out: |
558 print ">>> Test timed out after %ss." % runnable.timeout | 642 print ">>> Test timed out after %ss." % runnable.timeout |
559 return output.stdout | 643 return output.stdout |
560 | 644 |
561 | 645 |
562 class AndroidPlatform(Platform): # pragma: no cover | 646 class AndroidPlatform(Platform): # pragma: no cover |
563 DEVICE_DIR = "/data/local/tmp/v8/" | 647 DEVICE_DIR = "/data/local/tmp/v8/" |
564 | 648 |
565 def __init__(self, options): | 649 def __init__(self, options): |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
619 # location and then copy them with a shell command. | 703 # location and then copy them with a shell command. |
620 output = self._SendCommand( | 704 output = self._SendCommand( |
621 "push %s %s" % (file_on_host, file_on_device_tmp)) | 705 "push %s %s" % (file_on_host, file_on_device_tmp)) |
622 # Success looks like this: "3035 KB/s (12512056 bytes in 4.025s)". | 706 # Success looks like this: "3035 KB/s (12512056 bytes in 4.025s)". |
623 # Errors look like this: "failed to copy ... ". | 707 # Errors look like this: "failed to copy ... ". |
624 if output and not re.search('^[0-9]', output.splitlines()[-1]): | 708 if output and not re.search('^[0-9]', output.splitlines()[-1]): |
625 logging.critical('PUSH FAILED: ' + output) | 709 logging.critical('PUSH FAILED: ' + output) |
626 self._SendCommand("shell mkdir -p %s" % folder_on_device) | 710 self._SendCommand("shell mkdir -p %s" % folder_on_device) |
627 self._SendCommand("shell cp %s %s" % (file_on_device_tmp, file_on_device)) | 711 self._SendCommand("shell cp %s %s" % (file_on_device_tmp, file_on_device)) |
628 | 712 |
| 713 def _PushExecutable(self, shell_dir, target_dir, binary): |
| 714 self._PushFile(shell_dir, binary, target_dir) |
| 715 |
| 716 # Push external startup data. Backwards compatible for revisions where |
| 717 # these files didn't exist. |
| 718 self._PushFile( |
| 719 shell_dir, |
| 720 "natives_blob.bin", |
| 721 target_dir, |
| 722 skip_if_missing=True, |
| 723 ) |
| 724 self._PushFile( |
| 725 shell_dir, |
| 726 "snapshot_blob.bin", |
| 727 target_dir, |
| 728 skip_if_missing=True, |
| 729 ) |
| 730 |
629 def PreTests(self, node, path): | 731 def PreTests(self, node, path): |
630 suite_dir = os.path.abspath(os.path.dirname(path)) | 732 suite_dir = os.path.abspath(os.path.dirname(path)) |
631 if node.path: | 733 if node.path: |
632 bench_rel = os.path.normpath(os.path.join(*node.path)) | 734 bench_rel = os.path.normpath(os.path.join(*node.path)) |
633 bench_abs = os.path.join(suite_dir, bench_rel) | 735 bench_abs = os.path.join(suite_dir, bench_rel) |
634 else: | 736 else: |
635 bench_rel = "." | 737 bench_rel = "." |
636 bench_abs = suite_dir | 738 bench_abs = suite_dir |
637 | 739 |
638 self._PushFile(self.shell_dir, node.binary, "bin") | 740 self._PushExecutable(self.shell_dir, "bin", node.binary) |
639 | 741 if self.shell_dir_no_patch: |
640 # Push external startup data. Backwards compatible for revisions where | 742 self._PushExecutable( |
641 # these files didn't exist. | 743 self.shell_dir_no_patch, "bin_no_patch", node.binary) |
642 self._PushFile( | |
643 self.shell_dir, | |
644 "natives_blob.bin", | |
645 "bin", | |
646 skip_if_missing=True, | |
647 ) | |
648 self._PushFile( | |
649 self.shell_dir, | |
650 "snapshot_blob.bin", | |
651 "bin", | |
652 skip_if_missing=True, | |
653 ) | |
654 | 744 |
655 if isinstance(node, RunnableConfig): | 745 if isinstance(node, RunnableConfig): |
656 self._PushFile(bench_abs, node.main, bench_rel) | 746 self._PushFile(bench_abs, node.main, bench_rel) |
657 for resource in node.resources: | 747 for resource in node.resources: |
658 self._PushFile(bench_abs, resource, bench_rel) | 748 self._PushFile(bench_abs, resource, bench_rel) |
659 | 749 |
660 def Run(self, runnable, count): | 750 def _Run(self, runnable, count, no_patch=False): |
| 751 suffix = ' - without patch' if no_patch else '' |
| 752 target_dir = "bin_no_patch" if no_patch else "bin" |
| 753 title = ">>> %%s (#%d)%s:" % ((count + 1), suffix) |
661 cache = cache_control.CacheControl(self.device) | 754 cache = cache_control.CacheControl(self.device) |
662 cache.DropRamCaches() | 755 cache.DropRamCaches() |
663 binary_on_device = os.path.join( | 756 binary_on_device = os.path.join( |
664 AndroidPlatform.DEVICE_DIR, "bin", runnable.binary) | 757 AndroidPlatform.DEVICE_DIR, target_dir, runnable.binary) |
665 cmd = [binary_on_device] + runnable.GetCommandFlags(self.extra_flags) | 758 cmd = [binary_on_device] + runnable.GetCommandFlags(self.extra_flags) |
666 | 759 |
667 # Relative path to benchmark directory. | 760 # Relative path to benchmark directory. |
668 if runnable.path: | 761 if runnable.path: |
669 bench_rel = os.path.normpath(os.path.join(*runnable.path)) | 762 bench_rel = os.path.normpath(os.path.join(*runnable.path)) |
670 else: | 763 else: |
671 bench_rel = "." | 764 bench_rel = "." |
672 | 765 |
673 try: | 766 try: |
674 output = self.device.RunShellCommand( | 767 output = self.device.RunShellCommand( |
675 cmd, | 768 cmd, |
676 cwd=os.path.join(AndroidPlatform.DEVICE_DIR, bench_rel), | 769 cwd=os.path.join(AndroidPlatform.DEVICE_DIR, bench_rel), |
677 timeout=runnable.timeout, | 770 timeout=runnable.timeout, |
678 retries=0, | 771 retries=0, |
679 ) | 772 ) |
680 stdout = "\n".join(output) | 773 stdout = "\n".join(output) |
681 print ">>> Stdout (#%d):" % (count + 1) | 774 print title % "Stdout" |
682 print stdout | 775 print stdout |
683 except device_errors.CommandTimeoutError: | 776 except device_errors.CommandTimeoutError: |
684 print ">>> Test timed out after %ss." % runnable.timeout | 777 print ">>> Test timed out after %ss." % runnable.timeout |
685 stdout = "" | 778 stdout = "" |
686 return stdout | 779 return stdout |
687 | 780 |
688 | 781 |
689 # TODO: Implement results_processor. | 782 # TODO: Implement results_processor. |
690 def Main(args): | 783 def Main(args): |
691 logging.getLogger().setLevel(logging.INFO) | 784 logging.getLogger().setLevel(logging.INFO) |
692 parser = optparse.OptionParser() | 785 parser = optparse.OptionParser() |
693 parser.add_option("--android-build-tools", | 786 parser.add_option("--android-build-tools", |
694 help="Path to chromium's build/android. Specifying this " | 787 help="Path to chromium's build/android. Specifying this " |
695 "option will run tests using android platform.") | 788 "option will run tests using android platform.") |
696 parser.add_option("--arch", | 789 parser.add_option("--arch", |
697 help=("The architecture to run tests for, " | 790 help=("The architecture to run tests for, " |
698 "'auto' or 'native' for auto-detect"), | 791 "'auto' or 'native' for auto-detect"), |
699 default="x64") | 792 default="x64") |
700 parser.add_option("--buildbot", | 793 parser.add_option("--buildbot", |
701 help="Adapt to path structure used on buildbots", | 794 help="Adapt to path structure used on buildbots", |
702 default=False, action="store_true") | 795 default=False, action="store_true") |
703 parser.add_option("--device", | 796 parser.add_option("--device", |
704 help="The device ID to run Android tests on. If not given " | 797 help="The device ID to run Android tests on. If not given " |
705 "it will be autodetected.") | 798 "it will be autodetected.") |
706 parser.add_option("--extra-flags", | 799 parser.add_option("--extra-flags", |
707 help="Additional flags to pass to the test executable", | 800 help="Additional flags to pass to the test executable", |
708 default="") | 801 default="") |
709 parser.add_option("--json-test-results", | 802 parser.add_option("--json-test-results", |
710 help="Path to a file for storing json results.") | 803 help="Path to a file for storing json results.") |
| 804 parser.add_option("--json-test-results-no-patch", |
| 805 help="Path to a file for storing json results from run " |
| 806 "without patch.") |
711 parser.add_option("--outdir", help="Base directory with compile output", | 807 parser.add_option("--outdir", help="Base directory with compile output", |
712 default="out") | 808 default="out") |
| 809 parser.add_option("--outdir-no-patch", |
| 810 help="Base directory with compile output without patch") |
713 (options, args) = parser.parse_args(args) | 811 (options, args) = parser.parse_args(args) |
714 | 812 |
715 if len(args) == 0: # pragma: no cover | 813 if len(args) == 0: # pragma: no cover |
716 parser.print_help() | 814 parser.print_help() |
717 return 1 | 815 return 1 |
718 | 816 |
719 if options.arch in ["auto", "native"]: # pragma: no cover | 817 if options.arch in ["auto", "native"]: # pragma: no cover |
720 options.arch = ARCH_GUESS | 818 options.arch = ARCH_GUESS |
721 | 819 |
722 if not options.arch in SUPPORTED_ARCHS: # pragma: no cover | 820 if not options.arch in SUPPORTED_ARCHS: # pragma: no cover |
723 print "Unknown architecture %s" % options.arch | 821 print "Unknown architecture %s" % options.arch |
724 return 1 | 822 return 1 |
725 | 823 |
726 if (options.device and not options.android_build_tools): # pragma: no cover | 824 if options.device and not options.android_build_tools: # pragma: no cover |
727 print "Specifying a device requires Android build tools." | 825 print "Specifying a device requires Android build tools." |
728 return 1 | 826 return 1 |
729 | 827 |
| 828 if (options.json_test_results_no_patch and |
| 829 not options.outdir_no_patch): # pragma: no cover |
| 830 print("For writing json test results without patch, an outdir without " |
| 831 "patch must be specified.") |
| 832 return 1 |
| 833 |
730 workspace = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) | 834 workspace = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) |
731 | 835 |
732 if options.buildbot: | 836 if options.buildbot: |
733 options.shell_dir = os.path.join(workspace, options.outdir, "Release") | 837 build_config = "Release" |
734 else: | 838 else: |
735 options.shell_dir = os.path.join(workspace, options.outdir, | 839 build_config = "%s.release" % options.arch |
736 "%s.release" % options.arch) | 840 |
| 841 options.shell_dir = os.path.join(workspace, options.outdir, build_config) |
| 842 |
| 843 if options.outdir_no_patch: |
| 844 options.shell_dir_no_patch = os.path.join( |
| 845 workspace, options.outdir_no_patch, build_config) |
| 846 else: |
| 847 options.shell_dir_no_patch = None |
737 | 848 |
738 platform = Platform.GetPlatform(options) | 849 platform = Platform.GetPlatform(options) |
739 | 850 |
740 results = Results() | 851 results = Results() |
| 852 results_no_patch = Results() |
741 for path in args: | 853 for path in args: |
742 path = os.path.abspath(path) | 854 path = os.path.abspath(path) |
743 | 855 |
744 if not os.path.exists(path): # pragma: no cover | 856 if not os.path.exists(path): # pragma: no cover |
745 results.errors.append("Configuration file %s does not exist." % path) | 857 results.errors.append("Configuration file %s does not exist." % path) |
746 continue | 858 continue |
747 | 859 |
748 with open(path) as f: | 860 with open(path) as f: |
749 suite = json.loads(f.read()) | 861 suite = json.loads(f.read()) |
750 | 862 |
(...skipping 15 matching lines...) Expand all Loading... |
766 print ">>> Running suite: %s" % "/".join(runnable.graphs) | 878 print ">>> Running suite: %s" % "/".join(runnable.graphs) |
767 | 879 |
768 def Runner(): | 880 def Runner(): |
769 """Output generator that reruns several times.""" | 881 """Output generator that reruns several times.""" |
770 for i in xrange(0, max(1, runnable.run_count)): | 882 for i in xrange(0, max(1, runnable.run_count)): |
771 # TODO(machenbach): Allow timeout per arch like with run_count per | 883 # TODO(machenbach): Allow timeout per arch like with run_count per |
772 # arch. | 884 # arch. |
773 yield platform.Run(runnable, i) | 885 yield platform.Run(runnable, i) |
774 | 886 |
775 # Let runnable iterate over all runs and handle output. | 887 # Let runnable iterate over all runs and handle output. |
776 results += runnable.Run(Runner) | 888 result, result_no_patch = runnable.Run( |
777 | 889 Runner, trybot=options.shell_dir_no_patch) |
| 890 results += result |
| 891 results_no_patch += result_no_patch |
778 platform.PostExecution() | 892 platform.PostExecution() |
779 | 893 |
780 if options.json_test_results: | 894 if options.json_test_results: |
781 results.WriteToFile(options.json_test_results) | 895 results.WriteToFile(options.json_test_results) |
782 else: # pragma: no cover | 896 else: # pragma: no cover |
783 print results | 897 print results |
784 | 898 |
| 899 if options.json_test_results_no_patch: |
| 900 results_no_patch.WriteToFile(options.json_test_results_no_patch) |
| 901 else: # pragma: no cover |
| 902 print results_no_patch |
| 903 |
785 return min(1, len(results.errors)) | 904 return min(1, len(results.errors)) |
786 | 905 |
787 if __name__ == "__main__": # pragma: no cover | 906 if __name__ == "__main__": # pragma: no cover |
788 sys.exit(Main(sys.argv[1:])) | 907 sys.exit(Main(sys.argv[1:])) |
OLD | NEW |