text: # This file isn't really meant to be run as a test, though it won't really
  # make a difference.
  
  import gc
  
  # Sometimes pointer objects from popped stack frames remain up the stack
  # and end up being marked when the GC conservatively scans the stack, but
  # this causes flaky tests because we really want the object to be collected.
  # By having a deep recursive function, we ensure that the object we want to
  # collect is really far in the stack and won't get scanned.
  def call_function_far_up_the_stack(fn, num_calls_left=200):
      if num_calls_left == 0:
          return fn()
      else:
          return call_function_far_up_the_stack(fn, num_calls_left - 1)
  
  # It's useful to call the GC at different locations in the stack in case that it's the
  # call to the GC itself that left a lingering pointer (e.g. the pointer could be the
  # __del__ attribute of an object we'd like to collect).
  def call_gc_throughout_the_stack(number_of_gc_calls, num_calls_left=30):
      if num_calls_left > 0:
          call_gc_throughout_the_stack(number_of_gc_calls, num_calls_left - 1)
          if number_of_gc_calls >= num_calls_left:
              gc.collect()
  
  # test_gc takes in a function fn that presumably allocations some objects and
  # attempts to collect those objects in order to trigger a call to the finalizers.
  #
  # The problem is that it's actually quite hard to guarantee finalizer calls
  # because with conservative scanning, there can always be lingering pointers
  # on the stack. This function has a bunch of hacks to attempt to clear those
  # lingering pointers.
  def test_gc(fn, number_of_gc_calls=3):
      class DummyNewObject(object):
          pass
      class DummyOldObject():
          pass
  
      def dummyFunctionThatDoesSomeAllocation():
          # Allocating a few objects on the heap seems to be helpful.
          for _ in xrange(100):
              n, o = DummyNewObject(), DummyOldObject()
          objs = [DummyNewObject() for _ in xrange(100)]
  
      # Call fn after a few recursive calls to get those allocations.
      val = call_function_far_up_the_stack(fn)
  
      # Call a dummy function in the same way as fn. By following the same
      # code path, there is a better chance of clearing lingering references.
      call_function_far_up_the_stack(dummyFunctionThatDoesSomeAllocation)
  
      # Force garbage collection.
      call_gc_throughout_the_stack(number_of_gc_calls - 1)
      gc.collect()
      return val
  
  