"""Run with: blender --background --factory-startup --python tests/blender_smoke.py""" import importlib.util import sys import types from pathlib import Path import bpy ROOT = Path(__file__).resolve().parents[1] PACKAGE_NAME = "blender_se_palette" spec = importlib.util.spec_from_file_location( PACKAGE_NAME, ROOT / "__init__.py", submodule_search_locations=[str(ROOT)], ) addon = importlib.util.module_from_spec(spec) sys.modules[PACKAGE_NAME] = addon spec.loader.exec_module(addon) addon.register() try: assert hasattr(addon.panels, "SEPALETTE_PT_main") scene = bpy.context.scene props = scene.se_palette props.folder = "/home/hare/Assets/SE/" count = addon.operators.populate_items(props) assert count == 42, f"Expected 42 sounds, got {count}" valid_icons = bpy.types.UILayout.bl_rna.functions["operator"].parameters["icon"].enum_items.keys() for icon, *_rest in addon.operators.ICON_ITEMS: assert icon in valid_icons, f"Unknown Blender icon: {icon}" # Headless Blender has no OpenAL output device, but the same decoder used by # the preview operator must still be able to load the selected file. import aud aud.Sound(props.items[0].filepath) # Repeated previews must reuse a single output device instead of leaking # one OpenAL device per button press. real_aud = sys.modules["aud"] created_devices = [] class FakeHandle: status = 1 def stop(self): self.status = 3 class FakeDevice: def __init__(self): created_devices.append(self) def play(self, sound): return FakeHandle() def stopAll(self): pass sys.modules["aud"] = types.SimpleNamespace( Device=FakeDevice, Sound=lambda filepath: filepath, STATUS_PLAYING=1, ) try: addon.operators.stop_preview(release_device=True) addon.operators._play_preview(props.items[0].filepath) first_device = addon.operators._preview_device addon.operators.stop_preview() addon.operators._play_preview(props.items[1].filepath) assert addon.operators._preview_device is first_device assert len(created_devices) == 1 finally: addon.operators.stop_preview(release_device=True) sys.modules["aud"] = real_aud result = bpy.ops.se_palette.insert(filepath=props.items[0].filepath) assert result == {"FINISHED"}, result assert len(scene.sequence_editor.strips_all) == 1 strip = scene.sequence_editor.active_strip assert strip.type == "SOUND" assert strip.channel == props.channel assert Path(strip.sound.filepath).name == props.items[0].filename finally: addon.unregister() print("SE_PALETTE_BLENDER_SMOKE_OK")