rack-unreloader 1.4.0 → 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,858 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'spec_helper')
2
+
3
+ describe Rack::Unreloader do
4
+ def self.it(*)
5
+ exit(1) unless Process.waitpid2(fork{super}).last.success?
6
+ end
7
+
8
+ def chroot
9
+ @ru.strip_path_prefix(Dir.pwd)
10
+ Dir.chroot(Dir.pwd)
11
+ end
12
+
13
+ it "should not reload files automatically if cooldown option is nil" do
14
+ ru(:cooldown => nil).call({}).must_equal [1]
15
+ chroot
16
+ update_app(code(2))
17
+ ru.call({}).must_equal [1]
18
+ @ru.reload!
19
+ ru.call({}).must_equal [2]
20
+ end
21
+
22
+ it "should not setup a reloader if reload option is false" do
23
+ @filename = 'spec/app_no_reload.rb'
24
+ ru(:reload => false).call({}).must_equal [1]
25
+ file = 'spec/app_no_reload2.rb'
26
+ File.open(file, 'wb'){|f| f.write('ANR2 = 2')}
27
+ chroot
28
+ ru.require 'spec/app_no_*2.rb'
29
+ ANR2.must_equal 2
30
+ end
31
+
32
+ it "should unload constants contained in file and reload file if file changes" do
33
+ ru.call({}).must_equal [1]
34
+ chroot
35
+ update_app(code(2))
36
+ ru.call({}).must_equal [2]
37
+ log_match %r{\ALoading.*spec/app\.rb\z},
38
+ %r{\ANew classes in .*spec/app\.rb: App\z},
39
+ %r{\AUnloading /spec/app\.rb\z},
40
+ "Removed constant App",
41
+ %r{\ALoading /spec/app\.rb\z},
42
+ %r{\ANew classes in /spec/app\.rb: App\z}
43
+ end
44
+
45
+ it "should stop monitoring file for changes if it is deleted constants contained in file and reload file if file changes" do
46
+ ru.call({}).must_equal [1]
47
+ chroot
48
+ File.delete('spec/app.rb')
49
+ proc{ru.call({})}.must_raise NameError
50
+ log_match %r{\ALoading.*spec/app\.rb\z},
51
+ %r{\ANew classes in .*spec/app\.rb: App\z},
52
+ %r{\AUnloading /spec/app\.rb\z},
53
+ "Removed constant App"
54
+ end
55
+
56
+ it "should check constants using ObjectSpace if require proc returns :ObjectSpace" do
57
+ base_ru
58
+ update_app(code(1))
59
+ @ru.require(@filename){|f| :ObjectSpace}
60
+ ru.call({}).must_equal [1]
61
+ chroot
62
+ update_app(code(2))
63
+ ru.call({}).must_equal [2]
64
+ log_match %r{\ALoading.*spec/app\.rb\z},
65
+ %r{\ANew classes in .*spec/app\.rb: App\z},
66
+ %r{\AUnloading /spec/app\.rb\z},
67
+ "Removed constant App",
68
+ %r{\ALoading /spec/app\.rb\z},
69
+ %r{\ANew classes in /spec/app\.rb: App\z}
70
+ end
71
+
72
+ it "should pickup files added as dependencies when chrooting early" do
73
+ ru.call({}).must_equal [1]
74
+ chroot
75
+ update_app("RU.require 'spec/app2.rb'; class App; def self.call(env) [@a, App2.call(env)] end; @a ||= []; @a << 2; end")
76
+ update_app("class App2; def self.call(env) @a end; @a ||= []; @a << 3; end", 'spec/app2.rb')
77
+ ru.call({}).must_equal [[2], [3]]
78
+ update_app("class App2; def self.call(env) @a end; @a ||= []; @a << 4; end", 'spec/app2.rb')
79
+ ru.call({}).must_equal [[2], [4]]
80
+ log_match %r{\ALoading.*spec/app\.rb\z},
81
+ %r{\ANew classes in .*spec/app\.rb: App\z},
82
+ %r{\AUnloading /spec/app\.rb\z},
83
+ "Removed constant App",
84
+ %r{\ALoading /spec/app\.rb\z},
85
+ %r{\ALoading /spec/app2\.rb\z},
86
+ %r{\ANew classes in /spec/app2\.rb: App2\z},
87
+ %r{\ANew classes in /spec/app\.rb: (App App2|App2 App)\z},
88
+ %r{\ANew features in /spec/app\.rb: /spec/app2\.rb\z},
89
+ %r{\AUnloading /spec/app2\.rb\z},
90
+ "Removed constant App2",
91
+ %r{\ALoading /spec/app2\.rb\z},
92
+ %r{\ANew classes in /spec/app2\.rb: App2\z}
93
+ end
94
+
95
+ it "should pickup files added as dependencies when chrooting late" do
96
+ ru.call({}).must_equal [1]
97
+ update_app("RU.require 'spec/app2.rb'; class App; def self.call(env) [@a, App2.call(env)] end; @a ||= []; @a << 2; end")
98
+ update_app("class App2; def self.call(env) @a end; @a ||= []; @a << 3; end", 'spec/app2.rb')
99
+ ru.call({}).must_equal [[2], [3]]
100
+ chroot
101
+ update_app("class App2; def self.call(env) @a end; @a ||= []; @a << 4; end", 'spec/app2.rb')
102
+ ru.call({}).must_equal [[2], [4]]
103
+ log_match %r{\ALoading.*spec/app\.rb\z},
104
+ %r{\ANew classes in .*spec/app\.rb: App\z},
105
+ %r{\AUnloading.*spec/app\.rb\z},
106
+ "Removed constant App",
107
+ %r{\ALoading.*spec/app\.rb\z},
108
+ %r{\ALoading.*spec/app2\.rb\z},
109
+ %r{\ANew classes in .*spec/app2\.rb: App2\z},
110
+ %r{\ANew classes in .*spec/app\.rb: (App App2|App2 App)\z},
111
+ %r{\ANew features in .*spec/app\.rb: .*spec/app2\.rb\z},
112
+ %r{\AUnloading /spec/app2\.rb\z},
113
+ "Removed constant App2",
114
+ %r{\ALoading /spec/app2\.rb\z},
115
+ %r{\ANew classes in /spec/app2\.rb: App2\z}
116
+ end
117
+
118
+ it "should support :subclasses option and only unload subclasses of given class when chrooting early" do
119
+ ru(:subclasses=>'App').call({}).must_equal [1]
120
+ chroot
121
+ update_app("RU.require 'spec/app2.rb'; class App; def self.call(env) [@a, App2.call(env)] end; @a ||= []; @a << 2; end")
122
+ update_app("class App2 < App; def self.call(env) @a end; @a ||= []; @a << 3; end", 'spec/app2.rb')
123
+ ru.call({}).must_equal [[1, 2], [3]]
124
+ update_app("class App2 < App; def self.call(env) @a end; @a ||= []; @a << 4; end", 'spec/app2.rb')
125
+ ru.call({}).must_equal [[1, 2], [4]]
126
+ update_app("RU.require 'spec/app2.rb'; class App; def self.call(env) [@a, App2.call(env)] end; @a ||= []; @a << 2; end")
127
+ log_match %r{\ALoading.*spec/app\.rb\z},
128
+ %r{\AUnloading /spec/app\.rb\z},
129
+ %r{\ALoading /spec/app\.rb\z},
130
+ %r{\ALoading /spec/app2\.rb\z},
131
+ %r{\ANew classes in /spec/app2\.rb: App2\z},
132
+ %r{\ANew classes in /spec/app\.rb: App2\z},
133
+ %r{\ANew features in /spec/app\.rb: /spec/app2\.rb\z},
134
+ %r{\AUnloading /spec/app2\.rb\z},
135
+ "Removed constant App2",
136
+ %r{\ALoading /spec/app2\.rb\z},
137
+ %r{\ANew classes in /spec/app2\.rb: App2\z}
138
+ end
139
+
140
+ it "should support :subclasses option and only unload subclasses of given class when chrooting late" do
141
+ ru(:subclasses=>'App').call({}).must_equal [1]
142
+ update_app("RU.require 'spec/app2.rb'; class App; def self.call(env) [@a, App2.call(env)] end; @a ||= []; @a << 2; end")
143
+ update_app("class App2 < App; def self.call(env) @a end; @a ||= []; @a << 3; end", 'spec/app2.rb')
144
+ ru.call({}).must_equal [[1, 2], [3]]
145
+ chroot
146
+ update_app("class App2 < App; def self.call(env) @a end; @a ||= []; @a << 4; end", 'spec/app2.rb')
147
+ ru.call({}).must_equal [[1, 2], [4]]
148
+ update_app("RU.require 'spec/app2.rb'; class App; def self.call(env) [@a, App2.call(env)] end; @a ||= []; @a << 2; end")
149
+ log_match %r{\ALoading.*spec/app\.rb\z},
150
+ %r{\AUnloading.*spec/app\.rb\z},
151
+ %r{\ALoading.*spec/app\.rb\z},
152
+ %r{\ALoading.*spec/app2\.rb\z},
153
+ %r{\ANew classes in .*spec/app2\.rb: App2\z},
154
+ %r{\ANew classes in .*spec/app\.rb: App2\z},
155
+ %r{\ANew features in .*spec/app\.rb: .*spec/app2\.rb\z},
156
+ %r{\AUnloading /spec/app2\.rb\z},
157
+ "Removed constant App2",
158
+ %r{\ALoading /spec/app2\.rb\z},
159
+ %r{\ANew classes in /spec/app2\.rb: App2\z}
160
+ end
161
+
162
+
163
+ it "should unload modules before reloading similar to classes" do
164
+ ru(:code=>"module App; def self.call(env) @a end; @a ||= []; @a << 1; end").call({}).must_equal [1]
165
+ chroot
166
+ update_app("module App; def self.call(env) @a end; @a ||= []; @a << 2; end")
167
+ ru.call({}).must_equal [2]
168
+ log_match %r{\ALoading.*spec/app\.rb\z},
169
+ %r{\ANew classes in .*spec/app\.rb: App\z},
170
+ %r{\AUnloading /spec/app\.rb\z},
171
+ "Removed constant App",
172
+ %r{\ALoading /spec/app\.rb\z},
173
+ %r{\ANew classes in /spec/app\.rb: App\z}
174
+ end
175
+
176
+ it "should unload specific modules by name via :subclasses option" do
177
+ ru(:subclasses=>'App', :code=>"module App; def self.call(env) @a end; @a ||= []; @a << 1; end").call({}).must_equal [1]
178
+ chroot
179
+ update_app("module App; def self.call(env) @a end; @a ||= []; @a << 2; end")
180
+ ru.call({}).must_equal [2]
181
+ log_match %r{\ALoading.*spec/app\.rb\z},
182
+ %r{\ANew classes in .*spec/app\.rb: App\z},
183
+ %r{\AUnloading /spec/app\.rb\z},
184
+ "Removed constant App",
185
+ %r{\ALoading /spec/app\.rb\z},
186
+ %r{\ANew classes in /spec/app\.rb: App\z}
187
+ end
188
+
189
+ it "should not unload modules by name if :subclasses option used and module not present" do
190
+ ru(:subclasses=>'Foo', :code=>"module App; def self.call(env) @a end; @a ||= []; @a << 1; end").call({}).must_equal [1]
191
+ chroot
192
+ update_app("module App; def self.call(env) @a end; @a ||= []; @a << 2; end")
193
+ ru.call({}).must_equal [1, 2]
194
+ log_match %r{\ALoading.*spec/app\.rb\z},
195
+ %r{\AUnloading /spec/app\.rb\z},
196
+ %r{\ALoading /spec/app\.rb\z}
197
+ end
198
+
199
+ it "should unload partially loaded modules if loading fails, and allow future loading when chrooting early" do
200
+ ru.call({}).must_equal [1]
201
+ chroot
202
+ update_app("module App; def self.call(env) @a end; @a ||= []; raise 'foo'; end")
203
+ proc{ru.call({})}.must_raise RuntimeError
204
+ defined?(::App).must_be_nil
205
+ update_app(code(2))
206
+ ru.call({}).must_equal [2]
207
+ log_match %r{\ALoading.*spec/app\.rb\z},
208
+ %r{\ANew classes in .*spec/app\.rb: App\z},
209
+ %r{\AUnloading /spec/app\.rb\z},
210
+ "Removed constant App",
211
+ %r{\ALoading /spec/app\.rb\z},
212
+ %r{\AFailed to load /spec/app\.rb; removing partially defined constants\z},
213
+ "Removed constant App",
214
+ %r{\ALoading /spec/app\.rb\z},
215
+ %r{\ANew classes in /spec/app\.rb: App\z}
216
+ end
217
+
218
+ it "should unload partially loaded modules if loading fails, and allow future loading when chrooting late" do
219
+ ru.call({}).must_equal [1]
220
+ update_app("module App; def self.call(env) @a end; @a ||= []; raise 'foo'; end")
221
+ proc{ru.call({})}.must_raise RuntimeError
222
+ defined?(::App).must_be_nil
223
+ chroot
224
+ update_app(code(2))
225
+ ru.call({}).must_equal [2]
226
+ log_match %r{\ALoading.*spec/app\.rb\z},
227
+ %r{\ANew classes in .*spec/app\.rb: App\z},
228
+ %r{\AUnloading.*spec/app\.rb\z},
229
+ "Removed constant App",
230
+ %r{\ALoading.*spec/app\.rb\z},
231
+ %r{\AFailed to load .*spec/app\.rb; removing partially defined constants\z},
232
+ "Removed constant App",
233
+ %r{\ALoading /spec/app\.rb\z},
234
+ %r{\ANew classes in /spec/app\.rb: App\z}
235
+ end
236
+
237
+ it "should unload classes in namespaces" do
238
+ ru(:code=>"class Array::App; def self.call(env) @a end; @a ||= []; @a << 1; end", :block=>proc{Array::App}).call({}).must_equal [1]
239
+ chroot
240
+ update_app("class Array::App; def self.call(env) @a end; @a ||= []; @a << 2; end")
241
+ ru.call({}).must_equal [2]
242
+ log_match %r{\ALoading.*spec/app\.rb\z},
243
+ %r{\ANew classes in .*spec/app\.rb: Array::App\z},
244
+ %r{\AUnloading /spec/app\.rb\z},
245
+ "Removed constant Array::App",
246
+ %r{\ALoading /spec/app\.rb\z},
247
+ %r{\ANew classes in /spec/app\.rb: Array::App\z}
248
+ end
249
+
250
+ it "should not unload class defined in dependency if already defined in parent when chrooting early" do
251
+ base_ru
252
+ update_app("class App; def self.call(env) @a end; @a ||= []; @a << 2; RU.require 'spec/app2.rb'; end")
253
+ update_app("class App; @a << 3 end", 'spec/app2.rb')
254
+ @ru.require 'spec/app.rb'
255
+ ru.call({}).must_equal [2, 3]
256
+ chroot
257
+ update_app("class App; @a << 4 end", 'spec/app2.rb')
258
+ ru.call({}).must_equal [2, 3, 4]
259
+ update_app("class App; def self.call(env) @a end; @a ||= []; @a << 2; RU.require 'spec/app2.rb'; end")
260
+ ru.call({}).must_equal [2, 4]
261
+ log_match %r{\ALoading.*spec/app\.rb\z},
262
+ %r{\ALoading.*spec/app2\.rb\z},
263
+ %r{\ANew classes in .*spec/app\.rb: App\z},
264
+ %r{\ANew features in .*spec/app\.rb: .*spec/app2\.rb\z},
265
+ %r{\AUnloading /spec/app2\.rb\z},
266
+ %r{\ALoading /spec/app2\.rb\z},
267
+ %r{\AUnloading /spec/app\.rb\z},
268
+ %r{\AUnloading /spec/app2\.rb\z},
269
+ "Removed constant App",
270
+ %r{\ALoading /spec/app\.rb\z},
271
+ %r{\ALoading /spec/app2\.rb\z},
272
+ %r{\ANew classes in /spec/app\.rb: App\z},
273
+ %r{\ANew features in /spec/app\.rb: /spec/app2\.rb\z}
274
+ end
275
+
276
+ it "should not unload class defined in dependency if already defined in parent when chrooting late" do
277
+ base_ru
278
+ update_app("class App; def self.call(env) @a end; @a ||= []; @a << 2; RU.require 'spec/app2.rb'; end")
279
+ update_app("class App; @a << 3 end", 'spec/app2.rb')
280
+ @ru.require 'spec/app.rb'
281
+ ru.call({}).must_equal [2, 3]
282
+ update_app("class App; @a << 4 end", 'spec/app2.rb')
283
+ ru.call({}).must_equal [2, 3, 4]
284
+ chroot
285
+ update_app("class App; def self.call(env) @a end; @a ||= []; @a << 2; RU.require 'spec/app2.rb'; end")
286
+ ru.call({}).must_equal [2, 4]
287
+ log_match %r{\ALoading.*spec/app\.rb\z},
288
+ %r{\ALoading.*spec/app2\.rb\z},
289
+ %r{\ANew classes in .*spec/app\.rb: App\z},
290
+ %r{\ANew features in .*spec/app\.rb: .*spec/app2\.rb\z},
291
+ %r{\AUnloading.*spec/app2\.rb\z},
292
+ %r{\ALoading.*spec/app2\.rb\z},
293
+ %r{\AUnloading.*spec/app\.rb\z},
294
+ %r{\AUnloading.*spec/app2\.rb\z},
295
+ "Removed constant App",
296
+ %r{\ALoading /spec/app\.rb\z},
297
+ %r{\ALoading /spec/app2\.rb\z},
298
+ %r{\ANew classes in /spec/app\.rb: App\z},
299
+ %r{\ANew features in /spec/app\.rb: /spec/app2\.rb\z}
300
+ end
301
+
302
+ it "should allow specifying proc for which constants get removed" do
303
+ base_ru
304
+ update_app("class App; def self.call(env) [@a, App2.a] end; @a ||= []; @a << 1; end; class App2; def self.a; @a end; @a ||= []; @a << 2; end")
305
+ @ru.require('spec/app.rb'){|f| File.basename(f).sub(/\.rb/, '').capitalize}
306
+ ru.call({}).must_equal [[1], [2]]
307
+ chroot
308
+ update_app("class App; def self.call(env) [@a, App2.a] end; @a ||= []; @a << 3; end; class App2; def self.a; @a end; @a ||= []; @a << 4; end")
309
+ ru.call({}).must_equal [[3], [2, 4]]
310
+ log_match %r{\ALoading.*spec/app\.rb\z},
311
+ %r{\ANew classes in .*spec/app\.rb: App\z},
312
+ %r{\AUnloading /spec/app\.rb\z},
313
+ "Removed constant App",
314
+ %r{\ALoading /spec/app\.rb\z},
315
+ %r{\ANew classes in /spec/app\.rb: App\z}
316
+ end
317
+
318
+ it "should handle anonymous classes" do
319
+ base_ru(:block=>proc{$app})
320
+ update_app("$app = Class.new do def self.call(env) @a end; @a ||= []; @a << 1; end")
321
+ @ru.require('spec/app.rb')
322
+ ru.call({}).must_equal [1]
323
+ chroot
324
+ update_app("$app = Class.new do def self.call(env) @a end; @a ||= []; @a << 2; end")
325
+ ru.call({}).must_equal [2]
326
+ log_match %r{\ALoading.*spec/app\.rb\z},
327
+ %r{\AUnloading /spec/app\.rb\z},
328
+ %r{\ALoading /spec/app\.rb\z}
329
+ end
330
+
331
+ it "should log when attempting to remove a class that doesn't exist" do
332
+ base_ru
333
+ update_app(code(1))
334
+ @ru.require('spec/app.rb'){|f| 'Foo'}
335
+ ru.call({}).must_equal [1]
336
+ chroot
337
+ update_app(code(2))
338
+ ru.call({}).must_equal [1, 2]
339
+ log_match %r{\ALoading.*spec/app\.rb\z},
340
+ %r{\AConstants not defined after loading .*spec/app\.rb: Foo\z},
341
+ %r{\AUnloading /spec/app\.rb\z},
342
+ "Error removing constant: Foo",
343
+ %r{\ALoading /spec/app\.rb\z},
344
+ %r{\AConstants not defined after loading /spec/app\.rb: Foo\z}
345
+ end
346
+
347
+ it "should handle recorded dependencies when chrooting early" do
348
+ base_ru
349
+ update_app("module A; B = 1; end", 'spec/app_mod.rb')
350
+ update_app("class App; A = ::A; def self.call(env) A::B end; end")
351
+ ru.require 'spec/app_mod.rb'
352
+ ru.require 'spec/app.rb'
353
+ ru.record_dependency 'spec/app_mod.rb', 'spec/app.rb'
354
+ ru.call({}).must_equal 1
355
+ chroot
356
+ update_app("module A; B = 2; end", 'spec/app_mod.rb')
357
+ ru.call({}).must_equal 2
358
+ update_app("module A; include C; end", 'spec/app_mod.rb')
359
+ update_app("module C; B = 3; end", 'spec/app_mod2.rb')
360
+ ru.record_dependency 'spec/app_mod2.rb', 'spec/app_mod.rb'
361
+ ru.require 'spec/app_mod2.rb'
362
+ ru.call({}).must_equal 3
363
+ update_app("module C; B = 4; end", 'spec/app_mod2.rb')
364
+ ru.call({}).must_equal 4
365
+ end
366
+
367
+ it "should handle recorded dependencies when chrooting middle" do
368
+ base_ru
369
+ update_app("module A; B = 1; end", 'spec/app_mod.rb')
370
+ update_app("class App; A = ::A; def self.call(env) A::B end; end")
371
+ ru.require 'spec/app_mod.rb'
372
+ ru.require 'spec/app.rb'
373
+ ru.record_dependency 'spec/app_mod.rb', 'spec/app.rb'
374
+ ru.call({}).must_equal 1
375
+ update_app("module A; B = 2; end", 'spec/app_mod.rb')
376
+ ru.call({}).must_equal 2
377
+ chroot
378
+ update_app("module A; include C; end", 'spec/app_mod.rb')
379
+ update_app("module C; B = 3; end", 'spec/app_mod2.rb')
380
+ ru.record_dependency 'spec/app_mod2.rb', 'spec/app_mod.rb'
381
+ ru.require 'spec/app_mod2.rb'
382
+ ru.call({}).must_equal 3
383
+ update_app("module C; B = 4; end", 'spec/app_mod2.rb')
384
+ ru.call({}).must_equal 4
385
+ end
386
+
387
+ it "should handle recorded dependencies when chrooting late" do
388
+ base_ru
389
+ update_app("module A; B = 1; end", 'spec/app_mod.rb')
390
+ update_app("class App; A = ::A; def self.call(env) A::B end; end")
391
+ ru.require 'spec/app_mod.rb'
392
+ ru.require 'spec/app.rb'
393
+ ru.record_dependency 'spec/app_mod.rb', 'spec/app.rb'
394
+ ru.call({}).must_equal 1
395
+ update_app("module A; B = 2; end", 'spec/app_mod.rb')
396
+ ru.call({}).must_equal 2
397
+ update_app("module A; include C; end", 'spec/app_mod.rb')
398
+ update_app("module C; B = 3; end", 'spec/app_mod2.rb')
399
+ ru.record_dependency 'spec/app_mod2.rb', 'spec/app_mod.rb'
400
+ ru.require 'spec/app_mod2.rb'
401
+ ru.call({}).must_equal 3
402
+ chroot
403
+ update_app("module C; B = 4; end", 'spec/app_mod2.rb')
404
+ ru.call({}).must_equal 4
405
+ end
406
+
407
+ describe "with a directory" do
408
+ include Minitest::Hooks
409
+
410
+ before(:all) do
411
+ Dir.mkdir('spec/dir')
412
+ Dir.mkdir('spec/dir/subdir')
413
+ Dir.mkdir('spec/dir/subdir2')
414
+ end
415
+
416
+ after do
417
+ Dir['spec/dir/**/*.rb'].each{|f| File.delete(f)}
418
+ end
419
+
420
+ after(:all) do
421
+ Dir.rmdir('spec/dir/subdir')
422
+ Dir.rmdir('spec/dir/subdir2')
423
+ Dir.rmdir('spec/dir')
424
+ end
425
+
426
+ it "should handle recorded dependencies in directories when chrooting early" do
427
+ base_ru
428
+ update_app("module A; B = 1; end", 'spec/dir/subdir/app_mod.rb')
429
+ update_app("class App; A = ::A; def self.call(env) A::B end; end")
430
+ ru.require 'spec/dir/subdir'
431
+ ru.require 'spec/app.rb'
432
+ ru.record_dependency 'spec/dir/subdir', 'spec/app.rb'
433
+ ru.call({}).must_equal 1
434
+ chroot
435
+ update_app("module A; B = 2; end", 'spec/dir/subdir/app_mod.rb')
436
+ ru.call({}).must_equal 2
437
+ update_app("module A; include C; end", 'spec/dir/subdir/app_mod.rb')
438
+ update_app("module C; B = 3; end", 'spec/dir/subdir2/app_mod2.rb')
439
+ ru.require 'spec/dir/subdir2/app_mod2.rb'
440
+ ru.record_dependency 'spec/dir/subdir2/app_mod2.rb', 'spec/dir/subdir'
441
+ ru.call({}).must_equal 3
442
+ update_app("module C; B = 4; end", 'spec/dir/subdir2/app_mod2.rb')
443
+ ru.call({}).must_equal 4
444
+ end
445
+
446
+ it "should handle recorded dependencies in directories when chrooting middle" do
447
+ base_ru
448
+ update_app("module A; B = 1; end", 'spec/dir/subdir/app_mod.rb')
449
+ update_app("class App; A = ::A; def self.call(env) A::B end; end")
450
+ ru.require 'spec/dir/subdir'
451
+ ru.require 'spec/app.rb'
452
+ ru.record_dependency 'spec/dir/subdir', 'spec/app.rb'
453
+ ru.call({}).must_equal 1
454
+ update_app("module A; B = 2; end", 'spec/dir/subdir/app_mod.rb')
455
+ ru.call({}).must_equal 2
456
+ chroot
457
+ update_app("module A; include C; end", 'spec/dir/subdir/app_mod.rb')
458
+ update_app("module C; B = 3; end", 'spec/dir/subdir2/app_mod2.rb')
459
+ ru.require 'spec/dir/subdir2/app_mod2.rb'
460
+ ru.record_dependency 'spec/dir/subdir2/app_mod2.rb', 'spec/dir/subdir'
461
+ ru.call({}).must_equal 3
462
+ update_app("module C; B = 4; end", 'spec/dir/subdir2/app_mod2.rb')
463
+ ru.call({}).must_equal 4
464
+ end
465
+
466
+ it "should handle recorded dependencies in directories when chrooting late" do
467
+ base_ru
468
+ update_app("module A; B = 1; end", 'spec/dir/subdir/app_mod.rb')
469
+ update_app("class App; A = ::A; def self.call(env) A::B end; end")
470
+ ru.require 'spec/dir/subdir'
471
+ ru.require 'spec/app.rb'
472
+ ru.record_dependency 'spec/dir/subdir', 'spec/app.rb'
473
+ ru.call({}).must_equal 1
474
+ update_app("module A; B = 2; end", 'spec/dir/subdir/app_mod.rb')
475
+ ru.call({}).must_equal 2
476
+ update_app("module A; include C; end", 'spec/dir/subdir/app_mod.rb')
477
+ update_app("module C; B = 3; end", 'spec/dir/subdir2/app_mod2.rb')
478
+ ru.require 'spec/dir/subdir2/app_mod2.rb'
479
+ ru.record_dependency 'spec/dir/subdir2/app_mod2.rb', 'spec/dir/subdir'
480
+ ru.call({}).must_equal 3
481
+ chroot
482
+ update_app("module C; B = 4; end", 'spec/dir/subdir2/app_mod2.rb')
483
+ ru.call({}).must_equal 4
484
+ end
485
+
486
+ it "should handle recorded dependencies in directories when files are added or removed later when chrooting 1" do
487
+ base_ru
488
+ update_app("class App; A = defined?(::A) ? ::A : Module.new{self::B = 0}; def self.call(env) A::B end; end")
489
+ ru.record_dependency 'spec/dir/subdir', 'spec/app.rb'
490
+ ru.record_dependency 'spec/dir/subdir2', 'spec/dir/subdir'
491
+ ru.require 'spec/app.rb'
492
+ ru.require 'spec/dir/subdir'
493
+ ru.require 'spec/dir/subdir2'
494
+ ru.call({}).must_equal 0
495
+ chroot
496
+ update_app("module A; B = 1; end", 'spec/dir/subdir/app_mod.rb')
497
+ ru.call({}).must_equal 1
498
+ update_app("module A; B = 2; end", 'spec/dir/subdir/app_mod.rb')
499
+ ru.call({}).must_equal 2
500
+ update_app("module C; B = 3; end", 'spec/dir/subdir2/app_mod2.rb')
501
+ ru.call({}).must_equal 2
502
+ update_app("module A; include C; end", 'spec/dir/subdir/app_mod.rb')
503
+ ru.call({}).must_equal 3
504
+ update_app("module C; B = 4; end", 'spec/dir/subdir2/app_mod2.rb')
505
+ ru.call({}).must_equal 4
506
+ File.delete 'spec/dir/subdir/app_mod.rb'
507
+ ru.call({}).must_equal 0
508
+ end
509
+
510
+ it "should handle recorded dependencies in directories when files are added or removed later when chrooting 2" do
511
+ base_ru
512
+ update_app("class App; A = defined?(::A) ? ::A : Module.new{self::B = 0}; def self.call(env) A::B end; end")
513
+ ru.record_dependency 'spec/dir/subdir', 'spec/app.rb'
514
+ ru.record_dependency 'spec/dir/subdir2', 'spec/dir/subdir'
515
+ ru.require 'spec/app.rb'
516
+ ru.require 'spec/dir/subdir'
517
+ ru.require 'spec/dir/subdir2'
518
+ ru.call({}).must_equal 0
519
+ update_app("module A; B = 1; end", 'spec/dir/subdir/app_mod.rb')
520
+ ru.call({}).must_equal 1
521
+ chroot
522
+ update_app("module A; B = 2; end", 'spec/dir/subdir/app_mod.rb')
523
+ ru.call({}).must_equal 2
524
+ update_app("module C; B = 3; end", 'spec/dir/subdir2/app_mod2.rb')
525
+ ru.call({}).must_equal 2
526
+ update_app("module A; include C; end", 'spec/dir/subdir/app_mod.rb')
527
+ ru.call({}).must_equal 3
528
+ update_app("module C; B = 4; end", 'spec/dir/subdir2/app_mod2.rb')
529
+ ru.call({}).must_equal 4
530
+ File.delete 'spec/dir/subdir/app_mod.rb'
531
+ ru.call({}).must_equal 0
532
+ end
533
+
534
+ it "should handle recorded dependencies in directories when files are added or removed later when chrooting 3" do
535
+ base_ru
536
+ update_app("class App; A = defined?(::A) ? ::A : Module.new{self::B = 0}; def self.call(env) A::B end; end")
537
+ ru.record_dependency 'spec/dir/subdir', 'spec/app.rb'
538
+ ru.record_dependency 'spec/dir/subdir2', 'spec/dir/subdir'
539
+ ru.require 'spec/app.rb'
540
+ ru.require 'spec/dir/subdir'
541
+ ru.require 'spec/dir/subdir2'
542
+ ru.call({}).must_equal 0
543
+ update_app("module A; B = 1; end", 'spec/dir/subdir/app_mod.rb')
544
+ ru.call({}).must_equal 1
545
+ update_app("module A; B = 2; end", 'spec/dir/subdir/app_mod.rb')
546
+ ru.call({}).must_equal 2
547
+ chroot
548
+ update_app("module C; B = 3; end", 'spec/dir/subdir2/app_mod2.rb')
549
+ ru.call({}).must_equal 2
550
+ update_app("module A; include C; end", 'spec/dir/subdir/app_mod.rb')
551
+ ru.call({}).must_equal 3
552
+ update_app("module C; B = 4; end", 'spec/dir/subdir2/app_mod2.rb')
553
+ ru.call({}).must_equal 4
554
+ File.delete 'spec/dir/subdir/app_mod.rb'
555
+ ru.call({}).must_equal 0
556
+ end
557
+
558
+ it "should handle recorded dependencies in directories when files are added or removed later when chrooting 4" do
559
+ base_ru
560
+ update_app("class App; A = defined?(::A) ? ::A : Module.new{self::B = 0}; def self.call(env) A::B end; end")
561
+ ru.record_dependency 'spec/dir/subdir', 'spec/app.rb'
562
+ ru.record_dependency 'spec/dir/subdir2', 'spec/dir/subdir'
563
+ ru.require 'spec/app.rb'
564
+ ru.require 'spec/dir/subdir'
565
+ ru.require 'spec/dir/subdir2'
566
+ ru.call({}).must_equal 0
567
+ update_app("module A; B = 1; end", 'spec/dir/subdir/app_mod.rb')
568
+ ru.call({}).must_equal 1
569
+ update_app("module A; B = 2; end", 'spec/dir/subdir/app_mod.rb')
570
+ ru.call({}).must_equal 2
571
+ update_app("module C; B = 3; end", 'spec/dir/subdir2/app_mod2.rb')
572
+ ru.call({}).must_equal 2
573
+ chroot
574
+ update_app("module A; include C; end", 'spec/dir/subdir/app_mod.rb')
575
+ ru.call({}).must_equal 3
576
+ update_app("module C; B = 4; end", 'spec/dir/subdir2/app_mod2.rb')
577
+ ru.call({}).must_equal 4
578
+ File.delete 'spec/dir/subdir/app_mod.rb'
579
+ ru.call({}).must_equal 0
580
+ end
581
+
582
+ it "should handle recorded dependencies in directories when files are added or removed later when chrooting 5" do
583
+ base_ru
584
+ update_app("class App; A = defined?(::A) ? ::A : Module.new{self::B = 0}; def self.call(env) A::B end; end")
585
+ ru.record_dependency 'spec/dir/subdir', 'spec/app.rb'
586
+ ru.record_dependency 'spec/dir/subdir2', 'spec/dir/subdir'
587
+ ru.require 'spec/app.rb'
588
+ ru.require 'spec/dir/subdir'
589
+ ru.require 'spec/dir/subdir2'
590
+ ru.call({}).must_equal 0
591
+ update_app("module A; B = 1; end", 'spec/dir/subdir/app_mod.rb')
592
+ ru.call({}).must_equal 1
593
+ update_app("module A; B = 2; end", 'spec/dir/subdir/app_mod.rb')
594
+ ru.call({}).must_equal 2
595
+ update_app("module C; B = 3; end", 'spec/dir/subdir2/app_mod2.rb')
596
+ ru.call({}).must_equal 2
597
+ update_app("module A; include C; end", 'spec/dir/subdir/app_mod.rb')
598
+ ru.call({}).must_equal 3
599
+ chroot
600
+ update_app("module C; B = 4; end", 'spec/dir/subdir2/app_mod2.rb')
601
+ ru.call({}).must_equal 4
602
+ File.delete 'spec/dir/subdir/app_mod.rb'
603
+ ru.call({}).must_equal 0
604
+ end
605
+
606
+ it "should handle recorded dependencies in directories when files are added or removed later when chrooting 6" do
607
+ base_ru
608
+ update_app("class App; A = defined?(::A) ? ::A : Module.new{self::B = 0}; def self.call(env) A::B end; end")
609
+ ru.record_dependency 'spec/dir/subdir', 'spec/app.rb'
610
+ ru.record_dependency 'spec/dir/subdir2', 'spec/dir/subdir'
611
+ ru.require 'spec/app.rb'
612
+ ru.require 'spec/dir/subdir'
613
+ ru.require 'spec/dir/subdir2'
614
+ ru.call({}).must_equal 0
615
+ update_app("module A; B = 1; end", 'spec/dir/subdir/app_mod.rb')
616
+ ru.call({}).must_equal 1
617
+ update_app("module A; B = 2; end", 'spec/dir/subdir/app_mod.rb')
618
+ ru.call({}).must_equal 2
619
+ update_app("module C; B = 3; end", 'spec/dir/subdir2/app_mod2.rb')
620
+ ru.call({}).must_equal 2
621
+ update_app("module A; include C; end", 'spec/dir/subdir/app_mod.rb')
622
+ ru.call({}).must_equal 3
623
+ update_app("module C; B = 4; end", 'spec/dir/subdir2/app_mod2.rb')
624
+ ru.call({}).must_equal 4
625
+ chroot
626
+ File.delete 'spec/dir/subdir/app_mod.rb'
627
+ ru.call({}).must_equal 0
628
+ end
629
+
630
+ it "should handle classes split into multiple files when chrooting 1" do
631
+ base_ru
632
+ update_app("class App; RU.require('spec/dir'); def self.call(env) \"\#{a if respond_to?(:a)}\#{b if respond_to?(:b)}1\".to_i end; end")
633
+ ru.require 'spec/app.rb'
634
+ ru.record_split_class 'spec/app.rb', 'spec/dir'
635
+ ru.call({}).must_equal 1
636
+ chroot
637
+ update_app("class App; def self.a; 2 end end", 'spec/dir/appa.rb')
638
+ ru.call({}).must_equal 21
639
+ update_app("class App; def self.a; 3 end end", 'spec/dir/appa.rb')
640
+ ru.call({}).must_equal 31
641
+ update_app("class App; def self.b; 4 end end", 'spec/dir/appb.rb')
642
+ ru.call({}).must_equal 341
643
+ update_app("class App; def self.a; 5 end end", 'spec/dir/appa.rb')
644
+ update_app("class App; def self.b; 6 end end", 'spec/dir/appb.rb')
645
+ ru.call({}).must_equal 561
646
+ update_app("class App; end", 'spec/dir/appa.rb')
647
+ ru.call({}).must_equal 61
648
+ File.delete 'spec/dir/appb.rb'
649
+ ru.call({}).must_equal 1
650
+ end
651
+
652
+ it "should handle classes split into multiple files when chrooting 2" do
653
+ base_ru
654
+ update_app("class App; RU.require('spec/dir'); def self.call(env) \"\#{a if respond_to?(:a)}\#{b if respond_to?(:b)}1\".to_i end; end")
655
+ ru.require 'spec/app.rb'
656
+ ru.record_split_class 'spec/app.rb', 'spec/dir'
657
+ ru.call({}).must_equal 1
658
+ update_app("class App; def self.a; 2 end end", 'spec/dir/appa.rb')
659
+ ru.call({}).must_equal 21
660
+ chroot
661
+ update_app("class App; def self.a; 3 end end", 'spec/dir/appa.rb')
662
+ ru.call({}).must_equal 31
663
+ update_app("class App; def self.b; 4 end end", 'spec/dir/appb.rb')
664
+ ru.call({}).must_equal 341
665
+ update_app("class App; def self.a; 5 end end", 'spec/dir/appa.rb')
666
+ update_app("class App; def self.b; 6 end end", 'spec/dir/appb.rb')
667
+ ru.call({}).must_equal 561
668
+ update_app("class App; end", 'spec/dir/appa.rb')
669
+ ru.call({}).must_equal 61
670
+ File.delete 'spec/dir/appb.rb'
671
+ ru.call({}).must_equal 1
672
+ end
673
+
674
+ it "should handle classes split into multiple files when chrooting 3" do
675
+ base_ru
676
+ update_app("class App; RU.require('spec/dir'); def self.call(env) \"\#{a if respond_to?(:a)}\#{b if respond_to?(:b)}1\".to_i end; end")
677
+ ru.require 'spec/app.rb'
678
+ ru.record_split_class 'spec/app.rb', 'spec/dir'
679
+ ru.call({}).must_equal 1
680
+ update_app("class App; def self.a; 2 end end", 'spec/dir/appa.rb')
681
+ ru.call({}).must_equal 21
682
+ update_app("class App; def self.a; 3 end end", 'spec/dir/appa.rb')
683
+ ru.call({}).must_equal 31
684
+ chroot
685
+ update_app("class App; def self.b; 4 end end", 'spec/dir/appb.rb')
686
+ ru.call({}).must_equal 341
687
+ update_app("class App; def self.a; 5 end end", 'spec/dir/appa.rb')
688
+ update_app("class App; def self.b; 6 end end", 'spec/dir/appb.rb')
689
+ ru.call({}).must_equal 561
690
+ update_app("class App; end", 'spec/dir/appa.rb')
691
+ ru.call({}).must_equal 61
692
+ File.delete 'spec/dir/appb.rb'
693
+ ru.call({}).must_equal 1
694
+ end
695
+
696
+ it "should handle classes split into multiple files when chrooting 4" do
697
+ base_ru
698
+ update_app("class App; RU.require('spec/dir'); def self.call(env) \"\#{a if respond_to?(:a)}\#{b if respond_to?(:b)}1\".to_i end; end")
699
+ ru.require 'spec/app.rb'
700
+ ru.record_split_class 'spec/app.rb', 'spec/dir'
701
+ ru.call({}).must_equal 1
702
+ update_app("class App; def self.a; 2 end end", 'spec/dir/appa.rb')
703
+ ru.call({}).must_equal 21
704
+ update_app("class App; def self.a; 3 end end", 'spec/dir/appa.rb')
705
+ ru.call({}).must_equal 31
706
+ update_app("class App; def self.b; 4 end end", 'spec/dir/appb.rb')
707
+ ru.call({}).must_equal 341
708
+ chroot
709
+ update_app("class App; def self.a; 5 end end", 'spec/dir/appa.rb')
710
+ update_app("class App; def self.b; 6 end end", 'spec/dir/appb.rb')
711
+ ru.call({}).must_equal 561
712
+ update_app("class App; end", 'spec/dir/appa.rb')
713
+ ru.call({}).must_equal 61
714
+ File.delete 'spec/dir/appb.rb'
715
+ ru.call({}).must_equal 1
716
+ end
717
+
718
+ it "should handle classes split into multiple files when chrooting 5" do
719
+ base_ru
720
+ update_app("class App; RU.require('spec/dir'); def self.call(env) \"\#{a if respond_to?(:a)}\#{b if respond_to?(:b)}1\".to_i end; end")
721
+ ru.require 'spec/app.rb'
722
+ ru.record_split_class 'spec/app.rb', 'spec/dir'
723
+ ru.call({}).must_equal 1
724
+ update_app("class App; def self.a; 2 end end", 'spec/dir/appa.rb')
725
+ ru.call({}).must_equal 21
726
+ update_app("class App; def self.a; 3 end end", 'spec/dir/appa.rb')
727
+ ru.call({}).must_equal 31
728
+ update_app("class App; def self.b; 4 end end", 'spec/dir/appb.rb')
729
+ ru.call({}).must_equal 341
730
+ update_app("class App; def self.a; 5 end end", 'spec/dir/appa.rb')
731
+ update_app("class App; def self.b; 6 end end", 'spec/dir/appb.rb')
732
+ ru.call({}).must_equal 561
733
+ chroot
734
+ update_app("class App; end", 'spec/dir/appa.rb')
735
+ ru.call({}).must_equal 61
736
+ File.delete 'spec/dir/appb.rb'
737
+ ru.call({}).must_equal 1
738
+ end
739
+
740
+ it "should handle classes split into multiple files when chrooting 6" do
741
+ base_ru
742
+ update_app("class App; RU.require('spec/dir'); def self.call(env) \"\#{a if respond_to?(:a)}\#{b if respond_to?(:b)}1\".to_i end; end")
743
+ ru.require 'spec/app.rb'
744
+ ru.record_split_class 'spec/app.rb', 'spec/dir'
745
+ ru.call({}).must_equal 1
746
+ update_app("class App; def self.a; 2 end end", 'spec/dir/appa.rb')
747
+ ru.call({}).must_equal 21
748
+ update_app("class App; def self.a; 3 end end", 'spec/dir/appa.rb')
749
+ ru.call({}).must_equal 31
750
+ update_app("class App; def self.b; 4 end end", 'spec/dir/appb.rb')
751
+ ru.call({}).must_equal 341
752
+ update_app("class App; def self.a; 5 end end", 'spec/dir/appa.rb')
753
+ update_app("class App; def self.b; 6 end end", 'spec/dir/appb.rb')
754
+ ru.call({}).must_equal 561
755
+ update_app("class App; end", 'spec/dir/appa.rb')
756
+ ru.call({}).must_equal 61
757
+ chroot
758
+ File.delete 'spec/dir/appb.rb'
759
+ ru.call({}).must_equal 1
760
+ end
761
+
762
+ it "should pick up changes to files in that directory" do
763
+ base_ru
764
+ update_app("class App; @a = {}; def self.call(env=nil) @a end; end; RU.require 'spec/dir'")
765
+ update_app("App.call[:foo] = 1", 'spec/dir/a.rb')
766
+ @ru.require('spec/app.rb')
767
+ ru.call({}).must_equal(:foo=>1)
768
+ chroot
769
+ update_app("App.call[:foo] = 2", 'spec/dir/a.rb')
770
+ ru.call({}).must_equal(:foo=>2)
771
+ log_match %r{\ALoading.*spec/app\.rb\z},
772
+ %r{\ALoading.*spec/dir/a\.rb\z},
773
+ %r{\ANew classes in .*spec/app\.rb: App\z},
774
+ %r{\ANew features in .*spec/app\.rb: .*spec/dir/a\.rb\z},
775
+ %r{\AUnloading /spec/dir/a.rb\z},
776
+ %r{\ALoading /spec/dir/a.rb\z}
777
+ end
778
+
779
+ it "should pick up changes to files in subdirectories" do
780
+ base_ru
781
+ update_app("class App; @a = {}; def self.call(env=nil) @a end; end; RU.require 'spec/dir'")
782
+ update_app("App.call[:foo] = 1", 'spec/dir/subdir/a.rb')
783
+ @ru.require('spec/app.rb')
784
+ ru.call({}).must_equal(:foo=>1)
785
+ chroot
786
+ update_app("App.call[:foo] = 2", 'spec/dir/subdir/a.rb')
787
+ ru.call({}).must_equal(:foo=>2)
788
+ log_match %r{\ALoading.*spec/app\.rb\z},
789
+ %r{\ALoading.*spec/dir/subdir/a\.rb\z},
790
+ %r{\ANew classes in .*spec/app\.rb: App\z},
791
+ %r{\ANew features in .*spec/app\.rb: .*spec/dir/subdir/a\.rb\z},
792
+ %r{\AUnloading /spec/dir/subdir/a.rb\z},
793
+ %r{\ALoading /spec/dir/subdir/a.rb\z}
794
+ end
795
+
796
+ it "should pick up new files added to the directory" do
797
+ base_ru
798
+ update_app("class App; @a = {}; def self.call(env=nil) @a end; end; RU.require 'spec/dir'")
799
+ @ru.require('spec/app.rb')
800
+ ru.call({}).must_equal({})
801
+ chroot
802
+ update_app("App.call[:foo] = 2", 'spec/dir/a.rb')
803
+ ru.call({}).must_equal(:foo=>2)
804
+ log_match %r{\ALoading.*spec/app\.rb\z},
805
+ %r{\ANew classes in .*spec/app\.rb: App\z},
806
+ %r{\ALoading /spec/dir/a\.rb\z}
807
+ end
808
+
809
+ it "should pick up new files added to subdirectories" do
810
+ base_ru
811
+ update_app("class App; @a = {}; def self.call(env=nil) @a end; end; RU.require 'spec/dir'")
812
+ @ru.require('spec/app.rb')
813
+ ru.call({}).must_equal({})
814
+ chroot
815
+ update_app("App.call[:foo] = 2", 'spec/dir/subdir/a.rb')
816
+ ru.call({}).must_equal(:foo=>2)
817
+ log_match %r{\ALoading.*spec/app\.rb\z},
818
+ %r{\ANew classes in .*spec/app\.rb: App\z},
819
+ %r{\ALoading /spec/dir/subdir/a\.rb\z}
820
+ end
821
+
822
+ it "should drop files deleted from the directory" do
823
+ base_ru
824
+ update_app("class App; @a = {}; def self.call(env=nil) @a end; end; RU.require 'spec/dir'")
825
+ update_app("App.call[:foo] = 1", 'spec/dir/a.rb')
826
+ @ru.require('spec/app.rb')
827
+ ru.call({}).must_equal(:foo=>1)
828
+ chroot
829
+ File.delete('spec/dir/a.rb')
830
+ update_app("App.call[:foo] = 2", 'spec/dir/b.rb')
831
+ ru.call({}).must_equal(:foo=>2)
832
+ log_match %r{\ALoading.*spec/app\.rb\z},
833
+ %r{\ALoading.*spec/dir/a\.rb\z},
834
+ %r{\ANew classes in .*spec/app\.rb: App\z},
835
+ %r{\ANew features in .*spec/app\.rb: .*spec/dir/a\.rb\z},
836
+ %r{\AUnloading /spec/dir/a.rb\z},
837
+ %r{\ALoading /spec/dir/b\.rb\z}
838
+ end
839
+
840
+ it "should drop files deleted from subdirectories" do
841
+ base_ru
842
+ update_app("class App; @a = {}; def self.call(env=nil) @a end; end; RU.require 'spec/dir'")
843
+ update_app("App.call[:foo] = 1", 'spec/dir/subdir/a.rb')
844
+ @ru.require('spec/app.rb')
845
+ ru.call({}).must_equal(:foo=>1)
846
+ chroot
847
+ File.delete('spec/dir/subdir/a.rb')
848
+ update_app("App.call[:foo] = 2", 'spec/dir/subdir/b.rb')
849
+ ru.call({}).must_equal(:foo=>2)
850
+ log_match %r{\ALoading.*spec/app\.rb\z},
851
+ %r{\ALoading.*spec/dir/subdir/a\.rb\z},
852
+ %r{\ANew classes in .*spec/app\.rb: App\z},
853
+ %r{\ANew features in .*spec/app\.rb: .*spec/dir/subdir/a\.rb\z},
854
+ %r{\AUnloading /spec/dir/subdir/a.rb\z},
855
+ %r{\ALoading /spec/dir/subdir/b\.rb\z}
856
+ end
857
+ end
858
+ end