Module: Minitest

Defined in:
lib/minitest/parallel_fork.rb

Defined Under Namespace

Modules: Unparallelize

Class Method Summary collapse

Class Method Details

.__run(reporter, options) ⇒ Object

Override __run to use a child forks to run the speeds, which allows for parallel spec execution on MRI.



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
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/minitest/parallel_fork.rb', line 33

def self.__run(reporter, options)
  suites = Runnable.runnables.shuffle
  stat_reporter = parallel_fork_stat_reporter(reporter)

  n = (ENV['NCPU'] || 4).to_i
  reads = []
  if @before_parallel_fork
    @before_parallel_fork.call
  end
  n.times do |i|
    read, write = IO.pipe.each{|io| io.binmode}
    reads << read
    fork do
      read.close
      if @after_parallel_fork
        @after_parallel_fork.call(i)
      end

      p_suites = []
      suites.each_with_index{|s, j| p_suites << s if j % n == i}
      p_suites.each do |s|
        if s.is_a?(Minitest::Parallel::Test::ClassMethods)
         s.extend(Unparallelize)
        end

        s.run(reporter, options)
      end

      data = %w'count assertions results'.map{|meth| stat_reporter.send(meth)}
      if data[-1].any?{|result| !result.is_a?(Minitest::Result)}
        data[-1] = data[-1].map do |result|
          Minitest::Result.from(result)
        end
      end

      data[-1].each do |result|
        result.failures.each do |failure|
          if failure.is_a?(Minitest::UnexpectedError)
            e = failure.respond_to?(:error) ? failure.error : failure.exception
            begin
              Marshal.dump(e)
            rescue TypeError
              e2 = RuntimeError.new("Wrapped undumpable exception for: #{e.class}: #{e.message}")
              e2.set_backtrace(e.backtrace)
              if failure.respond_to?(:error=)
                failure.error = e2
              else
                failure.exception = e2
              end
            end
          end
        end
      end

      write.write(Marshal.dump(data))
      write.close
    end
    write.close
  end

  reads.map{|read| Thread.new(read, &:read)}.map(&:value).each do |data|
    begin
      count, assertions, results = Marshal.load(data)
    rescue ArgumentError
      if @on_parallel_fork_marshal_failure
        @on_parallel_fork_marshal_failure.call
      end
      raise
    end
    stat_reporter.count += count
    stat_reporter.assertions += assertions
    stat_reporter.results.concat(results)
  end

  nil
end

.after_parallel_fork(i = nil, &block) ⇒ Object

Set the after_parallel_fork block to the given block



12
13
14
# File 'lib/minitest/parallel_fork.rb', line 12

def self.after_parallel_fork(i=nil, &block)
  @after_parallel_fork = block
end

.before_parallel_fork(&block) ⇒ Object

Set the before_parallel_fork block to the given block



6
7
8
# File 'lib/minitest/parallel_fork.rb', line 6

def self.before_parallel_fork(&block)
  @before_parallel_fork = block
end

.on_parallel_fork_marshal_failure(&block) ⇒ Object

Set the on_parallel_fork_marshal_failure block to the given block



18
19
20
# File 'lib/minitest/parallel_fork.rb', line 18

def self.on_parallel_fork_marshal_failure(&block)
  @on_parallel_fork_marshal_failure = block
end

.parallel_fork_stat_reporter(reporter) ⇒ Object



27
28
29
# File 'lib/minitest/parallel_fork.rb', line 27

def self.parallel_fork_stat_reporter(reporter)
  reporter.reporters.detect{|rep| rep.is_a?(StatisticsReporter)}
end