Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to include or exclude files in the testing coverage #15840

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions docs/test/coverage.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

These configurations provide flexibility to focus coverage on specific files or directories while ignoring others.
20 changes: 20 additions & 0 deletions src/bunfig.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
25 changes: 24 additions & 1 deletion src/cli/test_command.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -1315,7 +1318,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
Expand Down Expand Up @@ -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 (!glob.detectGlobSyntax(includes) or !glob.matchImpl(test_name, includes)) continue;
}
if (coverage_options.exclude) |excludes| {
if (glob.detectGlobSyntax(includes) and glob.matchImpl(test_name, includes)) continue;
}
try filtered_files.append(test_file);
}

test_files = filtered_files.items;
}

if (test_files.len > 0) {
vm.hot_reload = ctx.debug.hot_reload;

Expand Down
94 changes: 94 additions & 0 deletions test/cli/test/coverage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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": `
[test]
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": `
[test]
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": `
[test]
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": `
[test]
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();
});