From 4b6f66fd512c254b5a82220dc77411fe391dd258 Mon Sep 17 00:00:00 2001 From: David Czihak Date: Sat, 9 May 2026 13:01:50 +0200 Subject: Chore: Rework examples for thorough extension testing --- examples/test-suite/.gitignore | 2 + examples/test-suite/.nova/Configuration.json | 4 + .../test-suite/.nova/Tasks/Zig Test (all).json | 10 +++ .../test-suite/.nova/Tasks/Zig Test (math).json | 11 +++ examples/test-suite/build.zig | 41 +++++++++++ examples/test-suite/build.zig.zon | 12 +++ examples/test-suite/src/math.zig | 70 ++++++++++++++++++ examples/test-suite/src/strings.zig | 86 ++++++++++++++++++++++ 8 files changed, 236 insertions(+) create mode 100644 examples/test-suite/.gitignore create mode 100644 examples/test-suite/.nova/Configuration.json create mode 100644 examples/test-suite/.nova/Tasks/Zig Test (all).json create mode 100644 examples/test-suite/.nova/Tasks/Zig Test (math).json create mode 100644 examples/test-suite/build.zig create mode 100644 examples/test-suite/build.zig.zon create mode 100644 examples/test-suite/src/math.zig create mode 100644 examples/test-suite/src/strings.zig (limited to 'examples/test-suite') diff --git a/examples/test-suite/.gitignore b/examples/test-suite/.gitignore new file mode 100644 index 0000000..3389c86 --- /dev/null +++ b/examples/test-suite/.gitignore @@ -0,0 +1,2 @@ +.zig-cache/ +zig-out/ diff --git a/examples/test-suite/.nova/Configuration.json b/examples/test-suite/.nova/Configuration.json new file mode 100644 index 0000000..44cadf8 --- /dev/null +++ b/examples/test-suite/.nova/Configuration.json @@ -0,0 +1,4 @@ +{ + "zls.enable_build_on_save" : false, + "zls.zig_exe_path" : "\/opt\/homebrew\/bin\/zig" +} diff --git a/examples/test-suite/.nova/Tasks/Zig Test (all).json b/examples/test-suite/.nova/Tasks/Zig Test (all).json new file mode 100644 index 0000000..44edb03 --- /dev/null +++ b/examples/test-suite/.nova/Tasks/Zig Test (all).json @@ -0,0 +1,10 @@ +{ + "extension" : { + "identifier" : "at.dcz.nova-zig", + "name" : "Zig" + }, + "extensionTemplate" : "zigTest", + "extensionValues" : { + "summary" : "all" + } +} diff --git a/examples/test-suite/.nova/Tasks/Zig Test (math).json b/examples/test-suite/.nova/Tasks/Zig Test (math).json new file mode 100644 index 0000000..a4d1580 --- /dev/null +++ b/examples/test-suite/.nova/Tasks/Zig Test (math).json @@ -0,0 +1,11 @@ +{ + "extension" : { + "identifier" : "at.dcz.nova-zig", + "name" : "Zig" + }, + "extensionTemplate" : "zigTest", + "extensionValues" : { + "summary" : "all", + "testFilter" : "math:" + } +} diff --git a/examples/test-suite/build.zig b/examples/test-suite/build.zig new file mode 100644 index 0000000..3b723e1 --- /dev/null +++ b/examples/test-suite/build.zig @@ -0,0 +1,41 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + const math_mod = b.addModule("math", .{ + .root_source_file = b.path("src/math.zig"), + .target = target, + }); + + const strings_mod = b.addModule("strings", .{ + .root_source_file = b.path("src/strings.zig"), + .target = target, + }); + + const math_tests = b.addTest(.{ + .root_module = b.createModule(.{ + .root_source_file = b.path("src/math.zig"), + .target = target, + .optimize = optimize, + }), + }); + const strings_tests = b.addTest(.{ + .root_module = b.createModule(.{ + .root_source_file = b.path("src/strings.zig"), + .target = target, + .optimize = optimize, + }), + }); + + _ = math_mod; + _ = strings_mod; + + const run_math = b.addRunArtifact(math_tests); + const run_strings = b.addRunArtifact(strings_tests); + + const test_step = b.step("test", "Run all tests"); + test_step.dependOn(&run_math.step); + test_step.dependOn(&run_strings.step); +} diff --git a/examples/test-suite/build.zig.zon b/examples/test-suite/build.zig.zon new file mode 100644 index 0000000..3d11b66 --- /dev/null +++ b/examples/test-suite/build.zig.zon @@ -0,0 +1,12 @@ +.{ + .name = .test_suite, + .version = "0.1.0", + .fingerprint = 0x1ee2422d1c238201, + .minimum_zig_version = "0.16.0", + .dependencies = .{}, + .paths = .{ + "build.zig", + "build.zig.zon", + "src", + }, +} diff --git a/examples/test-suite/src/math.zig b/examples/test-suite/src/math.zig new file mode 100644 index 0000000..ef870ce --- /dev/null +++ b/examples/test-suite/src/math.zig @@ -0,0 +1,70 @@ +const std = @import("std"); + +pub fn factorial(n: u64) u64 { + if (n == 0) return 1; + return n * factorial(n - 1); +} + +pub fn isPrime(n: u64) bool { + if (n < 2) return false; + if (n == 2) return true; + if (n % 2 == 0) return false; + var i: u64 = 3; + while (i * i <= n) : (i += 2) { + if (n % i == 0) return false; + } + return true; +} + +pub fn gcd(a: u64, b: u64) u64 { + var x = a; + var y = b; + while (y != 0) { + const t = y; + y = x % y; + x = t; + } + return x; +} + +// ── Tests with "math: " prefix ──────────────────────────────────────────────── +// Run all: zig build test +// Run math only: zig build test -- --test-filter "math:" + +test "math: factorial base cases" { + try std.testing.expectEqual(@as(u64, 1), factorial(0)); + try std.testing.expectEqual(@as(u64, 1), factorial(1)); + try std.testing.expectEqual(@as(u64, 2), factorial(2)); +} + +test "math: factorial larger values" { + try std.testing.expectEqual(@as(u64, 120), factorial(5)); + try std.testing.expectEqual(@as(u64, 5040), factorial(7)); + try std.testing.expectEqual(@as(u64, 3628800), factorial(10)); +} + +test "math: isPrime small primes" { + try std.testing.expect(isPrime(2)); + try std.testing.expect(isPrime(3)); + try std.testing.expect(isPrime(5)); + try std.testing.expect(isPrime(97)); +} + +test "math: isPrime composites and edge cases" { + try std.testing.expect(!isPrime(0)); + try std.testing.expect(!isPrime(1)); + try std.testing.expect(!isPrime(4)); + try std.testing.expect(!isPrime(100)); +} + +test "math: gcd basics" { + try std.testing.expectEqual(@as(u64, 6), gcd(12, 18)); + try std.testing.expectEqual(@as(u64, 1), gcd(7, 13)); + try std.testing.expectEqual(@as(u64, 5), gcd(5, 0)); + try std.testing.expectEqual(@as(u64, 5), gcd(0, 5)); +} + +// Deliberately named differently — filtered OUT by "--test-filter math:" +test "number theory: Euclid handles equal inputs" { + try std.testing.expectEqual(@as(u64, 42), gcd(42, 42)); +} diff --git a/examples/test-suite/src/strings.zig b/examples/test-suite/src/strings.zig new file mode 100644 index 0000000..ba53f8c --- /dev/null +++ b/examples/test-suite/src/strings.zig @@ -0,0 +1,86 @@ +const std = @import("std"); + +/// Trim leading and trailing ASCII whitespace. +pub fn trim(s: []const u8) []const u8 { + return std.mem.trim(u8, s, " \t\r\n"); +} + +/// Count non-overlapping occurrences of `needle` in `haystack`. +pub fn countOccurrences(haystack: []const u8, needle: []const u8) usize { + if (needle.len == 0) return 0; + var count: usize = 0; + var i: usize = 0; + while (i + needle.len <= haystack.len) { + if (std.mem.eql(u8, haystack[i .. i + needle.len], needle)) { + count += 1; + i += needle.len; + } else { + i += 1; + } + } + return count; +} + +/// Reverse a string byte-by-byte (ASCII safe; not grapheme-safe). +pub fn reverseAlloc(allocator: std.mem.Allocator, s: []const u8) ![]u8 { + const result = try allocator.alloc(u8, s.len); + for (s, 0..) |byte, i| result[s.len - 1 - i] = byte; + return result; +} + +/// Returns true if `s` starts with `prefix`. +pub fn startsWith(s: []const u8, prefix: []const u8) bool { + return std.mem.startsWith(u8, s, prefix); +} + +// ── Tests with "strings: " prefix ──────────────────────────────────────────── +// Run all: zig build test +// Run strings only: zig build test -- --test-filter "strings:" + +test "strings: trim removes spaces" { + try std.testing.expectEqualStrings("hello", trim(" hello ")); +} + +test "strings: trim handles empty and whitespace-only" { + try std.testing.expectEqualStrings("", trim("")); + try std.testing.expectEqualStrings("", trim(" \t\n")); +} + +test "strings: trim preserves inner whitespace" { + try std.testing.expectEqualStrings("hello world", trim(" hello world ")); +} + +test "strings: countOccurrences basic" { + try std.testing.expectEqual(@as(usize, 3), countOccurrences("abcabcabc", "abc")); +} + +test "strings: countOccurrences empty needle" { + try std.testing.expectEqual(@as(usize, 0), countOccurrences("hello", "")); +} + +test "strings: countOccurrences no match" { + try std.testing.expectEqual(@as(usize, 0), countOccurrences("hello", "xyz")); +} + +test "strings: countOccurrences overlapping boundary" { + // Non-overlapping: "aa" in "aaaa" = 2 + try std.testing.expectEqual(@as(usize, 2), countOccurrences("aaaa", "aa")); +} + +test "strings: reverseAlloc" { + const reversed = try reverseAlloc(std.testing.allocator, "hello"); + defer std.testing.allocator.free(reversed); + try std.testing.expectEqualStrings("olleh", reversed); +} + +test "strings: reverseAlloc empty" { + const reversed = try reverseAlloc(std.testing.allocator, ""); + defer std.testing.allocator.free(reversed); + try std.testing.expectEqualStrings("", reversed); +} + +test "strings: startsWith" { + try std.testing.expect(startsWith("hello world", "hello")); + try std.testing.expect(!startsWith("hello world", "world")); + try std.testing.expect(startsWith("abc", "")); +} -- cgit v1.3