aboutsummaryrefslogtreecommitdiff
path: root/examples/test-suite/src/strings.zig
blob: ba53f8c0fd8fec24c53e1de83d0bbf177e3e85c1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
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", ""));
}