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
152 lines
4.8 KiB
C
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();
|
|
}
|