labfy-rgb/tests/test_cli_integration.c
2026-11-09 19:01:00 +01:00

130 lines
5.8 KiB
C

#define _POSIX_C_SOURCE 200809L
#include "hid_device.h"
#include "rgb_state.h"
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#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;
}