diff --git a/test_engine.c b/test_engine.c index eb2e195..2f5ebe1 100644 --- a/test_engine.c +++ b/test_engine.c @@ -11,35 +11,35 @@ // Test helpers // ============================================================ -static AppState create_test_state(void) { - AppState state; - memset(&state, 0, sizeof(state)); +static AppState* create_test_state(void) { + AppState *state = (AppState *)calloc(1, sizeof(AppState)); + assert(state != NULL); - state.transport_state = TRANSPORT_STOPPED; - state.clock_source = CLOCK_SOURCE_INTERNAL; - state.bpm = DEFAULT_BPM; - state.sample_rate = 48000; - state.samples_per_beat = (state.sample_rate * 60.0) / state.bpm; - state.sample_accumulator = 0.0; - state.quantize_mode = QUANTIZE_OFF; - state.quantize_threshold = 0; - state.running = true; + state->transport_state = TRANSPORT_STOPPED; + state->clock_source = CLOCK_SOURCE_INTERNAL; + state->bpm = DEFAULT_BPM; + state->sample_rate = 48000; + state->samples_per_beat = (state->sample_rate * 60.0) / state->bpm; + state->sample_accumulator = 0.0; + state->quantize_mode = QUANTIZE_OFF; + state->quantize_threshold = 0; + state->running = true; for (int i = 0; i < MAX_CLIPS; i++) { - state.clips[i].state = CLIP_EMPTY; - state.clips[i].buffer = (float *)calloc(MAX_BUFFER_SIZE, sizeof(float)); - assert(state.clips[i].buffer != NULL); - state.clips[i].buffer_size = 0; - state.clips[i].write_position = 0; - state.clips[i].read_position = 0; + state->clips[i].state = CLIP_EMPTY; + state->clips[i].buffer = (float *)calloc(MAX_BUFFER_SIZE, sizeof(float)); + assert(state->clips[i].buffer != NULL); + state->clips[i].buffer_size = 0; + state->clips[i].write_position = 0; + state->clips[i].read_position = 0; - // NEW: Initialize midi clips - state.midi_clips[i].state = CLIP_EMPTY; - state.midi_clips[i].events = (MidiEvent *)calloc(MAX_MIDI_EVENTS, sizeof(MidiEvent)); - assert(state.midi_clips[i].events != NULL); - state.midi_clips[i].max_events = MAX_MIDI_EVENTS; - state.midi_clips[i].event_count = 0; - state.midi_clips[i].read_index = 0; + // Initialize midi clips + state->midi_clips[i].state = CLIP_EMPTY; + state->midi_clips[i].events = (MidiEvent *)calloc(MAX_MIDI_EVENTS, sizeof(MidiEvent)); + assert(state->midi_clips[i].events != NULL); + state->midi_clips[i].max_events = MAX_MIDI_EVENTS; + state->midi_clips[i].event_count = 0; + state->midi_clips[i].read_index = 0; } return state; @@ -50,9 +50,10 @@ static void destroy_test_state(AppState *state) { for (int i = 0; i < MAX_CLIPS; i++) { free(state->clips[i].buffer); state->clips[i].buffer = NULL; - free(state->midi_clips[i].events); // NEW + free(state->midi_clips[i].events); state->midi_clips[i].events = NULL; } + free(state); } } @@ -61,16 +62,16 @@ static void destroy_test_state(AppState *state) { // ============================================================ void test_initial_state(void) { printf("Test 1: Initial state is empty... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); for (int i = 0; i < MAX_CLIPS; i++) { - assert(state.clips[i].state == CLIP_EMPTY); - assert(state.clips[i].buffer_size == 0); - assert(state.clips[i].write_position == 0); - assert(state.clips[i].read_position == 0); + assert(state->clips[i].state == CLIP_EMPTY); + assert(state->clips[i].buffer_size == 0); + assert(state->clips[i].write_position == 0); + assert(state->clips[i].read_position == 0); } - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } @@ -79,15 +80,15 @@ void test_initial_state(void) { // ============================================================ void test_trigger_empty_starts_recording(void) { printf("Test 2: Trigger empty clip starts recording... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); Action action = { .type = ACTION_TRIGGER_CLIP, .data.trigger_clip = { .clip_index = 0 } }; - state = reducer(state, action); + *state = reducer(*state, action); - assert(state.clips[0].state == CLIP_RECORDING); - assert(state.clips[0].write_position == 0); + assert(state->clips[0].state == CLIP_RECORDING); + assert(state->clips[0].write_position == 0); - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } @@ -96,23 +97,23 @@ void test_trigger_empty_starts_recording(void) { // ============================================================ void test_trigger_recording_starts_looping(void) { printf("Test 3: Trigger recording clip stops and starts looping... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); // Start recording Action action = { .type = ACTION_TRIGGER_CLIP, .data.trigger_clip = { .clip_index = 0 } }; - state = reducer(state, action); - assert(state.clips[0].state == CLIP_RECORDING); + *state = reducer(*state, action); + assert(state->clips[0].state == CLIP_RECORDING); // Simulate some recording - state.clips[0].write_position = 100; + state->clips[0].write_position = 100; // Trigger again to stop recording and start looping - state = reducer(state, action); - assert(state.clips[0].state == CLIP_LOOPING); - assert(state.clips[0].buffer_size == 100); - assert(state.clips[0].read_position == 0); + *state = reducer(*state, action); + assert(state->clips[0].state == CLIP_LOOPING); + assert(state->clips[0].buffer_size == 100); + assert(state->clips[0].read_position == 0); - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } @@ -121,19 +122,19 @@ void test_trigger_recording_starts_looping(void) { // ============================================================ void test_trigger_looping_stops(void) { printf("Test 4: Trigger looping clip stops it... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); // Set up a looping clip - state.clips[0].state = CLIP_LOOPING; - state.clips[0].buffer_size = 100; - state.clips[0].read_position = 50; + state->clips[0].state = CLIP_LOOPING; + state->clips[0].buffer_size = 100; + state->clips[0].read_position = 50; Action action = { .type = ACTION_TRIGGER_CLIP, .data.trigger_clip = { .clip_index = 0 } }; - state = reducer(state, action); - assert(state.clips[0].state == CLIP_STOPPED); - assert(state.clips[0].read_position == 0); + *state = reducer(*state, action); + assert(state->clips[0].state == CLIP_STOPPED); + assert(state->clips[0].read_position == 0); - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } @@ -142,18 +143,18 @@ void test_trigger_looping_stops(void) { // ============================================================ void test_trigger_stopped_resumes_looping(void) { printf("Test 5: Trigger stopped clip starts looping again... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); // Set up a stopped clip - state.clips[0].state = CLIP_STOPPED; - state.clips[0].buffer_size = 100; + state->clips[0].state = CLIP_STOPPED; + state->clips[0].buffer_size = 100; Action action = { .type = ACTION_TRIGGER_CLIP, .data.trigger_clip = { .clip_index = 0 } }; - state = reducer(state, action); - assert(state.clips[0].state == CLIP_LOOPING); - assert(state.clips[0].read_position == 0); + *state = reducer(*state, action); + assert(state->clips[0].state == CLIP_LOOPING); + assert(state->clips[0].read_position == 0); - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } @@ -162,29 +163,29 @@ void test_trigger_stopped_resumes_looping(void) { // ============================================================ void test_full_cycle(void) { printf("Test 6: Full cycle test... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); Action action = { .type = ACTION_TRIGGER_CLIP, .data.trigger_clip = { .clip_index = 0 } }; // Empty -> Recording - state = reducer(state, action); - assert(state.clips[0].state == CLIP_RECORDING); + *state = reducer(*state, action); + assert(state->clips[0].state == CLIP_RECORDING); // Recording -> Looping - state.clips[0].write_position = 200; - state = reducer(state, action); - assert(state.clips[0].state == CLIP_LOOPING); - assert(state.clips[0].buffer_size == 200); + state->clips[0].write_position = 200; + *state = reducer(*state, action); + assert(state->clips[0].state == CLIP_LOOPING); + assert(state->clips[0].buffer_size == 200); // Looping -> Stopped - state = reducer(state, action); - assert(state.clips[0].state == CLIP_STOPPED); + *state = reducer(*state, action); + assert(state->clips[0].state == CLIP_STOPPED); // Stopped -> Looping - state = reducer(state, action); - assert(state.clips[0].state == CLIP_LOOPING); + *state = reducer(*state, action); + assert(state->clips[0].state == CLIP_LOOPING); - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } @@ -193,31 +194,31 @@ void test_full_cycle(void) { // ============================================================ void test_multiple_clips(void) { printf("Test 7: Multiple clips work independently... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); Action action0 = { .type = ACTION_TRIGGER_CLIP, .data.trigger_clip = { .clip_index = 0 } }; Action action1 = { .type = ACTION_TRIGGER_CLIP, .data.trigger_clip = { .clip_index = 1 } }; Action action2 = { .type = ACTION_TRIGGER_CLIP, .data.trigger_clip = { .clip_index = 2 } }; // Clip 0: Empty -> Recording - state = reducer(state, action0); - assert(state.clips[0].state == CLIP_RECORDING); + *state = reducer(*state, action0); + assert(state->clips[0].state == CLIP_RECORDING); // Clip 1: Empty -> Recording - state = reducer(state, action1); - assert(state.clips[1].state == CLIP_RECORDING); + *state = reducer(*state, action1); + assert(state->clips[1].state == CLIP_RECORDING); // Clip 0: Recording -> Looping - state.clips[0].write_position = 100; - state = reducer(state, action0); - assert(state.clips[0].state == CLIP_LOOPING); - assert(state.clips[1].state == CLIP_RECORDING); // Clip 1 unaffected + state->clips[0].write_position = 100; + *state = reducer(*state, action0); + assert(state->clips[0].state == CLIP_LOOPING); + assert(state->clips[1].state == CLIP_RECORDING); // Clip 1 unaffected // Clip 2: Empty -> Recording - state = reducer(state, action2); - assert(state.clips[2].state == CLIP_RECORDING); + *state = reducer(*state, action2); + assert(state->clips[2].state == CLIP_RECORDING); - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } @@ -226,22 +227,22 @@ void test_multiple_clips(void) { // ============================================================ void test_reset_clip(void) { printf("Test 8: Reset clip... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); // Set up a clip with data - state.clips[0].state = CLIP_LOOPING; - state.clips[0].buffer_size = 100; - state.clips[0].write_position = 100; - state.clips[0].read_position = 50; + state->clips[0].state = CLIP_LOOPING; + state->clips[0].buffer_size = 100; + state->clips[0].write_position = 100; + state->clips[0].read_position = 50; Action action = { .type = ACTION_RESET_CLIP, .data.reset_clip = { .clip_index = 0 } }; - state = reducer(state, action); - assert(state.clips[0].state == CLIP_EMPTY); - assert(state.clips[0].buffer_size == 0); - assert(state.clips[0].write_position == 0); - assert(state.clips[0].read_position == 0); + *state = reducer(*state, action); + assert(state->clips[0].state == CLIP_EMPTY); + assert(state->clips[0].buffer_size == 0); + assert(state->clips[0].write_position == 0); + assert(state->clips[0].read_position == 0); - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } @@ -278,7 +279,7 @@ void test_state_to_string(void) { // ============================================================ void test_invalid_clip_index(void) { printf("Test 11: Invalid clip index... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); // These should not crash Action action_neg = { .type = ACTION_TRIGGER_CLIP, .data.trigger_clip = { .clip_index = -1 } }; @@ -286,15 +287,15 @@ void test_invalid_clip_index(void) { Action reset_neg = { .type = ACTION_RESET_CLIP, .data.reset_clip = { .clip_index = -1 } }; Action reset_max = { .type = ACTION_RESET_CLIP, .data.reset_clip = { .clip_index = MAX_CLIPS } }; - state = reducer(state, action_neg); - state = reducer(state, action_max); - state = reducer(state, reset_neg); - state = reducer(state, reset_max); + *state = reducer(*state, action_neg); + *state = reducer(*state, action_max); + *state = reducer(*state, reset_neg); + *state = reducer(*state, reset_max); // Verify no state changes - assert(state.clips[0].state == CLIP_EMPTY); + assert(state->clips[0].state == CLIP_EMPTY); - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } @@ -303,23 +304,23 @@ void test_invalid_clip_index(void) { // ============================================================ void test_buffer_overflow(void) { printf("Test 12: Buffer overflow protection... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); Action action = { .type = ACTION_TRIGGER_CLIP, .data.trigger_clip = { .clip_index = 0 } }; // Start recording - state = reducer(state, action); - assert(state.clips[0].state == CLIP_RECORDING); + *state = reducer(*state, action); + assert(state->clips[0].state == CLIP_RECORDING); // Fill buffer to max - state.clips[0].write_position = MAX_BUFFER_SIZE; + state->clips[0].write_position = MAX_BUFFER_SIZE; // Trigger should stop recording and start looping - state = reducer(state, action); - assert(state.clips[0].state == CLIP_LOOPING); - assert(state.clips[0].buffer_size == MAX_BUFFER_SIZE); + *state = reducer(*state, action); + assert(state->clips[0].state == CLIP_LOOPING); + assert(state->clips[0].buffer_size == MAX_BUFFER_SIZE); - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } @@ -328,15 +329,15 @@ void test_buffer_overflow(void) { // ============================================================ void test_transport_initial_state(void) { printf("Test 13: Transport initial state... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); - assert(state.transport_state == TRANSPORT_STOPPED); - assert(state.clock_count == 0); - assert(state.beat_position == 0); - assert(state.bar_position == 0); - assert(state.sample_position == 0); + assert(state->transport_state == TRANSPORT_STOPPED); + assert(state->clock_count == 0); + assert(state->beat_position == 0); + assert(state->bar_position == 0); + assert(state->sample_position == 0); - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } @@ -345,25 +346,25 @@ void test_transport_initial_state(void) { // ============================================================ void test_transport_reset(void) { printf("Test 14: Transport reset... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); // Simulate some transport state - state.transport_state = TRANSPORT_PLAYING; - state.clock_count = 100; - state.beat_position = 2; - state.bar_position = 5; - state.sample_position = 10000; + state->transport_state = TRANSPORT_PLAYING; + state->clock_count = 100; + state->beat_position = 2; + state->bar_position = 5; + state->sample_position = 10000; Action action = { .type = ACTION_RESET_TRANSPORT }; - state = reducer(state, action); + *state = reducer(*state, action); - assert(state.transport_state == TRANSPORT_STOPPED); - assert(state.clock_count == 0); - assert(state.beat_position == 0); - assert(state.bar_position == 0); - assert(state.sample_position == 0); + assert(state->transport_state == TRANSPORT_STOPPED); + assert(state->clock_count == 0); + assert(state->beat_position == 0); + assert(state->bar_position == 0); + assert(state->sample_position == 0); - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } @@ -372,24 +373,24 @@ void test_transport_reset(void) { // ============================================================ void test_quantize_mode_setting(void) { printf("Test 15: Quantize mode setting... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); - assert(state.quantize_mode == QUANTIZE_OFF); + assert(state->quantize_mode == QUANTIZE_OFF); Action action_beat = { .type = ACTION_SET_QUANTIZE_MODE, .data.set_quantize_mode = { .mode = QUANTIZE_BEAT } }; Action action_bar = { .type = ACTION_SET_QUANTIZE_MODE, .data.set_quantize_mode = { .mode = QUANTIZE_BAR } }; Action action_off = { .type = ACTION_SET_QUANTIZE_MODE, .data.set_quantize_mode = { .mode = QUANTIZE_OFF } }; - state = reducer(state, action_beat); - assert(state.quantize_mode == QUANTIZE_BEAT); + *state = reducer(*state, action_beat); + assert(state->quantize_mode == QUANTIZE_BEAT); - state = reducer(state, action_bar); - assert(state.quantize_mode == QUANTIZE_BAR); + *state = reducer(*state, action_bar); + assert(state->quantize_mode == QUANTIZE_BAR); - state = reducer(state, action_off); - assert(state.quantize_mode == QUANTIZE_OFF); + *state = reducer(*state, action_off); + assert(state->quantize_mode == QUANTIZE_OFF); - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } @@ -411,19 +412,19 @@ void test_quantize_mode_to_string(void) { // ============================================================ void test_quantize_threshold_setting(void) { printf("Test 17: Quantize threshold setting... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); - assert(state.quantize_threshold == 0); + assert(state->quantize_threshold == 0); Action action = { .type = ACTION_SET_QUANTIZE_THRESHOLD, .data.set_quantize_threshold = { .threshold = 1000 } }; - state = reducer(state, action); - assert(state.quantize_threshold == 1000); + *state = reducer(*state, action); + assert(state->quantize_threshold == 1000); action.data.set_quantize_threshold.threshold = 0; - state = reducer(state, action); - assert(state.quantize_threshold == 0); + *state = reducer(*state, action); + assert(state->quantize_threshold == 0); - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } @@ -432,24 +433,24 @@ void test_quantize_threshold_setting(void) { // ============================================================ void test_undo_redo(void) { printf("Test 18: Undo/Redo basic... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); // Trigger clip 0 (empty -> recording) Action trigger = { .type = ACTION_TRIGGER_CLIP, .data.trigger_clip = { .clip_index = 0 } }; - state = reducer(state, trigger); - assert(state.clips[0].state == CLIP_RECORDING); + *state = reducer(*state, trigger); + assert(state->clips[0].state == CLIP_RECORDING); // Undo Action undo = { .type = ACTION_UNDO }; - state = reducer(state, undo); - assert(state.clips[0].state == CLIP_EMPTY); + *state = reducer(*state, undo); + assert(state->clips[0].state == CLIP_EMPTY); // Redo Action redo = { .type = ACTION_REDO }; - state = reducer(state, redo); - assert(state.clips[0].state == CLIP_RECORDING); + *state = reducer(*state, redo); + assert(state->clips[0].state == CLIP_RECORDING); - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } @@ -458,15 +459,15 @@ void test_undo_redo(void) { // ============================================================ void test_midi_note_on(void) { printf("Test 19: MIDI note on triggers clip... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); Action action = { .type = ACTION_MIDI_NOTE_ON, .data.midi_note_on = { .note = 60, .velocity = 100, .channel = 0, .time = 0 } }; - state = reducer(state, action); + *state = reducer(*state, action); int clip_idx = 60 % MAX_CLIPS; - assert(state.clips[clip_idx].state == CLIP_RECORDING); + assert(state->clips[clip_idx].state == CLIP_RECORDING); - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } @@ -475,17 +476,17 @@ void test_midi_note_on(void) { // ============================================================ void test_scene_trigger(void) { printf("Test 20: Scene trigger... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); Action action = { .type = ACTION_TRIGGER_SCENE, .data.trigger_scene = { .scene_index = 0 } }; - state = reducer(state, action); + *state = reducer(*state, action); for (int ch = 0; ch < MAX_CHANNELS; ch++) { int clip_idx = CLIP_INDEX(0, ch); - assert(state.clips[clip_idx].state == CLIP_RECORDING); + assert(state->clips[clip_idx].state == CLIP_RECORDING); } - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } @@ -494,30 +495,30 @@ void test_scene_trigger(void) { // ============================================================ void test_transport_play_pause_stop(void) { printf("Test 21: Transport play/pause/stop... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); Action play = { .type = ACTION_TRANSPORT_PLAY }; Action pause = { .type = ACTION_TRANSPORT_PAUSE }; Action stop = { .type = ACTION_TRANSPORT_STOP }; Action toggle = { .type = ACTION_TRANSPORT_TOGGLE_PLAY }; - state = reducer(state, play); - assert(state.transport_state == TRANSPORT_PLAYING); + *state = reducer(*state, play); + assert(state->transport_state == TRANSPORT_PLAYING); - state = reducer(state, pause); - assert(state.transport_state == TRANSPORT_PAUSED); + *state = reducer(*state, pause); + assert(state->transport_state == TRANSPORT_PAUSED); - state = reducer(state, toggle); - assert(state.transport_state == TRANSPORT_PLAYING); + *state = reducer(*state, toggle); + assert(state->transport_state == TRANSPORT_PLAYING); - state = reducer(state, stop); - assert(state.transport_state == TRANSPORT_STOPPED); - assert(state.clock_count == 0); - assert(state.beat_position == 0); - assert(state.bar_position == 0); - assert(state.sample_position == 0); + *state = reducer(*state, stop); + assert(state->transport_state == TRANSPORT_STOPPED); + assert(state->clock_count == 0); + assert(state->beat_position == 0); + assert(state->bar_position == 0); + assert(state->sample_position == 0); - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } @@ -526,17 +527,17 @@ void test_transport_play_pause_stop(void) { // ============================================================ void test_bpm_setting(void) { printf("Test 22: BPM setting... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); - assert(state.bpm == DEFAULT_BPM); + assert(state->bpm == DEFAULT_BPM); Action action = { .type = ACTION_SET_BPM, .data.set_bpm = { .bpm = 140.0 } }; - state = reducer(state, action); - assert(state.bpm == 140.0); - double expected_spp = (state.sample_rate * 60.0) / 140.0; - assert(state.samples_per_beat == expected_spp); + *state = reducer(*state, action); + assert(state->bpm == 140.0); + double expected_spp = (state->sample_rate * 60.0) / 140.0; + assert(state->samples_per_beat == expected_spp); - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } @@ -545,19 +546,19 @@ void test_bpm_setting(void) { // ============================================================ void test_clock_source_setting(void) { printf("Test 23: Clock source setting... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); - assert(state.clock_source == CLOCK_SOURCE_INTERNAL); + assert(state->clock_source == CLOCK_SOURCE_INTERNAL); Action action = { .type = ACTION_SET_CLOCK_SOURCE, .data.set_clock_source = { .source = CLOCK_SOURCE_MIDI } }; - state = reducer(state, action); - assert(state.clock_source == CLOCK_SOURCE_MIDI); - assert(state.clock_count == 0); - assert(state.beat_position == 0); - assert(state.bar_position == 0); - assert(state.sample_position == 0); + *state = reducer(*state, action); + assert(state->clock_source == CLOCK_SOURCE_MIDI); + assert(state->clock_count == 0); + assert(state->beat_position == 0); + assert(state->bar_position == 0); + assert(state->sample_position == 0); - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } @@ -566,36 +567,36 @@ void test_clock_source_setting(void) { // ============================================================ void test_quantization_with_transport(void) { printf("Test 24: Quantization with transport rolling... "); - AppState state = create_test_state(); - state.sample_rate = 48000; - state.transport_state = TRANSPORT_PLAYING; - state.clock_count = MIDI_CLOCKS_PER_BEAT * 2; // 2 beats in - state.sample_position = state.sample_rate * 2; // 2 beats in samples + AppState *state = create_test_state(); + state->sample_rate = 48000; + state->transport_state = TRANSPORT_PLAYING; + state->clock_count = MIDI_CLOCKS_PER_BEAT * 2; // 2 beats in + state->sample_position = state->sample_rate * 2; // 2 beats in samples // Set quantize to beat Action action = { .type = ACTION_SET_QUANTIZE_MODE, .data.set_quantize_mode = { .mode = QUANTIZE_BEAT } }; - state = reducer(state, action); + *state = reducer(*state, action); // Calculate next beat boundary - jack_nframes_t frames_per_beat = state.sample_rate; // 48000 at 120 BPM - jack_nframes_t current_pos = state.sample_position; + jack_nframes_t frames_per_beat = state->sample_rate; // 48000 at 120 BPM + jack_nframes_t current_pos = state->sample_position; jack_nframes_t next_beat = ((current_pos / frames_per_beat) + 1) * frames_per_beat; - jack_nframes_t quantize_frame = next_beat - state.sample_position; + jack_nframes_t quantize_frame = next_beat - state->sample_position; // Should be 48000 samples to next beat assert(quantize_frame == frames_per_beat); // Test bar quantization action.data.set_quantize_mode.mode = QUANTIZE_BAR; - state = reducer(state, action); + *state = reducer(*state, action); jack_nframes_t frames_per_bar = frames_per_beat * BEATS_PER_BAR; jack_nframes_t next_bar = ((current_pos / frames_per_bar) + 1) * frames_per_bar; - quantize_frame = next_bar - state.sample_position; + quantize_frame = next_bar - state->sample_position; // Should be 96000 samples to next bar (2 beats into 4-beat bar) assert(quantize_frame == frames_per_beat * 2); - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } @@ -604,14 +605,14 @@ void test_quantization_with_transport(void) { // ============================================================ void test_quantization_off_with_transport(void) { printf("Test 25: Quantization off with transport rolling... "); - AppState state = create_test_state(); - state.sample_rate = 48000; - state.transport_state = TRANSPORT_PLAYING; - state.clock_count = MIDI_CLOCKS_PER_BEAT * 2; - state.sample_position = state.sample_rate * 2; + AppState *state = create_test_state(); + state->sample_rate = 48000; + state->transport_state = TRANSPORT_PLAYING; + state->clock_count = MIDI_CLOCKS_PER_BEAT * 2; + state->sample_position = state->sample_rate * 2; Action action = { .type = ACTION_SET_QUANTIZE_MODE, .data.set_quantize_mode = { .mode = QUANTIZE_OFF } }; - state = reducer(state, action); + *state = reducer(*state, action); // When quantize is off, trigger should be immediate jack_nframes_t current_frame = 100; @@ -619,7 +620,7 @@ void test_quantization_off_with_transport(void) { assert(quantize_frame == 100); - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } @@ -628,12 +629,12 @@ void test_quantization_off_with_transport(void) { // ============================================================ void test_quantization_without_transport(void) { printf("Test 26: Quantization without transport rolling... "); - AppState state = create_test_state(); - state.sample_rate = 48000; - state.transport_state = TRANSPORT_STOPPED; + AppState *state = create_test_state(); + state->sample_rate = 48000; + state->transport_state = TRANSPORT_STOPPED; Action action = { .type = ACTION_SET_QUANTIZE_MODE, .data.set_quantize_mode = { .mode = QUANTIZE_BEAT } }; - state = reducer(state, action); + *state = reducer(*state, action); // When transport is not rolling, trigger should be immediate jack_nframes_t current_frame = 100; @@ -641,7 +642,7 @@ void test_quantization_without_transport(void) { assert(quantize_frame == 100); - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } @@ -650,33 +651,33 @@ void test_quantization_without_transport(void) { // ============================================================ void test_save_load_clip(void) { printf("Test 27: Save/Load clip (basic)... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); // Set up a clip with data - state.clips[0].state = CLIP_LOOPING; - state.clips[0].buffer_size = 100; - state.clips[0].write_position = 100; - state.clips[0].read_position = 0; + state->clips[0].state = CLIP_LOOPING; + state->clips[0].buffer_size = 100; + state->clips[0].write_position = 100; + state->clips[0].read_position = 0; for (size_t i = 0; i < 100; i++) { - state.clips[0].buffer[i] = (float)i; + state->clips[0].buffer[i] = (float)i; } // Save clip Action save = { .type = ACTION_SAVE_CLIP, .data.save_clip = { .clip_index = 0 } }; - state = reducer(state, save); + *state = reducer(*state, save); // Reset clip Action reset = { .type = ACTION_RESET_CLIP, .data.reset_clip = { .clip_index = 0 } }; - state = reducer(state, reset); - assert(state.clips[0].state == CLIP_EMPTY); + *state = reducer(*state, reset); + assert(state->clips[0].state == CLIP_EMPTY); // Load clip (requires a filename; we'll use a dummy that will fail gracefully) Action load = { .type = ACTION_LOAD_CLIP, .data.load_clip = { .clip_index = 0, .filename = "samples/clip_0.wav" } }; - state = reducer(state, load); + *state = reducer(*state, load); // If file doesn't exist, clip remains empty (no crash) // We just verify no crash - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } @@ -685,14 +686,14 @@ void test_save_load_clip(void) { // ============================================================ void test_quit_action(void) { printf("Test 28: Quit action... "); - AppState state = create_test_state(); - assert(state.running == true); + AppState *state = create_test_state(); + assert(state->running == true); Action quit = { .type = ACTION_QUIT }; - state = reducer(state, quit); - assert(state.running == false); + *state = reducer(*state, quit); + assert(state->running == false); - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } @@ -701,17 +702,17 @@ void test_quit_action(void) { // ============================================================ void test_midi_clip_initial_state(void) { printf("Test 29: MIDI clip initial state... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); for (int i = 0; i < MAX_CLIPS; i++) { - assert(state.midi_clips[i].state == CLIP_EMPTY); - assert(state.midi_clips[i].event_count == 0); - assert(state.midi_clips[i].read_index == 0); - assert(state.midi_clips[i].events != NULL); - assert(state.midi_clips[i].max_events == MAX_MIDI_EVENTS); + assert(state->midi_clips[i].state == CLIP_EMPTY); + assert(state->midi_clips[i].event_count == 0); + assert(state->midi_clips[i].read_index == 0); + assert(state->midi_clips[i].events != NULL); + assert(state->midi_clips[i].max_events == MAX_MIDI_EVENTS); } - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } @@ -720,16 +721,16 @@ void test_midi_clip_initial_state(void) { // ============================================================ void test_midi_clip_trigger_empty_starts_recording(void) { printf("Test 30: MIDI clip trigger empty starts recording... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); Action action = { .type = ACTION_MIDI_CLIP_TRIGGER, .data.midi_clip_trigger = { .clip_index = 0 } }; - state = reducer(state, action); + *state = reducer(*state, action); - assert(state.midi_clips[0].state == CLIP_RECORDING); - assert(state.midi_clips[0].event_count == 0); - assert(state.midi_clips[0].read_index == 0); + assert(state->midi_clips[0].state == CLIP_RECORDING); + assert(state->midi_clips[0].event_count == 0); + assert(state->midi_clips[0].read_index == 0); - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } @@ -738,23 +739,23 @@ void test_midi_clip_trigger_empty_starts_recording(void) { // ============================================================ void test_midi_clip_trigger_recording_starts_looping(void) { printf("Test 31: MIDI clip trigger recording starts looping... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); // Start recording Action action = { .type = ACTION_MIDI_CLIP_TRIGGER, .data.midi_clip_trigger = { .clip_index = 0 } }; - state = reducer(state, action); - assert(state.midi_clips[0].state == CLIP_RECORDING); + *state = reducer(*state, action); + assert(state->midi_clips[0].state == CLIP_RECORDING); // Simulate some recorded events - state.midi_clips[0].event_count = 10; + state->midi_clips[0].event_count = 10; // Trigger again to stop recording and start looping - state = reducer(state, action); - assert(state.midi_clips[0].state == CLIP_LOOPING); - assert(state.midi_clips[0].read_index == 0); - assert(state.midi_clips[0].event_count == 10); + *state = reducer(*state, action); + assert(state->midi_clips[0].state == CLIP_LOOPING); + assert(state->midi_clips[0].read_index == 0); + assert(state->midi_clips[0].event_count == 10); - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } @@ -763,19 +764,19 @@ void test_midi_clip_trigger_recording_starts_looping(void) { // ============================================================ void test_midi_clip_trigger_looping_stops(void) { printf("Test 32: MIDI clip trigger looping stops it... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); // Set up a looping MIDI clip - state.midi_clips[0].state = CLIP_LOOPING; - state.midi_clips[0].event_count = 10; - state.midi_clips[0].read_index = 5; + state->midi_clips[0].state = CLIP_LOOPING; + state->midi_clips[0].event_count = 10; + state->midi_clips[0].read_index = 5; Action action = { .type = ACTION_MIDI_CLIP_TRIGGER, .data.midi_clip_trigger = { .clip_index = 0 } }; - state = reducer(state, action); - assert(state.midi_clips[0].state == CLIP_STOPPED); - assert(state.midi_clips[0].read_index == 0); + *state = reducer(*state, action); + assert(state->midi_clips[0].state == CLIP_STOPPED); + assert(state->midi_clips[0].read_index == 0); - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } @@ -784,18 +785,18 @@ void test_midi_clip_trigger_looping_stops(void) { // ============================================================ void test_midi_clip_trigger_stopped_resumes_looping(void) { printf("Test 33: MIDI clip trigger stopped resumes looping... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); // Set up a stopped MIDI clip - state.midi_clips[0].state = CLIP_STOPPED; - state.midi_clips[0].event_count = 10; + state->midi_clips[0].state = CLIP_STOPPED; + state->midi_clips[0].event_count = 10; Action action = { .type = ACTION_MIDI_CLIP_TRIGGER, .data.midi_clip_trigger = { .clip_index = 0 } }; - state = reducer(state, action); - assert(state.midi_clips[0].state == CLIP_LOOPING); - assert(state.midi_clips[0].read_index == 0); + *state = reducer(*state, action); + assert(state->midi_clips[0].state == CLIP_LOOPING); + assert(state->midi_clips[0].read_index == 0); - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } @@ -804,29 +805,29 @@ void test_midi_clip_trigger_stopped_resumes_looping(void) { // ============================================================ void test_midi_clip_full_cycle(void) { printf("Test 34: MIDI clip full cycle... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); Action action = { .type = ACTION_MIDI_CLIP_TRIGGER, .data.midi_clip_trigger = { .clip_index = 0 } }; // Empty -> Recording - state = reducer(state, action); - assert(state.midi_clips[0].state == CLIP_RECORDING); + *state = reducer(*state, action); + assert(state->midi_clips[0].state == CLIP_RECORDING); // Recording -> Looping - state.midi_clips[0].event_count = 20; - state = reducer(state, action); - assert(state.midi_clips[0].state == CLIP_LOOPING); - assert(state.midi_clips[0].event_count == 20); + state->midi_clips[0].event_count = 20; + *state = reducer(*state, action); + assert(state->midi_clips[0].state == CLIP_LOOPING); + assert(state->midi_clips[0].event_count == 20); // Looping -> Stopped - state = reducer(state, action); - assert(state.midi_clips[0].state == CLIP_STOPPED); + *state = reducer(*state, action); + assert(state->midi_clips[0].state == CLIP_STOPPED); // Stopped -> Looping - state = reducer(state, action); - assert(state.midi_clips[0].state == CLIP_LOOPING); + *state = reducer(*state, action); + assert(state->midi_clips[0].state == CLIP_LOOPING); - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } @@ -835,22 +836,22 @@ void test_midi_clip_full_cycle(void) { // ============================================================ void test_midi_clip_reset(void) { printf("Test 35: MIDI clip reset... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); // Set up a MIDI clip with data - state.midi_clips[0].state = CLIP_LOOPING; - state.midi_clips[0].event_count = 20; - state.midi_clips[0].read_index = 10; + state->midi_clips[0].state = CLIP_LOOPING; + state->midi_clips[0].event_count = 20; + state->midi_clips[0].read_index = 10; Action action = { .type = ACTION_MIDI_CLIP_RESET, .data.midi_clip_reset = { .clip_index = 0 } }; - state = reducer(state, action); - assert(state.midi_clips[0].state == CLIP_EMPTY); - assert(state.midi_clips[0].event_count == 0); - assert(state.midi_clips[0].read_index == 0); + *state = reducer(*state, action); + assert(state->midi_clips[0].state == CLIP_EMPTY); + assert(state->midi_clips[0].event_count == 0); + assert(state->midi_clips[0].read_index == 0); // events pointer should still be valid - assert(state.midi_clips[0].events != NULL); + assert(state->midi_clips[0].events != NULL); - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } @@ -859,31 +860,31 @@ void test_midi_clip_reset(void) { // ============================================================ void test_midi_clip_multiple_clips(void) { printf("Test 36: MIDI clip multiple clips work independently... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); Action action0 = { .type = ACTION_MIDI_CLIP_TRIGGER, .data.midi_clip_trigger = { .clip_index = 0 } }; Action action1 = { .type = ACTION_MIDI_CLIP_TRIGGER, .data.midi_clip_trigger = { .clip_index = 1 } }; Action action2 = { .type = ACTION_MIDI_CLIP_TRIGGER, .data.midi_clip_trigger = { .clip_index = 2 } }; // Clip 0: Empty -> Recording - state = reducer(state, action0); - assert(state.midi_clips[0].state == CLIP_RECORDING); + *state = reducer(*state, action0); + assert(state->midi_clips[0].state == CLIP_RECORDING); // Clip 1: Empty -> Recording - state = reducer(state, action1); - assert(state.midi_clips[1].state == CLIP_RECORDING); + *state = reducer(*state, action1); + assert(state->midi_clips[1].state == CLIP_RECORDING); // Clip 0: Recording -> Looping - state.midi_clips[0].event_count = 10; - state = reducer(state, action0); - assert(state.midi_clips[0].state == CLIP_LOOPING); - assert(state.midi_clips[1].state == CLIP_RECORDING); // Clip 1 unaffected + state->midi_clips[0].event_count = 10; + *state = reducer(*state, action0); + assert(state->midi_clips[0].state == CLIP_LOOPING); + assert(state->midi_clips[1].state == CLIP_RECORDING); // Clip 1 unaffected // Clip 2: Empty -> Recording - state = reducer(state, action2); - assert(state.midi_clips[2].state == CLIP_RECORDING); + *state = reducer(*state, action2); + assert(state->midi_clips[2].state == CLIP_RECORDING); - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } @@ -892,7 +893,7 @@ void test_midi_clip_multiple_clips(void) { // ============================================================ void test_midi_clip_invalid_index(void) { printf("Test 37: MIDI clip invalid index... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); // These should not crash Action action_neg = { .type = ACTION_MIDI_CLIP_TRIGGER, .data.midi_clip_trigger = { .clip_index = -1 } }; @@ -900,15 +901,15 @@ void test_midi_clip_invalid_index(void) { Action reset_neg = { .type = ACTION_MIDI_CLIP_RESET, .data.midi_clip_reset = { .clip_index = -1 } }; Action reset_max = { .type = ACTION_MIDI_CLIP_RESET, .data.midi_clip_reset = { .clip_index = MAX_CLIPS } }; - state = reducer(state, action_neg); - state = reducer(state, action_max); - state = reducer(state, reset_neg); - state = reducer(state, reset_max); + *state = reducer(*state, action_neg); + *state = reducer(*state, action_max); + *state = reducer(*state, reset_neg); + *state = reducer(*state, reset_max); // Verify no state changes - assert(state.midi_clips[0].state == CLIP_EMPTY); + assert(state->midi_clips[0].state == CLIP_EMPTY); - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } @@ -917,19 +918,19 @@ void test_midi_clip_invalid_index(void) { // ============================================================ void test_midi_clip_show_grid_toggle(void) { printf("Test 38: MIDI clip show_midi_grid toggle... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); - assert(state.show_midi_grid == false); + assert(state->show_midi_grid == false); Action action_on = { .type = ACTION_SET_SHOW_MIDI_GRID, .data.set_show_midi_grid = { .show = true } }; - state = reducer(state, action_on); - assert(state.show_midi_grid == true); + *state = reducer(*state, action_on); + assert(state->show_midi_grid == true); Action action_off = { .type = ACTION_SET_SHOW_MIDI_GRID, .data.set_show_midi_grid = { .show = false } }; - state = reducer(state, action_off); - assert(state.show_midi_grid == false); + *state = reducer(*state, action_off); + assert(state->show_midi_grid == false); - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } @@ -938,29 +939,29 @@ void test_midi_clip_show_grid_toggle(void) { // ============================================================ void test_midi_clip_events_persist_after_reset(void) { printf("Test 39: MIDI clip events pointer persists after reset... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); // Get the original events pointer - MidiEvent *original_events = state.midi_clips[0].events; + MidiEvent *original_events = state->midi_clips[0].events; assert(original_events != NULL); // Set up some events - state.midi_clips[0].events[0].note = 60; - state.midi_clips[0].events[0].velocity = 100; - state.midi_clips[0].events[0].timestamp = 0; - state.midi_clips[0].event_count = 1; - state.midi_clips[0].state = CLIP_LOOPING; + state->midi_clips[0].events[0].note = 60; + state->midi_clips[0].events[0].velocity = 100; + state->midi_clips[0].events[0].timestamp = 0; + state->midi_clips[0].event_count = 1; + state->midi_clips[0].state = CLIP_LOOPING; // Reset the clip Action action = { .type = ACTION_MIDI_CLIP_RESET, .data.midi_clip_reset = { .clip_index = 0 } }; - state = reducer(state, action); + *state = reducer(*state, action); // Events pointer should still be the same (not freed) - assert(state.midi_clips[0].events == original_events); - assert(state.midi_clips[0].state == CLIP_EMPTY); - assert(state.midi_clips[0].event_count == 0); + assert(state->midi_clips[0].events == original_events); + assert(state->midi_clips[0].state == CLIP_EMPTY); + assert(state->midi_clips[0].event_count == 0); - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } @@ -969,31 +970,31 @@ void test_midi_clip_events_persist_after_reset(void) { // ============================================================ void test_midi_clip_trigger_with_events(void) { printf("Test 40: MIDI clip trigger with events in buffer... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); // Set up a MIDI clip with events already recorded - state.midi_clips[0].events[0].note = 60; - state.midi_clips[0].events[0].velocity = 100; - state.midi_clips[0].events[0].timestamp = 0; - state.midi_clips[0].events[1].note = 64; - state.midi_clips[0].events[1].velocity = 80; - state.midi_clips[0].events[1].timestamp = 100; - state.midi_clips[0].event_count = 2; - state.midi_clips[0].state = CLIP_STOPPED; + state->midi_clips[0].events[0].note = 60; + state->midi_clips[0].events[0].velocity = 100; + state->midi_clips[0].events[0].timestamp = 0; + state->midi_clips[0].events[1].note = 64; + state->midi_clips[0].events[1].velocity = 80; + state->midi_clips[0].events[1].timestamp = 100; + state->midi_clips[0].event_count = 2; + state->midi_clips[0].state = CLIP_STOPPED; // Trigger to resume looping Action action = { .type = ACTION_MIDI_CLIP_TRIGGER, .data.midi_clip_trigger = { .clip_index = 0 } }; - state = reducer(state, action); + *state = reducer(*state, action); - assert(state.midi_clips[0].state == CLIP_LOOPING); - assert(state.midi_clips[0].event_count == 2); - assert(state.midi_clips[0].read_index == 0); + assert(state->midi_clips[0].state == CLIP_LOOPING); + assert(state->midi_clips[0].event_count == 2); + assert(state->midi_clips[0].read_index == 0); // Verify events are intact - assert(state.midi_clips[0].events[0].note == 60); - assert(state.midi_clips[0].events[1].note == 64); + assert(state->midi_clips[0].events[0].note == 60); + assert(state->midi_clips[0].events[1].note == 64); - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); }