aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar dave caruso <me@paperdave.net> 2023-08-06 06:13:39 -0700
committerGravatar GitHub <noreply@github.com> 2023-08-06 06:13:39 -0700
commitcd0774cd89f44ae3880ae5d3840787012d9df603 (patch)
treeb1285dd26a5134b227f56afcc58c8e2b519fd3a1
parentcf8650937aa03b7410c24675868eec36b7e2f390 (diff)
downloadbun-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.cpp35
-rw-r--r--src/bun.js/bindings/RegularExpression.zig57
-rw-r--r--src/bun.js/test/jest.zig35
-rw-r--r--src/bun.js/test/test.zig0
-rw-r--r--src/bun.zig2
-rw-r--r--src/cli.zig23
-rw-r--r--src/cli/test_command.zig2
-rw-r--r--src/string_mutable.zig4
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) {