#define _POSIX_C_SOURCE 200809L #include "hid_device.h" #include "rgb_state.h" #include #include #include #include #include #include #include "../src/profile.inc" int rgb_cli_main(int, char **); int __real_rgb_state_save(int, const struct rgb_state *); static char runtime[] = "/tmp/labfy-rgb-cli-XXXXXX"; static FILE *node; static unsigned operations, writes, restores, host_sets, range_sets; static unsigned fail_operation, fail_recovery; static int open_error, cache_error, save_error, discovery_error, descriptor_error; static unsigned logical_count = 1, programmable = 1; static uint8_t last_range[10]; int __wrap_hid_discover(const char *root, char *name, size_t capacity) { assert(!strcmp(root, "/sys/class/hidraw") && capacity >= 10); if (discovery_error) { errno = discovery_error; return -1; } memcpy(name, "hidraw77", 9); return 0; } int __wrap_hid_descriptor(const char *root, const char *name, uint8_t *r, size_t cap, size_t *n) { (void)root; assert(!strcmp(name, "hidraw77") && cap >= sizeof profile); memcpy(r, profile, sizeof profile); *n = sizeof profile; if (descriptor_error) r[0] ^= 1; return 0; } int __wrap_hid_open_control(const char *name) { assert(!strcmp(name, "hidraw77")); if (open_error) { errno = open_error; return -1; } return dup(fileno(node)); } int __wrap_hid_open_verified(const char *name) { return __wrap_hid_open_control(name); } static int operation(void) { ++operations; if (operations == fail_operation || operations == fail_recovery) { errno = EIO; return -1; } return 0; } int __wrap_hid_get(void *context, uint8_t *r, size_t n) { assert(context && *(int *)context >= 0); if (operation() < 0) return -1; if (r[0] == 0x41) { assert(n == 23); memset(r, 0, n); r[0] = 0x41; r[1] = (uint8_t)logical_count; r[19] = 0xb8; r[20] = 0x0b; } else { assert(n == 29 && r[0] == 0x43); memset(r, 0, n); r[0] = 0x43; r[23] = 255; r[24] = 255; r[25] = 255; r[26] = 100; r[27] = (uint8_t)programmable; } return (int)n; } int __wrap_hid_set(void *context, const uint8_t *r, size_t n) { assert(context && *(int *)context >= 0); ++writes; if (r[0] == 0x46 && r[1] == 1) ++restores; else if (r[0] == 0x46) ++host_sets; else if (r[0] == 0x45) { assert(n == 10); memcpy(last_range, r, n); ++range_sets; } else assert(n == 3 && r[0] == 0x42 && !r[1] && !r[2]); if (operation() < 0) return -1; return (int)n; } int __wrap_hid_wait(void *context, uint32_t us) { assert(context && us == 3000); return operation(); } int __wrap_rgb_state_open(void) { if (cache_error) { errno = cache_error; return -1; } return rgb_state_open_at(runtime); } int __wrap_rgb_state_save(int dir, const struct rgb_state *s) { if (save_error) { errno = save_error; return -1; } return __real_rgb_state_save(dir, s); } static void reset(void) { operations = writes = restores = host_sets = range_sets = 0; fail_operation = fail_recovery = 0; open_error = cache_error = save_error = discovery_error = descriptor_error = 0; logical_count = programmable = 1; } static int invoke(const char *command, const char *value) { char *args[] = {"labfy-rgb", (char *)command, (char *)value, NULL}; return rgb_cli_main(value ? 3 : 2, args); } int main(void) { assert(mkdtemp(runtime)); node = tmpfile(); assert(node); reset(); assert(invoke("brightness", "60") == 7 && !writes); reset(); assert(invoke("status", NULL) == 0 && !writes && operations == 1); reset(); assert(invoke("status", "--json") == 0 && !writes && operations == 1); reset(); assert(invoke("probe", NULL) == 0 && writes == 1 && !host_sets && !restores); reset(); assert(invoke("set", "#B4BEFE") == 0 && writes == 3 && !restores); assert(last_range[6] == 180 && last_range[7] == 190 && last_range[8] == 254 && last_range[9] == 100); reset(); assert(invoke("brightness", "60") == 0 && !restores && last_range[6] == 180 && last_range[9] == 60); reset(); assert(invoke("off", NULL) == 0 && !restores); for (size_t i = 6; i < 10; ++i) assert(last_range[i] == 0); reset(); assert(invoke("brightness", "60") == 0 && last_range[6] == 180 && last_range[9] == 60); reset(); assert(invoke("auto", NULL) == 0 && writes == 1 && restores == 1 && operations == 1); reset(); assert(invoke("brightness", "60") == 7 && !writes); reset(); fail_operation = 6; assert(invoke("set", "#FF0000") == 6 && restores == 1); reset(); assert(invoke("brightness", "60") == 7 && !writes); reset(); fail_operation = 6; fail_recovery = 7; assert(invoke("set", "#FF0000") == 8 && restores == 1); reset(); save_error = EIO; assert(invoke("set", "#FF0000") == 9 && restores == 1); reset(); cache_error = EACCES; assert(invoke("auto", NULL) == 9 && restores == 1 && operations == 1); reset(); cache_error = EACCES; assert(invoke("set", "#FF0000") == 9 && !writes); reset(); open_error = EACCES; assert(invoke("auto", NULL) == 4 && !writes); reset(); open_error = EWOULDBLOCK; assert(invoke("auto", NULL) == 10 && !writes); reset(); discovery_error = ENODEV; assert(invoke("set", "#FF0000") == 3 && !writes); reset(); descriptor_error = 1; assert(invoke("auto", NULL) == 5 && !writes); reset(); logical_count = 2; assert(invoke("set", "#FF0000") == 5 && !writes); reset(); programmable = 0; assert(invoke("set", "#FF0000") == 5 && writes == 1 && !host_sets); reset(); fail_operation = 1; assert(invoke("auto", NULL) == 6 && restores == 1 && writes == 1); int dir = rgb_state_open_at(runtime); assert(dir >= 0); assert(rgb_state_invalidate(dir) == 0 && close(dir) == 0); assert(fclose(node) == 0 && rmdir(runtime) == 0); puts("Real CLI command and exit-code integration passed; all HID endpoints replaced with memory doubles."); return 0; }