test-queue 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +2 -0
- data/Gemfile.lock +26 -0
- data/README.md +58 -0
- data/bin/minitest-queue +5 -0
- data/bin/rspec-queue +4 -0
- data/lib/test-queue.rb +1 -0
- data/lib/test_queue.rb +8 -0
- data/lib/test_queue/iterator.rb +44 -0
- data/lib/test_queue/runner.rb +201 -0
- data/lib/test_queue/runner/minitest.rb +69 -0
- data/lib/test_queue/runner/rspec.rb +65 -0
- data/lib/test_queue/runner/sample.rb +78 -0
- data/test-queue.gemspec +20 -0
- data/test/sample_spec.rb +21 -0
- data/test/sample_test.rb +21 -0
- metadata +108 -0
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
test-queue (0.1.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
diff-lcs (1.2.4)
|
10
|
+
minitest (4.7.3)
|
11
|
+
rspec (2.13.0)
|
12
|
+
rspec-core (~> 2.13.0)
|
13
|
+
rspec-expectations (~> 2.13.0)
|
14
|
+
rspec-mocks (~> 2.13.0)
|
15
|
+
rspec-core (2.13.1)
|
16
|
+
rspec-expectations (2.13.0)
|
17
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
18
|
+
rspec-mocks (2.13.1)
|
19
|
+
|
20
|
+
PLATFORMS
|
21
|
+
ruby
|
22
|
+
|
23
|
+
DEPENDENCIES
|
24
|
+
minitest
|
25
|
+
rspec
|
26
|
+
test-queue!
|
data/README.md
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
## test-queue
|
2
|
+
|
3
|
+
Yet another parallel test runner, built using a centralized queue to ensure
|
4
|
+
optimal distribution of tests between workers.
|
5
|
+
|
6
|
+
Specifially optimized for CI environments: build statistics from each run
|
7
|
+
are stored locally and used to sort the queue at the beginning of the
|
8
|
+
next run.
|
9
|
+
|
10
|
+
### usage
|
11
|
+
|
12
|
+
```
|
13
|
+
$ minitest-queue $(find test/ -name \*_test.rb)
|
14
|
+
$ rspec-queue $(find spec/ -name \*_spec.rb)
|
15
|
+
```
|
16
|
+
|
17
|
+
### design
|
18
|
+
|
19
|
+
test-queue uses a simple master + pre-fork worker model. The master
|
20
|
+
exposes a unix domain socket server which workers use to grab tests off
|
21
|
+
the queue.
|
22
|
+
|
23
|
+
```
|
24
|
+
─┬─ 21232 minitest-queue master
|
25
|
+
├─── 21571 minitest-queue worker [3] - AuthenticationTest
|
26
|
+
├─── 21568 minitest-queue worker [2] - ApiTest
|
27
|
+
├─── 21565 minitest-queue worker [1] - UsersControllerTest
|
28
|
+
└─── 21562 minitest-queue worker [0] - UserTest
|
29
|
+
```
|
30
|
+
|
31
|
+
### customization
|
32
|
+
|
33
|
+
Since test-queue uses `fork(2)` to spawn off workers, you must ensure each worker
|
34
|
+
runs in an isolated environment. Use the `after_fork` hook with a custom
|
35
|
+
runner to reset any global state:
|
36
|
+
|
37
|
+
``` ruby
|
38
|
+
class CustomMiniTestRunner < TestQueue::Runner::MiniTest
|
39
|
+
def after_fork(num)
|
40
|
+
super
|
41
|
+
|
42
|
+
# use separate mysql database (we assume it exists and has the right schema already)
|
43
|
+
ActiveRecord::Base.configurations['test']['database'] << num.to_s
|
44
|
+
ActiveRecord::Base.establish_connection(:test)
|
45
|
+
|
46
|
+
# use separate redis database
|
47
|
+
$redis.client.db = num
|
48
|
+
$redis.client.reconnect
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
CustomMiniTestRunner.new.execute
|
53
|
+
```
|
54
|
+
|
55
|
+
### see also
|
56
|
+
|
57
|
+
* https://github.com/Shopify/rails_parallel
|
58
|
+
* https://github.com/grosser/parallel_tests
|
data/bin/minitest-queue
ADDED
data/bin/rspec-queue
ADDED
data/lib/test-queue.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'test_queue'
|
data/lib/test_queue.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
module TestQueue
|
2
|
+
class Iterator
|
3
|
+
attr_reader :stats
|
4
|
+
|
5
|
+
def initialize(sock)
|
6
|
+
@sock = sock
|
7
|
+
@done = false
|
8
|
+
@stats = {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def each
|
12
|
+
fail 'already used this iterator' if @done
|
13
|
+
|
14
|
+
while true
|
15
|
+
client = UNIXSocket.new(@sock)
|
16
|
+
r, w, e = IO.select([client], nil, [client], nil)
|
17
|
+
break if !e.empty?
|
18
|
+
|
19
|
+
if data = client.read(16384)
|
20
|
+
client.close
|
21
|
+
item = Marshal.load(data)
|
22
|
+
|
23
|
+
start = Time.now
|
24
|
+
yield item
|
25
|
+
@stats[item] = Time.now - start
|
26
|
+
else
|
27
|
+
break
|
28
|
+
end
|
29
|
+
end
|
30
|
+
rescue Errno::ENOENT, Errno::ECONNRESET, Errno::ECONNREFUSED
|
31
|
+
ensure
|
32
|
+
@done = true
|
33
|
+
File.open("/tmp/test_queue_worker_#{$$}_stats", "wb") do |f|
|
34
|
+
f.write Marshal.dump(@stats)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
include Enumerable
|
39
|
+
|
40
|
+
def empty?
|
41
|
+
false
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,201 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
module TestQueue
|
5
|
+
class Worker
|
6
|
+
attr_accessor :pid, :status, :output, :stats, :num
|
7
|
+
attr_accessor :start_time, :end_time
|
8
|
+
|
9
|
+
def initialize(pid, num)
|
10
|
+
@pid = pid
|
11
|
+
@num = num
|
12
|
+
@start_time = Time.now
|
13
|
+
@output = ''
|
14
|
+
end
|
15
|
+
|
16
|
+
def lines
|
17
|
+
@output.split("\n")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Runner
|
22
|
+
attr_accessor :concurrency
|
23
|
+
|
24
|
+
def initialize(queue, concurrency=nil)
|
25
|
+
raise ArgumentError, 'array required' unless Array === queue
|
26
|
+
|
27
|
+
@queue = queue
|
28
|
+
|
29
|
+
@workers = {}
|
30
|
+
@completed = []
|
31
|
+
|
32
|
+
@concurrency =
|
33
|
+
concurrency ||
|
34
|
+
(ENV['TEST_QUEUE_WORKERS'] && ENV['TEST_QUEUE_WORKERS'].to_i) ||
|
35
|
+
if File.exists?('/proc/cpuinfo')
|
36
|
+
File.read('/proc/cpuinfo').split("\n").grep(/processor/).size
|
37
|
+
elsif RUBY_PLATFORM =~ /darwin/
|
38
|
+
`/usr/sbin/sysctl -n hw.activecpu`.to_i
|
39
|
+
else
|
40
|
+
2
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def stats
|
45
|
+
@stats ||=
|
46
|
+
if File.exists?('.test_queue_stats')
|
47
|
+
Marshal.load(IO.binread('.test_queue_stats'))
|
48
|
+
else
|
49
|
+
{}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def execute
|
54
|
+
@concurrency > 0 ?
|
55
|
+
execute_parallel :
|
56
|
+
execute_sequential
|
57
|
+
ensure
|
58
|
+
puts
|
59
|
+
puts "==> Summary"
|
60
|
+
puts
|
61
|
+
|
62
|
+
@failures = ''
|
63
|
+
@completed.each do |worker|
|
64
|
+
summary, failures = summarize_worker(worker)
|
65
|
+
@failures << failures if failures
|
66
|
+
|
67
|
+
puts " [%d] %55s in %.4fs (pid %d exit %d)" % [
|
68
|
+
worker.num,
|
69
|
+
summary,
|
70
|
+
worker.end_time - worker.start_time,
|
71
|
+
worker.pid,
|
72
|
+
worker.status.exitstatus
|
73
|
+
]
|
74
|
+
end
|
75
|
+
|
76
|
+
unless @failures.empty?
|
77
|
+
puts
|
78
|
+
puts "==> Failures"
|
79
|
+
puts
|
80
|
+
puts @failures
|
81
|
+
end
|
82
|
+
|
83
|
+
puts
|
84
|
+
|
85
|
+
File.open('.test_queue_stats', 'wb') do |f|
|
86
|
+
f.write Marshal.dump(@stats)
|
87
|
+
end
|
88
|
+
|
89
|
+
exit! @completed.inject(0){ |s, worker| s + worker.status.exitstatus }
|
90
|
+
end
|
91
|
+
|
92
|
+
def execute_sequential
|
93
|
+
exit! run_worker(@queue)
|
94
|
+
end
|
95
|
+
|
96
|
+
def execute_parallel
|
97
|
+
start_master
|
98
|
+
spawn_workers
|
99
|
+
distribute_queue
|
100
|
+
ensure
|
101
|
+
stop_master
|
102
|
+
|
103
|
+
@workers.each do |pid, worker|
|
104
|
+
Process.kill 'KILL', pid
|
105
|
+
end
|
106
|
+
|
107
|
+
until @workers.empty?
|
108
|
+
cleanup_worker
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def start_master
|
113
|
+
@socket = "/tmp/test_queue_#{$$}_#{object_id}.sock"
|
114
|
+
FileUtils.rm(@socket) if File.exists?(@socket)
|
115
|
+
@server = UNIXServer.new(@socket)
|
116
|
+
end
|
117
|
+
|
118
|
+
def stop_master
|
119
|
+
FileUtils.rm_f(@socket) if @socket
|
120
|
+
@server.close rescue nil if @server
|
121
|
+
@socket = @server = nil
|
122
|
+
end
|
123
|
+
|
124
|
+
def spawn_workers
|
125
|
+
@concurrency.times do |i|
|
126
|
+
pid = fork do
|
127
|
+
@server.close
|
128
|
+
after_fork(i)
|
129
|
+
exit! run_worker(iterator = Iterator.new(@socket)) || 0
|
130
|
+
end
|
131
|
+
|
132
|
+
@workers[pid] = Worker.new(pid, i)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def after_fork(num)
|
137
|
+
srand
|
138
|
+
|
139
|
+
output = File.open("/tmp/test_queue_worker_#{$$}_output", 'w')
|
140
|
+
output.sync = true
|
141
|
+
|
142
|
+
$stdout.reopen(output)
|
143
|
+
$stderr.reopen($stdout)
|
144
|
+
|
145
|
+
$0 = "ruby test-queue worker [#{num}]"
|
146
|
+
puts
|
147
|
+
puts "==> Starting #$0 (#{Process.pid})"
|
148
|
+
puts
|
149
|
+
end
|
150
|
+
|
151
|
+
def run_worker(iterator)
|
152
|
+
iterator.each do |item|
|
153
|
+
puts " #{item.inspect}"
|
154
|
+
end
|
155
|
+
|
156
|
+
return 0 # exit status
|
157
|
+
end
|
158
|
+
|
159
|
+
def summarize_worker(worker)
|
160
|
+
num_tests = ''
|
161
|
+
failures = ''
|
162
|
+
|
163
|
+
[ num_tests, failures ]
|
164
|
+
end
|
165
|
+
|
166
|
+
def cleanup_worker
|
167
|
+
if pid = Process.waitpid and worker = @workers.delete(pid)
|
168
|
+
@completed << worker
|
169
|
+
worker.status = $?
|
170
|
+
worker.end_time = Time.now
|
171
|
+
|
172
|
+
if File.exists?(file = "/tmp/test_queue_worker_#{pid}_output")
|
173
|
+
worker.output = IO.binread(file)
|
174
|
+
puts worker.output
|
175
|
+
FileUtils.rm(file)
|
176
|
+
end
|
177
|
+
|
178
|
+
if File.exists?(file = "/tmp/test_queue_worker_#{pid}_stats")
|
179
|
+
worker.stats = Marshal.load(IO.binread(file))
|
180
|
+
FileUtils.rm(file)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def distribute_queue
|
186
|
+
until @queue.empty?
|
187
|
+
IO.select([@server], nil, nil, nil)
|
188
|
+
|
189
|
+
sock = @server.accept
|
190
|
+
sock.write(Marshal.dump(@queue.shift))
|
191
|
+
sock.close
|
192
|
+
end
|
193
|
+
ensure
|
194
|
+
stop_master
|
195
|
+
|
196
|
+
until @workers.empty?
|
197
|
+
cleanup_worker
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'test_queue/runner'
|
2
|
+
require 'minitest/unit'
|
3
|
+
|
4
|
+
class MiniTestQueueRunner < MiniTest::Unit
|
5
|
+
def _run_suites(*)
|
6
|
+
self.class.output = $stdout
|
7
|
+
super
|
8
|
+
end
|
9
|
+
|
10
|
+
def _run_anything(*)
|
11
|
+
ret = super
|
12
|
+
output.puts
|
13
|
+
ret
|
14
|
+
end
|
15
|
+
|
16
|
+
def _run_suite(suite, type)
|
17
|
+
output.print ' '
|
18
|
+
output.print suite
|
19
|
+
output.print ': '
|
20
|
+
|
21
|
+
start = Time.now
|
22
|
+
ret = super
|
23
|
+
diff = Time.now - start
|
24
|
+
|
25
|
+
output.puts(" <%.3f>" % diff)
|
26
|
+
ret
|
27
|
+
end
|
28
|
+
|
29
|
+
self.runner = self.new
|
30
|
+
self.output = StringIO.new
|
31
|
+
end
|
32
|
+
|
33
|
+
class MiniTest::Unit::TestCase
|
34
|
+
class << self
|
35
|
+
attr_accessor :test_suites
|
36
|
+
|
37
|
+
def original_test_suites
|
38
|
+
@@test_suites.keys.reject{ |s| s.test_methods.empty? }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
module TestQueue
|
44
|
+
class Runner
|
45
|
+
class MiniTest < Runner
|
46
|
+
def initialize
|
47
|
+
super(::MiniTest::Unit::TestCase.original_test_suites.sort_by{ |s| -(stats[s.to_s] || 0) })
|
48
|
+
end
|
49
|
+
|
50
|
+
def run_worker(iterator)
|
51
|
+
::MiniTest::Unit::TestCase.test_suites = iterator
|
52
|
+
::MiniTest::Unit.new.run
|
53
|
+
end
|
54
|
+
|
55
|
+
def summarize_worker(worker)
|
56
|
+
worker.stats.each do |s, val|
|
57
|
+
stats[s.to_s] = val
|
58
|
+
end
|
59
|
+
|
60
|
+
num_tests = worker.lines.grep(/ errors?, /).first
|
61
|
+
failures = worker.lines.select{ |line|
|
62
|
+
line if (line =~ /^Finished/) ... (line =~ / errors?, /)
|
63
|
+
}[1..-2].join("\n").strip
|
64
|
+
|
65
|
+
[ num_tests, failures ]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'test_queue/runner'
|
2
|
+
require 'rspec/core'
|
3
|
+
|
4
|
+
module RSpec::Core
|
5
|
+
class QueueRunner < CommandLine
|
6
|
+
def initialize
|
7
|
+
super(ARGV)
|
8
|
+
end
|
9
|
+
|
10
|
+
def example_groups
|
11
|
+
@options.configure(@configuration)
|
12
|
+
@configuration.load_spec_files
|
13
|
+
@world.announce_filters
|
14
|
+
@world.example_groups
|
15
|
+
end
|
16
|
+
|
17
|
+
def run_each(iterator)
|
18
|
+
@configuration.error_stream = $stderr
|
19
|
+
@configuration.output_stream = $stdout
|
20
|
+
|
21
|
+
@configuration.reporter.report(0, @configuration.randomize? ? @configuration.seed : nil) do |reporter|
|
22
|
+
begin
|
23
|
+
@configuration.run_hook(:before, :suite)
|
24
|
+
iterator.map {|g|
|
25
|
+
print " #{g.description}: "
|
26
|
+
start = Time.now
|
27
|
+
ret = g.run(reporter)
|
28
|
+
diff = Time.now-start
|
29
|
+
puts(" <%.3f>" % diff)
|
30
|
+
|
31
|
+
ret
|
32
|
+
}.all? ? 0 : @configuration.failure_exit_code
|
33
|
+
ensure
|
34
|
+
@configuration.run_hook(:after, :suite)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
module TestQueue
|
42
|
+
class Runner
|
43
|
+
class RSpec < Runner
|
44
|
+
def initialize
|
45
|
+
@rspec = ::RSpec::Core::QueueRunner.new
|
46
|
+
super(@rspec.example_groups.sort_by{ |s| -(stats[s.description] || 0) })
|
47
|
+
end
|
48
|
+
|
49
|
+
def run_worker(iterator)
|
50
|
+
@rspec.run_each(iterator)
|
51
|
+
end
|
52
|
+
|
53
|
+
def summarize_worker(worker)
|
54
|
+
worker.stats.each do |s, val|
|
55
|
+
stats[s.description] = val
|
56
|
+
end
|
57
|
+
|
58
|
+
num_tests = worker.lines.grep(/ examples?, /).first
|
59
|
+
failures = worker.output[/^Failures:\n\n(.*)\n^Finished/m, 1]
|
60
|
+
|
61
|
+
[ num_tests, failures ]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'test_queue'
|
2
|
+
require 'test_queue/runner'
|
3
|
+
|
4
|
+
module TestQueue
|
5
|
+
class Runner
|
6
|
+
class Sample < Runner
|
7
|
+
def spawn_workers
|
8
|
+
puts "Spawning #@concurrency workers"
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
12
|
+
def after_fork(num)
|
13
|
+
puts " -- worker #{num} booted as pid #{$$}"
|
14
|
+
super
|
15
|
+
end
|
16
|
+
|
17
|
+
def run_worker(iterator)
|
18
|
+
sum = 0
|
19
|
+
iterator.each do |item|
|
20
|
+
puts " #{item.inspect}"
|
21
|
+
sum += item
|
22
|
+
end
|
23
|
+
sum
|
24
|
+
end
|
25
|
+
|
26
|
+
def summarize_worker(worker)
|
27
|
+
stats.update(worker.stats)
|
28
|
+
|
29
|
+
summary = worker.output.scan(/^\s*(\d+)/).join(', ')
|
30
|
+
failures = ''
|
31
|
+
|
32
|
+
[ summary, failures ]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
if __FILE__ == $0
|
39
|
+
TestQueue::Runner::Sample.new(Array(1..10)).execute
|
40
|
+
end
|
41
|
+
|
42
|
+
__END__
|
43
|
+
|
44
|
+
Spawning 4 workers
|
45
|
+
-- worker 0 booted as pid 40406
|
46
|
+
-- worker 1 booted as pid 40407
|
47
|
+
-- worker 2 booted as pid 40408
|
48
|
+
-- worker 3 booted as pid 40409
|
49
|
+
|
50
|
+
==> Starting ruby test-queue worker [1] (40407)
|
51
|
+
|
52
|
+
2
|
53
|
+
5
|
54
|
+
8
|
55
|
+
|
56
|
+
==> Starting ruby test-queue worker [3] (40409)
|
57
|
+
|
58
|
+
|
59
|
+
==> Starting ruby test-queue worker [2] (40408)
|
60
|
+
|
61
|
+
3
|
62
|
+
6
|
63
|
+
9
|
64
|
+
|
65
|
+
==> Starting ruby test-queue worker [0] (40406)
|
66
|
+
|
67
|
+
1
|
68
|
+
4
|
69
|
+
7
|
70
|
+
10
|
71
|
+
|
72
|
+
==> Summary
|
73
|
+
|
74
|
+
[1] 2, 5, 8 in 0.0024s (pid 40407 exit 15)
|
75
|
+
[3] in 0.0036s (pid 40409 exit 0)
|
76
|
+
[2] 3, 6, 9 in 0.0038s (pid 40408 exit 18)
|
77
|
+
[0] 1, 4, 7, 10 in 0.0044s (pid 40406 exit 22)
|
78
|
+
|
data/test-queue.gemspec
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
spec = Gem::Specification.new do |s|
|
2
|
+
s.name = 'test-queue'
|
3
|
+
s.version = '0.1.0'
|
4
|
+
s.summary = 'parallel test runner'
|
5
|
+
|
6
|
+
s.homepage = "http://github.com/tmm1/test-queue"
|
7
|
+
|
8
|
+
s.authors = ["Aman Gupta"]
|
9
|
+
s.email = "ruby@tmm1.net"
|
10
|
+
|
11
|
+
s.has_rdoc = false
|
12
|
+
s.bindir = 'bin'
|
13
|
+
s.executables << 'rspec-queue'
|
14
|
+
s.executables << 'minitest-queue'
|
15
|
+
|
16
|
+
s.add_development_dependency 'rspec'
|
17
|
+
s.add_development_dependency 'minitest'
|
18
|
+
|
19
|
+
s.files = `git ls-files`.split("\n")
|
20
|
+
end
|
data/test/sample_spec.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
|
3
|
+
describe 'RSpecEqual' do
|
4
|
+
it 'checks equality' do
|
5
|
+
1.should equal(1)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
describe 'RSpecSleep' do
|
10
|
+
it 'sleeps' do
|
11
|
+
start = Time.now
|
12
|
+
sleep 0.25
|
13
|
+
(Time.now-start).should be_within(0.02).of(0.25)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe 'RSpecFailure' do
|
18
|
+
it 'fails' do
|
19
|
+
:foo.should eq(:bar)
|
20
|
+
end
|
21
|
+
end
|
data/test/sample_test.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'minitest/unit'
|
2
|
+
|
3
|
+
class MiniTestEqual < MiniTest::Unit::TestCase
|
4
|
+
def test_equal
|
5
|
+
assert_equal 1, 1
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class MiniTestSleep < MiniTest::Unit::TestCase
|
10
|
+
def test_sleep
|
11
|
+
start = Time.now
|
12
|
+
sleep 0.25
|
13
|
+
assert_in_delta Time.now-start, 0.25, 0.02
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class MiniTestFailure < MiniTest::Unit::TestCase
|
18
|
+
def test_fail
|
19
|
+
assert_equal 0, 1
|
20
|
+
end
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: test-queue
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Aman Gupta
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2013-04-24 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: rspec
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 3
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
version: "0"
|
32
|
+
type: :development
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: minitest
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
hash: 3
|
43
|
+
segments:
|
44
|
+
- 0
|
45
|
+
version: "0"
|
46
|
+
type: :development
|
47
|
+
version_requirements: *id002
|
48
|
+
description:
|
49
|
+
email: ruby@tmm1.net
|
50
|
+
executables:
|
51
|
+
- rspec-queue
|
52
|
+
- minitest-queue
|
53
|
+
extensions: []
|
54
|
+
|
55
|
+
extra_rdoc_files: []
|
56
|
+
|
57
|
+
files:
|
58
|
+
- Gemfile
|
59
|
+
- Gemfile.lock
|
60
|
+
- README.md
|
61
|
+
- bin/minitest-queue
|
62
|
+
- bin/rspec-queue
|
63
|
+
- lib/test-queue.rb
|
64
|
+
- lib/test_queue.rb
|
65
|
+
- lib/test_queue/iterator.rb
|
66
|
+
- lib/test_queue/runner.rb
|
67
|
+
- lib/test_queue/runner/minitest.rb
|
68
|
+
- lib/test_queue/runner/rspec.rb
|
69
|
+
- lib/test_queue/runner/sample.rb
|
70
|
+
- test-queue.gemspec
|
71
|
+
- test/sample_spec.rb
|
72
|
+
- test/sample_test.rb
|
73
|
+
homepage: http://github.com/tmm1/test-queue
|
74
|
+
licenses: []
|
75
|
+
|
76
|
+
post_install_message:
|
77
|
+
rdoc_options: []
|
78
|
+
|
79
|
+
require_paths:
|
80
|
+
- lib
|
81
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
82
|
+
none: false
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
hash: 3
|
87
|
+
segments:
|
88
|
+
- 0
|
89
|
+
version: "0"
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
|
+
none: false
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
hash: 3
|
96
|
+
segments:
|
97
|
+
- 0
|
98
|
+
version: "0"
|
99
|
+
requirements: []
|
100
|
+
|
101
|
+
rubyforge_project:
|
102
|
+
rubygems_version: 1.8.24
|
103
|
+
signing_key:
|
104
|
+
specification_version: 3
|
105
|
+
summary: parallel test runner
|
106
|
+
test_files: []
|
107
|
+
|
108
|
+
has_rdoc: false
|