diff options
| author | Your Name <you@example.com> | 2026-05-20 02:14:45 +0530 |
|---|---|---|
| committer | Your Name <you@example.com> | 2026-05-20 02:14:45 +0530 |
| commit | 899016795c389151e6b486ec470653f5688e5c5f (patch) | |
| tree | 64b4c732d6f585552fd14df5f47890764c66ac1a /tests/unit | |
| parent | 4f713120dbedd37a3512c2ec1af0766025a59d57 (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')
| -rw-r--r-- | tests/unit/stubs/driver/gpio.h | 44 | ||||
| -rw-r--r-- | tests/unit/stubs/driver/i2c_master.h | 39 | ||||
| -rw-r--r-- | tests/unit/stubs/driver/i2c_types.h | 45 | ||||
| -rwxr-xr-x | tests/unit/test_keyboard | bin | 0 -> 35416 bytes | |||
| -rw-r--r-- | tests/unit/test_keyboard.c | 172 | ||||
| -rwxr-xr-x | tests/unit/test_touch | bin | 0 -> 30304 bytes | |||
| -rw-r--r-- | tests/unit/test_touch.c | 93 | ||||
| -rwxr-xr-x | tests/unit/test_wifi_setup | bin | 0 -> 33336 bytes | |||
| -rw-r--r-- | tests/unit/test_wifi_setup.c | 121 |
9 files changed, 514 insertions, 0 deletions
diff --git a/tests/unit/stubs/driver/gpio.h b/tests/unit/stubs/driver/gpio.h new file mode 100644 index 0000000..d8dda0a --- /dev/null +++ b/tests/unit/stubs/driver/gpio.h | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | #ifndef STUBS_DRIVER_GPIO_H | ||
| 2 | #define STUBS_DRIVER_GPIO_H | ||
| 3 | |||
| 4 | #include <stdint.h> | ||
| 5 | |||
| 6 | typedef enum { | ||
| 7 | GPIO_MODE_DISABLE = 0, | ||
| 8 | GPIO_MODE_INPUT, | ||
| 9 | GPIO_MODE_OUTPUT, | ||
| 10 | GPIO_MODE_OUTPUT_OD, | ||
| 11 | GPIO_MODE_INPUT_OUTPUT_OD, | ||
| 12 | GPIO_MODE_INPUT_OUTPUT, | ||
| 13 | } gpio_mode_t; | ||
| 14 | |||
| 15 | typedef enum { | ||
| 16 | GPIO_INTR_DISABLE = 0, | ||
| 17 | } gpio_int_type_t; | ||
| 18 | |||
| 19 | typedef enum { | ||
| 20 | GPIO_PULLUP_DISABLE = 0, | ||
| 21 | GPIO_PULLUP_ENABLE, | ||
| 22 | } gpio_pullup_t; | ||
| 23 | |||
| 24 | typedef enum { | ||
| 25 | GPIO_PULLDOWN_DISABLE = 0, | ||
| 26 | GPIO_PULLDOWN_ENABLE, | ||
| 27 | } gpio_pulldown_t; | ||
| 28 | |||
| 29 | typedef struct { | ||
| 30 | uint64_t pin_bit_mask; | ||
| 31 | gpio_mode_t mode; | ||
| 32 | gpio_pullup_t pull_up_en; | ||
| 33 | gpio_pulldown_t pull_down_en; | ||
| 34 | gpio_int_type_t intr_type; | ||
| 35 | } gpio_config_t; | ||
| 36 | |||
| 37 | static inline int gpio_config(const gpio_config_t *cfg) { (void)cfg; return 0; } | ||
| 38 | static inline int gpio_set_level(uint32_t gpio_num, uint32_t level) { (void)gpio_num; (void)level; return 0; } | ||
| 39 | |||
| 40 | #define GPIO_INTR_DISABLE 0 | ||
| 41 | #define GPIO_PULLUP_DISABLE 0 | ||
| 42 | #define GPIO_PULLDOWN_DISABLE 0 | ||
| 43 | |||
| 44 | #endif | ||
diff --git a/tests/unit/stubs/driver/i2c_master.h b/tests/unit/stubs/driver/i2c_master.h new file mode 100644 index 0000000..f49eaad --- /dev/null +++ b/tests/unit/stubs/driver/i2c_master.h | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | #ifndef STUBS_DRIVER_I2C_MASTER_H | ||
| 2 | #define STUBS_DRIVER_I2C_MASTER_H | ||
| 3 | |||
| 4 | #include "driver/i2c_types.h" | ||
| 5 | #include "esp_err.h" | ||
| 6 | #include <stdint.h> | ||
| 7 | #include <stddef.h> | ||
| 8 | |||
| 9 | static inline esp_err_t i2c_new_master_bus(const i2c_master_bus_config_t *cfg, i2c_master_bus_handle_t *ret) { | ||
| 10 | (void)cfg; (void)ret; | ||
| 11 | return ESP_OK; | ||
| 12 | } | ||
| 13 | |||
| 14 | static inline esp_err_t i2c_master_bus_add_device(i2c_master_bus_handle_t bus, const i2c_device_config_t *cfg, i2c_master_dev_handle_t *ret) { | ||
| 15 | (void)bus; (void)cfg; (void)ret; | ||
| 16 | return ESP_OK; | ||
| 17 | } | ||
| 18 | |||
| 19 | static inline esp_err_t i2c_master_transmit(i2c_master_dev_handle_t dev, const uint8_t *buf, size_t len, int timeout_ms) { | ||
| 20 | (void)dev; (void)buf; (void)len; (void)timeout_ms; | ||
| 21 | return ESP_OK; | ||
| 22 | } | ||
| 23 | |||
| 24 | static inline esp_err_t i2c_master_receive(i2c_master_dev_handle_t dev, uint8_t *buf, size_t len, int timeout_ms) { | ||
| 25 | (void)dev; (void)buf; (void)len; (void)timeout_ms; | ||
| 26 | return ESP_OK; | ||
| 27 | } | ||
| 28 | |||
| 29 | static inline esp_err_t i2c_master_bus_rm_device(i2c_master_dev_handle_t dev) { | ||
| 30 | (void)dev; | ||
| 31 | return ESP_OK; | ||
| 32 | } | ||
| 33 | |||
| 34 | static inline esp_err_t i2c_del_master_bus(i2c_master_bus_handle_t bus) { | ||
| 35 | (void)bus; | ||
| 36 | return ESP_OK; | ||
| 37 | } | ||
| 38 | |||
| 39 | #endif | ||
diff --git a/tests/unit/stubs/driver/i2c_types.h b/tests/unit/stubs/driver/i2c_types.h new file mode 100644 index 0000000..3590a8b --- /dev/null +++ b/tests/unit/stubs/driver/i2c_types.h | |||
| @@ -0,0 +1,45 @@ | |||
| 1 | #ifndef STUBS_DRIVER_I2C_TYPES_H | ||
| 2 | #define STUBS_DRIVER_I2C_TYPES_H | ||
| 3 | |||
| 4 | #include <stdint.h> | ||
| 5 | #include <stddef.h> | ||
| 6 | |||
| 7 | typedef int i2c_port_num_t; | ||
| 8 | #define I2C_NUM_0 0 | ||
| 9 | |||
| 10 | typedef enum { | ||
| 11 | I2C_ADDR_BIT_LEN_7 = 0, | ||
| 12 | } i2c_addr_bit_len_t; | ||
| 13 | |||
| 14 | typedef enum { | ||
| 15 | I2C_CLK_SRC_DEFAULT = 0, | ||
| 16 | } i2c_clock_source_t; | ||
| 17 | |||
| 18 | typedef struct i2c_master_bus_t *i2c_master_bus_handle_t; | ||
| 19 | typedef struct i2c_master_dev_t *i2c_master_dev_handle_t; | ||
| 20 | |||
| 21 | typedef struct { | ||
| 22 | i2c_port_num_t i2c_port; | ||
| 23 | int sda_io_num; | ||
| 24 | int scl_io_num; | ||
| 25 | i2c_clock_source_t clk_source; | ||
| 26 | uint8_t glitch_ignore_cnt; | ||
| 27 | int intr_priority; | ||
| 28 | size_t trans_queue_depth; | ||
| 29 | struct { | ||
| 30 | uint32_t enable_internal_pullup : 1; | ||
| 31 | uint32_t allow_pd : 1; | ||
| 32 | } flags; | ||
| 33 | } i2c_master_bus_config_t; | ||
| 34 | |||
| 35 | typedef struct { | ||
| 36 | i2c_addr_bit_len_t dev_addr_length; | ||
| 37 | uint16_t device_address; | ||
| 38 | uint32_t scl_speed_hz; | ||
| 39 | uint32_t scl_wait_us; | ||
| 40 | struct { | ||
| 41 | uint32_t disable_ack_check : 1; | ||
| 42 | } flags; | ||
| 43 | } i2c_device_config_t; | ||
| 44 | |||
| 45 | #endif | ||
diff --git a/tests/unit/test_keyboard b/tests/unit/test_keyboard new file mode 100755 index 0000000..61cc9f5 --- /dev/null +++ b/tests/unit/test_keyboard | |||
| Binary files differ | |||
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 | |||
| 5 | int 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 | } | ||
diff --git a/tests/unit/test_touch b/tests/unit/test_touch new file mode 100755 index 0000000..43c8790 --- /dev/null +++ b/tests/unit/test_touch | |||
| Binary files differ | |||
diff --git a/tests/unit/test_touch.c b/tests/unit/test_touch.c new file mode 100644 index 0000000..13f04b5 --- /dev/null +++ b/tests/unit/test_touch.c | |||
| @@ -0,0 +1,93 @@ | |||
| 1 | #include "test_framework.h" | ||
| 2 | #include "../../main/touch.h" | ||
| 3 | #include <string.h> | ||
| 4 | |||
| 5 | int main(void) | ||
| 6 | { | ||
| 7 | touch_point_t pt; | ||
| 8 | uint8_t data[8]; | ||
| 9 | |||
| 10 | printf("=== test_touch ===\n"); | ||
| 11 | |||
| 12 | memset(data, 0, sizeof(data)); | ||
| 13 | touch_parse_raw(data, &pt); | ||
| 14 | ASSERT(!pt.touched, "All-zero data = no touch"); | ||
| 15 | |||
| 16 | data[0] = 1; | ||
| 17 | data[1] = 1; | ||
| 18 | data[2] = 0; | ||
| 19 | data[3] = 0; | ||
| 20 | touch_parse_raw(data, &pt); | ||
| 21 | ASSERT(!pt.touched, "data[0]=1 = no touch (gesture byte nonzero)"); | ||
| 22 | |||
| 23 | data[0] = 0; | ||
| 24 | data[1] = 0; | ||
| 25 | touch_parse_raw(data, &pt); | ||
| 26 | ASSERT(!pt.touched, "data[1]=0 = no touch (touch count zero)"); | ||
| 27 | |||
| 28 | data[0] = 0; | ||
| 29 | data[1] = 1; | ||
| 30 | data[2] = 0x00; | ||
| 31 | data[3] = 0x64; | ||
| 32 | data[4] = 0x00; | ||
| 33 | data[5] = 0xC8; | ||
| 34 | data[6] = 0; | ||
| 35 | data[7] = 0; | ||
| 36 | touch_parse_raw(data, &pt); | ||
| 37 | ASSERT(pt.touched, "Valid touch: touched=true"); | ||
| 38 | ASSERT_EQ_INT(100, (int)pt.x, "Valid touch: x=100"); | ||
| 39 | ASSERT_EQ_INT(200, (int)pt.y, "Valid touch: y=200"); | ||
| 40 | |||
| 41 | data[0] = 0; | ||
| 42 | data[1] = 1; | ||
| 43 | data[2] = 0x0F; | ||
| 44 | data[3] = 0xFF; | ||
| 45 | data[4] = 0x0F; | ||
| 46 | data[5] = 0xFF; | ||
| 47 | touch_parse_raw(data, &pt); | ||
| 48 | ASSERT(pt.touched, "Max raw coords: touched=true"); | ||
| 49 | ASSERT_EQ_INT(TOUCH_MAX_X, (int)pt.x, "Max raw coords clamped to 319"); | ||
| 50 | ASSERT_EQ_INT(TOUCH_MAX_Y, (int)pt.y, "Max raw coords clamped to 479"); | ||
| 51 | |||
| 52 | data[0] = 0; | ||
| 53 | data[1] = 1; | ||
| 54 | data[2] = 0x05; | ||
| 55 | data[3] = 0x00; | ||
| 56 | data[4] = 0x08; | ||
| 57 | data[5] = 0x00; | ||
| 58 | touch_parse_raw(data, &pt); | ||
| 59 | ASSERT(pt.touched, "12-bit coords: touched=true"); | ||
| 60 | ASSERT_EQ_INT(TOUCH_MAX_X, (int)pt.x, "12-bit x: (0x05 << 8) | 0x00 = 1280, clamped to 319"); | ||
| 61 | |||
| 62 | data[0] = 0; | ||
| 63 | data[1] = 2; | ||
| 64 | touch_parse_raw(data, &pt); | ||
| 65 | ASSERT(!pt.touched, "data[1]=2 = too many touches, reject"); | ||
| 66 | |||
| 67 | touch_parse_raw(NULL, &pt); | ||
| 68 | ASSERT(!pt.touched, "NULL data = no touch"); | ||
| 69 | |||
| 70 | data[0] = 0; | ||
| 71 | data[1] = 1; | ||
| 72 | data[2] = 0x00; | ||
| 73 | data[3] = 0x00; | ||
| 74 | data[4] = 0x00; | ||
| 75 | data[5] = 0x00; | ||
| 76 | touch_parse_raw(data, &pt); | ||
| 77 | ASSERT(pt.touched, "Origin (0,0): touched=true"); | ||
| 78 | ASSERT_EQ_INT(0, (int)pt.x, "Origin: x=0"); | ||
| 79 | ASSERT_EQ_INT(0, (int)pt.y, "Origin: y=0"); | ||
| 80 | |||
| 81 | data[0] = 0; | ||
| 82 | data[1] = 1; | ||
| 83 | data[2] = 0x01; | ||
| 84 | data[3] = 0x3F; | ||
| 85 | data[4] = 0x01; | ||
| 86 | data[5] = 0xDF; | ||
| 87 | touch_parse_raw(data, &pt); | ||
| 88 | ASSERT(pt.touched, "Mid-screen: touched=true"); | ||
| 89 | ASSERT_EQ_INT(319, (int)pt.x, "Mid-screen: x=0x13F=319"); | ||
| 90 | ASSERT_EQ_INT(479, (int)pt.y, "Mid-screen: y=0x1DF=479"); | ||
| 91 | |||
| 92 | TEST_SUMMARY(); | ||
| 93 | } | ||
diff --git a/tests/unit/test_wifi_setup b/tests/unit/test_wifi_setup new file mode 100755 index 0000000..aa0e0b4 --- /dev/null +++ b/tests/unit/test_wifi_setup | |||
| Binary files differ | |||
diff --git a/tests/unit/test_wifi_setup.c b/tests/unit/test_wifi_setup.c new file mode 100644 index 0000000..5f1b8f0 --- /dev/null +++ b/tests/unit/test_wifi_setup.c | |||
| @@ -0,0 +1,121 @@ | |||
| 1 | #include "test_framework.h" | ||
| 2 | #include "../../main/wifi_setup.h" | ||
| 3 | #include <string.h> | ||
| 4 | |||
| 5 | int main(void) | ||
| 6 | { | ||
| 7 | printf("=== test_wifi_setup ===\n"); | ||
| 8 | |||
| 9 | wifi_setup_t setup; | ||
| 10 | wifi_setup_init(&setup); | ||
| 11 | |||
| 12 | ASSERT_EQ_INT(SETUP_SCAN, (int)setup.state, "Init state = SCAN"); | ||
| 13 | ASSERT_EQ_INT(0, setup.ap_count, "Init ap_count = 0"); | ||
| 14 | ASSERT_EQ_INT(-1, setup.selected_ap, "Init selected_ap = -1"); | ||
| 15 | |||
| 16 | wifi_ap_info_t test_aps[3] = { | ||
| 17 | {"FastNet", -30, true}, | ||
| 18 | {"SlowNet", -70, true}, | ||
| 19 | {"OpenNet", -50, false}, | ||
| 20 | }; | ||
| 21 | wifi_setup_set_aps(&setup, test_aps, 3); | ||
| 22 | |||
| 23 | ASSERT_EQ_INT(SETUP_LIST, (int)setup.state, "After set_aps: state = LIST"); | ||
| 24 | ASSERT_EQ_INT(3, setup.ap_count, "After set_aps: ap_count = 3"); | ||
| 25 | ASSERT_EQ_INT(3, wifi_setup_visible_count(&setup), "Visible count = 3"); | ||
| 26 | |||
| 27 | { | ||
| 28 | const wifi_ap_info_t *ap = wifi_setup_get_visible(&setup, 0); | ||
| 29 | ASSERT(ap != NULL, "Visible AP 0 is not NULL"); | ||
| 30 | ASSERT_EQ_STR("FastNet", ap->ssid, "AP 0 = FastNet"); | ||
| 31 | ASSERT_EQ_INT(-30, ap->rssi, "AP 0 RSSI = -30"); | ||
| 32 | ASSERT(ap->secured, "AP 0 is secured"); | ||
| 33 | } | ||
| 34 | |||
| 35 | { | ||
| 36 | const wifi_ap_info_t *ap = wifi_setup_get_visible(&setup, 2); | ||
| 37 | ASSERT(ap != NULL, "Visible AP 2 is not NULL"); | ||
| 38 | ASSERT_EQ_STR("OpenNet", ap->ssid, "AP 2 = OpenNet"); | ||
| 39 | ASSERT(!ap->secured, "AP 2 is open"); | ||
| 40 | } | ||
| 41 | |||
| 42 | { | ||
| 43 | const wifi_ap_info_t *ap = wifi_setup_get_visible(&setup, 3); | ||
| 44 | ASSERT(ap == NULL, "Out of range returns NULL"); | ||
| 45 | } | ||
| 46 | |||
| 47 | setup_state_t s = wifi_setup_handle_select(&setup, 0); | ||
| 48 | ASSERT_EQ_INT(SETUP_PASSWORD, (int)s, "Select AP 0: state = PASSWORD"); | ||
| 49 | ASSERT_EQ_INT(0, setup.selected_ap, "Selected AP index = 0"); | ||
| 50 | ASSERT_EQ_STR("FastNet", setup.selected_ssid, "Selected SSID = FastNet"); | ||
| 51 | |||
| 52 | wifi_setup_handle_connect(&setup); | ||
| 53 | ASSERT_EQ_INT(SETUP_CONNECTING, (int)setup.state, "Connect: state = CONNECTING"); | ||
| 54 | |||
| 55 | s = wifi_setup_handle_connect_result(&setup, true, "192.168.1.42"); | ||
| 56 | ASSERT_EQ_INT(SETUP_SUCCESS, (int)s, "Connect success: state = SUCCESS"); | ||
| 57 | ASSERT_EQ_STR("192.168.1.42", setup.connect_ip, "Connect IP stored"); | ||
| 58 | |||
| 59 | wifi_setup_init(&setup); | ||
| 60 | wifi_setup_set_aps(&setup, test_aps, 3); | ||
| 61 | wifi_setup_handle_select(&setup, 1); | ||
| 62 | wifi_setup_handle_connect(&setup); | ||
| 63 | |||
| 64 | s = wifi_setup_handle_connect_result(&setup, false, NULL); | ||
| 65 | ASSERT_EQ_INT(SETUP_FAILED, (int)s, "Connect fail: state = FAILED"); | ||
| 66 | ASSERT(setup.connect_failed_auth, "Failed auth flag set"); | ||
| 67 | |||
| 68 | s = wifi_setup_handle_retry(&setup); | ||
| 69 | ASSERT_EQ_INT(SETUP_PASSWORD, (int)s, "Retry: state = PASSWORD"); | ||
| 70 | ASSERT(!setup.connect_failed_auth, "Retry clears auth flag"); | ||
| 71 | |||
| 72 | wifi_setup_init(&setup); | ||
| 73 | wifi_setup_set_aps(&setup, test_aps, 3); | ||
| 74 | wifi_setup_handle_select(&setup, 0); | ||
| 75 | wifi_setup_handle_connect(&setup); | ||
| 76 | wifi_setup_handle_connect_result(&setup, false, NULL); | ||
| 77 | |||
| 78 | s = wifi_setup_handle_change_network(&setup); | ||
| 79 | ASSERT_EQ_INT(SETUP_LIST, (int)s, "Change network: state = LIST"); | ||
| 80 | |||
| 81 | wifi_setup_init(&setup); | ||
| 82 | s = wifi_setup_handle_cancel(&setup); | ||
| 83 | ASSERT_EQ_INT(SETUP_CANCELLED, (int)s, "Cancel: state = CANCELLED"); | ||
| 84 | |||
| 85 | wifi_setup_init(&setup); | ||
| 86 | wifi_ap_info_t many_aps[12]; | ||
| 87 | for (int i = 0; i < 12; i++) { | ||
| 88 | snprintf(many_aps[i].ssid, sizeof(many_aps[i].ssid), "Net%d", i); | ||
| 89 | many_aps[i].rssi = -30 - i * 5; | ||
| 90 | many_aps[i].secured = true; | ||
| 91 | } | ||
| 92 | wifi_setup_set_aps(&setup, many_aps, 12); | ||
| 93 | ASSERT_EQ_INT(12, setup.ap_count, "12 APs stored"); | ||
| 94 | ASSERT_EQ_INT(8, wifi_setup_visible_count(&setup), "Only 8 visible"); | ||
| 95 | |||
| 96 | { | ||
| 97 | const wifi_ap_info_t *ap = wifi_setup_get_visible(&setup, 7); | ||
| 98 | ASSERT(ap != NULL, "8th visible AP exists"); | ||
| 99 | ASSERT_EQ_STR("Net7", ap->ssid, "8th visible = Net7"); | ||
| 100 | } | ||
| 101 | |||
| 102 | wifi_setup_init(&setup); | ||
| 103 | s = wifi_setup_handle_select(&setup, 0); | ||
| 104 | ASSERT_EQ_INT(SETUP_SCAN, (int)s, "Select in SCAN state = no change"); | ||
| 105 | |||
| 106 | s = wifi_setup_handle_select(&setup, -1); | ||
| 107 | ASSERT_EQ_INT(SETUP_SCAN, (int)s, "Select invalid idx = no change"); | ||
| 108 | |||
| 109 | wifi_setup_init(&setup); | ||
| 110 | wifi_setup_set_aps(&setup, test_aps, 3); | ||
| 111 | s = wifi_setup_handle_retry(&setup); | ||
| 112 | ASSERT_EQ_INT(SETUP_LIST, (int)s, "Retry in LIST state = no change"); | ||
| 113 | |||
| 114 | s = wifi_setup_handle_change_network(&setup); | ||
| 115 | ASSERT_EQ_INT(SETUP_LIST, (int)s, "Change network in LIST state = no change"); | ||
| 116 | |||
| 117 | ASSERT_EQ_INT(0, wifi_setup_visible_count(NULL), "NULL setup returns 0"); | ||
| 118 | ASSERT(NULL == wifi_setup_get_visible(NULL, 0), "NULL setup returns NULL AP"); | ||
| 119 | |||
| 120 | TEST_SUMMARY(); | ||
| 121 | } | ||