hydra 0.18.0 → 0.19.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -17,5 +17,6 @@ tmtags
17
17
  coverage
18
18
  rdoc
19
19
  pkg
20
+ tags
20
21
 
21
22
  ## PROJECT::SPECIFIC
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.18.0
1
+ 0.19.0
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{hydra}
8
- s.version = "0.18.0"
8
+ s.version = "0.19.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Nick Gauthier"]
12
- s.date = %q{2010-05-28}
12
+ s.date = %q{2010-06-07}
13
13
  s.description = %q{Spread your tests over multiple machines to test your code faster.}
14
14
  s.email = %q{nick@smartlogicsolutions.com}
15
15
  s.extra_rdoc_files = [
@@ -51,6 +51,7 @@ Gem::Specification.new do |s|
51
51
  "lib/hydra/spec/hydra_formatter.rb",
52
52
  "lib/hydra/ssh.rb",
53
53
  "lib/hydra/stdio.rb",
54
+ "lib/hydra/sync.rb",
54
55
  "lib/hydra/tasks.rb",
55
56
  "lib/hydra/trace.rb",
56
57
  "lib/hydra/worker.rb",
@@ -73,6 +74,7 @@ Gem::Specification.new do |s|
73
74
  "test/pipe_test.rb",
74
75
  "test/runner_test.rb",
75
76
  "test/ssh_test.rb",
77
+ "test/sync_test.rb",
76
78
  "test/test_helper.rb",
77
79
  "test/worker_test.rb"
78
80
  ]
@@ -83,20 +85,21 @@ Gem::Specification.new do |s|
83
85
  s.summary = %q{Distributed testing toolkit}
84
86
  s.test_files = [
85
87
  "test/pipe_test.rb",
88
+ "test/test_helper.rb",
86
89
  "test/ssh_test.rb",
90
+ "test/message_test.rb",
91
+ "test/master_test.rb",
92
+ "test/fixtures/write_file.rb",
93
+ "test/fixtures/slow.rb",
94
+ "test/fixtures/write_file_with_pending_spec.rb",
95
+ "test/fixtures/write_file_spec.rb",
96
+ "test/fixtures/features/step_definitions.rb",
97
+ "test/fixtures/hello_world.rb",
87
98
  "test/fixtures/write_file_alternate_spec.rb",
88
99
  "test/fixtures/sync_test.rb",
89
- "test/fixtures/hello_world.rb",
90
- "test/fixtures/features/step_definitions.rb",
91
100
  "test/fixtures/assert_true.rb",
92
- "test/fixtures/slow.rb",
93
- "test/fixtures/write_file_spec.rb",
94
- "test/fixtures/write_file_with_pending_spec.rb",
95
- "test/fixtures/write_file.rb",
96
- "test/message_test.rb",
97
- "test/test_helper.rb",
98
- "test/master_test.rb",
99
101
  "test/runner_test.rb",
102
+ "test/sync_test.rb",
100
103
  "test/worker_test.rb"
101
104
  ]
102
105
 
@@ -7,6 +7,7 @@ require 'hydra/safe_fork'
7
7
  require 'hydra/runner'
8
8
  require 'hydra/worker'
9
9
  require 'hydra/master'
10
+ require 'hydra/sync'
10
11
  require 'hydra/listener/abstract'
11
12
  require 'hydra/listener/minimal_output'
12
13
  require 'hydra/listener/report_generator'
@@ -1,15 +1,20 @@
1
1
  require 'hydra/hash'
2
2
  require 'open3'
3
3
  require 'tmpdir'
4
+ require 'erb'
4
5
  require 'yaml'
6
+
5
7
  module Hydra #:nodoc:
6
8
  # Hydra class responsible for delegate work down to workers.
7
9
  #
8
10
  # The Master is run once for any given testing session.
11
+ class YmlLoadError < StandardError; end
12
+
9
13
  class Master
10
14
  include Hydra::Messages::Master
11
15
  include Open3
12
16
  traceable('MASTER')
17
+
13
18
  # Create a new Master
14
19
  #
15
20
  # Options:
@@ -28,10 +33,29 @@ module Hydra #:nodoc:
28
33
  # * :autosort
29
34
  # * Set to false to disable automatic sorting by historical run-time per file
30
35
  def initialize(opts = { })
36
+ trap("SIGINT") do
37
+ puts "Testing halted by user. Untested files:"
38
+ puts @incomplete_files.join("\n")
39
+ exit
40
+ end
41
+
31
42
  opts.stringify_keys!
32
43
  config_file = opts.delete('config') { nil }
33
44
  if config_file
34
- opts.merge!(YAML.load_file(config_file).stringify_keys!)
45
+
46
+ begin
47
+ config_erb = ERB.new(IO.read(config_file)).result(binding)
48
+ rescue Exception => e
49
+ raise(YmlLoadError,"config file was found, but could not be parsed with ERB.\n#{$!.inspect}")
50
+ end
51
+
52
+ begin
53
+ config_yml = YAML::load(config_erb)
54
+ rescue StandardError => e
55
+ raise(YmlLoadError,"config file was found, but could not be parsed.\n#{$!.inspect}")
56
+ end
57
+
58
+ opts.merge!(config_yml.stringify_keys!)
35
59
  end
36
60
  @files = Array(opts.fetch('files') { nil })
37
61
  raise "No files, nothing to do" if @files.empty?
@@ -47,6 +71,7 @@ module Hydra #:nodoc:
47
71
  @verbose = opts.fetch('verbose') { false }
48
72
  @autosort = opts.fetch('autosort') { true }
49
73
  @sync = opts.fetch('sync') { nil }
74
+ @environment = opts.fetch('environment') { 'test' }
50
75
 
51
76
  if @autosort
52
77
  sort_files_from_report
@@ -134,39 +159,16 @@ module Hydra #:nodoc:
134
159
  end
135
160
 
136
161
  def boot_ssh_worker(worker)
162
+ sync = Sync.new(worker, @sync, @verbose)
163
+
137
164
  runners = worker.fetch('runners') { raise "You must specify the number of runners" }
138
- connect = worker.fetch('connect') { raise "You must specify an SSH connection target" }
139
- ssh_opts = worker.fetch('ssh_opts') { "" }
140
- directory = worker.fetch('directory') { raise "You must specify a remote directory" }
141
165
  command = worker.fetch('command') {
142
- "ruby -e \"require 'rubygems'; require 'hydra'; Hydra::Worker.new(:io => Hydra::Stdio.new, :runners => #{runners}, :verbose => #{@verbose});\""
166
+ "RAILS_ENV=#{@environment} ruby -e \"require 'rubygems'; require 'hydra'; Hydra::Worker.new(:io => Hydra::Stdio.new, :runners => #{runners}, :verbose => #{@verbose});\""
143
167
  }
144
168
 
145
- if @sync
146
- @sync.stringify_keys!
147
- trace "Synchronizing with #{connect}\n\t#{@sync.inspect}"
148
- local_dir = @sync.fetch('directory') {
149
- raise "You must specify a synchronization directory"
150
- }
151
- exclude_paths = @sync.fetch('exclude') { [] }
152
- exclude_opts = exclude_paths.inject(''){|memo, path| memo += "--exclude=#{path} "}
153
-
154
- rsync_command = [
155
- 'rsync',
156
- '-avz',
157
- '--delete',
158
- exclude_opts,
159
- File.expand_path(local_dir)+'/',
160
- "-e \"ssh #{ssh_opts}\"",
161
- "#{connect}:#{directory}"
162
- ].join(" ")
163
- trace rsync_command
164
- trace `#{rsync_command}`
165
- end
166
-
167
169
  trace "Booting SSH worker"
168
- ssh = Hydra::SSH.new("#{ssh_opts} #{connect}", directory, command)
169
- return { :io => ssh, :idle => false, :type => :ssh }
170
+ ssh = Hydra::SSH.new("#{sync.ssh_opts} #{sync.connect}", sync.remote_dir, command)
171
+ return { :io => ssh, :idle => false, :type => :ssh, :connect => sync.connect }
170
172
  end
171
173
 
172
174
  def shutdown_all_workers
@@ -180,7 +180,7 @@ module Hydra #:nodoc:
180
180
  def run_javascript_file(file)
181
181
  errors = []
182
182
  require 'v8'
183
- V8::Context.open do |context|
183
+ V8::Context.new do |context|
184
184
  context.load(File.expand_path(File.join(File.dirname(__FILE__), 'js', 'lint.js')))
185
185
  context['input'] = lambda{
186
186
  File.read(file)
@@ -27,6 +27,7 @@ module Hydra #:nodoc:
27
27
  # list all the files.
28
28
  def initialize(connection_options, directory, command)
29
29
  @writer, @reader, @error = popen3("ssh -tt #{connection_options}")
30
+ @writer.write("mkdir -p #{directory}\n")
30
31
  @writer.write("cd #{directory}\n")
31
32
  @writer.write(command+"\n")
32
33
  end
@@ -0,0 +1,99 @@
1
+ require 'yaml'
2
+ module Hydra #:nodoc:
3
+ # Hydra class responsible for delegate work down to workers.
4
+ #
5
+ # The Sync is run once for each remote worker.
6
+ class Sync
7
+ traceable('SYNC')
8
+ self.class.traceable('SYNC MANY')
9
+
10
+ attr_reader :connect, :ssh_opts, :remote_dir
11
+
12
+ # Create a new Sync instance to rsync source from the local machine to a remote worker
13
+ #
14
+ # Arguments:
15
+ # * :worker_opts
16
+ # * A hash of the configuration options for a worker.
17
+ # * :sync
18
+ # * A hash of settings specifically for copying the source directory to be tested
19
+ # to the remote worked
20
+ # * :verbose
21
+ # * Set to true to see lots of Hydra output (for debugging)
22
+ def initialize(worker_opts, sync_opts, verbose = false)
23
+ worker_opts ||= {}
24
+ worker_opts.stringify_keys!
25
+ @verbose = verbose
26
+ @connect = worker_opts.fetch('connect') { raise "You must specify an SSH connection target" }
27
+ @ssh_opts = worker_opts.fetch('ssh_opts') { "" }
28
+ @remote_dir = worker_opts.fetch('directory') { raise "You must specify a remote directory" }
29
+
30
+ return unless sync_opts
31
+ sync_opts.stringify_keys!
32
+ @local_dir = sync_opts.fetch('directory') { raise "You must specify a synchronization directory" }
33
+ @exclude_paths = sync_opts.fetch('exclude') { [] }
34
+
35
+ trace "Initialized"
36
+ trace " Worker: (#{worker_opts.inspect})"
37
+ trace " Sync: (#{sync_opts.inspect})"
38
+
39
+ sync
40
+ end
41
+
42
+ def sync
43
+ #trace "Synchronizing with #{connect}\n\t#{sync_opts.inspect}"
44
+ exclude_opts = @exclude_paths.inject(''){|memo, path| memo += "--exclude=#{path} "}
45
+
46
+ rsync_command = [
47
+ 'rsync',
48
+ '-avz',
49
+ '--delete',
50
+ exclude_opts,
51
+ File.expand_path(@local_dir)+'/',
52
+ "-e \"ssh #{@ssh_opts}\"",
53
+ "#{@connect}:#{@remote_dir}"
54
+ ].join(" ")
55
+ trace rsync_command
56
+ trace `#{rsync_command}`
57
+ end
58
+
59
+ def self.sync_many opts
60
+ opts.stringify_keys!
61
+ config_file = opts.delete('config') { nil }
62
+ if config_file
63
+ opts.merge!(YAML.load_file(config_file).stringify_keys!)
64
+ end
65
+ @verbose = opts.fetch('verbose') { false }
66
+ @sync = opts.fetch('sync') { {} }
67
+
68
+ workers_opts = opts.fetch('workers') { [] }
69
+ @remote_worker_opts = []
70
+ workers_opts.each do |worker_opts|
71
+ worker_opts.stringify_keys!
72
+ if worker_opts['type'].to_s == 'ssh'
73
+ @remote_worker_opts << worker_opts
74
+ end
75
+ end
76
+
77
+ trace "Initialized"
78
+ trace " Sync: (#{@sync.inspect})"
79
+ trace " Workers: (#{@remote_worker_opts.inspect})"
80
+
81
+ Thread.abort_on_exception = true
82
+ trace "Processing workers"
83
+ @listeners = []
84
+ @remote_worker_opts.each do |worker_opts|
85
+ @listeners << Thread.new do
86
+ begin
87
+ trace "Syncing #{worker_opts.inspect}"
88
+ Sync.new worker_opts, @sync, @verbose
89
+ rescue
90
+ trace "Syncing failed [#{worker_opts.inspect}]"
91
+ end
92
+ end
93
+ end
94
+
95
+ @listeners.each{|l| l.join}
96
+ end
97
+
98
+ end
99
+ end
@@ -177,6 +177,43 @@ module Hydra #:nodoc:
177
177
  end
178
178
  end
179
179
 
180
+ # Define a sync task that uses hydra to rsync the source tree under test to remote workers.
181
+ #
182
+ # This task is very useful to run before a remote db:reset task to make sure the db/schema.rb
183
+ # file is up to date on the remote workers.
184
+ #
185
+ # Hydra::SyncTask.new('hydra:sync') do |t|
186
+ # t.verbose = false # optionally set to true for lots of debug messages
187
+ # end
188
+ class SyncTask < Hydra::Task
189
+
190
+ # Create a new SyncTestTask
191
+ def initialize(name = :sync)
192
+ @name = name
193
+ @verbose = false
194
+
195
+ yield self if block_given?
196
+
197
+ @config = find_config_file
198
+
199
+ @opts = {
200
+ :verbose => @verbose
201
+ }
202
+ @opts.merge!(:config => @config) if @config
203
+
204
+ define
205
+ end
206
+
207
+ private
208
+ # Create the rake task defined by this HydraSyncTask
209
+ def define
210
+ desc "Hydra Tests" + (@name == :hydra ? "" : " for #{@name}")
211
+ task @name do
212
+ Hydra::Sync.sync_many(@opts)
213
+ end
214
+ end
215
+ end
216
+
180
217
  # Setup a task that will be run across all remote workers
181
218
  # Hydra::RemoteTask.new('db:reset')
182
219
  #
@@ -202,32 +239,59 @@ module Hydra #:nodoc:
202
239
  desc "Run #{@name} remotely on all workers"
203
240
  task "hydra:remote:#{@name}" do
204
241
  config = YAML.load_file(@config)
242
+ environment = config.fetch('environment') { 'test' }
205
243
  workers = config.fetch('workers') { [] }
206
244
  workers = workers.select{|w| w['type'] == 'ssh'}
245
+
246
+ $stdout.write "==== Hydra Running #{@name} ====\n"
247
+ Thread.abort_on_exception = true
248
+ @listeners = []
249
+ @results = {}
207
250
  workers.each do |worker|
208
- $stdout.write "==== Hydra Running #{@name} on #{worker['connect']} ====\n"
209
- ssh_opts = worker.fetch('ssh_opts') { '' }
210
- writer, reader, error = popen3("ssh -tt #{ssh_opts} #{worker['connect']} ")
211
- writer.write("cd #{worker['directory']}\n")
212
- writer.write "echo BEGIN HYDRA\n"
213
- writer.write("RAILS_ENV=test rake #{@name}\n")
214
- writer.write "echo END HYDRA\n"
215
- writer.write("exit\n")
216
- writer.close
217
- ignoring = true
218
- while line = reader.gets
219
- line.chomp!
220
- if line =~ /echo END HYDRA$/
221
- ignoring = true
222
- end
223
- $stdout.write "#{line}\n" unless ignoring
224
- if line == 'BEGIN HYDRA'
225
- ignoring = false
251
+ @listeners << Thread.new do
252
+ begin
253
+ @results[worker] = if run_task(worker, environment)
254
+ "==== #{@name} passed on #{worker['connect']} ====\n"
255
+ else
256
+ "==== #{@name} failed on #{worker['connect']} ====\nPlease see above for more details.\n"
257
+ end
258
+ rescue
259
+ @results[worker] = "==== #{@name} failed for #{worker['connect']} ====\n#{$!.inspect}\n#{$!.backtrace.join("\n")}"
226
260
  end
227
261
  end
228
- $stdout.write "\n==== Hydra Running #{@name} COMPLETE ====\n\n"
262
+ end
263
+ @listeners.each{|l| l.join}
264
+ $stdout.write "\n==== Hydra Running #{@name} COMPLETE ====\n\n"
265
+ $stdout.write @results.values.join("\n")
266
+ end
267
+ end
268
+
269
+ def run_task worker, environment
270
+ $stdout.write "==== Hydra Running #{@name} on #{worker['connect']} ====\n"
271
+ ssh_opts = worker.fetch('ssh_opts') { '' }
272
+ writer, reader, error = popen3("ssh -tt #{ssh_opts} #{worker['connect']} ")
273
+ writer.write("cd #{worker['directory']}\n")
274
+ writer.write "echo BEGIN HYDRA\n"
275
+ writer.write("RAILS_ENV=#{environment} rake #{@name}\n")
276
+ writer.write "echo END HYDRA\n"
277
+ writer.write("exit\n")
278
+ writer.close
279
+ ignoring = true
280
+ passed = true
281
+ while line = reader.gets
282
+ line.chomp!
283
+ if line =~ /^rake aborted!$/
284
+ passed = false
285
+ end
286
+ if line =~ /echo END HYDRA$/
287
+ ignoring = true
288
+ end
289
+ $stdout.write "#{worker['connect']}: #{line}\n" unless ignoring
290
+ if line == 'BEGIN HYDRA'
291
+ ignoring = false
229
292
  end
230
293
  end
294
+ passed
231
295
  end
232
296
  end
233
297
 
@@ -0,0 +1,113 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ class SyncTest < Test::Unit::TestCase
4
+ context "with a file to test and a destination to verify" do
5
+ setup do
6
+ # avoid having other tests interfering with us
7
+ sleep(0.2)
8
+ #FileUtils.rm_f(target_file)
9
+ end
10
+
11
+ teardown do
12
+ #FileUtils.rm_f(target_file)
13
+ end
14
+
15
+ should "synchronize a test file over ssh with rsync" do
16
+ local = File.join(Dir.tmpdir, 'hydra', 'local')
17
+ remote = File.join(Dir.tmpdir, 'hydra', 'remote')
18
+ sync_test = File.join(File.dirname(__FILE__), 'fixtures', 'sync_test.rb')
19
+ [local, remote].each{|f| FileUtils.rm_rf f; FileUtils.mkdir_p f}
20
+
21
+ # setup the folders:
22
+ # local:
23
+ # - test_a
24
+ # - test_c
25
+ # remote:
26
+ # - test_b
27
+ #
28
+ # add test_c to exludes
29
+ FileUtils.cp(sync_test, File.join(local, 'test_a.rb'))
30
+ FileUtils.cp(sync_test, File.join(local, 'test_c.rb'))
31
+ FileUtils.cp(sync_test, File.join(remote, 'test_b.rb'))
32
+
33
+ # ensure a is not on remote
34
+ assert !File.exists?(File.join(remote, 'test_a.rb')), "A should not be on remote"
35
+ # ensure c is not on remote
36
+ assert !File.exists?(File.join(remote, 'test_c.rb')), "C should not be on remote"
37
+ # ensure b is on remote
38
+ assert File.exists?(File.join(remote, 'test_b.rb')), "B should be on remote"
39
+
40
+ Hydra::Sync.new(
41
+ {
42
+ :type => :ssh,
43
+ :connect => 'localhost',
44
+ :directory => remote,
45
+ :runners => 1
46
+ },
47
+ {
48
+ :directory => local,
49
+ :exclude => ['test_c.rb']
50
+ }
51
+ )
52
+ # ensure a is copied
53
+ assert File.exists?(File.join(remote, 'test_a.rb')), "A was not copied"
54
+ # ensure c is not copied
55
+ assert !File.exists?(File.join(remote, 'test_c.rb')), "C was copied, should be excluded"
56
+ # ensure b is deleted
57
+ assert !File.exists?(File.join(remote, 'test_b.rb')), "B was not deleted"
58
+ end
59
+
60
+ should "synchronize a test file over ssh with rsync to multiple workers" do
61
+ local = File.join(Dir.tmpdir, 'hydra', 'local')
62
+ remote_a = File.join(Dir.tmpdir, 'hydra', 'remote_a')
63
+ remote_b = File.join(Dir.tmpdir, 'hydra', 'remote_b')
64
+ sync_test = File.join(File.dirname(__FILE__), 'fixtures', 'sync_test.rb')
65
+ [local, remote_a, remote_b].each{|f| FileUtils.rm_rf f; FileUtils.mkdir_p f}
66
+
67
+ # setup the folders:
68
+ # local:
69
+ # - test_a
70
+ # remote_a:
71
+ # - test_b
72
+ # remote_b:
73
+ # - test_c
74
+ #
75
+ # add test_c to exludes
76
+ FileUtils.cp(sync_test, File.join(local, 'test_a.rb'))
77
+ FileUtils.cp(sync_test, File.join(remote_a, 'test_b.rb'))
78
+ FileUtils.cp(sync_test, File.join(remote_b, 'test_c.rb'))
79
+
80
+ # ensure a is not on remotes
81
+ assert !File.exists?(File.join(remote_a, 'test_a.rb')), "A should not be on remote_a"
82
+ assert !File.exists?(File.join(remote_b, 'test_a.rb')), "A should not be on remote_b"
83
+ # ensure b is on remote_a
84
+ assert File.exists?(File.join(remote_a, 'test_b.rb')), "B should be on remote_a"
85
+ # ensure c is on remote_b
86
+ assert File.exists?(File.join(remote_b, 'test_c.rb')), "C should be on remote_b"
87
+
88
+ Hydra::Sync.sync_many(
89
+ :workers => [{
90
+ :type => :ssh,
91
+ :connect => 'localhost',
92
+ :directory => remote_a,
93
+ :runners => 1
94
+ },
95
+ {
96
+ :type => :ssh,
97
+ :connect => 'localhost',
98
+ :directory => remote_b,
99
+ :runners => 1
100
+ }],
101
+ :sync => {
102
+ :directory => local
103
+ }
104
+ )
105
+ # ensure a is copied to both remotes
106
+ assert File.exists?(File.join(remote_a, 'test_a.rb')), "A was not copied to remote_a"
107
+ assert File.exists?(File.join(remote_b, 'test_a.rb')), "A was not copied to remote_b"
108
+ # ensure b and c are deleted from remotes
109
+ assert !File.exists?(File.join(remote_a, 'test_b.rb')), "B was not deleted from remote_a"
110
+ assert !File.exists?(File.join(remote_b, 'test_c.rb')), "C was not deleted from remote_b"
111
+ end
112
+ end
113
+ end
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 18
7
+ - 19
8
8
  - 0
9
- version: 0.18.0
9
+ version: 0.19.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Nick Gauthier
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-05-28 00:00:00 -04:00
17
+ date: 2010-06-07 00:00:00 -04:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -103,6 +103,7 @@ files:
103
103
  - lib/hydra/spec/hydra_formatter.rb
104
104
  - lib/hydra/ssh.rb
105
105
  - lib/hydra/stdio.rb
106
+ - lib/hydra/sync.rb
106
107
  - lib/hydra/tasks.rb
107
108
  - lib/hydra/trace.rb
108
109
  - lib/hydra/worker.rb
@@ -125,6 +126,7 @@ files:
125
126
  - test/pipe_test.rb
126
127
  - test/runner_test.rb
127
128
  - test/ssh_test.rb
129
+ - test/sync_test.rb
128
130
  - test/test_helper.rb
129
131
  - test/worker_test.rb
130
132
  has_rdoc: true
@@ -159,18 +161,19 @@ specification_version: 3
159
161
  summary: Distributed testing toolkit
160
162
  test_files:
161
163
  - test/pipe_test.rb
164
+ - test/test_helper.rb
162
165
  - test/ssh_test.rb
166
+ - test/message_test.rb
167
+ - test/master_test.rb
168
+ - test/fixtures/write_file.rb
169
+ - test/fixtures/slow.rb
170
+ - test/fixtures/write_file_with_pending_spec.rb
171
+ - test/fixtures/write_file_spec.rb
172
+ - test/fixtures/features/step_definitions.rb
173
+ - test/fixtures/hello_world.rb
163
174
  - test/fixtures/write_file_alternate_spec.rb
164
175
  - test/fixtures/sync_test.rb
165
- - test/fixtures/hello_world.rb
166
- - test/fixtures/features/step_definitions.rb
167
176
  - test/fixtures/assert_true.rb
168
- - test/fixtures/slow.rb
169
- - test/fixtures/write_file_spec.rb
170
- - test/fixtures/write_file_with_pending_spec.rb
171
- - test/fixtures/write_file.rb
172
- - test/message_test.rb
173
- - test/test_helper.rb
174
- - test/master_test.rb
175
177
  - test/runner_test.rb
178
+ - test/sync_test.rb
176
179
  - test/worker_test.rb