Source code for envisage.tests.test_service_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 service registry. """
# Standard library imports.
import sys
import unittest
from traits.api import HasTraits, Int, Interface, provides
# Enthought library imports.
from envisage.api import Application, NoSuchServiceError, ServiceRegistry
# This module's package.
PKG = "envisage.tests"
[docs]def service_factory(**properties):
"""A factory for foos."""
return HasTraits(**properties)
[docs]class ServiceRegistryTestCase(unittest.TestCase):
"""Tests for the service registry."""
###########################################################################
# 'TestCase' interface.
###########################################################################
[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.service_registry = Application(service_registry=ServiceRegistry())
# module 'foo' need to be cleared out when this test is run,
# because other tests also import foo.
if PKG + ".foo" in sys.modules:
del sys.modules[PKG + ".foo"]
###########################################################################
# Tests.
###########################################################################
[docs] def test_should_get_required_service(self):
class Foo(HasTraits):
price = Int
foo = Foo()
# Register a service factory.
self.service_registry.register_service(Foo, foo)
service = self.service_registry.get_required_service(Foo)
self.assertIs(foo, service)
[docs] def test_should_get_exception_if_required_service_is_missing(self):
class IFoo(Interface):
price = Int
with self.assertRaises(NoSuchServiceError):
self.service_registry.get_required_service(IFoo)
[docs] def test_imported_service_factory(self):
"""imported service factory"""
class IFoo(Interface):
price = Int
# Register a service factory.
self.service_registry.register_service(
HasTraits,
PKG + ".test_service_registry.service_factory",
{"price": 100},
)
# Create a query that matches the registered object.
service = self.service_registry.get_service(HasTraits, "price <= 100")
self.assertNotEqual(None, service)
self.assertEqual(HasTraits, type(service))
# This shows that the properties were passed in to the factory.
self.assertEqual(100, service.price)
# Make sure that the object created by the factory is cached (i.e. we
# get the same object back from now on!).
service2 = self.service_registry.get_service(HasTraits, "price <= 100")
self.assertTrue(service is service2)
[docs] def test_function_service_factory(self):
"""function service factory"""
class IFoo(Interface):
price = Int
@provides(IFoo)
class Foo(HasTraits):
price = Int
def foo_factory(**properties):
"""A factory for foos."""
return Foo(**properties)
# Register a service factory.
self.service_registry.register_service(
IFoo, foo_factory, {"price": 100}
)
# Create a query that matches the registered object.
service = self.service_registry.get_service(IFoo, "price <= 100")
self.assertNotEqual(None, service)
self.assertEqual(Foo, type(service))
# Make sure that the object created by the factory is cached (i.e. we
# get the same object back from now on!).
service2 = self.service_registry.get_service(IFoo, "price <= 100")
self.assertTrue(service is service2)
[docs] def test_lazy_function_service_factory(self):
"""lazy function service factory"""
# Register a service factory by name.
def foo_factory(**properties):
"""A factory for foos."""
from envisage.tests.foo import Foo
foo_factory.foo = Foo()
return foo_factory.foo
i_foo = PKG + ".i_foo.IFoo"
foo = PKG + ".foo"
self.service_registry.register_service(i_foo, foo_factory)
# Get rid of the 'foo' module (used in other tests).
if foo in sys.modules:
del sys.modules[foo]
# Make sure that we haven't imported the 'foo' module.
self.assertTrue(foo not in sys.modules)
# Look up a non-existent service.
services = self.service_registry.get_services("bogus.IBogus")
# Make sure that we *still* haven't imported the 'foo' module.
self.assertTrue(foo not in sys.modules)
# Look it up again.
services = self.service_registry.get_services(i_foo)
self.assertEqual([foo_factory.foo], services)
self.assertTrue(foo in sys.modules)
# Clean up!
del sys.modules[foo]
[docs] def test_lazy_bound_method_service_factory(self):
"""lazy bound method service factory"""
i_foo = PKG + ".i_foo.IFoo"
foo = PKG + ".foo"
class ServiceProvider(HasTraits):
"""A class that provides a service.
This is used to make sure a bound method can be used as a service
factory.
"""
# Register a service factory by name.
def foo_factory(self, **properties):
"""A factory for foos."""
from envisage.tests.foo import Foo
self.foo = Foo()
return self.foo
sp = ServiceProvider()
self.service_registry.register_service(i_foo, sp.foo_factory)
# Get rid of the 'foo' module (used in other tests).
if foo in sys.modules:
del sys.modules[foo]
# Make sure that we haven't imported the 'foo' module.
self.assertTrue(foo not in sys.modules)
# Look up a non-existent service.
services = self.service_registry.get_services("bogus.IBogus")
# Make sure that we *still* haven't imported the 'foo' module.
self.assertTrue(foo not in sys.modules)
# Look up the service.
services = self.service_registry.get_services(i_foo)
self.assertEqual([sp.foo], services)
self.assertTrue(foo in sys.modules)
# Clean up!
del sys.modules[foo]
[docs] def test_get_services(self):
"""get services"""
class IFoo(Interface):
pass
@provides(IFoo)
class Foo(HasTraits):
pass
# Register two services.
foo = Foo()
self.service_registry.register_service(IFoo, foo)
foo = Foo()
self.service_registry.register_service(IFoo, foo)
# Look it up again.
services = self.service_registry.get_services(IFoo)
self.assertEqual(2, len(services))
class IBar(Interface):
pass
# Lookup a non-existent service.
services = self.service_registry.get_services(IBar)
self.assertEqual([], services)
[docs] def test_get_services_with_strings(self):
"""get services with strings"""
from envisage.tests.foo import Foo
# Register a couple of services using a string protocol name.
protocol_name = "envisage.tests.foo.IFoo"
self.service_registry.register_service(protocol_name, Foo())
self.service_registry.register_service(protocol_name, Foo())
# Look them up using the same string!
services = self.service_registry.get_services(protocol_name)
self.assertEqual(2, len(services))
[docs] def test_get_services_with_query(self):
"""get services with query"""
class IFoo(Interface):
price = Int
@provides(IFoo)
class Foo(HasTraits):
price = Int
# Register two services.
#
# This one shows how the object's attributes are used when evaluating
# a query.
foo = Foo(price=100)
self.service_registry.register_service(IFoo, foo)
# This one shows how properties can be specified that *take precedence*
# over the object's attributes when evaluating a query.
goo = Foo(price=10)
self.service_registry.register_service(IFoo, goo, {"price": 200})
# Create a query that doesn't match any registered object.
services = self.service_registry.get_services(IFoo, 'color == "red"')
self.assertEqual([], services)
# Create a query that matches one of the registered objects.
services = self.service_registry.get_services(IFoo, "price <= 100")
self.assertEqual([foo], services)
# Create a query that matches both registered objects.
services = self.service_registry.get_services(IFoo, "price >= 100")
self.assertTrue(foo in services)
self.assertTrue(goo in services)
self.assertEqual(2, len(services))
class IBar(Interface):
pass
# Lookup a non-existent service.
services = self.service_registry.get_services(IBar, "price <= 100")
self.assertEqual([], services)
[docs] def test_get_service(self):
"""get service"""
class IFoo(Interface):
pass
@provides(IFoo)
class Foo(HasTraits):
pass
# Register a couple of services.
foo = Foo()
self.service_registry.register_service(IFoo, foo)
goo = Foo()
self.service_registry.register_service(IFoo, goo)
# Look up one of them!
service = self.service_registry.get_service(IFoo)
self.assertTrue(foo is service or goo is service)
class IBar(Interface):
pass
# Lookup a non-existent service.
service = self.service_registry.get_service(IBar)
self.assertEqual(None, service)
[docs] def test_get_service_with_query(self):
"""get service with query"""
class IFoo(Interface):
price = Int
@provides(IFoo)
class Foo(HasTraits):
price = Int
# Register two services.
#
# This one shows how the object's attributes are used when evaluating
# a query.
foo = Foo(price=100)
self.service_registry.register_service(IFoo, foo)
# This one shows how properties can be specified that *take precedence*
# over the object's attributes when evaluating a query.
goo = Foo(price=10)
self.service_registry.register_service(IFoo, goo, {"price": 200})
# Create a query that doesn't match any registered object.
service = self.service_registry.get_service(IFoo, "price < 100")
self.assertEqual(None, service)
# Create a query that matches one of the registered objects.
service = self.service_registry.get_service(IFoo, "price <= 100")
self.assertEqual(foo, service)
# Create a query that matches both registered objects.
service = self.service_registry.get_service(IFoo, "price >= 100")
self.assertTrue(foo is service or goo is service)
class IBar(Interface):
pass
# Lookup a non-existent service.
service = self.service_registry.get_service(IBar, "price <= 100")
self.assertEqual(None, service)
[docs] def test_get_and_set_service_properties(self):
"""get and set service properties"""
class IFoo(Interface):
price = Int
@provides(IFoo)
class Foo(HasTraits):
price = Int
# Register two services.
#
# This one has no properties.
foo = Foo(price=100)
foo_id = self.service_registry.register_service(IFoo, foo)
# This one has properties.
goo = Foo(price=10)
goo_id = self.service_registry.register_service(
IFoo, goo, {"price": 200}
)
# Get the properties.
foo_properties = self.service_registry.get_service_properties(foo_id)
self.assertEqual({}, foo_properties)
goo_properties = self.service_registry.get_service_properties(goo_id)
self.assertEqual(200, goo_properties["price"])
# Update the properties.
foo_properties["price"] = 300
goo_properties["price"] = 500
# Set the properties.
self.service_registry.set_service_properties(foo_id, foo_properties)
self.service_registry.set_service_properties(goo_id, goo_properties)
# Get the properties again.
foo_properties = self.service_registry.get_service_properties(foo_id)
self.assertEqual(300, foo_properties["price"])
goo_properties = self.service_registry.get_service_properties(goo_id)
self.assertEqual(500, goo_properties["price"])
# Try to get the properties of a non-existent service.
with self.assertRaises(ValueError):
self.service_registry.get_service_properties(-1)
# Try to set the properties of a non-existent service.
with self.assertRaises(ValueError):
self.service_registry.set_service_properties(-1, {})
[docs] def test_unregister_service(self):
"""unregister service"""
class IFoo(Interface):
price = Int
@provides(IFoo)
class Foo(HasTraits):
price = Int
# Register two services.
#
# This one shows how the object's attributes are used when evaluating
# a query.
foo = Foo(price=100)
foo_id = self.service_registry.register_service(IFoo, foo)
# This one shows how properties can be specified that *take precedence*
# over the object's attributes when evaluating a query.
goo = Foo(price=10)
goo_id = self.service_registry.register_service(
IFoo, goo, {"price": 200}
)
# Create a query that doesn't match any registered object.
service = self.service_registry.get_service(IFoo, "price < 100")
self.assertEqual(None, service)
# Create a query that matches one of the registered objects.
service = self.service_registry.get_service(IFoo, "price <= 100")
self.assertEqual(foo, service)
# Create a query that matches both registered objects.
service = self.service_registry.get_service(IFoo, "price >= 100")
self.assertTrue(foo is service or goo is service)
#### Now do some unregistering! ####
# Unregister 'foo'.
self.service_registry.unregister_service(foo_id)
# This query should no longer match any of the registered objects.
service = self.service_registry.get_service(IFoo, "price <= 100")
self.assertEqual(None, service)
# Unregister 'goo'.
self.service_registry.unregister_service(goo_id)
# This query should no longer match any of the registered objects.
service = self.service_registry.get_service(IFoo, "price >= 100")
self.assertEqual(None, service)
# Try to unregister a non-existent service.
with self.assertRaises(ValueError):
self.service_registry.unregister_service(-1)
[docs] def test_minimize_and_maximize(self):
"""minimize and maximize"""
class IFoo(Interface):
price = Int
@provides(IFoo)
class Foo(HasTraits):
price = Int
# Register some objects with various prices.
x = Foo(price=10)
y = Foo(price=5)
z = Foo(price=100)
for foo in [x, y, z]:
self.service_registry.register_service(IFoo, foo)
# Find the service with the lowest price.
service = self.service_registry.get_service(IFoo, minimize="price")
self.assertNotEqual(None, service)
self.assertEqual(Foo, type(service))
self.assertEqual(y, service)
# Find the service with the highest price.
service = self.service_registry.get_service(IFoo, maximize="price")
self.assertNotEqual(None, service)
self.assertEqual(Foo, type(service))
self.assertEqual(z, service)