spin 0.7.0 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,5 @@
1
1
  require 'spin/version'
2
2
  require 'spin/hooks'
3
- require 'spin/test_process'
4
3
  require 'spin/logger'
5
4
  require 'socket'
6
5
  require 'tempfile' # Dir.tmpdir
@@ -17,8 +16,9 @@ module Spin
17
16
 
18
17
  PUSH_FILE_SEPARATOR = '|'
19
18
  ARGS_SEPARATOR = ' -- '
20
- # The time window used to detect 2 successive SIGINT (Ctrl+C) signals.
21
- SIGINT_TIME_WINDOW = 5
19
+ # Messages written to/read from the self-pipe queue.
20
+ SIGQUIT_MESSAGE = 'SIGQUIT'
21
+ SIGINT_MESSAGE = 'SIGINT'
22
22
 
23
23
  class << self
24
24
  def serve(options)
@@ -35,15 +35,66 @@ module Spin
35
35
 
36
36
  open_socket do |socket|
37
37
  preload(options) if root_path
38
+ self_read, self_write = IO.pipe
38
39
 
39
- logger.info "Pushing test results back to push processes" if options[:push_results]
40
+ if options[:push_results]
41
+ logger.info "Pushing test results back to push processes"
42
+ else
43
+ trap('SIGQUIT') { sigquit_handler(self_write) }
44
+ end
45
+ trap('SIGINT') { sigint_handler(self_write) }
40
46
 
41
47
  loop do
42
- run_pushed_tests(socket, options)
48
+ readable_io = ready_while do
49
+ IO.select([socket, self_read])[0][0]
50
+ end
51
+
52
+ if readable_io == self_read
53
+ # One of our signal handlers has fired
54
+ case readable_io.gets.strip
55
+ when SIGQUIT_MESSAGE
56
+ rerun_last_tests(options)
57
+ when SIGINT_MESSAGE
58
+ exit_server(socket)
59
+ end
60
+ else
61
+ # The socket must have had a new test written to it
62
+ run_pushed_tests(socket, options)
63
+ end
43
64
  end
44
65
  end
45
66
  end
46
67
 
68
+ # This method is called when a SIGQUIT ought to be handled.
69
+ #
70
+ # Given the self-pipe +queue+, adds a SIGQUIT message to it. Message is
71
+ # *not* queued if the current process is not the Spin server process (i.e.
72
+ # it's a test process). Otherwise, more than one message would be added to
73
+ # the queue when Ctrl+\ is pressed.
74
+ #
75
+ def sigquit_handler(queue)
76
+ return unless server_process?
77
+
78
+ queue.puts(SIGQUIT_MESSAGE)
79
+ end
80
+
81
+ # This method is called when a SIGINT ought to be handled.
82
+ #
83
+ # Given the self-pipe +queue+, adds a SIGINT message to it. Message is
84
+ # *not* queued if either of these are true:
85
+ #
86
+ # 1. The current process is not the Spin server process (i.e. it's a test
87
+ # process). Instead, the signal is "bubbled up" by exiting.
88
+ #
89
+ # 2. The Spin server is not ready for a new command.
90
+ #
91
+ def sigint_handler(queue)
92
+ exit unless server_process?
93
+ return unless ready?
94
+
95
+ queue.puts(SIGINT_MESSAGE)
96
+ end
97
+
47
98
  def logger
48
99
  @logger ||= Spin::Logger.new
49
100
  end
@@ -136,10 +187,6 @@ module Spin
136
187
  end
137
188
 
138
189
  def run_pushed_tests(socket, options)
139
- rerun_last_tests_on_quit(options) unless options[:push_results]
140
-
141
- notify_ready
142
-
143
190
  # Since `spin push` reconnects each time it has new files for us we just
144
191
  # need to accept(2) connections from it.
145
192
  conn = socket.accept
@@ -172,36 +219,28 @@ module Spin
172
219
  end
173
220
  end
174
221
 
175
- # Trap SIGQUIT (Ctrl+\) and re-run the last files that were pushed
176
- # TODO test this
177
- def rerun_last_tests_on_quit(options)
178
- trap('QUIT') { sigquit_handler(options) }
179
- end
180
-
181
- # This method is called when a SIGQUIT ought to be handled.
182
- def sigquit_handler(options)
183
- # If the current process is not the Spin server process, ignore the
184
- # signal by doing nothing.
185
- return unless server_process?
186
-
222
+ # Reruns the last tests that were pushed.
223
+ def rerun_last_tests(options)
187
224
  unless @last_files_ran
188
225
  logger.fatal "Cannot rerun last tests, please push a file to Spin server first"
189
226
  return
190
227
  end
191
228
 
192
- if test_process.alive?
193
- logger.fatal "Cannot rerun last tests, test process #{test_process} still alive"
194
- return
195
- end
196
-
197
229
  fork_and_run(@last_files_ran, nil, options.merge(:trailing_args => @last_trailing_args_used))
230
+ end
198
231
 
199
- notify_ready
232
+ # Changes Spin server's "ready" state to +true+ while the given +block+
233
+ # executes. Returns the result of the +block+.
234
+ def ready_while(&block)
235
+ @ready = true
236
+ logger.info('Ready')
237
+ yield.tap { @ready = false }
200
238
  end
201
239
 
202
- # Notify the user that Spin server is ready for new tests.
203
- def notify_ready
204
- logger.info "Ready"
240
+ # Returns Spin server's "ready" state. If +true+, this indicates that the
241
+ # server is available for new tests or commands.
242
+ def ready?
243
+ @ready
205
244
  end
206
245
 
207
246
  def preload(options)
@@ -252,38 +291,16 @@ module Spin
252
291
  File.delete(file) if File.exist?(file)
253
292
  socket = UNIXServer.open(file)
254
293
 
255
- trap('SIGINT') { sigint_handler(socket) }
256
-
257
294
  yield socket
258
295
  ensure
259
296
  File.delete(file) if file && File.exist?(file)
260
297
  end
261
298
 
262
- # This method is called when a SIGINT ought to be handled.
263
- def sigint_handler(socket)
264
- # If the current process is not the Spin server process, allow the signal
265
- # to "bubble up" by exiting.
266
- exit unless server_process?
267
-
268
- if sigint_recently_sent?
269
- socket.close
270
- exit
271
- else
272
- set_last_sigint_sent
273
- logger.info "Press Ctrl+C again (within #{SIGINT_TIME_WINDOW}s) to exit"
274
- end
275
- end
276
-
277
- # Updates the timestamp when the last SIGINT was sent.
278
- def set_last_sigint_sent
279
- @last_sigint_sent = Time.now
280
- end
281
-
282
- # Returns +true+ if a SIGINT has been sent within the time window.
283
- def sigint_recently_sent?
284
- return if @last_sigint_sent.nil?
285
-
286
- (Time.now - SIGINT_TIME_WINDOW) < @last_sigint_sent
299
+ # Exits the server process.
300
+ def exit_server(socket)
301
+ logger.info "Exiting"
302
+ socket.close
303
+ exit
287
304
  end
288
305
 
289
306
  def determine_test_framework
@@ -308,17 +325,12 @@ module Spin
308
325
  path
309
326
  end
310
327
 
311
- # Returns (and caches) a TestProcess instance.
312
- def test_process
313
- @test_process ||= Spin::TestProcess.new
314
- end
315
-
316
328
  def fork_and_run(files, conn, options)
317
329
  execute_hook(:before_fork)
318
330
  # We fork(2) before loading the file so that our pristine preloaded
319
331
  # environment is untouched. The child process will load whatever code it
320
332
  # needs to, then it exits and we're back to the baseline preloaded app.
321
- test_process.pid = fork do
333
+ fork do
322
334
  # To push the test results to the push process instead of having them
323
335
  # displayed by the server, we reopen $stdout/$stderr to the open
324
336
  # connection.
@@ -361,7 +373,7 @@ module Spin
361
373
  # WAIT: We don't want the parent process handling multiple test runs at the same
362
374
  # time because then we'd need to deal with multiple test databases, and
363
375
  # that destroys the idea of being simple to use.
364
- test_process.wait
376
+ Process.wait
365
377
  end
366
378
 
367
379
  def socket_file
@@ -1,3 +1,3 @@
1
1
  module Spin
2
- VERSION = "0.7.0"
2
+ VERSION = "0.7.1"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spin
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.7.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-07-25 00:00:00.000000000 Z
12
+ date: 2013-08-18 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -60,7 +60,6 @@ files:
60
60
  - lib/spin/cli.rb
61
61
  - lib/spin/hooks.rb
62
62
  - lib/spin/logger.rb
63
- - lib/spin/test_process.rb
64
63
  - lib/spin/version.rb
65
64
  - lib/spin.rb
66
65
  - spec/integration_spec.rb
@@ -91,3 +90,4 @@ specification_version: 3
91
90
  summary: Spin preloads your Rails environment to speed up your autotest(ish) workflow.
92
91
  test_files:
93
92
  - spec/integration_spec.rb
93
+ has_rdoc:
@@ -1,21 +0,0 @@
1
- module Spin
2
- class TestProcess
3
- attr_accessor :pid
4
-
5
- # Use wait(2) to block execution until the test process has finished. When
6
- # finished, reset the assigned pid.
7
- def wait
8
- Process.wait
9
- @pid = nil
10
- end
11
-
12
- # Returns +true+ if the test process is alive.
13
- def alive?
14
- !@pid.nil?
15
- end
16
-
17
- def to_s
18
- @pid.to_s
19
- end
20
- end
21
- end