Skip to content

Commit

Permalink
Implement initial LCOV reporter (no function names support) (#11883)
Browse files Browse the repository at this point in the history
Co-authored-by: Jarred Sumner <[email protected]>
Co-authored-by: dave caruso <[email protected]>
  • Loading branch information
3 people authored Jun 22, 2024
1 parent ff2080d commit 4830e2d
Show file tree
Hide file tree
Showing 7 changed files with 554 additions and 199 deletions.
18 changes: 18 additions & 0 deletions docs/test/coverage.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,21 @@ Internally, Bun transpiles all files by default, so Bun automatically generates
[test]
coverageIgnoreSourcemaps = true # default false
```

### Coverage reporters

By default, coverage reports will be printed to the console.

You can specify the reporters and the directory where the reports will be saved.
This is needed, especially when you integrate coverages with tools like CodeCov, CodeClimate, Coveralls and so on.

```toml
coverageReporters = ["console", "lcov"] # default ["console"]
coverageDir = "path/to/somewhere" # default "coverage"
```

| Reporter | Description |
|-----------|-------------|
| `console` | Prints a text summary of the coverage to the console. |
| `lcov` | Save coverage in [lcov](https://github.com/linux-test-project/lcov) format. |

28 changes: 28 additions & 0 deletions src/bunfig.zig
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const Npm = @import("./install/npm.zig");
const PackageManager = @import("./install/install.zig").PackageManager;
const PackageJSON = @import("./resolver/package_json.zig").PackageJSON;
const resolver = @import("./resolver/resolver.zig");
const TestCommand = @import("./cli/test_command.zig").TestCommand;
pub const MacroImportReplacementMap = bun.StringArrayHashMap(string);
pub const MacroMap = bun.StringArrayHashMapUnmanaged(MacroImportReplacementMap);
pub const BundlePackageOverride = bun.StringArrayHashMapUnmanaged(options.BundleOverride);
Expand Down Expand Up @@ -55,6 +56,11 @@ pub const Bunfig = struct {
return error.@"Invalid Bunfig";
}

fn addErrorFormat(this: *Parser, loc: logger.Loc, allocator: std.mem.Allocator, comptime text: string, args: anytype) !void {
this.log.addErrorFmt(this.source, loc, allocator, text, args) catch unreachable;
return error.@"Invalid Bunfig";
}

fn parseRegistryURLString(this: *Parser, str: *js_ast.E.String) !Api.NpmRegistry {
const url = URL.parse(str.data);
var registry = std.mem.zeroes(Api.NpmRegistry);
Expand Down Expand Up @@ -252,6 +258,28 @@ pub const Bunfig = struct {
this.ctx.test_options.coverage.enabled = expr.data.e_boolean.value;
}

if (test_.get("coverageReporters")) |expr| {
this.ctx.test_options.coverage.reporters = .{ .console = false, .lcov = false };
try this.expect(expr, .e_array);
const items = expr.data.e_array.items.slice();
for (items) |item| {
try this.expectString(item);
const item_str = item.asString(bun.default_allocator) orelse "";
if (bun.strings.eqlComptime(item_str, "console")) {
this.ctx.test_options.coverage.reporters.console = true;
} else if (bun.strings.eqlComptime(item_str, "lcov")) {
this.ctx.test_options.coverage.reporters.lcov = true;
} else {
try this.addErrorFormat(item.loc, allocator, "Invalid coverage reporter \"{s}\"", .{item_str});
}
}
}

if (test_.get("coverageDir")) |expr| {
try this.expectString(expr);
this.ctx.test_options.coverage.reports_directory = try expr.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
20 changes: 20 additions & 0 deletions src/cli.zig
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,8 @@ 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("--coverage Generate a coverage profile") catch unreachable,
clap.parseParam("--coverage-reporter <STR>... Report coverage in 'console' and/or 'lcov'. Defaults to 'console'.") catch unreachable,
clap.parseParam("--coverage-dir <STR> Directory for coverage files. Defaults to 'coverage'.") 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,
};
Expand Down Expand Up @@ -441,6 +443,24 @@ pub const Arguments = struct {
ctx.test_options.coverage.enabled = args.flag("--coverage");
}

if (args.options("--coverage-reporter").len > 0) {
ctx.test_options.coverage.reporters = .{ .console = false, .lcov = false };
for (args.options("--coverage-reporter")) |reporter| {
if (bun.strings.eqlComptime(reporter, "console")) {
ctx.test_options.coverage.reporters.console = true;
} else if (bun.strings.eqlComptime(reporter, "lcov")) {
ctx.test_options.coverage.reporters.lcov = true;
} else {
Output.prettyErrorln("<r><red>error<r>: --coverage-reporter received invalid reporter: \"{s}\"", .{reporter});
Global.exit(1);
}
}
}

if (args.option("--coverage-dir")) |dir| {
ctx.test_options.coverage.reports_directory = dir;
}

if (args.option("--bail")) |bail| {
if (bail.len > 0) {
ctx.test_options.bail = std.fmt.parseInt(u32, bail, 10) catch |e| {
Expand Down
Loading

0 comments on commit 4830e2d

Please sign in to comment.