diff --git a/test_tui.c b/test_tui.c index 6fee78e..c2392d7 100644 --- a/test_tui.c +++ b/test_tui.c @@ -7,48 +7,48 @@ #include "tui.h" // Test helper functions -static AppState create_test_state(void) { - AppState state; - memset(&state, 0, sizeof(AppState)); +static AppState* create_test_state(void) { + AppState *state = (AppState *)calloc(1, sizeof(AppState)); + assert(state != NULL); // Initialize clips 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; } // Initialize transport - state.transport_state = TRANSPORT_STOPPED; - state.clock_source = CLOCK_SOURCE_INTERNAL; - state.bpm = 120.0; - state.samples_per_beat = (48000 * 60.0) / 120.0; - state.clock_count = 0; - state.beat_position = 0; - state.bar_position = 0; - state.sample_position = 0; - state.sample_accumulator = 0.0; + state->transport_state = TRANSPORT_STOPPED; + state->clock_source = CLOCK_SOURCE_INTERNAL; + state->bpm = 120.0; + state->samples_per_beat = (48000 * 60.0) / 120.0; + state->clock_count = 0; + state->beat_position = 0; + state->bar_position = 0; + state->sample_position = 0; + state->sample_accumulator = 0.0; // Initialize quantize - state.quantize_mode = QUANTIZE_OFF; - state.quantize_threshold = 0; + state->quantize_mode = QUANTIZE_OFF; + state->quantize_threshold = 0; // Initialize undo - state.undo.undo_index = 0; - state.undo.redo_index = 0; - state.undo.count = 0; - state.undo.current_batch_size = 0; + state->undo.undo_index = 0; + state->undo.redo_index = 0; + state->undo.count = 0; + state->undo.current_batch_size = 0; for (int i = 0; i < MAX_UNDO_HISTORY; i++) { - state.undo.prev_clip_indices[i] = -1; - state.undo.batch_sizes[i] = 0; + state->undo.prev_clip_indices[i] = -1; + state->undo.batch_sizes[i] = 0; } // JACK info - state.sample_rate = 48000; - state.running = true; + state->sample_rate = 48000; + state->running = true; return state; } @@ -59,6 +59,7 @@ static void destroy_test_state(AppState *state) { free(state->clips[i].buffer); state->clips[i].buffer = NULL; } + free(state); } } @@ -79,132 +80,132 @@ void test_grid_to_clip_index(void) { // Test 2: Trigger clip via grid position void test_trigger_via_grid(void) { printf("Test 2: Trigger clip via grid position... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); // Simulate pressing 't' on grid position (3, 4) = clip 28 int clip_idx = 3 * 8 + 4; Action action = { .type = ACTION_TRIGGER_CLIP, .data.trigger_clip.clip_index = clip_idx }; - state = reducer(state, action); + reducer(state, action); - 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"); } // Test 3: Reset clip via grid position void test_reset_via_grid(void) { printf("Test 3: Reset clip via grid position... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); // Set up a clip at grid position (1, 2) = clip 10 int clip_idx = 1 * 8 + 2; - state.clips[clip_idx].state = CLIP_LOOPING; - state.clips[clip_idx].buffer_size = 100; + state->clips[clip_idx].state = CLIP_LOOPING; + state->clips[clip_idx].buffer_size = 100; // Simulate pressing 'r' Action action = { .type = ACTION_RESET_CLIP, .data.reset_clip.clip_index = clip_idx }; - state = reducer(state, action); + reducer(state, action); - assert(state.clips[clip_idx].state == CLIP_EMPTY); - assert(state.clips[clip_idx].buffer_size == 0); + assert(state->clips[clip_idx].state == CLIP_EMPTY); + assert(state->clips[clip_idx].buffer_size == 0); - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } // Test 4: Scene trigger via grid row void test_scene_via_grid(void) { printf("Test 4: Scene trigger via grid row... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); // Simulate pressing 's' on row 3 int scene_index = 3; Action action = { .type = ACTION_TRIGGER_SCENE, .data.trigger_scene.scene_index = scene_index }; - state = reducer(state, action); + reducer(state, action); // All clips in scene 3 should be recording for (int ch = 0; ch < MAX_CHANNELS; ch++) { int clip_idx = CLIP_INDEX(scene_index, 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"); } // Test 5: Quantize mode cycling void test_quantize_cycling(void) { printf("Test 5: Quantize mode cycling... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); // Simulate pressing 'q' to cycle through modes - assert(state.quantize_mode == QUANTIZE_OFF); + assert(state->quantize_mode == QUANTIZE_OFF); // Cycle: OFF -> BEAT Action action = { .type = ACTION_SET_QUANTIZE_MODE, .data.set_quantize_mode.mode = QUANTIZE_BEAT }; - state = reducer(state, action); - assert(state.quantize_mode == QUANTIZE_BEAT); + reducer(state, action); + assert(state->quantize_mode == QUANTIZE_BEAT); // Cycle: BEAT -> BAR action.data.set_quantize_mode.mode = QUANTIZE_BAR; - state = reducer(state, action); - assert(state.quantize_mode == QUANTIZE_BAR); + reducer(state, action); + assert(state->quantize_mode == QUANTIZE_BAR); // Cycle: BAR -> OFF action.data.set_quantize_mode.mode = QUANTIZE_OFF; - state = reducer(state, action); - assert(state.quantize_mode == QUANTIZE_OFF); + reducer(state, action); + assert(state->quantize_mode == QUANTIZE_OFF); - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } // Test 6: Threshold toggling void test_threshold_toggle(void) { printf("Test 6: Threshold toggling... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); // Simulate pressing 'T' to toggle threshold - assert(state.quantize_threshold == 0); + assert(state->quantize_threshold == 0); // Toggle to 1000 Action action = { .type = ACTION_SET_QUANTIZE_THRESHOLD, .data.set_quantize_threshold.threshold = 1000 }; - state = reducer(state, action); - assert(state.quantize_threshold == 1000); + reducer(state, action); + assert(state->quantize_threshold == 1000); // Toggle back to 0 action.data.set_quantize_threshold.threshold = 0; - state = reducer(state, action); - assert(state.quantize_threshold == 0); + reducer(state, action); + assert(state->quantize_threshold == 0); - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } // Test 7: Transport reset void test_transport_reset_via_tui(void) { printf("Test 7: Transport reset via TUI... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); // Set up 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; // Simulate pressing 'x' Action action = { .type = ACTION_TRANSPORT_STOP }; - state = reducer(state, action); + 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"); } @@ -239,37 +240,37 @@ void test_navigation_wrapping(void) { // Test 9: Multiple clips in different states void test_multiple_clip_states(void) { printf("Test 9: Multiple clips in different states... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); // Set up clips in various states - state.clips[0].state = CLIP_EMPTY; - state.clips[1].state = CLIP_RECORDING; - state.clips[2].state = CLIP_LOOPING; - state.clips[3].state = CLIP_STOPPED; + state->clips[0].state = CLIP_EMPTY; + state->clips[1].state = CLIP_RECORDING; + state->clips[2].state = CLIP_LOOPING; + state->clips[3].state = CLIP_STOPPED; // Verify states - assert(state.clips[0].state == CLIP_EMPTY); - assert(state.clips[1].state == CLIP_RECORDING); - assert(state.clips[2].state == CLIP_LOOPING); - assert(state.clips[3].state == CLIP_STOPPED); + assert(state->clips[0].state == CLIP_EMPTY); + assert(state->clips[1].state == CLIP_RECORDING); + assert(state->clips[2].state == CLIP_LOOPING); + assert(state->clips[3].state == CLIP_STOPPED); - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } // Test 10: Buffer size display void test_buffer_size_display(void) { printf("Test 10: Buffer size display... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); // Set up a clip with known buffer size - state.clips[5].state = CLIP_LOOPING; - state.clips[5].buffer_size = 48000; // 1 second at 48kHz + state->clips[5].state = CLIP_LOOPING; + state->clips[5].buffer_size = 48000; // 1 second at 48kHz // Verify buffer size - assert(state.clips[5].buffer_size == 48000); + assert(state->clips[5].buffer_size == 48000); - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } @@ -307,13 +308,13 @@ void test_escape_handling(void) { // Test 13: TUI init and cleanup (without ncurses) void test_tui_init_cleanup(void) { printf("Test 13: TUI init and cleanup... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); // Verify state is valid - assert(state.sample_rate == 48000); - assert(state.running == true); + assert(state->sample_rate == 48000); + assert(state->running == true);; - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED (skipped ncurses init)\n"); } @@ -333,118 +334,118 @@ void test_state_to_color_mapping(void) { // Test 15: Full grid coverage void test_full_grid_coverage(void) { printf("Test 15: Full grid coverage... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); // Trigger all 64 clips via grid positions for (int row = 0; row < 8; row++) { for (int col = 0; col < 8; col++) { int clip_idx = row * 8 + col; Action action = { .type = ACTION_TRIGGER_CLIP, .data.trigger_clip.clip_index = clip_idx }; - state = reducer(state, action); - assert(state.clips[clip_idx].state == CLIP_RECORDING); + reducer(state, action); + assert(state->clips[clip_idx].state == CLIP_RECORDING); } } // Verify all clips are recording for (int i = 0; i < 64; i++) { // Only check the 8x8 grid clips - assert(state.clips[i].state == CLIP_RECORDING); + assert(state->clips[i].state == CLIP_RECORDING); } - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } // Test 16: Scene trigger from each row void test_scene_from_each_row(void) { printf("Test 16: Scene trigger from each row... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); // Trigger scene from each row for (int row = 0; row < 8; row++) { Action action = { .type = ACTION_TRIGGER_SCENE, .data.trigger_scene.scene_index = row }; - state = reducer(state, action); + reducer(state, action); // Verify all clips in this scene are recording for (int ch = 0; ch < MAX_CHANNELS; ch++) { int clip_idx = CLIP_INDEX(row, 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"); } // Test 17: Quantize mode cycle through all modes void test_quantize_full_cycle(void) { printf("Test 17: Quantize mode full cycle... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); // Cycle through all modes twice for (int cycle = 0; cycle < 2; cycle++) { Action action = { .type = ACTION_SET_QUANTIZE_MODE, .data.set_quantize_mode.mode = QUANTIZE_OFF }; - state = reducer(state, action); - assert(state.quantize_mode == QUANTIZE_OFF); + reducer(state, action); + assert(state->quantize_mode == QUANTIZE_OFF); action.data.set_quantize_mode.mode = QUANTIZE_BEAT; - state = reducer(state, action); - assert(state.quantize_mode == QUANTIZE_BEAT); + reducer(state, action); + assert(state->quantize_mode == QUANTIZE_BEAT); action.data.set_quantize_mode.mode = QUANTIZE_BAR; - state = reducer(state, action); - assert(state.quantize_mode == QUANTIZE_BAR); + reducer(state, action); + assert(state->quantize_mode == QUANTIZE_BAR); } - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } // Test 18: Multiple threshold toggles void test_multiple_threshold_toggles(void) { printf("Test 18: Multiple threshold toggles... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); // Toggle threshold multiple times for (int i = 0; i < 5; i++) { - if (state.quantize_threshold == 0) { + if (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); + reducer(state, action); + assert(state->quantize_threshold == 1000); } else { Action action = { .type = ACTION_SET_QUANTIZE_THRESHOLD, .data.set_quantize_threshold.threshold = 0 }; - state = reducer(state, action); - assert(state.quantize_threshold == 0); + reducer(state, action); + assert(state->quantize_threshold == 0); } } - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } // Test 19: Transport reset multiple times void test_multiple_transport_resets(void) { printf("Test 19: Multiple transport resets... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); // Reset transport multiple times for (int i = 0; i < 5; i++) { - state.transport_state = TRANSPORT_PLAYING; - state.clock_count = 100 + i; - state.beat_position = i % 4; - state.bar_position = i; - state.sample_position = 10000 * i; + state->transport_state = TRANSPORT_PLAYING; + state->clock_count = 100 + i; + state->beat_position = i % 4; + state->bar_position = i; + state->sample_position = 10000 * i; Action action = { .type = ACTION_TRANSPORT_STOP }; - state = reducer(state, action); + 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"); } @@ -746,107 +747,107 @@ void test_move_mode_escape(void) { // Test 36: Delete (reset) single clip void test_delete_single_clip(void) { printf("Test 36: Delete (reset) single clip... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); // Set up a looping clip int clip_idx = 10; - state.clips[clip_idx].state = CLIP_LOOPING; - state.clips[clip_idx].buffer_size = 100; - state.clips[clip_idx].write_position = 100; - state.clips[clip_idx].read_position = 50; + state->clips[clip_idx].state = CLIP_LOOPING; + state->clips[clip_idx].buffer_size = 100; + state->clips[clip_idx].write_position = 100; + state->clips[clip_idx].read_position = 50; // Simulate pressing 'd' on selected clip Action action = { .type = ACTION_RESET_CLIP, .data.reset_clip.clip_index = clip_idx }; - state = reducer(state, action); + reducer(state, action); - assert(state.clips[clip_idx].state == CLIP_EMPTY); - assert(state.clips[clip_idx].buffer_size == 0); - assert(state.clips[clip_idx].write_position == 0); - assert(state.clips[clip_idx].read_position == 0); + assert(state->clips[clip_idx].state == CLIP_EMPTY); + assert(state->clips[clip_idx].buffer_size == 0); + assert(state->clips[clip_idx].write_position == 0); + assert(state->clips[clip_idx].read_position == 0); - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } // Test 37: Delete (reset) multiple clips via visual selection void test_delete_visual_selection(void) { printf("Test 37: Delete visual selection... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); // Set up clips in a 2x2 selection area int clips[] = {18, 19, 26, 27}; // rows 2-3, cols 2-3 for (int i = 0; i < 4; i++) { - state.clips[clips[i]].state = CLIP_LOOPING; - state.clips[clips[i]].buffer_size = 100; - state.clips[clips[i]].write_position = 100; - state.clips[clips[i]].read_position = 50; + state->clips[clips[i]].state = CLIP_LOOPING; + state->clips[clips[i]].buffer_size = 100; + state->clips[clips[i]].write_position = 100; + state->clips[clips[i]].read_position = 50; } // Simulate deleting the selection for (int i = 0; i < 4; i++) { Action action = { .type = ACTION_RESET_CLIP, .data.reset_clip.clip_index = clips[i] }; - state = reducer(state, action); + reducer(state, action); } for (int i = 0; i < 4; i++) { - assert(state.clips[clips[i]].state == CLIP_EMPTY); - assert(state.clips[clips[i]].buffer_size == 0); + assert(state->clips[clips[i]].state == CLIP_EMPTY); + assert(state->clips[clips[i]].buffer_size == 0); } - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } // Test 38: Yank single clip void test_yank_single_clip(void) { printf("Test 38: Yank single clip... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); // Set up a clip int clip_idx = 15; - state.clips[clip_idx].state = CLIP_LOOPING; - state.clips[clip_idx].buffer_size = 100; + state->clips[clip_idx].state = CLIP_LOOPING; + state->clips[clip_idx].buffer_size = 100; // Simulate yanking the clip - should stop it (looping -> stopped) Action action = { .type = ACTION_TRIGGER_CLIP, .data.trigger_clip.clip_index = clip_idx }; - state = reducer(state, action); + reducer(state, action); - assert(state.clips[clip_idx].state == CLIP_STOPPED); + assert(state->clips[clip_idx].state == CLIP_STOPPED); - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } // Test 39: Yank multiple clips via visual selection void test_yank_visual_selection(void) { printf("Test 39: Yank visual selection... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); // Set up clips in a 2x2 selection int clips[] = {18, 19, 26, 27}; for (int i = 0; i < 4; i++) { - state.clips[clips[i]].state = CLIP_LOOPING; - state.clips[clips[i]].buffer_size = 100; + state->clips[clips[i]].state = CLIP_LOOPING; + state->clips[clips[i]].buffer_size = 100; } // Simulate yanking the selection - should stop all clips for (int i = 0; i < 4; i++) { Action action = { .type = ACTION_TRIGGER_CLIP, .data.trigger_clip.clip_index = clips[i] }; - state = reducer(state, action); + reducer(state, action); } for (int i = 0; i < 4; i++) { - assert(state.clips[clips[i]].state == CLIP_STOPPED); + assert(state->clips[clips[i]].state == CLIP_STOPPED); } - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } // Test 40: Paste clips void test_paste_clips(void) { printf("Test 40: Paste clips... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); // Simulate yanking clip at position (1, 1) = clip 9 int yank_buffer[] = {9}; @@ -873,19 +874,19 @@ void test_paste_clips(void) { assert(new_clip_idx == 27); Action action = { .type = ACTION_TRIGGER_CLIP, .data.trigger_clip.clip_index = new_clip_idx }; - state = reducer(state, action); - state = reducer(state, action); - state = reducer(state, action); - assert(state.clips[27].state == CLIP_STOPPED); + reducer(state, action); + reducer(state, action); + reducer(state, action); + assert(state->clips[27].state == CLIP_STOPPED); - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } // Test 41: Paste with bounds checking void test_paste_bounds_checking(void) { printf("Test 41: Paste bounds checking... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); // Yank clip at position (7, 7) = clip 63 (bottom-right) int yank_buffer[] = {63}; @@ -918,7 +919,7 @@ void test_paste_bounds_checking(void) { new_col = 0 + col_offset; assert(new_row >= 0 && new_row < 8 && new_col >= 0 && new_col < 8); - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } @@ -1001,55 +1002,55 @@ void test_go_to_unset_mark(void) { // Test 45: Play next scene void test_play_next_scene(void) { printf("Test 45: Play next scene... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); int selected_row = 3; // Play next scene int next_row = (selected_row + 1) % 8; Action action = { .type = ACTION_TRIGGER_SCENE, .data.trigger_scene.scene_index = next_row }; - state = reducer(state, action); + reducer(state, action); assert(next_row == 4); // Verify clips in scene 4 are recording for (int ch = 0; ch < MAX_CHANNELS; ch++) { int clip_idx = CLIP_INDEX(4, 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"); } // Test 46: Play previous scene void test_play_prev_scene(void) { printf("Test 46: Play previous scene... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); int selected_row = 3; // Play previous scene int prev_row = (selected_row - 1 + 8) % 8; Action action = { .type = ACTION_TRIGGER_SCENE, .data.trigger_scene.scene_index = prev_row }; - state = reducer(state, action); + reducer(state, action); assert(prev_row == 2); // Verify clips in scene 2 are recording for (int ch = 0; ch < MAX_CHANNELS; ch++) { int clip_idx = CLIP_INDEX(2, 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"); } // Test 47: Play next scene wraps around void test_play_next_scene_wrap(void) { printf("Test 47: Play next scene wraps around... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); int selected_row = 7; // Last row @@ -1058,21 +1059,21 @@ void test_play_next_scene_wrap(void) { assert(next_row == 0); Action action = { .type = ACTION_TRIGGER_SCENE, .data.trigger_scene.scene_index = next_row }; - state = reducer(state, action); + 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"); } // Test 48: Play previous scene wraps around void test_play_prev_scene_wrap(void) { printf("Test 48: Play previous scene wraps around... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); int selected_row = 0; // First row @@ -1081,76 +1082,76 @@ void test_play_prev_scene_wrap(void) { assert(prev_row == 7); Action action = { .type = ACTION_TRIGGER_SCENE, .data.trigger_scene.scene_index = prev_row }; - state = reducer(state, action); + reducer(state, action); for (int ch = 0; ch < MAX_CHANNELS; ch++) { int clip_idx = CLIP_INDEX(7, 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"); } // Test 49: Visual mode delete then escape void test_visual_delete_then_escape(void) { printf("Test 49: Visual mode delete then escape... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); // Set up clips in visual selection int clips[] = {18, 19, 26, 27}; for (int i = 0; i < 4; i++) { - state.clips[clips[i]].state = CLIP_LOOPING; - state.clips[clips[i]].buffer_size = 100; + state->clips[clips[i]].state = CLIP_LOOPING; + state->clips[clips[i]].buffer_size = 100; } // Simulate visual mode delete for (int i = 0; i < 4; i++) { Action action = { .type = ACTION_RESET_CLIP, .data.reset_clip.clip_index = clips[i] }; - state = reducer(state, action); + reducer(state, action); } // Verify clips are reset for (int i = 0; i < 4; i++) { - assert(state.clips[clips[i]].state == CLIP_EMPTY); + assert(state->clips[clips[i]].state == CLIP_EMPTY); } // Simulate returning to normal mode int current_mode = 0; // MODE_NORMAL assert(current_mode == 0); - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } // Test 50: Visual mode yank then escape void test_visual_yank_then_escape(void) { printf("Test 50: Visual mode yank then escape... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); // Set up clips in visual selection int clips[] = {18, 19, 26, 27}; for (int i = 0; i < 4; i++) { - state.clips[clips[i]].state = CLIP_LOOPING; - state.clips[clips[i]].buffer_size = 100; + state->clips[clips[i]].state = CLIP_LOOPING; + state->clips[clips[i]].buffer_size = 100; } // Simulate yanking the selection - should stop all clips for (int i = 0; i < 4; i++) { Action action = { .type = ACTION_TRIGGER_CLIP, .data.trigger_clip.clip_index = clips[i] }; - state = reducer(state, action); + reducer(state, action); } // Verify clips are stopped for (int i = 0; i < 4; i++) { - assert(state.clips[clips[i]].state == CLIP_STOPPED); + assert(state->clips[clips[i]].state == CLIP_STOPPED); } // Simulate returning to normal mode int current_mode = 0; // MODE_NORMAL assert(current_mode == 0); - destroy_test_state(&state); + destroy_test_state(state); printf("PASSED\n"); } @@ -1205,257 +1206,257 @@ void test_remark_existing_mark(void) { // Test 53: Undo single clip trigger void test_undo_single_trigger(void) { printf("Test 53: Undo single clip trigger... "); - AppState state = create_test_state();; + AppState *state = create_test_state(); // Start with empty clip int clip_idx = 10; - assert(state.clips[clip_idx].state == CLIP_EMPTY); + assert(state->clips[clip_idx].state == CLIP_EMPTY); // Trigger clip (empty -> recording) Action action = { .type = ACTION_TRIGGER_CLIP, .data.trigger_clip.clip_index = clip_idx }; - state = reducer(state, action); - assert(state.clips[clip_idx].state == CLIP_RECORDING); + reducer(state, action); + assert(state->clips[clip_idx].state == CLIP_RECORDING); // Undo: should go back to empty action.type = ACTION_UNDO; - state = reducer(state, action); - assert(state.clips[clip_idx].state == CLIP_EMPTY); + reducer(state, action); + assert(state->clips[clip_idx].state == CLIP_EMPTY); printf("PASSED\n"); - destroy_test_state(&state); + destroy_test_state(state); } // Test 54: Undo multiple clip triggers void test_undo_multiple_triggers(void) { printf("Test 54: Undo multiple clip triggers... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); int clip1 = 10, clip2 = 11, clip3 = 12; // Trigger three clips Action action = { .type = ACTION_TRIGGER_CLIP, .data.trigger_clip.clip_index = clip1 }; - state = reducer(state, action); - assert(state.clips[clip1].state == CLIP_RECORDING); + reducer(state, action); + assert(state->clips[clip1].state == CLIP_RECORDING); action.data.trigger_clip.clip_index = clip2; - state = reducer(state, action); - assert(state.clips[clip2].state == CLIP_RECORDING); + reducer(state, action); + assert(state->clips[clip2].state == CLIP_RECORDING); action.data.trigger_clip.clip_index = clip3; - state = reducer(state, action); - assert(state.clips[clip3].state == CLIP_RECORDING); + reducer(state, action); + assert(state->clips[clip3].state == CLIP_RECORDING); // Undo last action: clip3 should go back to empty action.type = ACTION_UNDO; - state = reducer(state, action); - assert(state.clips[clip3].state == CLIP_EMPTY); - assert(state.clips[clip2].state == CLIP_RECORDING); - assert(state.clips[clip1].state == CLIP_RECORDING); + reducer(state, action); + assert(state->clips[clip3].state == CLIP_EMPTY); + assert(state->clips[clip2].state == CLIP_RECORDING); + assert(state->clips[clip1].state == CLIP_RECORDING); // Undo again: clip2 should go back to empty - state = reducer(state, action); - assert(state.clips[clip2].state == CLIP_EMPTY); - assert(state.clips[clip1].state == CLIP_RECORDING); + reducer(state, action); + assert(state->clips[clip2].state == CLIP_EMPTY); + assert(state->clips[clip1].state == CLIP_RECORDING); printf("PASSED\n"); - destroy_test_state(&state); + destroy_test_state(state); } // Test 55: Redo single undo void test_redo_single_undo(void) { printf("Test 55: Redo single undo... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); int clip_idx = 10; // Trigger clip Action action = { .type = ACTION_TRIGGER_CLIP, .data.trigger_clip.clip_index = clip_idx }; - state = reducer(state, action); - assert(state.clips[clip_idx].state == CLIP_RECORDING); + reducer(state, action); + assert(state->clips[clip_idx].state == CLIP_RECORDING); // Undo action.type = ACTION_UNDO; - state = reducer(state, action); - assert(state.clips[clip_idx].state == CLIP_EMPTY); + reducer(state, action); + assert(state->clips[clip_idx].state == CLIP_EMPTY); // Redo: should go back to recording action.type = ACTION_REDO; - state = reducer(state, action); - assert(state.clips[clip_idx].state == CLIP_RECORDING); + reducer(state, action); + assert(state->clips[clip_idx].state == CLIP_RECORDING); printf("PASSED\n"); - destroy_test_state(&state); + destroy_test_state(state); } // Test 56: Redo after multiple undos void test_redo_multiple_undos(void) { printf("Test 56: Redo after multiple undos... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); int clip1 = 10, clip2 = 11; // Trigger two clips Action action = { .type = ACTION_TRIGGER_CLIP, .data.trigger_clip.clip_index = clip1 }; - state = reducer(state, action); + reducer(state, action); action.data.trigger_clip.clip_index = clip2; - state = reducer(state, action); - assert(state.clips[clip1].state == CLIP_RECORDING); - assert(state.clips[clip2].state == CLIP_RECORDING); + reducer(state, action); + assert(state->clips[clip1].state == CLIP_RECORDING); + assert(state->clips[clip2].state == CLIP_RECORDING); // Undo twice action.type = ACTION_UNDO; - state = reducer(state, action); - state = reducer(state, action); - assert(state.clips[clip1].state == CLIP_EMPTY); - assert(state.clips[clip2].state == CLIP_EMPTY); + reducer(state, action); + reducer(state, action); + assert(state->clips[clip1].state == CLIP_EMPTY); + assert(state->clips[clip2].state == CLIP_EMPTY); // Redo twice action.type = ACTION_REDO; - state = reducer(state, action); - assert(state.clips[clip1].state == CLIP_RECORDING); - assert(state.clips[clip2].state == CLIP_EMPTY); + reducer(state, action); + assert(state->clips[clip1].state == CLIP_RECORDING); + assert(state->clips[clip2].state == CLIP_EMPTY); - state = reducer(state, action); - assert(state.clips[clip1].state == CLIP_RECORDING); - assert(state.clips[clip2].state == CLIP_RECORDING); + reducer(state, action); + assert(state->clips[clip1].state == CLIP_RECORDING); + assert(state->clips[clip2].state == CLIP_RECORDING); printf("PASSED\n"); - destroy_test_state(&state); + destroy_test_state(state); } // Test 57: Undo scene trigger void test_undo_scene_trigger(void) { printf("Test 57: Undo scene trigger... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); int scene_idx = 3; // Trigger scene Action action = { .type = ACTION_TRIGGER_SCENE, .data.trigger_scene.scene_index = scene_idx }; - state = reducer(state, action); + reducer(state, action); // All clips in scene should be recording for (int ch = 0; ch < MAX_CHANNELS; ch++) { int clip_idx = CLIP_INDEX(scene_idx, ch); - assert(state.clips[clip_idx].state == CLIP_RECORDING); + assert(state->clips[clip_idx].state == CLIP_RECORDING); } // Undo: all clips should go back to empty action.type = ACTION_UNDO; - state = reducer(state, action); + reducer(state, action); for (int ch = 0; ch < MAX_CHANNELS; ch++) { int clip_idx = CLIP_INDEX(scene_idx, ch); - assert(state.clips[clip_idx].state == CLIP_EMPTY); + assert(state->clips[clip_idx].state == CLIP_EMPTY); } printf("PASSED\n"); - destroy_test_state(&state); + destroy_test_state(state); } // Test 58: Undo clip state cycle (empty -> recording -> looping -> stopped) void test_undo_clip_state_cycle(void) { printf("Test 58: Undo clip state cycle... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); int clip_idx = 10; // Cycle through all states Action action = { .type = ACTION_TRIGGER_CLIP, .data.trigger_clip.clip_index = clip_idx }; - state = reducer(state, action); // empty -> recording - assert(state.clips[clip_idx].state == CLIP_RECORDING); + reducer(state, action); // empty -> recording + assert(state->clips[clip_idx].state == CLIP_RECORDING); - state = reducer(state, action); // recording -> looping - assert(state.clips[clip_idx].state == CLIP_LOOPING); + reducer(state, action); // recording -> looping + assert(state->clips[clip_idx].state == CLIP_LOOPING); - state = reducer(state, action); // looping -> stopped - assert(state.clips[clip_idx].state == CLIP_STOPPED); + reducer(state, action); // looping -> stopped + assert(state->clips[clip_idx].state == CLIP_STOPPED); // Undo three times to go back to empty action.type = ACTION_UNDO; - state = reducer(state, action); // stopped -> looping - assert(state.clips[clip_idx].state == CLIP_LOOPING); + reducer(state, action); // stopped -> looping + assert(state->clips[clip_idx].state == CLIP_LOOPING); - state = reducer(state, action); // looping -> recording - assert(state.clips[clip_idx].state == CLIP_RECORDING); + reducer(state, action); // looping -> recording + assert(state->clips[clip_idx].state == CLIP_RECORDING); - state = reducer(state, action); // recording -> empty - assert(state.clips[clip_idx].state == CLIP_EMPTY); + reducer(state, action); // recording -> empty + assert(state->clips[clip_idx].state == CLIP_EMPTY); printf("PASSED\n"); - destroy_test_state(&state); + destroy_test_state(state); } // Test 59: Undo after new action clears redo history void test_undo_clears_redo_on_new_action(void) { printf("Test 59: Undo after new action clears redo history... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); int clip1 = 10, clip2 = 11; // Trigger clip1 Action action = { .type = ACTION_TRIGGER_CLIP, .data.trigger_clip.clip_index = clip1 }; - state = reducer(state, action); - assert(state.clips[clip1].state == CLIP_RECORDING); + reducer(state, action); + assert(state->clips[clip1].state == CLIP_RECORDING); // Undo action.type = ACTION_UNDO; - state = reducer(state, action); - assert(state.clips[clip1].state == CLIP_EMPTY); + reducer(state, action); + assert(state->clips[clip1].state == CLIP_EMPTY); // Redo should work action.type = ACTION_REDO; - state = reducer(state, action); - assert(state.clips[clip1].state == CLIP_RECORDING); + reducer(state, action); + assert(state->clips[clip1].state == CLIP_RECORDING); // Undo again action.type = ACTION_UNDO; - state = reducer(state, action); - assert(state.clips[clip1].state == CLIP_EMPTY); + reducer(state, action); + assert(state->clips[clip1].state == CLIP_EMPTY); // Now do a new action (trigger clip2) action.type = ACTION_TRIGGER_CLIP; action.data.trigger_clip.clip_index = clip2; - state = reducer(state, action); - assert(state.clips[clip2].state == CLIP_RECORDING); + reducer(state, action); + assert(state->clips[clip2].state == CLIP_RECORDING); // Redo should NOT work now (redo history cleared) action.type = ACTION_REDO; - state = reducer(state, action); - assert(state.clips[clip1].state == CLIP_EMPTY); // Should still be empty + reducer(state, action); + assert(state->clips[clip1].state == CLIP_EMPTY); // Should still be empty printf("PASSED\n"); - destroy_test_state(&state); + destroy_test_state(state); } // Test 60: Undo reset clip void test_undo_reset_clip(void) { printf("Test 60: Undo reset clip... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); int clip_idx = 10; // Set up a looping clip with data - state.clips[clip_idx].state = CLIP_LOOPING; - state.clips[clip_idx].buffer_size = 100; - state.clips[clip_idx].write_position = 100; - state.clips[clip_idx].read_position = 50; + state->clips[clip_idx].state = CLIP_LOOPING; + state->clips[clip_idx].buffer_size = 100; + state->clips[clip_idx].write_position = 100; + state->clips[clip_idx].read_position = 50; // Reset the clip Action action = { .type = ACTION_RESET_CLIP, .data.reset_clip.clip_index = clip_idx }; - state = reducer(state, action); - assert(state.clips[clip_idx].state == CLIP_EMPTY); - assert(state.clips[clip_idx].buffer_size == 0); + reducer(state, action); + assert(state->clips[clip_idx].state == CLIP_EMPTY); + assert(state->clips[clip_idx].buffer_size == 0); // Undo: should restore clip to previous state action.type = ACTION_UNDO; - state = reducer(state, action); - assert(state.clips[clip_idx].state == CLIP_LOOPING); - assert(state.clips[clip_idx].buffer_size == 100); - assert(state.clips[clip_idx].write_position == 100); - assert(state.clips[clip_idx].read_position == 50); + reducer(state, action); + assert(state->clips[clip_idx].state == CLIP_LOOPING); + assert(state->clips[clip_idx].buffer_size == 100); + assert(state->clips[clip_idx].write_position == 100); + assert(state->clips[clip_idx].read_position == 50); printf("PASSED\n"); - destroy_test_state(&state); + destroy_test_state(state); } @@ -1464,43 +1465,43 @@ void test_undo_reset_clip(void) { // Test 64: Undo/redo with paste operation void test_undo_paste(void) { printf("Test 64: Undo paste operation... "); - AppState state = create_test_state(); + AppState *state = create_test_state(); int clip_idx = 27; // Simulate paste: trigger clip three times to go empty -> recording -> looping -> stopped Action action = { .type = ACTION_TRIGGER_CLIP, .data.trigger_clip.clip_index = clip_idx }; - state = reducer(state, action); - state = reducer(state, action); - state = reducer(state, action); - assert(state.clips[clip_idx].state == CLIP_STOPPED); + reducer(state, action); + reducer(state, action); + reducer(state, action); + assert(state->clips[clip_idx].state == CLIP_STOPPED); // Undo: should go back to looping (undo last trigger: stopped -> looping) action.type = ACTION_UNDO; - state = reducer(state, action); - assert(state.clips[clip_idx].state == CLIP_LOOPING); + reducer(state, action); + assert(state->clips[clip_idx].state == CLIP_LOOPING); // Undo again: should go back to recording - state = reducer(state, action); - assert(state.clips[clip_idx].state == CLIP_RECORDING); + reducer(state, action); + assert(state->clips[clip_idx].state == CLIP_RECORDING); // Undo again: should go back to empty - state = reducer(state, action); - assert(state.clips[clip_idx].state == CLIP_EMPTY); + reducer(state, action); + assert(state->clips[clip_idx].state == CLIP_EMPTY); // Redo three times: should go back to stopped action.type = ACTION_REDO; - state = reducer(state, action); - assert(state.clips[clip_idx].state == CLIP_RECORDING); + reducer(state, action); + assert(state->clips[clip_idx].state == CLIP_RECORDING); - state = reducer(state, action); - assert(state.clips[clip_idx].state == CLIP_LOOPING); + reducer(state, action); + assert(state->clips[clip_idx].state == CLIP_LOOPING); - state = reducer(state, action); - assert(state.clips[clip_idx].state == CLIP_STOPPED); + reducer(state, action); + assert(state->clips[clip_idx].state == CLIP_STOPPED); printf("PASSED\n"); - destroy_test_state(&state); + destroy_test_state(state); } int main(void) {