Skip to content

Commit

Permalink
ci: add code coverage checking script
Browse files Browse the repository at this point in the history
  • Loading branch information
ashutosh-ukey committed Dec 24, 2024
1 parent ee66cdd commit c4f15c4
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 3 deletions.
1 change: 1 addition & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Mandatory section.

### Checklist
- [ ] Did you add new tests and confirm all tests pass? (`yarn test`)
- [ ] Did you ensure any new Solidity source code files meet minimum test coverage requirements? (`yarn coverage`)
- [ ] Did you update relevant docs? (docs are found in the `docs` folder)
- [ ] Do your commits follow the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) standard?
- [ ] Does your PR title also follow the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) standard?
Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,5 @@ jobs:
- name: Setup environment
uses: ./.github/actions/setup

# TODO: fail the build if coverage is below the threshold
# TODO: print the coverage report in the github output
- name: Run Test Coverage
run: yarn coverage
1 change: 1 addition & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ via_ir = true
auto_detect_solc = false
auto_detect_remappings = false
deny_warnings = true
no_match_coverage = "test/|script/"

[fuzz]
runs = 1024
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"lint": "solhint -w 0 -c .solhint-src.json './src/**/*.sol' && solhint -w 0 -c .solhint-test.json './test/**/*.sol' && solhint -w 0 -c .solhint-script.json './script/**/*.sol'",
"test": "forge test -vv",
"gasreport": "forge test --gas-report > gas/reports/gas-report.txt",
"coverage": "forge coverage",
"coverage": "forge coverage > temp-ci_yarn_check-coverage.out && node script/test/checkCoverage.js temp-ci_yarn_check-coverage.out && rm temp-ci_yarn_check-coverage.out",
"format:check": "forge fmt --check",
"format:write": "forge fmt"
},
Expand Down
103 changes: 103 additions & 0 deletions script/test/checkCoverage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
const fs = require('fs');

const COVERAGE_TABLE_HEADER = "| File | % Lines | % Statements | % Branches | % Funcs |";
const COVERAGE_TABLE_HEADER_SEPARATOR = "|--------------------------------------------------------------------------------|--------------------|--------------------|------------------|------------------|";
const COVERAGE_TABLE_TOTAL_ROW_NAME = 'Total';
const COVERAGE_TABLE_COLUMN_DELIM = '|';

// Matches expressions like "12.25%"
const COVERAGE_TABLE_COVERAGE_PECENTAGE_REGEXP = /[\d\.]+%/;

const MIN_REQUIRED_LINE_COVERAGE_PERCENTAGE = 90;
const MIN_REQUIRED_STATEMENT_COVERAGE_PERCENTAGE = 90;
const MIN_REQUIRED_BRANCH_COVERAGE_PERCENTAGE = 90;
const MIN_REQUIRED_FUNCTION_COVERAGE_PERCENTAGE = 90;

const NUM_COLUMNS = COVERAGE_TABLE_HEADER.split(COVERAGE_TABLE_COLUMN_DELIM).length - 2;

function parsePercentage(rawCoveragePercentText) {
const numericDecimalText = COVERAGE_TABLE_COVERAGE_PECENTAGE_REGEXP.exec(rawCoveragePercentText)[0].slice(0, -1);
return parseFloat(numericDecimalText);
}

function parseCoverageTableRow(rawRowText) {
let rowParts = rawRowText.split(COVERAGE_TABLE_COLUMN_DELIM);
if (rowParts.length - 2 != NUM_COLUMNS) {
return null
}

rowParts = rowParts.slice(1, -1);
return {
fileName: rowParts[0].trim(),
lineCoveragePercent: parsePercentage(rowParts[1]),
statementCoveragePercent: parsePercentage(rowParts[2]),
branchCoveragePercent: parsePercentage(rowParts[3]),
functionCoveragePercent: parsePercentage(rowParts[4]),
}
}

function getFormattedCoverageTableRowsTest(coverageTableRows) {
return COVERAGE_TABLE_HEADER + '\n'
+ COVERAGE_TABLE_HEADER_SEPARATOR + '\n'
+ coverageTableRows.join('\n') + '\n';
}

(async function main() {
const coverateReportFileName = process.argv[2];
const coverageReportRawText = fs.readFileSync(coverateReportFileName, "utf8");

let coverageTableBodyRaw = "";
try {
coverageTableBodyRaw = coverageReportRawText.split(COVERAGE_TABLE_HEADER)[1];
} catch (error) {
console.error("Unexpected coverage report format");
console.error(error);
process.exit(1);
}

const belowThresholdFiles = [];
const aboveThresholdFiles = [];
let totalCoverageRow = "";
const coverageTableRows = coverageTableBodyRaw.split("\n").slice(3);

for (const coverageTableRowRaw of coverageTableRows) {
const coverageRow = parseCoverageTableRow(coverageTableRowRaw);
if (!coverageRow) {
continue;
}

// Check minimum required coverage percentages
if (coverageRow.fileName == COVERAGE_TABLE_TOTAL_ROW_NAME) {
totalCoverageRow = coverageTableRowRaw;
} else if (coverageRow.lineCoveragePercent < MIN_REQUIRED_LINE_COVERAGE_PERCENTAGE ||
coverageRow.statementCoveragePercent < MIN_REQUIRED_STATEMENT_COVERAGE_PERCENTAGE ||
coverageRow.branchCoveragePercent < MIN_REQUIRED_BRANCH_COVERAGE_PERCENTAGE ||
coverageRow.functionCoveragePercent < MIN_REQUIRED_FUNCTION_COVERAGE_PERCENTAGE) {

belowThresholdFiles.push(coverageTableRowRaw);
} else {
aboveThresholdFiles.push(coverageTableRowRaw);
}
}

// Print coverage breakdown details
console.log("Total coverage: ");
console.log(getFormattedCoverageTableRowsTest([totalCoverageRow]));

if (belowThresholdFiles.length > 0) {
console.log("Found files below coverage threshold: ");
console.log(getFormattedCoverageTableRowsTest(belowThresholdFiles));
} else {
console.log("All source code files meet minimum coverage requirements.");
}
if (aboveThresholdFiles.length > 0) {
console.log("Files above coverage threshold: ");
console.log(getFormattedCoverageTableRowsTest(aboveThresholdFiles));
}

// Fail if any files found below the minimum coverage threshold
if (belowThresholdFiles.length > 0) {
// TODO: uncomment line once source code coverages have been bumped up
// process.exit(2);
}
})();

0 comments on commit c4f15c4

Please sign in to comment.