zebra-report/test/integration.c
Russell Ballestrini b77da42bbe phase 1: unfirehose reconstruction from session JSONL ingest
Source: ~/.unfirehose/unfirehose.db (project_id=81, 4 sessions covering
2026-03-29 through 2026-04-05). Reconstructed via chronological replay
of Write/Edit tool_input on file_paths under /home/fox/zebra-report/.

stats:
  files reconstructed:    20
  writes baselined:       all (zero missing)
  edits applied:          68
  edits unapplied:        8 (1 SKIP pre-baseline, 6 FAIL old_string drift, 1 AMBIGUOUS)

unapplied edits represent small drift in 6 files; baseline content for
each is intact. quality verification deferred to phase 2.

recovered tree:
  CLAUDE.md, Makefile
  src/{tx,rx,pulse,carrier,chat,bt}.c
  include/{modem,zebra}.h
  test/{functional,integration,unit}.c, test/test.h
  web/{index,kernel}.html, web/blog/style.css
  blog/build.py, blog/posts/{001-volume-modem,002-sse-chatroom}.md

report: /tmp/zebra_recover_report.txt
script: /tmp/zebra_recover.py
2026-05-27 13:51:14 -04:00

152 lines
4.8 KiB
C

/*
* integration.c — PulseAudio connection and volume I/O tests
*
* Requires a live PulseAudio / PipeWire session.
* Set ZEBRA_TEST_SINK to a sink-input index (from ./tx -l) before running.
* If unset, PA connection tests still run; volume tests are skipped.
*
* Compile: gcc -Wall -O2 -Iinclude $(pkg-config --cflags libpulse) \
* -o test/integration test/integration.c src/pulse.c \
* $(pkg-config --libs libpulse) -lrt -lpthread
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "zebra.h"
#include "modem.h"
#include "test.h"
static zebra_pulse_t z;
static uint32_t test_sink = (uint32_t)-1;
static int have_sink = 0;
/* ------------------------------------------------------------------ *
* PA connection *
* ------------------------------------------------------------------ */
static void t_connect(void) {
int r = zebra_pulse_connect(&z, "zebra-test");
T_EQ(r, 0);
}
static void t_list_sinks(void) {
zebra_sink_t buf[64];
int n = zebra_list_sinks(&z, buf, 64);
T_GE(n, 0);
if (n > 0) {
/* every entry must have a valid channel count */
for (int i = 0; i < n; i++)
T_GE((int)buf[i].channels, 1);
}
}
static void t_find_sink_miss(void) {
zebra_sink_t s;
int r = zebra_find_sink(&z, "____no_such_sink_xyzzy____", &s);
T_EQ(r, -1);
}
/* ------------------------------------------------------------------ *
* volume I/O (needs ZEBRA_TEST_SINK) *
* ------------------------------------------------------------------ */
static void t_get_volume(void) {
if (!have_sink) { fprintf(stderr, " (skip — ZEBRA_TEST_SINK not set)\n"); return; }
uint8_t vol = 255;
int r = zebra_get_volume(&z, test_sink, &vol);
T_EQ(r, 0);
T_LE((int)vol, 100);
}
static void t_set_get_roundtrip(void) {
if (!have_sink) { fprintf(stderr, " (skip — ZEBRA_TEST_SINK not set)\n"); return; }
uint8_t original = 255;
zebra_get_volume(&z, test_sink, &original);
/* set to MARK, read back */
zebra_set_volume(&z, test_sink, ZEBRA_VOL_MARK);
uint8_t got = 0;
int r = zebra_get_volume(&z, test_sink, &got);
T_EQ(r, 0);
/* PA quantises to PA_VOLUME_NORM steps — allow ±2% tolerance */
T_GE((int)got, (int)ZEBRA_VOL_MARK - 2);
T_LE((int)got, (int)ZEBRA_VOL_MARK + 2);
/* restore */
zebra_set_volume(&z, test_sink, original);
}
static void t_set_get_space(void) {
if (!have_sink) { fprintf(stderr, " (skip — ZEBRA_TEST_SINK not set)\n"); return; }
uint8_t original = 255;
zebra_get_volume(&z, test_sink, &original);
zebra_set_volume(&z, test_sink, ZEBRA_VOL_SPACE);
uint8_t got = 0;
zebra_get_volume(&z, test_sink, &got);
T_GE((int)got, (int)ZEBRA_VOL_SPACE - 2);
T_LE((int)got, (int)ZEBRA_VOL_SPACE + 2);
zebra_set_volume(&z, test_sink, original);
}
static void t_vol_clamp_over_100(void) {
if (!have_sink) { fprintf(stderr, " (skip — ZEBRA_TEST_SINK not set)\n"); return; }
uint8_t original = 255;
zebra_get_volume(&z, test_sink, &original);
/* pct > 100 should be clamped, not crash */
int r = zebra_set_volume(&z, test_sink, 200);
T_EQ(r, 0);
uint8_t got = 0;
zebra_get_volume(&z, test_sink, &got);
T_LE((int)got, 100);
zebra_set_volume(&z, test_sink, original);
}
/* ------------------------------------------------------------------ *
* benchmark sanity *
* ------------------------------------------------------------------ */
static void t_benchmark_range(void) {
if (!have_sink) { fprintf(stderr, " (skip — ZEBRA_TEST_SINK not set)\n"); return; }
int baud = zebra_benchmark_baud(&z, test_sink);
T_GE(baud, ZEBRA_BAUD_MIN);
T_LE(baud, ZEBRA_BAUD_MAX);
fprintf(stderr, " measured: %d baud\n", baud);
}
/* ------------------------------------------------------------------ *
* main *
* ------------------------------------------------------------------ */
int main(void) {
const char *sink_env = getenv("ZEBRA_TEST_SINK");
if (sink_env) {
test_sink = (uint32_t)atoi(sink_env);
have_sink = 1;
fprintf(stderr, "ZEBRA_TEST_SINK=%u\n", test_sink);
} else {
fprintf(stderr, "ZEBRA_TEST_SINK not set — volume tests will skip\n");
fprintf(stderr, " run: ./tx -l to list sinks, then set the env var\n");
}
T_SECTION("PA connection");
T_RUN(t_connect);
T_RUN(t_list_sinks);
T_RUN(t_find_sink_miss);
T_SECTION("volume I/O");
T_RUN(t_get_volume);
T_RUN(t_set_get_roundtrip);
T_RUN(t_set_get_space);
T_RUN(t_vol_clamp_over_100);
T_SECTION("benchmark");
T_RUN(t_benchmark_range);
zebra_pulse_disconnect(&z);
T_SUMMARY();
}