test-queue 0.1.3 → 0.2.0.beta.1
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/README.md +59 -12
- data/lib/test_queue/iterator.rb +31 -8
- data/lib/test_queue/runner.rb +143 -25
- data/lib/test_queue/runner/minitest.rb +4 -2
- data/test-queue.gemspec +1 -1
- data/test/sample_minispec.rb +31 -0
- data/test/sample_spec.rb +7 -5
- data/test/sample_test.rb +8 -6
- metadata +45 -61
data/README.md
CHANGED
@@ -7,13 +7,6 @@ Specifically optimized for CI environments: build statistics from each run
|
|
7
7
|
are stored locally and used to sort the queue at the beginning of the
|
8
8
|
next run.
|
9
9
|
|
10
|
-
### usage
|
11
|
-
|
12
|
-
```
|
13
|
-
$ minitest-queue $(find test/ -name \*_test.rb)
|
14
|
-
$ rspec-queue --format progress spec
|
15
|
-
```
|
16
|
-
|
17
10
|
### design
|
18
11
|
|
19
12
|
test-queue uses a simple master + pre-fork worker model. The master
|
@@ -28,17 +21,45 @@ the queue.
|
|
28
21
|
└─── 21562 minitest-queue worker [0] - UserTest
|
29
22
|
```
|
30
23
|
|
31
|
-
|
24
|
+
test-queue also has a distributed mode, where additional masters can share
|
25
|
+
the workload and relay results back to a central master.
|
26
|
+
|
27
|
+
### environment variables
|
28
|
+
|
29
|
+
- `TEST_QUEUE_WORKERS`: number of workers to use per master (default: all available cores)
|
30
|
+
- `TEST_QUEUE_VERBOSE`: show results as they are available (default: `0`)
|
31
|
+
- `TEST_QUEUE_SOCKET`: unix socket `path` (or tcp `address:port` pair) used for communication (default: `/tmp/test_queue_XXXXX.sock`)
|
32
|
+
- `TEST_QUEUE_RELAY`: relay results back to a central master, specified as tcp `address:port`
|
33
|
+
- `TEST_QUEUE_STATS`: `path` to cache build stats in-build CI runs (default: `.test_queue_stats`)
|
34
|
+
|
35
|
+
### usage
|
36
|
+
|
37
|
+
test-queue bundles `minitest-queue` and `rspec-queue` binaries which can be used directly:
|
38
|
+
|
39
|
+
```
|
40
|
+
$ minitest-queue $(find test/ -name \*_test.rb)
|
41
|
+
$ rspec-queue --format progress spec
|
42
|
+
```
|
43
|
+
|
44
|
+
But the underlying `TestQueue::Runner::MiniTest` and `TestQueue::Runner::Rspec` are
|
45
|
+
built to be subclassed by your application. I recommend checking a new
|
46
|
+
executable into your project using one of these superclasses.
|
47
|
+
|
48
|
+
```
|
49
|
+
$ vim script/test-queue
|
50
|
+
$ chmod +x script/test-queue
|
51
|
+
$ git add script/test-queue
|
52
|
+
```
|
32
53
|
|
33
54
|
Since test-queue uses `fork(2)` to spawn off workers, you must ensure each worker
|
34
55
|
runs in an isolated environment. Use the `after_fork` hook with a custom
|
35
|
-
runner to reset any global state
|
56
|
+
runner to reset any global state.
|
36
57
|
|
37
58
|
``` ruby
|
38
|
-
|
39
|
-
def after_fork(num)
|
40
|
-
super
|
59
|
+
#!/usr/bin/env ruby
|
41
60
|
|
61
|
+
class MyAppTestRunner < TestQueue::Runner::MiniTest
|
62
|
+
def after_fork(num)
|
42
63
|
# use separate mysql database (we assume it exists and has the right schema already)
|
43
64
|
ActiveRecord::Base.configurations['test']['database'] << num.to_s
|
44
65
|
ActiveRecord::Base.establish_connection(:test)
|
@@ -47,11 +68,37 @@ class CustomMiniTestRunner < TestQueue::Runner::MiniTest
|
|
47
68
|
$redis.client.db = num
|
48
69
|
$redis.client.reconnect
|
49
70
|
end
|
71
|
+
|
72
|
+
def prepare(concurrency)
|
73
|
+
# create mysql databases exists with correct schema
|
74
|
+
concurrency.times do |i|
|
75
|
+
# ...
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def around_filter(suite)
|
80
|
+
$stats.timing("test.#{suite}.runtime") do
|
81
|
+
yield
|
82
|
+
end
|
83
|
+
end
|
50
84
|
end
|
51
85
|
|
52
86
|
CustomMiniTestRunner.new.execute
|
53
87
|
```
|
54
88
|
|
89
|
+
### distributed mode
|
90
|
+
|
91
|
+
To use distributed mode, the central master must listen on a tcp port. Additional masters can be booted
|
92
|
+
in relay mode to connect to the central master.
|
93
|
+
|
94
|
+
```
|
95
|
+
$ TEST_QUEUE_SOCKET=0.0.0.0:12345 bundle exec minitest-queue ./test/sample_test.rb
|
96
|
+
$ TEST_QUEUE_RELAY=0.0.0.0:12345 bundle exec minitest-queue ./test/sample_test.rb
|
97
|
+
```
|
98
|
+
|
99
|
+
See the [Parameterized Trigger Plugin](https://wiki.jenkins-ci.org/display/JENKINS/Parameterized+Trigger+Plugin)
|
100
|
+
for a simple way to do this with jenkins.
|
101
|
+
|
55
102
|
### see also
|
56
103
|
|
57
104
|
* https://github.com/Shopify/rails_parallel
|
data/lib/test_queue/iterator.rb
CHANGED
@@ -1,30 +1,42 @@
|
|
1
1
|
module TestQueue
|
2
2
|
class Iterator
|
3
|
-
attr_reader :stats
|
3
|
+
attr_reader :stats, :sock
|
4
4
|
|
5
|
-
def initialize(sock)
|
6
|
-
@sock = sock
|
5
|
+
def initialize(sock, suites, filter=nil)
|
7
6
|
@done = false
|
8
7
|
@stats = {}
|
9
8
|
@procline = $0
|
9
|
+
@sock = sock
|
10
|
+
@suites = suites
|
11
|
+
@filter = filter
|
12
|
+
if @sock =~ /^(.+):(\d+)$/
|
13
|
+
@tcp_address = $1
|
14
|
+
@tcp_port = $2.to_i
|
15
|
+
end
|
10
16
|
end
|
11
17
|
|
12
18
|
def each
|
13
19
|
fail 'already used this iterator' if @done
|
14
20
|
|
15
21
|
while true
|
16
|
-
client =
|
22
|
+
client = connect_to_master('POP')
|
17
23
|
r, w, e = IO.select([client], nil, [client], nil)
|
18
24
|
break if !e.empty?
|
19
25
|
|
20
|
-
if data = client.read(
|
26
|
+
if data = client.read(65536)
|
21
27
|
client.close
|
22
28
|
item = Marshal.load(data)
|
23
|
-
|
29
|
+
break if item.nil?
|
30
|
+
suite = @suites[item]
|
24
31
|
|
32
|
+
$0 = "#{@procline} - #{suite.respond_to?(:description) ? suite.description : suite}"
|
25
33
|
start = Time.now
|
26
|
-
|
27
|
-
|
34
|
+
if @filter
|
35
|
+
@filter.call(suite){ yield suite }
|
36
|
+
else
|
37
|
+
yield suite
|
38
|
+
end
|
39
|
+
@stats[suite.to_s] = Time.now - start
|
28
40
|
else
|
29
41
|
break
|
30
42
|
end
|
@@ -37,6 +49,17 @@ module TestQueue
|
|
37
49
|
end
|
38
50
|
end
|
39
51
|
|
52
|
+
def connect_to_master(cmd)
|
53
|
+
sock =
|
54
|
+
if @tcp_address
|
55
|
+
TCPSocket.new(@tcp_address, @tcp_port)
|
56
|
+
else
|
57
|
+
UNIXSocket.new(@sock)
|
58
|
+
end
|
59
|
+
sock.puts(cmd)
|
60
|
+
sock
|
61
|
+
end
|
62
|
+
|
40
63
|
include Enumerable
|
41
64
|
|
42
65
|
def empty?
|
data/lib/test_queue/runner.rb
CHANGED
@@ -3,7 +3,7 @@ require 'fileutils'
|
|
3
3
|
|
4
4
|
module TestQueue
|
5
5
|
class Worker
|
6
|
-
attr_accessor :pid, :status, :output, :stats, :num
|
6
|
+
attr_accessor :pid, :status, :output, :stats, :num, :host
|
7
7
|
attr_accessor :start_time, :end_time
|
8
8
|
|
9
9
|
def initialize(pid, num)
|
@@ -11,6 +11,7 @@ module TestQueue
|
|
11
11
|
@num = num
|
12
12
|
@start_time = Time.now
|
13
13
|
@output = ''
|
14
|
+
@stats = {}
|
14
15
|
end
|
15
16
|
|
16
17
|
def lines
|
@@ -21,10 +22,12 @@ module TestQueue
|
|
21
22
|
class Runner
|
22
23
|
attr_accessor :concurrency
|
23
24
|
|
24
|
-
def initialize(queue, concurrency=nil)
|
25
|
+
def initialize(queue, concurrency=nil, socket=nil, relay=nil)
|
25
26
|
raise ArgumentError, 'array required' unless Array === queue
|
26
27
|
|
28
|
+
@procline = $0
|
27
29
|
@queue = queue
|
30
|
+
@suites = queue.inject(Hash.new){ |hash, suite| hash.update suite.to_s => suite }
|
28
31
|
|
29
32
|
@workers = {}
|
30
33
|
@completed = []
|
@@ -39,11 +42,27 @@ module TestQueue
|
|
39
42
|
else
|
40
43
|
2
|
41
44
|
end
|
45
|
+
|
46
|
+
@socket =
|
47
|
+
socket ||
|
48
|
+
ENV['TEST_QUEUE_SOCKET'] ||
|
49
|
+
"/tmp/test_queue_#{$$}_#{object_id}.sock"
|
50
|
+
|
51
|
+
@relay =
|
52
|
+
relay ||
|
53
|
+
ENV['TEST_QUEUE_RELAY']
|
54
|
+
|
55
|
+
if @relay == @socket
|
56
|
+
STDERR.puts "*** Detected TEST_QUEUE_RELAY == TEST_QUEUE_SOCKET. Disabling relay mode."
|
57
|
+
@relay = nil
|
58
|
+
elsif @relay
|
59
|
+
@queue = []
|
60
|
+
end
|
42
61
|
end
|
43
62
|
|
44
63
|
def stats
|
45
64
|
@stats ||=
|
46
|
-
if File.exists?(file =
|
65
|
+
if File.exists?(file = stats_file)
|
47
66
|
Marshal.load(IO.binread(file)) || {}
|
48
67
|
else
|
49
68
|
{}
|
@@ -52,13 +71,18 @@ module TestQueue
|
|
52
71
|
|
53
72
|
def execute
|
54
73
|
$stdout.sync = $stderr.sync = true
|
74
|
+
@start_time = Time.now
|
55
75
|
|
56
76
|
@concurrency > 0 ?
|
57
77
|
execute_parallel :
|
58
78
|
execute_sequential
|
59
79
|
ensure
|
80
|
+
summarize_internal
|
81
|
+
end
|
82
|
+
|
83
|
+
def summarize_internal
|
60
84
|
puts
|
61
|
-
puts "==> Summary"
|
85
|
+
puts "==> Summary (#{@completed.size} workers in %.4fs)" % (Time.now-@start_time)
|
62
86
|
puts
|
63
87
|
|
64
88
|
@failures = ''
|
@@ -66,12 +90,14 @@ module TestQueue
|
|
66
90
|
summary, failures = summarize_worker(worker)
|
67
91
|
@failures << failures if failures
|
68
92
|
|
69
|
-
puts " [%2d] %
|
93
|
+
puts " [%2d] %60s %4d suites in %.4fs (pid %d exit %d%s)" % [
|
70
94
|
worker.num,
|
71
95
|
summary,
|
96
|
+
worker.stats.size,
|
72
97
|
worker.end_time - worker.start_time,
|
73
98
|
worker.pid,
|
74
|
-
worker.status.exitstatus
|
99
|
+
worker.status.exitstatus,
|
100
|
+
worker.host && " on #{worker.host.split('.').first}"
|
75
101
|
]
|
76
102
|
end
|
77
103
|
|
@@ -85,14 +111,23 @@ module TestQueue
|
|
85
111
|
puts
|
86
112
|
|
87
113
|
if @stats
|
88
|
-
File.open(
|
114
|
+
File.open(stats_file, 'wb') do |f|
|
89
115
|
f.write Marshal.dump(stats)
|
90
116
|
end
|
91
117
|
end
|
92
118
|
|
119
|
+
summarize
|
93
120
|
exit! @completed.inject(0){ |s, worker| s + worker.status.exitstatus }
|
94
121
|
end
|
95
122
|
|
123
|
+
def summarize
|
124
|
+
end
|
125
|
+
|
126
|
+
def stats_file
|
127
|
+
ENV['TEST_QUEUE_STATS'] ||
|
128
|
+
'.test_queue_stats'
|
129
|
+
end
|
130
|
+
|
96
131
|
def execute_sequential
|
97
132
|
exit! run_worker(@queue)
|
98
133
|
end
|
@@ -114,32 +149,59 @@ module TestQueue
|
|
114
149
|
end
|
115
150
|
|
116
151
|
def start_master
|
117
|
-
|
118
|
-
|
119
|
-
|
152
|
+
if relay?
|
153
|
+
begin
|
154
|
+
sock = connect_to_relay
|
155
|
+
sock.puts("SLAVE #{@concurrency}")
|
156
|
+
sock.close
|
157
|
+
rescue Errno::ECONNREFUSED
|
158
|
+
STDERR.puts "*** Unable to connect to relay #{@relay}. Aborting.."
|
159
|
+
exit! 1
|
160
|
+
end
|
161
|
+
else
|
162
|
+
if @socket =~ /^(?:(.+):)?(\d+)$/
|
163
|
+
address = $1 || '0.0.0.0'
|
164
|
+
port = $2.to_i
|
165
|
+
@socket = "#$1:#$2"
|
166
|
+
@server = TCPServer.new(address, port)
|
167
|
+
else
|
168
|
+
FileUtils.rm(@socket) if File.exists?(@socket)
|
169
|
+
@server = UNIXServer.new(@socket)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
desc = "test-queue master (#{relay?? "relaying to #{@relay}" : @socket})"
|
174
|
+
puts "Starting #{desc}"
|
175
|
+
$0 = "#{desc} - #{@procline}"
|
120
176
|
end
|
121
177
|
|
122
178
|
def stop_master
|
123
|
-
|
179
|
+
return if relay?
|
180
|
+
|
181
|
+
FileUtils.rm_f(@socket) if @socket && @server.is_a?(UNIXServer)
|
124
182
|
@server.close rescue nil if @server
|
125
183
|
@socket = @server = nil
|
126
184
|
end
|
127
185
|
|
128
186
|
def spawn_workers
|
187
|
+
prepare(@concurrency)
|
188
|
+
|
129
189
|
@concurrency.times do |i|
|
130
190
|
num = i+1
|
131
191
|
|
132
192
|
pid = fork do
|
133
|
-
@server.close
|
134
|
-
|
135
|
-
|
193
|
+
@server.close if @server
|
194
|
+
|
195
|
+
iterator = Iterator.new(relay?? @relay : @socket, @suites, method(:around_filter))
|
196
|
+
after_fork_internal(num, iterator)
|
197
|
+
exit! run_worker(iterator) || 0
|
136
198
|
end
|
137
199
|
|
138
200
|
@workers[pid] = Worker.new(pid, num)
|
139
201
|
end
|
140
202
|
end
|
141
203
|
|
142
|
-
def
|
204
|
+
def after_fork_internal(num, iterator)
|
143
205
|
srand
|
144
206
|
|
145
207
|
output = File.open("/tmp/test_queue_worker_#{$$}_output", 'w')
|
@@ -150,8 +212,20 @@ module TestQueue
|
|
150
212
|
|
151
213
|
$0 = "test-queue worker [#{num}]"
|
152
214
|
puts
|
153
|
-
puts "==> Starting #$0 (#{Process.pid})"
|
215
|
+
puts "==> Starting #$0 (#{Process.pid}) - iterating over #{iterator.sock}"
|
154
216
|
puts
|
217
|
+
|
218
|
+
after_fork(num)
|
219
|
+
end
|
220
|
+
|
221
|
+
def prepare(concurrency)
|
222
|
+
end
|
223
|
+
|
224
|
+
def around_filter(suite)
|
225
|
+
yield
|
226
|
+
end
|
227
|
+
|
228
|
+
def after_fork(num)
|
155
229
|
end
|
156
230
|
|
157
231
|
def run_worker(iterator)
|
@@ -169,15 +243,13 @@ module TestQueue
|
|
169
243
|
[ num_tests, failures ]
|
170
244
|
end
|
171
245
|
|
172
|
-
def cleanup_worker
|
173
|
-
if pid = Process.waitpid and worker = @workers.delete(pid)
|
174
|
-
@completed << worker
|
246
|
+
def cleanup_worker(blocking=true)
|
247
|
+
if pid = Process.waitpid(-1, blocking ? 0 : Process::WNOHANG) and worker = @workers.delete(pid)
|
175
248
|
worker.status = $?
|
176
249
|
worker.end_time = Time.now
|
177
250
|
|
178
251
|
if File.exists?(file = "/tmp/test_queue_worker_#{pid}_output")
|
179
252
|
worker.output = IO.binread(file)
|
180
|
-
puts worker.output
|
181
253
|
FileUtils.rm(file)
|
182
254
|
end
|
183
255
|
|
@@ -185,16 +257,43 @@ module TestQueue
|
|
185
257
|
worker.stats = Marshal.load(IO.binread(file))
|
186
258
|
FileUtils.rm(file)
|
187
259
|
end
|
260
|
+
|
261
|
+
relay_to_master(worker) if relay?
|
262
|
+
worker_completed(worker)
|
188
263
|
end
|
189
264
|
end
|
190
265
|
|
266
|
+
def worker_completed(worker)
|
267
|
+
@completed << worker
|
268
|
+
puts worker.output if ENV['TEST_QUEUE_VERBOSE']
|
269
|
+
end
|
270
|
+
|
191
271
|
def distribute_queue
|
192
|
-
|
193
|
-
|
272
|
+
return if relay?
|
273
|
+
remote_workers = 0
|
194
274
|
|
195
|
-
|
196
|
-
|
197
|
-
|
275
|
+
until @queue.empty? && remote_workers == 0
|
276
|
+
if IO.select([@server], nil, nil, 0.1).nil?
|
277
|
+
cleanup_worker(false) # check for worker deaths
|
278
|
+
else
|
279
|
+
sock = @server.accept
|
280
|
+
cmd = sock.gets.strip
|
281
|
+
case cmd
|
282
|
+
when 'POP'
|
283
|
+
data = Marshal.dump(@queue.shift.to_s)
|
284
|
+
sock.write(data)
|
285
|
+
when /^SLAVE (\d+)/
|
286
|
+
num = $1.to_i
|
287
|
+
remote_workers += num
|
288
|
+
STDERR.puts "*** slave connected with additional #{num} workers"
|
289
|
+
when /^WORKER (\d+)/
|
290
|
+
data = sock.read($1.to_i)
|
291
|
+
worker = Marshal.load(data)
|
292
|
+
worker_completed(worker)
|
293
|
+
remote_workers -= 1
|
294
|
+
end
|
295
|
+
sock.close
|
296
|
+
end
|
198
297
|
end
|
199
298
|
ensure
|
200
299
|
stop_master
|
@@ -203,5 +302,24 @@ module TestQueue
|
|
203
302
|
cleanup_worker
|
204
303
|
end
|
205
304
|
end
|
305
|
+
|
306
|
+
def relay?
|
307
|
+
!!@relay
|
308
|
+
end
|
309
|
+
|
310
|
+
def connect_to_relay
|
311
|
+
TCPSocket.new(*@relay.split(':'))
|
312
|
+
end
|
313
|
+
|
314
|
+
def relay_to_master(worker)
|
315
|
+
worker.host = Socket.gethostname
|
316
|
+
data = Marshal.dump(worker)
|
317
|
+
|
318
|
+
sock = connect_to_relay
|
319
|
+
sock.puts("WORKER #{data.bytesize}")
|
320
|
+
sock.write(data)
|
321
|
+
ensure
|
322
|
+
sock.close if sock
|
323
|
+
end
|
206
324
|
end
|
207
325
|
end
|
@@ -45,7 +45,8 @@ module TestQueue
|
|
45
45
|
class Runner
|
46
46
|
class MiniTest < Runner
|
47
47
|
def initialize
|
48
|
-
|
48
|
+
tests = ::MiniTest::Unit::TestCase.original_test_suites.sort_by{ |s| -(stats[s.to_s] || 0) }
|
49
|
+
super(tests)
|
49
50
|
end
|
50
51
|
|
51
52
|
def run_worker(iterator)
|
@@ -61,7 +62,8 @@ module TestQueue
|
|
61
62
|
num_tests = worker.lines.grep(/ errors?, /).first
|
62
63
|
failures = worker.lines.select{ |line|
|
63
64
|
line if (line =~ /^Finished/) ... (line =~ / errors?, /)
|
64
|
-
}[1..-2]
|
65
|
+
}[1..-2]
|
66
|
+
failures = failures.join("\n") if failures
|
65
67
|
|
66
68
|
[ num_tests, failures ]
|
67
69
|
end
|
data/test-queue.gemspec
CHANGED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'minitest/spec'
|
2
|
+
|
3
|
+
class Meme
|
4
|
+
def i_can_has_cheezburger?
|
5
|
+
"OHAI!"
|
6
|
+
end
|
7
|
+
|
8
|
+
def will_it_blend?
|
9
|
+
"YES!"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe Meme do
|
14
|
+
before do
|
15
|
+
@meme = Meme.new
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "when asked about cheeseburgers" do
|
19
|
+
it "must respond positively" do
|
20
|
+
sleep 0.1
|
21
|
+
@meme.i_can_has_cheezburger?.must_equal "OHAI!"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "when asked about blending possibilities" do
|
26
|
+
it "won't say no" do
|
27
|
+
sleep 0.1
|
28
|
+
@meme.will_it_blend?.wont_match /^no/i
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/test/sample_spec.rb
CHANGED
@@ -6,11 +6,13 @@ describe 'RSpecEqual' do
|
|
6
6
|
end
|
7
7
|
end
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
9
|
+
50.times do |i|
|
10
|
+
describe "RSpecSleep(#{i})" do
|
11
|
+
it "sleeps" do
|
12
|
+
start = Time.now
|
13
|
+
sleep(0.25)
|
14
|
+
(Time.now-start).should be_within(0.02).of(0.25)
|
15
|
+
end
|
14
16
|
end
|
15
17
|
end
|
16
18
|
|
data/test/sample_test.rb
CHANGED
@@ -6,12 +6,14 @@ class MiniTestEqual < MiniTest::Unit::TestCase
|
|
6
6
|
end
|
7
7
|
end
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
9
|
+
5.times do |i|
|
10
|
+
Object.const_set("MiniTestSleep#{i}", Class.new(MiniTest::Unit::TestCase) do
|
11
|
+
define_method('test_sleep') do
|
12
|
+
start = Time.now
|
13
|
+
sleep(0.25)
|
14
|
+
assert_in_delta Time.now-start, 0.25, 0.02
|
15
|
+
end
|
16
|
+
end)
|
15
17
|
end
|
16
18
|
|
17
19
|
class MiniTestFailure < MiniTest::Unit::TestCase
|
metadata
CHANGED
@@ -1,63 +1,56 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: test-queue
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
prerelease:
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 1
|
9
|
-
- 3
|
10
|
-
version: 0.1.3
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0.beta.1
|
5
|
+
prerelease: 6
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Aman Gupta
|
14
9
|
autorequire:
|
15
10
|
bindir: bin
|
16
11
|
cert_chain: []
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
- !ruby/object:Gem::Dependency
|
12
|
+
date: 2013-11-08 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
21
15
|
name: rspec
|
22
|
-
|
23
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
24
17
|
none: false
|
25
|
-
requirements:
|
18
|
+
requirements:
|
26
19
|
- - ~>
|
27
|
-
- !ruby/object:Gem::Version
|
28
|
-
|
29
|
-
segments:
|
30
|
-
- 2
|
31
|
-
- 13
|
32
|
-
version: "2.13"
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '2.13'
|
33
22
|
type: :development
|
34
|
-
version_requirements: *id001
|
35
|
-
- !ruby/object:Gem::Dependency
|
36
|
-
name: minitest
|
37
23
|
prerelease: false
|
38
|
-
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '2.13'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: minitest
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
39
33
|
none: false
|
40
|
-
requirements:
|
34
|
+
requirements:
|
41
35
|
- - ~>
|
42
|
-
- !ruby/object:Gem::Version
|
43
|
-
hash: 37
|
44
|
-
segments:
|
45
|
-
- 4
|
46
|
-
- 7
|
47
|
-
- 3
|
36
|
+
- !ruby/object:Gem::Version
|
48
37
|
version: 4.7.3
|
49
38
|
type: :development
|
50
|
-
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 4.7.3
|
51
46
|
description:
|
52
47
|
email: ruby@tmm1.net
|
53
|
-
executables:
|
48
|
+
executables:
|
54
49
|
- rspec-queue
|
55
50
|
- minitest-queue
|
56
51
|
extensions: []
|
57
|
-
|
58
52
|
extra_rdoc_files: []
|
59
|
-
|
60
|
-
files:
|
53
|
+
files:
|
61
54
|
- Gemfile
|
62
55
|
- Gemfile.lock
|
63
56
|
- README.md
|
@@ -72,41 +65,32 @@ files:
|
|
72
65
|
- lib/test_queue/runner/rspec.rb
|
73
66
|
- lib/test_queue/runner/sample.rb
|
74
67
|
- test-queue.gemspec
|
68
|
+
- test/sample_minispec.rb
|
75
69
|
- test/sample_spec.rb
|
76
70
|
- test/sample_test.rb
|
77
71
|
homepage: http://github.com/tmm1/test-queue
|
78
72
|
licenses: []
|
79
|
-
|
80
73
|
post_install_message:
|
81
74
|
rdoc_options: []
|
82
|
-
|
83
|
-
require_paths:
|
75
|
+
require_paths:
|
84
76
|
- lib
|
85
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
77
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
86
78
|
none: false
|
87
|
-
requirements:
|
88
|
-
- -
|
89
|
-
- !ruby/object:Gem::Version
|
90
|
-
|
91
|
-
|
92
|
-
- 0
|
93
|
-
version: "0"
|
94
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ! '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
95
84
|
none: false
|
96
|
-
requirements:
|
97
|
-
- -
|
98
|
-
- !ruby/object:Gem::Version
|
99
|
-
|
100
|
-
segments:
|
101
|
-
- 0
|
102
|
-
version: "0"
|
85
|
+
requirements:
|
86
|
+
- - ! '>'
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: 1.3.1
|
103
89
|
requirements: []
|
104
|
-
|
105
90
|
rubyforge_project:
|
106
|
-
rubygems_version: 1.8.
|
91
|
+
rubygems_version: 1.8.23
|
107
92
|
signing_key:
|
108
93
|
specification_version: 3
|
109
94
|
summary: parallel test runner
|
110
95
|
test_files: []
|
111
|
-
|
112
96
|
has_rdoc: false
|