diff --git a/easybuild/base/testing.py b/easybuild/base/testing.py index 58c60d21df..e69d347150 100644 --- a/easybuild/base/testing.py +++ b/easybuild/base/testing.py @@ -36,7 +36,6 @@ import difflib import os import pprint -import re import sys from contextlib import contextmanager from io import StringIO @@ -173,10 +172,7 @@ def assertErrorRegex(self, error, regex, call, *args, **kwargs): str_args = ', '.join(list(map(str, args)) + str_kwargs) self.fail("Expected errors with %s(%s) call should occur" % (call.__name__, str_args)) except error as err: - msg = self.convert_exception_to_str(err) - if isinstance(regex, str): - regex = re.compile(regex) - self.assertTrue(regex.search(msg), "Pattern '%s' is found in '%s'" % (regex.pattern, msg)) + self.assertRegex(self.convert_exception_to_str(err), regex) def mock_stdout(self, enable, force_tty=False): """Enable/disable mocking stdout.""" diff --git a/easybuild/tools/run.py b/easybuild/tools/run.py index 5477e07d2e..8981b6ba41 100644 --- a/easybuild/tools/run.py +++ b/easybuild/tools/run.py @@ -273,14 +273,16 @@ def create_cmd_scripts(cmd_str, work_dir, env, tmpdir, out_file, err_file): # Make script that sets up bash shell with specified environment and working directory cmd_fp = os.path.join(tmpdir, 'cmd.sh') - # using -i to force interactive shell, so env.sh is also sourced when -c is used to run commands - launch_cmd = 'bash --rcfile $EB_SCRIPT_DIR/env.sh -i "$@"' - + launch_cmd = 'bash' # prefix launch command with bwrap (if used) bwrap_cmd = os.getenv('EB_BWRAP_CMD') if bwrap_cmd: launch_cmd = bwrap_cmd + ' ' + launch_cmd + # using -i for interactive shell, so env.sh is sourced + launch_cmd_interactive = f'{launch_cmd} --rcfile $EB_SCRIPT_DIR/env.sh -i' + launch_cmd_with_args = f'BASH_ENV=$EB_SCRIPT_DIR/env.sh {launch_cmd} "$@"' + with open(cmd_fp, 'w') as fid: fid.write('#!/usr/bin/env bash\n') fid.write('# Run this script to set up a shell environment that EasyBuild used to run the shell command\n') @@ -288,7 +290,11 @@ def create_cmd_scripts(cmd_str, work_dir, env, tmpdir, out_file, err_file): 'EB_SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )', f'echo "# Shell for the command: \'"{shlex.quote(cmd_str)}"\'"', 'echo "# Use command history, exit to stop"', - launch_cmd, + 'if [ "$#" -eq 0 ]; then', + ' ' + launch_cmd_interactive, + 'else', + ' ' + launch_cmd_with_args, + 'fi', ])) os.chmod(cmd_fp, 0o775) diff --git a/test/framework/build_log.py b/test/framework/build_log.py index 3ff8d6801a..0be4e0d66e 100644 --- a/test/framework/build_log.py +++ b/test/framework/build_log.py @@ -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') @@ -154,8 +153,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, re.compile(r'^%s' % expected_logtxt, re.M)) 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') @@ -181,8 +179,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, re.compile(r'^%s' % expected_logtxt, re.M)) write_file(tmplog, '') logToFile(tmplog, enable=True) @@ -242,8 +239,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, re.compile(r'^%s' % expected_logtxt, re.M)) def test_print_warning(self): """Test print_warning""" diff --git a/test/framework/config.py b/test/framework/config.py index 03d7c3fbf3..9ee6b3fb3b 100644 --- a/test/framework/config.py +++ b/test/framework/config.py @@ -598,33 +598,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, re.compile(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, re.compile(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, re.compile(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, re.compile(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, re.compile(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$')) self.assertNotIn(res, prev_log_filenames) prev_log_filenames.append(res) @@ -676,9 +670,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): diff --git a/test/framework/containers.py b/test/framework/containers.py index 5684b55717..9786f6ad27 100644 --- a/test/framework/containers.py +++ b/test/framework/containers.py @@ -84,12 +84,6 @@ def run_main(self, args, raise_error=True): return stdout, stderr - def check_regexs(self, regexs, stdout): - """Helper function to check output of stdout.""" - for regex in regexs: - regex = re.compile(regex, re.M) - self.assertTrue(regex.search(stdout), "Pattern '%s' found in: %s" % (regex.pattern, stdout)) - def test_end2end_singularity_recipe_config(self): """End-to-end test for --containerize (recipe only), using --container-config.""" test_ecs = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'easyconfigs', 'test_ecs') @@ -122,7 +116,7 @@ def test_end2end_singularity_recipe_config(self): self.assertErrorRegex(EasyBuildError, error_pattern, self.run_main, args, raise_error=True) args[-1] = 'bootstrap=yum,osversion=7.6.1810' - stdout, stderr = self.run_main(args, raise_error=True) + self.run_main(args, raise_error=True) txt = read_file(test_container_recipe) expected = '\n'.join([ @@ -137,8 +131,7 @@ def test_end2end_singularity_recipe_config(self): # when installing from scratch, a bunch of OS packages are installed too pkgs = ['epel-release', 'python', 'setuptools', 'Lmod', r'gcc-c\+\+', 'make', 'patch', 'tar'] for pkg in pkgs: - regex = re.compile(r"^yum install .*%s" % pkg, re.M) - self.assertTrue(regex.search(txt), "Pattern '%s' found in: %s" % (regex.pattern, txt)) + self.assertRegex(txt, re.compile(r"^yum install .*%s" % pkg, re.M)) pip_patterns = [ # EasyBuild is installed with pip3 by default @@ -152,15 +145,13 @@ def test_end2end_singularity_recipe_config(self): r"if \[ ! -d /scratch \]; then mkdir -p /scratch", ] eb_pattern = r"eb toy-0.0.eb --robot\s*$" - for pattern in pip_patterns + post_commands_patterns + [eb_pattern]: - regex = re.compile('^' + pattern, re.M) - self.assertTrue(regex.search(txt), "Pattern '%s' found in: %s" % (regex.pattern, txt)) + self.assert_multi_regex(pip_patterns + post_commands_patterns + [eb_pattern], txt) remove_file(test_container_recipe) # can also specify a custom mirror URL args[-1] += ',mirrorurl=https://example.com' - stdout, stderr = self.run_main(args, raise_error=True) + self.run_main(args, raise_error=True) txt = read_file(test_container_recipe) expected = '\n'.join([ @@ -213,10 +204,9 @@ def test_end2end_singularity_recipe_config(self): # no OS packages are installed by default when starting from an existing image self.assertNotIn("yum install", txt) - - for pattern in pip_patterns + post_commands_patterns + [eb_pattern]: - regex = re.compile('^' + pattern, re.M) - self.assertTrue(regex.search(txt), "Pattern '%s' found in: %s" % (regex.pattern, txt)) + self.assert_multi_regex( + (f'^{pattern}' for pattern in pip_patterns + post_commands_patterns + [eb_pattern]), + txt) remove_file(test_container_recipe) @@ -226,12 +216,10 @@ def test_end2end_singularity_recipe_config(self): txt = read_file(test_container_recipe) for pattern in pip_patterns: - regex = re.compile('^' + pattern, re.M) - self.assertFalse(regex.search(txt), "Pattern '%s' should not be found in: %s" % (regex.pattern, txt)) + self.assertNotRegex(txt, re.compile('^' + pattern, re.M)) for pattern in ["easy_install easybuild", eb_pattern]: - regex = re.compile('^' + pattern, re.M) - self.assertTrue(regex.search(txt), "Pattern '%s' should be found in: %s" % (regex.pattern, txt)) + self.assertRegex(txt, re.compile('^' + pattern, re.M)) remove_file(test_container_recipe) @@ -241,12 +229,10 @@ def test_end2end_singularity_recipe_config(self): txt = read_file(test_container_recipe) for pattern in post_commands_patterns: - regex = re.compile('^' + pattern, re.M) - self.assertFalse(regex.search(txt), "Pattern '%s' should not be found in: %s" % (regex.pattern, txt)) + self.assertNotRegex(txt, re.compile('^' + pattern, re.M)) for pattern in ["id easybuild", eb_pattern]: - regex = re.compile('^' + pattern, re.M) - self.assertTrue(regex.search(txt), "Pattern '%s' should be found in: %s" % (regex.pattern, txt)) + self.assertRegex(txt, re.compile('^' + pattern, re.M)) remove_file(test_container_recipe) @@ -255,8 +241,7 @@ def test_end2end_singularity_recipe_config(self): stdout, stderr = self.run_main(args, raise_error=True) txt = read_file(test_container_recipe) - regex = re.compile(r"^eb toy-0.0.eb --robot --debug -l", re.M) - self.assertTrue(regex.search(txt), "Pattern '%s' should be found in: %s" % (regex.pattern, txt)) + self.assertRegex(txt, re.compile(r"^eb toy-0.0.eb --robot --debug -l", re.M)) def test_end2end_singularity_image(self): """End-to-end test for --containerize (recipe + image).""" @@ -306,7 +291,7 @@ def test_end2end_singularity_image(self): r"^== Running 'sudo\s*\S*/singularity build\s*/.* /.*', you may need to enter your 'sudo' password...", r"^== Singularity image created at %s/containers/toy-0.0\.%s" % (self.test_prefix, ext), ] - self.check_regexs(regexs, stdout) + self.assert_multi_regex(regexs, stdout) self.assertExists(os.path.join(containerpath, 'toy-0.0.%s' % ext)) @@ -327,7 +312,7 @@ def test_end2end_singularity_image(self): r"^== Running 'sudo\s*\S*/singularity build --writable /.* /.*', you may need to enter .*", r"^== Singularity image created at %s/containers/foo-bar\.img$" % self.test_prefix, ] - self.check_regexs(regexs, stdout) + self.assert_multi_regex(regexs, stdout) cont_img = os.path.join(containerpath, 'foo-bar.img') self.assertExists(cont_img) @@ -347,21 +332,21 @@ def test_end2end_singularity_image(self): regexs.extend([ "WARNING: overwriting existing container image at %s due to --force" % cont_img, ]) - self.check_regexs(regexs, stdout) + self.assert_multi_regex(regexs, stdout) self.assertExists(cont_img) # also check behaviour under --extended-dry-run args.append('--extended-dry-run') stdout, stderr = self.run_main(args) self.assertFalse(stderr) - self.check_regexs(regexs, stdout) + self.assert_multi_regex(regexs, stdout) # test use of --container-tmpdir args.append('--container-tmpdir=%s' % self.test_prefix) stdout, stderr = self.run_main(args) self.assertFalse(stderr) regexs[-3] = r"^== Running 'sudo\s*SINGULARITY_TMPDIR=%s \S*/singularity build .*" % self.test_prefix - self.check_regexs(regexs, stdout) + self.assert_multi_regex(regexs, stdout) def test_end2end_dockerfile(self): test_ecs = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'easyconfigs', 'test_ecs') @@ -390,7 +375,7 @@ def test_end2end_dockerfile(self): stdout, stderr = self.run_main(base_args + ['--container-config=%s' % cont_base]) self.assertFalse(stderr) regexs = ["^== Dockerfile definition file created at %s/containers/Dockerfile.toy-0.0" % self.test_prefix] - self.check_regexs(regexs, stdout) + self.assert_multi_regex(regexs, stdout) remove_file(os.path.join(self.test_prefix, 'containers', 'Dockerfile.toy-0.0')) self.run_main(base_args + ['--container-config=centos:7']) @@ -413,12 +398,10 @@ def test_end2end_dockerfile(self): "eb --robot toy-0.0.eb GCC-4.9.2.eb", "module load toy/0.0 GCC/4.9.2", ] - self.check_regexs(regexs, def_file) + self.assert_multi_regex(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_true=False, flags=0) def test_end2end_docker_image(self): @@ -462,12 +445,12 @@ def test_end2end_docker_image(self): r"^== Running 'sudo docker build -f .* -t .* \.', you may need to enter your 'sudo' password...", r"^== Docker image created at toy-0.0:latest", ] - self.check_regexs(regexs, stdout) + self.assert_multi_regex(regexs, stdout) args.extend(['--force', '--extended-dry-run']) stdout, stderr = self.run_main(args) self.assertFalse(stderr) - self.check_regexs(regexs, stdout) + self.assert_multi_regex(regexs, stdout) def test_container_config_template_recipe(self): """Test use of --container-config and --container-template-recipe.""" @@ -494,8 +477,7 @@ def test_container_config_template_recipe(self): stdout, stderr = self.run_main(args) self.assertFalse(stderr) - regex = re.compile("^== Singularity definition file created at .*/containers/Singularity.toy-0.0$") - self.assertTrue(regex.match(stdout), "Stdout matches pattern '%s': %s" % (regex.pattern, stdout)) + self.assertRegex(stdout, "^== Singularity definition file created at .*/containers/Singularity.toy-0.0$") expected = '\n'.join([ "# this is just a test", diff --git a/test/framework/docs.py b/test/framework/docs.py index ac72860515..f5803fae6e 100644 --- a/test/framework/docs.py +++ b/test/framework/docs.py @@ -806,8 +806,7 @@ def test_gen_easyblocks_overview(self): toc = [":ref:`" + n + "`" for n in sorted(set(names))] pattern = " - ".join(toc) - regex = re.compile(pattern) - self.assertTrue(re.search(regex, ebdoc), "Pattern %s found in %s" % (regex.pattern, ebdoc)) + self.assertRegex(ebdoc, pattern) # MarkDown format eb_overview = gen_easyblocks_overview_md(gen_easyblocks_pkg, 'easyconfigs', common_params, doc_functions) @@ -845,8 +844,7 @@ def test_gen_easyblocks_overview(self): toc = ["\\[" + n + "\\]\\(#" + n.lower() + "\\)" for n in sorted(names)] pattern = " - ".join(toc) - regex = re.compile(pattern) - self.assertTrue(re.search(regex, ebdoc), "Pattern %s found in %s" % (regex.pattern, ebdoc)) + self.assertRegex(ebdoc, pattern) def test_license_docs(self): """Test license_documentation function.""" @@ -855,12 +853,10 @@ def test_license_docs(self): self.assertIn(gplv3, lic_docs) lic_docs = avail_easyconfig_licenses(output_format='rst') - regex = re.compile(r"^``GPLv3``\s*The GNU General Public License", re.M) - self.assertTrue(regex.search(lic_docs), "%s found in: %s" % (regex.pattern, lic_docs)) + self.assertRegex(lic_docs, re.compile(r"^``GPLv3``\s*The GNU General Public License", re.M)) lic_docs = avail_easyconfig_licenses(output_format='md') - regex = re.compile(r"^``GPLv3``\s*|The GNU General Public License", re.M) - self.assertTrue(regex.search(lic_docs), "%s found in: %s" % (regex.pattern, lic_docs)) + self.assertRegex(lic_docs, re.compile(r"^``GPLv3``\s*|The GNU General Public License", re.M)) # expect NotImplementedError for JSON output self.assertRaises(NotImplementedError, avail_easyconfig_licenses, output_format='json') @@ -919,17 +915,17 @@ def test_list_software(self): # GCC/4.6.3 is installed, no gzip module installed txt = list_software(output_format='txt', detailed=True, only_installed=True) - self.assertTrue(re.search(r'^\* GCC', txt, re.M)) - self.assertTrue(re.search(r'^\s*\* GCC v4.6.3: system', txt, re.M)) - self.assertFalse(re.search(r'^\* gzip', txt, re.M)) - self.assertFalse(re.search(r'gzip v1\.', txt, re.M)) + self.assertRegex(txt, re.compile(r'^\* GCC', re.M)) + self.assertRegex(txt, re.compile(r'^\s*\* GCC v4.6.3: system', re.M)) + self.assertNotRegex(txt, re.compile(r'^\* gzip', re.M)) + self.assertNotRegex(txt, r'gzip v1\.') txt = list_software(output_format='rst', detailed=True, only_installed=True) - self.assertTrue(re.search(r'^\*GCC\*', txt, re.M)) - self.assertTrue(re.search(r'4\.6\.3.*system', txt, re.M)) - self.assertFalse(re.search(r'^\*gzip\*', txt, re.M)) - self.assertFalse(re.search(r'1\.4', txt, re.M)) - self.assertFalse(re.search(r'1\.5', txt, re.M)) + self.assertRegex(txt, re.compile(r'^\*GCC\*', re.M)) + self.assertRegex(txt, r'4\.6\.3.*system') + self.assertNotRegex(txt, re.compile(r'^\*gzip\*', re.M)) + self.assertNotRegex(txt, r'1\.4') + self.assertNotRegex(txt, r'1\.5') # check for specific patterns in output for larger set of test easyconfigs build_options = { @@ -994,9 +990,7 @@ def test_list_toolchains(self): ] for txt in (list_toolchains(), list_toolchains(output_format='txt')): - for pattern in txt_patterns: - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(txt), "Pattern '%s' should be found in: %s" % (regex.pattern, txt)) + self.assert_multi_regex(txt_patterns, txt) md_patterns = [ r"^# List of known toolchains", @@ -1006,9 +1000,7 @@ def test_list_toolchains(self): r"^\*\*system\*\*\s+\|\*\(none\)\*\s+\|\*\(none\)\*\s+\|\*\(none\)\*\s+\|\*\(none\)\*$", ] txt_md = list_toolchains(output_format='md') - for pattern in md_patterns: - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(txt_md), "Pattern '%s' should be found in: %s" % (regex.pattern, txt_md)) + self.assert_multi_regex(md_patterns, txt_md) rst_patterns = [ r"^List of known toolchains\n\-{24}", @@ -1018,9 +1010,7 @@ def test_list_toolchains(self): r"^\*\*system\*\*\s+\*\(none\)\*\s+\*\(none\)\*\s+\*\(none\)\*\s+\*\(none\)\*$", ] txt_rst = list_toolchains(output_format='rst') - for pattern in rst_patterns: - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(txt_rst), "Pattern '%s' should be found in: %s" % (regex.pattern, txt_rst)) + self.assert_multi_regex(rst_patterns, txt_rst) # expect NotImplementedError for json output format with self.assertRaises(NotImplementedError): @@ -1040,14 +1030,10 @@ def test_avail_cfgfile_constants(self): os.getenv('USER'), ] txt = avail_cfgfile_constants(option_parser.go_cfg_constants) - for pattern in txt_patterns: - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(txt), "Pattern '%s' should be found in: %s" % (regex.pattern, txt)) + self.assert_multi_regex(txt_patterns, txt) txt = avail_cfgfile_constants(option_parser.go_cfg_constants, output_format='txt') - for pattern in txt_patterns: - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(txt), "Pattern '%s' should be found in: %s" % (regex.pattern, txt)) + self.assert_multi_regex(txt_patterns, txt) md_patterns = [ r"^# Constants available \(only\) in configuration files", @@ -1057,9 +1043,7 @@ def test_avail_cfgfile_constants(self): os.getenv('USER'), ] txt_md = avail_cfgfile_constants(option_parser.go_cfg_constants, output_format='md') - for pattern in md_patterns: - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(txt_md), "Pattern '%s' should be found in: %s" % (regex.pattern, txt_md)) + self.assert_multi_regex(md_patterns, txt_md) rst_patterns = [ r"^Constants available \(only\) in configuration files\n-{49}\n", @@ -1069,9 +1053,7 @@ def test_avail_cfgfile_constants(self): os.getenv('USER'), ] txt_rst = avail_cfgfile_constants(option_parser.go_cfg_constants, output_format='rst') - for pattern in rst_patterns: - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(txt_rst), "Pattern '%s' should be found in: %s" % (regex.pattern, txt_rst)) + self.assert_multi_regex(rst_patterns, txt_rst) # expect NotImplementedError for json output format with self.assertRaises(NotImplementedError): @@ -1089,14 +1071,10 @@ def test_avail_easyconfig_constants(self): ] txt = avail_easyconfig_constants() - for pattern in txt_patterns: - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(txt), "Pattern '%s' should be found in: %s" % (regex.pattern, txt)) + self.assert_multi_regex(txt_patterns, txt) txt = avail_easyconfig_constants(output_format='txt') - for pattern in txt_patterns: - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(txt), "Pattern '%s' should be found in: %s" % (regex.pattern, txt)) + self.assert_multi_regex(txt_patterns, txt) md_patterns = [ r"^# Constants that can be used in easyconfigs", @@ -1105,9 +1083,7 @@ def test_avail_easyconfig_constants(self): r"OS packages providing openSSL development support$", ] txt_md = avail_easyconfig_constants(output_format='md') - for pattern in md_patterns: - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(txt_md), "Pattern '%s' should be found in: %s" % (regex.pattern, txt_md)) + self.assert_multi_regex(md_patterns, txt_md) rst_patterns = [ r"^Constants that can be used in easyconfigs\n-{41}", @@ -1116,9 +1092,7 @@ def test_avail_easyconfig_constants(self): r"OS packages providing openSSL development support$", ] txt_rst = avail_easyconfig_constants(output_format='rst') - for pattern in rst_patterns: - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(txt_rst), "Pattern '%s' should be found in: %s" % (regex.pattern, txt_rst)) + self.assert_multi_regex(rst_patterns, txt_rst) # expect NotImplementedError for json output format with self.assertRaises(NotImplementedError): @@ -1139,14 +1113,10 @@ def test_avail_easyconfig_templates(self): ] txt = avail_easyconfig_templates() - for pattern in txt_patterns: - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(txt), "Pattern '%s' should be found in: %s" % (regex.pattern, txt)) + self.assert_multi_regex(txt_patterns, txt) txt = avail_easyconfig_templates(output_format='txt') - for pattern in txt_patterns: - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(txt), "Pattern '%s' should be found in: %s" % (regex.pattern, txt)) + self.assert_multi_regex(txt_patterns, txt) md_patterns = [ r"^## Template names/values derived from easyconfig instance", @@ -1157,9 +1127,7 @@ def test_avail_easyconfig_templates(self): r"^``SOURCE_TAR_GZ``\s+|Source \.tar\.gz bundle \(%\(name\)s-%\(version\)s.tar.gz\)", ] txt_md = avail_easyconfig_templates(output_format='md') - for pattern in md_patterns: - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(txt_md), "Pattern '%s' should be found in: %s" % (regex.pattern, txt_md)) + self.assert_multi_regex(md_patterns, txt_md) rst_patterns = [ r"^Template names/values derived from easyconfig instance\n\-+", @@ -1170,9 +1138,7 @@ def test_avail_easyconfig_templates(self): r"^``SOURCE_TAR_GZ``\s+|Source \.tar\.gz bundle \(%\(name\)s-%\(version\)s.tar.gz\)", ] txt_rst = avail_easyconfig_templates(output_format='rst') - for pattern in rst_patterns: - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(txt_rst), "Pattern '%s' should be found in: %s" % (regex.pattern, txt_rst)) + self.assert_multi_regex(rst_patterns, txt_rst) # expect NotImplementedError for json output format with self.assertRaises(NotImplementedError): @@ -1191,12 +1157,8 @@ def test_avail_toolchain_opts(self): oneapi_txt = r"^\s+oneapi: Use oneAPI compilers icx/icpx/ifx instead of classic compilers \(default: None\)" for txt in (avail_toolchain_opts('foss'), avail_toolchain_opts('foss', output_format='txt')): - for pattern in txt_patterns_foss: - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(txt), "Pattern '%s' should be found in: %s" % (regex.pattern, txt)) - - regex = re.compile(oneapi_txt, re.M) - self.assertFalse(regex.search(txt), "Pattern '%s' should not be found in: %s" % (regex.pattern, txt)) + self.assert_multi_regex(txt_patterns_foss, txt) + self.assertNotRegex(txt, re.compile(oneapi_txt, re.M)) txt_patterns_intel = [ r"^Available options for intel toolchain:", @@ -1204,9 +1166,7 @@ def test_avail_toolchain_opts(self): ] + txt_patterns_foss[1:] for txt in (avail_toolchain_opts('intel'), avail_toolchain_opts('intel', output_format='txt')): - for pattern in txt_patterns_intel: - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(txt), "Pattern '%s' should be found in: %s" % (regex.pattern, txt)) + self.assert_multi_regex(txt_patterns_intel, txt) # MarkDown output format md_patterns_foss = [ @@ -1217,13 +1177,10 @@ def test_avail_toolchain_opts(self): ] txt_md = avail_toolchain_opts('foss', output_format='md') - for pattern in md_patterns_foss: - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(txt_md), "Pattern '%s' should be found in: %s" % (regex.pattern, txt_md)) + self.assert_multi_regex(md_patterns_foss, txt_md) oneapi_md = r"^``oneapi``\s+\|Use oneAPI compilers icx/icpx/ifx instead of classic compilers\s+\|``None``" - regex = re.compile(oneapi_md, re.M) - self.assertFalse(regex.search(txt_md), "Pattern '%s' should not be found in: %s" % (regex.pattern, txt_md)) + self.assertNotRegex(txt_md, re.compile(oneapi_md, re.M)) md_patterns_intel = [ r"^## Available options for intel toolchain", @@ -1231,9 +1188,7 @@ def test_avail_toolchain_opts(self): ] + md_patterns_foss[1:] txt_md = avail_toolchain_opts('intel', output_format='md') - for pattern in md_patterns_intel: - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(txt_md), "Pattern '%s' should be found in: %s" % (regex.pattern, txt_md)) + self.assert_multi_regex(md_patterns_intel, txt_md) # rst output format rst_patterns_foss = [ @@ -1244,13 +1199,10 @@ def test_avail_toolchain_opts(self): ] txt_rst = avail_toolchain_opts('foss', output_format='rst') - for pattern in rst_patterns_foss: - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(txt_rst), "Pattern '%s' should be found in: %s" % (regex.pattern, txt_rst)) + self.assert_multi_regex(rst_patterns_foss, txt_rst) oneapi_rst = r"^``oneapi``\s+Use oneAPI compilers icx/icpx/ifx instead of classic compilers\s+``None``" - regex = re.compile(oneapi_rst, re.M) - self.assertFalse(regex.search(txt_rst), "Pattern '%s' should not be found in: %s" % (regex.pattern, txt_rst)) + self.assertNotRegex(txt_rst, re.compile(oneapi_rst, re.M)) rst_patterns_intel = [ r"^Available options for intel toolchain\n-{37}", @@ -1258,9 +1210,7 @@ def test_avail_toolchain_opts(self): ] + rst_patterns_foss[1:] txt_rst = avail_toolchain_opts('intel', output_format='rst') - for pattern in rst_patterns_intel: - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(txt_rst), "Pattern '%s' should be found in: %s" % (regex.pattern, txt_rst)) + self.assert_multi_regex(rst_patterns_intel, txt_rst) # expect NotImplementedError for json output format with self.assertRaises(NotImplementedError): @@ -1372,9 +1322,7 @@ def get_eb_help_output(arg=''): r"^All long option names can be passed as environment variables", ] txt = get_eb_help_output() - for pattern in txt_patterns: - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(txt), "Pattern '%s' should be found in: %s" % (regex.pattern, txt)) + self.assert_multi_regex(txt_patterns, txt) short_patterns = [ r"^Usage: eb \[options\] easyconfig \[...\]", @@ -1387,18 +1335,14 @@ def get_eb_help_output(arg=''): r"^All long option names can be passed as environment variables", ] txt_short = get_eb_help_output('short') - for pattern in short_patterns: - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(txt_short), "Pattern '%s' should be found in: %s" % (regex.pattern, txt_short)) + self.assert_multi_regex(short_patterns, txt_short) config_patterns = [ r"^\[MAIN\]\n# Enable debug log mode \(default: False\)\n#debug=", r"^\[override\](\n.*)+#filter-deps=", ] txt_cfg = get_eb_help_output('config') - for pattern in config_patterns: - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(txt_cfg), "Pattern '%s' should be found in: %s" % (regex.pattern, txt_cfg)) + self.assert_multi_regex(config_patterns, txt_cfg) md_patterns = [ r"^## Usage\n\n``eb \[options\] easyconfig \[...\]``", @@ -1408,9 +1352,7 @@ def get_eb_help_output(arg=''): r"^``-e CLASS, --easyblock=CLASS``\s+\|easyblock to use", ] txt_md = get_eb_help_output('md') - for pattern in md_patterns: - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(txt_md), "Pattern '%s' should be found in: %s" % (regex.pattern, txt_md)) + self.assert_multi_regex(md_patterns, txt_md) rst_patterns = [ r"^Usage\n-{5}\n\n``eb \[options\] easyconfig \[...\]``", @@ -1420,9 +1362,7 @@ def get_eb_help_output(arg=''): r"^``-e CLASS, --easyblock=CLASS``\s+easyblock to use", ] txt_rst = get_eb_help_output('rst') - for pattern in rst_patterns: - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(txt_rst), "Pattern '%s' should be found in: %s" % (regex.pattern, txt_rst)) + self.assert_multi_regex(rst_patterns, txt_rst) def suite(loader=None): diff --git a/test/framework/easyblock.py b/test/framework/easyblock.py index fef50b14fe..a229e9900c 100644 --- a/test/framework/easyblock.py +++ b/test/framework/easyblock.py @@ -583,9 +583,9 @@ def test_make_module_req(self): with eb.module_generator.start_module_creation(): txt = eb.make_module_req() if get_module_syntax() == 'Tcl': - self.assertTrue(re.match(r"^\nprepend-path\s+PATH\s+\$root/bin\n$", txt, re.M)) + self.assertRegex(txt, r"\nprepend-path\s+PATH\s+\$root/bin\n") elif get_module_syntax() == 'Lua': - self.assertTrue(re.match(r'^\nprepend_path\("PATH", pathJoin\(root, "bin"\)\)\n$', txt, re.M)) + self.assertRegex(txt, r'\nprepend_path\("PATH", pathJoin\(root, "bin"\)\)\n') else: self.fail("Unknown module syntax: %s" % get_module_syntax()) @@ -663,11 +663,11 @@ def test_make_module_req(self): self.assertEqual(list(eb.module_load_environment), ['PATH', 'LD_LIBRARY_PATH', 'NONPATH']) if get_module_syntax() == 'Tcl': - self.assertTrue(re.match(r"^\nprepend-path\s+PATH\s+\$root/bin\n$", txt, re.M)) - self.assertFalse(re.match(r"^\nprepend-path\s+NONPATH\s+\$root/non_path\n$", txt, re.M)) + self.assertRegex(txt, r"\nprepend-path\s+PATH\s+\$root/bin\n") + self.assertNotRegex(txt, r"\nprepend-path\s+NONPATH\s+\$root/non_path\n") elif get_module_syntax() == 'Lua': - self.assertTrue(re.match(r'^\nprepend_path\("PATH", pathJoin\(root, "bin"\)\)\n$', txt, re.M)) - self.assertFalse(re.match(r'^\nprepend_path\("NONPATH", pathJoin\(root, "non_path"\)\)\n$', txt, re.M)) + self.assertRegex(txt, r'\nprepend_path\("PATH", pathJoin\(root, "bin"\)\)\n') + self.assertNotRegex(txt, r'\nprepend_path\("NONPATH", pathJoin\(root, "non_path"\)\)\n') else: self.fail("Unknown module syntax: %s" % get_module_syntax()) @@ -1116,11 +1116,9 @@ def test_make_module_dep_hmns(self): with self.mocked_stdout_stderr(): mod_dep_txt = eb.make_module_dep() for mod in ['GCC/6.4.0-2.28', 'OpenMPI/2.1.2']: - regex = re.compile('(load|depends[-_]on).*%s' % mod) - self.assertNotRegex(mod_dep_txt, regex) + self.assertNotRegex(mod_dep_txt, '(load|depends[-_]on).*%s' % mod) - regex = re.compile('(load|depends[-_]on).*FFTW/3.3.7') - self.assertRegex(mod_dep_txt, regex) + self.assertRegex(mod_dep_txt, '(load|depends[-_]on).*FFTW/3.3.7') def test_make_module_dep_of_dep_hmns(self): """Test for make_module_dep under HMNS with dependencies of dependencies""" @@ -1162,8 +1160,7 @@ def test_make_module_dep_of_dep_hmns(self): # GCC, OpenMPI and hwloc modules should *not* be included in loads for dependencies mod_dep_txt = eb.make_module_dep() for mod in ['GCC/6.4.0-2.28', 'OpenMPI/2.1.2', 'hwloc/1.11.8']: - regex = re.compile('load.*%s' % mod) - self.assertNotRegex(mod_dep_txt, regex) + self.assertNotRegex(mod_dep_txt, 'load.*%s' % mod) def test_det_iter_cnt(self): """Test det_iter_cnt method.""" @@ -1953,9 +1950,8 @@ def test_get_easyblock_instance(self): # check whether 'This is easyblock' log message is there tup = ('EB_toy', 'easybuild.easyblocks.toy', '.*test/framework/sandbox/easybuild/easyblocks/t/toy.pyc*') - eb_log_msg_re = re.compile(r"INFO This is easyblock %s from module %s (%s)" % tup, re.M) logtxt = read_file(eb.logfile) - self.assertRegex(logtxt, eb_log_msg_re) + self.assertRegex(logtxt, r"INFO This is easyblock %s from module %s (%s)" % tup) def test_fetch_sources(self): """Test fetch_sources method.""" @@ -2348,8 +2344,7 @@ def test_obtain_file(self): res = eb.obtain_file(file_url) except EasyBuildError as err: # if this fails, it should be because there's no online access - download_fail_regex = re.compile('socket error') - self.assertRegex(str(err), download_fail_regex) + self.assertIn('socket error', str(err)) # result may be None during offline testing if res is not None: @@ -2532,8 +2527,7 @@ def test_check_readiness(self): try: eb.check_readiness_step() except EasyBuildError as err: - err_regex = re.compile("Missing modules dependencies .*: nosuchsoftware/1.2.3-GCC-6.4.0-2.28") - self.assertRegex(str(err), err_regex) + self.assertRegex(str(err), "Missing modules dependencies .*: nosuchsoftware/1.2.3-GCC-6.4.0-2.28") shutil.rmtree(tmpdir) @@ -2581,9 +2575,9 @@ def test_exclude_path_to_top_of_module_tree(self): for dep in excluded_deps: if get_module_syntax() == 'Tcl': - self.assertNotRegex(modtxt, re.compile(r'module load %s' % dep)) + self.assertNotIn('module load %s' % dep, modtxt) elif get_module_syntax() == 'Lua': - self.assertNotRegex(modtxt, re.compile(r'load("%s")' % dep)) + self.assertNotIn('load("%s")' % dep, modtxt) else: self.fail("Unknown module syntax: %s" % get_module_syntax()) @@ -3959,8 +3953,7 @@ def test_exts_deps_build_env(self): log_txt = read_file(self.logfile) # check whether $EBROOTZLIB is correctly set in build environment of 'bar' extension - regex = re.compile(f"^EBROOTZLIB=.*/software/zlib/{zlib_fn}$", re.M) - self.assertRegex(log_txt, regex) + self.assertRegex(log_txt, re.compile(f"^EBROOTZLIB=.*/software/zlib/{zlib_fn}$", re.M)) # check whether $C_INCLUDE_PATH is correctly set in build environment of 'bar' extension for env_var in env_vars[search_path_cpp_headers]: diff --git a/test/framework/easyconfig.py b/test/framework/easyconfig.py index e08465e05c..8cb802a4c5 100644 --- a/test/framework/easyconfig.py +++ b/test/framework/easyconfig.py @@ -452,7 +452,7 @@ def test_extra_options(self): regex = re.compile("^mandatory_key = 'default'$", re.M) ectxt = read_file(test_ecfile) - self.assertTrue(regex.search(ectxt), "Pattern '%s' found in: %s" % (regex.pattern, ectxt)) + self.assertRegex(ectxt, regex) # parsing again should work fine (if mandatory easyconfig parameters are indeed retained) ec = EasyConfig(test_ecfile, extra_options=extra_vars) @@ -528,8 +528,7 @@ def test_exts_list(self): modfile = os.path.join(eb.make_module_step(), 'PI', '3.14' + eb.module_generator.MODULE_FILE_EXTENSION) modtxt = read_file(modfile) # verify that templates used for extensions are resolved as they should - regex = re.compile('EBEXTSLISTPI.*"ext1-1.0,ext2-2.0,ext-PI-3.14,ext-pi-3.0') - self.assertTrue(regex.search(modtxt), "Pattern '%s' found in: %s" % (regex.pattern, modtxt)) + self.assertRegex(modtxt, re.compile('EBEXTSLISTPI.*"ext1-1.0,ext2-2.0,ext-PI-3.14,ext-pi-3.0')) def test_extensions_default_class(self): """Test that exts_defaultclass doesn't need to be specified if explicit one is present.""" @@ -1047,7 +1046,7 @@ def test_obtain_easyconfig(self): ec = EasyConfig(res[1]) self.assertEqual(ec['version'], specs['version']) txt = read_file(res[1]) - self.assertTrue(re.search("^version = [\"']%s[\"']$" % ver, txt, re.M)) + self.assertRegex(txt, re.compile("^version = [\"']%s[\"']$" % ver, re.M)) remove_file(res[1]) # should pick correct toolchain version as well, i.e. now newer than what's specified, @@ -1063,7 +1062,7 @@ def test_obtain_easyconfig(self): self.assertEqual(ec['toolchain']['version'], specs['toolchain_version']) txt = read_file(res[1]) pattern = "^toolchain = .*version.*[\"']%s[\"'].*}$" % specs['toolchain_version'] - self.assertTrue(re.search(pattern, txt, re.M)) + self.assertRegex(txt, re.compile(pattern, re.M)) os.remove(res[1]) # should be able to prepend to list of patches and handle list of dependencies @@ -1291,10 +1290,10 @@ def test_templating_constants(self): # should match lib/x86_64/2.7.18, lib/aarch64/3.8.6, lib/ppc64le/3.9.2, etc. lib_arch_regex = re.compile(r'^lib/[a-z0-9_]+/[23]\.[0-9]+\.[0-9]+$') dirs1 = ec['sanity_check_paths']['dirs'][1] - self.assertTrue(lib_arch_regex.match(dirs1), "Pattern '%s' should match '%s'" % (lib_arch_regex.pattern, dirs1)) + self.assertRegex(dirs1, lib_arch_regex) inc_regex = re.compile('^include/(aarch64|ppc64le|x86_64)$') dirs2 = ec['sanity_check_paths']['dirs'][2] - self.assertTrue(inc_regex.match(dirs2), "Pattern '%s' should match '%s'" % (inc_regex, dirs2)) + self.assertRegex(dirs2, inc_regex) self.assertEqual(ec['homepage'], "http://example.com/P/p/v3/") expected = ("CUDA: 10.1.105, 10, 1, 10.1; " "Java: 1.7.80, 1, 7, 1.7; " @@ -2613,9 +2612,7 @@ def test_dump(self): r'^description = ["\']', r"^toolchain = {'name': .*, 'version': .*}", ] - for pattern in patterns: - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(ectxt), "Pattern '%s' found in: %s" % (regex.pattern, ectxt)) + self.assert_multi_regex(patterns, ectxt) # parse result again dumped_ec = EasyConfig(test_ec) @@ -2902,10 +2899,7 @@ def test_dump_template(self): r"\('ext1', '1\.0\.0'\),", r"sanity_check_paths = {\n 'files': \['files/%\(namelower\)s/foobar', 'files/x-test'\]", ] - - for pattern in patterns: - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(ectxt), "Pattern '%s' found in: %s" % (regex.pattern, ectxt)) + self.assert_multi_regex(patterns, ectxt) ectxt.endswith('moduleclass = "tools"') @@ -2984,9 +2978,7 @@ def test_dump_comments(self): r" 'files': \['files/foobar'\], # comment on files", r" 'dirs': \[\],", ] - for pattern in patterns: - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(ectxt), "Pattern '%s' found in: %s" % (regex.pattern, ectxt)) + self.assert_multi_regex(patterns, ectxt) self.assertTrue(ectxt.endswith("# trailing comment\n")) @@ -3220,9 +3212,7 @@ def test_dump_comments(self): r" 'https://anotherexample\.com', # fallback URL", ]), ] - for pattern in patterns: - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(ectxt), "Pattern '%s' found in: %s" % (regex.pattern, ectxt)) + self.assert_multi_regex(patterns, ectxt) self.assertTrue(ectxt.endswith('\n'.join([ '#', @@ -3601,9 +3591,7 @@ def test_copy_easyconfigs(self): r"# Build statistics", r"buildstats\s*=", ] - for regex in regexs: - regex = re.compile(regex, re.M) - self.assertFalse(regex.search(txt), "Pattern '%s' NOT found in: %s" % (regex.pattern, txt)) + self.assert_multi_regex(regexs, txt, assert_true=False) # make sure copied easyconfig still parses ec = EasyConfig(copied_toy_ec) @@ -3644,7 +3632,7 @@ def test_template_constant_dict(self): # 'arch' needs to be handled separately, since value depends on system architecture self.assertIn('arch', res) arch = res.pop('arch') - self.assertTrue(arch_regex.match(arch), "'%s' matches with pattern '%s'" % (arch, arch_regex.pattern)) + self.assertRegex(arch, arch_regex) self.assertEqual(res, expected) @@ -3733,7 +3721,7 @@ def test_template_constant_dict(self): self.assertIn('arch', res) arch = res.pop('arch') - self.assertTrue(arch_regex.match(arch), "'%s' matches with pattern '%s'" % (arch, arch_regex.pattern)) + self.assertRegex(arch, arch_regex) self.assertEqual(res, expected) @@ -3752,7 +3740,7 @@ def test_template_constant_dict(self): self.assertIn('arch', res) arch = res.pop('arch') - self.assertTrue(arch_regex.match(arch), "'%s' matches with pattern '%s'" % (arch, arch_regex.pattern)) + self.assertRegex(arch, arch_regex) self.assertEqual(res, expected) @@ -3769,7 +3757,7 @@ def test_template_constant_dict(self): self.assertIn('arch', res) arch = res.pop('arch') - self.assertTrue(arch_regex.match(arch), "'%s' matches with pattern '%s'" % (arch, arch_regex.pattern)) + self.assertRegex(arch, arch_regex) expected = { 'module_name': None, @@ -3871,8 +3859,8 @@ def test_hidden_toolchain(self): '--robot', ] outtxt = self.eb_main(args, raise_error=True) - self.assertTrue(re.search(r'module: GCC/\.4\.9\.2', outtxt)) - self.assertTrue(re.search(r'module: gzip/1\.6-GCC-4\.9\.2', outtxt)) + self.assertIn('module: GCC/.4.9.2', outtxt) + self.assertIn('module: gzip/1.6-GCC-4.9.2', outtxt) def test_categorize_files_by_type(self): """Test categorize_files_by_type""" @@ -4283,8 +4271,7 @@ def test_check_sha256_checksums(self): res = check_sha256_checksums(ecs) self.assertEqual(len(res), 1) - regex = re.compile(r"Non-SHA256 checksum\(s\) found for toy-0.0.tar.gz:.*not_really_a_sha256_checksum") - self.assertTrue(regex.match(res[0]), "Pattern '%s' found in: %s" % (regex.pattern, res[0])) + self.assertRegex(res[0], r"Non-SHA256 checksum\(s\) found for toy-0.0.tar.gz:.*not_really_a_sha256_checksum") # Extension with nosource: True test_ec_txt = passing_test_ec_txt + "exts_list = [('bar', '0.0', { 'nosource': True })]" @@ -4606,7 +4593,7 @@ def test_fix_deprecated_easyconfigs(self): r'^$', ] for idx, pattern in enumerate(patterns): - self.assertTrue(re.match(pattern, stdout[idx]), "Pattern '%s' matches '%s'" % (pattern, stdout[idx])) + self.assertRegex(stdout[idx], pattern) # cleanup remove_file(glob.glob(os.path.join(test_ec + '.orig*'))[0]) @@ -5007,10 +4994,8 @@ def test_recursive_module_unload(self): eb.prepare_step() eb.make_module_step() modtxt = read_file(test_module) - fail_msg = "Pattern '%s' should be found in: %s" % (guarded_load_regex.pattern, modtxt) - self.assertTrue(guarded_load_regex.search(modtxt), fail_msg) - fail_msg = "Pattern '%s' should not be found in: %s" % (recursive_unload_regex.pattern, modtxt) - self.assertFalse(recursive_unload_regex.search(modtxt), fail_msg) + self.assertRegex(modtxt, guarded_load_regex) + self.assertNotRegex(modtxt, recursive_unload_regex) remove_file(test_module) @@ -5030,15 +5015,11 @@ def test_recursive_module_unload(self): eb_bis.make_module_step() modtxt = read_file(test_module) if self.modtool.supports_safe_auto_load: - fail_msg = "Pattern '%s' should be found in: %s" % (guarded_load_regex.pattern, modtxt) - self.assertTrue(guarded_load_regex.search(modtxt), fail_msg) - fail_msg = "Pattern '%s' should not be found in: %s" % (recursive_unload_regex.pattern, modtxt) - self.assertFalse(recursive_unload_regex.search(modtxt), fail_msg) + self.assertRegex(modtxt, guarded_load_regex) + self.assertNotRegex(modtxt, recursive_unload_regex) else: - fail_msg = "Pattern '%s' should not be found in: %s" % (guarded_load_regex.pattern, modtxt) - self.assertFalse(guarded_load_regex.search(modtxt), fail_msg) - fail_msg = "Pattern '%s' should be found in: %s" % (recursive_unload_regex.pattern, modtxt) - self.assertTrue(recursive_unload_regex.search(modtxt), fail_msg) + self.assertNotRegex(modtxt, guarded_load_regex) + self.assertRegex(modtxt, recursive_unload_regex) # recursive_mod_unload build option is honored update_build_option('recursive_mod_unload', True) @@ -5049,15 +5030,11 @@ def test_recursive_module_unload(self): eb.make_module_step() modtxt = read_file(test_module) if self.modtool.supports_safe_auto_load: - fail_msg = "Pattern '%s' should be found in: %s" % (guarded_load_regex.pattern, modtxt) - self.assertTrue(guarded_load_regex.search(modtxt), fail_msg) - fail_msg = "Pattern '%s' should not be found in: %s" % (recursive_unload_regex.pattern, modtxt) - self.assertFalse(recursive_unload_regex.search(modtxt), fail_msg) + self.assertRegex(modtxt, guarded_load_regex) + self.assertNotRegex(modtxt, recursive_unload_regex) else: - fail_msg = "Pattern '%s' should not be found in: %s" % (guarded_load_regex.pattern, modtxt) - self.assertFalse(guarded_load_regex.search(modtxt), fail_msg) - fail_msg = "Pattern '%s' should be found in: %s" % (recursive_unload_regex.pattern, modtxt) - self.assertTrue(recursive_unload_regex.search(modtxt), fail_msg) + self.assertNotRegex(modtxt, guarded_load_regex) + self.assertRegex(modtxt, recursive_unload_regex) # disabling via easyconfig parameter works even when recursive_mod_unload build option is enabled self.assertTrue(build_option('recursive_mod_unload')) @@ -5074,10 +5051,8 @@ def test_recursive_module_unload(self): eb_bis.prepare_step() eb_bis.make_module_step() modtxt = read_file(test_module) - fail_msg = "Pattern '%s' should be found in: %s" % (guarded_load_regex.pattern, modtxt) - self.assertTrue(guarded_load_regex.search(modtxt), fail_msg) - fail_msg = "Pattern '%s' should not be found in: %s" % (recursive_unload_regex.pattern, modtxt) - self.assertFalse(recursive_unload_regex.search(modtxt), fail_msg) + self.assertRegex(modtxt, guarded_load_regex) + self.assertNotRegex(modtxt, recursive_unload_regex) def test_pure_ec(self): """ @@ -5359,8 +5334,7 @@ def test_easyconfigs_caches(self): stdout = self.get_stdout() self.mock_stdout(False) - regex = re.compile(r"generating module file @ .*/modules/all/libtoy/0.0", re.M) - self.assertTrue(regex.search(stdout), "Pattern '%s' should be found in: %s" % (regex.pattern, stdout)) + self.assertRegex(stdout, "generating module file @ .*/modules/all/libtoy/0.0") # wipe libtoy easyconfig (but path still needs to exist) write_file(libtoy_ec, '') @@ -5371,8 +5345,7 @@ def test_easyconfigs_caches(self): stdout = self.get_stdout() self.mock_stdout(False) - regex = re.compile(r"libtoy/0\.0 is already installed", re.M) - self.assertTrue(regex.search(stdout), "Pattern '%s' should be found in: %s" % (regex.pattern, stdout)) + self.assertRegex(stdout, r"libtoy/0\.0 is already installed") def test_templates(self): """ @@ -5408,8 +5381,7 @@ def test_templates(self): self.assertEqual(ec['description'], "name: %(name)s, version: %(version)s, pyshortver: %(pyshortver)s") self.assertFalse(stdout.getvalue()) - regex = re.compile(r"WARNING: Failed to resolve all templates.* %\(pyshortver\)s", re.M) - self.assertRegex(stderr.getvalue(), regex) + self.assertRegex(stderr.getvalue(), r"WARNING: Failed to resolve all templates.* %\(pyshortver\)s") def suite(loader=None): diff --git a/test/framework/easyconfigversion.py b/test/framework/easyconfigversion.py index dbce65dacf..4ccfd483a7 100644 --- a/test/framework/easyconfigversion.py +++ b/test/framework/easyconfigversion.py @@ -46,21 +46,21 @@ def test_parser_regex(self): """Test the version parser""" vop = VersionOperator() # version tests - self.assertTrue(vop.regex.search('< 4')) - self.assertTrue(vop.regex.search('>= 20131016')) - self.assertTrue(vop.regex.search('<= 1.2.3')) - self.assertTrue(vop.regex.search('> 2.4')) - self.assertTrue(vop.regex.search('== 1.2b')) - self.assertTrue(vop.regex.search('< 2.0dev')) - self.assertTrue(vop.regex.search('1.2.3')) # operator is optional, '==' is default - self.assertFalse(vop.regex.search('>=')) # version is mandatory (even if DEFAULT_UNDEFINED_VERSION exists) - self.assertFalse(vop.regex.search('%s1.2.3' % vop.SEPARATOR)) # no separator usage w/o something to separate - self.assertFalse(vop.regex.search('1.2.3%s' % vop.SEPARATOR)) # no separator usage w/o something to separate - self.assertFalse(vop.regex.search('>%s2.4' % vop.SEPARATOR * 2)) # double space as separator is not allowed - self.assertFalse(vop.regex.search('>%s 2.4' % vop.SEPARATOR)) # double separator is not allowed - self.assertTrue(vop.regex.search('>%sa2.4' % vop.SEPARATOR)) # version starts/ends with *any* word character - self.assertTrue(vop.regex.search('>%s2.4_' % vop.SEPARATOR)) # version starts/ends with *any* word character - self.assertTrue(vop.regex.search('>%sG2.4_' % vop.SEPARATOR)) # version starts/ends with *any* word character + self.assertRegex('< 4', vop.regex) + self.assertRegex('>= 20131016', vop.regex) + self.assertRegex('<= 1.2.3', vop.regex) + self.assertRegex('> 2.4', vop.regex) + self.assertRegex('== 1.2b', vop.regex) + self.assertRegex('< 2.0dev', vop.regex) + self.assertRegex('1.2.3', vop.regex) # operator is optional, '==' is default + self.assertNotRegex('>=', vop.regex) # version is mandatory (even if DEFAULT_UNDEFINED_VERSION exists) + self.assertNotRegex('%s1.2.3' % vop.SEPARATOR, vop.regex) # no separator usage w/o something to separate + self.assertNotRegex('1.2.3%s' % vop.SEPARATOR, vop.regex) # no separator usage w/o something to separate + self.assertNotRegex('>%s2.4' % vop.SEPARATOR * 2, vop.regex) # double space as separator is not allowed + self.assertNotRegex('>%s 2.4' % vop.SEPARATOR, vop.regex) # double separator is not allowed + self.assertRegex('>%sa2.4' % vop.SEPARATOR, vop.regex) # version starts/ends with *any* word character + self.assertRegex('>%s2.4_' % vop.SEPARATOR, vop.regex) # version starts/ends with *any* word character + self.assertRegex('>%sG2.4_' % vop.SEPARATOR, vop.regex) # version starts/ends with *any* word character def test_boolean(self): """Test boolean test""" @@ -184,7 +184,7 @@ def test_parser_toolchain_regex(self): (tc, None), # only toolchain name, no dict repr (default operator is >=, version is 0.0.0) ] for txt, as_dict in ok_tests: - self.assertTrue(top.regex.search(txt), "%s matches toolchain section marker regex" % txt) + self.assertRegex(txt, top.regex) tcversop = ToolchainVersionOperator(txt) self.assertTrue(tcversop) self.assertEqual(tcversop.as_dict(), as_dict) @@ -198,7 +198,7 @@ def test_parser_toolchain_regex(self): ">= 1.2.3", ] for txt in fail_tests: - self.assertFalse(top.regex.search(txt), "%s doesn't match toolchain section marker regex" % txt) + self.assertNotRegex(txt, top.regex) tcv = ToolchainVersionOperator(txt) self.assertEqual(tcv.tc_name, None) self.assertEqual(tcv.tcversop_str, None) diff --git a/test/framework/easystack.py b/test/framework/easystack.py index 5aa4d58d58..c9cf45853b 100644 --- a/test/framework/easystack.py +++ b/test/framework/easystack.py @@ -29,7 +29,6 @@ @author: Kenneth Hoste (Ghent University) """ import os -import re import sys import tempfile from unittest import TextTestRunner @@ -159,8 +158,7 @@ def test_easystack_restore_env_after_each_build(self): stdout = self.eb_main(args, do_build=True, raise_error=True) stdout = self.eb_main(args, do_build=True, raise_error=True, reset_env=False, redo_init_config=False) self.mock_stdout(False) - regex = re.compile(r"WARNING Loaded modules detected: \[.*gompi/2018.*\]\n") - self.assertFalse(regex.search(stdout), "Pattern '%s' should not be found in: %s" % (regex.pattern, stdout)) + self.assertNotRegex(stdout, r"WARNING Loaded modules detected: \[.*gompi/2018.*\]\n") # temporary directory after run should be exactly 2 levels deeper than original one: # - 1 level added by setting up configuration in EasyBuild main function diff --git a/test/framework/filetools.py b/test/framework/filetools.py index 5da4f55cc9..1bf5a3c494 100644 --- a/test/framework/filetools.py +++ b/test/framework/filetools.py @@ -649,7 +649,7 @@ def test_download_file(self): self.assertEqual(path, target_location) self.assertNotExists(target_location) - self.assertTrue(re.match("file written: .*/foo", txt)) + self.assertRegex(txt, "file written: .*/foo") with self.mocked_stdout_stderr(): ft.download_file(fn, source_url, target_location, forced=True) @@ -979,8 +979,7 @@ def test_read_write_file(self): ft.write_file(fp, 'foo', backup=True, verbose=True) stdout = self.get_stdout() self.mock_stdout(False) - regex = re.compile("^== Backup of .*/test.txt created at .*/test.txt.bak_[0-9]*") - self.assertTrue(regex.search(stdout), "Pattern '%s' found in: %s" % (regex.pattern, stdout)) + self.assertRegex(stdout, "^== Backup of .*/test.txt created at .*/test.txt.bak_[0-9]*") # by default, write_file will just blindly overwrite an already existing file self.assertExists(fp) @@ -1017,7 +1016,7 @@ def test_read_write_file(self): self.mock_stdout(False) self.assertNotExists(foo) - self.assertTrue(re.match("^file written: .*/foo.txt$", txt)) + self.assertRegex(txt, "^file written: .*/foo.txt$") ft.write_file(foo, 'bar', forced=True) self.assertExists(foo) @@ -1673,7 +1672,7 @@ def test_apply_regex_substitutions(self): r" \* regex pattern '= 5', replacement string '= 4'", '', ]) - self.assertTrue(re.search(regex, stdout), "Pattern '%s' should be found in: %s" % (regex, stdout)) + self.assertRegex(stdout, regex) def test_find_flexlm_license(self): """Test find_flexlm_license function.""" @@ -2044,7 +2043,7 @@ def test_copy_file(self): self.mock_stdout(False) self.assertNotExists(target_path) - self.assertTrue(re.search("^copied file .*/toy-0.0.eb to .*/toy.eb", txt)) + self.assertRegex(txt, "^copied file .*/toy-0.0.eb to .*/toy.eb") # forced copy, even in dry run mode self.mock_stdout(True) @@ -2066,7 +2065,7 @@ def test_copy_file(self): ft.copy_file(src, target, force_in_dry_run=False) txt = self.get_stdout() self.mock_stdout(False) - self.assertTrue(re.search("^copied file %s to %s" % (src, target), txt)) + self.assertRegex(txt, "^copied file %s to %s" % (src, target)) # However, if we add 'force_in_dry_run=True' it should throw an exception self.assertErrorRegex(EasyBuildError, "Could not copy *", ft.copy_file, src, target, force_in_dry_run=True) @@ -2194,8 +2193,7 @@ def test_copy_files(self): self.mock_stderr(False) self.mock_stdout(False) self.assertEqual(stderr, '') - regex = re.compile(r"^1 file\(s\) copied to .*/target") - self.assertTrue(regex.match(stdout), "Pattern '%s' should be found in: %s" % (regex.pattern, stdout)) + self.assertRegex(stdout, r"^1 file\(s\) copied to .*/target") ft.remove_dir(target) @@ -2206,8 +2204,7 @@ def test_copy_files(self): self.mock_stderr(False) self.mock_stdout(False) self.assertEqual(stderr, '') - regex = re.compile(r"/.*/toy-0\.0\.eb copied to .*/target") - self.assertTrue(regex.match(stdout), "Pattern '%s' should be found in: %s" % (regex.pattern, stdout)) + self.assertRegex(stdout, r"^/.*/toy-0\.0\.eb copied to .*/target") ft.remove_file(target) @@ -2225,8 +2222,7 @@ def test_copy_files(self): self.assertNotExists(os.path.join(target, 'test.eb')) self.assertEqual(stderr, '') - regex = re.compile("^copied test.eb to .*/target") - self.assertTrue(regex.match(stdout), "Pattern '%s' should be found in: %s" % (regex.pattern, stdout)) + self.assertRegex(stdout, "^copied test.eb to .*/target") self.mock_stderr(True) self.mock_stdout(True) @@ -2239,8 +2235,7 @@ def test_copy_files(self): self.assertNotExists(os.path.join(target, 'foo.eb')) self.assertEqual(stderr, '') - regex = re.compile("^copied 2 files to .*/target") - self.assertTrue(regex.match(stdout), "Pattern '%s' should be found in: %s" % (regex.pattern, stdout)) + self.assertRegex(stdout, "^copied 2 files to .*/target") def test_has_recursive_symlinks(self): """Test has_recursive_symlinks function""" @@ -2379,7 +2374,7 @@ def ignore_func(_, names): self.mock_stdout(False) self.assertNotExists(target_dir) - self.assertTrue(re.search("^copied directory .*/GCC to .*/%s" % os.path.basename(target_dir), txt)) + self.assertRegex(txt, "^copied directory .*/GCC to .*/%s" % os.path.basename(target_dir)) # forced copy, even in dry run mode self.mock_stdout(True) @@ -2426,8 +2421,8 @@ def test_copy(self): self.assertNotExists(os.path.join(self.test_prefix, 'toy')) self.assertNotExists(os.path.join(self.test_prefix, 'GCC-4.6.3.eb')) - self.assertTrue(re.search("^copied directory .*/toy to .*/toy", txt, re.M)) - self.assertTrue(re.search("^copied file .*/GCC-4.6.3.eb to .*/GCC-4.6.3.eb", txt, re.M)) + self.assertRegex(txt, re.compile("^copied directory .*/toy to .*/toy", re.M)) + self.assertRegex(txt, re.compile("^copied file .*/GCC-4.6.3.eb to .*/GCC-4.6.3.eb", re.M)) # forced copy, even in dry run mode self.mock_stdout(True) @@ -2520,7 +2515,7 @@ def test_extract_file(self): self.assertTrue(os.path.samefile(path, self.test_prefix)) self.assertNotExists(os.path.join(self.test_prefix, 'toy-0.0')) - self.assertTrue(re.search('running shell command "tar xzf .*/toy-0.0.tar.gz"', txt)) + self.assertRegex(txt, 'running shell command "tar xzf .*/toy-0.0.tar.gz"') with self.mocked_stdout_stderr(): path = ft.extract_file(toy_tarball, self.test_prefix, forced=True, change_into_dir=False) @@ -2586,8 +2581,7 @@ def test_empty_dir(self): txt = self.get_stdout() self.mock_stdout(False) - regex = re.compile("^directory [^ ]* emptied$") - self.assertTrue(regex.match(txt), f"Pattern '{regex.pattern}' found in: {txt}") + self.assertRegex(txt, "^directory [^ ]* emptied$") def test_remove(self): """Test remove_file, remove_dir and join remove functions.""" @@ -2651,8 +2645,7 @@ def test_remove(self): txt = self.get_stdout() self.mock_stdout(False) - regex = re.compile("^file [^ ]* removed$") - self.assertTrue(regex.match(txt), "Pattern '%s' found in: %s" % (regex.pattern, txt)) + self.assertRegex(txt, "^file [^ ]* removed$") for remove_dir_function in (ft.remove_dir, ft.remove): self.mock_stdout(True) @@ -2660,8 +2653,7 @@ def test_remove(self): txt = self.get_stdout() self.mock_stdout(False) - regex = re.compile("^directory [^ ]* removed$") - self.assertTrue(regex.match(txt), "Pattern '%s' found in: %s" % (regex.pattern, txt)) + self.assertRegex(txt, "^directory [^ ]* removed$") ft.adjust_permissions(self.test_prefix, stat.S_IWUSR, add=True) @@ -2739,8 +2731,7 @@ def test_index_functions(self): ] index_txt = ft.read_file(index_fp) for fn in expected_header + expected: - regex = re.compile('^%s$' % fn, re.M) - self.assertTrue(regex.search(index_txt), "Pattern '%s' found in: %s" % (regex.pattern, index_txt)) + self.assertRegex(index_txt, re.compile('^%s$' % fn, re.M)) # test load_index function self.mock_stderr(True) @@ -2770,8 +2761,7 @@ def test_index_functions(self): index_txt = ft.read_file(index_fp) expected_header[1] = r"# valid until: 9999-12-31 23:59:59\.9+" for fn in expected_header + expected: - regex = re.compile('^%s$' % fn, re.M) - self.assertTrue(regex.search(index_txt), "Pattern '%s' found in: %s" % (regex.pattern, index_txt)) + self.assertRegex(index_txt, re.compile('^%s$' % fn, re.M)) self.mock_stderr(True) self.mock_stdout(True) @@ -2803,8 +2793,7 @@ def test_index_functions(self): self.mock_stdout(False) self.assertIsNone(index) self.assertFalse(stdout) - regex = re.compile(r"WARNING: Index for %s is no longer valid \(too old\), so ignoring it" % ecs_dir) - self.assertTrue(regex.search(stderr), "Pattern '%s' found in: %s" % (regex.pattern, stderr)) + self.assertRegex(stderr, r"WARNING: Index for %s is no longer valid \(too old\), so ignoring it" % ecs_dir) # check whether load_index takes into account --ignore-index init_config(build_options={'ignore_index': True}) @@ -2998,8 +2987,7 @@ def test_move_file(self): self.mock_stderr(False) # informative message printed, but file was not actually moved - regex = re.compile(r"^moved file .*/test\.txt to .*/new_test\.txt$") - self.assertTrue(regex.search(stdout), "Pattern '%s' found in: %s" % (regex.pattern, stdout)) + self.assertRegex(stdout, r"^moved file .*/test\.txt to .*/new_test\.txt$") self.assertEqual(stderr, '') self.assertExists(test_file) @@ -3017,7 +3005,7 @@ def test_find_backup_name_candidate(self): res = ft.find_backup_name_candidate(test_file) self.assertTrue(os.path.samefile(os.path.dirname(res), self.test_prefix)) fn = os.path.basename(res) - self.assertTrue(regex.match(fn), "'%s' matches pattern '%s'" % (fn, regex.pattern)) + self.assertRegex(fn, regex) # create expected next backup location to (try and) see if it's handled well timestamp = datetime.datetime.now().strftime('%Y%m%d%H%M%S') @@ -3026,7 +3014,7 @@ def test_find_backup_name_candidate(self): res = ft.find_backup_name_candidate(test_file) self.assertTrue(os.path.samefile(os.path.dirname(res), self.test_prefix)) fn = os.path.basename(res) - self.assertTrue(regex.match(fn), "'%s' matches pattern '%s'" % (fn, regex.pattern)) + self.assertRegex(fn, regex) def test_diff_files(self): """Test for diff_files function""" @@ -3061,8 +3049,7 @@ def test_diff_files(self): ]) res = ft.diff_files(foo, bar) self.assertTrue(res.endswith(expected), "%s ends with %s" % (res, expected)) - regex = re.compile(r'^--- .*/foo\s*\n\+\+\+ .*/bar\s*$', re.M) - self.assertTrue(regex.search(res), "Pattern '%s' found in: %s" % (regex.pattern, res)) + self.assertRegex(res, re.compile(r'^--- .*/foo\s*\n\+\+\+ .*/bar\s*$', re.M)) @requires_github_access() def test_github_get_source_tarball_from_git(self): @@ -3085,8 +3072,7 @@ def run_check(): stdout = self.get_stdout() stderr = self.get_stderr() self.assertEqual(stderr, '') - regex = re.compile(expected) - self.assertTrue(regex.search(stdout), "Pattern '%s' found in: %s" % (regex.pattern, stdout)) + self.assertRegex(stdout, expected) self.assertEqual(os.path.dirname(res), target_dir) self.assertEqual(os.path.basename(res), 'test.tar.xz') @@ -3214,9 +3200,9 @@ def run_check(): regex = re.compile("Can not create reproducible archive.*") if test_filename in bad_filenames: - self.assertTrue(regex.search(stderr), f"Pattern '{regex.pattern}' found in: {stderr}") + self.assertRegex(stderr, regex) else: - self.assertFalse(regex.search(stderr), f"Pattern '{regex.pattern}' found in: {stderr}") + self.assertNotRegex(stderr, regex) ref_filename = f"{test_filename}.tar.xz" if test_filename in noext_filename else test_filename self.assertTrue(res.endswith(ref_filename)) @@ -3499,8 +3485,7 @@ def test_fake_vsc(self): self.assertEqual(stdout, '') error_pattern = r"Detected import from 'vsc' namespace in .*test/framework/filetools.py \(line [0-9]+\)" - regex = re.compile(r"^\nERROR: %s" % error_pattern) - self.assertTrue(regex.search(stderr), "Pattern '%s' found in: %s" % (regex.pattern, stderr)) + self.assertRegex(stderr, r"^\nERROR: %s" % error_pattern) # also test with import from another module test_python_mod = os.path.join(self.test_prefix, 'test_fake_vsc', 'import_vsc.py') @@ -3523,8 +3508,7 @@ def test_fake_vsc(self): self.assertEqual(stdout, '') error_pattern = r"Detected import from 'vsc' namespace in .*/test_fake_vsc/import_vsc.py \(line 1\)" - regex = re.compile(r"^\nERROR: %s" % error_pattern) - self.assertTrue(regex.search(stderr), "Pattern '%s' found in: %s" % (regex.pattern, stderr)) + self.assertRegex(stderr, r"^\nERROR: %s" % error_pattern) # no error if import was detected from pkgutil.py or pkg_resources/__init__.py, # since that may be triggered by a system-wide vsc-base installation diff --git a/test/framework/general.py b/test/framework/general.py index 857f55d74b..d4353e6a5a 100644 --- a/test/framework/general.py +++ b/test/framework/general.py @@ -61,7 +61,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.""" @@ -106,10 +106,10 @@ def test_docstrings(self): # easybuild.framework.__file__ provides location to /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): @@ -120,8 +120,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.""" diff --git a/test/framework/github.py b/test/framework/github.py index 3c0337a73a..4577fecfd8 100644 --- a/test/framework/github.py +++ b/test/framework/github.py @@ -595,10 +595,10 @@ def test_github_fetch_latest_commit_sha(self): return sha = gh.fetch_latest_commit_sha('easybuild-framework', 'easybuilders', github_user=GITHUB_TEST_ACCOUNT) - self.assertTrue(re.match('^[0-9a-f]{40}$', sha)) + self.assertRegex(sha, '^[0-9a-f]{40}$') sha = gh.fetch_latest_commit_sha('easybuild-easyblocks', 'easybuilders', github_user=GITHUB_TEST_ACCOUNT, branch='develop') - self.assertTrue(re.match('^[0-9a-f]{40}$', sha)) + self.assertRegex(sha, '^[0-9a-f]{40}$') def test_github_download_repo(self): """Test download_repo function.""" @@ -615,7 +615,7 @@ def test_github_download_repo(self): self.assertTrue(os.path.samefile(path, repodir)) self.assertExists(repodir) shafile = os.path.join(repodir, 'latest-sha') - self.assertTrue(re.match('^[0-9a-f]{40}$', read_file(shafile))) + self.assertRegex(read_file(shafile), '^[0-9a-f]{40}$') self.assertExists(os.path.join(repodir, 'easybuild', 'easyconfigs', 'f', 'foss', 'foss-2024a.eb')) # current directory should not have changed after calling download_repo @@ -640,7 +640,7 @@ def test_github_download_repo(self): github_user=GITHUB_TEST_ACCOUNT) self.assertTrue(os.path.samefile(path, repodir)) self.assertIn('easybuild', os.listdir(repodir)) - self.assertTrue(re.match('^[0-9a-f]{40}$', read_file(shafile))) + self.assertRegex(read_file(shafile), '^[0-9a-f]{40}$') self.assertExists(os.path.join(repodir, 'easybuild', 'easyblocks', '__init__.py')) self.mock_stdout(False) @@ -742,8 +742,7 @@ def test_github_find_easybuild_easyconfig(self): with self.mocked_stdout_stderr(): path = gh.find_easybuild_easyconfig(github_user=GITHUB_TEST_ACCOUNT) expected = os.path.join('e', 'EasyBuild', r'EasyBuild-[1-9]+\.[0-9]+\.[0-9]+\.eb') - regex = re.compile(expected) - self.assertTrue(regex.search(path), "Pattern '%s' found in '%s'" % (regex.pattern, path)) + self.assertRegex(path, expected) self.assertExists(path) def test_github_find_patches(self): @@ -766,7 +765,7 @@ def test_github_find_patches(self): self.assertEqual(ec, 'toy') reg = re.compile(r'[1-9]+ of [1-9]+ easyconfigs checked') - self.assertTrue(re.search(reg, txt)) + self.assertRegex(txt, reg) self.mock_stdout(True) self.assertEqual(gh.find_software_name_for_patch('test.patch', []), None) @@ -1248,8 +1247,7 @@ def test_push_branch_to_github(self): r"== fetching branch 'main' from https://github.com/%s\.\.\." % github_path, r"== pushing branch 'test123' to remote 'github_.*' \(git@github.com:%s\) \[DRY RUN\]" % github_path, ]) + r'$' - regex = re.compile(pattern) - self.assertTrue(regex.match(stdout.strip()), "Pattern '%s' doesn't match: %s" % (regex.pattern, stdout)) + self.assertRegex(stdout.strip(), pattern) def test_github_pr_test_report(self): """Test for post_pr_test_report function.""" @@ -1279,9 +1277,7 @@ def test_github_pr_test_report(self): r"^\[DRY RUN\] Adding comment to easybuild-easyconfigs issue #1234: 'Test report by @easybuild_test", r"^See https://gist.github.com/%s/DRY_RUN for a full test report.'" % GITHUB_TEST_ACCOUNT, ] - for pattern in patterns: - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(stdout), "Pattern '%s' should be found in: %s" % (regex.pattern, stdout)) + self.assert_multi_regex(patterns, stdout) self.mock_stderr(True) self.mock_stdout(True) @@ -1296,9 +1292,7 @@ def test_github_pr_test_report(self): r"^\[DRY RUN\] Adding comment to easybuild-easyblocks issue #1234: 'Test report by @easybuild_test", r"^See https://gist.github.com/%s/DRY_RUN for a full test report.'" % GITHUB_TEST_ACCOUNT, ] - for pattern in patterns: - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(stdout), "Pattern '%s' should be found in: %s" % (regex.pattern, stdout)) + self.assert_multi_regex(patterns, stdout) # also test combination of --from-pr and --include-easyblocks-from-pr update_build_option('include_easyblocks_from_pr', ['6789']) @@ -1317,9 +1311,7 @@ def test_github_pr_test_report(self): r"^See https://gist.github.com/%s/DRY_RUN for a full test report.'" % GITHUB_TEST_ACCOUNT, r"Using easyblocks from PR\(s\) https://github.com/easybuilders/easybuild-easyblocks/pull/6789", ] - for pattern in patterns: - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(stdout), "Pattern '%s' should be found in: %s" % (regex.pattern, stdout)) + self.assert_multi_regex(patterns, stdout) def test_github_create_test_report(self): """Test create_test_report function.""" diff --git a/test/framework/module_generator.py b/test/framework/module_generator.py index 5834fb731c..563d7a9b0d 100644 --- a/test/framework/module_generator.py +++ b/test/framework/module_generator.py @@ -828,9 +828,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') @@ -845,7 +843,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, re) # check if the extensions is missing if 'module_extensions' is disabled init_config(build_options={'module_extensions': False}) @@ -856,9 +854,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_true=False) def test_prepend_paths(self): """Test generating prepend-paths statements.""" diff --git a/test/framework/modules.py b/test/framework/modules.py index f3272cfa09..476be4193d 100644 --- a/test/framework/modules.py +++ b/test/framework/modules.py @@ -165,9 +165,7 @@ def test_run_module(self): r"^os.environ\[.EBROOTOPENBLAS.\]\s*=\s*./prefix/software/OpenBLAS/0.2.20-GCC-6.4.0-2.28.", r"^os.environ\[.LOADEDMODULES.\]\s*=.*OpenBLAS/0.2.20-GCC-6.4.0-2.28", ] - for pattern in patterns: - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(out), "Pattern '%s' should be found in: %s" % (regex.pattern, out)) + self.assert_multi_regex(patterns, out) # OpenBLAS module did *not* get loaded self.assertNotIn('EBROOTOPENBLAS', os.environ) @@ -183,13 +181,10 @@ def test_run_module(self): r"setenv\W+EBROOTOPENBLAS.+/prefix/software/OpenBLAS/0.2.20-GCC-6.4.0-2.28", r"prepend[_-]path\W+LD_LIBRARY_PATH.+/prefix/software/OpenBLAS/0.2.20-GCC-6.4.0-2.28/lib", ] - for pattern in patterns: - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(out), "Pattern '%s' should be found in: %s" % (regex.pattern, out)) + self.assert_multi_regex(patterns, out) # show method only returns user-facing output (obtained via stderr), not changes to the environment - regex = re.compile(r'^os\.environ\[', re.M) - self.assertFalse(regex.search(out), "Pattern '%s' should not be found in: %s" % (regex.pattern, out)) + self.assertNotRegex(out, re.compile(r'^os\.environ\[', re.M)) def test_list(self): """ @@ -504,13 +499,10 @@ def test_show(self): r"setenv\W+EBROOTGCC.+prefix/software/GCC/7.3.0-2.30", r"^prepend[_-]path\W+PATH.+/prefix/software/GCC/7.3.0-2.30/bin", ] - for pattern in patterns: - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(out), "Pattern '%s' should be found in: %s" % (regex.pattern, out)) + self.assert_multi_regex(patterns, out) # show method only returns user-facing output (obtained via stderr), not changes to the environment - regex = re.compile(r'^os\.environ\[', re.M) - self.assertFalse(regex.search(out), "Pattern '%s' should not be found in: %s" % (regex.pattern, out)) + self.assertNotRegex(out, re.compile(r'^os\.environ\[', re.M)) def test_curr_module_paths(self): """Test for curr_module_paths function.""" @@ -659,11 +651,11 @@ def test_ld_library_path(self): # load module and check that previous LD_LIBRARY_PATH is still there, at the end self.modtool.load(['GCC/4.6.3']) - self.assertTrue(re.search("%s$" % testpath, os.environ['LD_LIBRARY_PATH'])) + self.assertRegex(os.environ['LD_LIBRARY_PATH'], "%s$" % testpath) self.modtool.purge() # check that previous LD_LIBRARY_PATH is still there, at the end - self.assertTrue(re.search("%s$" % testpath, os.environ['LD_LIBRARY_PATH'])) + self.assertRegex(os.environ['LD_LIBRARY_PATH'], "%s$" % testpath) self.modtool.purge() def test_purge(self): @@ -1481,8 +1473,7 @@ def check_loaded_modules(): "To specify action to take when loaded modules are detected, use " "--detect-loaded-modules={error,ignore,purge,unload,warn}", ] - for pattern in patterns: - self.assertTrue(re.search(pattern, stderr, re.M), "Pattern '%s' found in: %s" % (pattern, stderr)) + self.assert_multi_regex(patterns, stderr) # reconfigure EasyBuild to ignore loaded modules for GCC & hwloc & error out when loaded modules are detected options = init_config(args=['--allow-loaded-modules=GCC,hwloc', '--detect-loaded-modules=error']) diff --git a/test/framework/modulestool.py b/test/framework/modulestool.py index 2daa9e3dc8..b09e4ba2ff 100644 --- a/test/framework/modulestool.py +++ b/test/framework/modulestool.py @@ -123,8 +123,7 @@ def test_module_mismatch(self): mt = MockModulesTool(testing=True) logtxt = read_file(self.logfile) - warn_regex = re.compile("WARNING .*pattern .* not found in defined 'module' function") - self.assertTrue(warn_regex.search(logtxt), "Found pattern '%s' in: %s" % (warn_regex.pattern, logtxt)) + self.assertRegex(logtxt, "WARNING .*pattern .* not found in defined 'module' function") # redefine 'module' function with correct module command os.environ['module'] = "() { eval `/bin/echo $*`\n}" @@ -135,8 +134,7 @@ def test_module_mismatch(self): del os.environ['module'] mt = MockModulesTool(testing=True) logtxt = read_file(self.logfile) - warn_regex = re.compile("WARNING No 'module' function defined, can't check if it matches .*") - self.assertTrue(warn_regex.search(logtxt), "Pattern %s found in %s" % (warn_regex.pattern, logtxt)) + self.assertRegex(logtxt, re.compile("WARNING No 'module' function defined, can't check if it matches .*")) fancylogger.logToFile(self.logfile, enable=False) diff --git a/test/framework/options.py b/test/framework/options.py index 63c923581f..ead783650b 100644 --- a/test/framework/options.py +++ b/test/framework/options.py @@ -166,8 +166,7 @@ def test_help_long(self): "Not all option groups included in short help (2)") # for boolean options, we mention in the help text how to disable them - regex = re.compile(r"default: True; disable with\s*--disable-\s*cleanup-\s*builddir", re.M) - self.assertRegex(outtxt, regex) + self.assertRegex(outtxt, r"default: True; disable with\s*--disable-\s*cleanup-\s*builddir") def test_help_rst(self): """Test generating --help in RST output format.""" @@ -220,14 +219,11 @@ def test_info(self): with self.mocked_stdout_stderr(): outtxt = self.eb_main(args) - error_tmpl = "%s log messages are included when using %s ( out: %s)" for log_msg_type in ['INFO', 'ERROR']: - res = re.search(' %s ' % log_msg_type, outtxt) - self.assertTrue(res, error_tmpl % (log_msg_type, info_arg, outtxt)) + self.assertIn(log_msg_type, outtxt) for log_msg_type in ['DEBUG']: - res = re.search(' %s ' % log_msg_type, outtxt) - self.assertTrue(not res, "%s log messages are *not* included when using %s" % (log_msg_type, info_arg)) + self.assertNotIn(log_msg_type, outtxt) def test_quiet(self): """Test enabling quiet logging (errors only).""" @@ -237,14 +233,10 @@ def test_quiet(self): out = self.eb_main(args) for log_msg_type in ['ERROR']: - res = re.search(' %s ' % log_msg_type, out) - msg = "%s log messages are included when using %s (out: %s)" % (log_msg_type, quiet_arg, out) - self.assertTrue(res, msg) + self.assertIn(log_msg_type, out) for log_msg_type in ['DEBUG', 'INFO']: - res = re.search(' %s ' % log_msg_type, out) - msg = "%s log messages are *not* included when using %s (out: %s)" % (log_msg_type, quiet_arg, out) - self.assertTrue(not res, msg) + self.assertNotIn(log_msg_type, out) def test_force(self): """Test forcing installation even if the module is already available.""" @@ -329,8 +321,8 @@ def test_skip(self): # make sure that sanity check is *NOT* skipped under --skip test_ec = os.path.join(self.test_prefix, 'test.eb') test_ec_txt = read_file(toy_ec) - regex = re.compile(r"sanity_check_paths = \{(.|\n)*\}", re.M) - test_ec_txt = regex.sub("sanity_check_paths = {'files': ['bin/nosuchfile'], 'dirs': []}", test_ec_txt) + test_ec_txt = re.sub(r"sanity_check_paths = \{(.|\n)*\}", + "sanity_check_paths = {'files': ['bin/nosuchfile'], 'dirs': []}", test_ec_txt) write_file(test_ec, test_ec_txt) args = [ test_ec, @@ -375,7 +367,7 @@ def test_skipsteps(self): # make sure that sanity check is *NOT* skipped test_ec = os.path.join(self.test_prefix, 'test.eb') test_ec_txt = read_file(toy_ec) - regex = re.compile(r"sanity_check_paths = \{(.|\n)*\}", re.M) + regex = re.compile(r"sanity_check_paths = \{(.|\n)*\}") test_ec_txt = regex.sub("sanity_check_paths = {'files': ['bin/nosuchfile'], 'dirs': []}", test_ec_txt) write_file(test_ec, test_ec_txt) args = [ @@ -624,8 +616,7 @@ def run_test(fmt=None): r'\s*OS_PKG_IBVERBS_DEV: .*', ] - regex = re.compile('\n'.join(pattern_lines), re.M) - self.assertRegex(stdout, regex) + self.assertRegex(stdout, re.compile('\n'.join(pattern_lines), re.M)) for fmt in [None, 'txt', 'rst']: run_test(fmt=fmt) @@ -675,9 +666,7 @@ def run_test(fmt=None): 'by --software-commit command line option', ] - for pattern_line in pattern_lines: - regex = re.compile(pattern_line, re.M) - self.assertRegex(stdout, regex) + self.assert_multi_regex(pattern_lines, stdout) for fmt in [None, 'txt', 'rst']: run_test(fmt=fmt) @@ -962,8 +951,7 @@ def test_avail_cfgfile_constants(self): 'DEFAULT_ROBOT_PATHS': os.path.join(tmpdir, 'easybuild', 'easyconfigs'), } for cst_name, cst_value in cfgfile_constants.items(): - cst_regex = re.compile(r"^\*\s%s:\s.*\s\[value: .*%s.*\]" % (cst_name, cst_value), re.M) - self.assertRegex(logtxt, cst_regex) + self.assertRegex(logtxt, re.compile(r"^\*\s%s:\s.*\s\[value: .*%s.*\]" % (cst_name, cst_value), re.M)) if os.path.exists(dummylogfn): os.remove(dummylogfn) @@ -1096,8 +1084,7 @@ def test_search(self): txt = self.get_stdout() for ec in ["gzip-1.4.eb", "gzip-1.4-GCC-4.6.3.eb"]: - regex = re.compile(r" \* \S*%s$" % ec, re.M) - self.assertRegex(txt, regex) + self.assertRegex(txt, re.compile(r" \* \S*%s$" % ec, re.M)) # search w/ regex args = [ @@ -1109,8 +1096,7 @@ def test_search(self): txt = self.get_stdout() for ec in ['GCC-4.8.2.eb', 'GCC-4.9.2.eb']: - regex = re.compile(r" \* \S*%s$" % ec, re.M) - self.assertRegex(txt, regex) + self.assertRegex(txt, re.compile(r" \* \S*%s$" % ec, re.M)) gcc_ecs = [ 'GCC-4.6.3.eb', @@ -1131,8 +1117,7 @@ def test_search(self): txt = self.get_stdout() for ec in gcc_ecs: - regex = re.compile(r"^ \* %s$" % ec, re.M) - self.assertRegex(txt, regex) + self.assertRegex(txt, re.compile(r"^ \* %s$" % ec, re.M)) # test --search-filename --terse args = [ @@ -1145,8 +1130,7 @@ def test_search(self): txt = self.get_stdout() for ec in gcc_ecs: - regex = re.compile(r"^%s$" % ec, re.M) - self.assertRegex(txt, regex) + self.assertRegex(txt, re.compile(r"^%s$" % ec, re.M)) # also test --search-short/-S for search_arg in ['-S', '--search-short']: @@ -1392,8 +1376,7 @@ def check_copied_files(): self.assertEqual(len(os.listdir(os.getcwd())), 0) args = ['--copy-ec', 'toy-0.0.eb'] stdout = self.mocked_main(args) - regex = re.compile('.*/toy-0.0.eb copied to .*/%s' % os.path.basename(test_working_dir)) - self.assertTrue(regex.match(stdout), "Pattern '%s' found in: %s" % (regex.pattern, stdout)) + self.assertRegex(stdout, '^.*/toy-0.0.eb copied to .*/%s$' % os.path.basename(test_working_dir)) copied_toy_cwd = os.path.join(test_working_dir, 'toy-0.0.eb') self.assertExists(copied_toy_cwd) self.assertEqual(read_file(copied_toy_cwd), toy_ec_txt) @@ -1604,8 +1587,7 @@ def test_dry_run(self): ("GCC-4.6.3.eb", "GCC/4.6.3", 'x'), ] for ec, mod, mark in ecs_mods: - regex = re.compile(r" \* \[%s\] \S+%s \(module: %s\)" % (mark, ec, mod), re.M) - self.assertRegex(logtxt, regex) + self.assertRegex(logtxt, r" \* \[%s\] \S+%s \(module: %s\)" % (mark, ec, mod)) # next test without --robot fd, dummylogfn = tempfile.mkstemp(prefix='easybuild-dummy', suffix='.log') @@ -1622,8 +1604,7 @@ def test_dry_run(self): info_msg = "Dry run: printing build status of easyconfigs" self.assertIn(info_msg, logtxt, "Info message dry running in '%s'" % logtxt) ec, mod, mark = ("gzip-1.4-GCC-4.6.3.eb", "gzip/1.4-GCC-4.6.3", ' ') - regex = re.compile(r" \* \[%s\] \S+%s \(module: %s\)" % (mark, ec, mod), re.M) - self.assertRegex(logtxt, regex) + self.assertRegex(logtxt, r" \* \[%s\] \S+%s \(module: %s\)" % (mark, ec, mod)) def test_persistence_copying_restrictions(self): """ @@ -1760,8 +1741,7 @@ def test_dry_run_short(self): ("GCC-4.6.3.eb", "GCC/4.6.3", 'x'), ] for ec, mod, mark in ecs_mods: - regex = re.compile(r" \* \[%s\] \$CFGS\S+%s \(module: %s\)" % (mark, ec, mod), re.M) - self.assertRegex(outtxt, regex) + self.assertRegex(outtxt, re.compile(r" \* \[%s\] \$CFGS\S+%s \(module: %s\)" % (mark, ec, mod), re.M)) if os.path.exists(dummylogfn): os.remove(dummylogfn) @@ -1810,8 +1790,7 @@ def test_try_robot_force(self): ("FFTW-3.3.7-gompi-2018b.eb", "FFTW/3.3.7-gompi-2018b", 'F'), ] for ec, mod, mark in ecs_mods: - regex = re.compile(r"^ \* \[%s\] \S+%s \(module: %s\)$" % (mark, ec, mod), re.M) - self.assertRegex(outtxt, regex) + self.assertRegex(outtxt, re.compile(r"^ \* \[%s\] \S+%s \(module: %s\)$" % (mark, ec, mod), re.M)) def test_try_toolchain_mapping(self): """Test mapping of subtoolchains with --try-toolchain.""" @@ -1979,8 +1958,9 @@ def test_dry_run_hierarchical(self): ("gzip-1.5-foss-2018a.eb", "MPI/GCC/6.4.0-2.28/OpenMPI/2.1.2", "gzip/1.5", ' '), ] for ec, mod_subdir, mod_name, mark in ecs_mods: - regex = re.compile(r"^ \* \[%s\] \S+%s \(module: %s \| %s\)$" % (mark, ec, mod_subdir, mod_name), re.M) - self.assertRegex(outtxt, regex) + self.assertRegex(outtxt, + re.compile(r"^ \* \[%s\] \S+%s \(module: %s \| %s\)$" % (mark, ec, mod_subdir, mod_name), + re.M)) if os.path.exists(dummylogfn): os.remove(dummylogfn) @@ -2022,8 +2002,9 @@ def test_dry_run_categorized(self): ("gzip-1.5-foss-2018a.eb", "MPI/GCC/6.4.0-2.28/OpenMPI/2.1.2/tools", "gzip/1.5", ' '), ] for ec, mod_subdir, mod_name, mark in ecs_mods: - regex = re.compile(r"^ \* \[%s\] \S+%s \(module: %s \| %s\)$" % (mark, ec, mod_subdir, mod_name), re.M) - self.assertRegex(outtxt, regex) + self.assertRegex(outtxt, + re.compile(r"^ \* \[%s\] \S+%s \(module: %s \| %s\)$" % (mark, ec, mod_subdir, mod_name), + re.M)) if os.path.exists(dummylogfn): os.remove(dummylogfn) @@ -2057,8 +2038,7 @@ def test_github_from_pr(self): for path_prefix, module in modules: ec_fn = "%s.eb" % '-'.join(module.split('/')) path = '.*%s' % os.path.dirname(path_prefix) - regex = re.compile(r"^ \* \[.\] %s.*%s \(module: %s\)$" % (path, ec_fn, module), re.M) - self.assertRegex(outtxt, regex) + self.assertRegex(outtxt, re.compile(r"^ \* \[.\] %s.*%s \(module: %s\)$" % (path, ec_fn, module), re.M)) pr_tmpdir = os.path.join(tmpdir, r'eb-\S{6,8}', 'files_pr22227') self.assertRegex(outtxt, r"Extended list of robot search paths with \['%s'\]:" % pr_tmpdir) @@ -2087,8 +2067,7 @@ def test_github_from_pr(self): for path_prefix, module in modules: ec_fn = "%s.eb" % '-'.join(module.split('/')) path = '.*%s' % os.path.dirname(path_prefix) - regex = re.compile(r"^ \* \[.\] %s.*%s \(module: %s\)$" % (path, ec_fn, module), re.M) - self.assertRegex(outtxt, regex) + self.assertRegex(outtxt, re.compile(r"^ \* \[.\] %s.*%s \(module: %s\)$" % (path, ec_fn, module), re.M)) for pr in ('22227', '19834'): pr_tmpdir = os.path.join(tmpdir, r'eb-\S{6,8}', 'files_pr%s' % pr) @@ -2170,8 +2149,8 @@ def test_github_from_pr_listed_ecs(self): ] for path_prefix, module in modules: ec_fn = "%s.eb" % '-'.join(module.split('/')) - regex = re.compile(r"^ \* \[.\] %s.*%s \(module: %s\)$" % (path_prefix, ec_fn, module), re.M) - self.assertRegex(outtxt, regex) + self.assertRegex(outtxt, + re.compile(r"^ \* \[.\] %s.*%s \(module: %s\)$" % (path_prefix, ec_fn, module), re.M)) # make sure that *only* these modules are listed, no others regex = re.compile(r"^ \* \[.\] .*/(?P.*) \(module: (?P.*)\)$", re.M) @@ -2247,8 +2226,7 @@ def test_from_commit(self): for path_prefix, module in modules: ec_fn = "%s.eb" % '-'.join(module.split('/')) path = '.*%s' % os.path.dirname(path_prefix) - regex = re.compile(r"^ \* \[.\] %s.*%s \(module: %s\)$" % (path, ec_fn, module), re.M) - self.assertRegex(outtxt, regex) + self.assertRegex(outtxt, re.compile(r"^ \* \[.\] %s.*%s \(module: %s\)$" % (path, ec_fn, module), re.M)) # make sure that *only* these modules are listed, no others regex = re.compile(r"^ \* \[.\] .*/(?P.*) \(module: (?P.*)\)$", re.M) @@ -2291,8 +2269,7 @@ def test_from_commit(self): for path_prefix, module in modules: ec_fn = "%s.eb" % '-'.join(module.split('/')) path = '.*%s' % os.path.dirname(path_prefix) - regex = re.compile(r"^ \* \[.\] %s.*%s \(module: %s\)$" % (path, ec_fn, module), re.M) - self.assertRegex(outtxt, regex) + self.assertRegex(outtxt, re.compile(r"^ \* \[.\] %s.*%s \(module: %s\)$" % (path, ec_fn, module), re.M)) # make sure that *only* these modules are listed, no others regex = re.compile(r"^ \* \[.\] .*/(?P.*) \(module: (?P.*)\)$", re.M) @@ -2348,8 +2325,7 @@ def test_xxx_include_easyblocks_from_commit(self): self.assertEqual(stderr, '') self.assertIn(pattern, stdout) - regex = re.compile(r"^ \* \[.\] .*/toy-0.0.eb \(module: toy/0.0\)$", re.M) - self.assertRegex(outtxt, regex) + self.assertRegex(outtxt, re.compile(r"^ \* \[.\] .*/toy-0.0.eb \(module: toy/0.0\)$", re.M)) except URLError as err: print("Ignoring URLError '%s' in test_include_easyblocks_from_commit" % err) @@ -2370,8 +2346,7 @@ def test_no_such_software(self): error_msg1 += "I'm all out of ideas." # error message when template is found error_msg2 = "ERROR Unable to find an easyconfig for the given specifications" - regex = re.compile("(%s|%s)" % (error_msg1, error_msg2)) - self.assertRegex(outtxt, regex) + self.assertRegex(outtxt, re.compile("(%s|%s)" % (error_msg1, error_msg2))) def test_header_footer(self): """Test specifying a module header/footer.""" @@ -2611,8 +2586,7 @@ def test_deprecated(self): except easybuild.tools.build_log.EasyBuildError as err: self.fail("Deprecated logging should work: %s" % err) - stderr_regex = re.compile("^\nWARNING: Deprecated functionality, will no longer work in") - self.assertRegex(stderr, stderr_regex) + self.assertRegex(stderr, re.compile("^\nWARNING: Deprecated functionality, will no longer work in")) # force it to current version, which should result in deprecation EasyBuildOptions( @@ -2747,8 +2721,7 @@ def test_try(self): for extra_args, mod in test_cases: with self.mocked_stdout_stderr(): outtxt = self.eb_main(args + extra_args, verbose=True, raise_error=True) - mod_regex = re.compile(r"\(module: %s\)$" % mod, re.M) - self.assertRegex(outtxt, mod_regex) + self.assertRegex(outtxt, re.compile(r"\(module: %s\)$" % mod, re.M)) for extra_arg in ['--try-software=foo', '--try-toolchain=gompi', '--try-toolchain=gomp,2018a,-a-suffix']: allargs = args + [extra_arg] @@ -2774,8 +2747,7 @@ def test_try(self): ] for extra_args, mod in test_cases: outtxt = self.eb_main(args + extra_args, verbose=True, raise_error=True) - mod_regex = re.compile(r"\(module: %s\)$" % mod, re.M) - self.assertRegex(outtxt, mod_regex) + self.assertRegex(outtxt, re.compile(r"\(module: %s\)$" % mod, re.M)) def test_try_with_copy(self): """Test whether --try options are taken into account.""" @@ -2828,8 +2800,7 @@ def test_software_version_ordering(self): with self.mocked_stdout_stderr(): out = self.eb_main(['--software=GCC,4.10.1'] + args[1:], raise_error=True) - regex = re.compile(r"GCC-4.10.1.eb \(module: GCC/4.10.1\)$", re.M) - self.assertRegex(out, regex) + self.assertRegex(out, re.compile(r"GCC-4.10.1.eb \(module: GCC/4.10.1\)$", re.M)) def test_recursive_try(self): """Test whether recursive --try-X works.""" @@ -2853,8 +2824,7 @@ def test_recursive_try(self): outtxt = self.eb_main(args + extra_args, verbose=True, raise_error=True) # toolchain GCC/4.7.2 (subtoolchain of gompi/2018a) should be listed (and present) - tc_regex = re.compile(r"^ \* \[x\] .*/GCC-6.4.0-2.28.eb \(module: .*GCC/6.4.0-2.28\)$", re.M) - self.assertRegex(outtxt, tc_regex) + self.assertRegex(outtxt, re.compile(r"^ \* \[x\] .*/GCC-6.4.0-2.28.eb \(module: .*GCC/6.4.0-2.28\)$", re.M)) # both toy and gzip dependency should be listed with new toolchains # in this case we map original toolchain `dummy` to the compiler-only GCC subtoolchain of gompi/2018a @@ -2865,8 +2835,7 @@ def test_recursive_try(self): mod = ec_name.replace('-', '/') else: mod = '%s-GCC-6.4.0-2.28' % ec_name.replace('-', '/') - mod_regex = re.compile(r"^ \* \[ \] \S+/eb-\S+/%s \(module: .*%s\)$" % (ec, mod), re.M) - self.assertRegex(outtxt, mod_regex) + self.assertRegex(outtxt, re.compile(r"^ \* \[ \] \S+/eb-\S+/%s \(module: .*%s\)$" % (ec, mod), re.M)) # recursive try also when --(try-)software(-X) is involved for extra_args in [[], @@ -2876,8 +2845,7 @@ def test_recursive_try(self): raise_error=True) # toolchain GCC/6.4.0-2.28 (subtoolchain of gompi/2018a) should be listed (and present) - tc_regex = re.compile(r"^ \* \[x\] .*/GCC-6.4.0-2.28.eb \(module: .*GCC/6.4.0-2.28\)$", re.M) - self.assertRegex(outtxt, tc_regex) + self.assertRegex(outtxt, re.compile(r"^ \* \[x\] .*/GCC-6.4.0-2.28.eb \(module: .*GCC/6.4.0-2.28\)$", re.M)) # both toy and gzip dependency should be listed with new toolchains # in this case we map original toolchain `dummy` to the compiler-only GCC subtoolchain of gompi/2018a @@ -2887,8 +2855,7 @@ def test_recursive_try(self): mod = ec_name.replace('-', '/') if not extra_args: mod += '-GCC-6.4.0-2.28' - mod_regex = re.compile(r"^ \* \[ \] \S+/eb-\S+/%s \(module: .*%s\)$" % (ec, mod), re.M) - self.assertRegex(outtxt, mod_regex) + self.assertRegex(outtxt, re.compile(r"^ \* \[ \] \S+/eb-\S+/%s \(module: .*%s\)$" % (ec, mod), re.M)) # clear fictitious dependency write_file(tweaked_toy_ec, "dependencies = []\n", append=True) @@ -2898,11 +2865,9 @@ def test_recursive_try(self): with self.mocked_stdout_stderr(): outtxt = self.eb_main(args + ['--disable-map-toolchains'] + extra_args, raise_error=True) for mod in ['toy/1.2.3-gompi-2018a', 'gompi/2018a', 'GCC/6.4.0-2.28']: - mod_regex = re.compile(r"\(module: %s\)$" % mod, re.M) - self.assertRegex(outtxt, mod_regex) + self.assertRegex(outtxt, re.compile(r"\(module: %s\)$" % mod, re.M)) for mod in ['gompi/1.2.3', 'GCC/1.2.3']: - mod_regex = re.compile(r"\(module: %s\)$" % mod, re.M) - self.assertNotRegex(outtxt, mod_regex) + self.assertNotRegex(outtxt, re.compile(r"\(module: %s\)$" % mod, re.M)) def test_cleanup_builddir(self): """Test cleaning up of build dir and --disable-cleanup-builddir.""" @@ -3427,8 +3392,7 @@ def test_robot(self): ] re_template = r'^\s\*\s\[[xF ]\]\s%s' for ecfile in ecfiles: - ec_regex = re.compile(re_template % os.path.join(test_ecs_path, ecfile), re.M) - self.assertRegex(outtxt, ec_regex) + self.assertRegex(outtxt, re.compile(re_template % os.path.join(test_ecs_path, ecfile), re.M)) # Check for disabling --robot args.append('--disable-robot') @@ -3518,9 +3482,7 @@ def test_show_default_moduleclasses(self): logtxt = read_file(self.logfile) lst = ["\t%s:[ ]*%s" % (c, d.replace('(', '\\(').replace(')', '\\)')) for (c, d) in DEFAULT_MODULECLASSES] - regex = re.compile("Default available module classes:\n\n" + '\n'.join(lst), re.M) - - self.assertRegex(logtxt, regex) + self.assertRegex(logtxt, "Default available module classes:\n\n" + '\n'.join(lst)) def test_show_default_configfiles(self): """Test --show-default-configfiles.""" @@ -3691,8 +3653,7 @@ def test_xxx_include_easyblocks(self): test_easyblocks = os.path.dirname(os.path.abspath(__file__)) path_pattern = os.path.join(test_easyblocks, 'sandbox', 'easybuild', 'easyblocks', 'f', 'foo.py') - foo_regex = re.compile(r"^\|-- EB_foo \(easybuild.easyblocks.foo @ %s\)" % path_pattern, re.M) - self.assertRegex(logtxt, foo_regex) + self.assertRegex(logtxt, re.compile(r"^\|-- EB_foo \(easybuild.easyblocks.foo @ %s\)" % path_pattern, re.M)) # 'undo' import of foo easyblock del sys.modules['easybuild.easyblocks.foo'] @@ -3749,8 +3710,7 @@ def __init__(self, *args, **kwargs): path_pattern = os.path.join(self.test_prefix, '.*', 'included-easyblocks-.*', 'easybuild', 'easyblocks', 'foo.py') - foo_regex = re.compile(r"^\|-- EB_foo \(easybuild.easyblocks.foo @ %s\)" % path_pattern, re.M) - self.assertRegex(logtxt, foo_regex) + self.assertRegex(logtxt, re.compile(r"^\|-- EB_foo \(easybuild.easyblocks.foo @ %s\)" % path_pattern, re.M)) ec_txt = '\n'.join([ 'easyblock = "EB_foo"', @@ -3809,8 +3769,8 @@ def test_xxx_include_generic_easyblocks(self): path_pattern = os.path.join(self.test_prefix, '.*', 'included-easyblocks-.*', 'easybuild', 'easyblocks', 'generic', 'foobar.py') - foo_regex = re.compile(r"^\|-- FooBar \(easybuild.easyblocks.generic.foobar @ %s\)" % path_pattern, re.M) - self.assertRegex(logtxt, foo_regex) + self.assertRegex(logtxt, + re.compile(r"^\|-- FooBar \(easybuild.easyblocks.generic.foobar @ %s\)" % path_pattern, re.M)) klass = get_easyblock_class('FooBar') self.assertTrue(issubclass(klass, EasyBlock), "%s is an EasyBlock derivative class" % klass) @@ -3856,8 +3816,7 @@ def test_xxx_include_generic_easyblocks(self): mod_pattern = 'easybuild.easyblocks.generic.generictest' path_pattern = os.path.join(self.test_prefix, '.*', 'included-easyblocks-.*', 'easybuild', 'easyblocks', 'generic', 'generictest.py') - foo_regex = re.compile(r"^\|-- GenericTest \(%s @ %s\)" % (mod_pattern, path_pattern), re.M) - self.assertRegex(logtxt, foo_regex) + self.assertRegex(logtxt, re.compile(r"^\|-- GenericTest \(%s @ %s\)" % (mod_pattern, path_pattern), re.M)) klass = get_easyblock_class('GenericTest') self.assertTrue(issubclass(klass, EasyBlock), "%s is an EasyBlock derivative class" % klass) @@ -3908,8 +3867,7 @@ def test_github_xxx_include_easyblocks_from_pr(self): # easyblock included from pr is found path_pattern = os.path.join(self.test_prefix, '.*', 'included-easyblocks-.*', 'easybuild', 'easyblocks') cmm_pattern = os.path.join(path_pattern, 'generic', 'cmakemake.py') - cmm_regex = re.compile(r"\|-- CMakeMake \(easybuild.easyblocks.generic.cmakemake @ %s\)" % cmm_pattern, re.M) - self.assertRegex(logtxt, cmm_regex) + self.assertRegex(logtxt, r"\|-- CMakeMake \(easybuild.easyblocks.generic.cmakemake @ %s\)" % cmm_pattern) # easyblock is found via get_easyblock_class klass = get_easyblock_class('CMakeMake') @@ -3951,8 +3909,7 @@ def test_github_xxx_include_easyblocks_from_pr(self): # easyblock included from pr is found path_pattern = os.path.join(self.test_prefix, '.*', 'included-easyblocks-.*', 'easybuild', 'easyblocks') cmm_pattern = os.path.join(path_pattern, 'generic', 'cmakemake.py') - cmm_regex = re.compile(r"\|-- CMakeMake \(easybuild.easyblocks.generic.cmakemake @ %s\)" % cmm_pattern, re.M) - self.assertRegex(logtxt, cmm_regex) + self.assertRegex(logtxt, r"\|-- CMakeMake \(easybuild.easyblocks.generic.cmakemake @ %s\)" % cmm_pattern) # easyblock is found via get_easyblock_class klass = get_easyblock_class('CMakeMake') @@ -3998,8 +3955,7 @@ def test_github_xxx_include_easyblocks_from_pr(self): # easyconfig from pr is found ec_pattern = os.path.join(self.test_prefix, '.*', 'files_pr22589', 'd', 'DIAMOND', 'DIAMOND-2.1.11-GCC-13.3.0.eb') - ec_regex = re.compile(r"Parsing easyconfig file %s" % ec_pattern, re.M) - self.assertRegex(logtxt, ec_regex) + self.assertRegex(logtxt, r"Parsing easyconfig file %s" % ec_pattern) # easyblock included from pr is found @@ -4376,8 +4332,7 @@ def test_minimal_toolchains(self): self.eb_main(args, do_build=True, raise_error=True, testing=False) txt = self.get_stdout() comp = 'Compiler/GCC/6.4.0-2.28' - sqlite_regex = re.compile(r"SQLite-3.8.10.2-GCC-6.4.0-2.28.eb \(module: %s \| SQLite/" % comp, re.M) - self.assertRegex(txt, sqlite_regex) + self.assertRegex(txt, r"SQLite-3.8.10.2-GCC-6.4.0-2.28.eb \(module: %s \| SQLite/" % comp) def test_extended_dry_run(self): """Test use of --extended-dry-run/-x.""" @@ -4805,8 +4760,7 @@ def test_github_new_update_pr(self): args.append('--pr-commit-msg=this is just a test') txt, _ = self._run_mock_eb(args, do_build=True, raise_error=True, testing=False) - regex = re.compile(r'^\* title: "this is just a test"', re.M) - self.assertRegex(txt, regex) + self.assertRegex(txt, re.compile(r'^\* title: "this is just a test"', re.M)) args = [ # PR for EasyBuild v2.5.0 release @@ -4877,8 +4831,8 @@ def test_github_new_pr_warning_missing_patch(self): test_ec_txt = patches_regex.sub('patches = ["%s"]' % patch_fn, test_ec_txt) write_file(test_ec, test_ec_txt) - new_pr_out_regex = re.compile(r"Opening pull request", re.M) - warning_regex = re.compile("new patch file %s, referenced by .*, is not included in this PR" % patch_fn, re.M) + new_pr_out_regex = re.compile(r"Opening pull request") + warning_regex = re.compile("new patch file %s, referenced by .*, is not included in this PR" % patch_fn) args = [ '--new-pr', @@ -4927,7 +4881,7 @@ def test_github_sync_pr_with_develop(self): r"== merging 'develop' branch into PR branch 'develop'\.\.\.", r"== pushing branch 'develop' to remote '.*' \(git@github\.com:%s\) \[DRY RUN\]" % github_path, ]) - self.assertTrue(re.match(pattern, txt), "Pattern '%s' doesn't match: %s" % (pattern, txt)) + self.assertRegex(txt, f"^{pattern}$") def test_github_sync_branch_with_develop(self): """Test use of --sync-branch-with-develop (dry run only).""" @@ -4956,7 +4910,7 @@ def test_github_sync_branch_with_develop(self): r"== merging 'develop' branch into PR branch '%s'\.\.\." % test_branch, r"== pushing branch '%s' to remote '.*' \(git@github\.com:%s\) \[DRY RUN\]" % (test_branch, github_path), ]) - self.assertTrue(re.match(pattern, stdout), "Pattern '%s' doesn't match: %s" % (pattern, stdout)) + self.assertRegex(stdout, f"^{pattern}$") def test_github_new_pr_python(self): """Check generated PR title for --new-pr on easyconfig that includes Python dependency.""" @@ -4982,8 +4936,7 @@ def test_github_new_pr_python(self): ] txt, _ = self._run_mock_eb(args, do_build=True, raise_error=True, testing=False) - regex = re.compile(r"^\* title: \"\{tools\}\[system/system\] toy v0.0\"$", re.M) - self.assertRegex(txt, regex) + self.assertRegex(txt, re.compile(r"^\* title: \"\{tools\}\[system/system\] toy v0.0\"$", re.M)) # if multiple easyconfigs depending on Python are included, Python version is only listed once gzip_ec = os.path.join(self.test_prefix, 'test.eb') @@ -4993,15 +4946,13 @@ def test_github_new_pr_python(self): txt, _ = self._run_mock_eb(args + [gzip_ec], do_build=True, raise_error=True, testing=False) - regex = re.compile(r"^\* title: \"\{tools\}\[system/system\] toy v0.0, gzip v1.4\"$", re.M) - self.assertRegex(txt, regex) + self.assertRegex(txt, re.compile(r"^\* title: \"\{tools\}\[system/system\] toy v0.0, gzip v1.4\"$", re.M)) # also check with Python listed via multi_deps write_file(toy_ec, toy_ec_txt + "\nmulti_deps = {'Python': ['3.7.2', '2.7.15']}") txt, _ = self._run_mock_eb(args, do_build=True, raise_error=True, testing=False) - regex = re.compile(r"^\* title: \"\{tools\}\[system/system\] toy v0.0\"$", re.M) - self.assertRegex(txt, regex) + self.assertRegex(txt, re.compile(r"^\* title: \"\{tools\}\[system/system\] toy v0.0\"$", re.M)) def test_github_new_pr_delete(self): """Test use of --new-pr to delete easyconfigs.""" @@ -5289,8 +5240,7 @@ def test_show_config(self): r"subdir-modules\s* \(F\) = mods", ] - regex = re.compile('\n'.join(expected_lines)) - self.assertTrue(regex.match(txt), "Pattern '%s' found in: %s" % (regex.pattern, txt)) + self.assertRegex(txt, '\n'.join(expected_lines)) args = ['--configfiles=%s' % cfgfile, '--show-full-config', '--buildpath=/weird/build/dir'] txt, _ = self._run_mock_eb(args, do_build=True, raise_error=True, testing=False) @@ -5325,8 +5275,7 @@ def test_show_config(self): os.environ['EASYBUILD_INCLUDE_EASYBLOCKS'] = testeasyblocktoinclude args = ['--show-config'] txt, _ = self._run_mock_eb(args, do_build=True, raise_error=True, testing=False, strip=True) - regex = re.compile(r'^include-easyblocks \(E\) = .*/testeasyblocktoinclude.py$', re.M) - self.assertRegex(txt, regex) + self.assertRegex(txt, re.compile(r'^include-easyblocks \(E\) = .*/testeasyblocktoinclude.py$', re.M)) def test_show_config_cfg_levels(self): """Test --show-config in relation to how configuring across multiple configuration levels interacts with it.""" @@ -5385,7 +5334,7 @@ def test_prefix_option(self): """Test which configuration settings are affected by --prefix.""" txt, _ = self._run_mock_eb(['--show-full-config', '--prefix=%s' % self.test_prefix], raise_error=True) - regex = re.compile(r"(?P\S*).*%s.*" % self.test_prefix, re.M) + regex = re.compile(r"(?P\S*).*%s.*" % self.test_prefix) expected = [ 'buildpath', @@ -5494,8 +5443,7 @@ def test_stop(self): args = ['toy-0.0.eb', '--force', '--stop=configure'] txt, _ = self._run_mock_eb(args, do_build=True, raise_error=True, testing=False, strip=True) - regex = re.compile(r"COMPLETED: Installation STOPPED successfully \(took .* secs?\)", re.M) - self.assertRegex(txt, regex) + self.assertRegex(txt, r"COMPLETED: Installation STOPPED successfully \(took .* secs?\)") # 'source' step was renamed to 'extract' in EasyBuild 5.0, # see https://github.com/easybuilders/easybuild-framework/pull/4629 @@ -5952,7 +5900,7 @@ def test_check_contrib_style(self): print("Skipping test_check_contrib_style pycodestyle is not available") return - regex = re.compile(r"Running style check on 2 easyconfig\(s\)(.|\n)*>> All style checks PASSed!", re.M) + regex = re.compile(r"Running style check on 2 easyconfig\(s\)(.|\n)*>> All style checks PASSed!") args = [ '--check-style', 'GCC-4.9.2.eb', @@ -6268,8 +6216,7 @@ def test_inject_checksums(self): self.assertIn("'checksums': ['d5bd9908cdefbe2d29c6f8d5b45b2aaed9fd904b5e6397418bb5094fbdb3d838'],", ec_txt) # single-line checksum entry for bar source tarball - regex = re.compile("^[ ]*{'bar-0.0.tar.gz': '%s'},$" % bar_tar_gz_sha256, re.M) - self.assertRegex(ec_txt, regex) + self.assertRegex(ec_txt, re.compile("^[ ]*{'bar-0.0.tar.gz': '%s'},$" % bar_tar_gz_sha256, re.M)) # no single-line checksum entry for bar patches, since line would be > 120 chars bar_patch_patterns = [ @@ -6404,8 +6351,7 @@ def test_inject_checksums(self): self._run_mock_eb(args, raise_error=True, strip=True) ec_txt = read_file(test_ec) - regex = re.compile(r"^checksums = \[\]", re.M) - self.assertNotRegex(ec_txt, regex) + self.assertNotRegex(ec_txt, re.compile(r"^checksums = \[\]", re.M)) ec = EasyConfigParser(test_ec).get_config_dict() expected_checksums = [ @@ -6464,9 +6410,7 @@ def test_inject_checksums(self): ] patterns = [r"^== injecting sha256 checksums for sources & patches in test\.eb\.\.\.$"] patterns.extend(r"^== \* %s: %s$" % next(iter(entry.items())) for entry in expected_checksums) - for pattern in patterns: - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(stdout), "Pattern '%s' found in: %s" % (regex.pattern, stdout)) + self.assert_multi_regex(patterns, stdout) ec = EasyConfigParser(test_ec).get_config_dict() self.assertEqual(ec['checksums'], expected_checksums) @@ -6494,9 +6438,7 @@ def test_inject_checksums(self): ] patterns = [r"^== injecting sha256 checksums for sources & patches in test\.eb\.\.\.$"] patterns.extend(r"^== \* %s: %s$" % next(iter(entry.items())) for entry in expected_checksums) - for pattern in patterns: - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(stdout), "Pattern '%s' found in: %s" % (regex.pattern, stdout)) + self.assert_multi_regex(patterns, stdout) ec = EasyConfigParser(test_ec).get_config_dict() self.assertEqual(ec['checksums'], expected_checksums) @@ -6562,8 +6504,8 @@ def test_force_download(self): '--sourcepath=%s' % self.test_prefix, ] _stdout, stderr = self._run_mock_eb(args, do_build=True, raise_error=True, verbose=True, strip=True) - regex = re.compile(r"^WARNING: Found file toy-0.0.tar.gz at .*, but re-downloading it anyway\.\.\.$") - self.assertTrue(regex.match(stderr), "Pattern '%s' matches: %s" % (regex.pattern, stderr)) + self.assertRegex(stderr, + re.compile(r"^WARNING: Found file toy-0.0.tar.gz at .*, but re-downloading it anyway\.\.\.$")) # check that existing source tarball was backed up toy_tar_backups = glob.glob(os.path.join(self.test_prefix, 't', 'toy', '*.bak_*')) @@ -6938,8 +6880,7 @@ def test_fake_vsc_include(self): with self.mocked_stdout_stderr(): self.assertErrorRegex(SystemExit, '1', self.eb_main, args, do_build=True, raise_error=True, verbose=True) stderr = self.get_stderr() - regex = re.compile("ERROR: Detected import from 'vsc' namespace in .*/test_mns.py") - self.assertRegex(stderr, regex) + self.assertRegex(stderr, "ERROR: Detected import from 'vsc' namespace in .*/test_mns.py") def test_installdir(self): """Check naming scheme of installation directory.""" @@ -6967,8 +6908,9 @@ def test_cuda_compute_capabilities(self): args = ['--cuda-compute-capabilities=3.5,6.2,7.0,9.0a,10.0f', '--show-config'] txt, _ = self._run_mock_eb(args, do_build=True, raise_error=True, testing=False, strip=True) - regex = re.compile(r"^cuda-compute-capabilities\s*\(C\)\s*=\s*3\.5, 6\.2, 7\.0, 9\.0a, 10\.0f$", re.M) - self.assertRegex(txt, regex) + self.assertRegex(txt, + re.compile(r"^cuda-compute-capabilities\s*\(C\)\s*=\s*3\.5, 6\.2, 7\.0, 9\.0a, 10\.0f$", + re.M)) def test_create_index(self): """Test --create-index option.""" @@ -7008,8 +6950,7 @@ def test_create_index(self): args.extend(['--index-max-age=0', '--force']) self._run_mock_eb(args, raise_error=True) index_txt = read_file(index_fp) - regex = re.compile(r"^# valid until: 9999-12-31 23:59:59", re.M) - self.assertRegex(index_txt, regex) + self.assertRegex(index_txt, re.compile(r"^# valid until: 9999-12-31 23:59:59", re.M)) def test_sysroot(self): """Test use of --sysroot option.""" @@ -7020,15 +6961,13 @@ def test_sysroot(self): stdout, stderr = self._run_mock_eb([sysroot_arg, '--show-config'], raise_error=True) self.assertEqual(stderr, '') - sysroot_regex = re.compile(r'^sysroot\s*\(C\) = %s$' % self.test_prefix, re.M) - self.assertRegex(stdout, sysroot_regex) + self.assertRegex(stdout, re.compile(r'^sysroot\s*\(C\) = %s$' % self.test_prefix, re.M)) os.environ['EASYBUILD_SYSROOT'] = self.test_prefix stdout, stderr = self._run_mock_eb(['--show-config'], raise_error=True) self.assertEqual(stderr, '') - sysroot_regex = re.compile(r'^sysroot\s*\(E\) = %s$' % self.test_prefix, re.M) - self.assertRegex(stdout, sysroot_regex) + self.assertRegex(stdout, re.compile(r'^sysroot\s*\(E\) = %s$' % self.test_prefix, re.M)) # specifying a non-existing path results in an error doesnotexist = os.path.join(self.test_prefix, 'non-existing-subdirectory') @@ -7175,14 +7114,12 @@ def test_config_repositorypath(self): ] txt, _ = self._run_mock_eb(args, do_build=True, raise_error=True, testing=False, strip=True) - regex = re.compile(r'repositorypath\s+\(C\) = %s' % repositorypath, re.M) - self.assertRegex(txt, regex) + self.assertRegex(txt, re.compile(r'repositorypath\s+\(C\) = %s' % repositorypath, re.M)) args[0] = '--repositorypath=%s,some/subdir' % repositorypath txt, _ = self._run_mock_eb(args, do_build=True, raise_error=True, testing=False, strip=True) - regex = re.compile(r"repositorypath\s+\(C\) = %s, some/subdir" % repositorypath, re.M) - self.assertRegex(txt, regex) + self.assertRegex(txt, r"repositorypath\s+\(C\) = %s, some/subdir" % repositorypath) # end-to-end testing of unknown filename def test_easystack_wrong_read(self): @@ -7315,11 +7252,9 @@ def test_easystack_easyconfigs_cache(self): stdout = self.eb_main(args, do_build=True, raise_error=True, redo_init_config=False) # check whether libtoy-0.0.eb comes from 2nd - regex = re.compile(r"^ \* \[ \] %s" % libtoy_ec, re.M) - self.assertRegex(stdout, regex) + self.assertRegex(stdout, re.compile(r"^ \* \[ \] %s" % libtoy_ec, re.M)) - regex = re.compile(r"^ \* \[ \] %s" % os.path.join(test_subdir, 'libtoy-0.0.eb'), re.M) - self.assertRegex(stdout, regex) + self.assertRegex(stdout, re.compile(r"^ \* \[ \] %s" % os.path.join(test_subdir, 'libtoy-0.0.eb'), re.M)) def test_set_up_configuration(self): """Tests for set_up_configuration function.""" diff --git a/test/framework/package.py b/test/framework/package.py index 1bb42f4bd6..c7ec1f4850 100644 --- a/test/framework/package.py +++ b/test/framework/package.py @@ -235,16 +235,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.MULTILINE)) toy_txt = read_file(os.path.join(test_easyconfigs, 't', 'toy', 'toy-0.0-gompi-2018a-test.eb')) replace_str = '''description = """Toy C program, 100% toy. Now with `backticks'\n''' @@ -253,8 +249,7 @@ 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) @@ -262,10 +257,8 @@ def test_package(self): 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, re.compile(r"""DESCRIPTION:.*\nand newlines""", re.MULTILINE)) self.mock_stdout(False) diff --git a/test/framework/parallelbuild.py b/test/framework/parallelbuild.py index cd1041beff..a0e17fcfe4 100644 --- a/test/framework/parallelbuild.py +++ b/test/framework/parallelbuild.py @@ -145,8 +145,7 @@ def test_build_easyconfigs_in_parallel_pbs_python(self): jobs = build_easyconfigs_in_parallel("echo '%(spec)s'", ordered_ecs, prepare_first=False) # only one job submitted since foss/2018a module is already available self.assertEqual(len(jobs), 1) - regex = re.compile("echo '.*/gzip-1.5-foss-2018a.eb'") - self.assertTrue(regex.search(jobs[-1].script), "Pattern '%s' found in: %s" % (regex.pattern, jobs[-1].script)) + self.assertRegex(jobs[-1].script, "echo '.*/gzip-1.5-foss-2018a.eb'") ec_file = os.path.join(topdir, 'easyconfigs', 'test_ecs', 'g', 'gzip', 'gzip-1.4-GCC-4.6.3.eb') ordered_ecs = resolve_dependencies(process_easyconfig(ec_file), self.modtool, retain_all_deps=True) @@ -155,10 +154,9 @@ def test_build_easyconfigs_in_parallel_pbs_python(self): # make sure command is correct, and that --hidden is there when it needs to be for i, ec in enumerate(ordered_ecs): if ec['hidden']: - regex = re.compile("eb %s.* --hidden" % ec['spec']) + self.assertRegex(jobs[i].script, "eb %s.* --hidden" % ec['spec']) else: - regex = re.compile("eb %s" % ec['spec']) - self.assertTrue(regex.search(jobs[i].script), "Pattern '%s' found in: %s" % (regex.pattern, jobs[i].script)) + self.assertRegex(jobs[i].script, "eb %s" % ec['spec']) for job in jobs: self.assertEqual(job.cores, build_options['job_cores']) @@ -175,10 +173,8 @@ def test_build_easyconfigs_in_parallel_pbs_python(self): # dependencies for gzip/1.4-GCC-4.6.3: GCC/4.6.3 (toolchain) + toy/.0.0-deps self.assertIn('gzip-1.4-GCC-4.6.3.eb', jobs[3].script) self.assertEqual(len(jobs[3].deps), 2) - regex = re.compile(r'toy-0.0-deps\.eb.* --hidden') script_txt = jobs[3].deps[0].script - fail_msg = "Pattern '%s' should be found in: %s" % (regex.pattern, script_txt) - self.assertTrue(regex.search(script_txt), fail_msg) + self.assertRegex(script_txt, r'toy-0.0-deps\.eb.* --hidden') self.assertIn('GCC-4.6.3.eb', jobs[3].deps[1].script) # also test use of --pre-create-installdir @@ -319,21 +315,17 @@ def test_submit_jobs(self): r' --testoutput=%\(output_dir\)s', r' --disable-job ', ] - for regex in regexs: - regex = re.compile(regex) - self.assertTrue(regex.search(cmd), "Pattern '%s' found in: %s" % (regex.pattern, cmd)) + self.assert_multi_regex(regexs, cmd) # these patterns should NOT be found, these options get filtered out # (self.test_prefix was argument to --robot) - for regex in ['--job', '--job-cores', '--try-toolchain', '--robot=[ =]', self.test_prefix + ' ']: - regex = re.compile(regex) - self.assertFalse(regex.search(cmd), "Pattern '%s' should *not* be found in: %s" % (regex.pattern, cmd)) + self.assert_multi_regex(['--job', '--job-cores', '--try-toolchain', '--robot=[ =]', self.test_prefix + ' '], + cmd, assert_true=False) # test again with custom EasyBuild command to use in jobs update_build_option('job_eb_cmd', "/just/testing/bin/eb --debug") cmd = submit_jobs(toy_ec, eb_go.generate_cmd_line(), testing=True) - regex = re.compile(r" && /just/testing/bin/eb --debug %\(spec\)s ") - self.assertTrue(regex.search(cmd), "Pattern '%s' found in: %s" % (regex.pattern, cmd)) + self.assertRegex(cmd, r" && /just/testing/bin/eb --debug %\(spec\)s ") def test_build_easyconfigs_in_parallel_slurm(self): """Test build_easyconfigs_in_parallel(), using (mocked) Slurm as backend for --job.""" diff --git a/test/framework/repository.py b/test/framework/repository.py index a5e992235d..7b16341826 100644 --- a/test/framework/repository.py +++ b/test/framework/repository.py @@ -28,7 +28,6 @@ @author: Toon Willems (Ghent University) """ import os -import re import shutil import sys import tempfile @@ -88,7 +87,7 @@ def test_gitrepo(self): shutil.rmtree(repo.wc) except EasyBuildError as err: print("ignoring failed subtest in test_gitrepo, testing offline?") - self.assertTrue(re.search("pull in working copy .* went wrong", str(err))) + self.assertRegex(str(err, "pull in working copy .* went wrong")) # filepath tmpdir = tempfile.mkdtemp() @@ -106,9 +105,8 @@ def test_gitrepo(self): 'GIT_COMMITTER_NAME': 'test', 'GIT_COMMITTER_EMAIL': 'test@test.org'}): repo.commit("toy/0.0") - log_regex = re.compile(r"toy/0.0 with EasyBuild v%s @ .* \(time: .*, user: .*\)" % VERSION, re.M) logmsg = repo.client.log('HEAD^!') - self.assertTrue(log_regex.search(logmsg), "Pattern '%s' found in %s" % (log_regex.pattern, logmsg)) + self.assertRegex(logmsg, r"toy/0.0 with EasyBuild v%s @ .* \(time: .*, user: .*\)" % VERSION) shutil.rmtree(repo.wc) shutil.rmtree(tmpdir) diff --git a/test/framework/robot.py b/test/framework/robot.py index 9f3815a468..4109d42dd9 100644 --- a/test/framework/robot.py +++ b/test/framework/robot.py @@ -515,9 +515,9 @@ def test_resolve_dependencies_minimal(self): }) impi_txt = read_file(os.path.join(test_easyconfigs, 'i', 'impi', 'impi-5.1.2.150.eb')) - self.assertTrue(re.search("^toolchain = SYSTEM", impi_txt, re.M)) + self.assertRegex(impi_txt, re.compile("^toolchain = SYSTEM", re.M)) gzip_txt = read_file(os.path.join(test_easyconfigs, 'g', 'gzip', 'gzip-1.4.eb')) - self.assertTrue(re.search("^toolchain = SYSTEM", gzip_txt, re.M)) + self.assertRegex(gzip_txt, re.compile("^toolchain = SYSTEM", re.M)) barec = os.path.join(self.test_prefix, 'bar-1.2.3-foss-2018a.eb') barec_lines = [ @@ -650,8 +650,8 @@ def test_det_easyconfig_paths(self): ] for path_prefix, module in modules: ec_fn = "%s.eb" % '-'.join(module.split('/')) - regex = re.compile(r"^ \* \[.\] %s.*%s \(module: %s\)$" % (path_prefix, ec_fn, module), re.M) - self.assertTrue(regex.search(outtxt), "Found pattern %s in %s" % (regex.pattern, outtxt)) + self.assertRegex(outtxt, + re.compile(r"^ \* \[.\] %s.*%s \(module: %s\)$" % (path_prefix, ec_fn, module), re.M)) # test using archived easyconfigs args = [ @@ -666,8 +666,8 @@ def test_det_easyconfig_paths(self): args.append('--consider-archived-easyconfigs') outtxt = self.eb_main(args, logfile=dummylogfn, raise_error=True) - regex = re.compile(r"^ \* \[.\] .*/__archive__/.*/intel-2012a.eb \(module: intel/2012a\)", re.M) - self.assertTrue(regex.search(outtxt), "Found pattern %s in %s" % (regex.pattern, outtxt)) + self.assertRegex(outtxt, + re.compile(r"^ \* \[.\] .*/__archive__/.*/intel-2012a.eb \(module: intel/2012a\)", re.M)) args = [ os.path.join(test_ecs_path, 't', 'toy', 'toy-0.0.eb'), @@ -680,11 +680,10 @@ def test_det_easyconfig_paths(self): ] outtxt = self.eb_main(args, raise_error=True) - regex = re.compile(r"^ \* \[.\] .*/toy-0.0-gompi-2018a.eb \(module: toy/0.0-gompi-2018a\)", re.M) - self.assertTrue(regex.search(outtxt), "Found pattern %s in %s" % (regex.pattern, outtxt)) + self.assertRegex(outtxt, + re.compile(r"^ \* \[.\] .*/toy-0.0-gompi-2018a.eb \(module: toy/0.0-gompi-2018a\)", re.M)) for ec in ('toy-0.0.eb', 'toy-0.0-gompi-2018a-test.eb'): - regex = re.compile(r"^ \* \[.\] .*/%s \(module:" % ec, re.M) - self.assertFalse(regex.search(outtxt), "%s should be fitered in %s" % (ec, outtxt)) + self.assertNotRegex(outtxt, re.compile(r"^ \* \[.\] .*/%s \(module:" % ec, re.M)) def test_search_paths(self): """Test search_paths command line argument.""" @@ -709,8 +708,7 @@ def test_search_paths(self): self.mock_stdout(False) # Make sure we found the copied file - regex = re.compile(r"^ \* %s$" % os.path.join(self.test_prefix, test_ec), re.M) - self.assertTrue(regex.search(outtxt), "Found pattern %s in %s" % (regex.pattern, outtxt)) + self.assertRegex(outtxt, re.compile(r"^ \* %s$" % os.path.join(self.test_prefix, test_ec), re.M)) def test_github_det_easyconfig_paths_from_commit(self): """Test det_easyconfig_paths function in combination with --from-commit.""" @@ -750,8 +748,8 @@ def test_github_det_easyconfig_paths_from_commit(self): ] for path_prefix, module in modules: ec_fn = "%s.eb" % '-'.join(module.split('/')) - regex = re.compile(r"^ \* \[.\] %s.*%s \(module: %s\)$" % (path_prefix, ec_fn, module), re.M) - self.assertTrue(regex.search(outtxt), "Found pattern %s in %s" % (regex.pattern, outtxt)) + self.assertRegex(outtxt, + re.compile(r"^ \* \[.\] %s.*%s \(module: %s\)$" % (path_prefix, ec_fn, module), re.M)) def test_github_det_easyconfig_paths_from_pr(self): """Test det_easyconfig_paths function, with --from-pr enabled as well.""" @@ -815,8 +813,8 @@ def test_github_det_easyconfig_paths_from_pr(self): ] for path_prefix, module in modules: ec_fn = "%s.eb" % '-'.join(module.split('/')) - regex = re.compile(r"^ \* \[.\] %s.*%s \(module: %s\)$" % (path_prefix, ec_fn, module), re.M) - self.assertTrue(regex.search(outtxt), "Found pattern %s in %s" % (regex.pattern, outtxt)) + self.assertRegex(outtxt, + re.compile(r"^ \* \[.\] %s.*%s \(module: %s\)$" % (path_prefix, ec_fn, module), re.M)) def test_get_toolchain_hierarchy(self): """Test get_toolchain_hierarchy function.""" @@ -1645,8 +1643,7 @@ def test_search_easyconfigs(self): path = os.path.join('test', 'framework', 'easyconfigs', 'test_ecs', 'b', 'binutils', ec_fn) pattern.append(r"^ \* .*%s$" % path) - regex = re.compile('\n'.join(pattern), re.M) - self.assertTrue(regex.search(stdout), "Pattern '%s' should be found in: %s" % (regex.pattern, stdout)) + self.assertRegex(stdout, re.compile('\n'.join(pattern), re.M)) def suite(loader=None): diff --git a/test/framework/run.py b/test/framework/run.py index 5536570a77..f8a4caf132 100644 --- a/test/framework/run.py +++ b/test/framework/run.py @@ -266,20 +266,22 @@ def test_run_shell_cmd_basic(self): cmd_script = os.path.join(cmd_tmpdir, 'cmd.sh') self.assertExists(cmd_script) - cmd = f"{cmd_script} -c 'echo pwd: $PWD; echo $FOOBAR; echo $EB_CMD_OUT_FILE; cat $EB_CMD_OUT_FILE'" + cmd = f"{cmd_script} -c '" + cmd += 'echo pwd: $PWD; echo "bash_env: $BASH_ENV"; echo $FOOBAR; echo $EB_CMD_OUT_FILE; cat $EB_CMD_OUT_FILE' + cmd += "'" with self.mocked_stdout_stderr(): res = run_shell_cmd(cmd, fail_on_error=False) self.assertEqual(res.exit_code, 0) - regex = re.compile("pwd: .*\nfoobar\n.*/echo-.*/out.txt\nhello$") - self.assertTrue(regex.search(res.output), f"Pattern '{regex.pattern}' should be found in {res.output}") + self.assertRegex(res.output, "pwd: .*\nbash_env: .*\nfoobar\n.*/echo-.*/out.txt\nhello$") # check whether working directory is what's expected - regex = re.compile('^pwd: .*', re.M) - res = regex.findall(res.output) - self.assertEqual(len(res), 1) - pwd = res[0].strip()[5:] + matches = re.findall('^pwd: (.*)', res.output, re.M) + self.assertEqual(len(matches), 1) + pwd = matches[0] self.assertTrue(os.path.samefile(pwd, self.test_prefix)) + # BASH_ENV is that of the current shell, not the one set in the cmd.sh + self.assertEqual(re.search('^bash_env: (.*)', res.output, re.M)[1], os.environ.get('BASH_ENV', '')) cmd = f"{cmd_script} -c 'module --version'" with self.mocked_stdout_stderr(): res = run_shell_cmd(cmd, fail_on_error=False) @@ -292,7 +294,7 @@ def test_run_shell_cmd_basic(self): else: self.fail("Unknown modules tool used!") - self.assertTrue(regex.search(res.output), f"Pattern '{regex.pattern}' should be found in {res.output}") + self.assertRegex(res.output, regex) # test running command that emits non-UTF-8 characters # this is constructed to reproduce errors like: @@ -386,9 +388,9 @@ def test_run_shell_cmd_env(self): self.assertEqual(res.cmd, cmd) self.assertEqual(res.exit_code, 0) self.assertIn("FOOBAR=foobar\n", res.output) - self.assertTrue(re.search("^_=.*/env$", res.output, re.M)) + self.assertRegex(res.output, re.compile("^_=.*/env$", re.M)) for var in ('HOME', 'USER'): - self.assertFalse(re.search('^' + var + '=.*', res.output, re.M)) + self.assertNotRegex(res.output, re.compile('^' + var + '=.*', re.M)) # check on helper scripts that were generated for this command paths = glob.glob(os.path.join(self.test_prefix, 'eb-*', 'run-shell-cmd-output', 'env-*')) @@ -493,9 +495,9 @@ def test_run_shell_cmd_log(self): fd, logfile = tempfile.mkstemp(suffix='.log', prefix='eb-test-') os.close(fd) - regex_start_cmd = re.compile(r"Running 'echo ...' shell command in .*:\n\techo hello", re.M) + regex_start_cmd = re.compile(r"Running 'echo ...' shell command in .*:\n\techo hello") regex_cmd_exit = re.compile(r"'echo ...' shell command completed successfully") - regex_cmd_output = re.compile(r"Output of 'echo \.\.\.' shell command \(stdout \+ stderr\):\nhello", re.M) + regex_cmd_output = re.compile(r"Output of 'echo \.\.\.' shell command \(stdout \+ stderr\):\nhello") # command output is logged init_logging(logfile, silent=True) @@ -632,9 +634,7 @@ def handler(signum, _): r"\s+output \(stdout \+ stderr\)\s* -> (.|\n)*/run-shell-cmd-output/kill-(.|\n)*/out.txt", r"\s+interactive shell script\s* -> (.|\n)*/run-shell-cmd-output/kill-(.|\n)*/cmd.sh", ] - for pattern in patterns: - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(stderr), "Pattern '%s' should be found in: %s" % (pattern, stderr)) + self.assert_multi_regex(patterns, stderr) # check error reporting output when stdout/stderr are collected separately try: @@ -670,9 +670,7 @@ def handler(signum, _): r"\s+error/warnings \(stderr\)\s+ -> (.|\n)*/run-shell-cmd-output/kill-(.|\n)*/err.txt", r"\s+interactive shell script\s* -> (.|\n)*/run-shell-cmd-output/kill-(.|\n)*/cmd.sh", ] - for pattern in patterns: - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(stderr), "Pattern '%s' should be found in: %s" % (pattern, stderr)) + self.assert_multi_regex(patterns, stderr) # no error reporting when fail_on_error is disabled with self.mocked_stdout_stderr() as (_, stderr): @@ -848,8 +846,7 @@ def test_run_shell_cmd_split_stderr(self): with self.mocked_stdout_stderr(): res = run_shell_cmd(cmd, fail_on_error=False) - regex = re.compile(".*/echo-.*/out.txt\nok\n.*/echo-.*/err.txt\nwarning$") - self.assertTrue(regex.search(res.output), f"Pattern '{regex.pattern}' should be found in {res.output}") + self.assertRegex(res.output, ".*/echo-.*/out.txt\nok\n.*/echo-.*/err.txt\nwarning$") def test_run_cmd_trace(self): """Test run_cmd in trace mode, and with tracing disabled.""" @@ -879,8 +876,7 @@ def test_run_cmd_trace(self): self.assertEqual(out, 'hello\n') self.assertEqual(ec, 0) self.assertTrue(stderr.strip().startswith("WARNING: Deprecated functionality")) - regex = re.compile('\n'.join(pattern)) - self.assertTrue(regex.search(stdout), "Pattern '%s' found in: %s" % (regex.pattern, stdout)) + self.assertRegex(stdout, '\n'.join(pattern)) update_build_option('trace', False) self.mock_stdout(True) @@ -909,8 +905,7 @@ def test_run_cmd_trace(self): self.assertTrue(stderr.strip().startswith("WARNING: Deprecated functionality")) pattern.insert(3, r"\t\[input: hello\]") pattern[-2] = "\tcat" - regex = re.compile('\n'.join(pattern)) - self.assertTrue(regex.search(stdout), "Pattern '%s' found in: %s" % (regex.pattern, stdout)) + self.assertRegex(stdout, '\n'.join(pattern)) update_build_option('trace', False) self.mock_stdout(True) @@ -965,8 +960,7 @@ def test_run_shell_cmd_trace(self): self.assertEqual(res.output, 'hello\n') self.assertEqual(res.exit_code, 0) self.assertEqual(stderr, '') - regex = re.compile('\n'.join(pattern)) - self.assertTrue(regex.search(stdout), "Pattern '%s' found in: %s" % (regex.pattern, stdout)) + self.assertRegex(stdout, '\n'.join(pattern)) init_config(build_options={'trace': False}) self.mock_stdout(True) @@ -1021,8 +1015,7 @@ def test_run_shell_cmd_trace_stdin(self): self.assertEqual(res.output, 'hello\n') self.assertEqual(res.exit_code, 0) self.assertEqual(stderr, '') - regex = re.compile('\n'.join(pattern)) - self.assertTrue(regex.search(stdout), "Pattern '%s' found in: %s" % (regex.pattern, stdout)) + self.assertRegex(stdout, '\n'.join(pattern)) # also test with command that is fed input via stdin self.mock_stdout(True) @@ -1037,8 +1030,7 @@ def test_run_shell_cmd_trace_stdin(self): self.assertEqual(stderr, '') pattern.insert(4, r"\t\[input: hello\]") pattern[1] = "\tcat" - regex = re.compile('\n'.join(pattern)) - self.assertTrue(regex.search(stdout), "Pattern '%s' found in: %s" % (regex.pattern, stdout)) + self.assertRegex(stdout, '\n'.join(pattern)) # trace output can be disabled on a per-command basis by enabling 'hidden' self.mock_stdout(True) @@ -1253,8 +1245,7 @@ def test_run_cmd_qa_buffering(self): (out, ec) = run_cmd_qa(cmd, {'Pick a number: ': '42'}, log_all=True, maxhits=5) self.assertEqual(ec, 0) - regex = re.compile("Picked number: 42$") - self.assertTrue(regex.search(out), "Pattern '%s' found in: %s" % (regex.pattern, out)) + self.assertRegex(out, "Picked number: 42$") # also test with script run as interactive command that quickly exits with non-zero exit code; # see https://github.com/easybuilders/easybuild-framework/issues/3593 @@ -1285,8 +1276,7 @@ def test_run_shell_cmd_qa_buffering(self): res = run_shell_cmd(cmd, qa_patterns=[('Pick a number: ', '42')], qa_timeout=10) self.assertEqual(res.exit_code, 0) - regex = re.compile("Picked number: 42$") - self.assertTrue(regex.search(res.output), f"Pattern '{regex.pattern}' found in: {res.output}") + self.assertRegex(res.output, "Picked number: 42$") # also test with script run as interactive command that quickly exits with non-zero exit code; # see https://github.com/easybuilders/easybuild-framework/issues/3593 @@ -1357,7 +1347,7 @@ def test_run_cmd_qa_trace(self): pattern += r"\t\[output logged in .*\]\n" pattern += r"\techo \'n: \'; read n; seq 1 \$n\n" pattern += r' >> interactive command completed: exit 0, ran in .*' - self.assertTrue(re.search(pattern, stdout), "Pattern '%s' found in: %s" % (pattern, stdout)) + self.assertRegex(stdout, pattern) # trace output can be disabled on a per-command basis self.mock_stdout(True) @@ -1388,7 +1378,7 @@ def test_run_shell_cmd_qa_trace(self): pattern += r"\t\[working dir: .*\]\n" pattern += r"\t\[output and state saved to .*\]\n" pattern += r' >> command completed: exit 0, ran in .*' - self.assertTrue(re.search(pattern, stdout), "Pattern '%s' found in: %s" % (pattern, stdout)) + self.assertRegex(stdout, pattern) # trace output can be disabled on a per-command basis self.mock_stdout(True) @@ -2213,8 +2203,7 @@ def post_run_shell_cmd_hook(cmd, *args, **kwargs): run_shell_cmd("make") stdout = self.get_stdout() - regex = re.compile('>> running shell command:\n\techo make', re.M) - self.assertTrue(regex.search(stdout), "Pattern '%s' found in: %s" % (regex.pattern, stdout)) + self.assertRegex(stdout, re.compile('>> running shell command:\n\techo make', re.M)) with self.mocked_stdout_stderr(): # run_shell_cmd will raise RunShellCmdError which we don't care about here, diff --git a/test/framework/systemtools.py b/test/framework/systemtools.py index be742e95b0..fc7bf50837 100644 --- a/test/framework/systemtools.py +++ b/test/framework/systemtools.py @@ -1020,14 +1020,14 @@ def test_platform_name_native(self): def test_platform_name_linux(self): """Test getting platform name (mocked for Linux).""" st.get_os_type = lambda: st.LINUX - self.assertTrue(re.match('.*-unknown-linux$', get_platform_name())) - self.assertTrue(re.match('.*-unknown-linux-gnu$', get_platform_name(withversion=True))) + self.assertRegex(get_platform_name(), '.*-unknown-linux$') + self.assertRegex(get_platform_name(withversion=True), '.*-unknown-linux-gnu$') def test_platform_name_darwin(self): """Test getting platform name (mocked for Darwin).""" st.get_os_type = lambda: st.DARWIN - self.assertTrue(re.match('.*-apple-darwin$', get_platform_name())) - self.assertTrue(re.match('.*-apple-darwin.*$', get_platform_name(withversion=True))) + self.assertRegex(get_platform_name(), '.*-apple-darwin$') + self.assertRegex(get_platform_name(withversion=True), '.*-apple-darwin.*$') def test_os_name(self): """Test getting OS name.""" @@ -1249,8 +1249,7 @@ def test_det_pypkg_version(self): self.assertIsNone(det_pypkg_version('doesnotexist', 'doesnotexist.foo')) rich_ver = det_pypkg_version('rich', 'rich') - regex = re.compile(r'^[0-9]+\.[0-9].*') - self.assertTrue(regex.match(rich_ver), f"Pattern {regex.pattern} should match for: {rich_ver}") + self.assertRegex(rich_ver, r'^[0-9]+\.[0-9].*') def test_pick_system_specific_value(self): """Test pick_system_specific_value function.""" @@ -1401,8 +1400,6 @@ def test_check_linked_shared_libs(self): test_file = os.path.join(self.test_prefix, 'test.txt') write_file(test_file, 'test') - warning_regex = re.compile(r"WARNING: Determining linked libraries.* via 'ldd .*/test.txt' failed!", re.M) - self.mock_stderr(True) self.mock_stdout(True) res = check_linked_shared_libs(test_file, banned_patterns=['/lib']) @@ -1411,8 +1408,7 @@ def test_check_linked_shared_libs(self): self.mock_stderr(False) self.mock_stdout(False) - fail_msg = "Pattern '%s' should be found in: %s" % (warning_regex.pattern, stderr) - self.assertTrue(warning_regex.search(stderr), fail_msg) + self.assertRegex(stderr, r"WARNING: Determining linked libraries.* via 'ldd .*/test.txt' failed!") self.assertFalse(stdout) self.assertEqual(res, None) @@ -1484,8 +1480,7 @@ def test_get_cuda_object_dump_raw(self): st._log.setLevel(old_log_level) logtxt = read_file(self.logfile) self.assertIsNone(res) - fail_msg = "Pattern '%s' should be found in: %s" % (debug_regex.pattern, logtxt) - self.assertTrue(debug_regex.search(logtxt), fail_msg) + self.assertRegex(logtxt, debug_regex) # Test case 5: call on a file where cuobjdump produces really unexpected output error_pattern = r"Dumping CUDA binary file information for .* via .* failed!" @@ -1539,8 +1534,7 @@ def test_get_cuda_architectures(self): st._log.setLevel(old_log_level) logtxt = read_file(self.logfile) self.assertIsNone(res_elf) - fail_msg = "Pattern '%s' should be found in: %s" % (warning_regex_elf.pattern, logtxt) - self.assertTrue(warning_regex_elf.search(logtxt), fail_msg) + self.assertRegex(logtxt, warning_regex_elf) self.assertEqual(res_ptx, ['9.0', '9.0a']) # Test case 5: call on CUDA static lib, which only contains device code @@ -1554,8 +1548,7 @@ def test_get_cuda_architectures(self): st._log.setLevel(old_log_level) logtxt = read_file(self.logfile) self.assertIsNone(res_ptx) - fail_msg = "Pattern '%s' should be found in: %s" % (warning_regex_ptx.pattern, logtxt) - self.assertTrue(warning_regex_ptx.search(logtxt), fail_msg) + self.assertRegex(logtxt, warning_regex_ptx) self.assertEqual(res_elf, ['10.0', '10.0a', '10.0f']) # Test case 6: call on CUDA shared lib which lacks an arch = sm_XX entry (should never happen) @@ -1567,8 +1560,7 @@ def test_get_cuda_architectures(self): res_elf = get_cuda_architectures('mock_invalid_cuda_sharedlib', 'elf') st._log.setLevel(old_log_level) logtxt = read_file(self.logfile) - fail_msg = "Pattern '%s' should be found in: %s" % (warning_regex_elf.pattern, logtxt) - self.assertTrue(warning_regex_elf.search(logtxt), fail_msg) + self.assertRegex(logtxt, warning_regex_elf) self.assertIsNone(res_elf) def test_get_linked_libs_raw(self): diff --git a/test/framework/toolchain.py b/test/framework/toolchain.py index 16255d1826..64558b7fbe 100644 --- a/test/framework/toolchain.py +++ b/test/framework/toolchain.py @@ -389,10 +389,10 @@ def test_toolchain_compiler_env_vars(self): self.assertEqual(os.getenv('OMPI_F77'), 'gfortran') self.assertEqual(os.getenv('OMPI_FC'), 'gfortran') - flags_regex = re.compile(r"-O2 -ftree-vectorize -m(arch|cpu)=native -fno-math-errno") + flags_regex = re.compile(r"^-O2 -ftree-vectorize -m(arch|cpu)=native -fno-math-errno$") for key in ['CFLAGS', 'CXXFLAGS', 'F90FLAGS', 'FCFLAGS', 'FFLAGS']: val = os.getenv(key) - self.assertTrue(flags_regex.match(val), "'%s' should match pattern '%s'" % (val, flags_regex.pattern)) + self.assertRegex(val, flags_regex) def test_get_variable_compilers(self): """Test get_variable function to obtain compiler variables.""" @@ -891,8 +891,7 @@ def test_easyconfig_optarch_flags(self): write_file(test_ec, toy_txt + "\ntoolchainopts = {'optarch': '-march=sandybridge'}") with self.mocked_stdout_stderr(): out = self.eb_main([test_ec], raise_error=True, do_build=True) - regex = re.compile("_set_optimal_architecture: using -march=sandybridge as optarch for x86_64") - self.assertTrue(regex.search(out), "Pattern '%s' found in: %s" % (regex.pattern, out)) + self.assertIn("_set_optimal_architecture: using -march=sandybridge as optarch for x86_64", out) def test_misc_flags_unique_fortran(self): """Test whether unique Fortran compiler flags are set correctly.""" @@ -927,10 +926,10 @@ def test_precision_flags(self): tc.set_options({}) with self.mocked_stdout_stderr(): tc.prepare() - flags_regex = re.compile(r"-O2 -ftree-vectorize -m(arch|cpu)=native -fno-math-errno") + flags_regex = re.compile(r"^-O2 -ftree-vectorize -m(arch|cpu)=native -fno-math-errno$") for var in flag_vars: val = os.getenv(var) - self.assertTrue(flags_regex.match(val), "'%s' should match pattern '%s'" % (val, flags_regex.pattern)) + self.assertRegex(val, flags_regex) # check other precision flags precs = ['strict', 'precise', 'loose', 'veryloose'] @@ -938,7 +937,7 @@ def test_precision_flags(self): for prec in precs: prec_flags[prec] = ' '.join(Gcc.COMPILER_UNIQUE_OPTION_MAP[prec]) - for prec in prec_flags: + for prec, flags in prec_flags.items(): for enable in [True, False]: tc = self.get_toolchain('foss', version='2018a') tc.set_options({prec: enable}) @@ -946,11 +945,11 @@ def test_precision_flags(self): tc.prepare() for var in flag_vars: if enable: - regex = re.compile(r"-O2 -ftree-vectorize -m(arch|cpu)=native %s" % prec_flags[prec]) + regex = re.compile(r"^-O2 -ftree-vectorize -m(arch|cpu)=native %s$" % flags) else: regex = flags_regex val = os.getenv(var) - self.assertTrue(regex.match(val), "%s: '%s' should match pattern '%s'" % (prec, val, regex.pattern)) + self.assertRegex(val, regex) self.modtool.purge() @@ -1394,9 +1393,9 @@ def test_fosscuda(self): self.assertEqual(tc.get_variable('CUDA_CXX'), 'nvcc -ccbin="g++"') # -L/path flags will not be there if the software installations are not available val = tc.get_variable('CUDA_CFLAGS') - self.assertTrue(re.compile(nvcc_flags).match(val), "'%s' matches '%s'" % (val, nvcc_flags)) + self.assertRegex(val, f'^{nvcc_flags}$') val = tc.get_variable('CUDA_CXXFLAGS') - self.assertTrue(re.compile(nvcc_flags).match(val), "'%s' matches '%s'" % (val, nvcc_flags)) + self.assertRegex(val, f'^{nvcc_flags}$') # check compiler prefixes self.assertEqual(tc.comp_family(prefix='CUDA'), "CUDA") @@ -1865,8 +1864,7 @@ def test_mpi_cmd_for(self): with self.mocked_stdout_stderr(): tc.prepare() - mpi_cmd_for_re = re.compile("^mpirun --file=.*/mpdboot -machinefile .*/nodes -np 4 test$") - self.assertTrue(mpi_cmd_for_re.match(tc.mpi_cmd_for('test', 4))) + self.assertRegex(tc.mpi_cmd_for('test', 4), "^mpirun --file=.*/mpdboot -machinefile .*/nodes -np 4 test$") # test specifying custom template for MPI commands init_config(build_options={'mpi_cmd_template': "mpiexec -np %(nr_ranks)s -- %(cmd)s", 'silent': True}) @@ -1908,13 +1906,11 @@ def test_get_mpi_cmd_template(self): self.assertEqual(params['nr_ranks'], 123) mpdbf = params['mpdbf'] - regex = re.compile('^--file=.*/mpdboot$') - self.assertTrue(regex.match(mpdbf), "'%s' should match pattern '%s'" % (mpdbf, regex.pattern)) + self.assertRegex(mpdbf, '^--file=.*/mpdboot$') self.assertExists(mpdbf.split('=')[1]) nodesfile = params['nodesfile'] - regex = re.compile('^-machinefile /.*/nodes$') - self.assertTrue(regex.match(nodesfile), "'%s' should match pattern '%s'" % (nodesfile, regex.pattern)) + self.assertRegex(nodesfile, '^-machinefile /.*/nodes$') self.assertExists(nodesfile.split(' ')[1]) def test_prepare_deps(self): @@ -2491,9 +2487,7 @@ def test_compiler_cache(self): "This is a ccache wrapper", "Command ccache found at .*%s" % os.path.dirname(path), ] - for pattern in patterns: - regex = re.compile(pattern) - self.assertTrue(regex.search(out), "Pattern '%s' found in: %s" % (regex.pattern, out)) + self.assert_multi_regex(patterns, out) # $CCACHE_DIR is defined by toolchain.prepare(), and should still be defined after running 'eb' ccache_path = os.path.join(self.test_prefix, 'scripts', 'ccache') @@ -2518,9 +2512,7 @@ def test_compiler_cache(self): with self.mocked_stdout_stderr(): out = self.eb_main(args, raise_error=True, do_build=True, reset_env=False) - for pattern in patterns: - regex = re.compile(pattern) - self.assertTrue(regex.search(out), "Pattern '%s' found in: %s" % (regex.pattern, out)) + self.assert_multi_regex(patterns, out) self.assertTrue(os.path.samefile(os.environ['CCACHE_DIR'], ccache_dir)) self.assertTrue(os.path.samefile(os.environ['F90CACHE_DIR'], f90cache_dir)) @@ -3345,8 +3337,8 @@ def prep(): # warning is printed and $TMPDIR is set to shorter path if existing $TMPDIR is too long os.environ['TMPDIR'] = long_tmpdir tc, stdout, stderr = prep() - regex = re.compile(r"^WARNING: Long \$TMPDIR .* problems with OpenMPI 2.x, using shorter path: /tmp/.{8}$") - self.assertTrue(regex.match(stderr), "Pattern '%s' found in: %s" % (regex.pattern, stderr)) + self.assertRegex(stderr, + r"WARNING: Long \$TMPDIR .* problems with OpenMPI 2.x, using shorter path: /tmp/.{8}$") # new $TMPDIR should be /tmp/xxxxxx tmpdir = os.environ.get('TMPDIR') @@ -3454,7 +3446,7 @@ def test_nvhpc_compatibility(self): self.assertNotIsInstance(tc, NVHPC) # check new NVHPC toolchain with nvidia-compilers dependency - from easybuild.toolchains.nvhpc import NVHPC as NVHPC + from easybuild.toolchains.nvhpc import NVHPC tc = NVHPC(version='25.1', tcdeps=[{'name': 'nvidia-compilers', 'version': '25.1'}]) self.assertIsInstance(tc, NVHPC) self.assertNotIsInstance(tc, NVHPCToolchain) diff --git a/test/framework/toy_build.py b/test/framework/toy_build.py index 2df3c03357..97faf3e11b 100644 --- a/test/framework/toy_build.py +++ b/test/framework/toy_build.py @@ -127,8 +127,7 @@ def check_toy(self, installpath, outtxt, name='toy', version='0.0', versionprefi error_msg = '' # check for success - success = re.compile(r"COMPLETED: Installation (ended|STOPPED) successfully \(took .* secs?\)") - self.assertTrue(success.search(outtxt), "COMPLETED message found in '%s'%s" % (outtxt, error_msg)) + self.assertRegex(outtxt, r"COMPLETED: Installation (ended|STOPPED) successfully \(took .* secs?\)") # if the module exists, it should be fine toy_module = os.path.join(installpath, 'modules', 'all', name, full_version) @@ -228,10 +227,7 @@ def _test_toy_build(self, extra_args=None, ec_file=None, tmpdir=None, verify=Tru r"Environment", ]) test_report_txt = read_file(test_report) - for regex_pattern in regex_patterns: - regex = re.compile(regex_pattern, re.M) - msg = "Pattern %s found in full test report: %s" % (regex.pattern, test_report_txt) - self.assertTrue(regex.search(test_report_txt), msg) + self.assert_multi_regex(regex_patterns, test_report_txt) return outtxt @@ -355,10 +351,8 @@ def test_toy_broken_copy_log_build_dir(self): output_regexs = [r"^\s*toy\.c:5:44: error: expected (;|.;.)"] log_txt = read_file(log_file) - for regex_pattern in output_regexs: - regex = re.compile(regex_pattern, re.M) - self.assertRegex(outtxt, regex) - self.assertRegex(log_txt, regex) + self.assert_multi_regex(output_regexs, outtxt) + self.assert_multi_regex(output_regexs, log_txt) def test_toy_tweaked(self): """Test toy build with tweaked easyconfig, for testing extra easyconfig parameters.""" @@ -417,30 +411,28 @@ def test_toy_tweaked(self): toy_module_txt = read_file(toy_module) if get_module_syntax() == 'Tcl': - self.assertTrue(re.search(r'^setenv\s*FOO\s*"bar"$', toy_module_txt, re.M)) - self.assertTrue(re.search(r'^prepend-path\s*SOMEPATH\s*\$root/foo/bar$', toy_module_txt, re.M)) - self.assertTrue(re.search(r'^prepend-path\s*SOMEPATH\s*\$root/baz$', toy_module_txt, re.M)) - self.assertTrue(re.search(r'^prepend-path\s*SOMEPATH\s*\$root$', toy_module_txt, re.M)) - self.assertTrue(re.search(r'^append-path\s*SOMEPATH_APPEND\s*\$root/qux/fred$', toy_module_txt, re.M)) - self.assertTrue(re.search(r'^append-path\s*SOMEPATH_APPEND\s*\$root/thud$', toy_module_txt, re.M)) - self.assertTrue(re.search(r'^append-path\s*SOMEPATH_APPEND\s*\$root$', toy_module_txt, re.M)) - mod_load_msg = r'module-info mode load.*\n\s*puts stderr\s*.*%s$' % modloadmsg_regex_tcl - self.assertTrue(re.search(mod_load_msg, toy_module_txt, re.M)) - self.assertTrue(re.search(r'^puts stderr "oh hai!"$', toy_module_txt, re.M)) + self.assert_multi_regex(( + r'^setenv\s*FOO\s*"bar"$', + r'^prepend-path\s*SOMEPATH\s*\$root/foo/bar$', + r'^prepend-path\s*SOMEPATH\s*\$root/baz$', + r'^prepend-path\s*SOMEPATH\s*\$root$', + r'^append-path\s*SOMEPATH_APPEND\s*\$root/qux/fred$', + r'^append-path\s*SOMEPATH_APPEND\s*\$root/thud$', + r'^append-path\s*SOMEPATH_APPEND\s*\$root$', + r'module-info mode load.*\n\s*puts stderr\s*.*%s$' % modloadmsg_regex_tcl, + r'^puts stderr "oh hai!"$', + ), toy_module_txt) elif get_module_syntax() == 'Lua': - self.assertTrue(re.search(r'^setenv\("FOO", "bar"\)', toy_module_txt, re.M)) - pattern = r'^prepend_path\("SOMEPATH", pathJoin\(root, "foo", "bar"\)\)$' - self.assertTrue(re.search(pattern, toy_module_txt, re.M)) - pattern = r'^append_path\("SOMEPATH_APPEND", pathJoin\(root, "qux", "fred"\)\)$' - self.assertTrue(re.search(pattern, toy_module_txt, re.M)) - pattern = r'^append_path\("SOMEPATH_APPEND", pathJoin\(root, "thud"\)\)$' - self.assertTrue(re.search(pattern, toy_module_txt, re.M)) - self.assertTrue(re.search(r'^append_path\("SOMEPATH_APPEND", root\)$', toy_module_txt, re.M)) - self.assertTrue(re.search(r'^prepend_path\("SOMEPATH", pathJoin\(root, "baz"\)\)$', toy_module_txt, re.M)) - self.assertTrue(re.search(r'^prepend_path\("SOMEPATH", root\)$', toy_module_txt, re.M)) - mod_load_msg = r'^if mode\(\) == "load" then\n\s*io.stderr:write\(%s\)$' % modloadmsg_regex_lua - regex = re.compile(mod_load_msg, re.M) - self.assertTrue(regex.search(toy_module_txt), "Pattern '%s' found in: %s" % (regex.pattern, toy_module_txt)) + self.assert_multi_regex(( + r'^setenv\("FOO", "bar"\)', + r'^prepend_path\("SOMEPATH", pathJoin\(root, "foo", "bar"\)\)$', + r'^append_path\("SOMEPATH_APPEND", pathJoin\(root, "qux", "fred"\)\)$', + r'^append_path\("SOMEPATH_APPEND", pathJoin\(root, "thud"\)\)$', + r'^append_path\("SOMEPATH_APPEND", root\)$', + r'^prepend_path\("SOMEPATH", pathJoin\(root, "baz"\)\)$', + r'^prepend_path\("SOMEPATH", root\)$', + r'^if mode\(\) == "load" then\n\s*io.stderr:write\(%s\)$' % modloadmsg_regex_lua, + ), toy_module_txt) else: self.fail("Unknown module syntax: %s" % get_module_syntax()) @@ -612,8 +604,7 @@ def test_toy_permissions(self): allargs = [test_ec] + args + ['--group=thisgroupdoesnotexist'] outtxt, err = self.run_eb_main_capture_output(allargs, logfile=self.dummylogfn, do_build=True, return_error=True) - err_regex = re.compile("Failed to get group ID .* group does not exist") - self.assertTrue(err_regex.search(outtxt), "Pattern '%s' found in '%s'" % (err_regex.pattern, outtxt)) + self.assertRegex(outtxt, "Failed to get group ID .* group does not exist") # determine current group name (at least we can use that) gid = os.getgid() @@ -861,12 +852,10 @@ def test_toy_group_check(self): r' error "%s[^"]*"' % error_msg_pattern, r'\}$', ]) - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(outtxt), "Pattern '%s' found in: %s" % (regex.pattern, toy_mod_txt)) + self.assertRegex(outtxt, re.compile(pattern, re.M)) else: pattern = "Can't generate robust check in Tcl modules for users belonging to group %s." % group_name - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(outtxt), "Pattern '%s' found in: %s" % (regex.pattern, outtxt)) + self.assertRegex(outtxt, re.compile(pattern, re.M)) elif get_module_syntax() == 'Lua': toy_mod = os.path.join(self.test_installpath, 'modules', 'all', 'toy', '0.0.lua') @@ -884,8 +873,7 @@ def test_toy_group_check(self): r' LmodError\("%s[^"]*"\)' % error_msg_pattern, r'end$', ]) - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(outtxt), "Pattern '%s' found in: %s" % (regex.pattern, toy_mod_txt)) + self.assertRegex(toy_mod_txt, re.compile(pattern, re.M)) else: self.fail("Unknown module syntax: %s" % get_module_syntax()) @@ -945,11 +933,9 @@ def test_toy_hierarchical(self): modtxt = read_file(toy_module_path) for dep in ['foss', 'GCC', 'OpenMPI']: - load_regex = re.compile(load_regex_template % dep) - self.assertFalse(load_regex.search(modtxt), "Pattern '%s' not found in %s" % (load_regex.pattern, modtxt)) + self.assertNotRegex(modtxt, load_regex_template % dep) for dep in ['OpenBLAS', 'FFTW', 'ScaLAPACK']: - load_regex = re.compile(load_regex_template % dep) - self.assertTrue(load_regex.search(modtxt), "Pattern '%s' found in %s" % (load_regex.pattern, modtxt)) + self.assertRegex(modtxt, load_regex_template % dep) os.remove(toy_module_path) @@ -968,7 +954,7 @@ def test_toy_hierarchical(self): # no dependencies or toolchain => no module load statements in module file modtxt = read_file(toy_module_path) - self.assertFalse(re.search("module load", modtxt)) + self.assertNotIn("module load", modtxt) os.remove(toy_module_path) # test module path with GCC/6.4.0-2.28 build, pretend to be an MPI lib by setting moduleclass extra_args = [ @@ -988,11 +974,10 @@ def test_toy_hierarchical(self): modtxt = read_file(toy_module_path) modpath_extension = os.path.join(mod_prefix, 'MPI', 'GCC', '6.4.0-2.28', 'toy', '0.0') if get_module_syntax() == 'Tcl': - self.assertTrue(re.search(r'^module\s*use\s*"%s"' % modpath_extension, modtxt, re.M)) + self.assertRegex(modtxt, re.compile(r'^module\s*use\s*"%s"' % modpath_extension, re.M)) elif get_module_syntax() == 'Lua': fullmodpath_extension = os.path.join(self.test_installpath, modpath_extension) - regex = re.compile(r'^prepend_path\("MODULEPATH", "%s"\)' % fullmodpath_extension, re.M) - self.assertTrue(regex.search(modtxt), "Pattern '%s' found in %s" % (regex.pattern, modtxt)) + self.assertRegex(modtxt, re.compile(r'^prepend_path\("MODULEPATH", "%s"\)' % fullmodpath_extension, re.M)) else: self.fail("Unknown module syntax: %s" % get_module_syntax()) os.remove(toy_module_path) @@ -1004,11 +989,11 @@ def test_toy_hierarchical(self): modtxt = read_file(toy_module_path) modpath_extension = os.path.join(mod_prefix, 'MPI', 'GCC', '6.4.0-2.28', 'toy', '0.0') if get_module_syntax() == 'Tcl': - self.assertFalse(re.search(r'^module\s*use\s*"%s"' % modpath_extension, modtxt, re.M)) + self.assertNotRegex(modtxt, re.compile(r'^module\s*use\s*"%s"' % modpath_extension, re.M)) elif get_module_syntax() == 'Lua': fullmodpath_extension = os.path.join(self.test_installpath, modpath_extension) - regex = re.compile(r'^prepend_path\("MODULEPATH", "%s"\)' % fullmodpath_extension, re.M) - self.assertFalse(regex.search(modtxt), "Pattern '%s' found in %s" % (regex.pattern, modtxt)) + self.assertNotRegex(modtxt, + re.compile(r'^prepend_path\("MODULEPATH", "%s"\)' % fullmodpath_extension, re.M)) else: self.fail("Unknown module syntax: %s" % get_module_syntax()) os.remove(toy_module_path) @@ -1028,7 +1013,7 @@ def test_toy_hierarchical(self): # no dependencies or toolchain => no module load statements in module file modtxt = read_file(toy_module_path) - self.assertFalse(re.search("module load", modtxt)) + self.assertNotIn("module load", modtxt) os.remove(toy_module_path) # test module path with system/system build, pretend to be a compiler by setting moduleclass @@ -1049,11 +1034,10 @@ def test_toy_hierarchical(self): modtxt = read_file(toy_module_path) modpath_extension = os.path.join(mod_prefix, 'Compiler', 'toy', '0.0') if get_module_syntax() == 'Tcl': - self.assertTrue(re.search(r'^module\s*use\s*"%s"' % modpath_extension, modtxt, re.M)) + self.assertRegex(modtxt, re.compile(r'^module\s*use\s*"%s"' % modpath_extension, re.M)) elif get_module_syntax() == 'Lua': fullmodpath_extension = os.path.join(self.test_installpath, modpath_extension) - regex = re.compile(r'^prepend_path\("MODULEPATH", "%s"\)' % fullmodpath_extension, re.M) - self.assertTrue(regex.search(modtxt), "Pattern '%s' found in %s" % (regex.pattern, modtxt)) + self.assertRegex(modtxt, re.compile(r'^prepend_path\("MODULEPATH", "%s"\)' % fullmodpath_extension, re.M)) else: self.fail("Unknown module syntax: %s" % get_module_syntax()) os.remove(toy_module_path) @@ -1128,12 +1112,10 @@ def test_toy_hierarchical_subdir_user_modules(self): # No math libs in original toolchain, --try-toolchain is too clever to upgrade it beyond necessary for modname in ['FFTW', 'OpenBLAS', 'ScaLAPACK']: - regex = re.compile('load.*' + modname, re.M) - self.assertFalse(regex.search(toy_modtxt), "Pattern '%s' not found in: %s" % (regex.pattern, toy_modtxt)) + self.assertNotRegex(toy_modtxt, re.compile('load.*' + modname, re.M)) for modname in ['GCC', 'OpenMPI']: - regex = re.compile('load.*' + modname, re.M) - self.assertFalse(regex.search(toy_modtxt), "Pattern '%s' not found in: %s" % (regex.pattern, toy_modtxt)) + self.assertNotRegex(toy_modtxt, re.compile('load.*' + modname, re.M)) # also check with Lua GCC/OpenMPI modules in case of Lmod if isinstance(self.modtool, Lmod): @@ -1179,15 +1161,8 @@ def test_toy_hierarchical_subdir_user_modules(self): toy_modtxt = read_file(toy_mod) # No math libs in original toolchain, --try-toolchain is too clever to upgrade it beyond necessary - for modname in ['FFTW', 'OpenBLAS', 'ScaLAPACK']: - regex = re.compile('load.*' + modname, re.M) - self.assertFalse(regex.search(toy_modtxt), "Pattern '%s' not found in: %s" % (regex.pattern, - toy_modtxt)) - - for modname in ['GCC', 'OpenMPI']: - regex = re.compile('load.*' + modname, re.M) - self.assertFalse(regex.search(toy_modtxt), - "Pattern '%s' not found in: %s" % (regex.pattern, toy_modtxt)) + for modname in ['FFTW', 'OpenBLAS', 'ScaLAPACK', 'GCC', 'OpenMPI']: + self.assertNotRegex(toy_modtxt, 'load.*' + modname) def test_toy_advanced(self): """Test toy build with extensions and non-system toolchain.""" @@ -1207,15 +1182,13 @@ def test_toy_advanced(self): '^setenv.*TOY_EXT_BAR.*bar', '^setenv.*TOY_EXT_BARBAR.*barbar', ] - for pattern in patterns: - self.assertTrue(re.search(pattern, toy_mod_txt, re.M), "Pattern '%s' found in: %s" % (pattern, toy_mod_txt)) + self.assert_multi_regex(patterns, toy_mod_txt) toy_installdir = os.path.join(self.test_installpath, 'software', 'toy', '0.0-gompi-2018a-test') toy_libs_path = os.path.join(toy_installdir, 'toy_libs_path.txt') self.assertTrue(os.path.exists(toy_libs_path)) txt = read_file(toy_libs_path) - regex = re.compile('^TOY_EXAMPLES=examples$') - self.assertTrue(regex.match(txt), f"Pattern '{regex.pattern}' should match in: {txt}") + self.assertRegex(txt, '^TOY_EXAMPLES=examples$') def test_toy_advanced_filter_deps(self): """Test toy build with extensions, and filtered build dependency.""" @@ -1701,9 +1674,7 @@ def test_toy_module_fulltxt(self): else: self.fail("Unknown module syntax: %s" % get_module_syntax()) - mod_txt_regex = re.compile(mod_txt_regex_pattern) - msg = "Pattern '%s' matches with: %s" % (mod_txt_regex.pattern, toy_mod_txt) - self.assertTrue(mod_txt_regex.match(toy_mod_txt), msg) + self.assertRegex(toy_mod_txt, mod_txt_regex_pattern) def test_external_dependencies(self): """Test specifying external (build) dependencies.""" @@ -1765,8 +1736,7 @@ def test_external_dependencies(self): # --dry-run still works when external modules are missing; external modules are treated as if they were there with self.mocked_stdout_stderr(): outtxt = self._test_toy_build(ec_file=toy_ec, verbose=True, extra_args=['--dry-run'], verify=False) - regex = re.compile(r"^ \* \[ \] .* \(module: toy/0.0-external-deps-broken2\)", re.M) - self.assertTrue(regex.search(outtxt), "Pattern '%s' found in: %s" % (regex.pattern, outtxt)) + self.assertRegex(outtxt, re.compile(r"^ \* \[ \] .* \(module: toy/0.0-external-deps-broken2\)", re.M)) def test_module_only(self): """Test use of --module-only.""" @@ -1796,10 +1766,8 @@ def test_module_only(self): # make sure load statements for dependencies are included in additional module file generated with --module-only modtxt = read_file(toy_mod) - self.assertTrue(re.search('(load|depends[-_]on).*intel/2018a', modtxt), - "load statement for intel/2018a found in module") - self.assertTrue(re.search('(load|depends[-_]on).*GCC/6.4.0-2.28', modtxt), - "load statement for GCC/6.4.0-2.28 found in module") + self.assertRegex(modtxt, '(load|depends[-_]on).*intel/2018a') + self.assertRegex(modtxt, '(load|depends[-_]on).*GCC/6.4.0-2.28') os.remove(toy_mod) @@ -1819,7 +1787,7 @@ def test_module_only(self): self.assertExists(toy_mod) self.assertExists(os.path.join(self.test_installpath, 'software', 'toy', '0.0-deps', 'bin')) modtxt = read_file(toy_mod) - self.assertTrue(re.search("set root %s" % prefix, modtxt)) + self.assertIn("set root %s" % prefix, modtxt) self.assertEqual(len(os.listdir(os.path.join(self.test_installpath, 'software'))), 2) self.assertEqual(len(os.listdir(os.path.join(self.test_installpath, 'software', 'toy'))), 1) @@ -1834,15 +1802,14 @@ def test_module_only(self): self.eb_main(args, do_build=True, raise_error=True) self.assertExists(toy_core_mod) # existing install is reused - modtxt2 = read_file(toy_core_mod) - self.assertTrue(re.search("set root %s" % prefix, modtxt2)) + modtxt = read_file(toy_core_mod) + self.assertIn("set root %s" % prefix, modtxt) self.assertEqual(len(os.listdir(os.path.join(self.test_installpath, 'software'))), 3) self.assertEqual(len(os.listdir(os.path.join(self.test_installpath, 'software', 'toy'))), 1) # make sure load statements for dependencies are included modtxt = read_file(toy_core_mod) - self.assertTrue(re.search('(load|depends[-_]on).*intel/2018a', modtxt), - "load statement for intel/2018a found in module") + self.assertRegex(modtxt, '(load|depends[-_]on).*intel/2018a') # Test we can create a module even for an installation where we don't have write permissions os.remove(toy_core_mod) @@ -1853,15 +1820,14 @@ def test_module_only(self): self.eb_main(args, do_build=True, raise_error=True) self.assertExists(toy_core_mod) # existing install is reused - modtxt2 = read_file(toy_core_mod) - self.assertTrue(re.search("set root %s" % prefix, modtxt2)) + modtxt = read_file(toy_core_mod) + self.assertIn("set root %s" % prefix, modtxt) self.assertEqual(len(os.listdir(os.path.join(self.test_installpath, 'software'))), 3) self.assertEqual(len(os.listdir(os.path.join(self.test_installpath, 'software', 'toy'))), 1) # make sure load statements for dependencies are included modtxt = read_file(toy_core_mod) - self.assertTrue(re.search('(load|depends[-_]on).*intel/2018a', modtxt), - "load statement for intel/2018a found in module") + self.assertRegex(modtxt, '(load|depends[-_]on).*intel/2018a') os.remove(toy_core_mod) os.remove(toy_mod) @@ -1881,14 +1847,13 @@ def test_module_only(self): self.assertExists(toy_mod + '.lua') # existing install is reused modtxt3 = read_file(toy_mod + '.lua') - self.assertTrue(re.search('local root = "%s"' % prefix, modtxt3)) + self.assertIn('local root = "%s"' % prefix, modtxt3) self.assertEqual(len(os.listdir(os.path.join(self.test_installpath, 'software'))), 3) self.assertEqual(len(os.listdir(os.path.join(self.test_installpath, 'software', 'toy'))), 1) # make sure load statements for dependencies are included modtxt = read_file(toy_mod + '.lua') - self.assertTrue(re.search('(load|depends[-_]on).*intel/2018a', modtxt), - "load statement for intel/2018a found in module") + self.assertRegex(modtxt, '(load|depends[-_]on).*intel/2018a') def test_module_only_extensions(self): """ @@ -1999,10 +1964,7 @@ def test_toy_exts_parallel(self): r"== 4 out of 4 extensions installed \(0 queued, 0 running: \)$", '', ] - for pattern in patterns: - regex = re.compile(pattern, re.M) - error_msg = "Expected pattern '%s' should be found in %s'" % (regex.pattern, stdout) - self.assertTrue(regex.search(stdout), error_msg) + self.assert_multi_regex(patterns, stdout) # also test skipping of extensions in parallel args.append('--skip') @@ -2017,10 +1979,7 @@ def test_toy_exts_parallel(self): r"^== skipping extension barbar$", r"^== skipping extension toy$", ] - for pattern in patterns: - regex = re.compile(pattern, re.M) - error_msg = "Expected pattern '%s' should be found in %s'" % (regex.pattern, stdout) - self.assertTrue(regex.search(stdout), error_msg) + self.assert_multi_regex(patterns, stdout) # check behaviour when using Toy_Extension easyblock that doesn't implement required_deps method; # framework should fall back to installing extensions sequentially @@ -2044,10 +2003,7 @@ def test_toy_exts_parallel(self): r"^== 4 out of 4 extensions installed \(0 queued, 0 running: \)$", '', ] - for pattern in patterns: - regex = re.compile(pattern, re.M) - error_msg = "Expected pattern '%s' should be found in %s'" % (regex.pattern, stdout) - self.assertTrue(regex.search(stdout), error_msg) + self.assert_multi_regex(patterns, stdout) def test_backup_modules(self): """Test use of backing up of modules with --module-only.""" @@ -2094,10 +2050,9 @@ def test_backup_modules(self): self.assertTrue(os.path.basename(first_toy_mod_backup).startswith('.')) toy_mod_bak = r".*/toy/\.0\.0-deps\.bak_[0-9]+_[0-9]+" - regex = re.compile("^== backup of existing module file stored at %s" % toy_mod_bak, re.M) - self.assertTrue(regex.search(stdout), "Pattern '%s' found in: %s" % (regex.pattern, stdout)) - regex = re.compile("^== comparing module file with backup %s; no differences found$" % toy_mod_bak, re.M) - self.assertTrue(regex.search(stdout), "Pattern '%s' found in: %s" % (regex.pattern, stdout)) + self.assertRegex(stdout, re.compile("^== backup of existing module file stored at %s" % toy_mod_bak, re.M)) + self.assertRegex(stdout, re.compile("^== comparing module file with backup %s; no differences found$" + % toy_mod_bak, re.M)) self.assertEqual(stderr, '') @@ -2121,12 +2076,9 @@ def test_backup_modules(self): toy_mod_backups = glob.glob(os.path.join(toy_mod_dir, '.' + toy_mod_fn + '.bak_*')) self.assertEqual(len(toy_mod_backups), 2) - regex = re.compile("^== backup of existing module file stored at %s" % toy_mod_bak, re.M) - self.assertTrue(regex.search(stdout), "Pattern '%s' found in: %s" % (regex.pattern, stdout)) - regex = re.compile("^== comparing module file with backup %s; diff is:$" % toy_mod_bak, re.M) - self.assertTrue(regex.search(stdout), "Pattern '%s' found in: %s" % (regex.pattern, stdout)) - regex = re.compile("^-some difference$", re.M) - self.assertTrue(regex.search(stdout), "Pattern '%s' found in: %s" % (regex.pattern, stdout)) + self.assertRegex(stdout, re.compile("^== backup of existing module file stored at %s" % toy_mod_bak, re.M)) + self.assertRegex(stdout, re.compile("^== comparing module file with backup %s; diff is:$" % toy_mod_bak, re.M)) + self.assertRegex(stdout, re.compile("^-some difference$", re.M)) self.assertEqual(stderr, '') # Test also with Lua syntax if Lmod is available. @@ -2182,10 +2134,9 @@ def test_backup_modules(self): self.assertIn('.bak_', os.path.basename(first_toy_lua_mod_backup)) # check messages in stdout/stderr - regex = re.compile("^== backup of existing module file stored at %s" % toy_mod_bak, re.M) - self.assertTrue(regex.search(stdout), "Pattern '%s' found in: %s" % (regex.pattern, stdout)) - regex = re.compile("^== comparing module file with backup %s; no differences found$" % toy_mod_bak, re.M) - self.assertTrue(regex.search(stdout), "Pattern '%s' found in: %s" % (regex.pattern, stdout)) + self.assertRegex(stdout, re.compile("^== backup of existing module file stored at %s" % toy_mod_bak, re.M)) + self.assertRegex(stdout, re.compile("^== comparing module file with backup %s; no differences found$" + % toy_mod_bak, re.M)) self.assertEqual(stderr, '') # tweak existing module file so we can verify diff of installed module with backup in stdout @@ -2212,12 +2163,12 @@ def test_backup_modules(self): hidden_toy_mod_backups = glob.glob(os.path.join(toy_mod_dir, '.' + toy_mod_fn + '.bak_*')) self.assertEqual(len(hidden_toy_mod_backups), backups_hidden) - regex = re.compile("^== backup of existing module file stored at %s" % toy_mod_bak, re.M) - self.assertTrue(regex.search(stdout), "Pattern '%s' found in: %s" % (regex.pattern, stdout)) - regex = re.compile("^== comparing module file with backup %s; diff is:$" % toy_mod_bak, re.M) - self.assertTrue(regex.search(stdout), "Pattern '%s' found in: %s" % (regex.pattern, stdout)) - regex = re.compile("^-some difference$", re.M) - self.assertTrue(regex.search(stdout), "Pattern '%s' found in: %s" % (regex.pattern, stdout)) + self.assertRegex(stdout, + re.compile("^== backup of existing module file stored at %s" % toy_mod_bak, re.M)) + self.assertRegex(stdout, + re.compile("^== comparing module file with backup %s; diff is:$" % toy_mod_bak, re.M)) + self.assertRegex(stdout, + re.compile("^-some difference$", re.M)) self.assertEqual(stderr, '') def test_package(self): @@ -2418,8 +2369,7 @@ def test_toy_toy(self): mod2_txt = read_file(mod2) - load1_regex = re.compile('(load|depends[-_]on).*toy/0.0-one', re.M) - self.assertTrue(load1_regex.search(mod2_txt), "Pattern '%s' found in: %s" % (load1_regex.pattern, mod2_txt)) + self.assertRegex(mod2_txt, re.compile('(load|depends[-_]on).*toy/0.0-one', re.M)) # Check the contents of the dumped env in the reprod dir to ensure it contains the dependency load reprod_dir = os.path.join(self.test_installpath, 'software', 'toy2', '0.0-two', 'easybuild', 'reprod') @@ -2491,8 +2441,7 @@ def test_toy_sanity_check_commands(self): self.assertExists(out_file) out_txt = read_file(out_file) # working dir for sanity check command should be an empty custom temporary directory - regex = re.compile('^.*/eb-[^/]+/eb-sanity-check-[^/]+\n[ ]*0$') - self.assertTrue(regex.match(out_txt), f"Pattern '{regex.pattern}' should match in: {out_txt}") + self.assertRegex(out_txt, '^.*/eb-[^/]+/eb-sanity-check-[^/]+\n[ ]*0$') def test_sanity_check_paths_lib64(self): """Test whether fallback in sanity check for lib64/ equivalents of library files works.""" @@ -2629,8 +2578,7 @@ def test_toy_build_enhanced_sanity_check(self): r"\s*\* toy", r'', ] - regex = re.compile(r'\n'.join(pattern_lines), re.M) - self.assertTrue(regex.search(stdout), "Pattern '%s' should be found in: %s" % (regex.pattern, stdout)) + self.assertRegex(stdout, re.compile(r'\n'.join(pattern_lines), re.M)) # we need to manually wipe the entry for the included toy easyblock, # to avoid trouble with subsequent EasyBuild sessions in this test @@ -2662,8 +2610,7 @@ def test_toy_build_enhanced_sanity_check(self): r"\s*\* ls .*/software/toy/0.0", r'', ] - regex = re.compile(r'\n'.join(pattern_lines), re.M) - self.assertTrue(regex.search(stdout), "Pattern '%s' should be found in: %s" % (regex.pattern, stdout)) + self.assertRegex(stdout, re.compile(r'\n'.join(pattern_lines), re.M)) del sys.modules['easybuild.easyblocks.toy'] @@ -2689,8 +2636,7 @@ def test_toy_build_enhanced_sanity_check(self): r"\s*\* toy", r'', ] - regex = re.compile(r'\n'.join(pattern_lines), re.M) - self.assertTrue(regex.search(stdout), "Pattern '%s' should be found in: %s" % (regex.pattern, stdout)) + self.assertRegex(stdout, re.compile(r'\n'.join(pattern_lines), re.M)) del sys.modules['easybuild.easyblocks.toy'] @@ -2714,12 +2660,10 @@ def test_toy_build_enhanced_sanity_check(self): r"^== sanity checking\.\.\.", r" >> file 'bin/toy' found: OK", ] - regex = re.compile(r'\n'.join(pattern_lines), re.M) - self.assertTrue(regex.search(stdout), "Pattern '%s' should be found in: %s" % (regex.pattern, stdout)) + self.assertRegex(stdout, re.compile(r'\n'.join(pattern_lines), re.M)) # no directories are checked in sanity check now, only files (since dirs is an empty list) - regex = re.compile(r"directory .* found:", re.M) - self.assertFalse(regex.search(stdout), "Pattern '%s' should be not found in: %s" % (regex.pattern, stdout)) + self.assertNotRegex(stdout, r"directory .* found:") del sys.modules['easybuild.easyblocks.toy'] @@ -2791,8 +2735,7 @@ def sanity_check_step(self): \s*\* python{pyshortver} """) for pyshortver in ('2.7', '3.7'): - regex = re.compile(pattern_template.format(pyshortver=pyshortver), re.M) - self.assertTrue(regex.search(stdout), "Pattern '%s' should be found in: %s" % (regex.pattern, stdout)) + self.assertRegex(stdout, re.compile(pattern_template.format(pyshortver=pyshortver), re.M)) # Enhance sanity check by extra paths to check for, the ones from the easyblock should be kept test_ec_txt += textwrap.dedent(""" @@ -2820,8 +2763,7 @@ def sanity_check_step(self): \s*\* python{pyshortver} """) for pyshortver in ('2.7', '3.7'): - regex = re.compile(pattern_template.format(pyshortver=pyshortver), re.M) - self.assertTrue(regex.search(stdout), "Pattern '%s' should be found in: %s" % (regex.pattern, stdout)) + self.assertRegex(stdout, re.compile(pattern_template.format(pyshortver=pyshortver), re.M)) def test_toy_dumped_easyconfig(self): """ Test dumping of file in eb_filerepo in both .eb format """ @@ -2857,23 +2799,22 @@ def test_toy_filter_env_vars(self): toy_mod_path += '.lua' regexs = [ - re.compile("prepend[-_]path.*LD_LIBRARY_PATH.*lib", re.M), - re.compile("prepend[-_]path.*LIBRARY_PATH.*lib", re.M), - re.compile("prepend[-_]path.*PATH.*bin", re.M), + "prepend[-_]path.*LD_LIBRARY_PATH.*lib", + "prepend[-_]path.*LIBRARY_PATH.*lib", + "prepend[-_]path.*PATH.*bin", ] with self.mocked_stdout_stderr(): self._test_toy_build() toy_mod_txt = read_file(toy_mod_path) - for regex in regexs: - self.assertTrue(regex.search(toy_mod_txt), "Pattern '%s' found in: %s" % (regex.pattern, toy_mod_txt)) + self.assert_multi_regex(regexs, toy_mod_txt) with self.mocked_stdout_stderr(): self._test_toy_build(extra_args=['--filter-env-vars=LD_LIBRARY_PATH,PATH']) toy_mod_txt = read_file(toy_mod_path) - self.assertFalse(regexs[0].search(toy_mod_txt), "Pattern '%s' found in: %s" % (regexs[0].pattern, toy_mod_txt)) - self.assertTrue(regexs[1].search(toy_mod_txt), "Pattern '%s' found in: %s" % (regexs[1].pattern, toy_mod_txt)) - self.assertFalse(regexs[2].search(toy_mod_txt), "Pattern '%s' found in: %s" % (regexs[2].pattern, toy_mod_txt)) + self.assertNotRegex(toy_mod_txt, regexs[0]) + self.assertRegex(toy_mod_txt, regexs[1]) + self.assertNotRegex(toy_mod_txt, regexs[2]) def test_toy_iter(self): """Test toy build that involves iterating over buildopts.""" @@ -3001,16 +2942,15 @@ def test_toy_filter_rpath_sanity_libs(self): rpath_regex = re.compile(r"\(RPATH\).*" + libtoy_libdir, re.M) with self.mocked_stdout_stderr(): res = run_shell_cmd(f"readelf -d {toyapp_bin}") - self.assertTrue(rpath_regex.search(res.output), - f"Pattern '{rpath_regex.pattern}' should be found in: {res.output}") + self.assertRegex(res.output, rpath_regex) with self.mocked_stdout_stderr(): res = run_shell_cmd(f"ldd {toyapp_bin}") out = res.output - libtoy_regex = re.compile(r"libtoy.so => /.*/libtoy.so", re.M) - notfound = re.compile(r"libtoy\.so\s*=>\s*not found", re.M) - self.assertTrue(libtoy_regex.search(out), f"Pattern '{libtoy_regex.pattern}' should be found in: {out}") - self.assertFalse(notfound.search(out), f"Pattern '{notfound.pattern}' should not be found in: {out}") + libtoy_regex = re.compile(r"libtoy.so => /.*/libtoy.so") + notfound_regex = re.compile(r"libtoy\.so\s*=>\s*not found") + self.assertRegex(out, libtoy_regex) + self.assertNotRegex(out, notfound_regex) # test sanity error when --rpath-filter is used to filter a required library # In this test, libtoy.so will be linked, but not RPATH-ed due to the --rpath-filter @@ -3030,15 +2970,12 @@ def test_toy_filter_rpath_sanity_libs(self): with self.mocked_stdout_stderr(): res = run_shell_cmd(f"readelf -d {toyapp_bin}") - self.assertFalse(rpath_regex.search(res.output), - f"Pattern '{rpath_regex.pattern}' should not be found in: {res.output}") + self.assertNotRegex(res.output, rpath_regex) with self.mocked_stdout_stderr(): res = run_shell_cmd(f"ldd {toyapp_bin}") - self.assertFalse(libtoy_regex.search(res.output), - f"Pattern '{libtoy_regex.pattern}' should not be found in: {res.output}") - self.assertTrue(notfound.search(res.output), - f"Pattern '{notfound.pattern}' should be found in: {res.output}") + self.assertNotRegex(res.output, libtoy_regex) + self.assertRegex(res.output, notfound_regex) # test again with list of library names passed to --filter-rpath-sanity-libs args = rpath_args + ['--rpath-filter=.*libtoy.*', '--filter-rpath-sanity-libs=libfoo.so,libtoy.so,libbar.so'] @@ -3047,15 +2984,12 @@ def test_toy_filter_rpath_sanity_libs(self): with self.mocked_stdout_stderr(): res = run_shell_cmd(f"readelf -d {toyapp_bin}") - self.assertFalse(rpath_regex.search(out), - f"Pattern '{rpath_regex.pattern}' should not be found in: {res.output}") + self.assertNotRegex(out, rpath_regex) with self.mocked_stdout_stderr(): res = run_shell_cmd(f"ldd {toyapp_bin}") - self.assertFalse(libtoy_regex.search(res.output), - f"Pattern '{libtoy_regex.pattern}' should not be found in: {res.output}") - self.assertTrue(notfound.search(res.output), - f"Pattern '{notfound.pattern}' should be found in: {res.output}") + self.assertNotRegex(res.output, libtoy_regex) + self.assertRegex(res.output, notfound_regex) # by default, without using --strict-rpath-sanity-check, there's no failure since RPATH sanity check # doesn't check for missing libraries with $LD_LIBRARY_PATH unset @@ -3210,12 +3144,9 @@ def test_toy_cuda_sanity_check(self): # Also, check if it's found in stdout, if defined # If either of these fail their assert, print an informative, standardized message def assert_regex(pattern, log, stdout=None): - regex = re.compile(pattern, re.M) - msg = "Pattern '%s' not found in full build log: %s" % (pattern, log) - self.assertTrue(regex.search(log), msg) + self.assertRegex(log, pattern) if stdout is not None: - msg2 = "Pattern '%s' not found in standard output: %s" % (pattern, stdout) - self.assertTrue(regex.search(stdout), msg2) + self.assertRegex(stdout, pattern) def assert_cuda_report(missing_cc, additional_cc, missing_ptx, log, stdout=None, missing_cc_but_ptx=None, num_checked=None): @@ -3257,7 +3188,7 @@ def assert_cuda_report(missing_cc, additional_cc, missing_ptx, log, stdout=None, # It should not matter for the result, but triggers slightly different code paths in easyblock.py # This should succeed (since the default for --cuda-sanity-check-error-on-failed-checks is False) # as to not break backwards compatibility - write_file(cuobjdump_file, cuobjdump_txt_shebang), + write_file(cuobjdump_file, cuobjdump_txt_shebang) write_file(cuobjdump_file, cuobjdump_txt_sm90, append=True) write_file(cuobjdump_file, cuobjdump_txt_sm80_ptx, append=True) write_file(cuobjdump_file, cuobjdump_txt_sm70, append=True) @@ -3267,10 +3198,8 @@ def assert_cuda_report(missing_cc, additional_cc, missing_ptx, log, stdout=None, with self.mocked_stdout_stderr(): outtxt = self._test_toy_build(ec_file=toy_ec_cuda, extra_args=args, raise_error=True) stdout = self.get_stdout() - msg = "Pattern '%s' not found in full build log: %s" % (device_additional_70_90_code_regex.pattern, outtxt) - self.assertTrue(device_additional_70_90_code_regex.search(outtxt), msg) - msg = "Pattern '%s' not found in full build log: %s" % (device_missing_80_code_regex.pattern, outtxt) - self.assertTrue(device_missing_80_code_regex.search(outtxt), msg) + self.assertRegex(outtxt, device_additional_70_90_code_regex) + self.assertRegex(outtxt, device_missing_80_code_regex) assert_cuda_report(missing_cc=3, additional_cc=3, missing_ptx=0, log=outtxt, stdout=stdout) # Test case 2: same as Test case 1, but add --cuda-sanity-check-error-on-failed-checks @@ -3283,10 +3212,8 @@ def assert_cuda_report(missing_cc, additional_cc, missing_ptx, log, stdout=None, extra_args=args, raise_error=True) outtxt = self._test_toy_build(ec_file=toy_ec_cuda, extra_args=args, raise_error=False, verify=False) stdout = self.get_stdout() - msg = "Pattern '%s' not found in full build log: %s" % (device_additional_70_90_code_regex.pattern, outtxt) - self.assertTrue(device_additional_70_90_code_regex.search(outtxt), msg) - msg = "Pattern '%s' not found in full build log: %s" % (device_missing_80_code_regex.pattern, outtxt) - self.assertTrue(device_missing_80_code_regex.search(outtxt), msg) + self.assertRegex(outtxt, device_additional_70_90_code_regex) + self.assertRegex(outtxt, device_missing_80_code_regex) assert_cuda_report(missing_cc=3, additional_cc=3, missing_ptx=0, log=outtxt, stdout=stdout) # Test case 3: same as Test case 2, but add --cuda-sanity-check-accept-ptx-as-devcode @@ -3299,10 +3226,8 @@ def assert_cuda_report(missing_cc, additional_cc, missing_ptx, log, stdout=None, with self.mocked_stdout_stderr(): outtxt = self._test_toy_build(ec_file=toy_ec_cuda, extra_args=args, raise_error=True) stdout = self.get_stdout() - msg = "Pattern '%s' not found in full build log: %s" % (device_additional_70_90_code_regex.pattern, outtxt) - self.assertTrue(device_additional_70_90_code_regex.search(outtxt), msg) - msg = "Pattern '%s' not found in full build log: %s" % (device_missing_80_code_regex.pattern, outtxt) - self.assertTrue(device_missing_80_code_regex.search(outtxt), msg) + self.assertRegex(outtxt, device_additional_70_90_code_regex) + self.assertRegex(outtxt, device_missing_80_code_regex) assert_cuda_report(missing_cc=0, additional_cc=3, missing_ptx=0, log=outtxt, stdout=stdout, missing_cc_but_ptx=3) @@ -3316,8 +3241,7 @@ def assert_cuda_report(missing_cc, additional_cc, missing_ptx, log, stdout=None, extra_args=args, raise_error=True) outtxt = self._test_toy_build(ec_file=toy_ec_cuda, extra_args=args, raise_error=False, verify=False) stdout = self.get_stdout() - msg = "Pattern '%s' not found in full build log: %s" % (device_additional_70_code_regex.pattern, outtxt) - self.assertTrue(device_additional_70_code_regex.search(outtxt), msg) + self.assertRegex(outtxt, device_additional_70_code_regex) assert_cuda_report(missing_cc=0, additional_cc=3, missing_ptx=3, log=outtxt, stdout=stdout) # Test case 5: same as Test case 4, but add --cuda-sanity-check-accept-missing-ptx @@ -3332,10 +3256,8 @@ def assert_cuda_report(missing_cc, additional_cc, missing_ptx, log, stdout=None, with self.mocked_stdout_stderr(): outtxt = self._test_toy_build(ec_file=toy_ec_cuda, extra_args=args, raise_error=True) stdout = self.get_stdout() - msg = "Pattern '%s' not found in full build log: %s" % (device_additional_70_code_regex.pattern, outtxt) - self.assertTrue(device_additional_70_code_regex.search(outtxt), msg) - msg = "Pattern '%s' not found in full build log: %s" % (warning_pattern, outtxt) - self.assertTrue(warning_pattern_regex.search(outtxt), msg) + self.assertRegex(outtxt, device_additional_70_code_regex) + self.assertRegex(outtxt, warning_pattern_regex) assert_cuda_report(missing_cc=0, additional_cc=3, missing_ptx=3, log=outtxt, stdout=stdout) # Test case 6: same as Test case 5, but add --cuda-sanity-check-strict @@ -3350,8 +3272,7 @@ def assert_cuda_report(missing_cc, additional_cc, missing_ptx, log, stdout=None, extra_args=args, raise_error=True) outtxt = self._test_toy_build(ec_file=toy_ec_cuda, extra_args=args, raise_error=False, verify=False) stdout = self.get_stdout() - msg = "Pattern '%s' not found in full build log: %s" % (device_additional_70_code_regex.pattern, outtxt) - self.assertTrue(device_additional_70_code_regex.search(outtxt), msg) + self.assertRegex(outtxt, device_additional_70_code_regex) assert_cuda_report(missing_cc=0, additional_cc=3, missing_ptx=3, log=outtxt, stdout=stdout) # Test case 7: same as Test case 6, but add the failing file to the cuda_sanity_ignore_files @@ -3372,8 +3293,7 @@ def assert_cuda_report(missing_cc, additional_cc, missing_ptx, log, stdout=None, with self.mocked_stdout_stderr(): outtxt = self._test_toy_build(ec_file=toy_whitelist_ec, extra_args=args, raise_error=True, verify=False) stdout = self.get_stdout() - msg = "Pattern '%s' not found in full build log: %s" % (device_additional_70_code_regex.pattern, outtxt) - self.assertTrue(device_additional_70_code_regex.search(outtxt), msg) + self.assertRegex(outtxt, device_additional_70_code_regex) assert_cuda_report(missing_cc=0, additional_cc=1, missing_ptx=1, log=outtxt, stdout=stdout) # Test case 8: try with --cuda-sanity-check-error-on-failed-checks --cuda-compute-capabilities=9.0,9.0a @@ -3393,14 +3313,10 @@ def assert_cuda_report(missing_cc, additional_cc, missing_ptx, log, stdout=None, with self.mocked_stdout_stderr(): outtxt = self._test_toy_build(ec_file=toy_ec_cuda, extra_args=args, raise_error=True) stdout = self.get_stdout() - msg = "Pattern '%s' not found in full build log: %s" % (device_code_regex_success.pattern, outtxt) - self.assertTrue(device_code_regex_success.search(outtxt), msg) - msg = "Pattern '%s' not found in full build log: %s" % (ptx_code_regex_success.pattern, outtxt) - self.assertTrue(ptx_code_regex_success.search(outtxt), msg) + self.assertRegex(outtxt, device_code_regex_success) + self.assertRegex(outtxt, ptx_code_regex_success) expected_result_pattern = "INFO Sanity check for toy successful" - expected_result = re.compile(expected_result_pattern, re.M) - msg = "Pattern '%s' not found in full build log: %s" % (expected_result, outtxt) - self.assertTrue(expected_result.search(outtxt), msg) + self.assertRegex(outtxt, re.compile(expected_result_pattern, re.M)) assert_cuda_report(missing_cc=0, additional_cc=0, missing_ptx=0, log=outtxt, stdout=stdout) # Test case 9: same as 8, but no --cuda-compute-capabilities are defined @@ -3411,14 +3327,9 @@ def assert_cuda_report(missing_cc, additional_cc, missing_ptx, log, stdout=None, outtxt = self._test_toy_build(ec_file=toy_ec_cuda, extra_args=args, raise_error=True) stdout = self.get_stdout() cuda_sanity_skipped = r"INFO Skipping CUDA sanity check, as no CUDA compute capabilities were configured" - cuda_sanity_skipped_regex = re.compile(cuda_sanity_skipped, re.M) - msg = "Pattern '%s' not found in full build log: %s" % (cuda_sanity_skipped, outtxt) - self.assertTrue(cuda_sanity_skipped_regex.search(outtxt), msg) - expected_result_pattern = "INFO Sanity check for toy successful" - expected_result = re.compile(expected_result_pattern, re.M) - msg = "Pattern '%s' not found in full build log: %s" % (expected_result, outtxt) - self.assertTrue(expected_result.search(outtxt), msg) - + self.assertIn(cuda_sanity_skipped, outtxt) + expected_result = "INFO Sanity check for toy successful" + self.assertIn(expected_result, outtxt) # Test case 10: running with default options and a binary that does not contain ANY CUDA device code # This is expected to succeed, since the default is --disable-cuda-sanity-check-error-on-failed-checks write_file(cuobjdump_file, cuobjdump_txt_shebang) @@ -3431,13 +3342,9 @@ def assert_cuda_report(missing_cc, additional_cc, missing_ptx, log, stdout=None, stdout = self.get_stdout() no_cuda_pattern = r".*/bin/toy does not appear to be a CUDA executable \(no CUDA device code found\), " no_cuda_pattern += r"so skipping CUDA sanity check" - no_cuda_regex = re.compile(no_cuda_pattern, re.M) - msg = "Pattern '%s' not found in full build log: %s" % (no_cuda_pattern, outtxt) - self.assertTrue(no_cuda_regex.search(outtxt), msg) - expected_result_pattern = "INFO Sanity check for toy successful" - expected_result = re.compile(expected_result_pattern, re.M) - msg = "Pattern '%s' not found in full build log: %s" % (expected_result, outtxt) - self.assertTrue(expected_result.search(outtxt), msg) + self.assertRegex(outtxt, re.compile(no_cuda_pattern, re.M)) + expected_result = "INFO Sanity check for toy successful" + self.assertIn(expected_result, outtxt) assert_cuda_report(missing_cc=0, additional_cc=0, missing_ptx=0, log=outtxt, stdout=stdout, num_checked=0) # Test case 11: same as Test case 10, but add --cuda-sanity-check-error-on-failed-checks @@ -3449,18 +3356,14 @@ def assert_cuda_report(missing_cc, additional_cc, missing_ptx, log, stdout=None, stdout = self.get_stdout() no_cuda_pattern = r".*/bin/toy does not appear to be a CUDA executable \(no CUDA device code found\), " no_cuda_pattern += r"so skipping CUDA sanity check" - no_cuda_regex = re.compile(no_cuda_pattern, re.M) - msg = "Pattern '%s' not found in full build log: %s" % (no_cuda_pattern, outtxt) - self.assertTrue(no_cuda_regex.search(outtxt), msg) + self.assertRegex(outtxt, no_cuda_pattern) expected_result_pattern = "INFO Sanity check for toy successful" - expected_result = re.compile(expected_result_pattern, re.M) - msg = "Pattern '%s' not found in full build log: %s" % (expected_result, outtxt) - self.assertTrue(expected_result.search(outtxt), msg) + self.assertRegex(outtxt, re.compile(expected_result_pattern, re.M)) assert_cuda_report(missing_cc=0, additional_cc=0, missing_ptx=0, log=outtxt, stdout=stdout, num_checked=0) def test_toy_modaltsoftname(self): """Build two dependent toys as in test_toy_toy but using modaltsoftname""" - self.assertFalse(re.search('^modaltsoftname', TOY_EC_TXT, re.M)) + self.assertNotRegex(TOY_EC_TXT, re.compile('^modaltsoftname', re.M)) ec1 = os.path.join(self.test_prefix, 'toy-0.0-one.eb') ec1_txt = '\n'.join([ @@ -3552,9 +3455,7 @@ def test_toy_build_trace(self): ]) + r'$', r"^== creating module\.\.\.\n >> generating module file @ .*/modules/all/toy/0\.0(?:\.lua)?$", ] - for pattern in patterns: - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(stdout), "Pattern '%s' found in: %s" % (regex.pattern, stdout)) + self.assert_multi_regex(patterns, stdout) def test_toy_build_hooks(self): """Test use of --hooks.""" @@ -3786,16 +3687,14 @@ def test_toy_multi_deps(self): "This module is compatible with the following modules, one of each line is required:", "* GCC/4.6.3 (default), GCC/7.3.0-2.30", ]) - error_msg_descr = "Pattern '%s' should be found in: %s" % (expected_descr, toy_mod_txt) - self.assertIn(expected_descr, toy_mod_txt, error_msg_descr) + self.assertIn(expected_descr, toy_mod_txt) if get_module_syntax() == 'Lua': expected_whatis = "whatis([==[Compatible modules: GCC/4.6.3 (default), GCC/7.3.0-2.30]==])" else: expected_whatis = "module-whatis {Compatible modules: GCC/4.6.3 (default), GCC/7.3.0-2.30}" - error_msg_whatis = "Pattern '%s' should be found in: %s" % (expected_whatis, toy_mod_txt) - self.assertIn(expected_whatis, toy_mod_txt, error_msg_whatis) + self.assertIn(expected_whatis, toy_mod_txt) def check_toy_load(depends_on=False): # by default, toy/0.0 should load GCC/4.6.3 (first listed GCC version in multi_deps) @@ -3885,16 +3784,14 @@ def check_toy_load(depends_on=False): "This module is compatible with the following modules, one of each line is required:", "* GCC/4.6.3, GCC/7.3.0-2.30", ]) - error_msg_descr = "Pattern '%s' should be found in: %s" % (expected_descr_no_default, toy_mod_txt) - self.assertIn(expected_descr_no_default, toy_mod_txt, error_msg_descr) + self.assertIn(expected_descr_no_default, toy_mod_txt,) if get_module_syntax() == 'Lua': expected_whatis_no_default = "whatis([==[Compatible modules: GCC/4.6.3, GCC/7.3.0-2.30]==])" else: expected_whatis_no_default = "module-whatis {Compatible modules: GCC/4.6.3, GCC/7.3.0-2.30}" - error_msg_whatis = "Pattern '%s' should be found in: %s" % (expected_whatis_no_default, toy_mod_txt) - self.assertIn(expected_whatis_no_default, toy_mod_txt, error_msg_whatis) + self.assertIn(expected_whatis_no_default, toy_mod_txt) # disable showing of progress bars (again), doesn't make sense when running tests os.environ['EASYBUILD_DISABLE_SHOW_PROGRESS_BAR'] = '1' @@ -3929,10 +3826,8 @@ def check_toy_load(depends_on=False): ]) self.assertIn(expected, toy_mod_txt) - error_msg_descr = "Pattern '%s' should be found in: %s" % (expected_descr, toy_mod_txt) - self.assertIn(expected_descr, toy_mod_txt, error_msg_descr) - error_msg_whatis = "Pattern '%s' should be found in: %s" % (expected_whatis, toy_mod_txt) - self.assertIn(expected_whatis, toy_mod_txt, error_msg_whatis) + self.assertIn(expected_descr, toy_mod_txt) + self.assertIn(expected_whatis, toy_mod_txt) check_toy_load(depends_on=True) @@ -4037,8 +3932,7 @@ def test_fix_shebang(self): for script in scripts[ext]: bin_path = os.path.join(toy_bindir, script) bin_txt = read_file(bin_path) - self.assertTrue(regexes[ext].match(bin_txt), - "Pattern '%s' found in %s: %s" % (regexes[ext].pattern, bin_path, bin_txt)) + self.assertRegex(bin_txt, regexes[ext]) # now test with a custom env command extra_args = ['--env-for-shebang=/usr/bin/env -S'] @@ -4065,12 +3959,9 @@ def check_shebangs(): bin_txt = read_file(bin_path) # the scripts b1.py, b1.pl, b1.sh, b2.sh should keep their original shebang if script.startswith('b'): - self.assertTrue(regexes[ext].match(bin_txt), - "Pattern '%s' found in %s: %s" % (regexes[ext].pattern, bin_path, bin_txt)) + self.assertRegex(bin_txt, regexes[ext]) else: - regex_shebang = regexes_shebang[ext] - self.assertTrue(regex_shebang.match(bin_txt), - "Pattern '%s' found in %s: %s" % (regex_shebang.pattern, bin_path, bin_txt)) + self.assertRegex(bin_txt, regexes_shebang[ext]) # no re.M, this should match at start of file! regexes_shebang['py'] = re.compile(r'^#!/usr/bin/env -S python\n# test$') @@ -4147,8 +4038,7 @@ def test_toy_ghost_installdir(self): stdout, stderr = self.run_test_toy_build_with_output() # by default, a warning is printed for ghost installation directories (but they're left untouched) - regex = re.compile("WARNING: Likely ghost installation directory detected: %s" % toy_installdir) - self.assertTrue(regex.search(stderr), "Pattern '%s' found in: %s" % (regex.pattern, stderr)) + self.assertIn("WARNING: Likely ghost installation directory detected: %s" % toy_installdir, stderr) self.assertExists(toy_installdir) # cleanup of ghost installation directories can be enable via --remove-ghost-install-dirs @@ -4157,8 +4047,7 @@ def test_toy_ghost_installdir(self): self.assertFalse(stderr) - regex = re.compile("== Ghost installation directory %s removed" % toy_installdir) - self.assertRegex(stdout, regex) + self.assertIn("== Ghost installation directory %s removed" % toy_installdir, stdout) self.assertNotExists(toy_installdir) @@ -4256,7 +4145,7 @@ def __exit__(self, type, value, traceback): # we can't rely on an exact number of 'waiting' messages, so let's go with a range... self.assertIn(len(wait_matches), range(1, 5)) - self.assertTrue(ok_regex.search(stdout), "Pattern '%s' found in: %s" % (ok_regex.pattern, stdout)) + self.assertRegex(stdout, ok_regex) # check use of --wait-on-lock-limit: if lock is never removed, we should give up when limit is reached mkdir(toy_lock_path) @@ -4286,8 +4175,8 @@ def __exit__(self, type, value, traceback): self.mock_stdout(False) self.assertEqual(stderr, '') - self.assertTrue(ok_regex.search(stdout), "Pattern '%s' found in: %s" % (ok_regex.pattern, stdout)) - self.assertFalse(wait_regex.search(stdout), "Pattern '%s' not found in: %s" % (wait_regex.pattern, stdout)) + self.assertRegex(stdout, ok_regex) + self.assertNotRegex(stdout, wait_regex) # check for clean error on creation of lock extra_args = ['--locks-dir=/'] @@ -4358,8 +4247,7 @@ def __exit__(self, type, value, traceback): pattern = r"^WARNING: signal received \(%s\), " % int(signum) pattern += r"cleaning up locks \(.*software_toy_0.0\)\.\.\." - regex = re.compile(pattern) - self.assertTrue(regex.search(stderr), "Pattern '%s' found in: %s" % (regex.pattern, stderr)) + self.assertRegex(stderr, pattern) def test_toy_build_unicode_description(self): """Test installation of easyconfig file that has non-ASCII characters in description.""" @@ -4630,9 +4518,7 @@ def test_toy_unavailable_os_dep(self): r"Failed to process easyconfig", r"One or more OS dependencies were not found", ] - for pattern in patterns: - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(stdout), "Pattern '%s' should be found in: %s" % (regex.pattern, stdout)) + self.assert_multi_regex(patterns, stdout) def test_toy_post_install_messages(self): """ @@ -4656,9 +4542,7 @@ def test_toy_post_install_messages(self): r"== This is post install message 1", r"== This is post install message 2", ] - for pattern in patterns: - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(stdout), "Pattern '%s' should be found in: %s" % (regex.pattern, stdout)) + self.assert_multi_regex(patterns, stdout) def test_toy_build_info_msg(self): """ @@ -4678,8 +4562,7 @@ def test_toy_build_info_msg(self): r'', r"Are you sure you want to install this toy software\?", ]) - regex = re.compile(pattern, re.M) - self.assertTrue(regex.search(stdout), "Pattern '%s' should be found in: %s" % (regex.pattern, stdout)) + self.assertRegex(stdout, re.compile(pattern, re.M)) def test_toy_failing_test_step(self): """ @@ -4750,7 +4633,7 @@ def pre_configure_hook(self, *args, **kwargs): regex = re.compile(r"EasyBuild crashed! Please consider reporting a bug, this should not happen") stderr = stderr.getvalue() - self.assertTrue(regex.search(stderr), f"Pattern '{regex.pattern}' should be found in {stderr}") + self.assertRegex(stderr, regex) def test_eb_error(self): """ @@ -4770,7 +4653,7 @@ def test_eb_error(self): regex = re.compile("^ERROR: Missing dependencies", re.M) stderr = stderr.getvalue() - self.assertTrue(regex.search(stderr), f"Pattern '{regex.pattern}' should be found in {stderr}") + self.assertRegex(stderr, regex) def test_toy_python(self): """ @@ -4803,8 +4686,7 @@ def test_toy_python(self): pythonpath_regex = re.compile('^prepend.path.*PYTHONPATH.*lib.*python3.6.*site-packages', re.M) - self.assertTrue(pythonpath_regex.search(toy_mod_txt), - f"Pattern '{pythonpath_regex.pattern}' found in: {toy_mod_txt}") + self.assertRegex(toy_mod_txt, pythonpath_regex) # also check when opting in to use $EBPYTHONPREFIXES instead of $PYTHONPATH args = ['--prefer-python-search-path=EBPYTHONPREFIXES'] @@ -4813,8 +4695,7 @@ def test_toy_python(self): # if Python is not listed as a runtime dependency then $PYTHONPATH is still used, # because the Python dependency used must be aware of $EBPYTHONPREFIXES # (see sitecustomize.py installed by Python easyblock) - self.assertTrue(pythonpath_regex.search(toy_mod_txt), - f"Pattern '{pythonpath_regex.pattern}' found in: {toy_mod_txt}") + self.assertRegex(toy_mod_txt, pythonpath_regex) # if Python is listed as runtime dependency, then $EBPYTHONPREFIXES is used if it's preferred write_file(test_ec, test_ec_txt + "\ndependencies = [('Python', '3.6', '', SYSTEM)]") @@ -4822,16 +4703,14 @@ def test_toy_python(self): toy_mod_txt = read_file(toy_mod) ebpythonprefixes_regex = re.compile('^prepend.path.*EBPYTHONPREFIXES.*root', re.M) - self.assertTrue(ebpythonprefixes_regex.search(toy_mod_txt), - f"Pattern '{ebpythonprefixes_regex.pattern}' found in: {toy_mod_txt}") + self.assertRegex(toy_mod_txt, ebpythonprefixes_regex) # if Python is listed in multi_deps, then $EBPYTHONPREFIXES is used, even if it's not explicitely preferred write_file(test_ec, test_ec_txt + "\nmulti_deps = {'Python': ['2.7', '3.6']}") self.run_test_toy_build_with_output(ec_file=test_ec) toy_mod_txt = read_file(toy_mod) - self.assertTrue(ebpythonprefixes_regex.search(toy_mod_txt), - f"Pattern '{ebpythonprefixes_regex.pattern}' found in: {toy_mod_txt}") + self.assertRegex(toy_mod_txt, ebpythonprefixes_regex) def test_toy_multiple_ecs_module(self): """ @@ -4861,16 +4740,14 @@ def test_toy_multiple_ecs_module(self): if get_module_syntax() == 'Lua': toy_mod += '.lua' toy_modtxt = read_file(toy_mod) - regex = re.compile('prepend[-_]path.*CPATH.*toy-headers', re.M) - self.assertTrue(regex.search(toy_modtxt), - f"Pattern '{regex.pattern}' should be found in: {toy_modtxt}") + regex = re.compile('prepend[-_]path.*CPATH.*toy-headers') + self.assertRegex(toy_modtxt, regex) toy_app_mod = os.path.join(self.test_installpath, 'modules', 'all', 'toy-app', '0.0') if get_module_syntax() == 'Lua': toy_app_mod += '.lua' toy_app_modtxt = read_file(toy_app_mod) - self.assertFalse(regex.search(toy_app_modtxt), - f"Pattern '{regex.pattern}' should *not* be found in: {toy_app_modtxt}") + self.assertNotRegex(toy_app_modtxt, regex) def test_easyconfig_instances(self): """ @@ -4888,9 +4765,8 @@ def test_easyconfig_instances(self): # count how many times an EasyConfig instance was created, # either by process_easyconfig function or by EasyConfig.copy, based on log messages - regex = re.compile("Creating.* EasyConfig instance .*", re.M) logtxt = read_file(logfile) - matches = regex.findall(logtxt) + matches = re.findall("Creating.* EasyConfig instance .*", logtxt) cnt = len(matches) # we expect to find 12 EasyConfig instances being created: gzip itself + full toolchain; # note: multiple EasyConfig instances are currently created for (sub)toolchains (like foss/2018a, gompi/2018a) diff --git a/test/framework/utilities.py b/test/framework/utilities.py index b698ea013a..dae5246145 100644 --- a/test/framework/utilities.py +++ b/test/framework/utilities.py @@ -450,10 +450,10 @@ def setup_categorized_hmns_modules(self): line) sys.stdout.write(line) - def assert_multi_regex(self, regexs, txt, assert_true=True): + 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""" for regex in regexs: - regex = re.compile(regex, re.M) + regex = re.compile(regex, flags) if assert_true: self.assertRegex(txt, regex) else: