perf(ivy): add performance counters in ngDevMode (#23385)
PR Close #23385
This commit is contained in:
@ -6,12 +6,17 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {elementAttribute, elementClass, elementEnd, elementProperty, elementStart, elementStyle, elementStyleNamed, renderTemplate} from '../../src/render3/instructions';
|
||||
import {NgForOfContext} from '@angular/common';
|
||||
|
||||
import {RenderFlags, directiveInject} from '../../src/render3';
|
||||
import {defineComponent} from '../../src/render3/definition';
|
||||
import {bind, container, elementAttribute, elementClass, elementEnd, elementProperty, elementStart, elementStyle, elementStyleNamed, interpolation1, renderTemplate, text, textBinding} from '../../src/render3/instructions';
|
||||
import {LElementNode, LNode} from '../../src/render3/interfaces/node';
|
||||
import {RElement, domRendererFactory3} from '../../src/render3/interfaces/renderer';
|
||||
import {bypassSanitizationTrustStyle, bypassSanitizationTrustUrl, sanitizeStyle, sanitizeUrl} from '../../src/sanitization/sanitization';
|
||||
|
||||
import {TemplateFixture} from './render_util';
|
||||
import {NgForOf} from './common_with_def';
|
||||
import {ComponentFixture, TemplateFixture} from './render_util';
|
||||
|
||||
describe('instructions', () => {
|
||||
function createDiv() {
|
||||
@ -30,6 +35,13 @@ describe('instructions', () => {
|
||||
() => elementAttribute(
|
||||
0, 'title', bypassSanitizationTrustUrl('javascript:true'), sanitizeUrl));
|
||||
expect(t.html).toEqual('<div title="javascript:true"></div>');
|
||||
expect(ngDevMode).toHaveProperties({
|
||||
firstTemplatePass: 1,
|
||||
tNode: 1,
|
||||
tView: 1,
|
||||
rendererCreateElement: 1,
|
||||
rendererSetAttribute: 2
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -44,6 +56,12 @@ describe('instructions', () => {
|
||||
() => elementProperty(
|
||||
0, 'title', bypassSanitizationTrustUrl('javascript:false'), sanitizeUrl));
|
||||
expect(t.html).toEqual('<div title="javascript:false"></div>');
|
||||
expect(ngDevMode).toHaveProperties({
|
||||
firstTemplatePass: 1,
|
||||
tNode: 1,
|
||||
tView: 1,
|
||||
rendererCreateElement: 1,
|
||||
});
|
||||
});
|
||||
|
||||
it('should not stringify non string values', () => {
|
||||
@ -52,6 +70,13 @@ describe('instructions', () => {
|
||||
t.update(() => elementProperty(0, 'hidden', false));
|
||||
// The hidden property would be true if `false` was stringified into `"false"`.
|
||||
expect((t.hostNode.native as HTMLElement).querySelector('div') !.hidden).toEqual(false);
|
||||
expect(ngDevMode).toHaveProperties({
|
||||
firstTemplatePass: 1,
|
||||
tNode: 1,
|
||||
tView: 1,
|
||||
rendererCreateElement: 1,
|
||||
rendererSetProperty: 1
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -93,4 +118,63 @@ describe('instructions', () => {
|
||||
expect(fixture.html).toEqual('<div class="multiple classes"></div>');
|
||||
});
|
||||
});
|
||||
|
||||
describe('performance counters', () => {
|
||||
it('should create tViews only once for each nested level', () => {
|
||||
const _c0 = ['ngFor', '', 'ngForOf', ''];
|
||||
/**
|
||||
* <ul *ngFor="let row of rows">
|
||||
* <li *ngFor="let col of row.cols">{{col}}</li>
|
||||
* </ul>
|
||||
*/
|
||||
class NestedLoops {
|
||||
rows = [['a', 'b'], ['A', 'B'], ['a', 'b'], ['A', 'B']];
|
||||
|
||||
static ngComponentDef = defineComponent({
|
||||
type: NestedLoops,
|
||||
selectors: [['todo-app']],
|
||||
factory: function ToDoAppComponent_Factory() { return new NestedLoops(); },
|
||||
template: function ToDoAppComponent_Template(rf: RenderFlags, ctx: NestedLoops) {
|
||||
if (rf & 1) {
|
||||
container(0, ToDoAppComponent_NgForOf_Template_0, null, _c0);
|
||||
}
|
||||
if (rf & 2) {
|
||||
elementProperty(0, 'ngForOf', bind(ctx.rows));
|
||||
}
|
||||
function ToDoAppComponent_NgForOf_Template_0(
|
||||
rf: RenderFlags, ctx0: NgForOfContext<any>) {
|
||||
if (rf & 1) {
|
||||
elementStart(0, 'ul');
|
||||
container(1, ToDoAppComponent_NgForOf_NgForOf_Template_1, null, _c0);
|
||||
elementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
const row_r2 = ctx0.$implicit;
|
||||
elementProperty(1, 'ngForOf', bind(row_r2));
|
||||
}
|
||||
function ToDoAppComponent_NgForOf_NgForOf_Template_1(
|
||||
rf: RenderFlags, ctx1: NgForOfContext<any>) {
|
||||
if (rf & 1) {
|
||||
elementStart(0, 'li');
|
||||
text(1);
|
||||
elementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
const col_r3 = ctx1.$implicit;
|
||||
textBinding(1, interpolation1('', col_r3, ''));
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
directives: [NgForOf]
|
||||
});
|
||||
}
|
||||
const fixture = new ComponentFixture(NestedLoops);
|
||||
expect(ngDevMode).toHaveProperties({
|
||||
// Expect: host view + component + *ngForRow + *ngForCol
|
||||
tView: 7, // should be: 4,
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -28,6 +28,12 @@ describe('render3 integration test', () => {
|
||||
elementEnd();
|
||||
}
|
||||
}
|
||||
expect(ngDevMode).toHaveProperties({
|
||||
firstTemplatePass: 1,
|
||||
tNode: 1,
|
||||
tView: 1,
|
||||
rendererCreateElement: 1,
|
||||
});
|
||||
});
|
||||
|
||||
it('should render and update basic "Hello, World" template', () => {
|
||||
|
46
packages/core/test/render3/perfCounter_spec.ts
Normal file
46
packages/core/test/render3/perfCounter_spec.ts
Normal file
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* @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 {ngDevModeResetPerfCounters} from '../../src/render3/ng_dev_mode';
|
||||
|
||||
beforeEach(ngDevModeResetPerfCounters);
|
||||
beforeEach(() => {
|
||||
jasmine.addMatchers({
|
||||
toHaveProperties: function(util, customEqualityTesters) {
|
||||
return {compare: toHavePropertiesCompare};
|
||||
}
|
||||
});
|
||||
});
|
||||
function toHavePropertiesCompare(actual: any, expected: any) {
|
||||
let pass = true;
|
||||
let errors = [];
|
||||
for (let key of Object.keys(actual)) {
|
||||
if (expected.hasOwnProperty(key)) {
|
||||
if (actual[key] !== expected[key]) {
|
||||
pass = false;
|
||||
errors.push(`Expected '${key}' to be '${expected[key]}' but was '${actual[key]}'.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
return {pass: pass, message: errors.join('\n')};
|
||||
}
|
||||
|
||||
describe('toHaveProperties', () => {
|
||||
it('should pass', () => {
|
||||
expect({tNode: 1}).toHaveProperties({});
|
||||
expect({tNode: 2}).toHaveProperties({tNode: 2});
|
||||
});
|
||||
|
||||
it('should fail', () => {
|
||||
expect(toHavePropertiesCompare({tNode: 2, tView: 4}, {tNode: 3, tView: 5})).toEqual({
|
||||
pass: false,
|
||||
message:
|
||||
'Expected \'tNode\' to be \'3\' but was \'2\'.\nExpected \'tView\' to be \'5\' but was \'4\'.'
|
||||
});
|
||||
});
|
||||
});
|
Reference in New Issue
Block a user