upleb.uk

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

summaryrefslogtreecommitdiff
path: root/tests/unit
diff options
context:
space:
mode:
authorYour Name <you@example.com>2026-05-18 22:46:53 +0530
committerYour Name <you@example.com>2026-05-18 22:46:53 +0530
commit910fe47b2ed1284d93ff4d274f5c2341cb0d2d0b (patch)
tree2c0e48e153337b22dbe7f9cc531f5e92e91561fb /tests/unit
parentf2c4b15c72a2fa89caed5d8a11a4ea9832c48be6 (diff)
feat: add on-screen keyboard with layout/hit detection tests
Diffstat (limited to 'tests/unit')
-rw-r--r--tests/unit/Makefile5
-rw-r--r--tests/unit/test_keyboard.c168
2 files changed, 172 insertions, 1 deletions
diff --git a/tests/unit/Makefile b/tests/unit/Makefile
index 5bc5eb1..b978891 100644
--- a/tests/unit/Makefile
+++ b/tests/unit/Makefile
@@ -22,7 +22,7 @@ LDFLAGS := -lmbedcrypto -lcjson -lm
22 22
23SECP256K1_OBJ := secp256k1.o precomputed_ecmult.o precomputed_ecmult_gen.o 23SECP256K1_OBJ := secp256k1.o precomputed_ecmult.o precomputed_ecmult_gen.o
24 24
25TESTS := test_geohash test_identity test_nostr_event test_cashu test_session test_tollgate_client test_lnurl_pay test_lightning_payout test_mcp_handler test_nip04 test_cvm_server test_touch 25TESTS := test_geohash test_identity test_nostr_event test_cashu test_session test_tollgate_client test_lnurl_pay test_lightning_payout test_mcp_handler test_nip04 test_cvm_server test_touch test_keyboard
26 26
27.PHONY: all test clean $(TESTS) 27.PHONY: all test clean $(TESTS)
28 28
@@ -84,5 +84,8 @@ test_cvm_server: test_cvm_server.c
84test_touch: test_touch.c $(REPO_ROOT)/main/touch.c 84test_touch: test_touch.c $(REPO_ROOT)/main/touch.c
85 $(CC) $(CFLAGS) $< $(REPO_ROOT)/main/touch.c -o $@ $(LDFLAGS) 85 $(CC) $(CFLAGS) $< $(REPO_ROOT)/main/touch.c -o $@ $(LDFLAGS)
86 86
87test_keyboard: test_keyboard.c $(REPO_ROOT)/main/keyboard.c
88 $(CC) $(CFLAGS) $< $(REPO_ROOT)/main/keyboard.c -o $@ $(LDFLAGS)
89
87clean: 90clean:
88 rm -f $(TESTS) $(SECP256K1_OBJ) 91 rm -f $(TESTS) $(SECP256K1_OBJ)
diff --git a/tests/unit/test_keyboard.c b/tests/unit/test_keyboard.c
new file mode 100644
index 0000000..4cb923d
--- /dev/null
+++ b/tests/unit/test_keyboard.c
@@ -0,0 +1,168 @@
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, 100, KB_ALPHA_LOWER);
42 ASSERT(r.action == KB_ACTION_NONE, "Touch above keyboard = NONE");
43
44 r = kb_hit_test(160, 500, KB_ALPHA_LOWER);
45 ASSERT(r.action == KB_ACTION_NONE, "Touch below keyboard = NONE");
46 }
47
48 {
49 int mid_x = 5 + KB_KEY_W / 2;
50 int mid_y = KB_START_Y + KB_KEY_H / 2;
51 kb_result_t r = kb_hit_test(mid_x, mid_y, KB_ALPHA_LOWER);
52 ASSERT(r.action == KB_ACTION_CHAR, "Row 0 first key is a char");
53 ASSERT_EQ_INT('q', r.ch, "Row 0 first key = 'q'");
54 }
55
56 {
57 int x = 5 + KB_KEY_W + KB_KEY_GAP + KB_KEY_W / 2;
58 int y = KB_START_Y + KB_KEY_H / 2;
59 kb_result_t r = kb_hit_test(x, y, KB_ALPHA_LOWER);
60 ASSERT(r.action == KB_ACTION_CHAR, "Row 0 second key is a char");
61 ASSERT_EQ_INT('w', r.ch, "Row 0 second key = 'w'");
62 }
63
64 {
65 int y_row1 = KB_START_Y + (KB_KEY_H + KB_KEY_GAP) + KB_KEY_H / 2;
66 int x_row1 = 14 + KB_KEY_W / 2;
67 kb_result_t r = kb_hit_test(x_row1, y_row1, KB_ALPHA_LOWER);
68 ASSERT(r.action == KB_ACTION_CHAR, "Row 1 first key is a char");
69 ASSERT_EQ_INT('a', r.ch, "Row 1 first key = 'a'");
70 }
71
72 {
73 int y_row2 = KB_START_Y + 2 * (KB_KEY_H + KB_KEY_GAP) + KB_KEY_H / 2;
74 int x_row2 = 23 + 42 / 2;
75 kb_result_t r = kb_hit_test(x_row2, y_row2, KB_ALPHA_LOWER);
76 ASSERT(r.action == KB_ACTION_SHIFT, "Row 2 first key = SHIFT");
77 }
78
79 {
80 kb_state_t st;
81 kb_state_init(&st);
82 ASSERT_EQ_INT(0, st.cursor, "Initial cursor = 0");
83 ASSERT_EQ_INT(KB_ALPHA_LOWER, st.layer, "Initial layer = lower");
84 ASSERT_EQ_STR("", st.input, "Initial input is empty");
85
86 kb_apply(&st, (kb_result_t){KB_ACTION_CHAR, 'h'});
87 ASSERT_EQ_STR("h", st.input, "After typing 'h': input='h'");
88 ASSERT_EQ_INT(1, st.cursor, "After typing 'h': cursor=1");
89
90 kb_apply(&st, (kb_result_t){KB_ACTION_CHAR, 'i'});
91 ASSERT_EQ_STR("hi", st.input, "After typing 'i': input='hi'");
92
93 kb_apply(&st, (kb_result_t){KB_ACTION_BACKSPACE, 0});
94 ASSERT_EQ_STR("h", st.input, "After backspace: input='h'");
95 ASSERT_EQ_INT(1, st.cursor, "After backspace: cursor=1");
96
97 kb_apply(&st, (kb_result_t){KB_ACTION_BACKSPACE, 0});
98 ASSERT_EQ_STR("", st.input, "After second backspace: empty");
99 ASSERT_EQ_INT(0, st.cursor, "After second backspace: cursor=0");
100
101 kb_apply(&st, (kb_result_t){KB_ACTION_BACKSPACE, 0});
102 ASSERT_EQ_INT(0, st.cursor, "Backspace on empty stays at 0");
103 }
104
105 {
106 kb_state_t st;
107 kb_state_init(&st);
108
109 kb_apply(&st, (kb_result_t){KB_ACTION_SHIFT, 0});
110 ASSERT_EQ_INT(KB_ALPHA_UPPER, st.layer, "Shift: lower->upper");
111
112 kb_apply(&st, (kb_result_t){KB_ACTION_SHIFT, 0});
113 ASSERT_EQ_INT(KB_ALPHA_LOWER, st.layer, "Shift: upper->lower");
114
115 kb_apply(&st, (kb_result_t){KB_ACTION_LAYER, 0});
116 ASSERT_EQ_INT(KB_NUMSYM, st.layer, "Layer: lower->numsym");
117
118 kb_apply(&st, (kb_result_t){KB_ACTION_LAYER, 0});
119 ASSERT_EQ_INT(KB_ALPHA_LOWER, st.layer, "Layer: numsym->lower");
120 }
121
122 {
123 kb_state_t st;
124 kb_state_init(&st);
125
126 for (int i = 0; i < KB_INPUT_MAX; i++) {
127 kb_apply(&st, (kb_result_t){KB_ACTION_CHAR, 'a' + (i % 26)});
128 }
129 ASSERT_EQ_INT(KB_INPUT_MAX, st.cursor, "Filled to max");
130 ASSERT_EQ_INT(KB_INPUT_MAX, (int)strlen(st.input), "String length = max");
131
132 kb_apply(&st, (kb_result_t){KB_ACTION_CHAR, 'Z'});
133 ASSERT_EQ_INT(KB_INPUT_MAX, st.cursor, "Overflow blocked");
134 ASSERT_EQ_INT(KB_INPUT_MAX, (int)strlen(st.input), "Length unchanged after overflow");
135 }
136
137 {
138 kb_state_t st;
139 kb_state_init(&st);
140
141 kb_apply(&st, (kb_result_t){KB_ACTION_SPACE, ' '});
142 ASSERT_EQ_STR(" ", st.input, "Space adds space char");
143 ASSERT_EQ_INT(1, st.cursor, "Space increments cursor");
144 }
145
146 {
147 kb_state_t st;
148 kb_state_init(&st);
149 kb_result_t none = {KB_ACTION_NONE, 0};
150 kb_apply(&st, none);
151 ASSERT_EQ_STR("", st.input, "NONE action does nothing");
152
153 kb_apply(NULL, (kb_result_t){KB_ACTION_CHAR, 'x'});
154 }
155
156 {
157 kb_state_t st;
158 kb_state_init(&st);
159
160 kb_apply(&st, (kb_result_t){KB_ACTION_CHAR, 'P'});
161 kb_apply(&st, (kb_result_t){KB_ACTION_CHAR, '@'});
162 kb_apply(&st, (kb_result_t){KB_ACTION_CHAR, 's'});
163 kb_apply(&st, (kb_result_t){KB_ACTION_CHAR, 's'});
164 ASSERT_EQ_STR("P@ss", st.input, "Password build: P@ss");
165 }
166
167 TEST_SUMMARY();
168}