Welcome to systemfixtures’s documentation!¶
Overview¶
A collection of Python fixtures to fake out various system resources (processes, users, groups, etc.).
Each fake resource typically behaves as an “overlay” on the real resource, in that it can be programmed with fake behavior for a set of inputs, but falls back to the real behavior for the rest. See the examples below for more information.
The implementation is mostly built on top of the basic MonkeyPatch fixture.
Examples¶
Users¶
The FakeUsers
fixture lets you add fake system users, that do not
exist for real, but behave the same as real ones:
>>> import pwd
>>> from systemfixtures import FakeUsers
>>> users = FakeUsers()
>>> users.setUp()
>>> pwd.getpwnam("foo")
Traceback (most recent call last):
...
KeyError: 'getpwnam(): name not found: foo'
>>> users.add("foo", 123)
>>> info = pwd.getpwnam("foo")
>>> info.pw_uid
123
>>> users.cleanUp()
Groups¶
The FakeGroups
fixture lets you add fake system groups, that do not
exist for real, but behave the same as real ones:
>>> import grp
>>> from systemfixtures import FakeGroups
>>> groups = FakeGroups()
>>> groups.setUp()
>>> grp.getgrnam("foo")
Traceback (most recent call last):
...
KeyError: 'getgrnam(): name not found: foo'
>>> groups.add("foo", 123)
>>> info = grp.getgrnam("foo")
>>> info.gr_gid
123
>>> groups.cleanUp()
Filesystem¶
The FakeFilesystem
fixture lets you add a temporary directory as
filesystem “overlay”. You can declare certain paths as belonging
to the overlay, and filesystem APIs like open()
, os.mkdir()
,
os.chown()
, os.chmod()
and os.stat()
will be transparently
redirected to act on the temporary directory instead of the real filesystem
path:
>>> import os
>>> import tempfile
>>> from systemfixtures import FakeFilesystem
>>> filesystem = FakeFilesystem()
>>> filesystem.setUp()
Trying to create a directory under the root one will fail, since we are running as unprivileged user:
>>> os.mkdir("/foo")
Traceback (most recent call last):
...
PermissionError: [Errno 13] Permission denied: '/foo'
However, if we add the directory path to the fake filesystem, it will be possible to create it as overlay directory:
>>> filesystem.add("/foo")
>>> os.mkdir("/foo")
>>> os.path.isdir("/foo")
True
The overlay directory actually lives under the temporary tree of the fake filesystem fixture:
>>> filesystem.root.path.startswith(tempfile.gettempdir())
True
>>> os.listdir(filesystem.root.path)
['foo']
It’s possible to operate on the overlay directory as if it was a real top-level directory:
>>> with open("/foo/bar", "w") as fd:
... fd.write("Hello world!")
12
>>> with open("/foo/bar") as fd:
... fd.read()
'Hello world!'
>>> os.listdir("/foo")
['bar']
It’s possible to change the ownership of files in the overlay directory, even without superuser priviliges:
>>> os.chown("/foo/bar", 0, 0)
>>> os.chmod("/foo/bar", 0o600)
>>> info = os.stat("/foo/bar")
>>> info.st_uid, info.st_gid
(0, 0)
>>> oct(info.st_mode)
'0o100600'
>>> filesystem.cleanUp()
Network¶
The FakeNetwork
fixture is simply fixture-compatible adapter of
the requests-mock
package, which provides facilities to stub
out responses from the requests
package. For further details
see the official documentation.
>>> import requests
>>> from systemfixtures import FakeNetwork
>>> network = FakeNetwork()
>>> network.setUp()
>>> network.get("http://test.com", text="data")
<requests_mock.adapter._Matcher object at ...>
>>> response = requests.get("http://test.com")
>>> response.text
'data'
>>> network.cleanUp()
Time¶
The FakeTime
fixture is simply fixture-compatible adapter of
the fakesleep
package, which provides facilities to stub
out the API of time
package from the standard library. See
the external documentation
>>> import time
>>> from systemfixtures import FakeTime
>>> fake_time = FakeTime()
>>> fake_time.setUp()
>>> stamp1 = time.time()
>>> time.sleep(1)
>>> stamp2 = time.time()
Since sleep()
and time()
are fake, we get exactly 1.0:
>>> stamp2 - stamp1
1.0
>>> fake_time.cleanUp()
Processes¶
The FakeProcesses
fixture lets you fake out processes spawed with
subprocess.Popen
, and have custom Python code be executed instead.
You can both override available system executables, or add new ones are not available on the system:
>>> import io
>>> import subprocess
>>> from systemfixtures import FakeProcesses
>>> processes = FakeProcesses()
>>> processes.setUp()
>>> subprocess.check_output(["uname"])
b'Linux\n'
>>> def uname(proc_args):
... return {"stdout": io.BytesIO(b"Darwin\n")}
>>> processes.add(uname, name="uname")
>>> processes.uname
<function uname at ...>
>>> subprocess.check_output(["uname"])
b'Darwin\n'
>>> def foo(proc_args):
... return {"stdout": io.BytesIO(b"Hello world!")}
>>> processes.add(foo, name="foo")
>>> subprocess.check_output(["foo"])
b'Hello world!'
Some stock fake processes are provided as well:
wget¶
>>> from systemfixtures.processes import Wget
>>> processes.add(Wget())
>>> processes.wget.locations["http://foo"] = b"Hello world!"
>>> subprocess.check_output(["wget", "-O", "-", "http://foo"])
b'Hello world!'
systemctl¶
>>> from systemfixtures.processes import Systemctl
>>> processes.add(Systemctl())
>>> try:
... subprocess.check_output(["systemctl", "is-active", "foo"])
... except subprocess.CalledProcessError as error:
... error.output
b'inactive\n'
>>> subprocess.check_call(["systemctl", "start", "foo"])
0
>>> subprocess.check_output(["systemctl", "is-active", "foo"])
b'active\n'
>>> subprocess.check_call(["systemctl", "stop", "foo"])
0
>>> processes.systemctl.actions["foo"]
['start', 'stop']
dpkg¶
>>> from systemfixtures.processes import Dpkg
>>> processes.add(Dpkg())
>>> subprocess.check_call(["dpkg", "-i", "foo_1.0-1.deb"])
0
>>> processes.dpkg.actions["foo"]
['install']
>>> processes.cleanUp()
Threads¶
The FakeThreads
fixture lets you fake out threads spawed with the
threading
module, and partially simulate their behavior in a
synchronous way:
>>> import threading
>>> from systemfixtures import FakeThreads
>>> threads = FakeThreads()
>>> threads.setUp()
>>> calls = [None]
>>> thread = threading.Thread(target=calls.pop)
>>> thread.name
'fake-thread-0'
>>> thread.start()
>>> calls
[]
>>> thread.join()
>>> thread.isAlive()
False
It’s also possible to simulate a hung thread:
>>> threads.hang()
>>> thread = threading.Thread()
>>> thread.name
'fake-thread-1'
>>> thread.start()
>>> thread.join(timeout=60) # This returns immediately
>>> thread.isAlive()
True
Executables¶
The FakeExecutable
fixture lets you create temporary Python scripts
that can be configured to mimic the behavior of some real executable (for
instance listening to a port or emitting certain output):
>>> import stat
>>> from systemfixtures import FakeExecutable
>>> executable = FakeExecutable()
>>> executable.setUp()
>>> bool(os.stat(executable.path).st_mode & stat.S_IXUSR)
True
>>> executable.out('hello')
>>> subprocess.check_output([executable.path])
b'hello\n'