mirror of
https://github.com/uutils/coreutils
synced 2024-10-07 00:19:14 +00:00
docs: add page with test coverage
This commit is contained in:
parent
1167d811d5
commit
ac11d8793e
13
.github/workflows/GnuTests.yml
vendored
13
.github/workflows/GnuTests.yml
vendored
|
@ -32,7 +32,8 @@ jobs:
|
||||||
TEST_FILESET_PREFIX='test-fileset-IDs.sha1#'
|
TEST_FILESET_PREFIX='test-fileset-IDs.sha1#'
|
||||||
TEST_FILESET_SUFFIX='.txt'
|
TEST_FILESET_SUFFIX='.txt'
|
||||||
TEST_SUMMARY_FILE='gnu-result.json'
|
TEST_SUMMARY_FILE='gnu-result.json'
|
||||||
outputs SUITE_LOG_FILE TEST_FILESET_PREFIX TEST_FILESET_SUFFIX TEST_LOGS_GLOB TEST_SUMMARY_FILE
|
TEST_FULL_SUMMARY_FILE='gnu-full-result.json'
|
||||||
|
outputs SUITE_LOG_FILE TEST_FILESET_PREFIX TEST_FILESET_SUFFIX TEST_LOGS_GLOB TEST_SUMMARY_FILE TEST_FULL_SUMMARY_FILE
|
||||||
- name: Checkout code (uutil)
|
- name: Checkout code (uutil)
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
|
@ -92,6 +93,11 @@ jobs:
|
||||||
path_GNU='${{ steps.vars.outputs.path_GNU }}'
|
path_GNU='${{ steps.vars.outputs.path_GNU }}'
|
||||||
path_UUTILS='${{ steps.vars.outputs.path_UUTILS }}'
|
path_UUTILS='${{ steps.vars.outputs.path_UUTILS }}'
|
||||||
bash "${path_UUTILS}/util/run-gnu-test.sh"
|
bash "${path_UUTILS}/util/run-gnu-test.sh"
|
||||||
|
- name: Extract testing info into JSON
|
||||||
|
shell: bash
|
||||||
|
run : |
|
||||||
|
path_UUTILS='${{ steps.vars.outputs.path_UUTILS }}'
|
||||||
|
python ${path_UUTILS}/util/gnu-json-result.py ${{ steps.vars.outputs.path_GNU_tests }} > ${{ steps.vars.outputs.TEST_FULL_SUMMARY_FILE }}
|
||||||
- name: Extract/summarize testing info
|
- name: Extract/summarize testing info
|
||||||
id: summary
|
id: summary
|
||||||
shell: bash
|
shell: bash
|
||||||
|
@ -146,6 +152,11 @@ jobs:
|
||||||
with:
|
with:
|
||||||
name: test-logs
|
name: test-logs
|
||||||
path: "${{ steps.vars.outputs.TEST_LOGS_GLOB }}"
|
path: "${{ steps.vars.outputs.TEST_LOGS_GLOB }}"
|
||||||
|
- name: Upload full json results
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: gnu-full-result.json
|
||||||
|
path: ${{ steps.vars.outputs.TEST_FULL_SUMMARY_FILE }}
|
||||||
- name: Compare test failures VS reference
|
- name: Compare test failures VS reference
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
|
|
46
docs/src/test_coverage.css
Normal file
46
docs/src/test_coverage.css
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
:root {
|
||||||
|
--PASS: #44AF69;
|
||||||
|
--ERROR: #F8333C;
|
||||||
|
--FAIL: #F8333C;
|
||||||
|
--SKIP: #d3c994;
|
||||||
|
}
|
||||||
|
.PASS {
|
||||||
|
color: var(--PASS);
|
||||||
|
}
|
||||||
|
.ERROR {
|
||||||
|
color: var(--ERROR);
|
||||||
|
}
|
||||||
|
.FAIL {
|
||||||
|
color: var(--FAIL);
|
||||||
|
}
|
||||||
|
.SKIP {
|
||||||
|
color: var(--SKIP);
|
||||||
|
}
|
||||||
|
.testSummary {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 90%;
|
||||||
|
}
|
||||||
|
.progress {
|
||||||
|
width: 80%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: right;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.progress-bar {
|
||||||
|
height: 10px;
|
||||||
|
width: calc(100% - 15ch);
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
.result {
|
||||||
|
font-weight: bold;
|
||||||
|
width: 7ch;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.result-line {
|
||||||
|
margin: 8px;
|
||||||
|
}
|
||||||
|
.counts {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
77
docs/src/test_coverage.js
Normal file
77
docs/src/test_coverage.js
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
// spell-checker:ignore hljs
|
||||||
|
function progressBar(totals) {
|
||||||
|
const bar = document.createElement("div");
|
||||||
|
bar.className = "progress-bar";
|
||||||
|
let totalTests = 0;
|
||||||
|
for (const [key, value] of Object.entries(totals)) {
|
||||||
|
totalTests += value;
|
||||||
|
}
|
||||||
|
const passPercentage = Math.round(100 * totals["PASS"] / totalTests);
|
||||||
|
const skipPercentage = passPercentage + Math.round(100 * totals["PASS"] / totalTests);
|
||||||
|
bar.style = `background: linear-gradient(
|
||||||
|
to right,
|
||||||
|
var(--PASS) ${passPercentage}%,
|
||||||
|
var(--SKIP) ${passPercentage}%,
|
||||||
|
var(--SKIP) ${skipPercentage}%,
|
||||||
|
var(--FAIL) 0)`;
|
||||||
|
|
||||||
|
const progress = document.createElement("div");
|
||||||
|
progress.className = "progress"
|
||||||
|
progress.innerHTML = `
|
||||||
|
<span class="counts">
|
||||||
|
<span class="PASS">${totals["PASS"]}</span>
|
||||||
|
/
|
||||||
|
<span class="SKIP">${totals["SKIP"]}</span>
|
||||||
|
/
|
||||||
|
<span class="FAIL">${totals["FAIL"] + totals["ERROR"]}</span>
|
||||||
|
</span>
|
||||||
|
`;
|
||||||
|
progress.appendChild(bar);
|
||||||
|
return progress
|
||||||
|
}
|
||||||
|
|
||||||
|
function parse_result(parent, obj) {
|
||||||
|
const totals = {
|
||||||
|
PASS: 0,
|
||||||
|
SKIP: 0,
|
||||||
|
FAIL: 0,
|
||||||
|
ERROR: 0,
|
||||||
|
};
|
||||||
|
for (const [category, content] of Object.entries(obj)) {
|
||||||
|
if (typeof content === "string") {
|
||||||
|
const p = document.createElement("p");
|
||||||
|
p.className = "result-line";
|
||||||
|
totals[content]++;
|
||||||
|
p.innerHTML = `<span class="result" style="color: var(--${content})">${content}</span> ${category}`;
|
||||||
|
parent.appendChild(p);
|
||||||
|
} else {
|
||||||
|
const categoryName = document.createElement("code");
|
||||||
|
categoryName.innerHTML = category;
|
||||||
|
categoryName.className = "hljs";
|
||||||
|
|
||||||
|
const details = document.createElement("details");
|
||||||
|
const subtotals = parse_result(details, content);
|
||||||
|
for (const [subtotal, count] of Object.entries(subtotals)) {
|
||||||
|
totals[subtotal] += count;
|
||||||
|
}
|
||||||
|
const summaryDiv = document.createElement("div");
|
||||||
|
summaryDiv.className = "testSummary";
|
||||||
|
summaryDiv.appendChild(categoryName);
|
||||||
|
summaryDiv.appendChild(progressBar(subtotals));
|
||||||
|
|
||||||
|
const summary = document.createElement("summary");
|
||||||
|
summary.appendChild(summaryDiv);
|
||||||
|
|
||||||
|
details.appendChild(summary);
|
||||||
|
parent.appendChild(details);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return totals;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch("https://github.com/uutils/coreutils-tracking/blob/main/gnu-full-result.json")
|
||||||
|
.then((r) => r.json())
|
||||||
|
.then((obj) => {
|
||||||
|
let parent = document.getElementById("test-cov");
|
||||||
|
parse_result(parent, obj);
|
||||||
|
});
|
19
docs/src/test_coverage.md
Normal file
19
docs/src/test_coverage.md
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# GNU Test Coverage
|
||||||
|
|
||||||
|
uutils is actively tested against the GNU coreutils test suite. The results
|
||||||
|
below are automatically updated every day.
|
||||||
|
|
||||||
|
## Coverage per category
|
||||||
|
|
||||||
|
Click on the categories to see the names of the tests. Green indicates a passing
|
||||||
|
test, yellow indicates a skipped test and red means that the test either failed
|
||||||
|
or resulted in an error.
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="test_coverage.css">
|
||||||
|
<script src="test_coverage.js"></script>
|
||||||
|
|
||||||
|
<div id="test-cov"></div>
|
||||||
|
|
||||||
|
## Progress over time
|
||||||
|
|
||||||
|
<image src="https://github.com/uutils/coreutils-tracking/blob/main/gnu-results.png?raw=true">
|
|
@ -26,6 +26,7 @@ fn main() -> io::Result<()> {
|
||||||
[Introduction](index.md)\n\
|
[Introduction](index.md)\n\
|
||||||
* [Installation](installation.md)\n\
|
* [Installation](installation.md)\n\
|
||||||
* [Contributing](contributing.md)\n\
|
* [Contributing](contributing.md)\n\
|
||||||
|
* [GNU test coverage](test_coverage.md)\n\
|
||||||
\n\
|
\n\
|
||||||
# Reference\n\
|
# Reference\n\
|
||||||
* [Multi-call binary](multicall.md)\n",
|
* [Multi-call binary](multicall.md)\n",
|
||||||
|
|
27
util/gnu-json-result.py
Normal file
27
util/gnu-json-result.py
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
"""
|
||||||
|
Extract the GNU logs into a JSON file.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
import sys
|
||||||
|
from os import environ
|
||||||
|
|
||||||
|
out = {}
|
||||||
|
|
||||||
|
test_dir = Path(sys.argv[1])
|
||||||
|
for filepath in test_dir.glob("**/*.log"):
|
||||||
|
path = Path(filepath)
|
||||||
|
current = out
|
||||||
|
for key in path.parent.relative_to(test_dir).parts:
|
||||||
|
if key not in current:
|
||||||
|
current[key] = {}
|
||||||
|
current = current[key]
|
||||||
|
try:
|
||||||
|
with open(path) as f:
|
||||||
|
content = f.read()
|
||||||
|
current[path.name] = content.split("\n")[-2].split(" ")[0]
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
print(json.dumps(out, indent=2, sort_keys=True))
|
Loading…
Reference in a new issue