diff --git a/client/makefile b/client/makefile index 1f38c27..411cfac 100644 --- a/client/makefile +++ b/client/makefile @@ -13,6 +13,7 @@ TEST_PLUGINS_BIN = test_plugins TEST_CLIENT_BIN = test_client TEST_CARLA_BIN = test_carla_host TEST_CLIENT_CMD_BIN = test_client_cmd +TEST_INTEGRATION_BIN = test_integration all: looper-client test_status_parse @@ -29,6 +30,11 @@ $(PLUGINS_OBJ): src/plugins.c src/plugins.h $(CARLA_OBJ): src/carla_host.c src/carla_host.h $(CC) -Wall -Wextra -std=c11 -Isrc $(CARLA_INC) -c -o $@ $< +CARLA_TEST_OBJ = src/carla_host_test.o + +$(CARLA_TEST_OBJ): src/carla_host.c src/carla_host.h + $(CC) -Wall -Wextra -std=c11 -Isrc $(CARLA_INC) -DTESTING -c -o $@ $< + $(CLIENT_CMD_OBJ): src/client_cmd.c src/client_cmd.h $(CC) $(CFLAGS) $(CARLA_INC) -c -o $@ $< @@ -71,14 +77,19 @@ $(TEST_CARLA_OBJ): tests/test_carla_host.c src/carla_host.h $(TEST_CARLA_BIN): $(TEST_CARLA_OBJ) $(CARLA_OBJ) $(CC) $(CFLAGS) -o $@ $^ $(CARLA_LIB) -ljack -test: looper-client test_status_parse $(TEST_PLUGINS_BIN) $(TEST_CLIENT_BIN) $(TEST_CARLA_BIN) $(TEST_CLIENT_CMD_BIN) +# --- Integration test (requires TESTING symbol) --- +$(TEST_INTEGRATION_BIN): tests/test_integration.c $(CARLA_TEST_OBJ) + $(CC) $(CFLAGS) $(CARLA_INC) -DTESTING -o $@ $^ $(CARLA_LIB) -ljack + +test: looper-client test_status_parse $(TEST_PLUGINS_BIN) $(TEST_CLIENT_BIN) $(TEST_CARLA_BIN) $(TEST_CLIENT_CMD_BIN) $(TEST_INTEGRATION_BIN) ./test_status_parse ./$(TEST_PLUGINS_BIN) ./$(TEST_CLIENT_BIN) ./$(TEST_CARLA_BIN) ./$(TEST_CLIENT_CMD_BIN) + ./$(TEST_INTEGRATION_BIN) .PHONY: all test clean clean: - rm -f looper-client test_status_parse $(TEST_PLUGINS_BIN) $(TEST_CLIENT_BIN) $(TEST_CARLA_BIN) $(TEST_CLIENT_CMD_BIN) *.o tests/*.o src/*.o + rm -f looper-client test_status_parse $(TEST_PLUGINS_BIN) $(TEST_CLIENT_BIN) $(TEST_CARLA_BIN) $(TEST_CLIENT_CMD_BIN) $(TEST_INTEGRATION_BIN) *.o tests/*.o src/*.o diff --git a/client/src/carla_host.c b/client/src/carla_host.c index 161e2b4..ee682e7 100644 --- a/client/src/carla_host.c +++ b/client/src/carla_host.c @@ -173,6 +173,27 @@ int carla_disconnect_plugin(int id) { return any ? 0 : -1; // return -1 if no connections were found (harmless) } +#ifdef TESTING +int carla_test_connection_count(void) { + return conn_count; +} + +int carla_test_add_connection(int plugin_id, const char *plugin_port, const char *looper_port) { + if (!plugin_port || !looper_port) return -1; + if (conn_count >= MAX_CONNECTIONS) return -1; + + strncpy(connections[conn_count].plugin_port, plugin_port, + sizeof(connections[conn_count].plugin_port) - 1); + connections[conn_count].plugin_port[sizeof(connections[conn_count].plugin_port) - 1] = '\0'; + strncpy(connections[conn_count].looper_port, looper_port, + sizeof(connections[conn_count].looper_port) - 1); + connections[conn_count].looper_port[sizeof(connections[conn_count].looper_port) - 1] = '\0'; + connections[conn_count].plugin_id = plugin_id; + conn_count++; + return 0; +} +#endif + CarlaHostHandle carla_get_handle(void) { return handle; } diff --git a/client/src/carla_host.h b/client/src/carla_host.h index 546432d..b7f123f 100644 --- a/client/src/carla_host.h +++ b/client/src/carla_host.h @@ -19,4 +19,9 @@ void carla_set_bypass(int id, bool bypass); int carla_disconnect_plugin(int id); CarlaHostHandle carla_get_handle(void); +#ifdef TESTING +int carla_test_connection_count(void); +int carla_test_add_connection(int plugin_id, const char *plugin_port, const char *looper_port); +#endif + #endif diff --git a/client/src/tui.c b/client/src/tui.c index 429b82f..633fb88 100644 --- a/client/src/tui.c +++ b/client/src/tui.c @@ -235,6 +235,19 @@ void tui_run(void) { colon_buf[colon_len] = '\0'; colon_len = 0; in_colon = false; + // Check first token before calling handle_client_command + char cmd_copy[256]; + strncpy(cmd_copy, colon_buf, sizeof(cmd_copy)-1); + cmd_copy[sizeof(cmd_copy)-1] = '\0'; + char *first = strtok(cmd_copy, " "); + if (first) { + if (strcmp(first, "rack") == 0) { + rack_mode = true; + rack_selected = 0; + } else if (strcmp(first, "grid") == 0) { + rack_mode = false; + } + } int dummy_id; handle_client_command(colon_buf, &dummy_id); draw_grid(); diff --git a/client/tests/test_client_cmd.c b/client/tests/test_client_cmd.c index 1a6dd14..6efad21 100644 --- a/client/tests/test_client_cmd.c +++ b/client/tests/test_client_cmd.c @@ -26,6 +26,57 @@ static int tests_failed = 0; } \ } while(0) +/* Test from command */ +static void test_from_store(void) +{ + int ret = handle_client_command("from looper:out_0", NULL); + ASSERT_EQ(0, ret, "handle_client_command('from looper:out_0', NULL) returns 0"); + const char *stored = get_stored_from(); + ASSERT_STR_EQ("looper:out_0", stored, "get_stored_from() returns 'looper:out_0'"); +} + +/* Test to command */ +static void test_to_store(void) +{ + int ret = handle_client_command("to plugin:in", NULL); + ASSERT_EQ(0, ret, "handle_client_command('to plugin:in', NULL) returns 0"); + const char *stored = get_stored_to(); + ASSERT_STR_EQ("plugin:in", stored, "get_stored_to() returns 'plugin:in'"); +} + +/* Test connect using stored from/to (should call plugin_connect with those ports, fail because no plugin) */ +static void test_connect_uses_stored(void) +{ + /* Ensure stored from and to are set */ + handle_client_command("from looper:out_0", NULL); + handle_client_command("to plugin:in", NULL); + int id = -1; + int ret = handle_client_command("connect", &id); + /* Should return -1 because plugin_connect fails (no plugin loaded), but not -1 from missing args */ + ASSERT_EQ(-1, ret, "handle_client_command('connect', ...) returns -1 when plugin_connect fails (no JACK)"); +} + +/* Test disconnect using stored from/to */ +static void test_disconnect_uses_stored(void) +{ + handle_client_command("from looper:out_0", NULL); + handle_client_command("to plugin:in", NULL); + int id = -1; + int ret = handle_client_command("disconnect", &id); + /* plugin_disconnect returns 0 even without JACK, so we expect 0 */ + ASSERT_EQ(0, ret, "handle_client_command('disconnect', ...) returns 0 (safe stub)"); +} + +/* Test rack/grid commands return 0 */ +static void test_rack_grid_commands(void) +{ + int id = -1; + int ret = handle_client_command("rack", &id); + ASSERT_EQ(0, ret, "handle_client_command('rack', ...) returns 0"); + ret = handle_client_command("grid", &id); + ASSERT_EQ(0, ret, "handle_client_command('grid', ...) returns 0"); +} + /* Test invalid commands */ static void test_unknown_command(void) { @@ -105,6 +156,11 @@ int main(void) test_connect_no_args(); test_connect_missing_to(); test_connect_invalid_id(); + test_from_store(); + test_to_store(); + test_connect_uses_stored(); + test_disconnect_uses_stored(); + test_rack_grid_commands(); printf("\nResults: %d passed, %d failed\n", tests_passed, tests_failed); return tests_failed > 0 ? 1 : 0; diff --git a/client/tests/test_integration.c b/client/tests/test_integration.c new file mode 100644 index 0000000..95689c6 --- /dev/null +++ b/client/tests/test_integration.c @@ -0,0 +1,35 @@ +#define TESTING 1 +#include "carla_host.h" +#include +#include + +int main(void) +{ + printf("=== Integration test (requires JACK server) ===\n"); + + /* Fail if no JACK server */ + if (carla_init_jack() != 0) { + fprintf(stderr, "FAIL: cannot initialise Carla/JACK – is the JACK server running?\n"); + return 1; + } + + /* Verify handle is now non‑NULL */ + CarlaHostHandle h = carla_get_handle(); + assert(h != NULL); + + /* Test connection tracking without loading a real plugin. + carla_test_add_connection adds a fake connection entry. */ + int ret = carla_test_add_connection(0, "test:out", "looper:in"); + assert(ret == 0); + assert(carla_test_connection_count() == 1); + + /* Disconnect plugin ID 0 – should clear the list */ + ret = carla_disconnect_plugin(0); + assert(ret == 0); + assert(carla_test_connection_count() == 0); + + carla_cleanup_jack(); + + printf("PASS: all integration tests passed (with JACK server).\n"); + return 0; +}