Skip to content

Commit 0468197

Browse files
authored
Merge pull request #377 from chadlwilson/bundler-system
[tests] fix flaky tests
2 parents 05c5326 + 9e80b84 commit 0468197

File tree

10 files changed

+116
-111
lines changed

10 files changed

+116
-111
lines changed

.bundle/config

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
---
2+
BUNDLE_VERSION: "system"

examples/camping/.bundle/config

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
---
2+
BUNDLE_VERSION: "system"

examples/rails8/.bundle/config

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
---
2+
BUNDLE_VERSION: "system"

examples/sinatra/.bundle/config

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
---
2+
BUNDLE_VERSION: "system"

gemfiles/.bundle/config

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
---
2-
BUNDLE_RETRY: "1"
2+
BUNDLE_VERSION: "system"

src/main/java/org/jruby/rack/RackApplicationFactoryDecorator.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,8 @@ public void destroy() {
133133
public RackApplication getApplication() throws RackException {
134134
final RuntimeException error = getInitError();
135135
if ( error != null ) {
136-
log(DEBUG, "due a previous initialization failure application instance can not be returned");
137-
throw error; // this is better - we shall never return null here ...
136+
log(DEBUG, "due to a previous initialization failure application instance can not be returned");
137+
throw error;
138138
}
139139
return getApplicationImpl();
140140
}

src/spec/ruby/jruby/rack/booter_spec.rb

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -294,17 +294,17 @@
294294
@runtime.evalScriptlet("load 'jruby/rack/boot/rack.rb'")
295295

296296
# booter got setup :
297-
should_not_eval_as_nil "defined?(JRuby::Rack.booter)"
298-
should_not_eval_as_nil "JRuby::Rack.booter"
297+
should_eval_as_not_nil "defined?(JRuby::Rack.booter)"
298+
should_eval_as_not_nil "JRuby::Rack.booter"
299299
should_eval_as_eql_to "JRuby::Rack.booter.class.name", 'JRuby::Rack::Booter'
300300

301301
# Booter.boot! run :
302-
should_not_eval_as_nil "ENV['RACK_ENV']"
302+
should_eval_as_not_nil "ENV['RACK_ENV']"
303303
# rack got required :
304-
should_not_eval_as_nil "defined?(Rack::RELEASE)"
305-
should_not_eval_as_nil "defined?(Rack.release)"
304+
should_eval_as_not_nil "defined?(Rack::RELEASE)"
305+
should_eval_as_not_nil "defined?(Rack.release)"
306306
# check if it got loaded correctly :
307-
should_not_eval_as_nil "Rack::Request.new({}) rescue nil"
307+
should_eval_as_not_nil "Rack::Request.new({}) rescue nil"
308308
end
309309

310310
end
@@ -341,13 +341,13 @@
341341
@runtime.evalScriptlet("load 'jruby/rack/boot/rails.rb'")
342342

343343
# booter got setup :
344-
should_not_eval_as_nil "defined?(JRuby::Rack.booter)"
345-
should_not_eval_as_nil "JRuby::Rack.booter"
344+
should_eval_as_not_nil "defined?(JRuby::Rack.booter)"
345+
should_eval_as_not_nil "JRuby::Rack.booter"
346346
should_eval_as_eql_to "JRuby::Rack.booter.class.name", 'JRuby::Rack::RailsBooter'
347347

348348
# Booter.boot! run :
349-
should_not_eval_as_nil "ENV['RACK_ENV']"
350-
should_not_eval_as_nil "ENV['RAILS_ENV']"
349+
should_eval_as_not_nil "ENV['RACK_ENV']"
350+
should_eval_as_not_nil "ENV['RAILS_ENV']"
351351

352352
# rack not yet required (let bundler decide which rack version to load) :
353353
should_eval_as_nil "defined?(Rack::RELEASE)"

src/spec/ruby/jruby/rack/rails_booter_spec.rb

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,10 +122,6 @@
122122
end
123123
end
124124

125-
after :all do
126-
$servlet_context = nil
127-
end
128-
129125
let(:railtie_class) { Class.new(Rails::Railtie) }
130126

131127
it "should have loaded the railtie" do

src/spec/ruby/rack/application_spec.rb

Lines changed: 74 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ def it_should_rewind_body
104104

105105
before :each do
106106
@app_factory = DefaultRackApplicationFactory.new
107+
reset_booter
108+
reset_servlet_context_global
107109
end
108110

109111
it "should receive a rackup script via the 'rackup' parameter" do
@@ -161,11 +163,6 @@ def it_should_rewind_body
161163
expect(input_stream.getMaximumBufferSize).to eq 420
162164
end
163165

164-
before do
165-
reset_booter
166-
JRuby::Rack.context = $servlet_context = nil
167-
end
168-
169166
it "should init and create application object without a rackup script" do
170167
$servlet_context = @servlet_context
171168
# NOTE: a workaround to be able to mock it :
@@ -303,8 +300,8 @@ def newRuntime()
303300

304301
it "creates a new Ruby runtime with the jruby-rack environment pre-loaded" do
305302
@runtime = app_factory.newRuntime
306-
should_not_eval_as_nil "defined?(::Rack)"
307-
should_not_eval_as_nil "defined?(::Rack::Handler::Servlet)"
303+
should_eval_as_not_nil "defined?(::Rack)"
304+
should_eval_as_not_nil "defined?(::Rack::Handler::Servlet)"
308305
should_eval_as_nil "defined?(Rack::Handler::Bogus)"
309306
end
310307

@@ -333,14 +330,14 @@ def newRuntime()
333330
app_factory.checkAndSetRackVersion(@runtime)
334331
@runtime.evalScriptlet "require 'rack'"
335332

336-
should_not_eval_as_nil "defined?(Bundler)"
333+
should_eval_as_not_nil "defined?(Bundler)"
337334
should_eval_as_eql_to "Rack.release", '2.2.0'
338335
should_eval_as_eql_to "Gem.loaded_specs['rack'].version.to_s", '2.2.0'
339336
end
340337

341338
it "initializes the $servlet_context global variable" do
342339
@runtime = app_factory.new_runtime
343-
should_not_eval_as_nil "defined?($servlet_context)"
340+
expect(@runtime.evalScriptlet("defined?($servlet_context)")).to be_truthy
344341
end
345342

346343
it "clears environment variables if the configuration ignores the environment" do
@@ -516,7 +513,7 @@ def reset_config
516513

517514
describe org.jruby.rack.rails.RailsRackApplicationFactory do
518515

519-
java_import org.jruby.rack.rails.RailsRackApplicationFactory
516+
require 'jruby/rack/rails_booter'
520517

521518
before :each do
522519
@app_factory = RailsRackApplicationFactory.new
@@ -557,8 +554,20 @@ def createRackServletWrapper(runtime, rackup, filename)
557554

558555
describe org.jruby.rack.PoolingRackApplicationFactory do
559556

557+
# Workaround rspec mocks/proxies not being thread-safe which causes occasional failures
558+
class Synchronized
559+
def initialize(obj)
560+
@delegate = obj
561+
@lock = Mutex.new
562+
end
563+
564+
def method_missing(name, *args, &block)
565+
@lock.synchronize { @delegate.send(name, *args, &block) }
566+
end
567+
end
568+
560569
before :each do
561-
@factory = double "factory"
570+
@factory = Synchronized.new(double("factory").as_null_object)
562571
@pooling_factory = org.jruby.rack.PoolingRackApplicationFactory.new @factory
563572
@pooling_factory.context = @rack_context
564573
end
@@ -608,7 +617,7 @@ def createRackServletWrapper(runtime, rackup, filename)
608617
it "creates applications during initialization according to the jruby.min.runtimes context parameter" do
609618
allow(@factory).to receive(:init)
610619
allow(@factory).to receive(:newApplication) do
611-
app = double "app"
620+
app = Synchronized.new(double("app").as_null_object)
612621
expect(app).to receive(:init)
613622
app
614623
end
@@ -641,7 +650,7 @@ def createRackServletWrapper(runtime, rackup, filename)
641650
it "forces the maximum size to be greater or equal to the initial size" do
642651
allow(@factory).to receive(:init)
643652
allow(@factory).to receive(:newApplication) do
644-
app = double "app"
653+
app = Synchronized.new(double("app").as_null_object)
645654
expect(app).to receive(:init)
646655
app
647656
end
@@ -655,17 +664,17 @@ def createRackServletWrapper(runtime, rackup, filename)
655664
end
656665

657666
it "retrieves the error application from the delegate factory" do
658-
app = double("app")
667+
app = double "app"
659668
expect(@factory).to receive(:getErrorApplication).and_return app
660669
expect(@pooling_factory.getErrorApplication).to eq app
661670
end
662671

663672
it "waits till initial runtimes get initialized (with wait set to true)" do
664673
allow(@factory).to receive(:init)
665674
allow(@factory).to receive(:newApplication) do
666-
app = double "app"
675+
app = Synchronized.new(double("app").as_null_object)
667676
allow(app).to receive(:init) do
668-
sleep(0.10)
677+
sleep(0.05)
669678
end
670679
app
671680
end
@@ -679,15 +688,16 @@ def createRackServletWrapper(runtime, rackup, filename)
679688

680689
it "throws an exception from getApplication when an app failed to initialize " +
681690
"(even when only a single application initialization fails)" do
691+
app_init_secs = 0.05
682692
allow(@factory).to receive(:init)
683693
app_count = java.util.concurrent.atomic.AtomicInteger.new(0)
684694
allow(@factory).to receive(:newApplication) do
685-
app = double "app"
695+
app = Synchronized.new(double("app").as_null_object)
686696
allow(app).to receive(:init) do
687697
if app_count.addAndGet(1) == 2
688698
raise org.jruby.rack.RackInitializationException.new('failed app init')
689699
end
690-
sleep(0.05)
700+
sleep(app_init_secs)
691701
end
692702
app
693703
end
@@ -701,7 +711,7 @@ def createRackServletWrapper(runtime, rackup, filename)
701711
rescue org.jruby.rack.RackInitializationException
702712
# ignore - sometimes initialization happens fast enough that the init error is thrown already
703713
end
704-
sleep(0.20)
714+
sleep(num_runtimes * app_init_secs + 0.07) # sleep with a buffer
705715

706716
failed = 0
707717
num_runtimes.times do
@@ -717,28 +727,29 @@ def createRackServletWrapper(runtime, rackup, filename)
717727
end
718728

719729
it "wait until pool is filled when invoking getApplication (with wait set to false)" do
730+
app_init_secs = 0.2
720731
allow(@factory).to receive(:init)
721732
allow(@factory).to receive(:newApplication) do
722-
app = double "app"
723-
allow(app).to receive(:init) { sleep(0.2) }
733+
app = Synchronized.new(double("app").as_null_object)
734+
allow(app).to receive(:init) { sleep(app_init_secs) }
724735
app
725736
end
726737
allow(@rack_config).to receive(:getBooleanProperty).with("jruby.runtime.init.wait").and_return false
727738
expect(@rack_config).to receive(:getInitialRuntimes).and_return 3
728739
expect(@rack_config).to receive(:getMaximumRuntimes).and_return 4
729740

730741
@pooling_factory.init(@rack_context)
731-
millis = java.lang.System.currentTimeMillis
742+
start = java.lang.System.currentTimeMillis
732743
expect(@pooling_factory.getApplication).not_to be nil
733-
millis = java.lang.System.currentTimeMillis - millis
734-
expect(millis).to be >= 150 # getApplication waited ~ 0.2 secs
744+
expect(java.lang.System.currentTimeMillis - start).to be_within(70).of(app_init_secs * 1000) # getApplication waited ~ sleep time
735745
end
736746

737747
it "waits acquire timeout till an application is available from the pool (than raises)" do
748+
app_init_secs = 0.2
738749
allow(@factory).to receive(:init)
739750
expect(@factory).to receive(:newApplication).twice do
740-
app = double "app"
741-
expect(app).to receive(:init) { sleep(0.2) }
751+
app = Synchronized.new(double("app").as_null_object)
752+
expect(app).to receive(:init) { sleep(app_init_secs) }
742753
app
743754
end
744755
allow(@rack_config).to receive(:getBooleanProperty).with("jruby.runtime.init.wait").and_return false
@@ -747,86 +758,90 @@ def createRackServletWrapper(runtime, rackup, filename)
747758

748759
@pooling_factory.init(@rack_context)
749760
@pooling_factory.acquire_timeout = 1.to_java # second
750-
millis = java.lang.System.currentTimeMillis
761+
start = java.lang.System.currentTimeMillis
751762
expect(@pooling_factory.getApplication).not_to be nil
752-
millis = java.lang.System.currentTimeMillis - millis
753-
expect(millis).to be >= 150 # getApplication waited ~ 0.2 secs
763+
expect(java.lang.System.currentTimeMillis - start).to be_within(70).of(app_init_secs * 1000)
754764

755765
app2 = @pooling_factory.getApplication # now the pool is empty
756-
757-
@pooling_factory.acquire_timeout = 0.1.to_java # second
758-
millis = java.lang.System.currentTimeMillis
766+
timeout_secs = 0.1
767+
@pooling_factory.acquire_timeout = (timeout_secs).to_java
768+
start = java.lang.System.currentTimeMillis
759769
expect { @pooling_factory.getApplication }.to raise_error(org.jruby.rack.AcquireTimeoutException)
760-
millis = java.lang.System.currentTimeMillis - millis
761-
expect(millis).to be >= 90 # waited about ~ 0.1 secs
770+
expect(java.lang.System.currentTimeMillis - start).to be_within(20).of(timeout_secs * 1000)
762771

763772
@pooling_factory.finishedWithApplication(app2) # gets back to the pool
764773
expect(@pooling_factory.getApplication).to eq app2
765774
end
766775

767776
it "gets and initializes new applications until maximum allows to create more" do
777+
app_init_secs = 0.1
768778
allow(@factory).to receive(:init)
769779
expect(@factory).to receive(:newApplication).twice do
770-
app = double "app (new)"
771-
expect(app).to receive(:init) { sleep(0.1) }
780+
app = Synchronized.new(double("app (new)").as_null_object)
781+
expect(app).to receive(:init) { sleep(app_init_secs) }
772782
app
773783
end
774784
allow(@rack_config).to receive(:getBooleanProperty).with("jruby.runtime.init.wait").and_return false
775785
allow(@rack_config).to receive(:getInitialRuntimes).and_return 2
776786
allow(@rack_config).to receive(:getMaximumRuntimes).and_return 4
777787

788+
timeout_secs = 0.1
778789
@pooling_factory.init(@rack_context)
779-
@pooling_factory.acquire_timeout = 0.10.to_java # second
790+
@pooling_factory.acquire_timeout = (timeout_secs).to_java # second
780791

781792
2.times { expect(@pooling_factory.getApplication).not_to be nil }
782793

794+
app_get_secs = 0.15
783795
expect(@factory).to receive(:getApplication).twice do
784-
app = double "app (get)"; sleep(0.15); app
796+
app = Synchronized.new(double("app (get)").as_null_object)
797+
sleep(app_get_secs)
798+
app
785799
end
786800

787-
millis = java.lang.System.currentTimeMillis
801+
start = java.lang.System.currentTimeMillis
788802
2.times { expect(@pooling_factory.getApplication).not_to be nil }
789-
millis = java.lang.System.currentTimeMillis - millis
790-
expect(millis).to be >= 300 # waited about 2 x 0.15 secs
803+
expect(java.lang.System.currentTimeMillis - start).to be_within(70).of(2 * app_get_secs * 1000)
791804

792-
millis = java.lang.System.currentTimeMillis
805+
start = java.lang.System.currentTimeMillis
793806
expect {
794807
@pooling_factory.getApplication
795808
}.to raise_error(org.jruby.rack.AcquireTimeoutException)
796-
millis = java.lang.System.currentTimeMillis - millis
797-
expect(millis).to be >= 90 # waited about ~ 0.10 secs
809+
expect(java.lang.System.currentTimeMillis - start).to be_within(20).of(timeout_secs * 1000)
798810
end
799811

800-
it "initializes initial runtimes in paralel (with wait set to false)" do
812+
it "initializes initial runtimes in parallel (with wait set to false)" do
813+
app_init_secs = 0.15
801814
allow(@factory).to receive(:init)
802815
allow(@factory).to receive(:newApplication) do
803-
app = double "app"
804-
allow(app).to receive(:init) do
805-
sleep(0.15)
806-
end
816+
app = Synchronized.new(double("app").as_null_object)
817+
allow(app).to receive(:init) { sleep(app_init_secs) }
807818
app
808819
end
820+
821+
init_threads = 4
822+
init_runtimes = 6
809823
allow(@rack_config).to receive(:getBooleanProperty).with("jruby.runtime.init.wait").and_return false
810-
allow(@rack_config).to receive(:getInitialRuntimes).and_return 6
824+
allow(@rack_config).to receive(:getRuntimeInitThreads).and_return init_threads
825+
allow(@rack_config).to receive(:getInitialRuntimes).and_return init_runtimes
811826
allow(@rack_config).to receive(:getMaximumRuntimes).and_return 8
812827

828+
expected_initial_init_time = 1.1 * (init_runtimes.to_f / init_threads.to_f).ceil * app_init_secs # 10% margin
813829
@pooling_factory.init(@rack_context)
814-
sleep(0.10)
815-
expect(@pooling_factory.getApplicationPool.size).to be < 6
816-
sleep(0.9)
817-
expect(@pooling_factory.getApplicationPool.size).to be >= 6
830+
sleep(app_init_secs) # wait for at some (but not possibly all) to finish
831+
expect(@pooling_factory.getApplicationPool.size).to be < init_runtimes
832+
sleep(expected_initial_init_time - app_init_secs) # remaining time
833+
expect(@pooling_factory.getApplicationPool.size).to be >= init_runtimes
818834

819835
expect(@pooling_factory.getManagedApplications).to_not be_empty
820-
expect(@pooling_factory.getManagedApplications.size).to eql 6
836+
expect(@pooling_factory.getManagedApplications.size).to eql init_runtimes
821837
end
822838

823839
it "throws from init when application initialization in thread failed" do
840+
app_init_secs = 0.05
824841
allow(@factory).to receive(:init)
825842
allow(@factory).to receive(:newApplication) do
826-
app = double "app"
827-
allow(app).to receive(:init) do
828-
sleep(0.05); raise "app.init raising"
829-
end
843+
app = Synchronized.new(double("app").as_null_object)
844+
allow(app).to receive(:init) { sleep(app_init_secs); raise "app.init raising" }
830845
app
831846
end
832847
allow(@rack_config).to receive(:getInitialRuntimes).and_return 2
@@ -845,9 +860,6 @@ def createRackServletWrapper(runtime, rackup, filename)
845860

846861
expect { @pooling_factory.init(@rack_context) }.to raise_error org.jruby.rack.RackInitializationException
847862
expect(raise_error_logged).to eql 1 # logs same init exception once
848-
849-
# NOTE: seems it's not such a good idea to return empty on init error
850-
# expect(@pooling_factory.getManagedApplications).to be_empty
851863
end
852864

853865
end

0 commit comments

Comments
 (0)