Skip to content

Commit 0bc9edd

Browse files
authored
Merge pull request #174 from Polymer/multi
Ability to take multiple measures on the same page
2 parents 2cda8d1 + 9d54ad3 commit 0bc9edd

21 files changed

+450
-222
lines changed

CHANGELOG.md

+8-3
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,19 @@ project adheres to [Semantic Versioning](http://semver.org/).
77

88
## Unreleased
99

10+
- Add ability to specify multiple measurements from the same page load by
11+
setting the `measurement` property in the JSON config file to an array. For
12+
example, you can now use the performance API to define two intervals on the
13+
same page, and compare them to each other or to other pages.
14+
1015
- Add ability to pull measurements from the browser performance measurement API,
1116
e.g.:
1217

1318
```
1419
"benchmarks": [
1520
{
1621
"measurement": {
17-
"kind": "performance",
22+
"mode": "performance",
1823
"entryName": "foo"
1924
}
2025
}
@@ -27,12 +32,12 @@ project adheres to [Semantic Versioning](http://semver.org/).
2732
"benchmarks": [
2833
{
2934
"measurement": {
30-
"kind": "callback"
35+
"mode": "callback"
3136
}
3237
},
3338
{
3439
"measurement": {
35-
"kind": "expression",
40+
"mode": "expression",
3641
"expression": "window.tachometerResult"
3742
}
3843
}

README.md

+6-2
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,13 @@ confidence in them.
8383

8484
## Measurement modes
8585

86-
Tachometer supports four kinds of time interval measurements, controlled with
86+
Tachometer supports four modes of time interval measurements, controlled with
8787
the `measurement` config file property, or the `--measure` flag.
8888

89+
If `measurement` is an array, then all of the given measurements will be
90+
retrieved from each page load. Each measurement from a page is treated as its
91+
own benchmark.
92+
8993
#### Performance API
9094

9195
Retrieve a measure, mark, or paint timing from the
@@ -107,7 +111,7 @@ And in your config file:
107111
"benchmarks": [
108112
{
109113
"measurement": {
110-
"kind": "performance",
114+
"mode": "performance",
111115
"entryName": "foo"
112116
}
113117
}

config.schema.json

+22-6
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@
55
"CallbackMeasurement": {
66
"additionalProperties": false,
77
"properties": {
8-
"kind": {
8+
"mode": {
99
"enum": [
1010
"callback"
1111
],
1212
"type": "string"
1313
}
1414
},
1515
"required": [
16-
"kind"
16+
"mode"
1717
],
1818
"type": "object"
1919
},
@@ -113,6 +113,22 @@
113113
{
114114
"$ref": "#/definitions/ExpressionMeasurement"
115115
},
116+
{
117+
"items": {
118+
"anyOf": [
119+
{
120+
"$ref": "#/definitions/CallbackMeasurement"
121+
},
122+
{
123+
"$ref": "#/definitions/PerformanceEntryMeasurement"
124+
},
125+
{
126+
"$ref": "#/definitions/ExpressionMeasurement"
127+
}
128+
]
129+
},
130+
"type": "array"
131+
},
116132
{
117133
"enum": [
118134
"callback",
@@ -191,7 +207,7 @@
191207
"expression": {
192208
"type": "string"
193209
},
194-
"kind": {
210+
"mode": {
195211
"enum": [
196212
"expression"
197213
],
@@ -200,7 +216,7 @@
200216
},
201217
"required": [
202218
"expression",
203-
"kind"
219+
"mode"
204220
],
205221
"type": "object"
206222
},
@@ -291,7 +307,7 @@
291307
"entryName": {
292308
"type": "string"
293309
},
294-
"kind": {
310+
"mode": {
295311
"enum": [
296312
"performance"
297313
],
@@ -300,7 +316,7 @@
300316
},
301317
"required": [
302318
"entryName",
303-
"kind"
319+
"mode"
304320
],
305321
"type": "object"
306322
},

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,4 @@
9292
"typescript": "^3.6.4",
9393
"typescript-json-schema": "^0.42.0"
9494
}
95-
}
95+
}

src/config.ts

+8-6
Original file line numberDiff line numberDiff line change
@@ -123,12 +123,14 @@ export async function makeConfig(opts: Opts): Promise<Config> {
123123
}
124124

125125
for (const spec of config.benchmarks) {
126-
if (spec.measurement.kind === 'performance' &&
127-
spec.measurement.entryName === 'first-contentful-paint' &&
128-
!fcpBrowsers.has(spec.browser.name)) {
129-
throw new Error(
130-
`Browser ${spec.browser.name} does not support the ` +
131-
`first contentful paint (FCP) measurement`);
126+
for (const measurement of spec.measurement) {
127+
if (measurement.mode === 'performance' &&
128+
measurement.entryName === 'first-contentful-paint' &&
129+
!fcpBrowsers.has(spec.browser.name)) {
130+
throw new Error(
131+
`Browser ${spec.browser.name} does not support the ` +
132+
`first contentful paint (FCP) measurement`);
133+
}
132134
}
133135
}
134136

src/configfile.ts

+15-12
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,8 @@ interface ConfigFileBenchmark {
136136
expand?: ConfigFileBenchmark[];
137137
}
138138

139-
type ConfigFileMeasurement = 'callback'|'fcp'|'global'|Measurement;
139+
type ConfigFileMeasurement =
140+
'callback'|'fcp'|'global'|Measurement|Array<Measurement>;
140141

141142
type BrowserConfigs =
142143
ChromeConfig|FirefoxConfig|SafariConfig|EdgeConfig|IEConfig;
@@ -341,22 +342,24 @@ async function parseBenchmark(benchmark: ConfigFileBenchmark, root: string):
341342
}
342343

343344
if (benchmark.measurement === 'callback') {
344-
spec.measurement = {
345-
kind: 'callback',
346-
};
345+
spec.measurement = [{
346+
mode: 'callback',
347+
}];
347348
} else if (benchmark.measurement === 'fcp') {
348-
spec.measurement = {
349-
kind: 'performance',
349+
spec.measurement = [{
350+
mode: 'performance',
350351
entryName: 'first-contentful-paint',
351-
};
352+
}];
352353
} else if (benchmark.measurement === 'global') {
353-
spec.measurement = {
354-
kind: 'expression',
354+
spec.measurement = [{
355+
mode: 'expression',
355356
expression:
356357
benchmark.measurementExpression || defaults.measurementExpression,
357-
};
358-
} else {
358+
}];
359+
} else if (Array.isArray(benchmark.measurement)) {
359360
spec.measurement = benchmark.measurement;
361+
} else if (benchmark.measurement !== undefined) {
362+
spec.measurement = [benchmark.measurement];
360363
}
361364

362365
const url = benchmark.url;
@@ -471,7 +474,7 @@ function applyDefaults(partialSpec: Partial<BenchmarkSpec>): BenchmarkSpec {
471474
};
472475
}
473476
if (measurement === undefined) {
474-
measurement = defaults.measurement(url);
477+
measurement = [defaults.measurement(url)];
475478
}
476479
return {name, url, browser, measurement};
477480
}

src/defaults.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ export const measurementExpression = 'window.tachometerResult';
2828
export function measurement(url: LocalUrl|RemoteUrl): Measurement {
2929
if (url.kind === 'remote') {
3030
return {
31-
kind: 'performance',
31+
mode: 'performance',
3232
entryName: 'first-contentful-paint',
3333
};
3434
}
35-
return {kind: 'callback'};
35+
return {mode: 'callback'};
3636
}

src/measure.ts

+22-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export async function measure(
2424
driver: webdriver.WebDriver,
2525
measurement: Measurement,
2626
server: Server|undefined): Promise<number|undefined> {
27-
switch (measurement.kind) {
27+
switch (measurement.mode) {
2828
case 'callback':
2929
if (server === undefined) {
3030
throw new Error('Internal error: no server for spec');
@@ -119,3 +119,24 @@ function escapeStringLiteral(unescaped: string): string {
119119
.replace(/`/g, '\\`')
120120
.replace(/\$/g, '\\$');
121121
}
122+
123+
/**
124+
* Return a good-enough label for the given measurement, to disambiguate cases
125+
* where there are multiple measurements on the same page.
126+
*/
127+
export function measurementName(measurement: Measurement): string {
128+
switch (measurement.mode) {
129+
case 'callback':
130+
return 'callback';
131+
case 'expression':
132+
return measurement.expression;
133+
case 'performance':
134+
return measurement.entryName === 'first-contentful-paint' ?
135+
'fcp' :
136+
measurement.entryName;
137+
}
138+
throwUnreachable(
139+
measurement,
140+
`Internal error: unknown measurement type ` +
141+
JSON.stringify(measurement));
142+
}

0 commit comments

Comments
 (0)