mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-29 23:23:28 +00:00
Merge pull request #785 from lightpanda-io/build_and_test_speed
Improve build and test speed
This commit is contained in:
@@ -85,7 +85,7 @@ pub const CSSStyleDeclaration = struct {
|
||||
return self.order.items.len;
|
||||
}
|
||||
|
||||
pub fn get_parentRule() ?CSSRule {
|
||||
pub fn get_parentRule(_: *const CSSStyleDeclaration) ?CSSRule {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -337,20 +337,15 @@ test "Browser.HTML.Window" {
|
||||
// Note however that we in this test do not wait as the request is just send to the browser
|
||||
try runner.testCases(&.{
|
||||
.{
|
||||
\\ let start;
|
||||
\\ let start = 0;
|
||||
\\ function step(timestamp) {
|
||||
\\ if (start === undefined) {
|
||||
\\ start = timestamp;
|
||||
\\ }
|
||||
\\ const elapsed = timestamp - start;
|
||||
\\ if (elapsed < 2000) {
|
||||
\\ requestAnimationFrame(step);
|
||||
\\ }
|
||||
\\ }
|
||||
,
|
||||
null,
|
||||
},
|
||||
.{ "requestAnimationFrame(step);", null }, // returned id is checked in the next test
|
||||
.{ " start > 0", "true" },
|
||||
}, .{});
|
||||
|
||||
// cancelAnimationFrame should be able to cancel a request with the given id
|
||||
|
||||
@@ -23,7 +23,6 @@ const json = std.json;
|
||||
const log = @import("../log.zig");
|
||||
const App = @import("../app.zig").App;
|
||||
const Env = @import("../browser/env.zig").Env;
|
||||
const asUint = @import("../str/parser.zig").asUint;
|
||||
const Browser = @import("../browser/browser.zig").Browser;
|
||||
const Session = @import("../browser/session.zig").Session;
|
||||
const Page = @import("../browser/page.zig").Page;
|
||||
@@ -182,41 +181,41 @@ pub fn CDPT(comptime TypeProvider: type) type {
|
||||
|
||||
switch (domain.len) {
|
||||
3 => switch (@as(u24, @bitCast(domain[0..3].*))) {
|
||||
asUint("DOM") => return @import("domains/dom.zig").processMessage(command),
|
||||
asUint("Log") => return @import("domains/log.zig").processMessage(command),
|
||||
asUint("CSS") => return @import("domains/css.zig").processMessage(command),
|
||||
asUint(u24, "DOM") => return @import("domains/dom.zig").processMessage(command),
|
||||
asUint(u24, "Log") => return @import("domains/log.zig").processMessage(command),
|
||||
asUint(u24, "CSS") => return @import("domains/css.zig").processMessage(command),
|
||||
else => {},
|
||||
},
|
||||
4 => switch (@as(u32, @bitCast(domain[0..4].*))) {
|
||||
asUint("Page") => return @import("domains/page.zig").processMessage(command),
|
||||
asUint(u32, "Page") => return @import("domains/page.zig").processMessage(command),
|
||||
else => {},
|
||||
},
|
||||
5 => switch (@as(u40, @bitCast(domain[0..5].*))) {
|
||||
asUint("Fetch") => return @import("domains/fetch.zig").processMessage(command),
|
||||
asUint("Input") => return @import("domains/input.zig").processMessage(command),
|
||||
asUint(u40, "Fetch") => return @import("domains/fetch.zig").processMessage(command),
|
||||
asUint(u40, "Input") => return @import("domains/input.zig").processMessage(command),
|
||||
else => {},
|
||||
},
|
||||
6 => switch (@as(u48, @bitCast(domain[0..6].*))) {
|
||||
asUint("Target") => return @import("domains/target.zig").processMessage(command),
|
||||
asUint(u48, "Target") => return @import("domains/target.zig").processMessage(command),
|
||||
else => {},
|
||||
},
|
||||
7 => switch (@as(u56, @bitCast(domain[0..7].*))) {
|
||||
asUint("Browser") => return @import("domains/browser.zig").processMessage(command),
|
||||
asUint("Runtime") => return @import("domains/runtime.zig").processMessage(command),
|
||||
asUint("Network") => return @import("domains/network.zig").processMessage(command),
|
||||
asUint(u56, "Browser") => return @import("domains/browser.zig").processMessage(command),
|
||||
asUint(u56, "Runtime") => return @import("domains/runtime.zig").processMessage(command),
|
||||
asUint(u56, "Network") => return @import("domains/network.zig").processMessage(command),
|
||||
else => {},
|
||||
},
|
||||
8 => switch (@as(u64, @bitCast(domain[0..8].*))) {
|
||||
asUint("Security") => return @import("domains/security.zig").processMessage(command),
|
||||
asUint(u64, "Security") => return @import("domains/security.zig").processMessage(command),
|
||||
else => {},
|
||||
},
|
||||
9 => switch (@as(u72, @bitCast(domain[0..9].*))) {
|
||||
asUint("Emulation") => return @import("domains/emulation.zig").processMessage(command),
|
||||
asUint("Inspector") => return @import("domains/inspector.zig").processMessage(command),
|
||||
asUint(u72, "Emulation") => return @import("domains/emulation.zig").processMessage(command),
|
||||
asUint(u72, "Inspector") => return @import("domains/inspector.zig").processMessage(command),
|
||||
else => {},
|
||||
},
|
||||
11 => switch (@as(u88, @bitCast(domain[0..11].*))) {
|
||||
asUint("Performance") => return @import("domains/performance.zig").processMessage(command),
|
||||
asUint(u88, "Performance") => return @import("domains/performance.zig").processMessage(command),
|
||||
else => {},
|
||||
},
|
||||
else => {},
|
||||
@@ -696,6 +695,10 @@ const InputParams = struct {
|
||||
}
|
||||
};
|
||||
|
||||
fn asUint(comptime T: type, comptime string: []const u8) T {
|
||||
return @bitCast(string[0..string.len].*);
|
||||
}
|
||||
|
||||
const testing = @import("testing.zig");
|
||||
test "cdp: invalid json" {
|
||||
var ctx = testing.context();
|
||||
|
||||
@@ -3170,7 +3170,7 @@ test "HttpClient: async tls no body" {
|
||||
}
|
||||
}
|
||||
|
||||
test "HttpClient: async tls with body x" {
|
||||
test "HttpClient: async tls with body" {
|
||||
defer testing.reset();
|
||||
for (0..5) |_| {
|
||||
var client = try testClient();
|
||||
|
||||
27
src/log.zig
27
src/log.zig
@@ -146,6 +146,16 @@ fn logTo(comptime scope: Scope, level: Level, comptime msg: []const u8, data: an
|
||||
}
|
||||
|
||||
fn logLogfmt(comptime scope: Scope, level: Level, comptime msg: []const u8, data: anytype, writer: anytype) !void {
|
||||
try logLogFmtPrefix(scope, level, msg, writer);
|
||||
inline for (@typeInfo(@TypeOf(data)).@"struct".fields) |f| {
|
||||
const key = " " ++ f.name ++ "=";
|
||||
try writer.writeAll(key);
|
||||
try writeValue(.logfmt, @field(data, f.name), writer);
|
||||
}
|
||||
try writer.writeByte('\n');
|
||||
}
|
||||
|
||||
fn logLogFmtPrefix(comptime scope: Scope, level: Level, comptime msg: []const u8, writer: anytype) !void {
|
||||
try writer.writeAll("$time=");
|
||||
try writer.print("{d}", .{timestamp()});
|
||||
|
||||
@@ -164,15 +174,20 @@ fn logLogfmt(comptime scope: Scope, level: Level, comptime msg: []const u8, data
|
||||
break :blk prefix ++ "\"" ++ msg ++ "\"";
|
||||
};
|
||||
try writer.writeAll(full_msg);
|
||||
}
|
||||
|
||||
fn logPretty(comptime scope: Scope, level: Level, comptime msg: []const u8, data: anytype, writer: anytype) !void {
|
||||
try logPrettyPrefix(scope, level, msg, writer);
|
||||
inline for (@typeInfo(@TypeOf(data)).@"struct".fields) |f| {
|
||||
const key = " " ++ f.name ++ " = ";
|
||||
try writer.writeAll(key);
|
||||
try writeValue(.logfmt, @field(data, f.name), writer);
|
||||
try writeValue(.pretty, @field(data, f.name), writer);
|
||||
try writer.writeByte('\n');
|
||||
}
|
||||
try writer.writeByte('\n');
|
||||
}
|
||||
|
||||
fn logPretty(comptime scope: Scope, level: Level, comptime msg: []const u8, data: anytype, writer: anytype) !void {
|
||||
fn logPrettyPrefix(comptime scope: Scope, level: Level, comptime msg: []const u8, writer: anytype) !void {
|
||||
if (scope == .console and level == .fatal and comptime std.mem.eql(u8, msg, "lightpanda")) {
|
||||
try writer.writeAll("\x1b[0;104mWARN ");
|
||||
} else {
|
||||
@@ -201,14 +216,6 @@ fn logPretty(comptime scope: Scope, level: Level, comptime msg: []const u8, data
|
||||
try writer.print(" \x1b[0m[+{d}ms]", .{elapsed()});
|
||||
try writer.writeByte('\n');
|
||||
}
|
||||
|
||||
inline for (@typeInfo(@TypeOf(data)).@"struct".fields) |f| {
|
||||
const key = " " ++ f.name ++ " = ";
|
||||
try writer.writeAll(key);
|
||||
try writeValue(.pretty, @field(data, f.name), writer);
|
||||
try writer.writeByte('\n');
|
||||
}
|
||||
try writer.writeByte('\n');
|
||||
}
|
||||
|
||||
pub fn writeValue(comptime format: Format, value: anytype, writer: anytype) !void {
|
||||
|
||||
@@ -1876,7 +1876,7 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
|
||||
defer caller.deinit();
|
||||
|
||||
const named_function = comptime NamedFunction.init(Struct, "get_" ++ name);
|
||||
caller.getter(Struct, named_function, info) catch |err| {
|
||||
caller.method(Struct, named_function, info) catch |err| {
|
||||
caller.handleError(Struct, named_function, err, info);
|
||||
};
|
||||
}
|
||||
@@ -1891,13 +1891,13 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
|
||||
const setter_callback = v8.FunctionTemplate.initCallback(isolate, struct {
|
||||
fn callback(raw_info: ?*const v8.C_FunctionCallbackInfo) callconv(.c) void {
|
||||
const info = v8.FunctionCallbackInfo.initFromV8(raw_info);
|
||||
std.debug.assert(info.length() == 1);
|
||||
|
||||
var caller = Caller(Self, State).init(info);
|
||||
defer caller.deinit();
|
||||
|
||||
std.debug.assert(info.length() == 1);
|
||||
const js_value = info.getArg(0);
|
||||
const named_function = comptime NamedFunction.init(Struct, "set_" ++ name);
|
||||
caller.setter(Struct, named_function, js_value, info) catch |err| {
|
||||
caller.method(Struct, named_function, info) catch |err| {
|
||||
caller.handleError(Struct, named_function, err, info);
|
||||
};
|
||||
}
|
||||
@@ -2424,66 +2424,6 @@ fn Caller(comptime E: type, comptime State: type) type {
|
||||
info.getReturnValue().set(try js_context.zigValueToJs(res));
|
||||
}
|
||||
|
||||
fn getter(self: *Self, comptime Struct: type, comptime named_function: NamedFunction, info: v8.FunctionCallbackInfo) !void {
|
||||
const js_context = self.js_context;
|
||||
const func = @field(Struct, named_function.name);
|
||||
const Getter = @TypeOf(func);
|
||||
if (@typeInfo(Getter).@"fn".return_type == null) {
|
||||
@compileError(@typeName(Struct) ++ " has a getter without a return type: " ++ @typeName(Getter));
|
||||
}
|
||||
|
||||
var args: ParamterTypes(Getter) = undefined;
|
||||
const arg_fields = @typeInfo(@TypeOf(args)).@"struct".fields;
|
||||
switch (arg_fields.len) {
|
||||
0 => {}, // getters _can_ be parameterless
|
||||
1, 2 => {
|
||||
const zig_instance = try E.typeTaggedAnyOpaque(named_function, *Receiver(Struct), info.getThis());
|
||||
comptime assertSelfReceiver(Struct, named_function);
|
||||
@field(args, "0") = zig_instance;
|
||||
if (comptime arg_fields.len == 2) {
|
||||
comptime assertIsStateArg(Struct, named_function, 1);
|
||||
@field(args, "1") = js_context.state;
|
||||
}
|
||||
},
|
||||
else => @compileError(named_function.full_name + " has too many parmaters: " ++ @typeName(named_function.func)),
|
||||
}
|
||||
const res = @call(.auto, func, args);
|
||||
info.getReturnValue().set(try js_context.zigValueToJs(res));
|
||||
}
|
||||
|
||||
fn setter(self: *Self, comptime Struct: type, comptime named_function: NamedFunction, js_value: v8.Value, info: v8.FunctionCallbackInfo) !void {
|
||||
const js_context = self.js_context;
|
||||
const func = @field(Struct, named_function.name);
|
||||
comptime assertSelfReceiver(Struct, named_function);
|
||||
|
||||
const zig_instance = try E.typeTaggedAnyOpaque(named_function, *Receiver(Struct), info.getThis());
|
||||
|
||||
const Setter = @TypeOf(func);
|
||||
var args: ParamterTypes(Setter) = undefined;
|
||||
const arg_fields = @typeInfo(@TypeOf(args)).@"struct".fields;
|
||||
switch (arg_fields.len) {
|
||||
0 => unreachable, // assertSelfReceiver make sure of this
|
||||
1 => @compileError(named_function.full_name ++ " only has 1 parameter"),
|
||||
2, 3 => {
|
||||
@field(args, "0") = zig_instance;
|
||||
@field(args, "1") = try js_context.jsValueToZig(named_function, arg_fields[1].type, js_value);
|
||||
if (comptime arg_fields.len == 3) {
|
||||
comptime assertIsStateArg(Struct, named_function, 2);
|
||||
@field(args, "2") = js_context.state;
|
||||
}
|
||||
},
|
||||
else => @compileError(named_function.full_name ++ " setter with more than 3 parameters, why?"),
|
||||
}
|
||||
|
||||
if (@typeInfo(Setter).@"fn".return_type) |return_type| {
|
||||
if (@typeInfo(return_type) == .error_union) {
|
||||
_ = try @call(.auto, func, args);
|
||||
return;
|
||||
}
|
||||
}
|
||||
_ = @call(.auto, func, args);
|
||||
}
|
||||
|
||||
fn getIndex(self: *Self, comptime Struct: type, comptime named_function: NamedFunction, idx: u32, info: v8.PropertyCallbackInfo) !u8 {
|
||||
const js_context = self.js_context;
|
||||
const func = @field(Struct, named_function.name);
|
||||
@@ -2596,13 +2536,7 @@ fn Caller(comptime E: type, comptime State: type) type {
|
||||
|
||||
if (comptime builtin.mode == .Debug and @hasDecl(@TypeOf(info), "length")) {
|
||||
if (log.enabled(.js, .warn)) {
|
||||
const args_dump = self.serializeFunctionArgs(info) catch "failed to serialize args";
|
||||
log.warn(.js, "function call error", .{
|
||||
.name = named_function.full_name,
|
||||
.err = err,
|
||||
.args = args_dump,
|
||||
.stack = stackForLogs(self.call_arena, isolate) catch |err1| @errorName(err1),
|
||||
});
|
||||
logFunctionCallError(self.call_arena, self.isolate, self.v8_context, err, named_function.full_name, info);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2670,6 +2604,7 @@ fn Caller(comptime E: type, comptime State: type) type {
|
||||
// Does the error we want to return belong to the custom exeception's ErrorSet
|
||||
fn isErrorSetException(comptime Exception: type, err: anytype) bool {
|
||||
const Entry = std.meta.Tuple(&.{ []const u8, void });
|
||||
|
||||
const error_set = @typeInfo(Exception.ErrorSet).error_set.?;
|
||||
const entries = comptime blk: {
|
||||
var kv: [error_set.len]Entry = undefined;
|
||||
@@ -2808,28 +2743,6 @@ fn Caller(comptime E: type, comptime State: type) type {
|
||||
const Const_State = if (ti == .pointer) *const ti.pointer.child else State;
|
||||
return T == State or T == Const_State;
|
||||
}
|
||||
|
||||
fn serializeFunctionArgs(self: *const Self, info: anytype) ![]const u8 {
|
||||
const isolate = self.isolate;
|
||||
const v8_context = self.v8_context;
|
||||
const arena = self.call_arena;
|
||||
const separator = log.separator();
|
||||
const js_parameter_count = info.length();
|
||||
|
||||
var arr: std.ArrayListUnmanaged(u8) = .{};
|
||||
for (0..js_parameter_count) |i| {
|
||||
const js_value = info.getArg(@intCast(i));
|
||||
const value_string = try valueToDetailString(arena, js_value, isolate, v8_context);
|
||||
const value_type = try jsStringToZig(arena, try js_value.typeOf(isolate), isolate);
|
||||
try std.fmt.format(arr.writer(arena), "{s}{d}: {s} ({s})", .{
|
||||
separator,
|
||||
i + 1,
|
||||
value_string,
|
||||
value_type,
|
||||
});
|
||||
}
|
||||
return arr.items;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3270,6 +3183,37 @@ const NamedFunction = struct {
|
||||
}
|
||||
};
|
||||
|
||||
// This is extracted to speed up compilation. When left inlined in handleError,
|
||||
// this can add as much as 10 seconds of compilation time.
|
||||
fn logFunctionCallError(arena: Allocator, isolate: v8.Isolate, context: v8.Context, err: anyerror, function_name: []const u8, info: v8.FunctionCallbackInfo) void {
|
||||
const args_dump = serializeFunctionArgs(arena, isolate, context, info) catch "failed to serialize args";
|
||||
log.warn(.js, "function call error", .{
|
||||
.name = function_name,
|
||||
.err = err,
|
||||
.args = args_dump,
|
||||
.stack = stackForLogs(arena, isolate) catch |err1| @errorName(err1),
|
||||
});
|
||||
}
|
||||
|
||||
fn serializeFunctionArgs(arena: Allocator, isolate: v8.Isolate, context: v8.Context, info: v8.FunctionCallbackInfo) ![]const u8 {
|
||||
const separator = log.separator();
|
||||
const js_parameter_count = info.length();
|
||||
|
||||
var arr: std.ArrayListUnmanaged(u8) = .{};
|
||||
for (0..js_parameter_count) |i| {
|
||||
const js_value = info.getArg(@intCast(i));
|
||||
const value_string = try valueToDetailString(arena, js_value, isolate, context);
|
||||
const value_type = try jsStringToZig(arena, try js_value.typeOf(isolate), isolate);
|
||||
try std.fmt.format(arr.writer(arena), "{s}{d}: {s} ({s})", .{
|
||||
separator,
|
||||
i + 1,
|
||||
value_string,
|
||||
value_type,
|
||||
});
|
||||
}
|
||||
return arr.items;
|
||||
}
|
||||
|
||||
// This is called from V8. Whenever the v8 inspector has to describe a value
|
||||
// it'll call this function to gets its [optional] subtype - which, from V8's
|
||||
// point of view, is an arbitrary string.
|
||||
|
||||
@@ -1,125 +0,0 @@
|
||||
// Copyright (C) 2023-2024 Lightpanda (Selecy SAS)
|
||||
//
|
||||
// Francis Bouvier <francis@lightpanda.io>
|
||||
// Pierre Tachoire <pierre@lightpanda.io>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// some utils to parser strings.
|
||||
const std = @import("std");
|
||||
|
||||
pub const Reader = struct {
|
||||
pos: usize = 0,
|
||||
data: []const u8,
|
||||
|
||||
pub fn until(self: *Reader, c: u8) []const u8 {
|
||||
const pos = self.pos;
|
||||
const data = self.data;
|
||||
|
||||
const index = std.mem.indexOfScalarPos(u8, data, pos, c) orelse data.len;
|
||||
self.pos = index;
|
||||
return data[pos..index];
|
||||
}
|
||||
|
||||
pub fn tail(self: *Reader) []const u8 {
|
||||
const pos = self.pos;
|
||||
const data = self.data;
|
||||
if (pos > data.len) {
|
||||
return "";
|
||||
}
|
||||
self.pos = data.len;
|
||||
return data[pos..];
|
||||
}
|
||||
|
||||
pub fn skip(self: *Reader) bool {
|
||||
const pos = self.pos;
|
||||
if (pos >= self.data.len) {
|
||||
return false;
|
||||
}
|
||||
self.pos = pos + 1;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// converts a comptime-known string (i.e. null terminated) to an uint
|
||||
pub fn asUint(comptime string: anytype) AsUintReturn(string) {
|
||||
const byteLength = @bitSizeOf(@TypeOf(string.*)) / 8 - 1;
|
||||
const expectedType = *const [byteLength:0]u8;
|
||||
if (@TypeOf(string) != expectedType) {
|
||||
@compileError("expected : " ++ @typeName(expectedType) ++
|
||||
", got: " ++ @typeName(@TypeOf(string)));
|
||||
}
|
||||
|
||||
return @bitCast(@as(*const [byteLength]u8, string).*);
|
||||
}
|
||||
|
||||
fn AsUintReturn(comptime string: anytype) type {
|
||||
return @Type(.{
|
||||
.int = .{
|
||||
.bits = @bitSizeOf(@TypeOf(string.*)) - 8, // (- 8) to exclude sentinel 0
|
||||
.signedness = .unsigned,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const testing = std.testing;
|
||||
test "parser.Reader: skip" {
|
||||
var r = Reader{ .data = "foo" };
|
||||
try testing.expectEqual(true, r.skip());
|
||||
try testing.expectEqual(true, r.skip());
|
||||
try testing.expectEqual(true, r.skip());
|
||||
try testing.expectEqual(false, r.skip());
|
||||
try testing.expectEqual(false, r.skip());
|
||||
}
|
||||
|
||||
test "parser.Reader: tail" {
|
||||
var r = Reader{ .data = "foo" };
|
||||
try testing.expectEqualStrings("foo", r.tail());
|
||||
try testing.expectEqualStrings("", r.tail());
|
||||
try testing.expectEqualStrings("", r.tail());
|
||||
}
|
||||
|
||||
test "parser.Reader: until" {
|
||||
var r = Reader{ .data = "foo.bar.baz" };
|
||||
try testing.expectEqualStrings("foo", r.until('.'));
|
||||
_ = r.skip();
|
||||
try testing.expectEqualStrings("bar", r.until('.'));
|
||||
_ = r.skip();
|
||||
try testing.expectEqualStrings("baz", r.until('.'));
|
||||
|
||||
r = Reader{ .data = "foo" };
|
||||
try testing.expectEqualStrings("foo", r.until('.'));
|
||||
try testing.expectEqualStrings("", r.tail());
|
||||
|
||||
r = Reader{ .data = "" };
|
||||
try testing.expectEqualStrings("", r.until('.'));
|
||||
try testing.expectEqualStrings("", r.tail());
|
||||
}
|
||||
|
||||
test "parser: asUint" {
|
||||
const ASCII_x = @as(u8, @bitCast([1]u8{'x'}));
|
||||
const ASCII_ab = @as(u16, @bitCast([2]u8{ 'a', 'b' }));
|
||||
const ASCII_xyz = @as(u24, @bitCast([3]u8{ 'x', 'y', 'z' }));
|
||||
const ASCII_abcd = @as(u32, @bitCast([4]u8{ 'a', 'b', 'c', 'd' }));
|
||||
|
||||
try testing.expectEqual(ASCII_x, asUint("x"));
|
||||
try testing.expectEqual(ASCII_ab, asUint("ab"));
|
||||
try testing.expectEqual(ASCII_xyz, asUint("xyz"));
|
||||
try testing.expectEqual(ASCII_abcd, asUint("abcd"));
|
||||
|
||||
try testing.expectEqual(u8, @TypeOf(asUint("x")));
|
||||
try testing.expectEqual(u16, @TypeOf(asUint("ab")));
|
||||
try testing.expectEqual(u24, @TypeOf(asUint("xyz")));
|
||||
try testing.expectEqual(u32, @TypeOf(asUint("abcd")));
|
||||
}
|
||||
Reference in New Issue
Block a user