parent
f9908cd436
commit
e323c07ab9
|
@ -475,7 +475,7 @@ gulp.task('test.unit.dart/ci', function (done) {
|
|||
|
||||
|
||||
gulp.task('test.unit.cjs/ci', function(done) {
|
||||
runJasmineTests(['dist/js/cjs/angular2/test/**/*_spec.js'], done);
|
||||
runJasmineTests(['dist/js/cjs/{angular2,benchpress}/test/**/*_spec.js'], done);
|
||||
});
|
||||
|
||||
|
||||
|
|
|
@ -43,7 +43,9 @@ export function CONST() {
|
|||
return (target) => target;
|
||||
}
|
||||
|
||||
export class ABSTRACT {}
|
||||
export function ABSTRACT() {
|
||||
return (t) => t;
|
||||
}
|
||||
|
||||
// Note: This is only a marker annotation needed for ts2dart.
|
||||
// This is written so that is can be used as a Traceur annotation
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import { bind } from 'angular2/di';
|
||||
import { Options } from './common';
|
||||
/// <reference path="../angular2/typings/node/node.d.ts" />
|
||||
|
||||
import {bind} from 'angular2/di';
|
||||
import {Options} from './common';
|
||||
|
||||
export * from './common';
|
||||
export { SeleniumWebDriverAdapter } from './src/webdriver/selenium_webdriver_adapter';
|
||||
export {SeleniumWebDriverAdapter} from './src/webdriver/selenium_webdriver_adapter';
|
||||
|
||||
var fs = require('fs');
|
||||
|
||||
|
@ -11,11 +13,9 @@ var fs = require('fs');
|
|||
// find another way...
|
||||
// Note: Can't do the `require` call in a facade as it can't be loaded into the browser
|
||||
// for our unit tests via karma.
|
||||
Options.DEFAULT_BINDINGS.push(
|
||||
bind(Options.WRITE_FILE).toValue(writeFile)
|
||||
);
|
||||
Options.DEFAULT_BINDINGS.push(bind(Options.WRITE_FILE).toValue(writeFile));
|
||||
|
||||
function writeFile(filename, content) {
|
||||
function writeFile(filename, content): Promise<any> {
|
||||
return new Promise(function(resolve, reject) {
|
||||
fs.writeFile(filename, content, (error) => {
|
||||
if (error) {
|
|
@ -1,21 +0,0 @@
|
|||
export { Sampler, SampleState } from './src/sampler';
|
||||
export { Metric } from './src/metric';
|
||||
export { Validator } from './src/validator';
|
||||
export { Reporter } from './src/reporter';
|
||||
export { WebDriverExtension, PerfLogFeatures } from './src/web_driver_extension';
|
||||
export { WebDriverAdapter } from './src/web_driver_adapter';
|
||||
export { SizeValidator } from './src/validator/size_validator';
|
||||
export { RegressionSlopeValidator } from './src/validator/regression_slope_validator';
|
||||
export { ConsoleReporter } from './src/reporter/console_reporter';
|
||||
export { JsonFileReporter } from './src/reporter/json_file_reporter';
|
||||
export { SampleDescription } from './src/sample_description';
|
||||
export { PerflogMetric } from './src/metric/perflog_metric';
|
||||
export { ChromeDriverExtension } from './src/webdriver/chrome_driver_extension';
|
||||
export { IOsDriverExtension } from './src/webdriver/ios_driver_extension';
|
||||
export { Runner } from './src/runner';
|
||||
export { Options } from './src/common_options';
|
||||
export { MeasureValues } from './src/measure_values';
|
||||
export { MultiMetric } from './src/metric/multi_metric';
|
||||
export { MultiReporter } from './src/reporter/multi_reporter';
|
||||
|
||||
export { bind, Injector, OpaqueToken } from 'angular2/di';
|
|
@ -0,0 +1,21 @@
|
|||
export {Sampler, SampleState} from './src/sampler';
|
||||
export {Metric} from './src/metric';
|
||||
export {Validator} from './src/validator';
|
||||
export {Reporter} from './src/reporter';
|
||||
export {WebDriverExtension, PerfLogFeatures} from './src/web_driver_extension';
|
||||
export {WebDriverAdapter} from './src/web_driver_adapter';
|
||||
export {SizeValidator} from './src/validator/size_validator';
|
||||
export {RegressionSlopeValidator} from './src/validator/regression_slope_validator';
|
||||
export {ConsoleReporter} from './src/reporter/console_reporter';
|
||||
export {JsonFileReporter} from './src/reporter/json_file_reporter';
|
||||
export {SampleDescription} from './src/sample_description';
|
||||
export {PerflogMetric} from './src/metric/perflog_metric';
|
||||
export {ChromeDriverExtension} from './src/webdriver/chrome_driver_extension';
|
||||
export {IOsDriverExtension} from './src/webdriver/ios_driver_extension';
|
||||
export {Runner} from './src/runner';
|
||||
export {Options} from './src/common_options';
|
||||
export {MeasureValues} from './src/measure_values';
|
||||
export {MultiMetric} from './src/metric/multi_metric';
|
||||
export {MultiReporter} from './src/reporter/multi_reporter';
|
||||
|
||||
export {bind, Injector, OpaqueToken} from 'angular2/di';
|
|
@ -1,2 +0,0 @@
|
|||
require(require('traceur').RUNTIME_PATH);
|
||||
module.exports = require('./benchpress.js');
|
|
@ -1,5 +1,5 @@
|
|||
import { bind, OpaqueToken } from 'angular2/di';
|
||||
import { DateWrapper } from 'angular2/src/facade/lang';
|
||||
import {bind, OpaqueToken} from 'angular2/di';
|
||||
import {DateWrapper} from 'angular2/src/facade/lang';
|
||||
|
||||
export class Options {
|
||||
static get DEFAULT_BINDINGS() { return _DEFAULT_BINDINGS; }
|
||||
|
@ -40,10 +40,11 @@ var _NOW = new OpaqueToken('Options.now');
|
|||
var _WRITE_FILE = new OpaqueToken('Options.writeFile');
|
||||
|
||||
var _DEFAULT_BINDINGS = [
|
||||
bind(_DEFAULT_DESCRIPTION).toValue({}),
|
||||
bind(_DEFAULT_DESCRIPTION)
|
||||
.toValue({}),
|
||||
bind(_SAMPLE_DESCRIPTION).toValue({}),
|
||||
bind(_FORCE_GC).toValue(false),
|
||||
bind(_PREPARE).toValue(false),
|
||||
bind(_MICRO_METRICS).toValue({}),
|
||||
bind(_NOW).toValue( () => DateWrapper.now() )
|
||||
];
|
||||
bind(_NOW).toValue(() => DateWrapper.now())
|
||||
];
|
|
@ -0,0 +1,2 @@
|
|||
library benchpress.src.firefox_extension.data.installed_script;
|
||||
//no dart implementation
|
|
@ -1,12 +0,0 @@
|
|||
exportFunction(function() {
|
||||
self.port.emit('startProfiler');
|
||||
}, unsafeWindow, {defineAs: "startProfiler"});
|
||||
|
||||
exportFunction(function(filePath) {
|
||||
self.port.emit('stopAndRecord', filePath);
|
||||
}, unsafeWindow, {defineAs: "stopAndRecord"});
|
||||
|
||||
exportFunction(function() {
|
||||
self.port.emit('forceGC');
|
||||
}, unsafeWindow, {defineAs: "forceGC"});
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
declare var exportFunction;
|
||||
declare var unsafeWindow;
|
||||
|
||||
exportFunction(function() { (<any>self).port.emit('startProfiler'); }, unsafeWindow,
|
||||
{defineAs: "startProfiler"});
|
||||
|
||||
exportFunction(function(filePath) { (<any>self).port.emit('stopAndRecord', filePath); },
|
||||
unsafeWindow, {defineAs: "stopAndRecord"});
|
||||
|
||||
exportFunction(function() { (<any>self).port.emit('forceGC'); }, unsafeWindow,
|
||||
{defineAs: "forceGC"});
|
|
@ -0,0 +1,2 @@
|
|||
library benchpress.src.firefox_extension.lib.main;
|
||||
//no dart implementation
|
|
@ -1,61 +0,0 @@
|
|||
var file = require('sdk/io/file');
|
||||
var {Cc,Ci,Cu} = require("chrome");
|
||||
|
||||
function Profiler() {
|
||||
this._profiler = Cc["@mozilla.org/tools/profiler;1"].getService(Ci.nsIProfiler);
|
||||
};
|
||||
|
||||
Profiler.prototype = {
|
||||
start: function(entries, interval, features) {
|
||||
this._profiler.StartProfiler(entries, interval, features, features.length);
|
||||
},
|
||||
stop: function(cb) {
|
||||
this._profiler.StopProfiler();
|
||||
// this.gcStats.clear();
|
||||
},
|
||||
getProfileData: function() {
|
||||
return this._profiler.getProfileData();
|
||||
}
|
||||
};
|
||||
|
||||
function saveToFile(savePath, str) {
|
||||
var textWriter = file.open(savePath, 'w');
|
||||
textWriter.write(str);
|
||||
textWriter.close();
|
||||
}
|
||||
|
||||
function forceGC() {
|
||||
Cu.forceGC();
|
||||
var os = Cc["@mozilla.org/observer-service;1"]
|
||||
.getService(Ci.nsIObserverService);
|
||||
os.notifyObservers(null, "child-gc-request", null);
|
||||
}
|
||||
|
||||
var profiler = new Profiler();
|
||||
function startProfiler() {
|
||||
profiler.start(/* = profiler memory */ 10000000, 1, ['leaf', 'js', "stackwalk", 'gc']);
|
||||
};
|
||||
function stopAndRecord(filePath) {
|
||||
var profileData = profiler.getProfileData();
|
||||
profiler.stop();
|
||||
saveToFile(filePath, JSON.stringify(profileData, null, 2));
|
||||
};
|
||||
|
||||
|
||||
var mod = require("sdk/page-mod");
|
||||
var data = require("sdk/self").data;
|
||||
mod.PageMod({
|
||||
include: ['*'],
|
||||
contentScriptFile: data.url("installed_script.js"),
|
||||
onAttach: function(worker) {
|
||||
worker.port.on('startProfiler', function() {
|
||||
startProfiler();
|
||||
});
|
||||
worker.port.on('stopAndRecord', function(filePath) {
|
||||
stopAndRecord(filePath);
|
||||
});
|
||||
worker.port.on('forceGC', function() {
|
||||
forceGC();
|
||||
});
|
||||
}
|
||||
});
|
|
@ -0,0 +1,53 @@
|
|||
/// <reference path="../../../../angular2/typings/node/node.d.ts" />
|
||||
|
||||
var file = require('sdk/io/file');
|
||||
var {Cc, Ci, Cu} = require("chrome");
|
||||
|
||||
class Profiler {
|
||||
private _profiler;
|
||||
constructor() { this._profiler = Cc["@mozilla.org/tools/profiler;1"].getService(Ci.nsIProfiler); }
|
||||
|
||||
start(entries, interval, features) {
|
||||
this._profiler.StartProfiler(entries, interval, features, features.length);
|
||||
}
|
||||
|
||||
stop() { this._profiler.StopProfiler(); }
|
||||
getProfileData() { return this._profiler.getProfileData(); }
|
||||
}
|
||||
|
||||
|
||||
function saveToFile(savePath: string, body: string) {
|
||||
var textWriter = file.open(savePath, 'w');
|
||||
textWriter.write(body);
|
||||
textWriter.close();
|
||||
}
|
||||
|
||||
function forceGC() {
|
||||
Cu.forceGC();
|
||||
var os = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
|
||||
os.notifyObservers(null, "child-gc-request", null);
|
||||
}
|
||||
|
||||
var profiler = new Profiler();
|
||||
function startProfiler() {
|
||||
profiler.start(/* = profiler memory */ 10000000, 1, ['leaf', 'js', "stackwalk", 'gc']);
|
||||
};
|
||||
function stopAndRecord(filePath) {
|
||||
var profileData = profiler.getProfileData();
|
||||
profiler.stop();
|
||||
saveToFile(filePath, JSON.stringify(profileData, null, 2));
|
||||
};
|
||||
|
||||
|
||||
var mod = require("sdk/page-mod");
|
||||
var data = require("sdk/self").data;
|
||||
mod.PageMod({
|
||||
include: ['*'],
|
||||
contentScriptFile: data.url("installed_script.js"),
|
||||
onAttach: worker =>
|
||||
{
|
||||
worker.port.on('startProfiler', () => startProfiler());
|
||||
worker.port.on('stopAndRecord', filePath => stopAndRecord(filePath));
|
||||
worker.port.on('forceGC', () => forceGC());
|
||||
}
|
||||
});
|
|
@ -0,0 +1,2 @@
|
|||
library benchpress.src.firefox_extension.lib.test_helper;
|
||||
//no dart implementation
|
|
@ -19,12 +19,9 @@ exports.getFirefoxProfile = function(extensionPath) {
|
|||
var deferred = q.defer();
|
||||
|
||||
var firefoxProfile = new FirefoxProfile();
|
||||
firefoxProfile.addExtensions([extensionPath], function() {
|
||||
firefoxProfile.encoded(function(encodedProfile) {
|
||||
var multiCapabilities = [{
|
||||
browserName: 'firefox',
|
||||
firefox_profile : encodedProfile
|
||||
}];
|
||||
firefoxProfile.addExtensions([extensionPath], () => {
|
||||
firefoxProfile.encoded(encodedProfile => {
|
||||
var multiCapabilities = [{browserName: 'firefox', firefox_profile: encodedProfile}];
|
||||
deferred.resolve(multiCapabilities);
|
||||
});
|
||||
});
|
||||
|
@ -39,9 +36,9 @@ exports.getFirefoxProfileWithExtension = function() {
|
|||
var savedCwd = process.cwd();
|
||||
process.chdir(absPackageJsonDir);
|
||||
|
||||
return jpm(packageJson).then(function(xpiPath) {
|
||||
process.chdir(savedCwd);
|
||||
return exports.getFirefoxProfile(xpiPath);
|
||||
});
|
||||
return jpm(packageJson)
|
||||
.then(xpiPath => {
|
||||
process.chdir(savedCwd);
|
||||
return exports.getFirefoxProfile(xpiPath);
|
||||
});
|
||||
};
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
import { Date, DateWrapper } from 'angular2/src/facade/lang';
|
||||
import { StringMap } from 'angular2/src/facade/collection';
|
||||
|
||||
export class MeasureValues {
|
||||
timeStamp:Date;
|
||||
runIndex:number;
|
||||
values:StringMap;
|
||||
|
||||
constructor(runIndex:number, timeStamp:Date, values:StringMap) {
|
||||
this.timeStamp = timeStamp;
|
||||
this.runIndex = runIndex;
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
toJson() {
|
||||
return {
|
||||
'timeStamp': DateWrapper.toJson(this.timeStamp),
|
||||
'runIndex': this.runIndex,
|
||||
'values': this.values
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
import {Date, DateWrapper} from 'angular2/src/facade/lang';
|
||||
import {StringMap, Map} from 'angular2/src/facade/collection';
|
||||
|
||||
export class MeasureValues {
|
||||
constructor(public runIndex: number, public timeStamp: Date,
|
||||
public values: StringMap<string, any>) {}
|
||||
|
||||
toJson() {
|
||||
return {
|
||||
'timeStamp': DateWrapper.toJson(this.timeStamp),
|
||||
'runIndex': this.runIndex,
|
||||
'values': this.values
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
import { bind } from 'angular2/di';
|
||||
import {
|
||||
Promise, PromiseWrapper
|
||||
} from 'angular2/src/facade/async';
|
||||
import {
|
||||
ABSTRACT, BaseException
|
||||
} from 'angular2/src/facade/lang';
|
||||
import { StringMap } from 'angular2/src/facade/collection';
|
||||
|
||||
/**
|
||||
* A metric is measures values
|
||||
*/
|
||||
@ABSTRACT()
|
||||
export class Metric {
|
||||
static bindTo(delegateToken) {
|
||||
return [
|
||||
bind(Metric).toFactory(
|
||||
(delegate) => delegate, [delegateToken]
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts measuring
|
||||
*/
|
||||
beginMeasure():Promise {
|
||||
throw new BaseException('NYI');
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends measuring and reports the data
|
||||
* since the begin call.
|
||||
* @param restart: Whether to restart right after this.
|
||||
*/
|
||||
endMeasure(restart:boolean):Promise<StringMap> {
|
||||
throw new BaseException('NYI');
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes the metrics provided by this metric implementation.
|
||||
* (e.g. units, ...)
|
||||
*/
|
||||
describe():StringMap {
|
||||
throw new BaseException('NYI');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
import {bind} from 'angular2/di';
|
||||
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
||||
import {ABSTRACT, BaseException} from 'angular2/src/facade/lang';
|
||||
import {StringMap} from 'angular2/src/facade/collection';
|
||||
|
||||
/**
|
||||
* A metric is measures values
|
||||
*/
|
||||
@ABSTRACT()
|
||||
export class Metric {
|
||||
static bindTo(delegateToken) {
|
||||
return [bind(Metric).toFactory((delegate) => delegate, [delegateToken])];
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts measuring
|
||||
*/
|
||||
beginMeasure(): Promise<any> { throw new BaseException('NYI'); }
|
||||
|
||||
/**
|
||||
* Ends measuring and reports the data
|
||||
* since the begin call.
|
||||
* @param restart: Whether to restart right after this.
|
||||
*/
|
||||
endMeasure(restart: boolean): Promise<StringMap<string, any>> { throw new BaseException('NYI'); }
|
||||
|
||||
/**
|
||||
* Describes the metrics provided by this metric implementation.
|
||||
* (e.g. units, ...)
|
||||
*/
|
||||
describe(): StringMap<string, any> { throw new BaseException('NYI'); }
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
import { bind, Injector, OpaqueToken } from 'angular2/di';
|
||||
import { List, ListWrapper, StringMapWrapper, StringMap } from 'angular2/src/facade/collection';
|
||||
import { Promise, PromiseWrapper } from 'angular2/src/facade/async';
|
||||
|
||||
import { Metric } from '../metric';
|
||||
|
||||
export class MultiMetric extends Metric {
|
||||
static createBindings(childTokens) {
|
||||
return [
|
||||
bind(_CHILDREN).toAsyncFactory(
|
||||
(injector) => PromiseWrapper.all(ListWrapper.map(childTokens, (token) => injector.asyncGet(token) )),
|
||||
[Injector]
|
||||
),
|
||||
bind(MultiMetric).toFactory(
|
||||
(children) => new MultiMetric(children),
|
||||
[_CHILDREN]
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
_metrics:List;
|
||||
|
||||
constructor(metrics) {
|
||||
super();
|
||||
this._metrics = metrics;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts measuring
|
||||
*/
|
||||
beginMeasure():Promise {
|
||||
return PromiseWrapper.all(ListWrapper.map(
|
||||
this._metrics, (metric) => metric.beginMeasure()
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends measuring and reports the data
|
||||
* since the begin call.
|
||||
* @param restart: Whether to restart right after this.
|
||||
*/
|
||||
endMeasure(restart:boolean):Promise<StringMap> {
|
||||
return PromiseWrapper.all(ListWrapper.map(
|
||||
this._metrics, (metric) => metric.endMeasure(restart)
|
||||
)).then( (values) => {
|
||||
return mergeStringMaps(values);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes the metrics provided by this metric implementation.
|
||||
* (e.g. units, ...)
|
||||
*/
|
||||
describe():StringMap {
|
||||
return mergeStringMaps(this._metrics.map( (metric) => metric.describe() ));
|
||||
}
|
||||
}
|
||||
|
||||
function mergeStringMaps(maps) {
|
||||
var result = {};
|
||||
ListWrapper.forEach(maps, (map) => {
|
||||
StringMapWrapper.forEach(map, (value, prop) => {
|
||||
result[prop] = value;
|
||||
});
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
var _CHILDREN = new OpaqueToken('MultiMetric.children');
|
|
@ -0,0 +1,55 @@
|
|||
import {bind, Binding, Injector, OpaqueToken} from 'angular2/di';
|
||||
import {List, ListWrapper, StringMapWrapper, StringMap} from 'angular2/src/facade/collection';
|
||||
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
||||
|
||||
import {Metric} from '../metric';
|
||||
|
||||
export class MultiMetric extends Metric {
|
||||
static createBindings(childTokens): List<Binding> {
|
||||
return [
|
||||
bind(_CHILDREN)
|
||||
.toAsyncFactory((injector) => PromiseWrapper.all(
|
||||
ListWrapper.map(childTokens, (token) => injector.asyncGet(token))),
|
||||
[Injector]),
|
||||
bind(MultiMetric).toFactory((children) => new MultiMetric(children), [_CHILDREN])
|
||||
];
|
||||
}
|
||||
|
||||
constructor(private _metrics: List<Metric>) { super(); }
|
||||
|
||||
/**
|
||||
* Starts measuring
|
||||
*/
|
||||
beginMeasure(): Promise<any> {
|
||||
return PromiseWrapper.all(ListWrapper.map(this._metrics, (metric) => metric.beginMeasure()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends measuring and reports the data
|
||||
* since the begin call.
|
||||
* @param restart: Whether to restart right after this.
|
||||
*/
|
||||
endMeasure(restart: boolean): Promise<StringMap<string, any>> {
|
||||
return PromiseWrapper.all(
|
||||
ListWrapper.map(this._metrics, (metric) => metric.endMeasure(restart)))
|
||||
.then((values) => { return mergeStringMaps(values); });
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes the metrics provided by this metric implementation.
|
||||
* (e.g. units, ...)
|
||||
*/
|
||||
describe(): StringMap<string, any> {
|
||||
return mergeStringMaps(this._metrics.map((metric) => metric.describe()));
|
||||
}
|
||||
}
|
||||
|
||||
function mergeStringMaps(maps): Object {
|
||||
var result = {};
|
||||
ListWrapper.forEach(maps, (map) => {
|
||||
StringMapWrapper.forEach(map, (value, prop) => { result[prop] = value; });
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
var _CHILDREN = new OpaqueToken('MultiMetric.children');
|
|
@ -1,48 +1,49 @@
|
|||
import { PromiseWrapper, Promise, TimerWrapper } from 'angular2/src/facade/async';
|
||||
import {PromiseWrapper, Promise, TimerWrapper} from 'angular2/src/facade/async';
|
||||
import {
|
||||
isPresent, isBlank, int, BaseException, StringWrapper, Math, RegExpWrapper, NumberWrapper
|
||||
isPresent,
|
||||
isBlank,
|
||||
BaseException,
|
||||
StringWrapper,
|
||||
Math,
|
||||
RegExpWrapper,
|
||||
NumberWrapper
|
||||
} from 'angular2/src/facade/lang';
|
||||
import { ListWrapper, StringMap, StringMapWrapper } from 'angular2/src/facade/collection';
|
||||
import { bind, OpaqueToken } from 'angular2/di';
|
||||
import {ListWrapper, StringMap, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {bind, Binding, OpaqueToken} from 'angular2/di';
|
||||
|
||||
import { WebDriverExtension, PerfLogFeatures } from '../web_driver_extension';
|
||||
import { Metric } from '../metric';
|
||||
import { Options } from '../common_options';
|
||||
import {WebDriverExtension, PerfLogFeatures} from '../web_driver_extension';
|
||||
import {Metric} from '../metric';
|
||||
import {Options} from '../common_options';
|
||||
|
||||
/**
|
||||
* A metric that reads out the performance log
|
||||
*/
|
||||
export class PerflogMetric extends Metric {
|
||||
// TODO(tbosch): use static values when our transpiler supports them
|
||||
static get BINDINGS() { return _BINDINGS; }
|
||||
static get BINDINGS(): List<Binding> { return _BINDINGS; }
|
||||
// TODO(tbosch): use static values when our transpiler supports them
|
||||
static get SET_TIMEOUT() { return _SET_TIMEOUT; }
|
||||
static get SET_TIMEOUT(): OpaqueToken { return _SET_TIMEOUT; }
|
||||
|
||||
private _remainingEvents: List<StringMap<string, any>>;
|
||||
private _measureCount: int;
|
||||
_perfLogFeatures: PerfLogFeatures;
|
||||
|
||||
_driverExtension:WebDriverExtension;
|
||||
_remainingEvents:List;
|
||||
_measureCount:int;
|
||||
_setTimeout:Function;
|
||||
_microMetrics:StringMap<string, string>;
|
||||
_perfLogFeatures:PerfLogFeatures;
|
||||
_forceGc:boolean;
|
||||
|
||||
/**
|
||||
* @param driverExtension
|
||||
* @param setTimeout
|
||||
* @param microMetrics Name and description of metrics provided via console.time / console.timeEnd
|
||||
**/
|
||||
constructor(driverExtension:WebDriverExtension, setTimeout:Function, microMetrics:StringMap<string, string>, forceGc) {
|
||||
constructor(private _driverExtension: WebDriverExtension, private _setTimeout: Function,
|
||||
private _microMetrics: StringMap<string, any>, private _forceGc: boolean) {
|
||||
super();
|
||||
this._driverExtension = driverExtension;
|
||||
|
||||
this._remainingEvents = [];
|
||||
this._measureCount = 0;
|
||||
this._setTimeout = setTimeout;
|
||||
this._microMetrics = microMetrics;
|
||||
this._perfLogFeatures = driverExtension.perfLogFeatures();
|
||||
this._forceGc = forceGc;
|
||||
this._perfLogFeatures = _driverExtension.perfLogFeatures();
|
||||
}
|
||||
|
||||
describe():StringMap {
|
||||
describe(): StringMap<string, any> {
|
||||
var res = {
|
||||
'scriptTime': 'script execution time in ms, including gc and render',
|
||||
'pureScriptTime': 'script execution time in ms, without gc nor render'
|
||||
|
@ -59,21 +60,20 @@ export class PerflogMetric extends Metric {
|
|||
res['forcedGcAmount'] = 'forced gc amount in kbytes';
|
||||
}
|
||||
}
|
||||
StringMapWrapper.forEach(this._microMetrics, (desc, name) => {
|
||||
StringMapWrapper.set(res, name, desc);
|
||||
});
|
||||
StringMapWrapper.forEach(this._microMetrics,
|
||||
(desc, name) => { StringMapWrapper.set(res, name, desc); });
|
||||
return res;
|
||||
}
|
||||
|
||||
beginMeasure():Promise {
|
||||
beginMeasure(): Promise<any> {
|
||||
var resultPromise = PromiseWrapper.resolve(null);
|
||||
if (this._forceGc) {
|
||||
resultPromise = resultPromise.then( (_) => this._driverExtension.gc() );
|
||||
resultPromise = resultPromise.then((_) => this._driverExtension.gc());
|
||||
}
|
||||
return resultPromise.then( (_) => this._beginMeasure() );
|
||||
return resultPromise.then((_) => this._beginMeasure());
|
||||
}
|
||||
|
||||
endMeasure(restart:boolean):Promise<StringMap> {
|
||||
endMeasure(restart: boolean): Promise<StringMap<string, any>> {
|
||||
if (this._forceGc) {
|
||||
return this._endPlainMeasureAndMeasureForceGc(restart);
|
||||
} else {
|
||||
|
@ -81,47 +81,43 @@ export class PerflogMetric extends Metric {
|
|||
}
|
||||
}
|
||||
|
||||
_endPlainMeasureAndMeasureForceGc(restartMeasure:boolean) {
|
||||
return this._endMeasure(true).then( (measurValues) => {
|
||||
return this._driverExtension.gc()
|
||||
.then( (_) => this._endMeasure(restartMeasure) )
|
||||
.then( (forceGcMeasureValues) => {
|
||||
StringMapWrapper.set(measurValues, 'forcedGcTime', forceGcMeasureValues['gcTime']);
|
||||
StringMapWrapper.set(measurValues, 'forcedGcAmount', forceGcMeasureValues['gcAmount']);
|
||||
return measurValues;
|
||||
_endPlainMeasureAndMeasureForceGc(restartMeasure: boolean) {
|
||||
return this._endMeasure(true).then((measureValues) => {
|
||||
return this._driverExtension.gc()
|
||||
.then((_) => this._endMeasure(restartMeasure))
|
||||
.then((forceGcMeasureValues) => {
|
||||
StringMapWrapper.set(measureValues, 'forcedGcTime', forceGcMeasureValues['gcTime']);
|
||||
StringMapWrapper.set(measureValues, 'forcedGcAmount', forceGcMeasureValues['gcAmount']);
|
||||
return measureValues;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
_beginMeasure():Promise {
|
||||
_beginMeasure(): Promise<any> {
|
||||
return this._driverExtension.timeBegin(this._markName(this._measureCount++));
|
||||
}
|
||||
|
||||
_endMeasure(restart:boolean):Promise<StringMap> {
|
||||
var markName = this._markName(this._measureCount-1);
|
||||
_endMeasure(restart: boolean): Promise<StringMap<string, any>> {
|
||||
var markName = this._markName(this._measureCount - 1);
|
||||
var nextMarkName = restart ? this._markName(this._measureCount++) : null;
|
||||
return this._driverExtension.timeEnd(markName, nextMarkName)
|
||||
.then( (_) => this._readUntilEndMark(markName) );
|
||||
.then((_) => this._readUntilEndMark(markName));
|
||||
}
|
||||
|
||||
_readUntilEndMark(markName:string, loopCount:int = 0, startEvent = null) {
|
||||
_readUntilEndMark(markName: string, loopCount: int = 0, startEvent = null) {
|
||||
if (loopCount > _MAX_RETRY_COUNT) {
|
||||
throw new BaseException(`Tried too often to get the ending mark: ${loopCount}`);
|
||||
}
|
||||
return this._driverExtension.readPerfLog().then( (events) => {
|
||||
return this._driverExtension.readPerfLog().then((events) => {
|
||||
this._addEvents(events);
|
||||
var result = this._aggregateEvents(
|
||||
this._remainingEvents, markName
|
||||
);
|
||||
var result = this._aggregateEvents(this._remainingEvents, markName);
|
||||
if (isPresent(result)) {
|
||||
this._remainingEvents = events;
|
||||
return result;
|
||||
}
|
||||
var completer = PromiseWrapper.completer();
|
||||
this._setTimeout(
|
||||
() => completer.resolve(this._readUntilEndMark(markName, loopCount+1)),
|
||||
100
|
||||
);
|
||||
this._setTimeout(() => completer.resolve(this._readUntilEndMark(markName, loopCount + 1)),
|
||||
100);
|
||||
return completer.promise;
|
||||
});
|
||||
}
|
||||
|
@ -148,22 +144,15 @@ export class PerflogMetric extends Metric {
|
|||
});
|
||||
if (needSort) {
|
||||
// Need to sort because of the ph==='X' events
|
||||
ListWrapper.sort(this._remainingEvents, (a,b) => {
|
||||
ListWrapper.sort(this._remainingEvents, (a, b) => {
|
||||
var diff = a['ts'] - b['ts'];
|
||||
return diff > 0
|
||||
? 1
|
||||
: diff < 0
|
||||
? -1
|
||||
: 0;
|
||||
return diff > 0 ? 1 : diff < 0 ? -1 : 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_aggregateEvents(events, markName) {
|
||||
var result = {
|
||||
'scriptTime': 0,
|
||||
'pureScriptTime': 0
|
||||
};
|
||||
_aggregateEvents(events, markName): StringMap<string, any> {
|
||||
var result = {'scriptTime': 0, 'pureScriptTime': 0};
|
||||
if (this._perfLogFeatures.gc) {
|
||||
result['gcTime'] = 0;
|
||||
result['majorGcTime'] = 0;
|
||||
|
@ -172,9 +161,7 @@ export class PerflogMetric extends Metric {
|
|||
if (this._perfLogFeatures.render) {
|
||||
result['renderTime'] = 0;
|
||||
}
|
||||
StringMapWrapper.forEach(this._microMetrics, (desc, name) => {
|
||||
result[name] = 0;
|
||||
});
|
||||
StringMapWrapper.forEach(this._microMetrics, (desc, name) => { result[name] = 0; });
|
||||
|
||||
var markStartEvent = null;
|
||||
var markEndEvent = null;
|
||||
|
@ -182,7 +169,7 @@ export class PerflogMetric extends Metric {
|
|||
var renderTimeInScript = 0;
|
||||
|
||||
var intervalStarts = {};
|
||||
events.forEach( (event) => {
|
||||
events.forEach((event) => {
|
||||
var ph = event['ph'];
|
||||
var name = event['name'];
|
||||
var microIterations = 1;
|
||||
|
@ -197,16 +184,19 @@ export class PerflogMetric extends Metric {
|
|||
} else if (StringWrapper.equals(ph, 'e') && StringWrapper.equals(name, markName)) {
|
||||
markEndEvent = event;
|
||||
}
|
||||
if (isPresent(markStartEvent) && isBlank(markEndEvent) && event['pid'] === markStartEvent['pid']) {
|
||||
if (isPresent(markStartEvent) && isBlank(markEndEvent) &&
|
||||
event['pid'] === markStartEvent['pid']) {
|
||||
if (StringWrapper.equals(ph, 'B') || StringWrapper.equals(ph, 'b')) {
|
||||
intervalStarts[name] = event;
|
||||
} else if ((StringWrapper.equals(ph, 'E') || StringWrapper.equals(ph, 'e')) && isPresent(intervalStarts[name])) {
|
||||
} else if ((StringWrapper.equals(ph, 'E') || StringWrapper.equals(ph, 'e')) &&
|
||||
isPresent(intervalStarts[name])) {
|
||||
var startEvent = intervalStarts[name];
|
||||
var duration = (event['ts'] - startEvent['ts']);
|
||||
intervalStarts[name] = null;
|
||||
if (StringWrapper.equals(name, 'gc')) {
|
||||
result['gcTime'] += duration;
|
||||
var amount = (startEvent['args']['usedHeapSize'] - event['args']['usedHeapSize']) / 1000;
|
||||
var amount =
|
||||
(startEvent['args']['usedHeapSize'] - event['args']['usedHeapSize']) / 1000;
|
||||
result['gcAmount'] += amount;
|
||||
var majorGc = event['args']['majorGc'];
|
||||
if (isPresent(majorGc) && majorGc) {
|
||||
|
@ -232,21 +222,18 @@ export class PerflogMetric extends Metric {
|
|||
return isPresent(markStartEvent) && isPresent(markEndEvent) ? result : null;
|
||||
}
|
||||
|
||||
_markName(index) {
|
||||
return `${_MARK_NAME_PREFIX}${index}`;
|
||||
}
|
||||
_markName(index) { return `${_MARK_NAME_PREFIX}${index}`; }
|
||||
}
|
||||
|
||||
var _MICRO_ITERATIONS_REGEX = RegExpWrapper.create('(.+)\\*(\\d+)$');
|
||||
|
||||
var _MAX_RETRY_COUNT = 20;
|
||||
var _MARK_NAME_PREFIX = 'benchpress';
|
||||
var _SET_TIMEOUT = new OpaqueToken('PerflogMetric.setTimeout');
|
||||
|
||||
var _BINDINGS = [
|
||||
bind(PerflogMetric).toFactory(
|
||||
(driverExtension, setTimeout, microMetrics, forceGc) =>
|
||||
new PerflogMetric(driverExtension, setTimeout, microMetrics, forceGc),
|
||||
[WebDriverExtension, _SET_TIMEOUT, Options.MICRO_METRICS, Options.FORCE_GC]
|
||||
),
|
||||
bind(_SET_TIMEOUT).toValue( (fn, millis) => TimerWrapper.setTimeout(fn, millis) )
|
||||
bind(PerflogMetric)
|
||||
.toFactory((driverExtension, setTimeout, microMetrics, forceGc) =>
|
||||
new PerflogMetric(driverExtension, setTimeout, microMetrics, forceGc),
|
||||
[WebDriverExtension, _SET_TIMEOUT, Options.MICRO_METRICS, Options.FORCE_GC]),
|
||||
bind(_SET_TIMEOUT).toValue((fn, millis) => TimerWrapper.setTimeout(fn, millis))
|
||||
];
|
|
@ -1,31 +0,0 @@
|
|||
import { bind } from 'angular2/di';
|
||||
import {
|
||||
Promise, PromiseWrapper
|
||||
} from 'angular2/src/facade/async';
|
||||
import {
|
||||
ABSTRACT, BaseException
|
||||
} from 'angular2/src/facade/lang';
|
||||
|
||||
import { MeasureValues } from './measure_values';
|
||||
|
||||
/**
|
||||
* A reporter reports measure values and the valid sample.
|
||||
*/
|
||||
@ABSTRACT()
|
||||
export class Reporter {
|
||||
static bindTo(delegateToken) {
|
||||
return [
|
||||
bind(Reporter).toFactory(
|
||||
(delegate) => delegate, [delegateToken]
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
reportMeasureValues(values:MeasureValues):Promise {
|
||||
throw new BaseException('NYI');
|
||||
}
|
||||
|
||||
reportSample(completeSample:List<MeasureValues>, validSample:List<MeasureValues>):Promise {
|
||||
throw new BaseException('NYI');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
import {bind} from 'angular2/di';
|
||||
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
||||
import {ABSTRACT, BaseException} from 'angular2/src/facade/lang';
|
||||
import {MeasureValues} from './measure_values';
|
||||
import {List} from 'angular2/src/facade/collection';
|
||||
|
||||
/**
|
||||
* A reporter reports measure values and the valid sample.
|
||||
*/
|
||||
@ABSTRACT()
|
||||
export class Reporter {
|
||||
static bindTo(delegateToken) {
|
||||
return [bind(Reporter).toFactory((delegate) => delegate, [delegateToken])];
|
||||
}
|
||||
|
||||
reportMeasureValues(values: MeasureValues): Promise<any> { throw new BaseException('NYI'); }
|
||||
|
||||
reportSample(completeSample: List<MeasureValues>,
|
||||
validSample: List<MeasureValues>): Promise<any> {
|
||||
throw new BaseException('NYI');
|
||||
}
|
||||
}
|
|
@ -1,115 +0,0 @@
|
|||
import { print, isPresent, isBlank, NumberWrapper } from 'angular2/src/facade/lang';
|
||||
import { StringMapWrapper, ListWrapper, List } from 'angular2/src/facade/collection';
|
||||
import { Promise, PromiseWrapper } from 'angular2/src/facade/async';
|
||||
import { Math } from 'angular2/src/facade/math';
|
||||
import { bind, OpaqueToken } from 'angular2/di';
|
||||
|
||||
import { Statistic } from '../statistic';
|
||||
import { Reporter } from '../reporter';
|
||||
import { SampleDescription } from '../sample_description';
|
||||
import { MeasureValues } from '../measure_values';
|
||||
|
||||
/**
|
||||
* A reporter for the console
|
||||
*/
|
||||
export class ConsoleReporter extends Reporter {
|
||||
// TODO(tbosch): use static values when our transpiler supports them
|
||||
static get PRINT() { return _PRINT; }
|
||||
// TODO(tbosch): use static values when our transpiler supports them
|
||||
static get COLUMN_WIDTH() { return _COLUMN_WIDTH; }
|
||||
// TODO(tbosch): use static values when our transpiler supports them
|
||||
static get BINDINGS() { return _BINDINGS; }
|
||||
|
||||
static _lpad(value, columnWidth, fill = ' ') {
|
||||
var result = '';
|
||||
for (var i=0; i<columnWidth - value.length; i++) {
|
||||
result += fill;
|
||||
}
|
||||
return result + value;
|
||||
}
|
||||
|
||||
static _formatNum(n) {
|
||||
return NumberWrapper.toFixed(n, 2);
|
||||
}
|
||||
|
||||
static _sortedProps(obj) {
|
||||
var props = [];
|
||||
StringMapWrapper.forEach(obj, (value, prop) => ListWrapper.push(props, prop));
|
||||
props.sort();
|
||||
return props;
|
||||
}
|
||||
|
||||
_columnWidth:number;
|
||||
_metricNames:List;
|
||||
_print:Function;
|
||||
|
||||
constructor(columnWidth, sampleDescription, print) {
|
||||
super();
|
||||
this._columnWidth = columnWidth;
|
||||
this._metricNames = ConsoleReporter._sortedProps(sampleDescription.metrics);
|
||||
this._print = print;
|
||||
this._printDescription(sampleDescription);
|
||||
}
|
||||
|
||||
_printDescription(sampleDescription) {
|
||||
this._print(`BENCHMARK ${sampleDescription.id}`);
|
||||
this._print('Description:');
|
||||
var props = ConsoleReporter._sortedProps(sampleDescription.description);
|
||||
props.forEach( (prop) => {
|
||||
this._print(`- ${prop}: ${sampleDescription.description[prop]}`);
|
||||
});
|
||||
this._print('Metrics:');
|
||||
this._metricNames.forEach( (metricName) => {
|
||||
this._print(`- ${metricName}: ${sampleDescription.metrics[metricName]}`);
|
||||
});
|
||||
this._print('');
|
||||
this._printStringRow(this._metricNames);
|
||||
this._printStringRow(this._metricNames.map( (_) => '' ), '-');
|
||||
}
|
||||
|
||||
reportMeasureValues(measureValues:MeasureValues):Promise {
|
||||
var formattedValues = ListWrapper.map(this._metricNames, (metricName) => {
|
||||
var value = measureValues.values[metricName];
|
||||
return ConsoleReporter._formatNum(value);
|
||||
});
|
||||
this._printStringRow(formattedValues);
|
||||
return PromiseWrapper.resolve(null);
|
||||
}
|
||||
|
||||
reportSample(completeSample:List<MeasureValues>, validSample:List<MeasureValues>):Promise {
|
||||
this._printStringRow(this._metricNames.map( (_) => '' ), '=');
|
||||
this._printStringRow(
|
||||
ListWrapper.map(this._metricNames, (metricName) => {
|
||||
var sample = ListWrapper.map(validSample, (measureValues) => measureValues.values[metricName]);
|
||||
var mean = Statistic.calculateMean(sample);
|
||||
var cv = Statistic.calculateCoefficientOfVariation(sample, mean);
|
||||
var formattedMean = ConsoleReporter._formatNum(mean)
|
||||
// Note: Don't use the unicode character for +- as it might cause
|
||||
// hickups for consoles...
|
||||
return NumberWrapper.isNaN(cv) ? formattedMean : `${formattedMean}+-${Math.floor(cv)}%`;
|
||||
})
|
||||
);
|
||||
return PromiseWrapper.resolve(null);
|
||||
}
|
||||
|
||||
_printStringRow(parts, fill = ' ') {
|
||||
this._print(
|
||||
ListWrapper.map(parts, (part) => {
|
||||
var w = this._columnWidth;
|
||||
return ConsoleReporter._lpad(part, w, fill);
|
||||
}).join(' | ')
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var _PRINT = new OpaqueToken('ConsoleReporter.print');
|
||||
var _COLUMN_WIDTH = new OpaqueToken('ConsoleReporter.columnWidht');
|
||||
var _BINDINGS = [
|
||||
bind(ConsoleReporter).toFactory(
|
||||
(columnWidth, sampleDescription, print) => new ConsoleReporter(columnWidth, sampleDescription, print),
|
||||
[_COLUMN_WIDTH, SampleDescription, _PRINT]
|
||||
),
|
||||
bind(_COLUMN_WIDTH).toValue(18),
|
||||
bind(_PRINT).toValue(print)
|
||||
];
|
|
@ -0,0 +1,107 @@
|
|||
import {print, isPresent, isBlank, NumberWrapper} from 'angular2/src/facade/lang';
|
||||
import {StringMapWrapper, ListWrapper, List} from 'angular2/src/facade/collection';
|
||||
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
||||
import {Math} from 'angular2/src/facade/math';
|
||||
import {bind, Binding, OpaqueToken} from 'angular2/di';
|
||||
|
||||
import {Statistic} from '../statistic';
|
||||
import {Reporter} from '../reporter';
|
||||
import {SampleDescription} from '../sample_description';
|
||||
import {MeasureValues} from '../measure_values';
|
||||
|
||||
/**
|
||||
* A reporter for the console
|
||||
*/
|
||||
export class ConsoleReporter extends Reporter {
|
||||
// TODO(tbosch): use static values when our transpiler supports them
|
||||
static get PRINT(): OpaqueToken { return _PRINT; }
|
||||
// TODO(tbosch): use static values when our transpiler supports them
|
||||
static get COLUMN_WIDTH(): OpaqueToken { return _COLUMN_WIDTH; }
|
||||
// TODO(tbosch): use static values when our transpiler supports them
|
||||
static get BINDINGS(): List<Binding> { return _BINDINGS; }
|
||||
|
||||
|
||||
static _lpad(value, columnWidth, fill = ' ') {
|
||||
var result = '';
|
||||
for (var i = 0; i < columnWidth - value.length; i++) {
|
||||
result += fill;
|
||||
}
|
||||
return result + value;
|
||||
}
|
||||
|
||||
static _formatNum(n) { return NumberWrapper.toFixed(n, 2); }
|
||||
|
||||
static _sortedProps(obj) {
|
||||
var props = [];
|
||||
StringMapWrapper.forEach(obj, (value, prop) => ListWrapper.push(props, prop));
|
||||
props.sort();
|
||||
return props;
|
||||
}
|
||||
|
||||
private _metricNames: List<string>;
|
||||
|
||||
constructor(private _columnWidth: number, sampleDescription, private _print: Function) {
|
||||
super();
|
||||
this._metricNames = ConsoleReporter._sortedProps(sampleDescription.metrics);
|
||||
this._printDescription(sampleDescription);
|
||||
}
|
||||
|
||||
_printDescription(sampleDescription) {
|
||||
this._print(`BENCHMARK ${sampleDescription.id}`);
|
||||
this._print('Description:');
|
||||
var props = ConsoleReporter._sortedProps(sampleDescription.description);
|
||||
props.forEach((prop) => { this._print(`- ${prop}: ${sampleDescription.description[prop]}`); });
|
||||
this._print('Metrics:');
|
||||
this._metricNames.forEach((metricName) => {
|
||||
this._print(`- ${metricName}: ${sampleDescription.metrics[metricName]}`);
|
||||
});
|
||||
this._print('');
|
||||
this._printStringRow(this._metricNames);
|
||||
this._printStringRow(this._metricNames.map((_) => ''), '-');
|
||||
}
|
||||
|
||||
reportMeasureValues(measureValues: MeasureValues): Promise<any> {
|
||||
var formattedValues = ListWrapper.map(this._metricNames, (metricName) => {
|
||||
var value = measureValues.values[metricName];
|
||||
return ConsoleReporter._formatNum(value);
|
||||
});
|
||||
this._printStringRow(formattedValues);
|
||||
return PromiseWrapper.resolve(null);
|
||||
}
|
||||
|
||||
reportSample(completeSample: List<MeasureValues>,
|
||||
validSample: List<MeasureValues>): Promise<any> {
|
||||
this._printStringRow(this._metricNames.map((_) => ''), '=');
|
||||
this._printStringRow(ListWrapper.map(this._metricNames, (metricName) => {
|
||||
var sample =
|
||||
ListWrapper.map(validSample, (measureValues) => measureValues.values[metricName]);
|
||||
var mean = Statistic.calculateMean(sample);
|
||||
var cv = Statistic.calculateCoefficientOfVariation(sample, mean);
|
||||
var formattedMean = ConsoleReporter._formatNum(mean)
|
||||
// Note: Don't use the unicode character for +- as it might cause
|
||||
// hickups for consoles...
|
||||
return NumberWrapper.isNaN(cv) ?
|
||||
formattedMean :
|
||||
`${formattedMean}+-${Math.floor(cv)}%`;
|
||||
}));
|
||||
return PromiseWrapper.resolve(null);
|
||||
}
|
||||
|
||||
_printStringRow(parts, fill = ' ') {
|
||||
this._print(ListWrapper.map(parts, (part) => {
|
||||
var w = this._columnWidth;
|
||||
return ConsoleReporter._lpad(part, w, fill);
|
||||
}).join(' | '));
|
||||
}
|
||||
}
|
||||
|
||||
var _PRINT = new OpaqueToken('ConsoleReporter.print');
|
||||
var _COLUMN_WIDTH = new OpaqueToken('ConsoleReporter.columnWidth');
|
||||
var _BINDINGS = [
|
||||
bind(ConsoleReporter)
|
||||
.toFactory((columnWidth, sampleDescription, print) =>
|
||||
new ConsoleReporter(columnWidth, sampleDescription, print),
|
||||
[_COLUMN_WIDTH, SampleDescription, _PRINT]),
|
||||
bind(_COLUMN_WIDTH).toValue(18),
|
||||
bind(_PRINT).toValue(print)
|
||||
];
|
|
@ -1,55 +0,0 @@
|
|||
import { DateWrapper, isPresent, isBlank, Json } from 'angular2/src/facade/lang';
|
||||
import { List } from 'angular2/src/facade/collection';
|
||||
import { Promise, PromiseWrapper } from 'angular2/src/facade/async';
|
||||
|
||||
import { bind, OpaqueToken } from 'angular2/di';
|
||||
|
||||
import { Reporter } from '../reporter';
|
||||
import { SampleDescription } from '../sample_description';
|
||||
import { MeasureValues } from '../measure_values';
|
||||
import { Options } from '../common_options';
|
||||
|
||||
/**
|
||||
* A reporter that writes results into a json file.
|
||||
*/
|
||||
export class JsonFileReporter extends Reporter {
|
||||
// TODO(tbosch): use static values when our transpiler supports them
|
||||
static get PATH() { return _PATH; }
|
||||
static get BINDINGS() { return _BINDINGS; }
|
||||
|
||||
_writeFile:Function;
|
||||
_path:string;
|
||||
_description:SampleDescription;
|
||||
_now:Function;
|
||||
|
||||
constructor(sampleDescription, path, writeFile, now) {
|
||||
super();
|
||||
this._description = sampleDescription;
|
||||
this._path = path;
|
||||
this._writeFile = writeFile;
|
||||
this._now = now;
|
||||
}
|
||||
|
||||
reportMeasureValues(measureValues:MeasureValues):Promise {
|
||||
return PromiseWrapper.resolve(null);
|
||||
}
|
||||
|
||||
reportSample(completeSample:List<MeasureValues>, validSample:List<MeasureValues>):Promise {
|
||||
var content = Json.stringify({
|
||||
'description': this._description,
|
||||
'completeSample': completeSample,
|
||||
'validSample': validSample
|
||||
});
|
||||
var filePath = `${this._path}/${this._description.id}_${DateWrapper.toMillis(this._now())}.json`;
|
||||
return this._writeFile(filePath, content);
|
||||
}
|
||||
}
|
||||
|
||||
var _PATH = new OpaqueToken('JsonFileReporter.path');
|
||||
var _BINDINGS = [
|
||||
bind(JsonFileReporter).toFactory(
|
||||
(sampleDescription, path, writeFile, now) => new JsonFileReporter(sampleDescription, path, writeFile, now),
|
||||
[SampleDescription, _PATH, Options.WRITE_FILE, Options.NOW]
|
||||
),
|
||||
bind(_PATH).toValue('.')
|
||||
];
|
|
@ -0,0 +1,58 @@
|
|||
import {DateWrapper, isPresent, isBlank, Json} from 'angular2/src/facade/lang';
|
||||
import {List} from 'angular2/src/facade/collection';
|
||||
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
||||
|
||||
import {bind, Binding, OpaqueToken} from 'angular2/di';
|
||||
|
||||
import {Reporter} from '../reporter';
|
||||
import {SampleDescription} from '../sample_description';
|
||||
import {MeasureValues} from '../measure_values';
|
||||
import {Options} from '../common_options';
|
||||
|
||||
/**
|
||||
* A reporter that writes results into a json file.
|
||||
*/
|
||||
export class JsonFileReporter extends Reporter {
|
||||
// TODO(tbosch): use static values when our transpiler supports them
|
||||
static get PATH(): OpaqueToken { return _PATH; }
|
||||
// TODO(tbosch): use static values when our transpiler supports them
|
||||
static get BINDINGS(): List<Binding> { return _BINDINGS; }
|
||||
|
||||
_writeFile: Function;
|
||||
_path: string;
|
||||
_description: SampleDescription;
|
||||
_now: Function;
|
||||
|
||||
constructor(sampleDescription, path, writeFile, now) {
|
||||
super();
|
||||
this._description = sampleDescription;
|
||||
this._path = path;
|
||||
this._writeFile = writeFile;
|
||||
this._now = now;
|
||||
}
|
||||
|
||||
reportMeasureValues(measureValues: MeasureValues): Promise<any> {
|
||||
return PromiseWrapper.resolve(null);
|
||||
}
|
||||
|
||||
reportSample(completeSample: List<MeasureValues>,
|
||||
validSample: List<MeasureValues>): Promise<any> {
|
||||
var content = Json.stringify({
|
||||
'description': this._description,
|
||||
'completeSample': completeSample,
|
||||
'validSample': validSample
|
||||
});
|
||||
var filePath =
|
||||
`${this._path}/${this._description.id}_${DateWrapper.toMillis(this._now())}.json`;
|
||||
return this._writeFile(filePath, content);
|
||||
}
|
||||
}
|
||||
|
||||
var _PATH = new OpaqueToken('JsonFileReporter.path');
|
||||
var _BINDINGS = [
|
||||
bind(JsonFileReporter)
|
||||
.toFactory((sampleDescription, path, writeFile, now) =>
|
||||
new JsonFileReporter(sampleDescription, path, writeFile, now),
|
||||
[SampleDescription, _PATH, Options.WRITE_FILE, Options.NOW]),
|
||||
bind(_PATH).toValue('.')
|
||||
];
|
|
@ -1,42 +0,0 @@
|
|||
import { bind, Injector, OpaqueToken } from 'angular2/di';
|
||||
import { List, ListWrapper } from 'angular2/src/facade/collection';
|
||||
import { Promise, PromiseWrapper } from 'angular2/src/facade/async';
|
||||
|
||||
import { MeasureValues } from '../measure_values';
|
||||
import { Reporter } from '../reporter';
|
||||
|
||||
export class MultiReporter extends Reporter {
|
||||
static createBindings(childTokens) {
|
||||
return [
|
||||
bind(_CHILDREN).toAsyncFactory(
|
||||
(injector) => PromiseWrapper.all(ListWrapper.map(childTokens, (token) => injector.asyncGet(token) )),
|
||||
[Injector]
|
||||
),
|
||||
bind(MultiReporter).toFactory(
|
||||
(children) => new MultiReporter(children),
|
||||
[_CHILDREN]
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
_reporters:List;
|
||||
|
||||
constructor(reporters) {
|
||||
super();
|
||||
this._reporters = reporters;
|
||||
}
|
||||
|
||||
reportMeasureValues(values:MeasureValues):Promise {
|
||||
return PromiseWrapper.all(ListWrapper.map(
|
||||
this._reporters, (reporter) => reporter.reportMeasureValues(values)
|
||||
));
|
||||
}
|
||||
|
||||
reportSample(completeSample:List<MeasureValues>, validSample:List<MeasureValues>):Promise {
|
||||
return PromiseWrapper.all(ListWrapper.map(
|
||||
this._reporters, (reporter) => reporter.reportSample(completeSample, validSample)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
var _CHILDREN = new OpaqueToken('MultiReporter.children');
|
|
@ -0,0 +1,38 @@
|
|||
import {bind, Binding, Injector, OpaqueToken} from 'angular2/di';
|
||||
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
||||
|
||||
import {MeasureValues} from '../measure_values';
|
||||
import {Reporter} from '../reporter';
|
||||
|
||||
export class MultiReporter extends Reporter {
|
||||
static createBindings(childTokens: List<any>): List<Binding> {
|
||||
return [
|
||||
bind(_CHILDREN)
|
||||
.toAsyncFactory((injector) => PromiseWrapper.all(
|
||||
ListWrapper.map(childTokens, (token) => injector.asyncGet(token))),
|
||||
[Injector]),
|
||||
bind(MultiReporter).toFactory((children) => new MultiReporter(children), [_CHILDREN])
|
||||
];
|
||||
}
|
||||
|
||||
_reporters: List<Reporter>;
|
||||
|
||||
constructor(reporters) {
|
||||
super();
|
||||
this._reporters = reporters;
|
||||
}
|
||||
|
||||
reportMeasureValues(values: MeasureValues): Promise<List<any>> {
|
||||
return PromiseWrapper.all(
|
||||
ListWrapper.map(this._reporters, (reporter) => reporter.reportMeasureValues(values)));
|
||||
}
|
||||
|
||||
reportSample(completeSample: List<MeasureValues>,
|
||||
validSample: List<MeasureValues>): Promise<List<any>> {
|
||||
return PromiseWrapper.all(ListWrapper.map(
|
||||
this._reporters, (reporter) => reporter.reportSample(completeSample, validSample)));
|
||||
}
|
||||
}
|
||||
|
||||
var _CHILDREN = new OpaqueToken('MultiReporter.children');
|
|
@ -1,82 +0,0 @@
|
|||
import { Injector, bind } from 'angular2/di';
|
||||
import { isPresent, isBlank } from 'angular2/src/facade/lang';
|
||||
import { List, ListWrapper } from 'angular2/src/facade/collection';
|
||||
import { Promise } from 'angular2/src/facade/async';
|
||||
|
||||
import { Sampler, SampleState } from './sampler';
|
||||
import { ConsoleReporter } from './reporter/console_reporter';
|
||||
import { MultiReporter } from './reporter/multi_reporter';
|
||||
import { RegressionSlopeValidator } from './validator/regression_slope_validator';
|
||||
import { SizeValidator } from './validator/size_validator';
|
||||
import { Validator } from './validator';
|
||||
import { PerflogMetric } from './metric/perflog_metric';
|
||||
import { MultiMetric } from './metric/multi_metric';
|
||||
import { ChromeDriverExtension } from './webdriver/chrome_driver_extension';
|
||||
import { IOsDriverExtension } from './webdriver/ios_driver_extension';
|
||||
import { WebDriverExtension } from './web_driver_extension';
|
||||
import { SampleDescription } from './sample_description';
|
||||
import { WebDriverAdapter } from './web_driver_adapter';
|
||||
import { Reporter } from './reporter';
|
||||
import { Metric } from './metric';
|
||||
import { Options } from './common_options';
|
||||
|
||||
/**
|
||||
* The Runner is the main entry point for executing a sample run.
|
||||
* It provides defaults, creates the injector and calls the sampler.
|
||||
*/
|
||||
export class Runner {
|
||||
_defaultBindings:List;
|
||||
|
||||
constructor(defaultBindings:List = null) {
|
||||
if (isBlank(defaultBindings)) {
|
||||
defaultBindings = [];
|
||||
}
|
||||
this._defaultBindings = defaultBindings;
|
||||
}
|
||||
|
||||
sample({id, execute, prepare, microMetrics, bindings}):Promise<SampleState> {
|
||||
var sampleBindings = [
|
||||
_DEFAULT_BINDINGS,
|
||||
this._defaultBindings,
|
||||
bind(Options.SAMPLE_ID).toValue(id),
|
||||
bind(Options.EXECUTE).toValue(execute)
|
||||
];
|
||||
if (isPresent(prepare)) {
|
||||
ListWrapper.push(sampleBindings, bind(Options.PREPARE).toValue(prepare));
|
||||
}
|
||||
if (isPresent(microMetrics)) {
|
||||
ListWrapper.push(sampleBindings, bind(Options.MICRO_METRICS).toValue(microMetrics));
|
||||
}
|
||||
if (isPresent(bindings)) {
|
||||
ListWrapper.push(sampleBindings, bindings);
|
||||
}
|
||||
return Injector.resolveAndCreate(sampleBindings).asyncGet(Sampler)
|
||||
.then( (sampler) => sampler.sample() );
|
||||
}
|
||||
}
|
||||
|
||||
var _DEFAULT_BINDINGS = [
|
||||
Options.DEFAULT_BINDINGS,
|
||||
Sampler.BINDINGS,
|
||||
ConsoleReporter.BINDINGS,
|
||||
RegressionSlopeValidator.BINDINGS,
|
||||
SizeValidator.BINDINGS,
|
||||
ChromeDriverExtension.BINDINGS,
|
||||
IOsDriverExtension.BINDINGS,
|
||||
PerflogMetric.BINDINGS,
|
||||
SampleDescription.BINDINGS,
|
||||
MultiReporter.createBindings([ConsoleReporter]),
|
||||
MultiMetric.createBindings([PerflogMetric]),
|
||||
|
||||
Reporter.bindTo(MultiReporter),
|
||||
Validator.bindTo(RegressionSlopeValidator),
|
||||
WebDriverExtension.bindTo([ChromeDriverExtension, IOsDriverExtension]),
|
||||
Metric.bindTo(MultiMetric),
|
||||
|
||||
bind(Options.CAPABILITIES).toAsyncFactory(
|
||||
(adapter) => adapter.capabilities(), [WebDriverAdapter]
|
||||
),
|
||||
bind(Options.USER_AGENT).toAsyncFactory(
|
||||
(adapter) => adapter.executeScript('return window.navigator.userAgent;'), [WebDriverAdapter]
|
||||
)
|
||||
];
|
|
@ -0,0 +1,81 @@
|
|||
import {Injector, bind, Binding} from 'angular2/di';
|
||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {Promise} from 'angular2/src/facade/async';
|
||||
|
||||
import {Sampler, SampleState} from './sampler';
|
||||
import {ConsoleReporter} from './reporter/console_reporter';
|
||||
import {MultiReporter} from './reporter/multi_reporter';
|
||||
import {RegressionSlopeValidator} from './validator/regression_slope_validator';
|
||||
import {SizeValidator} from './validator/size_validator';
|
||||
import {Validator} from './validator';
|
||||
import {PerflogMetric} from './metric/perflog_metric';
|
||||
import {MultiMetric} from './metric/multi_metric';
|
||||
import {ChromeDriverExtension} from './webdriver/chrome_driver_extension';
|
||||
import {IOsDriverExtension} from './webdriver/ios_driver_extension';
|
||||
import {WebDriverExtension} from './web_driver_extension';
|
||||
import {SampleDescription} from './sample_description';
|
||||
import {WebDriverAdapter} from './web_driver_adapter';
|
||||
import {Reporter} from './reporter';
|
||||
import {Metric} from './metric';
|
||||
import {Options} from './common_options';
|
||||
|
||||
/**
|
||||
* The Runner is the main entry point for executing a sample run.
|
||||
* It provides defaults, creates the injector and calls the sampler.
|
||||
*/
|
||||
export class Runner {
|
||||
private _defaultBindings: List<Binding>;
|
||||
constructor(defaultBindings: List<Binding> = null) {
|
||||
if (isBlank(defaultBindings)) {
|
||||
defaultBindings = [];
|
||||
}
|
||||
this._defaultBindings = defaultBindings;
|
||||
}
|
||||
|
||||
sample({id, execute, prepare, microMetrics, bindings}): Promise<SampleState> {
|
||||
var sampleBindings = [
|
||||
_DEFAULT_BINDINGS,
|
||||
this._defaultBindings,
|
||||
bind(Options.SAMPLE_ID).toValue(id),
|
||||
bind(Options.EXECUTE).toValue(execute)
|
||||
];
|
||||
if (isPresent(prepare)) {
|
||||
ListWrapper.push(sampleBindings, bind(Options.PREPARE).toValue(prepare));
|
||||
}
|
||||
if (isPresent(microMetrics)) {
|
||||
ListWrapper.push(sampleBindings, bind(Options.MICRO_METRICS).toValue(microMetrics));
|
||||
}
|
||||
if (isPresent(bindings)) {
|
||||
ListWrapper.push(sampleBindings, bindings);
|
||||
}
|
||||
return Injector.resolveAndCreate(sampleBindings)
|
||||
.asyncGet(Sampler)
|
||||
.then((sampler) => sampler.sample());
|
||||
}
|
||||
}
|
||||
|
||||
var _DEFAULT_BINDINGS = [
|
||||
Options.DEFAULT_BINDINGS,
|
||||
Sampler.BINDINGS,
|
||||
ConsoleReporter.BINDINGS,
|
||||
RegressionSlopeValidator.BINDINGS,
|
||||
SizeValidator.BINDINGS,
|
||||
ChromeDriverExtension.BINDINGS,
|
||||
IOsDriverExtension.BINDINGS,
|
||||
PerflogMetric.BINDINGS,
|
||||
SampleDescription.BINDINGS,
|
||||
MultiReporter.createBindings([ConsoleReporter]),
|
||||
MultiMetric.createBindings([PerflogMetric]),
|
||||
|
||||
Reporter.bindTo(MultiReporter),
|
||||
Validator.bindTo(RegressionSlopeValidator),
|
||||
WebDriverExtension.bindTo([ChromeDriverExtension, IOsDriverExtension]),
|
||||
Metric.bindTo(MultiMetric),
|
||||
|
||||
bind(Options.CAPABILITIES)
|
||||
.toAsyncFactory((adapter) => adapter.capabilities(), [WebDriverAdapter]),
|
||||
bind(Options.USER_AGENT)
|
||||
.toAsyncFactory((adapter) => adapter.executeScript('return window.navigator.userAgent;'),
|
||||
[WebDriverAdapter])
|
||||
];
|
|
@ -1,51 +0,0 @@
|
|||
import { StringMapWrapper, ListWrapper, StringMap } from 'angular2/src/facade/collection';
|
||||
import { bind, OpaqueToken } from 'angular2/di';
|
||||
import { Validator } from './validator';
|
||||
import { Metric } from './metric';
|
||||
import { Options } from './common_options';
|
||||
|
||||
/**
|
||||
* SampleDescription merges all available descriptions about a sample
|
||||
*/
|
||||
export class SampleDescription {
|
||||
// TODO(tbosch): use static values when our transpiler supports them
|
||||
static get BINDINGS() { return _BINDINGS; }
|
||||
|
||||
id:string;
|
||||
description:StringMap;
|
||||
metrics:StringMap;
|
||||
|
||||
constructor(id, descriptions:List<StringMap>, metrics:StringMap) {
|
||||
this.id = id;
|
||||
this.metrics = metrics;
|
||||
this.description = {};
|
||||
ListWrapper.forEach(descriptions, (description) => {
|
||||
StringMapWrapper.forEach(description, (value, prop) => this.description[prop] = value );
|
||||
});
|
||||
}
|
||||
|
||||
toJson() {
|
||||
return {
|
||||
'id': this.id,
|
||||
'description': this.description,
|
||||
'metrics': this.metrics
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
var _BINDINGS = [
|
||||
bind(SampleDescription).toFactory(
|
||||
(metric, id, forceGc, userAgent, validator, defaultDesc, userDesc) => new SampleDescription(id,
|
||||
[
|
||||
{'forceGc': forceGc, 'userAgent': userAgent},
|
||||
validator.describe(),
|
||||
defaultDesc,
|
||||
userDesc
|
||||
],
|
||||
metric.describe()),
|
||||
[
|
||||
Metric, Options.SAMPLE_ID, Options.FORCE_GC, Options.USER_AGENT,
|
||||
Validator, Options.DEFAULT_DESCRIPTION, Options.SAMPLE_DESCRIPTION
|
||||
]
|
||||
)
|
||||
];
|
|
@ -0,0 +1,44 @@
|
|||
import {StringMapWrapper, ListWrapper, List, StringMap} from 'angular2/src/facade/collection';
|
||||
import {bind, Binding, OpaqueToken} from 'angular2/di';
|
||||
import {Validator} from './validator';
|
||||
import {Metric} from './metric';
|
||||
import {Options} from './common_options';
|
||||
|
||||
/**
|
||||
* SampleDescription merges all available descriptions about a sample
|
||||
*/
|
||||
export class SampleDescription {
|
||||
// TODO(tbosch): use static values when our transpiler supports them
|
||||
static get BINDINGS(): List<Binding> { return _BINDINGS; }
|
||||
description: StringMap<string, any>;
|
||||
|
||||
constructor(public id: string, descriptions: List<StringMap<string, any>>,
|
||||
public metrics: StringMap<string, any>) {
|
||||
this.description = {};
|
||||
ListWrapper.forEach(descriptions, (description) => {
|
||||
StringMapWrapper.forEach(description, (value, prop) => this.description[prop] = value);
|
||||
});
|
||||
}
|
||||
|
||||
toJson() { return {'id': this.id, 'description': this.description, 'metrics': this.metrics}; }
|
||||
}
|
||||
|
||||
var _BINDINGS =
|
||||
[bind(SampleDescription)
|
||||
.toFactory((metric, id, forceGc, userAgent, validator, defaultDesc, userDesc) =>
|
||||
new SampleDescription(id, [
|
||||
{'forceGc': forceGc, 'userAgent': userAgent},
|
||||
validator.describe(),
|
||||
defaultDesc,
|
||||
userDesc
|
||||
],
|
||||
metric.describe()),
|
||||
[
|
||||
Metric,
|
||||
Options.SAMPLE_ID,
|
||||
Options.FORCE_GC,
|
||||
Options.USER_AGENT,
|
||||
Validator,
|
||||
Options.DEFAULT_DESCRIPTION,
|
||||
Options.SAMPLE_DESCRIPTION
|
||||
])];
|
|
@ -1,123 +0,0 @@
|
|||
import { isPresent, isBlank, Date, DateWrapper } from 'angular2/src/facade/lang';
|
||||
import { Promise, PromiseWrapper } from 'angular2/src/facade/async';
|
||||
import { StringMapWrapper, StringMap, List, ListWrapper } from 'angular2/src/facade/collection';
|
||||
import { bind, OpaqueToken } from 'angular2/di';
|
||||
|
||||
import { Metric } from './metric';
|
||||
import { Validator } from './validator';
|
||||
import { Reporter } from './reporter';
|
||||
import { WebDriverAdapter } from './web_driver_adapter';
|
||||
|
||||
import { Options } from './common_options';
|
||||
import { MeasureValues} from './measure_values';
|
||||
|
||||
/**
|
||||
* The Sampler owns the sample loop:
|
||||
* 1. calls the prepare/execute callbacks,
|
||||
* 2. gets data from the metric
|
||||
* 3. asks the validator for a valid sample
|
||||
* 4. reports the new data to the reporter
|
||||
* 5. loop until there is a valid sample
|
||||
*/
|
||||
export class Sampler {
|
||||
// TODO(tbosch): use static values when our transpiler supports them
|
||||
static get BINDINGS() { return _BINDINGS; }
|
||||
|
||||
_driver:WebDriverAdapter;
|
||||
_metric:Metric;
|
||||
_reporter:Reporter;
|
||||
_validator:Validator;
|
||||
_prepare:Function;
|
||||
_execute:Function;
|
||||
_now:Function;
|
||||
|
||||
constructor({
|
||||
driver, metric, reporter, validator, prepare, execute, now
|
||||
}:{
|
||||
driver: WebDriverAdapter,
|
||||
metric: Metric, reporter: Reporter,
|
||||
validator: Validator, prepare: Function, execute: Function, now: Function
|
||||
}={}) {
|
||||
this._driver = driver;
|
||||
this._metric = metric;
|
||||
this._reporter = reporter;
|
||||
this._validator = validator;
|
||||
this._prepare = prepare;
|
||||
this._execute = execute;
|
||||
this._now = now;
|
||||
}
|
||||
|
||||
sample():Promise<SampleState> {
|
||||
var loop;
|
||||
loop = (lastState) => {
|
||||
return this._iterate(lastState)
|
||||
.then( (newState) => {
|
||||
if (isPresent(newState.validSample)) {
|
||||
return newState;
|
||||
} else {
|
||||
return loop(newState);
|
||||
}
|
||||
});
|
||||
}
|
||||
return loop(new SampleState([], null));
|
||||
}
|
||||
|
||||
_iterate(lastState) {
|
||||
var resultPromise;
|
||||
if (isPresent(this._prepare)) {
|
||||
resultPromise = this._driver.waitFor(this._prepare);
|
||||
} else {
|
||||
resultPromise = PromiseWrapper.resolve(null);
|
||||
}
|
||||
if (isPresent(this._prepare) || lastState.completeSample.length === 0) {
|
||||
resultPromise = resultPromise.then( (_) => this._metric.beginMeasure() );
|
||||
}
|
||||
return resultPromise
|
||||
.then( (_) => this._driver.waitFor(this._execute) )
|
||||
.then( (_) => this._metric.endMeasure(isBlank(this._prepare)) )
|
||||
.then( (measureValues) => this._report(lastState, measureValues) );
|
||||
}
|
||||
|
||||
_report(state:SampleState, metricValues:StringMap):Promise<SampleState> {
|
||||
var measureValues = new MeasureValues(state.completeSample.length, this._now(), metricValues);
|
||||
var completeSample = ListWrapper.concat(state.completeSample, [measureValues]);
|
||||
var validSample = this._validator.validate(completeSample);
|
||||
var resultPromise = this._reporter.reportMeasureValues(measureValues);
|
||||
if (isPresent(validSample)) {
|
||||
resultPromise = resultPromise.then( (_) => this._reporter.reportSample(completeSample, validSample) )
|
||||
}
|
||||
return resultPromise.then( (_) => new SampleState(completeSample, validSample) );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class SampleState {
|
||||
completeSample:List;
|
||||
validSample:List;
|
||||
|
||||
constructor(completeSample: List, validSample: List) {
|
||||
this.completeSample = completeSample;
|
||||
this.validSample = validSample;
|
||||
}
|
||||
}
|
||||
|
||||
var _BINDINGS = [
|
||||
bind(Sampler).toFactory(
|
||||
(driver, metric, reporter, validator, prepare, execute, now) => new Sampler({
|
||||
driver: driver,
|
||||
reporter: reporter,
|
||||
validator: validator,
|
||||
metric: metric,
|
||||
// TODO(tbosch): DI right now does not support null/undefined objects
|
||||
// Mostly because the cache would have to be initialized with a
|
||||
// special null object, which is expensive.
|
||||
prepare: prepare !== false ? prepare : null,
|
||||
execute: execute,
|
||||
now: now
|
||||
}),
|
||||
[
|
||||
WebDriverAdapter, Metric, Reporter, Validator,
|
||||
Options.PREPARE, Options.EXECUTE, Options.NOW
|
||||
]
|
||||
)
|
||||
];
|
|
@ -0,0 +1,120 @@
|
|||
import {isPresent, isBlank, Date, DateWrapper} from 'angular2/src/facade/lang';
|
||||
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
||||
import {StringMapWrapper, StringMap, List, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {bind, Binding, OpaqueToken} from 'angular2/di';
|
||||
|
||||
import {Metric} from './metric';
|
||||
import {Validator} from './validator';
|
||||
import {Reporter} from './reporter';
|
||||
import {WebDriverAdapter} from './web_driver_adapter';
|
||||
|
||||
import {Options} from './common_options';
|
||||
import {MeasureValues} from './measure_values';
|
||||
|
||||
/**
|
||||
* The Sampler owns the sample loop:
|
||||
* 1. calls the prepare/execute callbacks,
|
||||
* 2. gets data from the metric
|
||||
* 3. asks the validator for a valid sample
|
||||
* 4. reports the new data to the reporter
|
||||
* 5. loop until there is a valid sample
|
||||
*/
|
||||
export class Sampler {
|
||||
// TODO(tbosch): use static values when our transpiler supports them
|
||||
static get BINDINGS(): List<Binding> { return _BINDINGS; }
|
||||
|
||||
_driver: WebDriverAdapter;
|
||||
_metric: Metric;
|
||||
_reporter: Reporter;
|
||||
_validator: Validator;
|
||||
_prepare: Function;
|
||||
_execute: Function;
|
||||
_now: Function;
|
||||
|
||||
constructor({driver, metric, reporter, validator, prepare, execute, now}: {
|
||||
driver?: WebDriverAdapter,
|
||||
metric?: Metric,
|
||||
reporter?: Reporter,
|
||||
validator?: Validator,
|
||||
prepare?: Function,
|
||||
execute?: Function,
|
||||
now?: Function
|
||||
} = {}) {
|
||||
this._driver = driver;
|
||||
this._metric = metric;
|
||||
this._reporter = reporter;
|
||||
this._validator = validator;
|
||||
this._prepare = prepare;
|
||||
this._execute = execute;
|
||||
this._now = now;
|
||||
}
|
||||
|
||||
sample(): Promise<SampleState> {
|
||||
var loop;
|
||||
loop = (lastState) => {
|
||||
return this._iterate(lastState).then((newState) => {
|
||||
if (isPresent(newState.validSample)) {
|
||||
return newState;
|
||||
} else {
|
||||
return loop(newState);
|
||||
}
|
||||
});
|
||||
};
|
||||
return loop(new SampleState([], null));
|
||||
}
|
||||
|
||||
_iterate(lastState) {
|
||||
var resultPromise;
|
||||
if (isPresent(this._prepare)) {
|
||||
resultPromise = this._driver.waitFor(this._prepare);
|
||||
} else {
|
||||
resultPromise = PromiseWrapper.resolve(null);
|
||||
}
|
||||
if (isPresent(this._prepare) || lastState.completeSample.length === 0) {
|
||||
resultPromise = resultPromise.then((_) => this._metric.beginMeasure());
|
||||
}
|
||||
return resultPromise.then((_) => this._driver.waitFor(this._execute))
|
||||
.then((_) => this._metric.endMeasure(isBlank(this._prepare)))
|
||||
.then((measureValues) => this._report(lastState, measureValues));
|
||||
}
|
||||
|
||||
_report(state: SampleState, metricValues: StringMap<string, any>): Promise<SampleState> {
|
||||
var measureValues = new MeasureValues(state.completeSample.length, this._now(), metricValues);
|
||||
var completeSample = ListWrapper.concat(state.completeSample, [measureValues]);
|
||||
var validSample = this._validator.validate(completeSample);
|
||||
var resultPromise = this._reporter.reportMeasureValues(measureValues);
|
||||
if (isPresent(validSample)) {
|
||||
resultPromise =
|
||||
resultPromise.then((_) => this._reporter.reportSample(completeSample, validSample))
|
||||
}
|
||||
return resultPromise.then((_) => new SampleState(completeSample, validSample));
|
||||
}
|
||||
}
|
||||
|
||||
export class SampleState {
|
||||
constructor(public completeSample: List<any>, public validSample: List<any>) {}
|
||||
}
|
||||
|
||||
var _BINDINGS =
|
||||
[bind(Sampler)
|
||||
.toFactory((driver, metric, reporter, validator, prepare, execute, now) => new Sampler({
|
||||
driver: driver,
|
||||
reporter: reporter,
|
||||
validator: validator,
|
||||
metric: metric,
|
||||
// TODO(tbosch): DI right now does not support null/undefined objects
|
||||
// Mostly because the cache would have to be initialized with a
|
||||
// special null object, which is expensive.
|
||||
prepare: prepare !== false ? prepare : null,
|
||||
execute: execute,
|
||||
now: now
|
||||
}),
|
||||
[
|
||||
WebDriverAdapter,
|
||||
Metric,
|
||||
Reporter,
|
||||
Validator,
|
||||
Options.PREPARE,
|
||||
Options.EXECUTE,
|
||||
Options.NOW
|
||||
])];
|
|
@ -1,5 +1,5 @@
|
|||
import { Math } from 'angular2/src/facade/math';
|
||||
import { ListWrapper } from 'angular2/src/facade/collection';
|
||||
import {Math} from 'angular2/src/facade/math';
|
||||
import {ListWrapper, List} from 'angular2/src/facade/collection';
|
||||
|
||||
export class Statistic {
|
||||
static calculateCoefficientOfVariation(sample, mean) {
|
||||
|
@ -8,30 +8,27 @@ export class Statistic {
|
|||
|
||||
static calculateMean(sample) {
|
||||
var total = 0;
|
||||
ListWrapper.forEach(sample, (x) => { total += x } );
|
||||
ListWrapper.forEach(sample, (x) => {total += x});
|
||||
return total / sample.length;
|
||||
}
|
||||
|
||||
static calculateStandardDeviation(sample, mean) {
|
||||
var deviation = 0;
|
||||
ListWrapper.forEach(sample, (x) => {
|
||||
deviation += Math.pow(x - mean, 2);
|
||||
});
|
||||
ListWrapper.forEach(sample, (x) => { deviation += Math.pow(x - mean, 2); });
|
||||
deviation = deviation / (sample.length);
|
||||
deviation = Math.sqrt(deviation);
|
||||
return deviation;
|
||||
}
|
||||
|
||||
static calculateRegressionSlope(xValues, xMean, yValues, yMean) {
|
||||
static calculateRegressionSlope(xValues: List<number>, xMean: number, yValues: List<number>,
|
||||
yMean: number) {
|
||||
// See http://en.wikipedia.org/wiki/Simple_linear_regression
|
||||
var dividendSum = 0;
|
||||
var divisorSum = 0;
|
||||
for (var i=0; i<xValues.length; i++) {
|
||||
for (var i = 0; i < xValues.length; i++) {
|
||||
dividendSum += (xValues[i] - xMean) * (yValues[i] - yMean);
|
||||
divisorSum += Math.pow(xValues[i] - xMean, 2);
|
||||
}
|
||||
return dividendSum / divisorSum;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,10 +1,8 @@
|
|||
import { bind } from 'angular2/di';
|
||||
import { List, StringMap } from 'angular2/src/facade/collection';
|
||||
import {
|
||||
ABSTRACT, BaseException
|
||||
} from 'angular2/src/facade/lang';
|
||||
import {bind} from 'angular2/di';
|
||||
import {List, StringMap} from 'angular2/src/facade/collection';
|
||||
import {ABSTRACT, BaseException} from 'angular2/src/facade/lang';
|
||||
|
||||
import { MeasureValues } from './measure_values';
|
||||
import {MeasureValues} from './measure_values';
|
||||
|
||||
/**
|
||||
* A Validator calculates a valid sample out of the complete sample.
|
||||
|
@ -14,17 +12,13 @@ import { MeasureValues } from './measure_values';
|
|||
@ABSTRACT()
|
||||
export class Validator {
|
||||
static bindTo(delegateToken) {
|
||||
return [
|
||||
bind(Validator).toFactory(
|
||||
(delegate) => delegate, [delegateToken]
|
||||
)
|
||||
];
|
||||
return [bind(Validator).toFactory((delegate) => delegate, [delegateToken])];
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates a valid sample out of the complete sample
|
||||
*/
|
||||
validate(completeSample:List<MeasureValues>):List<MeasureValues> {
|
||||
validate(completeSample: List<MeasureValues>): List<MeasureValues> {
|
||||
throw new BaseException('NYI');
|
||||
}
|
||||
|
||||
|
@ -32,7 +26,5 @@ export class Validator {
|
|||
* Returns a Map that describes the properties of the validator
|
||||
* (e.g. sample size, ...)
|
||||
*/
|
||||
describe():StringMap {
|
||||
throw new BaseException('NYI');
|
||||
}
|
||||
}
|
||||
describe(): StringMap<string, any> { throw new BaseException('NYI'); }
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
import { List, ListWrapper, StringMap } from 'angular2/src/facade/collection';
|
||||
import { bind, OpaqueToken } from 'angular2/di';
|
||||
import {List, ListWrapper, StringMap} from 'angular2/src/facade/collection';
|
||||
import {bind, Binding, OpaqueToken} from 'angular2/di';
|
||||
|
||||
import { Validator } from '../validator';
|
||||
import { Statistic } from '../statistic';
|
||||
import { MeasureValues } from '../measure_values';
|
||||
import {Validator} from '../validator';
|
||||
import {Statistic} from '../statistic';
|
||||
import {MeasureValues} from '../measure_values';
|
||||
|
||||
/**
|
||||
* A validator that checks the regression slope of a specific metric.
|
||||
|
@ -11,14 +11,14 @@ import { MeasureValues } from '../measure_values';
|
|||
*/
|
||||
export class RegressionSlopeValidator extends Validator {
|
||||
// TODO(tbosch): use static values when our transpiler supports them
|
||||
static get SAMPLE_SIZE() { return _SAMPLE_SIZE; }
|
||||
static get SAMPLE_SIZE(): OpaqueToken { return _SAMPLE_SIZE; }
|
||||
// TODO(tbosch): use static values when our transpiler supports them
|
||||
static get METRIC() { return _METRIC; }
|
||||
static get METRIC(): OpaqueToken { return _METRIC; }
|
||||
// TODO(tbosch): use static values when our transpiler supports them
|
||||
static get BINDINGS() { return _BINDINGS; }
|
||||
static get BINDINGS(): List<Binding> { return _BINDINGS; }
|
||||
|
||||
_sampleSize:number;
|
||||
_metric:string;
|
||||
_sampleSize: number;
|
||||
_metric: string;
|
||||
|
||||
constructor(sampleSize, metric) {
|
||||
super();
|
||||
|
@ -26,44 +26,37 @@ export class RegressionSlopeValidator extends Validator {
|
|||
this._metric = metric;
|
||||
}
|
||||
|
||||
describe():StringMap {
|
||||
return {
|
||||
'sampleSize': this._sampleSize,
|
||||
'regressionSlopeMetric': this._metric
|
||||
};
|
||||
describe(): StringMap<string, any> {
|
||||
return {'sampleSize': this._sampleSize, 'regressionSlopeMetric': this._metric};
|
||||
}
|
||||
|
||||
validate(completeSample:List<MeasureValues>):List<MeasureValues> {
|
||||
validate(completeSample: List<MeasureValues>): List<MeasureValues> {
|
||||
if (completeSample.length >= this._sampleSize) {
|
||||
var latestSample =
|
||||
ListWrapper.slice(completeSample, completeSample.length - this._sampleSize, completeSample.length);
|
||||
var latestSample = ListWrapper.slice(completeSample, completeSample.length - this._sampleSize,
|
||||
completeSample.length);
|
||||
var xValues = [];
|
||||
var yValues = [];
|
||||
for (var i = 0; i<latestSample.length; i++) {
|
||||
for (var i = 0; i < latestSample.length; i++) {
|
||||
// For now, we only use the array index as x value.
|
||||
// TODO(tbosch): think about whether we should use time here instead
|
||||
ListWrapper.push(xValues, i);
|
||||
ListWrapper.push(yValues, latestSample[i].values[this._metric]);
|
||||
}
|
||||
var regressionSlope = Statistic.calculateRegressionSlope(
|
||||
xValues, Statistic.calculateMean(xValues),
|
||||
yValues, Statistic.calculateMean(yValues)
|
||||
);
|
||||
xValues, Statistic.calculateMean(xValues), yValues, Statistic.calculateMean(yValues));
|
||||
return regressionSlope >= 0 ? latestSample : null;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var _SAMPLE_SIZE = new OpaqueToken('RegressionSlopeValidator.sampleSize');
|
||||
var _METRIC = new OpaqueToken('RegressionSlopeValidator.metric');
|
||||
var _BINDINGS = [
|
||||
bind(RegressionSlopeValidator).toFactory(
|
||||
(sampleSize, metric) => new RegressionSlopeValidator(sampleSize, metric),
|
||||
[_SAMPLE_SIZE, _METRIC]
|
||||
),
|
||||
bind(RegressionSlopeValidator)
|
||||
.toFactory((sampleSize, metric) => new RegressionSlopeValidator(sampleSize, metric),
|
||||
[_SAMPLE_SIZE, _METRIC]),
|
||||
bind(_SAMPLE_SIZE).toValue(10),
|
||||
bind(_METRIC).toValue('scriptTime')
|
||||
];
|
|
@ -1,46 +1,40 @@
|
|||
import { List, ListWrapper, StringMap } from 'angular2/src/facade/collection';
|
||||
import { bind, OpaqueToken } from 'angular2/di';
|
||||
import {List, ListWrapper, StringMap} from 'angular2/src/facade/collection';
|
||||
import {bind, Binding, OpaqueToken} from 'angular2/di';
|
||||
|
||||
import { Validator } from '../validator';
|
||||
import { MeasureValues } from '../measure_values';
|
||||
import {Validator} from '../validator';
|
||||
import {MeasureValues} from '../measure_values';
|
||||
|
||||
/**
|
||||
* A validator that waits for the sample to have a certain size.
|
||||
*/
|
||||
export class SizeValidator extends Validator {
|
||||
// TODO(tbosch): use static values when our transpiler supports them
|
||||
static get BINDINGS() { return _BINDINGS; }
|
||||
static get BINDINGS(): List<Binding> { return _BINDINGS; }
|
||||
// TODO(tbosch): use static values when our transpiler supports them
|
||||
static get SAMPLE_SIZE() { return _SAMPLE_SIZE; }
|
||||
|
||||
_sampleSize:number;
|
||||
_sampleSize: number;
|
||||
|
||||
constructor(size) {
|
||||
super();
|
||||
this._sampleSize = size;
|
||||
}
|
||||
|
||||
describe():StringMap {
|
||||
return {
|
||||
'sampleSize': this._sampleSize
|
||||
};
|
||||
}
|
||||
describe(): StringMap<string, any> { return {'sampleSize': this._sampleSize}; }
|
||||
|
||||
validate(completeSample:List<MeasureValues>):List<MeasureValues> {
|
||||
validate(completeSample: List<MeasureValues>): List<MeasureValues> {
|
||||
if (completeSample.length >= this._sampleSize) {
|
||||
return ListWrapper.slice(completeSample, completeSample.length - this._sampleSize, completeSample.length);
|
||||
return ListWrapper.slice(completeSample, completeSample.length - this._sampleSize,
|
||||
completeSample.length);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var _SAMPLE_SIZE = new OpaqueToken('SizeValidator.sampleSize');
|
||||
var _BINDINGS = [
|
||||
bind(SizeValidator).toFactory(
|
||||
(size) => new SizeValidator(size),
|
||||
[_SAMPLE_SIZE]
|
||||
),
|
||||
bind(SizeValidator)
|
||||
.toFactory((size) => new SizeValidator(size), [_SAMPLE_SIZE]),
|
||||
bind(_SAMPLE_SIZE).toValue(10)
|
||||
];
|
||||
];
|
|
@ -1,33 +0,0 @@
|
|||
import { bind } from 'angular2/di';
|
||||
import { Promise } from 'angular2/src/facade/async';
|
||||
import { BaseException, ABSTRACT } from 'angular2/src/facade/lang';
|
||||
import { List, Map } from 'angular2/src/facade/collection';
|
||||
|
||||
/**
|
||||
* A WebDriverAdapter bridges API differences between different WebDriver clients,
|
||||
* e.g. JS vs Dart Async vs Dart Sync webdriver.
|
||||
* Needs one implementation for every supported WebDriver client.
|
||||
*/
|
||||
@ABSTRACT()
|
||||
export class WebDriverAdapter {
|
||||
static bindTo(delegateToken) {
|
||||
return [
|
||||
bind(WebDriverAdapter).toFactory(
|
||||
(delegate) => delegate, [delegateToken]
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
waitFor(callback:Function):Promise {
|
||||
throw new BaseException('NYI');
|
||||
}
|
||||
executeScript(script:string):Promise {
|
||||
throw new BaseException('NYI');
|
||||
}
|
||||
capabilities():Promise<Map> {
|
||||
throw new BaseException('NYI');
|
||||
}
|
||||
logs(type:string):Promise<List> {
|
||||
throw new BaseException('NYI');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
import {bind} from 'angular2/di';
|
||||
import {Promise} from 'angular2/src/facade/async';
|
||||
import {BaseException, ABSTRACT} from 'angular2/src/facade/lang';
|
||||
import {List, Map} from 'angular2/src/facade/collection';
|
||||
|
||||
/**
|
||||
* A WebDriverAdapter bridges API differences between different WebDriver clients,
|
||||
* e.g. JS vs Dart Async vs Dart Sync webdriver.
|
||||
* Needs one implementation for every supported WebDriver client.
|
||||
*/
|
||||
@ABSTRACT()
|
||||
export class WebDriverAdapter {
|
||||
static bindTo(delegateToken) {
|
||||
return [bind(WebDriverAdapter).toFactory((delegate) => delegate, [delegateToken])];
|
||||
}
|
||||
|
||||
waitFor(callback: Function): Promise<any> { throw new BaseException('NYI'); }
|
||||
executeScript(script: string): Promise<any> { throw new BaseException('NYI'); }
|
||||
capabilities(): Promise<Map<string, any>> { throw new BaseException('NYI'); }
|
||||
logs(type: string): Promise<List<any>> { throw new BaseException('NYI'); }
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
import { bind, Injector, OpaqueToken } from 'angular2/di';
|
||||
|
||||
import { BaseException, ABSTRACT, isBlank, isPresent } from 'angular2/src/facade/lang';
|
||||
import { Promise, PromiseWrapper } from 'angular2/src/facade/async';
|
||||
import { List, ListWrapper, StringMap } from 'angular2/src/facade/collection';
|
||||
|
||||
import { Options } from './common_options';
|
||||
|
||||
/**
|
||||
* A WebDriverExtension implements extended commands of the webdriver protocol
|
||||
* for a given browser, independent of the WebDriverAdapter.
|
||||
* Needs one implementation for every supported Browser.
|
||||
*/
|
||||
@ABSTRACT()
|
||||
export class WebDriverExtension {
|
||||
static bindTo(childTokens) {
|
||||
return [
|
||||
bind(_CHILDREN).toAsyncFactory(
|
||||
(injector) => PromiseWrapper.all(ListWrapper.map(childTokens, (token) => injector.asyncGet(token) )),
|
||||
[Injector]
|
||||
),
|
||||
bind(WebDriverExtension).toFactory(
|
||||
(children, capabilities) => {
|
||||
var delegate;
|
||||
ListWrapper.forEach(children, (extension) => {
|
||||
if (extension.supports(capabilities)) {
|
||||
delegate = extension;
|
||||
}
|
||||
});
|
||||
if (isBlank(delegate)) {
|
||||
throw new BaseException('Could not find a delegate for given capabilities!');
|
||||
}
|
||||
return delegate;
|
||||
},
|
||||
[_CHILDREN, Options.CAPABILITIES]
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
gc():Promise {
|
||||
throw new BaseException('NYI');
|
||||
}
|
||||
|
||||
timeBegin(name):Promise {
|
||||
throw new BaseException('NYI');
|
||||
}
|
||||
|
||||
timeEnd(name, restart:boolean):Promise {
|
||||
throw new BaseException('NYI');
|
||||
}
|
||||
|
||||
/**
|
||||
* Format:
|
||||
* - cat: category of the event
|
||||
* - name: event name: 'script', 'gc', 'render', ...
|
||||
* - ph: phase: 'B' (begin), 'E' (end), 'b' (nestable start), 'e' (nestable end), 'X' (Complete event)
|
||||
* - ts: timestamp in ms, e.g. 12345
|
||||
* - pid: process id
|
||||
* - args: arguments, e.g. {heapSize: 1234}
|
||||
*
|
||||
* Based on [Chrome Trace Event Format](https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/edit)
|
||||
**/
|
||||
readPerfLog():Promise<List> {
|
||||
throw new BaseException('NYI');
|
||||
}
|
||||
|
||||
perfLogFeatures():PerfLogFeatures {
|
||||
throw new BaseException('NYI');
|
||||
}
|
||||
|
||||
supports(capabilities:StringMap):boolean {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export class PerfLogFeatures {
|
||||
render:boolean;
|
||||
gc:boolean;
|
||||
|
||||
constructor({render, gc} = {}) {
|
||||
this.render = isPresent(render) && render;
|
||||
this.gc = isPresent(gc) && gc;
|
||||
}
|
||||
}
|
||||
|
||||
var _CHILDREN = new OpaqueToken('WebDriverExtension.children');
|
|
@ -0,0 +1,77 @@
|
|||
import {bind, Binding, Injector, OpaqueToken} from 'angular2/di';
|
||||
|
||||
import {BaseException, ABSTRACT, isBlank, isPresent} from 'angular2/src/facade/lang';
|
||||
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
||||
import {List, ListWrapper, StringMap} from 'angular2/src/facade/collection';
|
||||
|
||||
import {Options} from './common_options';
|
||||
|
||||
/**
|
||||
* A WebDriverExtension implements extended commands of the webdriver protocol
|
||||
* for a given browser, independent of the WebDriverAdapter.
|
||||
* Needs one implementation for every supported Browser.
|
||||
*/
|
||||
@ABSTRACT()
|
||||
export class WebDriverExtension {
|
||||
static bindTo(childTokens): List<Binding> {
|
||||
return [
|
||||
bind(_CHILDREN)
|
||||
.toAsyncFactory((injector) => PromiseWrapper.all(
|
||||
ListWrapper.map(childTokens, (token) => injector.asyncGet(token))),
|
||||
[Injector]),
|
||||
bind(WebDriverExtension)
|
||||
.toFactory((children, capabilities) =>
|
||||
{
|
||||
var delegate;
|
||||
ListWrapper.forEach(children, (extension) => {
|
||||
if (extension.supports(capabilities)) {
|
||||
delegate = extension;
|
||||
}
|
||||
});
|
||||
if (isBlank(delegate)) {
|
||||
throw new BaseException(
|
||||
'Could not find a delegate for given capabilities!');
|
||||
}
|
||||
return delegate;
|
||||
},
|
||||
[_CHILDREN, Options.CAPABILITIES])
|
||||
];
|
||||
}
|
||||
|
||||
gc(): Promise<any> { throw new BaseException('NYI'); }
|
||||
|
||||
timeBegin(name: string): Promise<any> { throw new BaseException('NYI'); }
|
||||
|
||||
timeEnd(name: string, restartName: string): Promise<any> { throw new BaseException('NYI'); }
|
||||
|
||||
/**
|
||||
* Format:
|
||||
* - cat: category of the event
|
||||
* - name: event name: 'script', 'gc', 'render', ...
|
||||
* - ph: phase: 'B' (begin), 'E' (end), 'b' (nestable start), 'e' (nestable end), 'X' (Complete
|
||||
*event)
|
||||
* - ts: timestamp in ms, e.g. 12345
|
||||
* - pid: process id
|
||||
* - args: arguments, e.g. {heapSize: 1234}
|
||||
*
|
||||
* Based on [Chrome Trace Event
|
||||
*Format](https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/edit)
|
||||
**/
|
||||
readPerfLog(): Promise<List<any>> { throw new BaseException('NYI'); }
|
||||
|
||||
perfLogFeatures(): PerfLogFeatures { throw new BaseException('NYI'); }
|
||||
|
||||
supports(capabilities: StringMap<string, any>): boolean { return true; }
|
||||
}
|
||||
|
||||
export class PerfLogFeatures {
|
||||
render: boolean;
|
||||
gc: boolean;
|
||||
|
||||
constructor({render = false, gc = false}: {render?: boolean, gc?: boolean} = {}) {
|
||||
this.render = render;
|
||||
this.gc = gc;
|
||||
}
|
||||
}
|
||||
|
||||
var _CHILDREN = new OpaqueToken('WebDriverExtension.children');
|
|
@ -1,157 +0,0 @@
|
|||
import { bind } from 'angular2/di';
|
||||
import { ListWrapper, StringMapWrapper, StringMap } from 'angular2/src/facade/collection';
|
||||
import {
|
||||
Json, isPresent, isBlank, RegExpWrapper, StringWrapper, BaseException, NumberWrapper
|
||||
} from 'angular2/src/facade/lang';
|
||||
|
||||
import { WebDriverExtension, PerfLogFeatures } from '../web_driver_extension';
|
||||
import { WebDriverAdapter } from '../web_driver_adapter';
|
||||
import { Promise } from 'angular2/src/facade/async';
|
||||
|
||||
|
||||
export class ChromeDriverExtension extends WebDriverExtension {
|
||||
// TODO(tbosch): use static values when our transpiler supports them
|
||||
static get BINDINGS() { return _BINDINGS; }
|
||||
|
||||
_driver:WebDriverAdapter;
|
||||
|
||||
constructor(driver:WebDriverAdapter) {
|
||||
super();
|
||||
this._driver = driver;
|
||||
}
|
||||
|
||||
gc() {
|
||||
return this._driver.executeScript('window.gc()');
|
||||
}
|
||||
|
||||
timeBegin(name:string):Promise {
|
||||
return this._driver.executeScript(`console.time('${name}');`);
|
||||
}
|
||||
|
||||
timeEnd(name:string, restartName:string = null):Promise {
|
||||
var script = `console.timeEnd('${name}');`;
|
||||
if (isPresent(restartName)) {
|
||||
script += `console.time('${restartName}');`
|
||||
}
|
||||
return this._driver.executeScript(script);
|
||||
}
|
||||
|
||||
// See [Chrome Trace Event Format](https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/edit)
|
||||
readPerfLog() {
|
||||
// TODO(tbosch): Chromedriver bug https://code.google.com/p/chromedriver/issues/detail?id=1098
|
||||
// Need to execute at least one command so that the browser logs can be read out!
|
||||
return this._driver.executeScript('1+1')
|
||||
.then( (_) => this._driver.logs('performance') )
|
||||
.then( (entries) => {
|
||||
var events = [];
|
||||
ListWrapper.forEach(entries, function(entry) {
|
||||
var message = Json.parse(entry['message'])['message'];
|
||||
if (StringWrapper.equals(message['method'], 'Tracing.dataCollected')) {
|
||||
ListWrapper.push(events, message['params']);
|
||||
}
|
||||
if (StringWrapper.equals(message['method'], 'Tracing.bufferUsage')) {
|
||||
throw new BaseException('The DevTools trace buffer filled during the test!');
|
||||
}
|
||||
});
|
||||
return this._convertPerfRecordsToEvents(events);
|
||||
});
|
||||
}
|
||||
|
||||
_convertPerfRecordsToEvents(chromeEvents, normalizedEvents = null) {
|
||||
if (isBlank(normalizedEvents)) {
|
||||
normalizedEvents = [];
|
||||
}
|
||||
var majorGCPids = {};
|
||||
chromeEvents.forEach( (event) => {
|
||||
var cat = event['cat'];
|
||||
var name = event['name'];
|
||||
var args = event['args'];
|
||||
var pid = event['pid'];
|
||||
var ph = event['ph'];
|
||||
if (StringWrapper.equals(cat, 'disabled-by-default-devtools.timeline')) {
|
||||
if (StringWrapper.equals(name, 'FunctionCall') &&
|
||||
(isBlank(args) || isBlank(args['data']) || !StringWrapper.equals(args['data']['scriptName'], 'InjectedScript'))) {
|
||||
ListWrapper.push(normalizedEvents, normalizeEvent(event, {
|
||||
'name': 'script'
|
||||
}));
|
||||
} else if (StringWrapper.equals(name, 'RecalculateStyles') ||
|
||||
StringWrapper.equals(name, 'Layout') ||
|
||||
StringWrapper.equals(name, 'UpdateLayerTree') ||
|
||||
StringWrapper.equals(name, 'Paint') ||
|
||||
StringWrapper.equals(name, 'Rasterize') ||
|
||||
StringWrapper.equals(name, 'CompositeLayers')) {
|
||||
ListWrapper.push(normalizedEvents, normalizeEvent(event, {
|
||||
'name': 'render'
|
||||
}));
|
||||
} else if (StringWrapper.equals(name, 'GCEvent')) {
|
||||
var normArgs = {
|
||||
'usedHeapSize': isPresent(args['usedHeapSizeAfter']) ? args['usedHeapSizeAfter'] : args['usedHeapSizeBefore']
|
||||
};
|
||||
if (StringWrapper.equals(event['ph'], 'E')) {
|
||||
normArgs['majorGc'] = isPresent(majorGCPids[pid]) && majorGCPids[pid];
|
||||
}
|
||||
majorGCPids[pid] = false;
|
||||
ListWrapper.push(normalizedEvents, normalizeEvent(event, {
|
||||
'name': 'gc',
|
||||
'args': normArgs
|
||||
}));
|
||||
}
|
||||
} else if (StringWrapper.equals(cat, 'blink.console')) {
|
||||
ListWrapper.push(normalizedEvents, normalizeEvent(event, {
|
||||
'name': name
|
||||
}));
|
||||
} else if (StringWrapper.equals(cat, 'v8')) {
|
||||
if (StringWrapper.equals(name, 'majorGC')) {
|
||||
if (StringWrapper.equals(ph, 'B')) {
|
||||
majorGCPids[pid] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return normalizedEvents;
|
||||
}
|
||||
|
||||
perfLogFeatures():PerfLogFeatures {
|
||||
return new PerfLogFeatures({
|
||||
render: true,
|
||||
gc: true
|
||||
});
|
||||
}
|
||||
|
||||
supports(capabilities:StringMap):boolean {
|
||||
return StringWrapper.equals(capabilities['browserName'].toLowerCase(), 'chrome');
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeEvent(chromeEvent, data) {
|
||||
var ph = chromeEvent['ph'];
|
||||
if (StringWrapper.equals(ph, 'S')) {
|
||||
ph = 'b';
|
||||
} else if (StringWrapper.equals(ph, 'F')) {
|
||||
ph = 'e';
|
||||
}
|
||||
var result = {
|
||||
'pid': chromeEvent['pid'],
|
||||
'ph': ph,
|
||||
'cat': 'timeline',
|
||||
'ts': chromeEvent['ts'] / 1000
|
||||
};
|
||||
if (chromeEvent['ph'] === 'X') {
|
||||
var dur = chromeEvent['dur'];
|
||||
if (isBlank(dur)) {
|
||||
dur = chromeEvent['tdur'];
|
||||
}
|
||||
result['dur'] = isBlank(dur) ? 0.0 : dur / 1000;
|
||||
}
|
||||
StringMapWrapper.forEach(data, (value, prop) => {
|
||||
result[prop] = value;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
var _BINDINGS = [
|
||||
bind(ChromeDriverExtension).toFactory(
|
||||
(driver) => new ChromeDriverExtension(driver),
|
||||
[WebDriverAdapter]
|
||||
)
|
||||
];
|
|
@ -0,0 +1,136 @@
|
|||
import {bind, Binding} from 'angular2/di';
|
||||
import {ListWrapper, StringMapWrapper, StringMap} from 'angular2/src/facade/collection';
|
||||
import {
|
||||
Json,
|
||||
isPresent,
|
||||
isBlank,
|
||||
RegExpWrapper,
|
||||
StringWrapper,
|
||||
BaseException,
|
||||
NumberWrapper
|
||||
} from 'angular2/src/facade/lang';
|
||||
|
||||
import {WebDriverExtension, PerfLogFeatures} from '../web_driver_extension';
|
||||
import {WebDriverAdapter} from '../web_driver_adapter';
|
||||
import {Promise} from 'angular2/src/facade/async';
|
||||
|
||||
export class ChromeDriverExtension extends WebDriverExtension {
|
||||
// TODO(tbosch): use static values when our transpiler supports them
|
||||
static get BINDINGS(): List<Binding> { return _BINDINGS; }
|
||||
|
||||
constructor(private _driver: WebDriverAdapter) { super(); }
|
||||
|
||||
gc() { return this._driver.executeScript('window.gc()'); }
|
||||
|
||||
timeBegin(name: string): Promise<any> {
|
||||
return this._driver.executeScript(`console.time('${name}');`);
|
||||
}
|
||||
|
||||
timeEnd(name: string, restartName: string = null): Promise<any> {
|
||||
var script = `console.timeEnd('${name}');`;
|
||||
if (isPresent(restartName)) {
|
||||
script += `console.time('${restartName}');`
|
||||
}
|
||||
return this._driver.executeScript(script);
|
||||
}
|
||||
|
||||
// See [Chrome Trace Event
|
||||
// Format](https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/edit)
|
||||
readPerfLog(): Promise<any> {
|
||||
// TODO(tbosch): Chromedriver bug https://code.google.com/p/chromedriver/issues/detail?id=1098
|
||||
// Need to execute at least one command so that the browser logs can be read out!
|
||||
return this._driver.executeScript('1+1')
|
||||
.then((_) => this._driver.logs('performance'))
|
||||
.then((entries) => {
|
||||
var events = [];
|
||||
ListWrapper.forEach(entries, function(entry) {
|
||||
var message = Json.parse(entry['message'])['message'];
|
||||
if (StringWrapper.equals(message['method'], 'Tracing.dataCollected')) {
|
||||
ListWrapper.push(events, message['params']);
|
||||
}
|
||||
if (StringWrapper.equals(message['method'], 'Tracing.bufferUsage')) {
|
||||
throw new BaseException('The DevTools trace buffer filled during the test!');
|
||||
}
|
||||
});
|
||||
return this._convertPerfRecordsToEvents(events);
|
||||
});
|
||||
}
|
||||
|
||||
_convertPerfRecordsToEvents(chromeEvents: List<StringMap<string, any>>,
|
||||
normalizedEvents: List<StringMap<string, any>> = null) {
|
||||
if (isBlank(normalizedEvents)) {
|
||||
normalizedEvents = [];
|
||||
}
|
||||
var majorGCPids = {};
|
||||
chromeEvents.forEach((event) => {
|
||||
var cat = event['cat'];
|
||||
var name = event['name'];
|
||||
var args = event['args'];
|
||||
var pid = event['pid'];
|
||||
var ph = event['ph'];
|
||||
if (StringWrapper.equals(cat, 'disabled-by-default-devtools.timeline')) {
|
||||
if (StringWrapper.equals(name, 'FunctionCall') &&
|
||||
(isBlank(args) || isBlank(args['data']) ||
|
||||
!StringWrapper.equals(args['data']['scriptName'], 'InjectedScript'))) {
|
||||
ListWrapper.push(normalizedEvents, normalizeEvent(event, {'name': 'script'}));
|
||||
} else if (StringWrapper.equals(name, 'RecalculateStyles') ||
|
||||
StringWrapper.equals(name, 'Layout') ||
|
||||
StringWrapper.equals(name, 'UpdateLayerTree') ||
|
||||
StringWrapper.equals(name, 'Paint') || StringWrapper.equals(name, 'Rasterize') ||
|
||||
StringWrapper.equals(name, 'CompositeLayers')) {
|
||||
ListWrapper.push(normalizedEvents, normalizeEvent(event, {'name': 'render'}));
|
||||
} else if (StringWrapper.equals(name, 'GCEvent')) {
|
||||
var normArgs = {
|
||||
'usedHeapSize': isPresent(args['usedHeapSizeAfter']) ? args['usedHeapSizeAfter'] :
|
||||
args['usedHeapSizeBefore']
|
||||
};
|
||||
if (StringWrapper.equals(event['ph'], 'E')) {
|
||||
normArgs['majorGc'] = isPresent(majorGCPids[pid]) && majorGCPids[pid];
|
||||
}
|
||||
majorGCPids[pid] = false;
|
||||
ListWrapper.push(normalizedEvents,
|
||||
normalizeEvent(event, {'name': 'gc', 'args': normArgs}));
|
||||
}
|
||||
} else if (StringWrapper.equals(cat, 'blink.console')) {
|
||||
ListWrapper.push(normalizedEvents, normalizeEvent(event, {'name': name}));
|
||||
} else if (StringWrapper.equals(cat, 'v8')) {
|
||||
if (StringWrapper.equals(name, 'majorGC')) {
|
||||
if (StringWrapper.equals(ph, 'B')) {
|
||||
majorGCPids[pid] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return normalizedEvents;
|
||||
}
|
||||
|
||||
perfLogFeatures(): PerfLogFeatures { return new PerfLogFeatures({render: true, gc: true}); }
|
||||
|
||||
supports(capabilities: StringMap<string, any>): boolean {
|
||||
return StringWrapper.equals(capabilities['browserName'].toLowerCase(), 'chrome');
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeEvent(chromeEvent: StringMap<string, any>,
|
||||
data: StringMap<string, any>): StringMap<string, any> {
|
||||
var ph = chromeEvent['ph'];
|
||||
if (StringWrapper.equals(ph, 'S')) {
|
||||
ph = 'b';
|
||||
} else if (StringWrapper.equals(ph, 'F')) {
|
||||
ph = 'e';
|
||||
}
|
||||
var result =
|
||||
{'pid': chromeEvent['pid'], 'ph': ph, 'cat': 'timeline', 'ts': chromeEvent['ts'] / 1000};
|
||||
if (chromeEvent['ph'] === 'X') {
|
||||
var dur = chromeEvent['dur'];
|
||||
if (isBlank(dur)) {
|
||||
dur = chromeEvent['tdur'];
|
||||
}
|
||||
result['dur'] = isBlank(dur) ? 0.0 : dur / 1000;
|
||||
}
|
||||
StringMapWrapper.forEach(data, (value, prop) => { result[prop] = value; });
|
||||
return result;
|
||||
}
|
||||
|
||||
var _BINDINGS = [bind(ChromeDriverExtension)
|
||||
.toFactory((driver) => new ChromeDriverExtension(driver), [WebDriverAdapter])];
|
|
@ -1,34 +1,31 @@
|
|||
import { bind } from 'angular2/di';
|
||||
import { ListWrapper, StringMap } from 'angular2/src/facade/collection';
|
||||
import {bind, Binding} from 'angular2/di';
|
||||
import {ListWrapper, StringMap} from 'angular2/src/facade/collection';
|
||||
import {
|
||||
Json, isPresent, isBlank, RegExpWrapper, StringWrapper, BaseException
|
||||
Json,
|
||||
isPresent,
|
||||
isBlank,
|
||||
RegExpWrapper,
|
||||
StringWrapper,
|
||||
BaseException
|
||||
} from 'angular2/src/facade/lang';
|
||||
|
||||
import { WebDriverExtension, PerfLogFeatures } from '../web_driver_extension';
|
||||
import { WebDriverAdapter } from '../web_driver_adapter';
|
||||
import { Promise } from 'angular2/src/facade/async';
|
||||
|
||||
import {WebDriverExtension, PerfLogFeatures} from '../web_driver_extension';
|
||||
import {WebDriverAdapter} from '../web_driver_adapter';
|
||||
import {Promise} from 'angular2/src/facade/async';
|
||||
|
||||
export class IOsDriverExtension extends WebDriverExtension {
|
||||
// TODO(tbosch): use static values when our transpiler supports them
|
||||
static get BINDINGS() { return _BINDINGS; }
|
||||
static get BINDINGS(): List<Binding> { return _BINDINGS; }
|
||||
|
||||
_driver:WebDriverAdapter;
|
||||
constructor(private _driver: WebDriverAdapter) { super(); }
|
||||
|
||||
constructor(driver:WebDriverAdapter) {
|
||||
super();
|
||||
this._driver = driver;
|
||||
}
|
||||
gc(): Promise<any> { throw new BaseException('Force GC is not supported on iOS'); }
|
||||
|
||||
gc() {
|
||||
throw new BaseException('Force GC is not supported on iOS');
|
||||
}
|
||||
|
||||
timeBegin(name:string):Promise {
|
||||
timeBegin(name: string): Promise<any> {
|
||||
return this._driver.executeScript(`console.time('${name}');`);
|
||||
}
|
||||
|
||||
timeEnd(name:string, restartName:string = null):Promise {
|
||||
timeEnd(name: string, restartName: string = null): Promise<any> {
|
||||
var script = `console.timeEnd('${name}');`;
|
||||
if (isPresent(restartName)) {
|
||||
script += `console.time('${restartName}');`
|
||||
|
@ -41,24 +38,24 @@ export class IOsDriverExtension extends WebDriverExtension {
|
|||
// TODO(tbosch): Bug in IOsDriver: Need to execute at least one command
|
||||
// so that the browser logs can be read out!
|
||||
return this._driver.executeScript('1+1')
|
||||
.then( (_) => this._driver.logs('performance') )
|
||||
.then( (entries) => {
|
||||
var records = [];
|
||||
ListWrapper.forEach(entries, function(entry) {
|
||||
var message = Json.parse(entry['message'])['message'];
|
||||
if (StringWrapper.equals(message['method'], 'Timeline.eventRecorded')) {
|
||||
ListWrapper.push(records, message['params']['record']);
|
||||
}
|
||||
.then((_) => this._driver.logs('performance'))
|
||||
.then((entries) => {
|
||||
var records = [];
|
||||
ListWrapper.forEach(entries, function(entry) {
|
||||
var message = Json.parse(entry['message'])['message'];
|
||||
if (StringWrapper.equals(message['method'], 'Timeline.eventRecorded')) {
|
||||
ListWrapper.push(records, message['params']['record']);
|
||||
}
|
||||
});
|
||||
return this._convertPerfRecordsToEvents(records);
|
||||
});
|
||||
return this._convertPerfRecordsToEvents(records);
|
||||
});
|
||||
}
|
||||
|
||||
_convertPerfRecordsToEvents(records, events = null) {
|
||||
if (isBlank(events)) {
|
||||
events = [];
|
||||
}
|
||||
records.forEach( (record) => {
|
||||
records.forEach((record) => {
|
||||
var endEvent = null;
|
||||
var type = record['type'];
|
||||
var data = record['data'];
|
||||
|
@ -74,11 +71,10 @@ export class IOsDriverExtension extends WebDriverExtension {
|
|||
} else if (StringWrapper.equals(type, 'TimeEnd')) {
|
||||
ListWrapper.push(events, createMarkEndEvent(data['message'], startTime));
|
||||
} else if (StringWrapper.equals(type, 'RecalculateStyles') ||
|
||||
StringWrapper.equals(type, 'Layout') ||
|
||||
StringWrapper.equals(type, 'UpdateLayerTree') ||
|
||||
StringWrapper.equals(type, 'Paint') ||
|
||||
StringWrapper.equals(type, 'Rasterize') ||
|
||||
StringWrapper.equals(type, 'CompositeLayers')) {
|
||||
StringWrapper.equals(type, 'Layout') ||
|
||||
StringWrapper.equals(type, 'UpdateLayerTree') ||
|
||||
StringWrapper.equals(type, 'Paint') || StringWrapper.equals(type, 'Rasterize') ||
|
||||
StringWrapper.equals(type, 'CompositeLayers')) {
|
||||
ListWrapper.push(events, createStartEvent('render', startTime));
|
||||
endEvent = createEndEvent('render', endTime);
|
||||
}
|
||||
|
@ -93,13 +89,9 @@ export class IOsDriverExtension extends WebDriverExtension {
|
|||
return events;
|
||||
}
|
||||
|
||||
perfLogFeatures():PerfLogFeatures {
|
||||
return new PerfLogFeatures({
|
||||
render: true
|
||||
});
|
||||
}
|
||||
perfLogFeatures(): PerfLogFeatures { return new PerfLogFeatures({render: true}); }
|
||||
|
||||
supports(capabilities:StringMap):boolean {
|
||||
supports(capabilities: StringMap<string, any>): boolean {
|
||||
return StringWrapper.equals(capabilities['browserName'].toLowerCase(), 'safari');
|
||||
}
|
||||
}
|
||||
|
@ -136,9 +128,5 @@ function createMarkEndEvent(name, time) {
|
|||
return createEvent('e', name, time);
|
||||
}
|
||||
|
||||
var _BINDINGS = [
|
||||
bind(IOsDriverExtension).toFactory(
|
||||
(driver) => new IOsDriverExtension(driver),
|
||||
[WebDriverAdapter]
|
||||
)
|
||||
];
|
||||
var _BINDINGS = [bind(IOsDriverExtension)
|
||||
.toFactory((driver) => new IOsDriverExtension(driver), [WebDriverAdapter])];
|
|
@ -0,0 +1,2 @@
|
|||
library benchpress.src.webdriver.selenium_webdriver_adapter;
|
||||
//no dart implementation
|
|
@ -1,68 +0,0 @@
|
|||
import { Promise, PromiseWrapper } from 'angular2/src/facade/async';
|
||||
import { bind } from 'angular2/di';
|
||||
import { WebDriverAdapter } from '../web_driver_adapter';
|
||||
|
||||
import webdriver from 'selenium-webdriver';
|
||||
|
||||
/**
|
||||
* Adapter for the selenium-webdriver.
|
||||
*/
|
||||
export class SeleniumWebDriverAdapter extends WebDriverAdapter {
|
||||
// TODO(tbosch): use static values when our transpiler supports them
|
||||
static get PROTRACTOR_BINDINGS() { return _PROTRACTOR_BINDINGS; }
|
||||
|
||||
_driver:any;
|
||||
|
||||
constructor(driver) {
|
||||
super();
|
||||
this._driver = driver;
|
||||
}
|
||||
|
||||
_convertPromise(thenable) {
|
||||
var completer = PromiseWrapper.completer();
|
||||
thenable.then(
|
||||
// selenium-webdriver uses an own Node.js context,
|
||||
// so we need to convert data into objects of this context.
|
||||
// (e.g. otherwise instanceof checks of rtts_assert would fail)
|
||||
(data) => completer.resolve(convertToLocalProcess(data)),
|
||||
completer.reject
|
||||
);
|
||||
return completer.promise;
|
||||
}
|
||||
|
||||
waitFor(callback):Promise {
|
||||
return this._convertPromise(this._driver.controlFlow().execute(callback));
|
||||
}
|
||||
|
||||
executeScript(script:string):Promise {
|
||||
return this._convertPromise(this._driver.executeScript(script));
|
||||
}
|
||||
|
||||
capabilities():Promise {
|
||||
return this._convertPromise(
|
||||
this._driver.getCapabilities().then( (capsObject) => capsObject.toJSON() )
|
||||
);
|
||||
}
|
||||
|
||||
logs(type:string):Promise {
|
||||
// Needed as selenium-webdriver does not forward
|
||||
// performance logs in the correct way via manage().logs
|
||||
return this._convertPromise(this._driver.schedule(
|
||||
new webdriver.Command(webdriver.CommandName.GET_LOG).
|
||||
setParameter('type', type),
|
||||
'WebDriver.manage().logs().get(' + type + ')'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function convertToLocalProcess(data) {
|
||||
var serialized = JSON.stringify(data);
|
||||
if (''+serialized === 'undefined') {
|
||||
return undefined;
|
||||
}
|
||||
return JSON.parse(serialized);
|
||||
}
|
||||
|
||||
var _PROTRACTOR_BINDINGS = [
|
||||
bind(WebDriverAdapter).toFactory( () => new SeleniumWebDriverAdapter(global.browser), [])
|
||||
];
|
|
@ -0,0 +1,56 @@
|
|||
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
||||
import {bind, Binding} from 'angular2/di';
|
||||
import {WebDriverAdapter} from '../web_driver_adapter';
|
||||
|
||||
import webdriver = require('selenium-webdriver');
|
||||
|
||||
/**
|
||||
* Adapter for the selenium-webdriver.
|
||||
*/
|
||||
export class SeleniumWebDriverAdapter extends WebDriverAdapter {
|
||||
static get PROTRACTOR_BINDINGS(): List<Binding> { return _PROTRACTOR_BINDINGS; }
|
||||
|
||||
constructor(private _driver: any) { super(); }
|
||||
|
||||
_convertPromise(thenable) {
|
||||
var completer = PromiseWrapper.completer();
|
||||
thenable.then(
|
||||
// selenium-webdriver uses an own Node.js context,
|
||||
// so we need to convert data into objects of this context.
|
||||
// (e.g. otherwise instanceof checks of rtts_assert would fail)
|
||||
(data) => completer.resolve(convertToLocalProcess(data)), completer.reject);
|
||||
return completer.promise;
|
||||
}
|
||||
|
||||
waitFor(callback): Promise<any> {
|
||||
return this._convertPromise(this._driver.controlFlow().execute(callback));
|
||||
}
|
||||
|
||||
executeScript(script: string): Promise<any> {
|
||||
return this._convertPromise(this._driver.executeScript(script));
|
||||
}
|
||||
|
||||
capabilities(): Promise<any> {
|
||||
return this._convertPromise(
|
||||
this._driver.getCapabilities().then((capsObject) => capsObject.toJSON()));
|
||||
}
|
||||
|
||||
logs(type: string): Promise<any> {
|
||||
// Needed as selenium-webdriver does not forward
|
||||
// performance logs in the correct way via manage().logs
|
||||
return this._convertPromise(this._driver.schedule(
|
||||
new webdriver.Command(webdriver.CommandName.GET_LOG).setParameter('type', type),
|
||||
'WebDriver.manage().logs().get(' + type + ')'));
|
||||
}
|
||||
}
|
||||
|
||||
function convertToLocalProcess(data): Object {
|
||||
var serialized = JSON.stringify(data);
|
||||
if ('' + serialized === 'undefined') {
|
||||
return undefined;
|
||||
}
|
||||
return JSON.parse(serialized);
|
||||
}
|
||||
|
||||
var _PROTRACTOR_BINDINGS =
|
||||
[bind(WebDriverAdapter).toFactory(() => new SeleniumWebDriverAdapter(global.browser), [])];
|
|
@ -0,0 +1,2 @@
|
|||
library benchpress.test.firefox_extension.conf;
|
||||
//empty as we don't have a version for dart
|
|
@ -1,3 +1,4 @@
|
|||
/// <reference path="../../../angular2/typings/node/node.d.ts" />
|
||||
var testHelper = require('../../src/firefox_extension/lib/test_helper.js');
|
||||
|
||||
// Where to save profile results (parent folder must exist)
|
||||
|
@ -8,11 +9,7 @@ exports.config = {
|
|||
|
||||
specs: ['spec.js'],
|
||||
|
||||
getMultiCapabilities: function() {
|
||||
return testHelper.getFirefoxProfileWithExtension();
|
||||
},
|
||||
getMultiCapabilities: function() { return testHelper.getFirefoxProfileWithExtension(); },
|
||||
|
||||
params: {
|
||||
profileSavePath: testHelper.getAbsolutePath(PROFILE_SAVE_PATH)
|
||||
}
|
||||
params: {profileSavePath: testHelper.getAbsolutePath(PROFILE_SAVE_PATH)}
|
||||
};
|
|
@ -0,0 +1,2 @@
|
|||
library benchpress.test.firefox_extension.spec;
|
||||
//no dart implementation
|
|
@ -1,3 +1,7 @@
|
|||
/// <reference path="../../../angular2/typings/node/node.d.ts" />
|
||||
/// <reference path="../../../angular2/typings/angular-protractor/angular-protractor.d.ts" />
|
||||
/// <reference path="../../../angular2/typings/jasmine/jasmine.d.ts" />
|
||||
|
||||
var fs = require('fs');
|
||||
|
||||
var validateFile = function() {
|
||||
|
@ -5,7 +9,7 @@ var validateFile = function() {
|
|||
var content = fs.readFileSync(browser.params.profileSavePath, 'utf8');
|
||||
// TODO(hankduan): This check not very useful. Ideally we want to validate
|
||||
// that the file contains all the events that we are looking for. Pending
|
||||
// on data transformer.
|
||||
// on data transformer.
|
||||
expect(content).toContain('forceGC');
|
||||
// Delete file
|
||||
fs.unlinkSync(browser.params.profileSavePath);
|
||||
|
@ -21,14 +25,13 @@ var validateFile = function() {
|
|||
};
|
||||
|
||||
describe('firefox extension', function() {
|
||||
it ('should measure performance', function() {
|
||||
browser.sleep(3000); // wait for extension to load
|
||||
it('should measure performance', function() {
|
||||
browser.sleep(3000); // wait for extension to load
|
||||
|
||||
browser.driver.get('http://www.angularjs.org');
|
||||
|
||||
browser.executeScript('window.startProfiler()').then(function() {
|
||||
console.log('started measuring perf');
|
||||
});
|
||||
browser.executeScript('window.startProfiler()')
|
||||
.then(function() { console.log('started measuring perf'); });
|
||||
|
||||
browser.executeScript('window.forceGC()');
|
||||
|
||||
|
@ -38,13 +41,10 @@ describe('firefox extension', function() {
|
|||
|
||||
browser.executeScript('window.forceGC()');
|
||||
|
||||
var script =
|
||||
'window.stopAndRecord("' + browser.params.profileSavePath + '")';
|
||||
browser.executeScript(script).then(function() {
|
||||
console.log('stopped measuring perf');
|
||||
});
|
||||
var script = 'window.stopAndRecord("' + browser.params.profileSavePath + '")';
|
||||
browser.executeScript(script).then(function() { console.log('stopped measuring perf'); });
|
||||
|
||||
// wait for it to finish, then validate file.
|
||||
browser.sleep(3000).then(validateFile);
|
||||
// wait for it to finish, then validate file.
|
||||
browser.sleep(3000).then(validateFile);
|
||||
})
|
||||
});
|
|
@ -1,91 +0,0 @@
|
|||
import {
|
||||
afterEach,
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import { List, ListWrapper, StringMap } from 'angular2/src/facade/collection';
|
||||
import { PromiseWrapper, Promise } from 'angular2/src/facade/async';
|
||||
|
||||
import { Metric, MultiMetric, bind, Injector } from 'benchpress/common';
|
||||
|
||||
export function main() {
|
||||
function createMetric(ids) {
|
||||
return Injector.resolveAndCreate([
|
||||
ListWrapper.map(ids, (id) => bind(id).toValue(new MockMetric(id)) ),
|
||||
MultiMetric.createBindings(ids)
|
||||
]).asyncGet(MultiMetric);
|
||||
}
|
||||
|
||||
describe('multi metric', () => {
|
||||
|
||||
it('should merge descriptions', inject([AsyncTestCompleter], (async) => {
|
||||
createMetric(['m1', 'm2']).then( (m) => {
|
||||
expect(m.describe()).toEqual({
|
||||
'm1': 'describe', 'm2': 'describe'
|
||||
});
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should merge all beginMeasure calls', inject([AsyncTestCompleter], (async) => {
|
||||
createMetric(['m1', 'm2'])
|
||||
.then( (m) => m.beginMeasure() )
|
||||
.then( (values) => {
|
||||
expect(values).toEqual([
|
||||
'm1_beginMeasure', 'm2_beginMeasure'
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
[false, true].forEach( (restartFlag) => {
|
||||
it(`should merge all endMeasure calls for restart=${restartFlag}`, inject([AsyncTestCompleter], (async) => {
|
||||
createMetric(['m1', 'm2'])
|
||||
.then( (m) => m.endMeasure(restartFlag) )
|
||||
.then( (values) => {
|
||||
expect(values).toEqual({
|
||||
'm1': { 'restart': restartFlag },
|
||||
'm2': { 'restart': restartFlag }
|
||||
});
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
class MockMetric extends Metric {
|
||||
_id:string;
|
||||
|
||||
constructor(id) {
|
||||
super();
|
||||
this._id = id;
|
||||
}
|
||||
|
||||
beginMeasure():Promise {
|
||||
return PromiseWrapper.resolve(`${this._id}_beginMeasure`);
|
||||
}
|
||||
|
||||
endMeasure(restart:boolean):Promise<StringMap> {
|
||||
var result = {};
|
||||
result[this._id] = {
|
||||
'restart': restart
|
||||
};
|
||||
return PromiseWrapper.resolve(result);
|
||||
}
|
||||
|
||||
describe():StringMap {
|
||||
var result = {};
|
||||
result[this._id] = 'describe';
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
import {
|
||||
afterEach,
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import {List, ListWrapper, StringMap} from 'angular2/src/facade/collection';
|
||||
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
|
||||
|
||||
import {Metric, MultiMetric, bind, Injector} from 'benchpress/common';
|
||||
|
||||
export function main() {
|
||||
function createMetric(ids) {
|
||||
return Injector.resolveAndCreate([
|
||||
ListWrapper.map(ids, (id) => bind(id).toValue(new MockMetric(id))),
|
||||
MultiMetric.createBindings(ids)
|
||||
])
|
||||
.asyncGet(MultiMetric);
|
||||
}
|
||||
|
||||
describe('multi metric', () => {
|
||||
|
||||
it('should merge descriptions', inject([AsyncTestCompleter], (async) => {
|
||||
createMetric(['m1', 'm2'])
|
||||
.then((m) => {
|
||||
expect(m.describe()).toEqual({'m1': 'describe', 'm2': 'describe'});
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should merge all beginMeasure calls', inject([AsyncTestCompleter], (async) => {
|
||||
createMetric(['m1', 'm2'])
|
||||
.then((m) => m.beginMeasure())
|
||||
.then((values) => {
|
||||
expect(values).toEqual(['m1_beginMeasure', 'm2_beginMeasure']);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
[false, true].forEach((restartFlag) => {
|
||||
it(`should merge all endMeasure calls for restart=${restartFlag}`,
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
createMetric(['m1', 'm2'])
|
||||
.then((m) => m.endMeasure(restartFlag))
|
||||
.then((values) => {
|
||||
expect(values)
|
||||
.toEqual({'m1': {'restart': restartFlag}, 'm2': {'restart': restartFlag}});
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
class MockMetric extends Metric {
|
||||
_id: string;
|
||||
|
||||
constructor(id) {
|
||||
super();
|
||||
this._id = id;
|
||||
}
|
||||
|
||||
beginMeasure(): Promise<string> { return PromiseWrapper.resolve(`${this._id}_beginMeasure`); }
|
||||
|
||||
endMeasure(restart: boolean): Promise<StringMap<string, any>> {
|
||||
var result = {};
|
||||
result[this._id] = {
|
||||
'restart': restart
|
||||
};
|
||||
return PromiseWrapper.resolve(result);
|
||||
}
|
||||
|
||||
describe(): StringMap<string, string> {
|
||||
var result = {};
|
||||
result[this._id] = 'describe';
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -1,485 +0,0 @@
|
|||
import {
|
||||
afterEach,
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import { List, ListWrapper, StringMapWrapper } from 'angular2/src/facade/collection';
|
||||
import { PromiseWrapper, Promise } from 'angular2/src/facade/async';
|
||||
import { isPresent, isBlank } from 'angular2/src/facade/lang';
|
||||
|
||||
import {
|
||||
Metric, PerflogMetric, WebDriverExtension,
|
||||
PerfLogFeatures,
|
||||
bind, Injector, Options
|
||||
} from 'benchpress/common';
|
||||
|
||||
import { TraceEventFactory } from '../trace_event_factory';
|
||||
|
||||
export function main() {
|
||||
var commandLog;
|
||||
var eventFactory = new TraceEventFactory('timeline', 'pid0');
|
||||
|
||||
function createMetric(perfLogs, microMetrics = null, perfLogFeatures = null, forceGc = null) {
|
||||
commandLog = [];
|
||||
if (isBlank(perfLogFeatures)) {
|
||||
perfLogFeatures = new PerfLogFeatures({render: true, gc: true});
|
||||
}
|
||||
if (isBlank(microMetrics)) {
|
||||
microMetrics = StringMapWrapper.create();
|
||||
}
|
||||
var bindings = [
|
||||
Options.DEFAULT_BINDINGS,
|
||||
PerflogMetric.BINDINGS,
|
||||
bind(Options.MICRO_METRICS).toValue(microMetrics),
|
||||
bind(PerflogMetric.SET_TIMEOUT).toValue( (fn, millis) => {
|
||||
ListWrapper.push(commandLog, ['setTimeout', millis]);
|
||||
fn();
|
||||
}),
|
||||
bind(WebDriverExtension).toValue(new MockDriverExtension(perfLogs, commandLog, perfLogFeatures))
|
||||
];
|
||||
if (isPresent(forceGc)) {
|
||||
ListWrapper.push(bindings, bind(Options.FORCE_GC).toValue(forceGc));
|
||||
}
|
||||
return Injector.resolveAndCreate(bindings).get(PerflogMetric);
|
||||
}
|
||||
|
||||
describe('perflog metric', () => {
|
||||
|
||||
function sortedKeys(stringMap) {
|
||||
var res = [];
|
||||
StringMapWrapper.forEach(stringMap, (_, key) => {
|
||||
ListWrapper.push(res, key);
|
||||
});
|
||||
res.sort();
|
||||
return res;
|
||||
}
|
||||
|
||||
it('should describe itself based on the perfLogFeatrues', () => {
|
||||
expect(sortedKeys(createMetric([[]], null, new PerfLogFeatures()).describe())).toEqual([
|
||||
'pureScriptTime', 'scriptTime'
|
||||
]);
|
||||
|
||||
expect(sortedKeys(createMetric([[]], null, new PerfLogFeatures({
|
||||
render: true,
|
||||
gc: false
|
||||
})).describe())).toEqual([
|
||||
'pureScriptTime', 'renderTime', 'scriptTime'
|
||||
]);
|
||||
|
||||
expect(sortedKeys(createMetric([[]]).describe())).toEqual([
|
||||
'gcAmount', 'gcTime', 'majorGcTime',
|
||||
'pureScriptTime', 'renderTime', 'scriptTime'
|
||||
]);
|
||||
|
||||
expect(sortedKeys(createMetric([[]], null, new PerfLogFeatures({
|
||||
render: true,
|
||||
gc: true
|
||||
}), true).describe())).toEqual([
|
||||
'forcedGcAmount', 'forcedGcTime',
|
||||
'gcAmount', 'gcTime', 'majorGcTime',
|
||||
'pureScriptTime', 'renderTime', 'scriptTime'
|
||||
]);
|
||||
});
|
||||
|
||||
it('should describe itself based on micro metrics', () => {
|
||||
var description = createMetric([[]], {
|
||||
'myMicroMetric': 'someDesc'
|
||||
}).describe();
|
||||
expect(description['myMicroMetric']).toEqual('someDesc');
|
||||
});
|
||||
|
||||
describe('beginMeasure', () => {
|
||||
|
||||
it('should not force gc and mark the timeline', inject([AsyncTestCompleter], (async) => {
|
||||
var metric = createMetric([[]]);
|
||||
metric.beginMeasure().then((_) => {
|
||||
expect(commandLog).toEqual([['timeBegin', 'benchpress0']]);
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should force gc and mark the timeline', inject([AsyncTestCompleter], (async) => {
|
||||
var metric = createMetric([[]], null, null, true);
|
||||
metric.beginMeasure().then((_) => {
|
||||
expect(commandLog).toEqual([['gc'], ['timeBegin', 'benchpress0']]);
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
describe('endMeasure', () => {
|
||||
|
||||
it('should mark and aggregate events in between the marks', inject([AsyncTestCompleter], (async) => {
|
||||
var events = [
|
||||
[
|
||||
eventFactory.markStart('benchpress0', 0),
|
||||
eventFactory.start('script', 4),
|
||||
eventFactory.end('script', 6),
|
||||
eventFactory.markEnd('benchpress0', 10)
|
||||
]
|
||||
];
|
||||
var metric = createMetric(events);
|
||||
metric.beginMeasure()
|
||||
.then( (_) => metric.endMeasure(false) )
|
||||
.then( (data) => {
|
||||
expect(commandLog).toEqual([
|
||||
['timeBegin', 'benchpress0'],
|
||||
['timeEnd', 'benchpress0', null],
|
||||
'readPerfLog'
|
||||
]);
|
||||
expect(data['scriptTime']).toBe(2);
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should restart timing', inject([AsyncTestCompleter], (async) => {
|
||||
var events = [
|
||||
[
|
||||
eventFactory.markStart('benchpress0', 0),
|
||||
eventFactory.markEnd('benchpress0', 1),
|
||||
eventFactory.markStart('benchpress1', 2),
|
||||
], [
|
||||
eventFactory.markEnd('benchpress1', 3)
|
||||
]
|
||||
];
|
||||
var metric = createMetric(events);
|
||||
metric.beginMeasure()
|
||||
.then( (_) => metric.endMeasure(true) )
|
||||
.then( (_) => metric.endMeasure(true) )
|
||||
.then( (_) => {
|
||||
expect(commandLog).toEqual([
|
||||
['timeBegin', 'benchpress0'],
|
||||
['timeEnd', 'benchpress0', 'benchpress1'],
|
||||
'readPerfLog',
|
||||
['timeEnd', 'benchpress1', 'benchpress2'],
|
||||
'readPerfLog'
|
||||
]);
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should loop and aggregate until the end mark is present', inject([AsyncTestCompleter], (async) => {
|
||||
var events = [
|
||||
[ 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()
|
||||
.then( (_) => metric.endMeasure(false) )
|
||||
.then( (data) => {
|
||||
expect(commandLog).toEqual([
|
||||
['timeBegin', 'benchpress0'],
|
||||
['timeEnd', 'benchpress0', null],
|
||||
'readPerfLog',
|
||||
[ 'setTimeout', 100 ],
|
||||
'readPerfLog',
|
||||
[ 'setTimeout', 100 ],
|
||||
'readPerfLog'
|
||||
]);
|
||||
expect(data['scriptTime']).toBe(3);
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should store events after the end mark for the next call', inject([AsyncTestCompleter], (async) => {
|
||||
var events = [
|
||||
[ 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()
|
||||
.then( (_) => metric.endMeasure(true) )
|
||||
.then( (data) => {
|
||||
expect(data['scriptTime']).toBe(0);
|
||||
return metric.endMeasure(true)
|
||||
})
|
||||
.then( (data) => {
|
||||
expect(commandLog).toEqual([
|
||||
['timeBegin', 'benchpress0'],
|
||||
['timeEnd', 'benchpress0', 'benchpress1'],
|
||||
'readPerfLog',
|
||||
['timeEnd', 'benchpress1', 'benchpress2'],
|
||||
'readPerfLog'
|
||||
]);
|
||||
expect(data['scriptTime']).toBe(3);
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
describe('with forced gc', () => {
|
||||
var events;
|
||||
beforeEach( () => {
|
||||
events = [
|
||||
[
|
||||
eventFactory.markStart('benchpress0', 0),
|
||||
eventFactory.start('script', 4),
|
||||
eventFactory.end('script', 6),
|
||||
eventFactory.markEnd('benchpress0', 10),
|
||||
eventFactory.markStart('benchpress1', 11),
|
||||
eventFactory.start('gc', 12, {'usedHeapSize': 2500}),
|
||||
eventFactory.end('gc', 15, {'usedHeapSize': 1000}),
|
||||
eventFactory.markEnd('benchpress1', 20)
|
||||
]
|
||||
];
|
||||
});
|
||||
|
||||
it('should measure forced gc', inject([AsyncTestCompleter], (async) => {
|
||||
var metric = createMetric(events, null, null, true);
|
||||
metric.beginMeasure()
|
||||
.then( (_) => metric.endMeasure(false) )
|
||||
.then( (data) => {
|
||||
expect(commandLog).toEqual([
|
||||
['gc'],
|
||||
['timeBegin', 'benchpress0'],
|
||||
['timeEnd', 'benchpress0', 'benchpress1'],
|
||||
'readPerfLog',
|
||||
['gc'],
|
||||
['timeEnd', 'benchpress1', null],
|
||||
'readPerfLog'
|
||||
]);
|
||||
expect(data['forcedGcTime']).toBe(3);
|
||||
expect(data['forcedGcAmount']).toBe(1.5);
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should restart after the forced gc if needed', inject([AsyncTestCompleter], (async) => {
|
||||
var metric = createMetric(events, null, null, true);
|
||||
metric.beginMeasure()
|
||||
.then( (_) => metric.endMeasure(true) )
|
||||
.then( (data) => {
|
||||
expect(commandLog[5]).toEqual(['timeEnd', 'benchpress1', 'benchpress2']);
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('aggregation', () => {
|
||||
|
||||
function aggregate(events, microMetrics = null) {
|
||||
ListWrapper.insert(events, 0, eventFactory.markStart('benchpress0', 0));
|
||||
ListWrapper.push(events, eventFactory.markEnd('benchpress0', 10));
|
||||
var metric = createMetric([events], microMetrics);
|
||||
return metric
|
||||
.beginMeasure().then( (_) => metric.endMeasure(false) );
|
||||
}
|
||||
|
||||
|
||||
it('should report a single interval', inject([AsyncTestCompleter], (async) => {
|
||||
aggregate([
|
||||
eventFactory.start('script', 0),
|
||||
eventFactory.end('script', 5)
|
||||
]).then((data) => {
|
||||
expect(data['scriptTime']).toBe(5);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should sum up multiple intervals', inject([AsyncTestCompleter], (async) => {
|
||||
aggregate([
|
||||
eventFactory.start('script', 0),
|
||||
eventFactory.end('script', 5),
|
||||
eventFactory.start('script', 10),
|
||||
eventFactory.end('script', 17)
|
||||
]).then((data) => {
|
||||
expect(data['scriptTime']).toBe(12);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should ignore not started intervals', inject([AsyncTestCompleter], (async) => {
|
||||
aggregate([
|
||||
eventFactory.end('script', 10)
|
||||
]).then((data) => {
|
||||
expect(data['scriptTime']).toBe(0);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should ignore not ended intervals', inject([AsyncTestCompleter], (async) => {
|
||||
aggregate([
|
||||
eventFactory.start('script', 10)
|
||||
]).then((data) => {
|
||||
expect(data['scriptTime']).toBe(0);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should ignore events from different processed as the start mark', inject([AsyncTestCompleter], (async) => {
|
||||
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['scriptTime']).toBe(5);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should support scriptTime metric', inject([AsyncTestCompleter], (async) => {
|
||||
aggregate([
|
||||
eventFactory.start('script', 0),
|
||||
eventFactory.end('script', 5)
|
||||
]).then((data) => {
|
||||
expect(data['scriptTime']).toBe(5);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should support renderTime metric', inject([AsyncTestCompleter], (async) => {
|
||||
aggregate([
|
||||
eventFactory.start('render', 0),
|
||||
eventFactory.end('render', 5)
|
||||
]).then((data) => {
|
||||
expect(data['renderTime']).toBe(5);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should support gcTime/gcAmount metric', inject([AsyncTestCompleter], (async) => {
|
||||
aggregate([
|
||||
eventFactory.start('gc', 0, {'usedHeapSize': 2500}),
|
||||
eventFactory.end('gc', 5, {'usedHeapSize': 1000})
|
||||
]).then((data) => {
|
||||
expect(data['gcTime']).toBe(5);
|
||||
expect(data['gcAmount']).toBe(1.5);
|
||||
expect(data['majorGcTime']).toBe(0);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should support majorGcTime metric', inject([AsyncTestCompleter], (async) => {
|
||||
aggregate([
|
||||
eventFactory.start('gc', 0, {'usedHeapSize': 2500}),
|
||||
eventFactory.end('gc', 5, {'usedHeapSize': 1000, 'majorGc': true})
|
||||
]).then((data) => {
|
||||
expect(data['gcTime']).toBe(5);
|
||||
expect(data['majorGcTime']).toBe(5);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should support pureScriptTime = scriptTime-gcTime-renderTime', inject([AsyncTestCompleter], (async) => {
|
||||
aggregate([
|
||||
eventFactory.start('script', 0),
|
||||
eventFactory.start('gc', 1, {'usedHeapSize': 1000}),
|
||||
eventFactory.end('gc', 4, {'usedHeapSize': 0}),
|
||||
eventFactory.start('render', 4),
|
||||
eventFactory.end('render', 5),
|
||||
eventFactory.end('script', 6)
|
||||
]).then((data) => {
|
||||
expect(data['scriptTime']).toBe(6);
|
||||
expect(data['pureScriptTime']).toBe(2);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
describe('microMetrics', () => {
|
||||
|
||||
it('should report micro metrics', inject([AsyncTestCompleter], (async) => {
|
||||
aggregate([
|
||||
eventFactory.markStart('mm1', 0),
|
||||
eventFactory.markEnd('mm1', 5),
|
||||
], {'mm1': 'micro metric 1'}).then((data) => {
|
||||
expect(data['mm1']).toBe(5.0);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should ignore micro metrics that were not specified', inject([AsyncTestCompleter], (async) => {
|
||||
aggregate([
|
||||
eventFactory.markStart('mm1', 0),
|
||||
eventFactory.markEnd('mm1', 5),
|
||||
]).then((data) => {
|
||||
expect(data['mm1']).toBeFalsy();
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should report micro metric averages', inject([AsyncTestCompleter], (async) => {
|
||||
aggregate([
|
||||
eventFactory.markStart('mm1*20', 0),
|
||||
eventFactory.markEnd('mm1*20', 5),
|
||||
], {'mm1': 'micro metric 1'}).then((data) => {
|
||||
expect(data['mm1']).toBe(5/20);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
class MockDriverExtension extends WebDriverExtension {
|
||||
_perfLogs:List;
|
||||
_commandLog:List;
|
||||
_perfLogFeatures:PerfLogFeatures;
|
||||
constructor(perfLogs, commandLog, perfLogFeatures) {
|
||||
super();
|
||||
this._perfLogs = perfLogs;
|
||||
this._commandLog = commandLog;
|
||||
this._perfLogFeatures = perfLogFeatures;
|
||||
}
|
||||
|
||||
timeBegin(name):Promise {
|
||||
ListWrapper.push(this._commandLog, ['timeBegin', name]);
|
||||
return PromiseWrapper.resolve(null);
|
||||
}
|
||||
|
||||
timeEnd(name, restartName):Promise {
|
||||
ListWrapper.push(this._commandLog, ['timeEnd', name, restartName]);
|
||||
return PromiseWrapper.resolve(null);
|
||||
}
|
||||
|
||||
perfLogFeatures():PerfLogFeatures {
|
||||
return this._perfLogFeatures;
|
||||
}
|
||||
|
||||
readPerfLog():Promise {
|
||||
ListWrapper.push(this._commandLog, 'readPerfLog');
|
||||
if (this._perfLogs.length > 0) {
|
||||
var next = this._perfLogs[0];
|
||||
ListWrapper.removeAt(this._perfLogs, 0);
|
||||
return PromiseWrapper.resolve(next);
|
||||
} else {
|
||||
return PromiseWrapper.resolve([]);
|
||||
}
|
||||
}
|
||||
|
||||
gc():Promise {
|
||||
ListWrapper.push(this._commandLog, ['gc']);
|
||||
return PromiseWrapper.resolve(null);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,501 @@
|
|||
import {
|
||||
afterEach,
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import {List, ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
|
||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
|
||||
import {
|
||||
Metric,
|
||||
PerflogMetric,
|
||||
WebDriverExtension,
|
||||
PerfLogFeatures,
|
||||
bind,
|
||||
Injector,
|
||||
Options
|
||||
} from 'benchpress/common';
|
||||
|
||||
import {TraceEventFactory} from '../trace_event_factory';
|
||||
|
||||
export function main() {
|
||||
var commandLog;
|
||||
var eventFactory = new TraceEventFactory('timeline', 'pid0');
|
||||
|
||||
function createMetric(perfLogs, microMetrics = null, perfLogFeatures = null, forceGc = null) {
|
||||
commandLog = [];
|
||||
if (isBlank(perfLogFeatures)) {
|
||||
perfLogFeatures = new PerfLogFeatures({render: true, gc: true});
|
||||
}
|
||||
if (isBlank(microMetrics)) {
|
||||
microMetrics = StringMapWrapper.create();
|
||||
}
|
||||
var bindings = [
|
||||
Options.DEFAULT_BINDINGS,
|
||||
PerflogMetric.BINDINGS,
|
||||
bind(Options.MICRO_METRICS).toValue(microMetrics),
|
||||
bind(PerflogMetric.SET_TIMEOUT)
|
||||
.toValue((fn, millis) =>
|
||||
{
|
||||
ListWrapper.push(commandLog, ['setTimeout', millis]);
|
||||
fn();
|
||||
}),
|
||||
bind(WebDriverExtension)
|
||||
.toValue(new MockDriverExtension(perfLogs, commandLog, perfLogFeatures))
|
||||
];
|
||||
if (isPresent(forceGc)) {
|
||||
ListWrapper.push(bindings, bind(Options.FORCE_GC).toValue(forceGc));
|
||||
}
|
||||
return Injector.resolveAndCreate(bindings).get(PerflogMetric);
|
||||
}
|
||||
|
||||
describe('perflog metric', () => {
|
||||
|
||||
function sortedKeys(stringMap) {
|
||||
var res = [];
|
||||
StringMapWrapper.forEach(stringMap, (_, key) => { ListWrapper.push(res, key); });
|
||||
res.sort();
|
||||
return res;
|
||||
}
|
||||
|
||||
it('should describe itself based on the perfLogFeatrues', () => {
|
||||
expect(sortedKeys(createMetric([[]], null, new PerfLogFeatures()).describe()))
|
||||
.toEqual(['pureScriptTime', 'scriptTime']);
|
||||
|
||||
expect(sortedKeys(createMetric([[]], null, new PerfLogFeatures({render: true, gc: false}))
|
||||
.describe()))
|
||||
.toEqual(['pureScriptTime', 'renderTime', 'scriptTime']);
|
||||
|
||||
expect(sortedKeys(createMetric([[]]).describe()))
|
||||
.toEqual(
|
||||
['gcAmount', 'gcTime', 'majorGcTime', 'pureScriptTime', 'renderTime', 'scriptTime']);
|
||||
|
||||
expect(
|
||||
sortedKeys(createMetric([[]], null, new PerfLogFeatures({render: true, gc: true}), true)
|
||||
.describe()))
|
||||
.toEqual([
|
||||
'forcedGcAmount',
|
||||
'forcedGcTime',
|
||||
'gcAmount',
|
||||
'gcTime',
|
||||
'majorGcTime',
|
||||
'pureScriptTime',
|
||||
'renderTime',
|
||||
'scriptTime'
|
||||
]);
|
||||
});
|
||||
|
||||
it('should describe itself based on micro metrics', () => {
|
||||
var description = createMetric([[]], {'myMicroMetric': 'someDesc'}).describe();
|
||||
expect(description['myMicroMetric']).toEqual('someDesc');
|
||||
});
|
||||
|
||||
describe('beginMeasure', () => {
|
||||
|
||||
it('should not force gc and mark the timeline', inject([AsyncTestCompleter], (async) => {
|
||||
var metric = createMetric([[]]);
|
||||
metric.beginMeasure().then((_) => {
|
||||
expect(commandLog).toEqual([['timeBegin', 'benchpress0']]);
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should force gc and mark the timeline', inject([AsyncTestCompleter], (async) => {
|
||||
var metric = createMetric([[]], null, null, true);
|
||||
metric.beginMeasure().then((_) => {
|
||||
expect(commandLog).toEqual([['gc'], ['timeBegin', 'benchpress0']]);
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
describe('endMeasure', () => {
|
||||
|
||||
it('should mark and aggregate events in between the marks',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
var events = [[
|
||||
eventFactory.markStart('benchpress0', 0),
|
||||
eventFactory.start('script', 4),
|
||||
eventFactory.end('script', 6),
|
||||
eventFactory.markEnd('benchpress0', 10)
|
||||
]];
|
||||
var metric = createMetric(events);
|
||||
metric.beginMeasure()
|
||||
.then((_) => metric.endMeasure(false))
|
||||
.then((data) => {
|
||||
expect(commandLog)
|
||||
.toEqual([
|
||||
['timeBegin', 'benchpress0'],
|
||||
['timeEnd', 'benchpress0', null],
|
||||
'readPerfLog'
|
||||
]);
|
||||
expect(data['scriptTime']).toBe(2);
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should restart timing', inject([AsyncTestCompleter], (async) => {
|
||||
var events = [
|
||||
[
|
||||
eventFactory.markStart('benchpress0', 0),
|
||||
eventFactory.markEnd('benchpress0', 1),
|
||||
eventFactory.markStart('benchpress1', 2),
|
||||
],
|
||||
[eventFactory.markEnd('benchpress1', 3)]
|
||||
];
|
||||
var metric = createMetric(events);
|
||||
metric.beginMeasure()
|
||||
.then((_) => metric.endMeasure(true))
|
||||
.then((_) => metric.endMeasure(true))
|
||||
.then((_) => {
|
||||
expect(commandLog)
|
||||
.toEqual([
|
||||
['timeBegin', 'benchpress0'],
|
||||
['timeEnd', 'benchpress0', 'benchpress1'],
|
||||
'readPerfLog',
|
||||
['timeEnd', 'benchpress1', 'benchpress2'],
|
||||
'readPerfLog'
|
||||
]);
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should loop and aggregate until the end mark is present',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
var events = [
|
||||
[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()
|
||||
.then((_) => metric.endMeasure(false))
|
||||
.then((data) => {
|
||||
expect(commandLog)
|
||||
.toEqual([
|
||||
['timeBegin', 'benchpress0'],
|
||||
['timeEnd', 'benchpress0', null],
|
||||
'readPerfLog',
|
||||
['setTimeout', 100],
|
||||
'readPerfLog',
|
||||
['setTimeout', 100],
|
||||
'readPerfLog'
|
||||
]);
|
||||
expect(data['scriptTime']).toBe(3);
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should store events after the end mark for the next call',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
var events = [
|
||||
[
|
||||
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()
|
||||
.then((_) => metric.endMeasure(true))
|
||||
.then((data) =>
|
||||
{
|
||||
expect(data['scriptTime']).toBe(0);
|
||||
return metric.endMeasure(true)
|
||||
})
|
||||
.then((data) => {
|
||||
expect(commandLog)
|
||||
.toEqual([
|
||||
['timeBegin', 'benchpress0'],
|
||||
['timeEnd', 'benchpress0', 'benchpress1'],
|
||||
'readPerfLog',
|
||||
['timeEnd', 'benchpress1', 'benchpress2'],
|
||||
'readPerfLog'
|
||||
]);
|
||||
expect(data['scriptTime']).toBe(3);
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
describe('with forced gc', () => {
|
||||
var events;
|
||||
beforeEach(() => {
|
||||
events = [[
|
||||
eventFactory.markStart('benchpress0', 0),
|
||||
eventFactory.start('script', 4),
|
||||
eventFactory.end('script', 6),
|
||||
eventFactory.markEnd('benchpress0', 10),
|
||||
eventFactory.markStart('benchpress1', 11),
|
||||
eventFactory.start('gc', 12, {'usedHeapSize': 2500}),
|
||||
eventFactory.end('gc', 15, {'usedHeapSize': 1000}),
|
||||
eventFactory.markEnd('benchpress1', 20)
|
||||
]];
|
||||
});
|
||||
|
||||
it('should measure forced gc', inject([AsyncTestCompleter], (async) => {
|
||||
var metric = createMetric(events, null, null, true);
|
||||
metric.beginMeasure()
|
||||
.then((_) => metric.endMeasure(false))
|
||||
.then((data) => {
|
||||
expect(commandLog)
|
||||
.toEqual([
|
||||
['gc'],
|
||||
['timeBegin', 'benchpress0'],
|
||||
['timeEnd', 'benchpress0', 'benchpress1'],
|
||||
'readPerfLog',
|
||||
['gc'],
|
||||
['timeEnd', 'benchpress1', null],
|
||||
'readPerfLog'
|
||||
]);
|
||||
expect(data['forcedGcTime']).toBe(3);
|
||||
expect(data['forcedGcAmount']).toBe(1.5);
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should restart after the forced gc if needed', inject([AsyncTestCompleter], (async) => {
|
||||
var metric = createMetric(events, null, null, true);
|
||||
metric.beginMeasure()
|
||||
.then((_) => metric.endMeasure(true))
|
||||
.then((data) => {
|
||||
expect(commandLog[5]).toEqual(['timeEnd', 'benchpress1', 'benchpress2']);
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('aggregation', () => {
|
||||
|
||||
function aggregate(events, microMetrics = null) {
|
||||
ListWrapper.insert(events, 0, eventFactory.markStart('benchpress0', 0));
|
||||
ListWrapper.push(events, eventFactory.markEnd('benchpress0', 10));
|
||||
var metric = createMetric([events], microMetrics);
|
||||
return metric.beginMeasure().then((_) => metric.endMeasure(false));
|
||||
}
|
||||
|
||||
|
||||
it('should report a single interval', inject([AsyncTestCompleter], (async) => {
|
||||
aggregate([eventFactory.start('script', 0), eventFactory.end('script', 5)])
|
||||
.then((data) => {
|
||||
expect(data['scriptTime']).toBe(5);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should sum up multiple intervals', inject([AsyncTestCompleter], (async) => {
|
||||
aggregate([
|
||||
eventFactory.start('script', 0),
|
||||
eventFactory.end('script', 5),
|
||||
eventFactory.start('script', 10),
|
||||
eventFactory.end('script', 17)
|
||||
])
|
||||
.then((data) => {
|
||||
expect(data['scriptTime']).toBe(12);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should ignore not started intervals', inject([AsyncTestCompleter], (async) => {
|
||||
aggregate([eventFactory.end('script', 10)])
|
||||
.then((data) => {
|
||||
expect(data['scriptTime']).toBe(0);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should ignore not ended intervals', inject([AsyncTestCompleter], (async) => {
|
||||
aggregate([eventFactory.start('script', 10)])
|
||||
.then((data) => {
|
||||
expect(data['scriptTime']).toBe(0);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should ignore events from different processed as the start mark',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
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['scriptTime']).toBe(5);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should support scriptTime metric', inject([AsyncTestCompleter], (async) => {
|
||||
aggregate([eventFactory.start('script', 0), eventFactory.end('script', 5)])
|
||||
.then((data) => {
|
||||
expect(data['scriptTime']).toBe(5);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should support renderTime metric', inject([AsyncTestCompleter], (async) => {
|
||||
aggregate([eventFactory.start('render', 0), eventFactory.end('render', 5)])
|
||||
.then((data) => {
|
||||
expect(data['renderTime']).toBe(5);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should support gcTime/gcAmount metric', inject([AsyncTestCompleter], (async) => {
|
||||
aggregate([
|
||||
eventFactory.start('gc', 0, {'usedHeapSize': 2500}),
|
||||
eventFactory.end('gc', 5, {'usedHeapSize': 1000})
|
||||
])
|
||||
.then((data) => {
|
||||
expect(data['gcTime']).toBe(5);
|
||||
expect(data['gcAmount']).toBe(1.5);
|
||||
expect(data['majorGcTime']).toBe(0);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should support majorGcTime metric', inject([AsyncTestCompleter], (async) => {
|
||||
aggregate([
|
||||
eventFactory.start('gc', 0, {'usedHeapSize': 2500}),
|
||||
eventFactory.end('gc', 5, {'usedHeapSize': 1000, 'majorGc': true})
|
||||
])
|
||||
.then((data) => {
|
||||
expect(data['gcTime']).toBe(5);
|
||||
expect(data['majorGcTime']).toBe(5);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should support pureScriptTime = scriptTime-gcTime-renderTime',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
aggregate([
|
||||
eventFactory.start('script', 0),
|
||||
eventFactory.start('gc', 1, {'usedHeapSize': 1000}),
|
||||
eventFactory.end('gc', 4, {'usedHeapSize': 0}),
|
||||
eventFactory.start('render', 4),
|
||||
eventFactory.end('render', 5),
|
||||
eventFactory.end('script', 6)
|
||||
])
|
||||
.then((data) => {
|
||||
expect(data['scriptTime']).toBe(6);
|
||||
expect(data['pureScriptTime']).toBe(2);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
describe('microMetrics', () => {
|
||||
|
||||
it('should report micro metrics', inject([AsyncTestCompleter], (async) => {
|
||||
aggregate([
|
||||
eventFactory.markStart('mm1', 0),
|
||||
eventFactory.markEnd('mm1', 5),
|
||||
],
|
||||
{'mm1': 'micro metric 1'})
|
||||
.then((data) => {
|
||||
expect(data['mm1']).toBe(5.0);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should ignore micro metrics that were not specified',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
aggregate([
|
||||
eventFactory.markStart('mm1', 0),
|
||||
eventFactory.markEnd('mm1', 5),
|
||||
])
|
||||
.then((data) => {
|
||||
expect(data['mm1']).toBeFalsy();
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should report micro metric averages', inject([AsyncTestCompleter], (async) => {
|
||||
aggregate([
|
||||
eventFactory.markStart('mm1*20', 0),
|
||||
eventFactory.markEnd('mm1*20', 5),
|
||||
],
|
||||
{'mm1': 'micro metric 1'})
|
||||
.then((data) => {
|
||||
expect(data['mm1']).toBe(5 / 20);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
class MockDriverExtension extends WebDriverExtension {
|
||||
constructor(private _perfLogs: List<any>, private _commandLog: List<any>,
|
||||
private _perfLogFeatures: PerfLogFeatures) {
|
||||
super();
|
||||
}
|
||||
|
||||
timeBegin(name): Promise<any> {
|
||||
ListWrapper.push(this._commandLog, ['timeBegin', name]);
|
||||
return PromiseWrapper.resolve(null);
|
||||
}
|
||||
|
||||
timeEnd(name, restartName): Promise<any> {
|
||||
ListWrapper.push(this._commandLog, ['timeEnd', name, restartName]);
|
||||
return PromiseWrapper.resolve(null);
|
||||
}
|
||||
|
||||
perfLogFeatures(): PerfLogFeatures { return this._perfLogFeatures; }
|
||||
|
||||
readPerfLog(): Promise<any> {
|
||||
ListWrapper.push(this._commandLog, 'readPerfLog');
|
||||
if (this._perfLogs.length > 0) {
|
||||
var next = this._perfLogs[0];
|
||||
ListWrapper.removeAt(this._perfLogs, 0);
|
||||
return PromiseWrapper.resolve(next);
|
||||
} else {
|
||||
return PromiseWrapper.resolve([]);
|
||||
}
|
||||
}
|
||||
|
||||
gc(): Promise<any> {
|
||||
ListWrapper.push(this._commandLog, ['gc']);
|
||||
return PromiseWrapper.resolve(null);
|
||||
}
|
||||
}
|
|
@ -1,11 +1,16 @@
|
|||
import {describe, ddescribe, it, iit, xit, expect, beforeEach, afterEach} from 'angular2/test_lib';
|
||||
|
||||
import { isBlank, isPresent, Date, DateWrapper } from 'angular2/src/facade/lang';
|
||||
import { List, ListWrapper } from 'angular2/src/facade/collection';
|
||||
import {isBlank, isPresent, Date, DateWrapper} from 'angular2/src/facade/lang';
|
||||
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import {
|
||||
SampleState, Reporter, bind, Injector,
|
||||
ConsoleReporter, SampleDescription, MeasureValues
|
||||
SampleState,
|
||||
Reporter,
|
||||
bind,
|
||||
Injector,
|
||||
ConsoleReporter,
|
||||
SampleDescription,
|
||||
MeasureValues
|
||||
} from 'benchpress/common';
|
||||
|
||||
export function main() {
|
||||
|
@ -13,7 +18,9 @@ export function main() {
|
|||
var reporter;
|
||||
var log;
|
||||
|
||||
function createReporter({columnWidth, sampleId, descriptions, metrics}) {
|
||||
function createReporter({columnWidth = null, sampleId = null, descriptions = null, metrics = null}:{
|
||||
columnWidth?, sampleId?, descriptions?, metrics?
|
||||
}) {
|
||||
log = [];
|
||||
if (isBlank(descriptions)) {
|
||||
descriptions = [];
|
||||
|
@ -36,14 +43,8 @@ export function main() {
|
|||
createReporter({
|
||||
columnWidth: 8,
|
||||
sampleId: 'someSample',
|
||||
descriptions: [{
|
||||
'a': 1,
|
||||
'b': 2
|
||||
}],
|
||||
metrics: {
|
||||
'm1': 'some desc',
|
||||
'm2': 'some other desc'
|
||||
}
|
||||
descriptions: [{'a': 1, 'b': 2}],
|
||||
metrics: {'m1': 'some desc', 'm2': 'some other desc'}
|
||||
});
|
||||
expect(log).toEqual([
|
||||
'BENCHMARK someSample',
|
||||
|
@ -60,56 +61,24 @@ export function main() {
|
|||
});
|
||||
|
||||
it('should print a table row', () => {
|
||||
createReporter({
|
||||
columnWidth: 8,
|
||||
metrics: {
|
||||
'a': '',
|
||||
'b': ''
|
||||
}
|
||||
});
|
||||
createReporter({columnWidth: 8, metrics: {'a': '', 'b': ''}});
|
||||
log = [];
|
||||
reporter.reportMeasureValues(mv(0, 0, {
|
||||
'a': 1.23, 'b': 2
|
||||
}));
|
||||
expect(log).toEqual([
|
||||
' 1.23 | 2.00'
|
||||
]);
|
||||
reporter.reportMeasureValues(mv(0, 0, {'a': 1.23, 'b': 2}));
|
||||
expect(log).toEqual([' 1.23 | 2.00']);
|
||||
});
|
||||
|
||||
it('should print the table footer and stats when there is a valid sample', () => {
|
||||
createReporter({
|
||||
columnWidth: 8,
|
||||
metrics: {
|
||||
'a': '',
|
||||
'b': ''
|
||||
}
|
||||
});
|
||||
createReporter({columnWidth: 8, metrics: {'a': '', 'b': ''}});
|
||||
log = [];
|
||||
reporter.reportSample([], [mv(0,0,{
|
||||
'a': 3, 'b': 6
|
||||
}), mv(1,1,{
|
||||
'a': 5, 'b': 9
|
||||
})]);
|
||||
expect(log).toEqual([
|
||||
'======== | ========',
|
||||
'4.00+-25% | 7.50+-20%'
|
||||
]);
|
||||
reporter.reportSample([], [mv(0, 0, {'a': 3, 'b': 6}), mv(1, 1, {'a': 5, 'b': 9})]);
|
||||
expect(log).toEqual(['======== | ========', '4.00+-25% | 7.50+-20%']);
|
||||
});
|
||||
|
||||
it('should print the coefficient of variation only when it is meaningful', () => {
|
||||
createReporter({
|
||||
columnWidth: 8,
|
||||
metrics: { 'a': '', 'b': '' }
|
||||
});
|
||||
createReporter({columnWidth: 8, metrics: {'a': '', 'b': ''}});
|
||||
log = [];
|
||||
reporter.reportSample([], [
|
||||
mv(0, 0, { 'a': 3, 'b': 0 }),
|
||||
mv(1, 1, { 'a': 5, 'b': 0 })
|
||||
]);
|
||||
expect(log).toEqual([
|
||||
'======== | ========',
|
||||
'4.00+-25% | 0.00'
|
||||
]);
|
||||
reporter.reportSample([], [mv(0, 0, {'a': 3, 'b': 0}), mv(1, 1, {'a': 5, 'b': 0})]);
|
||||
expect(log).toEqual(['======== | ========', '4.00+-25% | 0.00']);
|
||||
});
|
||||
|
||||
});
|
|
@ -1,109 +0,0 @@
|
|||
import {
|
||||
afterEach,
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import { DateWrapper, Json, RegExpWrapper, isPresent } from 'angular2/src/facade/lang';
|
||||
import { PromiseWrapper } from 'angular2/src/facade/async';
|
||||
|
||||
import {
|
||||
bind, Injector,
|
||||
SampleDescription,
|
||||
MeasureValues,
|
||||
Options
|
||||
} from 'benchpress/common';
|
||||
|
||||
|
||||
import { JsonFileReporter } from 'benchpress/src/reporter/json_file_reporter';
|
||||
|
||||
export function main() {
|
||||
describe('file reporter', () => {
|
||||
var loggedFile;
|
||||
|
||||
function createReporter({sampleId, descriptions, metrics, path}) {
|
||||
var bindings = [
|
||||
JsonFileReporter.BINDINGS,
|
||||
bind(SampleDescription).toValue(new SampleDescription(sampleId, descriptions, metrics)),
|
||||
bind(JsonFileReporter.PATH).toValue(path),
|
||||
bind(Options.NOW).toValue( () => DateWrapper.fromMillis(1234) ),
|
||||
bind(Options.WRITE_FILE).toValue((filename, content) => {
|
||||
loggedFile = {
|
||||
'filename': filename,
|
||||
'content': content
|
||||
};
|
||||
return PromiseWrapper.resolve(null);
|
||||
})
|
||||
];
|
||||
return Injector.resolveAndCreate(bindings).get(JsonFileReporter);
|
||||
}
|
||||
|
||||
it('should write all data into a file', inject([AsyncTestCompleter], (async) => {
|
||||
createReporter({
|
||||
sampleId: 'someId',
|
||||
descriptions: [{ 'a': 2 }],
|
||||
path: 'somePath',
|
||||
metrics: {
|
||||
'script': 'script time'
|
||||
}
|
||||
}).reportSample([
|
||||
mv(0, 0, { 'a': 3, 'b': 6})
|
||||
], [mv(0, 0, {
|
||||
'a': 3, 'b': 6
|
||||
}), mv(1, 1, {
|
||||
'a': 5, 'b': 9
|
||||
})]);
|
||||
var regExp = RegExpWrapper.create('somePath/someId_\\d+\\.json');
|
||||
expect(isPresent(RegExpWrapper.firstMatch(regExp, loggedFile['filename']))).toBe(true);
|
||||
var parsedContent = Json.parse(loggedFile['content']);
|
||||
expect(parsedContent).toEqual({
|
||||
"description": {
|
||||
"id": "someId",
|
||||
"description": {
|
||||
"a": 2
|
||||
},
|
||||
"metrics": {"script": "script time"}
|
||||
},
|
||||
"completeSample": [{
|
||||
"timeStamp": "1970-01-01T00:00:00.000Z",
|
||||
"runIndex": 0,
|
||||
"values": {
|
||||
"a": 3,
|
||||
"b": 6
|
||||
}
|
||||
}],
|
||||
"validSample": [
|
||||
{
|
||||
"timeStamp": "1970-01-01T00:00:00.000Z",
|
||||
"runIndex": 0,
|
||||
"values": {
|
||||
"a": 3,
|
||||
"b": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
"timeStamp": "1970-01-01T00:00:00.001Z",
|
||||
"runIndex": 1,
|
||||
"values": {
|
||||
"a": 5,
|
||||
"b": 9
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
async.done();
|
||||
}));
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function mv(runIndex, time, values) {
|
||||
return new MeasureValues(runIndex, DateWrapper.fromMillis(time), values);
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
import {
|
||||
afterEach,
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import {DateWrapper, Json, RegExpWrapper, isPresent} from 'angular2/src/facade/lang';
|
||||
import {PromiseWrapper} from 'angular2/src/facade/async';
|
||||
|
||||
import {bind, Injector, SampleDescription, MeasureValues, Options} from 'benchpress/common';
|
||||
|
||||
|
||||
import {JsonFileReporter} from 'benchpress/src/reporter/json_file_reporter';
|
||||
|
||||
export function main() {
|
||||
describe('file reporter', () => {
|
||||
var loggedFile;
|
||||
|
||||
function createReporter({sampleId, descriptions, metrics, path}) {
|
||||
var bindings = [
|
||||
JsonFileReporter.BINDINGS,
|
||||
bind(SampleDescription).toValue(new SampleDescription(sampleId, descriptions, metrics)),
|
||||
bind(JsonFileReporter.PATH).toValue(path),
|
||||
bind(Options.NOW).toValue(() => DateWrapper.fromMillis(1234)),
|
||||
bind(Options.WRITE_FILE)
|
||||
.toValue((filename, content) =>
|
||||
{
|
||||
loggedFile = {
|
||||
'filename': filename,
|
||||
'content': content
|
||||
};
|
||||
return PromiseWrapper.resolve(null);
|
||||
})
|
||||
];
|
||||
return Injector.resolveAndCreate(bindings).get(JsonFileReporter);
|
||||
}
|
||||
|
||||
it('should write all data into a file', inject([AsyncTestCompleter], (async) => {
|
||||
createReporter({
|
||||
sampleId: 'someId',
|
||||
descriptions: [{'a': 2}],
|
||||
path: 'somePath',
|
||||
metrics: {'script': 'script time'}
|
||||
})
|
||||
.reportSample([mv(0, 0, {'a': 3, 'b': 6})],
|
||||
[mv(0, 0, {'a': 3, 'b': 6}), mv(1, 1, {'a': 5, 'b': 9})]);
|
||||
var regExp = RegExpWrapper.create('somePath/someId_\\d+\\.json');
|
||||
expect(isPresent(RegExpWrapper.firstMatch(regExp, loggedFile['filename']))).toBe(true);
|
||||
var parsedContent = Json.parse(loggedFile['content']);
|
||||
expect(parsedContent)
|
||||
.toEqual({
|
||||
"description":
|
||||
{"id": "someId", "description": {"a": 2}, "metrics": {"script": "script time"}},
|
||||
"completeSample": [{
|
||||
"timeStamp": "1970-01-01T00:00:00.000Z",
|
||||
"runIndex": 0,
|
||||
"values": {"a": 3, "b": 6}
|
||||
}],
|
||||
"validSample": [
|
||||
{
|
||||
"timeStamp": "1970-01-01T00:00:00.000Z",
|
||||
"runIndex": 0,
|
||||
"values": {"a": 3, "b": 6}
|
||||
},
|
||||
{
|
||||
"timeStamp": "1970-01-01T00:00:00.001Z",
|
||||
"runIndex": 1,
|
||||
"values": {"a": 5, "b": 9}
|
||||
}
|
||||
]
|
||||
});
|
||||
async.done();
|
||||
}));
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function mv(runIndex, time, values) {
|
||||
return new MeasureValues(runIndex, DateWrapper.fromMillis(time), values);
|
||||
}
|
|
@ -1,89 +0,0 @@
|
|||
import {
|
||||
afterEach,
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import { List, ListWrapper, StringMap } from 'angular2/src/facade/collection';
|
||||
import { PromiseWrapper, Promise } from 'angular2/src/facade/async';
|
||||
import { DateWrapper } from 'angular2/src/facade/lang';
|
||||
|
||||
import { Reporter, MultiReporter, bind, Injector, MeasureValues } from 'benchpress/common';
|
||||
|
||||
export function main() {
|
||||
function createReporters(ids) {
|
||||
return Injector.resolveAndCreate([
|
||||
ListWrapper.map(ids, (id) => bind(id).toValue(new MockReporter(id)) ),
|
||||
MultiReporter.createBindings(ids)
|
||||
]).asyncGet(MultiReporter);
|
||||
}
|
||||
|
||||
describe('multi reporter', () => {
|
||||
|
||||
it('should reportMeasureValues to all', inject([AsyncTestCompleter], (async) => {
|
||||
var mv = new MeasureValues(0, DateWrapper.now(), {});
|
||||
createReporters(['m1', 'm2'])
|
||||
.then( (r) => r.reportMeasureValues(mv) )
|
||||
.then( (values) => {
|
||||
|
||||
expect(values).toEqual([
|
||||
{'id': 'm1', 'values': mv},
|
||||
{'id': 'm2', 'values': mv}
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should reportSample to call', inject([AsyncTestCompleter], (async) => {
|
||||
var completeSample = [
|
||||
new MeasureValues(0, DateWrapper.now(), {}),
|
||||
new MeasureValues(1, DateWrapper.now(), {})
|
||||
];
|
||||
var validSample = [completeSample[1]];
|
||||
|
||||
createReporters(['m1', 'm2'])
|
||||
.then( (r) => r.reportSample(completeSample, validSample) )
|
||||
.then( (values) => {
|
||||
|
||||
expect(values).toEqual([
|
||||
{'id': 'm1', 'completeSample': completeSample, 'validSample': validSample},
|
||||
{'id': 'm2', 'completeSample': completeSample, 'validSample': validSample}
|
||||
]);
|
||||
async.done();
|
||||
})
|
||||
}));
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
class MockReporter extends Reporter {
|
||||
_id:string;
|
||||
|
||||
constructor(id) {
|
||||
super();
|
||||
this._id = id;
|
||||
}
|
||||
|
||||
reportMeasureValues(values:MeasureValues):Promise {
|
||||
return PromiseWrapper.resolve({
|
||||
'id': this._id,
|
||||
'values': values
|
||||
});
|
||||
}
|
||||
|
||||
reportSample(completeSample:List<MeasureValues>, validSample:List<MeasureValues>):Promise {
|
||||
return PromiseWrapper.resolve({
|
||||
'id': this._id,
|
||||
'completeSample': completeSample,
|
||||
'validSample': validSample
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
import {
|
||||
afterEach,
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import {List, ListWrapper, StringMap} from 'angular2/src/facade/collection';
|
||||
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
|
||||
import {DateWrapper} from 'angular2/src/facade/lang';
|
||||
|
||||
import {Reporter, MultiReporter, bind, Injector, MeasureValues} from 'benchpress/common';
|
||||
|
||||
export function main() {
|
||||
function createReporters(ids) {
|
||||
return Injector.resolveAndCreate([
|
||||
ListWrapper.map(ids, (id) => bind(id).toValue(new MockReporter(id))),
|
||||
MultiReporter.createBindings(ids)
|
||||
])
|
||||
.asyncGet(MultiReporter);
|
||||
}
|
||||
|
||||
describe('multi reporter', () => {
|
||||
|
||||
it('should reportMeasureValues to all', inject([AsyncTestCompleter], (async) => {
|
||||
var mv = new MeasureValues(0, DateWrapper.now(), {});
|
||||
createReporters(['m1', 'm2'])
|
||||
.then((r) => r.reportMeasureValues(mv))
|
||||
.then((values) => {
|
||||
|
||||
expect(values).toEqual([{'id': 'm1', 'values': mv}, {'id': 'm2', 'values': mv}]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should reportSample to call', inject([AsyncTestCompleter], (async) => {
|
||||
var completeSample = [
|
||||
new MeasureValues(0, DateWrapper.now(), {}),
|
||||
new MeasureValues(1, DateWrapper.now(), {})
|
||||
];
|
||||
var validSample = [completeSample[1]];
|
||||
|
||||
createReporters(['m1', 'm2'])
|
||||
.then((r) => r.reportSample(completeSample, validSample))
|
||||
.then((values) => {
|
||||
|
||||
expect(values).toEqual([
|
||||
{'id': 'm1', 'completeSample': completeSample, 'validSample': validSample},
|
||||
{'id': 'm2', 'completeSample': completeSample, 'validSample': validSample}
|
||||
]);
|
||||
async.done();
|
||||
})
|
||||
}));
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
class MockReporter extends Reporter {
|
||||
constructor(private _id: string) { super(); }
|
||||
|
||||
reportMeasureValues(values: MeasureValues): Promise<StringMap<string, any>> {
|
||||
return PromiseWrapper.resolve({'id': this._id, 'values': values});
|
||||
}
|
||||
|
||||
reportSample(completeSample: List<MeasureValues>,
|
||||
validSample: List<MeasureValues>): Promise<StringMap<string, any>> {
|
||||
return PromiseWrapper.resolve(
|
||||
{'id': this._id, 'completeSample': completeSample, 'validSample': validSample});
|
||||
}
|
||||
}
|
|
@ -1,154 +0,0 @@
|
|||
import {
|
||||
afterEach,
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
} from 'angular2/test_lib';
|
||||
import {
|
||||
Runner, Sampler, SampleDescription,
|
||||
Validator, bind, Injector, Metric,
|
||||
Options, WebDriverAdapter
|
||||
} from 'benchpress/common';
|
||||
import { isBlank } from 'angular2/src/facade/lang';
|
||||
import { Promise, PromiseWrapper } from 'angular2/src/facade/async';
|
||||
|
||||
export function main() {
|
||||
describe('runner', () => {
|
||||
var injector;
|
||||
var runner;
|
||||
|
||||
function createRunner(defaultBindings = null) {
|
||||
if (isBlank(defaultBindings)) {
|
||||
defaultBindings = [];
|
||||
}
|
||||
runner = new Runner([
|
||||
defaultBindings,
|
||||
bind(Sampler).toFactory(
|
||||
(_injector) => {
|
||||
injector = _injector;
|
||||
return new MockSampler();
|
||||
}, [Injector]
|
||||
),
|
||||
bind(Metric).toFactory( () => new MockMetric(), []),
|
||||
bind(Validator).toFactory( () => new MockValidator(), []),
|
||||
bind(WebDriverAdapter).toFactory( () => new MockWebDriverAdapter(), [])
|
||||
]);
|
||||
return runner;
|
||||
}
|
||||
|
||||
it('should set SampleDescription.id', inject([AsyncTestCompleter], (async) => {
|
||||
createRunner().sample({id: 'someId'})
|
||||
.then( (_) => injector.asyncGet(SampleDescription) )
|
||||
.then( (desc) => {
|
||||
expect(desc.id).toBe('someId');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should merge SampleDescription.description', inject([AsyncTestCompleter], (async) => {
|
||||
createRunner([
|
||||
bind(Options.DEFAULT_DESCRIPTION).toValue({'a': 1})
|
||||
]).sample({id: 'someId', bindings: [
|
||||
bind(Options.SAMPLE_DESCRIPTION).toValue({'b': 2})
|
||||
]}).then( (_) => injector.asyncGet(SampleDescription) )
|
||||
.then( (desc) => {
|
||||
|
||||
expect(desc.description).toEqual({
|
||||
'forceGc': false,
|
||||
'userAgent': 'someUserAgent',
|
||||
'a': 1,
|
||||
'b': 2,
|
||||
'v': 11
|
||||
});
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should fill SampleDescription.metrics from the Metric', inject([AsyncTestCompleter], (async) => {
|
||||
createRunner().sample({id: 'someId'})
|
||||
.then( (_) => injector.asyncGet(SampleDescription) )
|
||||
.then( (desc) => {
|
||||
|
||||
expect(desc.metrics).toEqual({ 'm1': 'some metric' });
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should bind Options.EXECUTE', inject([AsyncTestCompleter], (async) => {
|
||||
var execute = () => {};
|
||||
createRunner().sample({id: 'someId', execute: execute}).then( (_) => {
|
||||
expect(injector.get(Options.EXECUTE)).toEqual(execute);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should bind Options.PREPARE', inject([AsyncTestCompleter], (async) => {
|
||||
var prepare = () => {};
|
||||
createRunner().sample({id: 'someId', prepare: prepare}).then( (_) => {
|
||||
expect(injector.get(Options.PREPARE)).toEqual(prepare);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should bind Options.MICRO_METRICS', inject([AsyncTestCompleter], (async) => {
|
||||
createRunner().sample({id: 'someId', microMetrics: {'a': 'b'}}).then( (_) => {
|
||||
expect(injector.get(Options.MICRO_METRICS)).toEqual({'a': 'b'});
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should overwrite bindings per sample call', inject([AsyncTestCompleter], (async) => {
|
||||
createRunner([
|
||||
bind(Options.DEFAULT_DESCRIPTION).toValue({'a': 1}),
|
||||
]).sample({id: 'someId', bindings: [
|
||||
bind(Options.DEFAULT_DESCRIPTION).toValue({'a': 2}),
|
||||
]}).then( (_) => injector.asyncGet(SampleDescription) )
|
||||
.then( (desc) => {
|
||||
|
||||
expect(injector.get(SampleDescription).description['a']).toBe(2);
|
||||
async.done();
|
||||
});
|
||||
|
||||
}));
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
class MockWebDriverAdapter extends WebDriverAdapter {
|
||||
executeScript(script):Promise {
|
||||
return PromiseWrapper.resolve('someUserAgent');
|
||||
}
|
||||
}
|
||||
|
||||
class MockValidator extends Validator {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
describe() {
|
||||
return { 'v': 11 };
|
||||
}
|
||||
}
|
||||
|
||||
class MockMetric extends Metric {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
describe() {
|
||||
return { 'm1': 'some metric' };
|
||||
}
|
||||
}
|
||||
|
||||
class MockSampler extends Sampler {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
sample():Promise {
|
||||
return PromiseWrapper.resolve(23);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
import {
|
||||
afterEach,
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
} from 'angular2/test_lib';
|
||||
import {
|
||||
Runner,
|
||||
Sampler,
|
||||
SampleDescription,
|
||||
Validator,
|
||||
bind,
|
||||
Injector,
|
||||
Metric,
|
||||
Options,
|
||||
WebDriverAdapter,
|
||||
SampleState
|
||||
} from 'benchpress/common';
|
||||
import {isBlank} from 'angular2/src/facade/lang';
|
||||
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
||||
|
||||
export function main() {
|
||||
describe('runner', () => {
|
||||
var injector;
|
||||
var runner;
|
||||
|
||||
function createRunner(defaultBindings = null) {
|
||||
if (isBlank(defaultBindings)) {
|
||||
defaultBindings = [];
|
||||
}
|
||||
runner = new Runner([
|
||||
defaultBindings,
|
||||
bind(Sampler).toFactory((_injector) =>
|
||||
{
|
||||
injector = _injector;
|
||||
return new MockSampler();
|
||||
},
|
||||
[Injector]),
|
||||
bind(Metric).toFactory(() => new MockMetric(), []),
|
||||
bind(Validator).toFactory(() => new MockValidator(), []),
|
||||
bind(WebDriverAdapter).toFactory(() => new MockWebDriverAdapter(), [])
|
||||
]);
|
||||
return runner;
|
||||
}
|
||||
|
||||
it('should set SampleDescription.id', inject([AsyncTestCompleter], (async) => {
|
||||
createRunner()
|
||||
.sample({id: 'someId'})
|
||||
.then((_) => injector.asyncGet(SampleDescription))
|
||||
.then((desc) => {
|
||||
expect(desc.id).toBe('someId');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should merge SampleDescription.description', inject([AsyncTestCompleter], (async) => {
|
||||
createRunner([bind(Options.DEFAULT_DESCRIPTION).toValue({'a': 1})])
|
||||
.sample(
|
||||
{id: 'someId', bindings: [bind(Options.SAMPLE_DESCRIPTION).toValue({'b': 2})]})
|
||||
.then((_) => injector.asyncGet(SampleDescription))
|
||||
.then((desc) => {
|
||||
|
||||
expect(desc.description)
|
||||
.toEqual(
|
||||
{'forceGc': false, 'userAgent': 'someUserAgent', 'a': 1, 'b': 2, 'v': 11});
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should fill SampleDescription.metrics from the Metric',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
createRunner()
|
||||
.sample({id: 'someId'})
|
||||
.then((_) => injector.asyncGet(SampleDescription))
|
||||
.then((desc) => {
|
||||
|
||||
expect(desc.metrics).toEqual({'m1': 'some metric'});
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should bind Options.EXECUTE', inject([AsyncTestCompleter], (async) => {
|
||||
var execute = () => {};
|
||||
createRunner()
|
||||
.sample({id: 'someId', execute: execute})
|
||||
.then((_) => {
|
||||
expect(injector.get(Options.EXECUTE)).toEqual(execute);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should bind Options.PREPARE', inject([AsyncTestCompleter], (async) => {
|
||||
var prepare = () => {};
|
||||
createRunner()
|
||||
.sample({id: 'someId', prepare: prepare})
|
||||
.then((_) => {
|
||||
expect(injector.get(Options.PREPARE)).toEqual(prepare);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should bind Options.MICRO_METRICS', inject([AsyncTestCompleter], (async) => {
|
||||
createRunner()
|
||||
.sample({id: 'someId', microMetrics: {'a': 'b'}})
|
||||
.then((_) => {
|
||||
expect(injector.get(Options.MICRO_METRICS)).toEqual({'a': 'b'});
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should overwrite bindings per sample call', inject([AsyncTestCompleter], (async) => {
|
||||
createRunner([
|
||||
bind(Options.DEFAULT_DESCRIPTION)
|
||||
.toValue({'a': 1}),
|
||||
])
|
||||
.sample({
|
||||
id: 'someId',
|
||||
bindings: [
|
||||
bind(Options.DEFAULT_DESCRIPTION)
|
||||
.toValue({'a': 2}),
|
||||
]
|
||||
})
|
||||
.then((_) => injector.asyncGet(SampleDescription))
|
||||
.then((desc) => {
|
||||
|
||||
expect(injector.get(SampleDescription).description['a']).toBe(2);
|
||||
async.done();
|
||||
});
|
||||
|
||||
}));
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
class MockWebDriverAdapter extends WebDriverAdapter {
|
||||
executeScript(script): Promise<string> { return PromiseWrapper.resolve('someUserAgent'); }
|
||||
}
|
||||
|
||||
class MockValidator extends Validator {
|
||||
constructor() { super(); }
|
||||
describe() { return {'v': 11}; }
|
||||
}
|
||||
|
||||
class MockMetric extends Metric {
|
||||
constructor() { super(); }
|
||||
describe() { return {'m1': 'some metric'}; }
|
||||
}
|
||||
|
||||
class MockSampler extends Sampler {
|
||||
constructor() { super(); }
|
||||
sample(): Promise<SampleState> { return PromiseWrapper.resolve(23); }
|
||||
}
|
|
@ -1,331 +0,0 @@
|
|||
import {
|
||||
afterEach,
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import { isBlank, isPresent, BaseException, stringify, Date, DateWrapper } from 'angular2/src/facade/lang';
|
||||
import { ListWrapper, List } from 'angular2/src/facade/collection';
|
||||
import { PromiseWrapper, Promise } from 'angular2/src/facade/async';
|
||||
|
||||
import {
|
||||
Sampler, WebDriverAdapter,
|
||||
Validator, Metric, Reporter, Browser,
|
||||
bind, Injector, Options, MeasureValues
|
||||
} from 'benchpress/common';
|
||||
|
||||
export function main() {
|
||||
var EMPTY_EXECUTE = () => {};
|
||||
|
||||
describe('sampler', () => {
|
||||
var sampler;
|
||||
|
||||
function createSampler({
|
||||
driver,
|
||||
metric,
|
||||
reporter,
|
||||
validator,
|
||||
prepare,
|
||||
execute
|
||||
} = {}) {
|
||||
var time = 1000;
|
||||
if (isBlank(metric)) {
|
||||
metric = new MockMetric([]);
|
||||
}
|
||||
if (isBlank(reporter)) {
|
||||
reporter = new MockReporter([]);
|
||||
}
|
||||
if (isBlank(driver)) {
|
||||
driver = new MockDriverAdapter([]);
|
||||
}
|
||||
var bindings = [
|
||||
Options.DEFAULT_BINDINGS,
|
||||
Sampler.BINDINGS,
|
||||
bind(Metric).toValue(metric),
|
||||
bind(Reporter).toValue(reporter),
|
||||
bind(WebDriverAdapter).toValue(driver),
|
||||
bind(Options.EXECUTE).toValue(execute),
|
||||
bind(Validator).toValue(validator),
|
||||
bind(Options.NOW).toValue( () => DateWrapper.fromMillis(time++) )
|
||||
];
|
||||
if (isPresent(prepare)) {
|
||||
ListWrapper.push(bindings, bind(Options.PREPARE).toValue(prepare));
|
||||
}
|
||||
|
||||
sampler = Injector.resolveAndCreate(bindings).get(Sampler);
|
||||
}
|
||||
|
||||
it('should call the prepare and execute callbacks using WebDriverAdapter.waitFor', inject([AsyncTestCompleter], (async) => {
|
||||
var log = [];
|
||||
var count = 0;
|
||||
var driver = new MockDriverAdapter([], (callback) => {
|
||||
var result = callback();
|
||||
ListWrapper.push(log, result);
|
||||
return PromiseWrapper.resolve(result);
|
||||
});
|
||||
createSampler({
|
||||
driver: driver,
|
||||
validator: createCountingValidator(2),
|
||||
prepare: () => {
|
||||
return count++;
|
||||
},
|
||||
execute: () => {
|
||||
return count++;
|
||||
}
|
||||
});
|
||||
sampler.sample().then( (_) => {
|
||||
expect(count).toBe(4);
|
||||
expect(log).toEqual([0,1,2,3]);
|
||||
async.done();
|
||||
});
|
||||
|
||||
}));
|
||||
|
||||
it('should call prepare, beginMeasure, execute, endMeasure for every iteration', inject([AsyncTestCompleter], (async) => {
|
||||
var workCount = 0;
|
||||
var log = [];
|
||||
createSampler({
|
||||
metric: createCountingMetric(log),
|
||||
validator: createCountingValidator(2),
|
||||
prepare: () => {
|
||||
ListWrapper.push(log, `p${workCount++}`);
|
||||
},
|
||||
execute: () => {
|
||||
ListWrapper.push(log, `w${workCount++}`);
|
||||
}
|
||||
});
|
||||
sampler.sample().then( (_) => {
|
||||
expect(log).toEqual([
|
||||
'p0',
|
||||
['beginMeasure'],
|
||||
'w1',
|
||||
['endMeasure', false, {'script': 0}],
|
||||
'p2',
|
||||
['beginMeasure'],
|
||||
'w3',
|
||||
['endMeasure', false, {'script': 1}],
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should call execute, endMeasure for every iteration if there is no prepare callback', inject([AsyncTestCompleter], (async) => {
|
||||
var log = [];
|
||||
var workCount = 0;
|
||||
createSampler({
|
||||
metric: createCountingMetric(log),
|
||||
validator: createCountingValidator(2),
|
||||
execute: () => {
|
||||
ListWrapper.push(log, `w${workCount++}`);
|
||||
},
|
||||
prepare: null
|
||||
});
|
||||
sampler.sample().then( (_) => {
|
||||
expect(log).toEqual([
|
||||
['beginMeasure'],
|
||||
'w0',
|
||||
['endMeasure', true, {'script': 0}],
|
||||
'w1',
|
||||
['endMeasure', true, {'script': 1}],
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should only collect metrics for execute and ignore metrics from prepare', inject([AsyncTestCompleter], (async) => {
|
||||
var scriptTime = 0;
|
||||
var iterationCount = 1;
|
||||
createSampler({
|
||||
validator: createCountingValidator(2),
|
||||
metric: new MockMetric([], () => {
|
||||
var result = PromiseWrapper.resolve({'script': scriptTime});
|
||||
scriptTime = 0;
|
||||
return result;
|
||||
}),
|
||||
prepare: () => {
|
||||
scriptTime = 1 * iterationCount;
|
||||
},
|
||||
execute: () => {
|
||||
scriptTime = 10 * iterationCount;
|
||||
iterationCount++;
|
||||
}
|
||||
});
|
||||
sampler.sample().then( (state) => {
|
||||
expect(state.completeSample.length).toBe(2);
|
||||
expect(state.completeSample[0]).toEqual(mv(0, 1000, {'script': 10}));
|
||||
expect(state.completeSample[1]).toEqual(mv(1, 1001, {'script': 20}));
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should call the validator for every execution and store the valid sample', inject([AsyncTestCompleter], (async) => {
|
||||
var log = [];
|
||||
var validSample = [{}];
|
||||
|
||||
createSampler({
|
||||
metric: createCountingMetric(),
|
||||
validator: createCountingValidator(2, validSample, log),
|
||||
execute: EMPTY_EXECUTE
|
||||
});
|
||||
sampler.sample().then( (state) => {
|
||||
expect(state.validSample).toBe(validSample);
|
||||
// TODO(tbosch): Why does this fail??
|
||||
// expect(log).toEqual([
|
||||
// ['validate', [{'script': 0}], null],
|
||||
// ['validate', [{'script': 0}, {'script': 1}], validSample]
|
||||
// ]);
|
||||
|
||||
expect(log.length).toBe(2);
|
||||
expect(log[0]).toEqual(
|
||||
['validate', [mv(0, 1000, {'script': 0})], null]
|
||||
);
|
||||
expect(log[1]).toEqual(
|
||||
['validate', [mv(0, 1000, {'script': 0}), mv(1, 1001, {'script': 1})], validSample]
|
||||
);
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should report the metric values', inject([AsyncTestCompleter], (async) => {
|
||||
var log = [];
|
||||
var validSample = [{}];
|
||||
createSampler({
|
||||
validator: createCountingValidator(2, validSample),
|
||||
metric: createCountingMetric(),
|
||||
reporter: new MockReporter(log),
|
||||
execute: EMPTY_EXECUTE
|
||||
});
|
||||
sampler.sample().then( (_) => {
|
||||
// TODO(tbosch): Why does this fail??
|
||||
// expect(log).toEqual([
|
||||
// ['reportMeasureValues', 0, {'script': 0}],
|
||||
// ['reportMeasureValues', 1, {'script': 1}],
|
||||
// ['reportSample', [{'script': 0}, {'script': 1}], validSample]
|
||||
// ]);
|
||||
expect(log.length).toBe(3);
|
||||
expect(log[0]).toEqual(
|
||||
['reportMeasureValues', mv(0, 1000, {'script': 0})]
|
||||
);
|
||||
expect(log[1]).toEqual(
|
||||
['reportMeasureValues', mv(1, 1001, {'script': 1})]
|
||||
);
|
||||
expect(log[2]).toEqual(
|
||||
['reportSample', [mv(0, 1000, {'script': 0}), mv(1, 1001, {'script': 1})], validSample]
|
||||
);
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function mv(runIndex, time, values) {
|
||||
return new MeasureValues(runIndex, DateWrapper.fromMillis(time), values);
|
||||
}
|
||||
|
||||
function createCountingValidator(count, validSample = null, log = null) {
|
||||
return new MockValidator(log, (completeSample) => {
|
||||
count--;
|
||||
if (count === 0) {
|
||||
return isPresent(validSample) ? validSample : completeSample;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function createCountingMetric(log = null) {
|
||||
var scriptTime = 0;
|
||||
return new MockMetric(log, () => {
|
||||
return { 'script': scriptTime++ };
|
||||
});
|
||||
}
|
||||
|
||||
class MockDriverAdapter extends WebDriverAdapter {
|
||||
_log:List;
|
||||
_waitFor:Function;
|
||||
constructor(log = null, waitFor = null) {
|
||||
super();
|
||||
if (isBlank(log)) {
|
||||
log = [];
|
||||
}
|
||||
this._log = log;
|
||||
this._waitFor = waitFor;
|
||||
}
|
||||
waitFor(callback:Function):Promise {
|
||||
if (isPresent(this._waitFor)) {
|
||||
return this._waitFor(callback);
|
||||
} else {
|
||||
return PromiseWrapper.resolve(callback());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class MockValidator extends Validator {
|
||||
_validate:Function;
|
||||
_log:List;
|
||||
constructor(log = null, validate = null) {
|
||||
super();
|
||||
this._validate = validate;
|
||||
if (isBlank(log)) {
|
||||
log = [];
|
||||
}
|
||||
this._log = log;
|
||||
}
|
||||
validate(completeSample:List<MeasureValues>):List<MeasureValues> {
|
||||
var stableSample = isPresent(this._validate) ? this._validate(completeSample) : completeSample;
|
||||
ListWrapper.push(this._log, ['validate', completeSample, stableSample]);
|
||||
return stableSample;
|
||||
}
|
||||
}
|
||||
|
||||
class MockMetric extends Metric {
|
||||
_endMeasure:Function;
|
||||
_log:List;
|
||||
constructor(log = null, endMeasure = null) {
|
||||
super();
|
||||
this._endMeasure = endMeasure;
|
||||
if (isBlank(log)) {
|
||||
log = [];
|
||||
}
|
||||
this._log = log;
|
||||
}
|
||||
beginMeasure() {
|
||||
ListWrapper.push(this._log, ['beginMeasure']);
|
||||
return PromiseWrapper.resolve(null);
|
||||
}
|
||||
endMeasure(restart) {
|
||||
var measureValues = isPresent(this._endMeasure) ? this._endMeasure() : {};
|
||||
ListWrapper.push(this._log, ['endMeasure', restart, measureValues]);
|
||||
return PromiseWrapper.resolve(measureValues);
|
||||
}
|
||||
}
|
||||
|
||||
class MockReporter extends Reporter {
|
||||
_log:List;
|
||||
constructor(log = null) {
|
||||
super();
|
||||
if (isBlank(log)) {
|
||||
log = [];
|
||||
}
|
||||
this._log = log;
|
||||
}
|
||||
reportMeasureValues(values):Promise {
|
||||
ListWrapper.push(this._log, ['reportMeasureValues', values]);
|
||||
return PromiseWrapper.resolve(null);
|
||||
}
|
||||
reportSample(completeSample, validSample):Promise {
|
||||
ListWrapper.push(this._log, ['reportSample', completeSample, validSample]);
|
||||
return PromiseWrapper.resolve(null);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,331 @@
|
|||
import {
|
||||
afterEach,
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import {
|
||||
isBlank,
|
||||
isPresent,
|
||||
BaseException,
|
||||
stringify,
|
||||
Date,
|
||||
DateWrapper
|
||||
} from 'angular2/src/facade/lang';
|
||||
import {ListWrapper, List} from 'angular2/src/facade/collection';
|
||||
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
|
||||
|
||||
import {
|
||||
Sampler,
|
||||
WebDriverAdapter,
|
||||
Validator,
|
||||
Metric,
|
||||
Reporter,
|
||||
bind,
|
||||
Injector,
|
||||
Options,
|
||||
MeasureValues
|
||||
} from 'benchpress/common';
|
||||
|
||||
export function main() {
|
||||
var EMPTY_EXECUTE = () => {};
|
||||
|
||||
describe('sampler', () => {
|
||||
var sampler;
|
||||
|
||||
function createSampler({driver, metric, reporter, validator, prepare, execute}: {
|
||||
driver?: any,
|
||||
metric?: Metric,
|
||||
reporter?: Reporter,
|
||||
validator?: Validator,
|
||||
prepare?: any,
|
||||
execute?: any
|
||||
} = {}) {
|
||||
var time = 1000;
|
||||
if (isBlank(metric)) {
|
||||
metric = new MockMetric([]);
|
||||
}
|
||||
if (isBlank(reporter)) {
|
||||
reporter = new MockReporter([]);
|
||||
}
|
||||
if (isBlank(driver)) {
|
||||
driver = new MockDriverAdapter([]);
|
||||
}
|
||||
var bindings = [
|
||||
Options.DEFAULT_BINDINGS,
|
||||
Sampler.BINDINGS,
|
||||
bind(Metric).toValue(metric),
|
||||
bind(Reporter).toValue(reporter),
|
||||
bind(WebDriverAdapter).toValue(driver),
|
||||
bind(Options.EXECUTE).toValue(execute),
|
||||
bind(Validator).toValue(validator),
|
||||
bind(Options.NOW).toValue(() => DateWrapper.fromMillis(time++))
|
||||
];
|
||||
if (isPresent(prepare)) {
|
||||
ListWrapper.push(bindings, bind(Options.PREPARE).toValue(prepare));
|
||||
}
|
||||
|
||||
sampler = Injector.resolveAndCreate(bindings).get(Sampler);
|
||||
}
|
||||
|
||||
it('should call the prepare and execute callbacks using WebDriverAdapter.waitFor',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
var log = [];
|
||||
var count = 0;
|
||||
var driver = new MockDriverAdapter([], (callback) => {
|
||||
var result = callback();
|
||||
ListWrapper.push(log, result);
|
||||
return PromiseWrapper.resolve(result);
|
||||
});
|
||||
createSampler({
|
||||
driver: driver,
|
||||
validator: createCountingValidator(2),
|
||||
prepare: () => { return count++; },
|
||||
execute: () => { return count++; }
|
||||
});
|
||||
sampler.sample().then((_) => {
|
||||
expect(count).toBe(4);
|
||||
expect(log).toEqual([0, 1, 2, 3]);
|
||||
async.done();
|
||||
});
|
||||
|
||||
}));
|
||||
|
||||
it('should call prepare, beginMeasure, execute, endMeasure for every iteration',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
var workCount = 0;
|
||||
var log = [];
|
||||
createSampler({
|
||||
metric: createCountingMetric(log),
|
||||
validator: createCountingValidator(2),
|
||||
prepare: () => { ListWrapper.push(log, `p${workCount++}`); },
|
||||
execute: () => { ListWrapper.push(log, `w${workCount++}`); }
|
||||
});
|
||||
sampler.sample().then((_) => {
|
||||
expect(log).toEqual([
|
||||
'p0',
|
||||
['beginMeasure'],
|
||||
'w1',
|
||||
['endMeasure', false, {'script': 0}],
|
||||
'p2',
|
||||
['beginMeasure'],
|
||||
'w3',
|
||||
['endMeasure', false, {'script': 1}],
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should call execute, endMeasure for every iteration if there is no prepare callback',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
var log = [];
|
||||
var workCount = 0;
|
||||
createSampler({
|
||||
metric: createCountingMetric(log),
|
||||
validator: createCountingValidator(2),
|
||||
execute: () => { ListWrapper.push(log, `w${workCount++}`); },
|
||||
prepare: null
|
||||
});
|
||||
sampler.sample().then((_) => {
|
||||
expect(log).toEqual([
|
||||
['beginMeasure'],
|
||||
'w0',
|
||||
['endMeasure', true, {'script': 0}],
|
||||
'w1',
|
||||
['endMeasure', true, {'script': 1}],
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should only collect metrics for execute and ignore metrics from prepare',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
var scriptTime = 0;
|
||||
var iterationCount = 1;
|
||||
createSampler({
|
||||
validator: createCountingValidator(2),
|
||||
metric: new MockMetric([], () =>
|
||||
{
|
||||
var result = PromiseWrapper.resolve({'script': scriptTime});
|
||||
scriptTime = 0;
|
||||
return result;
|
||||
}),
|
||||
prepare: () => { scriptTime = 1 * iterationCount; },
|
||||
execute: () =>
|
||||
{
|
||||
scriptTime = 10 * iterationCount;
|
||||
iterationCount++;
|
||||
}
|
||||
});
|
||||
sampler.sample().then((state) => {
|
||||
expect(state.completeSample.length).toBe(2);
|
||||
expect(state.completeSample[0]).toEqual(mv(0, 1000, {'script': 10}));
|
||||
expect(state.completeSample[1]).toEqual(mv(1, 1001, {'script': 20}));
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should call the validator for every execution and store the valid sample',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
var log = [];
|
||||
var validSample = [{}];
|
||||
|
||||
createSampler({
|
||||
metric: createCountingMetric(),
|
||||
validator: createCountingValidator(2, validSample, log),
|
||||
execute: EMPTY_EXECUTE
|
||||
});
|
||||
sampler.sample().then((state) => {
|
||||
expect(state.validSample).toBe(validSample);
|
||||
// TODO(tbosch): Why does this fail??
|
||||
// expect(log).toEqual([
|
||||
// ['validate', [{'script': 0}], null],
|
||||
// ['validate', [{'script': 0}, {'script': 1}], validSample]
|
||||
// ]);
|
||||
|
||||
expect(log.length).toBe(2);
|
||||
expect(log[0]).toEqual(['validate', [mv(0, 1000, {'script': 0})], null]);
|
||||
expect(log[1]).toEqual([
|
||||
'validate',
|
||||
[mv(0, 1000, {'script': 0}), mv(1, 1001, {'script': 1})],
|
||||
validSample
|
||||
]);
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should report the metric values', inject([AsyncTestCompleter], (async) => {
|
||||
var log = [];
|
||||
var validSample = [{}];
|
||||
createSampler({
|
||||
validator: createCountingValidator(2, validSample),
|
||||
metric: createCountingMetric(),
|
||||
reporter: new MockReporter(log),
|
||||
execute: EMPTY_EXECUTE
|
||||
});
|
||||
sampler.sample().then((_) => {
|
||||
// TODO(tbosch): Why does this fail??
|
||||
// expect(log).toEqual([
|
||||
// ['reportMeasureValues', 0, {'script': 0}],
|
||||
// ['reportMeasureValues', 1, {'script': 1}],
|
||||
// ['reportSample', [{'script': 0}, {'script': 1}], validSample]
|
||||
// ]);
|
||||
expect(log.length).toBe(3);
|
||||
expect(log[0]).toEqual(['reportMeasureValues', mv(0, 1000, {'script': 0})]);
|
||||
expect(log[1]).toEqual(['reportMeasureValues', mv(1, 1001, {'script': 1})]);
|
||||
expect(log[2]).toEqual([
|
||||
'reportSample',
|
||||
[mv(0, 1000, {'script': 0}), mv(1, 1001, {'script': 1})],
|
||||
validSample
|
||||
]);
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function mv(runIndex, time, values) {
|
||||
return new MeasureValues(runIndex, DateWrapper.fromMillis(time), values);
|
||||
}
|
||||
|
||||
function createCountingValidator(count, validSample = null, log = null) {
|
||||
return new MockValidator(log, (completeSample) => {
|
||||
count--;
|
||||
if (count === 0) {
|
||||
return isPresent(validSample) ? validSample : completeSample;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function createCountingMetric(log = null) {
|
||||
var scriptTime = 0;
|
||||
return new MockMetric(log, () => { return {'script': scriptTime++}; });
|
||||
}
|
||||
|
||||
class MockDriverAdapter extends WebDriverAdapter {
|
||||
private _log: List<any>;
|
||||
private _waitFor: Function;
|
||||
constructor(log = null, waitFor = null) {
|
||||
super();
|
||||
if (isBlank(log)) {
|
||||
log = [];
|
||||
}
|
||||
this._log = log;
|
||||
this._waitFor = waitFor;
|
||||
}
|
||||
waitFor(callback: Function): Promise<any> {
|
||||
if (isPresent(this._waitFor)) {
|
||||
return this._waitFor(callback);
|
||||
} else {
|
||||
return PromiseWrapper.resolve(callback());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class MockValidator extends Validator {
|
||||
private _log: List<any>;
|
||||
constructor(log = null, private _validate: Function = null) {
|
||||
super();
|
||||
if (isBlank(log)) {
|
||||
log = [];
|
||||
}
|
||||
this._log = log;
|
||||
}
|
||||
validate(completeSample: List<MeasureValues>): List<MeasureValues> {
|
||||
var stableSample = isPresent(this._validate) ? this._validate(completeSample) : completeSample;
|
||||
ListWrapper.push(this._log, ['validate', completeSample, stableSample]);
|
||||
return stableSample;
|
||||
}
|
||||
}
|
||||
|
||||
class MockMetric extends Metric {
|
||||
private _log: List<any>;
|
||||
constructor(log = null, private _endMeasure: Function = null) {
|
||||
super();
|
||||
if (isBlank(log)) {
|
||||
log = [];
|
||||
}
|
||||
this._log = log;
|
||||
}
|
||||
beginMeasure() {
|
||||
ListWrapper.push(this._log, ['beginMeasure']);
|
||||
return PromiseWrapper.resolve(null);
|
||||
}
|
||||
endMeasure(restart) {
|
||||
var measureValues = isPresent(this._endMeasure) ? this._endMeasure() : {};
|
||||
ListWrapper.push(this._log, ['endMeasure', restart, measureValues]);
|
||||
return PromiseWrapper.resolve(measureValues);
|
||||
}
|
||||
}
|
||||
|
||||
class MockReporter extends Reporter {
|
||||
_log: List<any>;
|
||||
constructor(log = null) {
|
||||
super();
|
||||
if (isBlank(log)) {
|
||||
log = [];
|
||||
}
|
||||
this._log = log;
|
||||
}
|
||||
reportMeasureValues(values): Promise<any> {
|
||||
ListWrapper.push(this._log, ['reportMeasureValues', values]);
|
||||
return PromiseWrapper.resolve(null);
|
||||
}
|
||||
reportSample(completeSample, validSample): Promise<any> {
|
||||
ListWrapper.push(this._log, ['reportSample', completeSample, validSample]);
|
||||
return PromiseWrapper.resolve(null);
|
||||
}
|
||||
}
|
|
@ -1,15 +1,15 @@
|
|||
import {describe, ddescribe, it, iit, xit, expect, beforeEach, afterEach} from 'angular2/test_lib';
|
||||
|
||||
import { Statistic } from 'benchpress/src/statistic';
|
||||
import {Statistic} from 'benchpress/src/statistic';
|
||||
|
||||
import { NaN } from 'angular2/src/facade/math';
|
||||
import {NaN} from 'angular2/src/facade/math';
|
||||
|
||||
export function main() {
|
||||
describe('statistic', () => {
|
||||
|
||||
it('should calculate the mean', () => {
|
||||
expect(Statistic.calculateMean([])).toBeNaN();
|
||||
expect(Statistic.calculateMean([1,2,3])).toBe(2.0);
|
||||
expect(Statistic.calculateMean([1, 2, 3])).toBe(2.0);
|
||||
});
|
||||
|
||||
it('should calculate the standard deviation', () => {
|
||||
|
@ -27,7 +27,7 @@ export function main() {
|
|||
it('should calculate the regression slope', () => {
|
||||
expect(Statistic.calculateRegressionSlope([], NaN, [], NaN)).toBeNaN();
|
||||
expect(Statistic.calculateRegressionSlope([1], 1, [2], 2)).toBeNaN();
|
||||
expect(Statistic.calculateRegressionSlope([1,2], 1.5, [2,4], 3)).toBe(2.0);
|
||||
expect(Statistic.calculateRegressionSlope([1, 2], 1.5, [2, 4], 3)).toBe(2.0);
|
||||
});
|
||||
|
||||
});
|
|
@ -1,47 +0,0 @@
|
|||
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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
import {isPresent} from 'angular2/src/facade/lang';
|
||||
|
||||
export class TraceEventFactory {
|
||||
private _cat: string;
|
||||
private _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;
|
||||
}
|
||||
}
|
|
@ -1,9 +1,13 @@
|
|||
import {describe, ddescribe, it, iit, xit, expect, beforeEach, afterEach} from 'angular2/test_lib';
|
||||
import { Date, DateWrapper } from 'angular2/src/facade/lang';
|
||||
import { ListWrapper } from 'angular2/src/facade/collection';
|
||||
import {Date, DateWrapper} from 'angular2/src/facade/lang';
|
||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import {
|
||||
Validator, RegressionSlopeValidator, Injector, bind, MeasureValues
|
||||
Validator,
|
||||
RegressionSlopeValidator,
|
||||
Injector,
|
||||
bind,
|
||||
MeasureValues
|
||||
} from 'benchpress/common';
|
||||
|
||||
export function main() {
|
||||
|
@ -12,43 +16,43 @@ export function main() {
|
|||
|
||||
function createValidator({size, metric}) {
|
||||
validator = Injector.resolveAndCreate([
|
||||
RegressionSlopeValidator.BINDINGS,
|
||||
bind(RegressionSlopeValidator.METRIC).toValue(metric),
|
||||
bind(RegressionSlopeValidator.SAMPLE_SIZE).toValue(size)
|
||||
]).get(RegressionSlopeValidator);
|
||||
RegressionSlopeValidator.BINDINGS,
|
||||
bind(RegressionSlopeValidator.METRIC).toValue(metric),
|
||||
bind(RegressionSlopeValidator.SAMPLE_SIZE).toValue(size)
|
||||
])
|
||||
.get(RegressionSlopeValidator);
|
||||
}
|
||||
|
||||
it('should return sampleSize and metric as description', () => {
|
||||
createValidator({size: 2, metric: 'script'});
|
||||
expect(validator.describe()).toEqual({
|
||||
'sampleSize': 2,
|
||||
'regressionSlopeMetric': 'script'
|
||||
});
|
||||
expect(validator.describe()).toEqual({'sampleSize': 2, 'regressionSlopeMetric': 'script'});
|
||||
});
|
||||
|
||||
it('should return null while the completeSample is smaller than the given size', () => {
|
||||
createValidator({size: 2, metric: 'script'});
|
||||
expect(validator.validate([])).toBe(null);
|
||||
expect(validator.validate([mv(0,0,{})])).toBe(null);
|
||||
expect(validator.validate([mv(0, 0, {})])).toBe(null);
|
||||
});
|
||||
|
||||
it('should return null while the regression slope is < 0', () => {
|
||||
createValidator({size: 2, metric: 'script'});
|
||||
expect(validator.validate([mv(0,0,{'script':2}), mv(1,1,{'script':1})])).toBe(null);
|
||||
expect(validator.validate([mv(0, 0, {'script': 2}), mv(1, 1, {'script': 1})])).toBe(null);
|
||||
});
|
||||
|
||||
it('should return the last sampleSize runs when the regression slope is ==0', () => {
|
||||
createValidator({size: 2, metric: 'script'});
|
||||
var sample = [mv(0,0,{'script':1}), mv(1,1,{'script':1}), mv(2,2,{'script':1})];
|
||||
expect(validator.validate(ListWrapper.slice(sample,0,2))).toEqual(ListWrapper.slice(sample,0,2));
|
||||
expect(validator.validate(sample)).toEqual(ListWrapper.slice(sample,1,3));
|
||||
var sample = [mv(0, 0, {'script': 1}), mv(1, 1, {'script': 1}), mv(2, 2, {'script': 1})];
|
||||
expect(validator.validate(ListWrapper.slice(sample, 0, 2)))
|
||||
.toEqual(ListWrapper.slice(sample, 0, 2));
|
||||
expect(validator.validate(sample)).toEqual(ListWrapper.slice(sample, 1, 3));
|
||||
});
|
||||
|
||||
it('should return the last sampleSize runs when the regression slope is >0', () => {
|
||||
createValidator({size: 2, metric: 'script'});
|
||||
var sample = [mv(0,0,{'script':1}), mv(1,1,{'script':2}), mv(2,2,{'script':3})];
|
||||
expect(validator.validate(ListWrapper.slice(sample,0,2))).toEqual(ListWrapper.slice(sample,0,2));
|
||||
expect(validator.validate(sample)).toEqual(ListWrapper.slice(sample,1,3));
|
||||
var sample = [mv(0, 0, {'script': 1}), mv(1, 1, {'script': 2}), mv(2, 2, {'script': 3})];
|
||||
expect(validator.validate(ListWrapper.slice(sample, 0, 2)))
|
||||
.toEqual(ListWrapper.slice(sample, 0, 2));
|
||||
expect(validator.validate(sample)).toEqual(ListWrapper.slice(sample, 1, 3));
|
||||
});
|
||||
|
||||
});
|
|
@ -1,39 +1,36 @@
|
|||
import {describe, ddescribe, it, iit, xit, expect, beforeEach, afterEach} from 'angular2/test_lib';
|
||||
import { Date, DateWrapper } from 'angular2/src/facade/lang';
|
||||
import { ListWrapper } from 'angular2/src/facade/collection';
|
||||
import {Date, DateWrapper} from 'angular2/src/facade/lang';
|
||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import {
|
||||
Validator, SizeValidator, Injector, bind, MeasureValues
|
||||
} from 'benchpress/common';
|
||||
import {Validator, SizeValidator, Injector, bind, MeasureValues} from 'benchpress/common';
|
||||
|
||||
export function main() {
|
||||
describe('size validator', () => {
|
||||
var validator;
|
||||
|
||||
function createValidator(size) {
|
||||
validator = Injector.resolveAndCreate([
|
||||
SizeValidator.BINDINGS,
|
||||
bind(SizeValidator.SAMPLE_SIZE).toValue(size)
|
||||
]).get(SizeValidator);
|
||||
validator =
|
||||
Injector.resolveAndCreate(
|
||||
[SizeValidator.BINDINGS, bind(SizeValidator.SAMPLE_SIZE).toValue(size)])
|
||||
.get(SizeValidator);
|
||||
}
|
||||
|
||||
it('should return sampleSize as description', () => {
|
||||
createValidator(2);
|
||||
expect(validator.describe()).toEqual({
|
||||
'sampleSize': 2
|
||||
});
|
||||
expect(validator.describe()).toEqual({'sampleSize': 2});
|
||||
});
|
||||
|
||||
it('should return null while the completeSample is smaller than the given size', () => {
|
||||
createValidator(2);
|
||||
expect(validator.validate([])).toBe(null);
|
||||
expect(validator.validate([mv(0,0,{})])).toBe(null);
|
||||
expect(validator.validate([mv(0, 0, {})])).toBe(null);
|
||||
});
|
||||
|
||||
it('should return the last sampleSize runs when it has at least the given size', () => {
|
||||
createValidator(2);
|
||||
var sample = [mv(0,0,{'a':1}), mv(1,1,{'b':2}), mv(2,2,{'c':3})];
|
||||
expect(validator.validate(ListWrapper.slice(sample, 0, 2))).toEqual(ListWrapper.slice(sample, 0, 2));
|
||||
var sample = [mv(0, 0, {'a': 1}), mv(1, 1, {'b': 2}), mv(2, 2, {'c': 3})];
|
||||
expect(validator.validate(ListWrapper.slice(sample, 0, 2)))
|
||||
.toEqual(ListWrapper.slice(sample, 0, 2));
|
||||
expect(validator.validate(sample)).toEqual(ListWrapper.slice(sample, 1, 3));
|
||||
});
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
import {
|
||||
afterEach,
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import { StringMap, ListWrapper } from 'angular2/src/facade/collection';
|
||||
import { isPresent, StringWrapper } from 'angular2/src/facade/lang';
|
||||
import { PromiseWrapper } from 'angular2/src/facade/async';
|
||||
|
||||
import { WebDriverExtension, bind, Injector, Options } from 'benchpress/common';
|
||||
|
||||
export function main() {
|
||||
function createExtension(ids, caps) {
|
||||
return Injector.resolveAndCreate([
|
||||
ListWrapper.map(ids, (id) => bind(id).toValue(new MockExtension(id)) ),
|
||||
bind(Options.CAPABILITIES).toValue(caps),
|
||||
WebDriverExtension.bindTo(ids)
|
||||
]).asyncGet(WebDriverExtension);
|
||||
}
|
||||
|
||||
describe('WebDriverExtension.bindTo', () => {
|
||||
|
||||
it('should bind the extension that matches the capabilities', inject([AsyncTestCompleter], (async) => {
|
||||
createExtension(['m1', 'm2', 'm3'], {'browser': 'm2'}).then( (m) => {
|
||||
expect(m.id).toEqual('m2');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should throw if there is no match', inject([AsyncTestCompleter], (async) => {
|
||||
PromiseWrapper.catchError(
|
||||
createExtension(['m1'], {'browser': 'm2'}),
|
||||
(err) => {
|
||||
expect(isPresent(err)).toBe(true);
|
||||
async.done();
|
||||
}
|
||||
);
|
||||
}));
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
class MockExtension extends WebDriverExtension {
|
||||
id:string;
|
||||
|
||||
constructor(id) {
|
||||
super();
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
supports(capabilities:StringMap):boolean {
|
||||
return StringWrapper.equals(capabilities['browser'], this.id);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
import {
|
||||
afterEach,
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import {StringMap, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {isPresent, StringWrapper} from 'angular2/src/facade/lang';
|
||||
import {PromiseWrapper} from 'angular2/src/facade/async';
|
||||
|
||||
import {WebDriverExtension, bind, Injector, Options} from 'benchpress/common';
|
||||
|
||||
export function main() {
|
||||
function createExtension(ids, caps) {
|
||||
return Injector.resolveAndCreate([
|
||||
ListWrapper.map(ids, (id) => bind(id).toValue(new MockExtension(id))),
|
||||
bind(Options.CAPABILITIES).toValue(caps),
|
||||
WebDriverExtension.bindTo(ids)
|
||||
])
|
||||
.asyncGet(WebDriverExtension);
|
||||
}
|
||||
|
||||
describe('WebDriverExtension.bindTo', () => {
|
||||
|
||||
it('should bind the extension that matches the capabilities',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
createExtension(['m1', 'm2', 'm3'], {'browser': 'm2'})
|
||||
.then((m) => {
|
||||
expect(m.id).toEqual('m2');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should throw if there is no match', inject([AsyncTestCompleter], (async) => {
|
||||
PromiseWrapper.catchError(createExtension(['m1'], {'browser': 'm2'}), (err) => {
|
||||
expect(isPresent(err)).toBe(true);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
class MockExtension extends WebDriverExtension {
|
||||
id: string;
|
||||
|
||||
constructor(id) {
|
||||
super();
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
supports(capabilities: StringMap<string, any>): boolean {
|
||||
return StringWrapper.equals(capabilities['browser'], this.id);
|
||||
}
|
||||
}
|
|
@ -1,270 +0,0 @@
|
|||
import {
|
||||
afterEach,
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import { ListWrapper } from 'angular2/src/facade/collection';
|
||||
import { PromiseWrapper } from 'angular2/src/facade/async';
|
||||
import { Json, isBlank } from 'angular2/src/facade/lang';
|
||||
|
||||
import {
|
||||
WebDriverExtension, ChromeDriverExtension,
|
||||
WebDriverAdapter, Injector, bind
|
||||
} from 'benchpress/common';
|
||||
|
||||
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 v8Events = new TraceEventFactory('v8', 'pid0');
|
||||
var v8EventsOtherProcess = new TraceEventFactory('v8', 'pid1');
|
||||
var chromeTimelineEvents = new TraceEventFactory('disabled-by-default-devtools.timeline', 'pid0');
|
||||
var normEvents = new TraceEventFactory('timeline', 'pid0');
|
||||
|
||||
function createExtension(perfRecords = null, messageMethod = 'Tracing.dataCollected') {
|
||||
if (isBlank(perfRecords)) {
|
||||
perfRecords = [];
|
||||
}
|
||||
log = [];
|
||||
extension = Injector.resolveAndCreate([
|
||||
ChromeDriverExtension.BINDINGS,
|
||||
bind(WebDriverAdapter).toValue(new MockDriverAdapter(log, perfRecords, messageMethod))
|
||||
]).get(ChromeDriverExtension);
|
||||
return extension;
|
||||
}
|
||||
|
||||
it('should force gc via window.gc()', inject([AsyncTestCompleter], (async) => {
|
||||
createExtension().gc().then( (_) => {
|
||||
expect(log).toEqual([['executeScript', 'window.gc()']]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should mark the timeline via console.time()', inject([AsyncTestCompleter], (async) => {
|
||||
createExtension().timeBegin('someName').then( (_) => {
|
||||
expect(log).toEqual([['executeScript', `console.time('someName');`]]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should mark the timeline via console.timeEnd()', inject([AsyncTestCompleter], (async) => {
|
||||
createExtension().timeEnd('someName').then( (_) => {
|
||||
expect(log).toEqual([['executeScript', `console.timeEnd('someName');`]]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should mark the timeline via console.time() and console.timeEnd()', inject([AsyncTestCompleter], (async) => {
|
||||
createExtension().timeEnd('name1', 'name2').then( (_) => {
|
||||
expect(log).toEqual([['executeScript', `console.timeEnd('name1');console.time('name2');`]]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
describe('readPerfLog', () => {
|
||||
|
||||
it('should execute a dummy script before reading them', inject([AsyncTestCompleter], (async) => {
|
||||
// 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' ] ]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should normalize times to ms and forward ph and pid event properties', inject([AsyncTestCompleter], (async) => {
|
||||
createExtension([
|
||||
chromeTimelineEvents.complete('FunctionCall', 1100, 5500, null)
|
||||
]).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) => {
|
||||
var event = chromeTimelineEvents.create('X', 'FunctionCall', 1100, null);
|
||||
event['tdur'] = 5500;
|
||||
createExtension([event]).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) => {
|
||||
createExtension([
|
||||
chromeTimelineEvents.start('FunctionCall', 0)
|
||||
]).readPerfLog().then( (events) => {
|
||||
expect(events).toEqual([
|
||||
normEvents.start('script', 0),
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should ignore FunctionCalls from webdriver', inject([AsyncTestCompleter], (async) => {
|
||||
createExtension([
|
||||
chromeTimelineEvents.start('FunctionCall', 0, {'data': {'scriptName': 'InjectedScript'}})
|
||||
]).readPerfLog().then( (events) => {
|
||||
expect(events).toEqual([]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should report begin timestamps', inject([AsyncTestCompleter], (async) => {
|
||||
createExtension([
|
||||
blinkEvents.create('S', 'someName', 1000)
|
||||
]).readPerfLog().then( (events) => {
|
||||
expect(events).toEqual([
|
||||
normEvents.markStart('someName', 1.0)
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should report end timestamps', inject([AsyncTestCompleter], (async) => {
|
||||
createExtension([
|
||||
blinkEvents.create('F', 'someName', 1000)
|
||||
]).readPerfLog().then( (events) => {
|
||||
expect(events).toEqual([
|
||||
normEvents.markEnd('someName', 1.0)
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should report gc', inject([AsyncTestCompleter], (async) => {
|
||||
createExtension([
|
||||
chromeTimelineEvents.start('GCEvent', 1000, {'usedHeapSizeBefore': 1000}),
|
||||
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': false}),
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should report major gc', inject([AsyncTestCompleter], (async) => {
|
||||
createExtension([
|
||||
chromeTimelineEvents.start('GCEvent', 1000, {'usedHeapSizeBefore': 1000}),
|
||||
v8EventsOtherProcess.start('majorGC', 1100, null),
|
||||
v8EventsOtherProcess.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': false}),
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should ignore major gc from different processes', inject([AsyncTestCompleter], (async) => {
|
||||
createExtension([
|
||||
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', 'Rasterize', 'CompositeLayers'].forEach( (recordType) => {
|
||||
it(`should report ${recordType} as "render"`, inject([AsyncTestCompleter], (async) => {
|
||||
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 throw an error on buffer overflow', inject([AsyncTestCompleter], (async) => {
|
||||
PromiseWrapper.catchError(createExtension([
|
||||
chromeTimelineEvents.start('FunctionCall', 1234),
|
||||
], 'Tracing.bufferUsage').readPerfLog(), (err) => {
|
||||
expect( () => {
|
||||
throw err;
|
||||
}).toThrowError('The DevTools trace buffer filled during the test!');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should match chrome browsers', () => {
|
||||
expect(createExtension().supports({
|
||||
'browserName': 'chrome'
|
||||
})).toBe(true);
|
||||
|
||||
expect(createExtension().supports({
|
||||
'browserName': 'Chrome'
|
||||
})).toBe(true);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
class MockDriverAdapter extends WebDriverAdapter {
|
||||
_log:List;
|
||||
_events:List;
|
||||
_messageMethod:string;
|
||||
constructor(log, events, messageMethod) {
|
||||
super();
|
||||
this._log = log;
|
||||
this._events = events;
|
||||
this._messageMethod = messageMethod;
|
||||
}
|
||||
|
||||
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._events.map( (event) => {
|
||||
return {
|
||||
'message': Json.stringify({
|
||||
'message': {
|
||||
'method': this._messageMethod,
|
||||
'params': event
|
||||
}
|
||||
})
|
||||
};
|
||||
}));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,281 @@
|
|||
import {
|
||||
afterEach,
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {PromiseWrapper} from 'angular2/src/facade/async';
|
||||
import {Json, isBlank} from 'angular2/src/facade/lang';
|
||||
|
||||
import {
|
||||
WebDriverExtension,
|
||||
ChromeDriverExtension,
|
||||
WebDriverAdapter,
|
||||
Injector,
|
||||
bind
|
||||
} from 'benchpress/common';
|
||||
|
||||
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 v8Events = new TraceEventFactory('v8', 'pid0');
|
||||
var v8EventsOtherProcess = new TraceEventFactory('v8', 'pid1');
|
||||
var chromeTimelineEvents =
|
||||
new TraceEventFactory('disabled-by-default-devtools.timeline', 'pid0');
|
||||
var normEvents = new TraceEventFactory('timeline', 'pid0');
|
||||
|
||||
function createExtension(perfRecords = null, messageMethod = 'Tracing.dataCollected') {
|
||||
if (isBlank(perfRecords)) {
|
||||
perfRecords = [];
|
||||
}
|
||||
log = [];
|
||||
extension = Injector.resolveAndCreate([
|
||||
ChromeDriverExtension.BINDINGS,
|
||||
bind(WebDriverAdapter)
|
||||
.toValue(new MockDriverAdapter(log, perfRecords, messageMethod))
|
||||
])
|
||||
.get(ChromeDriverExtension);
|
||||
return extension;
|
||||
}
|
||||
|
||||
it('should force gc via window.gc()', inject([AsyncTestCompleter], (async) => {
|
||||
createExtension().gc().then((_) => {
|
||||
expect(log).toEqual([['executeScript', 'window.gc()']]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should mark the timeline via console.time()', inject([AsyncTestCompleter], (async) => {
|
||||
createExtension()
|
||||
.timeBegin('someName')
|
||||
.then((_) => {
|
||||
expect(log).toEqual([['executeScript', `console.time('someName');`]]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should mark the timeline via console.timeEnd()', inject([AsyncTestCompleter], (async) => {
|
||||
createExtension()
|
||||
.timeEnd('someName')
|
||||
.then((_) => {
|
||||
expect(log).toEqual([['executeScript', `console.timeEnd('someName');`]]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should mark the timeline via console.time() and console.timeEnd()',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
createExtension()
|
||||
.timeEnd('name1', 'name2')
|
||||
.then((_) => {
|
||||
expect(log).toEqual(
|
||||
[['executeScript', `console.timeEnd('name1');console.time('name2');`]]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
describe('readPerfLog', () => {
|
||||
|
||||
it('should execute a dummy script before reading them',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
// 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']]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should normalize times to ms and forward ph and pid event properties',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
createExtension([chromeTimelineEvents.complete('FunctionCall', 1100, 5500, null)])
|
||||
.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) => {
|
||||
var event = chromeTimelineEvents.create('X', 'FunctionCall', 1100, null);
|
||||
event['tdur'] = 5500;
|
||||
createExtension([event]).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) => {
|
||||
createExtension([chromeTimelineEvents.start('FunctionCall', 0)])
|
||||
.readPerfLog()
|
||||
.then((events) => {
|
||||
expect(events).toEqual([
|
||||
normEvents.start('script', 0),
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should ignore FunctionCalls from webdriver', inject([AsyncTestCompleter], (async) => {
|
||||
createExtension([chromeTimelineEvents.start(
|
||||
'FunctionCall', 0, {'data': {'scriptName': 'InjectedScript'}})])
|
||||
.readPerfLog()
|
||||
.then((events) => {
|
||||
expect(events).toEqual([]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should report begin timestamps', inject([AsyncTestCompleter], (async) => {
|
||||
createExtension([blinkEvents.create('S', 'someName', 1000)])
|
||||
.readPerfLog()
|
||||
.then((events) => {
|
||||
expect(events).toEqual([normEvents.markStart('someName', 1.0)]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should report end timestamps', inject([AsyncTestCompleter], (async) => {
|
||||
createExtension([blinkEvents.create('F', 'someName', 1000)])
|
||||
.readPerfLog()
|
||||
.then((events) => {
|
||||
expect(events).toEqual([normEvents.markEnd('someName', 1.0)]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should report gc', inject([AsyncTestCompleter], (async) => {
|
||||
createExtension([
|
||||
chromeTimelineEvents.start('GCEvent', 1000, {'usedHeapSizeBefore': 1000}),
|
||||
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': false}),
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should report major gc', inject([AsyncTestCompleter], (async) => {
|
||||
createExtension([
|
||||
chromeTimelineEvents.start('GCEvent', 1000, {'usedHeapSizeBefore': 1000}),
|
||||
v8EventsOtherProcess.start('majorGC', 1100, null),
|
||||
v8EventsOtherProcess.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': false}),
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should ignore major gc from different processes',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
createExtension([
|
||||
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', 'Rasterize', 'CompositeLayers']
|
||||
.forEach((recordType) => {
|
||||
it(`should report ${recordType} as "render"`, inject([AsyncTestCompleter], (async) => {
|
||||
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 throw an error on buffer overflow', inject([AsyncTestCompleter], (async) => {
|
||||
PromiseWrapper.catchError(
|
||||
createExtension([
|
||||
chromeTimelineEvents.start('FunctionCall', 1234),
|
||||
],
|
||||
'Tracing.bufferUsage')
|
||||
.readPerfLog(),
|
||||
(err) => {
|
||||
expect(() => { throw err; })
|
||||
.toThrowError('The DevTools trace buffer filled during the test!');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should match chrome browsers', () => {
|
||||
expect(createExtension().supports({'browserName': 'chrome'})).toBe(true);
|
||||
|
||||
expect(createExtension().supports({'browserName': 'Chrome'})).toBe(true);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
class MockDriverAdapter extends WebDriverAdapter {
|
||||
constructor(private _log: List<any>, private _events: List<any>, private _messageMethod: string) {
|
||||
super();
|
||||
}
|
||||
|
||||
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._events.map((event) => {
|
||||
return {
|
||||
'message':
|
||||
Json.stringify({'message': {'method': this._messageMethod, 'params': event}})
|
||||
};
|
||||
}));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,246 +0,0 @@
|
|||
import {
|
||||
afterEach,
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
} 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/common';
|
||||
|
||||
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 = Injector.resolveAndCreate([
|
||||
IOsDriverExtension.BINDINGS,
|
||||
bind(WebDriverAdapter).toValue(new MockDriverAdapter(log, perfRecords))
|
||||
]).get(IOsDriverExtension);
|
||||
return extension;
|
||||
}
|
||||
|
||||
it('should throw on forcing gc', () => {
|
||||
expect( () => createExtension().gc() ).toThrowError('Force GC is not supported on iOS');
|
||||
});
|
||||
|
||||
it('should mark the timeline via console.time()', inject([AsyncTestCompleter], (async) => {
|
||||
createExtension().timeBegin('someName').then( (_) => {
|
||||
expect(log).toEqual([['executeScript', `console.time('someName');`]]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should mark the timeline via console.timeEnd()', inject([AsyncTestCompleter], (async) => {
|
||||
createExtension().timeEnd('someName').then( (_) => {
|
||||
expect(log).toEqual([['executeScript', `console.timeEnd('someName');`]]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should mark the timeline via console.time() and console.timeEnd()', inject([AsyncTestCompleter], (async) => {
|
||||
createExtension().timeEnd('name1', 'name2').then( (_) => {
|
||||
expect(log).toEqual([['executeScript', `console.timeEnd('name1');console.time('name2');`]]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
describe('readPerfLog', () => {
|
||||
|
||||
it('should execute a dummy script before reading them', inject([AsyncTestCompleter], (async) => {
|
||||
// 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' ] ]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should report FunctionCall records as "script"', inject([AsyncTestCompleter], (async) => {
|
||||
createExtension([
|
||||
durationRecord('FunctionCall', 1, 5)
|
||||
]).readPerfLog().then( (events) => {
|
||||
expect(events).toEqual([
|
||||
normEvents.start('script', 1),
|
||||
normEvents.end('script', 5)
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should ignore FunctionCalls from webdriver', inject([AsyncTestCompleter], (async) => {
|
||||
createExtension([
|
||||
internalScriptRecord(1, 5)
|
||||
]).readPerfLog().then( (events) => {
|
||||
expect(events).toEqual([]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should report begin time', inject([AsyncTestCompleter], (async) => {
|
||||
createExtension([
|
||||
timeBeginRecord('someName', 12)
|
||||
]).readPerfLog().then( (events) => {
|
||||
expect(events).toEqual([
|
||||
normEvents.markStart('someName', 12)
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should report end timestamps', inject([AsyncTestCompleter], (async) => {
|
||||
createExtension([
|
||||
timeEndRecord('someName', 12)
|
||||
]).readPerfLog().then( (events) => {
|
||||
expect(events).toEqual([
|
||||
normEvents.markEnd('someName', 12)
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
['RecalculateStyles', 'Layout', 'UpdateLayerTree', 'Paint', 'Rasterize', 'CompositeLayers'].forEach( (recordType) => {
|
||||
it(`should report ${recordType}`, inject([AsyncTestCompleter], (async) => {
|
||||
createExtension([
|
||||
durationRecord(recordType, 0, 1)
|
||||
]).readPerfLog().then( (events) => {
|
||||
expect(events).toEqual([
|
||||
normEvents.start('render', 0),
|
||||
normEvents.end('render', 1),
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
it('should walk children', inject([AsyncTestCompleter], (async) => {
|
||||
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)
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should match safari browsers', () => {
|
||||
expect(createExtension().supports({
|
||||
'browserName': 'safari'
|
||||
})).toBe(true);
|
||||
|
||||
expect(createExtension().supports({
|
||||
'browserName': 'Safari'
|
||||
})).toBe(true);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
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'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,216 @@
|
|||
import {
|
||||
afterEach,
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
} 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/common';
|
||||
|
||||
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 = Injector.resolveAndCreate([
|
||||
IOsDriverExtension.BINDINGS,
|
||||
bind(WebDriverAdapter).toValue(new MockDriverAdapter(log, perfRecords))
|
||||
])
|
||||
.get(IOsDriverExtension);
|
||||
return extension;
|
||||
}
|
||||
|
||||
it('should throw on forcing gc', () => {
|
||||
expect(() => createExtension().gc()).toThrowError('Force GC is not supported on iOS');
|
||||
});
|
||||
|
||||
it('should mark the timeline via console.time()', inject([AsyncTestCompleter], (async) => {
|
||||
createExtension()
|
||||
.timeBegin('someName')
|
||||
.then((_) => {
|
||||
expect(log).toEqual([['executeScript', `console.time('someName');`]]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should mark the timeline via console.timeEnd()', inject([AsyncTestCompleter], (async) => {
|
||||
createExtension()
|
||||
.timeEnd('someName')
|
||||
.then((_) => {
|
||||
expect(log).toEqual([['executeScript', `console.timeEnd('someName');`]]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should mark the timeline via console.time() and console.timeEnd()',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
createExtension()
|
||||
.timeEnd('name1', 'name2')
|
||||
.then((_) => {
|
||||
expect(log).toEqual(
|
||||
[['executeScript', `console.timeEnd('name1');console.time('name2');`]]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
describe('readPerfLog', () => {
|
||||
|
||||
it('should execute a dummy script before reading them',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
// 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']]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should report FunctionCall records as "script"', inject([AsyncTestCompleter], (async) => {
|
||||
createExtension([durationRecord('FunctionCall', 1, 5)])
|
||||
.readPerfLog()
|
||||
.then((events) => {
|
||||
expect(events)
|
||||
.toEqual([normEvents.start('script', 1), normEvents.end('script', 5)]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should ignore FunctionCalls from webdriver', inject([AsyncTestCompleter], (async) => {
|
||||
createExtension([internalScriptRecord(1, 5)])
|
||||
.readPerfLog()
|
||||
.then((events) => {
|
||||
expect(events).toEqual([]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should report begin time', inject([AsyncTestCompleter], (async) => {
|
||||
createExtension([timeBeginRecord('someName', 12)])
|
||||
.readPerfLog()
|
||||
.then((events) => {
|
||||
expect(events).toEqual([normEvents.markStart('someName', 12)]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should report end timestamps', inject([AsyncTestCompleter], (async) => {
|
||||
createExtension([timeEndRecord('someName', 12)])
|
||||
.readPerfLog()
|
||||
.then((events) => {
|
||||
expect(events).toEqual([normEvents.markEnd('someName', 12)]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
['RecalculateStyles', 'Layout', 'UpdateLayerTree', 'Paint', 'Rasterize', 'CompositeLayers']
|
||||
.forEach((recordType) => {
|
||||
it(`should report ${recordType}`, inject([AsyncTestCompleter], (async) => {
|
||||
createExtension([durationRecord(recordType, 0, 1)])
|
||||
.readPerfLog()
|
||||
.then((events) => {
|
||||
expect(events).toEqual([
|
||||
normEvents.start('render', 0),
|
||||
normEvents.end('render', 1),
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
it('should walk children', inject([AsyncTestCompleter], (async) => {
|
||||
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)
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should match safari browsers', () => {
|
||||
expect(createExtension().supports({'browserName': 'safari'})).toBe(true);
|
||||
|
||||
expect(createExtension().supports({'browserName': 'Safari'})).toBe(true);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
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'}
|
||||
};
|
||||
}
|
||||
|
||||
class MockDriverAdapter extends WebDriverAdapter {
|
||||
constructor(private _log: List<any>, private _perfRecords: List<any>) { super(); }
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"emitDecoratorMetadata": true,
|
||||
"declaration": true,
|
||||
"module": "commonjs",
|
||||
"rootDir": ".",
|
||||
"sourceMap": true,
|
||||
"sourceRoot": ".",
|
||||
"target": "ES5",
|
||||
"outDir": "../../dist/js/cjs"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue