1require 'test/unit'
2require 'timeout'
3
4module TestParallel
5  PARALLEL_RB = "#{File.dirname(__FILE__)}/../../lib/test/unit/parallel.rb"
6  TESTS = "#{File.dirname(__FILE__)}/tests_for_parallel"
7
8
9  class TestParallelWorker < Test::Unit::TestCase
10    def setup
11      i, @worker_in = IO.pipe
12      @worker_out, o = IO.pipe
13      @worker_pid = spawn(*@options[:ruby], PARALLEL_RB,
14                          "--ruby", @options[:ruby].join(" "),
15                          "-j", "t1", "-v", out: o, in: i)
16      [i,o].each(&:close)
17    end
18
19    def teardown
20      if @worker_pid && @worker_in
21        begin
22          begin
23            @worker_in.puts "quit"
24          rescue IOError, Errno::EPIPE
25          end
26          timeout(2) do
27            Process.waitpid(@worker_pid)
28          end
29        rescue Timeout::Error
30          begin
31            Process.kill(:KILL, @worker_pid)
32          rescue Errno::ESRCH
33          end
34        end
35      end
36    end
37
38    def test_run
39      timeout(10) do
40        assert_match(/^ready/,@worker_out.gets)
41        @worker_in.puts "run #{TESTS}/ptest_first.rb test"
42        assert_match(/^okay/,@worker_out.gets)
43        assert_match(/^p/,@worker_out.gets)
44        assert_match(/^done/,@worker_out.gets)
45        assert_match(/^ready/,@worker_out.gets)
46      end
47    end
48
49    def test_run_multiple_testcase_in_one_file
50      timeout(10) do
51        assert_match(/^ready/,@worker_out.gets)
52        @worker_in.puts "run #{TESTS}/ptest_second.rb test"
53        assert_match(/^okay/,@worker_out.gets)
54        assert_match(/^p/,@worker_out.gets)
55        assert_match(/^done/,@worker_out.gets)
56        assert_match(/^p/,@worker_out.gets)
57        assert_match(/^done/,@worker_out.gets)
58        assert_match(/^ready/,@worker_out.gets)
59      end
60    end
61
62    def test_accept_run_command_multiple_times
63      timeout(10) do
64        assert_match(/^ready/,@worker_out.gets)
65        @worker_in.puts "run #{TESTS}/ptest_first.rb test"
66        assert_match(/^okay/,@worker_out.gets)
67        assert_match(/^p/,@worker_out.gets)
68        assert_match(/^done/,@worker_out.gets)
69        assert_match(/^ready/,@worker_out.gets)
70        @worker_in.puts "run #{TESTS}/ptest_second.rb test"
71        assert_match(/^okay/,@worker_out.gets)
72        assert_match(/^p/,@worker_out.gets)
73        assert_match(/^done/,@worker_out.gets)
74        assert_match(/^p/,@worker_out.gets)
75        assert_match(/^done/,@worker_out.gets)
76        assert_match(/^ready/,@worker_out.gets)
77      end
78    end
79
80    def test_p
81      timeout(10) do
82        @worker_in.puts "run #{TESTS}/ptest_first.rb test"
83        while buf = @worker_out.gets
84          break if /^p (.+?)$/ =~ buf
85        end
86        assert_match(/TestA#test_nothing_test = \d+\.\d+ s = \.\n/, $1.chomp.unpack("m")[0])
87      end
88    end
89
90    def test_done
91      timeout(10) do
92        @worker_in.puts "run #{TESTS}/ptest_forth.rb test"
93        i = 0
94        6.times { @worker_out.gets }
95        buf = @worker_out.gets
96        assert_match(/^done (.+?)$/, buf)
97
98        /^done (.+?)$/ =~ buf
99
100        result = Marshal.load($1.chomp.unpack("m")[0])
101
102        assert_equal(4, result[0])
103        assert_equal(2, result[1])
104        assert_kind_of(Array,result[2])
105        assert_kind_of(Array,result[3])
106        assert_kind_of(Array,result[4])
107        assert_kind_of(Array,result[2][1])
108        assert_kind_of(MiniTest::Assertion,result[2][0][2])
109        assert_kind_of(MiniTest::Skip,result[2][1][2])
110        assert_kind_of(Exception, result[2][2][2])
111        assert_equal(result[5], "TestE")
112      end
113    end
114
115    def test_quit
116      timeout(10) do
117        @worker_in.puts "quit"
118        assert_match(/^bye$/m,@worker_out.read)
119      end
120    end
121  end
122
123  class TestParallel < Test::Unit::TestCase
124    def spawn_runner(*opt_args)
125      @test_out, o = IO.pipe
126      @test_pid = spawn(*@options[:ruby], TESTS+"/runner.rb",
127                        "--ruby", @options[:ruby].join(" "),
128                        "-j","t1",*opt_args, out: o, err: o)
129      o.close
130    end
131
132    def teardown
133      begin
134        if @test_pid
135          timeout(2) do
136            Process.waitpid(@test_pid)
137          end
138        end
139      rescue Timeout::Error
140        Process.kill(:KILL, @test_pid) if @test_pid
141      ensure
142        @test_out.close if @test_out
143      end
144    end
145
146    def test_ignore_jzero
147      @test_out, o = IO.pipe
148      @test_pid = spawn(*@options[:ruby], TESTS+"/runner.rb",
149                        "--ruby", @options[:ruby].join(" "),
150                        "-j","0", out: File::NULL, err: o)
151      o.close
152      timeout(10) {
153        assert_match(/Error: parameter of -j option should be greater than 0/,@test_out.read)
154      }
155    end
156
157    def test_should_run_all_without_any_leaks
158      spawn_runner
159      buf = timeout(10){@test_out.read}
160      assert_match(/^[SFE\.]{8}$/,buf)
161    end
162
163    def test_should_retry_failed_on_workers
164      spawn_runner
165      buf = timeout(10){@test_out.read}
166      assert_match(/^Retrying\.+$/,buf)
167    end
168
169    def test_no_retry_option
170      spawn_runner "--no-retry"
171      buf = timeout(10){@test_out.read}
172      refute_match(/^Retrying\.+$/,buf)
173      assert_match(/^ +\d+\) Failure:\ntest_fail_at_worker\(TestD\)/,buf)
174    end
175
176    def test_jobs_status
177      spawn_runner "--jobs-status"
178      buf = timeout(10){@test_out.read}
179      assert_match(/\d+=ptest_(first|second|third|forth) */,buf)
180    end
181
182    def test_separate
183      # this test depends to --jobs-status
184      spawn_runner "--jobs-status", "--separate"
185      buf = timeout(10){@test_out.read}
186      assert(buf.scan(/(\d+?)[:=]/).flatten.uniq.size > 1)
187    end
188  end
189end
190