From 45503548867ad641915a27bcda175ca80704011a Mon Sep 17 00:00:00 2001 From: Asaf Benjaminov Date: Wed, 18 Dec 2024 12:37:53 +0200 Subject: [PATCH 01/10] add coverage include and exclude --- docs/test/coverage.md | 17 +++++ src/bunfig.zig | 20 ++++++ src/cli/test_command.zig | 130 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 167 insertions(+) diff --git a/docs/test/coverage.md b/docs/test/coverage.md index 333eb3fda8adaa..0f138ca619d304 100644 --- a/docs/test/coverage.md +++ b/docs/test/coverage.md @@ -89,3 +89,20 @@ To generate an lcov report, you can use the `lcov` reporter. This will generate [test] coverageReporter = "lcov" ``` + +#### Include or Exclude files from coverate +You can customize the files included or excluded from test coverage using the `coverageInclude` and `coverageExclude` options in your `bunfig.toml` file: + +```toml +[test] +coverageInclude = ["**/*.service.ts"] +coverageExclude = ["**/*.test.ts"] +``` + +- `coverageInclude`: Specifies the files or patterns to include in coverage calculations. +- `coverageExclude`: Specifies the files or patterns to exclude from coverage calculations. +- Supported patterns: + - **Glob patterns**: For example, **/*.service.ts matches all .service.ts files in any folder. + - **Regular expressions**: For more advanced and precise matching, such as .*\.service\.ts$. + +These configurations provide flexibility to focus coverage on specific files or directories while ignoring others. diff --git a/src/bunfig.zig b/src/bunfig.zig index f5e41c434deb0e..5362e85afef873 100644 --- a/src/bunfig.zig +++ b/src/bunfig.zig @@ -312,6 +312,26 @@ pub const Bunfig = struct { this.ctx.test_options.coverage.reports_directory = try expr.data.e_string.string(allocator); } + if (test_.get("coverageInclude")) |expr| { + try this.expect(expr, .e_array); + const items = expr.data.e_array.items.slice(); + this.ctx.test_options.coverage.include = try allocator.alloc(string, items.len); + for (items, 0..) |item, i| { + try this.expectString(item); + this.ctx.test_options.coverage.include[i] = try item.data.e_string.string(allocator); + } + } + + if (test_.get("coverageExclude")) |expr| { + try this.expect(expr, .e_array); + const items = expr.data.e_array.items.slice(); + this.ctx.test_options.coverage.exclude = try allocator.alloc(string, items.len); + for (items, 0..) |item, i| { + try this.expectString(item); + this.ctx.test_options.coverage.exclude[i] = try item.data.e_string.string(allocator); + } + } + if (test_.get("coverageThreshold")) |expr| outer: { if (expr.data == .e_number) { this.ctx.test_options.coverage.fractions.functions = expr.data.e_number.value; diff --git a/src/cli/test_command.zig b/src/cli/test_command.zig index a1229b5edca095..c23117438cf5d7 100644 --- a/src/cli/test_command.zig +++ b/src/cli/test_command.zig @@ -9,6 +9,7 @@ const stringZ = bun.stringZ; const default_allocator = bun.default_allocator; const C = bun.C; const std = @import("std"); +const c_size_t = std.c_size_t; const OOM = bun.OOM; const lex = bun.js_lexer; @@ -1163,6 +1164,8 @@ pub const TestCommand = struct { ignore_sourcemap: bool = false, enabled: bool = false, fail_on_low_coverage: bool = false, + include: ?[]const []const u8 = null, + exclude: ?[]const []const u8 = null, }; pub const Reporter = enum { text, @@ -1371,6 +1374,26 @@ pub const TestCommand = struct { break :scan .{ scanner.results.items, scanner.search_count }; }; + const coverage_options = ctx.test_options.coverage; + + if (coverage_options.include or coverage_options.exclude) { + var filtered_files = try std.ArrayList(PathString).initCapacity(ctx.allocator, test_files.len); + defer filtered_files.deinit(); + + for (test_files) |test_file| { + const test_name = test_file.slice(); + if (coverage_options.include) |includes| { + if (!matchesAnyPattern(test_name, includes)) continue; + } + if (coverage_options.exclude) |excludes| { + if (matchesAnyPattern(test_name, excludes)) continue; + } + try filtered_files.append(test_file); + } + + test_files = filtered_files.items; + } + if (test_files.len > 0) { vm.hot_reload = ctx.debug.hot_reload; @@ -1581,6 +1604,113 @@ pub const TestCommand = struct { } } + fn matchesRegex(target: string, pattern: string) bool { + const allocator = std.heap.c_allocator; + + // Import the PCRE2 library + const pcre2 = @cImport({ + @cInclude("pcre2.h"); + }); + + const _options = pcre2.PCRE2_ZERO_TERMINATED; + const error_code: *c_int = undefined; + const error_offset: *c_size_t = undefined; + + // Compile the regex pattern + const re = pcre2.pcre2_compile( + pattern.ptr, + pattern.len, + _options, + error_code, + error_offset, + null, + ); + + if (re == null) { + std.debug.warn("Failed to compile regex: {}\n", .{pattern}); + return false; + } + + const match_data = pcre2.pcre2_match_data_create_from_pattern(re, allocator); + if (match_data == null) { + pcre2.pcre2_code_free(re); + return false; + } + + const result = pcre2.pcre2_match( + re, + target.ptr, + target.len, + 0, + 0, + match_data, + null, + ); + + pcre2.pcre2_match_data_free(match_data); + pcre2.pcre2_code_free(re); + + return result >= 0; + } + + fn isGlobPattern(pattern: string) bool { + return std.mem.contains(u8, pattern, '*') or std.mem.contains(u8, pattern, '?'); + } + + fn matchesGlob(pattern: string, target: string) bool { + var i = 0; + var j = 0; + + while (i < pattern.len and j < target.len) { + switch (pattern[i]) { + '*' => { + if (i + 1 < pattern.len and pattern[i + 1] == '*') { + // Handle '**' (any directory level) + i += 2; + while (j < target.len and target[j] != '/') { + j += 1; + } + } else { + // Handle '*' (any characters except '/') + i += 1; + while (j < target.len and target[j] != '/') { + j += 1; + } + } + }, + '?' => { + // Handle '?' (any single character) + i += 1; + j += 1; + }, + else => { + // Match characters literally + if (pattern[i] != target[j]) return false; + i += 1; + j += 1; + }, + } + } + + // Ensure the entire pattern and target are consumed + return i == pattern.len and j == target.len; + } + + fn matchesAnyPattern(target: string, patterns: []const string) bool { + for (patterns) |pattern| { + if (isGlobPattern(pattern)) { + if (matchesGlob(target, pattern)) { + return true; + } + } else { + if (matchesRegex(target, pattern)) { + return true; + } + } + } + return false; + } + fn runEventLoopForWatch(vm: *JSC.VirtualMachine) void { vm.eventLoop().tickPossiblyForever(); From 6307bac9ff2b22748b217f61bf66c2de3fcdc238 Mon Sep 17 00:00:00 2001 From: Asaf Benjaminov Date: Wed, 18 Dec 2024 12:38:14 +0200 Subject: [PATCH 02/10] extract to patterns file and add tests --- src/cli/test_command.zig | 112 +---------------------------- src/patterns.zig | 147 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+), 109 deletions(-) create mode 100644 src/patterns.zig diff --git a/src/cli/test_command.zig b/src/cli/test_command.zig index c23117438cf5d7..d33d1a4369a692 100644 --- a/src/cli/test_command.zig +++ b/src/cli/test_command.zig @@ -46,6 +46,7 @@ const Snapshots = JSC.Snapshot.Snapshots; const Test = TestRunner.Test; const CodeCoverageReport = bun.sourcemap.CodeCoverageReport; const uws = bun.uws; +const patterns = @import("../patterns.zig"); fn escapeXml(str: string, writer: anytype) !void { var last: usize = 0; @@ -1383,10 +1384,10 @@ pub const TestCommand = struct { for (test_files) |test_file| { const test_name = test_file.slice(); if (coverage_options.include) |includes| { - if (!matchesAnyPattern(test_name, includes)) continue; + if (!patterns.matchesAnyPattern(test_name, includes)) continue; } if (coverage_options.exclude) |excludes| { - if (matchesAnyPattern(test_name, excludes)) continue; + if (patterns.matchesAnyPattern(test_name, excludes)) continue; } try filtered_files.append(test_file); } @@ -1604,113 +1605,6 @@ pub const TestCommand = struct { } } - fn matchesRegex(target: string, pattern: string) bool { - const allocator = std.heap.c_allocator; - - // Import the PCRE2 library - const pcre2 = @cImport({ - @cInclude("pcre2.h"); - }); - - const _options = pcre2.PCRE2_ZERO_TERMINATED; - const error_code: *c_int = undefined; - const error_offset: *c_size_t = undefined; - - // Compile the regex pattern - const re = pcre2.pcre2_compile( - pattern.ptr, - pattern.len, - _options, - error_code, - error_offset, - null, - ); - - if (re == null) { - std.debug.warn("Failed to compile regex: {}\n", .{pattern}); - return false; - } - - const match_data = pcre2.pcre2_match_data_create_from_pattern(re, allocator); - if (match_data == null) { - pcre2.pcre2_code_free(re); - return false; - } - - const result = pcre2.pcre2_match( - re, - target.ptr, - target.len, - 0, - 0, - match_data, - null, - ); - - pcre2.pcre2_match_data_free(match_data); - pcre2.pcre2_code_free(re); - - return result >= 0; - } - - fn isGlobPattern(pattern: string) bool { - return std.mem.contains(u8, pattern, '*') or std.mem.contains(u8, pattern, '?'); - } - - fn matchesGlob(pattern: string, target: string) bool { - var i = 0; - var j = 0; - - while (i < pattern.len and j < target.len) { - switch (pattern[i]) { - '*' => { - if (i + 1 < pattern.len and pattern[i + 1] == '*') { - // Handle '**' (any directory level) - i += 2; - while (j < target.len and target[j] != '/') { - j += 1; - } - } else { - // Handle '*' (any characters except '/') - i += 1; - while (j < target.len and target[j] != '/') { - j += 1; - } - } - }, - '?' => { - // Handle '?' (any single character) - i += 1; - j += 1; - }, - else => { - // Match characters literally - if (pattern[i] != target[j]) return false; - i += 1; - j += 1; - }, - } - } - - // Ensure the entire pattern and target are consumed - return i == pattern.len and j == target.len; - } - - fn matchesAnyPattern(target: string, patterns: []const string) bool { - for (patterns) |pattern| { - if (isGlobPattern(pattern)) { - if (matchesGlob(target, pattern)) { - return true; - } - } else { - if (matchesRegex(target, pattern)) { - return true; - } - } - } - return false; - } - fn runEventLoopForWatch(vm: *JSC.VirtualMachine) void { vm.eventLoop().tickPossiblyForever(); diff --git a/src/patterns.zig b/src/patterns.zig new file mode 100644 index 00000000000000..8d2262f03b1dd1 --- /dev/null +++ b/src/patterns.zig @@ -0,0 +1,147 @@ +const bun = @import("root").bun; +const string = bun.string; +const std = @import("std"); +const c_size_t = std.c_size_t; + +fn matchesRegex(target: string, pattern: string) bool { + const allocator = std.heap.c_allocator; + + // Import the PCRE2 library + const pcre2 = @cImport({ + @cInclude("pcre2.h"); + }); + + const _options = pcre2.PCRE2_ZERO_TERMINATED; + const error_code: *c_int = undefined; + const error_offset: *c_size_t = undefined; + + // Compile the regex pattern + const re = pcre2.pcre2_compile( + pattern.ptr, + pattern.len, + _options, + error_code, + error_offset, + null, + ); + + if (re == null) { + std.debug.warn("Failed to compile regex: {}\n", .{pattern}); + return false; + } + + const match_data = pcre2.pcre2_match_data_create_from_pattern(re, allocator); + if (match_data == null) { + pcre2.pcre2_code_free(re); + return false; + } + + const result = pcre2.pcre2_match( + re, + target.ptr, + target.len, + 0, + 0, + match_data, + null, + ); + + pcre2.pcre2_match_data_free(match_data); + pcre2.pcre2_code_free(re); + + return result >= 0; +} + +fn isGlobPattern(pattern: string) bool { + return std.mem.contains(u8, pattern, '*') or std.mem.contains(u8, pattern, '?'); +} + +fn matchesGlob(pattern: string, target: string) bool { + var i = 0; + var j = 0; + + while (i < pattern.len and j < target.len) { + switch (pattern[i]) { + '*' => { + if (i + 1 < pattern.len and pattern[i + 1] == '*') { + // Handle '**' (any directory level) + i += 2; + while (j < target.len and target[j] != '/') { + j += 1; + } + } else { + // Handle '*' (any characters except '/') + i += 1; + while (j < target.len and target[j] != '/') { + j += 1; + } + } + }, + '?' => { + // Handle '?' (any single character) + i += 1; + j += 1; + }, + else => { + // Match characters literally + if (pattern[i] != target[j]) return false; + i += 1; + j += 1; + }, + } + } + + // Ensure the entire pattern and target are consumed + return i == pattern.len and j == target.len; +} + +pub fn matchesAnyPattern(target: string, patterns: []const string) bool { + for (patterns) |pattern| { + if (isGlobPattern(pattern)) { + if (matchesGlob(target, pattern)) { + return true; + } + } else { + if (matchesRegex(target, pattern)) { + return true; + } + } + } + return false; +} + +test "matchesRegex should correctly match valid regex patterns" { + try testing.expect(matchesRegex("hello", "h.*o")); + try testing.expect(!matchesRegex("hello", "^world$")); + try testing.expect(matchesRegex("12345", "\\d+")); + try testing.expect(!matchesRegex("abc", "\\d+")); +} + +test "isGlobPattern should correctly identify glob patterns" { + try testing.expect(isGlobPattern("*.ts")); + try testing.expect(isGlobPattern("test?.txt")); + try testing.expect(!isGlobPattern("plain-text")); + try testing.expect(isGlobPattern("dir/**/*.js")); +} + +test "matchesGlob should correctly match glob patterns" { + try testing.expect(matchesGlob("*.ts", "file.ts")); + try testing.expect(!matchesGlob("*.ts", "file.js")); + try testing.expect(matchesGlob("test?.txt", "test1.txt")); + try testing.expect(!matchesGlob("test?.txt", "test12.txt")); + try testing.expect(matchesGlob("dir/**/*.js", "dir/subdir/file.js")); + try testing.expect(!matchesGlob("dir/**/*.js", "other/file.js")); +} + +test "matchesAnyPattern should correctly match against multiple patterns" { + const patternsList = &[_][]const u8{ + "*.ts", + "\\d+", + "file?.txt", + }; + + try testing.expect(matchesAnyPattern("file.ts", patternsList)); + try testing.expect(matchesAnyPattern("12345", patternsList)); + try testing.expect(matchesAnyPattern("file1.txt", patternsList)); + try testing.expect(!matchesAnyPattern("file.jpg", patternsList)); + try testing.expect(!matchesAnyPattern("abcdef", patternsList)); \ No newline at end of file From 2cb4758f5136afa34a0ea7040a2e3749efd52338 Mon Sep 17 00:00:00 2001 From: Asaf Benjaminov Date: Wed, 18 Dec 2024 12:56:37 +0200 Subject: [PATCH 03/10] change const to var --- src/cli/test_command.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli/test_command.zig b/src/cli/test_command.zig index d33d1a4369a692..78eb58713390f0 100644 --- a/src/cli/test_command.zig +++ b/src/cli/test_command.zig @@ -1319,7 +1319,7 @@ pub const TestCommand = struct { // try vm.ensureDebugger(false); - const test_files, const search_count = scan: { + var test_files, const search_count = scan: { if (for (ctx.positionals) |arg| { if (std.fs.path.isAbsolute(arg) or strings.startsWith(arg, "./") or From f04a7838e8d4873798a1c8282fcaa4a0731d3e2c Mon Sep 17 00:00:00 2001 From: Asaf Benjaminov Date: Wed, 25 Dec 2024 14:45:26 +0200 Subject: [PATCH 04/10] use bun glob --- src/patterns.zig | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/patterns.zig b/src/patterns.zig index 8d2262f03b1dd1..80e0b1f4ab34eb 100644 --- a/src/patterns.zig +++ b/src/patterns.zig @@ -1,4 +1,5 @@ const bun = @import("root").bun; +const glob = @import("../glob.zig"); const string = bun.string; const std = @import("std"); const c_size_t = std.c_size_t; @@ -97,8 +98,8 @@ fn matchesGlob(pattern: string, target: string) bool { pub fn matchesAnyPattern(target: string, patterns: []const string) bool { for (patterns) |pattern| { - if (isGlobPattern(pattern)) { - if (matchesGlob(target, pattern)) { + if (glob.detectGlobSyntax(pattern)) { + if (glob.matchImpl(pattern, target)) { return true; } } else { From 25730e51ded965e33060c2b2dd828d2d7206d55c Mon Sep 17 00:00:00 2001 From: Asaf Benjaminov Date: Wed, 25 Dec 2024 14:50:12 +0200 Subject: [PATCH 05/10] use bun glob --- src/patterns.zig | 52 +----------------------------------------------- 1 file changed, 1 insertion(+), 51 deletions(-) diff --git a/src/patterns.zig b/src/patterns.zig index 80e0b1f4ab34eb..a4b2b727f36fb4 100644 --- a/src/patterns.zig +++ b/src/patterns.zig @@ -1,5 +1,5 @@ const bun = @import("root").bun; -const glob = @import("../glob.zig"); +const glob = @import("./glob.zig"); const string = bun.string; const std = @import("std"); const c_size_t = std.c_size_t; @@ -53,49 +53,6 @@ fn matchesRegex(target: string, pattern: string) bool { return result >= 0; } -fn isGlobPattern(pattern: string) bool { - return std.mem.contains(u8, pattern, '*') or std.mem.contains(u8, pattern, '?'); -} - -fn matchesGlob(pattern: string, target: string) bool { - var i = 0; - var j = 0; - - while (i < pattern.len and j < target.len) { - switch (pattern[i]) { - '*' => { - if (i + 1 < pattern.len and pattern[i + 1] == '*') { - // Handle '**' (any directory level) - i += 2; - while (j < target.len and target[j] != '/') { - j += 1; - } - } else { - // Handle '*' (any characters except '/') - i += 1; - while (j < target.len and target[j] != '/') { - j += 1; - } - } - }, - '?' => { - // Handle '?' (any single character) - i += 1; - j += 1; - }, - else => { - // Match characters literally - if (pattern[i] != target[j]) return false; - i += 1; - j += 1; - }, - } - } - - // Ensure the entire pattern and target are consumed - return i == pattern.len and j == target.len; -} - pub fn matchesAnyPattern(target: string, patterns: []const string) bool { for (patterns) |pattern| { if (glob.detectGlobSyntax(pattern)) { @@ -118,13 +75,6 @@ test "matchesRegex should correctly match valid regex patterns" { try testing.expect(!matchesRegex("abc", "\\d+")); } -test "isGlobPattern should correctly identify glob patterns" { - try testing.expect(isGlobPattern("*.ts")); - try testing.expect(isGlobPattern("test?.txt")); - try testing.expect(!isGlobPattern("plain-text")); - try testing.expect(isGlobPattern("dir/**/*.js")); -} - test "matchesGlob should correctly match glob patterns" { try testing.expect(matchesGlob("*.ts", "file.ts")); try testing.expect(!matchesGlob("*.ts", "file.js")); From 5d02c093ff5560e38ec122c05afa808c09695fcf Mon Sep 17 00:00:00 2001 From: Asaf Benjaminov Date: Wed, 25 Dec 2024 14:58:00 +0200 Subject: [PATCH 06/10] use glob in testing coverage --- src/cli/test_command.zig | 4 ++-- src/patterns.zig | 9 --------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/src/cli/test_command.zig b/src/cli/test_command.zig index 78eb58713390f0..dc6e85f43706fd 100644 --- a/src/cli/test_command.zig +++ b/src/cli/test_command.zig @@ -1384,10 +1384,10 @@ pub const TestCommand = struct { for (test_files) |test_file| { const test_name = test_file.slice(); if (coverage_options.include) |includes| { - if (!patterns.matchesAnyPattern(test_name, includes)) continue; + if (!glob.detectGlobSyntax(includes) or !glob.matchImpl(test_name, includes)) continue; } if (coverage_options.exclude) |excludes| { - if (patterns.matchesAnyPattern(test_name, excludes)) continue; + if (glob.detectGlobSyntax(includes) and glob.matchImpl(test_name, includes)) continue; } try filtered_files.append(test_file); } diff --git a/src/patterns.zig b/src/patterns.zig index a4b2b727f36fb4..e3cd2088c8d8c7 100644 --- a/src/patterns.zig +++ b/src/patterns.zig @@ -75,15 +75,6 @@ test "matchesRegex should correctly match valid regex patterns" { try testing.expect(!matchesRegex("abc", "\\d+")); } -test "matchesGlob should correctly match glob patterns" { - try testing.expect(matchesGlob("*.ts", "file.ts")); - try testing.expect(!matchesGlob("*.ts", "file.js")); - try testing.expect(matchesGlob("test?.txt", "test1.txt")); - try testing.expect(!matchesGlob("test?.txt", "test12.txt")); - try testing.expect(matchesGlob("dir/**/*.js", "dir/subdir/file.js")); - try testing.expect(!matchesGlob("dir/**/*.js", "other/file.js")); -} - test "matchesAnyPattern should correctly match against multiple patterns" { const patternsList = &[_][]const u8{ "*.ts", From 83d667d3c0fddf6cca90cf8a65586b685a21bfa5 Mon Sep 17 00:00:00 2001 From: Asaf Benjaminov Date: Wed, 25 Dec 2024 14:58:42 +0200 Subject: [PATCH 07/10] use glob in testing coverage --- src/cli/test_command.zig | 1 - src/patterns.zig | 89 ---------------------------------------- 2 files changed, 90 deletions(-) delete mode 100644 src/patterns.zig diff --git a/src/cli/test_command.zig b/src/cli/test_command.zig index dc6e85f43706fd..61a5212237ad09 100644 --- a/src/cli/test_command.zig +++ b/src/cli/test_command.zig @@ -46,7 +46,6 @@ const Snapshots = JSC.Snapshot.Snapshots; const Test = TestRunner.Test; const CodeCoverageReport = bun.sourcemap.CodeCoverageReport; const uws = bun.uws; -const patterns = @import("../patterns.zig"); fn escapeXml(str: string, writer: anytype) !void { var last: usize = 0; diff --git a/src/patterns.zig b/src/patterns.zig deleted file mode 100644 index e3cd2088c8d8c7..00000000000000 --- a/src/patterns.zig +++ /dev/null @@ -1,89 +0,0 @@ -const bun = @import("root").bun; -const glob = @import("./glob.zig"); -const string = bun.string; -const std = @import("std"); -const c_size_t = std.c_size_t; - -fn matchesRegex(target: string, pattern: string) bool { - const allocator = std.heap.c_allocator; - - // Import the PCRE2 library - const pcre2 = @cImport({ - @cInclude("pcre2.h"); - }); - - const _options = pcre2.PCRE2_ZERO_TERMINATED; - const error_code: *c_int = undefined; - const error_offset: *c_size_t = undefined; - - // Compile the regex pattern - const re = pcre2.pcre2_compile( - pattern.ptr, - pattern.len, - _options, - error_code, - error_offset, - null, - ); - - if (re == null) { - std.debug.warn("Failed to compile regex: {}\n", .{pattern}); - return false; - } - - const match_data = pcre2.pcre2_match_data_create_from_pattern(re, allocator); - if (match_data == null) { - pcre2.pcre2_code_free(re); - return false; - } - - const result = pcre2.pcre2_match( - re, - target.ptr, - target.len, - 0, - 0, - match_data, - null, - ); - - pcre2.pcre2_match_data_free(match_data); - pcre2.pcre2_code_free(re); - - return result >= 0; -} - -pub fn matchesAnyPattern(target: string, patterns: []const string) bool { - for (patterns) |pattern| { - if (glob.detectGlobSyntax(pattern)) { - if (glob.matchImpl(pattern, target)) { - return true; - } - } else { - if (matchesRegex(target, pattern)) { - return true; - } - } - } - return false; -} - -test "matchesRegex should correctly match valid regex patterns" { - try testing.expect(matchesRegex("hello", "h.*o")); - try testing.expect(!matchesRegex("hello", "^world$")); - try testing.expect(matchesRegex("12345", "\\d+")); - try testing.expect(!matchesRegex("abc", "\\d+")); -} - -test "matchesAnyPattern should correctly match against multiple patterns" { - const patternsList = &[_][]const u8{ - "*.ts", - "\\d+", - "file?.txt", - }; - - try testing.expect(matchesAnyPattern("file.ts", patternsList)); - try testing.expect(matchesAnyPattern("12345", patternsList)); - try testing.expect(matchesAnyPattern("file1.txt", patternsList)); - try testing.expect(!matchesAnyPattern("file.jpg", patternsList)); - try testing.expect(!matchesAnyPattern("abcdef", patternsList)); \ No newline at end of file From a7c5766fd25a4d37c1972130c69520d72fbd569f Mon Sep 17 00:00:00 2001 From: Asaf Benjaminov Date: Wed, 25 Dec 2024 15:10:57 +0200 Subject: [PATCH 08/10] add testing for coverage --- test/cli/test/coverage.test.ts | 94 ++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/test/cli/test/coverage.test.ts b/test/cli/test/coverage.test.ts index 40f8db3dc7b390..645862c525b3e6 100644 --- a/test/cli/test/coverage.test.ts +++ b/test/cli/test/coverage.test.ts @@ -77,3 +77,97 @@ test("coverage excludes node_modules directory", () => { expect(result.exitCode).toBe(0); expect(result.signalCode).toBeUndefined(); }); + +test("coverage include coverageInclude from bunfig", () => { + const dir = tempDirWithFiles("cov", { + "bunfig.toml": ` + [coverage] + coverageIncludes = ["demo.test.ts"] + `, + "demo.test.ts": ` + export const pi = 3.14; + `, + }); + const result = Bun.spawnSync([bunExe(), "test", "--coverage"], { + cwd: dir, + env: { + ...bunEnv, + }, + stdio: [null, null, "pipe"], + }); + expect(result.stderr.toString("utf-8")).toContain("demo.test.ts"); + expect(result.exitCode).toBe(0); + expect(result.signalCode).toBeUndefined(); +}); + +test("coverage exclude coverageExclude from bunfig", () => { + const dir = tempDirWithFiles("cov", { + "bunfig.toml": ` + [coverage] + coverageExclude = ["demo.test.ts"] + `, + "demo.test.ts": ` + export const pi = 3.14; + `, + }); + const result = Bun.spawnSync([bunExe(), "test", "--coverage"], { + cwd: dir, + env: { + ...bunEnv, + }, + stdio: [null, null, "pipe"], + }); + expect(result.stderr.toString("utf-8")).not.toContain("demo.test.ts"); + expect(result.exitCode).toBe(0); + expect(result.signalCode).toBeUndefined(); +}); + +test("coverage include and exclude coverageInclude and coverageExclude from bunfig", () => { + const dir = tempDirWithFiles("cov", { + "bunfig.toml": ` + [coverage] + coverageIncludes = ["demo.test.ts"] + coverageExclude = ["demo.test.ts"] + `, + "demo.test.ts": ` + export const pi = 3.14; + `, + }); + const result = Bun.spawnSync([bunExe(), "test", "--coverage"], { + cwd: dir, + env: { + ...bunEnv, + }, + stdio: [null, null, "pipe"], + }); + expect(result.stderr.toString("utf-8")).not.toContain("demo.test.ts"); + expect(result.exitCode).toBe(0); + expect(result.signalCode).toBeUndefined(); +}); + +test("coverage include and exclude glob", () => { + const dir = tempDirWithFiles("cov", { + "bunfig.toml": ` + [coverage] + coverageIncludes = ["**/*.test.ts"] + coverageExclude = ["demo2.test.ts"] + `, + "demo.test.ts": ` + export const pi = 3.14; + `, + "demo2.test.ts": ` + export const pi = 3.14; + `, + }); + const result = Bun.spawnSync([bunExe(), "test", "--coverage"], { + cwd: dir, + env: { + ...bunEnv, + }, + stdio: [null, null, "pipe"], + }); + expect(result.stderr.toString("utf-8")).toContain("demo.test.ts"); + expect(result.stderr.toString("utf-8")).not.toContain("demo2.test.ts"); + expect(result.exitCode).toBe(0); + expect(result.signalCode).toBeUndefined(); +}); From c00468051607d2efd808a292647ca952dcc58f67 Mon Sep 17 00:00:00 2001 From: Asaf Benjaminov Date: Wed, 25 Dec 2024 15:11:19 +0200 Subject: [PATCH 09/10] use glob in testing coverage --- docs/test/coverage.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/test/coverage.md b/docs/test/coverage.md index 0f138ca619d304..53b52e8d03c10e 100644 --- a/docs/test/coverage.md +++ b/docs/test/coverage.md @@ -91,6 +91,7 @@ coverageReporter = "lcov" ``` #### Include or Exclude files from coverate + You can customize the files included or excluded from test coverage using the `coverageInclude` and `coverageExclude` options in your `bunfig.toml` file: ```toml @@ -102,7 +103,6 @@ coverageExclude = ["**/*.test.ts"] - `coverageInclude`: Specifies the files or patterns to include in coverage calculations. - `coverageExclude`: Specifies the files or patterns to exclude from coverage calculations. - Supported patterns: - - **Glob patterns**: For example, **/*.service.ts matches all .service.ts files in any folder. - - **Regular expressions**: For more advanced and precise matching, such as .*\.service\.ts$. - + - **Glob patterns**: For example, \*_/_.service.ts matches all .service.ts files in any folder. + These configurations provide flexibility to focus coverage on specific files or directories while ignoring others. From 14c3b87ffd19d222e42f3e4322c93e4feaa3a521 Mon Sep 17 00:00:00 2001 From: Asaf Benjaminov Date: Wed, 25 Dec 2024 15:14:21 +0200 Subject: [PATCH 10/10] use glob in testing coverage --- test/cli/test/coverage.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/cli/test/coverage.test.ts b/test/cli/test/coverage.test.ts index 645862c525b3e6..94972f2f746aef 100644 --- a/test/cli/test/coverage.test.ts +++ b/test/cli/test/coverage.test.ts @@ -81,7 +81,7 @@ test("coverage excludes node_modules directory", () => { test("coverage include coverageInclude from bunfig", () => { const dir = tempDirWithFiles("cov", { "bunfig.toml": ` - [coverage] + [test] coverageIncludes = ["demo.test.ts"] `, "demo.test.ts": ` @@ -103,7 +103,7 @@ test("coverage include coverageInclude from bunfig", () => { test("coverage exclude coverageExclude from bunfig", () => { const dir = tempDirWithFiles("cov", { "bunfig.toml": ` - [coverage] + [test] coverageExclude = ["demo.test.ts"] `, "demo.test.ts": ` @@ -125,7 +125,7 @@ test("coverage exclude coverageExclude from bunfig", () => { test("coverage include and exclude coverageInclude and coverageExclude from bunfig", () => { const dir = tempDirWithFiles("cov", { "bunfig.toml": ` - [coverage] + [test] coverageIncludes = ["demo.test.ts"] coverageExclude = ["demo.test.ts"] `, @@ -148,7 +148,7 @@ test("coverage include and exclude coverageInclude and coverageExclude from bunf test("coverage include and exclude glob", () => { const dir = tempDirWithFiles("cov", { "bunfig.toml": ` - [coverage] + [test] coverageIncludes = ["**/*.test.ts"] coverageExclude = ["demo2.test.ts"] `,