refactor: move angular source to /packages rather than modules/@angular

This commit is contained in:
Jason Aden
2017-03-02 10:48:42 -08:00
parent 5ad5301a3e
commit 3e51a19983
1051 changed files with 18 additions and 18 deletions

View File

@ -0,0 +1,208 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* 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
*/
import {Inject, Injectable} from '@angular/core';
import {Options} from '../common_options';
import {WebDriverAdapter} from '../web_driver_adapter';
import {PerfLogEvent, PerfLogFeatures, WebDriverExtension} from '../web_driver_extension';
/**
* Set the following 'traceCategories' to collect metrics in Chrome:
* 'v8,blink.console,disabled-by-default-devtools.timeline,devtools.timeline,blink.user_timing'
*
* In order to collect the frame rate related metrics, add 'benchmark'
* to the list above.
*/
@Injectable()
export class ChromeDriverExtension extends WebDriverExtension {
static PROVIDERS = [ChromeDriverExtension];
private _majorChromeVersion: number;
constructor(private _driver: WebDriverAdapter, @Inject(Options.USER_AGENT) userAgent: string) {
super();
this._majorChromeVersion = this._parseChromeVersion(userAgent);
}
private _parseChromeVersion(userAgent: string): number {
if (!userAgent) {
return -1;
}
let v = userAgent.split(/Chrom(e|ium)\//g)[2];
if (!v) {
return -1;
}
v = v.split('.')[0];
if (!v) {
return -1;
}
return parseInt(v, 10);
}
gc() { return this._driver.executeScript('window.gc()'); }
timeBegin(name: string): Promise<any> {
return this._driver.executeScript(`console.time('${name}');`);
}
timeEnd(name: string, restartName: string = null): Promise<any> {
let script = `console.timeEnd('${name}');`;
if (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<PerfLogEvent[]> {
// 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) => {
const events: PerfLogEvent[] = [];
entries.forEach((entry: any) => {
const message = JSON.parse(entry['message'])['message'];
if (message['method'] === 'Tracing.dataCollected') {
events.push(message['params']);
}
if (message['method'] === 'Tracing.bufferUsage') {
throw new Error('The DevTools trace buffer filled during the test!');
}
});
return this._convertPerfRecordsToEvents(events);
});
}
private _convertPerfRecordsToEvents(
chromeEvents: Array<{[key: string]: any}>, normalizedEvents: PerfLogEvent[] = null) {
if (!normalizedEvents) {
normalizedEvents = [];
}
chromeEvents.forEach((event) => {
const categories = this._parseCategories(event['cat']);
const normalizedEvent = this._convertEvent(event, categories);
if (normalizedEvent != null) normalizedEvents.push(normalizedEvent);
});
return normalizedEvents;
}
private _convertEvent(event: {[key: string]: any}, categories: string[]) {
const name = event['name'];
const args = event['args'];
if (this._isEvent(categories, name, ['blink.console'])) {
return normalizeEvent(event, {'name': name});
} else if (this._isEvent(
categories, name, ['benchmark'],
'BenchmarkInstrumentation::ImplThreadRenderingStats')) {
// TODO(goderbauer): Instead of BenchmarkInstrumentation::ImplThreadRenderingStats the
// following events should be used (if available) for more accurate measurments:
// 1st choice: vsync_before - ground truth on Android
// 2nd choice: BenchmarkInstrumentation::DisplayRenderingStats - available on systems with
// new surfaces framework (not broadly enabled yet)
// 3rd choice: BenchmarkInstrumentation::ImplThreadRenderingStats - fallback event that is
// always available if something is rendered
const frameCount = event['args']['data']['frame_count'];
if (frameCount > 1) {
throw new Error('multi-frame render stats not supported');
}
if (frameCount == 1) {
return normalizeEvent(event, {'name': 'frame'});
}
} else if (
this._isEvent(categories, name, ['disabled-by-default-devtools.timeline'], 'Rasterize') ||
this._isEvent(
categories, name, ['disabled-by-default-devtools.timeline'], 'CompositeLayers')) {
return normalizeEvent(event, {'name': 'render'});
} else if (this._isEvent(categories, name, ['devtools.timeline', 'v8'], 'MajorGC')) {
const normArgs = {
'majorGc': true,
'usedHeapSize': args['usedHeapSizeAfter'] !== undefined ? args['usedHeapSizeAfter'] :
args['usedHeapSizeBefore']
};
return normalizeEvent(event, {'name': 'gc', 'args': normArgs});
} else if (this._isEvent(categories, name, ['devtools.timeline', 'v8'], 'MinorGC')) {
const normArgs = {
'majorGc': false,
'usedHeapSize': args['usedHeapSizeAfter'] !== undefined ? args['usedHeapSizeAfter'] :
args['usedHeapSizeBefore']
};
return normalizeEvent(event, {'name': 'gc', 'args': normArgs});
} else if (
this._isEvent(categories, name, ['devtools.timeline'], 'FunctionCall') &&
(!args || !args['data'] ||
(args['data']['scriptName'] !== 'InjectedScript' && args['data']['scriptName'] !== ''))) {
return normalizeEvent(event, {'name': 'script'});
} else if (this._isEvent(categories, name, ['devtools.timeline'], 'EvaluateScript')) {
return normalizeEvent(event, {'name': 'script'});
} else if (this._isEvent(
categories, name, ['devtools.timeline', 'blink'], 'UpdateLayoutTree')) {
return normalizeEvent(event, {'name': 'render'});
} else if (
this._isEvent(categories, name, ['devtools.timeline'], 'UpdateLayerTree') ||
this._isEvent(categories, name, ['devtools.timeline'], 'Layout') ||
this._isEvent(categories, name, ['devtools.timeline'], 'Paint')) {
return normalizeEvent(event, {'name': 'render'});
} else if (this._isEvent(categories, name, ['devtools.timeline'], 'ResourceReceivedData')) {
const normArgs = {'encodedDataLength': args['data']['encodedDataLength']};
return normalizeEvent(event, {'name': 'receivedData', 'args': normArgs});
} else if (this._isEvent(categories, name, ['devtools.timeline'], 'ResourceSendRequest')) {
const data = args['data'];
const normArgs = {'url': data['url'], 'method': data['requestMethod']};
return normalizeEvent(event, {'name': 'sendRequest', 'args': normArgs});
} else if (this._isEvent(categories, name, ['blink.user_timing'], 'navigationStart')) {
return normalizeEvent(event, {'name': 'navigationStart'});
}
return null; // nothing useful in this event
}
private _parseCategories(categories: string): string[] { return categories.split(','); }
private _isEvent(
eventCategories: string[], eventName: string, expectedCategories: string[],
expectedName: string = null): boolean {
const hasCategories = expectedCategories.reduce(
(value, cat) => value && eventCategories.indexOf(cat) !== -1, true);
return !expectedName ? hasCategories : hasCategories && eventName === expectedName;
}
perfLogFeatures(): PerfLogFeatures {
return new PerfLogFeatures({render: true, gc: true, frameCapture: true, userTiming: true});
}
supports(capabilities: {[key: string]: any}): boolean {
return this._majorChromeVersion >= 44 && capabilities['browserName'].toLowerCase() === 'chrome';
}
}
function normalizeEvent(chromeEvent: {[key: string]: any}, data: PerfLogEvent): PerfLogEvent {
let ph = chromeEvent['ph'].toUpperCase();
if (ph === 'S') {
ph = 'B';
} else if (ph === 'F') {
ph = 'E';
} else if (ph === 'R') {
// mark events from navigation timing
ph = 'I';
}
const result: {[key: string]: any} =
{'pid': chromeEvent['pid'], 'ph': ph, 'cat': 'timeline', 'ts': chromeEvent['ts'] / 1000};
if (ph === 'X') {
let dur = chromeEvent['dur'];
if (dur === undefined) {
dur = chromeEvent['tdur'];
}
result['dur'] = !dur ? 0.0 : dur / 1000;
}
for (const prop in data) {
result[prop] = data[prop];
}
return result;
}

View File

@ -0,0 +1,52 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* 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
*/
import {Injectable} from '@angular/core';
import {WebDriverAdapter} from '../web_driver_adapter';
import {PerfLogEvent, PerfLogFeatures, WebDriverExtension} from '../web_driver_extension';
@Injectable()
export class FirefoxDriverExtension extends WebDriverExtension {
static PROVIDERS = [FirefoxDriverExtension];
private _profilerStarted: boolean;
constructor(private _driver: WebDriverAdapter) {
super();
this._profilerStarted = false;
}
gc() { return this._driver.executeScript('window.forceGC()'); }
timeBegin(name: string): Promise<any> {
if (!this._profilerStarted) {
this._profilerStarted = true;
this._driver.executeScript('window.startProfiler();');
}
return this._driver.executeScript('window.markStart("' + name + '");');
}
timeEnd(name: string, restartName: string = null): Promise<any> {
let script = 'window.markEnd("' + name + '");';
if (restartName != null) {
script += 'window.markStart("' + restartName + '");';
}
return this._driver.executeScript(script);
}
readPerfLog(): Promise<PerfLogEvent> {
return this._driver.executeAsyncScript('var cb = arguments[0]; window.getProfile(cb);');
}
perfLogFeatures(): PerfLogFeatures { return new PerfLogFeatures({render: true, gc: true}); }
supports(capabilities: {[key: string]: any}): boolean {
return capabilities['browserName'].toLowerCase() === 'firefox';
}
}

View File

@ -0,0 +1,126 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* 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
*/
import {Injectable} from '@angular/core';
import {WebDriverAdapter} from '../web_driver_adapter';
import {PerfLogEvent, PerfLogFeatures, WebDriverExtension} from '../web_driver_extension';
@Injectable()
export class IOsDriverExtension extends WebDriverExtension {
static PROVIDERS = [IOsDriverExtension];
constructor(private _driver: WebDriverAdapter) { super(); }
gc(): Promise<any> { throw new Error('Force GC is not supported on iOS'); }
timeBegin(name: string): Promise<any> {
return this._driver.executeScript(`console.time('${name}');`);
}
timeEnd(name: string, restartName: string = null): Promise<any> {
let script = `console.timeEnd('${name}');`;
if (restartName != null) {
script += `console.time('${restartName}');`;
}
return this._driver.executeScript(script);
}
// See https://github.com/WebKit/webkit/tree/master/Source/WebInspectorUI/Versions
readPerfLog() {
// 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) => {
const records: any[] = [];
entries.forEach((entry: any) => {
const message = JSON.parse(entry['message'])['message'];
if (message['method'] === 'Timeline.eventRecorded') {
records.push(message['params']['record']);
}
});
return this._convertPerfRecordsToEvents(records);
});
}
/** @internal */
private _convertPerfRecordsToEvents(records: any[], events: PerfLogEvent[] = null) {
if (!events) {
events = [];
}
records.forEach((record) => {
let endEvent: PerfLogEvent = null;
const type = record['type'];
const data = record['data'];
const startTime = record['startTime'];
const endTime = record['endTime'];
if (type === 'FunctionCall' && (data == null || data['scriptName'] !== 'InjectedScript')) {
events.push(createStartEvent('script', startTime));
endEvent = createEndEvent('script', endTime);
} else if (type === 'Time') {
events.push(createMarkStartEvent(data['message'], startTime));
} else if (type === 'TimeEnd') {
events.push(createMarkEndEvent(data['message'], startTime));
} else if (
type === 'RecalculateStyles' || type === 'Layout' || type === 'UpdateLayerTree' ||
type === 'Paint' || type === 'Rasterize' || type === 'CompositeLayers') {
events.push(createStartEvent('render', startTime));
endEvent = createEndEvent('render', endTime);
}
// Note: ios used to support GCEvent up until iOS 6 :-(
if (record['children'] != null) {
this._convertPerfRecordsToEvents(record['children'], events);
}
if (endEvent != null) {
events.push(endEvent);
}
});
return events;
}
perfLogFeatures(): PerfLogFeatures { return new PerfLogFeatures({render: true}); }
supports(capabilities: {[key: string]: any}): boolean {
return capabilities['browserName'].toLowerCase() === 'safari';
}
}
function createEvent(
ph: 'X' | 'B' | 'E' | 'B' | 'E', name: string, time: number, args: any = null) {
const result: PerfLogEvent = {
'cat': 'timeline',
'name': name,
'ts': time,
'ph': ph,
// The ios protocol does not support the notions of multiple processes in
// the perflog...
'pid': 'pid0'
};
if (args != null) {
result['args'] = args;
}
return result;
}
function createStartEvent(name: string, time: number, args: any = null) {
return createEvent('B', name, time, args);
}
function createEndEvent(name: string, time: number, args: any = null) {
return createEvent('E', name, time, args);
}
function createMarkStartEvent(name: string, time: number) {
return createEvent('B', name, time);
}
function createMarkEndEvent(name: string, time: number) {
return createEvent('E', name, time);
}

View File

@ -0,0 +1,70 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* 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
*/
import {WebDriverAdapter} from '../web_driver_adapter';
/**
* Adapter for the selenium-webdriver.
*/
export class SeleniumWebDriverAdapter extends WebDriverAdapter {
static PROTRACTOR_PROVIDERS = [{
provide: WebDriverAdapter,
useFactory: () => new SeleniumWebDriverAdapter((<any>global).browser)
}];
constructor(private _driver: any) { super(); }
waitFor(callback: () => any): Promise<any> { return this._driver.call(callback); }
executeScript(script: string): Promise<any> { return this._driver.executeScript(script); }
executeAsyncScript(script: string): Promise<any> {
return this._driver.executeAsyncScript(script);
}
capabilities(): Promise<{[key: string]: any}> {
return this._driver.getCapabilities().then((capsObject: any) => {
const localData: {[key: string]: any} = {};
capsObject.forEach((value: any, key: string) => { localData[key] = value; });
return localData;
});
}
logs(type: string): Promise<any> {
// Needed as selenium-webdriver does not forward
// performance logs in the correct way via manage().logs
return this._driver.schedule(
new Command('getLog').setParameter('type', type),
'WebDriver.manage().logs().get(' + type + ')');
}
}
/**
* Copy of the `Command` class of webdriver as
* it is not exposed via index.js in selenium-webdriver.
*/
class Command {
private parameters_: {[key: string]: any} = {};
constructor(private name_: string) {}
getName() { return this.name_; }
setParameter(name: string, value: any) {
this.parameters_[name] = value;
return this;
}
setParameters(parameters: {[key: string]: any}) {
this.parameters_ = parameters;
return this;
}
getParameter(key: string) { return this.parameters_[key]; }
getParameters() { return this.parameters_; }
}