feat(identity): rename user-visible identity to voculinux with one-time legacy import

The fork takes its own name everywhere a user can see it: binaries
(voculinux, voculinux-gui), desktop entry, autostart, tray, D-Bus
namespace (com.voculinux.Voculinux, no alias for the old name),
config/data/cache dirs, single-instance lock, IBus engine identity,
tray icons, resource prefixes, VOCULINUX_* environment names, and the
update-checker user agent. The internal Python module stays vocalinux
and the distribution metadata keeps the upstream URLs as provenance.

On first run a previous Vocalinux install's config.json, custom
dictionary, and portal restore token are copied once into the new
locations (new config always wins, no merge, no write-back); models
re-resolve through the pinned-checksum download path. An offline
naming-sweep test proves no old-name strings remain on user-visible
surfaces outside the allowlist.

Refs #18
This commit is contained in:
soubarnak
2026-11-06 01:29:39 +05:30
parent c565e6d946
commit ea7be5f111
251 changed files with 2449 additions and 1991 deletions
+18 -18
View File
@@ -197,7 +197,7 @@ class TestTextInjector(unittest.TestCase):
def test_wayland_wtype_fail_xdotool_when_uinput_blocked(self) -> None:
"""ydotool in PATH without /dev/uinput still cannot rescue a wtype miss."""
with patch.dict(
"os.environ", {"XDG_SESSION_TYPE": "wayland", "SNAP": "/snap/vocalinux/x1"}
"os.environ", {"XDG_SESSION_TYPE": "wayland", "SNAP": "/snap/voculinux/x1"}
):
self.mock_which.side_effect = lambda cmd: {
"wtype": "/usr/bin/wtype",
@@ -298,7 +298,7 @@ class TestTextInjector(unittest.TestCase):
injector.inject_text("Hello world")
# Clipboard paste fails (no wl-copy/xclip/xsel in this mock); type fallback.
# Default key-delay is 2 (overridable via VOCALINUX_YDOTOOL_KEY_DELAY).
# Default key-delay is 2 (overridable via VOCULINUX_YDOTOOL_KEY_DELAY).
self.mock_subprocess.assert_any_call(
["ydotool", "type", "--key-delay", "2", "--", "Hello world"],
check=True,
@@ -891,7 +891,7 @@ class TestTextInjector(unittest.TestCase):
"""Flatpak pins ydotool 1.0.4; always use keycode:value form."""
mock_which.return_value = "/app/bin/ydotool"
injector = TextInjector.__new__(TextInjector)
with patch.dict("os.environ", {"FLATPAK_ID": "com.vocalinux.Vocalinux"}):
with patch.dict("os.environ", {"FLATPAK_ID": "com.voculinux.Voculinux"}):
cmd = injector._ydotool_ctrl_v_command()
self.assertEqual(cmd, ["ydotool", "key", "29:1", "47:1", "47:0", "29:0"])
@@ -3516,7 +3516,7 @@ class TestBackendPreference(unittest.TestCase):
def test_environment_overrides_config(self):
"""A one-off experiment must not require editing the user's config."""
with _fake_config({"text_injection": {"backend": "ibus"}}):
with patch.dict("os.environ", {"VOCALINUX_FORCE_BACKEND": "wtype"}):
with patch.dict("os.environ", {"VOCULINUX_FORCE_BACKEND": "wtype"}):
self.assertEqual(TextInjector._backend_preference(), "wtype")
def test_config_used_when_environment_unset(self):
@@ -3530,14 +3530,14 @@ class TestBackendPreference(unittest.TestCase):
self.assertEqual(TextInjector._backend_preference(), "auto")
def test_explicit_auto_overrides_a_saved_pin(self):
"""``VOCALINUX_FORCE_BACKEND=auto`` asks for autodetection *this run*.
"""``VOCULINUX_FORCE_BACKEND=auto`` asks for autodetection *this run*.
It must not be read as "nothing was set" and fall through to the saved
pin, or the variable cannot undo a pin for a single run -- which is the
one-off A/B test it exists for.
"""
with _fake_config({"text_injection": {"backend": "wtype"}}):
with patch.dict("os.environ", {"VOCALINUX_FORCE_BACKEND": "auto"}):
with patch.dict("os.environ", {"VOCULINUX_FORCE_BACKEND": "auto"}):
self.assertEqual(TextInjector._backend_preference(), "auto")
def test_unset_environment_uses_the_saved_pin(self):
@@ -3554,7 +3554,7 @@ class TestBackendPreference(unittest.TestCase):
``test_explicit_auto_overrides_a_saved_pin``, where they did ask.
"""
with _fake_config({"text_injection": {"backend": "wtype"}}):
with patch.dict("os.environ", {"VOCALINUX_FORCE_BACKEND": "wtpye"}):
with patch.dict("os.environ", {"VOCULINUX_FORCE_BACKEND": "wtpye"}):
self.assertEqual(TextInjector._backend_preference(), "wtype")
@@ -3562,7 +3562,7 @@ class TestSelectableBackends(unittest.TestCase):
"""xdotool is a pinnable value, and both readers accept the same set."""
def test_xdotool_is_accepted_from_the_environment(self):
with patch.dict("os.environ", {"VOCALINUX_FORCE_BACKEND": "xdotool"}):
with patch.dict("os.environ", {"VOCULINUX_FORCE_BACKEND": "xdotool"}):
self.assertEqual(TextInjector._forced_backend_setting(), "xdotool")
def test_xdotool_is_accepted_from_config(self):
@@ -3572,7 +3572,7 @@ class TestSelectableBackends(unittest.TestCase):
def test_both_readers_accept_the_same_values(self):
"""A value valid in one source must be valid in the other."""
for value in TextInjector._SELECTABLE_BACKENDS:
with patch.dict("os.environ", {"VOCALINUX_FORCE_BACKEND": value}):
with patch.dict("os.environ", {"VOCULINUX_FORCE_BACKEND": value}):
self.assertEqual(TextInjector._forced_backend_setting(), value)
with _fake_config({"text_injection": {"backend": value}}):
self.assertEqual(TextInjector._configured_backend(), value)
@@ -3586,7 +3586,7 @@ class TestSelectableBackends(unittest.TestCase):
class TestForcedBackendSetting(unittest.TestCase):
"""The raw three-state reading of VOCALINUX_FORCE_BACKEND.
"""The raw three-state reading of VOCULINUX_FORCE_BACKEND.
``_forced_backend()`` collapses unset and explicit ``auto`` together, which
is fine for its own callers but loses the distinction ``_backend_preference()``
@@ -3599,40 +3599,40 @@ class TestForcedBackendSetting(unittest.TestCase):
def test_explicit_auto_is_the_string_not_none(self):
for value in ("auto", " AUTO "):
with patch.dict("os.environ", {"VOCALINUX_FORCE_BACKEND": value}):
with patch.dict("os.environ", {"VOCULINUX_FORCE_BACKEND": value}):
self.assertEqual(TextInjector._forced_backend_setting(), "auto")
def test_empty_value_counts_as_unset(self):
with patch.dict("os.environ", {"VOCALINUX_FORCE_BACKEND": " "}):
with patch.dict("os.environ", {"VOCULINUX_FORCE_BACKEND": " "}):
self.assertIsNone(TextInjector._forced_backend_setting())
def test_recognised_backends_are_returned(self):
for value in ("ibus", "wtype", "ydotool", "xdotool"):
with patch.dict("os.environ", {"VOCALINUX_FORCE_BACKEND": value.upper()}):
with patch.dict("os.environ", {"VOCULINUX_FORCE_BACKEND": value.upper()}):
self.assertEqual(TextInjector._forced_backend_setting(), value)
def test_unknown_value_is_unset_rather_than_auto(self):
"""See ``test_typo_in_environment_is_treated_as_unset_not_as_auto``."""
with patch.dict("os.environ", {"VOCALINUX_FORCE_BACKEND": "ibsu"}):
with patch.dict("os.environ", {"VOCULINUX_FORCE_BACKEND": "ibsu"}):
self.assertIsNone(TextInjector._forced_backend_setting())
class TestForcedBackend(unittest.TestCase):
"""VOCALINUX_FORCE_BACKEND overrides backend autodetection."""
"""VOCULINUX_FORCE_BACKEND overrides backend autodetection."""
def test_unset_or_auto_means_auto(self):
for value in ("", "auto", " AUTO "):
with patch.dict("os.environ", {"VOCALINUX_FORCE_BACKEND": value}):
with patch.dict("os.environ", {"VOCULINUX_FORCE_BACKEND": value}):
self.assertEqual(TextInjector._forced_backend(), "auto")
def test_recognised_backends(self):
for value in ("ibus", "wtype", "ydotool", "xdotool"):
with patch.dict("os.environ", {"VOCALINUX_FORCE_BACKEND": value.upper()}):
with patch.dict("os.environ", {"VOCULINUX_FORCE_BACKEND": value.upper()}):
self.assertEqual(TextInjector._forced_backend(), value)
def test_unknown_value_falls_back_to_auto(self):
"""A typo must not silently pin the wrong backend."""
with patch.dict("os.environ", {"VOCALINUX_FORCE_BACKEND": "ibsu"}):
with patch.dict("os.environ", {"VOCULINUX_FORCE_BACKEND": "ibsu"}):
self.assertEqual(TextInjector._forced_backend(), "auto")
def test_missing_variable_means_auto(self):