test: add tests for vim-like visual mode, move mode, yank/paste, marks, and scene navigation

Co-authored-by: aider (deepseek/deepseek-coder) <aider@aider.chat>
This commit is contained in:
Loic Coenen
2026-05-01 19:20:08 +00:00
parent 2c4129f640
commit e6d5d3d0fb

View File

@@ -595,6 +595,618 @@ void test_command_mode_colon(void) {
printf("PASSED\n");
}
// Test 29: Visual mode entry
void test_visual_mode_entry(void) {
printf("Test 29: Visual mode entry... ");
// Simulate pressing 'v' to enter visual mode
UIMode current_mode = MODE_NORMAL;
int selected_row = 3, selected_col = 4;
int visual_start_row = 0, visual_start_col = 0;
int visual_end_row = 0, visual_end_col = 0;
// Press 'v'
current_mode = MODE_VISUAL;
visual_start_row = selected_row;
visual_start_col = selected_col;
visual_end_row = selected_row;
visual_end_col = selected_col;
assert(current_mode == MODE_VISUAL);
assert(visual_start_row == 3);
assert(visual_start_col == 4);
assert(visual_end_row == 3);
assert(visual_end_col == 4);
printf("PASSED\n");
}
// Test 30: Visual mode selection expansion
void test_visual_mode_selection(void) {
printf("Test 30: Visual mode selection expansion... ");
int visual_start_row = 2, visual_start_col = 2;
int visual_end_row = 2, visual_end_col = 2;
// Move right
visual_end_col = (visual_end_col + 1) % 8;
assert(visual_end_col == 3);
// Move down
visual_end_row = (visual_end_row + 1) % 8;
assert(visual_end_row == 3);
// Check selection bounds
int min_row = (visual_start_row < visual_end_row) ? visual_start_row : visual_end_row;
int max_row = (visual_start_row > visual_end_row) ? visual_start_row : visual_end_row;
int min_col = (visual_start_col < visual_end_col) ? visual_start_col : visual_end_col;
int max_col = (visual_start_col > visual_end_col) ? visual_start_col : visual_end_col;
assert(min_row == 2);
assert(max_row == 3);
assert(min_col == 2);
assert(max_col == 3);
printf("PASSED\n");
}
// Test 31: Visual mode escape returns to normal
void test_visual_mode_escape(void) {
printf("Test 31: Visual mode escape returns to normal... ");
UIMode current_mode = MODE_VISUAL;
// Press Escape
current_mode = MODE_NORMAL;
assert(current_mode == MODE_NORMAL);
printf("PASSED\n");
}
// Test 32: Visual line selection
void test_visual_line_selection(void) {
printf("Test 32: Visual line selection... ");
int selected_row = 3;
UIMode current_mode = MODE_NORMAL;
int visual_start_row = 0, visual_start_col = 0;
int visual_end_row = 0, visual_end_col = 0;
// Press 'V'
current_mode = MODE_VISUAL;
visual_start_row = selected_row;
visual_start_col = 0;
visual_end_row = selected_row;
visual_end_col = 7; // GRID_COLS - 1
assert(current_mode == MODE_VISUAL);
assert(visual_start_row == 3);
assert(visual_start_col == 0);
assert(visual_end_row == 3);
assert(visual_end_col == 7);
printf("PASSED\n");
}
// Test 33: Move mode entry and navigation
void test_move_mode_navigation(void) {
printf("Test 33: Move mode navigation... ");
UIMode current_mode = MODE_NORMAL;
int selected_row = 3, selected_col = 4;
// Enter move mode
current_mode = MODE_MOVE;
assert(current_mode == MODE_MOVE);
// Move left
selected_col = (selected_col - 1 + 8) % 8;
assert(selected_col == 3);
// Move down
selected_row = (selected_row + 1) % 8;
assert(selected_row == 4);
// Move up
selected_row = (selected_row - 1 + 8) % 8;
assert(selected_row == 3);
// Move right
selected_col = (selected_col + 1) % 8;
assert(selected_col == 4);
printf("PASSED\n");
}
// Test 34: Move mode returns to normal on enter
void test_move_mode_enter(void) {
printf("Test 34: Move mode returns to normal on enter... ");
UIMode current_mode = MODE_MOVE;
// Press Enter
current_mode = MODE_NORMAL;
assert(current_mode == MODE_NORMAL);
printf("PASSED\n");
}
// Test 35: Move mode returns to normal on escape
void test_move_mode_escape(void) {
printf("Test 35: Move mode returns to normal on escape... ");
UIMode current_mode = MODE_MOVE;
// Press Escape
current_mode = MODE_NORMAL;
assert(current_mode == MODE_NORMAL);
printf("PASSED\n");
}
// Test 36: Delete (reset) single clip
void test_delete_single_clip(void) {
printf("Test 36: Delete (reset) single clip... ");
Engine *engine = create_test_engine();
// Set up a looping clip
int clip_idx = 10;
engine->clips[clip_idx].state = CLIP_LOOPING;
engine->clips[clip_idx].buffer_size = 100;
engine->clips[clip_idx].write_position = 100;
engine->clips[clip_idx].read_position = 50;
// Simulate pressing 'd' on selected clip
engine_reset_clip(engine, clip_idx);
engine_process_commands(engine);
assert(engine->clips[clip_idx].state == CLIP_EMPTY);
assert(engine->clips[clip_idx].buffer_size == 0);
assert(engine->clips[clip_idx].write_position == 0);
assert(engine->clips[clip_idx].read_position == 0);
destroy_test_engine(engine);
printf("PASSED\n");
}
// Test 37: Delete (reset) multiple clips via visual selection
void test_delete_visual_selection(void) {
printf("Test 37: Delete visual selection... ");
Engine *engine = create_test_engine();
// 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++) {
engine->clips[clips[i]].state = CLIP_LOOPING;
engine->clips[clips[i]].buffer_size = 100;
engine->clips[clips[i]].write_position = 100;
engine->clips[clips[i]].read_position = 50;
}
// Simulate deleting the selection
for (int i = 0; i < 4; i++) {
engine_reset_clip(engine, clips[i]);
}
engine_process_commands(engine);
for (int i = 0; i < 4; i++) {
assert(engine->clips[clips[i]].state == CLIP_EMPTY);
assert(engine->clips[clips[i]].buffer_size == 0);
}
destroy_test_engine(engine);
printf("PASSED\n");
}
// Test 38: Yank single clip
void test_yank_single_clip(void) {
printf("Test 38: Yank single clip... ");
Engine *engine = create_test_engine();
// Set up a clip
int clip_idx = 15;
engine->clips[clip_idx].state = CLIP_LOOPING;
engine->clips[clip_idx].buffer_size = 100;
// Simulate yanking the clip
int yank_buffer[1] = {clip_idx};
int yank_count = 1;
assert(yank_buffer[0] == 15);
assert(yank_count == 1);
destroy_test_engine(engine);
printf("PASSED\n");
}
// Test 39: Yank multiple clips via visual selection
void test_yank_visual_selection(void) {
printf("Test 39: Yank visual selection... ");
Engine *engine = create_test_engine();
// Set up clips in a 2x2 selection
int clips[] = {18, 19, 26, 27};
for (int i = 0; i < 4; i++) {
engine->clips[clips[i]].state = CLIP_LOOPING;
engine->clips[clips[i]].buffer_size = 100;
}
// Simulate yanking the selection
int yank_buffer[4];
int yank_count = 4;
for (int i = 0; i < 4; i++) {
yank_buffer[i] = clips[i];
}
assert(yank_count == 4);
assert(yank_buffer[0] == 18);
assert(yank_buffer[1] == 19);
assert(yank_buffer[2] == 26);
assert(yank_buffer[3] == 27);
destroy_test_engine(engine);
printf("PASSED\n");
}
// Test 40: Paste clips
void test_paste_clips(void) {
printf("Test 40: Paste clips... ");
Engine *engine = create_test_engine();
// Simulate yanking clip at position (1, 1) = clip 9
int yank_buffer[] = {9};
int yank_count = 1;
int selected_row = 3, selected_col = 3; // Paste at position (3, 3) = clip 27
// Calculate offset
int first_yanked_row = yank_buffer[0] / 8;
int first_yanked_col = yank_buffer[0] % 8;
int row_offset = selected_row - first_yanked_row;
int col_offset = selected_col - first_yanked_col;
assert(first_yanked_row == 1);
assert(first_yanked_col == 1);
assert(row_offset == 2);
assert(col_offset == 2);
// Simulate paste: trigger clip at new position
int new_row = first_yanked_row + row_offset;
int new_col = first_yanked_col + col_offset;
int new_clip_idx = new_row * 8 + new_col;
assert(new_row == 3);
assert(new_col == 3);
assert(new_clip_idx == 27);
engine_trigger_clip(engine, new_clip_idx);
engine_process_commands(engine);
assert(engine->clips[27].state == CLIP_RECORDING);
destroy_test_engine(engine);
printf("PASSED\n");
}
// Test 41: Paste with bounds checking
void test_paste_bounds_checking(void) {
printf("Test 41: Paste bounds checking... ");
Engine *engine = create_test_engine();
// Yank clip at position (7, 7) = clip 63 (bottom-right)
int yank_buffer[] = {63};
int yank_count = 1;
int selected_row = 0, selected_col = 0; // Paste at top-left
// Calculate offset
int first_yanked_row = yank_buffer[0] / 8;
int first_yanked_col = yank_buffer[0] % 8;
int row_offset = selected_row - first_yanked_row;
int col_offset = selected_col - first_yanked_col;
assert(row_offset == -7);
assert(col_offset == -7);
// Simulate paste: new position should be (0, 0) = clip 0
int new_row = first_yanked_row + row_offset;
int new_col = first_yanked_col + col_offset;
assert(new_row == 0);
assert(new_col == 0);
// Test out-of-bounds paste (should be clipped)
selected_row = 0;
selected_col = 0;
row_offset = selected_row - 0;
col_offset = selected_col - 0;
// Yank clip at (0, 0) and try to paste at (0, 0) - should work
new_row = 0 + row_offset;
new_col = 0 + col_offset;
assert(new_row >= 0 && new_row < 8 && new_col >= 0 && new_col < 8);
destroy_test_engine(engine);
printf("PASSED\n");
}
// Test 42: Mark setting
void test_mark_setting(void) {
printf("Test 42: Mark setting... ");
int marks[26];
for (int i = 0; i < 26; i++) {
marks[i] = -1;
}
int selected_row = 3, selected_col = 4;
int clip_idx = selected_row * 8 + selected_col;
// Set mark 'a'
char mark_char = 'a';
int idx = mark_char - 'a';
marks[idx] = clip_idx;
assert(marks[0] == 28); // 3 * 8 + 4 = 28
// Set mark 'z'
mark_char = 'z';
idx = mark_char - 'a';
marks[idx] = clip_idx;
assert(marks[25] == 28);
printf("PASSED\n");
}
// Test 43: Go to mark
void test_go_to_mark(void) {
printf("Test 43: Go to mark... ");
int marks[26];
for (int i = 0; i < 26; i++) {
marks[i] = -1;
}
// Set mark 'b' to clip 42
marks[1] = 42; // 'b' - 'a' = 1
// Go to mark 'b'
char mark_char = 'b';
int idx = mark_char - 'a';
int clip_idx = marks[idx];
assert(clip_idx == 42);
// Calculate row and col
int row = clip_idx / 8;
int col = clip_idx % 8;
assert(row == 5);
assert(col == 2);
printf("PASSED\n");
}
// Test 44: Go to unset mark
void test_go_to_unset_mark(void) {
printf("Test 44: Go to unset mark... ");
int marks[26];
for (int i = 0; i < 26; i++) {
marks[i] = -1;
}
// Try to go to unset mark 'c'
char mark_char = 'c';
int idx = mark_char - 'a';
int clip_idx = marks[idx];
assert(clip_idx == -1); // Mark not set
printf("PASSED\n");
}
// Test 45: Play next scene
void test_play_next_scene(void) {
printf("Test 45: Play next scene... ");
Engine *engine = create_test_engine();
int selected_row = 3;
// Play next scene
int next_row = (selected_row + 1) % 8;
engine_trigger_scene(engine, next_row);
engine_process_commands(engine);
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(engine->clips[clip_idx].state == CLIP_RECORDING);
}
destroy_test_engine(engine);
printf("PASSED\n");
}
// Test 46: Play previous scene
void test_play_prev_scene(void) {
printf("Test 46: Play previous scene... ");
Engine *engine = create_test_engine();
int selected_row = 3;
// Play previous scene
int prev_row = (selected_row - 1 + 8) % 8;
engine_trigger_scene(engine, prev_row);
engine_process_commands(engine);
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(engine->clips[clip_idx].state == CLIP_RECORDING);
}
destroy_test_engine(engine);
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... ");
Engine *engine = create_test_engine();
int selected_row = 7; // Last row
// Play next scene should wrap to row 0
int next_row = (selected_row + 1) % 8;
assert(next_row == 0);
engine_trigger_scene(engine, next_row);
engine_process_commands(engine);
for (int ch = 0; ch < MAX_CHANNELS; ch++) {
int clip_idx = CLIP_INDEX(0, ch);
assert(engine->clips[clip_idx].state == CLIP_RECORDING);
}
destroy_test_engine(engine);
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... ");
Engine *engine = create_test_engine();
int selected_row = 0; // First row
// Play previous scene should wrap to row 7
int prev_row = (selected_row - 1 + 8) % 8;
assert(prev_row == 7);
engine_trigger_scene(engine, prev_row);
engine_process_commands(engine);
for (int ch = 0; ch < MAX_CHANNELS; ch++) {
int clip_idx = CLIP_INDEX(7, ch);
assert(engine->clips[clip_idx].state == CLIP_RECORDING);
}
destroy_test_engine(engine);
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... ");
Engine *engine = create_test_engine();
// Set up clips in visual selection
int clips[] = {18, 19, 26, 27};
for (int i = 0; i < 4; i++) {
engine->clips[clips[i]].state = CLIP_LOOPING;
engine->clips[clips[i]].buffer_size = 100;
}
// Simulate visual mode delete
for (int i = 0; i < 4; i++) {
engine_reset_clip(engine, clips[i]);
}
engine_process_commands(engine);
// Verify clips are reset
for (int i = 0; i < 4; i++) {
assert(engine->clips[clips[i]].state == CLIP_EMPTY);
}
// Simulate returning to normal mode
UIMode current_mode = MODE_NORMAL;
assert(current_mode == MODE_NORMAL);
destroy_test_engine(engine);
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... ");
Engine *engine = create_test_engine();
// Set up clips in visual selection
int clips[] = {18, 19, 26, 27};
for (int i = 0; i < 4; i++) {
engine->clips[clips[i]].state = CLIP_LOOPING;
engine->clips[clips[i]].buffer_size = 100;
}
// Simulate yanking the selection
int yank_buffer[4];
int yank_count = 4;
for (int i = 0; i < 4; i++) {
yank_buffer[i] = clips[i];
}
assert(yank_count == 4);
assert(yank_buffer[0] == 18);
assert(yank_buffer[3] == 27);
// Simulate returning to normal mode
UIMode current_mode = MODE_NORMAL;
assert(current_mode == MODE_NORMAL);
destroy_test_engine(engine);
printf("PASSED\n");
}
// Test 51: Multiple marks
void test_multiple_marks(void) {
printf("Test 51: Multiple marks... ");
int marks[26];
for (int i = 0; i < 26; i++) {
marks[i] = -1;
}
// Set multiple marks
marks[0] = 0; // 'a' = clip 0
marks[1] = 63; // 'b' = clip 63
marks[2] = 28; // 'c' = clip 28
assert(marks[0] == 0);
assert(marks[1] == 63);
assert(marks[2] == 28);
// Go to mark 'b'
int clip_idx = marks[1];
int row = clip_idx / 8;
int col = clip_idx % 8;
assert(row == 7);
assert(col == 7);
printf("PASSED\n");
}
// Test 52: Re-mark existing mark
void test_remark_existing_mark(void) {
printf("Test 52: Re-mark existing mark... ");
int marks[26];
for (int i = 0; i < 26; i++) {
marks[i] = -1;
}
// Set mark 'a' to clip 10
marks[0] = 10;
assert(marks[0] == 10);
// Re-mark 'a' to clip 42
marks[0] = 42;
assert(marks[0] == 42);
printf("PASSED\n");
}
int main(void) {
printf("Running TUI tests...\n\n");
@@ -626,6 +1238,30 @@ int main(void) {
test_command_mode_escape();
test_command_mode_enter();
test_command_mode_colon();
test_visual_mode_entry();
test_visual_mode_selection();
test_visual_mode_escape();
test_visual_line_selection();
test_move_mode_navigation();
test_move_mode_enter();
test_move_mode_escape();
test_delete_single_clip();
test_delete_visual_selection();
test_yank_single_clip();
test_yank_visual_selection();
test_paste_clips();
test_paste_bounds_checking();
test_mark_setting();
test_go_to_mark();
test_go_to_unset_mark();
test_play_next_scene();
test_play_prev_scene();
test_play_next_scene_wrap();
test_play_prev_scene_wrap();
test_visual_delete_then_escape();
test_visual_yank_then_escape();
test_multiple_marks();
test_remark_existing_mark();
printf("\nAll TUI tests passed!\n");
return 0;