1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
|
module Ref
# This module provides mock weak and strong references that are designed to be
# used in tests. You can define a block where all weak and soft references created
# will be mock references. You can then mimic running the garbage collector on
# the objects pointed to by the references.
#
# Example usage:
#
# Ref::Mock.use do
# obj = Object.new
# ref = Ref::WeakReference.new(obj)
# ref.object # obj
# Ref::Mock.gc(obj) # mimics the garbage collector reclaiming the referenced object
# ref.object # nil
# end
module Mock
class << self
# Use the mock implementation inside a block and then restore the original implementation.
def use
if object_space
yield
else
setup
begin
yield
ensure
cleanup
end
end
end
# Start using mock references.
def setup
raise "Ref::Mock already setup" if object_space
@object_space = {}
class << ObjectSpace
unless method_defined?(:define_finalizer_with_mock_reference)
def define_finalizer_with_mock_reference(obj, finalizer)
if ::Ref::Mock.object_space.include?(obj.__id__)
::Ref::Mock.object_space[obj.__id__] << finalizer
else
define_finalizer_without_mock_reference(obj, finalizer)
end
end
end
alias_method :define_finalizer_without_mock_reference, :define_finalizer
alias_method :define_finalizer, :define_finalizer_with_mock_reference
end
class << WeakReference
unless method_defined?(:new_with_mock_reference)
def new_with_mock_reference(obj)
if self == Mock::MockWeakReference
new_without_mock_reference(obj)
else
Mock::MockWeakReference.new(obj)
end
end
end
alias_method :new_without_mock_reference, :new
alias_method :new, :new_with_mock_reference
end
class << SoftReference
unless method_defined?(:new_with_mock_reference)
def new_with_mock_reference(obj)
if self == Mock::MockSoftReference
new_without_mock_reference(obj)
else
Mock::MockSoftReference.new(obj)
end
end
end
alias_method :new_without_mock_reference, :new
alias_method :new, :new_with_mock_reference
end
end
# Stop using mock references.
def cleanup
@object_space = nil
class << ObjectSpace
alias_method :define_finalizer_with_mock_reference, :define_finalizer
alias_method :define_finalizer, :define_finalizer_without_mock_reference
end
class << WeakReference
alias_method :new_with_mock_reference, :new
alias_method :new, :new_without_mock_reference
end
class << SoftReference
alias_method :new_with_mock_reference, :new
alias_method :new, :new_without_mock_reference
end
end
def object_space # :nodoc:
@object_space if instance_variable_defined?(:@object_space)
end
# Simulate garbage collection of the objects passed in as arguments. If no objects
# are specified, all objects will be reclaimed.
def gc(*objects)
objects = if objects.empty?
object_space.keys
else
objects.map { |obj| obj.__id__ }
end
objects.each do |id|
finalizers = object_space.delete(id)
if finalizers
finalizers.each{|finalizer| finalizer.call(id)}
end
end
end
end
module MockReference #:nodoc:
def initialize(obj)
@object = obj
@referenced_object_id = obj.__id__
raise "Reference::Mock not setup yet" unless Mock.object_space
Mock.object_space[obj.__id__] ||= []
end
def object
if @object && Mock.object_space.include?(@object.__id__)
@object
else
@object = nil
end
end
end
class MockWeakReference < WeakReference #:nodoc:
include MockReference
end
class MockSoftReference < SoftReference #:nodoc:
include MockReference
end
end
end
|