Skip to content

Commit

Permalink
revive tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Techatrix committed Mar 26, 2024
1 parent 6c63c4a commit 4db9d23
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 65 deletions.
10 changes: 4 additions & 6 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,16 @@ jobs:

- run: zig env

- run: zig build

- name: Build artifacts
- name: zig build test
run: |
wget https://raw.githubusercontent.com/microsoft/vscode-languageserver-node/main/protocol/metaModel.json
zig build run -- metaModel.json artifacts/lsp.zig
zig build test
- run: zig fmt --ast-check --check artifacts/lsp.zig
- run: zig fmt --ast-check --check zig-out/artifacts/lsp.zig

- name: Upload artifacts
if: ${{ matrix.os == 'ubuntu-latest' }}
uses: actions/upload-artifact@v2
with:
name: builds
path: artifacts/*
path: zig-out/artifacts/*
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ Zig LSP codegen from the newly released, official metamodel! This actually good

1. `git clone`
2. Plop `metaModel.json` in this cloned repo. A copy can be found [here](https://github.com/microsoft/vscode-languageserver-node/blob/main/protocol/metaModel.json).
3. `zig build run -- metaModel.json lsp.zig`
4. Tada! You should now have a `lsp.zig` file that can be used to your heart's content! Enjoy :)
3. `zig build`
4. Tada! You should now have a `zig-out/artifacts/lsp.zig` file that can be used to your heart's content! Enjoy :)
20 changes: 12 additions & 8 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});

const meta_model_path = b.option([]const u8, "meta-model", "Specify path to the metaMode.json") orelse "metaModel.json";

const exe = b.addExecutable(.{
.name = "zig-lsp-codegen",
.root_source_file = .{ .path = "src/main.zig" },
Expand All @@ -12,15 +14,16 @@ pub fn build(b: *std.Build) void {
});
b.installArtifact(exe);

const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep());

if (b.args) |args| {
run_cmd.addArgs(args);
}
const run_codegen = b.addRunArtifact(exe);
run_codegen.addFileArg(.{ .cwd_relative = meta_model_path });
const lsp_output_path = run_codegen.addOutputFileArg("lsp.zig");

const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step);
const lsp_module = b.addModule("lsp", .{
.root_source_file = lsp_output_path,
.target = target,
.optimize = optimize,
});
b.getInstallStep().dependOn(&b.addInstallFile(lsp_output_path, "artifacts/lsp.zig").step);

const test_step = b.step("test", "Run all the tests");
test_step.dependOn(b.getInstallStep());
Expand All @@ -30,6 +33,7 @@ pub fn build(b: *std.Build) void {
.target = target,
.optimize = optimize,
});
tests.root_module.addImport("lsp", lsp_module);

test_step.dependOn(&b.addRunArtifact(tests).step);
}
4 changes: 2 additions & 2 deletions src/base.zig
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ pub const RegExp = []const u8;

pub const LSPAny = std.json.Value;
pub const LSPArray = []LSPAny;
pub const LSPObject = std.json.ObjectMap;
pub const LSPObject = std.json.ArrayHashMap(std.json.Value);

pub const Message = union(enum) {
request: Request,
Expand Down Expand Up @@ -808,7 +808,7 @@ pub fn EnumCustomStringValues(comptime T: type, comptime contains_empty_enum: bo
}

pub fn jsonParse(allocator: std.mem.Allocator, source: anytype, options: std.json.ParseOptions) std.json.ParseError(@TypeOf(source.*))!T {
const slice = try std.json.Value.jsonParse(allocator, source, options);
const slice = try std.json.innerParse([]const u8, allocator, source, options);
if (contains_empty_enum and slice.len == 0) return .empty;
return map.get(slice) orelse return .{ .custom_value = slice };
}
Expand Down
128 changes: 81 additions & 47 deletions tests/tests.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,34 @@ const Server = @This();
const lsp = @import("lsp");
const std = @import("std");

const SampleDirection = enum {
client_to_server,
server_to_client,
};
const SampleEntry = struct {
isLSPMessage: bool,
type: Kind,
message: std.json.Value,
timestamp: u64,

const SampleEntryKind = enum {
@"send-request",
@"receive-request",
const Kind = enum {
@"send-request",
@"receive-request",

@"send-response",
@"receive-response",
@"send-notification",
@"receive-notification",

@"send-notification",
@"receive-notification",
@"send-response",
@"receive-response",

fn getDirection(self: SampleEntryKind) SampleDirection {
return switch (self) {
.@"send-request", .@"send-response", .@"send-notification" => .client_to_server,
else => .server_to_client,
};
}
};
fn getDirection(self: Kind) Direction {
switch (self) {
.@"send-request", .@"send-response", .@"send-notification" => return .client_to_server,
.@"receive-request", .@"receive-notification", .@"receive-response" => return .server_to_client,
}
}
};

// TODO: Handle responses
const SampleEntry = struct {
isLSPMessage: bool,
type: SampleEntryKind,
message: std.json.Value,
const Direction = enum {
client_to_server,
server_to_client,
};
};

test {
Expand All @@ -43,38 +43,72 @@ test {
defer read_buffer.deinit();

while (true) {
try reader.readUntilDelimiterArrayList(&read_buffer, '\n', std.math.maxInt(u32)) catch |err| switch (err) {
read_buffer.clearRetainingCapacity();
reader.readUntilDelimiterArrayList(&read_buffer, '\n', std.math.maxInt(u32)) catch |err| switch (err) {
error.EndOfStream => break,
else => |e| return e,
};

const parsed_sample_entry = try std.json.parseFromSlice(SampleEntry, std.testing.allocator, read_buffer.items, .{});
defer parsed_sample_entry.deinit();
const sample_entry = parsed_sample_entry.value;

if (sample_entry.isLSPMessage) {
switch (sample_entry.type) {
.@"send-notification",
.@"receive-notification",
=> a: {
_ = tres.parse(lsp.Notification, sample_entry.message, std.testing.allocator) catch |err| {
// Ignore unknown methods such as custom VSCode LSP methods
if (err == error.UnknownMethod) break :a;
std.log.err("Cannot handle Request or Notification of method \"{s}\"", .{entry.message.Object.get("method").?.String});
break :a;
};
},
.@"send-request",
.@"receive-request",
=> a: {
_ = tres.parse(lsp.Request, entry.message, std.testing.allocator) catch |err| {
// Ignore unknown methods such as custom VSCode LSP methods
if (err == error.UnknownMethod) break :a;
std.log.err("Cannot handle Request or Notification of method \"{s}\"", .{entry.message.Object.get("method").?.String});
break :a;
};
},
else => {},
}
const message = try std.json.parseFromValue(lsp.Message, std.testing.allocator, sample_entry.message, .{});
defer message.deinit();

const MessageTag = std.meta.Tag(lsp.Message);
const expected_tag: MessageTag = switch (sample_entry.type) {
.@"send-request", .@"receive-request" => .request,
.@"send-notification", .@"receive-notification" => .notification,
.@"send-response", .@"receive-response" => .response,
};
try std.testing.expectEqual(expected_tag, std.meta.activeTag(message.value));
} else {
@panic("TODO");
}
}
}

comptime {
for (lsp.notification_metadata) |metadata| {
if (metadata.Params) |Params| {
testType(Params);
}
}
for (lsp.request_metadata) |metadata| {
if (std.mem.eql(u8, metadata.method, "textDocument/selectionRange")) continue; // TODO
if (metadata.Params) |Params| {
testType(Params);
}
testType(metadata.Result);
if (metadata.PartialResult) |PartialResult| {
testType(PartialResult);
}
if (metadata.ErrorData) |ErrorData| {
testType(ErrorData);
}
}
}

inline fn testType(comptime T: type) void {
if (T == void) return;
if (T == ?void) return;

const S = struct {
fn parseFromValue() void {
_ = std.json.parseFromValue(T, undefined, undefined, undefined) catch unreachable;
}
fn innerParse() void {
var source: std.json.Scanner = undefined;
_ = std.json.innerParse(T, undefined, &source, undefined) catch unreachable;
}
fn stringify() void {
const value: T = undefined;
_ = std.json.stringify(value, undefined, std.io.null_writer) catch unreachable;
}
};
_ = &S.parseFromValue;
_ = &S.innerParse;
_ = &S.stringify;
}

0 comments on commit 4db9d23

Please sign in to comment.