From 670ac2f7cc7541a53a7913294c05e67d81423d96 Mon Sep 17 00:00:00 2001 From: Vasileios Karakasis Date: Mon, 2 Dec 2024 17:37:21 +0100 Subject: [PATCH] Support relative imports in deeply nested tests --- reframe/frontend/loader.py | 29 +++++++++++++------ .../testlib/nested/__init__.py | 0 .../checks_unlisted/testlib/nested/dummy.py | 17 +++++++++++ .../checks_unlisted/testlib/simple.py | 2 +- unittests/test_loader.py | 6 ++++ 5 files changed, 44 insertions(+), 10 deletions(-) create mode 100644 unittests/resources/checks_unlisted/testlib/nested/__init__.py create mode 100644 unittests/resources/checks_unlisted/testlib/nested/dummy.py diff --git a/reframe/frontend/loader.py b/reframe/frontend/loader.py index 6486ebc77d..874cf029ad 100644 --- a/reframe/frontend/loader.py +++ b/reframe/frontend/loader.py @@ -193,17 +193,28 @@ def load_from_file(self, filename, force=False): try: dirname = os.path.dirname(filename) - with osext.change_dir(dirname): - with util.temp_sys_path(dirname): - if os.path.exists(os.path.join(dirname, '__init__.py')): - # If the containing directory is a package, - # import it, too. - parent = util.import_module_from_file(dirname).__name__ - else: - parent = None + # Load all parent modules of test file + parents = [] + while os.path.exists(os.path.join(dirname, '__init__.py')): + parents.append(os.path.join(dirname)) + dirname = os.path.split(dirname)[0] + + parent_module = None + for pdir in reversed(parents): + with osext.change_dir(pdir): + with util.temp_sys_path(pdir): + package_path = os.path.join(pdir, '__init__.py') + parent_module = util.import_module_from_file( + package_path, parent=parent_module + ).__name__ + + # Now load the actual test file + with osext.change_dir(pdir): + with util.temp_sys_path(pdir): return self.load_from_module( - util.import_module_from_file(filename, force, parent) + util.import_module_from_file(filename, force, + parent_module) ) except Exception: exc_info = sys.exc_info() diff --git a/unittests/resources/checks_unlisted/testlib/nested/__init__.py b/unittests/resources/checks_unlisted/testlib/nested/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/unittests/resources/checks_unlisted/testlib/nested/dummy.py b/unittests/resources/checks_unlisted/testlib/nested/dummy.py new file mode 100644 index 0000000000..41e54fa7cd --- /dev/null +++ b/unittests/resources/checks_unlisted/testlib/nested/dummy.py @@ -0,0 +1,17 @@ +# Copyright 2016-2024 Swiss National Supercomputing Centre (CSCS/ETH Zurich) +# ReFrame Project Developers. See the top-level LICENSE file for details. +# +# SPDX-License-Identifier: BSD-3-Clause + +import reframe as rfm +import reframe.utility.sanity as sn +from ..utility import dummy_fixture + + +@rfm.simple_test +class dummy_test(rfm.RunOnlyRegressionTest): + valid_systems = ['*'] + valid_prog_environs = ['*'] + executable = 'true' + sanity_patterns = sn.assert_true(1) + dummy = fixture(dummy_fixture) diff --git a/unittests/resources/checks_unlisted/testlib/simple.py b/unittests/resources/checks_unlisted/testlib/simple.py index 1316168bd5..10218115a6 100644 --- a/unittests/resources/checks_unlisted/testlib/simple.py +++ b/unittests/resources/checks_unlisted/testlib/simple.py @@ -16,7 +16,7 @@ class simple_echo_check(rfm.RunOnlyRegressionTest, pin_prefix=True): executable = 'echo' executable_opts = ['Hello'] message = variable(str, value='World') - dummy = fixture(dummy_fixture, scope='environment') + dummy = fixture(dummy_fixture) @run_before('run') def set_executable_opts(self): diff --git a/unittests/test_loader.py b/unittests/test_loader.py index d64fec6ba9..729708bde7 100644 --- a/unittests/test_loader.py +++ b/unittests/test_loader.py @@ -154,3 +154,9 @@ def test_relative_import_outside_rfm_prefix(loader, tmp_path): ) tests = loader.load_from_file(str(tmp_path / 'testlib' / 'simple.py')) assert len(tests) == 2 + + # Test nested library tests + tests = loader.load_from_file( + str(tmp_path / 'testlib' / 'nested' / 'dummy.py') + ) + assert len(tests) == 2