From 740ebaa969f9226dfb0a16d5d0431eff59491859 Mon Sep 17 00:00:00 2001 From: Loic Coenen Date: Sat, 9 May 2026 10:09:51 +0000 Subject: [PATCH] test: add integration tests for control-key modifier and channel removal Co-authored-by: aider (deepseek/deepseek-reasoner) --- tests/integration.c | 193 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 191 insertions(+), 2 deletions(-) diff --git a/tests/integration.c b/tests/integration.c index 6f88fe6..11d0d47 100644 --- a/tests/integration.c +++ b/tests/integration.c @@ -415,6 +415,177 @@ static int test_multiple_channels(void) { return 0; } +/* test control‑key modifier (note 64 + note 62) */ +static int test_control_key_modifier(void) { + printf("Test: control‑key modifier triggers state transition via note 62\n"); + pid_t pid = start_looper(); + if (pid < 0) return 1; + jack_client_t *client; + jack_status_t status; + client = jack_client_open("test_ctrl_key", JackNoStartServer, &status); + if (!client) { + kill(pid, SIGTERM); waitpid(pid, NULL, 0); + fprintf(stderr, " SKIP: no JACK\n"); + return 1; + } + /* connect same as in test_looper_looping but no beep generation */ + jack_port_t *audio_out = jack_port_register(client, "out", + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsOutput, 0); + jack_port_t *audio_in = jack_port_register(client, "in", + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsInput, 0); + if (!audio_out || !audio_in) { + jack_client_close(client); + kill(pid, SIGTERM); waitpid(pid, NULL, 0); + return 1; + } + usleep(200000); + char my_out[64], my_in[64]; + snprintf(my_out, sizeof(my_out), "test_ctrl_key:out"); + snprintf(my_in, sizeof(my_in), "test_ctrl_key:in"); + if (jack_connect(client, my_out, "looper:input") || + jack_connect(client, "looper:output", my_in)) { + jack_client_close(client); + kill(pid, SIGTERM); waitpid(pid, NULL, 0); + return 1; + } + /* First send note 64 (control key) */ + if (send_jack_note_on("looper:control", 64, 127) != 0) { + jack_client_close(client); + kill(pid, SIGTERM); waitpid(pid, NULL, 0); + fprintf(stderr, " FAIL: send note 64 failed\n"); + return 1; + } + usleep(200000); + /* Now send note 62 (toggle channel 0) */ + if (send_jack_note_on("looper:control", 62, 127) != 0) { + jack_client_close(client); + kill(pid, SIGTERM); waitpid(pid, NULL, 0); + fprintf(stderr, " FAIL: send note 62 failed\n"); + return 1; + } + /* Wait for looper to enter RECORD and detect audio */ + int sr = jack_get_sample_rate(client); + continuous_sine = 0; + beep_remaining = (int)(0.1f * sr); /* 0.1 second beep */ + bursts = 0; + prev_above = 0; + passthrough_output_port = audio_out; + passthrough_input_port = audio_in; + passthrough_phase = 0.0f; + passthrough_freq = 440.0f; + passthrough_sample_rate = sr; + passthrough_total_samples = 0; + passthrough_sum_sq = 0.0; + passthrough_done = 0; + jack_set_process_callback(client, passthrough_process, NULL); + if (jack_activate(client)) { + jack_client_close(client); + kill(pid, SIGTERM); waitpid(pid, NULL, 0); + return 1; + } + usleep(200000); /* allow beep */ + /* send note 62 again under control key to move RECORD->LOOPING */ + if (send_jack_note_on("looper:control", 64, 127) != 0) { + jack_client_close(client); + kill(pid, SIGTERM); waitpid(pid, NULL, 0); + fprintf(stderr, " FAIL: control key re‑send\n"); + return 1; + } + usleep(200000); + if (send_jack_note_on("looper:control", 62, 127) != 0) { + jack_client_close(client); + kill(pid, SIGTERM); waitpid(pid, NULL, 0); + fprintf(stderr, " FAIL: send note 62 for loop\n"); + return 1; + } + usleep(2000000); /* wait for a few loops */ + jack_deactivate(client); + jack_client_close(client); + kill(pid, SIGTERM); + waitpid(pid, NULL, 0); + int got_bursts = bursts; + printf(" detected bursts: %d\n", got_bursts); + if (got_bursts < 3) { + fprintf(stderr, " FAIL: expected ≥3 bursts, got %d\n", got_bursts); + return 1; + } + printf(" PASS (control‑key modifier works)\n"); + return 0; +} + +/* test remove channel */ +static int test_remove_channel(void) { + printf("Test: dynamic channel removal via MIDI command\n"); + pid_t pid = start_looper(); + if (pid < 0) return 1; + jack_client_t *client; + jack_status_t status; + client = jack_client_open("test_remove", JackNoStartServer, &status); + if (!client) { + kill(pid, SIGTERM); waitpid(pid, NULL, 0); + fprintf(stderr, " SKIP: no JACK\n"); + return 1; + } + /* add channel */ + if (send_jack_note_on("looper:control", 60, 127) != 0) { + jack_client_close(client); + kill(pid, SIGTERM); waitpid(pid, NULL, 0); + fprintf(stderr, " FAIL: send note 60 failed\n"); + return 1; + } + usleep(1500000); + /* verify channel1_input exists */ + const char **ports = jack_get_ports(client, NULL, JACK_DEFAULT_AUDIO_TYPE, 0); + int found = 0; + if (ports) { + for (int i = 0; ports[i]; i++) { + if (strstr(ports[i], "looper:channel1_input")) { + found = 1; + break; + } + } + jack_free(ports); + } + if (!found) { + jack_client_close(client); + kill(pid, SIGTERM); waitpid(pid, NULL, 0); + fprintf(stderr, " FAIL: channel1_input not created\n"); + return 1; + } + printf(" channel1_input created\n"); + /* remove channel */ + if (send_jack_note_on("looper:control", 61, 127) != 0) { + jack_client_close(client); + kill(pid, SIGTERM); waitpid(pid, NULL, 0); + fprintf(stderr, " FAIL: send note 61 failed\n"); + return 1; + } + usleep(1500000); + /* verify channel1_input has disappeared */ + ports = jack_get_ports(client, NULL, JACK_DEFAULT_AUDIO_TYPE, 0); + int still_found = 0; + if (ports) { + for (int i = 0; ports[i]; i++) { + if (strstr(ports[i], "looper:channel1_input")) { + still_found = 1; + break; + } + } + jack_free(ports); + } + jack_client_close(client); + kill(pid, SIGTERM); + waitpid(pid, NULL, 0); + if (still_found) { + fprintf(stderr, " FAIL: channel1_input not removed after remove command\n"); + return 1; + } + printf(" PASS (channel removed)\n"); + return 0; +} + int main(void) { /* 1. binary must exist */ @@ -428,18 +599,36 @@ int main(void) { /* 3. Audio pass‑through test – must work for basic connectivity */ test_audio_pass_through(); + int failures = 0; + /* 4. Test that looping feature is now implemented */ if (test_looper_looping() != 0) { fprintf(stderr, " FAILED\n"); - return 1; + failures++; } /* 5. Test multiple dynamic channels */ if (test_multiple_channels() != 0) { fprintf(stderr, " FAILED\n"); - return 1; + failures++; } + /* 6. Test control‑key modifier */ + if (test_control_key_modifier() != 0) { + fprintf(stderr, " FAILED\n"); + failures++; + } + + /* 7. Test channel removal */ + if (test_remove_channel() != 0) { + fprintf(stderr, " FAILED\n"); + failures++; + } + + if (failures > 0) { + fprintf(stderr, "%d test(s) FAILED\n", failures); + return 1; + } printf("All tests completed successfully.\n"); return 0; }