feat(app-binding): lier les presets aux applications

Ajoute la configuration des associations application/preset, le service de suivi du focus, l'intégration GUI et les fichiers d'installation nécessaires.

Ajoute les tests unitaires ciblés des bindings, du service de focus, des migrations et des composants GUI associés.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-19 23:52:34 +02:00
parent 0ab77d2a64
commit 9ef2c1439c
42 changed files with 4353 additions and 12 deletions

View File

@ -19,10 +19,12 @@
# along with input-remapper. If not, see <https://www.gnu.org/licenses/>.
import json
import os
import unittest
from inputremapper.configs.global_config import GlobalConfig
from inputremapper.logging.logger import VERSION
from tests.lib.test_setup import test_setup
@ -81,8 +83,16 @@ class TestGlobalConfig(unittest.TestCase):
self.assertTrue(os.path.exists(global_config.path))
with open(global_config.path, "r") as file:
contents = file.read()
self.assertIn('"autoload": {}', contents)
config = json.load(file)
self.assertEqual(
config,
{
"version": VERSION,
"autoload": {},
"app_binding_enabled": False,
"app_bindings": [],
},
)
def test_save_load(self):
global_config = GlobalConfig()
@ -101,6 +111,71 @@ class TestGlobalConfig(unittest.TestCase):
[("d1", "a"), ("d2", "b")],
)
# -- app bindings ------------------------------------------------------
def test_app_binding_enabled_default_false(self):
global_config = GlobalConfig()
self.assertFalse(global_config.get_app_binding_enabled())
def test_set_app_binding_enabled_roundtrip(self):
global_config = GlobalConfig()
global_config.set_app_binding_enabled(True)
self.assertTrue(global_config.get_app_binding_enabled())
# persisted and reloadable
reloaded = GlobalConfig()
reloaded.load_config()
self.assertTrue(reloaded.get_app_binding_enabled())
global_config.set_app_binding_enabled(False)
self.assertFalse(global_config.get_app_binding_enabled())
def test_app_bindings_default_empty(self):
global_config = GlobalConfig()
self.assertEqual(global_config.get_app_bindings(), [])
def test_set_app_bindings_roundtrip(self):
from inputremapper.configs.app_binding import (
AppBinding,
BoundPreset,
MatchType,
)
bindings = [
AppBinding(
app_id="firefox",
match=MatchType.wm_class,
presets=[BoundPreset(group_key="Foo Device", preset="preset1")],
),
AppBinding(app_id="term.*", match=MatchType.title_regex),
]
global_config = GlobalConfig()
global_config.set_app_bindings(bindings)
# serialized as plain dicts in config.json
with open(global_config.path, "r") as file:
raw = json.load(file)
self.assertEqual(len(raw["app_bindings"]), 2)
self.assertEqual(raw["app_bindings"][0]["app_id"], "firefox")
# reloaded into model objects
reloaded = GlobalConfig()
reloaded.load_config()
result = reloaded.get_app_bindings()
self.assertEqual(result, bindings)
def test_get_app_bindings_skips_invalid(self):
global_config = GlobalConfig()
global_config.load_config()
# inject an invalid binding (empty app_id) directly into the raw config
global_config._config["app_bindings"] = [
{"app_id": "valid", "match": "wm_class", "presets": []},
{"app_id": "", "match": "wm_class", "presets": []},
]
result = global_config.get_app_bindings()
self.assertEqual(len(result), 1)
self.assertEqual(result[0].app_id, "valid")
if __name__ == "__main__":
unittest.main()