File: test-suite-thread-runner.rb

package info (click to toggle)
ruby-test-unit 3.7.7-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 1,348 kB
  • sloc: ruby: 16,403; makefile: 9
file content (96 lines) | stat: -rw-r--r-- 3,343 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#--
#
# Author:: Tsutomu Katsube.
# Copyright:: Copyright (c) 2024-2025 Tsutomu Katsube. All rights reserved.
# License:: Ruby license.

require_relative "sub-test-result"
require_relative "test-suite-runner"
require_relative "test-thread-run-context"

module Test
  module Unit
    class TestSuiteThreadRunner < TestSuiteRunner
      class << self
        def run_all_tests(result, options)
          n_workers = TestSuiteRunner.n_workers
          test_suite = options[:test_suite]

          queue = Thread::Queue.new
          run_context = TestThreadRunContext.new(self, queue)
          yield(run_context)
          run_context.progress_block.call(TestSuite::STARTED, test_suite.name)
          run_context.progress_block.call(TestSuite::STARTED_OBJECT, test_suite)
          run_context.parallel_unsafe_tests.each(&:call)
          n_workers.times do
            queue << nil
          end

          workers = []
          sub_exceptions = []
          n_workers.times do |i|
            workers << Thread.new(i + 1) do |worker_id|
              begin
                loop do
                  task = queue.pop
                  break if task.nil?
                  catch do |stop_tag|
                    task.call(stop_tag, worker_id)
                  end
                end
              rescue Exception => exception
                sub_exceptions << exception
              end
            end
          end
          workers.each(&:join)

          run_context.progress_block.call(TestSuite::FINISHED, test_suite.name)
          run_context.progress_block.call(TestSuite::FINISHED_OBJECT, test_suite)

          sub_exceptions.each do |exception|
            raise exception
          end
        end
      end

      def run(worker_context, &progress_block)
        worker_context.run_context.progress_block = progress_block
        run_tests_recursive(@test_suite, worker_context, &progress_block)
      end

      private
      def run_tests_recursive(test_suite, worker_context, &progress_block)
        run_context = worker_context.run_context
        if test_suite.have_fixture?
          task = lambda do |stop_tag, worker_id|
            sub_result = SubTestResult.new(worker_context.result)
            sub_result.stop_tag = stop_tag
            sub_runner = TestSuiteRunner.new(test_suite)
            sub_worker_context = WorkerContext.new(worker_id, run_context, sub_result)
            sub_runner.run(sub_worker_context, &progress_block)
          end
          run_context.queue << task
        else
          test_suite.tests.each do |test|
            if test.is_a?(TestSuite)
              run_tests_recursive(test, worker_context, &progress_block)
            elsif test_suite.parallel_safe?
              task = lambda do |stop_tag, worker_id|
                sub_result = SubTestResult.new(worker_context.result)
                sub_result.stop_tag = stop_tag
                sub_worker_context = WorkerContext.new(worker_id, run_context, sub_result)
                run_test(test, sub_worker_context, &progress_block)
              end
              run_context.queue << task
            else
              run_context.parallel_unsafe_tests << lambda do
                run_test(test, worker_context, &progress_block)
              end
            end
          end
        end
      end
    end
  end
end