Cleanup: reformat with Black

This commit is contained in:
Sybren A. Stüvel
2021-07-22 10:14:43 +02:00
parent 99389e8ece
commit 803c38dac1
52 changed files with 2587 additions and 1782 deletions
+3 -3
View File
@@ -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
View File
@@ -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)
+4 -4
View File
@@ -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
View File
@@ -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)
+30 -26
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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,
)
+5 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+4 -4
View File
@@ -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)
+30 -21
View File
@@ -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
View File
@@ -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:
+9 -9
View File
@@ -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)
+8 -7
View File
@@ -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)