mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-17 10:01:20 -08:00
test: [ENG-08] add verified TS coverage and paired performance reports
This commit is contained in:
1 parent
8ed0a5228c
commit
4a9a26d4fc
38 files changed
+24335
-61
No files matched your search
@@ -0,0 +1,58 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { createHash } from 'node:crypto'
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import ts from 'typescript'
|
||||
|
||||
const walk = directory => fs.readdirSync(directory, { withFileTypes: true }).flatMap(entry => entry.isDirectory() ? walk(path.join(directory, entry.name)) : [path.join(directory, entry.name)])
|
||||
const relative = (root, file) => path.relative(root, file).split(path.sep).join('/')
|
||||
|
||||
export function sourceInventory(root, policy) {
|
||||
assert.equal(policy.schemaVersion, 1)
|
||||
assert(policy.packages.length > 0)
|
||||
const files = []
|
||||
const excluded = []
|
||||
for (const name of policy.packages) {
|
||||
assert(/^artplayer(?:-[a-z-]+)?$/.test(name), `Invalid coverage package: ${name}`)
|
||||
for (const file of walk(path.join(root, 'packages', name, 'src')).filter(file => /\.[jt]s$/.test(file))) {
|
||||
const name = relative(root, file)
|
||||
const source = fs.readFileSync(file, 'utf8').replaceAll('\r\n', '\n')
|
||||
const sha256 = createHash('sha256').update(source).digest('hex')
|
||||
const emitted = file.endsWith('.d.ts') ? '' : ts.transpileModule(source, { compilerOptions: { target: ts.ScriptTarget.ESNext, module: ts.ModuleKind.ESNext } }).outputText.trim()
|
||||
const reason = policy.exclude[name] || (/^(?:export\s*\{\s*\}\s*;?)?$/.test(emitted) ? 'Type-only module; no emitted runtime code.' : undefined)
|
||||
if (reason) {
|
||||
excluded.push({ file: name, sha256, reason })
|
||||
}
|
||||
else {
|
||||
assert(!/\b(?:c8|v8|istanbul)\s+ignore\b/i.test(source), `Undocumented coverage suppression: ${name}`)
|
||||
files.push({ file: name, sha256 })
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const name of Object.keys(policy.exclude))
|
||||
assert(excluded.some(item => item.file === name), `Stale coverage exclusion: ${name}`)
|
||||
for (const name of Object.keys(policy.minimum))
|
||||
assert(files.some(item => item.file === name), `Critical coverage file is absent or excluded: ${name}`)
|
||||
return { files: files.sort((a, b) => a.file.localeCompare(b.file)), excluded }
|
||||
}
|
||||
|
||||
export function analyzeCoverage(root, summary, inventory, policy) {
|
||||
const normalized = new Map(Object.entries(summary).filter(([file]) => file !== 'total').map(([file, value]) => [relative(root, file), value]))
|
||||
assert.deepEqual([...normalized.keys()].sort(), inventory.files.map(item => item.file).sort(), 'Coverage source inventory differs; a source map or zero-coverage file is missing')
|
||||
const violations = []
|
||||
const files = inventory.files.map((source) => {
|
||||
const metrics = normalized.get(source.file)
|
||||
const values = {}
|
||||
for (const metric of ['lines', 'branches', 'functions', 'statements']) {
|
||||
const { covered, total, skipped } = metrics[metric]
|
||||
assert(Number.isInteger(total) && total >= 0 && Number.isInteger(covered) && covered >= 0 && covered <= total && skipped === 0, `Invalid or ignored ${metric}: ${source.file}`)
|
||||
const percent = total ? covered * 100 / total : 100
|
||||
values[metric] = { covered, total, percent }
|
||||
const minimum = policy.minimum[source.file]?.[metric]
|
||||
if (minimum !== undefined && (total === 0 || percent < minimum))
|
||||
violations.push({ file: source.file, metric, minimum, actual: percent, total })
|
||||
}
|
||||
return { ...source, metrics: values }
|
||||
})
|
||||
return { files, excluded: inventory.excluded, minimum: policy.minimum, violations }
|
||||
}
|
||||
Reference in new issue
Block a user