Files
brave-core/build/commands/scripts/generateCoverageReport.js
Aleksei Khoroshilov 240fb3f915 Migrate config.js to TS in build/commands (#35374)
* Replace imports.

* Migrate config.js to TS.

* Replace build references.

* Adjust TargetOS to infer from a runtime list.

* Apply stricter rules for optional properties.

* Restore options.outputDir to keep the PR without any logic change.
2026-04-15 08:26:13 +01:00

142 lines
4.7 KiB
JavaScript

// Copyright (c) 2025 The Brave Authors. All rights reserved.
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.
import { glob, rm } from 'node:fs/promises'
import fs from 'fs-extra'
import utils from '../lib/util.js'
import config from '../lib/config.ts'
const dedupe = (xs) => [...new Set(xs)]
const getTestBinariesFromRecordingsPath = (outputDir) => {
const regex = new RegExp(outputDir + '/coverage/(.*?)/')
return (recording) => {
const testName = recording.match(regex).at(1)
return `${outputDir}/${testName}`
}
}
export default (program) =>
program
.command('coverage_report')
.description(
[
'generates a coverage report.',
'Requires to build with --use_clang_coverage and run test.',
'HTML report will be in out/$BUILD_DIR/coverage/report/$NAME/dist',
].join('\n'),
)
.option('-C [build_dir]', 'build config (out/Debug, out/Release)')
.option('--target_arch [target_arch]', 'target architecture')
.option('--target_os <target_os>', 'target OS')
.option(
'--name [name]',
'name of the test report [defaults to coverage]',
'all',
)
.option(
'--clean',
'delete recordings and reports. Either all or those that match --tests [testSuites]',
)
.option(
'--tests [testSuites]',
'comma seperated list of testsuites to consider. By default it parses all recordings',
)
.arguments('[build_config]')
.action(async (buildConfig, args) => {
config.buildConfig = buildConfig || config.buildConfig
config.update(args)
const dirsGlob = args.tests ? `{${args.tests}}/**` : '**'
const out = `${config.outputDir}/coverage`
const reportPath = `${out}/report/${args.name}`
const profDataPath = `${reportPath}/coverage.profdata`
const distPath = `${reportPath}/dist`
const recordings = await Array.fromAsync(
glob(`${out}/${dirsGlob}/*.profraw`),
)
if (args.clean) {
await Promise.all(
recordings
.concat([reportPath])
.map((path) => rm(path, { recursive: true, force: true })),
)
return
}
const allTestSuites = dedupe(
recordings.map(getTestBinariesFromRecordingsPath(config.outputDir)),
)
// llvm-cov needs a .profdata (llvms .lcov equivalent) and
// binaries that include the symbols that are instrumented to generate a report
// Weirdly it appears that the json export is unaffected.
// We probably want to use gcov / genhtml instead of llvm-cov
// For now - if we can - only use brave_browser_tests binary
// since it will have the most amount of symbols to generate the report.
// It will still include coverage information of code paths of other test suites
// as long as their symbols are present in the brave_browser_test binary
const hasBBT = allTestSuites.find((x) =>
x.includes('brave_browser_tests'),
)
const testSuites = hasBBT
? [`${config.outputDir}/brave_browser_tests`]
: allTestSuites
if (!recordings.length) {
console.log(
`glob ${out}/${dirsGlob}/*.profraw didn't yield any recordings!\n make sure you've built with --use_coverage and ran the appropriate tests`,
)
return
}
const cwd = config.outputDir
const coverageToolPath = `${config.srcDir}/third_party/llvm-build/Release+Asserts/bin`
// fetch coverage tools if not available
await fs.mkdirp(distPath)
await utils.runAsync(
`${coverageToolPath}/llvm-profdata`,
['merge', '-sparse', '-o', profDataPath, ...recordings],
{ cwd },
)
await utils.runAsync(
`${coverageToolPath}/llvm-cov`,
[
'show',
`-compilation-dir=${config.outputDir}`,
`-instr-profile=${profDataPath}`,
'-format=html',
`-output-dir=${distPath}`,
...testSuites,
],
{ cwd },
)
const output = await utils.runAsync(
`${coverageToolPath}/llvm-cov`,
[
'export',
`-compilation-dir=${config.outputDir}`,
`-instr-profile=${profDataPath}`,
'--summary-only',
...testSuites,
],
{ cwd, stdio: 'pipe' },
)
try {
const summary = JSON.parse(output)
await fs.writeJSON(`${distPath}/coverage.json`, summary)
console.log(summary.data[0].totals)
} catch (e) {
console.error(e)
}
console.log(`\ncoverage reports written to ${out}/report`)
})