From aae1d7f2f6a2ededa7d933af04ca99501cbbc628 Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Wed, 8 Apr 2026 13:52:59 +0200 Subject: [PATCH 1/3] Enhance some checks --- easybuild/framework/easyblock.py | 1 + easybuild/tools/variables.py | 2 +- test/framework/run.py | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/easybuild/framework/easyblock.py b/easybuild/framework/easyblock.py index 04b10406d4..66bf8d5404 100644 --- a/easybuild/framework/easyblock.py +++ b/easybuild/framework/easyblock.py @@ -4725,6 +4725,7 @@ def test_cases_step(self): else: original_perms = None try: + self.log.debug(f"Running test {test_cmd}") run_shell_cmd(test_cmd) except RunShellCmdError: raise # Let that propagate which will report more information diff --git a/easybuild/tools/variables.py b/easybuild/tools/variables.py index 6f81e198de..4f2d5cdd20 100644 --- a/easybuild/tools/variables.py +++ b/easybuild/tools/variables.py @@ -54,7 +54,7 @@ def get_class(name, default_class, map_class=None): if name is not None: try: klass = map_class[name] - except BaseException: + except KeyError: for k, v in map_class.items(): if type(k) in (type,) and name in v: klass = k diff --git a/test/framework/run.py b/test/framework/run.py index 5536570a77..9f6bcf8821 100644 --- a/test/framework/run.py +++ b/test/framework/run.py @@ -639,7 +639,7 @@ def handler(signum, _): # check error reporting output when stdout/stderr are collected separately try: run_shell_cmd(cmd, split_stderr=True) - self.assertFalse("This should never be reached, RunShellCmdError should occur!") + self.fail("This should never be reached, RunShellCmdError should occur!") except RunShellCmdError as err: self.assertEqual(str(err), "Shell command 'kill' failed!") self.assertEqual(err.cmd, "kill -9 $$") From d38174b2de84ef39289db39b06df107aec599955 Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Wed, 8 Apr 2026 13:55:36 +0200 Subject: [PATCH 2/3] Enhance test for test_cases_step --- test/framework/easyblock.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/test/framework/easyblock.py b/test/framework/easyblock.py index fef50b14fe..022760d7bd 100644 --- a/test/framework/easyblock.py +++ b/test/framework/easyblock.py @@ -1286,7 +1286,8 @@ def test_test_cases_step(self): eb.cfg['tests'] = ['/abs/path/does-not-exist'] self.assertRaisesRegex(EasyBuildError, 'non-existing path: /abs/path/does-not-exist', eb.test_cases_step) - mock_test_bin = os.path.join(self.test_prefix, 'pi', 'test_me') + new_source_path = os.path.join(tempfile.mkdtemp(), 'sources') + mock_test_bin = os.path.join(new_source_path, 'pi', 'test_me') os.environ['PATH'] += f':{os.path.dirname(mock_test_bin)}' write_file(mock_test_bin, "#!/bin/bash\necho 'Test case success'") @@ -1295,10 +1296,12 @@ def test_test_cases_step(self): fn = os.path.basename(mock_test_bin) self.assertRaisesRegex(EasyBuildError, f'non-existing path: {fn}', eb.test_cases_step) - init_config(args=[f"--sourcepath={self.test_prefix}"]) + init_config(args=[f"--sourcepath={new_source_path}", "--debug"]) write_file(eb.logfile, '') eb.test_cases_step() - self.assertIn('Test case success', read_file(eb.logfile)) + logtxt = read_file(eb.logfile) + self.assertIn(f'Running test {mock_test_bin}', logtxt) + self.assertIn('Test case success', logtxt) # Also works with non-executable file perms = stat.S_IREAD @@ -1321,13 +1324,17 @@ def test_test_cases_step(self): eb.cfg['tests'] = [mock_test_bin_fail] write_file(eb.logfile, '') self.assertRaisesRegex(RunShellCmdError, f"'{os.path.basename(mock_test_bin_fail)}' failed", eb.test_cases_step) - self.assertIn('Test case failure', read_file(eb.logfile)) + logtxt = read_file(eb.logfile) + self.assertIn(f'Running test {mock_test_bin_fail}', logtxt) + self.assertIn('Test case failure', logtxt) # Multiple tests eb.cfg['tests'] = [mock_test_bin, mock_test_bin_fail] write_file(eb.logfile, '') self.assertRaisesRegex(RunShellCmdError, f"'{os.path.basename(mock_test_bin_fail)}' failed", eb.test_cases_step) log_txt = read_file(eb.logfile) + self.assertIn(f'Running test {mock_test_bin}', logtxt) + self.assertIn(f'Running test {mock_test_bin_fail}', logtxt) self.assertIn('Test case success', log_txt) self.assertIn('Test case failure', log_txt) From 7f15da2017dd0750ea6313c07a1335347cf421bd Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Wed, 8 Apr 2026 14:10:56 +0200 Subject: [PATCH 3/3] Use `obtain_file` to get file for `test_cases_step` Searching only `source_paths` is inconsistent with how patches etc. are located making it impossible to place tests e.g. next to easyconfigs or in software-specific source folders. --- easybuild/framework/easyblock.py | 11 +++-------- test/framework/easyblock.py | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/easybuild/framework/easyblock.py b/easybuild/framework/easyblock.py index 66bf8d5404..e1c557b1c5 100644 --- a/easybuild/framework/easyblock.py +++ b/easybuild/framework/easyblock.py @@ -4707,14 +4707,9 @@ def test_cases_step(self): if os.path.isabs(test): test_cmd = test else: - for source_path in source_paths(): - test_cmd = os.path.join(source_path, self.name, test) - if os.path.exists(test_cmd): - break - else: - test_cmd = test + test_cmd = self.obtain_file(test, no_download=True, warning_only=True) or test if not os.path.exists(test_cmd): - raise EasyBuildError(f"Test specifies non-existing path: {test_cmd}") + raise EasyBuildError(f"Test specifies non-existing path: {test}") if os.path.isfile(test_cmd): original_perms = os.lstat(test_cmd).st_mode @@ -4724,8 +4719,8 @@ def test_cases_step(self): adjust_permissions(test_cmd, stat.S_IEXEC, add=True, recursive=False) else: original_perms = None + self.log.debug(f"Running test {test_cmd}") try: - self.log.debug(f"Running test {test_cmd}") run_shell_cmd(test_cmd) except RunShellCmdError: raise # Let that propagate which will report more information diff --git a/test/framework/easyblock.py b/test/framework/easyblock.py index 022760d7bd..7368990b8e 100644 --- a/test/framework/easyblock.py +++ b/test/framework/easyblock.py @@ -1338,6 +1338,21 @@ def test_test_cases_step(self): self.assertIn('Test case success', log_txt) self.assertIn('Test case failure', log_txt) + # Test is found when put next to easyconfig file, and preferred + new_mock_test_bin = os.path.join(os.path.dirname(self.eb_file), os.path.basename(mock_test_bin)) + write_file(new_mock_test_bin, '#!/bin/bash\necho "new test passed"') + new_mock_test_bin2 = os.path.join(os.path.dirname(self.eb_file), 'example_test2') + write_file(new_mock_test_bin2, '#!/bin/bash\necho "additional test passed"') + eb.cfg['tests'] = [os.path.basename(new_mock_test_bin), os.path.basename(new_mock_test_bin2)] + + write_file(eb.logfile, '') # reset log file + eb.test_cases_step() + logtxt = read_file(eb.logfile) + self.assertIn(f'Running test {new_mock_test_bin}', logtxt) + self.assertIn(f'Running test {new_mock_test_bin2}', logtxt) + self.assertIn("new test passed", logtxt) + self.assertIn("additional test passed", logtxt) + def test_post_processing_step(self): """Test post_processing_step and deprecated post_install_step.""" init_config(build_options={'silent': True})