text: # (C) Copyright 2007-2021 Enthought, Inc., Austin, TX
  # All rights reserved.
  #
  # This software is provided without warranty under the terms of the BSD
  # license included in LICENSE.txt and may be redistributed only under
  # the conditions described in the aforementioned license. The license
  # is also available online at http://www.enthought.com/licenses/BSD.txt
  #
  # Thanks for using Enthought open source!
  """ Tests for safe weakrefs. """
  
  
  # Standard library imports.
  import unittest
  import weakref
  
  # Enthought library imports.
  from envisage.safeweakref import ref
  from traits.api import HasTraits
  
  
  class SafeWeakrefTestCase(unittest.TestCase):
      """ Tests for safe weakrefs. """
  
      def test_can_create_weakref_to_bound_method(self):
          class Foo(HasTraits):
              def method(self):
                  self.method_called = True
  
          f = Foo()
  
          # Get a weak reference to a bound method.
          with self.assertWarns(DeprecationWarning):
              r = ref(f.method)
          self.assertNotEqual(None, r())
  
          # Make sure we can call it.
          r()()
          self.assertTrue(f.method_called)
  
          # Delete the object to delete the method!
          del f
  
          # The reference should now return None.
          self.assertEqual(None, r())
  
      def test_two_weakrefs_to_bound_method_are_identical(self):
          class Foo(HasTraits):
              def method(self):
                  pass
  
          f = Foo()
  
          with self.assertWarns(DeprecationWarning):
              ref1 = ref(f.method)
              ref2 = ref(f.method)
  
          self.assertIs(ref1, ref2)
  
      def test_internal_cache_is_weak_too(self):
          # smell: Fragile test because we are reaching into the internals of the
          # object under test.
          #
          # I can't see a (clean!) way around this without adding something to
          # the public API that would only exist for testing, but in terms of
          # 'bang for the buck' I think this is good enough despite the
          # fragility.
          cache = ref._cache
  
          class Foo(HasTraits):
              def method(self):
                  pass
  
          f = Foo()
  
          # Get the length of the cache before we do anything.
          len_cache = len(cache)
  
          # Create a weak reference to the bound method and make sure that
          # exactly one item has been added to the cache.
          with self.assertWarns(DeprecationWarning):
              r = ref(f.method)
          self.assertEqual(len_cache + 1, len(cache))
  
          # Delete the instance!
          del f
  
          # Our `ref` should now reference nothing...
          self.assertEqual(None, r())
  
          # ... and the cache should be back to its original size!
          self.assertEqual(len_cache, len(cache))
  
      def test_two_weakrefs_to_bound_method_are_equal(self):
          class Foo(HasTraits):
              def method(self):
                  pass
  
          f = Foo()
  
          # Make sure that two references to the same method compare as equal.
          with self.assertWarns(DeprecationWarning):
              r1 = ref(f.method)
              r2 = ref(f.method)
          self.assertEqual(r1, r2)
  
          # Make sure that a reference compares as unequal to non-references!
          self.assertTrue(not r1 == 99)
  
      def test_two_weakrefs_to_bound_method_hash_equally(self):
          class Foo(HasTraits):
              def method(self):
                  pass
  
          f = Foo()
  
          # Make sure we can hash the references.
          with self.assertWarns(DeprecationWarning):
              r1 = ref(f.method)
              r2 = ref(f.method)
  
          self.assertEqual(hash(r1), hash(r2))
  
          # Make sure we can hash non-bound methods.
          with self.assertWarns(DeprecationWarning):
              r1 = ref(Foo)
              r2 = ref(Foo)
  
          self.assertEqual(hash(r1), hash(r2))
  
      def test_get_builtin_weakref_for_non_bound_method(self):
          class Foo(HasTraits):
              pass
  
          f = Foo()
  
          # Get a weak reference to something that is not a bound method.
          with self.assertWarns(DeprecationWarning):
              r = ref(f)
          self.assertEqual(weakref.ref, type(r))
  
      def test_deprecated(self):
          class Foo(HasTraits):
              def method(self):
                  pass
  
          f = Foo()
  
          with self.assertWarns(DeprecationWarning):
              ref(f.method)
  
          with self.assertWarns(DeprecationWarning):
              ref(f)
  