340 lines
12 KiB
Python
340 lines
12 KiB
Python
import os
|
|
from pathlib import Path, PurePath, PurePosixPath, PureWindowsPath
|
|
import platform
|
|
import tempfile
|
|
import unittest
|
|
from unittest import mock
|
|
|
|
from blender_asset_tracer.bpathlib import BlendPath, make_absolute, strip_root
|
|
|
|
|
|
class BlendPathTest(unittest.TestCase):
|
|
def test_string_path(self):
|
|
p = BlendPath(PurePosixPath("//some/file.blend"))
|
|
self.assertEqual("//some/file.blend", str(PurePosixPath("//some/file.blend")))
|
|
self.assertEqual(b"//some/file.blend", p)
|
|
|
|
p = BlendPath(Path(r"C:\some\file.blend"))
|
|
self.assertEqual(b"C:/some/file.blend", p)
|
|
|
|
def test_invalid_type(self):
|
|
with self.assertRaises(TypeError):
|
|
BlendPath("//some/file.blend")
|
|
with self.assertRaises(TypeError):
|
|
BlendPath(47)
|
|
with self.assertRaises(TypeError):
|
|
BlendPath(None)
|
|
|
|
def test_repr(self):
|
|
p = BlendPath(b"//some/file.blend")
|
|
self.assertEqual("BlendPath(b'//some/file.blend')", repr(p))
|
|
p = BlendPath(PurePosixPath("//some/file.blend"))
|
|
self.assertEqual("BlendPath(b'//some/file.blend')", repr(p))
|
|
|
|
def test_to_path(self):
|
|
self.assertEqual(
|
|
PurePath("/some/file.blend"), BlendPath(b"/some/file.blend").to_path()
|
|
)
|
|
self.assertEqual(
|
|
PurePath("C:/some/file.blend"), BlendPath(b"C:/some/file.blend").to_path()
|
|
)
|
|
self.assertEqual(
|
|
PurePath("C:/some/file.blend"), BlendPath(br"C:\some\file.blend").to_path()
|
|
)
|
|
|
|
with mock.patch("sys.getfilesystemencoding") as mock_getfse:
|
|
mock_getfse.return_value = "latin1"
|
|
|
|
# \xe9 is Latin-1 for é, and BlendPath should revert to using the
|
|
# (mocked) filesystem encoding when decoding as UTF-8 fails.
|
|
self.assertEqual(
|
|
PurePath("C:/some/filé.blend"),
|
|
BlendPath(b"C:\\some\\fil\xe9.blend").to_path(),
|
|
)
|
|
|
|
with self.assertRaises(ValueError):
|
|
BlendPath(b"//relative/path.jpg").to_path()
|
|
|
|
def test_is_absolute(self):
|
|
self.assertFalse(BlendPath(b"//some/file.blend").is_absolute())
|
|
self.assertTrue(BlendPath(b"/some/file.blend").is_absolute())
|
|
self.assertTrue(BlendPath(b"C:/some/file.blend").is_absolute())
|
|
self.assertTrue(BlendPath(b"C:\\some\\file.blend").is_absolute())
|
|
self.assertFalse(BlendPath(b"some/file.blend").is_absolute())
|
|
|
|
def test_is_blendfile_relative(self):
|
|
self.assertTrue(BlendPath(b"//some/file.blend").is_blendfile_relative())
|
|
self.assertFalse(BlendPath(b"/some/file.blend").is_blendfile_relative())
|
|
self.assertFalse(BlendPath(b"C:/some/file.blend").is_blendfile_relative())
|
|
self.assertFalse(BlendPath(b"some/file.blend").is_blendfile_relative())
|
|
|
|
def test_make_absolute(self):
|
|
self.assertEqual(
|
|
b"/root/to/some/file.blend",
|
|
BlendPath(b"//some/file.blend").absolute(b"/root/to"),
|
|
)
|
|
self.assertEqual(
|
|
b"/root/to/some/file.blend",
|
|
BlendPath(b"some/file.blend").absolute(b"/root/to"),
|
|
)
|
|
self.assertEqual(
|
|
b"/root/to/../some/file.blend",
|
|
BlendPath(b"../some/file.blend").absolute(b"/root/to"),
|
|
)
|
|
self.assertEqual(
|
|
b"/shared/some/file.blend",
|
|
BlendPath(b"/shared/some/file.blend").absolute(b"/root/to"),
|
|
)
|
|
|
|
def test_slash(self):
|
|
self.assertEqual(
|
|
b"/root/and/parent.blend", BlendPath(b"/root/and") / b"parent.blend"
|
|
)
|
|
with self.assertRaises(ValueError):
|
|
BlendPath(b"/root/and") / b"/parent.blend"
|
|
|
|
self.assertEqual(
|
|
b"/root/and/parent.blend", b"/root/and" / BlendPath(b"parent.blend")
|
|
)
|
|
with self.assertRaises(ValueError):
|
|
b"/root/and" / BlendPath(b"/parent.blend")
|
|
|
|
# On Windows+Python 3.5.4 this resulted in b'//root//parent.blend',
|
|
# but only if the root is a single term (so not b'//root/and/').
|
|
self.assertEqual(
|
|
BlendPath(b"//root/parent.blend"), BlendPath(b"//root/") / b"parent.blend"
|
|
)
|
|
|
|
@unittest.skipIf(
|
|
platform.system() == "Windows", "POSIX paths cannot be used on Windows"
|
|
)
|
|
def test_mkrelative_posix(self):
|
|
self.assertEqual(
|
|
b"//asset.png",
|
|
BlendPath.mkrelative(
|
|
Path("/path/to/asset.png"),
|
|
PurePosixPath("/path/to/bfile.blend"),
|
|
),
|
|
)
|
|
self.assertEqual(
|
|
b"//to/asset.png",
|
|
BlendPath.mkrelative(
|
|
Path("/path/to/asset.png"),
|
|
PurePosixPath("/path/bfile.blend"),
|
|
),
|
|
)
|
|
self.assertEqual(
|
|
b"//../of/asset.png",
|
|
BlendPath.mkrelative(
|
|
Path("/path/of/asset.png"),
|
|
PurePosixPath("/path/to/bfile.blend"),
|
|
),
|
|
)
|
|
self.assertEqual(
|
|
b"//../../path/of/asset.png",
|
|
BlendPath.mkrelative(
|
|
Path("/path/of/asset.png"),
|
|
PurePosixPath("/some/weird/bfile.blend"),
|
|
),
|
|
)
|
|
self.assertEqual(
|
|
b"//very/very/very/very/very/deep/asset.png",
|
|
BlendPath.mkrelative(
|
|
Path("/path/to/very/very/very/very/very/deep/asset.png"),
|
|
PurePosixPath("/path/to/bfile.blend"),
|
|
),
|
|
)
|
|
self.assertEqual(
|
|
b"//../../../../../../../../shallow/asset.png",
|
|
BlendPath.mkrelative(
|
|
Path("/shallow/asset.png"),
|
|
PurePosixPath("/path/to/very/very/very/very/very/deep/bfile.blend"),
|
|
),
|
|
)
|
|
|
|
@unittest.skipIf(
|
|
platform.system() != "Windows", "Windows paths cannot be used on POSIX"
|
|
)
|
|
def test_mkrelative_windows(self):
|
|
self.assertEqual(
|
|
b"//asset.png",
|
|
BlendPath.mkrelative(
|
|
Path("Q:/path/to/asset.png"),
|
|
PureWindowsPath("Q:/path/to/bfile.blend"),
|
|
),
|
|
)
|
|
self.assertEqual(
|
|
b"//to/asset.png",
|
|
BlendPath.mkrelative(
|
|
Path("Q:/path/to/asset.png"),
|
|
PureWindowsPath("Q:/path/bfile.blend"),
|
|
),
|
|
)
|
|
self.assertEqual(
|
|
b"//../of/asset.png",
|
|
BlendPath.mkrelative(
|
|
Path("Q:/path/of/asset.png"),
|
|
PureWindowsPath("Q:/path/to/bfile.blend"),
|
|
),
|
|
)
|
|
self.assertEqual(
|
|
b"//../../path/of/asset.png",
|
|
BlendPath.mkrelative(
|
|
Path("Q:/path/of/asset.png"),
|
|
PureWindowsPath("Q:/some/weird/bfile.blend"),
|
|
),
|
|
)
|
|
self.assertEqual(
|
|
b"//very/very/very/very/very/deep/asset.png",
|
|
BlendPath.mkrelative(
|
|
Path("Q:/path/to/very/very/very/very/very/deep/asset.png"),
|
|
PureWindowsPath("Q:/path/to/bfile.blend"),
|
|
),
|
|
)
|
|
self.assertEqual(
|
|
b"//../../../../../../../../shallow/asset.png",
|
|
BlendPath.mkrelative(
|
|
Path("Q:/shallow/asset.png"),
|
|
PureWindowsPath("Q:/path/to/very/very/very/very/very/deep/bfile.blend"),
|
|
),
|
|
)
|
|
self.assertEqual(
|
|
b"D:/path/to/asset.png",
|
|
BlendPath.mkrelative(
|
|
Path("D:/path/to/asset.png"),
|
|
PureWindowsPath("Q:/path/to/bfile.blend"),
|
|
),
|
|
)
|
|
|
|
|
|
class MakeAbsoluteTest(unittest.TestCase):
|
|
def test_relative(self):
|
|
my_dir = Path(__file__).absolute().parent
|
|
cwd = os.getcwd()
|
|
try:
|
|
os.chdir(my_dir)
|
|
self.assertEqual(
|
|
my_dir / "blendfiles/Cube.btx",
|
|
make_absolute(Path("blendfiles/Cube.btx")),
|
|
)
|
|
except Exception:
|
|
os.chdir(cwd)
|
|
raise
|
|
|
|
@unittest.skipIf(platform.system() != "Windows", "This test uses drive letters")
|
|
def test_relative_drive(self):
|
|
cwd = os.getcwd()
|
|
my_drive = Path(f"{Path(cwd).drive}/")
|
|
self.assertEqual(
|
|
my_drive / "blendfiles/Cube.btx",
|
|
make_absolute(Path("/blendfiles/Cube.btx")),
|
|
)
|
|
|
|
def test_drive_letters(self):
|
|
"""PureWindowsPath should be accepted and work well on POSIX systems too."""
|
|
in_path = PureWindowsPath("R:/wrongroot/oops/../../path/to/a/file")
|
|
expect_path = Path("R:/path/to/a/file")
|
|
self.assertNotEqual(
|
|
expect_path, in_path, "pathlib should not automatically resolve ../"
|
|
)
|
|
self.assertEqual(expect_path, make_absolute(in_path))
|
|
|
|
@unittest.skipIf(platform.system() == "Windows", "This test ignores drive letters")
|
|
def test_dotdot_dotdot_posix(self):
|
|
in_path = Path("/wrongroot/oops/../../path/to/a/file")
|
|
expect_path = Path("/path/to/a/file")
|
|
self.assertNotEqual(
|
|
expect_path, in_path, "pathlib should not automatically resolve ../"
|
|
)
|
|
self.assertEqual(expect_path, make_absolute(in_path))
|
|
|
|
@unittest.skipIf(platform.system() != "Windows", "This test uses drive letters")
|
|
def test_dotdot_dotdot_windows(self):
|
|
in_path = Path("Q:/wrongroot/oops/../../path/to/a/file")
|
|
expect_path = Path("Q:/path/to/a/file")
|
|
self.assertNotEqual(
|
|
expect_path, in_path, "pathlib should not automatically resolve ../"
|
|
)
|
|
self.assertEqual(expect_path, make_absolute(in_path))
|
|
|
|
@unittest.skipIf(platform.system() == "Windows", "This test ignores drive letters")
|
|
def test_way_too_many_dotdot_posix(self):
|
|
in_path = Path("/webroot/../../../../../etc/passwd")
|
|
expect_path = Path("/etc/passwd")
|
|
self.assertEqual(expect_path, make_absolute(in_path))
|
|
|
|
@unittest.skipIf(platform.system() != "Windows", "This test uses drive letters")
|
|
def test_way_too_many_dotdot_windows(self):
|
|
in_path = Path("G:/webroot/../../../../../etc/passwd")
|
|
expect_path = Path("G:/etc/passwd")
|
|
self.assertEqual(expect_path, make_absolute(in_path))
|
|
|
|
@unittest.skipIf(
|
|
platform.system() == "Windows",
|
|
"Symlinks on Windows require Administrator rights",
|
|
)
|
|
def test_symlinks(self):
|
|
with tempfile.TemporaryDirectory(suffix="-bat-symlink-test") as tmpdir_str:
|
|
tmpdir = Path(tmpdir_str)
|
|
|
|
orig_path = tmpdir / "some_file.txt"
|
|
with orig_path.open("w") as outfile:
|
|
outfile.write("this file exists now")
|
|
|
|
symlink = tmpdir / "subdir" / "linked.txt"
|
|
symlink.parent.mkdir()
|
|
symlink.symlink_to(orig_path)
|
|
|
|
self.assertEqual(
|
|
symlink, make_absolute(symlink), "Symlinks should not be resolved"
|
|
)
|
|
|
|
@unittest.skipIf(
|
|
platform.system() != "Windows",
|
|
"Drive letters mapped to network share can only be tested on Windows",
|
|
)
|
|
@unittest.skip(
|
|
"Mapped drive letter testing should be mocked, but that is hard to do"
|
|
)
|
|
def test_mapped_drive_letters(self):
|
|
pass
|
|
|
|
def test_path_types(self):
|
|
platorm_path = type(PurePath())
|
|
self.assertIsInstance(
|
|
make_absolute(PureWindowsPath("/some/path")), platorm_path
|
|
)
|
|
self.assertIsInstance(make_absolute(PurePosixPath("/some/path")), platorm_path)
|
|
|
|
|
|
class StripRootTest(unittest.TestCase):
|
|
def test_windows_paths(self):
|
|
self.assertEqual(PurePosixPath(), strip_root(PureWindowsPath()))
|
|
self.assertEqual(
|
|
PurePosixPath("C/Program Files/Blender"),
|
|
strip_root(PureWindowsPath("C:/Program Files/Blender")),
|
|
)
|
|
self.assertEqual(
|
|
PurePosixPath("C/Program Files/Blender"),
|
|
strip_root(PureWindowsPath("C:\\Program Files\\Blender")),
|
|
)
|
|
self.assertEqual(
|
|
PurePosixPath("C/Program Files/Blender"),
|
|
strip_root(PureWindowsPath("C\\Program Files\\Blender")),
|
|
)
|
|
|
|
def test_posix_paths(self):
|
|
self.assertEqual(PurePosixPath(), strip_root(PurePosixPath()))
|
|
self.assertEqual(
|
|
PurePosixPath("C/path/to/blender"),
|
|
strip_root(PurePosixPath("C:/path/to/blender")),
|
|
)
|
|
self.assertEqual(
|
|
PurePosixPath("C/path/to/blender"),
|
|
strip_root(PurePosixPath("C/path/to/blender")),
|
|
)
|
|
self.assertEqual(
|
|
PurePosixPath("C/path/to/blender"),
|
|
strip_root(PurePosixPath("/C/path/to/blender")),
|
|
)
|