finished refactoring

This commit is contained in:
Tobias Bosch 2016-09-15 07:49:05 -07:00 committed by Alex Eagle
parent 37b8691c8c
commit c4114c2f66
4 changed files with 228 additions and 375 deletions

View File

@ -9,8 +9,6 @@
import {Inject, Injectable, OpaqueToken} from '@angular/core'; import {Inject, Injectable, OpaqueToken} from '@angular/core';
import {Options} from '../common_options'; import {Options} from '../common_options';
import {ListWrapper, StringMapWrapper} from '../facade/collection';
import {Math, NumberWrapper, StringWrapper, isBlank, isPresent} from '../facade/lang';
import {Metric} from '../metric'; import {Metric} from '../metric';
import {PerfLogEvent, PerfLogFeatures, WebDriverExtension} from '../web_driver_extension'; import {PerfLogEvent, PerfLogFeatures, WebDriverExtension} from '../web_driver_extension';
@ -95,8 +93,9 @@ export class PerflogMetric extends Metric {
res['frameTime.smooth'] = 'percentage of frames that hit 60fps'; res['frameTime.smooth'] = 'percentage of frames that hit 60fps';
} }
} }
StringMapWrapper.forEach( for (let name in this._microMetrics) {
this._microMetrics, (desc, name) => { StringMapWrapper.set(res, name, desc); }); res[name] = this._microMetrics[name];
}
return res; return res;
} }
@ -126,8 +125,8 @@ export class PerflogMetric extends Metric {
.then((_) => this._endMeasure(restartMeasure)) .then((_) => this._endMeasure(restartMeasure))
.then((forceGcMeasureValues) => { .then((forceGcMeasureValues) => {
this._captureFrames = originalFrameCaptureValue; this._captureFrames = originalFrameCaptureValue;
StringMapWrapper.set(measureValues, 'forcedGcTime', forceGcMeasureValues['gcTime']); measureValues['forcedGcTime'] = forceGcMeasureValues['gcTime'];
StringMapWrapper.set(measureValues, 'forcedGcAmount', forceGcMeasureValues['gcAmount']); measureValues['forcedGcAmount'] = forceGcMeasureValues['gcAmount'];
return measureValues; return measureValues;
}); });
}); });
@ -152,7 +151,7 @@ export class PerflogMetric extends Metric {
return this._driverExtension.readPerfLog().then((events) => { return this._driverExtension.readPerfLog().then((events) => {
this._addEvents(events); this._addEvents(events);
var result = this._aggregateEvents(this._remainingEvents, markName); var result = this._aggregateEvents(this._remainingEvents, markName);
if (isPresent(result)) { if (result) {
this._remainingEvents = events; this._remainingEvents = events;
return result; return result;
} }
@ -166,14 +165,14 @@ export class PerflogMetric extends Metric {
private _addEvents(events: PerfLogEvent[]) { private _addEvents(events: PerfLogEvent[]) {
var needSort = false; var needSort = false;
events.forEach(event => { events.forEach(event => {
if (StringWrapper.equals(event['ph'], 'X')) { if (event['ph'] === 'X') {
needSort = true; needSort = true;
var startEvent: PerfLogEvent = {}; var startEvent: PerfLogEvent = {};
var endEvent: PerfLogEvent = {}; var endEvent: PerfLogEvent = {};
StringMapWrapper.forEach(event, (value, prop) => { for (let prop in event) {
(<any>startEvent)[prop] = value; startEvent[prop] = event[prop];
(<any>endEvent)[prop] = value; endEvent[prop] = event[prop];
}); }
startEvent['ph'] = 'B'; startEvent['ph'] = 'B';
endEvent['ph'] = 'E'; endEvent['ph'] = 'E';
endEvent['ts'] = startEvent['ts'] + startEvent['dur']; endEvent['ts'] = startEvent['ts'] + startEvent['dur'];
@ -185,7 +184,7 @@ export class PerflogMetric extends Metric {
}); });
if (needSort) { if (needSort) {
// Need to sort because of the ph==='X' events // Need to sort because of the ph==='X' events
ListWrapper.sort(this._remainingEvents, (a, b) => { this._remainingEvents.sort((a, b) => {
var diff = a['ts'] - b['ts']; var diff = a['ts'] - b['ts'];
return diff > 0 ? 1 : diff < 0 ? -1 : 0; return diff > 0 ? 1 : diff < 0 ? -1 : 0;
}); });
@ -208,7 +207,9 @@ export class PerflogMetric extends Metric {
result['frameTime.worst'] = 0; result['frameTime.worst'] = 0;
result['frameTime.smooth'] = 0; result['frameTime.smooth'] = 0;
} }
StringMapWrapper.forEach(this._microMetrics, (desc, name) => { result[name] = 0; }); for (let name in this._microMetrics) {
result[name] = 0;
}
if (this._receivedData) { if (this._receivedData) {
result['receivedData'] = 0; result['receivedData'] = 0;
} }
@ -233,23 +234,23 @@ export class PerflogMetric extends Metric {
var name = event['name']; var name = event['name'];
var microIterations = 1; var microIterations = 1;
var microIterationsMatch = name.match(_MICRO_ITERATIONS_REGEX); var microIterationsMatch = name.match(_MICRO_ITERATIONS_REGEX);
if (isPresent(microIterationsMatch)) { if (microIterationsMatch) {
name = microIterationsMatch[1]; name = microIterationsMatch[1];
microIterations = NumberWrapper.parseInt(microIterationsMatch[2], 10); microIterations = parseInt(microIterationsMatch[2], 10);
} }
if (StringWrapper.equals(ph, 'b') && StringWrapper.equals(name, markName)) { if (ph === 'b' && name === markName) {
markStartEvent = event; markStartEvent = event;
} else if (StringWrapper.equals(ph, 'e') && StringWrapper.equals(name, markName)) { } else if (ph === 'e' && name === markName) {
markEndEvent = event; markEndEvent = event;
} }
let isInstant = StringWrapper.equals(ph, 'I') || StringWrapper.equals(ph, 'i'); let isInstant = ph === 'I' || ph === 'i';
if (this._requestCount && StringWrapper.equals(name, 'sendRequest')) { if (this._requestCount && name === 'sendRequest') {
result['requestCount'] += 1; result['requestCount'] += 1;
} else if (this._receivedData && StringWrapper.equals(name, 'receivedData') && isInstant) { } else if (this._receivedData && name === 'receivedData' && isInstant) {
result['receivedData'] += event['args']['encodedDataLength']; result['receivedData'] += event['args']['encodedDataLength'];
} else if (StringWrapper.equals(name, 'navigationStart')) { } else if (name === 'navigationStart') {
// We count data + requests since the last navigationStart // We count data + requests since the last navigationStart
// (there might be chrome extensions loaded by selenium before our page, so there // (there might be chrome extensions loaded by selenium before our page, so there
// will likely be more than one navigationStart). // will likely be more than one navigationStart).
@ -260,10 +261,9 @@ export class PerflogMetric extends Metric {
result['requestCount'] = 0; result['requestCount'] = 0;
} }
} }
if (isPresent(markStartEvent) && isBlank(markEndEvent) && if (markStartEvent && !markEndEvent && event['pid'] === markStartEvent['pid']) {
event['pid'] === markStartEvent['pid']) { if (ph === 'b' && name === _MARK_NAME_FRAME_CAPUTRE) {
if (StringWrapper.equals(ph, 'b') && StringWrapper.equals(name, _MARK_NAME_FRAME_CAPUTRE)) { if (frameCaptureStartEvent) {
if (isPresent(frameCaptureStartEvent)) {
throw new Error('can capture frames only once per benchmark run'); throw new Error('can capture frames only once per benchmark run');
} }
if (!this._captureFrames) { if (!this._captureFrames) {
@ -271,17 +271,15 @@ export class PerflogMetric extends Metric {
'found start event for frame capture, but frame capture was not requested in benchpress'); 'found start event for frame capture, but frame capture was not requested in benchpress');
} }
frameCaptureStartEvent = event; frameCaptureStartEvent = event;
} else if ( } else if (ph === 'e' && name === _MARK_NAME_FRAME_CAPUTRE) {
StringWrapper.equals(ph, 'e') && StringWrapper.equals(name, _MARK_NAME_FRAME_CAPUTRE)) { if (!frameCaptureStartEvent) {
if (isBlank(frameCaptureStartEvent)) {
throw new Error('missing start event for frame capture'); throw new Error('missing start event for frame capture');
} }
frameCaptureEndEvent = event; frameCaptureEndEvent = event;
} }
if (isInstant) { if (isInstant) {
if (isPresent(frameCaptureStartEvent) && isBlank(frameCaptureEndEvent) && if (frameCaptureStartEvent && !frameCaptureEndEvent && name === 'frame') {
StringWrapper.equals(name, 'frame')) {
frameTimestamps.push(event['ts']); frameTimestamps.push(event['ts']);
if (frameTimestamps.length >= 2) { if (frameTimestamps.length >= 2) {
frameTimes.push( frameTimes.push(
@ -291,57 +289,54 @@ export class PerflogMetric extends Metric {
} }
} }
if (StringWrapper.equals(ph, 'B') || StringWrapper.equals(ph, 'b')) { if (ph === 'B' || ph === 'b') {
if (isBlank(intervalStarts[name])) { if (!intervalStarts[name]) {
intervalStartCount[name] = 1; intervalStartCount[name] = 1;
intervalStarts[name] = event; intervalStarts[name] = event;
} else { } else {
intervalStartCount[name]++; intervalStartCount[name]++;
} }
} else if ( } else if ((ph === 'E' || ph === 'e') && intervalStarts[name]) {
(StringWrapper.equals(ph, 'E') || StringWrapper.equals(ph, 'e')) &&
isPresent(intervalStarts[name])) {
intervalStartCount[name]--; intervalStartCount[name]--;
if (intervalStartCount[name] === 0) { if (intervalStartCount[name] === 0) {
var startEvent = intervalStarts[name]; var startEvent = intervalStarts[name];
var duration = (event['ts'] - startEvent['ts']); var duration = (event['ts'] - startEvent['ts']);
intervalStarts[name] = null; intervalStarts[name] = null;
if (StringWrapper.equals(name, 'gc')) { if (name === 'gc') {
result['gcTime'] += duration; result['gcTime'] += duration;
var amount = var amount =
(startEvent['args']['usedHeapSize'] - event['args']['usedHeapSize']) / 1000; (startEvent['args']['usedHeapSize'] - event['args']['usedHeapSize']) / 1000;
result['gcAmount'] += amount; result['gcAmount'] += amount;
var majorGc = event['args']['majorGc']; var majorGc = event['args']['majorGc'];
if (isPresent(majorGc) && majorGc) { if (majorGc && majorGc) {
result['majorGcTime'] += duration; result['majorGcTime'] += duration;
} }
if (isPresent(intervalStarts['script'])) { if (intervalStarts['script']) {
gcTimeInScript += duration; gcTimeInScript += duration;
} }
} else if (StringWrapper.equals(name, 'render')) { } else if (name === 'render') {
result['renderTime'] += duration; result['renderTime'] += duration;
if (isPresent(intervalStarts['script'])) { if (intervalStarts['script']) {
renderTimeInScript += duration; renderTimeInScript += duration;
} }
} else if (StringWrapper.equals(name, 'script')) { } else if (name === 'script') {
result['scriptTime'] += duration; result['scriptTime'] += duration;
} else if (isPresent(this._microMetrics[name])) { } else if (this._microMetrics[name]) {
(<any>result)[name] += duration / microIterations; (<any>result)[name] += duration / microIterations;
} }
} }
} }
} }
}); });
if (!isPresent(markStartEvent) || !isPresent(markEndEvent)) { if (!markStartEvent || !markEndEvent) {
// not all events have been received, no further processing for now // not all events have been received, no further processing for now
return null; return null;
} }
if (isPresent(markEndEvent) && isPresent(frameCaptureStartEvent) && if (markEndEvent && frameCaptureStartEvent && !frameCaptureEndEvent) {
isBlank(frameCaptureEndEvent)) {
throw new Error('missing end event for frame capture'); throw new Error('missing end event for frame capture');
} }
if (this._captureFrames && isBlank(frameCaptureStartEvent)) { if (this._captureFrames && !frameCaptureStartEvent) {
throw new Error('frame capture requested in benchpress, but no start event was found'); throw new Error('frame capture requested in benchpress, but no start event was found');
} }
if (frameTimes.length > 0) { if (frameTimes.length > 0) {

View File

@ -12,8 +12,10 @@ import {Options} from './common_options';
import {isBlank, isPresent} from './facade/lang'; import {isBlank, isPresent} from './facade/lang';
export type PerfLogEvent = { export type PerfLogEvent = {
[key: string]: any
} & {
cat?: string, cat?: string,
ph?: 'X' | 'B' | 'E' | 'b' | 'e', ph?: 'X' | 'B' | 'E' | 'b' | 'e' | 'i' | 'I',
ts?: number, ts?: number,
dur?: number, dur?: number,
name?: string, name?: string,

View File

@ -14,7 +14,7 @@ import {PerfLogEvent, PerfLogFeatures, WebDriverExtension} from '../web_driver_e
/** /**
* Set the following 'traceCategories' to collect metrics in Chrome: * Set the following 'traceCategories' to collect metrics in Chrome:
* 'v8,blink.console,disabled-by-default-devtools.timeline,devtools.timeline' * 'v8,blink.console,disabled-by-default-devtools.timeline,devtools.timeline,blink.user_timing'
* *
* In order to collect the frame rate related metrics, add 'benchmark' * In order to collect the frame rate related metrics, add 'benchmark'
* to the list above. * to the list above.
@ -28,7 +28,6 @@ export class ChromeDriverExtension extends WebDriverExtension {
constructor(private _driver: WebDriverAdapter, @Inject(Options.USER_AGENT) userAgent: string) { constructor(private _driver: WebDriverAdapter, @Inject(Options.USER_AGENT) userAgent: string) {
super(); super();
this._majorChromeVersion = this._parseChromeVersion(userAgent); this._majorChromeVersion = this._parseChromeVersion(userAgent);
} }
private _parseChromeVersion(userAgent: string): number { private _parseChromeVersion(userAgent: string): number {
@ -74,7 +73,7 @@ export class ChromeDriverExtension extends WebDriverExtension {
if (message['method'] === 'Tracing.dataCollected') { if (message['method'] === 'Tracing.dataCollected') {
events.push(message['params']); events.push(message['params']);
} }
if (message['method'] === 'Tracing.bufferUsage') { if (message['method'] === 'Tracing.bufferUsage') {
throw new Error('The DevTools trace buffer filled during the test!'); throw new Error('The DevTools trace buffer filled during the test!');
} }
}); });
@ -87,64 +86,59 @@ export class ChromeDriverExtension extends WebDriverExtension {
if (!normalizedEvents) { if (!normalizedEvents) {
normalizedEvents = []; normalizedEvents = [];
} }
var majorGCPids = {};
chromeEvents.forEach((event) => { chromeEvents.forEach((event) => {
var categories = this._parseCategories(event['cat']); const categories = this._parseCategories(event['cat']);
var name = event['name']; const normalizedEvent = this._convertEvent(event, categories);
if (this._isEvent(categories, name, ['blink.console'])) { if (normalizedEvent != null) normalizedEvents.push(normalizedEvent);
normalizedEvents.push(normalizeEvent(event, {'name': name}));
} else if (this._isEvent(
categories, name, ['benchmark'],
'BenchmarkInstrumentation::ImplThreadRenderingStats')) {
// TODO(goderbauer): Instead of BenchmarkInstrumentation::ImplThreadRenderingStats the
// following events should be used (if available) for more accurate measurments:
// 1st choice: vsync_before - ground truth on Android
// 2nd choice: BenchmarkInstrumentation::DisplayRenderingStats - available on systems with
// new surfaces framework (not broadly enabled yet)
// 3rd choice: BenchmarkInstrumentation::ImplThreadRenderingStats - fallback event that is
// always available if something is rendered
var frameCount = event['args']['data']['frame_count'];
if (frameCount > 1) {
throw new Error('multi-frame render stats not supported');
}
if (frameCount == 1) {
normalizedEvents.push(normalizeEvent(event, {'name': 'frame'}));
}
} else if (
this._isEvent(categories, name, ['disabled-by-default-devtools.timeline'], 'Rasterize') ||
this._isEvent(
categories, name, ['disabled-by-default-devtools.timeline'], 'CompositeLayers')) {
normalizedEvents.push(normalizeEvent(event, {'name': 'render'}));
} else {
var normalizedEvent = this._processAsPostChrome44Event(event, categories);
if (normalizedEvent != null) normalizedEvents.push(normalizedEvent);
}
}); });
return normalizedEvents; return normalizedEvents;
} }
private _processAsPostChrome44Event(event: {[key: string]: any}, categories: string[]) { private _convertEvent(event: {[key: string]: any}, categories: string[]) {
var name = event['name']; var name = event['name'];
var args = event['args']; var args = event['args'];
if (this._isEvent(categories, name, ['devtools.timeline', 'v8'], 'MajorGC')) { if (this._isEvent(categories, name, ['blink.console'])) {
return normalizeEvent(event, {'name': name});
} else if (this._isEvent(
categories, name, ['benchmark'],
'BenchmarkInstrumentation::ImplThreadRenderingStats')) {
// TODO(goderbauer): Instead of BenchmarkInstrumentation::ImplThreadRenderingStats the
// following events should be used (if available) for more accurate measurments:
// 1st choice: vsync_before - ground truth on Android
// 2nd choice: BenchmarkInstrumentation::DisplayRenderingStats - available on systems with
// new surfaces framework (not broadly enabled yet)
// 3rd choice: BenchmarkInstrumentation::ImplThreadRenderingStats - fallback event that is
// always available if something is rendered
var frameCount = event['args']['data']['frame_count'];
if (frameCount > 1) {
throw new Error('multi-frame render stats not supported');
}
if (frameCount == 1) {
return normalizeEvent(event, {'name': 'frame'});
}
} else if (
this._isEvent(categories, name, ['disabled-by-default-devtools.timeline'], 'Rasterize') ||
this._isEvent(
categories, name, ['disabled-by-default-devtools.timeline'], 'CompositeLayers')) {
return normalizeEvent(event, {'name': 'render'});
} else if (this._isEvent(categories, name, ['devtools.timeline', 'v8'], 'MajorGC')) {
var normArgs = { var normArgs = {
'majorGc': true, 'majorGc': true,
'usedHeapSize': args['usedHeapSizeAfter'] !== undefined ? args['usedHeapSizeAfter'] : 'usedHeapSize': args['usedHeapSizeAfter'] !== undefined ? args['usedHeapSizeAfter'] :
args['usedHeapSizeBefore'] args['usedHeapSizeBefore']
}; };
return normalizeEvent(event, {'name': 'gc', 'args': normArgs}); return normalizeEvent(event, {'name': 'gc', 'args': normArgs});
} else if (this._isEvent(categories, name, ['devtools.timeline', 'v8'], 'MinorGC')) { } else if (this._isEvent(categories, name, ['devtools.timeline', 'v8'], 'MinorGC')) {
var normArgs = { var normArgs = {
'majorGc': false, 'majorGc': false,
'usedHeapSize': args['usedHeapSizeAfter'] !== undefined ? args['usedHeapSizeAfter'] : 'usedHeapSize': args['usedHeapSizeAfter'] !== undefined ? args['usedHeapSizeAfter'] :
args['usedHeapSizeBefore'] args['usedHeapSizeBefore']
}; };
return normalizeEvent(event, {'name': 'gc', 'args': normArgs}); return normalizeEvent(event, {'name': 'gc', 'args': normArgs});
} else if ( } else if (
this._isEvent(categories, name, ['devtools.timeline'], 'FunctionCall') && this._isEvent(categories, name, ['devtools.timeline'], 'FunctionCall') &&
(!args || !args['data'] || (!args || !args['data'] ||
(args['data']['scriptName'] !== 'InjectedScript' && (args['data']['scriptName'] !== 'InjectedScript' && args['data']['scriptName'] !== ''))) {
args['data']['scriptName'] !== ''))) {
return normalizeEvent(event, {'name': 'script'}); return normalizeEvent(event, {'name': 'script'});
} else if (this._isEvent( } else if (this._isEvent(
categories, name, ['devtools.timeline', 'blink'], 'UpdateLayoutTree')) { categories, name, ['devtools.timeline', 'blink'], 'UpdateLayoutTree')) {
@ -174,8 +168,7 @@ export class ChromeDriverExtension extends WebDriverExtension {
expectedName: string = null): boolean { expectedName: string = null): boolean {
var hasCategories = expectedCategories.reduce( var hasCategories = expectedCategories.reduce(
(value, cat) => { return value && eventCategories.indexOf(cat) !== -1; }, true); (value, cat) => { return value && eventCategories.indexOf(cat) !== -1; }, true);
return !expectedName ? hasCategories : return !expectedName ? hasCategories : hasCategories && eventName === expectedName;
hasCategories && eventName === expectedName;
} }
perfLogFeatures(): PerfLogFeatures { perfLogFeatures(): PerfLogFeatures {
@ -183,17 +176,16 @@ export class ChromeDriverExtension extends WebDriverExtension {
} }
supports(capabilities: {[key: string]: any}): boolean { supports(capabilities: {[key: string]: any}): boolean {
return this._majorChromeVersion >=44 && return this._majorChromeVersion >= 44 && capabilities['browserName'].toLowerCase() === 'chrome';
capabilities['browserName'].toLowerCase() === 'chrome';
} }
} }
function normalizeEvent( function normalizeEvent(
chromeEvent: {[key: string]: any}, data: {[key: string]: any}): PerfLogEvent { chromeEvent: {[key: string]: any}, data: {[key: string]: any}): PerfLogEvent {
var ph = chromeEvent['ph']; var ph = chromeEvent['ph'];
if (ph === 'S') { if (ph === 'S') {
ph = 'b'; ph = 'b';
} else if (ph === 'F') { } else if (ph === 'F') {
ph = 'e'; ph = 'e';
} }
var result: {[key: string]: any} = var result: {[key: string]: any} =

View File

@ -14,8 +14,6 @@ import {TraceEventFactory} from '../trace_event_factory';
export function main() { export function main() {
describe('chrome driver extension', () => { describe('chrome driver extension', () => {
var CHROME44_USER_AGENT =
'"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.0 Safari/537.36"';
var CHROME45_USER_AGENT = var CHROME45_USER_AGENT =
'"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2499.0 Safari/537.36"'; '"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2499.0 Safari/537.36"';
@ -41,7 +39,7 @@ export function main() {
perfRecords = []; perfRecords = [];
} }
if (isBlank(userAgent)) { if (isBlank(userAgent)) {
userAgent = CHROME44_USER_AGENT; userAgent = CHROME45_USER_AGENT;
} }
log = []; log = [];
extension = ReflectiveInjector extension = ReflectiveInjector
@ -89,228 +87,83 @@ export function main() {
}); });
})); }));
describe('readPerfLog Chrome44', () => { it('should normalize times to ms and forward ph and pid event properties',
it('should normalize times to ms and forward ph and pid event properties', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { createExtension([chromeTimelineV8Events.complete('FunctionCall', 1100, 5500, null)])
createExtension([chromeTimelineEvents.complete('FunctionCall', 1100, 5500, null)]) .readPerfLog()
.readPerfLog() .then((events) => {
.then((events) => { expect(events).toEqual([
expect(events).toEqual([ normEvents.complete('script', 1.1, 5.5, null),
normEvents.complete('script', 1.1, 5.5, null), ]);
]); async.done();
async.done(); });
}); }));
}));
it('should normalize "tdur" to "dur"', it('should normalize "tdur" to "dur"',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
var event: any = chromeTimelineEvents.create('X', 'FunctionCall', 1100, null); var event: any = chromeTimelineV8Events.create('X', 'FunctionCall', 1100, null);
event['tdur'] = 5500; event['tdur'] = 5500;
createExtension([event]).readPerfLog().then((events) => { createExtension([event]).readPerfLog().then((events) => {
expect(events).toEqual([ expect(events).toEqual([
normEvents.complete('script', 1.1, 5.5, null), normEvents.complete('script', 1.1, 5.5, null),
]); ]);
async.done(); async.done();
}); });
})); }));
it('should report FunctionCall events as "script"', it('should report FunctionCall events as "script"',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
createExtension([chromeTimelineEvents.start('FunctionCall', 0)]) createExtension([chromeTimelineV8Events.start('FunctionCall', 0)])
.readPerfLog() .readPerfLog()
.then((events) => { .then((events) => {
expect(events).toEqual([ expect(events).toEqual([
normEvents.start('script', 0), normEvents.start('script', 0),
]); ]);
async.done(); async.done();
}); });
})); }));
it('should report gc', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { it('should report minor gc', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
createExtension([ createExtension([
chromeTimelineEvents.start('GCEvent', 1000, {'usedHeapSizeBefore': 1000}), chromeTimelineV8Events.start('MinorGC', 1000, {'usedHeapSizeBefore': 1000}),
chromeTimelineEvents.end('GCEvent', 2000, {'usedHeapSizeAfter': 0}), chromeTimelineV8Events.end('MinorGC', 2000, {'usedHeapSizeAfter': 0}),
]) ])
.readPerfLog() .readPerfLog()
.then((events) => { .then((events) => {
expect(events).toEqual([ expect(events.length).toEqual(2);
normEvents.start('gc', 1.0, {'usedHeapSize': 1000}), expect(events[0]).toEqual(
normEvents.end('gc', 2.0, {'usedHeapSize': 0, 'majorGc': false}), normEvents.start('gc', 1.0, {'usedHeapSize': 1000, 'majorGc': false}));
]); expect(events[1]).toEqual(
async.done(); normEvents.end('gc', 2.0, {'usedHeapSize': 0, 'majorGc': false}));
}); async.done();
})); });
}));
it('should ignore major gc from different processes', it('should report major gc', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { createExtension(
createExtension([ [
chromeTimelineEvents.start('GCEvent', 1000, {'usedHeapSizeBefore': 1000}), chromeTimelineV8Events.start('MajorGC', 1000, {'usedHeapSizeBefore': 1000}),
v8EventsOtherProcess.start('majorGC', 1100, null), chromeTimelineV8Events.end('MajorGC', 2000, {'usedHeapSizeAfter': 0}),
v8EventsOtherProcess.end('majorGC', 1200, null), ], )
chromeTimelineEvents.end('GCEvent', 2000, {'usedHeapSizeAfter': 0}), .readPerfLog()
]) .then((events) => {
.readPerfLog() expect(events.length).toEqual(2);
.then((events) => { expect(events[0]).toEqual(
expect(events).toEqual([ normEvents.start('gc', 1.0, {'usedHeapSize': 1000, 'majorGc': true}));
normEvents.start('gc', 1.0, {'usedHeapSize': 1000}), expect(events[1]).toEqual(
normEvents.end('gc', 2.0, {'usedHeapSize': 0, 'majorGc': false}), normEvents.end('gc', 2.0, {'usedHeapSize': 0, 'majorGc': true}));
]); async.done();
async.done(); });
}); }));
}));
it('should report major gc', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { ['Layout', 'UpdateLayerTree', 'Paint'].forEach((recordType) => {
createExtension([ it(`should report ${recordType} as "render"`,
chromeTimelineEvents.start('GCEvent', 1000, {'usedHeapSizeBefore': 1000}),
v8Events.start('majorGC', 1100, null),
v8Events.end('majorGC', 1200, null),
chromeTimelineEvents.end('GCEvent', 2000, {'usedHeapSizeAfter': 0}),
])
.readPerfLog()
.then((events) => {
expect(events).toEqual([
normEvents.start('gc', 1.0, {'usedHeapSize': 1000}),
normEvents.end('gc', 2.0, {'usedHeapSize': 0, 'majorGc': true}),
]);
async.done();
});
}));
['RecalculateStyles', 'Layout', 'UpdateLayerTree', 'Paint'].forEach((recordType) => {
it(`should report ${recordType} as "render"`,
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
createExtension([
chromeTimelineEvents.start(recordType, 1234),
chromeTimelineEvents.end(recordType, 2345)
])
.readPerfLog()
.then((events) => {
expect(events).toEqual([
normEvents.start('render', 1.234),
normEvents.end('render', 2.345),
]);
async.done();
});
}));
});
it('should ignore FunctionCalls from webdriver',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
createExtension([chromeTimelineEvents.start(
'FunctionCall', 0, {'data': {'scriptName': 'InjectedScript'}})])
.readPerfLog()
.then((events) => {
expect(events).toEqual([]);
async.done();
});
}));
});
describe('readPerfLog Chrome45', () => {
it('should normalize times to ms and forward ph and pid event properties',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
createExtension(
[chromeTimelineV8Events.complete('FunctionCall', 1100, 5500, null)],
CHROME45_USER_AGENT)
.readPerfLog()
.then((events) => {
expect(events).toEqual([
normEvents.complete('script', 1.1, 5.5, null),
]);
async.done();
});
}));
it('should normalize "tdur" to "dur"',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
var event: any = chromeTimelineV8Events.create('X', 'FunctionCall', 1100, null);
event['tdur'] = 5500;
createExtension([event], CHROME45_USER_AGENT).readPerfLog().then((events) => {
expect(events).toEqual([
normEvents.complete('script', 1.1, 5.5, null),
]);
async.done();
});
}));
it('should report FunctionCall events as "script"',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
createExtension([chromeTimelineV8Events.start('FunctionCall', 0)], CHROME45_USER_AGENT)
.readPerfLog()
.then((events) => {
expect(events).toEqual([
normEvents.start('script', 0),
]);
async.done();
});
}));
it('should report minor gc', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
createExtension(
[
chromeTimelineV8Events.start('MinorGC', 1000, {'usedHeapSizeBefore': 1000}),
chromeTimelineV8Events.end('MinorGC', 2000, {'usedHeapSizeAfter': 0}),
],
CHROME45_USER_AGENT)
.readPerfLog()
.then((events) => {
expect(events.length).toEqual(2);
expect(events[0]).toEqual(
normEvents.start('gc', 1.0, {'usedHeapSize': 1000, 'majorGc': false}));
expect(events[1]).toEqual(
normEvents.end('gc', 2.0, {'usedHeapSize': 0, 'majorGc': false}));
async.done();
});
}));
it('should report major gc', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
createExtension(
[
chromeTimelineV8Events.start('MajorGC', 1000, {'usedHeapSizeBefore': 1000}),
chromeTimelineV8Events.end('MajorGC', 2000, {'usedHeapSizeAfter': 0}),
],
CHROME45_USER_AGENT)
.readPerfLog()
.then((events) => {
expect(events.length).toEqual(2);
expect(events[0]).toEqual(
normEvents.start('gc', 1.0, {'usedHeapSize': 1000, 'majorGc': true}));
expect(events[1]).toEqual(
normEvents.end('gc', 2.0, {'usedHeapSize': 0, 'majorGc': true}));
async.done();
});
}));
['Layout', 'UpdateLayerTree', 'Paint'].forEach((recordType) => {
it(`should report ${recordType} as "render"`,
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
createExtension(
[
chrome45TimelineEvents.start(recordType, 1234),
chrome45TimelineEvents.end(recordType, 2345)
],
CHROME45_USER_AGENT)
.readPerfLog()
.then((events) => {
expect(events).toEqual([
normEvents.start('render', 1.234),
normEvents.end('render', 2.345),
]);
async.done();
});
}));
});
it(`should report UpdateLayoutTree as "render"`,
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
createExtension( createExtension(
[ [
chromeBlinkTimelineEvents.start('UpdateLayoutTree', 1234), chrome45TimelineEvents.start(recordType, 1234),
chromeBlinkTimelineEvents.end('UpdateLayoutTree', 2345) chrome45TimelineEvents.end(recordType, 2345)
], ], )
CHROME45_USER_AGENT)
.readPerfLog() .readPerfLog()
.then((events) => { .then((events) => {
expect(events).toEqual([ expect(events).toEqual([
@ -320,70 +173,82 @@ export function main() {
async.done(); async.done();
}); });
})); }));
it('should ignore FunctionCalls from webdriver',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
createExtension([chromeTimelineV8Events.start(
'FunctionCall', 0, {'data': {'scriptName': 'InjectedScript'}})])
.readPerfLog()
.then((events) => {
expect(events).toEqual([]);
async.done();
});
}));
it('should ignore FunctionCalls with empty scriptName',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
createExtension(
[chromeTimelineV8Events.start('FunctionCall', 0, {'data': {'scriptName': ''}})])
.readPerfLog()
.then((events) => {
expect(events).toEqual([]);
async.done();
});
}));
it('should report navigationStart',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
createExtension(
[chromeBlinkUserTimingEvents.start('navigationStart', 1234)], CHROME45_USER_AGENT)
.readPerfLog()
.then((events) => {
expect(events).toEqual([normEvents.start('navigationStart', 1.234)]);
async.done();
});
}));
it('should report receivedData', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
createExtension(
[chrome45TimelineEvents.instant(
'ResourceReceivedData', 1234, {'data': {'encodedDataLength': 987}})],
CHROME45_USER_AGENT)
.readPerfLog()
.then((events) => {
expect(events).toEqual(
[normEvents.instant('receivedData', 1.234, {'encodedDataLength': 987})]);
async.done();
});
}));
it('should report sendRequest', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
createExtension(
[chrome45TimelineEvents.instant(
'ResourceSendRequest', 1234,
{'data': {'url': 'http://here', 'requestMethod': 'GET'}})],
CHROME45_USER_AGENT)
.readPerfLog()
.then((events) => {
expect(events).toEqual([normEvents.instant(
'sendRequest', 1.234, {'url': 'http://here', 'method': 'GET'})]);
async.done();
});
}));
}); });
it(`should report UpdateLayoutTree as "render"`,
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
createExtension(
[
chromeBlinkTimelineEvents.start('UpdateLayoutTree', 1234),
chromeBlinkTimelineEvents.end('UpdateLayoutTree', 2345)
], )
.readPerfLog()
.then((events) => {
expect(events).toEqual([
normEvents.start('render', 1.234),
normEvents.end('render', 2.345),
]);
async.done();
});
}));
it('should ignore FunctionCalls from webdriver',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
createExtension([chromeTimelineV8Events.start(
'FunctionCall', 0, {'data': {'scriptName': 'InjectedScript'}})])
.readPerfLog()
.then((events) => {
expect(events).toEqual([]);
async.done();
});
}));
it('should ignore FunctionCalls with empty scriptName',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
createExtension(
[chromeTimelineV8Events.start('FunctionCall', 0, {'data': {'scriptName': ''}})])
.readPerfLog()
.then((events) => {
expect(events).toEqual([]);
async.done();
});
}));
it('should report navigationStart',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
createExtension([chromeBlinkUserTimingEvents.start('navigationStart', 1234)])
.readPerfLog()
.then((events) => {
expect(events).toEqual([normEvents.start('navigationStart', 1.234)]);
async.done();
});
}));
it('should report receivedData', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
createExtension([chrome45TimelineEvents.instant(
'ResourceReceivedData', 1234, {'data': {'encodedDataLength': 987}})], )
.readPerfLog()
.then((events) => {
expect(events).toEqual(
[normEvents.instant('receivedData', 1.234, {'encodedDataLength': 987})]);
async.done();
});
}));
it('should report sendRequest', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
createExtension([chrome45TimelineEvents.instant(
'ResourceSendRequest', 1234,
{'data': {'url': 'http://here', 'requestMethod': 'GET'}})], )
.readPerfLog()
.then((events) => {
expect(events).toEqual([normEvents.instant(
'sendRequest', 1.234, {'url': 'http://here', 'method': 'GET'})]);
async.done();
});
}));
describe('readPerfLog (common)', () => { describe('readPerfLog (common)', () => {
it('should execute a dummy script before reading them', it('should execute a dummy script before reading them',
@ -404,8 +269,7 @@ export function main() {
[ [
chromeTimelineEvents.start(recordType, 1234), chromeTimelineEvents.start(recordType, 1234),
chromeTimelineEvents.end(recordType, 2345) chromeTimelineEvents.end(recordType, 2345)
], ], )
CHROME45_USER_AGENT)
.readPerfLog() .readPerfLog()
.then((events) => { .then((events) => {
expect(events).toEqual([ expect(events).toEqual([