Skip to content

Commit

Permalink
Merge pull request #3 from dylibso/mock-json-helper
Browse files Browse the repository at this point in the history
feat: add mock input json helper
  • Loading branch information
nilslice authored May 22, 2024
2 parents 67976fc + 9b2abdf commit 20f685d
Show file tree
Hide file tree
Showing 8 changed files with 80 additions and 8 deletions.
11 changes: 8 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,13 @@ jobs:
- name: Build example
run: zig build

- name: Test example
- name: Test basic example
run: |
# this is configured by the `xtp.toml` in the root
xtp plugin test
# this is configured by `examples/basic/xtp.toml`
xtp plugin test --path examples/basic
- name: Test json example
run: |
# this is configured by `examples/json/xtp.toml`
xtp plugin test --path examples/json
16 changes: 14 additions & 2 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,27 @@ pub fn build(b: *std.Build) void {

var basic_test = b.addExecutable(.{
.name = "basic-test",
.root_source_file = b.path("examples/basic.zig"),
.root_source_file = b.path("examples/basic/basic.zig"),
.target = target,
.optimize = optimize,
});
basic_test.rdynamic = true;
basic_test.entry = .disabled; // or, add an empty `pub fn main() void {}` in your code
basic_test.root_module.addImport("xtp-test", xtp_test_module);

b.installArtifact(basic_test);
const basic_test_step = b.step("basic_test", "Build basic_test");
basic_test_step.dependOn(b.getInstallStep());

var json_test = b.addExecutable(.{
.name = "json-test",
.root_source_file = b.path("examples/json/json.zig"),
.target = target,
.optimize = optimize,
});
json_test.rdynamic = true;
json_test.entry = .disabled; // or, add an empty `pub fn main() void {}` in your code
json_test.root_module.addImport("xtp-test", xtp_test_module);
b.installArtifact(json_test);
const json_test_step = b.step("json_test", "Build json_test");
json_test_step.dependOn(b.getInstallStep());
}
2 changes: 1 addition & 1 deletion build.zig.zon
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
.name = "xtp-test",
// This is a [Semantic Version](https://semver.org/).
// In a future version of Zig it will be used for package deduplication.
.version = "0.0.0",
.version = "0.1.0",

// This field is optional.
// This is currently advisory only; Zig does not yet do anything
Expand Down
2 changes: 1 addition & 1 deletion examples/basic.zig → examples/basic/basic.zig
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export fn @"test"() i32 {
xtp_test.assertLt("it should be fast", sec, 0.5);

const ns = xtp_test.timeNs("count_vowels", "this is a test");
xtp_test.assertLt("it should be really fast", ns, 1e5);
xtp_test.assertLt("it should be really fast", ns, 2e6);
simple_group.close();

return 0;
Expand Down
2 changes: 1 addition & 1 deletion xtp.toml → examples/basic/xtp.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ mock_input = { data = "this is my mock input data" }
name = "basic - file input"
build = "zig build basic_test"
with = "zig-out/bin/basic-test.wasm"
mock_input = { file = "examples/basic.zig" }
mock_input = { file = "examples/basic/basic.zig" }
22 changes: 22 additions & 0 deletions examples/json/json.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
const std = @import("std");
const Test = @import("xtp-test").Test;

const CountVowel = struct {
total: u32,
count: u32,
vowels: []const u8,
};

export fn @"test"() i32 {
const xtp_test = Test.init(std.heap.wasm_allocator);
const json = xtp_test.mockInputJson(CountVowel, null) catch unreachable;
defer json.deinit();
const input: CountVowel = json.value();
const example = CountVowel{ .total = 2, .count = 1, .vowels = "aeiouAEIOU" };

xtp_test.assertEq("json works (consistent .count)", input.count, example.count);
xtp_test.assertEq("json works (consistent .total)", input.total, example.total);
xtp_test.assert("json works (consistent .vowels)", std.mem.eql(u8, input.vowels, example.vowels), "expected count and vowels fields to match after conversion");

return 0;
}
7 changes: 7 additions & 0 deletions examples/json/xtp.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
bin = "https://raw.githubusercontent.com/extism/extism/main/wasm/code.wasm"

[[test]]
name = "json"
build = "zig build json_test"
with = "zig-out/bin/json-test.wasm"
mock_input = { data = '{ "count": 1, "total": 2, "vowels": "aeiouAEIOU" }' }
26 changes: 26 additions & 0 deletions src/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,21 @@ const harness = @import("harness.zig");

const FORMAT_FAILED = "-- test runner failed to format reason --";

pub fn Json(comptime T: type) type {
return struct {
parsed: std.json.Parsed(T),
slice: []const u8,

pub fn value(self: @This()) T {
return self.parsed.value;
}

pub fn deinit(self: @This()) void {
self.parsed.deinit();
}
};
}

pub const Test = struct {
plugin: Plugin,

Expand All @@ -29,6 +44,17 @@ pub const Test = struct {
}
}

// Parse mock input as JSON, returning a Json(T) struct.
// NOTE: it is the caller's responsibility to .deinit() the Json(T) struct when done with it.
pub fn mockInputJson(self: Test, comptime T: type, options: ?std.json.ParseOptions) !Json(T) {
const default_opts = .{ .allocate = .alloc_always, .ignore_unknown_fields = true };
const bytes = self.mockInput() orelse return error.NoMockInput;
const out = try std.json.parseFromSlice(T, self.plugin.allocator, bytes, options orelse default_opts);
const FromJson = Json(T);
const input = FromJson{ .parsed = out, .slice = bytes };
return input;
}

// Call a function from the Extism plugin being tested, passing input and returning its output.
pub fn call(self: Test, func_name: []const u8, input: []const u8) ![]const u8 {
const func_mem = self.plugin.allocateBytes(func_name);
Expand Down

0 comments on commit 20f685d

Please sign in to comment.