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:
315
test_engine.c
315
test_engine.c
@@ -696,6 +696,307 @@ void test_quit_action(void) {
|
|||||||
printf("PASSED\n");
|
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
|
// Main
|
||||||
// ============================================================
|
// ============================================================
|
||||||
@@ -731,6 +1032,20 @@ int main(void) {
|
|||||||
test_save_load_clip();
|
test_save_load_clip();
|
||||||
test_quit_action();
|
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");
|
printf("\nAll tests passed!\n");
|
||||||
|
exit(0);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user