spin 0.7.0 → 0.7.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.
@@ -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