mirror of
https://github.com/yusufipk/dikte.git
synced 2026-09-11 19:06:11 +00:00
570 tests over the standard library's unittest, so there is nothing to install beyond the PyQt6 the application already needs. They reach neither the network, the microphone, nor the real ~/.config/dikte: urllib is faked at one function, the tools are faked at shutil.which, and every test is handed its own config and data directory. What they hold onto is what a change is most likely to move without meaning to. The request each provider is sent, field by field. A settings window that loads a value into a widget and writes it back, which is where a setting added to one half and not the other is silently reset. The dictation chain end to end: what is transcribed, what is pasted, what lands in the history, and what happens to the audio afterwards. A config file written by an older version. A meeting whose two channels heard the same sentence. 59 of them carry @linux_only, because they cover what Dikte is on this desktop rather than what it does: PipeWire, wl-clipboard, ydotool, KDE's shortcut file. The other 511 pass on any platform, and that line is worth holding as the ports arrive. CONTRIBUTING.md says how to run them, what support.py offers, and the three things about this codebase that trip up a new test.
123 lines
4.8 KiB
Python
123 lines
4.8 KiB
Python
"""Translation lookup, and the table itself.
|
||
|
||
The table test is the one that matters for a pull request: a Turkish string
|
||
whose placeholder was renamed raises KeyError at the moment the message is
|
||
shown, which is exactly when nobody is watching a terminal.
|
||
"""
|
||
|
||
import string
|
||
import unittest
|
||
from unittest import mock
|
||
|
||
import i18n
|
||
from tests.support import DikteTest
|
||
|
||
|
||
def placeholders(text):
|
||
return {name for _, name, _, _ in string.Formatter().parse(text) if name}
|
||
|
||
|
||
class Resolve(unittest.TestCase):
|
||
def test_an_explicit_language_wins(self):
|
||
with mock.patch.dict("os.environ", {"LANG": "tr_TR.UTF-8"}):
|
||
self.assertEqual(i18n.resolve("en"), "en")
|
||
self.assertEqual(i18n.resolve("tr"), "tr")
|
||
|
||
def test_auto_reads_the_locale(self):
|
||
with mock.patch.dict("os.environ", {"LANG": "tr_TR.UTF-8"}, clear=True):
|
||
self.assertEqual(i18n.resolve("auto"), "tr")
|
||
with mock.patch.dict("os.environ", {"LANG": "en_GB.UTF-8"}, clear=True):
|
||
self.assertEqual(i18n.resolve("auto"), "en")
|
||
|
||
def test_lc_all_outranks_lang(self):
|
||
with mock.patch.dict("os.environ",
|
||
{"LC_ALL": "tr_TR.UTF-8", "LANG": "en_GB.UTF-8"},
|
||
clear=True):
|
||
self.assertEqual(i18n.resolve("auto"), "tr")
|
||
|
||
def test_no_locale_at_all_falls_back_to_english(self):
|
||
with mock.patch.dict("os.environ", {}, clear=True):
|
||
self.assertEqual(i18n.resolve("auto"), "en")
|
||
|
||
def test_an_unknown_code_is_not_taken_at_its_word(self):
|
||
with mock.patch.dict("os.environ", {}, clear=True):
|
||
self.assertEqual(i18n.resolve("de"), "en")
|
||
|
||
|
||
class Translate(DikteTest):
|
||
def test_english_returns_the_source_string(self):
|
||
self.assertEqual(i18n.t("Quit"), "Quit")
|
||
|
||
def test_turkish_looks_the_string_up(self):
|
||
i18n.set_language("tr")
|
||
self.assertEqual(i18n.t("Quit"), "Çık")
|
||
|
||
def test_an_untranslated_string_falls_through(self):
|
||
i18n.set_language("tr")
|
||
self.assertEqual(i18n.t("Nobody translated this"), "Nobody translated this")
|
||
|
||
def test_placeholders_are_filled_in_both_languages(self):
|
||
self.assertEqual(i18n.t("Unknown key: {key}", key="f13"), "Unknown key: f13")
|
||
i18n.set_language("tr")
|
||
self.assertIn("f13", i18n.t("Unknown key: {key}", key="f13"))
|
||
|
||
def test_a_string_with_no_arguments_is_not_formatted(self):
|
||
# Braces in the text itself must survive when nothing is passed in.
|
||
self.assertEqual(i18n.t("{not a placeholder}"), "{not a placeholder}")
|
||
|
||
def test_a_placeholder_may_be_called_anything(self):
|
||
"""Including the names of t()'s own parameters, which is why they are
|
||
positional-only: worker.py says {text}, and that has to work."""
|
||
self.assertEqual(i18n.t("Discarded: {text}", text="hello"), "Discarded: hello")
|
||
self.assertEqual(i18n.name("Claude", case="dative"), "Claude")
|
||
|
||
|
||
class Names(DikteTest):
|
||
def test_english_leaves_the_name_alone(self):
|
||
self.assertEqual(i18n.name("Claude", "dative"), "Claude")
|
||
|
||
def test_turkish_inflects_by_the_vowels_of_the_name(self):
|
||
i18n.set_language("tr")
|
||
self.assertEqual(i18n.name("Claude", "dative"), "Claude'a")
|
||
self.assertEqual(i18n.name("Codex", "dative"), "Codex'e")
|
||
self.assertEqual(i18n.name("OpenRouter", "accusative"), "OpenRouter'ı")
|
||
|
||
def test_no_case_asked_for(self):
|
||
i18n.set_language("tr")
|
||
self.assertEqual(i18n.name("Claude"), "Claude")
|
||
|
||
def test_an_unlisted_name_or_case_comes_back_unchanged(self):
|
||
i18n.set_language("tr")
|
||
self.assertEqual(i18n.name("Ollama", "dative"), "Ollama")
|
||
self.assertEqual(i18n.name("Claude", "ablative"), "Claude")
|
||
|
||
|
||
class Table(unittest.TestCase):
|
||
"""The Turkish table against the English strings it stands in for."""
|
||
|
||
def test_every_translation_keeps_the_placeholders_of_its_source(self):
|
||
for source, translated in i18n.TR.items():
|
||
with self.subTest(source=source[:50]):
|
||
self.assertEqual(
|
||
placeholders(source), placeholders(translated),
|
||
"the Turkish string does not take the same arguments",
|
||
)
|
||
|
||
def test_nothing_is_translated_to_an_empty_string(self):
|
||
for source, translated in i18n.TR.items():
|
||
with self.subTest(source=source[:50]):
|
||
self.assertTrue(translated.strip())
|
||
|
||
def test_every_translation_is_formattable(self):
|
||
"""Whatever the table holds, .format() must not blow up on it."""
|
||
for source, translated in i18n.TR.items():
|
||
names = placeholders(translated)
|
||
if not names:
|
||
continue
|
||
with self.subTest(source=source[:50]):
|
||
translated.format(**{key: "x" for key in names})
|
||
|
||
|
||
if __name__ == "__main__":
|
||
unittest.main()
|