aboutsummaryrefslogtreecommitdiff
path: root/examples/test-suite
diff options
context:
space:
mode:
authorDavid Czihak <git@dcz.at>2026-05-09 13:01:50 +0200
committerDavid Czihak <git@dcz.at>2026-05-09 13:01:50 +0200
commit4b6f66fd512c254b5a82220dc77411fe391dd258 (patch)
tree7d77d7966e9ad2e296986ea8cfeb607088028195 /examples/test-suite
parent64e9c56fc665972fdde5234c4fb2f2a882e237dc (diff)
Chore: Rework examples for thorough extension testing
Diffstat (limited to 'examples/test-suite')
-rw-r--r--examples/test-suite/.gitignore2
-rw-r--r--examples/test-suite/.nova/Configuration.json4
-rw-r--r--examples/test-suite/.nova/Tasks/Zig Test (all).json10
-rw-r--r--examples/test-suite/.nova/Tasks/Zig Test (math).json11
-rw-r--r--examples/test-suite/build.zig41
-rw-r--r--examples/test-suite/build.zig.zon12
-rw-r--r--examples/test-suite/src/math.zig70
-rw-r--r--examples/test-suite/src/strings.zig86
8 files changed, 236 insertions, 0 deletions
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", ""));
+}