Skip to content

Commit

Permalink
Merge pull request #5901 from richtja/env_manipulation
Browse files Browse the repository at this point in the history
Signed-off-by: Jan Richter <[email protected]>
  • Loading branch information
richtja authored Apr 29, 2024
2 parents 6a0127d + 112dd07 commit b337ef3
Show file tree
Hide file tree
Showing 7 changed files with 180 additions and 68 deletions.
14 changes: 14 additions & 0 deletions avocado/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,20 @@ def register_core_options():
help_msg=help_msg,
)

help_msg = (
"If and how to clear test environment. Two possible options, "
"`system` and `all`. "
"`system` = no variables set, besides avocados default and kwargs. "
"`all` = only kwargs are set."
)
stgs.register_option(
section="runner.exectest",
key="clear_env",
key_type=str,
default=None,
help_msg=help_msg,
)

help_msg = (
"By default Avocado runners will use the {uri} of a test as "
"its identifier. Use a custom f-string identifier in order to "
Expand Down
2 changes: 1 addition & 1 deletion avocado/core/nrunner/runnable.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ def identifier(self):

# For kwargs we can use the entire list of values or with a specific
# index.
kwargs = "-".join(self.kwargs.values())
kwargs = "-".join(str(self.kwargs.values()))
if "kwargs" in fmt and "[" in fmt:
kwargs = self.kwargs

Expand Down
21 changes: 18 additions & 3 deletions avocado/plugins/runners/exec_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,11 @@ class ExecTestRunner(BaseRunner):
name = "exec-test"
description = "Runner for standalone executables treated as tests"

CONFIGURATION_USED = ["run.keep_tmp", "runner.exectest.exitcodes.skip"]
CONFIGURATION_USED = [
"run.keep_tmp",
"runner.exectest.exitcodes.skip",
"runner.exectest.clear_env",
]

def _process_final_status(
self, process, runnable, stdout=None, stderr=None
Expand Down Expand Up @@ -119,12 +123,23 @@ def _is_uri_a_file_on_cwd(uri):
return False

def _get_env(self, runnable):
env = dict(os.environ)
clear_env = runnable.config.get("runner.exectest.clear_env", None)
if clear_env not in ["all", "system", None]:
raise ValueError(
f"The `runner.exectest.clear_env` value {clear_env} is not supported."
)
if clear_env in ["all", "system"]:
env = {}
else:
env = dict(os.environ)
if runnable.kwargs:
env.update(runnable.kwargs)
for key, value in runnable.kwargs.items():
if value is None:
del env[key]

# set default Avocado environment variables if running on a valid Task
if runnable.uri is not None:
if runnable.uri is not None and clear_env != "all":
avocado_test_env_variables = self._get_env_variables(runnable)
# save environment variables for further cleanup
runnable.kwargs.update(avocado_test_env_variables)
Expand Down
99 changes: 98 additions & 1 deletion docs/source/guides/writer/chapters/basics.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
==========================
Writing an Executable Test
--------------------------
==========================

This very simple example of an executable test in shell script::

Expand All @@ -11,3 +12,99 @@ Notice that the file is given executable permissions, which is a
requirement for Avocado to treat it as a executable test. Also notice
that the script exits with status code 0, which signals a successful
result to Avocado.

BASH extensions for Executable tests
------------------------------------

Exec-tests written in shell can use a few Avocado utilities. In your
shell code, check if the libraries are available with something like::

AVOCADO_SHELL_EXTENSIONS_DIR=$(avocado exec-path 2>/dev/null)

And if available, injects that directory containing those utilities
into the PATH used by the shell, making those utilities readily
accessible::

if [ $? == 0 ]; then
PATH=$AVOCADO_SHELL_EXTENSIONS_DIR:$PATH
fi

For a full list of utilities, take a look into at the directory return
by ``avocado exec-path`` (if any). Also, the example test
``examples/tests/simplewarning.sh`` can serve as further inspiration:

.. literalinclude:: ../../../../../examples/tests/simplewarning.sh

.. tip:: These extensions may be available as a separate package. For
RPM packages, look for the ``bash`` sub-package.

Environment Variables for Tests
-------------------------------

Avocado's environment variables
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Avocado exports some information, including test parameters, as environment
variables to the running test. Here is a list of the variables that Avocado
currently exports to exec-test tests in default:

+-----------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------+
| Environment Variable | Meaning | Example |
+=============================+=======================================+=====================================================================================================+
| AVOCADO_VERSION | Version of Avocado test runner | 92.0 |
+-----------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------+
| AVOCADO_TEST_BASEDIR | Base directory of Avocado tests. More | $HOME/src/avocado/avocado.dev/examples/tests |
| | info in :data:`avocado.Test.basedir` | |
+-----------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------+
| AVOCADO_TEST_WORKDIR | Work directory for the test. More | /var/tmp/.avocado-taskcx8of8di/test-results/tmp_dirfgqrnbu/1-Env.test |
| | info in :data:`avocado.Test.workdir` | |
+-----------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------+
| AVOCADO_TESTS_COMMON_TMPDIR | Temporary directory created by the | /var/tmp/avocado_XhEdo/ |
| | :ref:`plugin_teststmpdir` plugin. The| |
| | directory is persistent throughout the| |
| | tests in the same Job | |
+-----------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------+
| AVOCADO_TEST_LOGDIR | Log directory for the test | /var/tmp/.avocado-task_5t_srpn/test-results/1-Env.test |
+-----------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------+
| AVOCADO_TEST_LOGFILE | Log file for the test | /var/tmp/.avocado-taskcx8of8di/test-results/1-Env.test/debug.log |
+-----------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------+
| AVOCADO_TEST_OUTPUTDIR | Output directory for the test | /var/tmp/.avocado-taskcx8of8di/test-results/1-Env.test/data |
+-----------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------+
| `***` | All variables from --mux-yaml | TIMEOUT=60; IO_WORKERS=10; VM_BYTES=512M; ... |
+-----------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------+

User's environment variables
^^^^^^^^^^^^^^^^^^^^^^^^^^^^

You can also let avocado set your own environment variables. For that, you need
to pass the environment variables as keyword arguments (``kwargs``) to the exec-tests.
Here is an example of Job API which passes ``SLEEP_LENGTH`` to sleeptest.sh to set
the time for which the test should sleep:

.. literalinclude:: ../../../../../examples/jobs/custom_exec_test.py

And now we can see that sleeptest.sh can use ``SLEEP_LENGTH`` environment
variable:

.. literalinclude:: ../../../../../examples/tests/sleeptest.sh

.. note:: All environment variables set by avocado will be accessible only during
the test runtime and it won't change your environment.

Disabling environment variables
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Let's imagine that your testing environment has some important variables, but they
could have a negative impact on one of your tests. In that case, avocado lets you
disable those variables during the test runtime. To disable a test variable,
you need to set it in test ``kwargs`` to ``None`` like this::

Runnable("exec-test", "examples/tests/sleeptest.sh", SLEEP_LENGTH=None)

If you need to clear the whole environment before your test, then you can set
``runner.exectest.clear_env`` config variable. This variable has two options.
``system`` and ``all``. If you use ``system`` option the testing environment
will have only Avocado default variables and variables from test ``kwargs``.
If you use ``all`` option, the testing environment will have only variables
from test ``kwargs``::

Runnable("exec-test", "examples/tests/sleeptest.sh", config={'runner.exectest.clear_env': system}, SLEEP_LENGTH=1)
62 changes: 0 additions & 62 deletions docs/source/guides/writer/chapters/writing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1243,21 +1243,13 @@ from an outside source (say a "pickle" file). Finding and using a
reliable and safe location for saving such data is currently not in
the Avocado supported use cases.

.. _environment-variables-for-tests:

Environment Variables for Tests
-------------------------------

Avocado exports some information, including test parameters, as environment
variables to the running test.

The availability of the variable depends on the test type. A greater set of
variables are available to avocado-instrumented tests, while a reduced number of
variables are available to EXEC tests. Although the availability of the
variable, they are usually more interesting to EXEC tests. The reason is that
EXEC tests can not make direct use of Avocado API. avocado-instrumented tests will
usually have more powerful ways to access the same information.

Here is a list of the variables that Avocado currently exports to avocado-instrumented
tests:

Expand Down Expand Up @@ -1286,60 +1278,6 @@ tests:
| `***` | All variables from --mux-yaml | TIMEOUT=60; IO_WORKERS=10; VM_BYTES=512M; ... |
+-----------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------+

Here is a list of the variables that Avocado currently exports to exec-test
tests:

+-----------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------+
| Environment Variable | Meaning | Example |
+=============================+=======================================+=====================================================================================================+
| AVOCADO_VERSION | Version of Avocado test runner | 92.0 |
+-----------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------+
| AVOCADO_TEST_BASEDIR | Base directory of Avocado tests. More | $HOME/src/avocado/avocado.dev/examples/tests |
| | info in :data:`avocado.Test.basedir` | |
+-----------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------+
| AVOCADO_TEST_WORKDIR | Work directory for the test. More | /var/tmp/.avocado-taskcx8of8di/test-results/tmp_dirfgqrnbu/1-Env.test |
| | info in :data:`avocado.Test.workdir` | |
+-----------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------+
| AVOCADO_TESTS_COMMON_TMPDIR | Temporary directory created by the | /var/tmp/avocado_XhEdo/ |
| | :ref:`plugin_teststmpdir` plugin. The| |
| | directory is persistent throughout the| |
| | tests in the same Job | |
+-----------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------+
| AVOCADO_TEST_LOGDIR | Log directory for the test | /var/tmp/.avocado-task_5t_srpn/test-results/1-Env.test |
+-----------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------+
| AVOCADO_TEST_LOGFILE | Log file for the test | /var/tmp/.avocado-taskcx8of8di/test-results/1-Env.test/debug.log |
+-----------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------+
| AVOCADO_TEST_OUTPUTDIR | Output directory for the test | /var/tmp/.avocado-taskcx8of8di/test-results/1-Env.test/data |
+-----------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------+
| `***` | All variables from --mux-yaml | TIMEOUT=60; IO_WORKERS=10; VM_BYTES=512M; ... |
+-----------------------------+---------------------------------------+-----------------------------------------------------------------------------------------------------+


SIMPLE Tests BASH extensions
----------------------------

SIMPLE tests written in shell can use a few Avocado utilities. In your
shell code, check if the libraries are available with something like::

AVOCADO_SHELL_EXTENSIONS_DIR=$(avocado exec-path 2>/dev/null)

And if available, injects that directory containing those utilities
into the PATH used by the shell, making those utilities readily
accessible::

if [ $? == 0 ]; then
PATH=$AVOCADO_SHELL_EXTENSIONS_DIR:$PATH
fi

For a full list of utilities, take a look into at the directory return
by ``avocado exec-path`` (if any). Also, the example test
``examples/tests/simplewarning.sh`` can serve as further inspiration:

.. literalinclude:: ../../../../../examples/tests/simplewarning.sh

.. tip:: These extensions may be available as a separate package. For
RPM packages, look for the ``bash`` sub-package.

.. _docstring-directive-rules:

Docstring Directives Rules
Expand Down
2 changes: 1 addition & 1 deletion selftests/check.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"nrunner-requirement": 24,
"unit": 669,
"jobs": 11,
"functional-parallel": 303,
"functional-parallel": 306,
"functional-serial": 6,
"optional-plugins": 0,
"optional-plugins-golang": 2,
Expand Down
48 changes: 48 additions & 0 deletions selftests/functional/basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,18 @@ def test(self):
logging.getLogger("some.other.logger").info("SHOULD BE ON debug.log")
"""

EXEC_ENV_VARIABLE_TEST = """#!/bin/bash
if [[ -n "${TEST_ENV}" ]]; then
exit 1
fi
"""

EXEC_DEFAULT_ENV_VARIABLE_TEST = """#!/bin/bash
if [[ -n "${AVOCADO_VERSION}" ]]; then
exit 1
fi
"""


def probe_binary(binary):
try:
Expand Down Expand Up @@ -996,6 +1008,24 @@ def setUp(self):
)
self.fail_script.save()

def _test_env(self, configuration, expected_rc, test_file):
with script.TemporaryScript("exec_env_var.sh", test_file) as tst:
res = process.run(
f"env TEST_ENV=test avocado-runner-exec-test runnable-run -k exec-test -u {tst}",
)
result = res.stdout_text.split("\n")[-2]
self.assertIn(
"'returncode': 1",
result,
"Script unexpectedly passed with environment variable set.",
)

res = process.run(
f"env TEST_ENV=test avocado-runner-exec-test runnable-run -k exec-test -u {tst} {configuration}"
)
result = res.stdout_text.split("\n")[-2]
self.assertIn(f"'returncode': {expected_rc}", result)

def test_exec_test_pass(self):
cmd_line = (
f"{AVOCADO} run --job-results-dir {self.tmpdir.name} "
Expand Down Expand Up @@ -1093,6 +1123,24 @@ def test_non_absolute_path(self):
f"Avocado did not return rc {expected_rc}:\n{result}",
)

@skipUnlessPathExists("/bin/bash")
def test_env_var_disable(self):
self._test_env("TEST_ENV=json:null", 0, EXEC_ENV_VARIABLE_TEST)

@skipUnlessPathExists("/bin/sh")
def test_env_clear_system(self):
self._test_env(
'-c \'{"runner.exectest.clear_env": "system"}\'', 0, EXEC_ENV_VARIABLE_TEST
)

@skipUnlessPathExists("/bin/sh")
def test_env_clear_all(self):
self._test_env(
'-c \'{"runner.exectest.clear_env": "all"}\'',
0,
EXEC_DEFAULT_ENV_VARIABLE_TEST,
)

def tearDown(self):
self.pass_script.remove()
self.fail_script.remove()
Expand Down

0 comments on commit b337ef3

Please sign in to comment.