mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-12-12 11:50:54 -06:00
1. Add a script to generate a sorted list of most costly tests. A tests' cost is roughly `runtime% / number of edits`. A slow test that's only been updated once is much less valuable than a slow test that has been updated 20 times: the latter test is catching more changes in the type system. 2. Check in the results of running this script. I want to make the skipping behaviour deterministic and the same for everybody, even though you may get slightly better performance by examining only *your* test changes. 3. Add code to skip tests until it reaches a 5% chance of missing an edit. Right now this provides a 38% speedup. Still not done: 4. Make this value configurable. 5. Make the CI configuration specify a 0% chance of missing an edit.
105 lines
3.3 KiB
TypeScript
105 lines
3.3 KiB
TypeScript
// @ts-check
|
|
const fs = require("fs");
|
|
const git = require('simple-git/promise')('.')
|
|
const readline = require('readline')
|
|
|
|
/** @typedef {{ [s: string]: number}} Histogram */
|
|
|
|
async function main() {
|
|
/** @type {Histogram} */
|
|
const edits = Object.create(null)
|
|
/** @type {Histogram} */
|
|
const perf = JSON.parse(fs.readFileSync('.parallelperf.json', 'utf8'))
|
|
|
|
await collectCommits(git, "release-2.3", "master", /*author*/ undefined, files => fillMap(files, edits))
|
|
|
|
const totalTime = Object.values(perf).reduce((n,m) => n + m, 0)
|
|
const untouched = Object.values(perf).length - Object.values(edits).length
|
|
const totalEdits = Object.values(edits).reduce((n,m) => n + m, 0) + untouched + Object.values(edits).length
|
|
|
|
let i = 0
|
|
/** @type {{ name: string, time: number, edits: number, cost: number }[]} */
|
|
let data = []
|
|
for (const k in perf) {
|
|
const otherk = k.replace(/tsrunner-[a-z-]+?:\/\//, '')
|
|
const percentTime = perf[k] / totalTime
|
|
const percentHits = (1 + (edits[otherk] || 0)) / totalEdits
|
|
const cost = 5 + Math.log(percentTime / percentHits)
|
|
// TODO: Write counts instead of numbers to make JSON file smaller
|
|
data.push({ name: otherk, time: perf[k], edits: 1 + (edits[otherk] || 0), cost})
|
|
if (edits[otherk])
|
|
i++
|
|
}
|
|
const output = {
|
|
totalTime,
|
|
totalEdits,
|
|
data: data.sort((x,y) => y.cost - x.cost).map(x => ({ ...x, cost: x.cost.toFixed(2) }))
|
|
}
|
|
|
|
fs.writeFileSync('.test-cost.json', JSON.stringify(output), 'utf8')
|
|
}
|
|
|
|
main().catch(e => {
|
|
console.log(e);
|
|
process.exit(1);
|
|
})
|
|
|
|
/**
|
|
* @param {string[]} files
|
|
* @param {Histogram} histogram
|
|
*/
|
|
function fillMap(files, histogram) {
|
|
// keep edits to test cases (but not /users), and not file moves
|
|
const tests = files.filter(f => f.startsWith('tests/cases/') && !f.startsWith('tests/cases/user') && !/=>/.test(f))
|
|
for (const test of tests) {
|
|
histogram[test] = (histogram[test] || 0) + 1
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {string} s
|
|
*/
|
|
function isSquashMergeMessage(s) {
|
|
return /\(#[0-9]+\)$/.test(s)
|
|
}
|
|
|
|
/**
|
|
* @param {string} s
|
|
*/
|
|
function isMergeCommit(s) {
|
|
return /Merge pull request #[0-9]+/.test(s)
|
|
}
|
|
|
|
/**
|
|
* @param {string} s
|
|
*/
|
|
function parseFiles(s) {
|
|
const lines = s.split('\n')
|
|
// Note that slice(2) only works for merge commits, which have an empty newline after the title
|
|
return lines.slice(2, lines.length - 2).map(line => line.split("|")[0].trim())
|
|
}
|
|
|
|
/**
|
|
* @param {import('simple-git/promise').SimpleGit} git
|
|
* @param {string} from
|
|
* @param {string} to
|
|
* @param {string | undefined} author - only include commits from this author
|
|
* @param {(files: string[]) => void} update
|
|
*/
|
|
async function collectCommits(git, from, to, author, update) {
|
|
let i = 0
|
|
for (const commit of (await git.log({ from, to })).all) {
|
|
i++
|
|
if ((!author || commit.author_name === author) && isMergeCommit(commit.message) || isSquashMergeMessage(commit.message)) {
|
|
readline.clearLine(process.stdout, /*left*/ -1)
|
|
readline.cursorTo(process.stdout, 0)
|
|
process.stdout.write(i + ": " + commit.date)
|
|
const files = parseFiles(await git.show([commit.hash, "--stat=1000,960,40", "--pretty=oneline"]))
|
|
update(files)
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|