tests/run-tests.py
changeset 52458 a814534aaedd
parent 52326 d24731850b2e
child 52512 c2ee1ad78414
equal deleted inserted replaced
52457:8dede0df9de9 52458:a814534aaedd
   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: