Rewritten in python, functionality updated. Weather service improved, now closer to I38's method.
This commit is contained in:
1 parent
f2d1fafc94
commit
38889ded77
26 files changed
+2334
-747
No files matched your search
@@ -0,0 +1,97 @@
|
||||
"""Speech discovery is read-only; all dispatcher connections here are mocked."""
|
||||
import json
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import tempfile
|
||||
from types import SimpleNamespace
|
||||
import unittest
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
|
||||
from talking_clock.config import Config, Settings
|
||||
from talking_clock.speech import connect, discover, matching_voices, system_language
|
||||
|
||||
|
||||
class SpeechTests(unittest.TestCase):
|
||||
def test_locale_precedence_encoding_and_c_fallback(self):
|
||||
self.assertEqual(system_language({"LC_ALL": "de_DE.UTF-8", "LANG": "en_US.UTF-8"}), "de-de")
|
||||
self.assertEqual(system_language({"LC_ALL": "C.UTF-8", "LANGUAGE": "es_MX:en"}), "es-mx")
|
||||
self.assertEqual(system_language({"LC_MESSAGES": "fr_CA.UTF-8@modifier", "LANG": "en_US.UTF-8"}), "fr-ca")
|
||||
self.assertEqual(system_language({"LANG": "pt_BR.UTF-8"}), "pt-br")
|
||||
self.assertEqual(system_language({"LC_ALL": "POSIX", "LANG": "C"}), "en")
|
||||
|
||||
def test_voice_filter_prefers_region_and_falls_back_to_language(self):
|
||||
voices = (("British", "en-gb", ""), ("American", "en_US", ""),
|
||||
("French", "fr", ""), ("Unknown", None, None),
|
||||
("British", "en-gb", "other"), ("English", "EN", ""))
|
||||
self.assertEqual(matching_voices(voices, "en-us"), ("American",))
|
||||
self.assertEqual(matching_voices(voices, "en-au"), ("American", "British", "English"))
|
||||
self.assertEqual(matching_voices(voices, "en"), ("English",))
|
||||
self.assertEqual(matching_voices(voices, "fr-fr"), ("French",))
|
||||
self.assertEqual(matching_voices(voices, "es"), ())
|
||||
|
||||
def test_discovery_queries_selected_module_and_closes_without_speaking(self):
|
||||
client = Mock()
|
||||
client.list_output_modules.return_value = ("rhvoice", "espeak-ng")
|
||||
client.list_synthesis_voices.return_value = (("American", "en-us", ""), ("French", "fr", ""))
|
||||
with patch("talking_clock.speech.connect", return_value=client), patch(
|
||||
"talking_clock.speech.system_language", return_value="en-us"):
|
||||
catalog = discover("espeak-ng")
|
||||
self.assertEqual(catalog.modules, ("espeak-ng", "rhvoice"))
|
||||
self.assertEqual(catalog.voices, ("American",))
|
||||
client.set_output_module.assert_called_once_with("espeak-ng")
|
||||
client.close.assert_called_once()
|
||||
client.speak.assert_not_called()
|
||||
|
||||
def test_system_default_module_is_queried_without_overriding_it(self):
|
||||
client = Mock()
|
||||
client.list_output_modules.return_value = ("espeak-ng",)
|
||||
client.get_output_module.return_value = "espeak-ng"
|
||||
client.list_synthesis_voices.return_value = ()
|
||||
with patch("talking_clock.speech.connect", return_value=client):
|
||||
self.assertEqual(discover().module, "espeak-ng")
|
||||
client.set_output_module.assert_not_called()
|
||||
client.speak.assert_not_called()
|
||||
|
||||
def test_connection_does_not_autostart_dispatcher(self):
|
||||
module = SimpleNamespace(SSIPClient=Mock())
|
||||
with patch.dict(sys.modules, {"speechd": module}):
|
||||
connect()
|
||||
module.SSIPClient.assert_called_once_with("talking-clock", component="settings", autospawn=False)
|
||||
|
||||
def test_unavailable_dispatcher_and_module_are_reported(self):
|
||||
with patch("talking_clock.speech.connect", side_effect=OSError("offline")):
|
||||
with self.assertRaisesRegex(RuntimeError, "offline"):
|
||||
discover()
|
||||
client = Mock()
|
||||
client.list_output_modules.return_value = ()
|
||||
with patch("talking_clock.speech.connect", return_value=client):
|
||||
with self.assertRaisesRegex(RuntimeError, "unavailable"):
|
||||
discover("missing")
|
||||
client.close.assert_called_once()
|
||||
|
||||
def test_numeric_limits_and_saved_intermediate_values(self):
|
||||
for key in ("rate", "pitch", "volume"):
|
||||
for value in (-100, -37, 0, 37, 100):
|
||||
settings = Settings(**{key: value})
|
||||
self.assertIs(settings.validate(), settings)
|
||||
for value in (-101, 101, "10", True):
|
||||
with self.assertRaises(ValueError):
|
||||
Settings(**{key: value}).validate()
|
||||
|
||||
def test_old_python_speech_preferences_migrate_to_dispatcher_defaults(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
config = Config(directory)
|
||||
config.path.write_text(json.dumps({"voice": "custom", "voice_command": "some old command",
|
||||
"rhvoice": "alan", "gama_path": "/old", "font_size": 100}))
|
||||
settings = config.load()
|
||||
self.assertEqual(settings.module, "")
|
||||
self.assertEqual(settings.voice, "")
|
||||
self.assertEqual(settings.font_size, 100)
|
||||
config.save(settings)
|
||||
data = json.loads(config.path.read_text())
|
||||
self.assertFalse(set(data) & {"voice_command", "rhvoice", "gama_path"})
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user