| from __future__ import annotations |
|
|
| import builtins |
| import importlib.util |
| import sys |
| import unittest |
| from pathlib import Path |
| from types import ModuleType |
| from unittest.mock import patch |
|
|
| import numpy as np |
|
|
|
|
| BACKEND_DIR = Path(__file__).resolve().parents[1] |
| AUDIO_GENERATOR_PATH = BACKEND_DIR / "audio_generator.py" |
|
|
|
|
| class AudioGeneratorFallbackTests(unittest.TestCase): |
| def _load_module_with_missing_runtime(self) -> ModuleType: |
| module_name = "audio_generator_missing_runtime_test" |
| spec = importlib.util.spec_from_file_location(module_name, AUDIO_GENERATOR_PATH) |
| if spec is None or spec.loader is None: |
| self.fail("Unable to load audio_generator module for fallback test.") |
|
|
| module = importlib.util.module_from_spec(spec) |
| sys.modules[module_name] = module |
| self.addCleanup(sys.modules.pop, module_name, None) |
|
|
| original_import = builtins.__import__ |
|
|
| def guarded_import( |
| name: str, |
| globals_: dict[str, object] | None = None, |
| locals_: dict[str, object] | None = None, |
| fromlist: tuple[str, ...] = (), |
| level: int = 0, |
| ) -> ModuleType: |
| blocked_roots = {"torch", "transformers", "omnivoice"} |
| if name.split(".", 1)[0] in blocked_roots: |
| raise ImportError(f"blocked import for test: {name}") |
| return original_import(name, globals_, locals_, fromlist, level) |
|
|
| with patch("builtins.__import__", side_effect=guarded_import): |
| spec.loader.exec_module(module) |
| return module |
|
|
| def test_generator_falls_back_to_mock_when_runtime_stack_is_missing(self) -> None: |
| module = self._load_module_with_missing_runtime() |
|
|
| generator = module.AudioGenerator() |
| audio = generator.generate("Fallback should keep the app alive.", duration=1.0) |
|
|
| self.assertIsNone(generator.model) |
| self.assertEqual(generator.runtime_backend, "mock") |
| self.assertEqual(generator.runtime_device, "cpu") |
| self.assertEqual(len(audio), 1) |
| self.assertIsInstance(audio[0], np.ndarray) |
| self.assertEqual(audio[0].shape[0], 24000) |
|
|
| def test_generator_can_fail_fast_when_real_runtime_is_required(self) -> None: |
| module = self._load_module_with_missing_runtime() |
|
|
| with patch.dict(module.os.environ, {"OMNIVOICE_REQUIRE_REAL_RUNTIME": "1"}, clear=False): |
| with self.assertRaises(RuntimeError): |
| module.AudioGenerator() |
|
|