Import the vocalinux source tree (tray app, Next.js site in web/, packaging, installers, docs and tests) on top of the Gitea template commit. The project README replaces the template README, and the template CI files (.gitea/workflows/ci.yml, .jscpd.json, CI.md) are kept.
112 lines
4.5 KiB
Python
112 lines
4.5 KiB
Python
"""Tests for PostProcessor and apply_post_processing."""
|
|
|
|
import subprocess
|
|
import unittest
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
from vocalinux.post_processor import PostProcessor, apply_post_processing
|
|
from vocalinux.utils.host_process import host_env
|
|
|
|
|
|
def _make_run_result(returncode: int = 0, stdout: str = "", stderr: str = "") -> MagicMock:
|
|
result = MagicMock()
|
|
result.returncode = returncode
|
|
result.stdout = stdout
|
|
result.stderr = stderr
|
|
return result
|
|
|
|
|
|
class TestPostProcessor(unittest.TestCase):
|
|
def setUp(self) -> None:
|
|
self.patch_run = patch("vocalinux.post_processor.subprocess.run")
|
|
self.mock_run = self.patch_run.start()
|
|
|
|
def tearDown(self) -> None:
|
|
self.patch_run.stop()
|
|
|
|
def test_convenience_newline_stripped_when_input_had_none(self) -> None:
|
|
self.mock_run.return_value = _make_run_result(stdout="hello\n")
|
|
result = PostProcessor("/script.sh").process("hello")
|
|
self.assertEqual(result, "hello")
|
|
|
|
def test_internal_newlines_preserved(self) -> None:
|
|
self.mock_run.return_value = _make_run_result(stdout="a\nb\n")
|
|
result = PostProcessor("/script.sh").process("a\nb")
|
|
self.assertEqual(result, "a\nb")
|
|
|
|
def test_paragraph_break_preserved_verbatim(self) -> None:
|
|
"""A passthrough script must not eat the "\n\n" from "new paragraph"."""
|
|
self.mock_run.return_value = _make_run_result(stdout="end of paragraph.\n\n")
|
|
result = PostProcessor("/script.sh").process("end of paragraph.\n\n")
|
|
self.assertEqual(result, "end of paragraph.\n\n")
|
|
|
|
def test_script_added_paragraph_survives_convenience_strip(self) -> None:
|
|
"""Only one trailing newline is treated as a line terminator."""
|
|
self.mock_run.return_value = _make_run_result(stdout="a\n\n")
|
|
result = PostProcessor("/script.sh").process("a")
|
|
self.assertEqual(result, "a\n")
|
|
|
|
def test_empty_stdout_returns_empty_string(self) -> None:
|
|
self.mock_run.return_value = _make_run_result(stdout="")
|
|
result = PostProcessor("/script.sh").process("original")
|
|
self.assertEqual(result, "")
|
|
|
|
def test_nonzero_exit_returns_original(self) -> None:
|
|
self.mock_run.return_value = _make_run_result(returncode=1, stderr="oops")
|
|
result = PostProcessor("/script.sh").process("original")
|
|
self.assertEqual(result, "original")
|
|
|
|
def test_timeout_returns_original(self) -> None:
|
|
self.mock_run.side_effect = subprocess.TimeoutExpired(cmd="/script.sh", timeout=10)
|
|
result = PostProcessor("/script.sh").process("original")
|
|
self.assertEqual(result, "original")
|
|
|
|
def test_os_error_returns_original(self) -> None:
|
|
self.mock_run.side_effect = OSError("not found")
|
|
result = PostProcessor("/script.sh").process("original")
|
|
self.assertEqual(result, "original")
|
|
|
|
def test_subprocess_called_with_correct_args(self) -> None:
|
|
self.mock_run.return_value = _make_run_result(stdout="out")
|
|
PostProcessor("/my/script.sh").process("input text")
|
|
self.mock_run.assert_called_once_with(
|
|
["/my/script.sh"],
|
|
input="input text",
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=10,
|
|
env=host_env(),
|
|
)
|
|
|
|
|
|
class TestApplyPostProcessing(unittest.TestCase):
|
|
def setUp(self) -> None:
|
|
self.patch_processor = patch("vocalinux.post_processor.PostProcessor")
|
|
self.mock_processor_cls = self.patch_processor.start()
|
|
self.mock_processor = MagicMock()
|
|
self.mock_processor_cls.return_value = self.mock_processor
|
|
|
|
def tearDown(self) -> None:
|
|
self.patch_processor.stop()
|
|
|
|
def _config(self, script_path: str) -> MagicMock:
|
|
config_manager = MagicMock()
|
|
config_manager.get_str.return_value = script_path
|
|
return config_manager
|
|
|
|
def test_no_script_returns_original_text(self) -> None:
|
|
config_manager = self._config("")
|
|
result = apply_post_processing("hello", config_manager)
|
|
self.assertEqual(result, "hello")
|
|
self.mock_processor_cls.assert_not_called()
|
|
|
|
def test_script_configured_returns_processed_text(self) -> None:
|
|
self.mock_processor.process.return_value = "modified"
|
|
result = apply_post_processing("hello", self._config("/s.sh"))
|
|
self.assertEqual(result, "modified")
|
|
|
|
def test_script_returns_empty_string_gives_none(self) -> None:
|
|
self.mock_processor.process.return_value = ""
|
|
result = apply_post_processing("hello", self._config("/s.sh"))
|
|
self.assertIsNone(result)
|