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
This commit is contained in:
Nicolas Munnich 2026-01-08 01:01:05 +01:00 committed by GitHub
parent 70ab6b243a
commit 19582174f3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
38 changed files with 573 additions and 3 deletions

View File

@ -11,11 +11,12 @@
#include <zephyr/devicetree.h>
#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)))

View File

@ -1,4 +1,5 @@
zephyr_include_directories(include)
add_subdirectory(drivers)
add_subdirectory(test-behaviors)
add_subdirectory(lib)

View File

@ -1,3 +1,4 @@
rsource "drivers/Kconfig"
rsource "lib/Kconfig"
rsource "lib/Kconfig"
rsource "test-behaviors/Kconfig"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -0,0 +1,2 @@
config ZMK_TEST_BEHAVIORS
bool "Include behaviors used for testing purposes"

View File

@ -0,0 +1,50 @@
/*
* Copyright (c) 2025 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#define DT_DRV_COMPAT zmk_behavior_add_layer
#include <zephyr/device.h>
#include <drivers/behavior.h>
#include <zephyr/logging/log.h>
#include <zmk/keymap.h>
#include <zmk/behavior.h>
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)

View File

@ -0,0 +1,47 @@
/*
* Copyright (c) 2025 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#define DT_DRV_COMPAT zmk_behavior_move_layer
#include <zephyr/device.h>
#include <drivers/behavior.h>
#include <zephyr/logging/log.h>
#include <zmk/keymap.h>
#include <zmk/behavior.h>
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)

View File

@ -0,0 +1,49 @@
/*
* Copyright (c) 2025 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#define DT_DRV_COMPAT zmk_behavior_remove_layer
#include <zephyr/device.h>
#include <drivers/behavior.h>
#include <zephyr/logging/log.h>
#include <zmk/keymap.h>
#include <zmk/behavior.h>
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)

View File

@ -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 <zephyr/device.h>
#include <drivers/behavior.h>
#include <zephyr/logging/log.h>
#include <zmk/keymap.h>
#include <zmk/behavior.h>
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)

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1 @@
CONFIG_ZMK_TEST_BEHAVIORS=y

View File

@ -0,0 +1,23 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
#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)
>;
};

View File

@ -0,0 +1,32 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
/ {
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";
};
};
};

View File

@ -0,0 +1,3 @@
s/.*layer_changed/layer_changed/p
s/.*hid_listener_keycode_//p
s/.*on_move_layer_binding_pressed/move_layer/p

View File

@ -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

View File

@ -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

View File

@ -0,0 +1 @@
CONFIG_ZMK_TEST_BEHAVIORS=y

View File

@ -0,0 +1,26 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
#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)
>;
};

View File

@ -0,0 +1,34 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
/ {
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>;
};
};
};

View File

@ -0,0 +1,3 @@
s/.*layer_changed/layer_changed/p
s/.*hid_listener_keycode_//p
s/.*on_remove_layer_binding_pressed/remove_layer/p

View File

@ -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

View File

@ -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

View File

@ -0,0 +1 @@
CONFIG_ZMK_TEST_BEHAVIORS=y

View File

@ -0,0 +1,25 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
#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)
>;
};

View File

@ -0,0 +1,34 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
/ {
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>;
};
};
};

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1 @@
CONFIG_ZMK_TEST_BEHAVIORS=y

View File

@ -0,0 +1,26 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
#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)
>;
};

View File

@ -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.