|
34 | 34 | import os
|
35 | 35 | import re
|
36 | 36 | import shutil
|
| 37 | +import signal |
37 | 38 | import stat
|
38 | 39 | import sys
|
39 | 40 | import tempfile
|
40 | 41 | from distutils.version import LooseVersion
|
| 42 | +from functools import wraps |
41 | 43 | from test.framework.utilities import EnhancedTestCase, TestLoaderFiltered
|
42 | 44 | from test.framework.package import mock_fpm
|
43 | 45 | from unittest import TextTestRunner
|
@@ -1441,7 +1443,7 @@ def test_module_only(self):
|
1441 | 1443 | os.remove(toy_core_mod)
|
1442 | 1444 |
|
1443 | 1445 | # test installing (only) additional module in Lua syntax (if Lmod is available)
|
1444 |
| - lmod_abspath = which('lmod') |
| 1446 | + lmod_abspath = os.environ.get('LMOD_CMD') or which('lmod') |
1445 | 1447 | if lmod_abspath is not None:
|
1446 | 1448 | args = common_args[:-1] + [
|
1447 | 1449 | '--allow-modules-tool-mismatch',
|
@@ -2057,7 +2059,7 @@ def test_toy_modaltsoftname(self):
|
2057 | 2059 | self.assertTrue(os.path.exists(os.path.join(modules_path, 'yot', yot_name)))
|
2058 | 2060 |
|
2059 | 2061 | # only subdirectories for software should be created
|
2060 |
| - self.assertEqual(sorted(os.listdir(software_path)), sorted(['.locks', 'toy'])) |
| 2062 | + self.assertEqual(os.listdir(software_path), ['toy', '.locks']) |
2061 | 2063 | self.assertEqual(sorted(os.listdir(os.path.join(software_path, 'toy'))), ['0.0-one', '0.0-two'])
|
2062 | 2064 |
|
2063 | 2065 | # only subdirectories for modules with alternative names should be created
|
@@ -2516,6 +2518,88 @@ def test_toy_ghost_installdir(self):
|
2516 | 2518 |
|
2517 | 2519 | self.assertFalse(os.path.exists(toy_installdir))
|
2518 | 2520 |
|
| 2521 | + def test_toy_build_lock(self): |
| 2522 | + """Test toy installation when a lock is already in place.""" |
| 2523 | + |
| 2524 | + locks_dir = os.path.join(self.test_installpath, 'software', '.locks') |
| 2525 | + toy_installdir = os.path.join(self.test_installpath, 'software', 'toy', '0.0') |
| 2526 | + toy_lock_fn = toy_installdir.replace(os.path.sep, '_') + '.lock' |
| 2527 | + |
| 2528 | + toy_lock_path = os.path.join(locks_dir, toy_lock_fn) |
| 2529 | + mkdir(toy_lock_path, parents=True) |
| 2530 | + |
| 2531 | + error_pattern = "Lock .*_software_toy_0.0.lock already exists, aborting!" |
| 2532 | + self.assertErrorRegex(EasyBuildError, error_pattern, self.test_toy_build, raise_error=True, verbose=False) |
| 2533 | + |
| 2534 | + locks_dir = os.path.join(self.test_prefix, 'locks') |
| 2535 | + |
| 2536 | + # no lock in place, so installation proceeds as normal |
| 2537 | + extra_args = ['--locks-dir=%s' % locks_dir] |
| 2538 | + self.test_toy_build(extra_args=extra_args, verify=True, raise_error=True) |
| 2539 | + |
| 2540 | + # put lock in place in custom locks dir, try again |
| 2541 | + toy_lock_path = os.path.join(locks_dir, toy_lock_fn) |
| 2542 | + mkdir(toy_lock_path, parents=True) |
| 2543 | + self.assertErrorRegex(EasyBuildError, error_pattern, self.test_toy_build, |
| 2544 | + extra_args=extra_args, raise_error=True, verbose=False) |
| 2545 | + |
| 2546 | + # also test use of --ignore-locks |
| 2547 | + self.test_toy_build(extra_args=extra_args + ['--ignore-locks'], verify=True, raise_error=True) |
| 2548 | + |
| 2549 | + # define a context manager that remove a lock after a while, so we can check the use of --wait-for-lock |
| 2550 | + class remove_lock_after: |
| 2551 | + def __init__(self, seconds, lock_fp): |
| 2552 | + self.seconds = seconds |
| 2553 | + self.lock_fp = lock_fp |
| 2554 | + |
| 2555 | + def remove_lock(self, *args): |
| 2556 | + remove_dir(self.lock_fp) |
| 2557 | + |
| 2558 | + def __enter__(self): |
| 2559 | + signal.signal(signal.SIGALRM, self.remove_lock) |
| 2560 | + signal.alarm(self.seconds) |
| 2561 | + |
| 2562 | + def __exit__(self, type, value, traceback): |
| 2563 | + pass |
| 2564 | + |
| 2565 | + # wait for lock to be removed, with 1 second interval of checking |
| 2566 | + extra_args.append('--wait-on-lock=1') |
| 2567 | + |
| 2568 | + wait_regex = re.compile("^== lock .*_software_toy_0.0.lock exists, waiting 1 seconds", re.M) |
| 2569 | + ok_regex = re.compile("^== COMPLETED: Installation ended successfully", re.M) |
| 2570 | + |
| 2571 | + self.assertTrue(os.path.exists(toy_lock_path)) |
| 2572 | + |
| 2573 | + # use context manager to remove lock after 3 seconds |
| 2574 | + with remove_lock_after(3, toy_lock_path): |
| 2575 | + self.mock_stderr(True) |
| 2576 | + self.mock_stdout(True) |
| 2577 | + self.test_toy_build(extra_args=extra_args, verify=False, raise_error=True, testing=False) |
| 2578 | + stderr, stdout = self.get_stderr(), self.get_stdout() |
| 2579 | + self.mock_stderr(False) |
| 2580 | + self.mock_stdout(False) |
| 2581 | + |
| 2582 | + self.assertEqual(stderr, '') |
| 2583 | + |
| 2584 | + wait_matches = wait_regex.findall(stdout) |
| 2585 | + # we can't rely on an exact number of 'waiting' messages, so let's go with a range... |
| 2586 | + self.assertTrue(len(wait_matches) in range(2, 5)) |
| 2587 | + |
| 2588 | + self.assertTrue(ok_regex.search(stdout), "Pattern '%s' found in: %s" % (ok_regex.pattern, stdout)) |
| 2589 | + |
| 2590 | + # when there is no lock in place, --wait-on-lock has no impact |
| 2591 | + self.assertFalse(os.path.exists(toy_lock_path)) |
| 2592 | + self.mock_stderr(True) |
| 2593 | + self.mock_stdout(True) |
| 2594 | + self.test_toy_build(extra_args=extra_args, verify=False, raise_error=True, testing=False) |
| 2595 | + stderr, stdout = self.get_stderr(), self.get_stdout() |
| 2596 | + self.mock_stderr(False) |
| 2597 | + self.mock_stdout(False) |
| 2598 | + |
| 2599 | + self.assertEqual(stderr, '') |
| 2600 | + self.assertTrue(ok_regex.search(stdout), "Pattern '%s' found in: %s" % (ok_regex.pattern, stdout)) |
| 2601 | + self.assertFalse(wait_regex.search(stdout), "Pattern '%s' not found in: %s" % (wait_regex.pattern, stdout)) |
| 2602 | + |
2519 | 2603 |
|
2520 | 2604 | def suite():
|
2521 | 2605 | """ return all the tests in this file """
|
|
0 commit comments