提交 1f7fff70 编写于 作者: C Cleber Rosa

Docs: document the test process environment

Each test executed in Avocado gets its own process.  But, there are a
few peculiarities that test writers may have to deal with, so let's
document those.
Signed-off-by: NCleber Rosa <crosa@redhat.com>
上级 ac552f85
......@@ -313,7 +313,13 @@ class TestRunner(object):
signal.signal(signal.SIGTERM, sigterm_handler)
# Replace STDIN (0) with the /dev/null's fd
# At this point, the original `sys.stdin` has already been
# closed and replaced with `os.devnull` by
# `multiprocessing.Process()` (not directly from Avocado
# code). Still, tests trying to use file descriptor 0 would
# be able to read from the tty, and would hang. Let's replace
# STDIN fd (0), with the same fd previously set by
# `multiprocessing.Process()`
os.dup2(sys.stdin.fileno(), 0)
instance = loader.load_test(test_factory)
......
......@@ -361,6 +361,72 @@ The instances should have:
.. [#f1] Avocado plugins can introduce additional test types.
Test execution environment
--------------------------
Each test is executed in a separate process. Due to how the
underlying operating system works, a lot of the attributes of the
parent process (the Avocado test **runner**) are passed down to the
test process.
On GNU/Linux systems, a child process should be *"an exact duplicate
of the parent process, except"* some items that are documented in
the ``fork(2)`` man page.
Besides those operating system exceptions, the Avocado test runner
changes the test process in the following ways:
1) The standard input (``STDIN``) is set to a :data:`null device
<os.devnull>`. This is truth both for :data:`sys.stdin` and for
file descriptor ``0``. Both will point to the same open null
device file.
2) The standard output (``STDOUT``), as in :data:`sys.stdout`, is
redirected so that it doesn't interfere with the test runner's own
output. All content written to the test's :data:`sys.stdout` will
be available in the logs under the ``output`` prefix.
.. warning:: The file descriptor ``1`` (AKA ``/dev/stdout``, AKA
``/proc/self/fd/1``, etc) is **not** currently
redirected for INSTRUMENTED tests. Any attempt to
write directly to the file descriptor will interfere
with the runner's own output stream. This behavior
will be addressed in a future version.
3) The standard error (``STDERR``), as in :data:`sys.stderr`, is
redirected so that it doesn't interfere with the test runner's own
errors. All content written to the test's :data:`sys.stderr` will
be available in the logs under the ``output`` prefix.
.. warning:: The file descriptor ``2`` (AKA ``/dev/stderr``, AKA
``/proc/self/fd/2``, etc) is **not** currently
redirected for INSTRUMENTED tests. Any attempt to
write directly to the file descriptor will interfere
with the runner's own error stream. This behavior
will be addressed in a future version.
4) A custom handler for signal ``SIGTERM`` which will simply raise an
exception (with the appropriate message) to be handled by the
Avocado test runner, stating the fact that the test was interrupted
by such a signal.
.. tip:: By following the backtrace that is given alongside the in
the test log (look for ``RuntimeError: Test interrupted
by SIGTERM``) a user can quickly grasp at which point the
test was interrupted.
.. note:: If the test handles ``SIGTERM`` differently and doesn't
finish the test process quickly enough, it will receive
then a ``SIGKILL`` which is supposed to definitely end
the test process.
5) A number of :ref:`environment variables
<environment-variables-for-tests>` that are set by Avocado, all
prefixed with ``AVOCADO_``.
If you want to see for yourself what is described here, you may want
to run the example test ``test_env.py`` and examine its log messages.
Pre and post plugins
====================
......
import os
import sys
from avocado import Test
class Env(Test):
def test(self):
"""
Logs information about the environment under which the test is executed
"""
pid = os.getpid()
p_dir = '/proc/%d' % pid
def get_proc_content(rel_path):
try:
return open(os.path.join(p_dir, rel_path)).read().strip()
except:
return "<NOT AVAILABLE>"
self.log.debug('Process ID: %s', pid)
self.log.debug('Current workding directory: %s', os.getcwd())
self.log.debug('Process "name" (comm): %s', get_proc_content('comm'))
raw_cmdline = get_proc_content('cmdline')
massaged_cmdline = raw_cmdline.replace('\0', ' ')
self.log.debug('Process "cmdline": %s', massaged_cmdline)
def log_std_io(name, std_io):
self.log.debug('%s:', name.upper())
self.log.debug(' sys.%s: %s', name, std_io)
self.log.debug(' sys.%s is a tty: %s', name, std_io.isatty())
if hasattr(std_io, 'fileno'):
self.log.debug(' fd: %s', std_io.fileno())
self.log.debug(' fd is tty: %s', os.isatty(std_io.fileno()))
else:
self.log.debug(' fd: not available')
self.log.debug(' fd is a tty: can not determine, most possibly *not* a tty')
log_std_io('stdin', sys.stdin)
log_std_io('stdout', sys.stdout)
log_std_io('stderr', sys.stdout)
fd_dir = '/proc/%s/fd' % pid
if os.path.isdir('/proc/%s/fd' % pid):
fds = os.listdir(fd_dir)
self.log.debug('Open file descriptors:')
for fd in fds:
fd_path = os.path.join(fd_dir, fd)
if os.path.islink(fd_path):
self.log.debug(" %s: %s", fd, os.readlink(fd_path))
self.log.debug('Environment variables (probably) set by Avocado:')
for k, v in os.environ.items():
if k.startswith('AVOCADO_'):
self.log.debug(' %s: %s', k, v)
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册