From b23561d863d961fb85183f686ec29f08c4a72fc3 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 20 Aug 2019 13:12:18 +0200 Subject: [PATCH] tests: add unit tests for Python loader file types --- tests/pgraph/test_loader_types.py | 211 ++++++++++++++++++++++++++++++ 1 file changed, 211 insertions(+) create mode 100644 tests/pgraph/test_loader_types.py diff --git a/tests/pgraph/test_loader_types.py b/tests/pgraph/test_loader_types.py new file mode 100644 index 0000000000..3107a628cc --- /dev/null +++ b/tests/pgraph/test_loader_types.py @@ -0,0 +1,211 @@ +from panda3d.core import LoaderFileTypeRegistry, ModelRoot, Loader, LoaderOptions +import pytest +import tempfile +import os +from contextlib import contextmanager + + +@pytest.fixture +def test_filename(): + """Fixture returning a filename to an existent .test file.""" + fp = tempfile.NamedTemporaryFile(suffix='.test', delete=False) + fp.write(b"test") + fp.close() + yield fp.name + os.unlink(fp.name) + + +@pytest.fixture +def test_pz_filename(): + """Fixture returning a filename to an existent .test.pz file.""" + fp = tempfile.NamedTemporaryFile(suffix='.test.pz', delete=False) + fp.write(b"test") + fp.close() + yield fp.name + os.unlink(fp.name) + + +@contextmanager +def registered_type(type): + """Convenience method allowing use of register_type in a with block.""" + registry = LoaderFileTypeRegistry.get_global_ptr() + registry.register_type(type) + yield + registry.unregister_type(type) + + +class DummyLoader: + """The simplest possible successful LoaderFileType.""" + + extensions = ["test"] + + @staticmethod + def load_file(path, options, record=None): + return ModelRoot("loaded") + + +def test_loader_invalid(): + """Tests that registering a malformed loader fails.""" + + class MissingExtensionsLoader: + pass + + class InvalidTypeExtensionsLoader: + extensions = "abc" + + class EmptyExtensionsLoader: + extensions = [] + + class InvalidExtensionsLoader: + extensions = [123, None] + + registry = LoaderFileTypeRegistry.get_global_ptr() + + with pytest.raises(Exception): + registry.register_type("invalid") + + with pytest.raises(Exception): + registry.register_type(MissingExtensionsLoader) + + with pytest.raises(TypeError): + registry.register_type(InvalidTypeExtensionsLoader) + + with pytest.raises(ValueError): + registry.register_type(EmptyExtensionsLoader) + + with pytest.raises(TypeError): + registry.register_type(InvalidExtensionsLoader) + + +def test_loader_success(test_filename): + """Tests that a normal dummy loader successfully loads.""" + + with registered_type(DummyLoader): + model = Loader.get_global_ptr().load_sync(test_filename, LoaderOptions(LoaderOptions.LF_no_cache)) + assert model is not None + assert model.name == "loaded" + + +def test_loader_extensions(test_filename): + """Tests multi-extension loaders.""" + + class MultiExtensionLoader: + extensions = ["test1", "teSt2"] + + @staticmethod + def load_file(path, options, record=None): + return ModelRoot("loaded") + + fp1 = tempfile.NamedTemporaryFile(suffix='.test1', delete=False) + fp1.write(b"test1") + fp1.close() + fp2 = tempfile.NamedTemporaryFile(suffix='.TEST2', delete=False) + fp2.write(b"test2") + fp2.close() + + try: + with registered_type(MultiExtensionLoader): + model1 = Loader.get_global_ptr().load_sync(fp1.name, LoaderOptions(LoaderOptions.LF_no_cache)) + assert model1 is not None + assert model1.name == "loaded" + + model2 = Loader.get_global_ptr().load_sync(fp2.name, LoaderOptions(LoaderOptions.LF_no_cache)) + assert model2 is not None + assert model2.name == "loaded" + finally: + os.unlink(fp1.name) + os.unlink(fp2.name) + + # Ensure that both were unregistered. + registry = LoaderFileTypeRegistry.get_global_ptr() + assert not registry.get_type_from_extension("test1") + assert not registry.get_type_from_extension("test2") + + +def test_loader_nonexistent(): + """Verifies that non-existent files fail before calling load_file.""" + flag = [False] + + class AssertiveLoader: + extensions = ["test"] + + @staticmethod + def load_file(path, options, record=None): + flag[0] = True + assert False, "should never get here" + + with registered_type(AssertiveLoader): + model = Loader.get_global_ptr().load_sync("/non-existent", LoaderOptions(LoaderOptions.LF_no_cache)) + assert model is None + assert not flag[0] + + +def test_loader_exception(test_filename): + """Tests for a loader that raises an exception.""" + + class FailingLoader: + extensions = ["test"] + + @staticmethod + def load_file(path, options, record=None): + raise Exception("test error") + + with registered_type(FailingLoader): + model = Loader.get_global_ptr().load_sync(test_filename, LoaderOptions(LoaderOptions.LF_no_cache)) + assert model is None + + +def test_loader_compressed(test_pz_filename): + """Tests for loading .pz files and the supports_compressed flag.""" + + class TestLoader: + extensions = ["test"] + + @staticmethod + def load_file(path, options, record=None): + return ModelRoot("loaded") + + # Test with property absent + with registered_type(TestLoader): + model = Loader.get_global_ptr().load_sync(test_pz_filename, LoaderOptions(LoaderOptions.LF_no_cache)) + assert model is None + + # Test with property False, should give same result + TestLoader.supports_compressed = False + with registered_type(TestLoader): + model = Loader.get_global_ptr().load_sync(test_pz_filename, LoaderOptions(LoaderOptions.LF_no_cache)) + assert model is None + + # Test with property True, should work + TestLoader.supports_compressed = True + with registered_type(TestLoader): + model = Loader.get_global_ptr().load_sync(test_pz_filename, LoaderOptions(LoaderOptions.LF_no_cache)) + assert model is not None + assert model.name == "loaded" + + # Test with property invalid type, should not register + TestLoader.supports_compressed = None + with pytest.raises(TypeError): + LoaderFileTypeRegistry.get_global_ptr().register_type(TestLoader) + + +def test_loader_ram_cache(test_filename): + """Tests that the Python loader plug-ins write to the RAM cache.""" + + # Ensure a clean slate. + from panda3d.core import ModelPool + ModelPool.release_all_models() + + with registered_type(DummyLoader): + model1 = Loader.get_global_ptr().load_sync(test_filename, LoaderOptions(LoaderOptions.LF_no_disk_cache | LoaderOptions.LF_allow_instance)) + assert model1 is not None + assert model1.name == "loaded" + + assert ModelPool.has_model(test_filename) + assert ModelPool.get_model(test_filename, True) == model1 + + model2 = Loader.get_global_ptr().load_sync(test_filename, LoaderOptions(LoaderOptions.LF_cache_only | LoaderOptions.LF_allow_instance)) + assert model2 is not None + assert model1 == model2 + + ModelPool.release_model(model2)