From 5618f00c70731c5eb0942657469c25186abda00a Mon Sep 17 00:00:00 2001 From: NTBBloodbath Date: Thu, 28 Aug 2025 14:43:58 -0400 Subject: [PATCH] refactor: move tests to their respective source code files. Resolves #2 --- README.md | 2 +- build.zig | 28 ++++++-- src/config.zig | 15 ++++ src/editor.zig | 164 +++++++++++++++++++++++++++++++++++++++++++ src/syntax.zig | 9 +++ src/test.zig | 187 ------------------------------------------------- 6 files changed, 213 insertions(+), 192 deletions(-) delete mode 100644 src/test.zig diff --git a/README.md b/README.md index c4491cb..c9f7459 100644 --- a/README.md +++ b/README.md @@ -118,7 +118,7 @@ zig build ./zig-out/bin/koa ``` -## Key Bindings (Current) +## Key Bindings ### Normal Mode - `hjkl` - Movement diff --git a/build.zig b/build.zig index a7a48ad..fe322e9 100644 --- a/build.zig +++ b/build.zig @@ -32,12 +32,32 @@ pub fn build(b: *std.Build) void { const run_step = b.step("run", "Run the app"); run_step.dependOn(&run_cmd.step); - const exe_unit_tests = b.addTest(.{ - .root_module = exe_mod, + // Create tests for each module + const editor_tests = b.addTest(.{ + .root_source_file = b.path("src/editor.zig"), + .target = target, + .optimize = optimize, + }); + editor_tests.root_module.addImport("vaxis", vaxis.module("vaxis")); + + const syntax_tests = b.addTest(.{ + .root_source_file = b.path("src/syntax.zig"), + .target = target, + .optimize = optimize, + }); + + const config_tests = b.addTest(.{ + .root_source_file = b.path("src/config.zig"), + .target = target, + .optimize = optimize, }); - const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests); + const run_editor_tests = b.addRunArtifact(editor_tests); + const run_syntax_tests = b.addRunArtifact(syntax_tests); + const run_config_tests = b.addRunArtifact(config_tests); const test_step = b.step("test", "Run unit tests"); - test_step.dependOn(&run_exe_unit_tests.step); + test_step.dependOn(&run_editor_tests.step); + test_step.dependOn(&run_syntax_tests.step); + test_step.dependOn(&run_config_tests.step); } diff --git a/src/config.zig b/src/config.zig index af2e7be..d2eb7c4 100644 --- a/src/config.zig +++ b/src/config.zig @@ -116,3 +116,18 @@ pub const Colors = struct { // visual_mode: std.HashMap(Key, Action), // command_mode: std.HashMap(Key, Action), // }; + +const testing = std.testing; + +test "config default values" { + const cfg = Config.default(); + try testing.expect(cfg.show_line_numbers == true); + try testing.expect(cfg.tab_width == 4); + try testing.expect(cfg.use_spaces == true); +} + +test "color scheme colors" { + const colors = ColorScheme.default.getColors(); + try testing.expectEqual([3]u8{ 255, 255, 255 }, colors.foreground); + try testing.expectEqual([3]u8{ 0, 0, 0 }, colors.background); +} diff --git a/src/editor.zig b/src/editor.zig index 15837f9..83b04e6 100644 --- a/src/editor.zig +++ b/src/editor.zig @@ -449,3 +449,167 @@ pub const Editor = struct { self.clearSelection(); } }; + +const testing = std.testing; + +test "editor initialization and basic operations" { + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + defer _ = gpa.deinit(); + const allocator = gpa.allocator(); + + var ed = Editor.init(allocator); + defer ed.deinit(); + + // Test initial state + try testing.expect(ed.mode == .normal); + try testing.expect(ed.cursor_row == 0); + try testing.expect(ed.cursor_col == 0); + + // Test adding content + try ed.buffer.append(try allocator.dupe(u8, "test line 1")); + try ed.buffer.append(try allocator.dupe(u8, "test line 2")); + + try testing.expect(ed.buffer.items.len == 2); + try testing.expectEqualStrings("test line 1", ed.buffer.items[0]); + try testing.expectEqualStrings("test line 2", ed.buffer.items[1]); + + // Test cursor movement + ed.moveCursorDown(); + try testing.expect(ed.cursor_row == 1); + + ed.moveCursorUp(); + try testing.expect(ed.cursor_row == 0); +} + +test "mode string conversion" { + try testing.expectEqualStrings("NORMAL", Mode.normal.toString()); + try testing.expectEqualStrings("INSERT", Mode.insert.toString()); + try testing.expectEqualStrings("SELECT", Mode.select.toString()); + try testing.expectEqualStrings("COMMAND", Mode.command.toString()); +} + +test "forward delete functionality" { + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + defer _ = gpa.deinit(); + const allocator = gpa.allocator(); + + var ed = Editor.init(allocator); + defer ed.deinit(); + + // Add test content + try ed.buffer.append(try allocator.dupe(u8, "Hello World")); + + // Position cursor at 'o' in "Hello" + ed.cursor_row = 0; + ed.cursor_col = 4; // Position at 'o' + + // Delete forward should remove 'o' and cursor should stay at same position + try ed.deleteChar(true); + + try testing.expectEqualStrings("Hell World", ed.buffer.items[0]); + try testing.expect(ed.cursor_col == 4); // Cursor should stay at same position + + // Delete forward again should remove ' ' + try ed.deleteChar(true); + try testing.expectEqualStrings("HellWorld", ed.buffer.items[0]); + try testing.expect(ed.cursor_col == 4); +} + +test "forward delete at end of line merges with next line" { + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + defer _ = gpa.deinit(); + const allocator = gpa.allocator(); + + var ed = Editor.init(allocator); + defer ed.deinit(); + + // Add test content with two lines + try ed.buffer.append(try allocator.dupe(u8, "Hello")); + try ed.buffer.append(try allocator.dupe(u8, "World")); + + // Position cursor at end of first line + ed.cursor_row = 0; + ed.cursor_col = 5; // End of "Hello" + + // Press delete (forward delete) + try ed.deleteChar(true); + + // Should merge the lines + try testing.expect(ed.buffer.items.len == 1); + try testing.expectEqualStrings("HelloWorld", ed.buffer.items[0]); + + // Cursor should still be at position 5 (between "Hello" and "World") + try testing.expect(ed.cursor_row == 0); + try testing.expect(ed.cursor_col == 5); +} + +test "forward delete edge cases" { + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + defer _ = gpa.deinit(); + const allocator = gpa.allocator(); + + var ed = Editor.init(allocator); + defer ed.deinit(); + + // Test with empty line + try ed.buffer.append(try allocator.dupe(u8, "")); + ed.cursor_row = 0; + ed.cursor_col = 0; + + // Should not crash when trying to delete from empty line + try ed.deleteChar(true); + try testing.expectEqualStrings("", ed.buffer.items[0]); + + // Test at end of line + try ed.buffer.append(try allocator.dupe(u8, "test")); + ed.cursor_row = 1; + ed.cursor_col = 4; // At end of line + + // Should not crash when trying to delete at end of line + try ed.deleteChar(true); + try testing.expectEqualStrings("test", ed.buffer.items[1]); +} + +test "select mode functionality" { + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + defer _ = gpa.deinit(); + const allocator = gpa.allocator(); + + var ed = Editor.init(allocator); + defer ed.deinit(); + + // Add test content + try ed.buffer.append(try allocator.dupe(u8, "Hello World")); + try ed.buffer.append(try allocator.dupe(u8, "Second line")); + + // Test starting selection + ed.startSelection(); + try testing.expect(ed.selection != null); + + if (ed.selection) |sel| { + try testing.expect(sel.anchor_row == 0); + try testing.expect(sel.anchor_col == 0); + try testing.expect(sel.head_row == 0); + try testing.expect(sel.head_col == 0); + } + + // Test updating selection + ed.cursor_col = 5; + ed.updateSelection(); + + if (ed.selection) |sel| { + try testing.expect(sel.head_col == 5); + } + + // Test line selection + ed.selectWholeLine(0); + if (ed.selection) |sel| { + try testing.expect(sel.anchor_row == 0); + try testing.expect(sel.anchor_col == 0); + try testing.expect(sel.head_col == 11); // Length of "Hello World" + } + + // Test clearing selection + ed.clearSelection(); + try testing.expect(ed.selection == null); +} diff --git a/src/syntax.zig b/src/syntax.zig index 43f4745..90a9806 100644 --- a/src/syntax.zig +++ b/src/syntax.zig @@ -80,3 +80,12 @@ pub const HighlightResult = struct { self.tokens.deinit(); } }; + +const testing = std.testing; + +test "language detection" { + try testing.expectEqualStrings("zig", SyntaxHighlighter.detectLanguage("test.zig")); + try testing.expectEqualStrings("rust", SyntaxHighlighter.detectLanguage("test.rs")); + try testing.expectEqualStrings("c", SyntaxHighlighter.detectLanguage("test.c")); + try testing.expectEqualStrings("text", SyntaxHighlighter.detectLanguage("test.unknown")); +} diff --git a/src/test.zig b/src/test.zig deleted file mode 100644 index 8d45ed8..0000000 --- a/src/test.zig +++ /dev/null @@ -1,187 +0,0 @@ -const std = @import("std"); -const testing = std.testing; -const editor = @import("editor.zig"); -const syntax = @import("syntax.zig"); -const config = @import("config.zig"); - -test "editor initialization and basic operations" { - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - defer _ = gpa.deinit(); - const allocator = gpa.allocator(); - - var ed = editor.Editor.init(allocator); - defer ed.deinit(); - - // Test initial state - try testing.expect(ed.mode == .normal); - try testing.expect(ed.cursor_row == 0); - try testing.expect(ed.cursor_col == 0); - - // Test adding content - try ed.buffer.append(try allocator.dupe(u8, "test line 1")); - try ed.buffer.append(try allocator.dupe(u8, "test line 2")); - - try testing.expect(ed.buffer.items.len == 2); - try testing.expectEqualStrings("test line 1", ed.buffer.items[0]); - try testing.expectEqualStrings("test line 2", ed.buffer.items[1]); - - // Test cursor movement - ed.moveCursorDown(); - try testing.expect(ed.cursor_row == 1); - - ed.moveCursorUp(); - try testing.expect(ed.cursor_row == 0); -} - -test "mode string conversion" { - try testing.expectEqualStrings("NORMAL", editor.Mode.normal.toString()); - try testing.expectEqualStrings("INSERT", editor.Mode.insert.toString()); - try testing.expectEqualStrings("SELECT", editor.Mode.select.toString()); - try testing.expectEqualStrings("COMMAND", editor.Mode.command.toString()); -} - -test "language detection" { - try testing.expectEqualStrings("zig", syntax.SyntaxHighlighter.detectLanguage("test.zig")); - try testing.expectEqualStrings("rust", syntax.SyntaxHighlighter.detectLanguage("test.rs")); - try testing.expectEqualStrings("c", syntax.SyntaxHighlighter.detectLanguage("test.c")); - try testing.expectEqualStrings("text", syntax.SyntaxHighlighter.detectLanguage("test.unknown")); -} - -test "config default values" { - const cfg = config.Config.default(); - try testing.expect(cfg.show_line_numbers == true); - try testing.expect(cfg.tab_width == 4); - try testing.expect(cfg.use_spaces == true); -} - -test "color scheme colors" { - const colors = config.ColorScheme.default.getColors(); - try testing.expectEqual([3]u8{ 255, 255, 255 }, colors.foreground); - try testing.expectEqual([3]u8{ 0, 0, 0 }, colors.background); -} - -test "forward delete functionality" { - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - defer _ = gpa.deinit(); - const allocator = gpa.allocator(); - - var ed = editor.Editor.init(allocator); - defer ed.deinit(); - - // Add test content - try ed.buffer.append(try allocator.dupe(u8, "Hello World")); - - // Position cursor at 'o' in "Hello" - ed.cursor_row = 0; - ed.cursor_col = 4; // Position at 'o' - - // Delete forward should remove 'o' and cursor should stay at same position - try ed.deleteChar(true); - - try testing.expectEqualStrings("Hell World", ed.buffer.items[0]); - try testing.expect(ed.cursor_col == 4); // Cursor should stay at same position - - // Delete forward again should remove ' ' - try ed.deleteChar(true); - try testing.expectEqualStrings("HellWorld", ed.buffer.items[0]); - try testing.expect(ed.cursor_col == 4); -} - -test "forward delete at end of line merges with next line" { - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - defer _ = gpa.deinit(); - const allocator = gpa.allocator(); - - var ed = editor.Editor.init(allocator); - defer ed.deinit(); - - // Add test content with two lines - try ed.buffer.append(try allocator.dupe(u8, "Hello")); - try ed.buffer.append(try allocator.dupe(u8, "World")); - - // Position cursor at end of first line - ed.cursor_row = 0; - ed.cursor_col = 5; // End of "Hello" - - // Press delete (forward delete) - try ed.deleteChar(true); - - // Should merge the lines - try testing.expect(ed.buffer.items.len == 1); - try testing.expectEqualStrings("HelloWorld", ed.buffer.items[0]); - - // Cursor should still be at position 5 (between "Hello" and "World") - try testing.expect(ed.cursor_row == 0); - try testing.expect(ed.cursor_col == 5); -} - -test "forward delete edge cases" { - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - defer _ = gpa.deinit(); - const allocator = gpa.allocator(); - - var ed = editor.Editor.init(allocator); - defer ed.deinit(); - - // Test with empty line - try ed.buffer.append(try allocator.dupe(u8, "")); - ed.cursor_row = 0; - ed.cursor_col = 0; - - // Should not crash when trying to delete from empty line - try ed.deleteChar(true); - try testing.expectEqualStrings("", ed.buffer.items[0]); - - // Test at end of line - try ed.buffer.append(try allocator.dupe(u8, "test")); - ed.cursor_row = 1; - ed.cursor_col = 4; // At end of line - - // Should not crash when trying to delete at end of line - try ed.deleteChar(true); - try testing.expectEqualStrings("test", ed.buffer.items[1]); -} - -test "select mode functionality" { - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - defer _ = gpa.deinit(); - const allocator = gpa.allocator(); - - var ed = editor.Editor.init(allocator); - defer ed.deinit(); - - // Add test content - try ed.buffer.append(try allocator.dupe(u8, "Hello World")); - try ed.buffer.append(try allocator.dupe(u8, "Second line")); - - // Test starting selection - ed.startSelection(); - try testing.expect(ed.selection != null); - - if (ed.selection) |sel| { - try testing.expect(sel.anchor_row == 0); - try testing.expect(sel.anchor_col == 0); - try testing.expect(sel.head_row == 0); - try testing.expect(sel.head_col == 0); - } - - // Test updating selection - ed.cursor_col = 5; - ed.updateSelection(); - - if (ed.selection) |sel| { - try testing.expect(sel.head_col == 5); - } - - // Test line selection - ed.selectWholeLine(0); - if (ed.selection) |sel| { - try testing.expect(sel.anchor_row == 0); - try testing.expect(sel.anchor_col == 0); - try testing.expect(sel.head_col == 11); // Length of "Hello World" - } - - // Test clearing selection - ed.clearSelection(); - try testing.expect(ed.selection == null); -} -- 2.54.0