Remove tests/ directory, use inline tests in src files instead

This commit is contained in:
Jeremie Fraeys 2026-02-09 14:41:49 -05:00
parent 059c449ada
commit 26eb50d38f
10 changed files with 375 additions and 793 deletions

View file

@ -113,15 +113,11 @@ pub fn build(b: *std.Build) void {
});
release_step.dependOn(&install_exe.step);
// Test step
// Test step - run all unit tests
const run_unit_tests = b.addRunArtifact(unit_tests);
const test_step = b.step("test", "Run unit tests");
test_step.dependOn(&run_unit_tests.step);
// ============================================================================
// DEVELOPMENT TOOLS
// ============================================================================
// Check step - compilation check without output
const check_step = b.step("check", "Check code formatting and compilation");
check_step.dependOn(&check_exe.step);

View file

@ -94,3 +94,71 @@ pub fn displaySearchStart(config: types.SearchConfig) !void {
try stdout.flush(); // Flush search start message
}
}
// ============================================================================
// TESTS
// ============================================================================
test "displayManEntry - basic entry" {
const entry = types.ManEntry{
.name = "ls",
.section = "1",
.description = "list directory contents",
.path = null,
};
// Verify function doesn't panic
try displayManEntry(entry);
}
test "displaySearchResults - non-verbose no sections" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var entries = types.ManEntryList.init(allocator);
defer {
for (entries.items) |entry| {
allocator.free(entry.name);
allocator.free(entry.section);
allocator.free(entry.description);
}
entries.deinit();
}
try entries.append(types.ManEntry{
.name = try allocator.dupe(u8, "ls"),
.section = try allocator.dupe(u8, "1"),
.description = try allocator.dupe(u8, "list files"),
.path = null,
});
const config = types.SearchConfig{
.keyword = "ls",
.target_sections = null,
.show_paths = false,
.verbose = false,
};
try displaySearchResults(entries, config, 100.0);
}
test "displaySearchStart - non-verbose" {
const config = types.SearchConfig{
.keyword = "test",
.target_sections = null,
.show_paths = false,
.verbose = false,
};
// Should not output anything in non-verbose mode
try displaySearchStart(config);
}
test "displaySearchStart - verbose" {
const config = types.SearchConfig{
.keyword = "sleep",
.target_sections = null,
.show_paths = false,
.verbose = true,
};
try displaySearchStart(config);
}

View file

@ -1,4 +1,5 @@
const std = @import("std");
const types = @import("types.zig");
const parser = @import("parser.zig");
const search = @import("search.zig");
const display = @import("display.zig");

View file

@ -97,3 +97,107 @@ pub fn parseArgs(allocator: std.mem.Allocator, args: [][:0]u8) !types.SearchConf
.verbose = verbose,
};
}
// ============================================================================
// TESTS
// ============================================================================
test "parseArgs - basic keyword" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const args = &[_][:0]u8{ "manwhere", "sleep" };
const config = try parseArgs(allocator, args);
try std.testing.expectEqualStrings("sleep", config.keyword);
try std.testing.expect(config.target_sections == null);
try std.testing.expect(!config.show_paths);
try std.testing.expect(!config.verbose);
}
test "parseArgs - with verbose flag" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const args = &[_][:0]u8{ "manwhere", "-v", "ls" };
const config = try parseArgs(allocator, args);
try std.testing.expectEqualStrings("ls", config.keyword);
try std.testing.expect(config.verbose);
}
test "parseArgs - with paths flag" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const args = &[_][:0]u8{ "manwhere", "--paths", "printf" };
const config = try parseArgs(allocator, args);
try std.testing.expectEqualStrings("printf", config.keyword);
try std.testing.expect(config.show_paths);
}
test "parseArgs - with section flag" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const args = &[_][:0]u8{ "manwhere", "-s", "1", "ls" };
const config = try parseArgs(allocator, args);
try std.testing.expectEqualStrings("ls", config.keyword);
try std.testing.expect(config.target_sections != null);
try std.testing.expectEqual(@as(usize, 1), config.target_sections.?.len);
try std.testing.expectEqualStrings("1", config.target_sections.?[0]);
}
test "parseArgs - with multiple sections" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const args = &[_][:0]u8{ "manwhere", "-s", "1", "-s", "3", "test" };
const config = try parseArgs(allocator, args);
try std.testing.expectEqualStrings("test", config.keyword);
try std.testing.expect(config.target_sections != null);
try std.testing.expectEqual(@as(usize, 2), config.target_sections.?.len);
try std.testing.expectEqualStrings("1", config.target_sections.?[0]);
try std.testing.expectEqualStrings("3", config.target_sections.?[1]);
}
test "parseArgs - error no keyword" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const args = &[_][:0]u8{"manwhere"};
const result = parseArgs(allocator, args);
try std.testing.expectError(error.NoKeyword, result);
}
test "parseArgs - error invalid option" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const args = &[_][:0]u8{ "manwhere", "--invalid", "ls" };
const result = parseArgs(allocator, args);
try std.testing.expectError(error.InvalidOption, result);
}
test "parseArgs - error multiple keywords" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const args = &[_][:0]u8{ "manwhere", "ls", "cat" };
const result = parseArgs(allocator, args);
try std.testing.expectError(error.MultipleKeywords, result);
}

View file

@ -404,3 +404,85 @@ pub fn searchManPages(
return searchManPagesOriginal(keyword, target_section, show_paths, verbose, allocator, stderr);
};
}
// ============================================================================
// TESTS
// ============================================================================
test "ManSearcher - basic initialization" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// Test the types ManEntryList uses
var entries = types.ManEntryList.init(allocator);
defer {
for (entries.items) |entry| {
allocator.free(entry.name);
allocator.free(entry.section);
allocator.free(entry.description);
}
entries.deinit();
}
try std.testing.expectEqual(@as(usize, 0), entries.items.len);
}
test "ManEntry - full lifecycle" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const entry = types.ManEntry{
.name = try allocator.dupe(u8, "grep"),
.section = try allocator.dupe(u8, "1"),
.description = try allocator.dupe(u8, "global regular expression print"),
.path = try allocator.dupe(u8, "/usr/share/man/man1/grep.1.gz"),
};
defer {
allocator.free(entry.name);
allocator.free(entry.section);
allocator.free(entry.description);
if (entry.path) |path| allocator.free(path);
}
try std.testing.expectEqualStrings("grep", entry.name);
try std.testing.expectEqualStrings("1", entry.section);
try std.testing.expectEqualStrings("global regular expression print", entry.description);
try std.testing.expect(entry.path != null);
try std.testing.expectEqualStrings("/usr/share/man/man1/grep.1.gz", entry.path.?);
}
test "ManEntryList - append multiple" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var entries = types.ManEntryList.init(allocator);
defer {
for (entries.items) |entry| {
allocator.free(entry.name);
allocator.free(entry.section);
allocator.free(entry.description);
}
entries.deinit();
}
try entries.append(types.ManEntry{
.name = try allocator.dupe(u8, "ls"),
.section = try allocator.dupe(u8, "1"),
.description = try allocator.dupe(u8, "list"),
.path = null,
});
try entries.append(types.ManEntry{
.name = try allocator.dupe(u8, "cat"),
.section = try allocator.dupe(u8, "1"),
.description = try allocator.dupe(u8, "concatenate"),
.path = null,
});
try std.testing.expectEqual(@as(usize, 2), entries.items.len);
try std.testing.expectEqualStrings("ls", entries.items[0].name);
try std.testing.expectEqualStrings("cat", entries.items[1].name);
}

View file

@ -15,14 +15,12 @@ pub const ManEntry = struct {
'1' => "[*] Command",
'2' => "[S] Syscall",
'3' => blk: {
// Optimized library detection with early returns
if (self.section.len >= 4) {
if (std.mem.startsWith(u8, self.section, "3ssl")) break :blk "[L] OpenSSL";
if (std.mem.startsWith(u8, self.section, "3tcl")) break :blk "[L] Tcl API";
if (std.mem.startsWith(u8, self.section, "3pm")) break :blk "[L] Perl";
if (std.mem.startsWith(u8, self.section, "3p")) break :blk "[L] POSIX";
if (std.mem.startsWith(u8, self.section, "3x")) break :blk "[L] X11/ncurses";
}
// Check for specific 3xxx subsections (order matters - longer first)
if (std.mem.startsWith(u8, self.section, "3ssl")) break :blk "[L] OpenSSL";
if (std.mem.startsWith(u8, self.section, "3tcl")) break :blk "[L] Tcl API";
if (std.mem.startsWith(u8, self.section, "3pm")) break :blk "[L] Perl";
if (std.mem.startsWith(u8, self.section, "3p")) break :blk "[L] POSIX";
if (std.mem.startsWith(u8, self.section, "3x")) break :blk "[L] X11/ncurses";
break :blk "[L] C Library";
},
'4' => "[D] Device",
@ -55,3 +53,116 @@ pub const SearchConfig = struct {
show_paths: bool,
verbose: bool,
};
// ============================================================================
// TESTS
// ============================================================================
test "ManEntry.getSectionMarker - commands" {
const entry = ManEntry{
.name = "ls",
.section = "1",
.description = "list directory contents",
.path = null,
};
const marker = entry.getSectionMarker();
try std.testing.expectEqualStrings("[*] Command", marker);
}
test "ManEntry.getSectionMarker - syscalls" {
const entry = ManEntry{
.name = "open",
.section = "2",
.description = "open file",
.path = null,
};
const marker = entry.getSectionMarker();
try std.testing.expectEqualStrings("[S] Syscall", marker);
}
test "ManEntry.getSectionMarker - C library" {
const entry = ManEntry{
.name = "printf",
.section = "3",
.description = "format and print data",
.path = null,
};
const marker = entry.getSectionMarker();
try std.testing.expectEqualStrings("[L] C Library", marker);
}
test "ManEntry.getSectionMarker - OpenSSL" {
const entry = ManEntry{
.name = "SSL_new",
.section = "3ssl",
.description = "create SSL structure",
.path = null,
};
const marker = entry.getSectionMarker();
try std.testing.expectEqualStrings("[L] OpenSSL", marker);
}
test "ManEntry.getSectionMarker - POSIX" {
const entry = ManEntry{
.name = "pthread_create",
.section = "3p",
.description = "create thread",
.path = null,
};
const marker = entry.getSectionMarker();
try std.testing.expectEqualStrings("[L] POSIX", marker);
}
test "ManEntry.getSectionMarker - kernel" {
const entry = ManEntry{
.name = "kmalloc",
.section = "9",
.description = "kernel memory alloc",
.path = null,
};
const marker = entry.getSectionMarker();
try std.testing.expectEqualStrings("[K] Kernel", marker);
}
test "ManEntry.getSectionMarker - unknown" {
const entry = ManEntry{
.name = "unknown",
.section = "0",
.description = "invalid section",
.path = null,
};
const marker = entry.getSectionMarker();
try std.testing.expectEqualStrings("[?] Unknown", marker);
}
test "ManEntry.matchesSection - no target" {
const entry = ManEntry{
.name = "ls",
.section = "1",
.description = "list",
.path = null,
};
try std.testing.expect(entry.matchesSection(null));
}
test "ManEntry.matchesSection - exact match" {
const entry = ManEntry{
.name = "ls",
.section = "1",
.description = "list",
.path = null,
};
const targets = &[_][]const u8{"1"};
try std.testing.expect(entry.matchesSection(targets));
}
test "ManEntry.matchesSection - no match" {
const entry = ManEntry{
.name = "ls",
.section = "1",
.description = "list",
.path = null,
};
const targets = &[_][]const u8{"3"};
try std.testing.expect(!entry.matchesSection(targets));
}

View file

@ -1,236 +0,0 @@
const std = @import("std");
const types = @import("../../src/types.zig");
const display = @import("../../src/display.zig");
test "displayManEntry - basic entry" {
const entry = types.ManEntry{
.name = "ls",
.section = "1",
.description = "list directory contents",
.path = null,
};
// This test verifies the function doesn't panic
// Actual output testing would require capturing stdout
try display.displayManEntry(entry);
}
test "displayManEntry - with path" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const entry = types.ManEntry{
.name = "ls",
.section = "1",
.description = "list directory contents",
.path = try allocator.dupe(u8, "/usr/share/man/man1/ls.1"),
};
defer if (entry.path) |path| allocator.free(path);
try display.displayManEntry(entry);
}
test "displaySearchResults - non-verbose no sections" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var entries = types.ManEntryList.init(allocator);
defer {
for (entries.items) |entry| {
allocator.free(entry.name);
allocator.free(entry.section);
allocator.free(entry.description);
}
entries.deinit();
}
try entries.append(types.ManEntry{
.name = try allocator.dupe(u8, "ls"),
.section = try allocator.dupe(u8, "1"),
.description = try allocator.dupe(u8, "list files"),
.path = null,
});
const config = types.SearchConfig{
.keyword = "ls",
.target_sections = null,
.show_paths = false,
.verbose = false,
};
try display.displaySearchResults(entries, config, 100.0);
}
test "displaySearchResults - non-verbose with sections" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var entries = types.ManEntryList.init(allocator);
defer {
for (entries.items) |entry| {
allocator.free(entry.name);
allocator.free(entry.section);
allocator.free(entry.description);
}
entries.deinit();
}
const targets = &[_][]const u8{"1", "3"};
const config = types.SearchConfig{
.keyword = "test",
.target_sections = targets,
.show_paths = false,
.verbose = false,
};
try display.displaySearchResults(entries, config, 50.0);
}
test "displaySearchResults - verbose mode" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var entries = types.ManEntryList.init(allocator);
defer {
for (entries.items) |entry| {
allocator.free(entry.name);
allocator.free(entry.section);
allocator.free(entry.description);
}
entries.deinit();
}
try entries.append(types.ManEntry{
.name = try allocator.dupe(u8, "cat"),
.section = try allocator.dupe(u8, "1"),
.description = try allocator.dupe(u8, "concatenate files"),
.path = null,
});
const config = types.SearchConfig{
.keyword = "cat",
.target_sections = null,
.show_paths = false,
.verbose = true,
};
try display.displaySearchResults(entries, config, 250.5);
}
test "displaySearchResults - multiple entries" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var entries = types.ManEntryList.init(allocator);
defer {
for (entries.items) |entry| {
allocator.free(entry.name);
allocator.free(entry.section);
allocator.free(entry.description);
}
entries.deinit();
}
try entries.append(types.ManEntry{
.name = try allocator.dupe(u8, "ls"),
.section = try allocator.dupe(u8, "1"),
.description = try allocator.dupe(u8, "list files"),
.path = null,
});
try entries.append(types.ManEntry{
.name = try allocator.dupe(u8, "printf"),
.section = try allocator.dupe(u8, "3"),
.description = try allocator.dupe(u8, "format and print"),
.path = null,
});
try entries.append(types.ManEntry{
.name = try allocator.dupe(u8, "printf"),
.section = try allocator.dupe(u8, "1"),
.description = try allocator.dupe(u8, "format and print"),
.path = null,
});
const config = types.SearchConfig{
.keyword = "printf",
.target_sections = null,
.show_paths = false,
.verbose = false,
};
try display.displaySearchResults(entries, config, 150.0);
}
test "displaySearchStart - non-verbose" {
const config = types.SearchConfig{
.keyword = "test",
.target_sections = null,
.show_paths = false,
.verbose = false,
};
// Should not output anything in non-verbose mode
try display.displaySearchStart(config);
}
test "displaySearchStart - verbose no sections" {
const config = types.SearchConfig{
.keyword = "sleep",
.target_sections = null,
.show_paths = false,
.verbose = true,
};
try display.displaySearchStart(config);
}
test "displaySearchStart - verbose with single section" {
const targets = &[_][]const u8{"1"};
const config = types.SearchConfig{
.keyword = "ls",
.target_sections = targets,
.show_paths = false,
.verbose = true,
};
try display.displaySearchStart(config);
}
test "displaySearchStart - verbose with multiple sections" {
const targets = &[_][]const u8{"1", "3", "8"};
const config = types.SearchConfig{
.keyword = "admin",
.target_sections = targets,
.show_paths = false,
.verbose = true,
};
try display.displaySearchStart(config);
}
test "displaySearchResults - empty results" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var entries = types.ManEntryList.init(allocator);
defer entries.deinit();
const config = types.SearchConfig{
.keyword = "nonexistent12345",
.target_sections = null,
.show_paths = false,
.verbose = true,
};
try display.displaySearchResults(entries, config, 10.0);
}

View file

@ -1,196 +0,0 @@
const std = @import("std");
const parser = @import("../../src/parser.zig");
const types = @import("../../src/types.zig");
test "parseArgs - basic keyword" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const args = &[_][:0]u8{"manwhere", "sleep"};
const config = try parser.parseArgs(allocator, args);
try std.testing.expectEqualStrings("sleep", config.keyword);
try std.testing.expect(config.target_sections == null);
try std.testing.expect(!config.show_paths);
try std.testing.expect(!config.verbose);
}
test "parseArgs - with verbose flag" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const args = &[_][:0]u8{"manwhere", "-v", "ls"};
const config = try parser.parseArgs(allocator, args);
try std.testing.expectEqualStrings("ls", config.keyword);
try std.testing.expect(config.verbose);
}
test "parseArgs - with long verbose flag" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const args = &[_][:0]u8{"manwhere", "--verbose", "ls"};
const config = try parser.parseArgs(allocator, args);
try std.testing.expectEqualStrings("ls", config.keyword);
try std.testing.expect(config.verbose);
}
test "parseArgs - with paths flag" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const args = &[_][:0]u8{"manwhere", "--paths", "printf"};
const config = try parser.parseArgs(allocator, args);
try std.testing.expectEqualStrings("printf", config.keyword);
try std.testing.expect(config.show_paths);
}
test "parseArgs - with section flag short" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const args = &[_][:0]u8{"manwhere", "-s", "1", "ls"};
const config = try parser.parseArgs(allocator, args);
try std.testing.expectEqualStrings("ls", config.keyword);
try std.testing.expect(config.target_sections != null);
try std.testing.expectEqual(@as(usize, 1), config.target_sections.?.len);
try std.testing.expectEqualStrings("1", config.target_sections.?[0]);
}
test "parseArgs - with section flag long" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const args = &[_][:0]u8{"manwhere", "--section", "3", "printf"};
const config = try parser.parseArgs(allocator, args);
try std.testing.expectEqualStrings("printf", config.keyword);
try std.testing.expect(config.target_sections != null);
try std.testing.expectEqualStrings("3", config.target_sections.?[0]);
}
test "parseArgs - with multiple sections" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const args = &[_][:0]u8{"manwhere", "-s", "1", "-s", "3", "test"};
const config = try parser.parseArgs(allocator, args);
try std.testing.expectEqualStrings("test", config.keyword);
try std.testing.expect(config.target_sections != null);
try std.testing.expectEqual(@as(usize, 2), config.target_sections.?.len);
try std.testing.expectEqualStrings("1", config.target_sections.?[0]);
try std.testing.expectEqualStrings("3", config.target_sections.?[1]);
}
test "parseArgs - with section and verbose" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const args = &[_][:0]u8{"manwhere", "-v", "-s", "3ssl", "ssl"};
const config = try parser.parseArgs(allocator, args);
try std.testing.expectEqualStrings("ssl", config.keyword);
try std.testing.expect(config.verbose);
try std.testing.expectEqualStrings("3ssl", config.target_sections.?[0]);
}
test "parseArgs - error no keyword" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const args = &[_][:0]u8{"manwhere"};
const result = parser.parseArgs(allocator, args);
try std.testing.expectError(error.NoKeyword, result);
}
test "parseArgs - error invalid option" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const args = &[_][:0]u8{"manwhere", "--invalid", "ls"};
const result = parser.parseArgs(allocator, args);
try std.testing.expectError(error.InvalidOption, result);
}
test "parseArgs - error section without value" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const args = &[_][:0]u8{"manwhere", "-s"};
const result = parser.parseArgs(allocator, args);
try std.testing.expectError(error.InvalidOption, result);
}
test "parseArgs - error invalid section letter" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const args = &[_][:0]u8{"manwhere", "-s", "abc", "ls"};
const result = parser.parseArgs(allocator, args);
try std.testing.expectError(error.InvalidSection, result);
}
test "parseArgs - error section zero" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const args = &[_][:0]u8{"manwhere", "-s", "0", "ls"};
const result = parser.parseArgs(allocator, args);
try std.testing.expectError(error.InvalidSection, result);
}
test "parseArgs - error multiple keywords" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const args = &[_][:0]u8{"manwhere", "ls", "cat"};
const result = parser.parseArgs(allocator, args);
try std.testing.expectError(error.MultipleKeywords, result);
}
test "parseArgs - complex combination" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const args = &[_][:0]u8{"manwhere", "-v", "--paths", "-s", "1", "-s", "8", "admin"};
const config = try parser.parseArgs(allocator, args);
try std.testing.expectEqualStrings("admin", config.keyword);
try std.testing.expect(config.verbose);
try std.testing.expect(config.show_paths);
try std.testing.expectEqual(@as(usize, 2), config.target_sections.?.len);
}
test "HELP_TEXT - contains usage info" {
try std.testing.expect(std.mem.indexOf(u8, parser.HELP_TEXT, "Usage:") != null);
try std.testing.expect(std.mem.indexOf(u8, parser.HELP_TEXT, "OPTIONS:") != null);
try std.testing.expect(std.mem.indexOf(u8, parser.HELP_TEXT, "EXAMPLES:") != null);
try std.testing.expect(std.mem.indexOf(u8, parser.HELP_TEXT, "--verbose") != null);
try std.testing.expect(std.mem.indexOf(u8, parser.HELP_TEXT, "--section") != null);
}

View file

@ -1,108 +0,0 @@
const std = @import("std");
const types = @import("../../src/types.zig");
const FastError = error{ FastFail, OutOfMemory };
fn fakeFast() FastError!i32 {
return error.FastFail;
}
fn fakeOriginal() i32 {
return 42;
}
test "ManSearcher - basic initialization" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// ManSearcher would be tested here if exported from search module
// For now, we test the types it uses
var entries = types.ManEntryList.init(allocator);
defer {
for (entries.items) |entry| {
allocator.free(entry.name);
allocator.free(entry.section);
allocator.free(entry.description);
}
entries.deinit();
}
try std.testing.expectEqual(@as(usize, 0), entries.items.len);
}
test "ManEntry - full lifecycle" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const entry = types.ManEntry{
.name = try allocator.dupe(u8, "grep"),
.section = try allocator.dupe(u8, "1"),
.description = try allocator.dupe(u8, "global regular expression print"),
.path = try allocator.dupe(u8, "/usr/share/man/man1/grep.1.gz"),
};
defer {
allocator.free(entry.name);
allocator.free(entry.section);
allocator.free(entry.description);
if (entry.path) |path| allocator.free(path);
}
try std.testing.expectEqualStrings("grep", entry.name);
try std.testing.expectEqualStrings("1", entry.section);
try std.testing.expectEqualStrings("global regular expression print", entry.description);
try std.testing.expect(entry.path != null);
try std.testing.expectEqualStrings("/usr/share/man/man1/grep.1.gz", entry.path.?);
}
test "ManEntryList - append multiple" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var entries = types.ManEntryList.init(allocator);
defer {
for (entries.items) |entry| {
allocator.free(entry.name);
allocator.free(entry.section);
allocator.free(entry.description);
}
entries.deinit();
}
try entries.append(types.ManEntry{
.name = try allocator.dupe(u8, "ls"),
.section = try allocator.dupe(u8, "1"),
.description = try allocator.dupe(u8, "list"),
.path = null,
});
try entries.append(types.ManEntry{
.name = try allocator.dupe(u8, "cat"),
.section = try allocator.dupe(u8, "1"),
.description = try allocator.dupe(u8, "concatenate"),
.path = null,
});
try entries.append(types.ManEntry{
.name = try allocator.dupe(u8, "grep"),
.section = try allocator.dupe(u8, "1"),
.description = try allocator.dupe(u8, "search"),
.path = null,
});
try std.testing.expectEqual(@as(usize, 3), entries.items.len);
try std.testing.expectEqualStrings("ls", entries.items[0].name);
try std.testing.expectEqualStrings("cat", entries.items[1].name);
try std.testing.expectEqualStrings("grep", entries.items[2].name);
}
test "fallback error handling pattern" {
const result = fakeFast() catch |err| {
try std.testing.expect(err == error.FastFail);
return fakeOriginal();
};
try std.testing.expectEqual(@as(i32, 42), result);
}

View file

@ -1,240 +0,0 @@
const std = @import("std");
const types = @import("../../src/types.zig");
test "ManEntry.getSectionMarker - commands" {
const entry = types.ManEntry{
.name = "ls",
.section = "1",
.description = "list directory contents",
.path = null,
};
const marker = entry.getSectionMarker();
try std.testing.expectEqualStrings("[*] Command", marker);
}
test "ManEntry.getSectionMarker - syscalls" {
const entry = types.ManEntry{
.name = "open",
.section = "2",
.description = "open file",
.path = null,
};
const marker = entry.getSectionMarker();
try std.testing.expectEqualStrings("[S] Syscall", marker);
}
test "ManEntry.getSectionMarker - C library" {
const entry = types.ManEntry{
.name = "printf",
.section = "3",
.description = "format and print data",
.path = null,
};
const marker = entry.getSectionMarker();
try std.testing.expectEqualStrings("[L] C Library", marker);
}
test "ManEntry.getSectionMarker - OpenSSL library" {
const entry = types.ManEntry{
.name = "SSL_new",
.section = "3ssl",
.description = "create SSL structure",
.path = null,
};
const marker = entry.getSectionMarker();
try std.testing.expectEqualStrings("[L] OpenSSL", marker);
}
test "ManEntry.getSectionMarker - POSIX library" {
const entry = types.ManEntry{
.name = "pthread_create",
.section = "3p",
.description = "create thread",
.path = null,
};
const marker = entry.getSectionMarker();
try std.testing.expectEqualStrings("[L] POSIX", marker);
}
test "ManEntry.getSectionMarker - device files" {
const entry = types.ManEntry{
.name = "null",
.section = "4",
.description = "null device",
.path = null,
};
const marker = entry.getSectionMarker();
try std.testing.expectEqualStrings("[D] Device", marker);
}
test "ManEntry.getSectionMarker - file formats" {
const entry = types.ManEntry{
.name = "passwd",
.section = "5",
.description = "password file format",
.path = null,
};
const marker = entry.getSectionMarker();
try std.testing.expectEqualStrings("[F] Format", marker);
}
test "ManEntry.getSectionMarker - games" {
const entry = types.ManEntry{
.name = "tetris",
.section = "6",
.description = "Tetris game",
.path = null,
};
const marker = entry.getSectionMarker();
try std.testing.expectEqualStrings("[G] Game", marker);
}
test "ManEntry.getSectionMarker - misc" {
const entry = types.ManEntry{
.name = "ascii",
.section = "7",
.description = "ASCII table",
.path = null,
};
const marker = entry.getSectionMarker();
try std.testing.expectEqualStrings("[M] Misc", marker);
}
test "ManEntry.getSectionMarker - admin" {
const entry = types.ManEntry{
.name = "useradd",
.section = "8",
.description = "add user",
.path = null,
};
const marker = entry.getSectionMarker();
try std.testing.expectEqualStrings("[A] Admin", marker);
}
test "ManEntry.getSectionMarker - kernel" {
const entry = types.ManEntry{
.name = "kmalloc",
.section = "9",
.description = "kernel memory alloc",
.path = null,
};
const marker = entry.getSectionMarker();
try std.testing.expectEqualStrings("[K] Kernel", marker);
}
test "ManEntry.getSectionMarker - unknown" {
const entry = types.ManEntry{
.name = "unknown",
.section = "99",
.description = "unknown section",
.path = null,
};
const marker = entry.getSectionMarker();
try std.testing.expectEqualStrings("[?] Unknown", marker);
}
test "ManEntry.getSectionMarker - empty section" {
const entry = types.ManEntry{
.name = "test",
.section = "",
.description = "test",
.path = null,
};
const marker = entry.getSectionMarker();
try std.testing.expectEqualStrings("[?] Unknown", marker);
}
test "ManEntry.matchesSection - no target sections" {
const entry = types.ManEntry{
.name = "ls",
.section = "1",
.description = "list",
.path = null,
};
try std.testing.expect(entry.matchesSection(null));
}
test "ManEntry.matchesSection - exact match" {
const entry = types.ManEntry{
.name = "ls",
.section = "1",
.description = "list",
.path = null,
};
const targets = &[_][]const u8{"1"};
try std.testing.expect(entry.matchesSection(targets));
}
test "ManEntry.matchesSection - prefix match" {
const entry = types.ManEntry{
.name = "SSL_new",
.section = "3ssl",
.description = "SSL",
.path = null,
};
const targets = &[_][]const u8{"3"};
try std.testing.expect(entry.matchesSection(targets));
}
test "ManEntry.matchesSection - no match" {
const entry = types.ManEntry{
.name = "ls",
.section = "1",
.description = "list",
.path = null,
};
const targets = &[_][]const u8{"3"};
try std.testing.expect(!entry.matchesSection(targets));
}
test "ManEntry.matchesSection - multiple targets" {
const entry = types.ManEntry{
.name = "printf",
.section = "3",
.description = "print",
.path = null,
};
const targets = &[_][]const u8{"1", "3", "8"};
try std.testing.expect(entry.matchesSection(targets));
}
test "ManEntryList - basic operations" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var list = types.ManEntryList.init(allocator);
defer {
for (list.items) |entry| {
allocator.free(entry.name);
allocator.free(entry.section);
allocator.free(entry.description);
if (entry.path) |path| allocator.free(path);
}
list.deinit();
}
try list.append(types.ManEntry{
.name = try allocator.dupe(u8, "ls"),
.section = try allocator.dupe(u8, "1"),
.description = try allocator.dupe(u8, "list"),
.path = null,
});
try std.testing.expectEqual(@as(usize, 1), list.items.len);
try std.testing.expectEqualStrings("ls", list.items[0].name);
}
test "SearchConfig - struct creation" {
const config = types.SearchConfig{
.keyword = "test",
.target_sections = null,
.show_paths = false,
.verbose = true,
};
try std.testing.expectEqualStrings("test", config.keyword);
try std.testing.expect(config.target_sections == null);
try std.testing.expect(!config.show_paths);
try std.testing.expect(config.verbose);
}