Cleanup: reformat with Black
This commit is contained in:
@@ -24,14 +24,14 @@ import unittest
|
||||
from blender_asset_tracer import blendfile
|
||||
|
||||
logging.basicConfig(
|
||||
format='%(asctime)-15s %(levelname)8s %(name)s %(message)s',
|
||||
level=logging.INFO)
|
||||
format="%(asctime)-15s %(levelname)8s %(name)s %(message)s", level=logging.INFO
|
||||
)
|
||||
|
||||
|
||||
class AbstractBlendFileTest(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.blendfiles = pathlib.Path(__file__).with_name('blendfiles')
|
||||
cls.blendfiles = pathlib.Path(__file__).with_name("blendfiles")
|
||||
|
||||
def setUp(self):
|
||||
self.bf = None
|
||||
|
||||
+96
-83
@@ -8,66 +8,72 @@ from blender_asset_tracer.blendfile import dna, dna_io
|
||||
|
||||
class NameTest(unittest.TestCase):
|
||||
def test_simple_name(self):
|
||||
n = dna.Name(b'Suzanne')
|
||||
self.assertEqual(n.name_full, b'Suzanne')
|
||||
self.assertEqual(n.name_only, b'Suzanne')
|
||||
n = dna.Name(b"Suzanne")
|
||||
self.assertEqual(n.name_full, b"Suzanne")
|
||||
self.assertEqual(n.name_only, b"Suzanne")
|
||||
self.assertFalse(n.is_pointer)
|
||||
self.assertFalse(n.is_method_pointer)
|
||||
self.assertEqual(n.array_size, 1)
|
||||
|
||||
def test_pointer(self):
|
||||
n = dna.Name(b'*marker')
|
||||
self.assertEqual(n.name_full, b'*marker')
|
||||
self.assertEqual(n.name_only, b'marker')
|
||||
n = dna.Name(b"*marker")
|
||||
self.assertEqual(n.name_full, b"*marker")
|
||||
self.assertEqual(n.name_only, b"marker")
|
||||
self.assertTrue(n.is_pointer)
|
||||
self.assertFalse(n.is_method_pointer)
|
||||
self.assertEqual(n.array_size, 1)
|
||||
|
||||
def test_method_pointer(self):
|
||||
n = dna.Name(b'(*delta_cache)()')
|
||||
self.assertEqual(n.name_full, b'(*delta_cache)()')
|
||||
self.assertEqual(n.name_only, b'delta_cache')
|
||||
n = dna.Name(b"(*delta_cache)()")
|
||||
self.assertEqual(n.name_full, b"(*delta_cache)()")
|
||||
self.assertEqual(n.name_only, b"delta_cache")
|
||||
self.assertTrue(n.is_pointer)
|
||||
self.assertTrue(n.is_method_pointer)
|
||||
self.assertEqual(n.array_size, 1)
|
||||
|
||||
def test_simple_array(self):
|
||||
n = dna.Name(b'flame_smoke_color[3]')
|
||||
self.assertEqual(n.name_full, b'flame_smoke_color[3]')
|
||||
self.assertEqual(n.name_only, b'flame_smoke_color')
|
||||
n = dna.Name(b"flame_smoke_color[3]")
|
||||
self.assertEqual(n.name_full, b"flame_smoke_color[3]")
|
||||
self.assertEqual(n.name_only, b"flame_smoke_color")
|
||||
self.assertFalse(n.is_pointer)
|
||||
self.assertFalse(n.is_method_pointer)
|
||||
self.assertEqual(n.array_size, 3)
|
||||
|
||||
def test_nested_array(self):
|
||||
n = dna.Name(b'pattern_corners[4][2]')
|
||||
self.assertEqual(n.name_full, b'pattern_corners[4][2]')
|
||||
self.assertEqual(n.name_only, b'pattern_corners')
|
||||
n = dna.Name(b"pattern_corners[4][2]")
|
||||
self.assertEqual(n.name_full, b"pattern_corners[4][2]")
|
||||
self.assertEqual(n.name_only, b"pattern_corners")
|
||||
self.assertFalse(n.is_pointer)
|
||||
self.assertFalse(n.is_method_pointer)
|
||||
self.assertEqual(n.array_size, 8)
|
||||
|
||||
def test_pointer_array(self):
|
||||
n = dna.Name(b'*mtex[18]')
|
||||
self.assertEqual(n.name_full, b'*mtex[18]')
|
||||
self.assertEqual(n.name_only, b'mtex')
|
||||
n = dna.Name(b"*mtex[18]")
|
||||
self.assertEqual(n.name_full, b"*mtex[18]")
|
||||
self.assertEqual(n.name_only, b"mtex")
|
||||
self.assertTrue(n.is_pointer)
|
||||
self.assertFalse(n.is_method_pointer)
|
||||
self.assertEqual(n.array_size, 18)
|
||||
|
||||
def test_repr(self):
|
||||
self.assertEqual(repr(dna.Name(b'Suzanne')), "Name(b'Suzanne')")
|
||||
self.assertEqual(repr(dna.Name(b'*marker')), "Name(b'*marker')")
|
||||
self.assertEqual(repr(dna.Name(b'(*delta_cache)()')), "Name(b'(*delta_cache)()')")
|
||||
self.assertEqual(repr(dna.Name(b'flame_smoke_color[3]')), "Name(b'flame_smoke_color[3]')")
|
||||
self.assertEqual(repr(dna.Name(b'pattern_corners[4][2]')), "Name(b'pattern_corners[4][2]')")
|
||||
self.assertEqual(repr(dna.Name(b'*mtex[18]')), "Name(b'*mtex[18]')")
|
||||
self.assertEqual(repr(dna.Name(b"Suzanne")), "Name(b'Suzanne')")
|
||||
self.assertEqual(repr(dna.Name(b"*marker")), "Name(b'*marker')")
|
||||
self.assertEqual(
|
||||
repr(dna.Name(b"(*delta_cache)()")), "Name(b'(*delta_cache)()')"
|
||||
)
|
||||
self.assertEqual(
|
||||
repr(dna.Name(b"flame_smoke_color[3]")), "Name(b'flame_smoke_color[3]')"
|
||||
)
|
||||
self.assertEqual(
|
||||
repr(dna.Name(b"pattern_corners[4][2]")), "Name(b'pattern_corners[4][2]')"
|
||||
)
|
||||
self.assertEqual(repr(dna.Name(b"*mtex[18]")), "Name(b'*mtex[18]')")
|
||||
|
||||
def test_as_reference(self):
|
||||
n = dna.Name(b'(*delta_cache)()')
|
||||
self.assertEqual(n.as_reference(None), b'delta_cache')
|
||||
self.assertEqual(n.as_reference(b''), b'delta_cache')
|
||||
self.assertEqual(n.as_reference(b'parent'), b'parent.delta_cache')
|
||||
n = dna.Name(b"(*delta_cache)()")
|
||||
self.assertEqual(n.as_reference(None), b"delta_cache")
|
||||
self.assertEqual(n.as_reference(b""), b"delta_cache")
|
||||
self.assertEqual(n.as_reference(b"parent"), b"parent.delta_cache")
|
||||
|
||||
|
||||
class StructTest(unittest.TestCase):
|
||||
@@ -76,20 +82,20 @@ class StructTest(unittest.TestCase):
|
||||
endian = dna_io.BigEndianTypes
|
||||
|
||||
def setUp(self):
|
||||
self.s = dna.Struct(b'AlembicObjectPath')
|
||||
self.s_char = dna.Struct(b'char', 1)
|
||||
self.s_float = dna.Struct(b'float', 4)
|
||||
self.s_uint64 = dna.Struct(b'uint64_t', 8)
|
||||
self.s_uint128 = dna.Struct(b'uint128_t', 16) # non-supported type
|
||||
self.s = dna.Struct(b"AlembicObjectPath")
|
||||
self.s_char = dna.Struct(b"char", 1)
|
||||
self.s_float = dna.Struct(b"float", 4)
|
||||
self.s_uint64 = dna.Struct(b"uint64_t", 8)
|
||||
self.s_uint128 = dna.Struct(b"uint128_t", 16) # non-supported type
|
||||
|
||||
self.f_next = dna.Field(self.s, dna.Name(b'*next'), 8, 0)
|
||||
self.f_prev = dna.Field(self.s, dna.Name(b'*prev'), 8, 8)
|
||||
self.f_path = dna.Field(self.s_char, dna.Name(b'path[4096]'), 4096, 16)
|
||||
self.f_pointer = dna.Field(self.s_char, dna.Name(b'*ptr'), 3 * 8, 4112)
|
||||
self.f_number = dna.Field(self.s_uint64, dna.Name(b'numbah'), 8, 4136)
|
||||
self.f_floaty = dna.Field(self.s_float, dna.Name(b'floaty[2]'), 2 * 4, 4144)
|
||||
self.f_flag = dna.Field(self.s_char, dna.Name(b'bitflag'), 1, 4152)
|
||||
self.f_bignum = dna.Field(self.s_uint128, dna.Name(b'bignum'), 16, 4153)
|
||||
self.f_next = dna.Field(self.s, dna.Name(b"*next"), 8, 0)
|
||||
self.f_prev = dna.Field(self.s, dna.Name(b"*prev"), 8, 8)
|
||||
self.f_path = dna.Field(self.s_char, dna.Name(b"path[4096]"), 4096, 16)
|
||||
self.f_pointer = dna.Field(self.s_char, dna.Name(b"*ptr"), 3 * 8, 4112)
|
||||
self.f_number = dna.Field(self.s_uint64, dna.Name(b"numbah"), 8, 4136)
|
||||
self.f_floaty = dna.Field(self.s_float, dna.Name(b"floaty[2]"), 2 * 4, 4144)
|
||||
self.f_flag = dna.Field(self.s_char, dna.Name(b"bitflag"), 1, 4152)
|
||||
self.f_bignum = dna.Field(self.s_uint128, dna.Name(b"bignum"), 16, 4153)
|
||||
|
||||
self.s.append_field(self.f_next)
|
||||
self.s.append_field(self.f_prev)
|
||||
@@ -108,13 +114,13 @@ class StructTest(unittest.TestCase):
|
||||
# the size property is explicitly set anyway. The situation we
|
||||
# test here is for manually created Struct instances that don't
|
||||
# have any fields.
|
||||
dna.Struct(b'EmptyStruct').size
|
||||
dna.Struct(b"EmptyStruct").size
|
||||
|
||||
# Create AlebicObjectPath as it is actually used in Blender 2.79a
|
||||
s = dna.Struct(b'AlembicObjectPath')
|
||||
f_next = dna.Field(s, dna.Name(b'*next'), 8, 0)
|
||||
f_prev = dna.Field(s, dna.Name(b'*prev'), 8, 8)
|
||||
f_path = dna.Field(self.s_char, dna.Name(b'path[4096]'), 4096, 16)
|
||||
s = dna.Struct(b"AlembicObjectPath")
|
||||
f_next = dna.Field(s, dna.Name(b"*next"), 8, 0)
|
||||
f_prev = dna.Field(s, dna.Name(b"*prev"), 8, 8)
|
||||
f_path = dna.Field(self.s_char, dna.Name(b"path[4096]"), 4096, 16)
|
||||
s.append_field(f_next)
|
||||
s.append_field(f_prev)
|
||||
s.append_field(f_path)
|
||||
@@ -123,36 +129,42 @@ class StructTest(unittest.TestCase):
|
||||
|
||||
def test_field_from_path(self):
|
||||
psize = 8
|
||||
self.assertEqual(self.s.field_from_path(psize, b'path'),
|
||||
(self.f_path, 16))
|
||||
self.assertEqual(self.s.field_from_path(psize, (b'prev', b'path')),
|
||||
(self.f_path, 24))
|
||||
self.assertEqual(self.s.field_from_path(psize, (b'ptr', 2)),
|
||||
(self.f_pointer, 16 + 4096 + 2 * psize))
|
||||
self.assertEqual(self.s.field_from_path(psize, (b'floaty', 1)),
|
||||
(self.f_floaty, 4144 + self.s_float.size))
|
||||
self.assertEqual(self.s.field_from_path(psize, b"path"), (self.f_path, 16))
|
||||
self.assertEqual(
|
||||
self.s.field_from_path(psize, (b"prev", b"path")), (self.f_path, 24)
|
||||
)
|
||||
self.assertEqual(
|
||||
self.s.field_from_path(psize, (b"ptr", 2)),
|
||||
(self.f_pointer, 16 + 4096 + 2 * psize),
|
||||
)
|
||||
self.assertEqual(
|
||||
self.s.field_from_path(psize, (b"floaty", 1)),
|
||||
(self.f_floaty, 4144 + self.s_float.size),
|
||||
)
|
||||
|
||||
with self.assertRaises(OverflowError):
|
||||
self.s.field_from_path(psize, (b'floaty', 2))
|
||||
self.s.field_from_path(psize, (b"floaty", 2))
|
||||
|
||||
with self.assertRaises(KeyError):
|
||||
self.s.field_from_path(psize, b'non-existant')
|
||||
self.s.field_from_path(psize, b"non-existant")
|
||||
|
||||
with self.assertRaises(TypeError):
|
||||
self.s.field_from_path(psize, 'path')
|
||||
self.s.field_from_path(psize, "path")
|
||||
|
||||
def test_simple_field_get(self):
|
||||
fileobj = mock.MagicMock(io.BufferedReader)
|
||||
fileobj.read.return_value = b'\x01\x02\x03\x04\xff\xfe\xfd\xfa'
|
||||
_, val = self.s.field_get(self.FakeHeader(), fileobj, b'numbah')
|
||||
fileobj.read.return_value = b"\x01\x02\x03\x04\xff\xfe\xfd\xfa"
|
||||
_, val = self.s.field_get(self.FakeHeader(), fileobj, b"numbah")
|
||||
|
||||
self.assertEqual(val, 0x1020304fffefdfa)
|
||||
self.assertEqual(val, 0x1020304FFFEFDFA)
|
||||
fileobj.seek.assert_called_with(4136, os.SEEK_CUR)
|
||||
|
||||
def test_field_get_default(self):
|
||||
fileobj = mock.MagicMock(io.BufferedReader)
|
||||
fileobj.read.side_effect = RuntimeError
|
||||
_, val = self.s.field_get(self.FakeHeader(), fileobj, b'nonexistant', default=519871531)
|
||||
_, val = self.s.field_get(
|
||||
self.FakeHeader(), fileobj, b"nonexistant", default=519871531
|
||||
)
|
||||
|
||||
self.assertEqual(val, 519871531)
|
||||
fileobj.seek.assert_not_called()
|
||||
@@ -162,7 +174,7 @@ class StructTest(unittest.TestCase):
|
||||
fileobj.read.side_effect = RuntimeError
|
||||
|
||||
with self.assertRaises(KeyError):
|
||||
self.s.field_get(self.FakeHeader(), fileobj, b'nonexistant')
|
||||
self.s.field_get(self.FakeHeader(), fileobj, b"nonexistant")
|
||||
fileobj.seek.assert_not_called()
|
||||
|
||||
def test_field_get_unsupported_type(self):
|
||||
@@ -170,62 +182,63 @@ class StructTest(unittest.TestCase):
|
||||
fileobj.read.side_effect = RuntimeError
|
||||
|
||||
with self.assertRaises(NotImplementedError):
|
||||
self.s.field_get(self.FakeHeader(), fileobj, b'bignum')
|
||||
self.s.field_get(self.FakeHeader(), fileobj, b"bignum")
|
||||
fileobj.seek.assert_called_with(4153, os.SEEK_CUR)
|
||||
|
||||
def test_pointer_field_get(self):
|
||||
fileobj = mock.MagicMock(io.BufferedReader)
|
||||
fileobj.read.return_value = b'\xf0\x9f\xa6\x87\x00dum'
|
||||
_, val = self.s.field_get(self.FakeHeader(), fileobj, b'ptr')
|
||||
fileobj.read.return_value = b"\xf0\x9f\xa6\x87\x00dum"
|
||||
_, val = self.s.field_get(self.FakeHeader(), fileobj, b"ptr")
|
||||
|
||||
self.assertEqual(0xf09fa6870064756d, val)
|
||||
self.assertEqual(0xF09FA6870064756D, val)
|
||||
fileobj.seek.assert_called_with(4112, os.SEEK_CUR)
|
||||
|
||||
def test_string_field_get(self):
|
||||
fileobj = mock.MagicMock(io.BufferedReader)
|
||||
fileobj.read.return_value = b'\xf0\x9f\xa6\x87\x00dummydata'
|
||||
_, val = self.s.field_get(self.FakeHeader(), fileobj, b'path', as_str=True)
|
||||
fileobj.read.return_value = b"\xf0\x9f\xa6\x87\x00dummydata"
|
||||
_, val = self.s.field_get(self.FakeHeader(), fileobj, b"path", as_str=True)
|
||||
|
||||
self.assertEqual('🦇', val)
|
||||
self.assertEqual("🦇", val)
|
||||
fileobj.seek.assert_called_with(16, os.SEEK_CUR)
|
||||
|
||||
def test_string_field_get_single_char(self):
|
||||
fileobj = mock.MagicMock(io.BufferedReader)
|
||||
fileobj.read.return_value = b'\xf0'
|
||||
_, val = self.s.field_get(self.FakeHeader(), fileobj, b'bitflag')
|
||||
fileobj.read.return_value = b"\xf0"
|
||||
_, val = self.s.field_get(self.FakeHeader(), fileobj, b"bitflag")
|
||||
|
||||
self.assertEqual(0xf0, val)
|
||||
self.assertEqual(0xF0, val)
|
||||
fileobj.seek.assert_called_with(4152, os.SEEK_CUR)
|
||||
|
||||
def test_string_field_get_invalid_utf8(self):
|
||||
fileobj = mock.MagicMock(io.BufferedReader)
|
||||
fileobj.read.return_value = b'\x01\x02\x03\x04\xff\xfe\xfd\xfa'
|
||||
fileobj.read.return_value = b"\x01\x02\x03\x04\xff\xfe\xfd\xfa"
|
||||
|
||||
with self.assertRaises(UnicodeDecodeError):
|
||||
self.s.field_get(self.FakeHeader(), fileobj, b'path', as_str=True)
|
||||
self.s.field_get(self.FakeHeader(), fileobj, b"path", as_str=True)
|
||||
|
||||
def test_string_field_get_bytes_null_terminated(self):
|
||||
fileobj = mock.MagicMock(io.BufferedReader)
|
||||
fileobj.read.return_value = b'\x01\x02\x03\x04\xff\xfe\xfd\xfa\x00dummydata'
|
||||
fileobj.read.return_value = b"\x01\x02\x03\x04\xff\xfe\xfd\xfa\x00dummydata"
|
||||
|
||||
_, val = self.s.field_get(self.FakeHeader(), fileobj, b'path', as_str=False)
|
||||
self.assertEqual(b'\x01\x02\x03\x04\xff\xfe\xfd\xfa', val)
|
||||
_, val = self.s.field_get(self.FakeHeader(), fileobj, b"path", as_str=False)
|
||||
self.assertEqual(b"\x01\x02\x03\x04\xff\xfe\xfd\xfa", val)
|
||||
fileobj.seek.assert_called_with(16, os.SEEK_CUR)
|
||||
|
||||
def test_string_field_get_bytes(self):
|
||||
fileobj = mock.MagicMock(io.BufferedReader)
|
||||
fileobj.read.return_value = b'\x01\x02\x03\x04\xff\xfe\xfd\xfa\x00dummydata'
|
||||
fileobj.read.return_value = b"\x01\x02\x03\x04\xff\xfe\xfd\xfa\x00dummydata"
|
||||
|
||||
_, val = self.s.field_get(self.FakeHeader(), fileobj, b'path',
|
||||
as_str=False, null_terminated=False)
|
||||
self.assertEqual(b'\x01\x02\x03\x04\xff\xfe\xfd\xfa\x00dummydata', val)
|
||||
_, val = self.s.field_get(
|
||||
self.FakeHeader(), fileobj, b"path", as_str=False, null_terminated=False
|
||||
)
|
||||
self.assertEqual(b"\x01\x02\x03\x04\xff\xfe\xfd\xfa\x00dummydata", val)
|
||||
fileobj.seek.assert_called_with(16, os.SEEK_CUR)
|
||||
|
||||
def test_string_field_get_float_array(self):
|
||||
fileobj = mock.MagicMock(io.BufferedReader)
|
||||
fileobj.read.side_effect = (b'@333', b'@2\x8f\\')
|
||||
fileobj.read.side_effect = (b"@333", b"@2\x8f\\")
|
||||
|
||||
_, val = self.s.field_get(self.FakeHeader(), fileobj, b'floaty')
|
||||
_, val = self.s.field_get(self.FakeHeader(), fileobj, b"floaty")
|
||||
self.assertAlmostEqual(2.8, val[0])
|
||||
self.assertAlmostEqual(2.79, val[1])
|
||||
fileobj.seek.assert_called_with(4144, os.SEEK_CUR)
|
||||
|
||||
@@ -9,16 +9,16 @@ class StringTest(unittest.TestCase):
|
||||
fileobj = mock.Mock()
|
||||
# Sinhala for 'beer'. This is exactly 15 bytes in UTF-8, so the last
|
||||
# character won't fit in the field (due to the 0-byte required).
|
||||
dna_io.BigEndianTypes.write_string(fileobj, 'බියර්', 15)
|
||||
dna_io.BigEndianTypes.write_string(fileobj, "බියර්", 15)
|
||||
|
||||
expect_bytes = ('බියර්'[:-1]).encode('utf8') + b'\0'
|
||||
expect_bytes = ("බියර්"[:-1]).encode("utf8") + b"\0"
|
||||
fileobj.write.assert_called_with(expect_bytes)
|
||||
|
||||
def test_utf8(self):
|
||||
fileobj = mock.Mock()
|
||||
# Sinhala for 'beer'. This is exactly 15 bytes in UTF-8,
|
||||
# so with the 0-byte it just fits.
|
||||
dna_io.BigEndianTypes.write_string(fileobj, 'බියර්', 16)
|
||||
dna_io.BigEndianTypes.write_string(fileobj, "බියර්", 16)
|
||||
|
||||
expect_bytes = 'බියර්'.encode('utf8') + b'\0'
|
||||
expect_bytes = "බියර්".encode("utf8") + b"\0"
|
||||
fileobj.write.assert_called_with(expect_bytes)
|
||||
|
||||
+140
-127
@@ -9,64 +9,66 @@ from tests.abstract_test import AbstractBlendFileTest
|
||||
|
||||
class BlendFileBlockTest(AbstractBlendFileTest):
|
||||
def setUp(self):
|
||||
self.bf = blendfile.BlendFile(self.blendfiles / 'basic_file.blend')
|
||||
self.bf = blendfile.BlendFile(self.blendfiles / "basic_file.blend")
|
||||
|
||||
def test_loading(self):
|
||||
self.assertFalse(self.bf.is_compressed)
|
||||
|
||||
def test_some_properties(self):
|
||||
ob = self.bf.code_index[b'OB'][0]
|
||||
self.assertEqual('Object', ob.dna_type_name)
|
||||
ob = self.bf.code_index[b"OB"][0]
|
||||
self.assertEqual("Object", ob.dna_type_name)
|
||||
|
||||
# Try low level operation to read a property.
|
||||
self.bf.fileobj.seek(ob.file_offset, os.SEEK_SET)
|
||||
_, loc = ob.dna_type.field_get(self.bf.header, self.bf.fileobj, b'loc')
|
||||
_, loc = ob.dna_type.field_get(self.bf.header, self.bf.fileobj, b"loc")
|
||||
self.assertEqual([2.0, 3.0, 5.0], loc)
|
||||
|
||||
# Try low level operation to read an array element.
|
||||
self.bf.fileobj.seek(ob.file_offset, os.SEEK_SET)
|
||||
_, loc_z = ob.dna_type.field_get(self.bf.header, self.bf.fileobj, (b'loc', 2))
|
||||
_, loc_z = ob.dna_type.field_get(self.bf.header, self.bf.fileobj, (b"loc", 2))
|
||||
self.assertEqual(5.0, loc_z)
|
||||
|
||||
# Try high level operation to read the same property.
|
||||
loc = ob.get(b'loc')
|
||||
loc = ob.get(b"loc")
|
||||
self.assertEqual([2.0, 3.0, 5.0], loc)
|
||||
|
||||
# Try getting a subproperty.
|
||||
name = ob.get((b'id', b'name'), as_str=True)
|
||||
self.assertEqual('OBümlaut', name)
|
||||
name = ob.get((b"id", b"name"), as_str=True)
|
||||
self.assertEqual("OBümlaut", name)
|
||||
|
||||
loc_z = ob.get((b'loc', 2))
|
||||
loc_z = ob.get((b"loc", 2))
|
||||
self.assertEqual(5.0, loc_z)
|
||||
|
||||
# Try following a pointer.
|
||||
mesh_ptr = ob.get(b'data')
|
||||
mesh_ptr = ob.get(b"data")
|
||||
mesh = self.bf.block_from_addr[mesh_ptr]
|
||||
mname = mesh.get((b'id', b'name'), as_str=True)
|
||||
self.assertEqual('MECube³', mname)
|
||||
mname = mesh.get((b"id", b"name"), as_str=True)
|
||||
self.assertEqual("MECube³", mname)
|
||||
|
||||
def test_get_recursive_iter(self):
|
||||
ob = self.bf.code_index[b'OB'][0]
|
||||
ob = self.bf.code_index[b"OB"][0]
|
||||
assert isinstance(ob, blendfile.BlendFileBlock)
|
||||
|
||||
# No recursing, just an array property.
|
||||
gen = ob.get_recursive_iter(b'loc')
|
||||
self.assertEqual([(b'loc', [2.0, 3.0, 5.0])], list(gen))
|
||||
gen = ob.get_recursive_iter(b"loc")
|
||||
self.assertEqual([(b"loc", [2.0, 3.0, 5.0])], list(gen))
|
||||
|
||||
# Recurse into an object
|
||||
gen = ob.get_recursive_iter(b'id')
|
||||
gen = ob.get_recursive_iter(b"id")
|
||||
self.assertEqual(
|
||||
[((b'id', b'next'), 0),
|
||||
((b'id', b'prev'), 0),
|
||||
((b'id', b'newid'), 0),
|
||||
((b'id', b'lib'), 0),
|
||||
((b'id', b'name'), 'OBümlaut'),
|
||||
((b'id', b'flag'), 0),
|
||||
],
|
||||
list(gen)[:6])
|
||||
[
|
||||
((b"id", b"next"), 0),
|
||||
((b"id", b"prev"), 0),
|
||||
((b"id", b"newid"), 0),
|
||||
((b"id", b"lib"), 0),
|
||||
((b"id", b"name"), "OBümlaut"),
|
||||
((b"id", b"flag"), 0),
|
||||
],
|
||||
list(gen)[:6],
|
||||
)
|
||||
|
||||
def test_iter_recursive(self):
|
||||
ob = self.bf.code_index[b'OB'][0]
|
||||
ob = self.bf.code_index[b"OB"][0]
|
||||
assert isinstance(ob, blendfile.BlendFileBlock)
|
||||
|
||||
# We can't test all of them in a reliable way, but it shouldn't crash.
|
||||
@@ -74,17 +76,19 @@ class BlendFileBlockTest(AbstractBlendFileTest):
|
||||
|
||||
# And we can check the first few items.
|
||||
self.assertEqual(
|
||||
[((b'id', b'next'), 0),
|
||||
((b'id', b'prev'), 0),
|
||||
((b'id', b'newid'), 0),
|
||||
((b'id', b'lib'), 0),
|
||||
((b'id', b'name'),
|
||||
b'OB\xc3\xbcmlaut'),
|
||||
((b'id', b'flag'), 0),
|
||||
], all_items[:6])
|
||||
[
|
||||
((b"id", b"next"), 0),
|
||||
((b"id", b"prev"), 0),
|
||||
((b"id", b"newid"), 0),
|
||||
((b"id", b"lib"), 0),
|
||||
((b"id", b"name"), b"OB\xc3\xbcmlaut"),
|
||||
((b"id", b"flag"), 0),
|
||||
],
|
||||
all_items[:6],
|
||||
)
|
||||
|
||||
def test_items(self):
|
||||
ma = self.bf.code_index[b'MA'][0]
|
||||
ma = self.bf.code_index[b"MA"][0]
|
||||
assert isinstance(ma, blendfile.BlendFileBlock)
|
||||
|
||||
# We can't test all of them in a reliable way, but it shouldn't crash.
|
||||
@@ -92,18 +96,21 @@ class BlendFileBlockTest(AbstractBlendFileTest):
|
||||
|
||||
# And we can check the first few items.
|
||||
self.assertEqual(
|
||||
[(b'id', '<ID>'), # not recursed into.
|
||||
(b'adt', 0),
|
||||
(b'material_type', 0),
|
||||
(b'flag', 0),
|
||||
(b'r', 0.8000000715255737),
|
||||
(b'g', 0.03218378871679306),
|
||||
(b'b', 0.36836329102516174),
|
||||
(b'specr', 1.0)],
|
||||
all_items[:8])
|
||||
[
|
||||
(b"id", "<ID>"), # not recursed into.
|
||||
(b"adt", 0),
|
||||
(b"material_type", 0),
|
||||
(b"flag", 0),
|
||||
(b"r", 0.8000000715255737),
|
||||
(b"g", 0.03218378871679306),
|
||||
(b"b", 0.36836329102516174),
|
||||
(b"specr", 1.0),
|
||||
],
|
||||
all_items[:8],
|
||||
)
|
||||
|
||||
def test_keys(self):
|
||||
ma = self.bf.code_index[b'MA'][0]
|
||||
ma = self.bf.code_index[b"MA"][0]
|
||||
assert isinstance(ma, blendfile.BlendFileBlock)
|
||||
|
||||
# We can't test all of them in a reliable way, but it shouldn't crash.
|
||||
@@ -111,11 +118,12 @@ class BlendFileBlockTest(AbstractBlendFileTest):
|
||||
|
||||
# And we can check the first few items.
|
||||
self.assertEqual(
|
||||
[b'id', b'adt', b'material_type', b'flag', b'r', b'g', b'b', b'specr'],
|
||||
all_keys[:8])
|
||||
[b"id", b"adt", b"material_type", b"flag", b"r", b"g", b"b", b"specr"],
|
||||
all_keys[:8],
|
||||
)
|
||||
|
||||
def test_values(self):
|
||||
ma = self.bf.code_index[b'MA'][0]
|
||||
ma = self.bf.code_index[b"MA"][0]
|
||||
assert isinstance(ma, blendfile.BlendFileBlock)
|
||||
|
||||
# We can't test all of them in a reliable way, but it shouldn't crash.
|
||||
@@ -123,151 +131,156 @@ class BlendFileBlockTest(AbstractBlendFileTest):
|
||||
|
||||
# And we can check the first few items.
|
||||
self.assertEqual(
|
||||
['<ID>',
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.8000000715255737,
|
||||
0.03218378871679306,
|
||||
0.36836329102516174,
|
||||
1.0],
|
||||
all_values[:8])
|
||||
[
|
||||
"<ID>",
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.8000000715255737,
|
||||
0.03218378871679306,
|
||||
0.36836329102516174,
|
||||
1.0,
|
||||
],
|
||||
all_values[:8],
|
||||
)
|
||||
|
||||
def test_get_via_dict_interface(self):
|
||||
ma = self.bf.code_index[b'MA'][0]
|
||||
ma = self.bf.code_index[b"MA"][0]
|
||||
assert isinstance(ma, blendfile.BlendFileBlock)
|
||||
self.assertAlmostEqual(0.8000000715255737, ma[b'r'])
|
||||
self.assertAlmostEqual(0.8000000715255737, ma[b"r"])
|
||||
|
||||
ob = self.bf.code_index[b'OB'][0]
|
||||
ob = self.bf.code_index[b"OB"][0]
|
||||
assert isinstance(ob, blendfile.BlendFileBlock)
|
||||
self.assertEqual('OBümlaut', ob.id_name.decode())
|
||||
self.assertEqual("OBümlaut", ob.id_name.decode())
|
||||
|
||||
|
||||
class PointerTest(AbstractBlendFileTest):
|
||||
def setUp(self):
|
||||
self.bf = blendfile.BlendFile(self.blendfiles / 'with_sequencer.blend')
|
||||
self.bf = blendfile.BlendFile(self.blendfiles / "with_sequencer.blend")
|
||||
|
||||
def test_get_pointer_and_listbase(self):
|
||||
scenes = self.bf.code_index[b'SC']
|
||||
self.assertEqual(1, len(scenes), 'expecting 1 scene')
|
||||
scenes = self.bf.code_index[b"SC"]
|
||||
self.assertEqual(1, len(scenes), "expecting 1 scene")
|
||||
scene = scenes[0]
|
||||
self.assertEqual(b'SCScene', scene.id_name)
|
||||
self.assertEqual(b"SCScene", scene.id_name)
|
||||
|
||||
ed_ptr = scene[b'ed']
|
||||
ed_ptr = scene[b"ed"]
|
||||
self.assertEqual(140051431100936, ed_ptr)
|
||||
|
||||
ed = scene.get_pointer(b'ed')
|
||||
ed = scene.get_pointer(b"ed")
|
||||
self.assertEqual(140051431100936, ed.addr_old)
|
||||
|
||||
seqbase = ed.get_pointer((b'seqbase', b'first'))
|
||||
seqbase = ed.get_pointer((b"seqbase", b"first"))
|
||||
self.assertIsNotNone(seqbase)
|
||||
|
||||
types = {
|
||||
b'SQBlack': 28,
|
||||
b'SQCross': 8,
|
||||
b'SQPink': 28,
|
||||
b"SQBlack": 28,
|
||||
b"SQCross": 8,
|
||||
b"SQPink": 28,
|
||||
}
|
||||
seq = None
|
||||
for seq in iterators.listbase(seqbase):
|
||||
seq.refine_type(b'Sequence')
|
||||
name = seq[b'name']
|
||||
seq.refine_type(b"Sequence")
|
||||
name = seq[b"name"]
|
||||
expected_type = types[name]
|
||||
self.assertEqual(expected_type, seq[b'type'])
|
||||
self.assertEqual(expected_type, seq[b"type"])
|
||||
|
||||
# The last 'seq' from the loop should be the last in the list.
|
||||
seq_next = seq.get_pointer(b'next')
|
||||
seq_next = seq.get_pointer(b"next")
|
||||
self.assertIsNone(seq_next)
|
||||
|
||||
def test_refine_sdna_by_name(self):
|
||||
scene = self.bf.code_index[b'SC'][0]
|
||||
ed = scene.get_pointer(b'ed')
|
||||
scene = self.bf.code_index[b"SC"][0]
|
||||
ed = scene.get_pointer(b"ed")
|
||||
|
||||
seq = ed.get_pointer((b'seqbase', b'first'))
|
||||
seq = ed.get_pointer((b"seqbase", b"first"))
|
||||
|
||||
seq.refine_type(b'Sequence')
|
||||
self.assertEqual(b'SQBlack', seq[b'name'])
|
||||
self.assertEqual(28, seq[b'type'])
|
||||
seq.refine_type(b"Sequence")
|
||||
self.assertEqual(b"SQBlack", seq[b"name"])
|
||||
self.assertEqual(28, seq[b"type"])
|
||||
|
||||
def test_refine_sdna_by_idx(self):
|
||||
scene = self.bf.code_index[b'SC'][0]
|
||||
ed = scene.get_pointer(b'ed')
|
||||
seq = ed.get_pointer((b'seqbase', b'first'))
|
||||
scene = self.bf.code_index[b"SC"][0]
|
||||
ed = scene.get_pointer(b"ed")
|
||||
seq = ed.get_pointer((b"seqbase", b"first"))
|
||||
|
||||
sdna_idx_sequence = self.bf.sdna_index_from_id[b'Sequence']
|
||||
sdna_idx_sequence = self.bf.sdna_index_from_id[b"Sequence"]
|
||||
seq.refine_type_from_index(sdna_idx_sequence)
|
||||
self.assertEqual(b'SQBlack', seq[b'name'])
|
||||
self.assertEqual(28, seq[b'type'])
|
||||
self.assertEqual(b"SQBlack", seq[b"name"])
|
||||
self.assertEqual(28, seq[b"type"])
|
||||
|
||||
def test_segfault(self):
|
||||
scene = self.bf.code_index[b'SC'][0]
|
||||
ed_ptr = scene.get(b'ed')
|
||||
scene = self.bf.code_index[b"SC"][0]
|
||||
ed_ptr = scene.get(b"ed")
|
||||
del self.bf.block_from_addr[ed_ptr]
|
||||
|
||||
with self.assertRaises(exceptions.SegmentationFault):
|
||||
scene.get_pointer(b'ed')
|
||||
scene.get_pointer(b"ed")
|
||||
|
||||
def test_abs_offset(self):
|
||||
scene = self.bf.code_index[b'SC'][0]
|
||||
ed = scene.get_pointer(b'ed')
|
||||
scene = self.bf.code_index[b"SC"][0]
|
||||
ed = scene.get_pointer(b"ed")
|
||||
assert isinstance(ed, blendfile.BlendFileBlock)
|
||||
|
||||
abs_offset, field_size = ed.abs_offset((b'seqbase', b'first'))
|
||||
abs_offset, field_size = ed.abs_offset((b"seqbase", b"first"))
|
||||
self.assertEqual(ed.file_offset + 8, abs_offset)
|
||||
self.assertEqual(1, field_size)
|
||||
|
||||
|
||||
class ArrayTest(AbstractBlendFileTest):
|
||||
def test_array_of_pointers(self):
|
||||
self.bf = blendfile.BlendFile(self.blendfiles / 'multiple_materials.blend')
|
||||
mesh = self.bf.code_index[b'ME'][0]
|
||||
self.bf = blendfile.BlendFile(self.blendfiles / "multiple_materials.blend")
|
||||
mesh = self.bf.code_index[b"ME"][0]
|
||||
assert isinstance(mesh, blendfile.BlendFileBlock)
|
||||
|
||||
material_count = mesh[b'totcol']
|
||||
material_count = mesh[b"totcol"]
|
||||
self.assertEqual(4, material_count)
|
||||
|
||||
for i, material in enumerate(mesh.iter_array_of_pointers(b'mat', material_count)):
|
||||
for i, material in enumerate(
|
||||
mesh.iter_array_of_pointers(b"mat", material_count)
|
||||
):
|
||||
if i == 0:
|
||||
name = b'MAMaterial.000'
|
||||
name = b"MAMaterial.000"
|
||||
elif i in {1, 3}:
|
||||
name = b'MAMaterial.001'
|
||||
name = b"MAMaterial.001"
|
||||
else:
|
||||
name = b'MAMaterial.002'
|
||||
name = b"MAMaterial.002"
|
||||
self.assertEqual(name, material.id_name)
|
||||
|
||||
def test_array_of_lamp_textures(self):
|
||||
self.bf = blendfile.BlendFile(self.blendfiles / 'lamp_textures.blend')
|
||||
lamp = self.bf.code_index[b'LA'][0]
|
||||
self.bf = blendfile.BlendFile(self.blendfiles / "lamp_textures.blend")
|
||||
lamp = self.bf.code_index[b"LA"][0]
|
||||
assert isinstance(lamp, blendfile.BlendFileBlock)
|
||||
|
||||
mtex0 = lamp.get_pointer(b'mtex')
|
||||
tex = mtex0.get_pointer(b'tex')
|
||||
self.assertEqual(b'TE', tex.code)
|
||||
self.assertEqual(b'TEClouds', tex.id_name)
|
||||
mtex0 = lamp.get_pointer(b"mtex")
|
||||
tex = mtex0.get_pointer(b"tex")
|
||||
self.assertEqual(b"TE", tex.code)
|
||||
self.assertEqual(b"TEClouds", tex.id_name)
|
||||
|
||||
for i, mtex in enumerate(lamp.iter_fixed_array_of_pointers(b'mtex')):
|
||||
for i, mtex in enumerate(lamp.iter_fixed_array_of_pointers(b"mtex")):
|
||||
if i == 0:
|
||||
name = b'TEClouds'
|
||||
name = b"TEClouds"
|
||||
elif i == 1:
|
||||
name = b'TEVoronoi'
|
||||
name = b"TEVoronoi"
|
||||
else:
|
||||
self.fail('Too many textures reported: %r' % mtex)
|
||||
self.fail("Too many textures reported: %r" % mtex)
|
||||
|
||||
tex = mtex.get_pointer(b'tex')
|
||||
self.assertEqual(b'TE', tex.code)
|
||||
tex = mtex.get_pointer(b"tex")
|
||||
self.assertEqual(b"TE", tex.code)
|
||||
self.assertEqual(name, tex.id_name)
|
||||
|
||||
|
||||
class LoadCompressedTest(AbstractBlendFileTest):
|
||||
def test_loading(self):
|
||||
self.bf = blendfile.BlendFile(self.blendfiles / 'basic_file_compressed.blend')
|
||||
self.bf = blendfile.BlendFile(self.blendfiles / "basic_file_compressed.blend")
|
||||
self.assertTrue(self.bf.is_compressed)
|
||||
|
||||
ob = self.bf.code_index[b'OB'][0]
|
||||
name = ob.get((b'id', b'name'), as_str=True)
|
||||
self.assertEqual('OBümlaut', name)
|
||||
ob = self.bf.code_index[b"OB"][0]
|
||||
name = ob.get((b"id", b"name"), as_str=True)
|
||||
self.assertEqual("OBümlaut", name)
|
||||
|
||||
def test_as_context(self):
|
||||
with blendfile.BlendFile(self.blendfiles / 'basic_file_compressed.blend') as bf:
|
||||
with blendfile.BlendFile(self.blendfiles / "basic_file_compressed.blend") as bf:
|
||||
filepath = bf.filepath
|
||||
raw_filepath = bf.raw_filepath
|
||||
|
||||
@@ -283,7 +296,7 @@ class LoadNonBlendfileTest(AbstractBlendFileTest):
|
||||
|
||||
def test_no_datablocks(self):
|
||||
with self.assertRaises(exceptions.NoDNA1Block):
|
||||
blendfile.BlendFile(self.blendfiles / 'corrupt_only_magic.blend')
|
||||
blendfile.BlendFile(self.blendfiles / "corrupt_only_magic.blend")
|
||||
|
||||
|
||||
class BlendFileCacheTest(AbstractBlendFileTest):
|
||||
@@ -297,7 +310,7 @@ class BlendFileCacheTest(AbstractBlendFileTest):
|
||||
self.tdir.cleanup()
|
||||
|
||||
def test_open_cached(self):
|
||||
infile = self.blendfiles / 'basic_file.blend'
|
||||
infile = self.blendfiles / "basic_file.blend"
|
||||
bf1 = blendfile.open_cached(infile)
|
||||
bf2 = blendfile.open_cached(infile)
|
||||
|
||||
@@ -306,7 +319,7 @@ class BlendFileCacheTest(AbstractBlendFileTest):
|
||||
self.assertIs(bf1, blendfile._cached_bfiles[infile])
|
||||
|
||||
def test_compressed(self):
|
||||
infile = self.blendfiles / 'linked_cube_compressed.blend'
|
||||
infile = self.blendfiles / "linked_cube_compressed.blend"
|
||||
bf1 = blendfile.open_cached(infile)
|
||||
bf2 = blendfile.open_cached(infile)
|
||||
|
||||
@@ -315,7 +328,7 @@ class BlendFileCacheTest(AbstractBlendFileTest):
|
||||
self.assertIs(bf1, blendfile._cached_bfiles[infile])
|
||||
|
||||
def test_closed(self):
|
||||
infile = self.blendfiles / 'linked_cube_compressed.blend'
|
||||
infile = self.blendfiles / "linked_cube_compressed.blend"
|
||||
bf = blendfile.open_cached(infile)
|
||||
self.assertIs(bf, blendfile._cached_bfiles[infile])
|
||||
|
||||
@@ -324,8 +337,8 @@ class BlendFileCacheTest(AbstractBlendFileTest):
|
||||
self.assertEqual({}, blendfile._cached_bfiles)
|
||||
|
||||
def test_close_one_file(self):
|
||||
path1 = self.blendfiles / 'linked_cube_compressed.blend'
|
||||
path2 = self.blendfiles / 'basic_file.blend'
|
||||
path1 = self.blendfiles / "linked_cube_compressed.blend"
|
||||
path2 = self.blendfiles / "basic_file.blend"
|
||||
bf1 = blendfile.open_cached(path1)
|
||||
bf2 = blendfile.open_cached(path2)
|
||||
self.assertIs(bf1, blendfile._cached_bfiles[path1])
|
||||
@@ -336,13 +349,13 @@ class BlendFileCacheTest(AbstractBlendFileTest):
|
||||
self.assertEqual({path2: bf2}, blendfile._cached_bfiles)
|
||||
|
||||
def test_open_and_rebind(self):
|
||||
infile = self.blendfiles / 'linked_cube.blend'
|
||||
other = self.tpath / 'copy.blend'
|
||||
infile = self.blendfiles / "linked_cube.blend"
|
||||
other = self.tpath / "copy.blend"
|
||||
self._open_and_rebind_test(infile, other)
|
||||
|
||||
def test_open_and_rebind_compressed(self):
|
||||
infile = self.blendfiles / 'linked_cube_compressed.blend'
|
||||
other = self.tpath / 'copy.blend'
|
||||
infile = self.blendfiles / "linked_cube_compressed.blend"
|
||||
other = self.tpath / "copy.blend"
|
||||
self._open_and_rebind_test(infile, other)
|
||||
|
||||
def _open_and_rebind_test(self, infile: pathlib.Path, other: pathlib.Path):
|
||||
@@ -357,7 +370,7 @@ class BlendFileCacheTest(AbstractBlendFileTest):
|
||||
before_blocks = bf.blocks
|
||||
before_compressed = bf.is_compressed
|
||||
|
||||
bf.copy_and_rebind(other, mode='rb+')
|
||||
bf.copy_and_rebind(other, mode="rb+")
|
||||
|
||||
self.assertTrue(other.exists())
|
||||
self.assertEqual(before_compressed, bf.is_compressed)
|
||||
|
||||
@@ -8,11 +8,13 @@ from tests.abstract_test import AbstractBlendFileTest
|
||||
|
||||
class ModifyUncompressedTest(AbstractBlendFileTest):
|
||||
def setUp(self):
|
||||
self.orig = self.blendfiles / 'linked_cube.blend'
|
||||
self.to_modify = self.orig.with_name('linked_cube_modified.blend')
|
||||
self.orig = self.blendfiles / "linked_cube.blend"
|
||||
self.to_modify = self.orig.with_name("linked_cube_modified.blend")
|
||||
|
||||
copyfile(str(self.orig), str(self.to_modify)) # TODO: when requiring Python 3.6+, remove str()
|
||||
self.bf = blendfile.BlendFile(self.to_modify, mode='r+b')
|
||||
copyfile(
|
||||
str(self.orig), str(self.to_modify)
|
||||
) # TODO: when requiring Python 3.6+, remove str()
|
||||
self.bf = blendfile.BlendFile(self.to_modify, mode="r+b")
|
||||
|
||||
self.assertFalse(self.bf.is_compressed)
|
||||
|
||||
@@ -22,20 +24,20 @@ class ModifyUncompressedTest(AbstractBlendFileTest):
|
||||
self.to_modify.unlink()
|
||||
|
||||
def test_change_path(self):
|
||||
library = self.bf.code_index[b'LI'][0]
|
||||
library = self.bf.code_index[b"LI"][0]
|
||||
|
||||
# Change it from absolute to relative.
|
||||
library[b'filepath'] = b'//basic_file.blend'
|
||||
library[b'name'] = b'//basic_file.blend'
|
||||
library[b"filepath"] = b"//basic_file.blend"
|
||||
library[b"name"] = b"//basic_file.blend"
|
||||
|
||||
self.reload()
|
||||
|
||||
library = self.bf.code_index[b'LI'][0]
|
||||
self.assertEqual(b'//basic_file.blend', library[b'filepath'])
|
||||
self.assertEqual(b'//basic_file.blend', library[b'name'])
|
||||
library = self.bf.code_index[b"LI"][0]
|
||||
self.assertEqual(b"//basic_file.blend", library[b"filepath"])
|
||||
self.assertEqual(b"//basic_file.blend", library[b"name"])
|
||||
|
||||
def test_block_hash(self):
|
||||
scene = self.bf.code_index[b'SC'][0]
|
||||
scene = self.bf.code_index[b"SC"][0]
|
||||
assert isinstance(scene, blendfile.BlendFileBlock)
|
||||
|
||||
pre_hash = scene.hash()
|
||||
@@ -43,28 +45,30 @@ class ModifyUncompressedTest(AbstractBlendFileTest):
|
||||
|
||||
# Change the 'ed' pointer to some arbitrary value by hacking the blend file.
|
||||
psize = self.bf.header.pointer_size
|
||||
field, field_offset = scene.dna_type.field_from_path(psize, b'ed')
|
||||
field, field_offset = scene.dna_type.field_from_path(psize, b"ed")
|
||||
self.bf.fileobj.seek(scene.file_offset + field_offset, os.SEEK_SET)
|
||||
self.bf.fileobj.write(b'12345678'[:psize])
|
||||
self.bf.fileobj.write(b"12345678"[:psize])
|
||||
|
||||
self.reload()
|
||||
|
||||
scene = self.bf.code_index[b'SC'][0]
|
||||
scene = self.bf.code_index[b"SC"][0]
|
||||
post_hash = scene.hash()
|
||||
self.assertEqual(pre_hash, post_hash)
|
||||
|
||||
def reload(self):
|
||||
self.bf.close()
|
||||
self.bf = blendfile.BlendFile(self.to_modify, mode='r+b')
|
||||
self.bf = blendfile.BlendFile(self.to_modify, mode="r+b")
|
||||
|
||||
|
||||
class ModifyCompressedTest(AbstractBlendFileTest):
|
||||
def setUp(self):
|
||||
self.orig = self.blendfiles / 'linked_cube_compressed.blend'
|
||||
self.to_modify = self.orig.with_name('linked_cube_modified.blend')
|
||||
self.orig = self.blendfiles / "linked_cube_compressed.blend"
|
||||
self.to_modify = self.orig.with_name("linked_cube_modified.blend")
|
||||
|
||||
copyfile(str(self.orig), str(self.to_modify)) # TODO: when requiring Python 3.6+, remove str()
|
||||
self.bf = blendfile.BlendFile(self.to_modify, mode='r+b')
|
||||
copyfile(
|
||||
str(self.orig), str(self.to_modify)
|
||||
) # TODO: when requiring Python 3.6+, remove str()
|
||||
self.bf = blendfile.BlendFile(self.to_modify, mode="r+b")
|
||||
|
||||
self.assertTrue(self.bf.is_compressed)
|
||||
|
||||
@@ -73,16 +77,16 @@ class ModifyCompressedTest(AbstractBlendFileTest):
|
||||
self.to_modify.unlink()
|
||||
|
||||
def test_change_path(self):
|
||||
library = self.bf.code_index[b'LI'][0]
|
||||
library = self.bf.code_index[b"LI"][0]
|
||||
|
||||
# Change it from absolute to relative.
|
||||
library[b'filepath'] = b'//basic_file.blend'
|
||||
library[b'name'] = b'//basic_file.blend'
|
||||
library[b"filepath"] = b"//basic_file.blend"
|
||||
library[b"name"] = b"//basic_file.blend"
|
||||
|
||||
# Reload the blend file to inspect that it was written properly.
|
||||
self.bf.close()
|
||||
self.bf = blendfile.BlendFile(self.to_modify, mode='r+b')
|
||||
self.bf = blendfile.BlendFile(self.to_modify, mode="r+b")
|
||||
|
||||
library = self.bf.code_index[b'LI'][0]
|
||||
self.assertEqual(b'//basic_file.blend', library[b'filepath'])
|
||||
self.assertEqual(b'//basic_file.blend', library[b'name'])
|
||||
library = self.bf.code_index[b"LI"][0]
|
||||
self.assertEqual(b"//basic_file.blend", library[b"filepath"])
|
||||
self.assertEqual(b"//basic_file.blend", library[b"name"])
|
||||
|
||||
+230
-143
@@ -10,140 +10,201 @@ 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(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)
|
||||
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')
|
||||
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')
|
||||
p = BlendPath(b"//some/file.blend")
|
||||
self.assertEqual("BlendPath(b'//some/file.blend')", repr(p))
|
||||
p = BlendPath(PurePosixPath('//some/file.blend'))
|
||||
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())
|
||||
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'
|
||||
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())
|
||||
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()
|
||||
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())
|
||||
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())
|
||||
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'))
|
||||
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')
|
||||
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'
|
||||
BlendPath(b"/root/and") / b"/parent.blend"
|
||||
|
||||
self.assertEqual(b'/root/and/parent.blend', b'/root/and' / BlendPath(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')
|
||||
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')
|
||||
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")
|
||||
@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'),
|
||||
))
|
||||
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")
|
||||
@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'),
|
||||
))
|
||||
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):
|
||||
@@ -152,101 +213,127 @@ class MakeAbsoluteTest(unittest.TestCase):
|
||||
cwd = os.getcwd()
|
||||
try:
|
||||
os.chdir(my_dir)
|
||||
self.assertEqual(my_dir / 'blendfiles/Cube.btx',
|
||||
make_absolute(Path('blendfiles/Cube.btx')))
|
||||
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")
|
||||
@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')))
|
||||
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 ../')
|
||||
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")
|
||||
@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 ../')
|
||||
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")
|
||||
@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 ../')
|
||||
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")
|
||||
@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')
|
||||
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")
|
||||
@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')
|
||||
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")
|
||||
@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')
|
||||
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 = tmpdir / "subdir" / "linked.txt"
|
||||
symlink.parent.mkdir()
|
||||
symlink.symlink_to(orig_path)
|
||||
|
||||
self.assertEqual(symlink, make_absolute(symlink), 'Symlinks should not be resolved')
|
||||
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')
|
||||
@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)
|
||||
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')))
|
||||
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')))
|
||||
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')))
|
||||
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')))
|
||||
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')))
|
||||
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')))
|
||||
PurePosixPath("C/path/to/blender"),
|
||||
strip_root(PurePosixPath("/C/path/to/blender")),
|
||||
)
|
||||
|
||||
+16
-14
@@ -12,8 +12,8 @@ class CompressorTest(AbstractBlendFileTest):
|
||||
def setUp(self):
|
||||
self.temp = tempfile.TemporaryDirectory()
|
||||
tempdir = pathlib.Path(self.temp.name)
|
||||
self.srcdir = tempdir / 'src'
|
||||
self.destdir = tempdir / 'dest'
|
||||
self.srcdir = tempdir / "src"
|
||||
self.destdir = tempdir / "dest"
|
||||
|
||||
self.srcdir.mkdir()
|
||||
self.destdir.mkdir()
|
||||
@@ -43,31 +43,33 @@ class CompressorTest(AbstractBlendFileTest):
|
||||
self.assertEqual(source_must_remain, srcfile.exists())
|
||||
self.assertTrue(destfile.exists())
|
||||
|
||||
if destfile.suffix == '.blend':
|
||||
if destfile.suffix == ".blend":
|
||||
self.bf = blendfile.BlendFile(destfile)
|
||||
self.assertTrue(self.bf.is_compressed)
|
||||
return
|
||||
|
||||
with destfile.open('rb') as infile:
|
||||
with destfile.open("rb") as infile:
|
||||
magic = infile.read(3)
|
||||
if destfile.suffix == '.jpg':
|
||||
self.assertEqual(b'\xFF\xD8\xFF', magic,
|
||||
'Expected %s to be a JPEG' % destfile)
|
||||
if destfile.suffix == ".jpg":
|
||||
self.assertEqual(
|
||||
b"\xFF\xD8\xFF", magic, "Expected %s to be a JPEG" % destfile
|
||||
)
|
||||
else:
|
||||
self.assertNotEqual(b'\x1f\x8b', magic[:2],
|
||||
'Expected %s to be NOT compressed' % destfile)
|
||||
self.assertNotEqual(
|
||||
b"\x1f\x8b", magic[:2], "Expected %s to be NOT compressed" % destfile
|
||||
)
|
||||
|
||||
def test_move_already_compressed(self):
|
||||
self._test('basic_file_ñønæščii.blend', False)
|
||||
self._test("basic_file_ñønæščii.blend", False)
|
||||
|
||||
def test_move_compress_on_the_fly(self):
|
||||
self._test('basic_file.blend', False)
|
||||
self._test("basic_file.blend", False)
|
||||
|
||||
def test_copy_already_compressed(self):
|
||||
self._test('basic_file_ñønæščii.blend', True)
|
||||
self._test("basic_file_ñønæščii.blend", True)
|
||||
|
||||
def test_copy_compress_on_the_fly(self):
|
||||
self._test('basic_file.blend', True)
|
||||
self._test("basic_file.blend", True)
|
||||
|
||||
def test_move_jpeg(self):
|
||||
self._test('textures/Bricks/brick_dotted_04-color.jpg', False)
|
||||
self._test("textures/Bricks/brick_dotted_04-color.jpg", False)
|
||||
|
||||
+5
-5
@@ -13,11 +13,11 @@ class MypyRunnerTest(unittest.TestCase):
|
||||
# /path/to/blender-asset-tracer/.tox/py37/lib/python3.7/site-packages is in the PYTHONPATH.
|
||||
# Please change directory so it is not.
|
||||
for path in sys.path:
|
||||
if '/.tox/' in path and path.endswith('/site-packages'):
|
||||
if "/.tox/" in path and path.endswith("/site-packages"):
|
||||
self.skipTest("Mypy doesn't like Tox")
|
||||
|
||||
path = pathlib.Path(blender_asset_tracer.__file__).parent
|
||||
result = mypy.api.run(['--incremental', '--ignore-missing-imports', str(path)])
|
||||
result = mypy.api.run(["--incremental", "--ignore-missing-imports", str(path)])
|
||||
|
||||
stdout, stderr, status = result
|
||||
|
||||
@@ -27,6 +27,6 @@ class MypyRunnerTest(unittest.TestCase):
|
||||
if stdout:
|
||||
messages.append(stdout)
|
||||
if status:
|
||||
messages.append('Mypy failed with status %d' % status)
|
||||
if messages and not all(msg.startswith('Success: ') for msg in messages):
|
||||
self.fail('\n'.join(['Mypy errors:'] + messages))
|
||||
messages.append("Mypy failed with status %d" % status)
|
||||
if messages and not all(msg.startswith("Success: ") for msg in messages):
|
||||
self.fail("\n".join(["Mypy errors:"] + messages))
|
||||
|
||||
+254
-183
@@ -17,15 +17,21 @@ class AbstractPackTest(AbstractBlendFileTest):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
logging.getLogger('blender_asset_tracer.compressor').setLevel(logging.DEBUG)
|
||||
logging.getLogger('blender_asset_tracer.pack').setLevel(logging.DEBUG)
|
||||
logging.getLogger('blender_asset_tracer.blendfile.open_cached').setLevel(logging.DEBUG)
|
||||
logging.getLogger('blender_asset_tracer.blendfile.open_cached').setLevel(logging.DEBUG)
|
||||
logging.getLogger('blender_asset_tracer.blendfile.BlendFile').setLevel(logging.DEBUG)
|
||||
logging.getLogger("blender_asset_tracer.compressor").setLevel(logging.DEBUG)
|
||||
logging.getLogger("blender_asset_tracer.pack").setLevel(logging.DEBUG)
|
||||
logging.getLogger("blender_asset_tracer.blendfile.open_cached").setLevel(
|
||||
logging.DEBUG
|
||||
)
|
||||
logging.getLogger("blender_asset_tracer.blendfile.open_cached").setLevel(
|
||||
logging.DEBUG
|
||||
)
|
||||
logging.getLogger("blender_asset_tracer.blendfile.BlendFile").setLevel(
|
||||
logging.DEBUG
|
||||
)
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.tdir = tempfile.TemporaryDirectory(suffix='-packtest')
|
||||
self.tdir = tempfile.TemporaryDirectory(suffix="-packtest")
|
||||
self.tpath = Path(self.tdir.name)
|
||||
|
||||
def tearDown(self):
|
||||
@@ -34,120 +40,135 @@ class AbstractPackTest(AbstractBlendFileTest):
|
||||
|
||||
@staticmethod
|
||||
def rewrites(packer: pack.Packer):
|
||||
return {path: action.rewrites
|
||||
for path, action in packer._actions.items()
|
||||
if action.rewrites}
|
||||
return {
|
||||
path: action.rewrites
|
||||
for path, action in packer._actions.items()
|
||||
if action.rewrites
|
||||
}
|
||||
|
||||
def outside_project(self) -> Path:
|
||||
"""Return the '_outside_project' path for files in self.blendfiles."""
|
||||
# /tmp/target + /workspace/bat/tests/blendfiles → /tmp/target/workspace/bat/tests/blendfiles
|
||||
# /tmp/target + C:\workspace\bat\tests\blendfiles → /tmp/target/C/workspace/bat/tests/blendfiles
|
||||
extpath = Path(self.tpath, '_outside_project', bpathlib.strip_root(self.blendfiles))
|
||||
extpath = Path(
|
||||
self.tpath, "_outside_project", bpathlib.strip_root(self.blendfiles)
|
||||
)
|
||||
return extpath
|
||||
|
||||
|
||||
class PackTest(AbstractPackTest):
|
||||
def test_strategise_no_rewrite_required(self):
|
||||
infile = self.blendfiles / 'doubly_linked.blend'
|
||||
infile = self.blendfiles / "doubly_linked.blend"
|
||||
|
||||
packer = pack.Packer(infile, self.blendfiles, self.tpath)
|
||||
packer.strategise()
|
||||
|
||||
packed_files = (
|
||||
'doubly_linked.blend',
|
||||
'linked_cube.blend',
|
||||
'basic_file.blend',
|
||||
'material_textures.blend',
|
||||
'textures/Bricks/brick_dotted_04-bump.jpg',
|
||||
'textures/Bricks/brick_dotted_04-color.jpg',
|
||||
"doubly_linked.blend",
|
||||
"linked_cube.blend",
|
||||
"basic_file.blend",
|
||||
"material_textures.blend",
|
||||
"textures/Bricks/brick_dotted_04-bump.jpg",
|
||||
"textures/Bricks/brick_dotted_04-color.jpg",
|
||||
)
|
||||
for pf in packed_files:
|
||||
path = self.blendfiles / pf
|
||||
act = packer._actions[path]
|
||||
self.assertEqual(pack.PathAction.KEEP_PATH, act.path_action, 'for %s' % pf)
|
||||
self.assertEqual(self.tpath / pf, act.new_path, 'for %s' % pf)
|
||||
self.assertEqual(pack.PathAction.KEEP_PATH, act.path_action, "for %s" % pf)
|
||||
self.assertEqual(self.tpath / pf, act.new_path, "for %s" % pf)
|
||||
|
||||
self.assertEqual({}, self.rewrites(packer))
|
||||
self.assertEqual(len(packed_files), len(packer._actions))
|
||||
|
||||
def test_strategise_rewrite(self):
|
||||
ppath = self.blendfiles / 'subdir'
|
||||
infile = ppath / 'doubly_linked_up-windows.blend'
|
||||
ppath = self.blendfiles / "subdir"
|
||||
infile = ppath / "doubly_linked_up-windows.blend"
|
||||
|
||||
packer = pack.Packer(infile, ppath, self.tpath)
|
||||
packer.strategise()
|
||||
|
||||
external_files = (
|
||||
'linked_cube.blend',
|
||||
'basic_file.blend',
|
||||
'material_textures.blend',
|
||||
'textures/Bricks/brick_dotted_04-bump.jpg',
|
||||
'textures/Bricks/brick_dotted_04-color.jpg',
|
||||
"linked_cube.blend",
|
||||
"basic_file.blend",
|
||||
"material_textures.blend",
|
||||
"textures/Bricks/brick_dotted_04-bump.jpg",
|
||||
"textures/Bricks/brick_dotted_04-color.jpg",
|
||||
)
|
||||
extpath = self.outside_project()
|
||||
|
||||
act = packer._actions[infile]
|
||||
self.assertEqual(pack.PathAction.KEEP_PATH, act.path_action, 'for %s' % infile.name)
|
||||
self.assertEqual(self.tpath / infile.name, act.new_path, 'for %s' % infile.name)
|
||||
self.assertEqual(
|
||||
pack.PathAction.KEEP_PATH, act.path_action, "for %s" % infile.name
|
||||
)
|
||||
self.assertEqual(self.tpath / infile.name, act.new_path, "for %s" % infile.name)
|
||||
for fn in external_files:
|
||||
path = self.blendfiles / fn
|
||||
act = packer._actions[path]
|
||||
self.assertEqual(pack.PathAction.FIND_NEW_LOCATION, act.path_action, 'for %s' % fn)
|
||||
self.assertEqual(extpath / fn, act.new_path,
|
||||
f'\nEXPECT: {extpath / fn}\nACTUAL: {act.new_path}\nfor {fn}')
|
||||
self.assertEqual(
|
||||
pack.PathAction.FIND_NEW_LOCATION, act.path_action, "for %s" % fn
|
||||
)
|
||||
self.assertEqual(
|
||||
extpath / fn,
|
||||
act.new_path,
|
||||
f"\nEXPECT: {extpath / fn}\nACTUAL: {act.new_path}\nfor {fn}",
|
||||
)
|
||||
|
||||
to_rewrite = (
|
||||
'linked_cube.blend',
|
||||
'material_textures.blend',
|
||||
"linked_cube.blend",
|
||||
"material_textures.blend",
|
||||
str(infile.relative_to(self.blendfiles)),
|
||||
)
|
||||
rewrites = self.rewrites(packer)
|
||||
self.assertEqual([self.blendfiles / fn for fn in to_rewrite],
|
||||
sorted(rewrites.keys()))
|
||||
self.assertEqual(
|
||||
[self.blendfiles / fn for fn in to_rewrite], sorted(rewrites.keys())
|
||||
)
|
||||
|
||||
# Library link referencing basic_file.blend should (maybe) be rewritten.
|
||||
rw_linked_cube = rewrites[self.blendfiles / 'linked_cube.blend']
|
||||
rw_linked_cube = rewrites[self.blendfiles / "linked_cube.blend"]
|
||||
self.assertEqual(1, len(rw_linked_cube))
|
||||
self.assertEqual(b'LILib', rw_linked_cube[0].block_name)
|
||||
self.assertEqual(b'//basic_file.blend', rw_linked_cube[0].asset_path)
|
||||
self.assertEqual(b"LILib", rw_linked_cube[0].block_name)
|
||||
self.assertEqual(b"//basic_file.blend", rw_linked_cube[0].asset_path)
|
||||
|
||||
# Texture links to image assets should (maybe) be rewritten.
|
||||
rw_mattex = rewrites[self.blendfiles / 'material_textures.blend']
|
||||
rw_mattex = rewrites[self.blendfiles / "material_textures.blend"]
|
||||
self.assertEqual(2, len(rw_mattex))
|
||||
rw_mattex.sort() # for repeatable tests
|
||||
self.assertEqual(b'IMbrick_dotted_04-bump', rw_mattex[0].block_name)
|
||||
self.assertEqual(b'//textures/Bricks/brick_dotted_04-bump.jpg', rw_mattex[0].asset_path)
|
||||
self.assertEqual(b'IMbrick_dotted_04-color', rw_mattex[1].block_name)
|
||||
self.assertEqual(b'//textures/Bricks/brick_dotted_04-color.jpg', rw_mattex[1].asset_path)
|
||||
self.assertEqual(b"IMbrick_dotted_04-bump", rw_mattex[0].block_name)
|
||||
self.assertEqual(
|
||||
b"//textures/Bricks/brick_dotted_04-bump.jpg", rw_mattex[0].asset_path
|
||||
)
|
||||
self.assertEqual(b"IMbrick_dotted_04-color", rw_mattex[1].block_name)
|
||||
self.assertEqual(
|
||||
b"//textures/Bricks/brick_dotted_04-color.jpg", rw_mattex[1].asset_path
|
||||
)
|
||||
|
||||
# Library links from doubly_linked_up.blend to the above to blend files should be rewritten.
|
||||
rw_dbllink = rewrites[infile]
|
||||
self.assertEqual(2, len(rw_dbllink))
|
||||
rw_dbllink.sort() # for repeatable tests
|
||||
self.assertEqual(b'LILib', rw_dbllink[0].block_name)
|
||||
self.assertEqual(b'//../linked_cube.blend', rw_dbllink[0].asset_path)
|
||||
self.assertEqual(b'LILib.002', rw_dbllink[1].block_name)
|
||||
self.assertEqual(b'//../material_textures.blend', rw_dbllink[1].asset_path)
|
||||
self.assertEqual(b"LILib", rw_dbllink[0].block_name)
|
||||
self.assertEqual(b"//../linked_cube.blend", rw_dbllink[0].asset_path)
|
||||
self.assertEqual(b"LILib.002", rw_dbllink[1].block_name)
|
||||
self.assertEqual(b"//../material_textures.blend", rw_dbllink[1].asset_path)
|
||||
|
||||
def test_strategise_relative_only(self):
|
||||
infile = self.blendfiles / 'absolute_path.blend'
|
||||
infile = self.blendfiles / "absolute_path.blend"
|
||||
|
||||
packer = pack.Packer(infile, self.blendfiles, self.tpath,
|
||||
relative_only=True)
|
||||
packer = pack.Packer(infile, self.blendfiles, self.tpath, relative_only=True)
|
||||
packer.strategise()
|
||||
|
||||
packed_files = (
|
||||
'absolute_path.blend',
|
||||
"absolute_path.blend",
|
||||
# Linked with a relative path:
|
||||
'textures/Bricks/brick_dotted_04-color.jpg',
|
||||
"textures/Bricks/brick_dotted_04-color.jpg",
|
||||
# This file links to textures/Textures/Buildings/buildings_roof_04-color.png,
|
||||
# but using an absolute path, so that file should be skipped.
|
||||
)
|
||||
for pf in packed_files:
|
||||
path = self.blendfiles / pf
|
||||
act = packer._actions[path]
|
||||
self.assertEqual(pack.PathAction.KEEP_PATH, act.path_action, 'for %s' % pf)
|
||||
self.assertEqual(self.tpath / pf, act.new_path, 'for %s' % pf)
|
||||
self.assertEqual(pack.PathAction.KEEP_PATH, act.path_action, "for %s" % pf)
|
||||
self.assertEqual(self.tpath / pf, act.new_path, "for %s" % pf)
|
||||
|
||||
self.assertEqual(len(packed_files), len(packer._actions))
|
||||
self.assertEqual({}, self.rewrites(packer))
|
||||
@@ -157,31 +178,33 @@ class PackTest(AbstractPackTest):
|
||||
|
||||
# The original file shouldn't be touched.
|
||||
bfile = blendfile.open_cached(infile, assert_cached=False)
|
||||
libs = sorted(bfile.code_index[b'LI'])
|
||||
libs = sorted(bfile.code_index[b"LI"])
|
||||
|
||||
self.assertEqual(b'LILib', libs[0].id_name)
|
||||
self.assertEqual(b'//../linked_cube.blend', libs[0][b'name'])
|
||||
self.assertEqual(b'LILib.002', libs[1].id_name)
|
||||
self.assertEqual(b'//../material_textures.blend', libs[1][b'name'])
|
||||
self.assertEqual(b"LILib", libs[0].id_name)
|
||||
self.assertEqual(b"//../linked_cube.blend", libs[0][b"name"])
|
||||
self.assertEqual(b"LILib.002", libs[1].id_name)
|
||||
self.assertEqual(b"//../material_textures.blend", libs[1][b"name"])
|
||||
|
||||
def test_execute_rewrite(self):
|
||||
infile, _ = self._pack_with_rewrite()
|
||||
|
||||
if platform.system() == 'Windows':
|
||||
extpath = PurePosixPath('//_outside_project',
|
||||
self.blendfiles.drive[0],
|
||||
*self.blendfiles.parts[1:])
|
||||
if platform.system() == "Windows":
|
||||
extpath = PurePosixPath(
|
||||
"//_outside_project",
|
||||
self.blendfiles.drive[0],
|
||||
*self.blendfiles.parts[1:],
|
||||
)
|
||||
else:
|
||||
extpath = PurePosixPath('//_outside_project', *self.blendfiles.parts[1:])
|
||||
extpath = PurePosixPath("//_outside_project", *self.blendfiles.parts[1:])
|
||||
extbpath = bpathlib.BlendPath(extpath)
|
||||
|
||||
# Those libraries should be properly rewritten.
|
||||
bfile = blendfile.open_cached(self.tpath / infile.name, assert_cached=False)
|
||||
libs = sorted(bfile.code_index[b'LI'])
|
||||
self.assertEqual(b'LILib', libs[0].id_name)
|
||||
self.assertEqual(extbpath / b'linked_cube.blend', libs[0][b'name'])
|
||||
self.assertEqual(b'LILib.002', libs[1].id_name)
|
||||
self.assertEqual(extbpath / b'material_textures.blend', libs[1][b'name'])
|
||||
libs = sorted(bfile.code_index[b"LI"])
|
||||
self.assertEqual(b"LILib", libs[0].id_name)
|
||||
self.assertEqual(extbpath / b"linked_cube.blend", libs[0][b"name"])
|
||||
self.assertEqual(b"LILib.002", libs[1].id_name)
|
||||
self.assertEqual(extbpath / b"material_textures.blend", libs[1][b"name"])
|
||||
|
||||
def test_execute_rewrite_cleanup(self):
|
||||
infile, packer = self._pack_with_rewrite()
|
||||
@@ -195,8 +218,10 @@ class PackTest(AbstractPackTest):
|
||||
packer.close()
|
||||
self.assertFalse(packer._rewrite_in.exists())
|
||||
|
||||
@unittest.skipIf(platform.system() == 'Windows',
|
||||
"Symlinks on Windows require Administrator rights")
|
||||
@unittest.skipIf(
|
||||
platform.system() == "Windows",
|
||||
"Symlinks on Windows require Administrator rights",
|
||||
)
|
||||
def test_symlinked_files(self):
|
||||
"""Test that symlinks are NOT resolved.
|
||||
|
||||
@@ -206,7 +231,7 @@ class PackTest(AbstractPackTest):
|
||||
As a concrete example, a directory structure with only symlinked files
|
||||
in it should still be BAT-packable and produce the same structure.
|
||||
"""
|
||||
orig_ppath = self.blendfiles / 'subdir'
|
||||
orig_ppath = self.blendfiles / "subdir"
|
||||
|
||||
# This is the original structure when packing subdir/doubly_linked_up.blend:
|
||||
# .
|
||||
@@ -225,12 +250,12 @@ class PackTest(AbstractPackTest):
|
||||
# should be no different than packing the originals.
|
||||
|
||||
orig_paths = [
|
||||
Path('basic_file.blend'),
|
||||
Path('linked_cube.blend'),
|
||||
Path('material_textures.blend'),
|
||||
Path('subdir/doubly_linked_up.blend'),
|
||||
Path('textures/Bricks/brick_dotted_04-bump.jpg'),
|
||||
Path('textures/Bricks/brick_dotted_04-color.jpg'),
|
||||
Path("basic_file.blend"),
|
||||
Path("linked_cube.blend"),
|
||||
Path("material_textures.blend"),
|
||||
Path("subdir/doubly_linked_up.blend"),
|
||||
Path("textures/Bricks/brick_dotted_04-bump.jpg"),
|
||||
Path("textures/Bricks/brick_dotted_04-color.jpg"),
|
||||
]
|
||||
|
||||
import hashlib
|
||||
@@ -238,14 +263,14 @@ class PackTest(AbstractPackTest):
|
||||
with tempfile.TemporaryDirectory(suffix="-bat-symlink") as tmpdir_str:
|
||||
tmpdir = Path(tmpdir_str)
|
||||
|
||||
real_file_dir = tmpdir / 'real'
|
||||
symlinked_dir = tmpdir / 'symlinked'
|
||||
real_file_dir = tmpdir / "real"
|
||||
symlinked_dir = tmpdir / "symlinked"
|
||||
|
||||
real_file_dir.mkdir()
|
||||
symlinked_dir.mkdir()
|
||||
|
||||
for orig_path in orig_paths:
|
||||
hashed_name = hashlib.new('md5', bytes(orig_path)).hexdigest()
|
||||
hashed_name = hashlib.new("md5", bytes(orig_path)).hexdigest()
|
||||
# Copy the file to the temporary project, under a hashed name.
|
||||
# This will break Blendfile linking.
|
||||
real_file_path = real_file_dir / hashed_name
|
||||
@@ -259,20 +284,22 @@ class PackTest(AbstractPackTest):
|
||||
symlink.symlink_to(real_file_path)
|
||||
|
||||
# Pack the symlinked directory structure.
|
||||
pack_dir = tmpdir / 'packed'
|
||||
packer = pack.Packer(self.blendfiles / 'subdir/doubly_linked_up.blend',
|
||||
self.blendfiles,
|
||||
pack_dir)
|
||||
pack_dir = tmpdir / "packed"
|
||||
packer = pack.Packer(
|
||||
self.blendfiles / "subdir/doubly_linked_up.blend",
|
||||
self.blendfiles,
|
||||
pack_dir,
|
||||
)
|
||||
packer.strategise()
|
||||
packer.execute()
|
||||
|
||||
for orig_path in orig_paths:
|
||||
packed_path = pack_dir / orig_path
|
||||
self.assertTrue(packed_path.exists(), f'{packed_path} should exist')
|
||||
self.assertTrue(packed_path.exists(), f"{packed_path} should exist")
|
||||
|
||||
def _pack_with_rewrite(self):
|
||||
ppath = self.blendfiles / 'subdir'
|
||||
infile = ppath / 'doubly_linked_up.blend'
|
||||
ppath = self.blendfiles / "subdir"
|
||||
infile = ppath / "doubly_linked_up.blend"
|
||||
|
||||
packer = pack.Packer(infile, ppath, self.tpath)
|
||||
packer.strategise()
|
||||
@@ -281,34 +308,34 @@ class PackTest(AbstractPackTest):
|
||||
return infile, packer
|
||||
|
||||
def test_rewrite_sequence(self):
|
||||
ppath = self.blendfiles / 'subdir'
|
||||
infile = ppath / 'image_sequence_dir_up.blend'
|
||||
ppath = self.blendfiles / "subdir"
|
||||
infile = ppath / "image_sequence_dir_up.blend"
|
||||
|
||||
with pack.Packer(infile, ppath, self.tpath) as packer:
|
||||
packer.strategise()
|
||||
packer.execute()
|
||||
|
||||
bf = blendfile.open_cached(self.tpath / infile.name, assert_cached=False)
|
||||
scene = bf.code_index[b'SC'][0]
|
||||
ed = scene.get_pointer(b'ed')
|
||||
seq = ed.get_pointer((b'seqbase', b'first'))
|
||||
seq_strip = seq.get_pointer(b'strip')
|
||||
scene = bf.code_index[b"SC"][0]
|
||||
ed = scene.get_pointer(b"ed")
|
||||
seq = ed.get_pointer((b"seqbase", b"first"))
|
||||
seq_strip = seq.get_pointer(b"strip")
|
||||
|
||||
imgseq_path = bpathlib.make_absolute(self.blendfiles / 'imgseq')
|
||||
print(f'imgseq_path: {imgseq_path!r}')
|
||||
print(f' anchor: {imgseq_path.anchor!r}')
|
||||
imgseq_path = bpathlib.make_absolute(self.blendfiles / "imgseq")
|
||||
print(f"imgseq_path: {imgseq_path!r}")
|
||||
print(f" anchor: {imgseq_path.anchor!r}")
|
||||
as_bytes = bpathlib.strip_root(imgseq_path).as_posix().encode()
|
||||
print(f'as_bytes: {as_bytes!r}')
|
||||
relpath = bpathlib.BlendPath(b'//_outside_project') / as_bytes
|
||||
print(f'relpath: {relpath!r}')
|
||||
print(f"as_bytes: {as_bytes!r}")
|
||||
relpath = bpathlib.BlendPath(b"//_outside_project") / as_bytes
|
||||
print(f"relpath: {relpath!r}")
|
||||
|
||||
# The image sequence base path should be rewritten.
|
||||
self.assertEqual(b'SQ000210.png', seq[b'name'])
|
||||
self.assertEqual(relpath, seq_strip[b'dir'])
|
||||
self.assertEqual(b"SQ000210.png", seq[b"name"])
|
||||
self.assertEqual(relpath, seq_strip[b"dir"])
|
||||
|
||||
def test_noop(self):
|
||||
ppath = self.blendfiles / 'subdir'
|
||||
infile = ppath / 'doubly_linked_up.blend'
|
||||
ppath = self.blendfiles / "subdir"
|
||||
infile = ppath / "doubly_linked_up.blend"
|
||||
|
||||
packer = pack.Packer(infile, ppath, self.tpath, noop=True)
|
||||
packer.strategise()
|
||||
@@ -318,71 +345,77 @@ class PackTest(AbstractPackTest):
|
||||
|
||||
# The original file shouldn't be touched.
|
||||
bfile = blendfile.open_cached(infile)
|
||||
libs = sorted(bfile.code_index[b'LI'])
|
||||
libs = sorted(bfile.code_index[b"LI"])
|
||||
|
||||
self.assertEqual(b'LILib', libs[0].id_name)
|
||||
self.assertEqual(b'//../linked_cube.blend', libs[0][b'name'])
|
||||
self.assertEqual(b'LILib.002', libs[1].id_name)
|
||||
self.assertEqual(b'//../material_textures.blend', libs[1][b'name'])
|
||||
self.assertEqual(b"LILib", libs[0].id_name)
|
||||
self.assertEqual(b"//../linked_cube.blend", libs[0][b"name"])
|
||||
self.assertEqual(b"LILib.002", libs[1].id_name)
|
||||
self.assertEqual(b"//../material_textures.blend", libs[1][b"name"])
|
||||
|
||||
def test_missing_files(self):
|
||||
infile = self.blendfiles / 'missing_textures.blend'
|
||||
infile = self.blendfiles / "missing_textures.blend"
|
||||
packer = pack.Packer(infile, self.blendfiles, self.tpath)
|
||||
packer.strategise()
|
||||
|
||||
self.assertEqual(
|
||||
[self.blendfiles / 'textures/HDRI/Myanmar/Golden Palace 2, Old Bagan-1k.exr',
|
||||
self.blendfiles / 'textures/Textures/Marble/marble_decoration-color.png'],
|
||||
sorted(packer.missing_files)
|
||||
[
|
||||
self.blendfiles
|
||||
/ "textures/HDRI/Myanmar/Golden Palace 2, Old Bagan-1k.exr",
|
||||
self.blendfiles
|
||||
/ "textures/Textures/Marble/marble_decoration-color.png",
|
||||
],
|
||||
sorted(packer.missing_files),
|
||||
)
|
||||
|
||||
def test_exclude_filter(self):
|
||||
# Files shouldn't be reported missing if they should be ignored.
|
||||
infile = self.blendfiles / 'image_sequencer.blend'
|
||||
infile = self.blendfiles / "image_sequencer.blend"
|
||||
with pack.Packer(infile, self.blendfiles, self.tpath) as packer:
|
||||
packer.exclude('*.png', '*.nonsense')
|
||||
packer.exclude("*.png", "*.nonsense")
|
||||
packer.strategise()
|
||||
packer.execute()
|
||||
|
||||
self.assertFalse((self.tpath / 'imgseq').exists())
|
||||
self.assertFalse((self.tpath / "imgseq").exists())
|
||||
|
||||
def test_exclude_filter_missing_files(self):
|
||||
# Files shouldn't be reported missing if they should be ignored.
|
||||
infile = self.blendfiles / 'missing_textures.blend'
|
||||
infile = self.blendfiles / "missing_textures.blend"
|
||||
with pack.Packer(infile, self.blendfiles, self.tpath) as packer:
|
||||
packer.exclude('*.png')
|
||||
packer.exclude("*.png")
|
||||
packer.strategise()
|
||||
|
||||
self.assertEqual(
|
||||
[self.blendfiles / 'textures/HDRI/Myanmar/Golden Palace 2, Old Bagan-1k.exr'],
|
||||
list(packer.missing_files)
|
||||
[
|
||||
self.blendfiles
|
||||
/ "textures/HDRI/Myanmar/Golden Palace 2, Old Bagan-1k.exr"
|
||||
],
|
||||
list(packer.missing_files),
|
||||
)
|
||||
|
||||
def test_output_path(self):
|
||||
infile = self.blendfiles / 'basic_file.blend'
|
||||
infile = self.blendfiles / "basic_file.blend"
|
||||
packer = pack.Packer(infile, self.blendfiles.parent, self.tpath)
|
||||
packer.strategise()
|
||||
|
||||
self.assertEqual(
|
||||
self.tpath / self.blendfiles.name / infile.name,
|
||||
packer.output_path
|
||||
self.tpath / self.blendfiles.name / infile.name, packer.output_path
|
||||
)
|
||||
|
||||
def test_infofile(self):
|
||||
blendname = 'subdir/doubly_linked_up.blend'
|
||||
blendname = "subdir/doubly_linked_up.blend"
|
||||
infile = self.blendfiles / blendname
|
||||
|
||||
packer = pack.Packer(infile, self.blendfiles, self.tpath)
|
||||
packer.strategise()
|
||||
packer.execute()
|
||||
|
||||
infopath = self.tpath / 'pack-info.txt'
|
||||
infopath = self.tpath / "pack-info.txt"
|
||||
self.assertTrue(infopath.exists())
|
||||
info = infopath.open().read().splitlines(keepends=False)
|
||||
self.assertEqual(blendname, info[-1].strip())
|
||||
|
||||
def test_compression(self):
|
||||
blendname = 'subdir/doubly_linked_up.blend'
|
||||
blendname = "subdir/doubly_linked_up.blend"
|
||||
imgfile = self.blendfiles / blendname
|
||||
|
||||
packer = pack.Packer(imgfile, self.blendfiles, self.tpath, compress=True)
|
||||
@@ -393,31 +426,34 @@ class PackTest(AbstractPackTest):
|
||||
self.assertTrue(dest.exists())
|
||||
self.assertTrue(blendfile.open_cached(dest).is_compressed)
|
||||
|
||||
for bpath in self.tpath.rglob('*.blend'):
|
||||
for bpath in self.tpath.rglob("*.blend"):
|
||||
if bpath == dest:
|
||||
# Only test files that were bundled as dependency; the main
|
||||
# file was tested above already.
|
||||
continue
|
||||
self.assertTrue(blendfile.open_cached(bpath).is_compressed,
|
||||
'Expected %s to be compressed' % bpath)
|
||||
self.assertTrue(
|
||||
blendfile.open_cached(bpath).is_compressed,
|
||||
"Expected %s to be compressed" % bpath,
|
||||
)
|
||||
break
|
||||
else:
|
||||
self.fail(f'Expected to have Blend files in the BAT pack at {self.tpath}.')
|
||||
self.fail(f"Expected to have Blend files in the BAT pack at {self.tpath}.")
|
||||
|
||||
for imgpath in self.tpath.rglob('*.jpg'):
|
||||
with imgpath.open('rb') as imgfile:
|
||||
for imgpath in self.tpath.rglob("*.jpg"):
|
||||
with imgpath.open("rb") as imgfile:
|
||||
magic = imgfile.read(3)
|
||||
self.assertEqual(b'\xFF\xD8\xFF', magic,
|
||||
'Expected %s to NOT be compressed' % imgpath)
|
||||
self.assertEqual(
|
||||
b"\xFF\xD8\xFF", magic, "Expected %s to NOT be compressed" % imgpath
|
||||
)
|
||||
break
|
||||
else:
|
||||
self.fail(f'Expected to have JPEG files in the BAT pack at {self.tpath}.')
|
||||
self.fail(f"Expected to have JPEG files in the BAT pack at {self.tpath}.")
|
||||
|
||||
|
||||
class ProgressTest(AbstractPackTest):
|
||||
def test_strategise(self):
|
||||
cb = mock.Mock(progress.Callback)
|
||||
infile = self.blendfiles / 'subdir/doubly_linked_up.blend'
|
||||
infile = self.blendfiles / "subdir/doubly_linked_up.blend"
|
||||
with pack.Packer(infile, self.blendfiles, self.tpath) as packer:
|
||||
packer.progress_cb = cb
|
||||
packer.strategise()
|
||||
@@ -426,20 +462,20 @@ class ProgressTest(AbstractPackTest):
|
||||
self.assertEqual(0, cb.pack_done.call_count)
|
||||
|
||||
expected_calls = [
|
||||
mock.call(self.blendfiles / 'subdir/doubly_linked_up.blend'),
|
||||
mock.call(self.blendfiles / 'linked_cube.blend'),
|
||||
mock.call(self.blendfiles / 'basic_file.blend'),
|
||||
mock.call(self.blendfiles / 'material_textures.blend'),
|
||||
mock.call(self.blendfiles / "subdir/doubly_linked_up.blend"),
|
||||
mock.call(self.blendfiles / "linked_cube.blend"),
|
||||
mock.call(self.blendfiles / "basic_file.blend"),
|
||||
mock.call(self.blendfiles / "material_textures.blend"),
|
||||
]
|
||||
cb.trace_blendfile.assert_has_calls(expected_calls, any_order=True)
|
||||
self.assertEqual(len(expected_calls), cb.trace_blendfile.call_count)
|
||||
|
||||
expected_calls = [
|
||||
mock.call(self.blendfiles / 'linked_cube.blend'),
|
||||
mock.call(self.blendfiles / 'basic_file.blend'),
|
||||
mock.call(self.blendfiles / 'material_textures.blend'),
|
||||
mock.call(self.blendfiles / 'textures/Bricks/brick_dotted_04-color.jpg'),
|
||||
mock.call(self.blendfiles / 'textures/Bricks/brick_dotted_04-bump.jpg'),
|
||||
mock.call(self.blendfiles / "linked_cube.blend"),
|
||||
mock.call(self.blendfiles / "basic_file.blend"),
|
||||
mock.call(self.blendfiles / "material_textures.blend"),
|
||||
mock.call(self.blendfiles / "textures/Bricks/brick_dotted_04-color.jpg"),
|
||||
mock.call(self.blendfiles / "textures/Bricks/brick_dotted_04-bump.jpg"),
|
||||
]
|
||||
cb.trace_asset.assert_has_calls(expected_calls, any_order=True)
|
||||
self.assertEqual(len(expected_calls), cb.trace_asset.call_count)
|
||||
@@ -452,7 +488,7 @@ class ProgressTest(AbstractPackTest):
|
||||
|
||||
def test_execute_with_rewrite(self):
|
||||
cb = mock.Mock(progress.Callback)
|
||||
infile = self.blendfiles / 'subdir/doubly_linked_up.blend'
|
||||
infile = self.blendfiles / "subdir/doubly_linked_up.blend"
|
||||
with pack.Packer(infile, infile.parent, self.tpath) as packer:
|
||||
packer.progress_cb = cb
|
||||
packer.strategise()
|
||||
@@ -463,34 +499,43 @@ class ProgressTest(AbstractPackTest):
|
||||
|
||||
# rewrite_blendfile should only be called paths in a blendfile are
|
||||
# actually rewritten.
|
||||
cb.rewrite_blendfile.assert_called_with(self.blendfiles / 'subdir/doubly_linked_up.blend')
|
||||
cb.rewrite_blendfile.assert_called_with(
|
||||
self.blendfiles / "subdir/doubly_linked_up.blend"
|
||||
)
|
||||
self.assertEqual(1, cb.rewrite_blendfile.call_count)
|
||||
|
||||
# mock.ANY is used for temporary files in temporary paths, because they
|
||||
# are hard to predict.
|
||||
extpath = self.outside_project()
|
||||
expected_calls = [
|
||||
mock.call(mock.ANY, self.tpath / 'doubly_linked_up.blend'),
|
||||
mock.call(mock.ANY, self.tpath / 'pack-info.txt'),
|
||||
mock.call(mock.ANY, extpath / 'linked_cube.blend'),
|
||||
mock.call(mock.ANY, extpath / 'basic_file.blend'),
|
||||
mock.call(mock.ANY, extpath / 'material_textures.blend'),
|
||||
mock.call(self.blendfiles / 'textures/Bricks/brick_dotted_04-color.jpg',
|
||||
extpath / 'textures/Bricks/brick_dotted_04-color.jpg'),
|
||||
mock.call(self.blendfiles / 'textures/Bricks/brick_dotted_04-bump.jpg',
|
||||
extpath / 'textures/Bricks/brick_dotted_04-bump.jpg'),
|
||||
mock.call(mock.ANY, self.tpath / "doubly_linked_up.blend"),
|
||||
mock.call(mock.ANY, self.tpath / "pack-info.txt"),
|
||||
mock.call(mock.ANY, extpath / "linked_cube.blend"),
|
||||
mock.call(mock.ANY, extpath / "basic_file.blend"),
|
||||
mock.call(mock.ANY, extpath / "material_textures.blend"),
|
||||
mock.call(
|
||||
self.blendfiles / "textures/Bricks/brick_dotted_04-color.jpg",
|
||||
extpath / "textures/Bricks/brick_dotted_04-color.jpg",
|
||||
),
|
||||
mock.call(
|
||||
self.blendfiles / "textures/Bricks/brick_dotted_04-bump.jpg",
|
||||
extpath / "textures/Bricks/brick_dotted_04-bump.jpg",
|
||||
),
|
||||
]
|
||||
cb.transfer_file.assert_has_calls(expected_calls, any_order=True)
|
||||
self.assertEqual(len(expected_calls), cb.transfer_file.call_count)
|
||||
|
||||
self.assertEqual(0, cb.transfer_file_skipped.call_count)
|
||||
self.assertGreaterEqual(cb.transfer_progress.call_count, 6,
|
||||
'transfer_progress() should be called at least once per asset')
|
||||
self.assertGreaterEqual(
|
||||
cb.transfer_progress.call_count,
|
||||
6,
|
||||
"transfer_progress() should be called at least once per asset",
|
||||
)
|
||||
self.assertEqual(0, cb.missing_file.call_count)
|
||||
|
||||
def test_missing_files(self):
|
||||
cb = mock.Mock(progress.Callback)
|
||||
infile = self.blendfiles / 'missing_textures.blend'
|
||||
infile = self.blendfiles / "missing_textures.blend"
|
||||
with pack.Packer(infile, self.blendfiles, self.tpath) as packer:
|
||||
packer.progress_cb = cb
|
||||
packer.strategise()
|
||||
@@ -500,18 +545,29 @@ class ProgressTest(AbstractPackTest):
|
||||
self.assertEqual(1, cb.pack_done.call_count)
|
||||
|
||||
cb.rewrite_blendfile.assert_not_called()
|
||||
cb.transfer_file.assert_has_calls([
|
||||
mock.call(infile, self.tpath / 'missing_textures.blend'),
|
||||
mock.call(mock.ANY, self.tpath / 'pack-info.txt'),
|
||||
], any_order=True)
|
||||
cb.transfer_file.assert_has_calls(
|
||||
[
|
||||
mock.call(infile, self.tpath / "missing_textures.blend"),
|
||||
mock.call(mock.ANY, self.tpath / "pack-info.txt"),
|
||||
],
|
||||
any_order=True,
|
||||
)
|
||||
|
||||
self.assertEqual(0, cb.transfer_file_skipped.call_count)
|
||||
self.assertGreaterEqual(cb.transfer_progress.call_count, 1,
|
||||
'transfer_progress() should be called at least once per asset')
|
||||
self.assertGreaterEqual(
|
||||
cb.transfer_progress.call_count,
|
||||
1,
|
||||
"transfer_progress() should be called at least once per asset",
|
||||
)
|
||||
|
||||
expected_calls = [
|
||||
mock.call(self.blendfiles / 'textures/HDRI/Myanmar/Golden Palace 2, Old Bagan-1k.exr'),
|
||||
mock.call(self.blendfiles / 'textures/Textures/Marble/marble_decoration-color.png'),
|
||||
mock.call(
|
||||
self.blendfiles
|
||||
/ "textures/HDRI/Myanmar/Golden Palace 2, Old Bagan-1k.exr"
|
||||
),
|
||||
mock.call(
|
||||
self.blendfiles / "textures/Textures/Marble/marble_decoration-color.png"
|
||||
),
|
||||
]
|
||||
cb.missing_file.assert_has_calls(expected_calls, any_order=True)
|
||||
self.assertEqual(len(expected_calls), cb.missing_file.call_count)
|
||||
@@ -519,64 +575,79 @@ class ProgressTest(AbstractPackTest):
|
||||
def test_particle_cache(self):
|
||||
# The particle cache uses a glob to indicate which files to pack.
|
||||
cb = mock.Mock(progress.Callback)
|
||||
infile = self.blendfiles / 'T55539-particles/particle.blend'
|
||||
infile = self.blendfiles / "T55539-particles/particle.blend"
|
||||
with pack.Packer(infile, self.blendfiles, self.tpath) as packer:
|
||||
packer.progress_cb = cb
|
||||
packer.strategise()
|
||||
packer.execute()
|
||||
|
||||
# We should have all the *.bphys files now.
|
||||
count = len(list((self.tpath / 'T55539-particles/blendcache_particle').glob('*.bphys')))
|
||||
count = len(
|
||||
list((self.tpath / "T55539-particles/blendcache_particle").glob("*.bphys"))
|
||||
)
|
||||
self.assertEqual(27, count)
|
||||
|
||||
# Physics files + particle.blend + pack_info.txt
|
||||
self.assertGreaterEqual(cb.transfer_progress.call_count, 29,
|
||||
'transfer_progress() should be called at least once per asset')
|
||||
self.assertGreaterEqual(
|
||||
cb.transfer_progress.call_count,
|
||||
29,
|
||||
"transfer_progress() should be called at least once per asset",
|
||||
)
|
||||
|
||||
def test_particle_cache_with_ignore_glob(self):
|
||||
cb = mock.Mock(progress.Callback)
|
||||
infile = self.blendfiles / 'T55539-particles/particle.blend'
|
||||
infile = self.blendfiles / "T55539-particles/particle.blend"
|
||||
with pack.Packer(infile, self.blendfiles, self.tpath) as packer:
|
||||
packer.progress_cb = cb
|
||||
packer.exclude('*.bphys')
|
||||
packer.exclude("*.bphys")
|
||||
packer.strategise()
|
||||
packer.execute()
|
||||
|
||||
# We should have none of the *.bphys files now.
|
||||
count = len(list((self.tpath / 'T55539-particles/blendcache_particle').glob('*.bphys')))
|
||||
count = len(
|
||||
list((self.tpath / "T55539-particles/blendcache_particle").glob("*.bphys"))
|
||||
)
|
||||
self.assertEqual(0, count)
|
||||
|
||||
# Just particle.blend + pack_info.txt
|
||||
self.assertGreaterEqual(cb.transfer_progress.call_count, 2,
|
||||
'transfer_progress() should be called at least once per asset')
|
||||
self.assertGreaterEqual(
|
||||
cb.transfer_progress.call_count,
|
||||
2,
|
||||
"transfer_progress() should be called at least once per asset",
|
||||
)
|
||||
|
||||
def test_smoke_cache(self):
|
||||
# The smoke cache uses a glob to indicate which files to pack.
|
||||
cb = mock.Mock(progress.Callback)
|
||||
infile = self.blendfiles / 'T55542-smoke/smoke_cache.blend'
|
||||
infile = self.blendfiles / "T55542-smoke/smoke_cache.blend"
|
||||
with pack.Packer(infile, self.blendfiles, self.tpath) as packer:
|
||||
packer.progress_cb = cb
|
||||
packer.strategise()
|
||||
packer.execute()
|
||||
|
||||
# We should have all the *.bphys files now.
|
||||
count = len(list((self.tpath / 'T55542-smoke/blendcache_smoke_cache').glob('*.bphys')))
|
||||
count = len(
|
||||
list((self.tpath / "T55542-smoke/blendcache_smoke_cache").glob("*.bphys"))
|
||||
)
|
||||
self.assertEqual(10, count)
|
||||
|
||||
# Physics files + smoke_cache.blend + pack_info.txt
|
||||
self.assertGreaterEqual(cb.transfer_progress.call_count, 12,
|
||||
'transfer_progress() should be called at least once per asset')
|
||||
self.assertGreaterEqual(
|
||||
cb.transfer_progress.call_count,
|
||||
12,
|
||||
"transfer_progress() should be called at least once per asset",
|
||||
)
|
||||
|
||||
|
||||
class AbortTest(AbstractPackTest):
|
||||
def test_abort_strategise(self):
|
||||
infile = self.blendfiles / 'subdir/doubly_linked_up.blend'
|
||||
infile = self.blendfiles / "subdir/doubly_linked_up.blend"
|
||||
packer = pack.Packer(infile, self.blendfiles, self.tpath)
|
||||
|
||||
class AbortingCallback(progress.Callback):
|
||||
def trace_blendfile(self, filename: Path):
|
||||
# Call abort() somewhere during the strategise() call.
|
||||
if filename.name == 'linked_cube.blend':
|
||||
if filename.name == "linked_cube.blend":
|
||||
packer.abort()
|
||||
|
||||
packer.progress_cb = AbortingCallback()
|
||||
@@ -584,7 +655,7 @@ class AbortTest(AbstractPackTest):
|
||||
packer.strategise()
|
||||
|
||||
def test_abort_transfer(self):
|
||||
infile = self.blendfiles / 'subdir/doubly_linked_up.blend'
|
||||
infile = self.blendfiles / "subdir/doubly_linked_up.blend"
|
||||
packer = pack.Packer(infile, self.blendfiles, self.tpath)
|
||||
|
||||
first_file_size = infile.stat().st_size
|
||||
|
||||
+10
-10
@@ -21,12 +21,12 @@ class ThreadedProgressTest(unittest.TestCase):
|
||||
|
||||
def thread():
|
||||
tscb.pack_start()
|
||||
tscb.pack_done(Path('one'), {Path('two'), Path('three')})
|
||||
tscb.trace_asset(Path('four'))
|
||||
tscb.transfer_file(Path('five'), Path('six'))
|
||||
tscb.transfer_file_skipped(Path('seven'), Path('eight'))
|
||||
tscb.pack_done(Path("one"), {Path("two"), Path("three")})
|
||||
tscb.trace_asset(Path("four"))
|
||||
tscb.transfer_file(Path("five"), Path("six"))
|
||||
tscb.transfer_file_skipped(Path("seven"), Path("eight"))
|
||||
tscb.transfer_progress(327, 47)
|
||||
tscb.missing_file(Path('nine'))
|
||||
tscb.missing_file(Path("nine"))
|
||||
|
||||
t = threading.Thread(target=thread)
|
||||
t.start()
|
||||
@@ -34,9 +34,9 @@ class ThreadedProgressTest(unittest.TestCase):
|
||||
tscb.flush(timeout=3)
|
||||
|
||||
cb.pack_start.assert_called_with()
|
||||
cb.pack_done.assert_called_with(Path('one'), {Path('two'), Path('three')})
|
||||
cb.trace_asset.assert_called_with(Path('four'))
|
||||
cb.transfer_file.assert_called_with(Path('five'), Path('six'))
|
||||
cb.transfer_file_skipped.assert_called_with(Path('seven'), Path('eight'))
|
||||
cb.pack_done.assert_called_with(Path("one"), {Path("two"), Path("three")})
|
||||
cb.trace_asset.assert_called_with(Path("four"))
|
||||
cb.transfer_file.assert_called_with(Path("five"), Path("six"))
|
||||
cb.transfer_file_skipped.assert_called_with(Path("seven"), Path("eight"))
|
||||
cb.transfer_progress.assert_called_with(327, 47)
|
||||
cb.missing_file.assert_called_with(Path('nine'))
|
||||
cb.missing_file.assert_called_with(Path("nine"))
|
||||
|
||||
+19
-10
@@ -30,23 +30,32 @@ httpmock = responses.RequestsMock()
|
||||
class ShamanPackTest(AbstractPackTest):
|
||||
@httpmock.activate
|
||||
def test_all_files_already_uploaded(self):
|
||||
infile = self.blendfiles / 'basic_file_ñønæščii.blend'
|
||||
infile = self.blendfiles / "basic_file_ñønæščii.blend"
|
||||
|
||||
packer = shaman.ShamanPacker(infile, infile.parent, '/',
|
||||
endpoint='http://shaman.local',
|
||||
checkout_id='DA-JOBBY-ID')
|
||||
packer = shaman.ShamanPacker(
|
||||
infile,
|
||||
infile.parent,
|
||||
"/",
|
||||
endpoint="http://shaman.local",
|
||||
checkout_id="DA-JOBBY-ID",
|
||||
)
|
||||
|
||||
# Temporary hack
|
||||
httpmock.add('GET', 'http://shaman.local/get-token', body='AUTH-TOKEN')
|
||||
httpmock.add("GET", "http://shaman.local/get-token", body="AUTH-TOKEN")
|
||||
|
||||
# Just fake that everything is already available on the server.
|
||||
httpmock.add('POST', 'http://shaman.local/checkout/requirements', body='')
|
||||
httpmock.add('POST', 'http://shaman.local/checkout/create/DA-JOBBY-ID',
|
||||
body='DA/-JOBBY-ID')
|
||||
httpmock.add("POST", "http://shaman.local/checkout/requirements", body="")
|
||||
httpmock.add(
|
||||
"POST",
|
||||
"http://shaman.local/checkout/create/DA-JOBBY-ID",
|
||||
body="DA/-JOBBY-ID",
|
||||
)
|
||||
|
||||
with packer:
|
||||
packer.strategise()
|
||||
packer.execute()
|
||||
|
||||
self.assertEqual(pathlib.PurePosixPath('DA/-JOBBY-ID/basic_file_ñønæščii.blend'),
|
||||
packer.output_path)
|
||||
self.assertEqual(
|
||||
pathlib.PurePosixPath("DA/-JOBBY-ID/basic_file_ñønæščii.blend"),
|
||||
packer.output_path,
|
||||
)
|
||||
|
||||
@@ -25,8 +25,8 @@ from blender_asset_tracer.pack import zipped
|
||||
|
||||
class ZippedPackTest(AbstractPackTest):
|
||||
def test_basic_file(self):
|
||||
infile = self.blendfiles / 'basic_file_ñønæščii.blend'
|
||||
zippath = self.tpath / 'target.zip'
|
||||
infile = self.blendfiles / "basic_file_ñønæščii.blend"
|
||||
zippath = self.tpath / "target.zip"
|
||||
with zipped.ZipPacker(infile, infile.parent, zippath) as packer:
|
||||
packer.strategise()
|
||||
packer.execute()
|
||||
@@ -34,4 +34,6 @@ class ZippedPackTest(AbstractPackTest):
|
||||
self.assertTrue(zippath.exists())
|
||||
with zipfile.ZipFile(str(zippath)) as inzip:
|
||||
inzip.testzip()
|
||||
self.assertEqual({'pack-info.txt', 'basic_file_ñønæščii.blend'}, set(inzip.namelist()))
|
||||
self.assertEqual(
|
||||
{"pack-info.txt", "basic_file_ñønæščii.blend"}, set(inzip.namelist())
|
||||
)
|
||||
|
||||
+16
-16
@@ -6,40 +6,40 @@ from blender_asset_tracer.pack import shaman
|
||||
class ParseEndpointTest(unittest.TestCase):
|
||||
def test_path_slashyness(self):
|
||||
self.assertEqual(
|
||||
('https://endpoint/', '123'),
|
||||
shaman.parse_endpoint('shaman://endpoint#123'),
|
||||
("https://endpoint/", "123"),
|
||||
shaman.parse_endpoint("shaman://endpoint#123"),
|
||||
)
|
||||
self.assertEqual(
|
||||
('https://endpoint/', '123'),
|
||||
shaman.parse_endpoint('shaman://endpoint/#123'),
|
||||
("https://endpoint/", "123"),
|
||||
shaman.parse_endpoint("shaman://endpoint/#123"),
|
||||
)
|
||||
self.assertEqual(
|
||||
('https://endpoint/root', '123'),
|
||||
shaman.parse_endpoint('shaman://endpoint/root#123'),
|
||||
("https://endpoint/root", "123"),
|
||||
shaman.parse_endpoint("shaman://endpoint/root#123"),
|
||||
)
|
||||
self.assertEqual(
|
||||
('https://endpoint/root/is/longer/', '123'),
|
||||
shaman.parse_endpoint('shaman://endpoint/root/is/longer/#123'),
|
||||
("https://endpoint/root/is/longer/", "123"),
|
||||
shaman.parse_endpoint("shaman://endpoint/root/is/longer/#123"),
|
||||
)
|
||||
|
||||
def test_schemes_with_plus(self):
|
||||
self.assertEqual(
|
||||
('https://endpoint/', '123'),
|
||||
shaman.parse_endpoint('shaman+https://endpoint/#123'),
|
||||
("https://endpoint/", "123"),
|
||||
shaman.parse_endpoint("shaman+https://endpoint/#123"),
|
||||
)
|
||||
self.assertEqual(
|
||||
('http://endpoint/', '123'),
|
||||
shaman.parse_endpoint('shaman+http://endpoint/#123'),
|
||||
("http://endpoint/", "123"),
|
||||
shaman.parse_endpoint("shaman+http://endpoint/#123"),
|
||||
)
|
||||
|
||||
def test_checkout_ids(self):
|
||||
self.assertEqual(
|
||||
('https://endpoint/', ''),
|
||||
shaman.parse_endpoint('shaman+https://endpoint/'),
|
||||
("https://endpoint/", ""),
|
||||
shaman.parse_endpoint("shaman+https://endpoint/"),
|
||||
)
|
||||
|
||||
# Not a valid ID, but the parser should handle it gracefully anyway
|
||||
self.assertEqual(
|
||||
('http://endpoint/', 'ïđ'),
|
||||
shaman.parse_endpoint('shaman+http://endpoint/#%C3%AF%C4%91'),
|
||||
("http://endpoint/", "ïđ"),
|
||||
shaman.parse_endpoint("shaman+http://endpoint/#%C3%AF%C4%91"),
|
||||
)
|
||||
|
||||
+22
-20
@@ -29,8 +29,10 @@ class AbstractChecksumTest(AbstractBlendFileTest):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.test_file = cls.blendfiles / 'linked_cube_compressed.blend'
|
||||
cls.expected_checksum = '3c525e3a01ece11f26ded1e05e43284c4cce575c8074b97c6bdbc414fa2802ab'
|
||||
cls.test_file = cls.blendfiles / "linked_cube_compressed.blend"
|
||||
cls.expected_checksum = (
|
||||
"3c525e3a01ece11f26ded1e05e43284c4cce575c8074b97c6bdbc414fa2802ab"
|
||||
)
|
||||
|
||||
|
||||
class ChecksumTest(AbstractChecksumTest):
|
||||
@@ -39,49 +41,49 @@ class ChecksumTest(AbstractChecksumTest):
|
||||
|
||||
|
||||
class CachedChecksumTest(AbstractChecksumTest):
|
||||
@mock.patch('blender_asset_tracer.pack.shaman.cache._cache_path')
|
||||
@mock.patch('blender_asset_tracer.pack.shaman.cache.compute_checksum')
|
||||
@mock.patch("blender_asset_tracer.pack.shaman.cache._cache_path")
|
||||
@mock.patch("blender_asset_tracer.pack.shaman.cache.compute_checksum")
|
||||
def test_cache_invalid_json(self, mock_compute_checksum, mock_cache_path):
|
||||
mock_path = mock.MagicMock(spec=pathlib.Path)
|
||||
mock_path.open().__enter__().read.return_value = 'je moeder'
|
||||
mock_path.open().__enter__().read.return_value = "je moeder"
|
||||
mock_cache_path.return_value = mock_path
|
||||
|
||||
mock_compute_checksum.return_value = 'computed-checksum'
|
||||
mock_compute_checksum.return_value = "computed-checksum"
|
||||
|
||||
checksum = cache.compute_cached_checksum(self.test_file)
|
||||
self.assertEqual('computed-checksum', checksum)
|
||||
self.assertEqual("computed-checksum", checksum)
|
||||
|
||||
@mock.patch('blender_asset_tracer.pack.shaman.cache._cache_path')
|
||||
@mock.patch('blender_asset_tracer.pack.shaman.cache.compute_checksum')
|
||||
@mock.patch("blender_asset_tracer.pack.shaman.cache._cache_path")
|
||||
@mock.patch("blender_asset_tracer.pack.shaman.cache.compute_checksum")
|
||||
def test_cache_valid_json(self, mock_compute_checksum, mock_cache_path):
|
||||
stat = self.test_file.stat()
|
||||
cache_info = {
|
||||
'checksum': 'cached-checksum',
|
||||
'file_mtime': stat.st_mtime + 0.0001, # mimick a slight clock skew
|
||||
'file_size': stat.st_size,
|
||||
"checksum": "cached-checksum",
|
||||
"file_mtime": stat.st_mtime + 0.0001, # mimick a slight clock skew
|
||||
"file_size": stat.st_size,
|
||||
}
|
||||
|
||||
mock_path = mock.MagicMock(spec=pathlib.Path)
|
||||
mock_path.open().__enter__().read.return_value = json.dumps(cache_info)
|
||||
mock_cache_path.return_value = mock_path
|
||||
|
||||
mock_compute_checksum.return_value = 'computed-checksum'
|
||||
mock_compute_checksum.return_value = "computed-checksum"
|
||||
|
||||
checksum = cache.compute_cached_checksum(self.test_file)
|
||||
self.assertEqual('cached-checksum', checksum)
|
||||
self.assertEqual("cached-checksum", checksum)
|
||||
|
||||
@mock.patch('blender_asset_tracer.pack.shaman.cache._cache_path')
|
||||
@mock.patch('blender_asset_tracer.pack.shaman.cache.compute_checksum')
|
||||
@mock.patch("blender_asset_tracer.pack.shaman.cache._cache_path")
|
||||
@mock.patch("blender_asset_tracer.pack.shaman.cache.compute_checksum")
|
||||
def test_cache_not_exists(self, mock_compute_checksum, mock_cache_path):
|
||||
mock_path = mock.MagicMock(spec=pathlib.Path)
|
||||
mock_path.open.side_effect = [
|
||||
FileNotFoundError('Testing absent cache file'),
|
||||
FileExistsError('Testing I/O error when writing'),
|
||||
FileNotFoundError("Testing absent cache file"),
|
||||
FileExistsError("Testing I/O error when writing"),
|
||||
]
|
||||
mock_cache_path.return_value = mock_path
|
||||
|
||||
mock_compute_checksum.return_value = 'computed-checksum'
|
||||
mock_compute_checksum.return_value = "computed-checksum"
|
||||
|
||||
# This should not raise the FileExistsError
|
||||
checksum = cache.compute_cached_checksum(self.test_file)
|
||||
self.assertEqual('computed-checksum', checksum)
|
||||
self.assertEqual("computed-checksum", checksum)
|
||||
|
||||
@@ -5,25 +5,25 @@ from blender_asset_tracer.pack.shaman import time_tracker
|
||||
|
||||
|
||||
class TimeTrackerTest(unittest.TestCase):
|
||||
@mock.patch('time.monotonic')
|
||||
@mock.patch("time.monotonic")
|
||||
def test_empty_class(self, mock_monotonic):
|
||||
class TestClass:
|
||||
pass
|
||||
|
||||
mock_monotonic.side_effect = [1.25, 4.75]
|
||||
with time_tracker.track_time(TestClass, 'some_attr'):
|
||||
with time_tracker.track_time(TestClass, "some_attr"):
|
||||
pass
|
||||
|
||||
# noinspection PyUnresolvedReferences
|
||||
self.assertEqual(3.5, TestClass.some_attr)
|
||||
|
||||
@mock.patch('time.monotonic')
|
||||
@mock.patch("time.monotonic")
|
||||
def test_with_value(self, mock_monotonic):
|
||||
class TestClass:
|
||||
some_attr = 4.125
|
||||
|
||||
mock_monotonic.side_effect = [1.25, 4.75]
|
||||
with time_tracker.track_time(TestClass, 'some_attr'):
|
||||
with time_tracker.track_time(TestClass, "some_attr"):
|
||||
pass
|
||||
|
||||
self.assertEqual(3.5 + 4.125, TestClass.some_attr)
|
||||
|
||||
@@ -32,19 +32,19 @@ class ShamanTransferTest(AbstractBlendFileTest):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.test_file1 = cls.blendfiles / 'linked_cube_compressed.blend'
|
||||
cls.test_file2 = cls.blendfiles / 'basic_file.blend'
|
||||
cls.test_file1 = cls.blendfiles / "linked_cube_compressed.blend"
|
||||
cls.test_file2 = cls.blendfiles / "basic_file.blend"
|
||||
cls.expected_checksums = {
|
||||
cls.test_file1: '3c525e3a01ece11f26ded1e05e43284c4cce575c8074b97c6bdbc414fa2802ab',
|
||||
cls.test_file2: 'd5283988d95f259069d4cd3c25a40526090534b8d188577b6c6fb36c3d481454',
|
||||
cls.test_file1: "3c525e3a01ece11f26ded1e05e43284c4cce575c8074b97c6bdbc414fa2802ab",
|
||||
cls.test_file2: "d5283988d95f259069d4cd3c25a40526090534b8d188577b6c6fb36c3d481454",
|
||||
}
|
||||
cls.file_sizes = {
|
||||
cls.test_file1: cls.test_file1.stat().st_size,
|
||||
cls.test_file2: cls.test_file2.stat().st_size,
|
||||
}
|
||||
cls.packed_names = {
|
||||
cls.test_file1: pathlib.PurePosixPath('path/in/pack/test1.blend'),
|
||||
cls.test_file2: pathlib.PurePosixPath('path/in/pack/test2.blend'),
|
||||
cls.test_file1: pathlib.PurePosixPath("path/in/pack/test1.blend"),
|
||||
cls.test_file2: pathlib.PurePosixPath("path/in/pack/test2.blend"),
|
||||
}
|
||||
|
||||
def assertValidCheckoutDef(self, definition_file: bytes):
|
||||
@@ -54,8 +54,8 @@ class ShamanTransferTest(AbstractBlendFileTest):
|
||||
checksum = self.expected_checksums[filepath]
|
||||
fsize = self.file_sizes[filepath]
|
||||
relpath = str(self.packed_names[filepath])
|
||||
expect_lines.add(b'%s %d %s' % (checksum.encode(), fsize, relpath.encode()))
|
||||
self.assertEqual(expect_lines, set(definition_file.split(b'\n')))
|
||||
expect_lines.add(b"%s %d %s" % (checksum.encode(), fsize, relpath.encode()))
|
||||
self.assertEqual(expect_lines, set(definition_file.split(b"\n")))
|
||||
|
||||
@httpmock.activate
|
||||
def test_checkout_happy(self):
|
||||
@@ -63,26 +63,35 @@ class ShamanTransferTest(AbstractBlendFileTest):
|
||||
fsize1 = self.file_sizes[self.test_file1]
|
||||
|
||||
def mock_requirements(request):
|
||||
self.assertEqual('text/plain', request.headers['Content-Type'])
|
||||
self.assertEqual("text/plain", request.headers["Content-Type"])
|
||||
self.assertValidCheckoutDef(request.body)
|
||||
|
||||
body = 'file-unknown path/in/pack/test1.blend\n'
|
||||
return 200, {'Content-Type': 'text/plain'}, body
|
||||
body = "file-unknown path/in/pack/test1.blend\n"
|
||||
return 200, {"Content-Type": "text/plain"}, body
|
||||
|
||||
def mock_checkout_create(request):
|
||||
self.assertEqual('text/plain', request.headers['Content-Type'])
|
||||
self.assertEqual("text/plain", request.headers["Content-Type"])
|
||||
self.assertValidCheckoutDef(request.body)
|
||||
return 200, {'Content-Type': 'text/plain'}, 'DA/-JOB-ID'
|
||||
return 200, {"Content-Type": "text/plain"}, "DA/-JOB-ID"
|
||||
|
||||
httpmock.add_callback('POST', 'http://unittest.local:1234/checkout/requirements',
|
||||
callback=mock_requirements)
|
||||
httpmock.add_callback(
|
||||
"POST",
|
||||
"http://unittest.local:1234/checkout/requirements",
|
||||
callback=mock_requirements,
|
||||
)
|
||||
|
||||
httpmock.add('POST', 'http://unittest.local:1234/files/%s/%d' % (checksum1, fsize1))
|
||||
httpmock.add_callback('POST', 'http://unittest.local:1234/checkout/create/DA-JOB-ID',
|
||||
callback=mock_checkout_create)
|
||||
httpmock.add(
|
||||
"POST", "http://unittest.local:1234/files/%s/%d" % (checksum1, fsize1)
|
||||
)
|
||||
httpmock.add_callback(
|
||||
"POST",
|
||||
"http://unittest.local:1234/checkout/create/DA-JOB-ID",
|
||||
callback=mock_checkout_create,
|
||||
)
|
||||
|
||||
trans = transfer.ShamanTransferrer('auth-token', self.blendfiles,
|
||||
'http://unittest.local:1234/', 'DA-JOB-ID')
|
||||
trans = transfer.ShamanTransferrer(
|
||||
"auth-token", self.blendfiles, "http://unittest.local:1234/", "DA-JOB-ID"
|
||||
)
|
||||
|
||||
trans.start()
|
||||
trans.queue_copy(self.test_file1, self.packed_names[self.test_file1])
|
||||
@@ -90,4 +99,4 @@ class ShamanTransferTest(AbstractBlendFileTest):
|
||||
trans.done_and_join()
|
||||
|
||||
self.assertFalse(trans.has_error, trans.error_message())
|
||||
self.assertEqual('DA/-JOB-ID', trans.checkout_location)
|
||||
self.assertEqual("DA/-JOB-ID", trans.checkout_location)
|
||||
|
||||
+312
-130
@@ -9,20 +9,20 @@ from tests.abstract_test import AbstractBlendFileTest
|
||||
|
||||
# Mimicks a BlockUsage, but without having to set the block to an expected value.
|
||||
Expect = collections.namedtuple(
|
||||
'Expect',
|
||||
'type full_field dirname_field basename_field asset_path is_sequence')
|
||||
"Expect", "type full_field dirname_field basename_field asset_path is_sequence"
|
||||
)
|
||||
|
||||
|
||||
class AbstractTracerTest(AbstractBlendFileTest):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
logging.getLogger('blender_asset_tracer.tracer').setLevel(logging.DEBUG)
|
||||
logging.getLogger("blender_asset_tracer.tracer").setLevel(logging.DEBUG)
|
||||
|
||||
|
||||
class AssetHoldingBlocksTest(AbstractTracerTest):
|
||||
def setUp(self):
|
||||
self.bf = blendfile.BlendFile(self.blendfiles / 'basic_file.blend')
|
||||
self.bf = blendfile.BlendFile(self.blendfiles / "basic_file.blend")
|
||||
|
||||
def test_simple_file(self):
|
||||
# This file should not depend on external assets.
|
||||
@@ -38,17 +38,17 @@ class AssetHoldingBlocksTest(AbstractTracerTest):
|
||||
self.assertEqual(2, len(block.code))
|
||||
|
||||
# World blocks should not yielded either.
|
||||
self.assertNotEqual(b'WO', block.code)
|
||||
self.assertNotEqual(b"WO", block.code)
|
||||
|
||||
# Do some arbitrary tests that convince us stuff is read well.
|
||||
if block.code == b'SC':
|
||||
if block.code == b"SC":
|
||||
seen_scene = True
|
||||
self.assertEqual(b'SCScene', block.id_name)
|
||||
self.assertEqual(b"SCScene", block.id_name)
|
||||
continue
|
||||
|
||||
if block.code == b'OB':
|
||||
if block.code == b"OB":
|
||||
seen_ob = True
|
||||
self.assertEqual('OBümlaut', block.get((b'id', b'name'), as_str=True))
|
||||
self.assertEqual("OBümlaut", block.get((b"id", b"name"), as_str=True))
|
||||
continue
|
||||
|
||||
self.assertTrue(seen_scene)
|
||||
@@ -77,95 +77,150 @@ class DepsTest(AbstractTracerTest):
|
||||
actual_dirname = self.field_name(dep.path_dir_field)
|
||||
actual_basename = self.field_name(dep.path_base_field)
|
||||
|
||||
actual = Expect(actual_type, actual_full_field, actual_dirname, actual_basename,
|
||||
dep.asset_path, dep.is_sequence)
|
||||
actual = Expect(
|
||||
actual_type,
|
||||
actual_full_field,
|
||||
actual_dirname,
|
||||
actual_basename,
|
||||
dep.asset_path,
|
||||
dep.is_sequence,
|
||||
)
|
||||
|
||||
exp = expects.get(dep.block_name, None)
|
||||
if isinstance(exp, (set, list)):
|
||||
self.assertIn(actual, exp, msg='for block %s' % dep.block_name)
|
||||
self.assertIn(actual, exp, msg="for block %s" % dep.block_name)
|
||||
exp.remove(actual)
|
||||
if not exp:
|
||||
# Don't leave empty sets in expects.
|
||||
del expects[dep.block_name]
|
||||
elif exp is None:
|
||||
self.assertIsNone(actual, msg='unexpected dependency of block %s' % dep.block_name)
|
||||
self.assertIsNone(
|
||||
actual, msg="unexpected dependency of block %s" % dep.block_name
|
||||
)
|
||||
del expects[dep.block_name]
|
||||
else:
|
||||
self.assertEqual(exp, actual, msg='for block %s' % dep.block_name)
|
||||
self.assertEqual(exp, actual, msg="for block %s" % dep.block_name)
|
||||
del expects[dep.block_name]
|
||||
|
||||
# All expected uses should have been seen.
|
||||
self.assertEqual(expects, {}, 'Expected results were not seen.')
|
||||
self.assertEqual(expects, {}, "Expected results were not seen.")
|
||||
|
||||
def test_no_deps(self):
|
||||
self.assert_deps('basic_file.blend', {})
|
||||
self.assert_deps("basic_file.blend", {})
|
||||
|
||||
def test_ob_mat_texture(self):
|
||||
expects = {
|
||||
b'IMbrick_dotted_04-bump': Expect(
|
||||
'Image', 'name[1024]', None, None,
|
||||
b'//textures/Bricks/brick_dotted_04-bump.jpg', False),
|
||||
b'IMbrick_dotted_04-color': Expect(
|
||||
'Image', 'name[1024]', None, None,
|
||||
b'//textures/Bricks/brick_dotted_04-color.jpg', False),
|
||||
b"IMbrick_dotted_04-bump": Expect(
|
||||
"Image",
|
||||
"name[1024]",
|
||||
None,
|
||||
None,
|
||||
b"//textures/Bricks/brick_dotted_04-bump.jpg",
|
||||
False,
|
||||
),
|
||||
b"IMbrick_dotted_04-color": Expect(
|
||||
"Image",
|
||||
"name[1024]",
|
||||
None,
|
||||
None,
|
||||
b"//textures/Bricks/brick_dotted_04-color.jpg",
|
||||
False,
|
||||
),
|
||||
# This data block is in there, but the image is packed, so it
|
||||
# shouldn't be in the results.
|
||||
# b'IMbrick_dotted_04-specular': Expect(
|
||||
# 'Image', 'name[1024]', None, None,
|
||||
# b'//textures/Bricks/brick_dotted_04-specular.jpg', False),
|
||||
b'IMbuildings_roof_04-color': Expect(
|
||||
'Image', 'name[1024]', None, None,
|
||||
b'//textures/Textures/Buildings/buildings_roof_04-color.png', False),
|
||||
b"IMbuildings_roof_04-color": Expect(
|
||||
"Image",
|
||||
"name[1024]",
|
||||
None,
|
||||
None,
|
||||
b"//textures/Textures/Buildings/buildings_roof_04-color.png",
|
||||
False,
|
||||
),
|
||||
}
|
||||
self.assert_deps('material_textures.blend', expects)
|
||||
self.assert_deps("material_textures.blend", expects)
|
||||
|
||||
def test_seq_image_sequence(self):
|
||||
expects = {
|
||||
b'-unnamed-': [
|
||||
Expect('Strip', None, 'dir[768]', 'name[256]', b'//imgseq/000210.png', True),
|
||||
|
||||
b"-unnamed-": [
|
||||
Expect(
|
||||
"Strip", None, "dir[768]", "name[256]", b"//imgseq/000210.png", True
|
||||
),
|
||||
# Video strip reference.
|
||||
Expect('Strip', None, 'dir[768]', 'name[256]',
|
||||
b'//../../../../cloud/pillar/testfiles/video-tiny.mkv', False),
|
||||
|
||||
Expect(
|
||||
"Strip",
|
||||
None,
|
||||
"dir[768]",
|
||||
"name[256]",
|
||||
b"//../../../../cloud/pillar/testfiles/video-tiny.mkv",
|
||||
False,
|
||||
),
|
||||
# The sound will be referenced twice, from the sequence strip and an SO data block.
|
||||
Expect('Strip', None, 'dir[768]', 'name[256]',
|
||||
b'//../../../../cloud/pillar/testfiles/video-tiny.mkv', False),
|
||||
Expect(
|
||||
"Strip",
|
||||
None,
|
||||
"dir[768]",
|
||||
"name[256]",
|
||||
b"//../../../../cloud/pillar/testfiles/video-tiny.mkv",
|
||||
False,
|
||||
),
|
||||
],
|
||||
b'SOvideo-tiny.mkv': Expect(
|
||||
'bSound', 'name[1024]', None, None,
|
||||
b'//../../../../cloud/pillar/testfiles/video-tiny.mkv', False),
|
||||
b"SOvideo-tiny.mkv": Expect(
|
||||
"bSound",
|
||||
"name[1024]",
|
||||
None,
|
||||
None,
|
||||
b"//../../../../cloud/pillar/testfiles/video-tiny.mkv",
|
||||
False,
|
||||
),
|
||||
}
|
||||
self.assert_deps('image_sequencer.blend', expects)
|
||||
self.assert_deps("image_sequencer.blend", expects)
|
||||
|
||||
# Test the filename expansion.
|
||||
expected = [self.blendfiles / ('imgseq/%06d.png' % num)
|
||||
for num in range(210, 215)]
|
||||
for dep in trace.deps(self.blendfiles / 'image_sequencer.blend'):
|
||||
if dep.block_name != b'SQ000210.png':
|
||||
expected = [
|
||||
self.blendfiles / ("imgseq/%06d.png" % num) for num in range(210, 215)
|
||||
]
|
||||
for dep in trace.deps(self.blendfiles / "image_sequencer.blend"):
|
||||
if dep.block_name != b"SQ000210.png":
|
||||
continue
|
||||
|
||||
actual = list(dep.files())
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
def test_block_cf(self):
|
||||
self.assert_deps('alembic-user.blend', {
|
||||
b'CFclothsim.abc': Expect('CacheFile', 'filepath[1024]', None, None,
|
||||
b'//clothsim.abc', False),
|
||||
})
|
||||
self.assert_deps(
|
||||
"alembic-user.blend",
|
||||
{
|
||||
b"CFclothsim.abc": Expect(
|
||||
"CacheFile", "filepath[1024]", None, None, b"//clothsim.abc", False
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
def test_alembic_sequence(self):
|
||||
self.assert_deps('alembic-sequence-user.blend', {
|
||||
b'CFclothsim_alembic':
|
||||
Expect('CacheFile', 'filepath[1024]', None, None, b'//clothsim.030.abc', True),
|
||||
})
|
||||
self.assert_deps(
|
||||
"alembic-sequence-user.blend",
|
||||
{
|
||||
b"CFclothsim_alembic": Expect(
|
||||
"CacheFile",
|
||||
"filepath[1024]",
|
||||
None,
|
||||
None,
|
||||
b"//clothsim.030.abc",
|
||||
True,
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
# Test the filename expansion.
|
||||
expected = [self.blendfiles / ('clothsim.%03d.abc' % num)
|
||||
for num in range(30, 36)]
|
||||
expected = [
|
||||
self.blendfiles / ("clothsim.%03d.abc" % num) for num in range(30, 36)
|
||||
]
|
||||
performed_test = False
|
||||
for dep in trace.deps(self.blendfiles / 'alembic-sequence-user.blend'):
|
||||
if dep.block_name != b'CFclothsim_alembic':
|
||||
for dep in trace.deps(self.blendfiles / "alembic-sequence-user.blend"):
|
||||
if dep.block_name != b"CFclothsim_alembic":
|
||||
continue
|
||||
|
||||
actual = list(dep.files())
|
||||
@@ -174,114 +229,241 @@ class DepsTest(AbstractTracerTest):
|
||||
self.assertTrue(performed_test)
|
||||
|
||||
def test_block_mc(self):
|
||||
self.assert_deps('movieclip.blend', {
|
||||
b'MCvideo.mov': Expect('MovieClip', 'name[1024]', None, None,
|
||||
b'//../../../../cloud/pillar/testfiles/video.mov', False),
|
||||
})
|
||||
self.assert_deps(
|
||||
"movieclip.blend",
|
||||
{
|
||||
b"MCvideo.mov": Expect(
|
||||
"MovieClip",
|
||||
"name[1024]",
|
||||
None,
|
||||
None,
|
||||
b"//../../../../cloud/pillar/testfiles/video.mov",
|
||||
False,
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
def test_block_me(self):
|
||||
self.assert_deps('multires_external.blend', {
|
||||
b'MECube': Expect('Mesh', 'filename[1024]', None, None, b'//Cube.btx', False),
|
||||
})
|
||||
self.assert_deps(
|
||||
"multires_external.blend",
|
||||
{
|
||||
b"MECube": Expect(
|
||||
"Mesh", "filename[1024]", None, None, b"//Cube.btx", False
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
def test_ocean(self):
|
||||
self.assert_deps('ocean_modifier.blend', {
|
||||
b'OBPlane.modifiers[0]': Expect('OceanModifierData', 'cachepath[1024]', None, None,
|
||||
b'//cache_ocean', True),
|
||||
})
|
||||
self.assert_deps(
|
||||
"ocean_modifier.blend",
|
||||
{
|
||||
b"OBPlane.modifiers[0]": Expect(
|
||||
"OceanModifierData",
|
||||
"cachepath[1024]",
|
||||
None,
|
||||
None,
|
||||
b"//cache_ocean",
|
||||
True,
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
def test_particles(self):
|
||||
# This file has an empty name for the cache, which should result in some hex magic
|
||||
# to create a name. See ptcache_filename() in pointcache.c.
|
||||
self.assert_deps('T55539-particles/particle.blend', {
|
||||
b'OBCube.modifiers[0]': Expect('PointCache', 'name[64]', None, None,
|
||||
b'//blendcache_particle/43756265_*.bphys', True),
|
||||
})
|
||||
self.assert_deps(
|
||||
"T55539-particles/particle.blend",
|
||||
{
|
||||
b"OBCube.modifiers[0]": Expect(
|
||||
"PointCache",
|
||||
"name[64]",
|
||||
None,
|
||||
None,
|
||||
b"//blendcache_particle/43756265_*.bphys",
|
||||
True,
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
def test_smoke(self):
|
||||
# This file has an empty name for the cache, which should result in some hex magic
|
||||
# to create a name. See ptcache_filename() in pointcache.c.
|
||||
self.assert_deps('T55542-smoke/smoke_cache.blend', {
|
||||
b'OBSmoke Domain.modifiers[0]': Expect('PointCache', 'name[64]', None, None,
|
||||
b'//blendcache_smoke_cache/536D6F6B6520446F6D61696E_*.bphys', True),
|
||||
})
|
||||
self.assert_deps('T55542-smoke/smoke_cache_vdb.blend', {
|
||||
b'OBSmoke Domain.modifiers[0]': Expect('PointCache', 'name[64]', None, None,
|
||||
b'//blendcache_smoke_cache_vdb/536D6F6B6520446F6D61696E_*.vdb', True),
|
||||
})
|
||||
self.assert_deps(
|
||||
"T55542-smoke/smoke_cache.blend",
|
||||
{
|
||||
b"OBSmoke Domain.modifiers[0]": Expect(
|
||||
"PointCache",
|
||||
"name[64]",
|
||||
None,
|
||||
None,
|
||||
b"//blendcache_smoke_cache/536D6F6B6520446F6D61696E_*.bphys",
|
||||
True,
|
||||
),
|
||||
},
|
||||
)
|
||||
self.assert_deps(
|
||||
"T55542-smoke/smoke_cache_vdb.blend",
|
||||
{
|
||||
b"OBSmoke Domain.modifiers[0]": Expect(
|
||||
"PointCache",
|
||||
"name[64]",
|
||||
None,
|
||||
None,
|
||||
b"//blendcache_smoke_cache_vdb/536D6F6B6520446F6D61696E_*.vdb",
|
||||
True,
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
def test_mesh_cache(self):
|
||||
self.assert_deps('meshcache-user.blend', {
|
||||
b'OBPlane.modifiers[0]': Expect('MeshCacheModifierData', 'filepath[1024]', None, None,
|
||||
b'//meshcache.mdd', False),
|
||||
})
|
||||
self.assert_deps(
|
||||
"meshcache-user.blend",
|
||||
{
|
||||
b"OBPlane.modifiers[0]": Expect(
|
||||
"MeshCacheModifierData",
|
||||
"filepath[1024]",
|
||||
None,
|
||||
None,
|
||||
b"//meshcache.mdd",
|
||||
False,
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
def test_block_vf(self):
|
||||
self.assert_deps('with_font.blend', {
|
||||
b'VFHack-Bold': Expect('VFont', 'name[1024]', None, None,
|
||||
b'/usr/share/fonts/truetype/hack/Hack-Bold.ttf', False),
|
||||
})
|
||||
self.assert_deps(
|
||||
"with_font.blend",
|
||||
{
|
||||
b"VFHack-Bold": Expect(
|
||||
"VFont",
|
||||
"name[1024]",
|
||||
None,
|
||||
None,
|
||||
b"/usr/share/fonts/truetype/hack/Hack-Bold.ttf",
|
||||
False,
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
def test_block_li(self):
|
||||
self.assert_deps('linked_cube.blend', {
|
||||
b'LILib': Expect('Library', 'name[1024]', None, None, b'//basic_file.blend', False),
|
||||
})
|
||||
self.assert_deps(
|
||||
"linked_cube.blend",
|
||||
{
|
||||
b"LILib": Expect(
|
||||
"Library", "name[1024]", None, None, b"//basic_file.blend", False
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
def test_deps_recursive(self):
|
||||
self.assert_deps('doubly_linked.blend', {
|
||||
b'LILib': {
|
||||
# From doubly_linked.blend
|
||||
Expect('Library', 'name[1024]', None, None, b'//linked_cube.blend', False),
|
||||
|
||||
# From linked_cube.blend
|
||||
Expect('Library', 'name[1024]', None, None, b'//basic_file.blend', False),
|
||||
self.assert_deps(
|
||||
"doubly_linked.blend",
|
||||
{
|
||||
b"LILib": {
|
||||
# From doubly_linked.blend
|
||||
Expect(
|
||||
"Library",
|
||||
"name[1024]",
|
||||
None,
|
||||
None,
|
||||
b"//linked_cube.blend",
|
||||
False,
|
||||
),
|
||||
# From linked_cube.blend
|
||||
Expect(
|
||||
"Library",
|
||||
"name[1024]",
|
||||
None,
|
||||
None,
|
||||
b"//basic_file.blend",
|
||||
False,
|
||||
),
|
||||
},
|
||||
b"LILib.002": Expect(
|
||||
"Library",
|
||||
"name[1024]",
|
||||
None,
|
||||
None,
|
||||
b"//material_textures.blend",
|
||||
False,
|
||||
),
|
||||
# From material_texture.blend
|
||||
b"IMbrick_dotted_04-bump": Expect(
|
||||
"Image",
|
||||
"name[1024]",
|
||||
None,
|
||||
None,
|
||||
b"//textures/Bricks/brick_dotted_04-bump.jpg",
|
||||
False,
|
||||
),
|
||||
b"IMbrick_dotted_04-color": Expect(
|
||||
"Image",
|
||||
"name[1024]",
|
||||
None,
|
||||
None,
|
||||
b"//textures/Bricks/brick_dotted_04-color.jpg",
|
||||
False,
|
||||
),
|
||||
# This data block is in the basic_file.blend file, but not used by
|
||||
# any of the objects linked in from linked_cube.blend or
|
||||
# doubly_linked.blend, hence it should *not* be reported:
|
||||
# b'IMbuildings_roof_04-color': Expect(
|
||||
# 'Image', 'name[1024]', None, None,
|
||||
# b'//textures/Textures/Buildings/buildings_roof_04-color.png', False),
|
||||
},
|
||||
b'LILib.002': Expect('Library', 'name[1024]', None, None,
|
||||
b'//material_textures.blend', False),
|
||||
|
||||
# From material_texture.blend
|
||||
b'IMbrick_dotted_04-bump': Expect(
|
||||
'Image', 'name[1024]', None, None,
|
||||
b'//textures/Bricks/brick_dotted_04-bump.jpg', False),
|
||||
b'IMbrick_dotted_04-color': Expect(
|
||||
'Image', 'name[1024]', None, None,
|
||||
b'//textures/Bricks/brick_dotted_04-color.jpg', False),
|
||||
|
||||
# This data block is in the basic_file.blend file, but not used by
|
||||
# any of the objects linked in from linked_cube.blend or
|
||||
# doubly_linked.blend, hence it should *not* be reported:
|
||||
# b'IMbuildings_roof_04-color': Expect(
|
||||
# 'Image', 'name[1024]', None, None,
|
||||
# b'//textures/Textures/Buildings/buildings_roof_04-color.png', False),
|
||||
})
|
||||
)
|
||||
|
||||
def test_geometry_nodes(self):
|
||||
self.assert_deps('geometry-nodes/file_to_pack.blend', {
|
||||
b'LInode_lib.blend': Expect(
|
||||
type='Library', full_field='name[1024]', dirname_field=None,
|
||||
basename_field=None, asset_path=b'//node_lib.blend', is_sequence=False),
|
||||
b'LIobject_lib.blend': Expect(
|
||||
type='Library', full_field='name[1024]', dirname_field=None,
|
||||
basename_field=None, asset_path=b'//object_lib.blend', is_sequence=False),
|
||||
})
|
||||
self.assert_deps(
|
||||
"geometry-nodes/file_to_pack.blend",
|
||||
{
|
||||
b"LInode_lib.blend": Expect(
|
||||
type="Library",
|
||||
full_field="name[1024]",
|
||||
dirname_field=None,
|
||||
basename_field=None,
|
||||
asset_path=b"//node_lib.blend",
|
||||
is_sequence=False,
|
||||
),
|
||||
b"LIobject_lib.blend": Expect(
|
||||
type="Library",
|
||||
full_field="name[1024]",
|
||||
dirname_field=None,
|
||||
basename_field=None,
|
||||
asset_path=b"//object_lib.blend",
|
||||
is_sequence=False,
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
def test_usage_abspath(self):
|
||||
deps = [dep for dep in trace.deps(self.blendfiles / 'doubly_linked.blend')
|
||||
if dep.asset_path == b'//material_textures.blend']
|
||||
deps = [
|
||||
dep
|
||||
for dep in trace.deps(self.blendfiles / "doubly_linked.blend")
|
||||
if dep.asset_path == b"//material_textures.blend"
|
||||
]
|
||||
usage = deps[0]
|
||||
|
||||
expect = self.blendfiles / 'material_textures.blend'
|
||||
expect = self.blendfiles / "material_textures.blend"
|
||||
self.assertEqual(expect, usage.abspath)
|
||||
|
||||
def test_sim_data(self):
|
||||
self.assert_deps('T53562/bam_pack_bug.blend', {
|
||||
b'OBEmitter.modifiers[0]': Expect(
|
||||
'PointCache', 'name[64]', None, None,
|
||||
b'//blendcache_bam_pack_bug/particles_*.bphys', True),
|
||||
})
|
||||
self.assert_deps(
|
||||
"T53562/bam_pack_bug.blend",
|
||||
{
|
||||
b"OBEmitter.modifiers[0]": Expect(
|
||||
"PointCache",
|
||||
"name[64]",
|
||||
None,
|
||||
None,
|
||||
b"//blendcache_bam_pack_bug/particles_*.bphys",
|
||||
True,
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
def test_recursion_loop(self):
|
||||
infinite_bfile = self.blendfiles / 'recursive_dependency_1.blend'
|
||||
infinite_bfile = self.blendfiles / "recursive_dependency_1.blend"
|
||||
|
||||
reclim = sys.getrecursionlimit()
|
||||
try:
|
||||
|
||||
@@ -6,7 +6,7 @@ from tests.test_tracer import AbstractTracerTest
|
||||
|
||||
class File2BlocksTest(AbstractTracerTest):
|
||||
def test_id_blocks(self):
|
||||
self.bf = blendfile.BlendFile(self.blendfiles / 'doubly_linked.blend')
|
||||
self.bf = blendfile.BlendFile(self.blendfiles / "doubly_linked.blend")
|
||||
|
||||
foreign_blocks = {}
|
||||
for block in file2blocks.iter_blocks(self.bf):
|
||||
@@ -18,19 +18,19 @@ class File2BlocksTest(AbstractTracerTest):
|
||||
self.assertNotEqual({}, foreign_blocks)
|
||||
# It should find directly linked blocks (GRCubes and MABrick) as well
|
||||
# as indirectly linked (MECube³).
|
||||
self.assertIn(b'GRCubes', foreign_blocks)
|
||||
self.assertIn(b'MABrick', foreign_blocks)
|
||||
self.assertIn('MECube³'.encode(), foreign_blocks)
|
||||
self.assertIn('OBümlaut'.encode(), foreign_blocks)
|
||||
self.assertIn(b"GRCubes", foreign_blocks)
|
||||
self.assertIn(b"MABrick", foreign_blocks)
|
||||
self.assertIn("MECube³".encode(), foreign_blocks)
|
||||
self.assertIn("OBümlaut".encode(), foreign_blocks)
|
||||
|
||||
def test_circular_files(self):
|
||||
self.bf = blendfile.BlendFile(self.blendfiles / 'recursive_dependency_1.blend')
|
||||
self.bf = blendfile.BlendFile(self.blendfiles / "recursive_dependency_1.blend")
|
||||
|
||||
blocks = {}
|
||||
for block in file2blocks.iter_blocks(self.bf):
|
||||
blocks[block.id_name] = block
|
||||
|
||||
self.assertNotEqual({}, blocks)
|
||||
self.assertIn(b'MAMaterial', blocks)
|
||||
self.assertIn(b'OBCube', blocks)
|
||||
self.assertIn(b'MECube', blocks)
|
||||
self.assertIn(b"MAMaterial", blocks)
|
||||
self.assertIn(b"OBCube", blocks)
|
||||
self.assertIn(b"MECube", blocks)
|
||||
|
||||
@@ -6,28 +6,29 @@ from blender_asset_tracer.trace import file_sequence
|
||||
class ExpandFileSequenceTest(AbstractBlendFileTest):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.imgseq = [self.blendfiles / ('imgseq/%06d.png' % num)
|
||||
for num in range(210, 215)]
|
||||
self.imgseq = [
|
||||
self.blendfiles / ("imgseq/%06d.png" % num) for num in range(210, 215)
|
||||
]
|
||||
|
||||
def test_glob(self):
|
||||
path = self.blendfiles / 'imgseq/*.png'
|
||||
path = self.blendfiles / "imgseq/*.png"
|
||||
actual = list(file_sequence.expand_sequence(path))
|
||||
self.assertEqual(self.imgseq, actual)
|
||||
|
||||
def test_first_file(self):
|
||||
path = self.blendfiles / 'imgseq/000210.png'
|
||||
path = self.blendfiles / "imgseq/000210.png"
|
||||
actual = list(file_sequence.expand_sequence(path))
|
||||
self.assertEqual(self.imgseq, actual)
|
||||
|
||||
def test_nonexistent(self):
|
||||
path = self.blendfiles / 'nonexistant'
|
||||
path = self.blendfiles / "nonexistant"
|
||||
with self.assertRaises(file_sequence.DoesNotExist) as raises:
|
||||
for result in file_sequence.expand_sequence(path):
|
||||
self.fail('unexpected result %r' % result)
|
||||
self.fail("unexpected result %r" % result)
|
||||
|
||||
self.assertEqual(path, raises.exception.path)
|
||||
|
||||
def test_non_sequence_file(self):
|
||||
path = self.blendfiles / 'imgseq/LICENSE.txt'
|
||||
path = self.blendfiles / "imgseq/LICENSE.txt"
|
||||
actual = list(file_sequence.expand_sequence(path))
|
||||
self.assertEqual([path], actual)
|
||||
|
||||
Reference in New Issue
Block a user