2016-08-03 18:00:07 -04:00
|
|
|
/**
|
|
|
|
* @license
|
2020-05-19 15:08:49 -04:00
|
|
|
* Copyright Google LLC All Rights Reserved.
|
2016-08-03 18:00:07 -04:00
|
|
|
*
|
|
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
|
|
* found in the LICENSE file at https://angular.io/license
|
|
|
|
*/
|
2015-05-27 17:57:54 -04:00
|
|
|
|
perf: switch angular to use StaticInjector instead of ReflectiveInjector
This change allows ReflectiveInjector to be tree shaken resulting
in not needed Reflect polyfil and smaller bundles.
Code savings for HelloWorld using Closure:
Reflective: bundle.js: 105,864(34,190 gzip)
Static: bundle.js: 154,889(33,555 gzip)
645( 2%)
BREAKING CHANGE:
`platformXXXX()` no longer accepts providers which depend on reflection.
Specifically the method signature when from `Provider[]` to
`StaticProvider[]`.
Example:
Before:
```
[
MyClass,
{provide: ClassA, useClass: SubClassA}
]
```
After:
```
[
{provide: MyClass, deps: [Dep1,...]},
{provide: ClassA, useClass: SubClassA, deps: [Dep1,...]}
]
```
NOTE: This only applies to platform creation and providers for the JIT
compiler. It does not apply to `@Compotent` or `@NgModule` provides
declarations.
Benchpress note: Previously Benchpress also supported reflective
provides, which now require static providers.
DEPRECATION:
- `ReflectiveInjector` is now deprecated as it will be remove. Use
`Injector.create` as a replacement.
closes #18496
2017-08-03 15:33:29 -04:00
|
|
|
import {Injector, MeasureValues, Metric, Options, Reporter, Sampler, Validator, WebDriverAdapter} from '../index';
|
2015-05-27 17:57:54 -04:00
|
|
|
|
2017-12-16 17:42:55 -05:00
|
|
|
{
|
2016-11-12 08:08:58 -05:00
|
|
|
const EMPTY_EXECUTE = () => {};
|
2015-05-27 17:57:54 -04:00
|
|
|
|
|
|
|
describe('sampler', () => {
|
2016-11-12 08:08:58 -05:00
|
|
|
let sampler: Sampler;
|
2015-05-27 17:57:54 -04:00
|
|
|
|
|
|
|
function createSampler({driver, metric, reporter, validator, prepare, execute}: {
|
|
|
|
driver?: any,
|
|
|
|
metric?: Metric,
|
|
|
|
reporter?: Reporter,
|
|
|
|
validator?: Validator,
|
|
|
|
prepare?: any,
|
|
|
|
execute?: any
|
|
|
|
} = {}) {
|
2016-11-12 08:08:58 -05:00
|
|
|
let time = 1000;
|
2016-09-30 12:26:53 -04:00
|
|
|
if (!metric) {
|
2015-05-27 17:57:54 -04:00
|
|
|
metric = new MockMetric([]);
|
|
|
|
}
|
2016-09-30 12:26:53 -04:00
|
|
|
if (!reporter) {
|
2015-05-27 17:57:54 -04:00
|
|
|
reporter = new MockReporter([]);
|
|
|
|
}
|
2017-03-02 12:37:01 -05:00
|
|
|
if (driver == null) {
|
2015-05-27 17:57:54 -04:00
|
|
|
driver = new MockDriverAdapter([]);
|
|
|
|
}
|
2016-11-12 08:08:58 -05:00
|
|
|
const providers = [
|
2016-08-03 18:00:07 -04:00
|
|
|
Options.DEFAULT_PROVIDERS, Sampler.PROVIDERS, {provide: Metric, useValue: metric},
|
|
|
|
{provide: Reporter, useValue: reporter}, {provide: WebDriverAdapter, useValue: driver},
|
|
|
|
{provide: Options.EXECUTE, useValue: execute}, {provide: Validator, useValue: validator},
|
2016-10-02 17:12:14 -04:00
|
|
|
{provide: Options.NOW, useValue: () => new Date(time++)}
|
2015-05-27 17:57:54 -04:00
|
|
|
];
|
2017-03-02 12:37:01 -05:00
|
|
|
if (prepare != null) {
|
2016-06-02 20:30:40 -04:00
|
|
|
providers.push({provide: Options.PREPARE, useValue: prepare});
|
2015-05-27 17:57:54 -04:00
|
|
|
}
|
|
|
|
|
perf: switch angular to use StaticInjector instead of ReflectiveInjector
This change allows ReflectiveInjector to be tree shaken resulting
in not needed Reflect polyfil and smaller bundles.
Code savings for HelloWorld using Closure:
Reflective: bundle.js: 105,864(34,190 gzip)
Static: bundle.js: 154,889(33,555 gzip)
645( 2%)
BREAKING CHANGE:
`platformXXXX()` no longer accepts providers which depend on reflection.
Specifically the method signature when from `Provider[]` to
`StaticProvider[]`.
Example:
Before:
```
[
MyClass,
{provide: ClassA, useClass: SubClassA}
]
```
After:
```
[
{provide: MyClass, deps: [Dep1,...]},
{provide: ClassA, useClass: SubClassA, deps: [Dep1,...]}
]
```
NOTE: This only applies to platform creation and providers for the JIT
compiler. It does not apply to `@Compotent` or `@NgModule` provides
declarations.
Benchpress note: Previously Benchpress also supported reflective
provides, which now require static providers.
DEPRECATION:
- `ReflectiveInjector` is now deprecated as it will be remove. Use
`Injector.create` as a replacement.
closes #18496
2017-08-03 15:33:29 -04:00
|
|
|
sampler = Injector.create(providers).get(Sampler);
|
2015-05-27 17:57:54 -04:00
|
|
|
}
|
|
|
|
|
2021-05-23 16:16:02 -04:00
|
|
|
it('should call the prepare and execute callbacks using WebDriverAdapter.waitFor', done => {
|
|
|
|
const log: any[] = [];
|
|
|
|
let count = 0;
|
|
|
|
const driver = new MockDriverAdapter([], (callback: Function) => {
|
|
|
|
const result = callback();
|
|
|
|
log.push(result);
|
|
|
|
return Promise.resolve(result);
|
|
|
|
});
|
|
|
|
createSampler({
|
|
|
|
driver: driver,
|
|
|
|
validator: createCountingValidator(2),
|
|
|
|
prepare: () => count++,
|
|
|
|
execute: () => count++,
|
|
|
|
});
|
|
|
|
sampler.sample().then((_) => {
|
|
|
|
expect(count).toBe(4);
|
|
|
|
expect(log).toEqual([0, 1, 2, 3]);
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should call prepare, beginMeasure, execute, endMeasure for every iteration', done => {
|
|
|
|
let workCount = 0;
|
|
|
|
const log: any[] = [];
|
|
|
|
createSampler({
|
|
|
|
metric: createCountingMetric(log),
|
|
|
|
validator: createCountingValidator(2),
|
|
|
|
prepare: () => {
|
|
|
|
log.push(`p${workCount++}`);
|
|
|
|
},
|
|
|
|
execute: () => {
|
|
|
|
log.push(`w${workCount++}`);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
sampler.sample().then((_) => {
|
|
|
|
expect(log).toEqual([
|
|
|
|
'p0',
|
|
|
|
['beginMeasure'],
|
|
|
|
'w1',
|
|
|
|
['endMeasure', false, {'script': 0}],
|
|
|
|
'p2',
|
|
|
|
['beginMeasure'],
|
|
|
|
'w3',
|
|
|
|
['endMeasure', false, {'script': 1}],
|
|
|
|
]);
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
2015-05-27 17:57:54 -04:00
|
|
|
|
|
|
|
it('should call execute, endMeasure for every iteration if there is no prepare callback',
|
2021-05-23 16:16:02 -04:00
|
|
|
done => {
|
2016-11-12 08:08:58 -05:00
|
|
|
const log: any[] = [];
|
|
|
|
let workCount = 0;
|
2015-05-27 17:57:54 -04:00
|
|
|
createSampler({
|
|
|
|
metric: createCountingMetric(log),
|
|
|
|
validator: createCountingValidator(2),
|
2020-04-13 19:40:21 -04:00
|
|
|
execute: () => {
|
|
|
|
log.push(`w${workCount++}`);
|
|
|
|
},
|
2015-05-27 17:57:54 -04:00
|
|
|
prepare: null
|
|
|
|
});
|
|
|
|
sampler.sample().then((_) => {
|
|
|
|
expect(log).toEqual([
|
|
|
|
['beginMeasure'],
|
|
|
|
'w0',
|
|
|
|
['endMeasure', true, {'script': 0}],
|
|
|
|
'w1',
|
|
|
|
['endMeasure', true, {'script': 1}],
|
|
|
|
]);
|
2021-05-23 16:16:02 -04:00
|
|
|
done();
|
2015-05-27 17:57:54 -04:00
|
|
|
});
|
2021-05-23 16:16:02 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
it('should only collect metrics for execute and ignore metrics from prepare', done => {
|
|
|
|
let scriptTime = 0;
|
|
|
|
let iterationCount = 1;
|
|
|
|
createSampler({
|
|
|
|
validator: createCountingValidator(2),
|
|
|
|
metric: new MockMetric(
|
|
|
|
[],
|
|
|
|
() => {
|
|
|
|
const result = Promise.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}));
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should call the validator for every execution and store the valid sample', done => {
|
|
|
|
const log: any[] = [];
|
|
|
|
const validSample = [mv(null!, null!, {})];
|
|
|
|
|
|
|
|
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]);
|
|
|
|
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should report the metric values', done => {
|
|
|
|
const log: any[] = [];
|
|
|
|
const validSample = [mv(null!, null!, {})];
|
|
|
|
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
|
|
|
|
]);
|
|
|
|
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
2015-05-27 17:57:54 -04:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-08-26 19:34:08 -04:00
|
|
|
function mv(runIndex: number, time: number, values: {[key: string]: number}) {
|
2016-10-02 17:12:14 -04:00
|
|
|
return new MeasureValues(runIndex, new Date(time), values);
|
2015-05-27 17:57:54 -04:00
|
|
|
}
|
|
|
|
|
2017-03-24 12:56:50 -04:00
|
|
|
function createCountingValidator(count: number, validSample?: MeasureValues[], log: any[] = []) {
|
2016-08-26 19:34:08 -04:00
|
|
|
return new MockValidator(log, (completeSample: MeasureValues[]) => {
|
2015-05-27 17:57:54 -04:00
|
|
|
count--;
|
|
|
|
if (count === 0) {
|
2016-10-07 21:11:37 -04:00
|
|
|
return validSample || completeSample;
|
2015-05-27 17:57:54 -04:00
|
|
|
} else {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-08-26 19:34:08 -04:00
|
|
|
function createCountingMetric(log: any[] = []) {
|
2016-11-12 08:08:58 -05:00
|
|
|
let scriptTime = 0;
|
2016-10-04 18:57:37 -04:00
|
|
|
return new MockMetric(log, () => ({'script': scriptTime++}));
|
2015-05-27 17:57:54 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
class MockDriverAdapter extends WebDriverAdapter {
|
2020-04-13 19:40:21 -04:00
|
|
|
constructor(private _log: any[] = [], private _waitFor: Function|null = null) {
|
|
|
|
super();
|
|
|
|
}
|
2015-05-27 17:57:54 -04:00
|
|
|
waitFor(callback: Function): Promise<any> {
|
2017-03-02 12:37:01 -05:00
|
|
|
if (this._waitFor != null) {
|
2015-05-27 17:57:54 -04:00
|
|
|
return this._waitFor(callback);
|
|
|
|
} else {
|
2016-08-02 18:53:34 -04:00
|
|
|
return Promise.resolve(callback());
|
2015-05-27 17:57:54 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class MockValidator extends Validator {
|
2020-04-13 19:40:21 -04:00
|
|
|
constructor(private _log: any[] = [], private _validate: Function|null = null) {
|
|
|
|
super();
|
|
|
|
}
|
2015-08-28 14:29:19 -04:00
|
|
|
validate(completeSample: MeasureValues[]): MeasureValues[] {
|
2017-03-02 12:37:01 -05:00
|
|
|
const stableSample = this._validate != null ? this._validate(completeSample) : completeSample;
|
2015-06-17 14:17:21 -04:00
|
|
|
this._log.push(['validate', completeSample, stableSample]);
|
2015-05-27 17:57:54 -04:00
|
|
|
return stableSample;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class MockMetric extends Metric {
|
2020-04-13 19:40:21 -04:00
|
|
|
constructor(private _log: any[] = [], private _endMeasure: Function|null = null) {
|
|
|
|
super();
|
|
|
|
}
|
2015-05-27 17:57:54 -04:00
|
|
|
beginMeasure() {
|
2015-06-17 14:17:21 -04:00
|
|
|
this._log.push(['beginMeasure']);
|
2016-08-02 18:53:34 -04:00
|
|
|
return Promise.resolve(null);
|
2015-05-27 17:57:54 -04:00
|
|
|
}
|
2016-08-26 19:34:08 -04:00
|
|
|
endMeasure(restart: boolean) {
|
2017-03-02 12:37:01 -05:00
|
|
|
const measureValues = this._endMeasure != null ? this._endMeasure() : {};
|
2015-06-17 14:17:21 -04:00
|
|
|
this._log.push(['endMeasure', restart, measureValues]);
|
2016-08-02 18:53:34 -04:00
|
|
|
return Promise.resolve(measureValues);
|
2015-05-27 17:57:54 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class MockReporter extends Reporter {
|
2020-04-13 19:40:21 -04:00
|
|
|
constructor(private _log: any[] = []) {
|
|
|
|
super();
|
|
|
|
}
|
2016-08-26 19:34:08 -04:00
|
|
|
reportMeasureValues(values: MeasureValues): Promise<any> {
|
2015-06-17 14:17:21 -04:00
|
|
|
this._log.push(['reportMeasureValues', values]);
|
2016-08-02 18:53:34 -04:00
|
|
|
return Promise.resolve(null);
|
2015-05-27 17:57:54 -04:00
|
|
|
}
|
2016-08-26 19:34:08 -04:00
|
|
|
reportSample(completeSample: MeasureValues[], validSample: MeasureValues[]): Promise<any> {
|
2015-06-17 14:17:21 -04:00
|
|
|
this._log.push(['reportSample', completeSample, validSample]);
|
2016-08-02 18:53:34 -04:00
|
|
|
return Promise.resolve(null);
|
2015-05-27 17:57:54 -04:00
|
|
|
}
|
|
|
|
}
|