152 lines
4.2 KiB
Zig
152 lines
4.2 KiB
Zig
const interface = @import("interface.zig");
|
|
const Interface = interface.Interface;
|
|
const SelfType = interface.SelfType;
|
|
|
|
const std = @import("std");
|
|
const mem = std.mem;
|
|
const expectEqual = std.testing.expectEqual;
|
|
const assert = std.debug.assert;
|
|
|
|
test "Simple NonOwning interface" {
|
|
const NonOwningTest = struct {
|
|
fn run() !void {
|
|
const Fooer = Interface(struct {
|
|
foo: fn (*SelfType) usize,
|
|
}, interface.Storage.NonOwning);
|
|
|
|
const TestFooer = struct {
|
|
const Self = @This();
|
|
|
|
state: usize,
|
|
|
|
pub fn foo(self: *Self) usize {
|
|
const tmp = self.state;
|
|
self.state += 1;
|
|
return tmp;
|
|
}
|
|
};
|
|
|
|
var f = TestFooer{ .state = 42 };
|
|
var fooer = try Fooer.init(.{&f});
|
|
defer fooer.deinit();
|
|
|
|
expectEqual(@as(usize, 42), fooer.call("foo", .{}));
|
|
expectEqual(@as(usize, 43), fooer.call("foo", .{}));
|
|
}
|
|
};
|
|
|
|
try NonOwningTest.run();
|
|
comptime try NonOwningTest.run();
|
|
}
|
|
|
|
test "Comptime only interface" {
|
|
const TestIFace = Interface(struct {
|
|
foo: fn (*SelfType, u8) u8,
|
|
}, interface.Storage.Comptime);
|
|
|
|
const TestType = struct {
|
|
const Self = @This();
|
|
|
|
state: u8,
|
|
|
|
pub fn foo(self: Self, a: u8) u8 {
|
|
return self.state + a;
|
|
}
|
|
};
|
|
|
|
comptime var iface = try TestIFace.init(.{TestType{ .state = 0 }});
|
|
expectEqual(@as(u8, 42), iface.call("foo", .{42}));
|
|
}
|
|
|
|
test "Owning interface with optional function" {
|
|
const OwningOptionalFuncTest = struct {
|
|
fn run() !void {
|
|
const TestOwningIface = Interface(struct {
|
|
someFn: ?fn (*const SelfType, usize, usize) usize,
|
|
otherFn: fn (*SelfType, usize) anyerror!void,
|
|
}, interface.Storage.Owning);
|
|
|
|
const TestStruct = struct {
|
|
const Self = @This();
|
|
|
|
state: usize,
|
|
|
|
pub fn someFn(self: Self, a: usize, b: usize) usize {
|
|
return self.state * a + b;
|
|
}
|
|
|
|
// Note that our return type need only coerce to the virtual function's
|
|
// return type.
|
|
pub fn otherFn(self: *Self, new_state: usize) void {
|
|
self.state = new_state;
|
|
}
|
|
};
|
|
|
|
var iface_instance = try TestOwningIface.init(.{ comptime TestStruct{ .state = 0 }, std.testing.allocator });
|
|
defer iface_instance.deinit();
|
|
|
|
try iface_instance.call("otherFn", .{100});
|
|
expectEqual(@as(usize, 42), iface_instance.call("someFn", .{ 0, 42 }).?);
|
|
}
|
|
};
|
|
|
|
try OwningOptionalFuncTest.run();
|
|
}
|
|
|
|
test "Interface with virtual async function implemented by an async function" {
|
|
const AsyncIFace = Interface(struct {
|
|
const async_call_stack_size = 1024;
|
|
|
|
foo: fn (*SelfType) callconv(.Async) void,
|
|
}, interface.Storage.NonOwning);
|
|
|
|
const Impl = struct {
|
|
const Self = @This();
|
|
|
|
state: usize,
|
|
frame: anyframe = undefined,
|
|
|
|
pub fn foo(self: *Self) void {
|
|
suspend {
|
|
self.frame = @frame();
|
|
}
|
|
self.state += 1;
|
|
suspend;
|
|
self.state += 1;
|
|
}
|
|
};
|
|
|
|
var i = Impl{ .state = 0 };
|
|
var instance = try AsyncIFace.init(.{&i});
|
|
_ = async instance.call("foo", .{});
|
|
|
|
expectEqual(@as(usize, 0), i.state);
|
|
resume i.frame;
|
|
expectEqual(@as(usize, 1), i.state);
|
|
resume i.frame;
|
|
expectEqual(@as(usize, 2), i.state);
|
|
}
|
|
|
|
test "Interface with virtual async function implemented by a blocking function" {
|
|
const AsyncIFace = Interface(struct {
|
|
readBytes: fn (*SelfType, []u8) callconv(.Async) anyerror!void,
|
|
}, interface.Storage.Inline(8));
|
|
|
|
const Impl = struct {
|
|
const Self = @This();
|
|
|
|
pub fn readBytes(self: Self, outBuf: []u8) void {
|
|
for (outBuf) |*c| {
|
|
c.* = 3;
|
|
}
|
|
}
|
|
};
|
|
|
|
var instance = try AsyncIFace.init(.{Impl{}});
|
|
|
|
var buf: [256]u8 = undefined;
|
|
try await async instance.call("readBytes", .{buf[0..]});
|
|
|
|
expectEqual([_]u8{3} ** 256, buf);
|
|
}
|