diff options
author | Sybren A. Stüvel <sybren@blender.org> | 2020-08-17 13:55:23 +0300 |
---|---|---|
committer | Sybren A. Stüvel <sybren@blender.org> | 2020-08-17 15:07:45 +0300 |
commit | ad459302ce603b7503137251e8fecdd1a2009482 (patch) | |
tree | 58f856719216bc710340b77d4a8db77fb9bf4c61 /tests/python/alembic_export_tests.py | |
parent | 4a2ff0fef89765f1e0f766b066e63a8988df5e98 (diff) |
Cleanup: rename `alembic_tests.py` to `alembic_tests_export.py`
Rename `alembic_tests.py` to `alembic_tests_export.py`, as this makes it
clearer what is being tested.
No functional changes.
Diffstat (limited to 'tests/python/alembic_export_tests.py')
-rw-r--r-- | tests/python/alembic_export_tests.py | 538 |
1 files changed, 538 insertions, 0 deletions
diff --git a/tests/python/alembic_export_tests.py b/tests/python/alembic_export_tests.py new file mode 100644 index 00000000000..448048d0f17 --- /dev/null +++ b/tests/python/alembic_export_tests.py @@ -0,0 +1,538 @@ +#!/usr/bin/env python3 +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +import argparse +import pathlib +import subprocess +import sys +import unittest + +from modules.test_utils import ( + with_tempdir, + AbstractBlenderRunnerTest, +) + + +class AbcPropError(Exception): + """Raised when AbstractAlembicTest.abcprop() finds an error.""" + + +class AbstractAlembicTest(AbstractBlenderRunnerTest): + @classmethod + def setUpClass(cls): + import re + + cls.blender = args.blender + cls.testdir = pathlib.Path(args.testdir) + cls.alembic_root = pathlib.Path(args.alembic_root) + + # 'abcls' outputs ANSI colour codes, even when stdout is not a terminal. + # See https://github.com/alembic/alembic/issues/120 + cls.ansi_remove_re = re.compile(rb'\x1b[^m]*m') + + # 'abcls' array notation, like "name[16]" + cls.abcls_array = re.compile(r'^(?P<name>[^\[]+)(\[(?P<arraysize>\d+)\])?$') + + def abcprop(self, filepath: pathlib.Path, proppath: str) -> dict: + """Uses abcls to obtain compound property values from an Alembic object. + + A dict of subproperties is returned, where the values are Python values. + + The Python bindings for Alembic are old, and only compatible with Python 2.x, + so that's why we can't use them here, and have to rely on other tooling. + """ + import collections + + abcls = self.alembic_root / 'bin' / 'abcls' + + command = (str(abcls), '-vl', '%s%s' % (filepath, proppath)) + proc = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + timeout=30) + + coloured_output = proc.stdout + output = self.ansi_remove_re.sub(b'', coloured_output).decode('utf8') + + # Because of the ANSI colour codes, we need to remove those first before + # decoding to text. This means that we cannot use the universal_newlines + # parameter to subprocess.run(), and have to do the conversion ourselves + output = output.replace('\r\n', '\n').replace('\r', '\n') + + if proc.returncode: + raise AbcPropError('Error %d running %s:\n%s' % (proc.returncode, ' '.join(command), output)) + + # Mapping from value type to callable that can convert a string to Python values. + converters = { + 'bool_t': int, + 'uint8_t': int, + 'int16_t': int, + 'int32_t': int, + 'uint32_t': int, + 'uint64_t': int, + 'float64_t': float, + 'float32_t': float, + } + + result = {} + + # Ideally we'd get abcls to output JSON, see https://github.com/alembic/alembic/issues/121 + lines = collections.deque(output.split('\n')) + while lines: + info = lines.popleft() + if not info: + continue + parts = info.split() + proptype = parts[0] + + if proptype == 'CompoundProperty': + # To read those, call self.abcprop() on it. + continue + + try: + valtype_and_arrsize, name_and_extent = parts[1:] + except ValueError as ex: + raise ValueError('Error parsing result from abcprop "{info.strip()}": {ex}') from ex + + # Parse name and extent + m = self.abcls_array.match(name_and_extent) + if not m: + self.fail('Unparsable name/extent from abcls: %s' % name_and_extent) + name, extent = m.group('name'), m.group('arraysize') + + if extent != '1': + self.fail('Unsupported extent %s for property %s/%s' % (extent, proppath, name)) + + # Parse type + m = self.abcls_array.match(valtype_and_arrsize) + if not m: + self.fail('Unparsable value type from abcls: %s' % valtype_and_arrsize) + valtype, scalarsize = m.group('name'), m.group('arraysize') + + # Convert values + try: + conv = converters[valtype] + except KeyError: + self.fail('Unsupported type %s for property %s/%s' % (valtype, proppath, name)) + + def convert_single_line(linevalue): + try: + if scalarsize is None: + return conv(linevalue) + else: + return [conv(v.strip()) for v in linevalue.split(',')] + except ValueError as ex: + return str(ex) + + if proptype == 'ScalarProperty': + value = lines.popleft() + result[name] = convert_single_line(value) + elif proptype == 'ArrayProperty': + arrayvalue = [] + # Arrays consist of a variable number of items, and end in a blank line. + while True: + linevalue = lines.popleft() + if not linevalue: + break + arrayvalue.append(convert_single_line(linevalue)) + result[name] = arrayvalue + else: + self.fail('Unsupported type %s for property %s/%s' % (proptype, proppath, name)) + + return result + + def assertAlmostEqualFloatArray(self, actual, expect, places=6, delta=None): + """Asserts that the arrays of floats are almost equal.""" + + self.assertEqual(len(actual), len(expect), + 'Actual array has %d items, expected %d' % (len(actual), len(expect))) + + for idx, (act, exp) in enumerate(zip(actual, expect)): + self.assertAlmostEqual(act, exp, places=places, delta=delta, + msg='%f != %f at index %d' % (act, exp, idx)) + + +class HierarchicalAndFlatExportTest(AbstractAlembicTest): + @with_tempdir + def test_hierarchical_export(self, tempdir: pathlib.Path): + abc = tempdir / 'cubes_hierarchical.abc' + script = "import bpy; bpy.ops.wm.alembic_export(filepath='%s', start=1, end=1, " \ + "renderable_only=True, visible_objects_only=True, flatten=False)" % abc.as_posix() + self.run_blender('cubes-hierarchy.blend', script) + + # Now check the resulting Alembic file. + xform = self.abcprop(abc, '/Cube/Cube_002/Cube_012/.xform') + self.assertEqual(1, xform['.inherits']) + self.assertAlmostEqualFloatArray( + xform['.vals'], + [1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 3.07484, -2.92265, 0.0586434, 1.0] + ) + + @with_tempdir + def test_flat_export(self, tempdir: pathlib.Path): + abc = tempdir / 'cubes_flat.abc' + script = "import bpy; bpy.ops.wm.alembic_export(filepath='%s', start=1, end=1, " \ + "renderable_only=True, visible_objects_only=True, flatten=True)" % abc.as_posix() + self.run_blender('cubes-hierarchy.blend', script) + + # Now check the resulting Alembic file. + xform = self.abcprop(abc, '/Cube_012/.xform') + self.assertEqual(1, xform['.inherits'], "Blender transforms always inherit") + + self.assertAlmostEqualFloatArray( + xform['.vals'], + [0.343134, 0.485243, 0.804238, 0, + 0.0, 0.856222, -0.516608, 0, + -0.939287, 0.177266, 0.293799, 0, + 1, 3, 4, 1], + ) + + +class DupliGroupExportTest(AbstractAlembicTest): + @with_tempdir + def test_hierarchical_export(self, tempdir: pathlib.Path): + abc = tempdir / 'dupligroup_hierarchical.abc' + script = "import bpy; bpy.ops.wm.alembic_export(filepath='%s', start=1, end=1, " \ + "renderable_only=True, visible_objects_only=True, flatten=False)" % abc.as_posix() + self.run_blender('dupligroup-scene.blend', script) + + # Now check the resulting Alembic file. + xform = self.abcprop(abc, '/Real_Cube/Linked_Suzanne/Cylinder-0/Suzanne-1/.xform') + self.assertEqual(1, xform['.inherits']) + self.assertAlmostEqualFloatArray( + xform['.vals'], + [1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 2.0, 0.0, 1.0] + ) + + @with_tempdir + def test_flat_export(self, tempdir: pathlib.Path): + abc = tempdir / 'dupligroup_hierarchical.abc' + script = "import bpy; bpy.ops.wm.alembic_export(filepath='%s', start=1, end=1, " \ + "renderable_only=True, visible_objects_only=True, flatten=True)" % abc.as_posix() + self.run_blender('dupligroup-scene.blend', script) + + # Now check the resulting Alembic file. + xform = self.abcprop(abc, '/Suzanne-1/.xform') + self.assertEqual(1, xform['.inherits']) + + self.assertAlmostEqualFloatArray( + xform['.vals'], + [1.5, 0.0, 0.0, 0.0, + 0.0, 1.5, 0.0, 0.0, + 0.0, 0.0, 1.5, 0.0, + 2.0, 3.0, 0.0, 1.0] + ) + + @with_tempdir + def test_multiple_duplicated_hierarchies(self, tempdir: pathlib.Path): + abc = tempdir / "multiple-duplicated-hierarchies.abc" + script = "import bpy; bpy.ops.wm.alembic_export(filepath='%s', start=1, end=1)" % abc.as_posix() + self.run_blender('multiple-duplicated-hierarchies.blend', script) + + # This is the expected hierarchy: + # ABC + # `--Triangle + # |--Triangle + # |--Empty-1 + # | `--Pole-1-0 + # | |--Pole + # | `--Block-1-1 + # | `--Block + # |--Empty + # | `--Pole-0 + # | |--Pole + # | `--Block-1 + # | `--Block + # |--Empty-2 + # | `--Pole-2-0 + # | |--Pole + # | `--Block-2-1 + # | `--Block + # `--Empty-0 + # `--Pole-0-0 + # |--Pole + # `--Block-0-1 + # `--Block + + # Now check the resulting Alembic file. + xform = self.abcprop(abc, '/Triangle/Empty-1/Pole-1-0/Block-1-1/.xform') + self.assertEqual(1, xform['.inherits']) + self.assertAlmostEqualFloatArray( + xform['.vals'], + [1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 2.0, 0.0, 1.0] + ) + + # If the property can be gotten, the hierarchy is okay. No need to actually check each xform. + self.abcprop(abc, '/Triangle/.xform') + self.abcprop(abc, '/Triangle/Empty-1/.xform') + self.abcprop(abc, '/Triangle/Empty-1/Pole-1-0/.xform') + self.abcprop(abc, '/Triangle/Empty-1/Pole-1-0/Block-1-1/.xform') + self.abcprop(abc, '/Triangle/Empty/.xform') + self.abcprop(abc, '/Triangle/Empty/Pole-0/.xform') + self.abcprop(abc, '/Triangle/Empty/Pole-0/Block-1/.xform') + self.abcprop(abc, '/Triangle/Empty-2/.xform') + self.abcprop(abc, '/Triangle/Empty-2/Pole-2-0/.xform') + self.abcprop(abc, '/Triangle/Empty-2/Pole-2-0/Block-2-1/.xform') + self.abcprop(abc, '/Triangle/Empty-0/.xform') + self.abcprop(abc, '/Triangle/Empty-0/Pole-0-0/.xform') + self.abcprop(abc, '/Triangle/Empty-0/Pole-0-0/Block-0-1/.xform') + + +class CurveExportTest(AbstractAlembicTest): + @with_tempdir + def test_export_single_curve(self, tempdir: pathlib.Path): + abc = tempdir / 'single-curve.abc' + script = "import bpy; bpy.ops.wm.alembic_export(filepath='%s', start=1, end=1, " \ + "renderable_only=True, visible_objects_only=True, flatten=False)" % abc.as_posix() + self.run_blender('single-curve.blend', script) + + # Now check the resulting Alembic file. + abcprop = self.abcprop(abc, '/NurbsCurve/CurveData/.geom') + self.assertEqual(abcprop['.orders'], [4]) + + abcprop = self.abcprop(abc, '/NurbsCurve/CurveData/.geom/.userProperties') + self.assertEqual(abcprop['blender:resolution'], 10) + + +class HairParticlesExportTest(AbstractAlembicTest): + """Tests exporting with/without hair/particles. + + Just a basic test to ensure that the enabling/disabling works, and that export + works at all. NOT testing the quality/contents of the exported file. + """ + + def _do_test(self, tempdir: pathlib.Path, export_hair: bool, export_particles: bool) -> pathlib.Path: + abc = tempdir / 'hair-particles.abc' + script = "import bpy; bpy.ops.wm.alembic_export(filepath='%s', start=1, end=1, " \ + "renderable_only=True, visible_objects_only=True, flatten=False, " \ + "export_hair=%r, export_particles=%r, as_background_job=False)" \ + % (abc.as_posix(), export_hair, export_particles) + self.run_blender('hair-particles.blend', script) + return abc + + @with_tempdir + def test_with_both(self, tempdir: pathlib.Path): + abc = self._do_test(tempdir, True, True) + + abcprop = self.abcprop(abc, '/Suzanne/Hair_system/.geom') + self.assertIn('nVertices', abcprop) + + abcprop = self.abcprop(abc, '/Suzanne/Non-hair_particle_system/.geom') + self.assertIn('.velocities', abcprop) + + abcprop = self.abcprop(abc, '/Suzanne/MonkeyMesh/.geom') + self.assertIn('.faceIndices', abcprop) + + @with_tempdir + def test_with_hair_only(self, tempdir: pathlib.Path): + abc = self._do_test(tempdir, True, False) + + abcprop = self.abcprop(abc, '/Suzanne/Hair_system/.geom') + self.assertIn('nVertices', abcprop) + + self.assertRaises(AbcPropError, self.abcprop, abc, + '/Suzanne/Non-hair_particle_system/.geom') + + abcprop = self.abcprop(abc, '/Suzanne/MonkeyMesh/.geom') + self.assertIn('.faceIndices', abcprop) + + @with_tempdir + def test_with_particles_only(self, tempdir: pathlib.Path): + abc = self._do_test(tempdir, False, True) + + self.assertRaises(AbcPropError, self.abcprop, abc, '/Suzanne/Hair_system/.geom') + + abcprop = self.abcprop(abc, '/Suzanne/Non-hair_particle_system/.geom') + self.assertIn('.velocities', abcprop) + + abcprop = self.abcprop(abc, '/Suzanne/MonkeyMesh/.geom') + self.assertIn('.faceIndices', abcprop) + + @with_tempdir + def test_with_neither(self, tempdir: pathlib.Path): + abc = self._do_test(tempdir, False, False) + + self.assertRaises(AbcPropError, self.abcprop, abc, '/Suzanne/Hair_system/.geom') + self.assertRaises(AbcPropError, self.abcprop, abc, + '/Suzanne/Non-hair_particle_system/.geom') + + abcprop = self.abcprop(abc, '/Suzanne/MonkeyMesh/.geom') + self.assertIn('.faceIndices', abcprop) + + +class UVMapExportTest(AbstractAlembicTest): + @with_tempdir + def test_uvmap_export(self, tempdir: pathlib.Path): + """Minimal test for exporting multiple UV maps on an animated mesh. + + This covers the issue reported in T77021. + """ + basename = 'T77021-multiple-uvmaps-animated-mesh' + abc = tempdir / f'{basename}.abc' + script = f"import bpy; bpy.ops.wm.alembic_export(filepath='{abc.as_posix()}', start=1, end=1, " \ + f"renderable_only=True, visible_objects_only=True, flatten=False)" + self.run_blender(f'{basename}.blend', script) + + self.maxDiff = 1000 + + # The main UV map should be written to .geom + abcprop = self.abcprop(abc, '/Cube/Cube/.geom/uv') + self.assertEqual(abcprop['.vals'], [ + [0.625, 0.75], + [0.875, 0.75], + [0.875, 0.5], + [0.625, 0.5], + [0.375, 1.0], + [0.625, 1.0], + [0.375, 0.75], + [0.375, 0.25], + [0.625, 0.25], + [0.625, 0.0], + [0.375, 0.0], + [0.125, 0.75], + [0.375, 0.5], + [0.125, 0.5], + ]) + + # The second UV map should be written to .arbGeomParams + abcprop = self.abcprop(abc, '/Cube/Cube/.geom/.arbGeomParams/Secondary') + self.assertEqual(abcprop['.vals'], [ + [0.75, 0.375], + [0.75, 0.125], + [0.5, 0.125], + [0.5, 0.375], + [1.0, 0.625], + [1.0, 0.375], + [0.75, 0.625], + [0.25, 0.625], + [0.25, 0.375], + [0.0, 0.375], + [0.0, 0.625], + [0.75, 0.875], + [0.5, 0.625], + [0.5, 0.875], + ]) + + +class LongNamesExportTest(AbstractAlembicTest): + @with_tempdir + def test_export_long_names(self, tempdir: pathlib.Path): + abc = tempdir / 'long-names.abc' + script = "import bpy; bpy.ops.wm.alembic_export(filepath='%s', start=1, end=1, " \ + "renderable_only=False, visible_objects_only=False, flatten=False)" % abc.as_posix() + self.run_blender('long-names.blend', script) + + name_parts = [ + 'foG9aeLahgoh5goacee1dah6Hethaghohjaich5pasizairuWigee1ahPeekiGh', + 'yoNgoisheedah2ua0eigh2AeCaiTee5bo0uphoo7Aixephah9racahvaingeeH4', + 'zuthohnoi1thooS3eezoo8seuph2Boo5aefacaethuvee1aequoonoox1sookie', + 'wugh4ciTh3dipiepeequait5uug7thiseek5ca7Eijei5ietaizokohhaecieto', + 'up9aeheenein9oteiX6fohP3thiez6Ahvah0oohah1ep2Eesho4Beboechaipoh', + 'coh4aehiacheTh0ue0eegho9oku1lohl4loht9ohPoongoow7dasiego6yimuis', + 'lohtho8eigahfeipohviepajaix4it2peeQu6Iefee1nevihaes4cee2soh4noy', + 'kaht9ahv0ieXaiyih7ohxe8bah7eeyicahjoa2ohbu7Choxua7oongah6sei4bu', + 'deif0iPaechohkee5nahx6oi2uJeeN7ze3seunohJibe4shai0mah5Iesh3Quai', + 'ChohDahshooNee0NeNohthah0eiDeese3Vu6ohShil1Iey9ja0uebi2quiShae6', + 'Dee1kai7eiph2ahh2nufah3zai3eexeengohQue1caj0eeW0xeghi3eshuadoot', + 'aeshiup3aengajoog0AhCoo5tiu3ieghaeGhie4Tu1ohh1thee8aepheingah1E', + 'ooRa6ahciolohshaifoopeo9ZeiGhae2aech4raisheiWah9AaNga0uas9ahquo', + 'thaepheip2aip6shief4EaXopei8ohPo0ighuiXah2ashowai9nohp4uach6Mei', + 'ohph4yaev3quieji3phophiem3OoNuisheepahng4waithae3Naichai7aw3noo', + 'aibeawaneBahmieyuph8ieng8iopheereeD2uu9Uyee5bei2phahXeir8eeJ8oo', + 'ooshahphei2hoh3uth5chaen7ohsai6uutiesucheichai8ungah9Gie1Aiphie', + 'eiwohchoo7ere2iebohn4Aapheichaelooriiyaoxaik7ooqua7aezahx0aeJei', + 'Vah0ohgohphiefohTheshieghichaichahch5moshoo0zai5eeva7eisi4yae8T', + 'EibeeN0fee0Gohnguz8iec6yeigh7shuNg4eingu3siph9joucahpeidoom4ree', + 'iejiu3shohheeZahHusheimeefaihoh5eecachu5eeZie9ceisugu9taidohT3U', + 'eex6dilakaix5Eetai7xiCh5Jaa8aiD4Ag3tuij1aijohv5fo0heevah8hohs3m', + 'ohqueeNgahraew6uraemohtoo5qua3oojiex6ohqu6Aideibaithaiphuriquie', + 'cei0eiN4Shiey7Aeluy3unohboo5choiphahc2mahbei5paephaiKeso1thoog1', + 'ieghif4ohKequ7ong0jah5ooBah0eiGh1caechahnahThae9Shoo0phopashoo4', + 'roh9er3thohwi5am8iequeequuSh3aic0voocai3ihi5nie2abahphupiegh7vu', + 'uv3Quei7wujoo5beingei2aish5op4VaiX0aebai7iwoaPee5pei8ko9IepaPig', + 'co7aegh5beitheesi9lu7jeeQu3johgeiphee9cheichi8aithuDehu2gaeNein', + 'thai3Tiewoo4nuir1ohy4aithiuZ7shae1luuwei5phibohriepe2paeci1Ach8', + 'phoi3ribah7ufuvoh8eigh1oB6deeBaiPohphaghiPieshahfah5EiCi3toogoo', + 'aiM8geil7ooreinee4Cheiwea4yeec8eeshi7Sei4Shoo3wu6ohkaNgooQu1mai', + 'agoo3faciewah9ZeesiXeereek7am0eigaeShie3Tisu8haReeNgoo0ci2Hae5u', + 'Aesatheewiedohshaephaenohbooshee8eu7EiJ8isal1laech2eiHo0noaV3ta', + 'liunguep3ooChoo4eir8ahSie8eenee0oo1TooXu8Cais8Aimo4eir6Phoo3xei', + 'toe9heepeobein3teequachemei0Cejoomef9ujie3ohwae9AiNgiephi3ep0de', + 'ua6xooY9uzaeB3of6sheiyaedohoiS5Eev0Aequ9ahm1zoa5Aegh3ooz9ChahDa', + 'eevasah6Bu9wi7EiwiequumahkaeCheegh6lui8xoh4eeY4ieneavah8phaibun', + 'AhNgei2sioZeeng6phaecheemeehiShie5eFeiTh6ooV8iiphabud0die4siep4', + 'kushe6Xieg6ahQuoo9aex3aipheefiec1esa7OhBuG0ueziep9phai5eegh1vie', + 'Jie5yu8aafuQuoh9shaep3moboh3Pooy7och8oC6obeik6jaew2aiLooweib3ch', + 'ohohjajaivaiRail3odaimei6aekohVaicheip2wu7phieg5Gohsaing2ahxaiy', + 'hahzaht6yaiYu9re9jah9loisiit4ahtoh2quoh9xohishioz4oo4phofu3ogha', + 'pu4oorea0uh2tahB8aiZoonge1aophaes6ogaiK9ailaigeej4zoVou8ielotee', + 'cae2thei3Luphuqu0zeeG8leeZuchahxaicai4ui4Eedohte9uW6gae8Geeh0ea', + 'air7tuy7ohw5sho2Tahpai8aep4so5ria7eaShus5weaqu0Naquei2xaeyoo2ae', + 'vohge4aeCh7ahwoo7Jaex6sohl0Koong4Iejisei8Coir0iemeiz9uru9Iebaep', + 'aepeidie8aiw6waish9gie4Woolae2thuj5phae4phexux7gishaeph4Deu7ooS', + 'vahc5ia0xohHooViT0uyuxookiaquu2ogueth0ahquoudeefohshai8aeThahba', + 'mun3oagah2eequaenohfoo8DaigeghoozaV2eiveeQuee7kah0quaa6tiesheet', + 'ooSet4IdieC4ugow3za0die4ohGoh1oopoh6luaPhaeng4Eechea1hae0eimie5', + 'iedeimadaefu2NeiPaey2jooloov5iehiegeakoo4ueso7aeK9ahqu2Thahkaes', + 'nahquah9Quuu2uuf0aJah7eishi2siegh8ue5eiJa2EeVu8ebohkepoh4dahNgo', + 'io1bie7chioPiej5ae2oohe2fee6ooP2thaeJohjohb9Se8tang3eipaifeimai', + 'oungoqu6dieneejiechez1xeD2Zi9iox2Ahchaiy9ithah3ohVoolu2euQuuawo', + 'thaew0veigei4neishohd8mecaixuqu7eeshiex1chaigohmoThoghoitoTa0Eo', + 'ahroob2phohvaiz0Ohteik2ohtakie6Iu1vitho8IyiyeeleeShae9defaiw9ki', + 'DohHoothohzeaxolai3Toh5eJie7ahlah9reF0ohn1chaipoogain2aibahw4no', + 'aif8lo5she4aich5cho2rie8ieJaujeem2Joongeedae4vie3tah1Leequaix1O', + 'Aang0Shaih6chahthie1ahZ7aewei9thiethee7iuThah3yoongi8ahngiobaa5', + 'iephoBuayoothah0Ru6aichai4aiw8deg1umongauvaixai3ohy6oowohlee8ei', + 'ohn5shigoameer0aejohgoh8oChohlaecho9jie6shu0ahg9Bohngau6paevei9', + 'edahghaishak0paigh1eecuich3aad7yeB0ieD6akeeliem2beifufaekee6eat', + 'hiechahgheloh2zo7Ieghaiph0phahhu8aeyuiKie1xeipheech9zai4aeme0ee', + 'Cube' + ] + name = '/' + '/'.join(name_parts) + + # Now check the resulting Alembic file. + abcprop = self.abcprop(abc, '%s/.xform' % name) + self.assertEqual(abcprop['.vals'], [ + 1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 3.0, 0.0, 1.0, + ]) + + abcprop = self.abcprop(abc, '%s/Cube/.geom' % name) + self.assertIn('.faceCounts', abcprop) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--blender', required=True) + parser.add_argument('--testdir', required=True) + parser.add_argument('--alembic-root', required=True) + args, remaining = parser.parse_known_args() + + unittest.main(argv=sys.argv[0:1] + remaining) |