test-queue 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|