1
0
mirror of https://github.com/svg/svgo.git synced 2026-01-27 07:02:06 +03:00
Files
svgo/test/regression/lib.js
2025-12-01 23:05:14 +00:00

154 lines
4.1 KiB
JavaScript

/**
* @fileoverview Utilities to manage manage regression tests.
*/
import { exec } from 'node:child_process';
import crypto from 'node:crypto';
import path from 'node:path';
import fs from 'node:fs/promises';
import picocolors from 'picocolors';
/**
* @typedef {'KiB' | 'MiB' | 'GiB' | 'TiB'} StorageUnit
*/
/** @type {StorageUnit[]} */
const UNITS = ['KiB', 'MiB', 'GiB', 'TiB'];
/**
* Reads a list of file paths from a file in predefine, ignoring empty lines and
* any lines starting with a # as these are treated as comments.
*
* @param {string} path
* Path to a file relative to the working directory of the process.
* @returns {Promise<string[]>}
* File paths that were listed at the given path.
*/
export async function readFileList(path) {
const content = await fs.readFile(path, 'utf-8');
return content.split('\n').filter((l) => l.length !== 0 && l[0] !== '#');
}
/**
* @param {string} value
* @returns {string}
*/
export function md5sum(value) {
const hasher = crypto.createHash('md5');
return hasher.update(value).digest('hex');
}
/**
* @param {import('./regression-io.js').TestReport} report
*/
export async function printReport(report) {
const { shouldHaveMatched, shouldHaveMismatched } = report.errors;
console.log(`SVGO Test Suite Version: ${report.version}
▶ Test Results
Match: ${report.results.match.toLocaleString()} / ${report.files.toMatch.toLocaleString()}
Expected Mismatch: ${report.results.expectMismatch.toLocaleString()} / ${report.files.toMismatch.toLocaleString()}
Ignored: ${report.results.ignored.toLocaleString()} / ${report.files.toIgnore.toLocaleString()}
Skipped: ${report.files.toSkip.toLocaleString()}
▶ Metrics
Bytes Saved: ${bytesToHumanReadable(report.metrics.bytesSaved)}
Time Taken: ${secsToHumanReadable(report.metrics.timeTakenSecs)}
Peak Memory Alloc: ${bytesToHumanReadable(report.metrics.peakMemoryAlloc, 'KiB')}${
shouldHaveMatched.length !== 0
? picocolors.red(
`\n\n▶ Expected match, but actually mismatched:\n${toBulletPointList(shouldHaveMatched, '✖')}`,
)
: ''
}${
shouldHaveMismatched.length !== 0
? picocolors.red(
`\n\n▶ Expected mismatch, but actually matched:\n${toBulletPointList(shouldHaveMismatched, '✖')}`,
)
: ''
}`);
}
/**
* @param {number} bytes
* @param {StorageUnit=} startingUnits
* If the first argument is specified in a unit other than bytes, then a
* string representing the units that was passed.
* @returns {string}
*/
export function bytesToHumanReadable(bytes, startingUnits) {
let units = startingUnits ? UNITS.indexOf(startingUnits) : 0;
let value = startingUnits ? bytes : bytes / 1024;
while (value > 1024) {
units++;
value /= 1024;
}
return `${value.toLocaleString(undefined, {
maximumFractionDigits: 3,
})} ${UNITS[units]}`;
}
/**
* @param {number} secs
* @returns {string}
*/
export function secsToHumanReadable(secs) {
const hours = Math.floor(secs / 3600);
secs -= hours * 3600;
const minutes = Math.floor(secs / 60);
secs -= minutes * 60;
const arr = new Array(1);
if (hours) {
arr.push(`${hours}h`);
}
if (minutes) {
arr.push(`${minutes.toString().padStart(2, '0')}m`);
}
arr.push(`${Math.round(secs).toString().padStart(2, '0')}s`);
return arr.join('');
}
/**
* @param {string[]} arr
* @param {string} bullet
* @returns {string}
*/
export function toBulletPointList(arr, bullet = '*') {
return arr.map((s) => `${bullet} ${s}`).join('\n');
}
/**
* @param {string} filepath
* Path that uses file separators for the current operating system.
* ({@link path.sep})
* @returns {string}
* Same path but with POSIX file separators. ({@link path.posix.sep})
*/
export function pathToPosix(filepath) {
return filepath.replaceAll(path.sep, path.posix.sep);
}
/**
* @returns {Promise<string>}
*/
export async function getCommitRef() {
return new Promise((res, rej) => {
exec('git rev-parse HEAD', (err, stdout) => {
if (err) {
rej(err);
return;
}
res(stdout.trim());
});
});
}