equal
deleted
inserted
replaced
725 action="store_true", |
725 action="store_true", |
726 help="don't report skip tests verbosely", |
726 help="don't report skip tests verbosely", |
727 ) |
727 ) |
728 reporting.add_argument( |
728 reporting.add_argument( |
729 "--time", action="store_true", help="time how long each test takes" |
729 "--time", action="store_true", help="time how long each test takes" |
|
730 ) |
|
731 reporting.add_argument( |
|
732 "--tail-report", |
|
733 action="store_true", |
|
734 help="display information about the final test of each thread", |
730 ) |
735 ) |
731 reporting.add_argument("--view", help="external diff viewer") |
736 reporting.add_argument("--view", help="external diff viewer") |
732 reporting.add_argument( |
737 reporting.add_argument( |
733 "--xunit", help="record xunit results at specified path" |
738 "--xunit", help="record xunit results at specified path" |
734 ) |
739 ) |
2562 keywords=None, |
2567 keywords=None, |
2563 loop=False, |
2568 loop=False, |
2564 runs_per_test=1, |
2569 runs_per_test=1, |
2565 loadtest=None, |
2570 loadtest=None, |
2566 showchannels=False, |
2571 showchannels=False, |
|
2572 tail_report=False, |
2567 *args, |
2573 *args, |
2568 **kwargs, |
2574 **kwargs, |
2569 ): |
2575 ): |
2570 """Create a new instance that can run tests with a configuration. |
2576 """Create a new instance that can run tests with a configuration. |
2571 |
2577 |
2599 self._keywords = keywords |
2605 self._keywords = keywords |
2600 self._loop = loop |
2606 self._loop = loop |
2601 self._runs_per_test = runs_per_test |
2607 self._runs_per_test = runs_per_test |
2602 self._loadtest = loadtest |
2608 self._loadtest = loadtest |
2603 self._showchannels = showchannels |
2609 self._showchannels = showchannels |
|
2610 self._tail_report = tail_report |
2604 |
2611 |
2605 def run(self, result): |
2612 def run(self, result): |
2606 # We have a number of filters that need to be applied. We do this |
2613 # We have a number of filters that need to be applied. We do this |
2607 # here instead of inside Test because it makes the running logic for |
2614 # here instead of inside Test because it makes the running logic for |
2608 # Test simpler. |
2615 # Test simpler. |
2661 break |
2668 break |
2662 else: |
2669 else: |
2663 raise ValueError('Could not find output channel') |
2670 raise ValueError('Could not find output channel') |
2664 channels[channel] = "=" + test.name[5:].split(".")[0] |
2671 channels[channel] = "=" + test.name[5:].split(".")[0] |
2665 |
2672 |
2666 r = None |
2673 r = (None, test, None) |
2667 try: |
2674 try: |
2668 test(result) |
2675 test(result) |
2669 except KeyboardInterrupt: |
2676 except KeyboardInterrupt: |
2670 pass |
2677 pass |
2671 except: # re-raises |
2678 except: # re-raises |
2701 |
2708 |
2702 if self._showchannels: |
2709 if self._showchannels: |
2703 statthread = threading.Thread(target=stat, name="stat") |
2710 statthread = threading.Thread(target=stat, name="stat") |
2704 statthread.start() |
2711 statthread.start() |
2705 |
2712 |
|
2713 start_time = time.monotonic() |
|
2714 tail_data = [] |
2706 try: |
2715 try: |
2707 while tests or running: |
2716 while tests or running: |
|
2717 finished = None |
2708 if not done.empty() or running == self._jobs or not tests: |
2718 if not done.empty() or running == self._jobs or not tests: |
2709 try: |
2719 try: |
2710 done.get(True, 1) |
2720 _, finished, _ = done.get(True, 1) |
2711 running -= 1 |
2721 running -= 1 |
2712 if result and result.shouldStop: |
2722 if result and result.shouldStop: |
2713 stoppedearly = True |
2723 stoppedearly = True |
2714 break |
2724 break |
2715 except queue.Empty: |
2725 except queue.Empty: |
2728 t = threading.Thread( |
2738 t = threading.Thread( |
2729 target=job, name=test.name, args=(test, result) |
2739 target=job, name=test.name, args=(test, result) |
2730 ) |
2740 ) |
2731 t.start() |
2741 t.start() |
2732 running += 1 |
2742 running += 1 |
|
2743 if finished is not None and running < self._jobs: |
|
2744 tail_data.append((finished, running, time.monotonic())) |
2733 |
2745 |
2734 # If we stop early we still need to wait on started tests to |
2746 # If we stop early we still need to wait on started tests to |
2735 # finish. Otherwise, there is a race between the test completing |
2747 # finish. Otherwise, there is a race between the test completing |
2736 # and the test's cleanup code running. This could result in the |
2748 # and the test's cleanup code running. This could result in the |
2737 # test reporting incorrect. |
2749 # test reporting incorrect. |
2744 continue |
2756 continue |
2745 except KeyboardInterrupt: |
2757 except KeyboardInterrupt: |
2746 for test in runtests: |
2758 for test in runtests: |
2747 test.abort() |
2759 test.abort() |
2748 |
2760 |
2749 channels = [] |
2761 if self._tail_report: |
|
2762 with iolock: |
|
2763 sys.stdout.write('\n### test tail-report ###\n') |
|
2764 sys.stdout.flush() |
|
2765 channels = [] |
|
2766 for test, remaining, end_time in tail_data: |
|
2767 m = "[% 13.6f s] %d tests still running; finished %s\n" |
|
2768 m %= (end_time - start_time, remaining, test) |
|
2769 sys.stdout.write(m) |
|
2770 sys.stdout.flush() |
2750 |
2771 |
2751 return result |
2772 return result |
2752 |
2773 |
2753 |
2774 |
2754 # Save the most recent 5 wall-clock runtimes of each test to a |
2775 # Save the most recent 5 wall-clock runtimes of each test to a |
2838 failed = len(self._result.failures) |
2859 failed = len(self._result.failures) |
2839 skipped = len(self._result.skipped) |
2860 skipped = len(self._result.skipped) |
2840 ignored = len(self._result.ignored) |
2861 ignored = len(self._result.ignored) |
2841 |
2862 |
2842 with iolock: |
2863 with iolock: |
2843 self.stream.writeln('') |
2864 if not self._runner.options.tail_report: |
|
2865 self.stream.writeln('') |
2844 |
2866 |
2845 if not self._runner.options.noskips: |
2867 if not self._runner.options.noskips: |
2846 for test, msg in sorted( |
2868 for test, msg in sorted( |
2847 self._result.skipped, key=lambda s: s[0].name |
2869 self._result.skipped, key=lambda s: s[0].name |
2848 ): |
2870 ): |
3653 blacklist=self.options.blacklist, |
3675 blacklist=self.options.blacklist, |
3654 keywords=kws, |
3676 keywords=kws, |
3655 loop=self.options.loop, |
3677 loop=self.options.loop, |
3656 runs_per_test=self.options.runs_per_test, |
3678 runs_per_test=self.options.runs_per_test, |
3657 showchannels=self.options.showchannels, |
3679 showchannels=self.options.showchannels, |
|
3680 tail_report=self.options.tail_report, |
3658 tests=tests, |
3681 tests=tests, |
3659 loadtest=_reloadtest, |
3682 loadtest=_reloadtest, |
3660 ) |
3683 ) |
3661 verbosity = 1 |
3684 verbosity = 1 |
3662 if self.options.list_tests: |
3685 if self.options.list_tests: |