Skip to content

Commit 2cda8d1

Browse files
authored
Merge pull request #173 from Polymer/measure-refactor
Improve representation of measurements
2 parents 1b6e358 + eccade0 commit 2cda8d1

16 files changed

+385
-168
lines changed

CHANGELOG.md

+20-3
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,26 @@ project adheres to [Semantic Versioning](http://semver.org/).
1414
"benchmarks": [
1515
{
1616
"measurement": {
17-
"performanceEntry": {
18-
"name": "foo"
19-
}
17+
"kind": "performance",
18+
"entryName": "foo"
19+
}
20+
}
21+
]
22+
```
23+
24+
- Add new syntax for specifying benchmarks:
25+
26+
```
27+
"benchmarks": [
28+
{
29+
"measurement": {
30+
"kind": "callback"
31+
}
32+
},
33+
{
34+
"measurement": {
35+
"kind": "expression",
36+
"expression": "window.tachometerResult"
2037
}
2138
}
2239
]

README.md

+2-3
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,8 @@ And in your config file:
107107
"benchmarks": [
108108
{
109109
"measurement": {
110-
"performanceEntry": {
111-
"name": "foo"
112-
}
110+
"kind": "performance",
111+
"entryName": "foo"
113112
}
114113
}
115114
]

config.schema.json

+51-14
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,21 @@
22
"$schema": "http://json-schema.org/draft-07/schema#",
33
"additionalProperties": false,
44
"definitions": {
5+
"CallbackMeasurement": {
6+
"additionalProperties": false,
7+
"properties": {
8+
"kind": {
9+
"enum": [
10+
"callback"
11+
],
12+
"type": "string"
13+
}
14+
},
15+
"required": [
16+
"kind"
17+
],
18+
"type": "object"
19+
},
520
"ChromeConfig": {
621
"additionalProperties": false,
722
"properties": {
@@ -90,16 +105,13 @@
90105
"measurement": {
91106
"anyOf": [
92107
{
93-
"additionalProperties": false,
94-
"properties": {
95-
"performanceEntry": {
96-
"$ref": "#/definitions/PerformanceEntryCriteria"
97-
}
98-
},
99-
"required": [
100-
"performanceEntry"
101-
],
102-
"type": "object"
108+
"$ref": "#/definitions/CallbackMeasurement"
109+
},
110+
{
111+
"$ref": "#/definitions/PerformanceEntryMeasurement"
112+
},
113+
{
114+
"$ref": "#/definitions/ExpressionMeasurement"
103115
},
104116
{
105117
"enum": [
@@ -173,6 +185,25 @@
173185
],
174186
"type": "object"
175187
},
188+
"ExpressionMeasurement": {
189+
"additionalProperties": false,
190+
"properties": {
191+
"expression": {
192+
"type": "string"
193+
},
194+
"kind": {
195+
"enum": [
196+
"expression"
197+
],
198+
"type": "string"
199+
}
200+
},
201+
"required": [
202+
"expression",
203+
"kind"
204+
],
205+
"type": "object"
206+
},
176207
"FirefoxConfig": {
177208
"additionalProperties": false,
178209
"properties": {
@@ -254,16 +285,22 @@
254285
"description": "A mapping from NPM package name to version specifier, as used in a\npackage.json's \"dependencies\" and \"devDependencies\".",
255286
"type": "object"
256287
},
257-
"PerformanceEntryCriteria": {
288+
"PerformanceEntryMeasurement": {
258289
"additionalProperties": false,
259-
"description": "Criteria for matching a Performance Entry.",
260290
"properties": {
261-
"name": {
291+
"entryName": {
292+
"type": "string"
293+
},
294+
"kind": {
295+
"enum": [
296+
"performance"
297+
],
262298
"type": "string"
263299
}
264300
},
265301
"required": [
266-
"name"
302+
"entryName",
303+
"kind"
267304
],
268305
"type": "object"
269306
},

src/config.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,9 @@ export async function makeConfig(opts: Opts): Promise<Config> {
123123
}
124124

125125
for (const spec of config.benchmarks) {
126-
if (spec.measurement === 'fcp' && !fcpBrowsers.has(spec.browser.name)) {
126+
if (spec.measurement.kind === 'performance' &&
127+
spec.measurement.entryName === 'first-contentful-paint' &&
128+
!fcpBrowsers.has(spec.browser.name)) {
127129
throw new Error(
128130
`Browser ${spec.browser.name} does not support the ` +
129131
`first contentful paint (FCP) measurement`);

src/configfile.ts

+20-15
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ interface ConfigFileBenchmark {
114114
* }
115115
* }
116116
*/
117-
measurement?: Measurement;
117+
measurement?: ConfigFileMeasurement;
118118

119119
/**
120120
* Expression to use to retrieve global result. Defaults to
@@ -136,6 +136,8 @@ interface ConfigFileBenchmark {
136136
expand?: ConfigFileBenchmark[];
137137
}
138138

139+
type ConfigFileMeasurement = 'callback'|'fcp'|'global'|Measurement;
140+
139141
type BrowserConfigs =
140142
ChromeConfig|FirefoxConfig|SafariConfig|EdgeConfig|IEConfig;
141143

@@ -338,13 +340,24 @@ async function parseBenchmark(benchmark: ConfigFileBenchmark, root: string):
338340
spec.browser = browser;
339341
}
340342

341-
if (benchmark.measurement !== undefined) {
343+
if (benchmark.measurement === 'callback') {
344+
spec.measurement = {
345+
kind: 'callback',
346+
};
347+
} else if (benchmark.measurement === 'fcp') {
348+
spec.measurement = {
349+
kind: 'performance',
350+
entryName: 'first-contentful-paint',
351+
};
352+
} else if (benchmark.measurement === 'global') {
353+
spec.measurement = {
354+
kind: 'expression',
355+
expression:
356+
benchmark.measurementExpression || defaults.measurementExpression,
357+
};
358+
} else {
342359
spec.measurement = benchmark.measurement;
343360
}
344-
if (spec.measurement === 'global' &&
345-
benchmark.measurementExpression !== undefined) {
346-
spec.measurementExpression = benchmark.measurementExpression;
347-
}
348361

349362
const url = benchmark.url;
350363
if (url !== undefined) {
@@ -460,15 +473,7 @@ function applyDefaults(partialSpec: Partial<BenchmarkSpec>): BenchmarkSpec {
460473
if (measurement === undefined) {
461474
measurement = defaults.measurement(url);
462475
}
463-
const spec: BenchmarkSpec = {name, url, browser, measurement};
464-
if (measurement === 'global') {
465-
if (partialSpec.measurementExpression === undefined) {
466-
spec.measurementExpression = defaults.measurementExpression;
467-
} else {
468-
spec.measurementExpression = partialSpec.measurementExpression;
469-
}
470-
}
471-
return spec;
476+
return {name, url, browser, measurement};
472477
}
473478

474479
export async function writeBackSchemaIfNeeded(

src/defaults.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,10 @@ export const measurementExpression = 'window.tachometerResult';
2727

2828
export function measurement(url: LocalUrl|RemoteUrl): Measurement {
2929
if (url.kind === 'remote') {
30-
return 'fcp';
30+
return {
31+
kind: 'performance',
32+
entryName: 'first-contentful-paint',
33+
};
3134
}
32-
return 'callback';
35+
return {kind: 'callback'};
3336
}

src/flags.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import * as path from 'path';
1414

1515
import {supportedBrowsers} from './browser';
1616
import * as defaults from './defaults';
17-
import {Measurement, measurements} from './types';
17+
import {CommandLineMeasurements, measurements} from './types';
1818

1919
import commandLineArgs = require('command-line-args');
2020
import commandLineUsage = require('command-line-usage');
@@ -216,7 +216,7 @@ export interface Opts {
216216
'sample-size': number|undefined;
217217
manual: boolean;
218218
save: string;
219-
measure: Measurement|undefined;
219+
measure: CommandLineMeasurements|undefined;
220220
'measurement-expression': string|undefined;
221221
horizon: string|undefined;
222222
timeout: number|undefined;

src/measure.ts

+14-21
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
import * as webdriver from 'selenium-webdriver';
1313

1414
import {Server} from './server';
15-
import {BenchmarkSpec, PerformanceEntryCriteria} from './types';
15+
import {Measurement, PerformanceEntryMeasurement} from './types';
1616
import {throwUnreachable} from './util';
1717

1818
/**
@@ -22,25 +22,18 @@ import {throwUnreachable} from './util';
2222
*/
2323
export async function measure(
2424
driver: webdriver.WebDriver,
25-
{measurement, measurementExpression}: BenchmarkSpec,
25+
measurement: Measurement,
2626
server: Server|undefined): Promise<number|undefined> {
27-
if (measurement === 'fcp') {
28-
return queryForPerformanceEntry(driver, {name: 'first-contentful-paint'});
29-
}
30-
if (measurement === 'callback') {
31-
if (server === undefined) {
32-
throw new Error('Internal error: no server for spec');
33-
}
34-
return (await server.nextResults()).millis;
35-
}
36-
if (measurement === 'global') {
37-
if (!measurementExpression) {
38-
throw new Error('Internal error: no measurement expression');
39-
}
40-
return queryForExpression(driver, measurementExpression);
41-
}
42-
if (measurement && 'performanceEntry' in measurement) {
43-
return queryForPerformanceEntry(driver, measurement.performanceEntry);
27+
switch (measurement.kind) {
28+
case 'callback':
29+
if (server === undefined) {
30+
throw new Error('Internal error: no server for spec');
31+
}
32+
return (await server.nextResults()).millis;
33+
case 'expression':
34+
return queryForExpression(driver, measurement.expression);
35+
case 'performance':
36+
return queryForPerformanceEntry(driver, measurement);
4437
}
4538
throwUnreachable(
4639
measurement,
@@ -71,8 +64,8 @@ interface PerformanceEntry {
7164
*/
7265
async function queryForPerformanceEntry(
7366
driver: webdriver.WebDriver,
74-
criteria: PerformanceEntryCriteria): Promise<number|undefined> {
75-
const escaped = escapeStringLiteral(criteria.name);
67+
measurement: PerformanceEntryMeasurement): Promise<number|undefined> {
68+
const escaped = escapeStringLiteral(measurement.entryName);
7669
const script = `return window.performance.getEntriesByName(\`${escaped}\`);`;
7770
const entries = await driver.executeScript(script) as PerformanceEntry[];
7871
if (entries.length === 0) {

src/runner.ts

+4-9
Original file line numberDiff line numberDiff line change
@@ -203,8 +203,9 @@ export class Runner {
203203
await driver.get(url);
204204
for (let waited = 0; millis === undefined && waited <= 10000;
205205
waited += 50) {
206+
// TODO(aomarks) You don't have to wait in callback mode!
206207
await wait(50);
207-
millis = await measure(driver, spec, server);
208+
millis = await measure(driver, spec.measurement, server);
208209
}
209210

210211
// Close the active tab (but not the whole browser, since the
@@ -224,21 +225,15 @@ export class Runner {
224225

225226
console.log(
226227
`\n\nFailed ${attempt}/${maxAttempts} times ` +
227-
`to get a ${spec.measurement} measurement ` +
228-
(spec.measurement === 'global' ?
229-
`(from '${spec.measurementExpression}') ` :
230-
'') +
228+
`to get a measurement ` +
231229
`in ${spec.browser.name} from ${url}. Retrying.`);
232230
}
233231

234232
if (millis === undefined) {
235233
console.log();
236234
throw new Error(
237235
`\n\nFailed ${maxAttempts}/${maxAttempts} times ` +
238-
`to get a ${spec.measurement} measurement ` +
239-
(spec.measurement === 'global' ?
240-
`(from '${spec.measurementExpression}') ` :
241-
'') +
236+
`to get a measurement ` +
242237
`in ${spec.browser.name} from ${url}. Retrying.`);
243238
}
244239

0 commit comments

Comments
 (0)