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>
This commit is contained in:
dave caruso
2023-08-06 06:13:39 -07:00
committed by GitHub
parent cf8650937a
commit cd0774cd89
8 changed files with 151 additions and 7 deletions

View File

@@ -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);
}

View File

@@ -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);
}
};

View File

@@ -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;

0
src/bun.js/test/test.zig Normal file
View File

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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,

View File

@@ -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) {