/* JACK ABI fake: deterministic fault injection, linked without libjack. * Processing is driven synchronously here; realtime concurrency is tested * separately by test_realtime. No fake header replaces installed declarations. */ #include "xtc/xtc_backend.h" #include #include #include #include #include #define CHECK(c) do { if (!(c)) { fprintf(stderr,"FAIL %s:%d: %s\n",__FILE__,__LINE__,#c); return 1; } } while (0) static JackProcessCallback process_cb; static JackSampleRateCallback rate_cb; static JackBufferSizeCallback size_cb; static JackXRunCallback xrun_cb; static JackShutdownCallback shutdown_cb; static void *context; static float input[128], output[128]; static int client_token, input_token, output_token; static int open_fail, register_fail, callback_fail, activate_fail, deactivate_fail, close_fail; static int active, closed, port_calls, null_buffer; static unsigned int server_frames = 32U; jack_client_t *jack_client_open(const char *name, jack_options_t options, jack_status_t *status, ...) { (void)name; (void)status; if (options != JackNoStartServer || open_fail) return NULL; port_calls = 0; process_cb = NULL; rate_cb = NULL; size_cb = NULL; xrun_cb = NULL; shutdown_cb = NULL; return (jack_client_t *)&client_token; } int jack_client_close(jack_client_t *client) { (void)client; if (close_fail) return -1; active = 0; ++closed; return 0; } int jack_client_name_size(void) { return 64; } jack_nframes_t jack_get_sample_rate(jack_client_t *client) { (void)client; return 48000U; } jack_nframes_t jack_get_buffer_size(jack_client_t *client) { (void)client; return server_frames; } jack_port_t *jack_port_register(jack_client_t *client, const char *name, const char *type, unsigned long flags, unsigned long buffer_size) { (void)client; (void)name; (void)type; (void)buffer_size; ++port_calls; if (register_fail == port_calls) return NULL; return (jack_port_t *)(flags == JackPortIsInput ? &input_token : &output_token); } int jack_set_process_callback(jack_client_t *client, JackProcessCallback callback, void *arg) { (void)client; process_cb = callback; context = arg; return callback_fail ? -1 : 0; } int jack_set_sample_rate_callback(jack_client_t *client, JackSampleRateCallback callback, void *arg) { (void)client; (void)arg; rate_cb = callback; return 0; } int jack_set_buffer_size_callback(jack_client_t *client, JackBufferSizeCallback callback, void *arg) { (void)client; (void)arg; size_cb = callback; return 0; } int jack_set_xrun_callback(jack_client_t *client, JackXRunCallback callback, void *arg) { (void)client; (void)arg; xrun_cb = callback; return 0; } void jack_on_shutdown(jack_client_t *client, JackShutdownCallback callback, void *arg) { (void)client; (void)arg; shutdown_cb = callback; } int jack_activate(jack_client_t *client) { (void)client; if (activate_fail) return -1; active = 1; return 0; } int jack_deactivate(jack_client_t *client) { (void)client; if (deactivate_fail) return -1; active = 0; return 0; } void *jack_port_get_buffer(jack_port_t *port, jack_nframes_t frames) { (void)frames; if (null_buffer) return NULL; return port == (jack_port_t *)&input_token ? input : output; } const char *jack_port_name(const jack_port_t *port) { return port == (jack_port_t *)&input_token ? "test:input" : "test:output"; } const char **jack_get_ports(jack_client_t *client, const char *pattern, const char *type, unsigned long flags) { const char **list = malloc(2U * sizeof(*list)); (void)client; (void)pattern; (void)type; (void)flags; if (list != NULL) { list[0] = "fake:audio"; list[1] = NULL; } return list; } void jack_free(void *pointer) { free(pointer); } int jack_connect(jack_client_t *client, const char *source, const char *dest) { (void)client; return strstr(source, "missing") || strstr(dest, "missing") ? -1 : EEXIST; } int jack_disconnect(jack_client_t *client, const char *source, const char *dest) { (void)client; (void)source; (void)dest; return 0; } static int visit(const char *name, void *arg) { if (strcmp(name, "fake:audio") == 0) ++*(int *)arg; return 0; } int main(void) { xtc_backend_config config = {XTC_BACKEND_JACK, "test", 64U, 1U}; xtc_backend *backend = NULL; xtc_backend_status status; xtc_graph *graph = NULL; size_t node, i; const char *name; int visits = 0, iteration; CHECK(xtc_backend_open(NULL, &backend) == XTC_ERR_INVALID_ARGUMENT && backend == NULL); config.channel_count = 2U; CHECK(xtc_backend_open(&config, &backend) == XTC_ERR_INVALID_ARGUMENT); config.channel_count = 1U; config.kind = (xtc_backend_kind)999; CHECK(xtc_backend_open(&config, &backend) == XTC_ERR_UNSUPPORTED); config.kind = XTC_BACKEND_JACK; open_fail = 1; CHECK(xtc_backend_open(&config, &backend) == XTC_ERR_IO && backend == NULL); open_fail = 0; server_frames = 128U; CHECK(xtc_backend_open(&config, &backend) == XTC_ERR_CAPACITY && backend == NULL); server_frames = 32U; register_fail = 2; CHECK(xtc_backend_open(&config, &backend) == XTC_ERR_IO && backend == NULL); register_fail = 0; callback_fail = 1; CHECK(xtc_backend_open(&config, &backend) == XTC_ERR_IO && backend == NULL); close_fail = 1; CHECK(xtc_backend_open(&config, &backend) == XTC_ERR_IO && backend != NULL); CHECK(xtc_backend_start(backend) == XTC_ERR_INVALID_STATE); close_fail = 0; CHECK(xtc_backend_close(backend) == XTC_OK); callback_fail = 0; CHECK(xtc_graph_create(1U, &graph) == XTC_OK); CHECK(xtc_graph_add_gain(graph, 0.5f, &node) == XTC_OK); CHECK(xtc_backend_open(&config, &backend) == XTC_OK); CHECK(xtc_backend_submit(backend, graph) == XTC_OK); CHECK(xtc_backend_submit(backend, graph) == XTC_ERR_CAPACITY); CHECK(xtc_backend_port_name(backend, XTC_PORT_OUTPUT, &name) == XTC_OK && strcmp(name,"test:output") == 0); CHECK(xtc_backend_visit_ports(backend, XTC_PORT_INPUT, visit, &visits) == XTC_OK && visits == 1); CHECK(xtc_backend_connect(backend, XTC_PORT_OUTPUT, "fake:in") == XTC_ERR_INVALID_STATE); activate_fail = 1; CHECK(xtc_backend_start(backend) == XTC_ERR_IO); activate_fail = 0; CHECK(xtc_backend_start(backend) == XTC_OK); CHECK(xtc_backend_start(backend) == XTC_ERR_INVALID_STATE); CHECK(xtc_backend_connect(backend, XTC_PORT_OUTPUT, "fake:in") == XTC_OK); CHECK(xtc_backend_connect(backend, XTC_PORT_INPUT, "missing:out") == XTC_ERR_IO); CHECK(xtc_backend_disconnect(backend, XTC_PORT_OUTPUT, "fake:in") == XTC_OK); for (i = 0U; i < 128U; ++i) input[i] = 1.0f; CHECK(process_cb(32U, context) == 0 && output[0] == 0.5f); CHECK(size_cb(128U, context) == 0); CHECK(process_cb(128U, context) == 0 && output[127] == 0.0f); CHECK(xtc_backend_get_status(backend, &status) == XTC_OK && status.process_result == XTC_ERR_CAPACITY); CHECK(size_cb(16U, context) == 0); CHECK(process_cb(16U, context) == 0 && output[15] == 0.5f); CHECK(xrun_cb(context) == 0); CHECK(xtc_backend_get_status(backend, &status) == XTC_OK && status.xruns == 1U && status.buffer_frames == 16U); null_buffer = 1; CHECK(process_cb(16U, context) == 0); null_buffer = 0; CHECK(xtc_backend_get_status(backend, &status) == XTC_OK && status.process_result == XTC_ERR_IO); CHECK(rate_cb(44100U, context) == 0); CHECK(process_cb(16U, context) == 0 && output[0] == 0.0f); CHECK(xtc_backend_get_status(backend, &status) == XTC_OK && status.sample_rate_changed && status.sample_rate == 44100U); CHECK(xtc_backend_submit(backend, graph) == XTC_ERR_INVALID_STATE); deactivate_fail = 1; CHECK(xtc_backend_stop(backend) == XTC_ERR_IO); deactivate_fail = 0; CHECK(xtc_backend_stop(backend) == XTC_OK && !active); CHECK(xtc_backend_start(backend) == XTC_ERR_INVALID_STATE); CHECK(xtc_backend_close(backend) == XTC_OK); for (iteration = 0; iteration < 100; ++iteration) { CHECK(xtc_backend_open(&config, &backend) == XTC_OK); CHECK(xtc_backend_submit(backend, graph) == XTC_OK); CHECK(xtc_backend_start(backend) == XTC_OK); CHECK(process_cb(32U, context) == 0); if (iteration % 2 == 0) { shutdown_cb(context); CHECK(xtc_backend_get_status(backend, &status) == XTC_OK && status.server_lost && !status.running); CHECK(xtc_backend_start(backend) == XTC_ERR_INVALID_STATE); } else { CHECK(xtc_backend_stop(backend) == XTC_OK); CHECK(xtc_backend_start(backend) == XTC_OK); } CHECK(xtc_backend_submit(backend, graph) == (iteration % 2 == 0 ? XTC_ERR_INVALID_STATE : XTC_OK)); close_fail = 1; CHECK(xtc_backend_close(backend) == XTC_ERR_IO); close_fail = 0; CHECK(xtc_backend_close(backend) == XTC_OK); } CHECK(closed >= 104); CHECK(xtc_backend_close(NULL) == XTC_OK); xtc_graph_destroy(graph); puts("JACK backend fault-injection tests passed."); return 0; }