feat(perf): port table scrolling benchmark to Angular 2

This commit is contained in:
Yegor Jbanov 2015-02-05 18:33:57 -08:00
parent 93c18f5396
commit fcbdf02767
21 changed files with 1119 additions and 11 deletions

View File

@ -360,7 +360,7 @@ function _operationToFunction(operation:string):Function {
}
function s(v) {
return isPresent(v) ? '' + v : '';
return isPresent(v) ? `${v}` : '';
}
function _interpolationFn(strings:List) {

View File

@ -411,7 +411,8 @@ export class ProtoView {
// view might get dehydrated, in between the event queuing up and
// firing.
if (view.hydrated()) {
MapWrapper.set(locals, '\$event', event);
// HACK
MapWrapper.set(locals, `$event`, event);
var context = new ContextWithVariableBindings(view.context, locals);
expr.eval(context);
}

View File

@ -71,7 +71,7 @@ class StringMapWrapper {
}
class ListWrapper {
static List clone(List l) => new List.from(l);
static List clone(Iterable l) => new List.from(l);
static List create() => new List();
static List createFixedSize(int size) => new List(size);
static get(List m, int k) => m[k];
@ -137,6 +137,9 @@ class ListWrapper {
}
return true;
}
static List slice(List l, int from, int to) {
return l.sublist(from, to);
}
}
bool isListLikeIterable(obj) => obj is Iterable;

View File

@ -176,6 +176,9 @@ export class ListWrapper {
}
return true;
}
static slice(l:List, from:int, to:int):List {
return l.slice(from, to);
}
}
export function isListLikeIterable(obj):boolean {

View File

@ -6,4 +6,8 @@ class Math {
static num pow(num x, num exponent) {
return math.pow(x, exponent);
}
static num min(num a, num b) => math.min(a, b);
static num floor(num a) => a.floor();
}

View File

@ -0,0 +1,37 @@
var perfUtil = require('../../angular2/e2e_test/perf_util');
describe('ng2 naive infinite scroll benchmark', function () {
var URL = 'benchmarks/src/naive_infinite_scroll/index.html';
afterEach(perfUtil.verifyNoBrowserErrors);
[1, 2, 4].forEach(function(appSize) {
it('should run scroll benchmark and collect stats for appSize = ' +
appSize, function() {
perfUtil.runBenchmark({
url: URL,
id: 'ng2.naive_infinite_scroll',
work: function() {
browser.executeScript(
'document.querySelector("scroll-app /deep/ #reset-btn").click()');
browser.executeScript(
'document.querySelector("scroll-app /deep/ #run-btn").click()');
browser.wait(() => {
return $('#done').getText().then(
function() { return true; },
function() { return false; });
}, 10000);
},
params: [{
name: 'appSize', value: appSize
}, {
name: 'iterationCount', value: 20, scale: 'linear'
}, {
name: 'scrollIncrement', value: 40
}]
});
});
});
});

View File

@ -0,0 +1,22 @@
var testUtil = require('../../angular2/e2e_test/test_util');
describe('ng2 naive infinite scroll benchmark', function () {
var URL = 'benchmarks/src/naive_infinite_scroll/index.html';
afterEach(testUtil.verifyNoBrowserErrors);
it('should not throw errors', function() {
browser.get(URL);
browser.executeScript(
'document.querySelector("scroll-app /deep/ #reset-btn").click()');
browser.executeScript(
'document.querySelector("scroll-app /deep/ #run-btn").click()');
browser.wait(() => {
return $('#done').getText().then(
function() { return true; },
function() { return false; });
}, 10000);
});
});

View File

@ -20,6 +20,9 @@
<li>
<a href="tree/tree_benchmark.html">Tree benchmark</a>
</li>
<li>
<a href="naive_infinite_scroll/index.html">Naive infinite scroll benchmark</a>
</li>
</ul>
</body>
</html>

View File

@ -0,0 +1,108 @@
import {int, isPresent} from 'angular2/src/facade/lang';
import {reflector} from 'angular2/src/reflection/reflection';
import {getIntParameter, bindAction} from 'angular2/src/test_lib/benchmark_util';
import {bootstrap, Component, Template, TemplateConfig, ViewPort, Compiler}
from 'angular2/angular2';
import {PromiseWrapper} from 'angular2/src/facade/async';
import {ListWrapper} from 'angular2/src/facade/collection';
import {ScrollAreaComponent} from './scroll_area';
import {NgIf, NgRepeat} from 'angular2/directives';
import {DOM, document, Element} from 'angular2/src/facade/dom';
export class App {
scrollAreas:List<int>;
iterationCount:int;
scrollIncrement:int;
constructor() {
var appSize = getIntParameter('appSize');
this.iterationCount = getIntParameter('iterationCount');
this.scrollIncrement = getIntParameter('scrollIncrement');
appSize = appSize > 1 ? appSize - 1 : 0; // draw at least one table
this.scrollAreas = [];
for (var i = 0; i < appSize; i++) {
ListWrapper.push(this.scrollAreas, i);
}
// TODO(tbosch): change to bindAction when it works in pub serve
DOM.on(DOM.query('scroll-app /deep/ #run-btn'), 'click', (_) => {
this.runBenchmark();
});
DOM.on(DOM.query('scroll-app /deep/ #reset-btn'), 'click', (_) => {
this._getScrollDiv().scrollTop = 0;
var existingMarker = this._locateFinishedMarker();
if (isPresent(existingMarker)) {
DOM.removeChild(document.body, existingMarker);
}
});
}
runBenchmark() {
var scrollDiv = this._getScrollDiv();
var n:int = this.iterationCount;
var scheduleScroll;
scheduleScroll = () => {
PromiseWrapper.setTimeout(() => {
scrollDiv.scrollTop += this.scrollIncrement;
n--;
if (n > 0) {
scheduleScroll();
} else {
this._scheduleFinishedMarker();
}
}, 0);
}
scheduleScroll();
}
// Puts a marker indicating that the test is finished.
_scheduleFinishedMarker() {
var existingMarker = this._locateFinishedMarker();
if (isPresent(existingMarker)) {
// Nothing to do, the marker is already there
return;
}
PromiseWrapper.setTimeout(() => {
var finishedDiv = DOM.createElement('div');
finishedDiv.id = 'done';
DOM.setInnerHTML(finishedDiv, 'Finished');
DOM.appendChild(document.body, finishedDiv);
}, 0);
}
_locateFinishedMarker():Element {
return DOM.querySelector(document.body, '#done');
}
_getScrollDiv() {
return DOM.query('body /deep/ #testArea /deep/ #scrollDiv');
}
}
export function setupReflectorForApp() {
reflector.registerType(App, {
'factory': () => { return new App(); },
'parameters': [],
'annotations': [
new Component({
selector: 'scroll-app',
template: new TemplateConfig({
directives: [ScrollAreaComponent, NgIf, NgRepeat],
inline: `
<div>
<div style="display: flex">
<scroll-area id="testArea"></scroll-area>
<div style="padding-left: 20px">
<button id="run-btn">Run</button>
<button id="reset-btn">Reset</button>
</div>
</div>
<div template="ng-if scrollAreas.length > 0">
<p>Following tables are only here to add weight to the UI:</p>
<scroll-area template="ng-repeat #scrollArea in scrollAreas"></scroll-area>
</div>
</div>`
})
})
]
});
}

View File

@ -0,0 +1,220 @@
import {int} from 'angular2/src/facade/lang';
import {reflector} from 'angular2/src/reflection/reflection';
import {getIntParameter, bindAction} from 'angular2/src/test_lib/benchmark_util';
import {bootstrap, Component, Template, TemplateConfig, ViewPort, Compiler}
from 'angular2/angular2';
import {PromiseWrapper} from 'angular2/src/facade/async';
import {ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
import {Company, Opportunity, Offering, Account, CustomDate, STATUS_LIST}
from './common';
import {NgRepeat} from 'angular2/directives';
export class HasStyle {
style:Map;
constructor() {
this.style = MapWrapper.create();
}
set width(w) {
MapWrapper.set(this.style, 'width', w);
}
}
export class CompanyNameComponent extends HasStyle {
company:Company;
}
export class OpportunityNameComponent extends HasStyle {
opportunity:Opportunity;
}
export class OfferingNameComponent extends HasStyle {
offering:Offering;
}
export class Stage {
name:string;
isDisabled:boolean;
style:Map;
apply:Function;
}
export class StageButtonsComponent extends HasStyle {
_offering:Offering;
stages:List<Stage>;
get offering():Offering { return this._offering; }
set offering(offering:Offering) {
this._offering = offering;
this._computeStageButtons();
}
setStage(stage:Stage) {
this._offering.status = stage.name;
this._computeStageButtons();
}
_computeStageButtons() {
var disabled = true;
this.stages = ListWrapper.clone(STATUS_LIST
.map((status) => {
var isCurrent = this._offering.status == status;
var stage = new Stage();
stage.name = status;
stage.isDisabled = disabled;
var stageStyle = MapWrapper.create();
MapWrapper.set(stageStyle, 'background-color',
disabled
? '#DDD'
: isCurrent
? '#DDF'
: '#FDD');
stage.style = stageStyle;
if (isCurrent) {
disabled = false;
}
return stage;
}));
}
}
export class AccountCellComponent extends HasStyle {
account:Account;
}
export class FormattedCellComponent extends HasStyle {
formattedValue:string;
set value(value) {
if (value instanceof CustomDate) {
this.formattedValue = `${value.month}/${value.day}/${value.year}`;
} else {
this.formattedValue = value.toString();
}
}
}
export function setupReflectorForCells() {
reflector.registerType(CompanyNameComponent, {
'factory': () => new CompanyNameComponent(),
'parameters': [],
'annotations': [
new Component({
selector: 'company-name',
template: new TemplateConfig({
directives: [],
inline: `<div [style]="style">{{company.name}}</div>`
}),
bind: {
'cell-width': 'width',
'company': 'company'
}
})
]
});
reflector.registerType(OpportunityNameComponent, {
'factory': () => new OpportunityNameComponent(),
'parameters': [],
'annotations': [
new Component({
selector: 'opportunity-name',
template: new TemplateConfig({
directives: [],
inline: `<div [style]="style">{{opportunity.name}}</div>`
}),
bind: {
'cell-width': 'width',
'opportunity': 'opportunity'
}
})
]
});
reflector.registerType(OfferingNameComponent, {
'factory': () => new OfferingNameComponent(),
'parameters': [],
'annotations': [
new Component({
selector: 'offering-name',
template: new TemplateConfig({
directives: [],
inline: `<div [style]="style">{{offering.name}}</div>`
}),
bind: {
'cell-width': 'width',
'offering': 'offering'
}
})
]
});
reflector.registerType(StageButtonsComponent, {
'factory': () => new StageButtonsComponent(),
'parameters': [],
'annotations': [
new Component({
selector: 'stage-buttons',
template: new TemplateConfig({
directives: [NgRepeat],
inline: `
<div [style]="style">
<button template="ng-repeat #stage in stages"
[disabled]="stage.isDisabled"
[style]="stage.style"
on-click="setStage(stage)">
{{stage.name}}
</button>
</div>`
}),
bind: {
'cell-width': 'width',
'offering': 'offering'
}
})
]
});
reflector.registerType(AccountCellComponent, {
'factory': () => new AccountCellComponent(),
'parameters': [],
'annotations': [
new Component({
selector: 'account-cell',
template: new TemplateConfig({
directives: [],
inline: `
<div [style]="style">
<a href="/account/{{account.accountId}}">
{{account.accountId}}
</a>
</div>`
}),
bind: {
'cell-width': 'width',
'account': 'account'
}
})
]
});
reflector.registerType(FormattedCellComponent, {
'factory': () => new FormattedCellComponent(),
'parameters': [],
'annotations': [
new Component({
selector: 'formatted-cell',
template: new TemplateConfig({
directives: [],
inline: `<div [style]="style">{{formattedValue}}</div>`
}),
bind: {
'cell-width': 'width',
'value': 'value'
}
})
]
});
}

View File

@ -0,0 +1,205 @@
import {int} from 'angular2/src/facade/lang';
import {Math} from 'angular2/src/facade/math';
import {PromiseWrapper} from 'angular2/src/facade/async';
import {ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
export var ITEMS = 1000;
export var ITEM_HEIGHT = 40;
export var VISIBLE_ITEMS = 17;
export var HEIGHT = ITEMS * ITEM_HEIGHT;
export var VIEW_PORT_HEIGHT = ITEM_HEIGHT * VISIBLE_ITEMS;
export var COMPANY_NAME_WIDTH = 100;
export var OPPORTUNITY_NAME_WIDTH = 100;
export var OFFERING_NAME_WIDTH = 100;
export var ACCOUNT_CELL_WIDTH = 50;
export var BASE_POINTS_WIDTH = 50;
export var KICKER_POINTS_WIDTH = 50;
export var STAGE_BUTTONS_WIDTH = 220;
export var BUNDLES_WIDTH = 120;
export var DUE_DATE_WIDTH = 100;
export var END_DATE_WIDTH = 100;
export var AAT_STATUS_WIDTH = 100;
export var ROW_WIDTH = COMPANY_NAME_WIDTH +
OPPORTUNITY_NAME_WIDTH +
OFFERING_NAME_WIDTH +
ACCOUNT_CELL_WIDTH +
BASE_POINTS_WIDTH +
KICKER_POINTS_WIDTH +
STAGE_BUTTONS_WIDTH +
BUNDLES_WIDTH +
DUE_DATE_WIDTH +
END_DATE_WIDTH +
AAT_STATUS_WIDTH;
export var STATUS_LIST = [
'Planned', 'Pitched', 'Won', 'Lost'
];
export var AAT_STATUS_LIST = [
'Active', 'Passive', 'Abandoned'
];
// Imitate Streamy entities.
// Just a non-trivial object. Nothing fancy or correct.
export class CustomDate {
year: int;
month: int;
day: int;
constructor(y:int, m:int, d:int) {
this.year = y;
this.month = m;
this.day = d;
}
addDays(days:int):CustomDate {
var newDay = this.day + days;
var newMonth = this.month + Math.floor(newDay / 30);
newDay = newDay % 30;
var newYear = this.year + Math.floor(newMonth / 12);
return new CustomDate(newYear, newMonth, newDay);
}
static now():CustomDate {
return new CustomDate(2014, 1, 28);
}
}
export class RawEntity {
_data:Map;
constructor() {
this._data = MapWrapper.create();
}
get(key:string) {
if (key.indexOf('.') == -1) {
return this._data[key];
}
var pieces = key.split('.');
var last = ListWrapper.last(pieces);
pieces.length = pieces.length - 1;
var target = _resolve(pieces, this);
if (target == null) {
return null;
}
return target[last];
}
set(key:string, value) {
if (key.indexOf('.') == -1) {
this._data[key] = value;
return;
}
var pieces = key.split('.');
var last = ListWrapper.last(pieces);
pieces.length = pieces.length - 1;
var target = _resolve(pieces, this);
target[last] = value;
}
remove(key:string) {
if (!key.contains('.')) {
return this._data.remove(key);
}
var pieces = key.split('.');
var last = ListWrapper.last(pieces);
pieces.length = pieces.length - 1;
var target = _resolve(pieces, this);
return target.remove(last);
}
_resolve(pieces, start) {
var cur = start;
for (var i = 0; i < pieces.length; i++) {
cur = cur[pieces[i]];
if (cur == null) {
return null;
}
}
return cur;
}
}
export class Company extends RawEntity {
get name():string { return this.get('name'); }
set name(val:string) {
this.set('name', val);
}
}
export class Offering extends RawEntity {
get name():string { return this.get('name'); }
set name(val:string) {
this.set('name', val);
}
get company():Company { return this.get('company'); }
set company(val:Company) {
this.set('company', val);
}
get opportunity():Opportunity { return this.get('opportunity'); }
set opportunity(val:Opportunity) {
this.set('opportunity', val);
}
get account():Account { return this.get('account'); }
set account(val:Account) {
this.set('account', val);
}
get basePoints():int { return this.get('basePoints'); }
set basePoints(val:int) {
this.set('basePoints', val);
}
get kickerPoints():int { return this.get('kickerPoints'); }
set kickerPoints(val:int) {
this.set('kickerPoints', val);
}
get status():string { return this.get('status'); }
set status(val:string) {
this.set('status', val);
}
get bundles():string { return this.get('bundles'); }
set bundles(val:string) {
this.set('bundles', val);
}
get dueDate():CustomDate { return this.get('dueDate'); }
set dueDate(val:CustomDate) {
this.set('dueDate', val);
}
get endDate():CustomDate { return this.get('endDate'); }
set endDate(val:CustomDate) {
this.set('endDate', val);
}
get aatStatus():string { return this.get('aatStatus'); }
set aatStatus(val:string) {
this.set('aatStatus', val);
}
}
export class Opportunity extends RawEntity {
get name():string { return this.get('name'); }
set name(val:string) {
this.set('name', val);
}
}
export class Account extends RawEntity {
get accountId():int { return this.get('accountId'); }
set accountId(val:int) {
this.set('accountId', val);
}
}

View File

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<title>AngularDart Scrolling Benchmark</title>
</head>
<body>
<form>
App size: <input type="text" name="appSize" value="1"></input><br>
Iteration count: <input type="text" name="iterationCount" value="1"></input><br>
Scroll increment: <input type="text" name="scrollIncrement" value="1"></input><br>
</form>
<scroll-app></scroll-app>
$SCRIPTS$
</body>
</html>

View File

@ -0,0 +1,212 @@
import {int, isBlank} from 'angular2/src/facade/lang';
import {Element} from 'angular2/src/facade/dom';
import {MapWrapper} from 'angular2/src/facade/collection';
import {Parser, Lexer, ChangeDetector, ChangeDetection}
from 'angular2/change_detection';
import {bootstrap, Component, Template, TemplateConfig, ViewPort, Compiler}
from 'angular2/angular2';
import {reflector} from 'angular2/src/reflection/reflection';
import {CompilerCache} from 'angular2/src/core/compiler/compiler';
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
import {TemplateLoader} from 'angular2/src/core/compiler/template_loader';
import {LifeCycle} from 'angular2/src/core/life_cycle/life_cycle';
import {NgIf, NgRepeat} from 'angular2/directives';
import {App, setupReflectorForApp} from './app';
import {ScrollAreaComponent, setupReflectorForScrollArea} from './scroll_area';
import {ScrollItemComponent, setupReflectorForScrollItem} from './scroll_item';
import {CompanyNameComponent, OpportunityNameComponent, OfferingNameComponent,
AccountCellComponent, StageButtonsComponent, FormattedCellComponent,
setupReflectorForCells}
from './cells';
export function main() {
setupReflector();
bootstrap(App);
}
export function setupReflector() {
setupReflectorForAngular();
setupReflectorForApp();
setupReflectorForScrollArea();
setupReflectorForScrollItem();
setupReflectorForCells();
// TODO: the transpiler is not able to compiles templates used as keys
var evt = `$event`;
reflector.registerGetters({
'scrollAreas': (o) => o.scrollAreas,
'length': (o) => o.length,
'iterable': (o) => o.iterable,
'scrollArea': (o) => o.scrollArea,
'item': (o) => o.item,
'visibleItems': (o) => o.visibleItems,
'condition': (o) => o.condition,
'width': (o) => o.width,
'value': (o) => o.value,
'href': (o) => o.href,
'company': (o) => o.company,
'formattedValue': (o) => o.formattedValue,
'name': (o) => o.name,
'style': (o) => o.style,
'offering': (o) => o.offering,
'account': (o) => o.account,
'accountId': (o) => o.accountId,
'companyNameWidth': (o) => o.companyNameWidth,
'opportunityNameWidth': (o) => o.opportunityNameWidth,
'offeringNameWidth': (o) => o.offeringNameWidth,
'accountCellWidth': (o) => o.accountCellWidth,
'basePointsWidth': (o) => o.basePointsWidth,
'scrollDivStyle': (o) => o.scrollDivStyle,
'paddingStyle': (o) => o.paddingStyle,
'innerStyle': (o) => o.innerStyle,
'opportunity': (o) => o.opportunity,
'itemStyle': (o) => o.itemStyle,
'dueDateWidth': (o) => o.dueDateWidth,
'basePoints': (o) => o.basePoints,
'kickerPoints': (o) => o.kickerPoints,
'kickerPointsWidth': (o) => o.kickerPointsWidth,
'bundles': (o) => o.bundles,
'stageButtonsWidth': (o) => o.stageButtonsWidth,
'bundlesWidth': (o) => o.bundlesWidth,
'disabled': (o) => o.disabled,
'isDisabled': (o) => o.isDisabled,
'dueDate': (o) => o.dueDate,
'endDate': (o) => o.endDate,
'aatStatus': (o) => o.aatStatus,
'stage': (o) => o.stage,
'stages': (o) => o.stages,
'aatStatusWidth': (o) => o.aatStatusWidth,
'endDateWidth': (o) => o.endDateWidth,
evt: (o) => null
});
reflector.registerSetters({
'scrollAreas': (o, v) => o.scrollAreas = v,
'length': (o, v) => o.length = v,
'condition': (o, v) => o.condition = v,
'scrollArea': (o, v) => o.scrollArea = v,
'item': (o, v) => o.item = v,
'visibleItems': (o, v) => o.visibleItems = v,
'iterable': (o, v) => o.iterable = v,
'width': (o, v) => o.width = v,
'value': (o, v) => o.value = v,
'company': (o, v) => o.company = v,
'name': (o, v) => o.name = v,
'offering': (o, v) => o.offering = v,
'account': (o, v) => o.account = v,
'accountId': (o, v) => o.accountId = v,
'formattedValue': (o, v) => o.formattedValue = v,
'stage': (o, v) => o.stage = v,
'stages': (o, v) => o.stages = v,
'disabled': (o, v) => o.disabled = v,
'isDisabled': (o, v) => o.isDisabled = v,
'href': (o, v) => o.href = v,
'companyNameWidth': (o, v) => o.companyNameWidth = v,
'opportunityNameWidth': (o, v) => o.opportunityNameWidth = v,
'offeringNameWidth': (o, v) => o.offeringNameWidth = v,
'accountCellWidth': (o, v) => o.accountCellWidth = v,
'basePointsWidth': (o, v) => o.basePointsWidth = v,
'scrollDivStyle': (o, v) => o.scrollDivStyle = v,
'paddingStyle': (o, v) => o.paddingStyle = v,
'innerStyle': (o, v) => o.innerStyle = v,
'opportunity': (o, v) => o.opportunity = v,
'itemStyle': (o, v) => o.itemStyle = v,
'basePoints': (o, v) => o.basePoints = v,
'kickerPoints': (o, v) => o.kickerPoints = v,
'kickerPointsWidth': (o, v) => o.kickerPointsWidth = v,
'stageButtonsWidth': (o, v) => o.stageButtonsWidth = v,
'dueDate': (o, v) => o.dueDate = v,
'dueDateWidth': (o, v) => o.dueDateWidth = v,
'endDate': (o, v) => o.endDate = v,
'endDateWidth': (o, v) => o.endDate = v,
'aatStatus': (o, v) => o.aatStatus = v,
'aatStatusWidth': (o, v) => o.aatStatusWidth = v,
'bundles': (o, v) => o.bundles = v,
'bundlesWidth': (o, v) => o.bundlesWidth = v,
evt: (o, v) => null,
'style': (o, m) => {
//if (isBlank(m)) return;
// HACK
MapWrapper.forEach(m, function(v, k) {
o.style.setProperty(k, v);
});
}
});
reflector.registerMethods({
'onScroll': (o, args) => {
// HACK
o.onScroll(args[0]);
},
'setStage': (o, args) => o.setStage(args[0])
});
}
export function setupReflectorForAngular() {
reflector.registerType(NgIf, {
'factory': (vp) => new NgIf(vp),
'parameters': [[ViewPort]],
'annotations' : [new Template({
selector: '[ng-if]',
bind: {
'ng-if': 'condition'
}
})]
});
reflector.registerType(NgRepeat, {
'factory': (vp) => new NgRepeat(vp),
'parameters': [[ViewPort]],
'annotations' : [new Template({
selector: '[ng-repeat]',
bind: {
'in': 'iterable[]'
}
})]
});
reflector.registerType(Compiler, {
'factory': (changeDetection, templateLoader, reader, parser, compilerCache) => new Compiler(changeDetection, templateLoader, reader, parser, compilerCache),
'parameters': [[ChangeDetection], [TemplateLoader], [DirectiveMetadataReader], [Parser], [CompilerCache]],
'annotations': []
});
reflector.registerType(CompilerCache, {
'factory': () => new CompilerCache(),
'parameters': [],
'annotations': []
});
reflector.registerType(Parser, {
'factory': (lexer) => new Parser(lexer),
'parameters': [[Lexer]],
'annotations': []
});
reflector.registerType(TemplateLoader, {
'factory': () => new TemplateLoader(),
'parameters': [],
'annotations': []
});
reflector.registerType(DirectiveMetadataReader, {
'factory': () => new DirectiveMetadataReader(),
'parameters': [],
'annotations': []
});
reflector.registerType(Lexer, {
'factory': () => new Lexer(),
'parameters': [],
'annotations': []
});
reflector.registerType(LifeCycle, {
"factory": (cd) => new LifeCycle(cd),
"parameters": [[ChangeDetector]],
"annotations": []
});
}

View File

@ -0,0 +1,77 @@
import {int, StringWrapper} from 'angular2/src/facade/lang';
import {List, ListWrapper} from 'angular2/src/facade/collection';
import {CustomDate, Offering, Company, Opportunity, Account, STATUS_LIST,
AAT_STATUS_LIST} from './common';
export function generateOfferings(count:int):List<Offering> {
var res = [];
for(var i = 0; i < count; i++) {
ListWrapper.push(res, generateOffering(i));
}
return res;
}
export function generateOffering(seed:int):Offering {
var res = new Offering();
res.name = generateName(seed++);
res.company = generateCompany(seed++);
res.opportunity = generateOpportunity(seed++);
res.account = generateAccount(seed++);
res.basePoints = seed % 10;
res.kickerPoints = seed % 4;
res.status = STATUS_LIST[seed % STATUS_LIST.length];
res.bundles = randomString(seed++);
res.dueDate = randomDate(seed++);
res.endDate = randomDate(seed++, res.dueDate);
res.aatStatus = AAT_STATUS_LIST[seed % AAT_STATUS_LIST.length];
return res;
}
export function generateCompany(seed:int):Company {
var res = new Company();
res.name = generateName(seed);
return res;
}
export function generateOpportunity(seed:int):Opportunity {
var res = new Opportunity();
res.name = generateName(seed);
return res;
}
export function generateAccount(seed:int):Account {
var res = new Account();
res.accountId = seed;
return res;
}
var names = [
'Foo', 'Bar', 'Baz', 'Qux', 'Quux', 'Garply', 'Waldo', 'Fred', 'Plugh',
'Xyzzy', 'Thud', 'Cruft', 'Stuff'
];
function generateName(seed:int):string {
return names[seed % names.length];
}
var offsets = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
function randomDate(seed:int, minDate:CustomDate = null):CustomDate {
if (minDate == null) {
minDate = CustomDate.now();
}
return minDate.addDays(offsets[seed % offsets.length]);
}
var stringLengths = [5, 7, 9, 11, 13];
var charCodeOffsets = [0, 1, 2, 3, 4, 5, 6, 7, 8];
function randomString(seed:int):string {
var len = stringLengths[seed % 5];
var str = '';
for (var i = 0; i < len; i++) {
str += StringWrapper.fromCharCode(97 + charCodeOffsets[seed % 9] + i);
}
return str;
}

View File

@ -0,0 +1,90 @@
import {int, FINAL} from 'angular2/src/facade/lang';
import {reflector} from 'angular2/src/reflection/reflection';
import {getIntParameter, bindAction} from 'angular2/src/test_lib/benchmark_util';
import {Component, Template, TemplateConfig, ViewPort, Compiler}
from 'angular2/angular2';
import {PromiseWrapper} from 'angular2/src/facade/async';
import {ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
import {Element} from 'angular2/src/facade/dom';
import {Math} from 'angular2/src/facade/math';
import {Offering, ITEMS, ITEM_HEIGHT, VISIBLE_ITEMS, VIEW_PORT_HEIGHT,
ROW_WIDTH, HEIGHT} from './common';
import {generateOfferings} from './random_data';
import {ScrollItemComponent} from './scroll_item';
import {NgRepeat} from 'angular2/directives';
export class ScrollAreaComponent {
_fullList:List<Offering>;
visibleItems:List<Offering>;
scrollDivStyle;
paddingDiv;
innerDiv;
constructor() {
this._fullList = generateOfferings(ITEMS);
this.visibleItems = [];
this.scrollDivStyle = MapWrapper.createFromPairs([
['height', `${VIEW_PORT_HEIGHT}px`],
['width', '1000px'],
['border', '1px solid #000'],
['overflow', 'scroll']
]);
this.onScroll(null);
}
onScroll(evt) {
var scrollTop = 0;
if (evt != null) {
var scrollDiv = evt.target;
if (this.paddingDiv == null) {
this.paddingDiv = scrollDiv.querySelector('#padding');
}
if (this.innerDiv == null) {
this.innerDiv = scrollDiv.querySelector('#inner');
this.innerDiv.style.setProperty('width', `${ROW_WIDTH}px`);
}
scrollTop = scrollDiv.scrollTop;
}
var iStart = Math.floor(scrollTop / ITEM_HEIGHT);
var iEnd = Math.min(iStart + VISIBLE_ITEMS + 1, this._fullList.length);
var padding = iStart * ITEM_HEIGHT;
if (this.innerDiv != null) {
this.innerDiv.style.setProperty('height', `${HEIGHT - padding}px`);
}
if (this.paddingDiv != null) {
this.paddingDiv.style.setProperty('height', `${padding}px`);
}
this.visibleItems = ListWrapper.slice(this._fullList, iStart, iEnd);
}
}
export function setupReflectorForScrollArea() {
reflector.registerType(ScrollAreaComponent, {
'factory': () => new ScrollAreaComponent(),
'parameters': [],
'annotations': [
new Component({
selector: 'scroll-area',
template: new TemplateConfig({
directives: [ScrollItemComponent, NgRepeat],
inline: `
<div>
<div id="scrollDiv"
[style]="scrollDivStyle"
on-scroll="onScroll($event)">
<div id="padding"></div>
<div id="inner">
<scroll-item
template="ng-repeat #item in visibleItems"
[offering]="item">
</scroll-item>
</div>
</div>
</div>`
})
})
]
});
}

View File

@ -0,0 +1,106 @@
import {int} from 'angular2/src/facade/lang';
import {reflector} from 'angular2/src/reflection/reflection';
import {Component, Template, TemplateConfig, ViewPort, Compiler}
from 'angular2/angular2';
import {PromiseWrapper} from 'angular2/src/facade/async';
import {ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
import {Element} from 'angular2/src/facade/dom';
import {Math} from 'angular2/src/facade/math';
import {CompanyNameComponent, OpportunityNameComponent,
OfferingNameComponent, StageButtonsComponent, AccountCellComponent,
FormattedCellComponent} from './cells';
import {Offering, ITEM_HEIGHT, COMPANY_NAME_WIDTH, OPPORTUNITY_NAME_WIDTH,
OFFERING_NAME_WIDTH, ACCOUNT_CELL_WIDTH, BASE_POINTS_WIDTH,
KICKER_POINTS_WIDTH, STAGE_BUTTONS_WIDTH, BUNDLES_WIDTH, DUE_DATE_WIDTH,
END_DATE_WIDTH, AAT_STATUS_WIDTH} from './common';
import {generateOfferings} from './random_data';
export class ScrollItemComponent {
offering:Offering;
itemStyle;
constructor() {
this.itemStyle = MapWrapper.createFromPairs([
['height', `${ITEM_HEIGHT}px`],
['line-height', `${ITEM_HEIGHT}px`],
['font-size', '18px'],
['display', 'flex'],
['justify-content', 'space-between']
]);
}
get companyNameWidth() { return `${COMPANY_NAME_WIDTH}px`; }
get opportunityNameWidth() { return `${OPPORTUNITY_NAME_WIDTH}px`; }
get offeringNameWidth() { return `${OFFERING_NAME_WIDTH}px`; }
get accountCellWidth() { return `${ACCOUNT_CELL_WIDTH}px`; }
get basePointsWidth() { return `${BASE_POINTS_WIDTH}px`; }
get kickerPointsWidth() { return `${KICKER_POINTS_WIDTH}px`; }
get stageButtonsWidth() { return `${STAGE_BUTTONS_WIDTH}px`; }
get bundlesWidth() { return `${BUNDLES_WIDTH}px`; }
get dueDateWidth() { return `${DUE_DATE_WIDTH}px`; }
get endDateWidth() { return `${END_DATE_WIDTH}px`; }
get aatStatusWidth() { return `${AAT_STATUS_WIDTH}px`; }
}
export function setupReflectorForScrollItem() {
reflector.registerType(ScrollItemComponent, {
'factory': () => new ScrollItemComponent(),
'parameters': [],
'annotations': [
new Component({
selector: 'scroll-item',
template: new TemplateConfig({
directives: [
CompanyNameComponent,
OpportunityNameComponent,
OfferingNameComponent,
StageButtonsComponent,
AccountCellComponent,
FormattedCellComponent
],
inline: `
<div [style]="itemStyle">
<company-name [company]="offering.company"
[cell-width]="companyNameWidth">
</company-name>
<opportunity-name [opportunity]="offering.opportunity"
[cell-width]="opportunityNameWidth">
</opportunity-name>
<offering-name [offering]="offering"
[cell-width]="offeringNameWidth">
</offering-name>
<account-cell [account]="offering.account"
[cell-width]="accountCellWidth">
</account-cell>
<formatted-cell [value]="offering.basePoints"
[cell-width]="basePointsWidth">
</formatted-cell>
<formatted-cell [value]="offering.kickerPoints"
[cell-width]="kickerPointsWidth">
</formatted-cell>
<stage-buttons [offering]="offering"
[cell-width]="stageButtonsWidth">
</stage-buttons>
<formatted-cell [value]="offering.bundles"
[cell-width]="bundlesWidth">
</formatted-cell>
<formatted-cell [value]="offering.dueDate"
[cell-width]="dueDateWidth">
</formatted-cell>
<formatted-cell [value]="offering.endDate"
[cell-width]="endDateWidth">
</formatted-cell>
<formatted-cell [value]="offering.aatStatus"
[cell-width]="aatStatusWidth">
</formatted-cell>
</div>`
}),
bind: {
'offering': 'offering'
}
})
]
});
}

View File

@ -17,7 +17,11 @@ describe('ng-dart1.x naive infinite scroll benchmark', function () {
'document.querySelector("scroll-app /deep/ #reset-btn").click()');
browser.executeScript(
'document.querySelector("scroll-app /deep/ #run-btn").click()');
browser.sleep(1000);
var s = 1000;
if (appSize > 4) {
s = s + appSize * 100;
}
browser.sleep(s);
},
params: [{
name: 'appSize', value: appSize

View File

@ -4,7 +4,6 @@ environment:
dependencies:
angular: '>=1.0.0 <2.0.0'
browser: '>=0.10.0 <0.11.0'
fixnum: '>=0.9.0 <1.0.0'
dev_dependencies:
angular2:
path: ../angular2

View File

@ -2,7 +2,6 @@ library common.stuff;
import 'dart:async';
import 'dart:collection';
import 'package:fixnum/fixnum.dart';
import 'package:observe/observe.dart';
const ITEMS = 1000;
@ -204,8 +203,8 @@ class Opportunity extends RawEntity {
}
class Account extends RawEntity {
Int64 get accountId => this['accountId'];
set accountId(Int64 val) {
int get accountId => this['accountId'];
set accountId(int val) {
this['accountId'] = val;
}
}

View File

@ -1,7 +1,6 @@
library random_data;
import 'common.dart';
import 'package:fixnum/fixnum.dart';
List<Offering> generateOfferings(int count) =>
new List.generate(count, generateOffering);
@ -34,7 +33,7 @@ Opportunity generateOpportunity(int seed) {
Account generateAccount(int seed) {
return new Account()
..accountId = new Int64(seed);
..accountId = seed;
}
String generateName(int seed) {

View File

@ -10,7 +10,7 @@ var config = exports.config = {
onPrepare: function() {
browser.ignoreSynchronization = true;
var _get = browser.get;
var sleepInterval = process.env.TRAVIS || process.env.JENKINS_URL ? 5000 : 2000;
var sleepInterval = process.env.TRAVIS || process.env.JENKINS_URL ? 7000 : 3000;
browser.get = function() {
var result = _get.apply(this, arguments);
browser.sleep(sleepInterval);