diff --git a/gulpfile.js b/gulpfile.js index e0f982a67b..e0873d581c 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -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); }); diff --git a/modules/angular2/src/facade/lang.ts b/modules/angular2/src/facade/lang.ts index 357fce3b03..bb5982a119 100644 --- a/modules/angular2/src/facade/lang.ts +++ b/modules/angular2/src/facade/lang.ts @@ -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 diff --git a/modules/benchpress/benchpress.es6 b/modules/benchpress/benchpress.ts similarity index 59% rename from modules/benchpress/benchpress.es6 rename to modules/benchpress/benchpress.ts index 7b1f0f23f2..7215e59dd3 100644 --- a/modules/benchpress/benchpress.es6 +++ b/modules/benchpress/benchpress.ts @@ -1,8 +1,10 @@ -import { bind } from 'angular2/di'; -import { Options } from './common'; +/// + +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 { return new Promise(function(resolve, reject) { fs.writeFile(filename, content, (error) => { if (error) { diff --git a/modules/benchpress/common.js b/modules/benchpress/common.js deleted file mode 100644 index 0da19f38f2..0000000000 --- a/modules/benchpress/common.js +++ /dev/null @@ -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'; diff --git a/modules/benchpress/common.ts b/modules/benchpress/common.ts new file mode 100644 index 0000000000..36460ea5d3 --- /dev/null +++ b/modules/benchpress/common.ts @@ -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'; diff --git a/modules/benchpress/index.cjs b/modules/benchpress/index.cjs deleted file mode 100644 index c4dc17cbd6..0000000000 --- a/modules/benchpress/index.cjs +++ /dev/null @@ -1,2 +0,0 @@ -require(require('traceur').RUNTIME_PATH); -module.exports = require('./benchpress.js'); diff --git a/modules/benchpress/src/common_options.js b/modules/benchpress/src/common_options.ts similarity index 91% rename from modules/benchpress/src/common_options.js rename to modules/benchpress/src/common_options.ts index be4936163f..f4810bfb03 100644 --- a/modules/benchpress/src/common_options.js +++ b/modules/benchpress/src/common_options.ts @@ -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() ) -]; \ No newline at end of file + bind(_NOW).toValue(() => DateWrapper.now()) +]; diff --git a/modules/benchpress/src/firefox_extension/data/installed_script.dart b/modules/benchpress/src/firefox_extension/data/installed_script.dart new file mode 100644 index 0000000000..4a36357121 --- /dev/null +++ b/modules/benchpress/src/firefox_extension/data/installed_script.dart @@ -0,0 +1,2 @@ +library benchpress.src.firefox_extension.data.installed_script; +//no dart implementation diff --git a/modules/benchpress/src/firefox_extension/data/installed_script.es6 b/modules/benchpress/src/firefox_extension/data/installed_script.es6 deleted file mode 100644 index 88cc250371..0000000000 --- a/modules/benchpress/src/firefox_extension/data/installed_script.es6 +++ /dev/null @@ -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"}); - diff --git a/modules/benchpress/src/firefox_extension/data/installed_script.ts b/modules/benchpress/src/firefox_extension/data/installed_script.ts new file mode 100644 index 0000000000..d758c9bc7b --- /dev/null +++ b/modules/benchpress/src/firefox_extension/data/installed_script.ts @@ -0,0 +1,11 @@ +declare var exportFunction; +declare var unsafeWindow; + +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"}); diff --git a/modules/benchpress/src/firefox_extension/lib/main.dart b/modules/benchpress/src/firefox_extension/lib/main.dart new file mode 100644 index 0000000000..81e92e6923 --- /dev/null +++ b/modules/benchpress/src/firefox_extension/lib/main.dart @@ -0,0 +1,2 @@ +library benchpress.src.firefox_extension.lib.main; +//no dart implementation diff --git a/modules/benchpress/src/firefox_extension/lib/main.es6 b/modules/benchpress/src/firefox_extension/lib/main.es6 deleted file mode 100644 index ecfb56488f..0000000000 --- a/modules/benchpress/src/firefox_extension/lib/main.es6 +++ /dev/null @@ -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(); - }); - } -}); diff --git a/modules/benchpress/src/firefox_extension/lib/main.ts b/modules/benchpress/src/firefox_extension/lib/main.ts new file mode 100644 index 0000000000..4d71fe056c --- /dev/null +++ b/modules/benchpress/src/firefox_extension/lib/main.ts @@ -0,0 +1,53 @@ +/// + +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()); + } +}); diff --git a/modules/benchpress/src/firefox_extension/lib/test_helper.dart b/modules/benchpress/src/firefox_extension/lib/test_helper.dart new file mode 100644 index 0000000000..ff67437610 --- /dev/null +++ b/modules/benchpress/src/firefox_extension/lib/test_helper.dart @@ -0,0 +1,2 @@ +library benchpress.src.firefox_extension.lib.test_helper; +//no dart implementation diff --git a/modules/benchpress/src/firefox_extension/lib/test_helper.es6 b/modules/benchpress/src/firefox_extension/lib/test_helper.ts similarity index 72% rename from modules/benchpress/src/firefox_extension/lib/test_helper.es6 rename to modules/benchpress/src/firefox_extension/lib/test_helper.ts index ee3eea4bc1..b1bb5ff7e5 100644 --- a/modules/benchpress/src/firefox_extension/lib/test_helper.es6 +++ b/modules/benchpress/src/firefox_extension/lib/test_helper.ts @@ -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); + }); }; - diff --git a/modules/benchpress/src/measure_values.js b/modules/benchpress/src/measure_values.js deleted file mode 100644 index 89ed03c91a..0000000000 --- a/modules/benchpress/src/measure_values.js +++ /dev/null @@ -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 - }; - } -} diff --git a/modules/benchpress/src/measure_values.ts b/modules/benchpress/src/measure_values.ts new file mode 100644 index 0000000000..bdacf455e2 --- /dev/null +++ b/modules/benchpress/src/measure_values.ts @@ -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) {} + + toJson() { + return { + 'timeStamp': DateWrapper.toJson(this.timeStamp), + 'runIndex': this.runIndex, + 'values': this.values + }; + } +} diff --git a/modules/benchpress/src/metric.js b/modules/benchpress/src/metric.js deleted file mode 100644 index 23e11d3854..0000000000 --- a/modules/benchpress/src/metric.js +++ /dev/null @@ -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 { - throw new BaseException('NYI'); - } - - /** - * Describes the metrics provided by this metric implementation. - * (e.g. units, ...) - */ - describe():StringMap { - throw new BaseException('NYI'); - } -} diff --git a/modules/benchpress/src/metric.ts b/modules/benchpress/src/metric.ts new file mode 100644 index 0000000000..6b3c6f38e7 --- /dev/null +++ b/modules/benchpress/src/metric.ts @@ -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 { 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> { throw new BaseException('NYI'); } + + /** + * Describes the metrics provided by this metric implementation. + * (e.g. units, ...) + */ + describe(): StringMap { throw new BaseException('NYI'); } +} diff --git a/modules/benchpress/src/metric/multi_metric.js b/modules/benchpress/src/metric/multi_metric.js deleted file mode 100644 index 99cde83c2d..0000000000 --- a/modules/benchpress/src/metric/multi_metric.js +++ /dev/null @@ -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 { - 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'); diff --git a/modules/benchpress/src/metric/multi_metric.ts b/modules/benchpress/src/metric/multi_metric.ts new file mode 100644 index 0000000000..1b14857df0 --- /dev/null +++ b/modules/benchpress/src/metric/multi_metric.ts @@ -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 { + 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) { super(); } + + /** + * 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> { + 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): Object { + var result = {}; + ListWrapper.forEach(maps, (map) => { + StringMapWrapper.forEach(map, (value, prop) => { result[prop] = value; }); + }); + return result; +} + +var _CHILDREN = new OpaqueToken('MultiMetric.children'); diff --git a/modules/benchpress/src/metric/perflog_metric.js b/modules/benchpress/src/metric/perflog_metric.ts similarity index 60% rename from modules/benchpress/src/metric/perflog_metric.js rename to modules/benchpress/src/metric/perflog_metric.ts index d929e5f8cc..304a845311 100644 --- a/modules/benchpress/src/metric/perflog_metric.js +++ b/modules/benchpress/src/metric/perflog_metric.ts @@ -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 { 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>; + private _measureCount: int; + _perfLogFeatures: PerfLogFeatures; - _driverExtension:WebDriverExtension; - _remainingEvents:List; - _measureCount:int; - _setTimeout:Function; - _microMetrics:StringMap; - _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, forceGc) { + constructor(private _driverExtension: WebDriverExtension, private _setTimeout: Function, + private _microMetrics: StringMap, 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 { 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 { 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 { + endMeasure(restart: boolean): Promise> { 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 { return this._driverExtension.timeBegin(this._markName(this._measureCount++)); } - _endMeasure(restart:boolean):Promise { - var markName = this._markName(this._measureCount-1); + _endMeasure(restart: boolean): Promise> { + 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 { + 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)) ]; diff --git a/modules/benchpress/src/reporter.js b/modules/benchpress/src/reporter.js deleted file mode 100644 index ddeae7b139..0000000000 --- a/modules/benchpress/src/reporter.js +++ /dev/null @@ -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, validSample:List):Promise { - throw new BaseException('NYI'); - } -} diff --git a/modules/benchpress/src/reporter.ts b/modules/benchpress/src/reporter.ts new file mode 100644 index 0000000000..ccd31ebdcf --- /dev/null +++ b/modules/benchpress/src/reporter.ts @@ -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 { throw new BaseException('NYI'); } + + reportSample(completeSample: List, + validSample: List): Promise { + throw new BaseException('NYI'); + } +} diff --git a/modules/benchpress/src/reporter/console_reporter.js b/modules/benchpress/src/reporter/console_reporter.js deleted file mode 100644 index 3663075eb0..0000000000 --- a/modules/benchpress/src/reporter/console_reporter.js +++ /dev/null @@ -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 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, validSample:List):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) -]; diff --git a/modules/benchpress/src/reporter/console_reporter.ts b/modules/benchpress/src/reporter/console_reporter.ts new file mode 100644 index 0000000000..323bd7eb3d --- /dev/null +++ b/modules/benchpress/src/reporter/console_reporter.ts @@ -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 { 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; + + 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 { + 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, + validSample: List): 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.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) +]; diff --git a/modules/benchpress/src/reporter/json_file_reporter.js b/modules/benchpress/src/reporter/json_file_reporter.js deleted file mode 100644 index d1ccf50bb1..0000000000 --- a/modules/benchpress/src/reporter/json_file_reporter.js +++ /dev/null @@ -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, validSample:List):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('.') -]; diff --git a/modules/benchpress/src/reporter/json_file_reporter.ts b/modules/benchpress/src/reporter/json_file_reporter.ts new file mode 100644 index 0000000000..ae704c1b90 --- /dev/null +++ b/modules/benchpress/src/reporter/json_file_reporter.ts @@ -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 { 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, + validSample: List): 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('.') +]; diff --git a/modules/benchpress/src/reporter/multi_reporter.js b/modules/benchpress/src/reporter/multi_reporter.js deleted file mode 100644 index 239dbc631c..0000000000 --- a/modules/benchpress/src/reporter/multi_reporter.js +++ /dev/null @@ -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, validSample:List):Promise { - return PromiseWrapper.all(ListWrapper.map( - this._reporters, (reporter) => reporter.reportSample(completeSample, validSample) - )); - } -} - -var _CHILDREN = new OpaqueToken('MultiReporter.children'); diff --git a/modules/benchpress/src/reporter/multi_reporter.ts b/modules/benchpress/src/reporter/multi_reporter.ts new file mode 100644 index 0000000000..62fe2c302c --- /dev/null +++ b/modules/benchpress/src/reporter/multi_reporter.ts @@ -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): List { + 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, + validSample: List): Promise> { + return PromiseWrapper.all(ListWrapper.map( + this._reporters, (reporter) => reporter.reportSample(completeSample, validSample))); + } +} + +var _CHILDREN = new OpaqueToken('MultiReporter.children'); diff --git a/modules/benchpress/src/runner.js b/modules/benchpress/src/runner.js deleted file mode 100644 index e1eac007d2..0000000000 --- a/modules/benchpress/src/runner.js +++ /dev/null @@ -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 { - 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] - ) -]; diff --git a/modules/benchpress/src/runner.ts b/modules/benchpress/src/runner.ts new file mode 100644 index 0000000000..0a5cb08f07 --- /dev/null +++ b/modules/benchpress/src/runner.ts @@ -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; + constructor(defaultBindings: List = null) { + if (isBlank(defaultBindings)) { + defaultBindings = []; + } + this._defaultBindings = defaultBindings; + } + + sample({id, execute, prepare, microMetrics, bindings}): Promise { + 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]) +]; diff --git a/modules/benchpress/src/sample_description.js b/modules/benchpress/src/sample_description.js deleted file mode 100644 index 56949d36ce..0000000000 --- a/modules/benchpress/src/sample_description.js +++ /dev/null @@ -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, 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 - ] - ) -]; diff --git a/modules/benchpress/src/sample_description.ts b/modules/benchpress/src/sample_description.ts new file mode 100644 index 0000000000..72eb33aa14 --- /dev/null +++ b/modules/benchpress/src/sample_description.ts @@ -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 { return _BINDINGS; } + description: StringMap; + + constructor(public id: string, descriptions: List>, + public metrics: StringMap) { + 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 + ])]; diff --git a/modules/benchpress/src/sampler.js b/modules/benchpress/src/sampler.js deleted file mode 100644 index 19fb08bf7d..0000000000 --- a/modules/benchpress/src/sampler.js +++ /dev/null @@ -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 { - 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 { - 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 - ] - ) -]; diff --git a/modules/benchpress/src/sampler.ts b/modules/benchpress/src/sampler.ts new file mode 100644 index 0000000000..0b26fb8f30 --- /dev/null +++ b/modules/benchpress/src/sampler.ts @@ -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 { 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 { + 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 { + 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, public validSample: List) {} +} + +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 + ])]; diff --git a/modules/benchpress/src/statistic.js b/modules/benchpress/src/statistic.ts similarity index 62% rename from modules/benchpress/src/statistic.js rename to modules/benchpress/src/statistic.ts index 61a8d2df88..8fcb86930f 100644 --- a/modules/benchpress/src/statistic.js +++ b/modules/benchpress/src/statistic.ts @@ -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, xMean: number, yValues: List, + yMean: number) { // See http://en.wikipedia.org/wiki/Simple_linear_regression var dividendSum = 0; var divisorSum = 0; - for (var i=0; i delegate, [delegateToken] - ) - ]; + return [bind(Validator).toFactory((delegate) => delegate, [delegateToken])]; } /** * Calculates a valid sample out of the complete sample */ - validate(completeSample:List):List { + validate(completeSample: List): List { 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'); - } -} \ No newline at end of file + describe(): StringMap { throw new BaseException('NYI'); } +} diff --git a/modules/benchpress/src/validator/regression_slope_validator.js b/modules/benchpress/src/validator/regression_slope_validator.ts similarity index 52% rename from modules/benchpress/src/validator/regression_slope_validator.js rename to modules/benchpress/src/validator/regression_slope_validator.ts index 6960b839d4..435dad34d9 100644 --- a/modules/benchpress/src/validator/regression_slope_validator.js +++ b/modules/benchpress/src/validator/regression_slope_validator.ts @@ -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 { 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 { + return {'sampleSize': this._sampleSize, 'regressionSlopeMetric': this._metric}; } - validate(completeSample:List):List { + validate(completeSample: List): List { 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= 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') ]; diff --git a/modules/benchpress/src/validator/size_validator.js b/modules/benchpress/src/validator/size_validator.ts similarity index 51% rename from modules/benchpress/src/validator/size_validator.js rename to modules/benchpress/src/validator/size_validator.ts index cbc71550b3..3f407f9ad5 100644 --- a/modules/benchpress/src/validator/size_validator.js +++ b/modules/benchpress/src/validator/size_validator.ts @@ -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 { 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 { return {'sampleSize': this._sampleSize}; } - validate(completeSample:List):List { + validate(completeSample: List): List { 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) -]; \ No newline at end of file +]; diff --git a/modules/benchpress/src/web_driver_adapter.js b/modules/benchpress/src/web_driver_adapter.js deleted file mode 100644 index 9e27e06817..0000000000 --- a/modules/benchpress/src/web_driver_adapter.js +++ /dev/null @@ -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 { - throw new BaseException('NYI'); - } - logs(type:string):Promise { - throw new BaseException('NYI'); - } -} diff --git a/modules/benchpress/src/web_driver_adapter.ts b/modules/benchpress/src/web_driver_adapter.ts new file mode 100644 index 0000000000..f1b6d70b9d --- /dev/null +++ b/modules/benchpress/src/web_driver_adapter.ts @@ -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 { throw new BaseException('NYI'); } + executeScript(script: string): Promise { throw new BaseException('NYI'); } + capabilities(): Promise> { throw new BaseException('NYI'); } + logs(type: string): Promise> { throw new BaseException('NYI'); } +} diff --git a/modules/benchpress/src/web_driver_extension.js b/modules/benchpress/src/web_driver_extension.js deleted file mode 100644 index 9d70a3c2a1..0000000000 --- a/modules/benchpress/src/web_driver_extension.js +++ /dev/null @@ -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 { - 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'); diff --git a/modules/benchpress/src/web_driver_extension.ts b/modules/benchpress/src/web_driver_extension.ts new file mode 100644 index 0000000000..88d6dc1436 --- /dev/null +++ b/modules/benchpress/src/web_driver_extension.ts @@ -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 { + 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: string): Promise { throw new BaseException('NYI'); } + + timeEnd(name: string, restartName: string): 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> { 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 = false, gc = false}: {render?: boolean, gc?: boolean} = {}) { + this.render = render; + this.gc = gc; + } +} + +var _CHILDREN = new OpaqueToken('WebDriverExtension.children'); diff --git a/modules/benchpress/src/webdriver/chrome_driver_extension.js b/modules/benchpress/src/webdriver/chrome_driver_extension.js deleted file mode 100644 index ac31af663e..0000000000 --- a/modules/benchpress/src/webdriver/chrome_driver_extension.js +++ /dev/null @@ -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] - ) -]; diff --git a/modules/benchpress/src/webdriver/chrome_driver_extension.ts b/modules/benchpress/src/webdriver/chrome_driver_extension.ts new file mode 100644 index 0000000000..fdc801190d --- /dev/null +++ b/modules/benchpress/src/webdriver/chrome_driver_extension.ts @@ -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 { return _BINDINGS; } + + constructor(private _driver: WebDriverAdapter) { super(); } + + 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(): Promise { + // 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>, + normalizedEvents: List> = 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: StringMap, + data: StringMap): StringMap { + 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])]; diff --git a/modules/benchpress/src/webdriver/ios_driver_extension.js b/modules/benchpress/src/webdriver/ios_driver_extension.ts similarity index 61% rename from modules/benchpress/src/webdriver/ios_driver_extension.js rename to modules/benchpress/src/webdriver/ios_driver_extension.ts index 80d92d23a7..2d2435f89a 100644 --- a/modules/benchpress/src/webdriver/ios_driver_extension.js +++ b/modules/benchpress/src/webdriver/ios_driver_extension.ts @@ -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 { return _BINDINGS; } - _driver:WebDriverAdapter; + constructor(private _driver: WebDriverAdapter) { super(); } - constructor(driver:WebDriverAdapter) { - super(); - this._driver = driver; - } + gc(): Promise { 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 { return this._driver.executeScript(`console.time('${name}');`); } - timeEnd(name:string, restartName:string = null):Promise { + timeEnd(name: string, restartName: string = null): Promise { 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): 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] - ) -]; \ No newline at end of file +var _BINDINGS = [bind(IOsDriverExtension) + .toFactory((driver) => new IOsDriverExtension(driver), [WebDriverAdapter])]; diff --git a/modules/benchpress/src/webdriver/selenium_webdriver_adapter.dart b/modules/benchpress/src/webdriver/selenium_webdriver_adapter.dart new file mode 100644 index 0000000000..1beebecc47 --- /dev/null +++ b/modules/benchpress/src/webdriver/selenium_webdriver_adapter.dart @@ -0,0 +1,2 @@ +library benchpress.src.webdriver.selenium_webdriver_adapter; +//no dart implementation diff --git a/modules/benchpress/src/webdriver/selenium_webdriver_adapter.es6 b/modules/benchpress/src/webdriver/selenium_webdriver_adapter.es6 deleted file mode 100644 index 26ae6e31c3..0000000000 --- a/modules/benchpress/src/webdriver/selenium_webdriver_adapter.es6 +++ /dev/null @@ -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), []) -]; \ No newline at end of file diff --git a/modules/benchpress/src/webdriver/selenium_webdriver_adapter.ts b/modules/benchpress/src/webdriver/selenium_webdriver_adapter.ts new file mode 100644 index 0000000000..8c16a688e7 --- /dev/null +++ b/modules/benchpress/src/webdriver/selenium_webdriver_adapter.ts @@ -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 { 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 { + 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): 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), [])]; diff --git a/modules/benchpress/test/firefox_extension/conf.dart b/modules/benchpress/test/firefox_extension/conf.dart new file mode 100644 index 0000000000..150503ce48 --- /dev/null +++ b/modules/benchpress/test/firefox_extension/conf.dart @@ -0,0 +1,2 @@ +library benchpress.test.firefox_extension.conf; +//empty as we don't have a version for dart diff --git a/modules/benchpress/test/firefox_extension/conf.es6 b/modules/benchpress/test/firefox_extension/conf.ts similarity index 54% rename from modules/benchpress/test/firefox_extension/conf.es6 rename to modules/benchpress/test/firefox_extension/conf.ts index 057cd936c2..d267fe4c30 100644 --- a/modules/benchpress/test/firefox_extension/conf.es6 +++ b/modules/benchpress/test/firefox_extension/conf.ts @@ -1,3 +1,4 @@ +/// 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)} }; diff --git a/modules/benchpress/test/firefox_extension/spec.dart b/modules/benchpress/test/firefox_extension/spec.dart new file mode 100644 index 0000000000..a959897d70 --- /dev/null +++ b/modules/benchpress/test/firefox_extension/spec.dart @@ -0,0 +1,2 @@ +library benchpress.test.firefox_extension.spec; +//no dart implementation diff --git a/modules/benchpress/test/firefox_extension/spec.es6 b/modules/benchpress/test/firefox_extension/spec.ts similarity index 57% rename from modules/benchpress/test/firefox_extension/spec.es6 rename to modules/benchpress/test/firefox_extension/spec.ts index faddd525e9..c817880c87 100644 --- a/modules/benchpress/test/firefox_extension/spec.es6 +++ b/modules/benchpress/test/firefox_extension/spec.ts @@ -1,3 +1,7 @@ +/// +/// +/// + 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); }) }); diff --git a/modules/benchpress/test/metric/multi_metric_spec.js b/modules/benchpress/test/metric/multi_metric_spec.js deleted file mode 100644 index 1a2a50a207..0000000000 --- a/modules/benchpress/test/metric/multi_metric_spec.js +++ /dev/null @@ -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 { - var result = {}; - result[this._id] = { - 'restart': restart - }; - return PromiseWrapper.resolve(result); - } - - describe():StringMap { - var result = {}; - result[this._id] = 'describe'; - return result; - } -} diff --git a/modules/benchpress/test/metric/multi_metric_spec.ts b/modules/benchpress/test/metric/multi_metric_spec.ts new file mode 100644 index 0000000000..f412fdc22f --- /dev/null +++ b/modules/benchpress/test/metric/multi_metric_spec.ts @@ -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 { return PromiseWrapper.resolve(`${this._id}_beginMeasure`); } + + endMeasure(restart: boolean): Promise> { + var result = {}; + result[this._id] = { + 'restart': restart + }; + return PromiseWrapper.resolve(result); + } + + describe(): StringMap { + var result = {}; + result[this._id] = 'describe'; + return result; + } +} diff --git a/modules/benchpress/test/metric/perflog_metric_spec.js b/modules/benchpress/test/metric/perflog_metric_spec.js deleted file mode 100644 index c98c541bc7..0000000000 --- a/modules/benchpress/test/metric/perflog_metric_spec.js +++ /dev/null @@ -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); - } -} diff --git a/modules/benchpress/test/metric/perflog_metric_spec.ts b/modules/benchpress/test/metric/perflog_metric_spec.ts new file mode 100644 index 0000000000..ba1d4ba1cf --- /dev/null +++ b/modules/benchpress/test/metric/perflog_metric_spec.ts @@ -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, private _commandLog: List, + private _perfLogFeatures: PerfLogFeatures) { + super(); + } + + 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); + } +} diff --git a/modules/benchpress/test/reporter/console_reporter_spec.js b/modules/benchpress/test/reporter/console_reporter_spec.ts similarity index 54% rename from modules/benchpress/test/reporter/console_reporter_spec.js rename to modules/benchpress/test/reporter/console_reporter_spec.ts index e4b1fe0f96..53a5f4c405 100644 --- a/modules/benchpress/test/reporter/console_reporter_spec.js +++ b/modules/benchpress/test/reporter/console_reporter_spec.ts @@ -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']); }); }); diff --git a/modules/benchpress/test/reporter/json_file_reporter_spec.js b/modules/benchpress/test/reporter/json_file_reporter_spec.js deleted file mode 100644 index 57aaf4d30c..0000000000 --- a/modules/benchpress/test/reporter/json_file_reporter_spec.js +++ /dev/null @@ -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); -} diff --git a/modules/benchpress/test/reporter/json_file_reporter_spec.ts b/modules/benchpress/test/reporter/json_file_reporter_spec.ts new file mode 100644 index 0000000000..9e6b4bf33a --- /dev/null +++ b/modules/benchpress/test/reporter/json_file_reporter_spec.ts @@ -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); +} diff --git a/modules/benchpress/test/reporter/multi_reporter_spec.js b/modules/benchpress/test/reporter/multi_reporter_spec.js deleted file mode 100644 index 7c7c78d7a8..0000000000 --- a/modules/benchpress/test/reporter/multi_reporter_spec.js +++ /dev/null @@ -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, validSample:List):Promise { - return PromiseWrapper.resolve({ - 'id': this._id, - 'completeSample': completeSample, - 'validSample': validSample - }); - } - -} diff --git a/modules/benchpress/test/reporter/multi_reporter_spec.ts b/modules/benchpress/test/reporter/multi_reporter_spec.ts new file mode 100644 index 0000000000..f2f0fe89df --- /dev/null +++ b/modules/benchpress/test/reporter/multi_reporter_spec.ts @@ -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> { + return PromiseWrapper.resolve({'id': this._id, 'values': values}); + } + + reportSample(completeSample: List, + validSample: List): Promise> { + return PromiseWrapper.resolve( + {'id': this._id, 'completeSample': completeSample, 'validSample': validSample}); + } +} diff --git a/modules/benchpress/test/runner_spec.js b/modules/benchpress/test/runner_spec.js deleted file mode 100644 index 38a652db31..0000000000 --- a/modules/benchpress/test/runner_spec.js +++ /dev/null @@ -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); - } -} diff --git a/modules/benchpress/test/runner_spec.ts b/modules/benchpress/test/runner_spec.ts new file mode 100644 index 0000000000..1ce7c1a078 --- /dev/null +++ b/modules/benchpress/test/runner_spec.ts @@ -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 { 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); } +} diff --git a/modules/benchpress/test/sampler_spec.js b/modules/benchpress/test/sampler_spec.js deleted file mode 100644 index f4bab21ec6..0000000000 --- a/modules/benchpress/test/sampler_spec.js +++ /dev/null @@ -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):List { - 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); - } -} diff --git a/modules/benchpress/test/sampler_spec.ts b/modules/benchpress/test/sampler_spec.ts new file mode 100644 index 0000000000..16a6836684 --- /dev/null +++ b/modules/benchpress/test/sampler_spec.ts @@ -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; + private _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 { + private _log: List; + constructor(log = null, private _validate: Function = null) { + super(); + if (isBlank(log)) { + log = []; + } + this._log = log; + } + validate(completeSample: List): List { + 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; + 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; + 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); + } +} diff --git a/modules/benchpress/test/statistic_spec.js b/modules/benchpress/test/statistic_spec.ts similarity index 82% rename from modules/benchpress/test/statistic_spec.js rename to modules/benchpress/test/statistic_spec.ts index 83a062bfdf..3a8986f4d5 100644 --- a/modules/benchpress/test/statistic_spec.js +++ b/modules/benchpress/test/statistic_spec.ts @@ -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); }); }); diff --git a/modules/benchpress/test/trace_event_factory.js b/modules/benchpress/test/trace_event_factory.js deleted file mode 100644 index 4876ebbe94..0000000000 --- a/modules/benchpress/test/trace_event_factory.js +++ /dev/null @@ -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; - } -} \ No newline at end of file diff --git a/modules/benchpress/test/trace_event_factory.ts b/modules/benchpress/test/trace_event_factory.ts new file mode 100644 index 0000000000..3a413c4e5e --- /dev/null +++ b/modules/benchpress/test/trace_event_factory.ts @@ -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; + } +} diff --git a/modules/benchpress/test/validator/regression_slope_validator_spec.js b/modules/benchpress/test/validator/regression_slope_validator_spec.ts similarity index 52% rename from modules/benchpress/test/validator/regression_slope_validator_spec.js rename to modules/benchpress/test/validator/regression_slope_validator_spec.ts index 775a3e08b5..ec4c0a2a5b 100644 --- a/modules/benchpress/test/validator/regression_slope_validator_spec.js +++ b/modules/benchpress/test/validator/regression_slope_validator_spec.ts @@ -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)); }); }); diff --git a/modules/benchpress/test/validator/size_validator_spec.js b/modules/benchpress/test/validator/size_validator_spec.ts similarity index 57% rename from modules/benchpress/test/validator/size_validator_spec.js rename to modules/benchpress/test/validator/size_validator_spec.ts index e5e1533d0b..4261d086cd 100644 --- a/modules/benchpress/test/validator/size_validator_spec.js +++ b/modules/benchpress/test/validator/size_validator_spec.ts @@ -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)); }); diff --git a/modules/benchpress/test/web_driver_extension_spec.js b/modules/benchpress/test/web_driver_extension_spec.js deleted file mode 100644 index 7d0e84ee28..0000000000 --- a/modules/benchpress/test/web_driver_extension_spec.js +++ /dev/null @@ -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); - } -} diff --git a/modules/benchpress/test/web_driver_extension_spec.ts b/modules/benchpress/test/web_driver_extension_spec.ts new file mode 100644 index 0000000000..0bb2c9a73a --- /dev/null +++ b/modules/benchpress/test/web_driver_extension_spec.ts @@ -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): boolean { + return StringWrapper.equals(capabilities['browser'], this.id); + } +} diff --git a/modules/benchpress/test/webdriver/chrome_driver_extension_spec.js b/modules/benchpress/test/webdriver/chrome_driver_extension_spec.js deleted file mode 100644 index fc85c33cd5..0000000000 --- a/modules/benchpress/test/webdriver/chrome_driver_extension_spec.js +++ /dev/null @@ -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; - } - } - -} diff --git a/modules/benchpress/test/webdriver/chrome_driver_extension_spec.ts b/modules/benchpress/test/webdriver/chrome_driver_extension_spec.ts new file mode 100644 index 0000000000..df5583ecad --- /dev/null +++ b/modules/benchpress/test/webdriver/chrome_driver_extension_spec.ts @@ -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, private _events: List, 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; + } + } +} diff --git a/modules/benchpress/test/webdriver/ios_driver_extension_spec.js b/modules/benchpress/test/webdriver/ios_driver_extension_spec.js deleted file mode 100644 index 5ddaf35169..0000000000 --- a/modules/benchpress/test/webdriver/ios_driver_extension_spec.js +++ /dev/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; - } - } - -} diff --git a/modules/benchpress/test/webdriver/ios_driver_extension_spec.ts b/modules/benchpress/test/webdriver/ios_driver_extension_spec.ts new file mode 100644 index 0000000000..40ed3a2d04 --- /dev/null +++ b/modules/benchpress/test/webdriver/ios_driver_extension_spec.ts @@ -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, private _perfRecords: List) { 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; + } + } +} diff --git a/modules/benchpress/tsconfig.json b/modules/benchpress/tsconfig.json new file mode 100644 index 0000000000..13249a24e7 --- /dev/null +++ b/modules/benchpress/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "emitDecoratorMetadata": true, + "declaration": true, + "module": "commonjs", + "rootDir": ".", + "sourceMap": true, + "sourceRoot": ".", + "target": "ES5", + "outDir": "../../dist/js/cjs" + } +}