spring 1.1.3 → 1.3.0

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.
@@ -0,0 +1,346 @@
1
+ # encoding: utf-8
2
+
3
+ require "io/wait"
4
+ require "timeout"
5
+ require "spring/sid"
6
+ require "spring/client"
7
+
8
+ module Spring
9
+ module Test
10
+ class AcceptanceTest < ActiveSupport::TestCase
11
+ runnables.delete self # prevent Minitest running this class
12
+
13
+ DEFAULT_SPEEDUP = 0.8
14
+
15
+ def rails_version
16
+ ENV['RAILS_VERSION'] || '~> 4.2.0'
17
+ end
18
+
19
+ # Extension point for spring-watchers-listen
20
+ def generator_klass
21
+ Spring::Test::ApplicationGenerator
22
+ end
23
+
24
+ def generator
25
+ @@generator ||= generator_klass.new(rails_version)
26
+ end
27
+
28
+ def app
29
+ @app ||= Spring::Test::Application.new("#{Spring::Test.root}/apps/tmp")
30
+ end
31
+
32
+ def assert_output(artifacts, expected)
33
+ expected.each do |stream, output|
34
+ assert artifacts[stream].include?(output),
35
+ "expected #{stream} to include '#{output}'.\n\n#{app.debug(artifacts)}"
36
+ end
37
+ end
38
+
39
+ def assert_success(command, expected_output = nil)
40
+ artifacts = app.run(*Array(command))
41
+ assert artifacts[:status].success?, "expected successful exit status\n\n#{app.debug(artifacts)}"
42
+ assert_output artifacts, expected_output if expected_output
43
+ end
44
+
45
+ def assert_failure(command, expected_output = nil)
46
+ artifacts = app.run(*Array(command))
47
+ assert !artifacts[:status].success?, "expected unsuccessful exit status\n\n#{app.debug(artifacts)}"
48
+ assert_output artifacts, expected_output if expected_output
49
+ end
50
+
51
+ def assert_speedup(ratio = DEFAULT_SPEEDUP)
52
+ if ENV['CI']
53
+ yield
54
+ else
55
+ app.with_timing do
56
+ yield
57
+ assert app.timing_ratio < ratio, "#{app.last_time} was not less than #{ratio} of #{app.first_time}"
58
+ end
59
+ end
60
+ end
61
+
62
+ setup do
63
+ generator.generate_if_missing
64
+ generator.install_spring
65
+ generator.copy_to(app.root)
66
+ end
67
+
68
+ teardown do
69
+ app.stop_spring
70
+ end
71
+
72
+ test "basic" do
73
+ assert_speedup do
74
+ 2.times { app.run app.spring_test_command }
75
+ end
76
+ end
77
+
78
+ test "help message when called without arguments" do
79
+ assert_success "bin/spring", stdout: 'Usage: spring COMMAND [ARGS]'
80
+ end
81
+
82
+ test "test changes are picked up" do
83
+ assert_speedup do
84
+ assert_success app.spring_test_command, stdout: "0 failures"
85
+
86
+ File.write(app.test, app.test.read.sub("get :index", "raise 'omg'"))
87
+ assert_failure app.spring_test_command, stdout: "RuntimeError: omg"
88
+ end
89
+ end
90
+
91
+ test "code changes are picked up" do
92
+ assert_speedup do
93
+ assert_success app.spring_test_command, stdout: "0 failures"
94
+
95
+ File.write(app.controller, app.controller.read.sub("@posts = Post.all", "raise 'omg'"))
96
+ assert_failure app.spring_test_command, stdout: "RuntimeError: omg"
97
+ end
98
+ end
99
+
100
+ test "code changes in pre-referenced app files are picked up" do
101
+ File.write(app.path("config/initializers/load_posts_controller.rb"), "PostsController\n")
102
+
103
+ assert_speedup do
104
+ assert_success app.spring_test_command, stdout: "0 failures"
105
+
106
+ File.write(app.controller, app.controller.read.sub("@posts = Post.all", "raise 'omg'"))
107
+ assert_failure app.spring_test_command, stdout: "RuntimeError: omg"
108
+ end
109
+ end
110
+
111
+ test "app gets reloaded when preloaded files change" do
112
+ assert_success app.spring_test_command
113
+
114
+ File.write(app.application_config, app.application_config.read + <<-CODE)
115
+ class Foo
116
+ def self.omg
117
+ raise "omg"
118
+ end
119
+ end
120
+ CODE
121
+ File.write(app.test, app.test.read.sub("get :index", "Foo.omg"))
122
+
123
+ app.await_reload
124
+ assert_failure app.spring_test_command, stdout: "RuntimeError: omg"
125
+ end
126
+
127
+ test "app gets reloaded even with a ton of boot output" do
128
+ limit = UNIXSocket.pair.first.getsockopt(:SOCKET, :SNDBUF).int
129
+
130
+ assert_success app.spring_test_command
131
+ File.write(app.path("config/initializers/verbose.rb"), "#{limit}.times { puts 'x' }")
132
+
133
+ app.await_reload
134
+ assert_success app.spring_test_command
135
+ end
136
+
137
+ test "app recovers when a boot-level error is introduced" do
138
+ config = app.application_config.read
139
+
140
+ assert_success app.spring_test_command
141
+
142
+ File.write(app.application_config, "#{config}\nomg")
143
+ app.await_reload
144
+
145
+ assert_failure app.spring_test_command
146
+
147
+ File.write(app.application_config, config)
148
+ assert_success app.spring_test_command
149
+ end
150
+
151
+ test "stop command kills server" do
152
+ app.run app.spring_test_command
153
+ assert app.spring_env.server_running?, "The server should be running but it isn't"
154
+
155
+ assert_success "bin/spring stop"
156
+ assert !app.spring_env.server_running?, "The server should not be running but it is"
157
+ end
158
+
159
+ test "custom commands" do
160
+ # Start spring before setting up the command, to test that it gracefully upgrades itself
161
+ assert_success "bin/rails runner ''"
162
+
163
+ File.write(app.spring_config, <<-CODE)
164
+ class CustomCommand
165
+ def call
166
+ puts "omg"
167
+ end
168
+
169
+ def exec_name
170
+ "rake"
171
+ end
172
+ end
173
+
174
+ Spring.register_command "custom", CustomCommand.new
175
+ CODE
176
+
177
+ assert_success "bin/spring custom", stdout: "omg"
178
+
179
+ assert_success "bin/spring binstub custom"
180
+ assert_success "bin/custom", stdout: "omg"
181
+
182
+ app.env["DISABLE_SPRING"] = "1"
183
+ assert_success %{bin/custom -e 'puts "foo"'}, stdout: "foo"
184
+ end
185
+
186
+ test "binstub" do
187
+ assert_success "bin/rails server --help", stdout: "Usage: rails server" # rails command fallback
188
+
189
+ assert_success "#{app.spring} binstub rake", stdout: "bin/rake: spring already present"
190
+
191
+ assert_success "#{app.spring} binstub --remove rake", stdout: "bin/rake: spring removed"
192
+ assert !app.path("bin/rake").read.include?(Spring::Client::Binstub::LOADER)
193
+ assert_success "bin/rake -T", stdout: "rake db:migrate"
194
+ end
195
+
196
+ test "binstub when spring is uninstalled" do
197
+ app.run! "gem uninstall --ignore-dependencies spring"
198
+ File.write(app.gemfile, app.gemfile.read.gsub(/gem 'spring.*/, ""))
199
+ assert_success "bin/rake -T", stdout: "rake db:migrate"
200
+ end
201
+
202
+ test "binstub upgrade" do
203
+ File.write(app.path("bin/rake"), <<CODE)
204
+ #!/usr/bin/env ruby
205
+
206
+ if !Process.respond_to?(:fork) || Gem::Specification.find_all_by_name("spring").empty?
207
+ exec "bundle", "exec", "rake", *ARGV
208
+ else
209
+ ARGV.unshift "rake"
210
+ load Gem.bin_path("spring", "spring")
211
+ end
212
+ CODE
213
+
214
+ File.write(app.path("bin/rails"), <<CODE)
215
+ #!/usr/bin/env ruby
216
+
217
+ if !Process.respond_to?(:fork) || Gem::Specification.find_all_by_name("spring").empty?
218
+ APP_PATH = File.expand_path('../../config/application', __FILE__)
219
+ require_relative '../config/boot'
220
+ require 'rails/commands'
221
+ else
222
+ ARGV.unshift "rails"
223
+ load Gem.bin_path("spring", "spring")
224
+ end
225
+ CODE
226
+
227
+ assert_success "bin/spring binstub --all", stdout: "upgraded"
228
+
229
+ assert_equal app.path("bin/rake").read, <<CODE
230
+ #!/usr/bin/env ruby
231
+ #{Spring::Client::Binstub::LOADER.strip}
232
+ require 'bundler/setup'
233
+ load Gem.bin_path('rake', 'rake')
234
+ CODE
235
+
236
+ assert_equal app.path("bin/rails").read, <<CODE
237
+ #!/usr/bin/env ruby
238
+ #{Spring::Client::Binstub::LOADER.strip}
239
+ APP_PATH = File.expand_path('../../config/application', __FILE__)
240
+ require_relative '../config/boot'
241
+ require 'rails/commands'
242
+ CODE
243
+ end
244
+
245
+ test "after fork callback" do
246
+ File.write(app.spring_config, "Spring.after_fork { puts '!callback!' }")
247
+ assert_success "bin/rails runner 'puts 2'", stdout: "!callback!\n2"
248
+ end
249
+
250
+ test "global config file evaluated" do
251
+ File.write("#{app.user_home}/.spring.rb", "Spring.after_fork { puts '!callback!' }")
252
+ assert_success "bin/rails runner 'puts 2'", stdout: "!callback!\n2"
253
+ end
254
+
255
+ test "missing config/application.rb" do
256
+ app.application_config.delete
257
+ assert_failure "bin/rake -T", stderr: "unable to find your config/application.rb"
258
+ end
259
+
260
+ test "piping" do
261
+ assert_success "bin/rake -T | grep db", stdout: "rake db:migrate"
262
+ end
263
+
264
+ test "status" do
265
+ assert_success "bin/spring status", stdout: "Spring is not running"
266
+ assert_success "bin/rails runner ''"
267
+ assert_success "bin/spring status", stdout: "Spring is running"
268
+ end
269
+
270
+ test "runner command sets Rails environment from command-line options" do
271
+ assert_success "bin/rails runner -e test 'puts Rails.env'", stdout: "test"
272
+ assert_success "bin/rails runner --environment=test 'puts Rails.env'", stdout: "test"
273
+ end
274
+
275
+ test "forcing rails env via environment variable" do
276
+ app.env['RAILS_ENV'] = 'test'
277
+ assert_success "bin/rake -p 'Rails.env'", stdout: "test"
278
+ end
279
+
280
+ test "setting env vars with rake" do
281
+ File.write(app.path("lib/tasks/env.rake"), <<-'CODE')
282
+ task :print_rails_env => :environment do
283
+ puts Rails.env
284
+ end
285
+
286
+ task :print_env do
287
+ ENV.each { |k, v| puts "#{k}=#{v}" }
288
+ end
289
+
290
+ task(:default).clear.enhance [:print_rails_env]
291
+ CODE
292
+
293
+ assert_success "bin/rake RAILS_ENV=test print_rails_env", stdout: "test"
294
+ assert_success "bin/rake FOO=bar print_env", stdout: "FOO=bar"
295
+ assert_success "bin/rake", stdout: "test"
296
+ end
297
+
298
+ test "changing the Gemfile works" do
299
+ assert_success %(bin/rails runner 'require "sqlite3"')
300
+
301
+ File.write(app.gemfile, app.gemfile.read.sub(%{gem 'sqlite3'}, %{# gem 'sqlite3'}))
302
+ app.await_reload
303
+
304
+ assert_failure %(bin/rails runner 'require "sqlite3"'), stderr: "sqlite3"
305
+ end
306
+
307
+ test "changing the Gemfile works when spring calls into itself" do
308
+ File.write(app.path("script.rb"), <<-CODE)
309
+ gemfile = Rails.root.join("Gemfile")
310
+ File.write(gemfile, "\#{gemfile.read}gem 'devise'\\n")
311
+ Bundler.with_clean_env do
312
+ system(#{app.env.inspect}, "bundle install")
313
+ end
314
+ output = `\#{Rails.root.join('bin/rails')} runner 'require "devise"; puts "done";'`
315
+ exit output == "done\n"
316
+ CODE
317
+
318
+ assert_success [%(bin/rails runner 'load Rails.root.join("script.rb")'), timeout: 60]
319
+ end
320
+
321
+ test "changing the environment between runs" do
322
+ File.write(app.application_config, "#{app.application_config.read}\nENV['BAR'] = 'bar'")
323
+
324
+ app.env["OMG"] = "1"
325
+ app.env["FOO"] = "1"
326
+ app.env["RUBYOPT"] = "-rubygems"
327
+
328
+ assert_success %(bin/rails runner 'p ENV["OMG"]'), stdout: "1"
329
+ assert_success %(bin/rails runner 'p ENV["BAR"]'), stdout: "bar"
330
+ assert_success %(bin/rails runner 'p ENV.key?("BUNDLE_GEMFILE")'), stdout: "true"
331
+ assert_success %(bin/rails runner 'p ENV["RUBYOPT"]'), stdout: "bundler"
332
+
333
+ app.env["OMG"] = "2"
334
+ app.env.delete "FOO"
335
+
336
+ assert_success %(bin/rails runner 'p ENV["OMG"]'), stdout: "2"
337
+ assert_success %(bin/rails runner 'p ENV.key?("FOO")'), stdout: "false"
338
+ end
339
+
340
+ test "Kernel.raise remains private" do
341
+ expr = "p Kernel.private_instance_methods.include?(:raise)"
342
+ assert_success %(bin/rails runner '#{expr}'), stdout: "true"
343
+ end
344
+ end
345
+ end
346
+ end
@@ -2,43 +2,6 @@ require "spring/env"
2
2
 
3
3
  module Spring
4
4
  module Test
5
- class RailsVersion
6
- attr_reader :version
7
-
8
- def initialize(string)
9
- @version = Gem::Version.new(string)
10
- end
11
-
12
- def rails_3?
13
- version < Gem::Version.new("4.0.0")
14
- end
15
- alias needs_testunit? rails_3?
16
-
17
- def test_command
18
- needs_testunit? ? 'bin/testunit' : 'bin/rake test'
19
- end
20
-
21
- def controller_tests_dir
22
- rails_3? ? 'functional' : 'controllers'
23
- end
24
-
25
- def bundles_spring?
26
- version.segments.take(2) == [4, 1] || version > Gem::Version.new("4.1")
27
- end
28
-
29
- def major
30
- version.segments[0]
31
- end
32
-
33
- def minor
34
- version.segments[1]
35
- end
36
-
37
- def to_s
38
- version.to_s
39
- end
40
- end
41
-
42
5
  class Application
43
6
  DEFAULT_TIMEOUT = ENV['CI'] ? 30 : 10
44
7
 
@@ -238,7 +201,7 @@ module Spring
238
201
 
239
202
  def bundle
240
203
  run! "(gem list bundler | grep bundler) || gem install bundler", timeout: nil, retry: 2
241
- run! "bundle update --retry=2", timeout: nil
204
+ run! "bundle check || bundle update --retry=2", timeout: nil
242
205
  end
243
206
 
244
207
  private
@@ -250,95 +213,5 @@ module Spring
250
213
  false
251
214
  end
252
215
  end
253
-
254
- class ApplicationGenerator
255
- attr_reader :version_constraint, :version, :application
256
-
257
- def initialize(version_constraint)
258
- @version_constraint = version_constraint
259
- @version = RailsVersion.new(version_constraint.split(' ').last)
260
- @application = Application.new(root)
261
- @bundled = false
262
- end
263
-
264
- def root
265
- "#{TEST_ROOT}/apps/rails-#{version.major}-#{version.minor}-spring-#{Spring::VERSION}"
266
- end
267
-
268
- def system(command)
269
- if ENV["SPRING_DEBUG"]
270
- puts "$ #{command}\n"
271
- else
272
- command = "(#{command}) > /dev/null"
273
- end
274
-
275
- Kernel.system(command) or raise "command failed: #{command}"
276
- puts if ENV["SPRING_DEBUG"]
277
- end
278
-
279
- # Sporadic SSL errors keep causing test failures so there are anti-SSL workarounds here
280
- def generate
281
- Bundler.with_clean_env do
282
- system("gem list rails --installed --version '#{version_constraint}' || " \
283
- "gem install rails --clear-sources --source http://rubygems.org --version '#{version_constraint}'")
284
-
285
- @version = RailsVersion.new(`ruby -e 'puts Gem::Specification.find_by_name("rails", "#{version_constraint}").version'`.chomp)
286
-
287
- skips = %w(--skip-bundle --skip-javascript --skip-sprockets)
288
- skips << "--skip-spring" if version.bundles_spring?
289
-
290
- system("rails _#{version}_ new #{application.root} #{skips.join(' ')}")
291
- raise "application generation failed" unless application.exists?
292
-
293
- FileUtils.mkdir_p(application.gem_home)
294
- FileUtils.mkdir_p(application.user_home)
295
- FileUtils.rm_rf(application.path("test/performance"))
296
-
297
- File.write(application.gemfile, "#{application.gemfile.read}gem 'spring', '#{Spring::VERSION}'\n")
298
-
299
- if version.needs_testunit?
300
- File.write(application.gemfile, "#{application.gemfile.read}gem 'spring-commands-testunit'\n")
301
- end
302
-
303
- File.write(application.gemfile, application.gemfile.read.sub("https://rubygems.org", "http://rubygems.org"))
304
-
305
- if application.path("bin").exist?
306
- FileUtils.cp_r(application.path("bin"), application.path("bin_original"))
307
- end
308
- end
309
-
310
- install_spring
311
-
312
- application.run! "bundle exec rails g scaffold post title:string"
313
- application.run! "bundle exec rake db:migrate db:test:clone"
314
- end
315
-
316
- def generate_if_missing
317
- generate unless application.exists?
318
- end
319
-
320
- def install_spring
321
- return if @installed
322
-
323
- system("gem build spring.gemspec 2>&1")
324
- application.run! "gem install ../../../spring-#{Spring::VERSION}.gem", timeout: nil
325
-
326
- application.bundle
327
-
328
- FileUtils.rm_rf application.path("bin")
329
-
330
- if application.path("bin_original").exist?
331
- FileUtils.cp_r application.path("bin_original"), application.path("bin")
332
- end
333
-
334
- application.run! "#{application.spring} binstub --all"
335
- @installed = true
336
- end
337
-
338
- def copy_to(path)
339
- system("rm -rf #{path}")
340
- system("cp -r #{application.root} #{path}")
341
- end
342
- end
343
216
  end
344
217
  end
@@ -0,0 +1,121 @@
1
+ module Spring
2
+ module Test
3
+ class ApplicationGenerator
4
+ attr_reader :version_constraint, :version, :application
5
+
6
+ def initialize(version_constraint)
7
+ @version_constraint = version_constraint
8
+ @version = RailsVersion.new(version_constraint.split(' ').last)
9
+ @application = Application.new(root)
10
+ @bundled = false
11
+ end
12
+
13
+ def test_root
14
+ Pathname.new Spring::Test.root
15
+ end
16
+
17
+ def root
18
+ test_root.join("apps/rails-#{version.major}-#{version.minor}-spring-#{Spring::VERSION}")
19
+ end
20
+
21
+ def system(command)
22
+ if ENV["SPRING_DEBUG"]
23
+ puts "$ #{command}\n"
24
+ else
25
+ command = "(#{command}) > /dev/null"
26
+ end
27
+
28
+ Kernel.system(command) or raise "command failed: #{command}"
29
+ puts if ENV["SPRING_DEBUG"]
30
+ end
31
+
32
+ def generate
33
+ Bundler.with_clean_env { generate_files }
34
+ install_spring
35
+ generate_scaffold
36
+ end
37
+
38
+ # Sporadic SSL errors keep causing test failures so there are anti-SSL workarounds here
39
+ def generate_files
40
+ system("gem list '^rails$' --installed --version '#{version_constraint}' || " \
41
+ "gem install rails --clear-sources --source http://rubygems.org --version '#{version_constraint}'")
42
+
43
+ @version = RailsVersion.new(`ruby -e 'puts Gem::Specification.find_by_name("rails", "#{version_constraint}").version'`.chomp)
44
+
45
+ skips = %w(--skip-bundle --skip-javascript --skip-sprockets)
46
+ skips << "--skip-spring" if version.bundles_spring?
47
+
48
+ system("rails _#{version}_ new #{application.root} #{skips.join(' ')}")
49
+ raise "application generation failed" unless application.exists?
50
+
51
+ FileUtils.mkdir_p(application.gem_home)
52
+ FileUtils.mkdir_p(application.user_home)
53
+ FileUtils.rm_rf(application.path("test/performance"))
54
+
55
+ File.write(application.gemfile, "#{application.gemfile.read}gem 'spring', '#{Spring::VERSION}'\n")
56
+
57
+ if version.needs_testunit?
58
+ File.write(application.gemfile, "#{application.gemfile.read}gem 'spring-commands-testunit'\n")
59
+ end
60
+
61
+ File.write(application.gemfile, application.gemfile.read.sub("https://rubygems.org", "http://rubygems.org"))
62
+
63
+ if application.path("bin").exist?
64
+ FileUtils.cp_r(application.path("bin"), application.path("bin_original"))
65
+ end
66
+ end
67
+
68
+ def generate_if_missing
69
+ generate unless application.exists?
70
+ end
71
+
72
+ def install_spring
73
+ return if @installed
74
+
75
+ build_and_install_gems
76
+
77
+ application.bundle
78
+
79
+ FileUtils.rm_rf application.path("bin")
80
+
81
+ if application.path("bin_original").exist?
82
+ FileUtils.cp_r application.path("bin_original"), application.path("bin")
83
+ end
84
+
85
+ application.run! "#{application.spring} binstub --all"
86
+ @installed = true
87
+ end
88
+
89
+ def manually_built_gems
90
+ %w(spring)
91
+ end
92
+
93
+ def build_and_install_gems
94
+ manually_built_gems.each do |name|
95
+ spec = Gem::Specification.find_by_name(name)
96
+
97
+ FileUtils.cd(spec.gem_dir) do
98
+ FileUtils.rm(Dir.glob("#{name}-*.gem"))
99
+ system("gem build #{name}.gemspec 2>&1")
100
+ end
101
+
102
+ application.run! "gem install #{spec.gem_dir}/#{name}-*.gem", timeout: nil
103
+ end
104
+ end
105
+
106
+ def copy_to(path)
107
+ system("rm -rf #{path}")
108
+ system("cp -r #{application.root} #{path}")
109
+ end
110
+
111
+ def generate_scaffold
112
+ application.run! "bundle exec rails g scaffold post title:string"
113
+ application.run! "bundle exec rake db:migrate db:test:clone"
114
+ end
115
+
116
+ def gemspec(name)
117
+ "#{Gem::Specification.find_by_name(name).gem_dir}/#{name}.gemspec"
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,40 @@
1
+ module Spring
2
+ module Test
3
+ class RailsVersion
4
+ attr_reader :version
5
+
6
+ def initialize(string)
7
+ @version = Gem::Version.new(string)
8
+ end
9
+
10
+ def rails_3?
11
+ version < Gem::Version.new("4.0.0")
12
+ end
13
+ alias needs_testunit? rails_3?
14
+
15
+ def test_command
16
+ needs_testunit? ? 'bin/testunit' : 'bin/rake test'
17
+ end
18
+
19
+ def controller_tests_dir
20
+ rails_3? ? 'functional' : 'controllers'
21
+ end
22
+
23
+ def bundles_spring?
24
+ version.segments.take(2) == [4, 1] || version > Gem::Version.new("4.1")
25
+ end
26
+
27
+ def major
28
+ version.segments[0]
29
+ end
30
+
31
+ def minor
32
+ version.segments[1]
33
+ end
34
+
35
+ def to_s
36
+ version.to_s
37
+ end
38
+ end
39
+ end
40
+ end