testbot_instructure 0.7.8

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.
Files changed (60) hide show
  1. checksums.yaml +7 -0
  2. data/.gemtest +0 -0
  3. data/CHANGELOG +264 -0
  4. data/Gemfile +3 -0
  5. data/README.markdown +141 -0
  6. data/Rakefile +35 -0
  7. data/bin/testbot +59 -0
  8. data/lib/generators/testbot/templates/testbot.rake.erb +35 -0
  9. data/lib/generators/testbot/templates/testbot.yml.erb +45 -0
  10. data/lib/generators/testbot/testbot_generator.rb +19 -0
  11. data/lib/railtie.rb +16 -0
  12. data/lib/requester/requester.rb +171 -0
  13. data/lib/runner/job.rb +112 -0
  14. data/lib/runner/runner.rb +222 -0
  15. data/lib/runner/safe_result_text.rb +29 -0
  16. data/lib/server/build.rb +36 -0
  17. data/lib/server/group.rb +48 -0
  18. data/lib/server/job.rb +64 -0
  19. data/lib/server/memory_model.rb +91 -0
  20. data/lib/server/runner.rb +47 -0
  21. data/lib/server/server.rb +103 -0
  22. data/lib/server/status/javascripts/jquery-1.4.4.min.js +167 -0
  23. data/lib/server/status/status.html +48 -0
  24. data/lib/server/status/stylesheets/status.css +14 -0
  25. data/lib/shared/adapters/adapter.rb +27 -0
  26. data/lib/shared/adapters/cucumber_adapter.rb +91 -0
  27. data/lib/shared/adapters/helpers/ruby_env.rb +47 -0
  28. data/lib/shared/adapters/rspec2_adapter.rb +61 -0
  29. data/lib/shared/adapters/rspec_adapter.rb +79 -0
  30. data/lib/shared/adapters/test_unit_adapter.rb +44 -0
  31. data/lib/shared/color.rb +16 -0
  32. data/lib/shared/simple_daemonize.rb +25 -0
  33. data/lib/shared/ssh_tunnel.rb +36 -0
  34. data/lib/shared/testbot.rb +132 -0
  35. data/lib/shared/version.rb +12 -0
  36. data/lib/tasks/testbot.rake +30 -0
  37. data/lib/testbot.rb +2 -0
  38. data/test/fixtures/local/Rakefile +7 -0
  39. data/test/fixtures/local/config/testbot.yml +5 -0
  40. data/test/fixtures/local/log/test.log +0 -0
  41. data/test/fixtures/local/script/spec +2 -0
  42. data/test/fixtures/local/spec/models/car_spec.rb +0 -0
  43. data/test/fixtures/local/spec/models/house_spec.rb +0 -0
  44. data/test/fixtures/local/spec/spec.opts +0 -0
  45. data/test/fixtures/local/tmp/restart.txt +0 -0
  46. data/test/integration_test.rb +55 -0
  47. data/test/requester/requester_test.rb +407 -0
  48. data/test/requester/testbot.yml +7 -0
  49. data/test/requester/testbot_with_erb.yml +2 -0
  50. data/test/runner/job_test.rb +94 -0
  51. data/test/runner/safe_result_text_test.rb +20 -0
  52. data/test/server/group_test.rb +43 -0
  53. data/test/server/server_test.rb +511 -0
  54. data/test/shared/adapters/adapter_test.rb +22 -0
  55. data/test/shared/adapters/cucumber_adapter_test.rb +72 -0
  56. data/test/shared/adapters/helpers/ruby_env_test.rb +108 -0
  57. data/test/shared/adapters/rspec_adapter_test.rb +109 -0
  58. data/test/shared/testbot_test.rb +185 -0
  59. data/testbot.gemspec +34 -0
  60. metadata +313 -0
@@ -0,0 +1,55 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'fileutils'
4
+ require 'shoulda'
5
+
6
+ class IntegrationTest < Test::Unit::TestCase
7
+
8
+ def stop!
9
+ system "export INTEGRATION_TEST=true; bin/testbot --server stop > /dev/null"
10
+ system "export INTEGRATION_TEST=true; bin/testbot --runner stop > /dev/null"
11
+ end
12
+
13
+ # This is slow, and Test:Unit does not have "before/after :all" method, so I'm using a single testcase for multiple tests
14
+ should "be able to send a build request, have it run and show the results" do
15
+ Thread.new {
16
+
17
+ sleep 30
18
+ puts "Still running after 30 secs, stopping..."
19
+ stop!
20
+ }
21
+
22
+ system "rm -rf tmp; mkdir -p tmp; cp -rf test/fixtures/local tmp/local"
23
+ system "export INTEGRATION_TEST=true; bin/testbot --runner --connect 127.0.0.1 --working_dir tmp/runner > /dev/null"
24
+ system "export INTEGRATION_TEST=true; bin/testbot --server > /dev/null"
25
+
26
+ # For debug
27
+ # Thread.new do
28
+ # system "export INTEGRATION_TEST=true; bin/testbot --runner run --connect 127.0.0.1 --working_dir tmp/runner"
29
+ # end
30
+ # Thread.new do
31
+ # system "export INTEGRATION_TEST=true; bin/testbot --server run"
32
+ # end
33
+
34
+ sleep 2.0
35
+ result = `cd tmp/local; INTEGRATION_TEST=true ../../bin/testbot --spec --connect 127.0.0.1 --rsync_path ../server --rsync_ignores "log/* tmp/*"`
36
+
37
+ # Should include the result from script/spec
38
+ #puts result.inspect
39
+ assert result.include?('script/spec got called with ["-O", "spec/spec.opts", "spec/models/house_spec.rb", "spec/models/car_spec.rb"]') ||
40
+ result.include?('script/spec got called with ["-O", "spec/spec.opts", "spec/models/car_spec.rb", "spec/models/house_spec.rb"]')
41
+
42
+
43
+ # Should not include ignored files
44
+ assert !File.exists?("tmp/server/log/test.log")
45
+ assert !File.exists?("tmp/server/tmp/restart.txt")
46
+ assert !File.exists?("tmp/runner/local/log/test.log")
47
+ assert !File.exists?("tmp/runner/local/tmp/restart.txt")
48
+ end
49
+
50
+ def teardown
51
+ stop!
52
+ FileUtils.rm_rf "tmp"
53
+ end
54
+
55
+ end
@@ -0,0 +1,407 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '../../lib/requester/requester.rb'))
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+ require 'flexmock/test_unit'
5
+
6
+ # Probably a bug in flexmock, for 1.9.2
7
+ unless defined?(Test::Unit::AssertionFailedError)
8
+ class Test::Unit::AssertionFailedError
9
+ end
10
+ end
11
+
12
+ module Testbot::Requester
13
+
14
+ class RequesterTest < Test::Unit::TestCase
15
+
16
+ def requester_with_result(results)
17
+ requester = Requester.new(:server_host => "192.168.1.100", :rsync_path => 'user@server:/tmp/somewhere')
18
+
19
+ flexmock(requester).should_receive(:find_tests).and_return([])
20
+ flexmock(HTTParty).should_receive(:post).and_return(response_with_build_id)
21
+ flexmock(requester).should_receive(:sleep).once
22
+ flexmock(requester).should_receive(:print).once
23
+ flexmock(requester).should_receive(:puts).once
24
+ flexmock(requester).should_receive(:system)
25
+ flexmock(HTTParty).should_receive(:get).once.with("http://192.168.1.100:#{Testbot::SERVER_PORT}/builds/5",
26
+ :format => :json).and_return({ "done" => true, "results" => results })
27
+ requester
28
+ end
29
+
30
+ def response_with_build_id
31
+ OpenStruct.new(:response => OpenStruct.new(:code => "200", :body => "5"))
32
+ end
33
+
34
+ def error_response(opts = {})
35
+ OpenStruct.new(:response => OpenStruct.new(opts))
36
+ end
37
+
38
+ def build_with_result(results)
39
+ requester_with_result(results).run_tests(RspecAdapter, 'spec')
40
+ end
41
+
42
+ def setup
43
+ ENV['USE_JRUBY'] = nil
44
+ ENV['IN_TEST'] = 'true'
45
+ end
46
+
47
+ def mock_file_sizes
48
+ flexmock(File).should_receive(:stat).and_return(mock = Object.new)
49
+ flexmock(mock).should_receive(:size).and_return(0)
50
+ end
51
+
52
+ def fixture_path(local_path)
53
+ File.join(File.dirname(__FILE__), local_path)
54
+ end
55
+
56
+ context "self.create_by_config" do
57
+
58
+ should 'create a requester from config' do
59
+ requester = Requester.create_by_config(fixture_path("testbot.yml"))
60
+ assert_equal 'hostname', requester.config.server_host
61
+ assert_equal '/path', requester.config.rsync_path
62
+ assert_equal '.git tmp', requester.config.rsync_ignores
63
+ assert_equal 'appname', requester.config.project
64
+ assert_equal false, requester.config.ssh_tunnel
65
+ assert_equal 'user', requester.config.server_user
66
+ assert_equal '50%', requester.config.available_runner_usage
67
+ end
68
+
69
+ should 'accept ERB-snippets in testbot.yml' do
70
+ requester = Requester.create_by_config(fixture_path("testbot_with_erb.yml"))
71
+ assert_equal 'dynamic_host', requester.config.server_host
72
+ assert_equal '50%', requester.config.available_runner_usage
73
+ end
74
+ end
75
+
76
+ context "initialize" do
77
+
78
+ should "use defaults when values are missing" do
79
+ expected = { :server_host => 'hostname',
80
+ :rsync_path => Testbot::DEFAULT_SERVER_PATH,
81
+ :project => Testbot::DEFAULT_PROJECT,
82
+ :server_user => Testbot::DEFAULT_USER,
83
+ :available_runner_usage => Testbot::DEFAULT_RUNNER_USAGE }
84
+
85
+ actual = Requester.new({ "server_host" => 'hostname' }).config
86
+
87
+ assert_equal OpenStruct.new(expected), actual
88
+ end
89
+
90
+ end
91
+
92
+ context "run_tests" do
93
+
94
+ should "should be able to create a build" do
95
+ requester = Requester.new(:server_host => "192.168.1.100", :rsync_path => '/path', :available_runner_usage => '60%', :project => 'things', :server_user => "cruise")
96
+ flexmock(RspecAdapter).should_receive(:test_files).with('spec').once.and_return([ 'spec/models/house_spec.rb', 'spec/models/car_spec.rb' ])
97
+
98
+ flexmock(File).should_receive(:stat).once.with("spec/models/house_spec.rb").and_return(mock = Object.new); flexmock(mock).should_receive(:size).and_return(10)
99
+ flexmock(File).should_receive(:stat).once.with("spec/models/car_spec.rb").and_return(mock = Object.new); flexmock(mock).should_receive(:size).and_return(20)
100
+
101
+ flexmock(HTTParty).should_receive(:post).once.with("http://192.168.1.100:#{Testbot::SERVER_PORT}/builds",
102
+ :body => { :type => "spec",
103
+ :root => "cruise@192.168.1.100:/path",
104
+ :project => "things",
105
+ :available_runner_usage => "60%",
106
+ :files => "spec/models/house_spec.rb" +
107
+ " spec/models/car_spec.rb",
108
+ :sizes => "10 20",
109
+ :jruby => false }).and_return(response_with_build_id)
110
+
111
+ flexmock(HTTParty).should_receive(:get).and_return({ "done" => true, 'results' => '', "success" => true })
112
+ flexmock(requester).should_receive(:sleep)
113
+ flexmock(requester).should_receive(:print)
114
+ flexmock(requester).should_receive(:puts)
115
+ flexmock(requester).should_receive(:system)
116
+
117
+ assert_equal true, requester.run_tests(RspecAdapter, 'spec')
118
+ end
119
+
120
+ should "print a message and exit if the status is 503" do
121
+ requester = Requester.new(:server_host => "192.168.1.100")
122
+
123
+ flexmock(requester).should_receive(:find_tests).and_return([ 'spec/models/house_spec.rb', 'spec_models/car_spec.rb' ])
124
+ flexmock(requester).should_receive(:system)
125
+
126
+ flexmock(HTTParty).should_receive(:post).and_return(error_response(:code => "503"))
127
+ flexmock(requester).should_receive(:puts)
128
+ assert_equal false, requester.run_tests(RspecAdapter, 'spec')
129
+ end
130
+
131
+ should "print what the server returns in case there is anything but a 200 response" do
132
+ requester = Requester.new(:server_host => "192.168.1.100")
133
+
134
+ flexmock(requester).should_receive(:find_tests).and_return([ 'spec/models/house_spec.rb', 'spec_models/car_spec.rb' ])
135
+ flexmock(requester).should_receive(:system)
136
+
137
+ flexmock(HTTParty).should_receive(:post).and_return(error_response(:code => "123", :body => "Some error"))
138
+ flexmock(requester).should_receive(:puts).with("Could not create build, 123: Some error")
139
+ assert_equal false, requester.run_tests(RspecAdapter, 'spec')
140
+ end
141
+
142
+ should "print the sum of results formatted by the adapter" do
143
+ requester = Requester.new(:server_host => "192.168.1.100")
144
+
145
+ flexmock(requester).should_receive(:find_tests).and_return([ 'spec/models/house_spec.rb', 'spec_models/car_spec.rb' ])
146
+ flexmock(requester).should_receive(:system)
147
+
148
+ flexmock(HTTParty).should_receive(:post).and_return(response_with_build_id)
149
+
150
+ flexmock(HTTParty).should_receive(:get).times(2).with("http://192.168.1.100:#{Testbot::SERVER_PORT}/builds/5",
151
+ :format => :json).and_return({ "done" => false, "results" => "job 2 done: ...." },
152
+ { "done" => true, "results" => "job 2 done: ....job 1 done: ...." })
153
+ mock_file_sizes
154
+
155
+ flexmock(requester).should_receive(:sleep).times(2).with(0.5)
156
+ flexmock(requester).should_receive(:print).once.with("job 2 done: ....")
157
+ flexmock(requester).should_receive(:print).once.with("job 1 done: ....")
158
+ flexmock(requester).should_receive(:puts).once.with("\nformatted result")
159
+
160
+ flexmock(RspecAdapter).should_receive(:sum_results).with("job 2 done: ....job 1 done: ....").and_return("formatted result")
161
+ requester.run_tests(RspecAdapter, 'spec')
162
+ end
163
+
164
+ should "keep calling the server for results until done" do
165
+ requester = Requester.new(:server_host => "192.168.1.100")
166
+
167
+ flexmock(requester).should_receive(:find_tests).and_return([ 'spec/models/house_spec.rb', 'spec_models/car_spec.rb' ])
168
+ flexmock(requester).should_receive(:system)
169
+
170
+ flexmock(HTTParty).should_receive(:post).and_return(response_with_build_id)
171
+
172
+ flexmock(HTTParty).should_receive(:get).times(2).with("http://192.168.1.100:#{Testbot::SERVER_PORT}/builds/5",
173
+ :format => :json).and_return({ "done" => false, "results" => "job 2 done: ...." },
174
+ { "done" => true, "results" => "job 2 done: ....job 1 done: ...." })
175
+ mock_file_sizes
176
+
177
+ flexmock(requester).should_receive(:sleep).times(2).with(0.5)
178
+ flexmock(requester).should_receive(:print).once.with("job 2 done: ....")
179
+ flexmock(requester).should_receive(:print).once.with("job 1 done: ....")
180
+ flexmock(requester).should_receive(:puts).once.with("\n\033[32m0 examples, 0 failures\033[0m")
181
+
182
+ requester.run_tests(RspecAdapter, 'spec')
183
+ end
184
+
185
+ should "return false if not successful" do
186
+ requester = Requester.new(:server_host => "192.168.1.100")
187
+
188
+ flexmock(requester).should_receive(:find_tests).and_return([ 'spec/models/house_spec.rb', 'spec_models/car_spec.rb' ])
189
+ flexmock(requester).should_receive(:system)
190
+
191
+ flexmock(HTTParty).should_receive(:post).and_return(response_with_build_id)
192
+
193
+ flexmock(HTTParty).should_receive(:get).once.with("http://192.168.1.100:#{Testbot::SERVER_PORT}/builds/5",
194
+ :format => :json).and_return({ "success" => false, "done" => true, "results" => "job 2 done: ....job 1 done: ...." })
195
+
196
+ flexmock(requester).should_receive(:sleep).once.with(0.5)
197
+ flexmock(requester).should_receive(:print).once.with("job 2 done: ....job 1 done: ....")
198
+ flexmock(requester).should_receive(:puts).once.with("\n\033[32m0 examples, 0 failures\033[0m")
199
+ mock_file_sizes
200
+
201
+ assert_equal false, requester.run_tests(RspecAdapter, 'spec')
202
+ end
203
+
204
+ should "not print empty lines when there is no result" do
205
+ requester = Requester.new(:server_host => "192.168.1.100")
206
+
207
+ flexmock(requester).should_receive(:find_tests).and_return([ 'spec/models/house_spec.rb', 'spec_models/car_spec.rb' ])
208
+ flexmock(requester).should_receive(:system)
209
+
210
+ flexmock(HTTParty).should_receive(:post).and_return(response_with_build_id)
211
+
212
+ flexmock(HTTParty).should_receive(:get).times(2).with("http://192.168.1.100:#{Testbot::SERVER_PORT}/builds/5",
213
+ :format => :json).and_return({ "done" => false, "results" => "" },
214
+ { "done" => true, "results" => "job 2 done: ....job 1 done: ...." })
215
+
216
+ flexmock(requester).should_receive(:sleep).times(2).with(0.5)
217
+ flexmock(requester).should_receive(:print).once.with("job 2 done: ....job 1 done: ....")
218
+ flexmock(requester).should_receive(:puts).once.with("\n\033[32m0 examples, 0 failures\033[0m")
219
+ mock_file_sizes
220
+
221
+ requester.run_tests(RspecAdapter, 'spec')
222
+ end
223
+
224
+ should "sync the files to the server" do
225
+ requester = Requester.new(:server_host => "192.168.1.100", :rsync_path => '/path', :rsync_ignores => '.git tmp')
226
+
227
+ flexmock(requester).should_receive(:find_tests).and_return([ 'spec/models/house_spec.rb', 'spec_models/car_spec.rb' ])
228
+ flexmock(requester).should_receive(:system)
229
+
230
+ flexmock(HTTParty).should_receive(:post).and_return(response_with_build_id)
231
+ flexmock(requester).should_receive(:sleep).once
232
+ flexmock(requester).should_receive(:print)
233
+ flexmock(requester).should_receive(:puts)
234
+ flexmock(HTTParty).should_receive(:get).once.with("http://192.168.1.100:#{Testbot::SERVER_PORT}/builds/5",
235
+ :format => :json).and_return({ "done" => true, "results" => "" })
236
+
237
+ flexmock(requester).should_receive('system').with("rsync -az --delete --delete-excluded -e ssh --exclude='.git' --exclude='tmp' . testbot@192.168.1.100:/path")
238
+ mock_file_sizes
239
+
240
+ requester.run_tests(RspecAdapter, 'spec')
241
+ end
242
+
243
+ should "just try again if the request encounters an error while running and print on the fith time" do
244
+ requester = Requester.new(:server_host => "192.168.1.100")
245
+
246
+ flexmock(requester).should_receive(:find_tests).and_return([ 'spec/models/house_spec.rb', 'spec_models/car_spec.rb' ])
247
+ flexmock(requester).should_receive(:system)
248
+
249
+ flexmock(HTTParty).should_receive(:post).and_return(response_with_build_id)
250
+
251
+ flexmock(HTTParty).should_receive(:get).times(5).with("http://192.168.1.100:#{Testbot::SERVER_PORT}/builds/5",
252
+ :format => :json).and_raise('some connection error')
253
+ flexmock(HTTParty).should_receive(:get).times(1).with("http://192.168.1.100:#{Testbot::SERVER_PORT}/builds/5",
254
+ :format => :json).and_return({ "done" => true, "results" => "job 2 done: ....job 1 done: ...." })
255
+
256
+ flexmock(requester).should_receive(:sleep).times(6).with(0.5)
257
+ flexmock(requester).should_receive(:puts).once.with("Failed to get status: some connection error")
258
+ flexmock(requester).should_receive(:print).once.with("job 2 done: ....job 1 done: ....")
259
+ flexmock(requester).should_receive(:puts).once.with("\n\033[32m0 examples, 0 failures\033[0m")
260
+ mock_file_sizes
261
+
262
+ requester.run_tests(RspecAdapter, 'spec')
263
+ end
264
+
265
+ should "just try again if the status returns as nil" do
266
+ requester = Requester.new(:server_host => "192.168.1.100")
267
+
268
+ flexmock(requester).should_receive(:find_tests).and_return([ 'spec/models/house_spec.rb', 'spec_models/car_spec.rb' ])
269
+ flexmock(requester).should_receive(:system)
270
+
271
+ flexmock(HTTParty).should_receive(:post).and_return(response_with_build_id)
272
+
273
+ flexmock(HTTParty).should_receive(:get).times(2).with("http://192.168.1.100:#{Testbot::SERVER_PORT}/builds/5",
274
+ :format => :json).and_return(nil,
275
+ { "done" => true, "results" => "job 2 done: ....job 1 done: ...." })
276
+
277
+ flexmock(requester).should_receive(:sleep).times(2).with(0.5)
278
+ flexmock(requester).should_receive(:print).once.with("job 2 done: ....job 1 done: ....")
279
+ flexmock(requester).should_receive(:puts).once.with("\n\033[32m0 examples, 0 failures\033[0m")
280
+ mock_file_sizes
281
+
282
+ requester.run_tests(RspecAdapter, 'spec')
283
+ end
284
+
285
+ should "remove unnessesary output from rspec when told to do so" do
286
+ requester = Requester.new(:server_host => "192.168.1.100", :simple_output => true)
287
+
288
+ flexmock(requester).should_receive(:find_tests).and_return([ 'spec/models/house_spec.rb', 'spec_models/car_spec.rb' ])
289
+ flexmock(requester).should_receive(:system)
290
+
291
+ flexmock(HTTParty).should_receive(:post).and_return(response_with_build_id)
292
+
293
+ flexmock(HTTParty).should_receive(:get).times(2).with("http://192.168.1.100:#{Testbot::SERVER_PORT}/builds/5",
294
+ :format => :json).and_return(nil,
295
+ { "done" => true, "results" => "testbot4:\n....\n\nFinished in 84.333 seconds\n\n206 examples, 0 failures, 2 pending; testbot4:\n.F..\n\nFinished in 84.333 seconds\n\n206 examples, 0 failures, 2 pending" })
296
+
297
+ flexmock(requester).should_receive(:sleep).times(2).with(0.5)
298
+
299
+ # Imperfect match, includes "." in 84.333, but good enough.
300
+ flexmock(requester).should_receive(:print).once.with("......F...")
301
+ flexmock(requester).should_receive(:print)
302
+ flexmock(requester).should_receive(:puts)
303
+ mock_file_sizes
304
+
305
+ requester.run_tests(RspecAdapter, 'spec')
306
+ end
307
+
308
+ should "use SSHTunnel when specified (with a port that does not collide with the runner)" do
309
+ requester = Requester.new(:ssh_tunnel => true, :server_host => "somewhere")
310
+ flexmock(requester).should_receive(:system)
311
+
312
+ flexmock(SSHTunnel).should_receive(:new).once.with("somewhere", "testbot", 2299).and_return(ssh_tunnel = Object.new)
313
+ flexmock(ssh_tunnel).should_receive(:open).once
314
+
315
+ flexmock(requester).should_receive(:find_tests).and_return([ 'spec/models/house_spec.rb' ])
316
+ flexmock(HTTParty).should_receive(:post).with("http://127.0.0.1:2299/builds", any).and_return(response_with_build_id)
317
+ flexmock(HTTParty).should_receive(:get).and_return({ "done" => true, "results" => "job 1 done: ...." })
318
+ flexmock(requester).should_receive(:sleep)
319
+ flexmock(requester).should_receive(:print)
320
+ flexmock(requester).should_receive(:puts)
321
+ mock_file_sizes
322
+
323
+ requester.run_tests(RspecAdapter, 'spec')
324
+ end
325
+
326
+ should "use another user for rsync and ssh_tunnel when specified" do
327
+ requester = Requester.new(:ssh_tunnel => true, :server_host => "somewhere",
328
+ :server_user => "cruise", :rsync_path => "/tmp/testbot/foo")
329
+
330
+ flexmock(SSHTunnel).should_receive(:new).once.with("somewhere", "cruise", 2299).and_return(ssh_tunnel = Object.new)
331
+ flexmock(ssh_tunnel).should_receive(:open).once
332
+
333
+ flexmock(requester).should_receive(:find_tests).and_return([ 'spec/models/house_spec.rb' ])
334
+ flexmock(HTTParty).should_receive(:post).with("http://127.0.0.1:2299/builds", any).and_return(response_with_build_id)
335
+ flexmock(HTTParty).should_receive(:get).and_return({ "done" => true, "results" => "job 1 done: ...." })
336
+ flexmock(requester).should_receive(:sleep)
337
+ flexmock(requester).should_receive(:print)
338
+ flexmock(requester).should_receive(:puts)
339
+
340
+ flexmock(requester).should_receive('system').with("rsync -az --delete --delete-excluded -e ssh . cruise@somewhere:/tmp/testbot/foo")
341
+ mock_file_sizes
342
+
343
+ requester.run_tests(RspecAdapter, 'spec')
344
+ end
345
+
346
+ should "use another port for cucumber to be able to run at the same time as rspec" do
347
+ requester = Requester.new(:ssh_tunnel => true, :server_host => "somewhere")
348
+ flexmock(requester).should_receive(:system)
349
+
350
+ flexmock(SSHTunnel).should_receive(:new).once.with("somewhere", "testbot", 2230).and_return(ssh_tunnel = Object.new)
351
+ flexmock(ssh_tunnel).should_receive(:open).once
352
+
353
+ flexmock(requester).should_receive(:find_tests).and_return([ 'features/some.feature' ])
354
+ flexmock(HTTParty).should_receive(:post).with("http://127.0.0.1:2230/builds", any).and_return(response_with_build_id)
355
+ flexmock(HTTParty).should_receive(:get).and_return({ "done" => true, "results" => "job 1 done: ...." })
356
+ flexmock(requester).should_receive(:sleep)
357
+ flexmock(requester).should_receive(:print)
358
+ flexmock(requester).should_receive(:puts)
359
+ mock_file_sizes
360
+
361
+ requester.run_tests(CucumberAdapter, 'features')
362
+ end
363
+
364
+ should "use another port for Test::Unit" do
365
+ requester = Requester.new(:ssh_tunnel => true, :server_host => "somewhere")
366
+ flexmock(requester).should_receive(:system)
367
+
368
+ flexmock(SSHTunnel).should_receive(:new).once.with("somewhere", "testbot", 2231).and_return(ssh_tunnel = Object.new)
369
+ flexmock(ssh_tunnel).should_receive(:open).once
370
+
371
+ flexmock(requester).should_receive(:find_tests).and_return([ 'test/some_test.rb' ])
372
+ flexmock(HTTParty).should_receive(:post).with("http://127.0.0.1:2231/builds", any).and_return(response_with_build_id)
373
+ flexmock(HTTParty).should_receive(:get).and_return({ "done" => true, "results" => "job 1 done: ...." })
374
+ flexmock(requester).should_receive(:sleep)
375
+ flexmock(requester).should_receive(:print)
376
+ flexmock(requester).should_receive(:puts)
377
+ mock_file_sizes
378
+
379
+ requester.run_tests(TestUnitAdapter, 'test')
380
+ end
381
+
382
+ should "request a run with jruby if USE_JRUBY is set" do
383
+ ENV['USE_JRUBY'] = "true"
384
+ requester = Requester.new
385
+ flexmock(requester).should_receive(:system)
386
+
387
+ # This is quite ugly. I want something like hash_including instead...
388
+ other_args = { :type=>"test", :available_runner_usage=>"100%",
389
+ :root=>"testbot@:/tmp/testbot/#{ENV['USER']}", :files=>"test/some_test.rb",
390
+ :sizes=>"0", :project=>"project" }
391
+
392
+ flexmock(TestUnitAdapter).should_receive(:test_files).and_return([ 'test/some_test.rb' ])
393
+ flexmock(HTTParty).should_receive(:post).with(any, :body => other_args.merge({ :jruby => true })).and_return(response_with_build_id)
394
+ flexmock(HTTParty).should_receive(:get).and_return({ "done" => true, "results" => "job 1 done: ...." })
395
+ flexmock(requester).should_receive(:sleep)
396
+ flexmock(requester).should_receive(:print)
397
+ flexmock(requester).should_receive(:puts)
398
+ mock_file_sizes
399
+
400
+ requester.run_tests(TestUnitAdapter, 'test')
401
+ end
402
+
403
+ end
404
+
405
+ end
406
+
407
+ end
@@ -0,0 +1,7 @@
1
+ server_host: hostname
2
+ rsync_path: /path
3
+ rsync_ignores: .git tmp
4
+ project: appname
5
+ ssh_tunnel: false
6
+ server_user: user
7
+ available_runner_usage: 50%
@@ -0,0 +1,2 @@
1
+ server_host: <%= "dynamic_host" %>
2
+ available_runner_usage: "<%= 50 %>%"
@@ -0,0 +1,94 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '../../lib/shared/testbot.rb'))
2
+ require File.expand_path(File.join(File.dirname(__FILE__), '../../lib/runner/job.rb'))
3
+ require 'test/unit'
4
+ require 'shoulda'
5
+ require 'flexmock/test_unit'
6
+
7
+ module Testbot::Runner
8
+
9
+ class JobTest < Test::Unit::TestCase
10
+
11
+ def expect_put_with(id, result_text, status, time = 0)
12
+ expected_result = "\n#{`hostname`.chomp}:#{Dir.pwd}\n"
13
+ expected_result += result_text
14
+ flexmock(Server).should_receive(:put).once.with("/jobs/#{id}", :body =>
15
+ { :result => expected_result, :status => status, :time => time })
16
+ end
17
+
18
+ def expect_put
19
+ flexmock(Server).should_receive(:put).once
20
+ end
21
+
22
+ def expect_put_to_timeout
23
+ flexmock(Server).should_receive(:put).and_raise(Timeout::Error)
24
+ end
25
+
26
+ def stub_duration(seconds)
27
+ time ||= Time.now
28
+ flexmock(Time).should_receive(:now).and_return(time, time + seconds)
29
+ end
30
+
31
+ should "be able to run a successful job" do
32
+ job = Job.new(Runner.new({}), 10, "00:00", "project", "/tmp/testbot/user", "spec", "ruby", "spec/foo_spec.rb spec/bar_spec.rb")
33
+ flexmock(job).should_receive(:puts)
34
+ stub_duration(0)
35
+
36
+ expect_put_with(10, "result text", "successful")
37
+ flexmock(job).should_receive(:run_and_return_result).once.
38
+ with("export RAILS_ENV=test; export TEST_ENV_NUMBER=; cd project; export RSPEC_COLOR=true; ruby -S bundle exec rspec spec/foo_spec.rb spec/bar_spec.rb").
39
+ and_return('result text')
40
+ flexmock(RubyEnv).should_receive(:bundler?).returns(true)
41
+ flexmock(RubyEnv).should_receive(:rvm?).returns(false)
42
+ job.run(0)
43
+ end
44
+
45
+ should "not raise an error when posting results time out" do
46
+ job = Job.new(Runner.new({}), 10, "00:00", "project", "/tmp/testbot/user", "spec", "ruby", "spec/foo_spec.rb spec/bar_spec.rb")
47
+ flexmock(job).should_receive(:puts)
48
+
49
+ # We're using send here because triggering post_results though the rest of the
50
+ # code requires very complex setup. The code need to be refactored to be more testable.
51
+ expect_put
52
+ job.send(:post_results, "result text")
53
+ expect_put_to_timeout
54
+ job.send(:post_results, "result text")
55
+ end
56
+
57
+ should "not be successful when the job fails" do
58
+ job = Job.new(Runner.new({}), 10, "00:00", "project", "/tmp/testbot/user", "spec", "ruby", "spec/foo_spec.rb spec/bar_spec.rb")
59
+ flexmock(job).should_receive(:puts)
60
+ stub_duration(0)
61
+
62
+ expect_put_with(10, "result text", "failed")
63
+ flexmock(job).should_receive(:run_and_return_result).and_return('result text')
64
+ flexmock(job).should_receive(:success?).and_return(false)
65
+ job.run(0)
66
+ end
67
+
68
+ should "set an instance number when the instance is not 0" do
69
+ job = Job.new(Runner.new({}), 10, "00:00", "project", "/tmp/testbot/user", "spec", "ruby", "spec/foo_spec.rb spec/bar_spec.rb")
70
+ flexmock(job).should_receive(:puts)
71
+ stub_duration(0)
72
+
73
+ expect_put_with(10, "result text", "successful")
74
+ flexmock(job).should_receive(:run_and_return_result).
75
+ with(/TEST_ENV_NUMBER=2/).
76
+ and_return('result text')
77
+ flexmock(RubyEnv).should_receive(:rvm?).returns(false)
78
+ job.run(1)
79
+ end
80
+
81
+ should "return test runtime in milliseconds" do
82
+ job = Job.new(Runner.new({}), 10, "00:00", "project", "/tmp/testbot/user", "spec", "ruby", "spec/foo_spec.rb spec/bar_spec.rb")
83
+ flexmock(job).should_receive(:puts)
84
+
85
+ stub_duration(10.55)
86
+ expect_put_with(10, "result text", "successful", 1055)
87
+ flexmock(job).should_receive(:run_and_return_result).and_return('result text')
88
+ flexmock(RubyEnv).should_receive(:rvm?).returns(false)
89
+ job.run(0)
90
+ end
91
+
92
+ end
93
+
94
+ end
@@ -0,0 +1,20 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '../../lib/shared/testbot.rb'))
2
+ require File.expand_path(File.join(File.dirname(__FILE__), '../../lib/runner/safe_result_text.rb'))
3
+ require 'test/unit'
4
+ require 'shoulda'
5
+
6
+ module Testbot::Runner
7
+
8
+ class SafeResultTextTest < Test::Unit::TestCase
9
+
10
+ should "not break escape sequences" do
11
+ assert_equal "^[[32m.^[[0m^[[32m.^[[0m", SafeResultText.clean("^[[32m.^[[0m^[[32m.^[[0m^[[32m.")
12
+ assert_equal "^[[32m.^[[0m^[[32m.^[[0m", SafeResultText.clean("^[[32m.^[[0m^[[32m.^[[0m^[[3")
13
+ assert_equal "^[[32m.^[[0m", SafeResultText.clean("^[[32m.^[[0m^[")
14
+ assert_equal "[32m.[0m[32m.[0m[3", SafeResultText.clean("[32m.[0m[32m.[0m[3")
15
+ assert_equal "...", SafeResultText.clean("...")
16
+ end
17
+
18
+ end
19
+
20
+ end
@@ -0,0 +1,43 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '../../lib/server/group'))
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+ require 'flexmock/test_unit'
5
+
6
+ module Testbot::Server
7
+
8
+ class GroupTest < Test::Unit::TestCase
9
+
10
+ context "self.build" do
11
+
12
+ should "create file groups based on the number of instances" do
13
+ groups = Group.build([ 'spec/models/car_spec.rb', 'spec/models/car2_spec.rb',
14
+ 'spec/models/house_spec.rb', 'spec/models/house2_spec.rb' ], [ 1, 1, 1, 1 ], 2, 'spec')
15
+
16
+ assert_equal 2, groups.size
17
+ assert_equal [ 'spec/models/house2_spec.rb', 'spec/models/house_spec.rb' ], groups[0]
18
+ assert_equal [ 'spec/models/car2_spec.rb', 'spec/models/car_spec.rb' ], groups[1]
19
+ end
20
+
21
+ should "create a small grop when there isn't enough specs to fill a normal one" do
22
+ groups = Group.build(["spec/models/car_spec.rb", "spec/models/car2_spec.rb",
23
+ "spec/models/house_spec.rb", "spec/models/house2_spec.rb",
24
+ "spec/models/house3_spec.rb"], [ 1, 1, 1, 1, 1 ], 3, 'spec')
25
+
26
+ assert_equal 3, groups.size
27
+ assert_equal [ "spec/models/car_spec.rb" ], groups[2]
28
+ end
29
+
30
+ should "use sizes when building groups" do
31
+ groups = Group.build([ 'spec/models/car_spec.rb', 'spec/models/car2_spec.rb',
32
+ 'spec/models/house_spec.rb', 'spec/models/house2_spec.rb' ], [ 40, 10, 10, 20 ], 2, 'spec')
33
+
34
+ assert_equal [ 'spec/models/car_spec.rb' ], groups[0]
35
+ assert ![ 'spec/models/house2_spec.rb', 'spec/models/car2_spec.rb', 'spec/models/house_spec.rb' ].
36
+ find { |file| !groups[1].include?(file) }
37
+ end
38
+
39
+ end
40
+
41
+ end
42
+
43
+ end