fixtures-0.3.14/0000775000175000017500000000000012203313314014564 5ustar robertcrobertc00000000000000fixtures-0.3.14/lib/0000775000175000017500000000000012203313314015332 5ustar robertcrobertc00000000000000fixtures-0.3.14/lib/fixtures/0000775000175000017500000000000012203313314017203 5ustar robertcrobertc00000000000000fixtures-0.3.14/lib/fixtures/_fixtures/0000775000175000017500000000000012203313314021213 5ustar robertcrobertc00000000000000fixtures-0.3.14/lib/fixtures/_fixtures/__init__.py0000664000175000017500000000375712165105545023354 0ustar robertcrobertc00000000000000# fixtures: Fixtures with cleanups for testing and convenience. # # Copyright (c) 2010, 2011, Robert Collins # # Licensed under either the Apache License, Version 2.0 or the BSD 3-clause # license at the users choice. A copy of both licenses are available in the # project source as Apache-2.0 and BSD. You may not use this file except in # compliance with one of these two licences. # # Unless required by applicable law or agreed to in writing, software # distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # license you chose for the specific language governing permissions and # limitations under that license. """Included fixtures.""" __all__ = [ 'ByteStream', 'DetailStream', 'EnvironmentVariable', 'EnvironmentVariableFixture', 'FakeLogger', 'FakePopen', 'LoggerFixture', 'LogHandler', 'MonkeyPatch', 'NestedTempfile', 'PackagePathEntry', 'PopenFixture', 'PythonPackage', 'PythonPathEntry', 'StringStream', 'TempDir', 'TempHomeDir', 'Timeout', 'TimeoutException', ] from fixtures._fixtures.environ import ( EnvironmentVariable, EnvironmentVariableFixture, ) from fixtures._fixtures.logger import ( FakeLogger, LoggerFixture, LogHandler, ) from fixtures._fixtures.monkeypatch import MonkeyPatch from fixtures._fixtures.popen import ( FakePopen, PopenFixture, ) from fixtures._fixtures.packagepath import PackagePathEntry from fixtures._fixtures.pythonpackage import PythonPackage from fixtures._fixtures.pythonpath import PythonPathEntry from fixtures._fixtures.streams import ( ByteStream, DetailStream, StringStream, ) from fixtures._fixtures.tempdir import ( NestedTempfile, TempDir, ) from fixtures._fixtures.temphomedir import ( TempHomeDir, ) from fixtures._fixtures.timeout import ( Timeout, TimeoutException, ) fixtures-0.3.14/lib/fixtures/_fixtures/environ.py0000664000175000017500000000376312165105545023272 0ustar robertcrobertc00000000000000# fixtures: Fixtures with cleanups for testing and convenience. # # Copyright (c) 2010, 2011, Robert Collins # # Licensed under either the Apache License, Version 2.0 or the BSD 3-clause # license at the users choice. A copy of both licenses are available in the # project source as Apache-2.0 and BSD. You may not use this file except in # compliance with one of these two licences. # # Unless required by applicable law or agreed to in writing, software # distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # license you chose for the specific language governing permissions and # limitations under that license. __all__ = [ 'EnvironmentVariable', 'EnvironmentVariableFixture' ] import os from fixtures import Fixture class EnvironmentVariable(Fixture): """Isolate a specific environment variable.""" def __init__(self, varname, newvalue=None): """Create an EnvironmentVariable fixture. :param varname: the name of the variable to isolate. :param newvalue: A value to set the variable to. If None, the variable will be deleted. During setup the variable will be deleted or assigned the requested value, and this will be restored in cleanUp. """ super(EnvironmentVariable, self).__init__() self.varname = varname self.newvalue = newvalue def setUp(self): super(EnvironmentVariable, self).setUp() varname = self.varname orig_value = os.environ.get(varname) if orig_value is not None: self.addCleanup(os.environ.__setitem__, varname, orig_value) del os.environ[varname] else: self.addCleanup(os.environ.pop, varname, '') if self.newvalue is not None: os.environ[varname] = self.newvalue else: os.environ.pop(varname, '') EnvironmentVariableFixture = EnvironmentVariable fixtures-0.3.14/lib/fixtures/_fixtures/logger.py0000664000175000017500000000754212165105545023070 0ustar robertcrobertc00000000000000# fixtures: Fixtures with cleanups for testing and convenience. # # Copyright (c) 2011, Robert Collins # # Licensed under either the Apache License, Version 2.0 or the BSD 3-clause # license at the users choice. A copy of both licenses are available in the # project source as Apache-2.0 and BSD. You may not use this file except in # compliance with one of these two licences. # # Unless required by applicable law or agreed to in writing, software # distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # license you chose for the specific language governing permissions and # limitations under that license. from logging import StreamHandler, getLogger, INFO, Formatter from testtools.compat import _u from fixtures import Fixture from fixtures._fixtures.streams import StringStream __all__ = [ 'FakeLogger', 'LoggerFixture', 'LogHandler', ] class LogHandler(Fixture): """Replace a logger's handlers.""" def __init__(self, handler, name="", level=None, nuke_handlers=True): """Create a LogHandler fixture. :param handler: The handler to replace other handlers with. If nuke_handlers is False, then added as an extra handler. :param name: The name of the logger to replace. Defaults to "". :param level: The log level to set, defaults to not changing the level. :param nuke_handlers: If True remove all existing handles (prevents existing messages going to e.g. stdout). Defaults to True. """ super(LogHandler, self).__init__() self.handler = handler self._name = name self._level = level self._nuke_handlers = nuke_handlers def setUp(self): super(LogHandler, self).setUp() logger = getLogger(self._name) if self._level: self.addCleanup(logger.setLevel, logger.level) logger.setLevel(self._level) if self._nuke_handlers: for handler in reversed(logger.handlers): self.addCleanup(logger.addHandler, handler) logger.removeHandler(handler) try: logger.addHandler(self.handler) finally: self.addCleanup(logger.removeHandler, self.handler) class FakeLogger(Fixture): """Replace a logger and capture its output.""" def __init__(self, name="", level=INFO, format=None, nuke_handlers=True): """Create a FakeLogger fixture. :param name: The name of the logger to replace. Defaults to "". :param level: The log level to set, defaults to INFO. :param format: Logging format to use. Defaults to capturing supplied messages verbatim. :param nuke_handlers: If True remove all existing handles (prevents existing messages going to e.g. stdout). Defaults to True. Example: def test_log(self) fixture = self.useFixture(LoggerFixture()) logging.info('message') self.assertEqual('message', fixture.output) """ super(FakeLogger, self).__init__() self._name = name self._level = level self._format = format self._nuke_handlers = nuke_handlers def setUp(self): super(FakeLogger, self).setUp() name = _u("pythonlogging:'%s'") % self._name output = self.useFixture(StringStream(name)).stream self._output = output handler = StreamHandler(output) if self._format: handler.setFormatter(Formatter(self._format)) self.useFixture( LogHandler(handler, name=self._name, level=self._level, nuke_handlers=self._nuke_handlers)) @property def output(self): self._output.seek(0) return self._output.read() LoggerFixture = FakeLogger fixtures-0.3.14/lib/fixtures/_fixtures/monkeypatch.py0000664000175000017500000000507412165105545024131 0ustar robertcrobertc00000000000000# fixtures: Fixtures with cleanups for testing and convenience. # # Copyright (c) 2010, Robert Collins # # Licensed under either the Apache License, Version 2.0 or the BSD 3-clause # license at the users choice. A copy of both licenses are available in the # project source as Apache-2.0 and BSD. You may not use this file except in # compliance with one of these two licences. # # Unless required by applicable law or agreed to in writing, software # distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # license you chose for the specific language governing permissions and # limitations under that license. __all__ = [ 'MonkeyPatch' ] from fixtures import Fixture class MonkeyPatch(Fixture): """Replace or delete an attribute.""" delete = object() def __init__(self, name, new_value=None): """Create a MonkeyPatch. :param name: The fully qualified object name to override. :param new_value: A value to set the name to. If set to MonkeyPatch.delete the attribute will be deleted. During setup the name will be deleted or assigned the requested value, and this will be restored in cleanUp. """ Fixture.__init__(self) self.name = name self.new_value = new_value def setUp(self): Fixture.setUp(self) location, attribute = self.name.rsplit('.', 1) # Import, swallowing all errors as any element of location may be # a class or some such thing. try: __import__(location, {}, {}) except ImportError: pass components = location.split('.') current = __import__(components[0], {}, {}) for component in components[1:]: current = getattr(current, component) sentinel = object() old_value = getattr(current, attribute, sentinel) if self.new_value is self.delete: if old_value is not sentinel: delattr(current, attribute) else: setattr(current, attribute, self.new_value) if old_value is sentinel: self.addCleanup(self._safe_delete, current, attribute) else: self.addCleanup(setattr, current, attribute, old_value) def _safe_delete(self, obj, attribute): """Delete obj.attribute handling the case where its missing.""" sentinel = object() if getattr(obj, attribute, sentinel) is not sentinel: delattr(obj, attribute) fixtures-0.3.14/lib/fixtures/_fixtures/packagepath.py0000664000175000017500000000305212165105545024051 0ustar robertcrobertc00000000000000# fixtures: Fixtures with cleanups for testing and convenience. # # Copyright (c) 2011, Robert Collins # # Licensed under either the Apache License, Version 2.0 or the BSD 3-clause # license at the users choice. A copy of both licenses are available in the # project source as Apache-2.0 and BSD. You may not use this file except in # compliance with one of these two licences. # # Unless required by applicable law or agreed to in writing, software # distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # license you chose for the specific language governing permissions and # limitations under that license. __all__ = [ 'PackagePathEntry' ] import sys from fixtures import Fixture class PackagePathEntry(Fixture): """Add a path to the path of a python package. The python package needs to be already imported. If this new path is already in the packages __path__ list then the __path__ list will not be altered. """ def __init__(self, packagename, directory): """Create a PackagePathEntry. :param directory: The directory to add to the package.__path__. """ self.packagename = packagename self.directory = directory def setUp(self): Fixture.setUp(self) path = sys.modules[self.packagename].__path__ if self.directory in path: return self.addCleanup(path.remove, self.directory) path.append(self.directory) fixtures-0.3.14/lib/fixtures/_fixtures/popen.py0000664000175000017500000000774712203312575022735 0ustar robertcrobertc00000000000000# fixtures: Fixtures with cleanups for testing and convenience. # # Copyright (c) 2010, 2011, Robert Collins # # Licensed under either the Apache License, Version 2.0 or the BSD 3-clause # license at the users choice. A copy of both licenses are available in the # project source as Apache-2.0 and BSD. You may not use this file except in # compliance with one of these two licences. # # Unless required by applicable law or agreed to in writing, software # distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # license you chose for the specific language governing permissions and # limitations under that license. __all__ = [ 'FakePopen', 'PopenFixture' ] import random import subprocess from fixtures import Fixture class FakeProcess(object): """A test double process, roughly meeting subprocess.Popen's contract.""" def __init__(self, args, info): self._args = args self.stdin = info.get('stdin') self.stdout = info.get('stdout') self.stderr = info.get('stderr') self.pid = random.randint(0, 65536) self._returncode = info.get('returncode', 0) self.returncode = None def communicate(self): self.returncode = self._returncode if self.stdout: out = self.stdout.getvalue() else: out = '' if self.stderr: err = self.stderr.getvalue() else: err = '' return out, err def wait(self): if self.returncode is None: self.communicate() return self.returncode class FakePopen(Fixture): """Replace subprocess.Popen. Primarily useful for testing, this fixture replaces subprocess.Popen with a test double. :ivar procs: A list of the processes created by the fixture. """ _unpassed = object() def __init__(self, get_info=lambda _:{}): """Create a PopenFixture :param get_info: Optional callback to control the behaviour of the created process. This callback takes a kwargs dict for the Popen call, and should return a dict with any desired attributes. Only parameters that are supplied to the Popen call are in the dict, making it possible to detect the difference between 'passed with a default value' and 'not passed at all'. e.g. def get_info(proc_args): self.assertEqual(subprocess.PIPE, proc_args['stdin']) return {'stdin': StringIO('foobar')} The default behaviour if no get_info is supplied is for the return process to have returncode of None, empty streams and a random pid. """ super(FakePopen, self).__init__() self.get_info = get_info def setUp(self): super(FakePopen, self).setUp() self.addCleanup(setattr, subprocess, 'Popen', subprocess.Popen) subprocess.Popen = self self.procs = [] # The method has the correct signature so we error appropriately if called # wrongly. def __call__(self, args, bufsize=_unpassed, executable=_unpassed, stdin=_unpassed, stdout=_unpassed, stderr=_unpassed, preexec_fn=_unpassed, close_fds=_unpassed, shell=_unpassed, cwd=_unpassed, env=_unpassed, universal_newlines=_unpassed, startupinfo=_unpassed, creationflags=_unpassed): proc_args = dict(args=args) local = locals() for param in [ "bufsize", "executable", "stdin", "stdout", "stderr", "preexec_fn", "close_fds", "shell", "cwd", "env", "universal_newlines", "startupinfo", "creationflags"]: if local[param] is not FakePopen._unpassed: proc_args[param] = local[param] proc_info = self.get_info(proc_args) result = FakeProcess(proc_args, proc_info) self.procs.append(result) return result PopenFixture = FakePopen fixtures-0.3.14/lib/fixtures/_fixtures/pythonpackage.py0000664000175000017500000000457212165105545024446 0ustar robertcrobertc00000000000000# fixtures: Fixtures with cleanups for testing and convenience. # # Copyright (c) 2010, Robert Collins # # Licensed under either the Apache License, Version 2.0 or the BSD 3-clause # license at the users choice. A copy of both licenses are available in the # project source as Apache-2.0 and BSD. You may not use this file except in # compliance with one of these two licences. # # Unless required by applicable law or agreed to in writing, software # distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # license you chose for the specific language governing permissions and # limitations under that license. __all__ = [ 'PythonPackage' ] import os.path from fixtures import Fixture from fixtures._fixtures.tempdir import TempDir class PythonPackage(Fixture): """Create a temporary Python package. :ivar base: The path of the directory containing the module. E.g. for a module 'foo', the path base + '/foo/__init__.py' would be the file path for the module. """ def __init__(self, packagename, modulelist, init=True): """Create a PythonPackage. :param packagename: The name of the package to create - e.g. 'toplevel.subpackage.' :param modulelist: List of modules to include in the package. Each module should be a tuple with the filename and content it should have. :param init: If false, do not create a missing __init__.py. When True, if modulelist does not include an __init__.py, an empty one is created. """ self.packagename = packagename self.modulelist = modulelist self.init = init def setUp(self): Fixture.setUp(self) self.base = self.useFixture(TempDir()).path base = self.base root = os.path.join(base, self.packagename) os.mkdir(root) init_seen = not self.init for modulename, contents in self.modulelist: stream = open(os.path.join(root, modulename), 'wb') try: stream.write(contents) finally: stream.close() if modulename == '__init__.py': init_seen = True if not init_seen: open(os.path.join(root, '__init__.py'), 'wb').close() fixtures-0.3.14/lib/fixtures/_fixtures/pythonpath.py0000664000175000017500000000251712165105545024004 0ustar robertcrobertc00000000000000# fixtures: Fixtures with cleanups for testing and convenience. # # Copyright (c) 2011, Robert Collins # # Licensed under either the Apache License, Version 2.0 or the BSD 3-clause # license at the users choice. A copy of both licenses are available in the # project source as Apache-2.0 and BSD. You may not use this file except in # compliance with one of these two licences. # # Unless required by applicable law or agreed to in writing, software # distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # license you chose for the specific language governing permissions and # limitations under that license. __all__ = [ 'PythonPathEntry' ] import sys from fixtures import Fixture class PythonPathEntry(Fixture): """Add a path to sys.path. If the path is already in sys.path, sys.path will not be altered. """ def __init__(self, directory): """Create a PythonPathEntry. :param directory: The directory to add to sys.path. """ self.directory = directory def setUp(self): Fixture.setUp(self) if self.directory in sys.path: return self.addCleanup(sys.path.remove, self.directory) sys.path.append(self.directory) fixtures-0.3.14/lib/fixtures/_fixtures/streams.py0000664000175000017500000000600012165105545023253 0ustar robertcrobertc00000000000000# fixtures: Fixtures with cleanups for testing and convenience. # # Copyright (c) 2012, Robert Collins # # Licensed under either the Apache License, Version 2.0 or the BSD 3-clause # license at the users choice. A copy of both licenses are available in the # project source as Apache-2.0 and BSD. You may not use this file except in # compliance with one of these two licences. # # Unless required by applicable law or agreed to in writing, software # distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # license you chose for the specific language governing permissions and # limitations under that license. __all__ = [ 'ByteStream', 'DetailStream', 'StringStream', ] import io import sys from fixtures import Fixture import testtools class Stream(Fixture): """Expose a file-like object as a detail. :attr stream: The file-like object. """ def __init__(self, detail_name, stream_factory): """Create a ByteStream. :param detail_name: Use this as the name of the stream. :param stream_factory: Called to construct a pair of streams: (write_stream, content_stream). """ self._detail_name = detail_name self._stream_factory = stream_factory def setUp(self): super(Stream, self).setUp() write_stream, read_stream = self._stream_factory() self.stream = write_stream self.addDetail(self._detail_name, testtools.content.content_from_stream(read_stream, seek_offset=0)) def _byte_stream_factory(): result = io.BytesIO() return (result, result) def ByteStream(detail_name): """Provide a file-like object that accepts bytes and expose as a detail. :param detail_name: The name of the detail. :return: A fixture which has an attribute `stream` containing the file-like object. """ return Stream(detail_name, _byte_stream_factory) def _string_stream_factory(): lower = io.BytesIO() upper = io.TextIOWrapper(lower, encoding="utf8") # See http://bugs.python.org/issue7955 upper._CHUNK_SIZE = 1 # In theory, this is sufficient and correct, but on Python2, # upper.write(_b('foo")) will whinge louadly. if sys.version_info[0] < 3: upper_write = upper.write def safe_write(str_or_bytes): if type(str_or_bytes) is str: str_or_bytes = str_or_bytes.decode('utf8') return upper_write(str_or_bytes) upper.write = safe_write return upper, lower def StringStream(detail_name): """Provide a file-like object that accepts strings and expose as a detail. :param detail_name: The name of the detail. :return: A fixture which has an attribute `stream` containing the file-like object. """ return Stream(detail_name, _string_stream_factory) def DetailStream(detail_name): """Deprecated alias for ByteStream.""" return ByteStream(detail_name) fixtures-0.3.14/lib/fixtures/_fixtures/tempdir.py0000664000175000017500000000445412165105545023254 0ustar robertcrobertc00000000000000# fixtures: Fixtures with cleanups for testing and convenience. # # Copyright (c) 2010, Robert Collins # # Licensed under either the Apache License, Version 2.0 or the BSD 3-clause # license at the users choice. A copy of both licenses are available in the # project source as Apache-2.0 and BSD. You may not use this file except in # compliance with one of these two licences. # # Unless required by applicable law or agreed to in writing, software # distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # license you chose for the specific language governing permissions and # limitations under that license. __all__ = [ 'NestedTempfile', 'TempDir', ] import os import shutil import tempfile import fixtures class TempDir(fixtures.Fixture): """Create a temporary directory. :ivar path: The path of the temporary directory. """ def __init__(self, rootdir=None): """Create a TempDir. :param rootdir: If supplied force the temporary directory to be a child of rootdir. """ self.rootdir = rootdir def setUp(self): super(TempDir, self).setUp() self.path = tempfile.mkdtemp(dir=self.rootdir) self.addCleanup(shutil.rmtree, self.path, ignore_errors=True) def join(self, *children): """Return an absolute path, given one relative to this ``TempDir``. WARNING: This does not do any checking of ``children`` to make sure they aren't walking up the tree using path segments like '..' or '/usr'. Use at your own risk. """ return os.path.abspath(os.path.join(self.path, *children)) class NestedTempfile(fixtures.Fixture): """Nest all temporary files and directories inside another directory. This temporarily monkey-patches the default location that the `tempfile` package creates temporary files and directories in to be a new temporary directory. This new temporary directory is removed when the fixture is torn down. """ def setUp(self): super(NestedTempfile, self).setUp() tempdir = self.useFixture(TempDir()).path patch = fixtures.MonkeyPatch("tempfile.tempdir", tempdir) self.useFixture(patch) fixtures-0.3.14/lib/fixtures/_fixtures/temphomedir.py0000664000175000017500000000211212165105545024112 0ustar robertcrobertc00000000000000# fixtures: Fixtures with cleanups for testing and convenience. # # Copyright (c) 2010, Canonical Ltd. # # Licensed under either the Apache License, Version 2.0 or the BSD 3-clause # license at the users choice. A copy of both licenses are available in the # project source as Apache-2.0 and BSD. You may not use this file except in # compliance with one of these two licences. # # Unless required by applicable law or agreed to in writing, software # distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # license you chose for the specific language governing permissions and # limitations under that license. __all__ = [ 'TempHomeDir', ] import fixtures from fixtures._fixtures.tempdir import TempDir class TempHomeDir(TempDir): """Create a temporary directory and set it as $HOME :ivar path: the path of the temporary directory. """ def setUp(self): super(TempHomeDir, self).setUp() self.useFixture(fixtures.EnvironmentVariable("HOME", self.path)) fixtures-0.3.14/lib/fixtures/_fixtures/timeout.py0000664000175000017500000000445412165105545023276 0ustar robertcrobertc00000000000000# fixtures: Fixtures with cleanups for testing and convenience. # # Copyright (C) 2011, Martin Pool # # Licensed under either the Apache License, Version 2.0 or the BSD 3-clause # license at the users choice. A copy of both licenses are available in the # project source as Apache-2.0 and BSD. You may not use this file except in # compliance with one of these two licences. # # Unless required by applicable law or agreed to in writing, software # distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # license you chose for the specific language governing permissions and # limitations under that license. """Timeout fixture.""" import signal import fixtures __all__ = [ 'Timeout', 'TimeoutException', ] class TimeoutException(Exception): """Timeout expired""" class Timeout(fixtures.Fixture): """Fixture that aborts the contained code after a number of seconds. The interrupt can be either gentle, in which case TimeoutException is raised, or not gentle, in which case the process will typically be aborted by SIGALRM. Cautions: * This has no effect on Windows. * Only one Timeout can be used at any time per process. """ def __init__(self, timeout_secs, gentle): self.timeout_secs = timeout_secs self.alarm_fn = getattr(signal, 'alarm', None) self.gentle = gentle def signal_handler(self, signum, frame): raise TimeoutException() def setUp(self): super(Timeout, self).setUp() if self.alarm_fn is None: return # Can't run on Windows if self.gentle: # Install a handler for SIGARLM so we can raise an exception rather # than the default handler executing, which kills the process. old_handler = signal.signal(signal.SIGALRM, self.signal_handler) # We add the slarm cleanup before the cleanup for the signal handler, # otherwise there is a race condition where the signal handler is # cleaned up but the alarm still fires. self.addCleanup(lambda: self.alarm_fn(0)) self.alarm_fn(self.timeout_secs) if self.gentle: self.addCleanup(lambda: signal.signal(signal.SIGALRM, old_handler)) fixtures-0.3.14/lib/fixtures/tests/0000775000175000017500000000000012203313314020345 5ustar robertcrobertc00000000000000fixtures-0.3.14/lib/fixtures/tests/_fixtures/0000775000175000017500000000000012203313314022355 5ustar robertcrobertc00000000000000fixtures-0.3.14/lib/fixtures/tests/_fixtures/__init__.py0000664000175000017500000000236512165105545024510 0ustar robertcrobertc00000000000000# fixtures: Fixtures with cleanups for testing and convenience. # # Copyright (c) 2010, Robert Collins # # Licensed under either the Apache License, Version 2.0 or the BSD 3-clause # license at the users choice. A copy of both licenses are available in the # project source as Apache-2.0 and BSD. You may not use this file except in # compliance with one of these two licences. # # Unless required by applicable law or agreed to in writing, software # distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # license you chose for the specific language governing permissions and # limitations under that license. def load_tests(loader, standard_tests, pattern): test_modules = [ 'environ', 'logger', 'monkeypatch', 'packagepath', 'popen', 'pythonpackage', 'pythonpath', 'streams', 'tempdir', 'temphomedir', 'timeout', ] prefix = "fixtures.tests._fixtures.test_" test_mod_names = [prefix + test_module for test_module in test_modules] standard_tests.addTests(loader.loadTestsFromNames(test_mod_names)) return standard_tests fixtures-0.3.14/lib/fixtures/tests/_fixtures/test_environ.py0000664000175000017500000000607412165105545025471 0ustar robertcrobertc00000000000000# fixtures: Fixtures with cleanups for testing and convenience. # # Copyright (c) 2010, 2011, Robert Collins # # Licensed under either the Apache License, Version 2.0 or the BSD 3-clause # license at the users choice. A copy of both licenses are available in the # project source as Apache-2.0 and BSD. You may not use this file except in # compliance with one of these two licences. # # Unless required by applicable law or agreed to in writing, software # distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # license you chose for the specific language governing permissions and # limitations under that license. import os import testtools from fixtures import EnvironmentVariable, TestWithFixtures class TestEnvironmentVariable(testtools.TestCase, TestWithFixtures): def test_setup_ignores_missing(self): fixture = EnvironmentVariable('FIXTURES_TEST_VAR') os.environ.pop('FIXTURES_TEST_VAR', '') self.useFixture(fixture) self.assertEqual(None, os.environ.get('FIXTURES_TEST_VAR')) def test_setup_sets_when_missing(self): fixture = EnvironmentVariable('FIXTURES_TEST_VAR', 'bar') os.environ.pop('FIXTURES_TEST_VAR', '') self.useFixture(fixture) self.assertEqual('bar', os.environ.get('FIXTURES_TEST_VAR')) def test_setup_deletes(self): fixture = EnvironmentVariable('FIXTURES_TEST_VAR') os.environ['FIXTURES_TEST_VAR'] = 'foo' self.useFixture(fixture) self.assertEqual(None, os.environ.get('FIXTURES_TEST_VAR')) def test_setup_overrides(self): fixture = EnvironmentVariable('FIXTURES_TEST_VAR', 'bar') os.environ['FIXTURES_TEST_VAR'] = 'foo' self.useFixture(fixture) self.assertEqual('bar', os.environ.get('FIXTURES_TEST_VAR')) def test_cleanup_deletes_when_missing(self): fixture = EnvironmentVariable('FIXTURES_TEST_VAR') os.environ.pop('FIXTURES_TEST_VAR', '') with fixture: os.environ['FIXTURES_TEST_VAR'] = 'foo' self.assertEqual(None, os.environ.get('FIXTURES_TEST_VAR')) def test_cleanup_deletes_when_set(self): fixture = EnvironmentVariable('FIXTURES_TEST_VAR', 'bar') os.environ.pop('FIXTURES_TEST_VAR', '') with fixture: os.environ['FIXTURES_TEST_VAR'] = 'foo' self.assertEqual(None, os.environ.get('FIXTURES_TEST_VAR')) def test_cleanup_restores_when_missing(self): fixture = EnvironmentVariable('FIXTURES_TEST_VAR') os.environ['FIXTURES_TEST_VAR'] = 'bar' with fixture: os.environ.pop('FIXTURES_TEST_VAR', '') self.assertEqual('bar', os.environ.get('FIXTURES_TEST_VAR')) def test_cleanup_restores_when_set(self): fixture = EnvironmentVariable('FIXTURES_TEST_VAR') os.environ['FIXTURES_TEST_VAR'] = 'bar' with fixture: os.environ['FIXTURES_TEST_VAR'] = 'quux' self.assertEqual('bar', os.environ.get('FIXTURES_TEST_VAR')) fixtures-0.3.14/lib/fixtures/tests/_fixtures/test_logger.py0000664000175000017500000001442212165105545025264 0ustar robertcrobertc00000000000000# fixtures: Fixtures with cleanups for testing and convenience. # # Copyright (c) 2011, Robert Collins # # Licensed under either the Apache License, Version 2.0 or the BSD 3-clause # license at the users choice. A copy of both licenses are available in the # project source as Apache-2.0 and BSD. You may not use this file except in # compliance with one of these two licences. # # Unless required by applicable law or agreed to in writing, software # distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # license you chose for the specific language governing permissions and # limitations under that license. import logging from testtools import TestCase from testtools.compat import StringIO from fixtures import ( FakeLogger, LogHandler, TestWithFixtures, ) class FakeLoggerTest(TestCase, TestWithFixtures): def setUp(self): super(FakeLoggerTest, self).setUp() self.logger = logging.getLogger() self.addCleanup(self.removeHandlers, self.logger) def removeHandlers(self, logger): for handler in logger.handlers: logger.removeHandler(handler) def test_output_property_has_output(self): fixture = self.useFixture(FakeLogger()) logging.info("some message") self.assertEqual("some message\n", fixture.output) def test_replace_and_restore_handlers(self): stream = StringIO() logger = logging.getLogger() logger.addHandler(logging.StreamHandler(stream)) logger.setLevel(logging.INFO) logging.info("one") fixture = FakeLogger() with fixture: logging.info("two") logging.info("three") self.assertEqual("two\n", fixture.output) self.assertEqual("one\nthree\n", stream.getvalue()) def test_preserving_existing_handlers(self): stream = StringIO() self.logger.addHandler(logging.StreamHandler(stream)) self.logger.setLevel(logging.INFO) fixture = FakeLogger(nuke_handlers=False) with fixture: logging.info("message") self.assertEqual("message\n", fixture.output) self.assertEqual("message\n", stream.getvalue()) def test_logging_level_restored(self): self.logger.setLevel(logging.DEBUG) fixture = FakeLogger(level=logging.WARNING) with fixture: # The fixture won't capture this, because the DEBUG level # is lower than the WARNING one logging.debug("debug message") self.assertEqual(logging.WARNING, self.logger.level) self.assertEqual("", fixture.output) self.assertEqual(logging.DEBUG, self.logger.level) def test_custom_format(self): fixture = FakeLogger(format="%(module)s") self.useFixture(fixture) logging.info("message") self.assertEqual("test_logger\n", fixture.output) def test_logging_output_included_in_details(self): fixture = FakeLogger() detail_name = "pythonlogging:''" with fixture: content = fixture.getDetails()[detail_name] # Output after getDetails is called is included. logging.info('some message') self.assertEqual("some message\n", content.as_text()) # The old content object returns the old usage after cleanUp (not # strictly needed but convenient). Note that no guarantee is made that # it will work after setUp is called again. [It does on Python 2.x, not # on 3.x] self.assertEqual("some message\n", content.as_text()) with fixture: # A new one returns new output: self.assertEqual("", fixture.getDetails()[detail_name].as_text()) # The original content object may either fail, or return the old # content (it must not have been reset..). try: self.assertEqual("some message\n", content.as_text()) except AssertionError: raise except: pass class LogHandlerTest(TestCase, TestWithFixtures): class CustomHandler(logging.Handler): def __init__(self, *args, **kwargs): """Create the instance, and add a records attribute.""" logging.Handler.__init__(self, *args, **kwargs) self.msgs = [] def emit(self, record): self.msgs.append(record.msg) def setUp(self): super(LogHandlerTest, self).setUp() self.logger = logging.getLogger() self.addCleanup(self.removeHandlers, self.logger) def removeHandlers(self, logger): for handler in logger.handlers: logger.removeHandler(handler) def test_captures_logging(self): fixture = self.useFixture(LogHandler(self.CustomHandler())) logging.info("some message") self.assertEqual(["some message"], fixture.handler.msgs) def test_replace_and_restore_handlers(self): stream = StringIO() logger = logging.getLogger() logger.addHandler(logging.StreamHandler(stream)) logger.setLevel(logging.INFO) logging.info("one") fixture = LogHandler(self.CustomHandler()) with fixture: logging.info("two") logging.info("three") self.assertEqual(["two"], fixture.handler.msgs) self.assertEqual("one\nthree\n", stream.getvalue()) def test_preserving_existing_handlers(self): stream = StringIO() self.logger.addHandler(logging.StreamHandler(stream)) self.logger.setLevel(logging.INFO) fixture = LogHandler(self.CustomHandler(), nuke_handlers=False) with fixture: logging.info("message") self.assertEqual(["message"], fixture.handler.msgs) self.assertEqual("message\n", stream.getvalue()) def test_logging_level_restored(self): self.logger.setLevel(logging.DEBUG) fixture = LogHandler(self.CustomHandler(), level=logging.WARNING) with fixture: # The fixture won't capture this, because the DEBUG level # is lower than the WARNING one logging.debug("debug message") self.assertEqual(logging.WARNING, self.logger.level) self.assertEqual([], fixture.handler.msgs) self.assertEqual(logging.DEBUG, self.logger.level) fixtures-0.3.14/lib/fixtures/tests/_fixtures/test_monkeypatch.py0000664000175000017500000000461112165105545026326 0ustar robertcrobertc00000000000000# fixtures: Fixtures with cleanups for testing and convenience. # # Copyright (c) 2010, Robert Collins # # Licensed under either the Apache License, Version 2.0 or the BSD 3-clause # license at the users choice. A copy of both licenses are available in the # project source as Apache-2.0 and BSD. You may not use this file except in # compliance with one of these two licences. # # Unless required by applicable law or agreed to in writing, software # distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # license you chose for the specific language governing permissions and # limitations under that license. import testtools from fixtures import MonkeyPatch, TestWithFixtures reference = 23 class TestMonkeyPatch(testtools.TestCase, TestWithFixtures): def test_patch_and_restore(self): fixture = MonkeyPatch( 'fixtures.tests._fixtures.test_monkeypatch.reference', 45) self.assertEqual(23, reference) fixture.setUp() try: self.assertEqual(45, reference) finally: fixture.cleanUp() self.assertEqual(23, reference) def test_patch_missing_attribute(self): fixture = MonkeyPatch( 'fixtures.tests._fixtures.test_monkeypatch.new_attr', True) self.assertFalse('new_attr' in globals()) fixture.setUp() try: self.assertEqual(True, new_attr) finally: fixture.cleanUp() self.assertFalse('new_attr' in globals()) def test_delete_existing_attribute(self): fixture = MonkeyPatch( 'fixtures.tests._fixtures.test_monkeypatch.reference', MonkeyPatch.delete) self.assertEqual(23, reference) fixture.setUp() try: self.assertFalse('reference' in globals()) finally: fixture.cleanUp() self.assertEqual(23, reference) def test_delete_missing_attribute(self): fixture = MonkeyPatch( 'fixtures.tests._fixtures.test_monkeypatch.new_attr', MonkeyPatch.delete) self.assertFalse('new_attr' in globals()) fixture.setUp() try: self.assertFalse('new_attr' in globals()) finally: fixture.cleanUp() self.assertFalse('new_attr' in globals()) fixtures-0.3.14/lib/fixtures/tests/_fixtures/test_packagepath.py0000664000175000017500000000326612165105545026261 0ustar robertcrobertc00000000000000# fixtures: Fixtures with cleanups for testing and convenience. # # Copyright (c) 2011, Robert Collins # # Licensed under either the Apache License, Version 2.0 or the BSD 3-clause # license at the users choice. A copy of both licenses are available in the # project source as Apache-2.0 and BSD. You may not use this file except in # compliance with one of these two licences. # # Unless required by applicable law or agreed to in writing, software # distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # license you chose for the specific language governing permissions and # limitations under that license. import testtools import fixtures from fixtures import ( PackagePathEntry, TempDir, ) class TestPackagePathEntry(testtools.TestCase): def test_adds_missing_to_end_package_path(self): uniquedir = self.useFixture(TempDir()).path fixture = PackagePathEntry('fixtures', uniquedir) self.assertFalse(uniquedir in fixtures.__path__) with fixture: self.assertTrue(uniquedir in fixtures.__path__) self.assertFalse(uniquedir in fixtures.__path__) def test_doesnt_alter_existing_entry(self): existingdir = fixtures.__path__[0] expectedlen = len(fixtures.__path__) fixture = PackagePathEntry('fixtures', existingdir) with fixture: self.assertTrue(existingdir in fixtures.__path__) self.assertEqual(expectedlen, len(fixtures.__path__)) self.assertTrue(existingdir in fixtures.__path__) self.assertEqual(expectedlen, len(fixtures.__path__)) fixtures-0.3.14/lib/fixtures/tests/_fixtures/test_popen.py0000664000175000017500000000645712203313145025125 0ustar robertcrobertc00000000000000# fixtures: Fixtures with cleanups for testing and convenience. # # Copyright (c) 2010, 2011, Robert Collins # # Licensed under either the Apache License, Version 2.0 or the BSD 3-clause # license at the users choice. A copy of both licenses are available in the # project source as Apache-2.0 and BSD. You may not use this file except in # compliance with one of these two licences. # # Unless required by applicable law or agreed to in writing, software # distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # license you chose for the specific language governing permissions and # limitations under that license. import subprocess import testtools from testtools.compat import ( _b, BytesIO, ) from fixtures import FakePopen, TestWithFixtures from fixtures._fixtures.popen import FakeProcess class TestFakePopen(testtools.TestCase, TestWithFixtures): def test_installs_restores_global(self): fixture = FakePopen() popen = subprocess.Popen fixture.setUp() try: self.assertEqual(subprocess.Popen, fixture) finally: fixture.cleanUp() self.assertEqual(subprocess.Popen, popen) def test___call___is_recorded(self): fixture = self.useFixture(FakePopen()) proc = fixture(['foo', 'bar'], 1, None, 'in', 'out', 'err') self.assertEqual(1, len(fixture.procs)) self.assertEqual(dict(args=['foo', 'bar'], bufsize=1, executable=None, stdin='in', stdout='out', stderr='err'), proc._args) def test_inject_content_stdout(self): def get_info(args): return {'stdout': 'stdout'} fixture = self.useFixture(FakePopen(get_info)) proc = fixture(['foo']) self.assertEqual('stdout', proc.stdout) def test_handles_all_2_7_args(self): all_args = dict( args="args", bufsize="bufsize", executable="executable", stdin="stdin", stdout="stdout", stderr="stderr", preexec_fn="preexec_fn", close_fds="close_fds", shell="shell", cwd="cwd", env="env", universal_newlines="universal_newlines", startupinfo="startupinfo", creationflags="creationflags") def get_info(proc_args): self.assertEqual(all_args, proc_args) return {} fixture = self.useFixture(FakePopen(get_info)) proc = fixture(**all_args) def test_custom_returncode(self): def get_info(proc_args): return dict(returncode=1) proc = self.useFixture(FakePopen(get_info))(['foo']) self.assertEqual(None, proc.returncode) self.assertEqual(1, proc.wait()) self.assertEqual(1, proc.returncode) class TestFakeProcess(testtools.TestCase): def test_wait(self): proc = FakeProcess({}, {}) proc.returncode = 45 self.assertEqual(45, proc.wait()) def test_communicate(self): proc = FakeProcess({}, {}) self.assertEqual(('', ''), proc.communicate()) self.assertEqual(0, proc.returncode) def test_communicate_with_out(self): proc = FakeProcess({}, {'stdout': BytesIO(_b('foo'))}) self.assertEqual((_b('foo'), ''), proc.communicate()) self.assertEqual(0, proc.returncode) fixtures-0.3.14/lib/fixtures/tests/_fixtures/test_pythonpackage.py0000664000175000017500000000350012165105545026635 0ustar robertcrobertc00000000000000# fixtures: Fixtures with cleanups for testing and convenience. # # Copyright (c) 2010, Robert Collins # # Licensed under either the Apache License, Version 2.0 or the BSD 3-clause # license at the users choice. A copy of both licenses are available in the # project source as Apache-2.0 and BSD. You may not use this file except in # compliance with one of these two licences. # # Unless required by applicable law or agreed to in writing, software # distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # license you chose for the specific language governing permissions and # limitations under that license. import os.path import testtools from testtools.compat import _b from fixtures import PythonPackage, TestWithFixtures class TestPythonPackage(testtools.TestCase, TestWithFixtures): def test_has_tempdir(self): fixture = PythonPackage('foo', []) fixture.setUp() try: self.assertTrue(os.path.isdir(fixture.base)) finally: fixture.cleanUp() def test_writes_package(self): fixture = PythonPackage('foo', [('bar.py', _b('woo'))]) fixture.setUp() try: self.assertEqual('', open(os.path.join(fixture.base, 'foo', '__init__.py')).read()) self.assertEqual('woo', open(os.path.join(fixture.base, 'foo', 'bar.py')).read()) finally: fixture.cleanUp() def test_no__init__(self): fixture = PythonPackage('foo', [('bar.py', _b('woo'))], init=False) fixture.setUp() try: self.assertFalse(os.path.exists(os.path.join(fixture.base, 'foo', '__init__.py'))) finally: fixture.cleanUp() fixtures-0.3.14/lib/fixtures/tests/_fixtures/test_pythonpath.py0000664000175000017500000000310112165105545026173 0ustar robertcrobertc00000000000000# fixtures: Fixtures with cleanups for testing and convenience. # # Copyright (c) 2011, Robert Collins # # Licensed under either the Apache License, Version 2.0 or the BSD 3-clause # license at the users choice. A copy of both licenses are available in the # project source as Apache-2.0 and BSD. You may not use this file except in # compliance with one of these two licences. # # Unless required by applicable law or agreed to in writing, software # distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # license you chose for the specific language governing permissions and # limitations under that license. import sys import testtools from fixtures import ( PythonPathEntry, TempDir, ) class TestPythonPathEntry(testtools.TestCase): def test_adds_missing_to_end_sys_path(self): uniquedir = self.useFixture(TempDir()).path fixture = PythonPathEntry(uniquedir) self.assertFalse(uniquedir in sys.path) with fixture: self.assertTrue(uniquedir in sys.path) self.assertFalse(uniquedir in sys.path) def test_doesnt_alter_existing_entry(self): existingdir = sys.path[0] expectedlen = len(sys.path) fixture = PythonPathEntry(existingdir) with fixture: self.assertTrue(existingdir in sys.path) self.assertEqual(expectedlen, len(sys.path)) self.assertTrue(existingdir in sys.path) self.assertEqual(expectedlen, len(sys.path)) fixtures-0.3.14/lib/fixtures/tests/_fixtures/test_streams.py0000664000175000017500000000740012165105545025461 0ustar robertcrobertc00000000000000# fixtures: Fixtures with cleanups for testing and convenience. # # Copyright (c) 2012, Robert Collins # # Licensed under either the Apache License, Version 2.0 or the BSD 3-clause # license at the users choice. A copy of both licenses are available in the # project source as Apache-2.0 and BSD. You may not use this file except in # compliance with one of these two licences. # # Unless required by applicable law or agreed to in writing, software # distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # license you chose for the specific language governing permissions and # limitations under that license. from testtools import TestCase from testtools.compat import ( _b, _u, ) from testtools.matchers import Contains from fixtures import ( ByteStream, DetailStream, StringStream, ) class DetailStreamTest(TestCase): def test_doc_mentions_deprecated(self): self.assertThat(DetailStream.__doc__, Contains('Deprecated')) class TestByteStreams(TestCase): def test_empty_detail_stream(self): detail_name = 'test' fixture = ByteStream(detail_name) with fixture: content = fixture.getDetails()[detail_name] self.assertEqual(_u(""), content.as_text()) def test_stream_content_in_details(self): detail_name = 'test' fixture = ByteStream(detail_name) with fixture: stream = fixture.stream content = fixture.getDetails()[detail_name] # Output after getDetails is called is included. stream.write(_b("testing 1 2 3")) self.assertEqual("testing 1 2 3", content.as_text()) def test_stream_content_reset(self): detail_name = 'test' fixture = ByteStream(detail_name) with fixture: stream = fixture.stream content = fixture.getDetails()[detail_name] stream.write(_b("testing 1 2 3")) with fixture: # The old content object returns the old usage self.assertEqual(_u("testing 1 2 3"), content.as_text()) content = fixture.getDetails()[detail_name] # A new fixture returns the new output: stream = fixture.stream stream.write(_b("1 2 3 testing")) self.assertEqual(_u("1 2 3 testing"), content.as_text()) class TestStringStreams(TestCase): def test_empty_detail_stream(self): detail_name = 'test' fixture = StringStream(detail_name) with fixture: content = fixture.getDetails()[detail_name] self.assertEqual(_u(""), content.as_text()) def test_stream_content_in_details(self): detail_name = 'test' fixture = StringStream(detail_name) with fixture: stream = fixture.stream content = fixture.getDetails()[detail_name] # Output after getDetails is called is included. stream.write(_u("testing 1 2 3")) self.assertEqual("testing 1 2 3", content.as_text()) def test_stream_content_reset(self): detail_name = 'test' fixture = StringStream(detail_name) with fixture: stream = fixture.stream content = fixture.getDetails()[detail_name] stream.write(_u("testing 1 2 3")) with fixture: # The old content object returns the old usage self.assertEqual(_u("testing 1 2 3"), content.as_text()) content = fixture.getDetails()[detail_name] # A new fixture returns the new output: stream = fixture.stream stream.write(_u("1 2 3 testing")) self.assertEqual(_u("1 2 3 testing"), content.as_text()) fixtures-0.3.14/lib/fixtures/tests/_fixtures/test_tempdir.py0000664000175000017500000000651012165105545025450 0ustar robertcrobertc00000000000000# fixtures: Fixtures with cleanups for testing and convenience. # # Copyright (c) 2010, Robert Collins # # Licensed under either the Apache License, Version 2.0 or the BSD 3-clause # license at the users choice. A copy of both licenses are available in the # project source as Apache-2.0 and BSD. You may not use this file except in # compliance with one of these two licences. # # Unless required by applicable law or agreed to in writing, software # distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # license you chose for the specific language governing permissions and # limitations under that license. import os import tempfile import testtools from testtools.matchers import StartsWith from fixtures import ( NestedTempfile, TempDir, ) class TestTempDir(testtools.TestCase): def test_basic(self): fixture = TempDir() sentinel = object() self.assertEqual(sentinel, getattr(fixture, 'path', sentinel)) fixture.setUp() try: path = fixture.path self.assertTrue(os.path.isdir(path)) finally: fixture.cleanUp() self.assertFalse(os.path.isdir(path)) def test_under_dir(self): root = self.useFixture(TempDir()).path fixture = TempDir(root) fixture.setUp() with fixture: self.assertThat(fixture.path, StartsWith(root)) def test_join(self): temp_dir = self.useFixture(TempDir()) root = temp_dir.path relpath = 'foo/bar/baz' self.assertEqual( os.path.join(root, relpath), temp_dir.join(relpath)) def test_join_multiple_children(self): temp_dir = self.useFixture(TempDir()) root = temp_dir.path self.assertEqual( os.path.join(root, 'foo', 'bar', 'baz'), temp_dir.join('foo', 'bar', 'baz')) def test_join_naughty_children(self): temp_dir = self.useFixture(TempDir()) root = temp_dir.path self.assertEqual( os.path.abspath(os.path.join(root, '..', 'bar', 'baz')), temp_dir.join('..', 'bar', 'baz')) class NestedTempfileTest(testtools.TestCase): """Tests for `NestedTempfile`.""" def test_normal(self): # The temp directory is removed when the context is exited. starting_tempdir = tempfile.gettempdir() with NestedTempfile(): self.assertEqual(tempfile.tempdir, tempfile.gettempdir()) self.assertNotEqual(starting_tempdir, tempfile.tempdir) self.assertTrue(os.path.isdir(tempfile.tempdir)) nested_tempdir = tempfile.tempdir self.assertEqual(tempfile.tempdir, tempfile.gettempdir()) self.assertEqual(starting_tempdir, tempfile.tempdir) self.assertFalse(os.path.isdir(nested_tempdir)) def test_exception(self): # The temp directory is removed when the context is exited, even if # the code running in context raises an exception. class ContrivedException(Exception): pass try: with NestedTempfile(): nested_tempdir = tempfile.tempdir raise ContrivedException except ContrivedException: self.assertFalse(os.path.isdir(nested_tempdir)) fixtures-0.3.14/lib/fixtures/tests/_fixtures/test_temphomedir.py0000664000175000017500000000277612165105545026333 0ustar robertcrobertc00000000000000# fixtures: Fixtures with cleanups for testing and convenience. # # Copyright (c) 2011 Canonical Ltd. # # Licensed under either the Apache License, Version 2.0 or the BSD 3-clause # license at the users choice. A copy of both licenses are available in the # project source as Apache-2.0 and BSD. You may not use this file except in # compliance with one of these two licences. # # Unless required by applicable law or agreed to in writing, software # distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # license you chose for the specific language governing permissions and # limitations under that license. import os import testtools from testtools.matchers import StartsWith from fixtures import ( TempDir, TempHomeDir, ) class TestTempDir(testtools.TestCase): def test_basic(self): fixture = TempHomeDir() sentinel = object() self.assertEqual(sentinel, getattr(fixture, 'path', sentinel)) fixture.setUp() try: path = fixture.path self.assertTrue(os.path.isdir(path)) self.assertEqual(path, os.environ.get("HOME")) finally: fixture.cleanUp() self.assertFalse(os.path.isdir(path)) def test_under_dir(self): root = self.useFixture(TempDir()).path fixture = TempHomeDir(root) fixture.setUp() with fixture: self.assertThat(fixture.path, StartsWith(root)) fixtures-0.3.14/lib/fixtures/tests/_fixtures/test_timeout.py0000664000175000017500000000437412165105545025500 0ustar robertcrobertc00000000000000# fixtures: Fixtures with cleanups for testing and convenience. # # Copyright (C) 2011, Martin Pool # # Licensed under either the Apache License, Version 2.0 or the BSD 3-clause # license at the users choice. A copy of both licenses are available in the # project source as Apache-2.0 and BSD. You may not use this file except in # compliance with one of these two licences. # # Unless required by applicable law or agreed to in writing, software # distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # license you chose for the specific language governing permissions and # limitations under that license. import signal import time import testtools from testtools.testcase import ( TestSkipped, ) import fixtures def sample_timeout_passes(): with fixtures.Timeout(100, gentle=True): pass # Timeout shouldn't fire def sample_long_delay_with_gentle_timeout(): with fixtures.Timeout(1, gentle=True): time.sleep(100) # Expected to be killed here. def sample_long_delay_with_harsh_timeout(): with fixtures.Timeout(1, gentle=False): time.sleep(100) # Expected to be killed here. class TestTimeout(testtools.TestCase, fixtures.TestWithFixtures): def requireUnix(self): if getattr(signal, 'alarm', None) is None: raise TestSkipped("no alarm() function") def test_timeout_passes(self): # This can pass even on Windows - the test is skipped. sample_timeout_passes() def test_timeout_gentle(self): self.requireUnix() self.assertRaises( fixtures.TimeoutException, sample_long_delay_with_gentle_timeout) def test_timeout_harsh(self): self.requireUnix() # This will normally kill the whole process, which would be # inconvenient. Let's hook the alarm here so we can observe it. self.got_alarm = False def sigalrm_handler(signum, frame): self.got_alarm = True old_handler = signal.signal(signal.SIGALRM, sigalrm_handler) self.addCleanup(signal.signal, signal.SIGALRM, old_handler) sample_long_delay_with_harsh_timeout() self.assertTrue(self.got_alarm) fixtures-0.3.14/lib/fixtures/tests/__init__.py0000664000175000017500000000343312165105545022475 0ustar robertcrobertc00000000000000# fixtures: Fixtures with cleanups for testing and convenience. # # Copyright (c) 2010, Robert Collins # # Licensed under either the Apache License, Version 2.0 or the BSD 3-clause # license at the users choice. A copy of both licenses are available in the # project source as Apache-2.0 and BSD. You may not use this file except in # compliance with one of these two licences. # # Unless required by applicable law or agreed to in writing, software # distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # license you chose for the specific language governing permissions and # limitations under that license. import doctest import sys import unittest import fixtures.tests._fixtures def test_suite(): standard_tests = unittest.TestSuite() loader = unittest.TestLoader() return load_tests(loader, standard_tests, None) def load_tests(loader, standard_tests, pattern): test_modules = [ 'callmany', 'fixture', 'testcase', ] prefix = "fixtures.tests.test_" test_mod_names = [prefix + test_module for test_module in test_modules] standard_tests.addTests(loader.loadTestsFromNames(test_mod_names)) if sys.version_info >= (2, 7): # 2.7 calls load_tests for us standard_tests.addTests(loader.loadTestsFromName('fixtures.tests._fixtures')) else: # We need to call it ourselves. standard_tests.addTests(fixtures.tests._fixtures.load_tests( loader, loader.loadTestsFromName('fixtures.tests._fixtures'), pattern)) doctest.set_unittest_reportflags(doctest.REPORT_ONLY_FIRST_FAILURE) standard_tests.addTest(doctest.DocFileSuite("../../../README")) return standard_tests fixtures-0.3.14/lib/fixtures/tests/helpers.py0000664000175000017500000000234612165105545022402 0ustar robertcrobertc00000000000000# fixtures: Fixtures with cleanups for testing and convenience. # # Copyright (c) 2010, Robert Collins # # Licensed under either the Apache License, Version 2.0 or the BSD 3-clause # license at the users choice. A copy of both licenses are available in the # project source as Apache-2.0 and BSD. You may not use this file except in # compliance with one of these two licences. # # Unless required by applicable law or agreed to in writing, software # distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # license you chose for the specific language governing permissions and # limitations under that license. import fixtures class LoggingFixture(fixtures.Fixture): def __init__(self, suffix='', calls=None): super(LoggingFixture, self).__init__() if calls is None: calls = [] self.calls = calls self.suffix = suffix def setUp(self): super(LoggingFixture, self).setUp() self.calls.append('setUp' + self.suffix) self.addCleanup(self.calls.append, 'cleanUp' + self.suffix) def reset(self): self.calls.append('reset' + self.suffix) fixtures-0.3.14/lib/fixtures/tests/test_callmany.py0000664000175000017500000000474712165105545023606 0ustar robertcrobertc00000000000000# fixtures: Fixtures with cleanups for testing and convenience. # # Copyright (c) 2010, Robert Collins # # Licensed under either the Apache License, Version 2.0 or the BSD 3-clause # license at the users choice. A copy of both licenses are available in the # project source as Apache-2.0 and BSD. You may not use this file except in # compliance with one of these two licences. # # Unless required by applicable law or agreed to in writing, software # distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # license you chose for the specific language governing permissions and # limitations under that license. import types import testtools from fixtures.callmany import CallMany class TestCallMany(testtools.TestCase): def test__call__raise_errors_false_callsall_returns_exceptions(self): calls = [] def raise_exception1(): calls.append('1') raise Exception('woo') def raise_exception2(): calls.append('2') raise Exception('woo') call = CallMany() call.push(raise_exception2) call.push(raise_exception1) exceptions = call(raise_errors=False) self.assertEqual(['1', '2'], calls) # There should be two exceptions self.assertEqual(2, len(exceptions)) # They should be a sys.exc_info tuple. self.assertEqual(3, len(exceptions[0])) type, value, tb = exceptions[0] self.assertEqual(Exception, type) self.assertIsInstance(value, Exception) self.assertEqual(('woo',), value.args) self.assertIsInstance(tb, types.TracebackType) def test_exit_propogates_exceptions(self): call = CallMany() call.__enter__() self.assertEqual(False, call.__exit__(None, None, None)) def test_exit_runs_all_raises_first_exception(self): calls = [] def raise_exception1(): calls.append('1') raise Exception('woo') def raise_exception2(): calls.append('2') raise Exception('hoo') call = CallMany() call.push(raise_exception2) call.push(raise_exception1) call.__enter__() exc = self.assertRaises(Exception, call.__exit__, None, None, None) self.assertEqual(('woo',), exc.args[0][1].args) self.assertEqual(('hoo',), exc.args[1][1].args) self.assertEqual(['1', '2'], calls) fixtures-0.3.14/lib/fixtures/tests/test_fixture.py0000664000175000017500000002635312165105545023471 0ustar robertcrobertc00000000000000# fixtures: Fixtures with cleanups for testing and convenience. # # Copyright (c) 2010, Robert Collins # # Licensed under either the Apache License, Version 2.0 or the BSD 3-clause # license at the users choice. A copy of both licenses are available in the # project source as Apache-2.0 and BSD. You may not use this file except in # compliance with one of these two licences. # # Unless required by applicable law or agreed to in writing, software # distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # license you chose for the specific language governing permissions and # limitations under that license. import types import testtools from testtools.content import text_content from testtools.testcase import skipIf import fixtures from fixtures.fixture import gather_details from fixtures.tests.helpers import LoggingFixture require_gather_details = skipIf(gather_details is None, "gather_details() is not available.") # Note: the cleanup related tests are strictly speaking redundant, IFF they are # replaced with contract tests for correct use of CallMany. class TestFixture(testtools.TestCase): def test_resetCallsSetUpCleanUp(self): calls = [] class FixtureWithSetupOnly(fixtures.Fixture): def setUp(self): super(FixtureWithSetupOnly, self).setUp() calls.append('setUp') self.addCleanup(calls.append, 'cleanUp') fixture = FixtureWithSetupOnly() fixture.setUp() fixture.reset() fixture.cleanUp() self.assertEqual(['setUp', 'cleanUp', 'setUp', 'cleanUp'], calls) def test_reset_raises_if_cleanup_raises(self): class FixtureWithSetupOnly(fixtures.Fixture): def do_raise(self): raise Exception('foo') def setUp(self): super(FixtureWithSetupOnly, self).setUp() self.addCleanup(self.do_raise) fixture = FixtureWithSetupOnly() fixture.setUp() exc = self.assertRaises(Exception, fixture.reset) self.assertEqual(('foo',), exc.args) def test_cleanUp_raise_first_false_callscleanups_returns_exceptions(self): calls = [] def raise_exception1(): calls.append('1') raise Exception('woo') def raise_exception2(): calls.append('2') raise Exception('woo') class FixtureWithException(fixtures.Fixture): def setUp(self): super(FixtureWithException, self).setUp() self.addCleanup(raise_exception2) self.addCleanup(raise_exception1) fixture = FixtureWithException() fixture.setUp() exceptions = fixture.cleanUp(raise_first=False) self.assertEqual(['1', '2'], calls) # There should be two exceptions self.assertEqual(2, len(exceptions)) # They should be a sys.exc_info tuple. self.assertEqual(3, len(exceptions[0])) type, value, tb = exceptions[0] self.assertEqual(Exception, type) self.assertIsInstance(value, Exception) self.assertEqual(('woo',), value.args) self.assertIsInstance(tb, types.TracebackType) def test_exit_propogates_exceptions(self): fixture = fixtures.Fixture() fixture.__enter__() self.assertEqual(False, fixture.__exit__(None, None, None)) def test_exit_runs_all_raises_first_exception(self): calls = [] def raise_exception1(): calls.append('1') raise Exception('woo') def raise_exception2(): calls.append('2') raise Exception('hoo') class FixtureWithException(fixtures.Fixture): def setUp(self): super(FixtureWithException, self).setUp() self.addCleanup(raise_exception2) self.addCleanup(raise_exception1) fixture = FixtureWithException() fixture.__enter__() exc = self.assertRaises(Exception, fixture.__exit__, None, None, None) self.assertEqual(('woo',), exc.args[0][1].args) self.assertEqual(('hoo',), exc.args[1][1].args) self.assertEqual(['1', '2'], calls) def test_useFixture(self): parent = LoggingFixture('-outer') nested = LoggingFixture('-inner', calls=parent.calls) parent.setUp() parent.useFixture(nested) parent.cleanUp() self.assertEqual( ['setUp-outer', 'setUp-inner', 'cleanUp-inner', 'cleanUp-outer'], parent.calls) @require_gather_details def test_useFixture_details_captured_from_setUp(self): # Details added during fixture set-up are gathered even if setUp() # fails with an exception. class SomethingBroke(Exception): pass class BrokenFixture(fixtures.Fixture): def setUp(self): super(BrokenFixture, self).setUp() self.addDetail('content', text_content("foobar")) raise SomethingBroke() broken_fixture = BrokenFixture() class SimpleFixture(fixtures.Fixture): def setUp(self): super(SimpleFixture, self).setUp() self.useFixture(broken_fixture) simple_fixture = SimpleFixture() self.assertRaises(SomethingBroke, simple_fixture.setUp) self.assertEqual( {"content": text_content("foobar")}, broken_fixture.getDetails()) self.assertEqual( {"content": text_content("foobar")}, simple_fixture.getDetails()) def test_getDetails(self): fixture = fixtures.Fixture() with fixture: self.assertEqual({}, fixture.getDetails()) def test_details_from_child_fixtures_are_returned(self): parent = fixtures.Fixture() with parent: child = fixtures.Fixture() parent.useFixture(child) # Note that we add the detail *after* using the fixture: the parent # has to query just-in-time. child.addDetail('foo', 'content') self.assertEqual({'foo': 'content'}, parent.getDetails()) # And dropping it from the child drops it from the parent. del child._details['foo'] self.assertEqual({}, parent.getDetails()) # After cleanup the child details are still gone. child.addDetail('foo', 'content') self.assertEqual({}, parent.getDetails()) def test_duplicate_details_are_disambiguated(self): parent = fixtures.Fixture() with parent: parent.addDetail('foo', 'parent-content') child = fixtures.Fixture() parent.useFixture(child) # Note that we add the detail *after* using the fixture: the parent # has to query just-in-time. child.addDetail('foo', 'child-content') self.assertEqual({'foo': 'parent-content', 'foo-1': 'child-content',}, parent.getDetails()) def test_addDetail(self): fixture = fixtures.Fixture() with fixture: fixture.addDetail('foo', 'content') self.assertEqual({'foo': 'content'}, fixture.getDetails()) del fixture._details['foo'] self.assertEqual({}, fixture.getDetails()) fixture.addDetail('foo', 'content') # Cleanup clears the details too. self.assertEqual({}, fixture.getDetails()) class TestFunctionFixture(testtools.TestCase): def test_setup_only(self): fixture = fixtures.FunctionFixture(lambda: 42) fixture.setUp() self.assertEqual(42, fixture.fn_result) fixture.cleanUp() self.assertFalse(hasattr(fixture, 'fn_result')) def test_cleanup(self): results = [] fixture = fixtures.FunctionFixture(lambda: 84, results.append) fixture.setUp() self.assertEqual(84, fixture.fn_result) self.assertEqual([], results) fixture.cleanUp() self.assertEqual([84], results) def test_reset(self): results = [] expected = [21, 7] def setUp(): return expected.pop(0) def reset(result): results.append(('reset', result)) return expected.pop(0) fixture = fixtures.FunctionFixture(setUp, results.append, reset) fixture.setUp() self.assertEqual([], results) fixture.reset() self.assertEqual([('reset', 21)], results) self.assertEqual(7, fixture.fn_result) fixture.cleanUp() self.assertEqual([('reset', 21), 7], results) class TestMethodFixture(testtools.TestCase): def test_no_setup_cleanup(self): class Stub: pass fixture = fixtures.MethodFixture(Stub()) fixture.setUp() fixture.reset() self.assertIsInstance(fixture.obj, Stub) fixture.cleanUp() def test_setup_only(self): class Stub: def setUp(self): self.value = 42 fixture = fixtures.MethodFixture(Stub()) fixture.setUp() self.assertEqual(42, fixture.obj.value) self.assertIsInstance(fixture.obj, Stub) fixture.cleanUp() def test_cleanup_only(self): class Stub: value = None def tearDown(self): self.value = 42 fixture = fixtures.MethodFixture(Stub()) fixture.setUp() self.assertEqual(None, fixture.obj.value) self.assertIsInstance(fixture.obj, Stub) fixture.cleanUp() self.assertEqual(42, fixture.obj.value) def test_cleanup(self): class Stub: def setUp(self): self.value = 42 def tearDown(self): self.value = 84 fixture = fixtures.MethodFixture(Stub()) fixture.setUp() self.assertEqual(42, fixture.obj.value) self.assertIsInstance(fixture.obj, Stub) fixture.cleanUp() self.assertEqual(84, fixture.obj.value) def test_custom_setUp(self): class Stub: def mysetup(self): self.value = 42 obj = Stub() fixture = fixtures.MethodFixture(obj, setup=obj.mysetup) fixture.setUp() self.assertEqual(42, fixture.obj.value) self.assertEqual(obj, fixture.obj) fixture.cleanUp() def test_custom_cleanUp(self): class Stub: value = 42 def mycleanup(self): self.value = None obj = Stub() fixture = fixtures.MethodFixture(obj, cleanup=obj.mycleanup) fixture.setUp() self.assertEqual(42, fixture.obj.value) self.assertEqual(obj, fixture.obj) fixture.cleanUp() self.assertEqual(None, fixture.obj.value) def test_reset(self): class Stub: def setUp(self): self.value = 42 def tearDown(self): self.value = 84 def reset(self): self.value = 126 obj = Stub() fixture = fixtures.MethodFixture(obj, reset=obj.reset) fixture.setUp() self.assertEqual(obj, fixture.obj) self.assertEqual(42, obj.value) fixture.reset() self.assertEqual(126, obj.value) fixture.cleanUp() self.assertEqual(84, obj.value) fixtures-0.3.14/lib/fixtures/tests/test_testcase.py0000664000175000017500000001006712165105545023611 0ustar robertcrobertc00000000000000# fixtures: Fixtures with cleanups for testing and convenience. # # Copyright (c) 2010, Robert Collins # # Licensed under either the Apache License, Version 2.0 or the BSD 3-clause # license at the users choice. A copy of both licenses are available in the # project source as Apache-2.0 and BSD. You may not use this file except in # compliance with one of these two licences. # # Unless required by applicable law or agreed to in writing, software # distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # license you chose for the specific language governing permissions and # limitations under that license. import unittest import testtools from testtools.content import text_content from testtools.testcase import skipIf import fixtures from fixtures import TestWithFixtures from fixtures.fixture import gather_details from fixtures.tests.helpers import LoggingFixture class TestTestWithFixtures(unittest.TestCase): def test_useFixture(self): fixture = LoggingFixture() class SimpleTest(testtools.TestCase, TestWithFixtures): def test_foo(self): self.useFixture(fixture) result = unittest.TestResult() SimpleTest('test_foo').run(result) self.assertTrue(result.wasSuccessful()) self.assertEqual(['setUp', 'cleanUp'], fixture.calls) def test_useFixture_uses_raise_first(self): calls = [] def raiser(ignored): calls.append('called') raise Exception('foo') fixture = fixtures.FunctionFixture(lambda:None, raiser) class SimpleTest(testtools.TestCase, TestWithFixtures): def test_foo(self): self.useFixture(fixture) result = unittest.TestResult() SimpleTest('test_foo').run(result) self.assertFalse(result.wasSuccessful()) self.assertEqual(['called'], calls) @skipIf(gather_details is None, "gather_details() is not available.") def test_useFixture_details_captured_from_setUp(self): # Details added during fixture set-up are gathered even if setUp() # fails with an exception. class SomethingBroke(Exception): pass class BrokenFixture(fixtures.Fixture): def setUp(self): super(BrokenFixture, self).setUp() self.addDetail('content', text_content("foobar")) raise SomethingBroke() broken_fixture = BrokenFixture() class DetailedTestCase(TestWithFixtures, testtools.TestCase): def setUp(self): super(DetailedTestCase, self).setUp() self.useFixture(broken_fixture) def test(self): pass detailed_test_case = DetailedTestCase("test") self.assertRaises(SomethingBroke, detailed_test_case.setUp) self.assertEqual( {"content": text_content("foobar")}, broken_fixture.getDetails()) self.assertEqual( {"content": text_content("foobar")}, detailed_test_case.getDetails()) @skipIf(gather_details is None, "gather_details() is not available.") def test_useFixture_details_not_captured_from_setUp(self): # Details added during fixture set-up are not gathered if the test # case does not have the ability to accept those details. class SomethingBroke(Exception): pass class BrokenFixture(fixtures.Fixture): def setUp(self): super(BrokenFixture, self).setUp() self.addDetail('content', text_content("foobar")) raise SomethingBroke() broken_fixture = BrokenFixture() class NonDetailedTestCase(TestWithFixtures, unittest.TestCase): def setUp(self): super(NonDetailedTestCase, self).setUp() self.useFixture(broken_fixture) def test(self): pass non_detailed_test_case = NonDetailedTestCase("test") self.assertRaises(SomethingBroke, non_detailed_test_case.setUp) fixtures-0.3.14/lib/fixtures/__init__.py0000664000175000017500000000612212203313215021315 0ustar robertcrobertc00000000000000# fixtures: Fixtures with cleanups for testing and convenience. # # Copyright (c) 2010, 2011, Robert Collins # # Licensed under either the Apache License, Version 2.0 or the BSD 3-clause # license at the users choice. A copy of both licenses are available in the # project source as Apache-2.0 and BSD. You may not use this file except in # compliance with one of these two licences. # # Unless required by applicable law or agreed to in writing, software # distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # license you chose for the specific language governing permissions and # limitations under that license. """Fixtures provides a sensible contract for reusable test fixtures. It also provides glue for using these in common test runners and acts as a common repository for widely used Fixture classes. See the README for a manual, and the docstrings on individual functions and methods for details. Most users will want to look at TestWithFixtures and Fixture, to start with. """ # same format as sys.version_info: "A tuple containing the five components of # the version number: major, minor, micro, releaselevel, and serial. All # values except releaselevel are integers; the release level is 'alpha', # 'beta', 'candidate', or 'final'. The version_info value corresponding to the # Python version 2.0 is (2, 0, 0, 'final', 0)." Additionally we use a # releaselevel of 'dev' for unreleased under-development code. # # If the releaselevel is 'alpha' then the major/minor/micro components are not # established at this point, and setup.py will use a version of next-$(revno). # If the releaselevel is 'final', then the tarball will be major.minor.micro. # Otherwise it is major.minor.micro~$(revno). __version__ = (0, 3, 14, 'final', 0) __all__ = [ 'ByteStream', 'DetailStream', 'EnvironmentVariable', 'EnvironmentVariableFixture', 'FakeLogger', 'FakePopen', 'Fixture', 'FunctionFixture', 'LoggerFixture', 'LogHandler', 'MethodFixture', 'MonkeyPatch', 'NestedTempfile', 'PackagePathEntry', 'PopenFixture', 'PythonPackage', 'PythonPathEntry', 'StringStream', 'TempDir', 'TempHomeDir', 'TestWithFixtures', 'Timeout', 'TimeoutException', ] from fixtures.fixture import ( Fixture, FunctionFixture, MethodFixture, ) from fixtures._fixtures import ( ByteStream, DetailStream, EnvironmentVariable, EnvironmentVariableFixture, FakeLogger, FakePopen, LoggerFixture, LogHandler, MonkeyPatch, NestedTempfile, PackagePathEntry, PopenFixture, PythonPackage, PythonPathEntry, StringStream, TempDir, TempHomeDir, Timeout, TimeoutException, ) from fixtures.testcase import TestWithFixtures def test_suite(): import fixtures.tests return fixtures.tests.test_suite() def load_tests(loader, standard_tests, pattern): standard_tests.addTests(loader.loadTestsFromNames(["fixtures.tests"])) return standard_tests fixtures-0.3.14/lib/fixtures/callmany.py0000664000175000017500000000671212165105545021377 0ustar robertcrobertc00000000000000# fixtures: Fixtures with cleanups for testing and convenience. # # Copyright (c) 2010, Robert Collins # # Licensed under either the Apache License, Version 2.0 or the BSD 3-clause # license at the users choice. A copy of both licenses are available in the # project source as Apache-2.0 and BSD. You may not use this file except in # compliance with one of these two licences. # # Unless required by applicable law or agreed to in writing, software # distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # license you chose for the specific language governing permissions and # limitations under that license. __all__ = [ 'CallMany', ] import sys from testtools.compat import ( reraise, ) from testtools.helpers import try_import class MultipleExceptions(Exception): """Report multiple exc_info tuples in self.args.""" MultipleExceptions = try_import( "testtools.MultipleExceptions", MultipleExceptions) class CallMany(object): """A stack of functions which will all be called on __call__. CallMany also acts as a context manager for convenience. Functions are called in last pushed first executed order. This is used by Fixture to manage its addCleanup feature. """ def __init__(self): self._cleanups = [] def push(self, cleanup, *args, **kwargs): """Add a function to be called from __call__. On __call__ all functions are called - see __call__ for details on how multiple exceptions are handled. :param cleanup: A callable to call during cleanUp. :param *args: Positional args for cleanup. :param kwargs: Keyword args for cleanup. :return: None """ self._cleanups.append((cleanup, args, kwargs)) def __call__(self, raise_errors=True): """Run all the registered functions. :param raise_errors: Deprecated parameter from before testtools gained MultipleExceptions. raise_errors defaults to True. When True if exception(s) are raised while running functions, they are re-raised after all the functions have run. If multiple exceptions are raised, they are all wrapped into a MultipleExceptions object, and that is raised. Thus, to cach a specific exception from a function run by __call__, you need to catch both the exception and MultipleExceptions, and then check within a MultipleExceptions instance for an occurance of the type you wish to catch. :return: Either None or a list of the exc_info() for each exception that occured if raise_errors was False. """ cleanups = reversed(self._cleanups) self._cleanups = [] result = [] for cleanup, args, kwargs in cleanups: try: cleanup(*args, **kwargs) except Exception: result.append(sys.exc_info()) if result and raise_errors: if 1 == len(result): error = result[0] reraise(error[0], error[1], error[2]) else: raise MultipleExceptions(*result) if not raise_errors: return result def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): self() return False # propogate exceptions from the with body. fixtures-0.3.14/lib/fixtures/fixture.py0000664000175000017500000002765612165105545021277 0ustar robertcrobertc00000000000000# fixtures: Fixtures with cleanups for testing and convenience. # # Copyright (c) 2010, Robert Collins # # Licensed under either the Apache License, Version 2.0 or the BSD 3-clause # license at the users choice. A copy of both licenses are available in the # project source as Apache-2.0 and BSD. You may not use this file except in # compliance with one of these two licences. # # Unless required by applicable law or agreed to in writing, software # distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # license you chose for the specific language governing permissions and # limitations under that license. __all__ = [ 'Fixture', 'FunctionFixture', 'MethodFixture', 'MultipleExceptions', ] import itertools import sys from testtools.compat import ( advance_iterator, reraise, ) from testtools.helpers import try_import from fixtures.callmany import ( CallMany, # Deprecated, imported for compatibility. MultipleExceptions, ) gather_details = try_import("testtools.testcase.gather_details") # This would be better in testtools (or a common library) def combine_details(source_details, target_details): """Add every value from source to target deduping common keys.""" for name, content_object in source_details.items(): new_name = name disambiguator = itertools.count(1) while new_name in target_details: new_name = '%s-%d' % (name, advance_iterator(disambiguator)) name = new_name target_details[name] = content_object class Fixture(object): """A Fixture representing some state or resource. Often used in tests, a Fixture must be setUp before using it, and cleanUp called after it is finished with (because many Fixture classes have external resources such as temporary directories). The reset() method can be called to perform cleanUp and setUp automatically and potentially faster. """ def addCleanup(self, cleanup, *args, **kwargs): """Add a clean function to be called from cleanUp. All cleanup functions are called - see cleanUp for details on how multiple exceptions are handled. If for some reason you need to cancel cleanups, call self._clear_cleanups. :param cleanup: A callable to call during cleanUp. :param *args: Positional args for cleanup. :param kwargs: Keyword args for cleanup. :return: None """ self._cleanups.push(cleanup, *args, **kwargs) def addDetail(self, name, content_object): """Add a detail to the Fixture. This may only be called after setUp has been called. :param name: The name for the detail being added. Overrides existing identically named details. :param content_object: The content object (meeting the testtools.content.Content protocol) being added. """ self._details[name] = content_object def cleanUp(self, raise_first=True): """Cleanup the fixture. This function will free all resources managed by the Fixture, restoring it (and any external facilities such as databases, temporary directories and so forth_ to their original state. This should not typically be overridden, see addCleanup instead. :param raise_first: Deprecated parameter from before testtools gained MultipleExceptions. raise_first defaults to True. When True if a single exception is raised, it is reraised after all the cleanUps have run. If multiple exceptions are raised, they are all wrapped into a MultipleExceptions object, and that is reraised. Thus, to cach a specific exception from cleanUp, you need to catch both the exception and MultipleExceptions, and then check within a MultipleExceptions instance for the type you're catching. :return: A list of the exc_info() for each exception that occured if raise_first was False """ try: return self._cleanups(raise_errors=raise_first) finally: self._clear_cleanups() def _clear_cleanups(self): """Clean the cleanup queue without running them. This is a helper that can be useful for subclasses which define reset(): they may perform something equivalent to a typical cleanUp without actually calling the cleanups. This also clears the details dict. """ self._cleanups = CallMany() self._details = {} self._detail_sources = [] def __enter__(self): self.setUp() return self def __exit__(self, exc_type, exc_val, exc_tb): try: self._cleanups() finally: self._clear_cleanups() return False # propogate exceptions from the with body. def getDetails(self): """Get the current details registered with the fixture. This does not return the internal dictionary: mutating it will have no effect. If you need to mutate it, just do so directly. :return: Dict from name -> content_object. """ result = dict(self._details) for source in self._detail_sources: combine_details(source.getDetails(), result) return result def setUp(self): """Prepare the Fixture for use. This should be overridden by most concrete fixtures. When overriding be sure to include self.addCleanup calls to restore the fixture to an un-setUp state, so that a single Fixture instance can be reused. After setUp is called, the fixture will have one or more attributes which can be used (these depend totally on the concrete subclass). :return: None. """ self._clear_cleanups() def reset(self): """Reset a setUp Fixture to the 'just setUp' state again. The default implementation calls self.cleanUp() self.setUp() but this function may be overridden to provide an optimised routine to achieve the same result. :return: None. """ self.cleanUp() self.setUp() def useFixture(self, fixture): """Use another fixture. The fixture will be setUp, and self.addCleanup(fixture.cleanUp) called. :param fixture: The fixture to use. :return: The fixture, after setting it up and scheduling a cleanup for it. """ try: fixture.setUp() except: # The child failed to come up, capture any details it has (copying # the content, it may go away anytime). if gather_details is not None: gather_details(fixture.getDetails(), self._details) raise else: self.addCleanup(fixture.cleanUp) # Calls to getDetails while this fixture is setup will return # details from the child fixture. self._detail_sources.append(fixture) return fixture class FunctionFixture(Fixture): """An adapter to use function(s) as a Fixture. Typically used when an existing object or function interface exists but you wish to use it as a Fixture (e.g. because fixtures are in use in your test suite and this will fit in better). To adapt an object with differently named setUp and cleanUp methods: fixture = FunctionFixture(object.install, object.__class__.remove) Note that the indirection via __class__ is to get an unbound method which can accept the result from install. See also MethodFixture which is specialised for objects. To adapt functions: fixture = FunctionFixture(tempfile.mkdtemp, shutil.rmtree) With a reset function: fixture = FunctionFixture(setup, cleanup, reset) :ivar fn_result: The result of the setup_fn. Undefined outside of the setUp, cleanUp context. """ def __init__(self, setup_fn, cleanup_fn=None, reset_fn=None): """Create a FunctionFixture. :param setup_fn: A callable which takes no parameters and returns the thing you want to use. e.g. def setup_fn(): return 42 The result of setup_fn is assigned to the fn_result attribute bu FunctionFixture.setUp. :param cleanup_fn: Optional callable which takes a single parameter, which must be that which is returned from the setup_fn. This is called from cleanUp. :param reset_fn: Optional callable which takes a single parameter like cleanup_fn, but also returns a new object for use as the fn_result: if defined this replaces the use of cleanup_fn and setup_fn when reset() is called. """ super(FunctionFixture, self).__init__() self.setup_fn = setup_fn self.cleanup_fn = cleanup_fn self.reset_fn = reset_fn def setUp(self): super(FunctionFixture, self).setUp() fn_result = self.setup_fn() self._maybe_cleanup(fn_result) def reset(self): if self.reset_fn is None: super(FunctionFixture, self).reset() else: self._clear_cleanups() fn_result = self.reset_fn(self.fn_result) self._maybe_cleanup(fn_result) def _maybe_cleanup(self, fn_result): self.addCleanup(delattr, self, 'fn_result') if self.cleanup_fn is not None: self.addCleanup(self.cleanup_fn, fn_result) self.fn_result = fn_result class MethodFixture(Fixture): """An adapter to use a function as a Fixture. Typically used when an existing object exists but you wish to use it as a Fixture (e.g. because fixtures are in use in your test suite and this will fit in better). To adapt an object with setUp / tearDown methods: fixture = MethodFixture(object) If setUp / tearDown / reset are missing, they simply won't be called. The object is exposed on fixture.obj. To adapt an object with differently named setUp and cleanUp methods: fixture = MethodFixture(object, setup=object.mySetUp, teardown=object.myTearDown) With a differently named reset function: fixture = MethodFixture(object, reset=object.myReset) :ivar obj: The object which is being wrapped. """ def __init__(self, obj, setup=None, cleanup=None, reset=None): """Create a MethodFixture. :param obj: The object to wrap. Exposed as fixture.obj :param setup: A method which takes no parameters. e.g. def setUp(self): self.value = 42 If setup is not supplied, and the object has a setUp method, that method is used, otherwise nothing will happen during fixture.setUp. :param cleanup: Optional method to cleanup the object's state. If not supplied the method 'tearDown' is used if it exists. :param reset: Optional method to reset the wrapped object for use. If not supplied, then the method 'reset' is used if it exists, otherwise cleanUp and setUp are called as per Fixture.reset(). """ super(MethodFixture, self).__init__() self.obj = obj if setup is None: setup = getattr(obj, 'setUp', None) if setup is None: setup = lambda:None self._setup = setup if cleanup is None: cleanup = getattr(obj, 'tearDown', None) if cleanup is None: cleanup = lambda:None self._cleanup = cleanup if reset is None: reset = getattr(obj, 'reset', None) self._reset = reset def setUp(self): super(MethodFixture, self).setUp() self._setup() def cleanUp(self): super(MethodFixture, self).cleanUp() self._cleanup() def reset(self): if self._reset is None: super(MethodFixture, self).reset() else: self._reset() fixtures-0.3.14/lib/fixtures/testcase.py0000664000175000017500000000420612203273503021377 0ustar robertcrobertc00000000000000# fixtures: Fixtures with cleanups for testing and convenience. # # Copyright (c) 2010, Robert Collins # # Licensed under either the Apache License, Version 2.0 or the BSD 3-clause # license at the users choice. A copy of both licenses are available in the # project source as Apache-2.0 and BSD. You may not use this file except in # compliance with one of these two licences. # # Unless required by applicable law or agreed to in writing, software # distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # license you chose for the specific language governing permissions and # limitations under that license. __all__ = [ 'TestWithFixtures', ] import unittest from fixtures.fixture import gather_details class TestWithFixtures(unittest.TestCase): """A TestCase with a helper function to use fixtures. Normally used as a mix-in class to add useFixture. Note that test classes such as testtools.TestCase which already have a ``useFixture`` method do not need this mixed in. """ def useFixture(self, fixture): """Use fixture in a test case. The fixture will be setUp, and self.addCleanup(fixture.cleanUp) called. :param fixture: The fixture to use. :return: The fixture, after setting it up and scheduling a cleanup for it. """ use_details = ( gather_details is not None and getattr(self, "addDetail", None) is not None) try: fixture.setUp() except: if use_details: # Capture the details now, in case the fixture goes away. gather_details(fixture.getDetails(), self.getDetails()) raise else: self.addCleanup(fixture.cleanUp) if use_details: # Capture the details from the fixture during test teardown; # this will evaluate the details before tearing down the # fixture. self.addCleanup(gather_details, fixture, self) return fixture fixtures-0.3.14/.bzrignore0000664000175000017500000000012412203273017016570 0ustar robertcrobertc00000000000000TAGS tags lib/testtools MANIFEST dist .testrepository __pycache__ fixtures.egg-info fixtures-0.3.14/Apache-2.00000664000175000017500000002613612165105545016211 0ustar robertcrobertc00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. fixtures-0.3.14/BSD0000664000175000017500000000274612165105545015144 0ustar robertcrobertc00000000000000Copyright (c) Robert Collins and Fixtures contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of Robert Collins nor the names of Subunit contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY ROBERT COLLINS AND SUBUNIT CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. fixtures-0.3.14/COPYING0000664000175000017500000000270412165105545015636 0ustar robertcrobertc00000000000000Fixtures is licensed under two licenses, the Apache License, Version 2.0 or the 3-clause BSD License. You may use this project under either of these licenses - choose the one that works best for you. We require contributions to be licensed under both licenses. The primary difference between them is that the Apache license takes care of potential issues with Patents and other intellectual property concerns that some users or contributors may find important. Generally every source file in Fixtures needs a license grant under both these licenses. As the code is shipped as a single unit, a brief form is used: ---- Copyright (c) [yyyy][,yyyy]* [name or 'Fixtures Contributors'] Licensed under either the Apache License, Version 2.0 or the BSD 3-clause license at the users choice. A copy of both licenses are available in the project source as Apache-2.0 and BSD. You may not use this file except in compliance with one of these two licences. Unless required by applicable law or agreed to in writing, software distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the license you chose for the specific language governing permissions and limitations under that license. ---- Code that has been incorporated into Fixtures from other projects will naturally be under its own license, and will retain that license. A known list of such code is maintained here: * No entries. fixtures-0.3.14/GOALS0000664000175000017500000000146512165105545015376 0ustar robertcrobertc00000000000000fixtures goals ============== * Declarative interface for creating both simple and complex fixtures for tests. * Play nice with testscenarios (for parameterisation of tests) and testresources (for optimising the use and overhead of expensive fixtures. * Either define a protocol for decoration-to-inject-fixtures, or explicitly delegate that to testresources/testscenarios (but require that if delegated the simple case should still be simple). * Be able to, in princple, provide test isolation like chdir and $HOME substition so that tests which work with external processes aren't tainted by the users home dir. * Be able to manage external processes as a fixture. * usable in trial, bzr, Zope testrunner, nose and the plain python unittest module. * Be useful outside of a purely testing context. fixtures-0.3.14/HACKING0000664000175000017500000000263112203274252015564 0ustar robertcrobertc00000000000000Contributing to fixtures ======================== Code access +++++++++++ Branch from the trunk (all patches should be for trunk unless there are exceptional circumstances):: bzr branch lp:python-fixtures path-to-new-local-branch Publish your branches whereever you like, I encourage Launchpad hosting though, as it can notify the community of new fixtures branches:: bzr push lp:~YOURUSERNAME/python-fixtures/YOURBRANCHNAME Copyright +++++++++ Fixtures is Copyright (C) 2010 Robert Collins. I'd like to be able to offer it up for stdlib inclusion once it has proved itself, so am asking for copyright assignment to me - or for your contributions to be under the BSD and Apache-2.0 licences that Fixtures is under (which permit inclusion in Python). Coding standards ++++++++++++++++ PEP-8 coding style please, though perfection isn't needed. Make sure that 'make check' passes before sending in a patch. Code arrangement ++++++++++++++++ The ``fixtures`` module should simply import classes and functions from more specific modules, rather than becoming large and bloated itself. For instance, TestWithFixtures lives in fixtures.testcase, and is imported in the fixtures __init__.py. Releasing +++++++++ 1. Update the version number in __init__.py and setup.py and add a version to NEWS. 1. Upload to pypi, signed. 1. commit, tag. 1. Close bugs. 1. Rename the next milestone, release it, and make a new one. fixtures-0.3.14/MANIFEST.in0000664000175000017500000000022212165105545016332 0ustar robertcrobertc00000000000000include .bzrignore include Apache-2.0 include BSD include COPYING include GOALS include HACKING include MANIFEST.in include Makefile include NEWS fixtures-0.3.14/Makefile0000664000175000017500000000060512165105545016241 0ustar robertcrobertc00000000000000PYTHONPATH:=$(shell pwd)/lib:${PYTHONPATH} PYTHON ?= python all: check check: PYTHONPATH=$(PYTHONPATH) $(PYTHON) -m testtools.run \ fixtures.test_suite clean: find . -name '*.pyc' -print0 | xargs -0 rm -f TAGS: lib/fixtures/*.py lib/fixtures/tests/*.py ctags -e -R lib/fixtures/ tags: lib/fixtures/*.py lib/fixtures/tests/*.py ctags -R lib/fixtures/ .PHONY: all check clean fixtures-0.3.14/NEWS0000664000175000017500000001546312203313171015275 0ustar robertcrobertc00000000000000---------------------- fixtures release notes ---------------------- NEXT ~~~~ 0.3.14 ~~~~~~ CHANGES ------- * ``FakePopen`` can now override the returncode attribute. (Robert Collins) 0.3.13 ~~~~~~ CHANGES ------- * Documentation hopefully covers ``TestWithFixtures`` a little better. (Robert Collins, #1102688) * ``FakePopen`` now accepts all the parameters available in Python 2.7. (Robert Collins) * ``FakePopen`` now only passes parameters to the get_info routine if the caller supplied them. (Robert Collins) * ``setup.py`` now lists the ``testtools`` dependency which was missing. (Robert Collins, #1103823) 0.3.12 ~~~~~~ Brown bag fix up of StringStream from 0.3.11. 0.3.11 ~~~~~~ CHANGES ------- * ``DetailStream`` was ambiguous about whether it handled bytes or characters, which matters a lot for Python3. It now is deprecated with ByteStream and StringStream replacing it. (Robert Collins) * Fixtures is now Python3 compatible. (Robert Collins) * ``FakeLogger`` has been split out into a ``LogHandler`` fixture that can inject arbitrary handlers, giving more flexability. (Jonathan Lange) * pydoc is recommended as a source of info about fixtures. (Robert Collins, #812845) * The docs for fixtures have been updated to cover the full API. (Robert Collins, #1071649) 0.3.10 ~~~~~~ New ``DetailStream`` fixture to add file-like object content to testtools details, cleanup logic factored out into a CallMany class. CHANGES: * Add ``join`` method to ``TempDir`` to more readily get paths relative to a temporary directory. (Jonathan Lange) * Factor out new ``CallMany`` class to isolate the cleanup logic. (Robert Collins) * New ``DetailStream`` fixture to add file-like object content to testtools details. This allows for easy capture of sys.stdout and sys.stderr for example. (Clark Boylan) 0.3.9 ~~~~~ New ``TempHomeDir`` fixture and more log output included in ``FakeLogger``. CHANGES: * New TempHomeDir fixture to create and activate a temporary home directory. (James Westby) * ``FakeLogger`` now includes the output from python logging - the .output attribute as a content object in getDetails, so the logs will automatically be included in failed test output (or other similar circumstances). (Francesco Banconi) 0.3.8 ~~~~~ Simpler names for a number of fixtures, and two new fixtures NestedTempfile and Timeout. See the manual for more information. CHANGES: * EnvironmentVariable now upcalls via super(). (Jonathan Lange, #881120) * EnvironmentVariableFixture, LoggerFixture, PopenFixture renamed to EnvironmentVariable, FakeLogger and FakePopen respectively. All are still available under their old, deprecated names. (Jonathan Lange, #893539) * gather_details fixed to handle the case where two child fixtures have the same detail name. (Jonathan Lange, #895652) * ``NestedTempfile`` which will change the default path for tempfile temporary file / directory creation. (Gavin Panella) * New Timeout fixture. (Martin Pool) 0.3.7 ~~~~~ CHANGES: * New fixture LoggerFixture which replaces a logging module logger. (Free Ekanayaka) * On Python 2.7 and above the _fixtures tests are no longer run twice. (Robert Collins) * Python 3 now supported. (Jonathan Lange, #816665) * ``TempDir`` supports creating the temporary directory under a specific path. An example of where this is useful would be if you have some specific long lived temporary items and need to avoid running afoul of tmpreaper. (Robert Collins, #830757) * Updated to match the changed ``gather_details`` API in testtools. See #801027. This is an incompatible change in testtools and requires testtools 0.9.12. (Jonathan Lange) 0.3.6 ~~~~~ Another small API break - sorry. Fixture.getDetails no longer returns the internal details dict (self._details). Access that directly if needed. It now looks for details in used fixtures and returns those as well as details added directly. CHANGES: * Details from child fixtures for both Fixture and TestWithFixtures no longer quash same-named details if testtools 0.9.11 is available (for the gather_details helper). (Gavin Panella, #796253) * Fixture.getDetails will now return all the details of fixtures added using useFixture. (RobertCollins, #780806) * New fixture ``PackagePathEntry`` which patches the path of an existing package, allowing importing part of it from another directory. (Robert Collins) * New fixture ``PythonPathEntry`` which patches sys.path. (Robert Collins, #737503) * Test failure on some setups in test_cleanUp_raise_first_false_callscleanups_returns_exceptions. (Gavin Panella, #796249) * Testtools 0.9.8 is now a minimum requirement. (Gavin Panella) 0.3.5 ~~~~~ CHANGES: * New fixture ``MonkeyPatch`` which patches (or deletes) an existing attribute and restores it afterwards. (Robert Collins) * New fixture ``PythonPackage`` which manages a temporary python package. (Robert Collins) * New fixture ``TempDir`` which manages a temporary directory. (Robert Collins) 0.3.4 ~~~~~ CHANGES: * Fixture now supports ``addDetail`` and provides a``getDetails`` call compatible with the ``testtools.TestCase`` calls. (Robert Collins, #640119) * New helper ``MethodFixture`` for wrapping an object similarly to ``FunctionFixture`` (Robert Collins) 0.3.3 ~~~~~ Fixtures now have a ``useFixture`` method as well, making nesting of fixtures trivial. CHANGES: * New method ``Fixture.useFixture`` allowing fixtures to compose other fixtures. (Robert Collins) 0.3.2 ~~~~~ Point release adding new EnvironmentVariableFixture for controlling environment variables. CHANGES: * New EnvironmentVariableFixture which can set or unset environment variables. (Robert Collins) 0.3.1 ~~~~~ Point release adding experimental PopenFixture. CHANGES: * Experimental PopenFixture providing a test double for testing code that runs external processes. (Robert Collins) 0.3 ~~~ This release slightly breaks compatability in order to get the cleanUp API really solid : it now will report correctly with testtools 0.9.7, and generally does the right thing by default. Apologies to anyone affected by this churn, but I felt the cleanness of the API was worth it. CHANGES: * When multiple exceptions are being raised from cleanUp, MultipleExceptions is used to report them all. This is preferentially imported from testtools and failing that defined locally. See testtools 0.9.7 for its use. (Robert Collins) 0.2 ~~~ CHANGES: * Exceptions raised during cleanup are no longer silently swallowed. They are returned in a list by default, or the first one is raised if raise_first is supplied. The TestCase glue code passes raise_first to cleanUp. (Robert Collins, #629032) 0.1 ~~~ CHANGES: * Created project. The primary interfaces are ``fixtures.TestWithFixtures`` and ``fixtures.Fixture``. Documentation is primarily in README. (Robert Collins) fixtures-0.3.14/README0000664000175000017500000004010012203273366015453 0ustar robertcrobertc00000000000000************************************************************* fixtures: Fixtures with cleanups for testing and convenience. ************************************************************* Copyright (c) 2010, Robert Collins Licensed under either the Apache License, Version 2.0 or the BSD 3-clause license at the users choice. A copy of both licenses are available in the project source as Apache-2.0 and BSD. You may not use this file except in compliance with one of these two licences. Unless required by applicable law or agreed to in writing, software distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the license you chose for the specific language governing permissions and limitations under that license. Fixtures defines a Python contract for reusable state / support logic, primarily for unit testing. Helper and adaption logic is included to make it easy to write your own fixtures using the fixtures contract. Glue code is provided that makes using fixtures that meet the Fixtures contract in unittest compatible test cases easy and straight forward. Dependencies ============ * Python 2.6+ This is the base language fixtures is written in and for. * testtools 0.9.22 or newer. testtools provides helpful glue functions for the details API used to report information about a fixture (whether its used in a testing or production environment). For use in a unit test suite using the included glue, one of: * Python 2.7+ * unittest2 * bzrlib.tests * Or any other test environment that supports TestCase.addCleanup. Writing your own glue code is easy, or you can simply use Fixtures directly without any support code. To run the test suite for fixtures, testtools is needed. Why Fixtures ============ Standard Python unittest.py provides no obvious method for making and reusing state needed in a test case other than by adding a method on the test class. This scales poorly - complex helper functions propogating up a test class hierarchy is a regular pattern when this is done. Mocking while a great tool doesn't itself prevent this (and helpers to mock complex things can accumulate in the same way if placed on the test class). By defining a uniform contract where helpers have no dependency on the test class we permit all the regular code hygiene activities to take place without the distorting influence of being in a class hierarchy that is modelling an entirely different thing - which is what helpers on a TestCase suffer from. About Fixtures ============== A Fixture represents some state. Each fixture has attributes on it that are specific to the fixture. For instance, a fixture representing a directory that can be used for temporary files might have a attribute 'path'. Most fixtures have complete ``pydoc`` documentation, so be sure to check ``pydoc fixtures`` for usage information. Creating Fixtures ================= Minimally, subclass Fixture, define setUp to initialize your state and schedule a cleanup for when cleanUp is called and you're done:: >>> import unittest >>> import fixtures >>> class NoddyFixture(fixtures.Fixture): ... def setUp(self): ... super(NoddyFixture, self).setUp() ... self.frobnozzle = 42 ... self.addCleanup(delattr, self, 'frobnozzle') This will initialize frobnozzle when setUp is called, and when cleanUp is called get rid of the frobnozzle attribute. If your fixture has diagnostic data - for instance the log file of an application server, or log messages, it can expose that by creating a content object (``testtools.content.Content``) and calling ``addDetail``. >>> from testtools.content import text_content >>> class WithLog(fixtures.Fixture): ... def setUp(self): ... super(WithLog, self).setUp() ... self.addDetail('message', text_content('foo bar baz')) The method ``useFixture`` will use another fixture, call ``setUp`` on it, call ``self.addCleanup(thefixture.cleanUp)``, attach any details from it and return the fixture. This allows simple composition of different fixtures. >>> class ReusingFixture(fixtures.Fixture): ... def setUp(self): ... super(ReusingFixture, self).setUp() ... self.noddy = self.useFixture(NoddyFixture()) There is a helper for adapting a function or function pair into Fixtures. it puts the result of the function in fn_result:: >>> import os.path >>> import shutil >>> import tempfile >>> def setup_function(): ... return tempfile.mkdtemp() >>> def teardown_function(fixture): ... shutil.rmtree(fixture) >>> fixture = fixtures.FunctionFixture(setup_function, teardown_function) >>> fixture.setUp() >>> print (os.path.isdir(fixture.fn_result)) True >>> fixture.cleanUp() This can be expressed even more pithily: >>> fixture = fixtures.FunctionFixture(tempfile.mkdtemp, shutil.rmtree) >>> fixture.setUp() >>> print (os.path.isdir(fixture.fn_result)) True >>> fixture.cleanUp() Another variation is MethodFixture which is useful for adapting alternate fixture implementations to Fixture:: >>> class MyServer: ... def start(self): ... pass ... def stop(self): ... pass >>> server = MyServer() >>> fixture = fixtures.MethodFixture(server, server.start, server.stop) The Fixture API =============== The example above introduces some of the Fixture API. In order to be able to clean up after a fixture has been used, all fixtures define a ``cleanUp`` method which should be called when a fixture is finished with. Because its nice to be able to build a particular set of related fixtures in advance of using them, fixtures also have define a ``setUp`` method which should be called before trying to use them. One common desire with fixtures that are expensive to create is to reuse them in many test cases; to support this the base Fixture also defines a ``reset`` which calls ``self.cleanUp(); self.setUp()``. Fixtures that can more efficiently make themselves reusable should override this method. This can then be used with multiple test state via things like ``testresources``, ``setUpClass``, or ``setUpModule``. When using a fixture with a test you can manually call the setUp and cleanUp methods. More convenient though is to use the included glue from ``fixtures.TestWithFixtures`` which provides a mixin defining ``useFixture`` (camel case because unittest is camel case throughout) method. It will call setUp on the fixture, call self.addCleanup(fixture) to schedule a cleanup, and return the fixture. This lets one write:: >>> import testtools >>> import unittest Note that we use testtools TestCase here as we need to guarantee a TestCase.addCleanup method in this doctest. Unittest2 - Python2.7 and above - also have ``addCleanup``. testtools has it's own implementation of ``useFixture`` so there is no need to use ``fixtures.TestWithFixtures`` with ``testtools.TestCase``. >>> class NoddyTest(testtools.TestCase, fixtures.TestWithFixtures): ... def test_example(self): ... fixture = self.useFixture(NoddyFixture()) ... self.assertEqual(42, fixture.frobnozzle) >>> result = unittest.TestResult() >>> _ = NoddyTest('test_example').run(result) >>> print (result.wasSuccessful()) True Fixtures implement the context protocol, so you can also use a fixture as a context manager:: >>> with fixtures.FunctionFixture(setup_function, teardown_function) as fixture: ... print (os.path.isdir(fixture.fn_result)) True When multiple cleanups error, fixture.cleanUp() will raise a wrapper exception rather than choosing an arbitrary single exception to raise:: >>> import sys >>> from fixtures.fixture import MultipleExceptions >>> class BrokenFixture(fixtures.Fixture): ... def setUp(self): ... fixtures.Fixture.setUp(self) ... self.addCleanup(lambda:1/0) ... self.addCleanup(lambda:1/0) >>> fixture = BrokenFixture() >>> fixture.setUp() >>> try: ... fixture.cleanUp() ... except MultipleExceptions: ... exc_info = sys.exc_info() >>> print (exc_info[1].args[0][0].__name__) ZeroDivisionError Fixtures often expose diagnostic details that can be useful for tracking down issues. The ``getDetails`` method will return a dict of all the attached details. Each detail object is an instance of ``testtools.content.Content``. >>> with WithLog() as l: ... print(l.getDetails()['message'].as_text()) foo bar baz Shared Dependencies +++++++++++++++++++ A common use case within complex environments is having some fixtures shared by other ones. Consider the case of testing using a ``TempDir`` with two fixtures built on top of it; say a small database and a web server. Writing either one is nearly trivial. However handling ``reset()`` correctly is hard: both the database and web server would reasonably expect to be able to discard operating system resources they may have open within the temporary directory before its removed. A recursive ``reset()`` implementation would work for one, but not both. Calling ``reset()`` on the ``TempDir`` instance between each test is probably desirable but we don't want to have to do a complete ``cleanUp`` of the higher layer fixtures (which would make the ``TempDir`` be unused and trivially resettable. We have a few options available to us. Imagine that the webserver does not depend on the DB fixture in any way - we just want the webserver and DB fixture to coexist in the same tempdir. A simple option is to just provide an explicit dependency fixture for the higher layer fixtures to use. This pushes complexity out of the core and onto users of fixtures:: >>> class WithDep(fixtures.Fixture): ... def __init__(self, tempdir, dependency_fixture): ... super(WithDep, self).__init__() ... self.tempdir = tempdir ... self.dependency_fixture = dependency_fixture ... def setUp(self): ... super(WithDep, self).setUp() ... self.addCleanup(self.dependency_fixture.cleanUp) ... self.dependency_fixture.setUp() ... # we assume that at this point self.tempdir is usable. >>> DB = WithDep >>> WebServer = WithDep >>> tempdir = fixtures.TempDir() >>> db = DB(tempdir, tempdir) >>> server = WebServer(tempdir, db) >>> server.setUp() >>> server.cleanUp() Another option is to write the fixtures to gracefully handle a dependency being reset underneath them. This is insufficient if the fixtures would block the dependency resetting (for instance by holding file locks open in a tempdir - on Windows this will prevent the directory being deleted). Another approach which ``fixtures`` neither helps nor hinders is to raise a signal of some sort for each user of a fixture before it is reset. In the example here, ``TempDir`` might offer a subscribers attribute that both the DB and web server would be registered in. Calling ``reset`` or ``cleanUp`` on the tempdir would trigger a callback to all the subscribers; the DB and web server reset methods would look something like: >>> def reset(self): ... if not self._cleaned: ... self._clean() (Their action on the callback from the tempdir would be to do whatever work was needed and set ``self._cleaned``.) This approach has the (perhaps) suprising effect that resetting the webserver may reset the DB - if the webserver were to be depending on ``tempdir.reset`` as a way to reset the webservers state. Another approach which is not currently implemented is to provide an object graph of dependencies and a reset mechanism that can traverse that, along with a separation between 'reset starting' and 'reset finishing' - the DB and webserver would both have their ``reset_starting`` methods called, then the tempdir would be reset, and finally the DB and webserver would have ``reset_finishing`` called. Stock Fixtures ============== In addition to the Fixture, FunctionFixture and MethodFixture classes fixtures includes a number of precanned fixtures. The API docs for fixtures will list the complete set of these, should the dcs be out of date or not to hand. ByteStream ++++++++++ Trivial adapter to make a BytesIO (though it may in future auto-spill to disk for large content) and expose that as a detail object, for automatic inclusion in test failure descriptions. Very useful in combination with MonkeyPatch. >>> fixture = fixtures.StringStream('my-content') >>> fixture.setUp() >>> with fixtures.MonkeyPatch('sys.something', fixture.stream): ... pass >>> fixture.cleanUp() EnvironmentVariable +++++++++++++++++++ Isolate your code from environmental variables, delete them or set them to a new value. >>> fixture = fixtures.EnvironmentVariable('HOME') FakeLogger ++++++++++ Isolate your code from an external logging configuration - so that your test gets the output from logged messages, but they don't go to e.g. the console. >>> fixture = fixtures.FakeLogger() FakePopen +++++++++ Pretend to run an external command rather than needing it to be present to run tests. >>> from testtools.compat import BytesIO >>> fixture = fixtures.FakePopen(lambda _:{'stdout': BytesIO('foobar')}) MonkeyPatch +++++++++++ Control the value of a named python attribute. >>> def fake_open(path, mode): ... pass >>> fixture = fixtures.MonkeyPatch('__builtin__.open', fake_open) NestedTempfile ++++++++++++++ Change the default directory that the tempfile module places temporary files and directories in. This can be useful for containing the noise created by code which doesn't clean up its temporary files. This does not affect temporary file creation where an explicit containing directory was provided. >>> fixture = fixtures.NestedTempfile() PackagePathEntry ++++++++++++++++ Adds a single directory to the path for an existing python package. This adds to the package.__path__ list. If the directory is already in the path, nothing happens, if it isn't then it is added on setUp and removed on cleanUp. >>> fixture = fixtures.PackagePathEntry('package/name', '/foo/bar') PythonPackage +++++++++++++ Creates a python package directory. Particularly useful for testing code that dynamically loads packages/modules, or for mocking out the command line entry points to Python programs. >>> fixture = fixtures.PythonPackage('foo.bar', [('quux.py', '')]) PythonPathEntry +++++++++++++++ Adds a single directory to sys.path. If the directory is already in the path, nothing happens, if it isn't then it is added on setUp and removed on cleanUp. >>> fixture = fixtures.PythonPathEntry('/foo/bar') StringStream ++++++++++++ Trivial adapter to make a StringIO (though it may in future auto-spill to disk for large content) and expose that as a detail object, for automatic inclusion in test failure descriptions. Very useful in combination with MonkeyPatch. >>> fixture = fixtures.StringStream('stdout') >>> fixture.setUp() >>> with fixtures.MonkeyPatch('sys.stdout', fixture.stream): ... pass >>> fixture.cleanUp() TempDir +++++++ Create a temporary directory and clean it up later. >>> fixture = fixtures.TempDir() The created directory is stored in the ``path`` attribute of the fixture after setUp. TempHomeDir +++++++++++ Create a temporary directory and set it as $HOME in the environment. >>> fixture = fixtures.TempHomeDir() The created directory is stored in the ``path`` attribute of the fixture after setUp. The environment will now have $HOME set to the same path, and the value will be returned to its previous value after tearDown. Timeout +++++++ Aborts if the covered code takes more than a specified number of whole wall-clock seconds. There are two possibilities, controlled by the 'gentle' argument: when gentle, an exception will be raised and the test (or other covered code) will fail. When not gentle, the entire process will be terminated, which is less clean, but more likely to break hangs where no Python code is running. *Caution:* Only one timeout can be active at any time across all threads in a single process. Using more than one has undefined results. (This could be improved by chaining alarms.) *Note:* Currently supported only on Unix because it relies on the ``alarm`` system call. fixtures-0.3.14/setup.py0000775000175000017500000000230112203313277016305 0ustar robertcrobertc00000000000000#!/usr/bin/env python from distutils.core import setup import os.path description = open(os.path.join(os.path.dirname(__file__), 'README'), 'rt').read() setup(name="fixtures", version="0.3.14", description="Fixtures, reusable state for writing clean tests and more.", keywords="fixture fixtures unittest contextmanager", long_description=description, maintainer="Robert Collins", maintainer_email="robertc@robertcollins.net", url="https://launchpad.net/python-fixtures", packages=['fixtures', 'fixtures._fixtures', 'fixtures.tests', 'fixtures.tests._fixtures'], package_dir = {'':'lib'}, classifiers = [ 'Development Status :: 6 - Mature', 'Intended Audience :: Developers', 'License :: OSI Approved :: BSD License', 'License :: OSI Approved :: Apache Software License', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 3', 'Topic :: Software Development :: Quality Assurance', 'Topic :: Software Development :: Testing', ], requires = [ 'testtools', ], ) fixtures-0.3.14/PKG-INFO0000664000175000017500000005042212203313314015664 0ustar robertcrobertc00000000000000Metadata-Version: 1.1 Name: fixtures Version: 0.3.14 Summary: Fixtures, reusable state for writing clean tests and more. Home-page: https://launchpad.net/python-fixtures Author: Robert Collins Author-email: robertc@robertcollins.net License: UNKNOWN Description: ************************************************************* fixtures: Fixtures with cleanups for testing and convenience. ************************************************************* Copyright (c) 2010, Robert Collins Licensed under either the Apache License, Version 2.0 or the BSD 3-clause license at the users choice. A copy of both licenses are available in the project source as Apache-2.0 and BSD. You may not use this file except in compliance with one of these two licences. Unless required by applicable law or agreed to in writing, software distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the license you chose for the specific language governing permissions and limitations under that license. Fixtures defines a Python contract for reusable state / support logic, primarily for unit testing. Helper and adaption logic is included to make it easy to write your own fixtures using the fixtures contract. Glue code is provided that makes using fixtures that meet the Fixtures contract in unittest compatible test cases easy and straight forward. Dependencies ============ * Python 2.6+ This is the base language fixtures is written in and for. * testtools 0.9.22 or newer. testtools provides helpful glue functions for the details API used to report information about a fixture (whether its used in a testing or production environment). For use in a unit test suite using the included glue, one of: * Python 2.7+ * unittest2 * bzrlib.tests * Or any other test environment that supports TestCase.addCleanup. Writing your own glue code is easy, or you can simply use Fixtures directly without any support code. To run the test suite for fixtures, testtools is needed. Why Fixtures ============ Standard Python unittest.py provides no obvious method for making and reusing state needed in a test case other than by adding a method on the test class. This scales poorly - complex helper functions propogating up a test class hierarchy is a regular pattern when this is done. Mocking while a great tool doesn't itself prevent this (and helpers to mock complex things can accumulate in the same way if placed on the test class). By defining a uniform contract where helpers have no dependency on the test class we permit all the regular code hygiene activities to take place without the distorting influence of being in a class hierarchy that is modelling an entirely different thing - which is what helpers on a TestCase suffer from. About Fixtures ============== A Fixture represents some state. Each fixture has attributes on it that are specific to the fixture. For instance, a fixture representing a directory that can be used for temporary files might have a attribute 'path'. Most fixtures have complete ``pydoc`` documentation, so be sure to check ``pydoc fixtures`` for usage information. Creating Fixtures ================= Minimally, subclass Fixture, define setUp to initialize your state and schedule a cleanup for when cleanUp is called and you're done:: >>> import unittest >>> import fixtures >>> class NoddyFixture(fixtures.Fixture): ... def setUp(self): ... super(NoddyFixture, self).setUp() ... self.frobnozzle = 42 ... self.addCleanup(delattr, self, 'frobnozzle') This will initialize frobnozzle when setUp is called, and when cleanUp is called get rid of the frobnozzle attribute. If your fixture has diagnostic data - for instance the log file of an application server, or log messages, it can expose that by creating a content object (``testtools.content.Content``) and calling ``addDetail``. >>> from testtools.content import text_content >>> class WithLog(fixtures.Fixture): ... def setUp(self): ... super(WithLog, self).setUp() ... self.addDetail('message', text_content('foo bar baz')) The method ``useFixture`` will use another fixture, call ``setUp`` on it, call ``self.addCleanup(thefixture.cleanUp)``, attach any details from it and return the fixture. This allows simple composition of different fixtures. >>> class ReusingFixture(fixtures.Fixture): ... def setUp(self): ... super(ReusingFixture, self).setUp() ... self.noddy = self.useFixture(NoddyFixture()) There is a helper for adapting a function or function pair into Fixtures. it puts the result of the function in fn_result:: >>> import os.path >>> import shutil >>> import tempfile >>> def setup_function(): ... return tempfile.mkdtemp() >>> def teardown_function(fixture): ... shutil.rmtree(fixture) >>> fixture = fixtures.FunctionFixture(setup_function, teardown_function) >>> fixture.setUp() >>> print (os.path.isdir(fixture.fn_result)) True >>> fixture.cleanUp() This can be expressed even more pithily: >>> fixture = fixtures.FunctionFixture(tempfile.mkdtemp, shutil.rmtree) >>> fixture.setUp() >>> print (os.path.isdir(fixture.fn_result)) True >>> fixture.cleanUp() Another variation is MethodFixture which is useful for adapting alternate fixture implementations to Fixture:: >>> class MyServer: ... def start(self): ... pass ... def stop(self): ... pass >>> server = MyServer() >>> fixture = fixtures.MethodFixture(server, server.start, server.stop) The Fixture API =============== The example above introduces some of the Fixture API. In order to be able to clean up after a fixture has been used, all fixtures define a ``cleanUp`` method which should be called when a fixture is finished with. Because its nice to be able to build a particular set of related fixtures in advance of using them, fixtures also have define a ``setUp`` method which should be called before trying to use them. One common desire with fixtures that are expensive to create is to reuse them in many test cases; to support this the base Fixture also defines a ``reset`` which calls ``self.cleanUp(); self.setUp()``. Fixtures that can more efficiently make themselves reusable should override this method. This can then be used with multiple test state via things like ``testresources``, ``setUpClass``, or ``setUpModule``. When using a fixture with a test you can manually call the setUp and cleanUp methods. More convenient though is to use the included glue from ``fixtures.TestWithFixtures`` which provides a mixin defining ``useFixture`` (camel case because unittest is camel case throughout) method. It will call setUp on the fixture, call self.addCleanup(fixture) to schedule a cleanup, and return the fixture. This lets one write:: >>> import testtools >>> import unittest Note that we use testtools TestCase here as we need to guarantee a TestCase.addCleanup method in this doctest. Unittest2 - Python2.7 and above - also have ``addCleanup``. testtools has it's own implementation of ``useFixture`` so there is no need to use ``fixtures.TestWithFixtures`` with ``testtools.TestCase``. >>> class NoddyTest(testtools.TestCase, fixtures.TestWithFixtures): ... def test_example(self): ... fixture = self.useFixture(NoddyFixture()) ... self.assertEqual(42, fixture.frobnozzle) >>> result = unittest.TestResult() >>> _ = NoddyTest('test_example').run(result) >>> print (result.wasSuccessful()) True Fixtures implement the context protocol, so you can also use a fixture as a context manager:: >>> with fixtures.FunctionFixture(setup_function, teardown_function) as fixture: ... print (os.path.isdir(fixture.fn_result)) True When multiple cleanups error, fixture.cleanUp() will raise a wrapper exception rather than choosing an arbitrary single exception to raise:: >>> import sys >>> from fixtures.fixture import MultipleExceptions >>> class BrokenFixture(fixtures.Fixture): ... def setUp(self): ... fixtures.Fixture.setUp(self) ... self.addCleanup(lambda:1/0) ... self.addCleanup(lambda:1/0) >>> fixture = BrokenFixture() >>> fixture.setUp() >>> try: ... fixture.cleanUp() ... except MultipleExceptions: ... exc_info = sys.exc_info() >>> print (exc_info[1].args[0][0].__name__) ZeroDivisionError Fixtures often expose diagnostic details that can be useful for tracking down issues. The ``getDetails`` method will return a dict of all the attached details. Each detail object is an instance of ``testtools.content.Content``. >>> with WithLog() as l: ... print(l.getDetails()['message'].as_text()) foo bar baz Shared Dependencies +++++++++++++++++++ A common use case within complex environments is having some fixtures shared by other ones. Consider the case of testing using a ``TempDir`` with two fixtures built on top of it; say a small database and a web server. Writing either one is nearly trivial. However handling ``reset()`` correctly is hard: both the database and web server would reasonably expect to be able to discard operating system resources they may have open within the temporary directory before its removed. A recursive ``reset()`` implementation would work for one, but not both. Calling ``reset()`` on the ``TempDir`` instance between each test is probably desirable but we don't want to have to do a complete ``cleanUp`` of the higher layer fixtures (which would make the ``TempDir`` be unused and trivially resettable. We have a few options available to us. Imagine that the webserver does not depend on the DB fixture in any way - we just want the webserver and DB fixture to coexist in the same tempdir. A simple option is to just provide an explicit dependency fixture for the higher layer fixtures to use. This pushes complexity out of the core and onto users of fixtures:: >>> class WithDep(fixtures.Fixture): ... def __init__(self, tempdir, dependency_fixture): ... super(WithDep, self).__init__() ... self.tempdir = tempdir ... self.dependency_fixture = dependency_fixture ... def setUp(self): ... super(WithDep, self).setUp() ... self.addCleanup(self.dependency_fixture.cleanUp) ... self.dependency_fixture.setUp() ... # we assume that at this point self.tempdir is usable. >>> DB = WithDep >>> WebServer = WithDep >>> tempdir = fixtures.TempDir() >>> db = DB(tempdir, tempdir) >>> server = WebServer(tempdir, db) >>> server.setUp() >>> server.cleanUp() Another option is to write the fixtures to gracefully handle a dependency being reset underneath them. This is insufficient if the fixtures would block the dependency resetting (for instance by holding file locks open in a tempdir - on Windows this will prevent the directory being deleted). Another approach which ``fixtures`` neither helps nor hinders is to raise a signal of some sort for each user of a fixture before it is reset. In the example here, ``TempDir`` might offer a subscribers attribute that both the DB and web server would be registered in. Calling ``reset`` or ``cleanUp`` on the tempdir would trigger a callback to all the subscribers; the DB and web server reset methods would look something like: >>> def reset(self): ... if not self._cleaned: ... self._clean() (Their action on the callback from the tempdir would be to do whatever work was needed and set ``self._cleaned``.) This approach has the (perhaps) suprising effect that resetting the webserver may reset the DB - if the webserver were to be depending on ``tempdir.reset`` as a way to reset the webservers state. Another approach which is not currently implemented is to provide an object graph of dependencies and a reset mechanism that can traverse that, along with a separation between 'reset starting' and 'reset finishing' - the DB and webserver would both have their ``reset_starting`` methods called, then the tempdir would be reset, and finally the DB and webserver would have ``reset_finishing`` called. Stock Fixtures ============== In addition to the Fixture, FunctionFixture and MethodFixture classes fixtures includes a number of precanned fixtures. The API docs for fixtures will list the complete set of these, should the dcs be out of date or not to hand. ByteStream ++++++++++ Trivial adapter to make a BytesIO (though it may in future auto-spill to disk for large content) and expose that as a detail object, for automatic inclusion in test failure descriptions. Very useful in combination with MonkeyPatch. >>> fixture = fixtures.StringStream('my-content') >>> fixture.setUp() >>> with fixtures.MonkeyPatch('sys.something', fixture.stream): ... pass >>> fixture.cleanUp() EnvironmentVariable +++++++++++++++++++ Isolate your code from environmental variables, delete them or set them to a new value. >>> fixture = fixtures.EnvironmentVariable('HOME') FakeLogger ++++++++++ Isolate your code from an external logging configuration - so that your test gets the output from logged messages, but they don't go to e.g. the console. >>> fixture = fixtures.FakeLogger() FakePopen +++++++++ Pretend to run an external command rather than needing it to be present to run tests. >>> from testtools.compat import BytesIO >>> fixture = fixtures.FakePopen(lambda _:{'stdout': BytesIO('foobar')}) MonkeyPatch +++++++++++ Control the value of a named python attribute. >>> def fake_open(path, mode): ... pass >>> fixture = fixtures.MonkeyPatch('__builtin__.open', fake_open) NestedTempfile ++++++++++++++ Change the default directory that the tempfile module places temporary files and directories in. This can be useful for containing the noise created by code which doesn't clean up its temporary files. This does not affect temporary file creation where an explicit containing directory was provided. >>> fixture = fixtures.NestedTempfile() PackagePathEntry ++++++++++++++++ Adds a single directory to the path for an existing python package. This adds to the package.__path__ list. If the directory is already in the path, nothing happens, if it isn't then it is added on setUp and removed on cleanUp. >>> fixture = fixtures.PackagePathEntry('package/name', '/foo/bar') PythonPackage +++++++++++++ Creates a python package directory. Particularly useful for testing code that dynamically loads packages/modules, or for mocking out the command line entry points to Python programs. >>> fixture = fixtures.PythonPackage('foo.bar', [('quux.py', '')]) PythonPathEntry +++++++++++++++ Adds a single directory to sys.path. If the directory is already in the path, nothing happens, if it isn't then it is added on setUp and removed on cleanUp. >>> fixture = fixtures.PythonPathEntry('/foo/bar') StringStream ++++++++++++ Trivial adapter to make a StringIO (though it may in future auto-spill to disk for large content) and expose that as a detail object, for automatic inclusion in test failure descriptions. Very useful in combination with MonkeyPatch. >>> fixture = fixtures.StringStream('stdout') >>> fixture.setUp() >>> with fixtures.MonkeyPatch('sys.stdout', fixture.stream): ... pass >>> fixture.cleanUp() TempDir +++++++ Create a temporary directory and clean it up later. >>> fixture = fixtures.TempDir() The created directory is stored in the ``path`` attribute of the fixture after setUp. TempHomeDir +++++++++++ Create a temporary directory and set it as $HOME in the environment. >>> fixture = fixtures.TempHomeDir() The created directory is stored in the ``path`` attribute of the fixture after setUp. The environment will now have $HOME set to the same path, and the value will be returned to its previous value after tearDown. Timeout +++++++ Aborts if the covered code takes more than a specified number of whole wall-clock seconds. There are two possibilities, controlled by the 'gentle' argument: when gentle, an exception will be raised and the test (or other covered code) will fail. When not gentle, the entire process will be terminated, which is less clean, but more likely to break hangs where no Python code is running. *Caution:* Only one timeout can be active at any time across all threads in a single process. Using more than one has undefined results. (This could be improved by chaining alarms.) *Note:* Currently supported only on Unix because it relies on the ``alarm`` system call. Keywords: fixture fixtures unittest contextmanager Platform: UNKNOWN Classifier: Development Status :: 6 - Mature Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Topic :: Software Development :: Quality Assurance Classifier: Topic :: Software Development :: Testing Requires: testtools