Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1582,6 +1582,7 @@ pub fn build(b: *std.Build) void {
b.step("test-ts-static-markup-values", "Compare static markup dynamic values, exact payloads, keys and complete failure effects").dependOn(&b.addRunArtifact(filteredTestArtifact(b, ts_core_artifacts.surface_layout.root_module, "ts-static-markup-value-tests", &.{ "static markup values", "compiled markup values" })).step);
b.step("test-ts-static-markup-collections", "Compare static virtual windows, row consumption, exact keys, stepper indices and complete failure effects").dependOn(&b.addRunArtifact(filteredTestArtifact(b, ts_core_artifacts.surface_layout.root_module, "ts-static-markup-collection-tests", &.{ "static markup collections", "compiled markup numeric", "compiled markup specialized" })).step);
b.step("test-ts-static-markup-expansion", "Compare static branches, item sequencing, lexical captures, exact inherited keys and complete failure effects").dependOn(&b.addRunArtifact(filteredTestArtifact(b, ts_core_artifacts.surface_layout.root_module, "ts-static-markup-expansion-tests", &.{ "static markup expansion", "static markup collections" })).step);
b.step("test-ts-static-markup-specialized", "Compare static specialized metadata, forwarded handlers, tab triggers and complete failure effects").dependOn(&b.addRunArtifact(filteredTestArtifact(b, ts_core_artifacts.surface_layout.root_module, "ts-static-markup-specialized-tests", &.{ "static markup", "compiled markup values", "compiled markup metadata", "compiled markup options", "compiled markup postprocessing", "compiled markup numeric", "compiled markup admission", "compiled markup literals" })).step);
b.step("test-ts-markup-postprocessing", "Compare forwarded handlers, provenance, tabs and borrowed binding paths").dependOn(&b.addRunArtifact(filteredTestArtifact(b, ts_core_artifacts.surface_layout.root_module, "ts-markup-postprocessing-tests", &.{ "compiled markup postprocessing", "compiled markup literals", "compiled markup admission", "compiled markup construction" })).step);
b.step("test-ts-markup-literals", "Compare exact literal inference, template defaults and lexical capture ownership").dependOn(&b.addRunArtifact(filteredTestArtifact(b, ts_core_artifacts.surface_layout.root_module, "ts-markup-literal-tests", &.{ "compiled markup literals", "compiled markup admission", "compiled markup construction" })).step);
b.step("test-ts-markup-admission", "Compare specialized attribute forms, raw flags and complete eager failure effects").dependOn(&b.addRunArtifact(filteredTestArtifact(b, ts_core_artifacts.surface_layout.root_module, "ts-markup-admission-tests", &.{ "compiled markup admission", "compiled markup specialized", "compiled markup options", "compiled markup metadata" })).step);
Expand Down
5 changes: 4 additions & 1 deletion packages/core/src/markup_values.ts
Original file line number Diff line number Diff line change
Expand Up @@ -316,10 +316,13 @@ function nscmValues(r: NscElementReader, out: number[], mode: number): void {
const path = r.blob(), dot = nscmDot(path);
// Return borrowed byte offsets; a trailing dot has a present empty tail.
nsceEmit(out, 93, 0, dot < 0 ? path.length : dot, dot < 0 ? 0 : dot + 1);
} else if (mode === 36) {
// A use-site handler replaces the local timeline handler before construction.
nsceEmit(out, r.flag() ? 1 : 0);
} else throw new Error("invalid markup value mode");
}
function nscvMarkupValues(input: Uint8Array): Uint8Array {
if (input.length < 4 || input[0] !== 113 || input[1] !== 1 || input[2]! > 35 || input[3]! > 1) throw new Error("invalid markup value header");
if (input.length < 4 || input[0] !== 113 || input[1] !== 1 || input[2]! > 36 || input[3]! > 1) throw new Error("invalid markup value header");
const r = new NscElementReader(input), out: number[] = [];
nscmValues(r, out, input[2]!);
if (r.at !== input.length) throw new Error("trailing markup value facts");
Expand Down
9 changes: 9 additions & 0 deletions packages/core/test/markup_values.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -302,3 +302,12 @@ test("binding paths retain byte offsets including empty, trailing and malformed
assert.deepEqual(row!.fields, [dot < 0 ? path.length : dot, dot < 0 ? 0 : dot + 1, 0, 0, 0]);
}
});

test("timeline forwarding selects use-site handlers and rejects malformed facts", () => {
assert.deepEqual(actions(new Packet(36).word(0)), [0]);
assert.deepEqual(actions(new Packet(36).word(1)), [1]);
const valid = new Packet(36).word(1).bytes();
for (let end = 0; end < valid.length; end++) assert.throws(() => run(valid.slice(0, end)));
assert.throws(() => run(new Packet(36).word(2).bytes()));
assert.throws(() => run(new Packet(36).word(0, 1).bytes()));
});
307 changes: 306 additions & 1 deletion src/primitives/canvas/ui_markup_compiled.zig

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions src/primitives/canvas/ui_markup_values.zig
Original file line number Diff line number Diff line change
Expand Up @@ -356,3 +356,13 @@ pub fn message(code: u8) []const u8 {
else => @panic("invalid markup value diagnostic"),
};
}

/// The compiled program selects whether a forwarded root handler replaces the local handler.
pub fn forwardedPress(owner: markup.expr.Policy, present: bool) bool {
const row = decide(owner, 36, &.{@intFromBool(present)});
return switch (row.action) {
0 => false,
1 => true,
else => @panic("invalid forwarded press capability"),
};
}
2 changes: 1 addition & 1 deletion src/primitives/canvas/ui_markup_view.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2076,7 +2076,7 @@ pub fn MarkupView(comptime ModelT: type, comptime MsgT: type) type {
options.global_key = try self.attrKey(scope, node, attribute);
},
200 => {
if (forwarded_press) |msg| options.on_press = msg;
if (value_programs.forwardedPress(owner, forwarded_press != null)) options.on_press = forwarded_press.?;
return ui.timelineItem(options);
},
255 => return self.failNode(if (step.fields[0] == 0) node else node.children[step.fields[0] - 1], specialized.message(step.code)),
Expand Down
2 changes: 1 addition & 1 deletion tests/ts-core/markup_admission_e2e_tests.zig
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ fn exact(a: anytype, b: @TypeOf(a)) anyerror!void {
}
}

var operations: [36]usize = @splat(0);
var operations: [256]usize = @splat(0);
var forms: [14]usize = @splat(0);
fn observed(input: []const u8, output: []u8) usize {
if (input[0] == 113) {
Expand Down
2 changes: 1 addition & 1 deletion tests/ts-core/markup_literals_e2e_tests.zig
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ fn exact(a: anytype, b: @TypeOf(a)) anyerror!void {
}
}

var operations: [36]usize = @splat(0);
var operations: [256]usize = @splat(0);
fn observed(input: []const u8, output: []u8) usize {
if (input[0] == 113) {
operations[input[2]] += 1;
Expand Down
2 changes: 1 addition & 1 deletion tests/ts-core/markup_metadata_e2e_tests.zig
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ fn exact(a: anytype, b: @TypeOf(a)) anyerror!void {
else => try @import("surface_layout_e2e_tests.zig").expectComplete(a, b),
}
}
var operations: [36]usize = @splat(0);
var operations: [256]usize = @splat(0);
fn observed(input: []const u8, output: []u8) usize {
if (input[0] == 113) operations[input[2]] += 1;
const n = core.nativeWindowPolicy(input, output);
Expand Down
2 changes: 1 addition & 1 deletion tests/ts-core/markup_numeric_e2e_tests.zig
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ fn exact(a: anytype, b: @TypeOf(a)) anyerror!void {
}
}

var operations: [36]usize = @splat(0);
var operations: [256]usize = @splat(0);
var channels: [3]usize = @splat(0);
fn observed(input: []const u8, output: []u8) usize {
if (input[0] == 113) {
Expand Down
2 changes: 1 addition & 1 deletion tests/ts-core/markup_options_e2e_tests.zig
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ fn exact(a: anytype, b: @TypeOf(a)) anyerror!void {
else => try @import("surface_layout_e2e_tests.zig").expectComplete(a, b),
}
}
var operations: [36]usize = @splat(0);
var operations: [256]usize = @splat(0);
fn observed(input: []const u8, output: []u8) usize {
if (input[0] == 113) operations[input[2]] += 1;
const n = core.nativeWindowPolicy(input, output);
Expand Down
2 changes: 1 addition & 1 deletion tests/ts-core/markup_values_e2e_tests.zig
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ fn exact(a: anytype, b: @TypeOf(a)) anyerror!void {
else => try @import("surface_layout_e2e_tests.zig").expectComplete(a, b),
}
}
var operations: [36]usize = @splat(0);
var operations: [256]usize = @splat(0);
fn observed(input: []const u8, output: []u8) usize {
if (input[0] == 113) operations[input[2]] += 1;
const n = core.nativeWindowPolicy(input, output);
Expand Down
2 changes: 1 addition & 1 deletion tests/ts-core/static_markup_collections_e2e_tests.zig
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ fn exact(a: anytype, b: @TypeOf(a)) anyerror!void {
}
}

var values: [36]usize = @splat(0);
var values: [256]usize = @splat(0);
var specialized: [21]usize = @splat(0);
fn observed(input: []const u8, output: []u8) usize {
if (input[0] == 113) values[input[2]] += 1;
Expand Down
160 changes: 160 additions & 0 deletions tests/ts-core/static_markup_specialized_e2e_tests.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
const std = @import("std");
const sdk = @import("native_sdk");
const core = @import("surface_fixture_core");
const c = sdk.canvas;
fn exact(a: anytype, b: @TypeOf(a)) anyerror!void {
const T = @TypeOf(a);
switch (@typeInfo(T)) {
.@"struct" => |info| inline for (info.fields) |field| try exact(@field(a, field.name), @field(b, field.name)),
.optional => {
try std.testing.expectEqual(a != null, b != null);
if (a) |value| try exact(value, b.?);
},
.pointer => |info| if (info.size == .slice) {
try std.testing.expectEqual(a.len, b.len);
for (a, b) |l, r| try exact(l, r);
} else if (info.size == .one and (@typeInfo(info.child) == .@"struct" or @typeInfo(info.child) == .@"union" or info.child == bool)) try exact(a.*, b.*) else try std.testing.expectEqual(a, b),
.array => for (a, b) |l, r| try exact(l, r),
.@"union" => {
try std.testing.expectEqual(std.meta.activeTag(a), std.meta.activeTag(b));
switch (a) {
inline else => |value, tag| try exact(value, @field(b, @tagName(tag))),
}
},
else => try @import("surface_layout_e2e_tests.zig").expectComplete(a, b),
}
}

var calls: [4096]u64 = undefined;
var call_count: usize = 0;
var modes: [256]usize = @splat(0);
fn observed(input: []const u8, output: []u8) usize {
if (input[0] == 113) modes[input[2]] += 1;
const count = core.nativeWindowPolicy(input, output);
core.rt.frameReset();
return count;
}
fn event(value: u64) void {
calls[call_count] = value;
call_count += 1;
}
const Model = struct {
lines: []const u8 = "2-3,1,2",
removed: []const u8 = "3",
prefix: []const u8 = "https://example.invalid/issues/",
chosen: i64 = -9007199254740993,
pub fn code(_: *const Model) []const u8 {
event(1);
return "const x = 1;\nreturn x;\n// Done";
}
pub fn added(self: *const Model) []const u8 {
event(2);
return self.lines;
}
pub fn deleted(self: *const Model) []const u8 {
event(3);
return self.removed;
}
pub fn body(_: *const Model) []const u8 {
event(4);
return "# Document\nIssue #12\n<details><summary>More</summary>Body</details>";
}
pub fn base(self: *const Model) []const u8 {
event(5);
return self.prefix;
}
pub fn title(_: *const Model) []const u8 {
event(6);
return "Borrowed title";
}
};
const Msg = union(enum) { tick, local, selected: i64, link: []const u8, details: usize };
const Ui = c.Ui(Msg);
const unlimited = std.math.maxInt(usize);
const Outcome = struct { allocations: usize, resizes: usize, calls: usize };
fn aliases(a: Ui.Node, b: Ui.Node) anyerror!void {
if (a.on_press) |message| if (message == .selected) try std.testing.expectEqual(message.selected, b.on_press.?.selected);
for (a.nodes, b.nodes) |left, right| try aliases(left, right);
}
fn compare(comptime source: []const u8, model: Model, failure: usize, resize: usize) !Outcome {
const View = c.CompiledMarkupView(Model, Msg, source);
var aa = std.heap.ArenaAllocator.init(std.testing.allocator);
defer aa.deinit();
var ab = std.heap.ArenaAllocator.init(std.testing.allocator);
defer ab.deinit();
var fa = std.testing.FailingAllocator.init(aa.allocator(), .{ .fail_index = failure, .resize_fail_index = resize });
var fb = std.testing.FailingAllocator.init(ab.allocator(), .{ .fail_index = failure, .resize_fail_index = resize });
var left = Ui.init(fa.allocator());
var right = Ui.init(fb.allocator());
right.expression_policy = observed;
call_count = 0;
const a = View.build(&left, &model);
const before = calls;
const count = call_count;
call_count = 0;
const b = View.build(&right, &model);
errdefer std.debug.print("static specialized source={s} failure={d} resize={d}\n", .{ source, failure, resize });
try exact(before[0..count], calls[0..call_count]);
try exact(a, b);
try aliases(a, b);
const at = left.finalize(a);
const bt = right.finalize(b);
if (at) |tree| try exact(tree, try bt) else |err| try std.testing.expectError(err, bt);
try exact(left.failed, right.failed);
try exact(left.virtualWindows(), right.virtualWindows());
try exact(fa.alloc_index, fb.alloc_index);
try exact(fa.resize_index, fb.resize_index);
return .{ .allocations = fa.alloc_index, .resizes = fa.resize_index, .calls = count };
}
fn check(comptime source: []const u8, model: Model, faults: bool) !void {
const result = try compare(source, model, unlimited, unlimited);
if (faults) {
for (0..result.allocations + 1) |index| _ = try compare(source, model, index, unlimited);
for (0..result.resizes + 1) |index| _ = try compare(source, model, unlimited, index);
}
}
test "static markup specialized code retains all line grammar boundaries and complete failure effects" {
_ = core.initialModel();
modes = @splat(0);
const source = "<column><code source=\"{code}\" added-lines=\"{added}\" removed-lines=\"{deleted}\" language=\"zig\" label=\"Source\"/><text>{title}</text></column>";
for ([_][]const u8{ "", " \t\r\n", "1", "128", "1-128", "128,1,128,2-4", "+1 - 0__2,2", "1_2", "1,", ",1", "1,,2", "0", "129", "2-1", "-1", "1-2-3", "1-", "1 - \n2", "18446744073709551616", "1,2,x", "1,2,3-0", "\xff", "1\x00" }) |lines| {
try check(source, .{ .lines = lines }, false);
try check(source, .{ .removed = lines }, false);
}
try check(source, .{}, true);
try check(source, .{ .lines = "1,2,x", .removed = "3-1" }, true);
try std.testing.expect(modes[17] > 0 and modes[18] > 0);
}
test "static markup specialized literal and template code preserve callback order after invalid metadata" {
_ = core.initialModel();
const source = "<template name=\"code-card\" args=\"lines removed\"><code source=\"{code}\" added-lines=\"{lines}\" removed-lines=\"{removed}\"/></template><column><use template=\"code-card\" lines=\"{added}\" removed=\"{deleted}\"/><code source=\"{code}\" added-lines=\"1-3,1\" removed-lines=\"2\"/></column>";
try check(source, .{}, true);
try check(source, .{ .lines = "0" }, true);
}
test "static markup specialized markdown preserves nonempty issue bases and borrowed callbacks" {
_ = core.initialModel();
modes = @splat(0);
const source = "<column><markdown source=\"{body}\" issue-link-base=\"{base}\" on-link=\"link\" on-details=\"details\"/><text>{title}</text></column>";
for ([_][]const u8{ "", " ", "https://example.invalid/issues/", "ghissue://", "\x00\xff" }) |prefix| try check(source, .{ .prefix = prefix }, true);
try std.testing.expect(modes[28] > 0);
}
test "static markup specialized markdown template captures retain source and issue callback order" {
_ = core.initialModel();
const source = "<template name=\"document\" args=\"base\"><markdown source=\"{body}\" issue-link-base=\"{base}\" on-link=\"link\"/></template><column><use template=\"document\" base=\"{base}\"/><markdown source=\"{body}\" issue-link-base=\"https://example.invalid/\"/></column>";
try check(source, .{}, true);
try check(source, .{ .prefix = "" }, true);
}
test "static markup specialized root press publication preserves timeline affordances and exact payloads" {
_ = core.initialModel();
modes = @splat(0);
const source = "<template name=\"item\"><timeline-item title=\"{title}\" on-press=\"local\"/></template><template name=\"button-card\"><button on-press=\"local\">{title}</button></template><column><timeline><use template=\"item\" on-press=\"selected:{chosen}\"/><use template=\"item\"/><timeline-item title=\"{title}\"/></timeline><use template=\"button-card\" on-press=\"selected:{chosen}\"/><use template=\"button-card\"/></column>";
for ([_]i64{ std.math.minInt(i64), -9007199254740993, 0, 9007199254740993, std.math.maxInt(i64) }) |chosen| try check(source, .{ .chosen = chosen }, true);
try std.testing.expect(modes[33] > 0 and modes[36] > 0);
}
test "static markup specialized tab conversion preserves toggle contracts nested children and handlers" {
_ = core.initialModel();
modes = @splat(0);
const source = "<template name=\"trigger\"><button on-press=\"local\">{title}</button></template><column><tabs><button on-press=\"tick\">First</button><toggle-button on-toggle=\"tick\">Toggle</toggle-button><text>Caption</text><use template=\"trigger\" on-press=\"selected:{chosen}\"/><column><button on-press=\"local\">Nested</button></column></tabs><tabs/></column>";
try check(source, .{}, true);
try std.testing.expect(modes[34] > 0);
}
2 changes: 1 addition & 1 deletion tests/ts-core/static_markup_values_e2e_tests.zig
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ fn exact(a: anytype, b: @TypeOf(a)) anyerror!void {
}
}

var operations: [36]usize = @splat(0);
var operations: [256]usize = @splat(0);
var keys: usize = 0;
fn observed(input: []const u8, output: []u8) usize {
if (input[0] == 113) operations[input[2]] += 1;
Expand Down
1 change: 1 addition & 0 deletions tests/ts-core/surface_layout_e2e_tests.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1012,4 +1012,5 @@ test {
test {
_ = @import("static_markup_collections_e2e_tests.zig");
_ = @import("static_markup_expansion_e2e_tests.zig");
_ = @import("static_markup_specialized_e2e_tests.zig");
}
Loading