diff options
author | 2023-08-06 06:13:39 -0700 | |
---|---|---|
committer | 2023-08-06 06:13:39 -0700 | |
commit | cd0774cd89f44ae3880ae5d3840787012d9df603 (patch) | |
tree | b1285dd26a5134b227f56afcc58c8e2b519fd3a1 | |
parent | cf8650937aa03b7410c24675868eec36b7e2f390 (diff) | |
download | bun-cd0774cd89f44ae3880ae5d3840787012d9df603.tar.gz bun-cd0774cd89f44ae3880ae5d3840787012d9df603.tar.zst bun-cd0774cd89f44ae3880ae5d3840787012d9df603.zip |
Implement --test-name-pattern (#3998)
* Fix end not being emitted all the time
* stuff
* Implement -t
* Undo js_printer changes
* Undo http changes
* Update InternalModuleRegistryConstants.h
* Delete unrelated test
---------
Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
-rw-r--r-- | src/bun.js/bindings/RegularExpression.cpp | 35 | ||||
-rw-r--r-- | src/bun.js/bindings/RegularExpression.zig | 57 | ||||
-rw-r--r-- | src/bun.js/test/jest.zig | 35 | ||||
-rw-r--r-- | src/bun.js/test/test.zig | 0 | ||||
-rw-r--r-- | src/bun.zig | 2 | ||||
-rw-r--r-- | src/cli.zig | 23 | ||||
-rw-r--r-- | src/cli/test_command.zig | 2 | ||||
-rw-r--r-- | src/string_mutable.zig | 4 |
8 files changed, 151 insertions, 7 deletions
diff --git a/src/bun.js/bindings/RegularExpression.cpp b/src/bun.js/bindings/RegularExpression.cpp new file mode 100644 index 000000000..1c8df1bc0 --- /dev/null +++ b/src/bun.js/bindings/RegularExpression.cpp @@ -0,0 +1,35 @@ +#include "root.h" +#include "headers-handwritten.h" +#include "JavaScriptCore/RegularExpression.h" + +using namespace JSC; +using namespace JSC::Yarr; + +extern "C" RegularExpression* Yarr__RegularExpression__init(BunString pattern, uint16_t flags) +{ + return new RegularExpression(Bun::toWTFString(pattern), OptionSet<Flags>(static_cast<Flags>(flags))); +} +extern "C" void Yarr__RegularExpression__deinit(RegularExpression* re) +{ + delete re; +} +extern "C" bool Yarr__RegularExpression__isValid(RegularExpression* re) +{ + return re->isValid(); +} +extern "C" int Yarr__RegularExpression__matchedLength(RegularExpression* re) +{ + return re->matchedLength(); +} +extern "C" int Yarr__RegularExpression__searchRev(RegularExpression* re, BunString string) +{ + return re->searchRev(Bun::toWTFString(string)); +} +// extern "C" int Yarr__RegularExpression__match(RegularExpression* re, BunString string, int32_t start, int32_t* matchLength) +// { +// return re->match(Bun::toWTFString(string), start, matchLength); +// } +extern "C" int Yarr__RegularExpression__matches(RegularExpression* re, BunString string) +{ + return re->match(Bun::toWTFString(string), 0, 0); +}
\ No newline at end of file diff --git a/src/bun.js/bindings/RegularExpression.zig b/src/bun.js/bindings/RegularExpression.zig new file mode 100644 index 000000000..9c3da4fa2 --- /dev/null +++ b/src/bun.js/bindings/RegularExpression.zig @@ -0,0 +1,57 @@ +const bun = @import("root").bun; + +pub const RegularExpression = opaque { + pub const Flags = enum(u16) { + none = 0, + + hasIndices = 1 << 0, + global = 1 << 1, + ignoreCase = 1 << 2, + multiline = 1 << 3, + dotAll = 1 << 4, + unicode = 1 << 5, + unicodeSets = 1 << 6, + sticky = 1 << 7, + }; + + extern fn Yarr__RegularExpression__init(pattern: bun.String, flags: u16) *RegularExpression; + extern fn Yarr__RegularExpression__deinit(pattern: *RegularExpression) void; + extern fn Yarr__RegularExpression__isValid(this: *RegularExpression) bool; + extern fn Yarr__RegularExpression__matchedLength(this: *RegularExpression) i32; + extern fn Yarr__RegularExpression__searchRev(this: *RegularExpression) i32; + extern fn Yarr__RegularExpression__matches(this: *RegularExpression, string: bun.String) i32; + + pub inline fn init(pattern: bun.String, flags: Flags) !*RegularExpression { + var regex = Yarr__RegularExpression__init(pattern, @intFromEnum(flags)); + if (!regex.isValid()) { + regex.deinit(); + return error.InvalidRegex; + } + return regex; + } + + pub inline fn isValid(this: *RegularExpression) bool { + return Yarr__RegularExpression__isValid(this); + } + + // Reserving `match` for a full match result. + // pub inline fn match(this: *RegularExpression, str: bun.String, startFrom: i32) MatchResult { + // } + + // Simple boolean matcher + pub inline fn matches(this: *RegularExpression, str: bun.String) bool { + return Yarr__RegularExpression__matches(this, str) > 0; + } + + pub inline fn searchRev(this: *RegularExpression, str: bun.String) i32 { + return Yarr__RegularExpression__searchRev(this, str); + } + + pub inline fn matchedLength(this: *RegularExpression) i32 { + return Yarr__RegularExpression__matchedLength(this); + } + + pub inline fn deinit(this: *RegularExpression) void { + Yarr__RegularExpression__deinit(this); + } +}; diff --git a/src/bun.js/test/jest.zig b/src/bun.js/test/jest.zig index 39116054e..aacf671ce 100644 --- a/src/bun.js/test/jest.zig +++ b/src/bun.js/test/jest.zig @@ -34,6 +34,7 @@ const FeatureFlags = @import("root").bun.FeatureFlags; const ArrayBuffer = @import("../base.zig").ArrayBuffer; const Properties = @import("../base.zig").Properties; const getAllocator = @import("../base.zig").getAllocator; +const RegularExpression = bun.RegularExpression; const ZigString = JSC.ZigString; const JSInternalPromise = JSC.JSInternalPromise; @@ -96,6 +97,10 @@ pub const TestRunner = struct { afterAll: std.ArrayListUnmanaged(JSC.JSValue) = .{}, } = .{}, + // Used for --test-name-pattern to reduce allocations + filter_regex: ?*RegularExpression, + filter_buffer: MutableString, + pub const Drainer = JSC.AnyTask.New(TestRunner, drain); pub fn onTestTimeout(timer: *bun.uws.Timer) callconv(.C) void { @@ -1441,6 +1446,17 @@ pub const Result = union(TestRunner.Test.Status) { } }; +fn appendParentLabel( + buffer: *bun.MutableString, + parent: *DescribeScope, +) !void { + if (parent.parent) |par| { + try appendParentLabel(buffer, par); + } + try buffer.append(parent.label); + try buffer.append(" "); +} + inline fn createScope( globalThis: *JSGlobalObject, callframe: *CallFrame, @@ -1516,11 +1532,26 @@ inline fn createScope( return .zero; } - const is_skip = tag == .skip or + var is_skip = tag == .skip or (tag == .todo and (function == .zero or !Jest.runner.?.run_todo)) or (tag != .only and Jest.runner.?.only and parent.tag != .only); + var tag_to_use = tag; if (is_test) { + if (!is_skip) { + if (Jest.runner.?.filter_regex) |regex| { + var buffer: bun.MutableString = Jest.runner.?.filter_buffer; + buffer.reset(); + appendParentLabel(&buffer, parent) catch @panic("Bun ran out of memory while filtering tests"); + buffer.append(label) catch unreachable; + var str = bun.String.fromBytes(buffer.toOwnedSliceLeaky()); + is_skip = !regex.matches(str); + if (is_skip) { + tag_to_use = .skip; + } + } + } + if (is_skip) { parent.skip_count += 1; function.unprotect(); @@ -1531,7 +1562,7 @@ inline fn createScope( parent.tests.append(allocator, TestScope{ .label = label, .parent = parent, - .tag = tag, + .tag = tag_to_use, .callback = if (is_skip) .zero else function, .timeout_millis = timeout_ms, }) catch unreachable; diff --git a/src/bun.js/test/test.zig b/src/bun.js/test/test.zig new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/bun.js/test/test.zig diff --git a/src/bun.zig b/src/bun.zig index 8dd888b69..4dc5a296d 100644 --- a/src/bun.zig +++ b/src/bun.zig @@ -1571,3 +1571,5 @@ pub const WTF = struct { pub const ArenaAllocator = @import("./ArenaAllocator.zig").ArenaAllocator; pub const Wyhash = @import("./wyhash.zig").Wyhash; + +pub const RegularExpression = @import("./bun.js/bindings/RegularExpression.zig").RegularExpression; diff --git a/src/cli.zig b/src/cli.zig index d19ede800..aa36c63fb 100644 --- a/src/cli.zig +++ b/src/cli.zig @@ -20,6 +20,7 @@ const json_parser = bun.JSON; const js_printer = bun.js_printer; const js_ast = bun.JSAst; const linker = @import("linker.zig"); +const RegularExpression = bun.RegularExpression; const sync = @import("./sync.zig"); const Api = @import("api/schema.zig").Api; @@ -219,6 +220,7 @@ pub const Arguments = struct { clap.parseParam("--only Only run tests that are marked with \"test.only()\"") catch unreachable, clap.parseParam("--todo Include tests that are marked with \"test.todo()\"") catch unreachable, clap.parseParam("--bail <NUMBER>? Exit the test suite after <NUMBER> failures. If you do not specify a number, it defaults to 1.") catch unreachable, + clap.parseParam("-t, --test-name-pattern <STR> Run only tests with a name that matches the given regex.") catch unreachable, }; const build_params_public = public_params ++ build_only_params; @@ -388,12 +390,12 @@ pub const Arguments = struct { if (args.option("--bail")) |bail| { if (bail.len > 0) { ctx.test_options.bail = std.fmt.parseInt(u32, bail, 10) catch |e| { - Output.prettyErrorln("--bail expects a number: {s}", .{@errorName(e)}); + Output.prettyErrorln("<r><red>error<r>: --bail expects a number: {s}", .{@errorName(e)}); Global.exit(1); }; if (ctx.test_options.bail == 0) { - Output.prettyErrorln("--bail expects a number greater than 0", .{}); + Output.prettyErrorln("<r><red>error<r>: --bail expects a number greater than 0", .{}); Global.exit(1); } } else { @@ -403,11 +405,25 @@ pub const Arguments = struct { if (args.option("--rerun-each")) |repeat_count| { if (repeat_count.len > 0) { ctx.test_options.repeat_count = std.fmt.parseInt(u32, repeat_count, 10) catch |e| { - Output.prettyErrorln("--rerun-each expects a number: {s}", .{@errorName(e)}); + Output.prettyErrorln("<r><red>error<r>: --rerun-each expects a number: {s}", .{@errorName(e)}); Global.exit(1); }; } } + if (args.option("--test-name-pattern")) |namePattern| { + const regex = RegularExpression.init(bun.String.fromBytes(namePattern), RegularExpression.Flags.none) catch { + Output.prettyErrorln( + "<r><red>error<r>: --test-name-pattern expects a valid regular expression but received {}", + .{ + strings.QuotedFormatter{ + .text = namePattern, + }, + }, + ); + Global.exit(1); + }; + ctx.test_options.test_filter_regex = regex; + } ctx.test_options.update_snapshots = args.flag("--update-snapshots"); ctx.test_options.run_todo = args.flag("--todo"); ctx.test_options.only = args.flag("--only"); @@ -949,6 +965,7 @@ pub const Command = struct { run_todo: bool = false, only: bool = false, bail: u32 = 0, + test_filter_regex: ?*RegularExpression = null, }; pub const Context = struct { diff --git a/src/cli/test_command.zig b/src/cli/test_command.zig index 14eca012e..819aceb19 100644 --- a/src/cli/test_command.zig +++ b/src/cli/test_command.zig @@ -457,6 +457,8 @@ pub const TestCommand = struct { .run_todo = ctx.test_options.run_todo, .only = ctx.test_options.only, .bail = ctx.test_options.bail, + .filter_regex = ctx.test_options.test_filter_regex, + .filter_buffer = bun.MutableString.init(ctx.allocator, 0) catch unreachable, .snapshots = Snapshots{ .allocator = ctx.allocator, .update_snapshots = ctx.test_options.update_snapshots, diff --git a/src/string_mutable.zig b/src/string_mutable.zig index 239713407..e0734574f 100644 --- a/src/string_mutable.zig +++ b/src/string_mutable.zig @@ -228,7 +228,7 @@ pub const MutableString = struct { } pub fn toOwnedSlice(self: *MutableString) string { - return self.list.toOwnedSlice(self.allocator) catch @panic("TODO"); + return self.list.toOwnedSlice(self.allocator) catch @panic("Allocation Error"); // TODO } pub fn toOwnedSliceLeaky(self: *MutableString) []u8 { @@ -255,7 +255,7 @@ pub const MutableString = struct { pub fn toOwnedSliceLength(self: *MutableString, length: usize) string { self.list.shrinkAndFree(self.allocator, length); - return self.list.toOwnedSlice(self.allocator) catch @panic("TODO"); + return self.list.toOwnedSlice(self.allocator) catch @panic("Allocation Error"); // TODO } // pub fn deleteAt(self: *MutableString, i: usize) { |