From 19582174f3a446a07d40f479020b4ebb1aad8ddb Mon Sep 17 00:00:00 2001 From: Nicolas Munnich <98408764+nmunnich@users.noreply.github.com> Date: Thu, 8 Jan 2026 01:01:05 +0100 Subject: [PATCH] chore: Add basic tests for Studio's layer manipulation (#3164) chore: Add test behaviors for Studio testing chore: Add basic tests for studio layer adjustment chore: Fixes from code review --- app/include/zmk/keymap.h | 5 +- app/module/CMakeLists.txt | 1 + app/module/Kconfig | 3 +- .../zmk,behavior_add_layer.yaml | 8 ++ .../zmk,behavior_move_layer.yaml | 8 ++ .../zmk,behavior_remove_layer.yaml | 8 ++ ...zmk,behavior_set_layer_binding_at_idx.yaml | 13 +++ app/module/test-behaviors/CMakeLists.txt | 6 ++ app/module/test-behaviors/Kconfig | 2 + .../test-behaviors/behavior_add_layer.c | 50 +++++++++ .../test-behaviors/behavior_move_layer.c | 47 ++++++++ .../test-behaviors/behavior_remove_layer.c | 49 +++++++++ .../behavior_set_layer_binding_at_idx.c | 100 ++++++++++++++++++ app/run-test.sh | 6 ++ .../studio/add-layer/basic/events.patterns | 4 + .../studio/add-layer/basic/extra-cmake-args | 5 + .../add-layer/basic/keycode_events.snapshot | 6 ++ .../studio/add-layer/basic/native_sim.conf | 1 + .../studio/add-layer/basic/native_sim.keymap | 23 ++++ .../studio/add-layer/behavior_keymap.dtsi | 32 ++++++ .../studio/move-layer/basic/events.patterns | 3 + .../studio/move-layer/basic/extra-cmake-args | 5 + .../move-layer/basic/keycode_events.snapshot | 8 ++ .../studio/move-layer/basic/native_sim.conf | 1 + .../studio/move-layer/basic/native_sim.keymap | 26 +++++ .../studio/move-layer/behavior_keymap.dtsi | 34 ++++++ .../studio/remove-layer/basic/events.patterns | 3 + .../remove-layer/basic/extra-cmake-args | 5 + .../basic/keycode_events.snapshot | 7 ++ .../studio/remove-layer/basic/native_sim.conf | 1 + .../remove-layer/basic/native_sim.keymap | 25 +++++ .../studio/remove-layer/behavior_keymap.dtsi | 34 ++++++ .../remove-default-layer/events.patterns | 5 + .../remove-default-layer/extra-cmake-args | 5 + .../keycode_events.snapshot | 8 ++ .../remove-default-layer/native_sim.conf | 1 + .../remove-default-layer/native_sim.keymap | 26 +++++ .../remove-layer/remove-default-layer/pending | 2 + 38 files changed, 573 insertions(+), 3 deletions(-) create mode 100644 app/module/dts/bindings/test-behaviors/zmk,behavior_add_layer.yaml create mode 100644 app/module/dts/bindings/test-behaviors/zmk,behavior_move_layer.yaml create mode 100644 app/module/dts/bindings/test-behaviors/zmk,behavior_remove_layer.yaml create mode 100644 app/module/dts/bindings/test-behaviors/zmk,behavior_set_layer_binding_at_idx.yaml create mode 100644 app/module/test-behaviors/CMakeLists.txt create mode 100644 app/module/test-behaviors/Kconfig create mode 100644 app/module/test-behaviors/behavior_add_layer.c create mode 100644 app/module/test-behaviors/behavior_move_layer.c create mode 100644 app/module/test-behaviors/behavior_remove_layer.c create mode 100644 app/module/test-behaviors/behavior_set_layer_binding_at_idx.c create mode 100644 app/tests/studio/add-layer/basic/events.patterns create mode 100644 app/tests/studio/add-layer/basic/extra-cmake-args create mode 100644 app/tests/studio/add-layer/basic/keycode_events.snapshot create mode 100644 app/tests/studio/add-layer/basic/native_sim.conf create mode 100644 app/tests/studio/add-layer/basic/native_sim.keymap create mode 100644 app/tests/studio/add-layer/behavior_keymap.dtsi create mode 100644 app/tests/studio/move-layer/basic/events.patterns create mode 100644 app/tests/studio/move-layer/basic/extra-cmake-args create mode 100644 app/tests/studio/move-layer/basic/keycode_events.snapshot create mode 100644 app/tests/studio/move-layer/basic/native_sim.conf create mode 100644 app/tests/studio/move-layer/basic/native_sim.keymap create mode 100644 app/tests/studio/move-layer/behavior_keymap.dtsi create mode 100644 app/tests/studio/remove-layer/basic/events.patterns create mode 100644 app/tests/studio/remove-layer/basic/extra-cmake-args create mode 100644 app/tests/studio/remove-layer/basic/keycode_events.snapshot create mode 100644 app/tests/studio/remove-layer/basic/native_sim.conf create mode 100644 app/tests/studio/remove-layer/basic/native_sim.keymap create mode 100644 app/tests/studio/remove-layer/behavior_keymap.dtsi create mode 100644 app/tests/studio/remove-layer/remove-default-layer/events.patterns create mode 100644 app/tests/studio/remove-layer/remove-default-layer/extra-cmake-args create mode 100644 app/tests/studio/remove-layer/remove-default-layer/keycode_events.snapshot create mode 100644 app/tests/studio/remove-layer/remove-default-layer/native_sim.conf create mode 100644 app/tests/studio/remove-layer/remove-default-layer/native_sim.keymap create mode 100644 app/tests/studio/remove-layer/remove-default-layer/pending diff --git a/app/include/zmk/keymap.h b/app/include/zmk/keymap.h index 556e04567..c7b2681d5 100644 --- a/app/include/zmk/keymap.h +++ b/app/include/zmk/keymap.h @@ -11,11 +11,12 @@ #include #define ZMK_KEYMAP_LAYERS_FOREACH(_fn) \ - COND_CODE_1(IS_ENABLED(CONFIG_ZMK_STUDIO), (DT_FOREACH_CHILD(DT_INST(0, zmk_keymap), _fn)), \ + COND_CODE_1(IS_ENABLED(CONFIG_ZMK_KEYMAP_LAYER_REORDERING), \ + (DT_FOREACH_CHILD(DT_INST(0, zmk_keymap), _fn)), \ (DT_FOREACH_CHILD_STATUS_OKAY(DT_INST(0, zmk_keymap), _fn))) #define ZMK_KEYMAP_LAYERS_FOREACH_SEP(_fn, _sep) \ - COND_CODE_1(IS_ENABLED(CONFIG_ZMK_STUDIO), \ + COND_CODE_1(IS_ENABLED(CONFIG_ZMK_KEYMAP_LAYER_REORDERING), \ (DT_FOREACH_CHILD_SEP(DT_INST(0, zmk_keymap), _fn, _sep)), \ (DT_FOREACH_CHILD_STATUS_OKAY_SEP(DT_INST(0, zmk_keymap), _fn, _sep))) diff --git a/app/module/CMakeLists.txt b/app/module/CMakeLists.txt index db886ac69..8c4afb069 100644 --- a/app/module/CMakeLists.txt +++ b/app/module/CMakeLists.txt @@ -1,4 +1,5 @@ zephyr_include_directories(include) add_subdirectory(drivers) +add_subdirectory(test-behaviors) add_subdirectory(lib) \ No newline at end of file diff --git a/app/module/Kconfig b/app/module/Kconfig index 52c013151..e86c0d6ba 100644 --- a/app/module/Kconfig +++ b/app/module/Kconfig @@ -1,3 +1,4 @@ rsource "drivers/Kconfig" -rsource "lib/Kconfig" \ No newline at end of file +rsource "lib/Kconfig" +rsource "test-behaviors/Kconfig" \ No newline at end of file diff --git a/app/module/dts/bindings/test-behaviors/zmk,behavior_add_layer.yaml b/app/module/dts/bindings/test-behaviors/zmk,behavior_add_layer.yaml new file mode 100644 index 000000000..f07ef6088 --- /dev/null +++ b/app/module/dts/bindings/test-behaviors/zmk,behavior_add_layer.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2025 The ZMK Contributors +# SPDX-License-Identifier: MIT + +description: Layer Adder Behavior + +compatible: "zmk,behavior-add-layer" + +include: zero_param.yaml diff --git a/app/module/dts/bindings/test-behaviors/zmk,behavior_move_layer.yaml b/app/module/dts/bindings/test-behaviors/zmk,behavior_move_layer.yaml new file mode 100644 index 000000000..b23448d65 --- /dev/null +++ b/app/module/dts/bindings/test-behaviors/zmk,behavior_move_layer.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2025 The ZMK Contributors +# SPDX-License-Identifier: MIT + +description: Layer Mover Behavior + +compatible: "zmk,behavior-move-layer" + +include: two_param.yaml diff --git a/app/module/dts/bindings/test-behaviors/zmk,behavior_remove_layer.yaml b/app/module/dts/bindings/test-behaviors/zmk,behavior_remove_layer.yaml new file mode 100644 index 000000000..cdc20a8aa --- /dev/null +++ b/app/module/dts/bindings/test-behaviors/zmk,behavior_remove_layer.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2025 The ZMK Contributors +# SPDX-License-Identifier: MIT + +description: Layer Remover Behavior + +compatible: "zmk,behavior-remove-layer" + +include: one_param.yaml diff --git a/app/module/dts/bindings/test-behaviors/zmk,behavior_set_layer_binding_at_idx.yaml b/app/module/dts/bindings/test-behaviors/zmk,behavior_set_layer_binding_at_idx.yaml new file mode 100644 index 000000000..078587d43 --- /dev/null +++ b/app/module/dts/bindings/test-behaviors/zmk,behavior_set_layer_binding_at_idx.yaml @@ -0,0 +1,13 @@ +# Copyright (c) 2025 The ZMK Contributors +# SPDX-License-Identifier: MIT + +description: Layer Binding Setter Behavior + +compatible: "zmk,behavior-set-layer-binding-at-idx" + +include: two_param.yaml + +properties: + bindings: + type: phandle-array + required: true diff --git a/app/module/test-behaviors/CMakeLists.txt b/app/module/test-behaviors/CMakeLists.txt new file mode 100644 index 000000000..b228f5816 --- /dev/null +++ b/app/module/test-behaviors/CMakeLists.txt @@ -0,0 +1,6 @@ +if (((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL) AND CONFIG_ZMK_TEST_BEHAVIORS) + target_sources(app PRIVATE behavior_add_layer.c) + target_sources(app PRIVATE behavior_move_layer.c) + target_sources(app PRIVATE behavior_remove_layer.c) + target_sources(app PRIVATE behavior_set_layer_binding_at_idx.c) +endif() diff --git a/app/module/test-behaviors/Kconfig b/app/module/test-behaviors/Kconfig new file mode 100644 index 000000000..6a9458db1 --- /dev/null +++ b/app/module/test-behaviors/Kconfig @@ -0,0 +1,2 @@ +config ZMK_TEST_BEHAVIORS + bool "Include behaviors used for testing purposes" \ No newline at end of file diff --git a/app/module/test-behaviors/behavior_add_layer.c b/app/module/test-behaviors/behavior_add_layer.c new file mode 100644 index 000000000..47927cb72 --- /dev/null +++ b/app/module/test-behaviors/behavior_add_layer.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2025 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#define DT_DRV_COMPAT zmk_behavior_add_layer + +#include +#include +#include + +#include +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#if (IS_ENABLED(CONFIG_ZMK_KEYMAP_LAYER_REORDERING)) && (DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)) + +static int on_add_layer_binding_pressed(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + int new_layer = zmk_keymap_add_layer(); + if (new_layer >= 0) { + LOG_DBG("Added layer %d", new_layer); + return 0; + } + switch (new_layer) { + case -ENOSPC: + LOG_ERR("No more layers can be added. Out of memory."); + return -ENOSPC; + default: + LOG_ERR("Unknown error adding layer: %d", new_layer); + return new_layer; + } +} + +static int on_add_layer_binding_released(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + return 0; +} + +static const struct behavior_driver_api behavior_add_layer_driver_api = { + .binding_pressed = on_add_layer_binding_pressed, + .binding_released = on_add_layer_binding_released}; + +BEHAVIOR_DT_INST_DEFINE(0, NULL, NULL, NULL, NULL, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, + &behavior_add_layer_driver_api); + +#endif // IS_ENABLED(CONFIG_ZMK_KEYMAP_LAYER_REORDERING) AND + // DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) \ No newline at end of file diff --git a/app/module/test-behaviors/behavior_move_layer.c b/app/module/test-behaviors/behavior_move_layer.c new file mode 100644 index 000000000..74551629d --- /dev/null +++ b/app/module/test-behaviors/behavior_move_layer.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2025 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#define DT_DRV_COMPAT zmk_behavior_move_layer + +#include +#include +#include + +#include +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#if (IS_ENABLED(CONFIG_ZMK_KEYMAP_LAYER_REORDERING)) && (DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)) + +static int on_move_layer_binding_pressed(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + int result = zmk_keymap_move_layer(binding->param1, binding->param2); + + if (result < 0) { + LOG_ERR("Failed to move layer from index %d to index %d (err: %d)", binding->param1, + binding->param2, result); + return result; + } + + LOG_DBG("Moved layer from index %d to index %d", binding->param1, binding->param2); + return 0; +} + +static int on_move_layer_binding_released(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + return 0; +} + +static const struct behavior_driver_api behavior_move_layer_driver_api = { + .binding_pressed = on_move_layer_binding_pressed, + .binding_released = on_move_layer_binding_released}; + +BEHAVIOR_DT_INST_DEFINE(0, NULL, NULL, NULL, NULL, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, + &behavior_move_layer_driver_api); + +#endif // IS_ENABLED(CONFIG_ZMK_KEYMAP_LAYER_REORDERING) AND + // DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) \ No newline at end of file diff --git a/app/module/test-behaviors/behavior_remove_layer.c b/app/module/test-behaviors/behavior_remove_layer.c new file mode 100644 index 000000000..58c7fafd4 --- /dev/null +++ b/app/module/test-behaviors/behavior_remove_layer.c @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2025 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#define DT_DRV_COMPAT zmk_behavior_remove_layer + +#include +#include +#include + +#include +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#if (IS_ENABLED(CONFIG_ZMK_KEYMAP_LAYER_REORDERING)) && (DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)) + +static int on_remove_layer_binding_pressed(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + int result = zmk_keymap_remove_layer(binding->param1); + if (result >= 0) { + LOG_DBG("Removed layer at index %d", binding->param1); + return 0; + } + switch (result) { + case -EINVAL: + LOG_ERR("Layer at index %d not found", binding->param1); + return -EINVAL; + default: + LOG_DBG("Unknown error removing layer at index %d: %d", binding->param1, result); + return result; + } +} + +static int on_remove_layer_binding_released(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + return 0; +} + +static const struct behavior_driver_api behavior_remove_layer_driver_api = { + .binding_pressed = on_remove_layer_binding_pressed, + .binding_released = on_remove_layer_binding_released}; + +BEHAVIOR_DT_INST_DEFINE(0, NULL, NULL, NULL, NULL, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, + &behavior_remove_layer_driver_api); +#endif // IS_ENABLED(CONFIG_ZMK_KEYMAP_LAYER_REORDERING) AND + // DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) \ No newline at end of file diff --git a/app/module/test-behaviors/behavior_set_layer_binding_at_idx.c b/app/module/test-behaviors/behavior_set_layer_binding_at_idx.c new file mode 100644 index 000000000..49bf0dd38 --- /dev/null +++ b/app/module/test-behaviors/behavior_set_layer_binding_at_idx.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2025 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#define DT_DRV_COMPAT zmk_behavior_set_layer_binding_at_idx + +#include +#include +#include + +#include +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) + +struct behavior_set_layer_binding_at_idx_config { + size_t bindings_len; + struct zmk_behavior_binding *bindings; +}; + +struct behavior_set_layer_binding_at_idx_data { + size_t current_idx; +}; + +static int on_set_layer_binding_at_idx_binding_pressed(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + const struct device *dev = zmk_behavior_get_binding(binding->behavior_dev); + const struct behavior_set_layer_binding_at_idx_config *cfg = dev->config; + struct behavior_set_layer_binding_at_idx_data *data = dev->data; + + if (cfg->bindings_len == 0) { + LOG_ERR("No bindings configured"); + return -EINVAL; + } + + struct zmk_behavior_binding *binding_to_set = &cfg->bindings[data->current_idx]; + + int result = + zmk_keymap_set_layer_binding_at_idx(binding->param1, binding->param2, *binding_to_set); + + if (result < 0) { + LOG_ERR("Failed to set binding at layer %d, index %d (err: %d)", binding->param1, + binding->param2, result); + return result; + } + + LOG_DBG("Set binding at layer %d, index %d to binding %zu/%zu", binding->param1, + binding->param2, data->current_idx + 1, cfg->bindings_len); + + // Move to next binding, wrap around if at end + data->current_idx = (data->current_idx + 1) % cfg->bindings_len; + + return 0; +} + +static int on_set_layer_binding_at_idx_binding_released(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + return 0; +} + +static const struct behavior_driver_api behavior_set_layer_binding_at_idx_driver_api = { + .binding_pressed = on_set_layer_binding_at_idx_binding_pressed, + .binding_released = on_set_layer_binding_at_idx_binding_released}; + +static int behavior_set_layer_binding_at_idx_init(const struct device *dev) { + struct behavior_set_layer_binding_at_idx_data *data = dev->data; + data->current_idx = 0; + return 0; +}; + +#define _TRANSFORM_ENTRY(idx, node) ZMK_KEYMAP_EXTRACT_BINDING(idx, node) + +#define TRANSFORMED_BINDINGS(node) \ + {LISTIFY(DT_INST_PROP_LEN(node, bindings), _TRANSFORM_ENTRY, (, ), DT_DRV_INST(node))} + +#define SET_LAYER_BINDING_AT_IDX_INST(n) \ + static struct behavior_set_layer_binding_at_idx_data \ + behavior_set_layer_binding_at_idx_data_##n = {}; \ + \ + static const struct zmk_behavior_binding \ + behavior_set_layer_binding_at_idx_config_##n##_bindings[DT_INST_PROP_LEN(n, bindings)] = \ + TRANSFORMED_BINDINGS(n); \ + static const struct behavior_set_layer_binding_at_idx_config \ + behavior_set_layer_binding_at_idx_config_##n = { \ + .bindings_len = DT_INST_PROP_LEN(n, bindings), \ + .bindings = behavior_set_layer_binding_at_idx_config_##n##_bindings}; \ + \ + BEHAVIOR_DT_INST_DEFINE(n, behavior_set_layer_binding_at_idx_init, NULL, \ + &behavior_set_layer_binding_at_idx_data_##n, \ + &behavior_set_layer_binding_at_idx_config_##n, POST_KERNEL, \ + CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \ + &behavior_set_layer_binding_at_idx_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(SET_LAYER_BINDING_AT_IDX_INST) + +#endif // DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) diff --git a/app/run-test.sh b/app/run-test.sh index 61563d084..cd944c9a5 100755 --- a/app/run-test.sh +++ b/app/run-test.sh @@ -41,6 +41,12 @@ build_cmd="west build ${ZMK_SRC_DIR:+-s $ZMK_SRC_DIR} -d ${ZMK_BUILD_DIR}/tests/ -b native_sim/native/64 -p -- -DCONFIG_ASSERT=y -DZMK_CONFIG="$(realpath $path)" \ ${ZMK_EXTRA_MODULES:+-DZMK_EXTRA_MODULES="$(realpath ${ZMK_EXTRA_MODULES})"}" +# Add extra cmake arguments from file if it exists +if [ -f "$path/extra-cmake-args" ]; then + extra_args=$(cat "$path/extra-cmake-args" | tr '\n' ' ') + build_cmd="$build_cmd $extra_args" +fi + build_log_tmp="${ZMK_BUILD_DIR}/tmp/$testcase/build.log" build_log="${ZMK_BUILD_DIR}/tests/$testcase/build.log" mkdir -p $(dirname $build_log_tmp) diff --git a/app/tests/studio/add-layer/basic/events.patterns b/app/tests/studio/add-layer/basic/events.patterns new file mode 100644 index 000000000..8b349f8d9 --- /dev/null +++ b/app/tests/studio/add-layer/basic/events.patterns @@ -0,0 +1,4 @@ +s/.*on_add_layer_binding_pressed/add_layer/p +s/.*on_set_layer_binding_at_idx_binding_pressed/set_layer_binding/p +s/.*layer_changed/layer_changed/p +s/.*hid_listener_keycode_//p diff --git a/app/tests/studio/add-layer/basic/extra-cmake-args b/app/tests/studio/add-layer/basic/extra-cmake-args new file mode 100644 index 000000000..c5e4f18ef --- /dev/null +++ b/app/tests/studio/add-layer/basic/extra-cmake-args @@ -0,0 +1,5 @@ +-DCONFIG_ZMK_BEHAVIOR_METADATA=y +-DCONFIG_ZMK_BEHAVIOR_LOCAL_IDS=y +-DCONFIG_ZMK_KEYMAP_LAYER_REORDERING=y +-DCONFIG_ZMK_KEYMAP_SETTINGS_STORAGE=y +-DCONFIG_SETTINGS=y diff --git a/app/tests/studio/add-layer/basic/keycode_events.snapshot b/app/tests/studio/add-layer/basic/keycode_events.snapshot new file mode 100644 index 000000000..83fbf0fe1 --- /dev/null +++ b/app/tests/studio/add-layer/basic/keycode_events.snapshot @@ -0,0 +1,6 @@ +add_layer: Added layer 1 +set_layer_binding: Set binding at layer 1, index 0 to binding 1/2 +layer_changed: layer 1 state 1 locked 0 +pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +layer_changed: layer 1 state 0 locked 0 \ No newline at end of file diff --git a/app/tests/studio/add-layer/basic/native_sim.conf b/app/tests/studio/add-layer/basic/native_sim.conf new file mode 100644 index 000000000..3fb69a9d4 --- /dev/null +++ b/app/tests/studio/add-layer/basic/native_sim.conf @@ -0,0 +1 @@ +CONFIG_ZMK_TEST_BEHAVIORS=y \ No newline at end of file diff --git a/app/tests/studio/add-layer/basic/native_sim.keymap b/app/tests/studio/add-layer/basic/native_sim.keymap new file mode 100644 index 000000000..59ac62a20 --- /dev/null +++ b/app/tests/studio/add-layer/basic/native_sim.keymap @@ -0,0 +1,23 @@ +#include +#include +#include +#include "../behavior_keymap.dtsi" + +// Test adding a layer, setting a binding, activating it, and pressing the binding +&kscan { + events = < + // add a new layer + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(0,0,10) + // set binding A at layer 1, position 0 + ZMK_MOCK_PRESS(0,1,10) + ZMK_MOCK_RELEASE(0,1,10) + // activate layer 1 with momentary + ZMK_MOCK_PRESS(1,0,10) + // press position 0 on new layer (should be A) + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(0,0,10) + // release momentary layer 1 + ZMK_MOCK_RELEASE(1,0,10) + >; +}; diff --git a/app/tests/studio/add-layer/behavior_keymap.dtsi b/app/tests/studio/add-layer/behavior_keymap.dtsi new file mode 100644 index 000000000..243a6e0f8 --- /dev/null +++ b/app/tests/studio/add-layer/behavior_keymap.dtsi @@ -0,0 +1,32 @@ +#include +#include +#include + +/ { + behaviors { + add_layer: add_layer { + compatible = "zmk,behavior-add-layer"; + #binding-cells = <0>; + }; + + set_binding: set_binding { + compatible = "zmk,behavior-set-layer-binding-at-idx"; + #binding-cells = <2>; + bindings = <&kp A>, <&kp B>; + }; + }; + + keymap { + compatible = "zmk,keymap"; + + default_layer { + bindings = < + &add_layer &set_binding 1 0 + &mo 1 &kp B>; + }; + + layer_1 { + status = "reserved"; + }; + }; +}; diff --git a/app/tests/studio/move-layer/basic/events.patterns b/app/tests/studio/move-layer/basic/events.patterns new file mode 100644 index 000000000..a3fbafba3 --- /dev/null +++ b/app/tests/studio/move-layer/basic/events.patterns @@ -0,0 +1,3 @@ +s/.*layer_changed/layer_changed/p +s/.*hid_listener_keycode_//p +s/.*on_move_layer_binding_pressed/move_layer/p diff --git a/app/tests/studio/move-layer/basic/extra-cmake-args b/app/tests/studio/move-layer/basic/extra-cmake-args new file mode 100644 index 000000000..c5e4f18ef --- /dev/null +++ b/app/tests/studio/move-layer/basic/extra-cmake-args @@ -0,0 +1,5 @@ +-DCONFIG_ZMK_BEHAVIOR_METADATA=y +-DCONFIG_ZMK_BEHAVIOR_LOCAL_IDS=y +-DCONFIG_ZMK_KEYMAP_LAYER_REORDERING=y +-DCONFIG_ZMK_KEYMAP_SETTINGS_STORAGE=y +-DCONFIG_SETTINGS=y diff --git a/app/tests/studio/move-layer/basic/keycode_events.snapshot b/app/tests/studio/move-layer/basic/keycode_events.snapshot new file mode 100644 index 000000000..365afc620 --- /dev/null +++ b/app/tests/studio/move-layer/basic/keycode_events.snapshot @@ -0,0 +1,8 @@ +move_layer: Moved layer from index 1 to index 2 +layer_changed: layer 1 state 1 locked 0 +layer_changed: layer 2 state 1 locked 0 +pressed: usage_page 0x07 keycode 0x06 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x06 implicit_mods 0x00 explicit_mods 0x00 +layer_changed: layer 2 state 0 locked 0 +pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/studio/move-layer/basic/native_sim.conf b/app/tests/studio/move-layer/basic/native_sim.conf new file mode 100644 index 000000000..3fb69a9d4 --- /dev/null +++ b/app/tests/studio/move-layer/basic/native_sim.conf @@ -0,0 +1 @@ +CONFIG_ZMK_TEST_BEHAVIORS=y \ No newline at end of file diff --git a/app/tests/studio/move-layer/basic/native_sim.keymap b/app/tests/studio/move-layer/basic/native_sim.keymap new file mode 100644 index 000000000..91afb0943 --- /dev/null +++ b/app/tests/studio/move-layer/basic/native_sim.keymap @@ -0,0 +1,26 @@ +#include +#include +#include +#include "../behavior_keymap.dtsi" + +// Test moving a layer: swap layers 1 and 2, turn both on, +// press transparent on layer 1 at index 2 +// which should fall through to layer 2 at index 1 +&kscan { + events = < + // move layer 1 to position 2 (effectively swapping layers 1 and 2) + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(0,0,10) + // activate layers 1 and 2 + ZMK_MOCK_PRESS(1,0,10) + ZMK_MOCK_PRESS(1,1,10) + // press position 0,1 on layer 2 - should output C from layer 2 at index 1 + ZMK_MOCK_PRESS(0,1,10) + ZMK_MOCK_RELEASE(0,1,10) + // release layer 2 (at index 1) + ZMK_MOCK_RELEASE(1,1,10) + // press position 0,1 again - should output A from default layer + ZMK_MOCK_PRESS(0,1,10) + ZMK_MOCK_RELEASE(0,1,10) + >; +}; diff --git a/app/tests/studio/move-layer/behavior_keymap.dtsi b/app/tests/studio/move-layer/behavior_keymap.dtsi new file mode 100644 index 000000000..014bba467 --- /dev/null +++ b/app/tests/studio/move-layer/behavior_keymap.dtsi @@ -0,0 +1,34 @@ +#include +#include +#include + +/ { + behaviors { + move_layer: move_layer { + compatible = "zmk,behavior-move-layer"; + #binding-cells = <2>; + }; + }; + + keymap { + compatible = "zmk,keymap"; + + default_layer { + bindings = < + &move_layer 1 2 &kp A + &mo 1 &mo 2>; + }; + + layer_1 { + bindings = < + &kp B &trans + &trans &trans>; + }; + + layer_2 { + bindings = < + &trans &kp C + &trans &trans>; + }; + }; +}; diff --git a/app/tests/studio/remove-layer/basic/events.patterns b/app/tests/studio/remove-layer/basic/events.patterns new file mode 100644 index 000000000..2b04f256a --- /dev/null +++ b/app/tests/studio/remove-layer/basic/events.patterns @@ -0,0 +1,3 @@ +s/.*layer_changed/layer_changed/p +s/.*hid_listener_keycode_//p +s/.*on_remove_layer_binding_pressed/remove_layer/p diff --git a/app/tests/studio/remove-layer/basic/extra-cmake-args b/app/tests/studio/remove-layer/basic/extra-cmake-args new file mode 100644 index 000000000..c5e4f18ef --- /dev/null +++ b/app/tests/studio/remove-layer/basic/extra-cmake-args @@ -0,0 +1,5 @@ +-DCONFIG_ZMK_BEHAVIOR_METADATA=y +-DCONFIG_ZMK_BEHAVIOR_LOCAL_IDS=y +-DCONFIG_ZMK_KEYMAP_LAYER_REORDERING=y +-DCONFIG_ZMK_KEYMAP_SETTINGS_STORAGE=y +-DCONFIG_SETTINGS=y diff --git a/app/tests/studio/remove-layer/basic/keycode_events.snapshot b/app/tests/studio/remove-layer/basic/keycode_events.snapshot new file mode 100644 index 000000000..4aa4cd639 --- /dev/null +++ b/app/tests/studio/remove-layer/basic/keycode_events.snapshot @@ -0,0 +1,7 @@ +remove_layer: Removed layer at index 1 +layer_changed: layer 1 state 1 locked 1 +pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +layer_changed: layer 2 state 1 locked 1 +pressed: usage_page 0x07 keycode 0x06 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x06 implicit_mods 0x00 explicit_mods 0x00 \ No newline at end of file diff --git a/app/tests/studio/remove-layer/basic/native_sim.conf b/app/tests/studio/remove-layer/basic/native_sim.conf new file mode 100644 index 000000000..3fb69a9d4 --- /dev/null +++ b/app/tests/studio/remove-layer/basic/native_sim.conf @@ -0,0 +1 @@ +CONFIG_ZMK_TEST_BEHAVIORS=y \ No newline at end of file diff --git a/app/tests/studio/remove-layer/basic/native_sim.keymap b/app/tests/studio/remove-layer/basic/native_sim.keymap new file mode 100644 index 000000000..9f026de94 --- /dev/null +++ b/app/tests/studio/remove-layer/basic/native_sim.keymap @@ -0,0 +1,25 @@ +#include +#include +#include +#include "../behavior_keymap.dtsi" + +// Test removing layer 1 +&kscan { + events = < + // remove layer 1 + ZMK_MOCK_PRESS(1,0,10) + ZMK_MOCK_RELEASE(1,0,10) + // toggle layer 1 on + ZMK_MOCK_PRESS(0,1,10) + ZMK_MOCK_RELEASE(0,1,10) + // press position 0,0 - should output A (layer 0) because layer 1 was removed + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(0,0,10) + // toggle layer 2 on + ZMK_MOCK_PRESS(1,1,10) + ZMK_MOCK_RELEASE(1,1,10) + // press position 0,0 - should output C (layer 2) + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(0,0,10) + >; +}; diff --git a/app/tests/studio/remove-layer/behavior_keymap.dtsi b/app/tests/studio/remove-layer/behavior_keymap.dtsi new file mode 100644 index 000000000..14f093df8 --- /dev/null +++ b/app/tests/studio/remove-layer/behavior_keymap.dtsi @@ -0,0 +1,34 @@ +#include +#include +#include + +/ { + behaviors { + remove_layer_behavior: remove_layer_behavior { + compatible = "zmk,behavior-remove-layer"; + #binding-cells = <1>; + }; + }; + + keymap { + compatible = "zmk,keymap"; + + default_layer { + bindings = < + &kp A &tog 1 + &remove_layer_behavior 1 &tog 2>; + }; + + layer_1 { + bindings = < + &kp B &tog 1 + &remove_layer_behavior 0 &trans>; + }; + + layer_2 { + bindings = < + &kp C &trans + &none &trans>; + }; + }; +}; diff --git a/app/tests/studio/remove-layer/remove-default-layer/events.patterns b/app/tests/studio/remove-layer/remove-default-layer/events.patterns new file mode 100644 index 000000000..b11b5a68d --- /dev/null +++ b/app/tests/studio/remove-layer/remove-default-layer/events.patterns @@ -0,0 +1,5 @@ +s/.*layer_changed/layer_changed/p +s/.*hid_listener_keycode_//p +s/.*on_remove_layer_binding_pressed/remove_layer/p +s/.*tog_keymap_binding_pressed/tog_pressed/p + diff --git a/app/tests/studio/remove-layer/remove-default-layer/extra-cmake-args b/app/tests/studio/remove-layer/remove-default-layer/extra-cmake-args new file mode 100644 index 000000000..c5e4f18ef --- /dev/null +++ b/app/tests/studio/remove-layer/remove-default-layer/extra-cmake-args @@ -0,0 +1,5 @@ +-DCONFIG_ZMK_BEHAVIOR_METADATA=y +-DCONFIG_ZMK_BEHAVIOR_LOCAL_IDS=y +-DCONFIG_ZMK_KEYMAP_LAYER_REORDERING=y +-DCONFIG_ZMK_KEYMAP_SETTINGS_STORAGE=y +-DCONFIG_SETTINGS=y diff --git a/app/tests/studio/remove-layer/remove-default-layer/keycode_events.snapshot b/app/tests/studio/remove-layer/remove-default-layer/keycode_events.snapshot new file mode 100644 index 000000000..ad99cc87c --- /dev/null +++ b/app/tests/studio/remove-layer/remove-default-layer/keycode_events.snapshot @@ -0,0 +1,8 @@ +tog_pressed: position 1 layer 1 +layer_changed: layer 1 state 1 locked 1 +remove_layer: Removed layer at index 0 +pressed: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00 +tog_pressed: position 1 layer 1 +pressed: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/studio/remove-layer/remove-default-layer/native_sim.conf b/app/tests/studio/remove-layer/remove-default-layer/native_sim.conf new file mode 100644 index 000000000..3fb69a9d4 --- /dev/null +++ b/app/tests/studio/remove-layer/remove-default-layer/native_sim.conf @@ -0,0 +1 @@ +CONFIG_ZMK_TEST_BEHAVIORS=y \ No newline at end of file diff --git a/app/tests/studio/remove-layer/remove-default-layer/native_sim.keymap b/app/tests/studio/remove-layer/remove-default-layer/native_sim.keymap new file mode 100644 index 000000000..1aa43a6a5 --- /dev/null +++ b/app/tests/studio/remove-layer/remove-default-layer/native_sim.keymap @@ -0,0 +1,26 @@ +#include +#include +#include +#include "../behavior_keymap.dtsi" + +// Test removing layer 0: layer 1 becomes bottom layer +&kscan { + events = < + // toggle layer 1 on + ZMK_MOCK_PRESS(0,1,10) + ZMK_MOCK_RELEASE(0,1,10) + // remove layer 0 + ZMK_MOCK_PRESS(1,0,10) + ZMK_MOCK_RELEASE(1,0,10) + // press position 0,0 - should output B (layer 1) + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(0,0,10) + // try to toggle layer 1 off + ZMK_MOCK_PRESS(0,1,10) + ZMK_MOCK_RELEASE(0,1,10) + // press position 0,0 - should output B + // (layer 1 is now the bottom layer, so shouldn't be disabled) + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(0,0,10) + >; +}; diff --git a/app/tests/studio/remove-layer/remove-default-layer/pending b/app/tests/studio/remove-layer/remove-default-layer/pending new file mode 100644 index 000000000..2e4741006 --- /dev/null +++ b/app/tests/studio/remove-layer/remove-default-layer/pending @@ -0,0 +1,2 @@ +This test fails because the keymap function handling position state changes doesn't work correctly when the default layer is removed. +The iteration loop's end condition is incorrect. \ No newline at end of file