Skip to content

Commit

Permalink
feat: add diffing of lcov files
Browse files Browse the repository at this point in the history
  • Loading branch information
AdeAttwood committed Aug 16, 2023
1 parent b1c14af commit 4f7cf16
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 3 deletions.
26 changes: 25 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# Diff Cov

Simple CLI to print diffs highlighted with test coverage status
Simple CLI to print git and lcov diffs highlighted with test coverage status

![Example Output](assets/example-output.png)

Expand Down Expand Up @@ -45,6 +45,8 @@ git symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/0.x
## Usage

### Git diff

Before you run `diff-cov` you must run your test suite with coverage and output
a `lcov` coverage file. You must also have all your changes committed to ensure
it's included in the output.
Expand All @@ -62,3 +64,25 @@ diff-cov --coverageFile coverage/lcov.info

A report is printed at the bottom and colored with a threshold of `90%`
anything below this percentage coverage will be colored red.

### LCov diff

You can also print the coverage difference between to lcov.info files. To use
this run your tests with coverage for a first time. After its finished you can
copy your lcov.info file somewhere for later for example:

```shell
cp ./lcov.info /tmp/lcov.info
```

Then you can work on your test and run the tests once more with coverage to
generate you a new `lcov.info` file. Then you can print the difference in
coverage between the two files with:

```shell
diff-cov --compare /tmp/lcov.info
```

This can come in handy when you need to find out what a test is testing. You
can create your base coverage file, comment out a test, run the tests again and
diff the results.
20 changes: 18 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { hideBin } from "yargs/helpers";
import exec from "./exec";

import report from "./report";
import lcovDiff from "./lcov-diff";

// eslint-disable-next-line @typescript-eslint/no-var-requires
const parseDiff = require("./diff-parser");
Expand All @@ -21,11 +22,16 @@ const options: { [key: string]: Options } = {
description: "The path to the lcov report file",
type: "string",
},
compare: {
description: "The path to the lcov report you wish to compare to the `coverage-file`",
type: "string",
},
};

type Argv = Arguments<
Partial<{
coverageFile: string;
compare?: string;
}>
>;

Expand All @@ -36,6 +42,10 @@ export const validate = async (argv: Argv) => {
`Please ensure you have run tests with coverage enabled.`
);
}

if (argv.compare && !fs.existsSync(argv.compare)) {
return new Error(`Lcov compare file must be a valid file '${argv.compare}' provided.`);
}
};

export const run = async (argv = process.argv) => {
Expand All @@ -45,13 +55,19 @@ export const run = async (argv = process.argv) => {
return error(validationError.message);
}

const baseCoverage = await parseLcov.default(parsed.coverageFile);

if (parsed.compare) {
const compareCoverage = await parseLcov.default(parsed.compare);
return lcovDiff(baseCoverage, compareCoverage);
}

const diffText = await exec(`git diff origin/HEAD...HEAD`);
if (diffText.code > 0) {
return error("Error loading the diff\n\n" + diffText.stderr);
}

const diff = parseDiff.default(diffText.stdout);
const coverage = await parseLcov.default(parsed.coverageFile);

report(diff, coverage);
report(diff, baseCoverage);
};
40 changes: 40 additions & 0 deletions src/lcov-diff.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
const buildCoverageSet = (report: any) => {
const set = new Set<string>();
for (const coverage of report) {
for (const detail of coverage.lines.details) {
if (detail.hit > 0) {
set.add(`${coverage.file}:${detail.line}`);
}
}
}

return set;
};

function setDiff<T>(a: Set<T>, b: Set<T>) {
return new Set([...a].filter((x) => !b.has(x)));
}

export const lcovDiff = async (baseCoverage: any, compareCoverage: any) => {
const baseSet = buildCoverageSet(baseCoverage);
const compareSet = buildCoverageSet(compareCoverage);

const map = new Map<string, number>();

const added = setDiff(baseSet, compareSet);
for (const key of added) {
map.set(key, 1);
}

const removed = setDiff(compareSet, baseSet);
for (const key of removed) {
map.set(key, 0);
}

for (const [key, value] of [...map.entries()].sort()) {
const color = value > 0 ? "\x1b[32m" : "\x1b[31m";
console.log(color, key, "\x1b[0m");
}
};

export default lcovDiff;

0 comments on commit 4f7cf16

Please sign in to comment.