Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for custom measurement units #199

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/) and this
project adheres to [Semantic Versioning](http://semver.org/).

<!-- ## Unreleased -->
## Unreleased

- Add support for defining the unit of a measurement.

## [0.5.5] 2020-09-21

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ If `measurement` is an array, then all of the given measurements will be
retrieved from each page load. Each measurement from a page is treated as its
own benchmark.

A measurement can specify a `name` property that will be used to display its
results.
A measurement can specify a `name` and/or a `unit` property that will be used to
display its results.

#### Performance API

Expand Down
9 changes: 9 additions & 0 deletions config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
},
"name": {
"type": "string"
},
"unit": {
"type": "string"
}
},
"required": [
Expand Down Expand Up @@ -218,6 +221,9 @@
},
"name": {
"type": "string"
},
"unit": {
"type": "string"
}
},
"required": [
Expand Down Expand Up @@ -361,6 +367,9 @@
},
"name": {
"type": "string"
},
"unit": {
"type": "string"
}
},
"required": [
Expand Down
8 changes: 8 additions & 0 deletions src/configfile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,14 @@ async function parseBenchmark(benchmark: ConfigFileBenchmark, root: string):
spec.measurement = [benchmark.measurement];
}

if (spec.measurement) {
for (const measurement of spec.measurement) {
if (measurement.unit == null) {
measurement.unit = defaults.measurementUnit;
}
}
}

const url = benchmark.url;
if (url !== undefined) {
if (isHttpUrl(url)) {
Expand Down
4 changes: 2 additions & 2 deletions src/csv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,11 @@ export function formatCsvRaw(results: ResultStatsWithDifferences[]): string {
for (let r = 0; r < results.length; r++) {
const {result} = results[r];
headers.push(result.name);
for (let m = 0; m < result.millis.length; m++) {
for (let m = 0; m < result.rawData.length; m++) {
if (rows[m] === undefined) {
rows[m] = [];
}
rows[m][r] = result.millis[m];
rows[m][r] = result.rawData[m];
}
}
return csvStringify([headers, ...rows]);
Expand Down
4 changes: 3 additions & 1 deletion src/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@ export const mode = 'automatic';
export const resolveBareModules = true;
export const forceCleanNpmInstall = false;
export const measurementExpression = 'window.tachometerResult';
export const measurementUnit = 'ms';

export function measurement(url: LocalUrl|RemoteUrl): Measurement {
if (url.kind === 'remote') {
return {
mode: 'performance',
entryName: 'first-contentful-paint',
unit: measurementUnit
};
}
return {mode: 'callback'};
return {mode: 'callback', unit: measurementUnit};
}
25 changes: 14 additions & 11 deletions src/format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {UAParser} from 'ua-parser-js';
import ansi = require('ansi-escape-sequences');

import {Difference, ConfidenceInterval, ResultStats, ResultStatsWithDifferences} from './stats';
import {BenchmarkSpec, BenchmarkResult} from './types';
import {BenchmarkSpec, BenchmarkResult, Measurement} from './types';

export const spinner = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'].map(
(frame) => ansi.format(`[blue]{${frame}}`));
Expand Down Expand Up @@ -93,7 +93,7 @@ export function automaticResultTable(results: ResultStats[]): AutomaticResults {
if (diff === null) {
return ansi.format('\n[gray]{-} ');
}
return formatDifference(diff);
return formatDifference(diff, r.result.measurement);
},
});
}
Expand Down Expand Up @@ -262,7 +262,7 @@ const browserDimension: Dimension = {

const sampleSizeDimension: Dimension = {
label: 'Sample size',
format: (r: ResultStats) => r.result.millis.length.toString(),
format: (r: ResultStats) => r.result.rawData.length.toString(),
};

const bytesSentDimension: Dimension = {
Expand All @@ -275,25 +275,28 @@ const runtimeConfidenceIntervalDimension: Dimension = {
tableConfig: {
alignment: 'right',
},
format: (r: ResultStats) => formatConfidenceInterval(r.stats.meanCI, milli),
format: (r: ResultStats) => formatConfidenceInterval(
r.stats.meanCI, formatMeasure(r.result.measurement)),
};

function formatDifference({absolute, relative}: Difference): string {
function formatDifference(
{absolute, relative}: Difference, measurement: Measurement): string {
const format = formatMeasure(measurement);
let word, rel, abs;
if (absolute.low > 0 && relative.low > 0) {
word = `[bold red]{slower}`;
rel = formatConfidenceInterval(relative, percent);
abs = formatConfidenceInterval(absolute, milli);
abs = formatConfidenceInterval(absolute, format);

} else if (absolute.high < 0 && relative.high < 0) {
word = `[bold green]{faster}`;
rel = formatConfidenceInterval(negate(relative), percent);
abs = formatConfidenceInterval(negate(absolute), milli);
abs = formatConfidenceInterval(negate(absolute), format);

} else {
word = `[bold blue]{unsure}`;
rel = formatConfidenceInterval(relative, (n) => colorizeSign(n, percent));
abs = formatConfidenceInterval(absolute, (n) => colorizeSign(n, milli));
abs = formatConfidenceInterval(absolute, (n) => colorizeSign(n, format));
}

return ansi.format(`${word}\n${rel}\n${abs}`);
Expand All @@ -303,9 +306,9 @@ function percent(n: number): string {
return (n * 100).toFixed(0) + '%';
}

function milli(n: number): string {
return n.toFixed(2) + 'ms';
}
const formatMeasure = (measurement: Measurement) => (n: number) => {
return n.toFixed(2) + measurement.unit;
};

function negate(ci: ConfidenceInterval): ConfidenceInterval {
return {
Expand Down
2 changes: 1 addition & 1 deletion src/json-output.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export function jsonOutput(results: ResultStatsWithDifferences[]):
high: result.stats.meanCI.high,
},
differences,
samples: result.result.millis,
samples: result.result.rawData,
});
}
return {benchmarks};
Expand Down
6 changes: 3 additions & 3 deletions src/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ export class Runner {
if (primary === undefined) {
specResults[newResult.measurementIndex] = newResult;
} else {
primary.millis.push(...newResult.millis);
primary.rawData.push(...newResult.rawData);
}
}
}
Expand Down Expand Up @@ -320,7 +320,7 @@ export class Runner {
version: spec.url.kind === 'local' && spec.url.version !== undefined ?
spec.url.version.label :
'',
millis: [measurementResults[measurementIndex]],
rawData: [measurementResults[measurementIndex]],
bytesSent: session ? session.bytesSent : 0,
browser: spec.browser,
userAgent: session ? session.userAgent : '',
Expand All @@ -332,7 +332,7 @@ export class Runner {
for (const results of this.results.values()) {
for (let r = 0; r < results.length; r++) {
const result = results[r];
resultStats.push({result, stats: summaryStats(result.millis)});
resultStats.push({result, stats: summaryStats(result.rawData)});
}
}
return computeDifferences(resultStats);
Expand Down
3 changes: 3 additions & 0 deletions src/specs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,17 +73,20 @@ export async function specsFromOpts(opts: Opts): Promise<BenchmarkSpec[]> {
if (opts.measure === 'callback') {
measurement = {
mode: 'callback',
unit: defaults.measurementUnit,
};
} else if (opts.measure === 'fcp') {
measurement = {
mode: 'performance',
entryName: 'first-contentful-paint',
unit: defaults.measurementUnit,
};
} else if (opts.measure === 'global') {
measurement = {
mode: 'expression',
expression:
opts['measurement-expression'] || defaults.measurementExpression,
unit: defaults.measurementUnit,
};
} else if (opts.measure !== undefined) {
throwUnreachable(
Expand Down
16 changes: 4 additions & 12 deletions src/test/config_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,7 @@ suite('makeConfig', function() {
width: 1024,
},
},
measurement: [{
mode: 'callback',
}],
measurement: [{mode: 'callback', unit: 'ms'}],
name: 'random-global.html',
url: {
kind: 'local',
Expand Down Expand Up @@ -103,9 +101,7 @@ suite('makeConfig', function() {
width: 1024,
},
},
measurement: [{
mode: 'callback',
}],
measurement: [{mode: 'callback', unit: 'ms'}],
// TODO(aomarks) Why does this have a forward-slash?
name: '/random-global.html',
url: {
Expand Down Expand Up @@ -146,9 +142,7 @@ suite('makeConfig', function() {
width: 1024,
},
},
measurement: [{
mode: 'callback',
}],
measurement: [{mode: 'callback', unit: 'ms'}],
// TODO(aomarks) Why does this have a forward-slash?
name: '/random-global.html',
url: {
Expand Down Expand Up @@ -196,9 +190,7 @@ suite('makeConfig', function() {
width: 1024,
},
},
measurement: [{
mode: 'callback',
}],
measurement: [{mode: 'callback', unit: 'ms'}],
// TODO(aomarks) Why does this have a forward-slash?
name: '/random-global.html',
url: {
Expand Down
27 changes: 13 additions & 14 deletions src/test/configfile_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ suite('config', () => {
{
mode: 'performance',
entryName: 'first-contentful-paint',
unit: 'ms'
},
],
url: {
Expand All @@ -130,6 +131,7 @@ suite('config', () => {
measurement: [
{
mode: 'callback',
unit: 'ms',
},
],
url: {
Expand All @@ -156,6 +158,7 @@ suite('config', () => {
{
mode: 'performance',
entryName: 'first-contentful-paint',
unit: 'ms'
},
],
url: {
Expand Down Expand Up @@ -197,6 +200,7 @@ suite('config', () => {
{
mode: 'performance',
entryName: 'first-contentful-paint',
unit: 'ms'
},
],
browser: defaultBrowser,
Expand All @@ -209,9 +213,7 @@ suite('config', () => {
queryString: '?foo=bar',
},
measurement: [
{
mode: 'callback',
},
{mode: 'callback', unit: 'ms'},
],
browser: defaultBrowser,
},
Expand Down Expand Up @@ -261,9 +263,7 @@ suite('config', () => {
queryString: '?foo=a',
},
measurement: [
{
mode: 'callback',
},
{mode: 'callback', unit: 'ms'},
],
browser: defaultBrowser,
},
Expand All @@ -275,10 +275,7 @@ suite('config', () => {
queryString: '?foo=b',
},
measurement: [
{
mode: 'expression',
expression: 'window.foo',
},
{mode: 'expression', expression: 'window.foo', unit: 'ms'},
],
browser: defaultBrowser,
},
Expand All @@ -290,10 +287,7 @@ suite('config', () => {
queryString: '?foo=c',
},
measurement: [
{
mode: 'performance',
entryName: 'foo-measure',
},
{mode: 'performance', entryName: 'foo-measure', unit: 'ms'},
],
browser: defaultBrowser,
},
Expand Down Expand Up @@ -339,6 +333,7 @@ suite('config', () => {
{
mode: 'performance',
entryName: 'first-contentful-paint',
unit: 'ms',
},
],
browser: defaultBrowser,
Expand All @@ -350,6 +345,7 @@ suite('config', () => {
{
mode: 'performance',
entryName: 'first-contentful-paint',
unit: 'ms',
},
],
browser: {
Expand All @@ -363,6 +359,7 @@ suite('config', () => {
measurement: [
{
mode: 'callback',
unit: 'ms',
},
],
browser: defaultBrowser,
Expand All @@ -373,6 +370,7 @@ suite('config', () => {
measurement: [
{
mode: 'callback',
unit: 'ms',
},
],
browser: {
Expand Down Expand Up @@ -417,6 +415,7 @@ suite('config', () => {
{
mode: 'performance',
entryName: 'first-contentful-paint',
unit: 'ms',
},
],
browser: {
Expand Down
Loading