[前][次][番号順一覧][スレッド一覧]

ruby-changes:34216

From: akr <ko1@a...>
Date: Mon, 2 Jun 2014 09:36:39 +0900 (JST)
Subject: [ruby-changes:34216] akr:r46280 (trunk): * test/lib/leakchecker.rb: Leak checker extracted from

akr	2014-05-31 22:31:32 +0900 (Sat, 31 May 2014)

  New Revision: 46280

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?revision=46280&view=revision

  Log:
    * test/lib/leakchecker.rb: Leak checker extracted from
      test/lib/minitest/unit.rb.

  Added files:
    trunk/test/lib/leakchecker.rb
  Modified files:
    trunk/ChangeLog
    trunk/test/lib/minitest/unit.rb
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 46279)
+++ ChangeLog	(revision 46280)
@@ -1,3 +1,8 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1
+Sat May 31 22:30:14 2014  Tanaka Akira  <akr@f...>
+
+	* test/lib/leakchecker.rb: Leak checker extracted from
+	  test/lib/minitest/unit.rb.
+
 Sat May 31 21:15:43 2014  URABE Shyouhei  <shyouhei@r...>
 
 	* thread.c (rb_thread_atfork_internal): My compiler complains
Index: test/lib/leakchecker.rb
===================================================================
--- test/lib/leakchecker.rb	(revision 0)
+++ test/lib/leakchecker.rb	(revision 46280)
@@ -0,0 +1,159 @@ https://github.com/ruby/ruby/blob/trunk/test/lib/leakchecker.rb#L1
+class LeakChecker
+  def initialize
+    @fd_info = find_fds
+    @tempfile_info = find_tempfiles
+    @thread_info = find_threads
+  end
+
+  def check(test_name)
+    leaked1 = check_fd_leak(test_name)
+    leaked2 = check_thread_leak(test_name)
+    leaked3 = check_tempfile_leak(test_name)
+    GC.start if leaked1 || leaked2 || leaked3
+  end
+
+  def find_fds
+    fd_dir = "/proc/self/fd"
+    if File.directory?(fd_dir)
+      require "-test-/dir"
+      fds = Dir.open(fd_dir) {|d|
+        a = d.grep(/\A\d+\z/, &:to_i)
+        if d.respond_to? :fileno
+          a -= [d.fileno]
+        end
+        a
+      }
+      fds.sort
+    else
+      []
+    end
+  end
+
+  def check_fd_leak(test_name)
+    leaked = false
+    live1 = @fd_info
+    live2 = find_fds
+    fd_closed = live1 - live2
+    if !fd_closed.empty?
+      fd_closed.each {|fd|
+        puts "Closed file descriptor: #{test_name}: #{fd}"
+      }
+    end
+    fd_leaked = live2 - live1
+    if !fd_leaked.empty?
+      leaked = true
+      h = {}
+      ObjectSpace.each_object(IO) {|io|
+        begin
+          autoclose = io.autoclose?
+          fd = io.fileno
+        rescue IOError # closed IO object
+          next
+        end
+        (h[fd] ||= []) << [io, autoclose]
+      }
+      fd_leaked.each {|fd|
+        str = ''
+        if h[fd]
+          str << ' :'
+          h[fd].map {|io, autoclose|
+            s = ' ' + io.inspect
+            s << "(not-autoclose)" if !autoclose
+            s
+          }.sort.each {|s|
+            str << s
+          }
+        end
+        puts "Leaked file descriptor: #{test_name}: #{fd}#{str}"
+      }
+      h.each {|fd, list|
+        next if list.length <= 1
+        if 1 < list.count {|io, autoclose| autoclose }
+          str = list.map {|io, autoclose| " #{io.inspect}" + (autoclose ? "(autoclose)" : "") }.sort.join
+          puts "Multiple autoclose IO object for a file descriptor:#{str}"
+        end
+      }
+    end
+    @fd_info = live2
+    return leaked
+  end
+
+  def extend_tempfile_counter
+    return if defined? LeakChecker::TempfileCounter
+    m = Module.new {
+      @count = 0
+      class << self
+        attr_accessor :count
+      end
+
+      def new(data)
+        LeakChecker::TempfileCounter.count += 1
+        super(data)
+      end
+    }
+    LeakChecker.const_set(:TempfileCounter, m)
+
+    class << Tempfile::Remover
+      prepend LeakChecker::TempfileCounter
+    end
+  end
+
+  def find_tempfiles(prev_count=-1)
+    return [prev_count, []] unless defined? Tempfile
+    extend_tempfile_counter
+    count = TempfileCounter.count
+    if prev_count == count
+      [prev_count, []]
+    else
+      tempfiles = ObjectSpace.each_object(Tempfile).find_all {|t| t.path }
+      [count, tempfiles]
+    end
+  end
+
+  def check_tempfile_leak(test_name)
+    return false, @tempfile_info unless defined? Tempfile
+    count1, initial_tempfiles = @tempfile_info
+    count2, current_tempfiles = find_tempfiles(count1)
+    leaked = false
+    tempfiles_leaked = current_tempfiles - initial_tempfiles
+    if !tempfiles_leaked.empty?
+      leaked = true
+      list = tempfiles_leaked.map {|t| t.inspect }.sort
+      list.each {|str|
+        puts "Leaked tempfile: #{test_name}: #{str}"
+      }
+      tempfiles_leaked.each {|t| t.close! }
+    end
+    @tempfile_info = [count2, initial_tempfiles]
+    return leaked
+  end
+
+  def find_threads
+    Thread.list.find_all {|t|
+      t != Thread.current && t.alive?
+    }
+  end
+
+  def check_thread_leak(test_name)
+    live1 = @thread_info
+    live2 = find_threads
+    thread_finished = live1 - live2
+    leaked = false
+    if !thread_finished.empty?
+      list = thread_finished.map {|t| t.inspect }.sort
+      list.each {|str|
+        puts "Finished thread: #{test_name}: #{str}"
+      }
+    end
+    thread_leaked = live2 - live1
+    if !thread_leaked.empty?
+      leaked = true
+      list = thread_leaked.map {|t| t.inspect }.sort
+      list.each {|str|
+        puts "Leaked thread: #{test_name}: #{str}"
+      }
+    end
+    @thread_info = live2
+    return leaked
+  end
+end

Property changes on: test/lib/leakchecker.rb
___________________________________________________________________
Added: svn:eol-style
   + LF

Index: test/lib/minitest/unit.rb
===================================================================
--- test/lib/minitest/unit.rb	(revision 46279)
+++ test/lib/minitest/unit.rb	(revision 46280)
@@ -2,6 +2,7 @@ https://github.com/ruby/ruby/blob/trunk/test/lib/minitest/unit.rb#L2
 
 require "optparse"
 require "rbconfig"
+require "leakchecker"
 
 ##
 # Minimal (mostly drop-in) replacement for test-unit.
@@ -933,7 +934,7 @@ module MiniTest https://github.com/ruby/ruby/blob/trunk/test/lib/minitest/unit.rb#L934
         filter === m || filter === "#{suite}##{m}"
       }
 
-      leak_info = leak_check_init
+      leakchecker = LeakChecker.new
 
       assertions = filtered_test_methods.map { |method|
         inst = suite.new method
@@ -948,7 +949,7 @@ module MiniTest https://github.com/ruby/ruby/blob/trunk/test/lib/minitest/unit.rb#L949
         print result
         puts if @verbose
 
-        leak_info = leak_check(inst, leak_info)
+        leakchecker.check("#{inst.class}\##{inst.__name__}")
 
         inst._assertions
       }
@@ -956,164 +957,6 @@ module MiniTest https://github.com/ruby/ruby/blob/trunk/test/lib/minitest/unit.rb#L957
       return assertions.size, assertions.inject(0) { |sum, n| sum + n }
     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
-      Thread.list.find_all {|t|
-        t != Thread.current && t.alive?
-      }
-    end
-
-    def check_thread_leak(inst, live1)
-      live2 = find_threads
-      thread_finished = live1 - live2
-      leak_p = false
-      if !thread_finished.empty?
-        list = thread_finished.map {|t| t.inspect }.sort
-        list.each {|str|
-          puts "Finished thread: #{inst.class}\##{inst.__name__}: #{str}"
-        }
-      end
-      thread_leaked = live2 - live1
-      if !thread_leaked.empty?
-        leak_p = true
-        list = thread_leaked.map {|t| t.inspect }.sort
-        list.each {|str|
-          puts "Leaked thread: #{inst.class}\##{inst.__name__}: #{str}"
-        }
-      end
-      return leak_p, live2
-    end
-
-    def find_fds
-      fd_dir = "/proc/self/fd"
-      if File.directory?(fd_dir)
-        require "-test-/dir"
-        fds = Dir.open(fd_dir) {|d|
-          a = d.grep(/\A\d+\z/, &:to_i)
-          if d.respond_to? :fileno
-            a -= [d.fileno]
-          end
-          a
-        }
-        fds.sort
-      else
-        []
-      end
-    end
-
-    def check_fd_leak(inst, live1)
-      leak_p = false
-      live2 = find_fds
-      name = "#{inst.class}\##{inst.__name__}"
-      fd_closed = live1 - live2
-      if !fd_closed.empty?
-        fd_closed.each {|fd|
-          puts "Closed file descriptor: #{name}: #{fd}"
-        }
-      end
-      fd_leaked = live2 - live1
-      if !fd_leaked.empty?
-        leak_p = true
-        h = {}
-        ObjectSpace.each_object(IO) {|io|
-          begin
-            autoclose = io.autoclose?
-            fd = io.fileno
-          rescue IOError # closed IO object
-            next
-          end
-          (h[fd] ||= []) << [io, autoclose]
-        }
-        fd_leaked.each {|fd|
-          str = ''
-          if h[fd]
-            str << ' :'
-            h[fd].map {|io, autoclose|
-              s = ' ' + io.inspect
-              s << "(not-autoclose)" if !autoclose
-              s
-            }.sort.each {|s|
-              str << s
-            }
-          end
-          puts "Leaked file descriptor: #{name}: #{fd}#{str}"
-        }
-        h.each {|fd, list|
-          next if list.length <= 1
-          if 1 < list.count {|io, autoclose| autoclose }
-            str = list.map {|io, autoclose| " #{io.inspect}" + (autoclose ? "(autoclose)" : "") }.sort.join
-            puts "Multiple autoclose IO object for a file descriptor:#{str}"
-          end
-        }
-      end
-      return leak_p, live2
-    end
-
-    def extend_tempfile_counter
-      return if defined? ::MiniTest::TempfileCounter
-      m = Module.new {
-        @count = 0
-        class << self
-          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
-
-    def find_tempfiles(prev_count=-1)
-      return [prev_count, []] unless defined? Tempfile
-      extend_tempfile_counter
-      count = TempfileCounter.count
-      if prev_count == count
-        [prev_count, []]
-      else
-        tempfiles = ObjectSpace.each_object(Tempfile).find_all {|t| t.path }
-        [count, tempfiles]
-      end
-    end
-
-    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|
-          puts "Leaked tempfile: #{name}: #{str}"
-        }
-        tempfiles_leaked.each {|t| t.close! }
-      end
-      return leak_p, [count2, initial_tempfiles]
-    end
-
     ##
     # Record the result of a single test. Makes it very easy to gather
     # information. Eg:

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]