| #!/usr/bin/env python |
| # coding=utf-8 |
| # Copyright 2019 The LUCI Authors. All rights reserved. |
| # Use of this source code is governed under the Apache License, Version 2.0 |
| # that can be found in the LICENSE file. |
| |
| import os |
| import tempfile |
| import unittest |
| |
| # Mutates sys.path. |
| import test_env |
| |
| from utils import file_path |
| from utils import fs |
| |
| |
| def write_content(path, content): |
| with fs.open(path, 'wb') as f: |
| f.write(content) |
| |
| |
| class FSTest(unittest.TestCase): |
| @classmethod |
| def setUpClass(cls): |
| if not file_path.enable_symlink(): |
| raise Exception('Failed to enable symlink support') |
| |
| def setUp(self): |
| super(FSTest, self).setUp() |
| self._tempdir = None |
| |
| def tearDown(self): |
| try: |
| if self._tempdir: |
| file_path.rmtree(self._tempdir) |
| finally: |
| super(FSTest, self).tearDown() |
| |
| @property |
| def tempdir(self): |
| if not self._tempdir: |
| self._tempdir = tempfile.mkdtemp(prefix=u'fs_test') |
| return self._tempdir |
| |
| def test_symlink_relative(self): |
| # A symlink to a relative path is valid. |
| # /dir |
| # /dir/file |
| # /ld -> /dir |
| # /lf -> /ld/file |
| dirpath = os.path.join(self.tempdir, 'dir') |
| filepath = os.path.join(dirpath, 'file') |
| fs.mkdir(dirpath) |
| write_content(filepath, 'hello') |
| |
| linkfile = os.path.join(self.tempdir, 'lf') |
| linkdir = os.path.join(self.tempdir, 'ld') |
| dstfile = os.path.join('ld', 'file') |
| fs.symlink(dstfile, linkfile) |
| fs.symlink('dir', linkdir) |
| |
| self.assertEqual(True, fs.islink(linkfile)) |
| self.assertEqual(True, fs.islink(linkdir)) |
| self.assertEqual(dstfile, fs.readlink(linkfile)) |
| self.assertEqual('dir', fs.readlink(linkdir)) |
| self.assertEqual(['file'], fs.listdir(linkdir)) |
| # /lf resolves to /dir/file. |
| self.assertEqual('hello', fs.open(linkfile).read()) |
| |
| # Ensures that followlinks is respected in walk(). |
| expected = [ |
| (self.tempdir, ['dir', 'ld'], ['lf']), |
| (dirpath, [], ['file']), |
| ] |
| actual = [ |
| (r, sorted(d), sorted(f)) |
| for r, d, f in sorted(fs.walk(self.tempdir, followlinks=False)) |
| ] |
| self.assertEqual(expected, actual) |
| expected = [ |
| (self.tempdir, ['dir', 'ld'], ['lf']), |
| (dirpath, [], ['file']), |
| (linkdir, [], ['file']), |
| ] |
| actual = [ |
| (r, sorted(d), sorted(f)) |
| for r, d, f in sorted(fs.walk(self.tempdir, followlinks=True)) |
| ] |
| self.assertEqual(expected, actual) |
| |
| def test_symlink_absolute(self): |
| # A symlink to an absolute path is valid. |
| # /dir |
| # /dir/file |
| # /ld -> /dir |
| # /lf -> /ld/file |
| dirpath = os.path.join(self.tempdir, 'dir') |
| filepath = os.path.join(dirpath, 'file') |
| fs.mkdir(dirpath) |
| write_content(filepath, 'hello') |
| |
| linkfile = os.path.join(self.tempdir, 'lf') |
| linkdir = os.path.join(self.tempdir, 'ld') |
| dstfile = os.path.join(linkdir, 'file') |
| fs.symlink(dstfile, linkfile) |
| fs.symlink(dirpath, linkdir) |
| |
| self.assertEqual(True, fs.islink(linkfile)) |
| self.assertEqual(True, fs.islink(linkdir)) |
| self.assertEqual(dstfile, fs.readlink(linkfile)) |
| self.assertEqual(dirpath, fs.readlink(linkdir)) |
| self.assertEqual(['file'], fs.listdir(linkdir)) |
| # /lf resolves to /dir/file. |
| self.assertEqual('hello', fs.open(linkfile).read()) |
| |
| # Ensures that followlinks is respected in walk(). |
| expected = [ |
| (self.tempdir, ['dir', 'ld'], ['lf']), |
| (dirpath, [], ['file']), |
| ] |
| actual = [ |
| (r, sorted(d), sorted(f)) |
| for r, d, f in sorted(fs.walk(self.tempdir, followlinks=False)) |
| ] |
| self.assertEqual(expected, actual) |
| expected = [ |
| (self.tempdir, ['dir', 'ld'], ['lf']), |
| (dirpath, [], ['file']), |
| (linkdir, [], ['file']), |
| ] |
| actual = [ |
| (r, sorted(d), sorted(f)) |
| for r, d, f in sorted(fs.walk(self.tempdir, followlinks=True)) |
| ] |
| self.assertEqual(expected, actual) |
| |
| def test_symlink_missing_destination_rel(self): |
| # A symlink to a missing destination is valid and can be read back. |
| filepath = 'file' |
| linkfile = os.path.join(self.tempdir, 'lf') |
| fs.symlink(filepath, linkfile) |
| |
| self.assertEqual(True, fs.islink(linkfile)) |
| self.assertEqual(filepath, fs.readlink(linkfile)) |
| |
| def test_symlink_missing_destination_abs(self): |
| # A symlink to a missing destination is valid and can be read back. |
| filepath = os.path.join(self.tempdir, 'file') |
| linkfile = os.path.join(self.tempdir, 'lf') |
| fs.symlink(filepath, linkfile) |
| |
| self.assertEqual(True, fs.islink(linkfile)) |
| self.assertEqual(filepath, fs.readlink(linkfile)) |
| |
| def test_symlink_existing(self): |
| # Creating a symlink that overrides a file fails. |
| filepath = os.path.join(self.tempdir, 'file') |
| linkfile = os.path.join(self.tempdir, 'lf') |
| write_content(linkfile, 'hello') |
| with self.assertRaises(OSError): |
| fs.symlink(filepath, linkfile) |
| |
| def test_readlink_fail(self): |
| # Reading a non-existing symlink fails. Obvious but it's to make sure the |
| # Windows part acts the same. |
| with self.assertRaises(OSError): |
| fs.readlink(os.path.join(self.tempdir, 'not_there')) |
| |
| |
| if __name__ == '__main__': |
| test_env.main() |