feat(box): routine manifest parser + host test

This commit is contained in:
L'électron rare
2026-06-21 10:41:50 +02:00
parent 5c8b846f05
commit 6cda5d243d
4 changed files with 219 additions and 0 deletions
+70
View File
@@ -0,0 +1,70 @@
// Host unit test for the routine manifest parser.
#define LISAEL_HOST_TEST
#include "audio/routine_manifest.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
static const char *SAMPLE =
"{\"routines\":["
"{\"id\":\"matin\",\"title\":\"Le matin\",\"icon\":\"matin.bin\",\"steps\":["
"{\"text\":\"Habille-toi\",\"icon\":\"matin_01.bin\",\"audio\":\"matin_01.mp3\"},"
"{\"text\":\"Petit-déjeuner\",\"icon\":\"matin_02.bin\",\"audio\":\"matin_02.mp3\"}]},"
"{\"id\":\"soir\",\"title\":\"Le soir\",\"icon\":\"soir.bin\",\"steps\":["
"{\"text\":\"Brosse tes dents\",\"icon\":\"soir_01.bin\",\"audio\":\"soir_01.mp3\"}]},"
"{\"id\":\"vide\",\"title\":\"Vide\",\"icon\":\"vide.bin\",\"steps\":[]}"
"]}";
int main(void) {
lisael_routine_t r[LISAEL_MAX_ROUTINES];
int n = lisael_routine_parse(SAMPLE, r, LISAEL_MAX_ROUTINES);
assert(n == 2); // empty routine skipped
assert(strcmp(r[0].id, "matin") == 0);
assert(strcmp(r[0].title, "Le matin") == 0);
assert(strcmp(r[0].icon, "matin.bin") == 0);
assert(r[0].n_steps == 2);
assert(strcmp(r[0].steps[0].text, "Habille-toi") == 0);
assert(strcmp(r[0].steps[1].icon, "matin_02.bin") == 0);
assert(strcmp(r[0].steps[1].audio, "matin_02.mp3") == 0);
assert(strcmp(r[1].id, "soir") == 0);
assert(r[1].n_steps == 1);
// malformed / wrong-shape inputs -> -1
assert(lisael_routine_parse("not json", r, LISAEL_MAX_ROUTINES) == -1);
assert(lisael_routine_parse("[]", r, LISAEL_MAX_ROUTINES) == -1); // array root, not object
assert(lisael_routine_parse("{\"foo\":1}", r, LISAEL_MAX_ROUTINES) == -1); // no "routines"
// empty routines array -> 0 (valid, just no content)
lisael_routine_t r2[LISAEL_MAX_ROUTINES];
assert(lisael_routine_parse("{\"routines\":[]}", r2, LISAEL_MAX_ROUTINES) == 0);
// a step missing "text" is skipped; icon/audio optional (stay "")
int m = lisael_routine_parse(
"{\"routines\":[{\"id\":\"x\",\"title\":\"X\",\"steps\":["
"{\"icon\":\"a.bin\"}," // no text -> skipped
"{\"text\":\"Range\"}]}]}", // no icon/audio -> ""
r2, LISAEL_MAX_ROUTINES);
assert(m == 1 && r2[0].n_steps == 1);
assert(strcmp(r2[0].steps[0].text, "Range") == 0);
assert(r2[0].steps[0].icon[0] == '\0');
assert(r2[0].steps[0].audio[0] == '\0');
// guard clauses
assert(lisael_routine_parse(NULL, r2, LISAEL_MAX_ROUTINES) == -1);
assert(lisael_routine_parse("{\"routines\":[]}", r2, 0) == -1);
// caps: never exceed LISAEL_MAX_STEPS even if JSON has more
char big[2048];
int o = snprintf(big, sizeof(big), "{\"routines\":[{\"id\":\"b\",\"title\":\"B\",\"steps\":[");
for (int i = 0; i < 20; i++) { // 20 > LISAEL_MAX_STEPS (12)
o += snprintf(big + o, sizeof(big) - o,
"%s{\"text\":\"s%d\"}", i ? "," : "", i);
}
snprintf(big + o, sizeof(big) - o, "]}]}");
lisael_routine_t r3[LISAEL_MAX_ROUTINES];
int k = lisael_routine_parse(big, r3, LISAEL_MAX_ROUTINES);
assert(k == 1 && r3[0].n_steps == LISAEL_MAX_STEPS);
printf("OK: %d routines\n", n);
return 0;
}