RAHHH
This commit is contained in:
@@ -0,0 +1,638 @@
|
||||
import operator
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import textwrap
|
||||
import tempfile
|
||||
from unittest import skipIf, TestCase
|
||||
|
||||
|
||||
def isTwistedInstalled():
|
||||
try:
|
||||
__import__("twisted")
|
||||
except ImportError:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
class _WritesPythonModules(TestCase):
|
||||
"""
|
||||
A helper that enables generating Python module test fixtures.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(_WritesPythonModules, self).setUp()
|
||||
|
||||
from twisted.python.modules import getModule, PythonPath
|
||||
from twisted.python.filepath import FilePath
|
||||
|
||||
self.getModule = getModule
|
||||
self.PythonPath = PythonPath
|
||||
self.FilePath = FilePath
|
||||
|
||||
self.originalSysModules = set(sys.modules.keys())
|
||||
self.savedSysPath = sys.path[:]
|
||||
|
||||
self.pathDir = tempfile.mkdtemp()
|
||||
self.makeImportable(self.pathDir)
|
||||
|
||||
def tearDown(self):
|
||||
super(_WritesPythonModules, self).tearDown()
|
||||
|
||||
sys.path[:] = self.savedSysPath
|
||||
modulesToDelete = sys.modules.keys() - self.originalSysModules
|
||||
for module in modulesToDelete:
|
||||
del sys.modules[module]
|
||||
|
||||
shutil.rmtree(self.pathDir)
|
||||
|
||||
def makeImportable(self, path):
|
||||
sys.path.append(path)
|
||||
|
||||
def writeSourceInto(self, source, path, moduleName):
|
||||
directory = self.FilePath(path)
|
||||
|
||||
module = directory.child(moduleName)
|
||||
# FilePath always opens a file in binary mode - but that will
|
||||
# break on Python 3
|
||||
with open(module.path, "w") as f:
|
||||
f.write(textwrap.dedent(source))
|
||||
|
||||
return self.PythonPath([directory.path])
|
||||
|
||||
def makeModule(self, source, path, moduleName):
|
||||
pythonModuleName, _ = os.path.splitext(moduleName)
|
||||
return self.writeSourceInto(source, path, moduleName)[pythonModuleName]
|
||||
|
||||
def attributesAsDict(self, hasIterAttributes):
|
||||
return {attr.name: attr for attr in hasIterAttributes.iterAttributes()}
|
||||
|
||||
def loadModuleAsDict(self, module):
|
||||
module.load()
|
||||
return self.attributesAsDict(module)
|
||||
|
||||
def makeModuleAsDict(self, source, path, name):
|
||||
return self.loadModuleAsDict(self.makeModule(source, path, name))
|
||||
|
||||
|
||||
@skipIf(not isTwistedInstalled(), "Twisted is not installed.")
|
||||
class OriginalLocationTests(_WritesPythonModules):
|
||||
"""
|
||||
Tests that L{isOriginalLocation} detects when a
|
||||
L{PythonAttribute}'s FQPN refers to an object inside the module
|
||||
where it was defined.
|
||||
|
||||
For example: A L{twisted.python.modules.PythonAttribute} with a
|
||||
name of 'foo.bar' that refers to a 'bar' object defined in module
|
||||
'baz' does *not* refer to bar's original location, while a
|
||||
L{PythonAttribute} with a name of 'baz.bar' does.
|
||||
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(OriginalLocationTests, self).setUp()
|
||||
from .._discover import isOriginalLocation
|
||||
|
||||
self.isOriginalLocation = isOriginalLocation
|
||||
|
||||
def test_failsWithNoModule(self):
|
||||
"""
|
||||
L{isOriginalLocation} returns False when the attribute refers to an
|
||||
object whose source module cannot be determined.
|
||||
"""
|
||||
source = """\
|
||||
class Fake(object):
|
||||
pass
|
||||
hasEmptyModule = Fake()
|
||||
hasEmptyModule.__module__ = None
|
||||
"""
|
||||
|
||||
moduleDict = self.makeModuleAsDict(source, self.pathDir, "empty_module_attr.py")
|
||||
|
||||
self.assertFalse(
|
||||
self.isOriginalLocation(moduleDict["empty_module_attr.hasEmptyModule"])
|
||||
)
|
||||
|
||||
def test_failsWithDifferentModule(self):
|
||||
"""
|
||||
L{isOriginalLocation} returns False when the attribute refers to
|
||||
an object outside of the module where that object was defined.
|
||||
"""
|
||||
originalSource = """\
|
||||
class ImportThisClass(object):
|
||||
pass
|
||||
importThisObject = ImportThisClass()
|
||||
importThisNestingObject = ImportThisClass()
|
||||
importThisNestingObject.nestedObject = ImportThisClass()
|
||||
"""
|
||||
|
||||
importingSource = """\
|
||||
from original import (ImportThisClass,
|
||||
importThisObject,
|
||||
importThisNestingObject)
|
||||
"""
|
||||
|
||||
self.makeModule(originalSource, self.pathDir, "original.py")
|
||||
importingDict = self.makeModuleAsDict(
|
||||
importingSource, self.pathDir, "importing.py"
|
||||
)
|
||||
self.assertFalse(
|
||||
self.isOriginalLocation(importingDict["importing.ImportThisClass"])
|
||||
)
|
||||
self.assertFalse(
|
||||
self.isOriginalLocation(importingDict["importing.importThisObject"])
|
||||
)
|
||||
|
||||
nestingObject = importingDict["importing.importThisNestingObject"]
|
||||
nestingObjectDict = self.attributesAsDict(nestingObject)
|
||||
nestedObject = nestingObjectDict[
|
||||
"importing.importThisNestingObject.nestedObject"
|
||||
]
|
||||
|
||||
self.assertFalse(self.isOriginalLocation(nestedObject))
|
||||
|
||||
def test_succeedsWithSameModule(self):
|
||||
"""
|
||||
L{isOriginalLocation} returns True when the attribute refers to an
|
||||
object inside the module where that object was defined.
|
||||
"""
|
||||
mSource = textwrap.dedent(
|
||||
"""
|
||||
class ThisClassWasDefinedHere(object):
|
||||
pass
|
||||
anObject = ThisClassWasDefinedHere()
|
||||
aNestingObject = ThisClassWasDefinedHere()
|
||||
aNestingObject.nestedObject = ThisClassWasDefinedHere()
|
||||
"""
|
||||
)
|
||||
mDict = self.makeModuleAsDict(mSource, self.pathDir, "m.py")
|
||||
self.assertTrue(self.isOriginalLocation(mDict["m.ThisClassWasDefinedHere"]))
|
||||
self.assertTrue(self.isOriginalLocation(mDict["m.aNestingObject"]))
|
||||
|
||||
nestingObject = mDict["m.aNestingObject"]
|
||||
nestingObjectDict = self.attributesAsDict(nestingObject)
|
||||
nestedObject = nestingObjectDict["m.aNestingObject.nestedObject"]
|
||||
|
||||
self.assertTrue(self.isOriginalLocation(nestedObject))
|
||||
|
||||
|
||||
@skipIf(not isTwistedInstalled(), "Twisted is not installed.")
|
||||
class FindMachinesViaWrapperTests(_WritesPythonModules):
|
||||
"""
|
||||
L{findMachinesViaWrapper} recursively yields FQPN,
|
||||
L{MethodicalMachine} pairs in and under a given
|
||||
L{twisted.python.modules.PythonModule} or
|
||||
L{twisted.python.modules.PythonAttribute}.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(FindMachinesViaWrapperTests, self).setUp()
|
||||
from .._discover import findMachinesViaWrapper
|
||||
|
||||
self.findMachinesViaWrapper = findMachinesViaWrapper
|
||||
|
||||
def test_yieldsMachine(self):
|
||||
"""
|
||||
When given a L{twisted.python.modules.PythonAttribute} that refers
|
||||
directly to a L{MethodicalMachine}, L{findMachinesViaWrapper}
|
||||
yields that machine and its FQPN.
|
||||
"""
|
||||
source = """\
|
||||
from automat import MethodicalMachine
|
||||
|
||||
rootMachine = MethodicalMachine()
|
||||
"""
|
||||
|
||||
moduleDict = self.makeModuleAsDict(source, self.pathDir, "root.py")
|
||||
rootMachine = moduleDict["root.rootMachine"]
|
||||
self.assertIn(
|
||||
("root.rootMachine", rootMachine.load()),
|
||||
list(self.findMachinesViaWrapper(rootMachine)),
|
||||
)
|
||||
|
||||
def test_yieldsTypeMachine(self) -> None:
|
||||
"""
|
||||
When given a L{twisted.python.modules.PythonAttribute} that refers
|
||||
directly to a L{TypeMachine}, L{findMachinesViaWrapper} yields that
|
||||
machine and its FQPN.
|
||||
"""
|
||||
source = """\
|
||||
from automat import TypeMachineBuilder
|
||||
from typing import Protocol, Callable
|
||||
class P(Protocol):
|
||||
def method(self) -> None: ...
|
||||
class C:...
|
||||
def buildBuilder() -> Callable[[C], P]:
|
||||
builder = TypeMachineBuilder(P, C)
|
||||
return builder.build()
|
||||
rootMachine = buildBuilder()
|
||||
"""
|
||||
|
||||
moduleDict = self.makeModuleAsDict(source, self.pathDir, "root.py")
|
||||
rootMachine = moduleDict["root.rootMachine"]
|
||||
self.assertIn(
|
||||
("root.rootMachine", rootMachine.load()),
|
||||
list(self.findMachinesViaWrapper(rootMachine)),
|
||||
)
|
||||
|
||||
def test_yieldsMachineInClass(self):
|
||||
"""
|
||||
When given a L{twisted.python.modules.PythonAttribute} that refers
|
||||
to a class that contains a L{MethodicalMachine} as a class
|
||||
variable, L{findMachinesViaWrapper} yields that machine and
|
||||
its FQPN.
|
||||
"""
|
||||
source = """\
|
||||
from automat import MethodicalMachine
|
||||
|
||||
class PythonClass(object):
|
||||
_classMachine = MethodicalMachine()
|
||||
"""
|
||||
moduleDict = self.makeModuleAsDict(source, self.pathDir, "clsmod.py")
|
||||
PythonClass = moduleDict["clsmod.PythonClass"]
|
||||
self.assertIn(
|
||||
("clsmod.PythonClass._classMachine", PythonClass.load()._classMachine),
|
||||
list(self.findMachinesViaWrapper(PythonClass)),
|
||||
)
|
||||
|
||||
def test_yieldsMachineInNestedClass(self):
|
||||
"""
|
||||
When given a L{twisted.python.modules.PythonAttribute} that refers
|
||||
to a nested class that contains a L{MethodicalMachine} as a
|
||||
class variable, L{findMachinesViaWrapper} yields that machine
|
||||
and its FQPN.
|
||||
"""
|
||||
source = """\
|
||||
from automat import MethodicalMachine
|
||||
|
||||
class PythonClass(object):
|
||||
class NestedClass(object):
|
||||
_classMachine = MethodicalMachine()
|
||||
"""
|
||||
moduleDict = self.makeModuleAsDict(source, self.pathDir, "nestedcls.py")
|
||||
|
||||
PythonClass = moduleDict["nestedcls.PythonClass"]
|
||||
self.assertIn(
|
||||
(
|
||||
"nestedcls.PythonClass.NestedClass._classMachine",
|
||||
PythonClass.load().NestedClass._classMachine,
|
||||
),
|
||||
list(self.findMachinesViaWrapper(PythonClass)),
|
||||
)
|
||||
|
||||
def test_yieldsMachineInModule(self):
|
||||
"""
|
||||
When given a L{twisted.python.modules.PythonModule} that refers to
|
||||
a module that contains a L{MethodicalMachine},
|
||||
L{findMachinesViaWrapper} yields that machine and its FQPN.
|
||||
"""
|
||||
source = """\
|
||||
from automat import MethodicalMachine
|
||||
|
||||
rootMachine = MethodicalMachine()
|
||||
"""
|
||||
module = self.makeModule(source, self.pathDir, "root.py")
|
||||
rootMachine = self.loadModuleAsDict(module)["root.rootMachine"].load()
|
||||
self.assertIn(
|
||||
("root.rootMachine", rootMachine), list(self.findMachinesViaWrapper(module))
|
||||
)
|
||||
|
||||
def test_yieldsMachineInClassInModule(self):
|
||||
"""
|
||||
When given a L{twisted.python.modules.PythonModule} that refers to
|
||||
the original module of a class containing a
|
||||
L{MethodicalMachine}, L{findMachinesViaWrapper} yields that
|
||||
machine and its FQPN.
|
||||
"""
|
||||
source = """\
|
||||
from automat import MethodicalMachine
|
||||
|
||||
class PythonClass(object):
|
||||
_classMachine = MethodicalMachine()
|
||||
"""
|
||||
module = self.makeModule(source, self.pathDir, "clsmod.py")
|
||||
PythonClass = self.loadModuleAsDict(module)["clsmod.PythonClass"].load()
|
||||
self.assertIn(
|
||||
("clsmod.PythonClass._classMachine", PythonClass._classMachine),
|
||||
list(self.findMachinesViaWrapper(module)),
|
||||
)
|
||||
|
||||
def test_yieldsMachineInNestedClassInModule(self):
|
||||
"""
|
||||
When given a L{twisted.python.modules.PythonModule} that refers to
|
||||
the original module of a nested class containing a
|
||||
L{MethodicalMachine}, L{findMachinesViaWrapper} yields that
|
||||
machine and its FQPN.
|
||||
"""
|
||||
source = """\
|
||||
from automat import MethodicalMachine
|
||||
|
||||
class PythonClass(object):
|
||||
class NestedClass(object):
|
||||
_classMachine = MethodicalMachine()
|
||||
"""
|
||||
module = self.makeModule(source, self.pathDir, "nestedcls.py")
|
||||
PythonClass = self.loadModuleAsDict(module)["nestedcls.PythonClass"].load()
|
||||
|
||||
self.assertIn(
|
||||
(
|
||||
"nestedcls.PythonClass.NestedClass._classMachine",
|
||||
PythonClass.NestedClass._classMachine,
|
||||
),
|
||||
list(self.findMachinesViaWrapper(module)),
|
||||
)
|
||||
|
||||
def test_ignoresImportedClass(self):
|
||||
"""
|
||||
When given a L{twisted.python.modules.PythonAttribute} that refers
|
||||
to a class imported from another module, any
|
||||
L{MethodicalMachine}s on that class are ignored.
|
||||
|
||||
This behavior ensures that a machine is only discovered on a
|
||||
class when visiting the module where that class was defined.
|
||||
"""
|
||||
originalSource = """
|
||||
from automat import MethodicalMachine
|
||||
|
||||
class PythonClass(object):
|
||||
_classMachine = MethodicalMachine()
|
||||
"""
|
||||
|
||||
importingSource = """
|
||||
from original import PythonClass
|
||||
"""
|
||||
|
||||
self.makeModule(originalSource, self.pathDir, "original.py")
|
||||
importingModule = self.makeModule(importingSource, self.pathDir, "importing.py")
|
||||
|
||||
self.assertFalse(list(self.findMachinesViaWrapper(importingModule)))
|
||||
|
||||
def test_descendsIntoPackages(self):
|
||||
"""
|
||||
L{findMachinesViaWrapper} descends into packages to discover
|
||||
machines.
|
||||
"""
|
||||
pythonPath = self.PythonPath([self.pathDir])
|
||||
package = self.FilePath(self.pathDir).child("test_package")
|
||||
package.makedirs()
|
||||
package.child("__init__.py").touch()
|
||||
|
||||
source = """
|
||||
from automat import MethodicalMachine
|
||||
|
||||
|
||||
class PythonClass(object):
|
||||
_classMachine = MethodicalMachine()
|
||||
|
||||
|
||||
rootMachine = MethodicalMachine()
|
||||
"""
|
||||
self.makeModule(source, package.path, "module.py")
|
||||
|
||||
test_package = pythonPath["test_package"]
|
||||
machines = sorted(
|
||||
self.findMachinesViaWrapper(test_package), key=operator.itemgetter(0)
|
||||
)
|
||||
|
||||
moduleDict = self.loadModuleAsDict(test_package["module"])
|
||||
rootMachine = moduleDict["test_package.module.rootMachine"].load()
|
||||
PythonClass = moduleDict["test_package.module.PythonClass"].load()
|
||||
|
||||
expectedMachines = sorted(
|
||||
[
|
||||
("test_package.module.rootMachine", rootMachine),
|
||||
(
|
||||
"test_package.module.PythonClass._classMachine",
|
||||
PythonClass._classMachine,
|
||||
),
|
||||
],
|
||||
key=operator.itemgetter(0),
|
||||
)
|
||||
|
||||
self.assertEqual(expectedMachines, machines)
|
||||
|
||||
def test_infiniteLoop(self):
|
||||
"""
|
||||
L{findMachinesViaWrapper} ignores infinite loops.
|
||||
|
||||
Note this test can't fail - it can only run forever!
|
||||
"""
|
||||
source = """
|
||||
class InfiniteLoop(object):
|
||||
pass
|
||||
|
||||
InfiniteLoop.loop = InfiniteLoop
|
||||
"""
|
||||
module = self.makeModule(source, self.pathDir, "loop.py")
|
||||
self.assertFalse(list(self.findMachinesViaWrapper(module)))
|
||||
|
||||
|
||||
@skipIf(not isTwistedInstalled(), "Twisted is not installed.")
|
||||
class WrapFQPNTests(TestCase):
|
||||
"""
|
||||
Tests that ensure L{wrapFQPN} loads the
|
||||
L{twisted.python.modules.PythonModule} or
|
||||
L{twisted.python.modules.PythonAttribute} for a given FQPN.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
from twisted.python.modules import PythonModule, PythonAttribute
|
||||
from .._discover import wrapFQPN, InvalidFQPN, NoModule, NoObject
|
||||
|
||||
self.PythonModule = PythonModule
|
||||
self.PythonAttribute = PythonAttribute
|
||||
self.wrapFQPN = wrapFQPN
|
||||
self.InvalidFQPN = InvalidFQPN
|
||||
self.NoModule = NoModule
|
||||
self.NoObject = NoObject
|
||||
|
||||
def assertModuleWrapperRefersTo(self, moduleWrapper, module):
|
||||
"""
|
||||
Assert that a L{twisted.python.modules.PythonModule} refers to a
|
||||
particular Python module.
|
||||
"""
|
||||
self.assertIsInstance(moduleWrapper, self.PythonModule)
|
||||
self.assertEqual(moduleWrapper.name, module.__name__)
|
||||
self.assertIs(moduleWrapper.load(), module)
|
||||
|
||||
def assertAttributeWrapperRefersTo(self, attributeWrapper, fqpn, obj):
|
||||
"""
|
||||
Assert that a L{twisted.python.modules.PythonAttribute} refers to a
|
||||
particular Python object.
|
||||
"""
|
||||
self.assertIsInstance(attributeWrapper, self.PythonAttribute)
|
||||
self.assertEqual(attributeWrapper.name, fqpn)
|
||||
self.assertIs(attributeWrapper.load(), obj)
|
||||
|
||||
def test_failsWithEmptyFQPN(self):
|
||||
"""
|
||||
L{wrapFQPN} raises L{InvalidFQPN} when given an empty string.
|
||||
"""
|
||||
with self.assertRaises(self.InvalidFQPN):
|
||||
self.wrapFQPN("")
|
||||
|
||||
def test_failsWithBadDotting(self):
|
||||
""" "
|
||||
L{wrapFQPN} raises L{InvalidFQPN} when given a badly-dotted
|
||||
FQPN. (e.g., x..y).
|
||||
"""
|
||||
for bad in (".fails", "fails.", "this..fails"):
|
||||
with self.assertRaises(self.InvalidFQPN):
|
||||
self.wrapFQPN(bad)
|
||||
|
||||
def test_singleModule(self):
|
||||
"""
|
||||
L{wrapFQPN} returns a L{twisted.python.modules.PythonModule}
|
||||
referring to the single module a dotless FQPN describes.
|
||||
"""
|
||||
import os
|
||||
|
||||
moduleWrapper = self.wrapFQPN("os")
|
||||
|
||||
self.assertIsInstance(moduleWrapper, self.PythonModule)
|
||||
self.assertIs(moduleWrapper.load(), os)
|
||||
|
||||
def test_failsWithMissingSingleModuleOrPackage(self):
|
||||
"""
|
||||
L{wrapFQPN} raises L{NoModule} when given a dotless FQPN that does
|
||||
not refer to a module or package.
|
||||
"""
|
||||
with self.assertRaises(self.NoModule):
|
||||
self.wrapFQPN("this is not an acceptable name!")
|
||||
|
||||
def test_singlePackage(self):
|
||||
"""
|
||||
L{wrapFQPN} returns a L{twisted.python.modules.PythonModule}
|
||||
referring to the single package a dotless FQPN describes.
|
||||
"""
|
||||
import xml
|
||||
|
||||
self.assertModuleWrapperRefersTo(self.wrapFQPN("xml"), xml)
|
||||
|
||||
def test_multiplePackages(self):
|
||||
"""
|
||||
L{wrapFQPN} returns a L{twisted.python.modules.PythonModule}
|
||||
referring to the deepest package described by dotted FQPN.
|
||||
"""
|
||||
import xml.etree
|
||||
|
||||
self.assertModuleWrapperRefersTo(self.wrapFQPN("xml.etree"), xml.etree)
|
||||
|
||||
def test_multiplePackagesFinalModule(self):
|
||||
"""
|
||||
L{wrapFQPN} returns a L{twisted.python.modules.PythonModule}
|
||||
referring to the deepest module described by dotted FQPN.
|
||||
"""
|
||||
import xml.etree.ElementTree
|
||||
|
||||
self.assertModuleWrapperRefersTo(
|
||||
self.wrapFQPN("xml.etree.ElementTree"), xml.etree.ElementTree
|
||||
)
|
||||
|
||||
def test_singleModuleObject(self):
|
||||
"""
|
||||
L{wrapFQPN} returns a L{twisted.python.modules.PythonAttribute}
|
||||
referring to the deepest object an FQPN names, traversing one module.
|
||||
"""
|
||||
import os
|
||||
|
||||
self.assertAttributeWrapperRefersTo(
|
||||
self.wrapFQPN("os.path"), "os.path", os.path
|
||||
)
|
||||
|
||||
def test_multiplePackagesObject(self):
|
||||
"""
|
||||
L{wrapFQPN} returns a L{twisted.python.modules.PythonAttribute}
|
||||
referring to the deepest object described by an FQPN,
|
||||
descending through several packages.
|
||||
"""
|
||||
import xml.etree.ElementTree
|
||||
import automat
|
||||
|
||||
for fqpn, obj in [
|
||||
("xml.etree.ElementTree.fromstring", xml.etree.ElementTree.fromstring),
|
||||
("automat.MethodicalMachine.__doc__", automat.MethodicalMachine.__doc__),
|
||||
]:
|
||||
self.assertAttributeWrapperRefersTo(self.wrapFQPN(fqpn), fqpn, obj)
|
||||
|
||||
def test_failsWithMultiplePackagesMissingModuleOrPackage(self):
|
||||
"""
|
||||
L{wrapFQPN} raises L{NoObject} when given an FQPN that contains a
|
||||
missing attribute, module, or package.
|
||||
"""
|
||||
for bad in ("xml.etree.nope!", "xml.etree.nope!.but.the.rest.is.believable"):
|
||||
with self.assertRaises(self.NoObject):
|
||||
self.wrapFQPN(bad)
|
||||
|
||||
|
||||
@skipIf(not isTwistedInstalled(), "Twisted is not installed.")
|
||||
class FindMachinesIntegrationTests(_WritesPythonModules):
|
||||
"""
|
||||
Integration tests to check that L{findMachines} yields all
|
||||
machines discoverable at or below an FQPN.
|
||||
"""
|
||||
|
||||
SOURCE = """
|
||||
from automat import MethodicalMachine
|
||||
|
||||
class PythonClass(object):
|
||||
_machine = MethodicalMachine()
|
||||
ignored = "i am ignored"
|
||||
|
||||
rootLevel = MethodicalMachine()
|
||||
|
||||
ignored = "i am ignored"
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(FindMachinesIntegrationTests, self).setUp()
|
||||
from .._discover import findMachines
|
||||
|
||||
self.findMachines = findMachines
|
||||
|
||||
packageDir = self.FilePath(self.pathDir).child("test_package")
|
||||
packageDir.makedirs()
|
||||
self.pythonPath = self.PythonPath([self.pathDir])
|
||||
self.writeSourceInto(self.SOURCE, packageDir.path, "__init__.py")
|
||||
|
||||
subPackageDir = packageDir.child("subpackage")
|
||||
subPackageDir.makedirs()
|
||||
subPackageDir.child("__init__.py").touch()
|
||||
|
||||
self.makeModule(self.SOURCE, subPackageDir.path, "module.py")
|
||||
|
||||
self.packageDict = self.loadModuleAsDict(self.pythonPath["test_package"])
|
||||
self.moduleDict = self.loadModuleAsDict(
|
||||
self.pythonPath["test_package"]["subpackage"]["module"]
|
||||
)
|
||||
|
||||
def test_discoverAll(self):
|
||||
"""
|
||||
Given a top-level package FQPN, L{findMachines} discovers all
|
||||
L{MethodicalMachine} instances in and below it.
|
||||
"""
|
||||
machines = sorted(self.findMachines("test_package"), key=operator.itemgetter(0))
|
||||
|
||||
tpRootLevel = self.packageDict["test_package.rootLevel"].load()
|
||||
tpPythonClass = self.packageDict["test_package.PythonClass"].load()
|
||||
|
||||
mRLAttr = self.moduleDict["test_package.subpackage.module.rootLevel"]
|
||||
mRootLevel = mRLAttr.load()
|
||||
mPCAttr = self.moduleDict["test_package.subpackage.module.PythonClass"]
|
||||
mPythonClass = mPCAttr.load()
|
||||
|
||||
expectedMachines = sorted(
|
||||
[
|
||||
("test_package.rootLevel", tpRootLevel),
|
||||
("test_package.PythonClass._machine", tpPythonClass._machine),
|
||||
("test_package.subpackage.module.rootLevel", mRootLevel),
|
||||
(
|
||||
"test_package.subpackage.module.PythonClass._machine",
|
||||
mPythonClass._machine,
|
||||
),
|
||||
],
|
||||
key=operator.itemgetter(0),
|
||||
)
|
||||
|
||||
self.assertEqual(expectedMachines, machines)
|
||||
Reference in New Issue
Block a user