Viewing file: test_expose.py (9.5 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
# Copright 2008 Divmod, Inc. See LICENSE file for details.
""" L{epsilon.expose} is a module which allows a system that needs to expose code to a network endpoint do so in a manner which only exposes methods which have been explicitly designated. It provides utilities for convenient annotation and lookup of exposed methods. """
from epsilon.structlike import record
from epsilon.expose import Exposer, MethodNotExposed, NameRequired
from twisted.trial.unittest import TestCase
class ExposeTests: """ This mixin provides tests for expose, based on a parameterized base type for the class which methods are being exposed on. Subclass this before L{TestCase} and set L{superClass} to use this.
@ivar superClass: the class to be subclassed by all classes which expose methods. """
superClass = None def setUp(self): """ Create two exposers to expose methods in tests. """ self.exposer = Exposer("test exposer") self.otherExposer = Exposer("other exposer")
def test_exposeDocAttribute(self): """ Creating an exposer should require a docstring explaining what it's for. """ docstring = "This is my docstring." exposer = Exposer(docstring) self.assertEqual(exposer.__doc__, docstring)
def test_simpleExpose(self): """ Creating an exposer, defining a class and exposing a method of a class with that exposer, then retrieving a method of that class should result in the method of that class. """ class Foo(self.superClass): def __init__(self, num): self.num = num
@self.exposer.expose() def bar(self): return self.num + 1 f = Foo(3) method = self.exposer.get(f, 'bar') self.assertEqual(method(), 4)
def test_notExposed(self): """ Creating an exposer and then attempting to retrieve a method not exposed with it should result in a L{MethodNotExposed} exception. """ class Foo(self.superClass): def bar(self): return 1 f = Foo() self.assertRaises(MethodNotExposed, self.exposer.get, f, 'bar')
def test_differentMethodsDifferentExposers(self): """ Methods should only be able to be retrieved with the exposer that exposed them, not with any other exposer. """ class Foo(self.superClass): @self.exposer.expose() def bar(self): return 1 @self.otherExposer.expose() def baz(self): return 2 f = Foo() self.assertEqual(self.exposer.get(f, 'bar')(), 1) self.assertEqual(self.otherExposer.get(f, 'baz')(), 2) self.assertRaises(MethodNotExposed, self.otherExposer.get, f, 'bar') self.assertRaises(MethodNotExposed, self.exposer.get, f, 'baz')
def test_sameMethodExposedByDifferentExposers(self): """ If the same method is exposed by two different exposers, it should be accessible by both of them. """ class Foo(self.superClass): @self.exposer.expose() @self.otherExposer.expose() def bar(self): return 4 f = Foo() self.assertEqual(self.exposer.get(f, 'bar')(), 4) self.assertEqual(self.otherExposer.get(f, 'bar')(), 4)
def test_exposeWithDifferentKey(self): """ The 'key' argument to {Exposer.expose} should change the argument to 'get'. """ class Foo(self.superClass): @self.exposer.expose(key='hello') def bar(self): return 7 f = Foo() self.assertEqual(self.exposer.get(f, 'hello')(), 7)
def test_exposeOnDifferentClass(self): """ An exposer should only be able to retrieve a method from instances of types which it has explicitly exposed methods on. Instances of different types with the same method name should raise L{MethodNotExposed}. """ class Foo(self.superClass): @self.exposer.expose() def bar(self): return 7 class Baz(self.superClass): def bar(self): return 8 f = Foo() b = Baz() self.assertEqual(self.exposer.get(f, 'bar')(), 7) self.assertRaises(MethodNotExposed, self.otherExposer.get, b, 'bar')
def test_exposeUnnamedNoKey(self): """ L{Exposer.expose} raises L{NameRequired} when called without a value for the C{key} parameter if it is used to decorate a non-function object. """ def f(): class Foo(self.superClass): @self.exposer.expose() @classmethod def foo(self): pass self.assertRaises(NameRequired, f)
def test_exposeNonMethod(self): """ L{Exposer.expose} should work on methods which have been decorated by another decorator and will therefore not result in function objects when retrieved with __get__. """ class Getter(record('function')): def __get__(self, oself, type): return self.function
class Foo(self.superClass): @self.exposer.expose(key='bar') @Getter def bar(): return 7
f = Foo() # Sanity check self.assertEqual(f.bar(), 7) self.assertEqual(self.exposer.get(f, 'bar')(), 7)
def test_descriptorGetsType(self): """ L{Exposer.get} should not interfere with the appropriate type object being passed to the wrapped descriptor's C{__get__}. """ types = [] class Getter(record('function')): def __get__(self, oself, type): types.append(type) return self.function class Foo(self.superClass): @self.exposer.expose(key='bar') @Getter def bar(): return 7 f = Foo() self.exposer.get(f, 'bar') self.assertEqual(types, [Foo])
def test_descriptorGetsSubtype(self): """ When a descriptor is exposed through a superclass, getting it from a subclass results in the subclass being passed to the C{__get__} method. """ types = [] class Getter(record('function')): def __get__(self, oself, type): types.append(type) return self.function class Foo(self.superClass): @self.exposer.expose(key='bar') @Getter def bar(): return 7 class Baz(Foo): pass b = Baz() self.exposer.get(b, 'bar') self.assertEqual(types, [Baz])
def test_implicitSubclassExpose(self): """ L{Exposer.expose} should expose the given object on all subclasses. """ class Foo(self.superClass): @self.exposer.expose() def bar(self): return 7 class Baz(Foo): pass b = Baz() self.assertEqual(self.exposer.get(b, 'bar')(), 7)
def test_overrideDontExpose(self): """ L{Exposer.expose} should not expose overridden methods on subclasses. """ class Foo(self.superClass): @self.exposer.expose() def bar(self): return 7 class Baz(Foo): def bar(self): return 8 b = Baz() self.assertRaises(MethodNotExposed, self.otherExposer.get, b, 'bar')
def test_sameKeyOnDifferentTypes(self): """ L{Exposer.expose} should work with the same key on different types. """ class Foo(self.superClass): @self.exposer.expose() def bar(self): return 17 class Qux(self.superClass): @self.exposer.expose() def bar(self): return 71 q = Qux() f = Foo() self.assertEqual(self.exposer.get(q, 'bar')(), 71) self.assertEqual(self.exposer.get(f, 'bar')(), 17)
def test_overrideReExpose(self): """ L{Exposer.expose} should expose a method on a subclass if that method is overridden. """ class Foo(self.superClass): @self.exposer.expose() def bar(self): return 7 class Baz(Foo): @self.exposer.expose() def bar(self): return 8 f = Foo() b = Baz() self.assertEqual(self.exposer.get(f, 'bar')(), 7) self.assertEqual(self.exposer.get(b, 'bar')(), 8)
def test_deleteExposedAttribute(self): """ When an exposed attribute is deleted from a class, it should no longer be exposed; calling L{Exposer.get} should result in L{MethodNotExposed}. """ class Foo(self.superClass): @self.exposer.expose() def bar(self): return 7 f = Foo() del Foo.bar self.assertRaises(MethodNotExposed, self.otherExposer.get, f, 'bar')
class ExposeNewStyle(ExposeTests, TestCase): """ All of the above functionality should work on new-style classes. """ superClass = object
class Classic: """ A dummy classic class. """
class ExposeOldStyle(ExposeTests, TestCase): """ All of the above functionality should work on old-style classes. """ superClass = Classic
|