summaryrefslogtreecommitdiff
path: root/lib/test/unit/parallel.rb
blob: 9296165ebbb872192279dc50404b0a467b022c40 (plain)
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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
require 'test/unit'

module Test
  module Unit
    class Worker < Runner
      class << self
        undef autorun
      end

      alias orig_run_suite _run_suite
      undef _run_suite
      undef _run_suites
      undef run

      def _run_suites suites, type
        suites.map do |suite|
          result = _run_suite(suite, type)
        end
      end

      def _run_suite(suite, type)
        r = report.dup
        orig_stdout = MiniTest::Unit.output
        i,o = IO.pipe
        MiniTest::Unit.output = o

        stdout = STDOUT.dup

        th = Thread.new do
          begin
            while buf = (self.verbose ? i.gets : i.read(5))
              stdout.puts "p #{[buf].pack("m").gsub("\n","")}"
            end
          rescue IOError
          rescue Errno::EPIPE
          end
        end

        e, f, s = @errors, @failures, @skips

        result = orig_run_suite(suite, type)

        MiniTest::Unit.output = orig_stdout

        o.close
        begin
          th.join
        rescue IOError
          raise unless ["stream closed","closed stream"].include? $!.message
        end
        i.close

        result << (report - r)
        result << [@errors-e,@failures-f,@skips-s]
        result << ($: - @old_loadpath)
        result << suite.name

        begin
          STDOUT.puts "done #{[Marshal.dump(result)].pack("m").gsub("\n","")}"
        rescue Errno::EPIPE; end
        return result
      ensure
        MiniTest::Unit.output = orig_stdout
        o.close if o && !o.closed?
        i.close if i && !i.closed?
      end

      def run(args = [])
        process_args args
        @@stop_auto_run = true
        @opts = @options.dup

        Signal.trap(:INT,"IGNORE")
        @old_loadpath = []
        begin
          STDOUT.sync = true
          STDOUT.puts "ready"
          stdin = STDIN.dup
          stdout = STDOUT.dup
          while buf = stdin.gets
            case buf.chomp
            when /^loadpath (.+?)$/
              @old_loadpath = $:.dup
              $:.push(*Marshal.load($1.unpack("m")[0].force_encoding("ASCII-8BIT"))).uniq!
            when /^run (.+?) (.+?)$/
              STDOUT.puts "okay"

              th = Thread.new do
                while puf = stdin.gets
                  if puf.chomp == "quit"
                    begin
                      stdout.puts "bye"
                    rescue Errno::EPIPE; end
                    exit
                  end
                end
              end

              @options = @opts.dup
              suites = MiniTest::Unit::TestCase.test_suites

              begin
                require $1
              rescue LoadError
                th.kill
                STDOUT.puts "after #{[Marshal.dump([$1, $!])].pack("m").gsub("\n","")}"
                STDOUT.puts "ready"
                next
              end
              _run_suites MiniTest::Unit::TestCase.test_suites-suites, $2.to_sym

              STDIN.reopen(stdin)
              STDOUT.reopen(stdout)

              th.kill
              STDOUT.puts "ready"
            when /^quit$/
              begin
                STDOUT.puts "bye"
              rescue Errno::EPIPE; end
              exit
            end
          end
        rescue Errno::EPIPE
        rescue Exception => e
          begin
            STDOUT.puts "bye #{[Marshal.dump(e)].pack("m").gsub("\n","")}"
          rescue Errno::EPIPE;end
          exit
        ensure
          stdin.close
        end
      end
    end
  end
end

Test::Unit::Worker.new.run(ARGV)