diff options
author | Mats Wichmann <mats@linux.com> | 2024-10-06 17:34:37 (GMT) |
---|---|---|
committer | Mats Wichmann <mats@linux.com> | 2024-10-19 14:25:14 (GMT) |
commit | 80ff4c7530e9b01afa896423fa025f15fe91cb4a (patch) | |
tree | 756fc950159ca568bf1691343e8dae4a2ebad0bd /SCons | |
parent | cc8ea0f91830a0a4d1ae5582343df58cc1bb126b (diff) | |
download | SCons-80ff4c7530e9b01afa896423fa025f15fe91cb4a.zip SCons-80ff4c7530e9b01afa896423fa025f15fe91cb4a.tar.gz SCons-80ff4c7530e9b01afa896423fa025f15fe91cb4a.tar.bz2 |
Adjust tests in case running as root
Although validation tests are not normally run as root, there may be
cicrumstances when it happens - one known case is when the test suite
is run as part of a particular Linux distros package construction.
It isn't too hard to avoid the few places where we counted on something
failing because of permissions, which don't if the user is root -
added a few skips.
Signed-off-by: Mats Wichmann <mats@linux.com>
Diffstat (limited to 'SCons')
-rw-r--r-- | SCons/CacheDirTests.py | 41 | ||||
-rw-r--r-- | SCons/Node/FSTests.py | 40 | ||||
-rw-r--r-- | SCons/Variables/PathVariableTests.py | 32 |
3 files changed, 83 insertions, 30 deletions
diff --git a/SCons/CacheDirTests.py b/SCons/CacheDirTests.py index 6ec9e84..e904788 100644 --- a/SCons/CacheDirTests.py +++ b/SCons/CacheDirTests.py @@ -34,6 +34,13 @@ import SCons.CacheDir built_it = None +IS_WINDOWS = sys.platform == 'win32' +try: + IS_ROOT = os.geteuid() == 0 +except AttributeError: + IS_ROOT = False + + class Action: def __call__(self, targets, sources, env, **kw) -> int: global built_it @@ -85,7 +92,6 @@ class BaseTestCase(unittest.TestCase): def tearDown(self) -> None: os.remove(os.path.join(self._CacheDir.path, 'config')) os.rmdir(self._CacheDir.path) - # Should that be shutil.rmtree? class CacheDirTestCase(BaseTestCase): """ @@ -124,20 +130,29 @@ class ExceptionTestCase(unittest.TestCase): def tearDown(self) -> None: shutil.rmtree(self.tmpdir) - @unittest.skipIf(sys.platform.startswith("win"), "This fixture will not trigger an OSError on Windows") + @unittest.skipIf( + IS_WINDOWS, + "Skip privileged CacheDir test on Windows, cannot change directory rights", + ) + @unittest.skipIf( + IS_ROOT, + "Skip privileged CacheDir test if running as root.", + ) def test_throws_correct_on_OSError(self) -> None: - """Test that the correct error is thrown when cache directory cannot be created.""" + """Test for correct error when cache directory cannot be created.""" + test = TestCmd() privileged_dir = os.path.join(self.tmpdir, "privileged") - try: - os.mkdir(privileged_dir) - os.chmod(privileged_dir, stat.S_IREAD) - cd = SCons.CacheDir.CacheDir(os.path.join(privileged_dir, "cache")) - assert False, "Should have raised exception and did not" - except SCons.Errors.SConsEnvironmentError as e: - assert str(e) == "Failed to create cache directory {}".format(os.path.join(privileged_dir, "cache")) - finally: - os.chmod(privileged_dir, stat.S_IWRITE | stat.S_IEXEC | stat.S_IREAD) - shutil.rmtree(privileged_dir) + cachedir_path = os.path.join(privileged_dir, "cache") + os.makedirs(privileged_dir, exist_ok=True) + test.writable(privileged_dir, False) + with self.assertRaises(SCons.Errors.SConsEnvironmentError) as cm: + cd = SCons.CacheDir.CacheDir(cachedir_path) + self.assertEqual( + str(cm.exception), + "Failed to create cache directory " + cachedir_path + ) + test.writable(privileged_dir, True) + shutil.rmtree(privileged_dir) def test_throws_correct_when_failed_to_write_configfile(self) -> None: diff --git a/SCons/Node/FSTests.py b/SCons/Node/FSTests.py index 2036f92..0f20cd8 100644 --- a/SCons/Node/FSTests.py +++ b/SCons/Node/FSTests.py @@ -44,6 +44,11 @@ built_it = None scanner_count = 0 +try: + IS_ROOT = os.geteuid() == 0 +except AttributeError: + IS_ROOT = False + class Scanner: def __init__(self, node=None) -> None: @@ -958,6 +963,8 @@ class FSTestCase(_tempdirTestCase): This test case handles all of the file system node tests in one environment, so we don't have to set up a complicated directory structure for each test individually. + This isn't ideal: normally you want to separate tests a bit + more to make it easier to debug and not fail too fast. """ test = self.test @@ -1550,27 +1557,30 @@ class FSTestCase(_tempdirTestCase): assert r, r assert not os.path.islink(symlink), "symlink was not removed" - test.write('can_not_remove', "can_not_remove\n") - test.writable(test.workpath('.'), 0) - fp = open(test.workpath('can_not_remove')) - - f = fs.File('can_not_remove') - exc_caught = 0 - try: - r = f.remove() - except OSError: - exc_caught = 1 - - fp.close() - - assert exc_caught, "Should have caught an OSError, r = " + str(r) - f = fs.Entry('foo/bar/baz') assert f.for_signature() == 'baz', f.for_signature() assert f.get_string(0) == os.path.normpath('foo/bar/baz'), \ f.get_string(0) assert f.get_string(1) == 'baz', f.get_string(1) + + @unittest.skipIf(IS_ROOT, "Skip file removal in protected dir if running as root.") + def test_remove_fail(self) -> None: + """Test failure when removing a file where permissions don't allow. + + Split from :math:`test_runTest` to be able to skip on root. + We want to be able to skip only this one testcase and run the rest. + """ + test = self.test + fs = SCons.Node.FS.FS() + test.write('can_not_remove', "can_not_remove\n") + test.writable(test.workpath('.'), False) + with open(test.workpath('can_not_remove')): + f = fs.File('can_not_remove') + with self.assertRaises(OSError, msg="Should have caught an OSError"): + r = f.remove() + + def test_drive_letters(self) -> None: """Test drive-letter look-ups""" diff --git a/SCons/Variables/PathVariableTests.py b/SCons/Variables/PathVariableTests.py index 7fa8da1..87d6bdf 100644 --- a/SCons/Variables/PathVariableTests.py +++ b/SCons/Variables/PathVariableTests.py @@ -30,6 +30,12 @@ import SCons.Variables import TestCmd from TestCmd import IS_WINDOWS +try: + IS_ROOT = os.geteuid() == 0 +except AttributeError: + IS_ROOT = False + + class PathVariableTestCase(unittest.TestCase): def test_PathVariable(self) -> None: """Test PathVariable creation""" @@ -93,7 +99,7 @@ class PathVariableTestCase(unittest.TestCase): self.assertEqual(str(e), f"Directory path for variable 'X' does not exist: {dne}") def test_PathIsDirCreate(self): - """Test the PathIsDirCreate validator""" + """Test the PathIsDirCreate validator.""" opts = SCons.Variables.Variables() opts.Add(SCons.Variables.PathVariable('test', 'test build variable help', @@ -115,6 +121,27 @@ class PathVariableTestCase(unittest.TestCase): e = cm.exception self.assertEqual(str(e), f"Path for variable 'X' is a file, not a directory: {f}") + + @unittest.skipIf(IS_ROOT, "Skip creating bad directory if running as root.") + def test_PathIsDirCreate_bad_dir(self): + """Test the PathIsDirCreate validator with an uncreatable dir. + + Split from :meth:`test_PathIsDirCreate` to be able to skip on root. + We want to be able to skip only this one testcase and run the rest. + """ + opts = SCons.Variables.Variables() + opts.Add( + SCons.Variables.PathVariable( + 'test', + 'test build variable help', + '/default/path', + SCons.Variables.PathVariable.PathIsDirCreate, + ) + ) + + test = TestCmd.TestCmd(workdir='') + o = opts.options[0] + # pick a directory path that can't be mkdir'd if IS_WINDOWS: f = r'\\noserver\noshare\yyy\zzz' @@ -125,8 +152,9 @@ class PathVariableTestCase(unittest.TestCase): e = cm.exception self.assertEqual(str(e), f"Path for variable 'X' could not be created: {f}") + def test_PathIsFile(self): - """Test the PathIsFile validator""" + """Test the PathIsFile validator.""" opts = SCons.Variables.Variables() opts.Add(SCons.Variables.PathVariable('test', 'test build variable help', |