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>
793 lines
28 KiB
Python
793 lines
28 KiB
Python
#
|
|
# input-remapper is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# input-remapper is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with input-remapper. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
import json
|
|
import os
|
|
import shutil
|
|
import unittest
|
|
from packaging import version
|
|
|
|
from evdev.ecodes import (
|
|
EV_KEY,
|
|
EV_ABS,
|
|
ABS_HAT0X,
|
|
ABS_X,
|
|
ABS_Y,
|
|
ABS_RX,
|
|
ABS_RY,
|
|
EV_REL,
|
|
REL_X,
|
|
REL_Y,
|
|
REL_WHEEL_HI_RES,
|
|
REL_HWHEEL_HI_RES,
|
|
)
|
|
|
|
from inputremapper.configs.input_config import InputCombination, InputConfig
|
|
from inputremapper.configs.mapping import UIMapping
|
|
from inputremapper.configs.migrations import Migrations
|
|
from inputremapper.configs.paths import PathUtils
|
|
from inputremapper.configs.preset import Preset
|
|
from inputremapper.injection.global_uinputs import GlobalUInputs, UInput
|
|
from inputremapper.logging.logger import VERSION
|
|
from inputremapper.user import UserUtils
|
|
from tests.lib.test_setup import test_setup
|
|
from tests.lib.tmp import tmp
|
|
|
|
|
|
@test_setup
|
|
class TestMigrations(unittest.TestCase):
|
|
def setUp(self):
|
|
# some extra care to ensure those tests are not destroying actual presets
|
|
self.assertTrue(UserUtils.home.startswith("/tmp"))
|
|
self.assertTrue(PathUtils.config_path().startswith("/tmp"))
|
|
self.assertTrue(PathUtils.get_preset_path().startswith("/tmp"))
|
|
self.assertTrue(PathUtils.get_preset_path("foo", "bar").startswith("/tmp"))
|
|
self.assertTrue(PathUtils.get_config_path().startswith("/tmp"))
|
|
self.assertTrue(PathUtils.get_config_path("foo").startswith("/tmp"))
|
|
|
|
self.v1_dir = os.path.join(UserUtils.home, ".config", "input-remapper")
|
|
self.beta_dir = os.path.join(
|
|
UserUtils.home, ".config", "input-remapper", "beta_1.6.0-beta"
|
|
)
|
|
|
|
global_uinputs = GlobalUInputs(UInput)
|
|
global_uinputs.prepare_all()
|
|
self.migrations = Migrations(global_uinputs)
|
|
|
|
def test_migrate_suffix(self):
|
|
old = os.path.join(PathUtils.config_path(), "config")
|
|
new = os.path.join(PathUtils.config_path(), "config.json")
|
|
|
|
try:
|
|
os.remove(new)
|
|
except FileNotFoundError:
|
|
pass
|
|
|
|
PathUtils.touch(old)
|
|
with open(old, "w") as f:
|
|
f.write("{}")
|
|
|
|
self.migrations.migrate()
|
|
self.assertTrue(os.path.exists(new))
|
|
self.assertFalse(os.path.exists(old))
|
|
|
|
def test_rename_config(self):
|
|
old = os.path.join(UserUtils.home, ".config", "key-mapper")
|
|
new = PathUtils.config_path()
|
|
|
|
# we are not destroying our actual config files with this test
|
|
self.assertTrue(new.startswith(tmp), f'Expected "{new}" to start with "{tmp}"')
|
|
|
|
try:
|
|
shutil.rmtree(new)
|
|
except FileNotFoundError:
|
|
pass
|
|
|
|
old_config_json = os.path.join(old, "config.json")
|
|
PathUtils.touch(old_config_json)
|
|
with open(old_config_json, "w") as f:
|
|
f.write('{"foo":"bar"}')
|
|
|
|
self.migrations.migrate()
|
|
|
|
self.assertTrue(os.path.exists(new))
|
|
self.assertFalse(os.path.exists(old))
|
|
|
|
new_config_json = os.path.join(new, "config.json")
|
|
with open(new_config_json, "r") as f:
|
|
moved_config = json.loads(f.read())
|
|
self.assertEqual(moved_config["foo"], "bar")
|
|
|
|
def test_wont_migrate_suffix(self):
|
|
old = os.path.join(PathUtils.config_path(), "config")
|
|
new = os.path.join(PathUtils.config_path(), "config.json")
|
|
|
|
PathUtils.touch(new)
|
|
with open(new, "w") as f:
|
|
f.write("{}")
|
|
|
|
PathUtils.touch(old)
|
|
with open(old, "w") as f:
|
|
f.write("{}")
|
|
|
|
self.migrations.migrate()
|
|
self.assertTrue(os.path.exists(new))
|
|
self.assertTrue(os.path.exists(old))
|
|
|
|
def test_migrate_preset(self):
|
|
if os.path.exists(PathUtils.config_path()):
|
|
shutil.rmtree(PathUtils.config_path())
|
|
|
|
p1 = os.path.join(PathUtils.config_path(), "foo1", "bar1.json")
|
|
p2 = os.path.join(PathUtils.config_path(), "foo2", "bar2.json")
|
|
PathUtils.touch(p1)
|
|
PathUtils.touch(p2)
|
|
|
|
with open(p1, "w") as f:
|
|
f.write("{}")
|
|
|
|
with open(p2, "w") as f:
|
|
f.write("{}")
|
|
|
|
self.migrations.migrate()
|
|
|
|
self.assertFalse(
|
|
os.path.exists(os.path.join(PathUtils.config_path(), "foo1", "bar1.json"))
|
|
)
|
|
self.assertFalse(
|
|
os.path.exists(os.path.join(PathUtils.config_path(), "foo2", "bar2.json"))
|
|
)
|
|
|
|
self.assertTrue(
|
|
os.path.exists(
|
|
os.path.join(PathUtils.config_path(), "presets", "foo1", "bar1.json")
|
|
),
|
|
)
|
|
self.assertTrue(
|
|
os.path.exists(
|
|
os.path.join(PathUtils.config_path(), "presets", "foo2", "bar2.json")
|
|
),
|
|
)
|
|
|
|
def test_wont_migrate_preset(self):
|
|
if os.path.exists(PathUtils.config_path()):
|
|
shutil.rmtree(PathUtils.config_path())
|
|
|
|
p1 = os.path.join(PathUtils.config_path(), "foo1", "bar1.json")
|
|
p2 = os.path.join(PathUtils.config_path(), "foo2", "bar2.json")
|
|
PathUtils.touch(p1)
|
|
PathUtils.touch(p2)
|
|
|
|
with open(p1, "w") as f:
|
|
f.write("{}")
|
|
|
|
with open(p2, "w") as f:
|
|
f.write("{}")
|
|
|
|
# already migrated
|
|
PathUtils.mkdir(os.path.join(PathUtils.config_path(), "presets"))
|
|
|
|
self.migrations.migrate()
|
|
|
|
self.assertTrue(
|
|
os.path.exists(os.path.join(PathUtils.config_path(), "foo1", "bar1.json"))
|
|
)
|
|
self.assertTrue(
|
|
os.path.exists(os.path.join(PathUtils.config_path(), "foo2", "bar2.json"))
|
|
)
|
|
|
|
self.assertFalse(
|
|
os.path.exists(
|
|
os.path.join(PathUtils.config_path(), "presets", "foo1", "bar1.json")
|
|
),
|
|
)
|
|
self.assertFalse(
|
|
os.path.exists(
|
|
os.path.join(PathUtils.config_path(), "presets", "foo2", "bar2.json")
|
|
),
|
|
)
|
|
|
|
def test_migrate_mappings(self):
|
|
"""Test if mappings are migrated correctly
|
|
|
|
mappings like
|
|
{(type, code): symbol} or {(type, code, value): symbol} should migrate
|
|
to {InputCombination: {target: target, symbol: symbol, ...}}
|
|
"""
|
|
path = os.path.join(
|
|
PathUtils.config_path(), "presets", "Foo Device", "test.json"
|
|
)
|
|
os.makedirs(os.path.dirname(path), exist_ok=True)
|
|
with open(path, "w") as file:
|
|
json.dump(
|
|
{
|
|
"mapping": {
|
|
f"{EV_KEY},1": "a",
|
|
f"{EV_KEY}, 2, 1": "BTN_B", # can be mapped to "gamepad"
|
|
f"{EV_KEY}, 3, 1": "BTN_1", # can not be mapped
|
|
f"{EV_ABS},{ABS_HAT0X},-1": "b",
|
|
f"{EV_ABS},1,1+{EV_ABS},2,-1+{EV_ABS},3,1": "c",
|
|
f"{EV_KEY}, 4, 1": ("d", "keyboard"),
|
|
f"{EV_KEY}, 5, 1": ("e", "foo"), # unknown target
|
|
f"{EV_KEY}, 6, 1": ("key(a, b)", "keyboard"), # broken macro
|
|
# ignored because broken
|
|
"3,1,1,2": "e",
|
|
"3": "e",
|
|
",,+3,1,2": "g",
|
|
"": "h",
|
|
}
|
|
},
|
|
file,
|
|
)
|
|
self.migrations.migrate()
|
|
# use UIMapping to also load invalid mappings
|
|
preset = Preset(PathUtils.get_preset_path("Foo Device", "test"), UIMapping)
|
|
preset.load()
|
|
|
|
self.assertEqual(
|
|
preset.get_mapping(InputCombination([InputConfig(type=EV_KEY, code=1)])),
|
|
UIMapping(
|
|
input_combination=InputCombination([InputConfig(type=EV_KEY, code=1)]),
|
|
target_uinput="keyboard",
|
|
output_symbol="a",
|
|
),
|
|
)
|
|
self.assertEqual(
|
|
preset.get_mapping(InputCombination([InputConfig(type=EV_KEY, code=2)])),
|
|
UIMapping(
|
|
input_combination=InputCombination([InputConfig(type=EV_KEY, code=2)]),
|
|
target_uinput="gamepad",
|
|
output_symbol="BTN_B",
|
|
),
|
|
)
|
|
self.assertEqual(
|
|
preset.get_mapping(InputCombination([InputConfig(type=EV_KEY, code=3)])),
|
|
UIMapping(
|
|
input_combination=InputCombination([InputConfig(type=EV_KEY, code=3)]),
|
|
target_uinput="keyboard",
|
|
output_symbol="BTN_1\n# Broken mapping:\n# No target can handle all specified keycodes",
|
|
),
|
|
)
|
|
self.assertEqual(
|
|
preset.get_mapping(InputCombination([InputConfig(type=EV_KEY, code=4)])),
|
|
UIMapping(
|
|
input_combination=InputCombination([InputConfig(type=EV_KEY, code=4)]),
|
|
target_uinput="keyboard",
|
|
output_symbol="d",
|
|
),
|
|
)
|
|
self.assertEqual(
|
|
preset.get_mapping(
|
|
InputCombination(
|
|
[InputConfig(type=EV_ABS, code=ABS_HAT0X, analog_threshold=-1)]
|
|
)
|
|
),
|
|
UIMapping(
|
|
input_combination=InputCombination(
|
|
[InputConfig(type=EV_ABS, code=ABS_HAT0X, analog_threshold=-1)]
|
|
),
|
|
target_uinput="keyboard",
|
|
output_symbol="b",
|
|
),
|
|
)
|
|
self.assertEqual(
|
|
preset.get_mapping(
|
|
InputCombination(
|
|
InputCombination.from_tuples(
|
|
(EV_ABS, 1, 1), (EV_ABS, 2, -1), (EV_ABS, 3, 1)
|
|
)
|
|
),
|
|
),
|
|
UIMapping(
|
|
input_combination=InputCombination(
|
|
InputCombination.from_tuples(
|
|
(EV_ABS, 1, 1), (EV_ABS, 2, -1), (EV_ABS, 3, 1)
|
|
),
|
|
),
|
|
target_uinput="keyboard",
|
|
output_symbol="c",
|
|
),
|
|
)
|
|
self.assertEqual(
|
|
preset.get_mapping(InputCombination([InputConfig(type=EV_KEY, code=5)])),
|
|
UIMapping(
|
|
input_combination=InputCombination([InputConfig(type=EV_KEY, code=5)]),
|
|
target_uinput="foo",
|
|
output_symbol="e",
|
|
),
|
|
)
|
|
self.assertEqual(
|
|
preset.get_mapping(InputCombination([InputConfig(type=EV_KEY, code=6)])),
|
|
UIMapping(
|
|
input_combination=InputCombination([InputConfig(type=EV_KEY, code=6)]),
|
|
target_uinput="keyboard",
|
|
output_symbol="key(a, b)",
|
|
),
|
|
)
|
|
|
|
self.assertEqual(8, len(preset))
|
|
|
|
def test_migrate_otherwise(self):
|
|
path = os.path.join(
|
|
PathUtils.config_path(), "presets", "Foo Device", "test.json"
|
|
)
|
|
os.makedirs(os.path.dirname(path), exist_ok=True)
|
|
with open(path, "w") as file:
|
|
json.dump(
|
|
{
|
|
"mapping": {
|
|
f"{EV_KEY},1,1": ("otherwise + otherwise", "keyboard"),
|
|
f"{EV_KEY},2,1": ("bar($otherwise)", "keyboard"),
|
|
f"{EV_KEY},3,1": ("foo(otherwise=qux)", "keyboard"),
|
|
f"{EV_KEY},4,1": ("qux(otherwise).bar(otherwise = 1)", "foo"),
|
|
f"{EV_KEY},5,1": ("foo(otherwise1=2qux)", "keyboard"),
|
|
}
|
|
},
|
|
file,
|
|
)
|
|
|
|
self.migrations.migrate()
|
|
|
|
preset = Preset(PathUtils.get_preset_path("Foo Device", "test"), UIMapping)
|
|
preset.load()
|
|
|
|
self.assertEqual(
|
|
preset.get_mapping(InputCombination([InputConfig(type=EV_KEY, code=1)])),
|
|
UIMapping(
|
|
input_combination=InputCombination([InputConfig(type=EV_KEY, code=1)]),
|
|
target_uinput="keyboard",
|
|
output_symbol="otherwise + otherwise",
|
|
),
|
|
)
|
|
self.assertEqual(
|
|
preset.get_mapping(InputCombination([InputConfig(type=EV_KEY, code=2)])),
|
|
UIMapping(
|
|
input_combination=InputCombination([InputConfig(type=EV_KEY, code=2)]),
|
|
target_uinput="keyboard",
|
|
output_symbol="bar($otherwise)",
|
|
),
|
|
)
|
|
self.assertEqual(
|
|
preset.get_mapping(InputCombination([InputConfig(type=EV_KEY, code=3)])),
|
|
UIMapping(
|
|
input_combination=InputCombination([InputConfig(type=EV_KEY, code=3)]),
|
|
target_uinput="keyboard",
|
|
output_symbol="foo(else=qux)",
|
|
),
|
|
)
|
|
self.assertEqual(
|
|
preset.get_mapping(InputCombination([InputConfig(type=EV_KEY, code=4)])),
|
|
UIMapping(
|
|
input_combination=InputCombination([InputConfig(type=EV_KEY, code=4)]),
|
|
target_uinput="foo",
|
|
output_symbol="qux(otherwise).bar(else=1)",
|
|
),
|
|
)
|
|
self.assertEqual(
|
|
preset.get_mapping(InputCombination([InputConfig(type=EV_KEY, code=5)])),
|
|
UIMapping(
|
|
input_combination=InputCombination([InputConfig(type=EV_KEY, code=5)]),
|
|
target_uinput="keyboard",
|
|
output_symbol="foo(otherwise1=2qux)",
|
|
),
|
|
)
|
|
|
|
def test_add_version(self):
|
|
path = os.path.join(PathUtils.config_path(), "config.json")
|
|
os.makedirs(os.path.dirname(path), exist_ok=True)
|
|
with open(path, "w") as file:
|
|
file.write("{}")
|
|
|
|
self.migrations.migrate()
|
|
self.assertEqual(
|
|
version.parse(VERSION),
|
|
self.migrations.config_version(),
|
|
)
|
|
|
|
def test_update_version(self):
|
|
path = os.path.join(PathUtils.config_path(), "config.json")
|
|
os.makedirs(os.path.dirname(path), exist_ok=True)
|
|
with open(path, "w") as file:
|
|
json.dump({"version": "0.1.0"}, file)
|
|
|
|
self.migrations.migrate()
|
|
self.assertEqual(
|
|
version.parse(VERSION),
|
|
self.migrations.config_version(),
|
|
)
|
|
|
|
def test_config_version(self):
|
|
path = os.path.join(PathUtils.config_path(), "config.json")
|
|
with open(path, "w") as file:
|
|
file.write("{}")
|
|
|
|
self.assertEqual("0.0.0", self.migrations.config_version().public)
|
|
|
|
try:
|
|
os.remove(path)
|
|
except FileNotFoundError:
|
|
pass
|
|
|
|
self.assertEqual("0.0.0", self.migrations.config_version().public)
|
|
|
|
def test_migrate_left_and_right_purpose(self):
|
|
path = os.path.join(
|
|
PathUtils.config_path(), "presets", "Foo Device", "test.json"
|
|
)
|
|
os.makedirs(os.path.dirname(path), exist_ok=True)
|
|
with open(path, "w") as file:
|
|
json.dump(
|
|
{
|
|
"gamepad": {
|
|
"joystick": {
|
|
"left_purpose": "mouse",
|
|
"right_purpose": "wheel",
|
|
"pointer_speed": 50,
|
|
"x_scroll_speed": 10,
|
|
"y_scroll_speed": 20,
|
|
}
|
|
}
|
|
},
|
|
file,
|
|
)
|
|
self.migrations.migrate()
|
|
|
|
preset = Preset(PathUtils.get_preset_path("Foo Device", "test"), UIMapping)
|
|
preset.load()
|
|
# 2 mappings for mouse
|
|
# 2 mappings for wheel
|
|
self.assertEqual(len(preset), 4)
|
|
self.assertEqual(
|
|
preset.get_mapping(
|
|
InputCombination([InputConfig(type=EV_ABS, code=ABS_X)])
|
|
),
|
|
UIMapping(
|
|
input_combination=InputCombination(
|
|
[InputConfig(type=EV_ABS, code=ABS_X)]
|
|
),
|
|
target_uinput="mouse",
|
|
output_type=EV_REL,
|
|
output_code=REL_X,
|
|
gain=50 / 100,
|
|
),
|
|
)
|
|
self.assertEqual(
|
|
preset.get_mapping(
|
|
InputCombination([InputConfig(type=EV_ABS, code=ABS_Y)])
|
|
),
|
|
UIMapping(
|
|
input_combination=InputCombination(
|
|
[InputConfig(type=EV_ABS, code=ABS_Y)]
|
|
),
|
|
target_uinput="mouse",
|
|
output_type=EV_REL,
|
|
output_code=REL_Y,
|
|
gain=50 / 100,
|
|
),
|
|
)
|
|
self.assertEqual(
|
|
preset.get_mapping(
|
|
InputCombination([InputConfig(type=EV_ABS, code=ABS_RX)])
|
|
),
|
|
UIMapping(
|
|
input_combination=InputCombination(
|
|
[InputConfig(type=EV_ABS, code=ABS_RX)]
|
|
),
|
|
target_uinput="mouse",
|
|
output_type=EV_REL,
|
|
output_code=REL_HWHEEL_HI_RES,
|
|
gain=10,
|
|
),
|
|
)
|
|
self.assertEqual(
|
|
preset.get_mapping(
|
|
InputCombination([InputConfig(type=EV_ABS, code=ABS_RY)])
|
|
),
|
|
UIMapping(
|
|
input_combination=InputCombination(
|
|
[InputConfig(type=EV_ABS, code=ABS_RY)]
|
|
),
|
|
target_uinput="mouse",
|
|
output_type=EV_REL,
|
|
output_code=REL_WHEEL_HI_RES,
|
|
gain=20,
|
|
),
|
|
)
|
|
|
|
def test_migrate_left_and_right_purpose2(self):
|
|
# same as above, but left and right is swapped
|
|
|
|
path = os.path.join(
|
|
PathUtils.config_path(), "presets", "Foo Device", "test.json"
|
|
)
|
|
os.makedirs(os.path.dirname(path), exist_ok=True)
|
|
with open(path, "w") as file:
|
|
json.dump(
|
|
{
|
|
"gamepad": {
|
|
"joystick": {
|
|
"right_purpose": "mouse",
|
|
"left_purpose": "wheel",
|
|
"pointer_speed": 50,
|
|
"x_scroll_speed": 10,
|
|
"y_scroll_speed": 20,
|
|
}
|
|
}
|
|
},
|
|
file,
|
|
)
|
|
self.migrations.migrate()
|
|
|
|
preset = Preset(PathUtils.get_preset_path("Foo Device", "test"), UIMapping)
|
|
preset.load()
|
|
# 2 mappings for mouse
|
|
# 2 mappings for wheel
|
|
self.assertEqual(len(preset), 4)
|
|
self.assertEqual(
|
|
preset.get_mapping(
|
|
InputCombination([InputConfig(type=EV_ABS, code=ABS_RX)])
|
|
),
|
|
UIMapping(
|
|
input_combination=InputCombination(
|
|
[InputConfig(type=EV_ABS, code=ABS_RX)]
|
|
),
|
|
target_uinput="mouse",
|
|
output_type=EV_REL,
|
|
output_code=REL_X,
|
|
gain=50 / 100,
|
|
),
|
|
)
|
|
self.assertEqual(
|
|
preset.get_mapping(
|
|
InputCombination([InputConfig(type=EV_ABS, code=ABS_RY)])
|
|
),
|
|
UIMapping(
|
|
input_combination=InputCombination(
|
|
[InputConfig(type=EV_ABS, code=ABS_RY)]
|
|
),
|
|
target_uinput="mouse",
|
|
output_type=EV_REL,
|
|
output_code=REL_Y,
|
|
gain=50 / 100,
|
|
),
|
|
)
|
|
self.assertEqual(
|
|
preset.get_mapping(
|
|
InputCombination([InputConfig(type=EV_ABS, code=ABS_X)])
|
|
),
|
|
UIMapping(
|
|
input_combination=InputCombination(
|
|
[InputConfig(type=EV_ABS, code=ABS_X)]
|
|
),
|
|
target_uinput="mouse",
|
|
output_type=EV_REL,
|
|
output_code=REL_HWHEEL_HI_RES,
|
|
gain=10,
|
|
),
|
|
)
|
|
self.assertEqual(
|
|
preset.get_mapping(
|
|
InputCombination([InputConfig(type=EV_ABS, code=ABS_Y)])
|
|
),
|
|
UIMapping(
|
|
input_combination=InputCombination(
|
|
[InputConfig(type=EV_ABS, code=ABS_Y)]
|
|
),
|
|
target_uinput="mouse",
|
|
output_type=EV_REL,
|
|
output_code=REL_WHEEL_HI_RES,
|
|
gain=20,
|
|
),
|
|
)
|
|
|
|
def _create_v1_setup(self):
|
|
"""Create all files needed to mimic an outdated v1 configuration."""
|
|
device_name = "device_name"
|
|
|
|
PathUtils.mkdir(os.path.join(self.v1_dir, "presets", device_name))
|
|
v1_config = {"autoload": {device_name: "foo"}, "version": "1.0"}
|
|
with open(os.path.join(self.v1_dir, "config.json"), "w") as file:
|
|
json.dump(v1_config, file)
|
|
# insert something outdated that will be migrated, to ensure the files are
|
|
# first copied and then migrated.
|
|
with open(
|
|
os.path.join(self.v1_dir, "presets", device_name, "foo.json"), "w"
|
|
) as file:
|
|
json.dump({"mapping": {f"{EV_KEY},1": "a"}}, file)
|
|
|
|
def _create_beta_setup(self):
|
|
"""Create all files needed to mimic a beta configuration."""
|
|
device_name = "device_name"
|
|
|
|
# same here, but a different contents to tell the difference
|
|
PathUtils.mkdir(os.path.join(self.beta_dir, "presets", device_name))
|
|
beta_config = {"autoload": {device_name: "bar"}, "version": "1.6"}
|
|
with open(os.path.join(self.beta_dir, "config.json"), "w") as file:
|
|
json.dump(beta_config, file)
|
|
with open(
|
|
os.path.join(self.beta_dir, "presets", device_name, "bar.json"), "w"
|
|
) as file:
|
|
json.dump(
|
|
[
|
|
{
|
|
"input_combination": [
|
|
{"type": EV_KEY, "code": 1},
|
|
],
|
|
"target_uinput": "keyboard",
|
|
"output_symbol": "b",
|
|
"mapping_type": "key_macro",
|
|
}
|
|
],
|
|
file,
|
|
)
|
|
|
|
def test_prioritize_v1_over_beta_configs(self):
|
|
# if both v1 and beta presets and config exist, migrate v1
|
|
PathUtils.remove(PathUtils.get_config_path())
|
|
|
|
device_name = "device_name"
|
|
self._create_v1_setup()
|
|
self._create_beta_setup()
|
|
|
|
self.assertFalse(os.path.exists(PathUtils.get_preset_path(device_name, "foo")))
|
|
self.assertFalse(os.path.exists(PathUtils.get_config_path("config.json")))
|
|
|
|
self.migrations.migrate()
|
|
|
|
self.assertTrue(os.path.exists(PathUtils.get_preset_path(device_name, "foo")))
|
|
self.assertTrue(os.path.exists(PathUtils.get_config_path("config.json")))
|
|
self.assertFalse(os.path.exists(PathUtils.get_preset_path(device_name, "bar")))
|
|
|
|
# expect all original files to still exist
|
|
self.assertTrue(os.path.join(self.v1_dir, "config.json"))
|
|
self.assertTrue(os.path.join(self.v1_dir, "presets", "foo.json"))
|
|
self.assertTrue(os.path.join(self.beta_dir, "config.json"))
|
|
self.assertTrue(os.path.join(self.beta_dir, "presets", "bar.json"))
|
|
|
|
# v1 configs should be in the v2 dir now, and migrated
|
|
with open(PathUtils.get_config_path("config.json"), "r") as f:
|
|
config_json = json.load(f)
|
|
self.assertDictEqual(
|
|
config_json,
|
|
{
|
|
"autoload": {device_name: "foo"},
|
|
"version": VERSION,
|
|
"app_binding_enabled": False,
|
|
"app_bindings": [],
|
|
},
|
|
)
|
|
with open(PathUtils.get_preset_path(device_name, "foo.json"), "r") as f:
|
|
os.system(f'cat { PathUtils.get_preset_path(device_name, "foo.json") }')
|
|
preset_foo_json = json.load(f)
|
|
self.assertEqual(
|
|
preset_foo_json,
|
|
[
|
|
{
|
|
"input_combination": [
|
|
{"type": EV_KEY, "code": 1},
|
|
],
|
|
"target_uinput": "keyboard",
|
|
"output_symbol": "a",
|
|
"mapping_type": "key_macro",
|
|
}
|
|
],
|
|
)
|
|
|
|
def test_copy_over_beta_configs(self):
|
|
# same as test_prioritize_v1_over_beta_configs, but only create the beta
|
|
# directory without any v1 presets.
|
|
PathUtils.remove(PathUtils.get_config_path())
|
|
|
|
device_name = "device_name"
|
|
self._create_beta_setup()
|
|
|
|
self.assertFalse(os.path.exists(PathUtils.get_preset_path(device_name, "bar")))
|
|
self.assertFalse(os.path.exists(PathUtils.get_config_path("config.json")))
|
|
|
|
self.migrations.migrate()
|
|
|
|
self.assertTrue(os.path.exists(PathUtils.get_preset_path(device_name, "bar")))
|
|
self.assertTrue(os.path.exists(PathUtils.get_config_path("config.json")))
|
|
|
|
# expect all original files to still exist
|
|
self.assertTrue(os.path.join(self.beta_dir, "config.json"))
|
|
self.assertTrue(os.path.join(self.beta_dir, "presets", "bar.json"))
|
|
|
|
# beta configs should be in the v2 dir now
|
|
with open(PathUtils.get_config_path("config.json"), "r") as f:
|
|
config_json = json.load(f)
|
|
self.assertDictEqual(
|
|
config_json,
|
|
{
|
|
"autoload": {device_name: "bar"},
|
|
"version": VERSION,
|
|
"app_binding_enabled": False,
|
|
"app_bindings": [],
|
|
},
|
|
)
|
|
with open(PathUtils.get_preset_path(device_name, "bar.json"), "r") as f:
|
|
os.system(f'cat { PathUtils.get_preset_path(device_name, "bar.json") }')
|
|
preset_foo_json = json.load(f)
|
|
self.assertEqual(
|
|
preset_foo_json,
|
|
[
|
|
{
|
|
"input_combination": [
|
|
{"type": EV_KEY, "code": 1},
|
|
],
|
|
"target_uinput": "keyboard",
|
|
"output_symbol": "b",
|
|
"mapping_type": "key_macro",
|
|
}
|
|
],
|
|
)
|
|
|
|
def _write_config(self, data):
|
|
config_file = os.path.join(PathUtils.config_path(), "config.json")
|
|
PathUtils.touch(config_file)
|
|
with open(config_file, "w") as file:
|
|
json.dump(data, file, indent=4)
|
|
return config_file
|
|
|
|
def _read_config(self, config_file):
|
|
with open(config_file, "r") as file:
|
|
return json.load(file)
|
|
|
|
def test_add_app_binding_config(self):
|
|
# an old config without the app-binding keys gets them added
|
|
config_file = self._write_config(
|
|
{"version": "2.2.1", "autoload": {"Foo Device 2": "preset2"}}
|
|
)
|
|
self.migrations.migrate()
|
|
|
|
config = self._read_config(config_file)
|
|
self.assertEqual(config["app_binding_enabled"], False)
|
|
self.assertEqual(config["app_bindings"], [])
|
|
# additive: existing keys are preserved
|
|
self.assertEqual(config["autoload"], {"Foo Device 2": "preset2"})
|
|
self.assertEqual(config["version"], VERSION)
|
|
|
|
def test_add_app_binding_config_preserves_existing(self):
|
|
# a config already carrying app-binding data must not be reset
|
|
existing_binding = {
|
|
"app_id": "firefox",
|
|
"match": "wm_class",
|
|
"presets": [{"group_key": "Foo Device", "preset": "preset1"}],
|
|
}
|
|
config_file = self._write_config(
|
|
{
|
|
"version": "2.2.1",
|
|
"autoload": {},
|
|
"app_binding_enabled": True,
|
|
"app_bindings": [existing_binding],
|
|
}
|
|
)
|
|
self.migrations.migrate()
|
|
|
|
config = self._read_config(config_file)
|
|
self.assertEqual(config["app_binding_enabled"], True)
|
|
self.assertEqual(config["app_bindings"], [existing_binding])
|
|
|
|
def test_add_app_binding_config_no_file(self):
|
|
# no config.json yet -> migration is a no-op and does not create one
|
|
config_file = os.path.join(PathUtils.config_path(), "config.json")
|
|
if os.path.exists(config_file):
|
|
os.remove(config_file)
|
|
self.migrations._add_app_binding_config()
|
|
self.assertFalse(os.path.exists(config_file))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|