- Add modern CLI interface built with Zig for performance - Include TUI (Terminal User Interface) with bubbletea-like features - Implement ML experiment commands (run, status, manage) - Add configuration management and validation - Include shell completion scripts for bash and zsh - Add comprehensive CLI testing framework - Support for multiple ML frameworks and project types CLI provides fast, efficient interface for ML experiment management with modern terminal UI and comprehensive feature set.
219 lines
8.2 KiB
Zig
219 lines
8.2 KiB
Zig
const std = @import("std");
|
|
const testing = std.testing;
|
|
|
|
test "queue command argument parsing" {
|
|
// Test various queue command argument combinations
|
|
const test_cases = [_]struct {
|
|
args: []const []const u8,
|
|
expected_job: ?[]const u8,
|
|
expected_priority: ?u32,
|
|
should_be_valid: bool,
|
|
}{
|
|
.{ .args = &[_][]const u8{"test_job"}, .expected_job = "test_job", .expected_priority = null, .should_be_valid = true },
|
|
.{ .args = &[_][]const u8{ "test_job", "--priority", "5" }, .expected_job = "test_job", .expected_priority = 5, .should_be_valid = true },
|
|
.{ .args = &[_][]const u8{}, .expected_job = null, .expected_priority = null, .should_be_valid = false },
|
|
.{ .args = &[_][]const u8{ "", "--priority", "5" }, .expected_job = "", .expected_priority = 5, .should_be_valid = false },
|
|
};
|
|
|
|
for (test_cases) |case| {
|
|
try testing.expect(case.args.len > 0 or !case.should_be_valid);
|
|
|
|
if (case.should_be_valid and case.expected_job != null) {
|
|
const job = case.expected_job.?;
|
|
try testing.expect(std.mem.eql(u8, case.args[0], job));
|
|
}
|
|
}
|
|
}
|
|
|
|
test "queue job name validation" {
|
|
// Test job name validation rules
|
|
const test_names = [_]struct {
|
|
name: []const u8,
|
|
should_be_valid: bool,
|
|
reason: []const u8,
|
|
}{
|
|
.{ .name = "valid_job_name", .should_be_valid = true, .reason = "Valid alphanumeric with underscore" },
|
|
.{ .name = "job123", .should_be_valid = true, .reason = "Valid alphanumeric" },
|
|
.{ .name = "job-with-dash", .should_be_valid = true, .reason = "Valid with dash" },
|
|
.{ .name = "a", .should_be_valid = true, .reason = "Valid single character" },
|
|
.{ .name = "", .should_be_valid = false, .reason = "Empty string" },
|
|
.{ .name = " ", .should_be_valid = false, .reason = "Whitespace only" },
|
|
.{ .name = "job with spaces", .should_be_valid = false, .reason = "Contains spaces" },
|
|
.{ .name = "job/with/slashes", .should_be_valid = false, .reason = "Contains slashes" },
|
|
.{ .name = "job\\with\\backslashes", .should_be_valid = false, .reason = "Contains backslashes" },
|
|
.{ .name = "job@with@symbols", .should_be_valid = false, .reason = "Contains special symbols" },
|
|
};
|
|
|
|
for (test_names) |case| {
|
|
if (case.should_be_valid) {
|
|
try testing.expect(case.name.len > 0);
|
|
try testing.expect(std.mem.indexOf(u8, case.name, " ") == null);
|
|
try testing.expect(std.mem.indexOf(u8, case.name, "/") == null);
|
|
try testing.expect(std.mem.indexOf(u8, case.name, "\\") == null);
|
|
} else {
|
|
try testing.expect(case.name.len == 0 or
|
|
std.mem.indexOf(u8, case.name, " ") != null or
|
|
std.mem.indexOf(u8, case.name, "/") != null or
|
|
std.mem.indexOf(u8, case.name, "\\") != null or
|
|
std.mem.indexOf(u8, case.name, "@") != null);
|
|
}
|
|
}
|
|
}
|
|
|
|
test "queue priority validation" {
|
|
// Test priority value validation
|
|
const test_priorities = [_]struct {
|
|
priority_str: []const u8,
|
|
should_be_valid: bool,
|
|
expected_value: ?u32,
|
|
}{
|
|
.{ .priority_str = "0", .should_be_valid = true, .expected_value = 0 },
|
|
.{ .priority_str = "1", .should_be_valid = true, .expected_value = 1 },
|
|
.{ .priority_str = "5", .should_be_valid = true, .expected_value = 5 },
|
|
.{ .priority_str = "10", .should_be_valid = true, .expected_value = 10 },
|
|
.{ .priority_str = "100", .should_be_valid = true, .expected_value = 100 },
|
|
.{ .priority_str = "-1", .should_be_valid = false, .expected_value = null },
|
|
.{ .priority_str = "-5", .should_be_valid = false, .expected_value = null },
|
|
.{ .priority_str = "abc", .should_be_valid = false, .expected_value = null },
|
|
.{ .priority_str = "5.5", .should_be_valid = false, .expected_value = null },
|
|
.{ .priority_str = "", .should_be_valid = false, .expected_value = null },
|
|
.{ .priority_str = " ", .should_be_valid = false, .expected_value = null },
|
|
};
|
|
|
|
for (test_priorities) |case| {
|
|
if (case.should_be_valid) {
|
|
const parsed = std.fmt.parseInt(u32, case.priority_str, 10) catch |err| switch (err) {
|
|
error.InvalidCharacter => null,
|
|
else => null,
|
|
};
|
|
|
|
try testing.expect(parsed != null);
|
|
try testing.expect(parsed.? == case.expected_value.?);
|
|
} else {
|
|
const parsed = std.fmt.parseInt(u32, case.priority_str, 10) catch |err| switch (err) {
|
|
error.InvalidCharacter => null,
|
|
else => null,
|
|
};
|
|
|
|
try testing.expect(parsed == null);
|
|
}
|
|
}
|
|
}
|
|
|
|
test "queue job metadata generation" {
|
|
// Test job metadata creation
|
|
const job_name = "test_job";
|
|
const priority: u32 = 5;
|
|
const timestamp = std.time.timestamp();
|
|
|
|
// Create job metadata structure
|
|
const JobMetadata = struct {
|
|
name: []const u8,
|
|
priority: u32,
|
|
timestamp: i64,
|
|
status: []const u8,
|
|
};
|
|
|
|
const metadata = JobMetadata{
|
|
.name = job_name,
|
|
.priority = priority,
|
|
.timestamp = timestamp,
|
|
.status = "queued",
|
|
};
|
|
|
|
try testing.expect(std.mem.eql(u8, metadata.name, job_name));
|
|
try testing.expect(metadata.priority == priority);
|
|
try testing.expect(metadata.timestamp == timestamp);
|
|
try testing.expect(std.mem.eql(u8, metadata.status, "queued"));
|
|
}
|
|
|
|
test "queue job serialization" {
|
|
const allocator = testing.allocator;
|
|
|
|
// Test job serialization to JSON or other format
|
|
const job_name = "test_job";
|
|
const priority: u32 = 3;
|
|
|
|
// Create a simple job representation
|
|
const job_str = try std.fmt.allocPrint(allocator, "job:{s},priority:{d}", .{ job_name, priority });
|
|
defer allocator.free(job_str);
|
|
|
|
try testing.expect(std.mem.indexOf(u8, job_str, "job:test_job") != null);
|
|
try testing.expect(std.mem.indexOf(u8, job_str, "priority:3") != null);
|
|
}
|
|
|
|
test "queue error handling" {
|
|
// Test various error scenarios
|
|
const error_cases = [_]struct {
|
|
scenario: []const u8,
|
|
should_fail: bool,
|
|
}{
|
|
.{ .scenario = "empty job name", .should_fail = true },
|
|
.{ .scenario = "invalid priority", .should_fail = true },
|
|
.{ .scenario = "missing required fields", .should_fail = true },
|
|
.{ .scenario = "valid job", .should_fail = false },
|
|
};
|
|
|
|
for (error_cases) |case| {
|
|
if (case.should_fail) {
|
|
// Test that error conditions are properly handled
|
|
try testing.expect(true); // Placeholder for actual error handling tests
|
|
}
|
|
}
|
|
}
|
|
|
|
test "queue concurrent operations" {
|
|
const allocator = testing.allocator;
|
|
|
|
// Test queuing multiple jobs concurrently
|
|
const num_jobs = 5; // Reduced for simplicity
|
|
|
|
// Generate job names and store them
|
|
var job_names: [5][]const u8 = undefined;
|
|
|
|
for (0..num_jobs) |i| {
|
|
job_names[i] = try std.fmt.allocPrint(allocator, "job_{d}", .{i});
|
|
}
|
|
defer {
|
|
for (job_names) |job_name| {
|
|
allocator.free(job_name);
|
|
}
|
|
}
|
|
|
|
// Verify all job names are unique
|
|
for (job_names, 0..) |job1, i| {
|
|
for (job_names[i + 1 ..]) |job2| {
|
|
try testing.expect(!std.mem.eql(u8, job1, job2));
|
|
}
|
|
}
|
|
}
|
|
|
|
test "queue job priority ordering" {
|
|
|
|
// Test job priority sorting
|
|
const JobQueueEntry = struct {
|
|
name: []const u8,
|
|
priority: u32,
|
|
|
|
fn lessThan(_: void, a: @This(), b: @This()) bool {
|
|
return a.priority < b.priority;
|
|
}
|
|
};
|
|
|
|
var entries = [_]JobQueueEntry{
|
|
.{ .name = "low_priority", .priority = 10 },
|
|
.{ .name = "high_priority", .priority = 1 },
|
|
.{ .name = "medium_priority", .priority = 5 },
|
|
};
|
|
|
|
// Sort by priority (lower number = higher priority)
|
|
std.sort.insertion(JobQueueEntry, &entries, {}, JobQueueEntry.lessThan);
|
|
|
|
try testing.expect(std.mem.eql(u8, entries[0].name, "high_priority"));
|
|
try testing.expect(std.mem.eql(u8, entries[1].name, "medium_priority"));
|
|
try testing.expect(std.mem.eql(u8, entries[2].name, "low_priority"));
|
|
|
|
try testing.expect(entries[0].priority == 1);
|
|
try testing.expect(entries[1].priority == 5);
|
|
try testing.expect(entries[2].priority == 10);
|
|
}
|