#include #include #include #include #include "engine.h" // Test helper static Engine *create_test_engine(void) { Engine *engine = (Engine *)calloc(1, sizeof(Engine)); assert(engine != NULL); engine->control_channel = 0; engine->sample_rate = 48000; for (int i = 0; i < MAX_CLIPS; i++) { engine->clips[i].state = CLIP_EMPTY; engine->clips[i].buffer = (float *)calloc(MAX_BUFFER_SIZE, sizeof(float)); assert(engine->clips[i].buffer != NULL); engine->clips[i].buffer_size = 0; engine->clips[i].write_position = 0; engine->clips[i].read_position = 0; } return engine; } static void destroy_test_engine(Engine *engine) { if (engine) { for (int i = 0; i < MAX_CLIPS; i++) { free(engine->clips[i].buffer); } free(engine); } } // Test 1: Initial state is empty void test_initial_state(void) { printf("Test 1: Initial state is empty... "); Engine *engine = create_test_engine(); for (int i = 0; i < MAX_CLIPS; i++) { assert(engine->clips[i].state == CLIP_EMPTY); assert(engine->clips[i].buffer_size == 0); assert(engine->clips[i].write_position == 0); assert(engine->clips[i].read_position == 0); } destroy_test_engine(engine); printf("PASSED\n"); } // Test 2: Trigger empty clip starts recording void test_trigger_empty_starts_recording(void) { printf("Test 2: Trigger empty clip starts recording... "); Engine *engine = create_test_engine(); engine_trigger_clip(engine, 0); assert(engine->clips[0].state == CLIP_RECORDING); assert(engine->clips[0].write_position == 0); destroy_test_engine(engine); printf("PASSED\n"); } // Test 3: Trigger recording clip stops and starts looping void test_trigger_recording_starts_looping(void) { printf("Test 3: Trigger recording clip stops and starts looping... "); Engine *engine = create_test_engine(); // Start recording engine_trigger_clip(engine, 0); assert(engine->clips[0].state == CLIP_RECORDING); // Simulate some recording engine->clips[0].write_position = 100; // Trigger again to stop recording and start looping engine_trigger_clip(engine, 0); assert(engine->clips[0].state == CLIP_LOOPING); assert(engine->clips[0].buffer_size == 100); assert(engine->clips[0].read_position == 0); destroy_test_engine(engine); printf("PASSED\n"); } // Test 4: Trigger looping clip stops it void test_trigger_looping_stops(void) { printf("Test 4: Trigger looping clip stops it... "); Engine *engine = create_test_engine(); // Set up a looping clip engine->clips[0].state = CLIP_LOOPING; engine->clips[0].buffer_size = 100; engine->clips[0].read_position = 50; engine_trigger_clip(engine, 0); assert(engine->clips[0].state == CLIP_STOPPED); assert(engine->clips[0].read_position == 0); destroy_test_engine(engine); printf("PASSED\n"); } // Test 5: Trigger stopped clip starts looping again void test_trigger_stopped_resumes_looping(void) { printf("Test 5: Trigger stopped clip starts looping again... "); Engine *engine = create_test_engine(); // Set up a stopped clip engine->clips[0].state = CLIP_STOPPED; engine->clips[0].buffer_size = 100; engine_trigger_clip(engine, 0); assert(engine->clips[0].state == CLIP_LOOPING); assert(engine->clips[0].read_position == 0); destroy_test_engine(engine); printf("PASSED\n"); } // Test 6: Full cycle test void test_full_cycle(void) { printf("Test 6: Full cycle test... "); Engine *engine = create_test_engine(); // Empty -> Recording engine_trigger_clip(engine, 0); assert(engine->clips[0].state == CLIP_RECORDING); // Recording -> Looping engine->clips[0].write_position = 200; engine_trigger_clip(engine, 0); assert(engine->clips[0].state == CLIP_LOOPING); assert(engine->clips[0].buffer_size == 200); // Looping -> Stopped engine_trigger_clip(engine, 0); assert(engine->clips[0].state == CLIP_STOPPED); // Stopped -> Looping engine_trigger_clip(engine, 0); assert(engine->clips[0].state == CLIP_LOOPING); destroy_test_engine(engine); printf("PASSED\n"); } // Test 7: Multiple clips work independently void test_multiple_clips(void) { printf("Test 7: Multiple clips work independently... "); Engine *engine = create_test_engine(); // Clip 0: Empty -> Recording engine_trigger_clip(engine, 0); assert(engine->clips[0].state == CLIP_RECORDING); // Clip 1: Empty -> Recording engine_trigger_clip(engine, 1); assert(engine->clips[1].state == CLIP_RECORDING); // Clip 0: Recording -> Looping engine->clips[0].write_position = 100; engine_trigger_clip(engine, 0); assert(engine->clips[0].state == CLIP_LOOPING); assert(engine->clips[1].state == CLIP_RECORDING); // Clip 1 unaffected // Clip 2: Empty -> Recording engine_trigger_clip(engine, 2); assert(engine->clips[2].state == CLIP_RECORDING); destroy_test_engine(engine); printf("PASSED\n"); } // Test 8: Reset clip void test_reset_clip(void) { printf("Test 8: Reset clip... "); Engine *engine = create_test_engine(); // Set up a clip with data engine->clips[0].state = CLIP_LOOPING; engine->clips[0].buffer_size = 100; engine->clips[0].write_position = 100; engine->clips[0].read_position = 50; engine_reset_clip(engine, 0); assert(engine->clips[0].state == CLIP_EMPTY); assert(engine->clips[0].buffer_size == 0); assert(engine->clips[0].write_position == 0); assert(engine->clips[0].read_position == 0); destroy_test_engine(engine); printf("PASSED\n"); } // Test 9: Clip state to velocity mapping void test_state_to_velocity(void) { printf("Test 9: Clip state to velocity mapping... "); assert(clip_state_to_velocity(CLIP_EMPTY) == 0); assert(clip_state_to_velocity(CLIP_RECORDING) == 64); assert(clip_state_to_velocity(CLIP_LOOPING) == 127); assert(clip_state_to_velocity(CLIP_STOPPED) == 32); printf("PASSED\n"); } // Test 10: Clip state to string void test_state_to_string(void) { printf("Test 10: Clip state to string... "); assert(strcmp(clip_state_to_string(CLIP_EMPTY), "Empty") == 0); assert(strcmp(clip_state_to_string(CLIP_RECORDING), "Recording") == 0); assert(strcmp(clip_state_to_string(CLIP_LOOPING), "Looping") == 0); assert(strcmp(clip_state_to_string(CLIP_STOPPED), "Stopped") == 0); printf("PASSED\n"); } // Test 11: Invalid clip index void test_invalid_clip_index(void) { printf("Test 11: Invalid clip index... "); Engine *engine = create_test_engine(); // These should not crash engine_trigger_clip(engine, -1); engine_trigger_clip(engine, MAX_CLIPS); engine_reset_clip(engine, -1); engine_reset_clip(engine, MAX_CLIPS); // Verify no state changes assert(engine->clips[0].state == CLIP_EMPTY); destroy_test_engine(engine); printf("PASSED\n"); } // Test 12: Buffer overflow protection void test_buffer_overflow(void) { printf("Test 12: Buffer overflow protection... "); Engine *engine = create_test_engine(); // Start recording engine_trigger_clip(engine, 0); assert(engine->clips[0].state == CLIP_RECORDING); // Fill buffer to max engine->clips[0].write_position = MAX_BUFFER_SIZE; // Trigger should stop recording and start looping engine_trigger_clip(engine, 0); assert(engine->clips[0].state == CLIP_LOOPING); assert(engine->clips[0].buffer_size == MAX_BUFFER_SIZE); destroy_test_engine(engine); printf("PASSED\n"); } int main(void) { printf("Running JACK Looper tests...\n\n"); test_initial_state(); test_trigger_empty_starts_recording(); test_trigger_recording_starts_looping(); test_trigger_looping_stops(); test_trigger_stopped_resumes_looping(); test_full_cycle(); test_multiple_clips(); test_reset_clip(); test_state_to_velocity(); test_state_to_string(); test_invalid_clip_index(); test_buffer_overflow(); printf("\nAll tests passed!\n"); return 0; }