diff --git a/docs/tutorial_advanced.rst b/docs/tutorial_advanced.rst index ea3c1b1ede..a5c72b97d8 100644 --- a/docs/tutorial_advanced.rst +++ b/docs/tutorial_advanced.rst @@ -261,7 +261,8 @@ Here is the full regression test: :lines: 6- :emphasize-lines: 6 -There is nothing special for this test compared to those presented so far except that it derives from the :class:`~reframe.core.pipeline.RunOnlyRegressionTest`. +There is nothing special for this test compared to those presented so far except that it derives from the :class:`~reframe.core.pipeline.RunOnlyRegressionTest` class. +Note that setting the :attr:`~reframe.core.pipeline.RegressionTest.executable` in this type of test is always required. Run-only regression tests may also have resources, as for instance a pre-compiled executable or some input data. These resources may reside under the ``src/`` directory or under any directory specified in the :attr:`~reframe.core.pipeline.RegressionTest.sourcesdir` attribute. These resources will be copied to the stage directory at the beginning of the run phase. diff --git a/reframe/core/pipeline.py b/reframe/core/pipeline.py index 571e6e0a3a..250e61d6be 100644 --- a/reframe/core/pipeline.py +++ b/reframe/core/pipeline.py @@ -348,8 +348,16 @@ def pipeline_hooks(cls): #: The name of the executable to be launched during the run phase. #: + #: If this variable is undefined when entering the compile pipeline + #: stage, it will be set to ``os.path.join('.', self.name)``. Classes + #: that override the compile stage may leave this variable undefined. + #: #: :type: :class:`str` - #: :default: ``os.path.join('.', self.name)`` + #: :default: :class:`required` + #: + #: .. versionchanged:: 3.7.3 + #: Default value changed from ``os.path.join('.', self.name)`` to + #: :class:`required`. executable = variable(str) #: List of options to be passed to the :attr:`executable`. @@ -861,10 +869,6 @@ def __rfm_init__(self, *args, name=None, prefix=None, **kwargs): if not hasattr(self, 'descr'): self.descr = self.name - # Pass if the executable is a required variable. - if not hasattr(self, 'executable'): - self.executable = os.path.join('.', self.name) - self._perfvalues = {} # Static directories of the regression check @@ -1287,6 +1291,10 @@ def compile(self): self._copy_to_stagedir(os.path.join(self._prefix, self.sourcesdir)) + # Set executable (only if hasn't been provided) + if not hasattr(self, 'executable'): + self.executable = os.path.join('.', self.name) + # Verify the sourcepath and determine the sourcepath in the stagedir if (os.path.isabs(self.sourcepath) or os.path.normpath(self.sourcepath).startswith('..')): diff --git a/unittests/test_pipeline.py b/unittests/test_pipeline.py index 20030a2ab6..73ede68969 100644 --- a/unittests/test_pipeline.py +++ b/unittests/test_pipeline.py @@ -302,6 +302,15 @@ class MyTest(rfm.RunOnlyRegressionTest): _run(test, *local_exec_ctx) +def test_executable_is_required(local_exec_ctx): + class MyTest(rfm.RunOnlyRegressionTest): + valid_prog_environs = ['*'] + valid_systems = ['*'] + + with pytest.raises(AttributeError, match="'executable' has not been set"): + _run(MyTest(), *local_exec_ctx) + + def test_compile_only_failure(local_exec_ctx): @test_util.custom_prefix('unittests/resources/checks') class MyTest(rfm.CompileOnlyRegressionTest): @@ -319,7 +328,7 @@ def __init__(self): def test_compile_only_warning(local_exec_ctx): @test_util.custom_prefix('unittests/resources/checks') - class MyTest(rfm.RunOnlyRegressionTest): + class MyTest(rfm.CompileOnlyRegressionTest): def __init__(self): self.build_system = 'SingleSource' self.build_system.srcfile = 'compiler_warning.c' diff --git a/unittests/test_policies.py b/unittests/test_policies.py index 9e92b24997..5baf9ff94a 100644 --- a/unittests/test_policies.py +++ b/unittests/test_policies.py @@ -139,6 +139,7 @@ def check_and_skip(self): class _T1(rfm.RunOnlyRegressionTest): valid_systems = ['*'] valid_prog_environs = ['*'] + executable = 'echo' sanity_patterns = sn.assert_true(1) def __init__(self):