test: add MIDI grid tests and fix test program hang

Co-authored-by: aider (deepseek/deepseek-coder) <aider@aider.chat>
This commit is contained in:
Loic Coenen
2026-05-03 21:02:31 +00:00
parent b74db49d7f
commit 3158599a99

View File

@@ -696,6 +696,307 @@ void test_quit_action(void) {
printf("PASSED\n");
}
// ============================================================
// Test 29: MIDI clip initial state
// ============================================================
void test_midi_clip_initial_state(void) {
printf("Test 29: MIDI clip initial 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);
}
destroy_test_state(&state);
printf("PASSED\n");
}
// ============================================================
// Test 30: MIDI clip trigger empty starts recording
// ============================================================
void test_midi_clip_trigger_empty_starts_recording(void) {
printf("Test 30: MIDI clip trigger empty starts recording... ");
AppState state = create_test_state();
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);
assert(state.midi_clips[0].event_count == 0);
assert(state.midi_clips[0].read_index == 0);
destroy_test_state(&state);
printf("PASSED\n");
}
// ============================================================
// Test 31: MIDI clip trigger recording starts looping
// ============================================================
void test_midi_clip_trigger_recording_starts_looping(void) {
printf("Test 31: MIDI clip trigger recording starts looping... ");
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);
// Simulate some recorded events
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);
destroy_test_state(&state);
printf("PASSED\n");
}
// ============================================================
// Test 32: MIDI clip trigger looping stops it
// ============================================================
void test_midi_clip_trigger_looping_stops(void) {
printf("Test 32: MIDI clip trigger looping stops it... ");
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;
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);
destroy_test_state(&state);
printf("PASSED\n");
}
// ============================================================
// Test 33: MIDI clip trigger stopped resumes looping
// ============================================================
void test_midi_clip_trigger_stopped_resumes_looping(void) {
printf("Test 33: MIDI clip trigger stopped resumes looping... ");
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;
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);
destroy_test_state(&state);
printf("PASSED\n");
}
// ============================================================
// Test 34: MIDI clip full cycle
// ============================================================
void test_midi_clip_full_cycle(void) {
printf("Test 34: MIDI clip full cycle... ");
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);
// 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);
// Looping -> 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);
destroy_test_state(&state);
printf("PASSED\n");
}
// ============================================================
// Test 35: MIDI clip reset
// ============================================================
void test_midi_clip_reset(void) {
printf("Test 35: MIDI clip reset... ");
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;
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);
// events pointer should still be valid
assert(state.midi_clips[0].events != NULL);
destroy_test_state(&state);
printf("PASSED\n");
}
// ============================================================
// Test 36: MIDI clip multiple clips work independently
// ============================================================
void test_midi_clip_multiple_clips(void) {
printf("Test 36: MIDI clip multiple clips work independently... ");
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);
// Clip 1: Empty -> 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
// Clip 2: Empty -> Recording
state = reducer(state, action2);
assert(state.midi_clips[2].state == CLIP_RECORDING);
destroy_test_state(&state);
printf("PASSED\n");
}
// ============================================================
// Test 37: MIDI clip invalid index
// ============================================================
void test_midi_clip_invalid_index(void) {
printf("Test 37: MIDI clip invalid index... ");
AppState state = create_test_state();
// These should not crash
Action action_neg = { .type = ACTION_MIDI_CLIP_TRIGGER, .data.midi_clip_trigger = { .clip_index = -1 } };
Action action_max = { .type = ACTION_MIDI_CLIP_TRIGGER, .data.midi_clip_trigger = { .clip_index = MAX_CLIPS } };
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);
// Verify no state changes
assert(state.midi_clips[0].state == CLIP_EMPTY);
destroy_test_state(&state);
printf("PASSED\n");
}
// ============================================================
// Test 38: MIDI clip show_midi_grid toggle
// ============================================================
void test_midi_clip_show_grid_toggle(void) {
printf("Test 38: MIDI clip show_midi_grid toggle... ");
AppState state = create_test_state();
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);
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);
destroy_test_state(&state);
printf("PASSED\n");
}
// ============================================================
// Test 39: MIDI clip events pointer persists after reset
// ============================================================
void test_midi_clip_events_persist_after_reset(void) {
printf("Test 39: MIDI clip events pointer persists after reset... ");
AppState state = create_test_state();
// Get the original events pointer
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;
// Reset the clip
Action action = { .type = ACTION_MIDI_CLIP_RESET, .data.midi_clip_reset = { .clip_index = 0 } };
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);
destroy_test_state(&state);
printf("PASSED\n");
}
// ============================================================
// Test 40: MIDI clip trigger with events in buffer
// ============================================================
void test_midi_clip_trigger_with_events(void) {
printf("Test 40: MIDI clip trigger with events in buffer... ");
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;
// Trigger to resume looping
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].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);
destroy_test_state(&state);
printf("PASSED\n");
}
// ============================================================
// Main
// ============================================================
@@ -731,6 +1032,20 @@ int main(void) {
test_save_load_clip();
test_quit_action();
test_midi_clip_initial_state();
test_midi_clip_trigger_empty_starts_recording();
test_midi_clip_trigger_recording_starts_looping();
test_midi_clip_trigger_looping_stops();
test_midi_clip_trigger_stopped_resumes_looping();
test_midi_clip_full_cycle();
test_midi_clip_reset();
test_midi_clip_multiple_clips();
test_midi_clip_invalid_index();
test_midi_clip_show_grid_toggle();
test_midi_clip_events_persist_after_reset();
test_midi_clip_trigger_with_events();
printf("\nAll tests passed!\n");
exit(0);
return 0;
}