Skip to content

Commit 4fa741f

Browse files
committed
Remove use of finalizer in Rack::Test::UploadedFile (Fixes #338)
A finalizer was used in the Tempfile case to close and unlink the Tempfile created, but it was never needed, because Tempfile defines its own finalizer that closes and unlinks. Update the spec so that it actually tests that tempfiles are getting removed and unlinked. This uses 500 tempfiles in the JRuby test because some lower values I tried failed CI. CRuby could likely get away with only a handle of tempfiles, but the spec uses 50 to be sure it doesn't fail.
1 parent 822541d commit 4fa741f

File tree

3 files changed

+24
-34
lines changed

3 files changed

+24
-34
lines changed

History.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
## main
2+
3+
* Minor enhancements:
4+
* `Rack::Test::UploadedFile` no longer uses a finalizer for named
5+
paths to close and unlink the created Tempfile. Tempfile itself
6+
uses a finalizer to close and unlink itself, so there is no
7+
reason for `Rack::Test::UploadedFile` to do so (Jeremy Evans #338)
8+
19
## 2.1.0 / 2023-03-14
210

311
* Breaking changes:

lib/rack/test/uploaded_file.rb

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -72,18 +72,6 @@ def respond_to_missing?(method_name, include_private = false) #:nodoc:
7272
tempfile.respond_to?(method_name, include_private) || super
7373
end
7474

75-
# A proc that can be used as a finalizer to close and unlink the tempfile.
76-
def self.finalize(file)
77-
proc { actually_finalize file }
78-
end
79-
80-
# Close and unlink the given file, used as a finalizer for the tempfile,
81-
# if the tempfile is backed by a file in the filesystem.
82-
def self.actually_finalize(file)
83-
file.close
84-
file.unlink
85-
end
86-
8775
private
8876

8977
# Use the StringIO as the tempfile.
@@ -104,8 +92,6 @@ def initialize_from_file_path(path)
10492
@tempfile = Tempfile.new([::File.basename(@original_filename, extension), extension])
10593
@tempfile.set_encoding(Encoding::BINARY)
10694

107-
ObjectSpace.define_finalizer(self, self.class.finalize(@tempfile))
108-
10995
FileUtils.copy_file(path, @tempfile.path)
11096
end
11197
end

spec/rack/test/uploaded_file_spec.rb

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -51,35 +51,31 @@ def file_path
5151
proc{Rack::Test::UploadedFile.new('does_not_exist')}.must_raise RuntimeError
5252
end
5353

54-
it 'finalizes on garbage collection' do
55-
finalized = false
56-
c = Class.new(Rack::Test::UploadedFile) do
57-
define_singleton_method(:actually_finalize) do |file|
58-
finalized = true
59-
super(file)
60-
end
54+
def local_paths(n)
55+
local_paths = n.times.map do
56+
Rack::Test::UploadedFile.new(file_path)
6157
end
58+
local_paths.map(&:local_path).all?{|f| File.exist?(f)}.must_equal true
59+
local_paths.map!(&:local_path)
60+
local_paths.uniq.size.must_equal n
61+
local_paths
62+
end
63+
64+
it 'removes local paths on garbage collection' do
6265

6366
if RUBY_PLATFORM == 'java'
6467
require 'java'
6568
java_import 'java.lang.System'
6669

67-
50.times do |_i|
68-
c.new(file_path)
69-
System.gc
70-
end
70+
paths = local_paths(500)
71+
System.gc
7172
else
72-
50.times do |_i|
73-
c.new(file_path)
74-
GC.start
75-
end
73+
paths = local_paths(50)
74+
GC.start
7675
end
7776

78-
# Due to CRuby's conservative garbage collection, you can never guarantee
79-
# an object will be garbage collected, so this is a source of potential
80-
# nondeterministic failure
81-
finalized.must_equal true
82-
end if RUBY_VERSION >= '2.7' || RUBY_ENGINE != 'ruby'
77+
paths.all?{|f| File.exist?(f)}.must_equal false
78+
end
8379

8480
it '#initialize with an IO object sets the specified filename' do
8581
original_filename = 'content.txt'

0 commit comments

Comments
 (0)