Skip to content

Commit

Permalink
Fix tests
Browse files Browse the repository at this point in the history
  • Loading branch information
sam701 committed Oct 14, 2023
1 parent 4af9683 commit 357397d
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 52 deletions.
107 changes: 71 additions & 36 deletions src/tests.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const Allocator = std.mem.Allocator;

const command = @import("./command.zig");
const ppack = @import("./parser.zig");
const mkRef = @import("./value_ref.zig").mkRef;
const Parser = ppack.Parser;
const ParseResult = ppack.ParseResult;

Expand Down Expand Up @@ -38,10 +39,11 @@ fn run(app: *command.App, items: []const []const u8) !ParseResult {
fn dummy_action(_: []const []const u8) !void {}

test "long option" {
var aa: []const u8 = "test";
var opt = command.Option{
.long_name = "aa",
.help = "option aa",
.value = command.OptionValue{ .string = null },
.value_ref = mkRef(&aa),
};
var cmd = command.App{
.name = "abc",
Expand All @@ -50,18 +52,19 @@ test "long option" {
};

_ = try run(&cmd, &.{ "cmd", "--aa", "val" });
try expect(std.mem.eql(u8, opt.value.string.?, "val"));
try std.testing.expectEqualStrings("val", aa);

_ = try run(&cmd, &.{ "cmd", "--aa=bb" });
try expect(std.mem.eql(u8, opt.value.string.?, "bb"));
try std.testing.expectEqualStrings("bb", aa);
}

test "short option" {
var aa: []const u8 = undefined;
var opt = command.Option{
.long_name = "aa",
.short_alias = 'a',
.help = "option aa",
.value = command.OptionValue{ .string = null },
.value_ref = mkRef(&aa),
};
var app = command.App{
.name = "abc",
Expand All @@ -70,92 +73,124 @@ test "short option" {
};

_ = try run(&app, &.{ "abc", "-a", "val" });
try expect(std.mem.eql(u8, opt.value.string.?, "val"));
try std.testing.expectEqualStrings("val", aa);

_ = try run(&app, &.{ "abc", "-a=bb" });
try expect(std.mem.eql(u8, opt.value.string.?, "bb"));
try std.testing.expectEqualStrings("bb", aa);
}

test "concatenated aliases" {
var bb = command.Option{
var aa: []const u8 = undefined;
var bb: bool = false;
var bbopt = command.Option{
.long_name = "bb",
.short_alias = 'b',
.help = "option bb",
.value = command.OptionValue{ .bool = false },
.value_ref = mkRef(&bb),
};
var opt = command.Option{
.long_name = "aa",
.short_alias = 'a',
.help = "option aa",
.value = command.OptionValue{ .string = null },
.value_ref = mkRef(&aa),
};
var app = command.App{
.name = "abc",
.options = &.{ &bb, &opt },
.options = &.{ &bbopt, &opt },
.action = dummy_action,
};

_ = try run(&app, &.{ "abc", "-ba", "val" });
try expect(std.mem.eql(u8, opt.value.string.?, "val"));
try expect(bb.value.bool);
try std.testing.expectEqualStrings("val", aa);
try expect(bb);
}

test "int and float" {
var aa = command.Option{
var aa: i32 = undefined;
var bb: f64 = undefined;
var aa_opt = command.Option{
.long_name = "aa",
.help = "option aa",
.value = command.OptionValue{ .int = null },
.value_ref = mkRef(&aa),
};
var bb = command.Option{
var bb_opt = command.Option{
.long_name = "bb",
.help = "option bb",
.value = command.OptionValue{ .float = null },
.value_ref = mkRef(&bb),
};
var app = command.App{
.name = "abc",
.options = &.{ &aa, &bb },
.options = &.{ &aa_opt, &bb_opt },
.action = dummy_action,
};

_ = try run(&app, &.{ "abc", "--aa=34", "--bb", "15.25" });
try expect(aa.value.int.? == 34);
try expect(bb.value.float.? == 15.25);
try expect(34 == aa);
try expect(15.25 == bb);
}

test "int list" {
var aa: []u64 = undefined;
var aa_opt = command.Option{
.long_name = "aa",
.short_alias = 'a',
.help = "option aa",
.value_ref = mkRef(&aa),
};
var app = command.App{
.name = "abc",
.options = &.{&aa_opt},
.action = dummy_action,
};

_ = try run(&app, &.{ "abc", "--aa=100", "--aa", "200", "-a", "300", "-a=400" });
try expect(aa.len == 4);
try expect(aa[0] == 100);
try expect(aa[1] == 200);
try expect(aa[2] == 300);
try expect(aa[3] == 400);

// FIXME: it tries to deallocated u64 while the memory was allocated using u8 alignment
alloc.free(aa);
}

test "string list" {
var aa = command.Option{
var aa: [][]const u8 = undefined;
var aa_opt = command.Option{
.long_name = "aa",
.short_alias = 'a',
.help = "option aa",
.value = command.OptionValue{ .string_list = null },
.value_ref = mkRef(&aa),
};
var app = command.App{
.name = "abc",
.options = &.{&aa},
.options = &.{&aa_opt},
.action = dummy_action,
};

_ = try run(&app, &.{ "abc", "--aa=a1", "--aa", "a2", "-a", "a3", "-a=a4" });
try expect(aa.value.string_list.?.len == 4);
try expect(std.mem.eql(u8, aa.value.string_list.?[0], "a1"));
try expect(std.mem.eql(u8, aa.value.string_list.?[1], "a2"));
try expect(std.mem.eql(u8, aa.value.string_list.?[2], "a3"));
try expect(std.mem.eql(u8, aa.value.string_list.?[3], "a4"));
try expect(aa.len == 4);
try std.testing.expectEqualStrings("a1", aa[0]);
try std.testing.expectEqualStrings("a2", aa[1]);
try std.testing.expectEqualStrings("a3", aa[2]);
try std.testing.expectEqualStrings("a4", aa[3]);

alloc.free(aa.value.string_list.?);
alloc.free(aa);
}

test "mix positional arguments and options" {
var aav: []const u8 = undefined;
var bbv: []const u8 = undefined;
var aa = command.Option{
.long_name = "aa",
.short_alias = 'a',
.help = "option aa",
.value = command.OptionValue{ .string = null },
.value_ref = mkRef(&aav),
};
var bb = command.Option{
.long_name = "bb",
.help = "option bb",
.value = command.OptionValue{ .string = null },
.value_ref = mkRef(&bbv),
};
var app = command.App{
.name = "abc",
Expand All @@ -165,11 +200,11 @@ test "mix positional arguments and options" {

var result = try run(&app, &.{ "cmd", "--bb", "tt", "arg1", "-a", "val", "arg2", "--", "--arg3", "-arg4" });
defer std.testing.allocator.free(result.args);
try expect(std.mem.eql(u8, aa.value.string.?, "val"));
try expect(std.mem.eql(u8, bb.value.string.?, "tt"));
try std.testing.expectEqualStrings("val", aav);
try std.testing.expectEqualStrings("tt", bbv);
try expect(result.args.len == 4);
try expect(std.mem.eql(u8, result.args[0], "arg1"));
try expect(std.mem.eql(u8, result.args[1], "arg2"));
try expect(std.mem.eql(u8, result.args[2], "--arg3"));
try expect(std.mem.eql(u8, result.args[3], "-arg4"));
try std.testing.expectEqualStrings("arg1", result.args[0]);
try std.testing.expectEqualStrings("arg2", result.args[1]);
try std.testing.expectEqualStrings("--arg3", result.args[2]);
try std.testing.expectEqualStrings("-arg4", result.args[3]);
}
2 changes: 1 addition & 1 deletion src/value_parser.zig
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ fn intData(comptime ValueType: type, comptime DestinationType: type) ValueData {
.value_size = @sizeOf(DestinationType),
.value_parser = struct {
fn parser(dest: *anyopaque, value: []const u8) anyerror!void {
const dt: *DestinationType = @ptrCast(@alignCast(dest));
const dt: *DestinationType = @alignCast(@ptrCast(dest));
dt.* = try std.fmt.parseInt(ValueType, value, 10);
}
}.parser,
Expand Down
67 changes: 52 additions & 15 deletions src/value_ref.zig
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const std = @import("std");
const command = @import("./command.zig");
const vp = @import("./value_parser.zig");
const Allocator = std.mem.Allocator;

pub const ValueRef = struct {
dest: *anyopaque,
Expand All @@ -10,42 +11,41 @@ pub const ValueRef = struct {

const Self = @This();

pub fn put(self: *Self, value: []const u8, alloc: std.mem.Allocator) anyerror!void {
pub fn put(self: *Self, value: []const u8, alloc: Allocator) anyerror!void {
self.element_count += 1;
switch (self.value_type) {
.single => {
self.element_count += 1;
return self.value_data.value_parser(self.dest, value);
},
.multi => |*list| {
self.element_count += 1;
try list.ensureTotalCapacity(alloc, self.element_count * self.value_data.value_size);
const value_ptr = list.items.ptr + ((self.element_count - 1) * self.value_data.value_size);
if (list.list_ptr == null) {
list.list_ptr = try list.vtable.createList(alloc);
}
var value_ptr = try list.vtable.addOne(list.list_ptr.?, alloc);
try self.value_data.value_parser(value_ptr, value);
list.items.len += self.value_data.value_size;
},
}
}

pub fn finalize(self: *Self, alloc: std.mem.Allocator) anyerror!void {
pub fn finalize(self: *Self, alloc: Allocator) anyerror!void {
switch (self.value_type) {
.single => {},
.multi => |*list| {
var sl = try list.toOwnedSlice(alloc);
sl.len = self.element_count;

var dest: *[]u8 = @alignCast(@ptrCast(self.dest));
dest.* = sl;
if (list.list_ptr == null) {
list.list_ptr = try list.vtable.createList(alloc);
}
try list.vtable.finalize(list.list_ptr.?, self.dest, alloc);
},
}
}
};

const ValueType = union(enum) {
single,
multi: std.ArrayListUnmanaged(u8),
multi: ValueList,
};

const AllocError = std.mem.Allocator.Error;
const AllocError = Allocator.Error;
pub const Error = AllocError; // | error{NotImplemented};

pub fn mkRef(dest: anytype) ValueRef {
Expand All @@ -66,7 +66,7 @@ pub fn mkRef(dest: anytype) ValueRef {
return ValueRef{
.dest = @ptrCast(dest),
.value_data = vp.getValueData(pinfo.child),
.value_type = ValueType{ .multi = std.ArrayListUnmanaged(u8){} },
.value_type = ValueType{ .multi = ValueList.init(pinfo.child) },
};
}
},
Expand All @@ -82,3 +82,40 @@ pub fn mkRef(dest: anytype) ValueRef {
},
}
}

const ValueList = struct {
list_ptr: ?*anyopaque = null,
vtable: VTable,

const VTable = struct {
createList: *const fn (Allocator) anyerror!*anyopaque,
addOne: *const fn (list_ptr: *anyopaque, alloc: Allocator) anyerror!*anyopaque,
finalize: *const fn (list_ptr: *anyopaque, dest: *anyopaque, alloc: Allocator) anyerror!void,
};

fn init(comptime T: type) ValueList {
const List = std.ArrayListUnmanaged(T);
const gen = struct {
fn createList(alloc: Allocator) anyerror!*anyopaque {
var list = try alloc.create(List);
list.* = List{};
return list;
}
fn addOne(list_ptr: *anyopaque, alloc: Allocator) anyerror!*anyopaque {
const list: *List = @alignCast(@ptrCast(list_ptr));
return @ptrCast(try list.addOne(alloc));
}
fn finalize(list_ptr: *anyopaque, dest: *anyopaque, alloc: Allocator) anyerror!void {
const list: *List = @alignCast(@ptrCast(list_ptr));
var destSlice: *[]T = @alignCast(@ptrCast(dest));
destSlice.* = try list.toOwnedSlice(alloc);
alloc.destroy(list);
}
};
return ValueList{ .vtable = VTable{
.createList = gen.createList,
.addOne = gen.addOne,
.finalize = gen.finalize,
} };
}
};

0 comments on commit 357397d

Please sign in to comment.