1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

* test/lib/minitest/unit.rb: Check Tempfile leaks for each test method

again.



git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@46265 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
akr 2014-05-30 23:32:19 +00:00
parent cfe2cbda54
commit 40ec552861
2 changed files with 75 additions and 36 deletions

View file

@ -1,3 +1,8 @@
Sat May 31 08:31:41 2014 Tanaka Akira <akr@fsij.org>
* test/lib/minitest/unit.rb: Check Tempfile leaks for each test method
again.
Sat May 31 03:50:50 2014 Zachary Scott <e@zzak.io> Sat May 31 03:50:50 2014 Zachary Scott <e@zzak.io>
* lib/delegate.rb: [DOC] Document raise in Delegator class * lib/delegate.rb: [DOC] Document raise in Delegator class

View file

@ -933,9 +933,7 @@ module MiniTest
filter === m || filter === "#{suite}##{m}" filter === m || filter === "#{suite}##{m}"
} }
threads = find_threads leak_info = leak_check_init
fds = find_fds
tempfiles = find_tempfiles
assertions = filtered_test_methods.map { |method| assertions = filtered_test_methods.map { |method|
inst = suite.new method inst = suite.new method
@ -950,43 +948,55 @@ module MiniTest
print result print result
puts if @verbose puts if @verbose
threads = check_thread_leak inst, threads, find_threads leak_info = leak_check(inst, leak_info)
fds = check_fd_leak inst, fds, find_fds
# find_tempfiles is too slow to run for each test method.
#tempfiles = check_tempfile_leak inst, tempfiles, find_tempfiles
inst._assertions inst._assertions
} }
tempfiles = check_tempfile_leak suite, tempfiles, find_tempfiles
return assertions.size, assertions.inject(0) { |sum, n| sum + n } return assertions.size, assertions.inject(0) { |sum, n| sum + n }
end end
def leak_check_init
fd_info = find_fds
thread_info = find_threads
tempfile_info = find_tempfiles
[fd_info, thread_info, tempfile_info]
end
def leak_check(inst, info)
fd_info, thread_info, tempfile_info = info
leak_p_1, fd_info = check_fd_leak(inst, fd_info)
leak_p_2, thread_info = check_thread_leak(inst, thread_info)
leak_p_3, tempfile_info = check_tempfile_leak(inst, tempfile_info)
GC.start if leak_p_1 || leak_p_2 || leak_p_3
[fd_info, thread_info, tempfile_info]
end
def find_threads def find_threads
Thread.list.find_all {|t| Thread.list.find_all {|t|
t != Thread.current && t.alive? t != Thread.current && t.alive?
} }
end end
def check_thread_leak(inst, live1, live2) def check_thread_leak(inst, live1)
live2 = find_threads
thread_finished = live1 - live2 thread_finished = live1 - live2
leak_p = false
if !thread_finished.empty? if !thread_finished.empty?
list = thread_finished.map {|t| t.inspect }.sort list = thread_finished.map {|t| t.inspect }.sort
list.each {|str| list.each {|str|
puts "Finished thread: #{inst.class}\##{inst.__name__}: #{str}" puts "Finished thread: #{inst.class}\##{inst.__name__}: #{str}"
} }
end end
thread_retained = live2 - live1 thread_leaked = live2 - live1
if !thread_retained.empty? if !thread_leaked.empty?
list = thread_retained.map {|t| t.inspect }.sort leak_p = true
list = thread_leaked.map {|t| t.inspect }.sort
list.each {|str| list.each {|str|
puts "Leaked thread: #{inst.class}\##{inst.__name__}: #{str}" puts "Leaked thread: #{inst.class}\##{inst.__name__}: #{str}"
} }
end end
live2 return leak_p, live2
end end
def find_fds def find_fds
@ -1006,7 +1016,9 @@ module MiniTest
end end
end end
def check_fd_leak(inst, live1, live2) def check_fd_leak(inst, live1)
leak_p = false
live2 = find_fds
name = "#{inst.class}\##{inst.__name__}" name = "#{inst.class}\##{inst.__name__}"
fd_closed = live1 - live2 fd_closed = live1 - live2
if !fd_closed.empty? if !fd_closed.empty?
@ -1016,6 +1028,7 @@ module MiniTest
end end
fd_leaked = live2 - live1 fd_leaked = live2 - live1
if !fd_leaked.empty? if !fd_leaked.empty?
leak_p = true
h = {} h = {}
ObjectSpace.each_object(IO) {|io| ObjectSpace.each_object(IO) {|io|
begin begin
@ -1047,37 +1060,58 @@ module MiniTest
puts "Multiple autoclose IO object for a file descriptor:#{str}" puts "Multiple autoclose IO object for a file descriptor:#{str}"
end end
} }
h = nil
GC.start
end end
live2 return leak_p, live2
end end
def find_tempfiles def extend_tempfile_counter
if defined? Tempfile return if defined? ::MiniTest::TempfileCounter
ObjectSpace.each_object(Tempfile).find_all {|t| m = Module.new {
t.path @count = 0
} class << self
else attr_accessor :count
[] end
def new(data)
MiniTest::TempfileCounter.count += 1
super(data)
end
}
MiniTest.const_set(:TempfileCounter, m)
class << Tempfile::Remover
prepend MiniTest::TempfileCounter
end end
end end
def check_tempfile_leak(obj, live1, live2) def find_tempfiles(prev_count=-1)
if obj.respond_to?(:__name__) return [prev_count, []] unless defined? Tempfile
name = "#{obj.class}\##{obj.__name__}" extend_tempfile_counter
count = TempfileCounter.count
if prev_count == count
[prev_count, []]
else else
name = obj.name tempfiles = ObjectSpace.each_object(Tempfile).find_all {|t| t.path }
[count, tempfiles]
end end
tempfile_retained = live2 - live1 end
if !tempfile_retained.empty?
list = tempfile_retained.map {|t| t.inspect }.sort def check_tempfile_leak(inst, info)
return false, info unless defined? Tempfile
count1, initial_tempfiles = info
count2, current_tempfiles = find_tempfiles(count1)
leak_p = false
tempfiles_leaked = current_tempfiles - initial_tempfiles
if !tempfiles_leaked.empty?
name = "#{inst.class}\##{inst.__name__}"
leak_p = true
list = tempfiles_leaked.map {|t| t.inspect }.sort
list.each {|str| list.each {|str|
puts "Leaked tempfile: #{name}: #{str}" puts "Leaked tempfile: #{name}: #{str}"
} }
tempfile_retained.each {|t| t.close! } tempfiles_leaked.each {|t| t.close! }
end end
live2 return leak_p, [count2, initial_tempfiles]
end end
## ##