Source code for envisage.tests.test_extension_registry
# (C) Copyright 2007-2023 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 the base extension registry. """
# Standard library imports.
import contextlib
import unittest
from traits.api import List
# Enthought library imports.
from envisage.api import Application, ExtensionPoint, ExtensionRegistry
from envisage.tests.test_extension_registry_mixin import (
ExtensionRegistryTestMixin,
)
[docs]class ExtensionRegistryTestCase(ExtensionRegistryTestMixin, unittest.TestCase):
"""Tests for the base extension registry."""
[docs] def setUp(self):
"""Prepares the test fixture before each test method is called."""
# We do all of the testing via the application to make sure it offers
# the same interface!
self.registry = Application(extension_registry=ExtensionRegistry())
[docs] def test_remove_non_empty_extension_point(self):
"""remove non-empty extension point"""
registry = self.registry
# Add an extension point...
registry.add_extension_point(self.create_extension_point("my.ep"))
# ... with some extensions...
registry.set_extensions("my.ep", [42])
# ...and remove it!
registry.remove_extension_point("my.ep")
# Make sure there are no extension points.
extension_points = registry.get_extension_points()
self.assertEqual(0, len(extension_points))
# And that the extensions are gone too.
self.assertEqual([], registry.get_extensions("my.ep"))
[docs] def test_set_extensions(self):
"""set extensions"""
registry = self.registry
# Add an extension *point*.
registry.add_extension_point(self.create_extension_point("my.ep"))
# Set some extensions.
registry.set_extensions("my.ep", [1, 2, 3])
# Make sure we can get them.
self.assertEqual([1, 2, 3], registry.get_extensions("my.ep"))
[docs]def make_function_listener(events):
"""
Return a simple non-method extension point listener.
The listener appends events to the ``events`` list.
"""
def listener(registry, event):
events.append(event)
return listener
[docs]class ListensToExtensionPoint:
"""
Class with a method that can be used as an extension point listener.
"""
def __init__(self, events):
self.events = events
[docs] def listener(self, registry, event):
self.events.append(event)
[docs]class ExtensionPointListenerLifetimeTestCase(unittest.TestCase):
[docs] def setUp(self):
# We do all of the testing via the application to make sure it offers
# the same interface!
registry = Application(extension_registry=ExtensionRegistry())
extension_point = ExtensionPoint(id="my.ep", trait_type=List())
registry.add_extension_point(extension_point)
self.registry = registry
# A place to record events that listeners receive.
self.events = []
[docs] def test_add_nonmethod_listener(self):
listener = make_function_listener(self.events)
self.registry.add_extension_point_listener(listener, "my.ep")
with self.assertAppendsTo(self.events):
self.registry.set_extensions("my.ep", [1, 2, 3])
[docs] def test_remove_nonmethod_listener(self):
listener = make_function_listener(self.events)
self.registry.add_extension_point_listener(listener, "my.ep")
self.registry.remove_extension_point_listener(listener, "my.ep")
with self.assertDoesNotModify(self.events):
self.registry.set_extensions("my.ep", [4, 5, 6, 7])
[docs] def test_nonmethod_listener_lifetime(self):
listener = make_function_listener(self.events)
self.registry.add_extension_point_listener(listener, "my.ep")
# The listener should not kept alive by the registry.
del listener
with self.assertDoesNotModify(self.events):
self.registry.set_extensions("my.ep", [4, 5, 6, 7])
[docs] def test_add_method_listener(self):
obj = ListensToExtensionPoint(self.events)
self.registry.add_extension_point_listener(obj.listener, "my.ep")
# At this point, the bound method 'obj.listener' no longer
# exists; it's already been garbage collected. Nevertheless, the
# listener should still fire.
with self.assertAppendsTo(self.events):
self.registry.set_extensions("my.ep", [1, 2, 3])
[docs] def test_remove_method_listener(self):
obj = ListensToExtensionPoint(self.events)
# The two occurences of `obj.listener` below refer to different
# objects. Nevertheless, they _compare_ equal, so the removal
# should still be effective.
self.registry.add_extension_point_listener(obj.listener, "my.ep")
self.registry.remove_extension_point_listener(obj.listener, "my.ep")
with self.assertDoesNotModify(self.events):
self.registry.set_extensions("my.ep", [1, 2, 3])
[docs] def test_method_listener_lifetime(self):
obj = ListensToExtensionPoint(self.events)
self.registry.add_extension_point_listener(obj.listener, "my.ep")
# Removing the last reference to the object should deactivate
# the listener.
del obj
with self.assertDoesNotModify(self.events):
self.registry.set_extensions("my.ep", [1, 2, 3])
# Helper assertions #######################################################
[docs] @contextlib.contextmanager
def assertAppendsTo(self, some_list):
"""
Assert that exactly one element is appended to a list.
Return a context manager that checks that the code in the corresponding
with block appends exactly one element to the given list.
"""
old_length = len(some_list)
yield
new_length = len(some_list)
diff = new_length - old_length
self.assertEqual(
diff,
1,
msg="Expected exactly one new element; got {}".format(diff),
)
[docs] @contextlib.contextmanager
def assertDoesNotModify(self, some_list):
"""
Assert that a list is unchanged.
Return a context manager that checks that the code in the corresponding
with block does not modify the length of the given list.
"""
old_length = len(some_list)
yield
new_length = len(some_list)
diff = new_length - old_length
self.assertEqual(
diff,
0,
msg="Expected no new elements; got {}".format(diff),
)