feat(bench press): use chrome tracing protocol and initial iOS support
This commit is contained in:
@ -5,8 +5,11 @@ import { PromiseWrapper, Promise } from 'angular2/src/facade/async';
|
||||
|
||||
import { Metric, PerflogMetric, WebDriverExtension, bind, Injector } from 'benchpress/benchpress';
|
||||
|
||||
import { TraceEventFactory } from '../trace_event_factory';
|
||||
|
||||
export function main() {
|
||||
var commandLog;
|
||||
var eventFactory = new TraceEventFactory('timeline', 'pid0');
|
||||
|
||||
function createMetric(perfLogs) {
|
||||
commandLog = [];
|
||||
@ -44,10 +47,10 @@ export function main() {
|
||||
it('should mark and aggregate events in between the marks', (done) => {
|
||||
var events = [
|
||||
[
|
||||
markStartEvent('benchpress0'),
|
||||
startEvent('script', 4),
|
||||
endEvent('script', 6),
|
||||
markEndEvent('benchpress0')
|
||||
eventFactory.markStart('benchpress0', 0),
|
||||
eventFactory.start('script', 4),
|
||||
eventFactory.end('script', 6),
|
||||
eventFactory.markEnd('benchpress0', 10)
|
||||
]
|
||||
];
|
||||
var metric = createMetric(events);
|
||||
@ -68,11 +71,11 @@ export function main() {
|
||||
it('should restart timing', (done) => {
|
||||
var events = [
|
||||
[
|
||||
markStartEvent('benchpress0'),
|
||||
markEndEvent('benchpress0'),
|
||||
markStartEvent('benchpress1'),
|
||||
eventFactory.markStart('benchpress0', 0),
|
||||
eventFactory.markEnd('benchpress0', 1),
|
||||
eventFactory.markStart('benchpress1', 2),
|
||||
], [
|
||||
markEndEvent('benchpress1')
|
||||
eventFactory.markEnd('benchpress1', 3)
|
||||
]
|
||||
];
|
||||
var metric = createMetric(events);
|
||||
@ -94,9 +97,9 @@ export function main() {
|
||||
|
||||
it('should loop and aggregate until the end mark is present', (done) => {
|
||||
var events = [
|
||||
[ markStartEvent('benchpress0'), startEvent('script', 1) ],
|
||||
[ endEvent('script', 2) ],
|
||||
[ startEvent('script', 3), endEvent('script', 5), markEndEvent('benchpress0') ]
|
||||
[ eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 1) ],
|
||||
[ eventFactory.end('script', 2) ],
|
||||
[ eventFactory.start('script', 3), eventFactory.end('script', 5), eventFactory.markEnd('benchpress0', 10) ]
|
||||
];
|
||||
var metric = createMetric(events);
|
||||
metric.beginMeasure()
|
||||
@ -119,9 +122,9 @@ export function main() {
|
||||
|
||||
it('should store events after the end mark for the next call', (done) => {
|
||||
var events = [
|
||||
[ markStartEvent('benchpress0'), markEndEvent('benchpress0'), markStartEvent('benchpress1'),
|
||||
startEvent('script', 1), endEvent('script', 2) ],
|
||||
[ startEvent('script', 3), endEvent('script', 5), markEndEvent('benchpress1') ]
|
||||
[ eventFactory.markStart('benchpress0', 0), eventFactory.markEnd('benchpress0', 1), eventFactory.markStart('benchpress1', 1),
|
||||
eventFactory.start('script', 1), eventFactory.end('script', 2) ],
|
||||
[ eventFactory.start('script', 3), eventFactory.end('script', 5), eventFactory.markEnd('benchpress1', 6) ]
|
||||
];
|
||||
var metric = createMetric(events);
|
||||
metric.beginMeasure()
|
||||
@ -149,8 +152,8 @@ export function main() {
|
||||
describe('aggregation', () => {
|
||||
|
||||
function aggregate(events) {
|
||||
ListWrapper.insert(events, 0, markStartEvent('benchpress0'));
|
||||
ListWrapper.push(events, markEndEvent('benchpress0'));
|
||||
ListWrapper.insert(events, 0, eventFactory.markStart('benchpress0', 0));
|
||||
ListWrapper.push(events, eventFactory.markEnd('benchpress0', 10));
|
||||
var metric = createMetric([events]);
|
||||
return metric
|
||||
.beginMeasure().then( (_) => metric.endMeasure(false) );
|
||||
@ -159,8 +162,8 @@ export function main() {
|
||||
|
||||
it('should report a single interval', (done) => {
|
||||
aggregate([
|
||||
startEvent('script', 0),
|
||||
endEvent('script', 5)
|
||||
eventFactory.start('script', 0),
|
||||
eventFactory.end('script', 5)
|
||||
]).then((data) => {
|
||||
expect(data['script']).toBe(5);
|
||||
done();
|
||||
@ -169,10 +172,10 @@ export function main() {
|
||||
|
||||
it('should sum up multiple intervals', (done) => {
|
||||
aggregate([
|
||||
startEvent('script', 0),
|
||||
endEvent('script', 5),
|
||||
startEvent('script', 10),
|
||||
endEvent('script', 17)
|
||||
eventFactory.start('script', 0),
|
||||
eventFactory.end('script', 5),
|
||||
eventFactory.start('script', 10),
|
||||
eventFactory.end('script', 17)
|
||||
]).then((data) => {
|
||||
expect(data['script']).toBe(12);
|
||||
done();
|
||||
@ -181,7 +184,7 @@ export function main() {
|
||||
|
||||
it('should ignore not started intervals', (done) => {
|
||||
aggregate([
|
||||
endEvent('script', 10)
|
||||
eventFactory.end('script', 10)
|
||||
]).then((data) => {
|
||||
expect(data['script']).toBe(0);
|
||||
done();
|
||||
@ -190,18 +193,36 @@ export function main() {
|
||||
|
||||
it('should ignore not ended intervals', (done) => {
|
||||
aggregate([
|
||||
startEvent('script', 10)
|
||||
eventFactory.start('script', 10)
|
||||
]).then((data) => {
|
||||
expect(data['script']).toBe(0);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should ignore events from different processed as the start mark', (done) => {
|
||||
var otherProcessEventFactory = new TraceEventFactory('timeline', 'pid1');
|
||||
var metric = createMetric([[
|
||||
eventFactory.markStart('benchpress0', 0),
|
||||
eventFactory.start('script', 0, null),
|
||||
eventFactory.end('script', 5, null),
|
||||
otherProcessEventFactory.start('script', 10, null),
|
||||
otherProcessEventFactory.end('script', 17, null),
|
||||
eventFactory.markEnd('benchpress0', 20)
|
||||
]]);
|
||||
metric.beginMeasure()
|
||||
.then( (_) => metric.endMeasure(false) )
|
||||
.then((data) => {
|
||||
expect(data['script']).toBe(5);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
['script', 'gcTime', 'render'].forEach( (metricName) => {
|
||||
it(`should support ${metricName} metric`, (done) => {
|
||||
aggregate([
|
||||
startEvent(metricName, 0),
|
||||
endEvent(metricName, 5)
|
||||
eventFactory.start(metricName, 0),
|
||||
eventFactory.end(metricName, 5)
|
||||
]).then((data) => {
|
||||
expect(data[metricName]).toBe(5);
|
||||
done();
|
||||
@ -211,20 +232,20 @@ export function main() {
|
||||
|
||||
it('should support gcAmount metric', (done) => {
|
||||
aggregate([
|
||||
startEvent('gc', 0),
|
||||
endEvent('gc', 5, {'amount': 10})
|
||||
eventFactory.start('gc', 0, {'usedHeapSize': 2500}),
|
||||
eventFactory.end('gc', 5, {'usedHeapSize': 1000})
|
||||
]).then((data) => {
|
||||
expect(data['gcAmount']).toBe(10);
|
||||
expect(data['gcAmount']).toBe(1.5);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should subtract gcTime in script from script time', (done) => {
|
||||
aggregate([
|
||||
startEvent('script', 0),
|
||||
startEvent('gc', 1),
|
||||
endEvent('gc', 4, {'amount': 10}),
|
||||
endEvent('script', 5)
|
||||
eventFactory.start('script', 0),
|
||||
eventFactory.start('gc', 1, {'usedHeapSize': 1000}),
|
||||
eventFactory.end('gc', 4, {'usedHeapSize': 0}),
|
||||
eventFactory.end('script', 5)
|
||||
]).then((data) => {
|
||||
expect(data['script']).toBe(2);
|
||||
done();
|
||||
@ -233,28 +254,40 @@ export function main() {
|
||||
|
||||
describe('gcTimeInScript / gcAmountInScript', () => {
|
||||
|
||||
it('should use gc during script execution', (done) => {
|
||||
it('should detect gc during script execution with begin/end events', (done) => {
|
||||
aggregate([
|
||||
startEvent('script', 0),
|
||||
startEvent('gc', 1),
|
||||
endEvent('gc', 4, {'amount': 10}),
|
||||
endEvent('script', 5)
|
||||
eventFactory.start('script', 0),
|
||||
eventFactory.start('gc', 1, {'usedHeapSize': 10000}),
|
||||
eventFactory.end('gc', 4, {'usedHeapSize': 0}),
|
||||
eventFactory.end('script', 5)
|
||||
]).then((data) => {
|
||||
expect(data['gcTimeInScript']).toBe(3);
|
||||
expect(data['gcAmountInScript']).toBe(10);
|
||||
expect(data['gcAmountInScript']).toBe(10.0);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should detect gc during script execution with complete events', (done) => {
|
||||
aggregate([
|
||||
eventFactory.complete('script', 0, 5),
|
||||
eventFactory.start('gc', 1, {'usedHeapSize': 10000}),
|
||||
eventFactory.end('gc', 4, {'usedHeapSize': 0})
|
||||
]).then((data) => {
|
||||
expect(data['gcTimeInScript']).toBe(3);
|
||||
expect(data['gcAmountInScript']).toBe(10.0);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should ignore gc outside of script execution', (done) => {
|
||||
aggregate([
|
||||
startEvent('gc', 1),
|
||||
endEvent('gc', 4, {'amount': 10}),
|
||||
startEvent('script', 0),
|
||||
endEvent('script', 5)
|
||||
eventFactory.start('gc', 1, {'usedHeapSize': 10}),
|
||||
eventFactory.end('gc', 4, {'usedHeapSize': 0}),
|
||||
eventFactory.start('script', 0),
|
||||
eventFactory.end('script', 5)
|
||||
]).then((data) => {
|
||||
expect(data['gcTimeInScript']).toBe(0);
|
||||
expect(data['gcAmountInScript']).toBe(0);
|
||||
expect(data['gcTimeInScript']).toEqual(0.0);
|
||||
expect(data['gcAmountInScript']).toEqual(0.0);
|
||||
done();
|
||||
});
|
||||
});
|
||||
@ -266,37 +299,6 @@ export function main() {
|
||||
});
|
||||
}
|
||||
|
||||
function markStartEvent(type) {
|
||||
return {
|
||||
'name': type,
|
||||
'ph': 'b'
|
||||
}
|
||||
}
|
||||
|
||||
function markEndEvent(type) {
|
||||
return {
|
||||
'name': type,
|
||||
'ph': 'e'
|
||||
}
|
||||
}
|
||||
|
||||
function startEvent(type, time) {
|
||||
return {
|
||||
'name': type,
|
||||
'ts': time,
|
||||
'ph': 'B'
|
||||
}
|
||||
}
|
||||
|
||||
function endEvent(type, time, args = null) {
|
||||
return {
|
||||
'name': type,
|
||||
'ts': time,
|
||||
'ph': 'E',
|
||||
'args': args
|
||||
}
|
||||
}
|
||||
|
||||
class MockDriverExtension extends WebDriverExtension {
|
||||
_perfLogs:List;
|
||||
_commandLog:List;
|
||||
|
@ -148,7 +148,6 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should not gc if the flag is not set', (done) => {
|
||||
var workCount = 0;
|
||||
var log = [];
|
||||
createSampler({
|
||||
metric: createCountingMetric(),
|
||||
|
47
modules/benchpress/test/trace_event_factory.js
Normal file
47
modules/benchpress/test/trace_event_factory.js
Normal file
@ -0,0 +1,47 @@
|
||||
import { isPresent } from 'angular2/src/facade/lang';
|
||||
|
||||
export class TraceEventFactory {
|
||||
_cat:string;
|
||||
_pid;
|
||||
|
||||
constructor(cat, pid) {
|
||||
this._cat = cat;
|
||||
this._pid = pid;
|
||||
}
|
||||
|
||||
create(ph, name, time, args = null) {
|
||||
var res = {
|
||||
'name': name,
|
||||
'cat': this._cat,
|
||||
'ph': ph,
|
||||
'ts': time,
|
||||
'pid': this._pid
|
||||
};
|
||||
if (isPresent(args)) {
|
||||
res['args'] = args;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
markStart(name, time) {
|
||||
return this.create('b', name, time);
|
||||
}
|
||||
|
||||
markEnd(name, time) {
|
||||
return this.create('e', name, time);
|
||||
}
|
||||
|
||||
start(name, time, args = null) {
|
||||
return this.create('B', name, time, args);
|
||||
}
|
||||
|
||||
end(name, time, args = null) {
|
||||
return this.create('E', name, time, args);
|
||||
}
|
||||
|
||||
complete(name, time, duration, args = null) {
|
||||
var res = this.create('X', name, time, args);
|
||||
res['dur'] = duration;
|
||||
return res;
|
||||
}
|
||||
}
|
@ -2,18 +2,24 @@ import {describe, it, iit, xit, expect, beforeEach, afterEach} from 'angular2/te
|
||||
|
||||
import { ListWrapper } from 'angular2/src/facade/collection';
|
||||
import { PromiseWrapper } from 'angular2/src/facade/async';
|
||||
import { Json, perfRecords, isBlank } from 'angular2/src/facade/lang';
|
||||
import { Json, isBlank } from 'angular2/src/facade/lang';
|
||||
|
||||
import {
|
||||
WebDriverExtension, ChromeDriverExtension,
|
||||
WebDriverAdapter, Injector, bind
|
||||
} from 'benchpress/benchpress';
|
||||
|
||||
import { TraceEventFactory } from '../trace_event_factory';
|
||||
|
||||
export function main() {
|
||||
describe('chrome driver extension', () => {
|
||||
var log;
|
||||
var extension;
|
||||
|
||||
var blinkEvents = new TraceEventFactory('blink.console', 'pid0');
|
||||
var chromeTimelineEvents = new TraceEventFactory('disabled-by-default-devtools.timeline', 'pid0');
|
||||
var normEvents = new TraceEventFactory('timeline', 'pid0');
|
||||
|
||||
function createExtension(perfRecords = null) {
|
||||
if (isBlank(perfRecords)) {
|
||||
perfRecords = [];
|
||||
@ -33,23 +39,23 @@ export function main() {
|
||||
});
|
||||
});
|
||||
|
||||
it('should mark the timeline via console.timeStamp()', (done) => {
|
||||
it('should mark the timeline via console.time()', (done) => {
|
||||
createExtension().timeBegin('someName').then( (_) => {
|
||||
expect(log).toEqual([['executeScript', `console.timeStamp('begin_someName');`]]);
|
||||
expect(log).toEqual([['executeScript', `console.time('someName');`]]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should mark the timeline via console.timeEnd()', (done) => {
|
||||
createExtension().timeEnd('someName').then( (_) => {
|
||||
expect(log).toEqual([['executeScript', `console.timeStamp('end_someName');`]]);
|
||||
expect(log).toEqual([['executeScript', `console.timeEnd('someName');`]]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should mark the timeline via console.time() and console.timeEnd()', (done) => {
|
||||
createExtension().timeEnd('name1', 'name2').then( (_) => {
|
||||
expect(log).toEqual([['executeScript', `console.timeStamp('end_name1');console.timeStamp('begin_name2');`]]);
|
||||
expect(log).toEqual([['executeScript', `console.timeEnd('name1');console.time('name2');`]]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
@ -66,13 +72,23 @@ export function main() {
|
||||
});
|
||||
});
|
||||
|
||||
it('should report FunctionCall records as "script"', (done) => {
|
||||
it('should normalize times to ms and forward ph and pid event properties', (done) => {
|
||||
createExtension([
|
||||
durationRecord('FunctionCall', 1, 5)
|
||||
chromeTimelineEvents.complete('FunctionCall', 1100, 5500, null)
|
||||
]).readPerfLog().then( (events) => {
|
||||
expect(events).toEqual([
|
||||
startEvent('script', 1),
|
||||
endEvent('script', 5)
|
||||
normEvents.complete('script', 1.1, 5.5, null),
|
||||
]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should report FunctionCall events as "script"', (done) => {
|
||||
createExtension([
|
||||
chromeTimelineEvents.start('FunctionCall', 0)
|
||||
]).readPerfLog().then( (events) => {
|
||||
expect(events).toEqual([
|
||||
normEvents.start('script', 0),
|
||||
]);
|
||||
done();
|
||||
});
|
||||
@ -80,7 +96,7 @@ export function main() {
|
||||
|
||||
it('should ignore FunctionCalls from webdriver', (done) => {
|
||||
createExtension([
|
||||
internalScriptRecord(1, 5)
|
||||
chromeTimelineEvents.start('FunctionCall', 0, {'data': {'scriptName': 'InjectedScript'}})
|
||||
]).readPerfLog().then( (events) => {
|
||||
expect(events).toEqual([]);
|
||||
done();
|
||||
@ -89,10 +105,10 @@ export function main() {
|
||||
|
||||
it('should report begin timestamps', (done) => {
|
||||
createExtension([
|
||||
timeStampRecord('begin_someName')
|
||||
blinkEvents.create('S', 'someName', 1000)
|
||||
]).readPerfLog().then( (events) => {
|
||||
expect(events).toEqual([
|
||||
markStartEvent('someName')
|
||||
normEvents.markStart('someName', 1.0)
|
||||
]);
|
||||
done();
|
||||
});
|
||||
@ -100,10 +116,10 @@ export function main() {
|
||||
|
||||
it('should report end timestamps', (done) => {
|
||||
createExtension([
|
||||
timeStampRecord('end_someName')
|
||||
blinkEvents.create('F', 'someName', 1000)
|
||||
]).readPerfLog().then( (events) => {
|
||||
expect(events).toEqual([
|
||||
markEndEvent('someName')
|
||||
normEvents.markEnd('someName', 1.0)
|
||||
]);
|
||||
done();
|
||||
});
|
||||
@ -111,132 +127,44 @@ export function main() {
|
||||
|
||||
it('should report gc', (done) => {
|
||||
createExtension([
|
||||
gcRecord(1, 3, 21)
|
||||
chromeTimelineEvents.start('GCEvent', 1000, {'usedHeapSizeBefore': 1000}),
|
||||
chromeTimelineEvents.end('GCEvent', 2000, {'usedHeapSizeAfter': 0}),
|
||||
]).readPerfLog().then( (events) => {
|
||||
expect(events).toEqual([
|
||||
startEvent('gc', 1),
|
||||
endEvent('gc', 3, {'amount': 21}),
|
||||
normEvents.start('gc', 1.0, {'usedHeapSize': 1000}),
|
||||
normEvents.end('gc', 2.0, {'usedHeapSize': 0}),
|
||||
]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
['RecalculateStyles', 'Layout', 'UpdateLayerTree', 'Paint', 'Rasterize', 'CompositeLayers'].forEach( (recordType) => {
|
||||
it(`should report ${recordType}`, (done) => {
|
||||
it(`should report ${recordType} as "render"`, (done) => {
|
||||
createExtension([
|
||||
durationRecord(recordType, 0, 1)
|
||||
chromeTimelineEvents.start(recordType, 1234),
|
||||
chromeTimelineEvents.end(recordType, 2345)
|
||||
]).readPerfLog().then( (events) => {
|
||||
expect(events).toEqual([
|
||||
startEvent('render', 0),
|
||||
endEvent('render', 1),
|
||||
normEvents.start('render', 1.234),
|
||||
normEvents.end('render', 2.345),
|
||||
]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should walk children', (done) => {
|
||||
createExtension([
|
||||
durationRecord('FunctionCall', 1, 5, [
|
||||
timeStampRecord('begin_someName')
|
||||
])
|
||||
]).readPerfLog().then( (events) => {
|
||||
expect(events).toEqual([
|
||||
startEvent('script', 1),
|
||||
markStartEvent('someName'),
|
||||
endEvent('script', 5)
|
||||
]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function timeStampRecord(name) {
|
||||
return {
|
||||
'type': 'TimeStamp',
|
||||
'data': {
|
||||
'message': name
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function durationRecord(type, startTime, endTime, children = null) {
|
||||
if (isBlank(children)) {
|
||||
children = [];
|
||||
}
|
||||
return {
|
||||
'type': type,
|
||||
'startTime': startTime,
|
||||
'endTime': endTime,
|
||||
'children': children
|
||||
};
|
||||
}
|
||||
|
||||
function internalScriptRecord(startTime, endTime) {
|
||||
return {
|
||||
'type': 'FunctionCall',
|
||||
'startTime': startTime,
|
||||
'endTime': endTime,
|
||||
'data': {
|
||||
'scriptName': 'InjectedScript'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function gcRecord(startTime, endTime, gcAmount) {
|
||||
return {
|
||||
'type': 'GCEvent',
|
||||
'startTime': startTime,
|
||||
'endTime': endTime,
|
||||
'data': {
|
||||
'usedHeapSizeDelta': gcAmount
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function markStartEvent(type) {
|
||||
return {
|
||||
'name': type,
|
||||
'ph': 'b'
|
||||
}
|
||||
}
|
||||
|
||||
function markEndEvent(type) {
|
||||
return {
|
||||
'name': type,
|
||||
'ph': 'e'
|
||||
}
|
||||
}
|
||||
|
||||
function startEvent(type, time) {
|
||||
return {
|
||||
'name': type,
|
||||
'ts': time,
|
||||
'ph': 'B'
|
||||
}
|
||||
}
|
||||
|
||||
function endEvent(type, time, args = null) {
|
||||
return {
|
||||
'name': type,
|
||||
'ts': time,
|
||||
'ph': 'E',
|
||||
'args': args
|
||||
}
|
||||
}
|
||||
|
||||
class MockDriverAdapter extends WebDriverAdapter {
|
||||
_log:List;
|
||||
_perfRecords:List;
|
||||
constructor(log, perfRecords) {
|
||||
_events:List;
|
||||
constructor(log, events) {
|
||||
super();
|
||||
this._log = log;
|
||||
this._perfRecords = perfRecords;
|
||||
this._events = events;
|
||||
}
|
||||
|
||||
executeScript(script) {
|
||||
@ -247,14 +175,12 @@ class MockDriverAdapter extends WebDriverAdapter {
|
||||
logs(type) {
|
||||
ListWrapper.push(this._log, ['logs', type]);
|
||||
if (type === 'performance') {
|
||||
return PromiseWrapper.resolve(this._perfRecords.map(function(record) {
|
||||
return PromiseWrapper.resolve(this._events.map(function(event) {
|
||||
return {
|
||||
'message': Json.stringify({
|
||||
'message': {
|
||||
'method': 'Timeline.eventRecorded',
|
||||
'params': {
|
||||
'record': record
|
||||
}
|
||||
'method': 'Tracing.dataCollected',
|
||||
'params': event
|
||||
}
|
||||
})
|
||||
};
|
||||
|
251
modules/benchpress/test/webdriver/ios_driver_extension_spec.js
Normal file
251
modules/benchpress/test/webdriver/ios_driver_extension_spec.js
Normal file
@ -0,0 +1,251 @@
|
||||
import {describe, ddescribe, it, iit, xit, expect, beforeEach, afterEach} from 'angular2/test_lib';
|
||||
|
||||
import { ListWrapper } from 'angular2/src/facade/collection';
|
||||
import { PromiseWrapper } from 'angular2/src/facade/async';
|
||||
import { Json, isBlank, isPresent } from 'angular2/src/facade/lang';
|
||||
|
||||
import {
|
||||
WebDriverExtension, IOsDriverExtension,
|
||||
WebDriverAdapter, Injector, bind
|
||||
} from 'benchpress/benchpress';
|
||||
|
||||
import { TraceEventFactory } from '../trace_event_factory';
|
||||
|
||||
export function main() {
|
||||
describe('ios driver extension', () => {
|
||||
var log;
|
||||
var extension;
|
||||
|
||||
var normEvents = new TraceEventFactory('timeline', 'pid0');
|
||||
|
||||
function createExtension(perfRecords = null) {
|
||||
if (isBlank(perfRecords)) {
|
||||
perfRecords = [];
|
||||
}
|
||||
log = [];
|
||||
extension = new Injector([
|
||||
IOsDriverExtension.BINDINGS,
|
||||
bind(WebDriverAdapter).toValue(new MockDriverAdapter(log, perfRecords))
|
||||
]).get(WebDriverExtension);
|
||||
return extension;
|
||||
}
|
||||
|
||||
it('should force gc via window.gc()', (done) => {
|
||||
createExtension().gc().then( (_) => {
|
||||
expect(log).toEqual([['executeScript', 'window.gc()']]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should mark the timeline via console.time()', (done) => {
|
||||
createExtension().timeBegin('someName').then( (_) => {
|
||||
expect(log).toEqual([['executeScript', `console.time('someName');`]]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should mark the timeline via console.timeEnd()', (done) => {
|
||||
createExtension().timeEnd('someName').then( (_) => {
|
||||
expect(log).toEqual([['executeScript', `console.timeEnd('someName');`]]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should mark the timeline via console.time() and console.timeEnd()', (done) => {
|
||||
createExtension().timeEnd('name1', 'name2').then( (_) => {
|
||||
expect(log).toEqual([['executeScript', `console.timeEnd('name1');console.time('name2');`]]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('readPerfLog', () => {
|
||||
|
||||
it('should execute a dummy script before reading them', (done) => {
|
||||
// TODO(tbosch): This seems to be a bug in ChromeDriver:
|
||||
// Sometimes it does not report the newest events of the performance log
|
||||
// to the WebDriver client unless a script is executed...
|
||||
createExtension([]).readPerfLog().then( (_) => {
|
||||
expect(log).toEqual([ [ 'executeScript', '1+1' ], [ 'logs', 'performance' ] ]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should report FunctionCall records as "script"', (done) => {
|
||||
createExtension([
|
||||
durationRecord('FunctionCall', 1, 5)
|
||||
]).readPerfLog().then( (events) => {
|
||||
expect(events).toEqual([
|
||||
normEvents.start('script', 1),
|
||||
normEvents.end('script', 5)
|
||||
]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should ignore FunctionCalls from webdriver', (done) => {
|
||||
createExtension([
|
||||
internalScriptRecord(1, 5)
|
||||
]).readPerfLog().then( (events) => {
|
||||
expect(events).toEqual([]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should report begin time', (done) => {
|
||||
createExtension([
|
||||
timeBeginRecord('someName', 12)
|
||||
]).readPerfLog().then( (events) => {
|
||||
expect(events).toEqual([
|
||||
normEvents.markStart('someName', 12)
|
||||
]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should report end timestamps', (done) => {
|
||||
createExtension([
|
||||
timeEndRecord('someName', 12)
|
||||
]).readPerfLog().then( (events) => {
|
||||
expect(events).toEqual([
|
||||
normEvents.markEnd('someName', 12)
|
||||
]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should report gc', (done) => {
|
||||
createExtension([
|
||||
gcRecord(1, 3, 21)
|
||||
]).readPerfLog().then( (events) => {
|
||||
expect(events).toEqual([
|
||||
normEvents.start('gc', 1, {'usedHeapSize': 0}),
|
||||
normEvents.end('gc', 3, {'usedHeapSize': -21}),
|
||||
]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
['RecalculateStyles', 'Layout', 'UpdateLayerTree', 'Paint', 'Rasterize', 'CompositeLayers'].forEach( (recordType) => {
|
||||
it(`should report ${recordType}`, (done) => {
|
||||
createExtension([
|
||||
durationRecord(recordType, 0, 1)
|
||||
]).readPerfLog().then( (events) => {
|
||||
expect(events).toEqual([
|
||||
normEvents.start('render', 0),
|
||||
normEvents.end('render', 1),
|
||||
]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should walk children', (done) => {
|
||||
createExtension([
|
||||
durationRecord('FunctionCall', 1, 5, [
|
||||
timeBeginRecord('someName', 2)
|
||||
])
|
||||
]).readPerfLog().then( (events) => {
|
||||
expect(events).toEqual([
|
||||
normEvents.start('script', 1),
|
||||
normEvents.markStart('someName', 2),
|
||||
normEvents.end('script', 5)
|
||||
]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function timeBeginRecord(name, time) {
|
||||
return {
|
||||
'type': 'Time',
|
||||
'startTime': time,
|
||||
'data': {
|
||||
'message': name
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function timeEndRecord(name, time) {
|
||||
return {
|
||||
'type': 'TimeEnd',
|
||||
'startTime': time,
|
||||
'data': {
|
||||
'message': name
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function durationRecord(type, startTime, endTime, children = null) {
|
||||
if (isBlank(children)) {
|
||||
children = [];
|
||||
}
|
||||
return {
|
||||
'type': type,
|
||||
'startTime': startTime,
|
||||
'endTime': endTime,
|
||||
'children': children
|
||||
};
|
||||
}
|
||||
|
||||
function internalScriptRecord(startTime, endTime) {
|
||||
return {
|
||||
'type': 'FunctionCall',
|
||||
'startTime': startTime,
|
||||
'endTime': endTime,
|
||||
'data': {
|
||||
'scriptName': 'InjectedScript'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function gcRecord(startTime, endTime, gcAmount) {
|
||||
return {
|
||||
'type': 'GCEvent',
|
||||
'startTime': startTime,
|
||||
'endTime': endTime,
|
||||
'data': {
|
||||
'usedHeapSizeDelta': gcAmount
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
class MockDriverAdapter extends WebDriverAdapter {
|
||||
_log:List;
|
||||
_perfRecords:List;
|
||||
constructor(log, perfRecords) {
|
||||
super();
|
||||
this._log = log;
|
||||
this._perfRecords = perfRecords;
|
||||
}
|
||||
|
||||
executeScript(script) {
|
||||
ListWrapper.push(this._log, ['executeScript', script]);
|
||||
return PromiseWrapper.resolve(null);
|
||||
}
|
||||
|
||||
logs(type) {
|
||||
ListWrapper.push(this._log, ['logs', type]);
|
||||
if (type === 'performance') {
|
||||
return PromiseWrapper.resolve(this._perfRecords.map(function(record) {
|
||||
return {
|
||||
'message': Json.stringify({
|
||||
'message': {
|
||||
'method': 'Timeline.eventRecorded',
|
||||
'params': {
|
||||
'record': record
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
}));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user