Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 5 additions & 9 deletions test/framework/build_log.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,7 @@ def test_easybuilderror(self):
self.assertErrorRegex(EasyBuildError, 'BOOM', raise_easybuilderror, 'BOOM')
logtxt = read_file(logfile)

log_re = re.compile(r"^fancyroot ::.* BOOM \(at .*:[0-9]+ in [a-z_]+\)$", re.M)
self.assertTrue(log_re.match(logtxt), "%s matches %s" % (log_re.pattern, logtxt))
self.assertRegex(logtxt, re.compile(r"^fancyroot ::.* BOOM \(at .*:[0-9]+ in [a-z_]+\)$", re.M))

# test formatting of message
self.assertErrorRegex(EasyBuildError, 'BOOMBAF', raise_easybuilderror, 'BOOM%s', 'BAF')
Expand All @@ -93,7 +92,7 @@ def test_easybuilderror(self):
r'\s+easybuild/tools/filetools\.py:\d+ in read_file',
r'\s+easybuild/framework/easyconfig/tweak\.py:\d+ in tweak_one',
r'\s+easybuild/base/testing\.py:\d+ in assertErrorRegex',
)), re.M)
)))

def test_easybuildlog(self):
"""Tests for EasyBuildLog."""
Expand Down Expand Up @@ -153,8 +152,7 @@ def test_easybuildlog(self):
r"fancyroot.test_easybuildlog \[ERROR\] :: .*EasyBuild encountered an exception \(at .* in .*\): oops",
'',
])
logtxt_regex = re.compile(r'^%s' % expected_logtxt, re.M)
self.assertTrue(logtxt_regex.search(logtxt), "Pattern '%s' found in %s" % (logtxt_regex.pattern, logtxt))
self.assertRegex(logtxt, rf'^{expected_logtxt}')

self.assertErrorRegex(EasyBuildError, r"DEPRECATED \(since .*: kaput", log.deprecated, "kaput", older_ver)
self.assertErrorRegex(EasyBuildError, r"DEPRECATED \(since .*: 2>1", log.deprecated, "2>1", '2.0', '1.0')
Expand All @@ -180,8 +178,7 @@ def test_easybuildlog(self):
r"fancyroot.test_easybuildlog \[ERROR\] :: EasyBuild encountered an error \(at .* in .*\): foo baz baz",
'',
])
logtxt_regex = re.compile(r'^%s' % expected_logtxt, re.M)
self.assertTrue(logtxt_regex.search(logtxt), "Pattern '%s' found in %s" % (logtxt_regex.pattern, logtxt))
self.assertRegex(logtxt, rf'^{expected_logtxt}')

write_file(tmplog, '')
logToFile(tmplog, enable=True)
Expand Down Expand Up @@ -239,8 +236,7 @@ def test_log_levels(self):
error_msg, deprecated_msg, warning_msg, info_msg, debug_msg,
error_msg, deprecated_msg, warning_msg, info_msg, debug_msg, devel_msg,
])
logtxt_regex = re.compile(r'^%s' % expected_logtxt, re.M)
self.assertTrue(logtxt_regex.search(logtxt), "Pattern '%s' found in %s" % (logtxt_regex.pattern, logtxt))
self.assertRegex(logtxt, rf'^{expected_logtxt}')

def test_print_warning(self):
"""Test print_warning"""
Expand Down
24 changes: 8 additions & 16 deletions test/framework/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
@author: Stijn De Weirdt (Ghent University)
"""
import os
import re
import shutil
import sys
import tempfile
Expand Down Expand Up @@ -598,33 +597,27 @@ def test_get_log_filename(self):
tmpdir = tempfile.gettempdir()

res = get_log_filename('foo', '1.2.3')
regex = re.compile(os.path.join(tmpdir, r'easybuild-foo-1\.2\.3-[0-9]{8}\.[0-9]{6}\.log$'))
self.assertTrue(regex.match(res), "Pattern '%s' matches '%s'" % (regex.pattern, res))
self.assertRegex(res, os.path.join(tmpdir, r'easybuild-foo-1\.2\.3-[0-9]{8}\.[0-9]{6}\.log$'))

res = get_log_filename('foo', '1.2.3', date='19700101')
regex = re.compile(os.path.join(tmpdir, r'easybuild-foo-1\.2\.3-19700101\.[0-9]{6}\.log$'))
self.assertTrue(regex.match(res), "Pattern '%s' matches '%s'" % (regex.pattern, res))
self.assertRegex(res, os.path.join(tmpdir, r'easybuild-foo-1\.2\.3-19700101\.[0-9]{6}\.log$'))

res = get_log_filename('foo', '1.2.3', timestamp='094651')
regex = re.compile(os.path.join(tmpdir, r'easybuild-foo-1\.2\.3-[0-9]{8}\.094651\.log$'))
self.assertTrue(regex.match(res), "Pattern '%s' matches '%s'" % (regex.pattern, res))
self.assertRegex(res, os.path.join(tmpdir, r'easybuild-foo-1\.2\.3-[0-9]{8}\.094651\.log$'))

res = get_log_filename('foo', '1.2.3', date='19700101', timestamp='094651')
regex = re.compile(os.path.join(tmpdir, r'easybuild-foo-1\.2\.3-19700101\.094651\.log$'))
self.assertTrue(regex.match(res), "Pattern '%s' matches '%s'" % (regex.pattern, res))
self.assertRegex(res, os.path.join(tmpdir, r'easybuild-foo-1\.2\.3-19700101\.094651\.log$'))

# if log file already exists, numbers are added to the filename to obtain a new file path
write_file(res, '')
res = get_log_filename('foo', '1.2.3', date='19700101', timestamp='094651')
regex = re.compile(os.path.join(tmpdir, r'easybuild-foo-1\.2\.3-19700101\.094651\.log\.1$'))
self.assertTrue(regex.match(res), "Pattern '%s' matches '%s'" % (regex.pattern, res))
self.assertRegex(res, os.path.join(tmpdir, r'easybuild-foo-1\.2\.3-19700101\.094651\.log\.1$'))

# adding salt ensures a unique filename (pretty much)
prev_log_filenames = []
for i in range(10):
for _ in range(10):
res = get_log_filename('foo', '1.2.3', date='19700101', timestamp='094651', add_salt=True)
regex = re.compile(os.path.join(tmpdir, r'easybuild-foo-1\.2\.3-19700101\.094651\.[a-zA-Z]{5}\.log$'))
self.assertTrue(regex.match(res), "Pattern '%s' matches '%s'" % (regex.pattern, res))
self.assertRegex(res, os.path.join(tmpdir, r'easybuild-foo-1\.2\.3-19700101\.094651\.[a-zA-Z]{5}\.log$'))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the lack of re.compile here compared to the others above?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or perhaps the question should be why keep the re.compile above?

@Flamefire Flamefire Sep 11, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

re.compile was required when flags were used. E.g. when ^$ should match line begin/end. I removed the remaining ones that were no longer required now

self.assertNotIn(res, prev_log_filenames)
prev_log_filenames.append(res)

Expand Down Expand Up @@ -676,9 +669,8 @@ def test_log_path(self):

# reconfigure with value for log directory that includes templates
init_config(args=['--logfile-format=easybuild-%(name)s-%(version)s-%(date)s-%(time)s,log.txt'])
regex = re.compile(r'^easybuild-foo-1\.2\.3-[0-9-]{8}-[0-9]{6}$')
res = log_path(ec=ec)
self.assertTrue(regex.match(res), "Pattern '%s' matches '%s'" % (regex.pattern, res))
self.assertRegex(res, r'^easybuild-foo-1\.2\.3-[0-9-]{8}-[0-9]{6}$')
self.assertEqual(log_file_format(), 'log.txt')

def test_get_build_log_path(self):
Expand Down
4 changes: 1 addition & 3 deletions test/framework/containers.py
Original file line number Diff line number Diff line change
Expand Up @@ -405,9 +405,7 @@ def test_end2end_dockerfile(self):
self.check_regexs(regexs, def_file)

# there should be no leading/trailing whitespace included
for pattern in [r'^\s+', r'\s+$']:
regex = re.compile(pattern)
self.assertFalse(regex.search(def_file), "Pattern '%s' should *not* be found in: %s" % (pattern, def_file))
self.assert_multi_regex((r'^\s+', r'\s+$'), def_file, assert_match=False, multi_line=False)

def test_end2end_docker_image(self):

Expand Down
2 changes: 1 addition & 1 deletion test/framework/easyblock.py
Original file line number Diff line number Diff line change
Expand Up @@ -782,7 +782,7 @@ def test_make_module_req(self):
r"^prepend[-_]path.*CPATH.*root.*include/bar.*",
r"^prepend[-_]path.*TEST_VAR.*root.*baz",
]
self.assert_multi_regex(non_expected_patterns, txt, assert_true=False)
self.assert_multi_regex(non_expected_patterns, txt, assert_match=False)

# cleanup
eb.close_log()
Expand Down
21 changes: 10 additions & 11 deletions test/framework/general.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
@author: Kenneth hoste (Ghent University)
"""
import os
import re
import sys
from test.framework.utilities import EnhancedTestCase, TestLoaderFiltered
from unittest import TextTestRunner
Expand All @@ -49,9 +48,9 @@ def test_error_reporting(self):
easybuild_loc = os.path.dirname(os.path.dirname(os.path.abspath(easybuild.framework.__file__)))

log_method_regexes = [
re.compile(r"log\.error\("),
re.compile(r"log\.exception\("),
re.compile(r"log\.raiseException\("),
r"log\.error\(",
r"log\.exception\(",
r"log\.raiseException\(",
]

for dirpath, _, filenames in os.walk(easybuild_loc):
Expand All @@ -61,7 +60,7 @@ def test_error_reporting(self):
path = os.path.join(dirpath, filename)
txt = read_file(path)
for regex in log_method_regexes:
self.assertFalse(regex.search(txt), "No match for '%s' in %s" % (regex.pattern, path))
self.assertNotRegex(txt, regex)

def test_only_if_module_is_available(self):
"""Test only_if_module_is_available decorator."""
Expand Down Expand Up @@ -106,10 +105,10 @@ def test_docstrings(self):
# easybuild.framework.__file__ provides location to <prefix>/easybuild/framework/__init__.py
easybuild_loc = os.path.dirname(os.path.dirname(os.path.abspath(easybuild.framework.__file__)))

docstring_regexes = [
re.compile("@author"),
re.compile("@param"),
re.compile("@return"),
docstring_patterns = [
"@author",
"@param",
"@return",
]

for dirpath, _, filenames in os.walk(easybuild_loc):
Expand All @@ -120,8 +119,8 @@ def test_docstrings(self):

path = os.path.join(dirpath, filename)
txt = read_file(path)
for regex in docstring_regexes:
self.assertFalse(regex.search(txt), "No match for '%s' in %s" % (regex.pattern, path))
for pattern in docstring_patterns:
self.assertNotIn(pattern, txt)

def test_import_available_modules(self):
"""Test for import_available_modules function."""
Expand Down
10 changes: 3 additions & 7 deletions test/framework/module_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -830,9 +830,7 @@ def test_module_extensions(self):
r'\s*extensions\("bar/0.0,barbar/1.2,toy/0.0,ulimit"\)\nend$',
]

for pattern in patterns:
regex = re.compile(pattern, re.M)
self.assertTrue(regex.search(desc), "Pattern '%s' found in: %s" % (regex.pattern, desc))
self.assert_multi_regex(patterns, desc)

# check if the extensions is missing if there are no extensions
test_ec = os.path.join(test_dir, 'easyconfigs', 'test_ecs', 't', 'toy', 'toy-0.0-test.eb')
Expand All @@ -847,7 +845,7 @@ def test_module_extensions(self):
else:
pattern = r"\s*extensions\("

self.assertFalse(re.search(pattern, desc), "No extensions found in: %s" % desc)
self.assertNotRegex(pattern, desc)

# check if the extensions is missing if 'module_extensions' is disabled
init_config(build_options={'module_extensions': False})
Expand All @@ -858,9 +856,7 @@ def test_module_extensions(self):
modgen = self.MODULE_GENERATOR_CLASS(eb)
desc = modgen.get_description()

for pattern in patterns:
regex = re.compile(pattern, re.M)
self.assertFalse(regex.search(desc), "Pattern '%s' not found in: %s" % (regex.pattern, desc))
self.assert_multi_regex(patterns, desc, assert_match=False)

def test_module_extensions_extension_name(self):
"""Test that the 'extension_name' easyconfig parameter is included in the 'extensions' statement."""
Expand Down
14 changes: 7 additions & 7 deletions test/framework/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -1847,7 +1847,7 @@ def test_try_toolchain_mapping(self):
r"^ \* \[.\] .*-gompi-2018a",
r"^ \* \[.\] .*-GCC.*6\.4\.0",
]
self.assert_multi_regex(anti_patterns, outtxt, assert_true=False)
self.assert_multi_regex(anti_patterns, outtxt, assert_match=False)

def test_try_update_deps(self):
"""Test for --try-update-deps."""
Expand Down Expand Up @@ -3233,7 +3233,7 @@ def run_and_assert(args, words_expected=None, words_unexpected=None):
if words_expected is not None:
self.assert_multi_regex(words_expected, stdout)
if words_unexpected is not None:
self.assert_multi_regex(words_unexpected, stdout, assert_true=False)
self.assert_multi_regex(words_unexpected, stdout, assert_match=False)

# A: simple direct case (all is logged because passed directly via EasyBuild configuration options)
args = list(common_args)
Expand Down Expand Up @@ -3343,7 +3343,7 @@ def toy(extra_args=None):
test_report_txt = toy()
self.assertIn(test_var_secret_ondemand, test_report_txt)
self.assertIn(test_var_public, test_report_txt)
self.assert_multi_regex([test_var_secret_always, test_var_secret_always2], test_report_txt, assert_true=False)
self.assert_multi_regex([test_var_secret_always, test_var_secret_always2], test_report_txt, assert_match=False)

# filter out env vars that match specified regex pattern
filter_arg = "--test-report-env-filter=.*_IS_A_CUSTOM_ENV_VAR_FOR_EASYBUILD"
Expand All @@ -3353,7 +3353,7 @@ def toy(extra_args=None):
test_var_secret_always,
test_var_secret_always2,
]
self.assert_multi_regex(regexs, test_report_txt, assert_true=False)
self.assert_multi_regex(regexs, test_report_txt, assert_match=False)
# make sure that used filter is reported correctly in test report
filter_arg_regex = r"--test-report-env-filter='.\*_IS_A_CUSTOM_ENV_VAR_FOR_EASYBUILD'"
self.assertRegex(test_report_txt, filter_arg_regex)
Expand Down Expand Up @@ -4458,7 +4458,7 @@ def test_extended_dry_run(self):
self.assert_multi_regex(msg_regexs, stdout)

# no ignored errors should occur
self.assert_multi_regex([ignoring_error_regex, ignored_error_regex], stdout, assert_true=False)
self.assert_multi_regex([ignoring_error_regex, ignored_error_regex], stdout, assert_match=False)

def test_last_log(self):
"""Test --last-log."""
Expand Down Expand Up @@ -4908,7 +4908,7 @@ def test_github_new_update_pr(self):
r"# Build statistics",
r"buildstats\s*=",
]
self.assert_multi_regex(regexs, txt, assert_true=False)
self.assert_multi_regex(regexs, txt, assert_match=False)

def test_github_new_pr_warning_missing_patch(self):
"""Test warning printed by --new-pr (dry run only) when a specified patch file could not be found."""
Expand Down Expand Up @@ -5797,7 +5797,7 @@ def test_debug_module_cmds(self):
self.modtool.load(['GCC/4.6.3'])
logtxt = read_file(self.logfile)
self.assertRegex(logtxt, "Running .*\n.*load GCC/4.6.3")
self.assert_multi_regex(patterns, logtxt, assert_true=enable)
self.assert_multi_regex(patterns, logtxt, assert_match=enable)
self.modtool.purge()

def test_use_color(self):
Expand Down
19 changes: 6 additions & 13 deletions test/framework/package.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,16 +232,12 @@ def test_package(self):
self.assertTrue(os.path.isfile(pkgfile), "Found %s" % pkgfile)

# check whether extra packaging options were passed down
regex = re.compile("^got an unhandled option: --foo bar$", re.M)
self.assertTrue(regex.search(fpm_output), "Pattern '%s' found in: %s" % (regex.pattern, fpm_output))
self.assertRegex(fpm_output, re.compile("^got an unhandled option: --foo bar$", re.M))

pkgtxt = read_file(pkgfile)
pkgtxt_regex = re.compile("STARTCONTENTS of installdir %s" % easyblock.installdir)
self.assertTrue(pkgtxt_regex.search(pkgtxt), "Pattern '%s' found in: %s" % (pkgtxt_regex.pattern, pkgtxt))
self.assertIn("STARTCONTENTS of installdir %s" % easyblock.installdir, pkgtxt)

no_logfiles_regex = re.compile(r'STARTCONTENTS.*\.(log|md)$.*ENDCONTENTS', re.DOTALL | re.MULTILINE)
res = no_logfiles_regex.search(pkgtxt)
self.assertFalse(res, "Pattern not '%s' found in: %s" % (no_logfiles_regex.pattern, pkgtxt))
self.assertNotRegex(pkgtxt, re.compile(r'STARTCONTENTS.*\.(log|md)$.*ENDCONTENTS', re.DOTALL | re.M))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, this is the one I meant that has $ in the middle

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I had kept the re.M there.


toy_txt = read_file(os.path.join(TEST_ECS_DIR, 't', 'toy', 'toy-0.0-gompi-2018a-test.eb'))
replace_str = '''description = """Toy C program, 100% toy. Now with `backticks'\n'''
Expand All @@ -250,19 +246,16 @@ def test_package(self):
toy_file = os.path.join(self.test_prefix, 'toy-test-description.eb')
write_file(toy_file, toy_txt)

regex = re.compile(r"""`backticks'""")
self.assertTrue(regex.search(toy_txt), "Pattern '%s' found in: %s" % (regex.pattern, toy_txt))
self.assertRegex(toy_txt, r"""`backticks'""")
ec_desc = EasyConfig(toy_file, validate=False)
easyblock_desc = EB_toy(ec_desc)
easyblock_desc.run_all_steps(False)
pkgdir = package(easyblock_desc)
pkgfile = os.path.join(pkgdir, 'toy-0.0-gompi-2018a-test-eb-%s.1.rpm' % EASYBUILD_VERSION)
self.assertTrue(os.path.isfile(pkgfile))
pkgtxt = read_file(pkgfile)
regex_pkg = re.compile(r"""DESCRIPTION:.*`backticks'.*""")
self.assertTrue(regex_pkg.search(pkgtxt), "Pattern '%s' not found in: %s" % (regex_pkg.pattern, pkgtxt))
regex_pkg = re.compile(r"""DESCRIPTION:.*\nand newlines""", re.MULTILINE)
self.assertTrue(regex_pkg.search(pkgtxt), "Pattern '%s' not found in: %s" % (regex_pkg.pattern, pkgtxt))
self.assertRegex(pkgtxt, r"""DESCRIPTION:.*`backticks'.*""")
self.assertRegex(pkgtxt, r"""DESCRIPTION:.*\nand newlines""")
Comment on lines 240 to +258

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't these two actually need MULTILINE? the first one does have a "$" in the middle of the regex at least and the second one includes newline in the pattern

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first one? r"""DESCRIPTION:.*backticks'.*"""has no$`

MULTILINE only affects ^ and $ to match line start/end, so a \n always matches a newline



def suite(loader=None):
Expand Down
16 changes: 12 additions & 4 deletions test/framework/utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
from contextlib import contextmanager
from importlib import reload
from pathlib import Path
from typing import List, Pattern, Union

from test.framework import TEST_DIR, TEST_ECS_DIR, TEST_MODULES_DIR
from easybuild.base import fancylogger
Expand Down Expand Up @@ -449,11 +450,18 @@ def setup_categorized_hmns_modules(self):
line)
sys.stdout.write(line)

def assert_multi_regex(self, regexs, txt, assert_true=True, flags=re.M):
"""Helper function to assert presence/absence of list of regex patterns in a text"""
def assert_multi_regex(self, regexs: List[Union[str, Pattern]], txt: str,
assert_match: bool = True, multi_line: bool = True) -> None:
"""Helper function to assert presence/absence of list of regex patterns in a text
param: regexs: list of regex patterns to check for
param: txt: text to check for regex patterns
param: assert_match: Whether all regex patterns should match or not match
param: multi_line: if False, match ^/$ only at the beginning/end of the whole string
"""
for regex in regexs:
regex = re.compile(regex, flags)
if assert_true:
if multi_line:
regex = re.compile(regex, re.M)
if assert_match:
self.assertRegex(txt, regex)
else:
self.assertNotRegex(txt, regex)
Expand Down