upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/tests/unit/test_keyboard.c
diff options
context:
space:
mode:
authorYour Name <you@example.com>2026-05-20 02:14:45 +0530
committerYour Name <you@example.com>2026-05-20 02:14:45 +0530
commit899016795c389151e6b486ec470653f5688e5c5f (patch)
tree64b4c732d6f585552fd14df5f47890764c66ac1a /tests/unit/test_keyboard.c
parent4f713120dbedd37a3512c2ec1af0766025a59d57 (diff)
feat: merge display-fix — touch driver, keyboard, WiFi setup UI
Squash-merge of feature/display-fix (27 commits): - AXS15231B touch driver with coordinate parsing (touch.c/h) - On-screen keyboard with layout/hit detection (keyboard.c/h) - WiFi setup state machine + config_add_wifi (wifi_setup.c/h) - Web-based WiFi setup via captive portal - Display rotation fix (stride=480), color fixes, DMA byte-swap - WiFi QR code on BOOT and ERROR screens - ERROR state transition after WiFi retries exhausted - Unit tests: test_touch, test_keyboard, test_wifi_setup - Integration test: wifi_setup.mjs - E2E test: wifi-setup.spec.mjs - Per-board hardware locks for flash targets Conflicts resolved: took master for config/cvm/api/main (more current), took display-fix for display/axs15231b (newer fixes).
Diffstat (limited to 'tests/unit/test_keyboard.c')
-rw-r--r--tests/unit/test_keyboard.c172
1 files changed, 172 insertions, 0 deletions
diff --git a/tests/unit/test_keyboard.c b/tests/unit/test_keyboard.c
new file mode 100644
index 0000000..81ca328
--- /dev/null
+++ b/tests/unit/test_keyboard.c
@@ -0,0 +1,172 @@
1#include "test_framework.h"
2#include "../../main/keyboard.h"
3#include <string.h>
4
5int main(void)
6{
7 printf("=== test_keyboard ===\n");
8
9 const char *keys;
10 int count;
11
12 count = kb_get_row_keys(0, KB_ALPHA_LOWER, &keys);
13 ASSERT_EQ_INT(10, count, "Row 0 alpha lower has 10 keys");
14 ASSERT_EQ_INT('q', keys[0], "Row 0 starts with 'q'");
15 ASSERT_EQ_INT('p', keys[9], "Row 0 ends with 'p'");
16
17 count = kb_get_row_keys(1, KB_ALPHA_LOWER, &keys);
18 ASSERT_EQ_INT(9, count, "Row 1 alpha lower has 9 keys");
19 ASSERT_EQ_INT('a', keys[0], "Row 1 starts with 'a'");
20
21 count = kb_get_row_keys(2, KB_ALPHA_LOWER, &keys);
22 ASSERT(count > 0, "Row 2 alpha lower has keys");
23 ASSERT_EQ_INT('\001', keys[0], "Row 2 starts with SHIFT control char");
24
25 count = kb_get_row_keys(0, KB_ALPHA_UPPER, &keys);
26 ASSERT_EQ_INT(10, count, "Row 0 alpha upper has 10 keys");
27 ASSERT_EQ_INT('Q', keys[0], "Row 0 upper starts with 'Q'");
28
29 count = kb_get_row_keys(0, KB_NUMSYM, &keys);
30 ASSERT_EQ_INT(10, count, "Row 0 numsym has 10 keys");
31 ASSERT_EQ_INT('1', keys[0], "Row 0 numsym starts with '1'");
32 ASSERT_EQ_INT('0', keys[9], "Row 0 numsym ends with '0'");
33
34 count = kb_get_row_keys(-1, KB_ALPHA_LOWER, &keys);
35 ASSERT_EQ_INT(0, count, "Invalid row -1 returns 0");
36
37 count = kb_get_row_keys(99, KB_ALPHA_LOWER, &keys);
38 ASSERT_EQ_INT(0, count, "Invalid row 99 returns 0");
39
40 {
41 kb_result_t r = kb_hit_test(160, 10, KB_ALPHA_LOWER);
42 ASSERT(r.action == KB_ACTION_NONE, "Touch above keyboard = NONE");
43
44 r = kb_hit_test(160, 70 + 4 * (36 + 2) + 10, KB_ALPHA_LOWER);
45 ASSERT(r.action == KB_ACTION_NONE, "Touch below keyboard = NONE");
46 }
47
48 {
49 int margin_r0 = (320 - (10 * 28 + 9 * 2)) / 2;
50 int mid_x = margin_r0 + 28 / 2;
51 int mid_y = 70 + 36 / 2;
52 kb_result_t r = kb_hit_test(mid_x, mid_y, KB_ALPHA_LOWER);
53 ASSERT(r.action == KB_ACTION_CHAR, "Row 0 first key is a char");
54 ASSERT_EQ_INT('q', r.ch, "Row 0 first key = 'q'");
55 }
56
57 {
58 int margin_r0 = (320 - (10 * 28 + 9 * 2)) / 2;
59 int x = margin_r0 + 28 + 2 + 28 / 2;
60 int y = 70 + 36 / 2;
61 kb_result_t r = kb_hit_test(x, y, KB_ALPHA_LOWER);
62 ASSERT(r.action == KB_ACTION_CHAR, "Row 0 second key is a char");
63 ASSERT_EQ_INT('w', r.ch, "Row 0 second key = 'w'");
64 }
65
66 {
67 int margin_r1 = (320 - (9 * 28 + 8 * 2)) / 2;
68 int y_row1 = 70 + (36 + 2) + 36 / 2;
69 int x_row1 = margin_r1 + 28 / 2 + 28 / 2;
70 kb_result_t r = kb_hit_test(x_row1, y_row1, KB_ALPHA_LOWER);
71 ASSERT(r.action == KB_ACTION_CHAR, "Row 1 first key is a char");
72 ASSERT_EQ_INT('a', r.ch, "Row 1 first key = 'a'");
73 }
74
75 {
76 int margin_r2 = (320 - (9 * 28 + 8 * 2)) / 2 + 28;
77 int y_row2 = 70 + 2 * (36 + 2) + 36 / 2;
78 int x_row2 = margin_r2 + 28 / 2;
79 kb_result_t r = kb_hit_test(x_row2, y_row2, KB_ALPHA_LOWER);
80 ASSERT(r.action == KB_ACTION_SHIFT, "Row 2 first key = SHIFT");
81 }
82
83 {
84 kb_state_t st;
85 kb_state_init(&st);
86 ASSERT_EQ_INT(0, st.cursor, "Initial cursor = 0");
87 ASSERT_EQ_INT(KB_ALPHA_LOWER, st.layer, "Initial layer = lower");
88 ASSERT_EQ_STR("", st.input, "Initial input is empty");
89
90 kb_apply(&st, (kb_result_t){KB_ACTION_CHAR, 'h'});
91 ASSERT_EQ_STR("h", st.input, "After typing 'h': input='h'");
92 ASSERT_EQ_INT(1, st.cursor, "After typing 'h': cursor=1");
93
94 kb_apply(&st, (kb_result_t){KB_ACTION_CHAR, 'i'});
95 ASSERT_EQ_STR("hi", st.input, "After typing 'i': input='hi'");
96
97 kb_apply(&st, (kb_result_t){KB_ACTION_BACKSPACE, 0});
98 ASSERT_EQ_STR("h", st.input, "After backspace: input='h'");
99 ASSERT_EQ_INT(1, st.cursor, "After backspace: cursor=1");
100
101 kb_apply(&st, (kb_result_t){KB_ACTION_BACKSPACE, 0});
102 ASSERT_EQ_STR("", st.input, "After second backspace: empty");
103 ASSERT_EQ_INT(0, st.cursor, "After second backspace: cursor=0");
104
105 kb_apply(&st, (kb_result_t){KB_ACTION_BACKSPACE, 0});
106 ASSERT_EQ_INT(0, st.cursor, "Backspace on empty stays at 0");
107 }
108
109 {
110 kb_state_t st;
111 kb_state_init(&st);
112
113 kb_apply(&st, (kb_result_t){KB_ACTION_SHIFT, 0});
114 ASSERT_EQ_INT(KB_ALPHA_UPPER, st.layer, "Shift: lower->upper");
115
116 kb_apply(&st, (kb_result_t){KB_ACTION_SHIFT, 0});
117 ASSERT_EQ_INT(KB_ALPHA_LOWER, st.layer, "Shift: upper->lower");
118
119 kb_apply(&st, (kb_result_t){KB_ACTION_LAYER, 0});
120 ASSERT_EQ_INT(KB_NUMSYM, st.layer, "Layer: lower->numsym");
121
122 kb_apply(&st, (kb_result_t){KB_ACTION_LAYER, 0});
123 ASSERT_EQ_INT(KB_ALPHA_LOWER, st.layer, "Layer: numsym->lower");
124 }
125
126 {
127 kb_state_t st;
128 kb_state_init(&st);
129
130 for (int i = 0; i < KB_INPUT_MAX; i++) {
131 kb_apply(&st, (kb_result_t){KB_ACTION_CHAR, 'a' + (i % 26)});
132 }
133 ASSERT_EQ_INT(KB_INPUT_MAX, st.cursor, "Filled to max");
134 ASSERT_EQ_INT(KB_INPUT_MAX, (int)strlen(st.input), "String length = max");
135
136 kb_apply(&st, (kb_result_t){KB_ACTION_CHAR, 'Z'});
137 ASSERT_EQ_INT(KB_INPUT_MAX, st.cursor, "Overflow blocked");
138 ASSERT_EQ_INT(KB_INPUT_MAX, (int)strlen(st.input), "Length unchanged after overflow");
139 }
140
141 {
142 kb_state_t st;
143 kb_state_init(&st);
144
145 kb_apply(&st, (kb_result_t){KB_ACTION_SPACE, ' '});
146 ASSERT_EQ_STR(" ", st.input, "Space adds space char");
147 ASSERT_EQ_INT(1, st.cursor, "Space increments cursor");
148 }
149
150 {
151 kb_state_t st;
152 kb_state_init(&st);
153 kb_result_t none = {KB_ACTION_NONE, 0};
154 kb_apply(&st, none);
155 ASSERT_EQ_STR("", st.input, "NONE action does nothing");
156
157 kb_apply(NULL, (kb_result_t){KB_ACTION_CHAR, 'x'});
158 }
159
160 {
161 kb_state_t st;
162 kb_state_init(&st);
163
164 kb_apply(&st, (kb_result_t){KB_ACTION_CHAR, 'P'});
165 kb_apply(&st, (kb_result_t){KB_ACTION_CHAR, '@'});
166 kb_apply(&st, (kb_result_t){KB_ACTION_CHAR, 's'});
167 kb_apply(&st, (kb_result_t){KB_ACTION_CHAR, 's'});
168 ASSERT_EQ_STR("P@ss", st.input, "Password build: P@ss");
169 }
170
171 TEST_SUMMARY();
172}