Skip to content

Commit 0c107b7

Browse files
author
Vasileios Karakasis
authored
Merge branch 'master' into feat/post-init-hook
2 parents b485d9d + 8bf4581 commit 0c107b7

File tree

15 files changed

+185
-25
lines changed

15 files changed

+185
-25
lines changed

cscs-checks/prgenv/cuda-fortran/cuda_fortran_check.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,7 @@ def __init__(self):
1313
self.valid_systems = ['daint:gpu', 'dom:gpu']
1414
self.valid_prog_environs = ['PrgEnv-pgi']
1515
self.sourcepath = 'vecAdd_cuda.cuf'
16-
17-
# FIXME: PGI 20.x does not support CUDA 11, see case #275674
18-
self.modules = ['craype-accel-nvidia60',
19-
'cudatoolkit/10.2.89_3.29-7.0.2.1_3.5__g67354b4']
16+
self.modules = ['craype-accel-nvidia60']
2017
self.build_system = 'SingleSource'
2118
self.build_system.fflags = ['-ta=tesla:cc60']
2219
self.num_gpus_per_node = 1
@@ -25,3 +22,13 @@ def __init__(self):
2522
self.sanity_patterns = sn.assert_reference(result, 1., -1e-5, 1e-5)
2623
self.maintainers = ['TM', 'AJ']
2724
self.tags = {'production', 'craype'}
25+
26+
# FIXME: PGI 20.x does not support CUDA 11, see case #275674
27+
@rfm.run_before('compile')
28+
def cudatoolkit_pgi_20x_workaround(self):
29+
if self.current_system.name == 'daint':
30+
cudatoolkit_version = '10.2.89_3.29-7.0.2.1_3.5__g67354b4'
31+
else:
32+
cudatoolkit_version = '10.2.89_3.29-7.0.2.1_3.27__g67354b4'
33+
34+
self.modules += [f'cudatoolkit/{cudatoolkit_version}']

docs/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
jsonschema==3.2.0
22
semver==2.13.0
3-
Sphinx==3.5.2
3+
Sphinx==3.5.3
44
sphinx-rtd-theme==0.5.1

docs/tutorial_advanced.rst

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -790,3 +790,63 @@ Therefore, the ``release.txt`` file can now be used in the subsequent sanity che
790790
791791
For a complete list of the available attributes of a specific container platform, please have a look at the :ref:`container-platforms` section of the :doc:`regression_test_api` guide.
792792
On how to configure ReFrame for running containerized tests, please have a look at the :ref:`container-platform-configuration` section of the :doc:`config_reference`.
793+
794+
795+
Writing reusable tests
796+
----------------------
797+
798+
.. versionadded:: 3.5.0
799+
800+
So far, all the examples shown above were tight to a particular system or configuration, which makes reusing these tests in other systems not straightforward.
801+
However, the introduction of the :py:func:`~reframe.core.pipeline.RegressionTest.parameter` and :py:func:`~reframe.core.pipeline.RegressionTest.variable` ReFrame built-ins solves this problem, eliminating the need to specify any of the test variables in the :func:`__init__` method.
802+
Hence, these parameters and variables can be treated as simple class attributes, which allows us to leverage Python's class inheritance and write more modular tests.
803+
For simplicity, we illustrate this concept with the above :class:`ContainerTest` example, where the goal here is to re-write this test as a library that users can simply import from and derive their tests without having to rewrite the bulk of the test.
804+
Also, for illustrative purposes, we parameterize this library test on a few different image tags (the above example just used ``ubuntu:18.04``) and throw the container commands into a separate bash script just to create some source files.
805+
Thus, removing all the system and configuration specific variables, and moving as many assignments as possible into the class body, the system agnostic library test looks as follows:
806+
807+
.. code-block:: console
808+
809+
cat tutorials/advanced/library/lib/__init__.py
810+
811+
812+
.. literalinclude:: ../tutorials/advanced/library/lib/__init__.py
813+
:lines: 6-
814+
:emphasize-lines: 8-17
815+
816+
Note that the class :class:`ContainerBase` is not decorated since it does not specify the required variables ``valid_systems`` and ``valid_prog_environs``, and it declares the ``platform`` parameter without any defined values assigned.
817+
Hence, the user can simply derive from this test and specialize it to use the desired container platforms.
818+
Since the parameters are defined directly in the class body, the user is also free to override or extend any of the other parameters in a derived test.
819+
In this example, we have parametrized the base test to run with the ``ubuntu:18.04`` and ``ubuntu:20.04`` images, but these values from ``dist`` (and also the ``dist_name`` variable) could be modified by the derived class if needed.
820+
821+
On the other hand, the rest of the test depends on the values from the test parameters, and a parameter is only assigned a specific value after the class has been instantiated.
822+
Thus, the rest of the test is expressed as hooks, without the need to write anything in the :func:`__init__` method.
823+
In fact, writing the test in this way permits having hooks that depend on undefined variables or parameters.
824+
This is the case with the :func:`set_container_platform` hook, which depends on the undefined parameter ``platform``.
825+
Hence, the derived test **must** define all the required parameters and variables; otherwise ReFrame will notice that the test is not well defined and will raise an error accordingly.
826+
827+
Before moving onwards to the derived test, note that the :class:`ContainerBase` class takes the additional argument ``pin_prefix=True``, which locks the prefix of all derived tests to this base test.
828+
This will allow the retrieval of the sources located in the library by any derived test, regardless of what their containing directory is.
829+
830+
.. code-block:: console
831+
832+
cat tutorials/advanced/library/lib/src/get_os_release.sh
833+
834+
835+
.. literalinclude:: ../tutorials/advanced/library/lib/src/get_os_release.sh
836+
:lines: 1-
837+
838+
Now from the user's perspective, the only thing to do is to import the above base test and specify the required variables and parameters.
839+
For consistency with the above example, we set the ``platform`` parameter to use Sarus and Singularity, and we configure the test to run on Piz Daint with the built-in programming environment.
840+
Hence, the above :class:`ContainerTest` is now reduced to the following:
841+
842+
.. code-block:: console
843+
844+
cat tutorials/advanced/library/usr/container_test.py
845+
846+
847+
.. literalinclude:: ../tutorials/advanced/library/usr/container_test.py
848+
:lines: 6-
849+
850+
In a similar fashion, any other user could reuse the above :class:`ContainerBase` class and write the test for their own system with a few lines of code.
851+
852+
*Happy test sharing!*

reframe/core/buildsystems.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,11 @@ def post_build(self, buildjob):
147147
Build systems may use this information to do some post processing and
148148
provide additional, build system-specific, functionality to the users.
149149
150+
This function will always be executed from the test's stage directory.
151+
150152
.. versionadded:: 3.5.0
153+
.. versionchanged:: 3.5.2
154+
The function is executed from the stage directory.
151155
152156
:meta private:
153157
@@ -741,7 +745,6 @@ def __init__(self):
741745
self.package_opts = {}
742746
self.prefix = 'easybuild'
743747
self._eb_modules = []
744-
self._prefix_save = None
745748

746749
def emit_build_commands(self, environ):
747750
if not self.easyconfigs:
@@ -755,7 +758,6 @@ def emit_build_commands(self, environ):
755758

756759
prefix = os.path.join(os.getcwd(), self.prefix)
757760
options = ' '.join(self.options)
758-
self._prefix_save = prefix
759761
return [f'export EASYBUILD_BUILDPATH={prefix}/build',
760762
f'export EASYBUILD_INSTALLPATH={prefix}',
761763
f'export EASYBUILD_PREFIX={prefix}',
@@ -765,7 +767,8 @@ def emit_build_commands(self, environ):
765767
def post_build(self, buildjob):
766768
# Store the modules generated by EasyBuild
767769

768-
modulesdir = os.path.join(self._prefix_save, 'modules', 'all')
770+
modulesdir = os.path.join(os.getcwd(), self.prefix,
771+
'modules', 'all')
769772
with open(buildjob.stdout) as fp:
770773
out = fp.read()
771774

reframe/core/decorators.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@ def _validate_test(cls):
8585
'subclass of RegressionTest')
8686

8787
if (cls.is_abstract()):
88-
raise ValueError(f'decorated test ({cls.__qualname__!r}) is an'
89-
f' abstract test')
88+
raise ValueError(f'decorated test ({cls.__qualname__!r}) has one or '
89+
f'more undefined parameters')
9090

9191

9292
def simple_test(cls):

reframe/core/meta.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -220,13 +220,12 @@ def param_space(cls):
220220
return cls._rfm_param_space
221221

222222
def is_abstract(cls):
223-
'''Check if the test is an abstract test.
223+
'''Check if the class is an abstract test.
224224
225-
If the parameter space has undefined parameters, the test is considered
226-
an abstract test. If that is the case, the length of the parameter
227-
space is just 0.
225+
This is the case when some parameters are undefined, which results in
226+
the length of the parameter space being 0.
228227
229-
:return: bool indicating wheteher the test is abstract or not
228+
:return: bool indicating wheteher the test has undefined parameters.
230229
231230
:meta private:
232231
'''

reframe/core/pipeline.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -826,8 +826,8 @@ def __rfm_init__(self, name=None, prefix=None):
826826

827827
# Static directories of the regression check
828828
self._prefix = os.path.abspath(prefix)
829-
if (not os.path.isdir(os.path.join(self._prefix, self.sourcesdir)) and
830-
not osext.is_url(self.sourcesdir)):
829+
if (self.sourcesdir == 'src' and
830+
not os.path.isdir(os.path.join(self._prefix, self.sourcesdir))):
831831
self.sourcesdir = None
832832

833833
# Runtime information of the test
@@ -1282,7 +1282,8 @@ def compile_wait(self):
12821282
raise BuildError(self._build_job.stdout,
12831283
self._build_job.stderr, self._stagedir)
12841284

1285-
self.build_system.post_build(self._build_job)
1285+
with osext.change_dir(self._stagedir):
1286+
self.build_system.post_build(self._build_job)
12861287

12871288
@final
12881289
def run(self):

reframe/core/schedulers/slurm.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,10 @@ class SqueueJobScheduler(SlurmJobScheduler):
551551
SQUEUE_DELAY = 2
552552

553553
def poll(self, *jobs):
554+
if jobs:
555+
# Filter out non-jobs
556+
jobs = [job for job in jobs if job is not None]
557+
554558
if not jobs:
555559
return
556560

reframe/frontend/executors/policies.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,7 @@ def runcase(self, case):
409409
else:
410410
self.printer.status('HOLD', task.check.info(), just='right')
411411
except TaskExit:
412-
if not task.failed or not task.skipped:
412+
if not task.failed and not task.skipped:
413413
with contextlib.suppress(TaskExit):
414414
self._reschedule(task)
415415

requirements.txt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
argcomplete==1.12.2
2-
coverage==5.3
3-
importlib_metadata==2.0.0
2+
coverage==5.5
3+
importlib_metadata==3.10.0; python_version < '3.8'
44
jsonschema==3.2.0
5-
pytest==6.2.0
5+
pytest==6.2.2
66
pytest-forked==1.3.0
77
pytest-parallel==0.1.0
8-
PyYAML==5.4
8+
PyYAML==5.4.1
99
requests==2.25.1
1010
semver==2.13.0
11-
setuptools==50.3.0
11+
setuptools==54.2.0
1212
wcwidth==0.2.5
13-
#+pygelf%pygelf==0.3.6
13+
#+pygelf%pygelf==0.4.0

0 commit comments

Comments
 (0)