Merge "UI latency reporting"

This commit is contained in:
Andrew Bonventre
2016-08-23 17:37:31 +00:00
committed by Gerrit Code Review
10 changed files with 301 additions and 8 deletions

View File

@@ -0,0 +1,21 @@
<!--
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<link rel="import" href="../../../bower_components/polymer/polymer.html">
<dom-module id="gr-reporting">
<script src="gr-reporting.js"></script>
</dom-module>

View File

@@ -0,0 +1,95 @@
// Copyright (C) 2016 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
(function() {
'use strict';
var APP_STARTED = 'App Started';
var PAGE_LOADED = 'Page Loaded';
var TIMING_EVENT = 'timing-report';
var DEFAULT_CATEGORY = 'UI Latency';
var DEFAULT_TYPE = 'timing';
Polymer({
is: 'gr-reporting',
properties: {
_baselines: {
type: Array,
value: function() { return {}; },
}
},
get performanceTiming() {
return window.performance.timing;
},
now: function() {
return Math.round(10 * window.performance.now()) / 10;
},
reporter: function(type, category, eventName, eventValue) {
eventValue = eventValue;
var detail = {
type: type,
category: category,
name: eventName,
value: eventValue,
};
document.dispatchEvent(
new CustomEvent(TIMING_EVENT, {detail: detail}));
console.log(eventName + ': ' + eventValue);
},
/**
* User-perceived app start time, should be reported when the app is ready.
*/
appStarted: function() {
var startTime =
new Date().getTime() - this.performanceTiming.navigationStart;
this.reporter(
DEFAULT_TYPE, DEFAULT_CATEGORY, APP_STARTED, startTime);
},
/**
* Page load time, should be reported at any time after navigation.
*/
pageLoaded: function() {
if (this.performanceTiming.loadEventEnd === 0) {
console.error('pageLoaded should be called after window.onload');
this.async(this.pageLoaded, 100);
} else {
var loadTime = this.performanceTiming.loadEventEnd -
this.performanceTiming.navigationStart;
this.reporter(DEFAULT_TYPE, DEFAULT_CATEGORY, PAGE_LOADED, loadTime);
}
},
/**
* Reset named timer.
*/
time: function(name) {
this._baselines[name] = this.now();
},
/**
* Finish named timer and report it to server.
*/
timeEnd: function(name) {
var baseTime = this._baselines[name] || 0;
var time = this.now() - baseTime;
this.reporter(DEFAULT_TYPE, DEFAULT_CATEGORY, name, time);
delete this._baselines[name];
},
});
})();

View File

@@ -0,0 +1,93 @@
<!DOCTYPE html>
<!--
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-reporting</title>
<script src="../../../bower_components/webcomponentsjs/webcomponents.min.js"></script>
<script src="../../../bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="gr-reporting.html">
<test-fixture id="basic">
<template>
<gr-reporting></gr-reporting>
</template>
</test-fixture>
<script>
suite('gr-reporting tests', function() {
var element;
var sandbox;
var clock;
var fakePerformance;
var NOW_TIME = 100;
setup(function() {
sandbox = sinon.sandbox.create();
clock = sinon.useFakeTimers(NOW_TIME);
element = fixture('basic');
fakePerformance = {
navigationStart: 1,
loadEventEnd: 2,
};
sinon.stub(element, 'performanceTiming',
{get: function() {return fakePerformance;}});
sandbox.stub(element, 'reporter');
});
teardown(function() {
sandbox.restore();
clock.restore();
});
test('appStarted', function() {
element.appStarted();
assert.isTrue(
element.reporter.calledWithExactly(
'timing', 'UI Latency', 'App Started',
NOW_TIME - fakePerformance.navigationStart
));
});
test('pageLoaded', function() {
element.pageLoaded();
assert.isTrue(
element.reporter.calledWithExactly(
'timing', 'UI Latency', 'Page Loaded',
fakePerformance.loadEventEnd - fakePerformance.navigationStart)
);
});
test('time and timeEnd', function() {
var nowStub = sinon.stub(element, 'now').returns(0);
element.time('foo');
nowStub.returns(1);
element.time('bar');
nowStub.returns(2);
element.timeEnd('bar');
nowStub.returns(3.123);
element.timeEnd('foo');
assert.isTrue(element.reporter.calledWithExactly(
'timing', 'UI Latency', 'foo', 3.123
));
assert.isTrue(element.reporter.calledWithExactly(
'timing', 'UI Latency', 'bar', 1
));
});
});
</script>

View File

@@ -18,8 +18,12 @@
// custom element having the id "app", but it is made explicit here.
var app = document.querySelector('#app');
var restAPI = document.createElement('gr-rest-api-interface');
var reporting = document.createElement('gr-reporting');
window.addEventListener('WebComponentsReady', function() {
reporting.timeEnd('WebComponentsReady');
reporting.pageLoaded();
// Middleware
page(function(ctx, next) {
document.body.scrollTop = 0;

View File

@@ -14,10 +14,12 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
<link rel="import" href="../../../bower_components/polymer/polymer.html">
<link rel="import" href="../../core/gr-reporting/gr-reporting.html">
<link rel="import" href="../gr-diff-comment-thread/gr-diff-comment-thread.html">
<link rel="import" href="../gr-diff-processor/gr-diff-processor.html">
<link rel="import" href="../gr-ranged-comment-layer/gr-ranged-comment-layer.html">
<link rel="import" href="../gr-syntax-layer/gr-syntax-layer.html">
<dom-module id="gr-diff-builder">
<template>
<div class="contentWrapper">
@@ -32,6 +34,7 @@ limitations under the License.
<gr-diff-processor
id="processor"
groups="{{_groups}}"></gr-diff-processor>
<gr-reporting id="reporting"></gr-reporting>
</template>
<script src="../gr-diff/gr-diff-line.js"></script>
<script src="../gr-diff/gr-diff-group.js"></script>
@@ -111,17 +114,19 @@ limitations under the License.
this._clearDiffContent();
console.time(TimingLabel.TOTAL);
console.time(TimingLabel.CONTENT);
var reporting = this.$.reporting;
reporting.time(TimingLabel.TOTAL);
reporting.time(TimingLabel.CONTENT);
return this.$.processor.process(this.diff.content).then(function() {
if (this.isImageDiff) {
this._builder.renderDiffImages();
}
console.timeEnd(TimingLabel.CONTENT);
console.time(TimingLabel.SYNTAX);
reporting.timeEnd(TimingLabel.CONTENT);
reporting.time(TimingLabel.SYNTAX);
this.$.syntaxLayer.process().then(function() {
console.timeEnd(TimingLabel.SYNTAX);
console.timeEnd(TimingLabel.TOTAL);
reporting.timeEnd(TimingLabel.SYNTAX);
reporting.timeEnd(TimingLabel.TOTAL);
});
this.fire('render');
}.bind(this));

View File

@@ -437,6 +437,10 @@ limitations under the License.
]
},
];
stub('gr-reporting', {
time: sinon.stub(),
timeEnd: sinon.stub(),
});
element = fixture('basic');
outputEl = element.queryEffectiveChildren('#diffTable');
element.addEventListener('render', function() {
@@ -458,6 +462,20 @@ limitations under the License.
element.render({left: [], right: []}, prefs);
});
test('reporting', function(done) {
var timeStub = element.$.reporting.time;
var timeEndStub = element.$.reporting.timeEnd;
flush(function() {
assert.isTrue(timeStub.calledWithExactly('Diff Total Render'));
assert.isTrue(timeStub.calledWithExactly('Diff Content Render'));
assert.isTrue(timeStub.calledWithExactly('Diff Syntax Render'));
assert.isTrue(timeEndStub.calledWithExactly('Diff Total Render'));
assert.isTrue(timeEndStub.calledWithExactly('Diff Content Render'));
assert.isTrue(timeEndStub.calledWithExactly('Diff Syntax Render'));
done();
});
});
test('renderSection', function() {
var section = outputEl.querySelector('stub:nth-of-type(2)');
var prevInnerHTML = section.innerHTML;

View File

@@ -22,6 +22,7 @@ limitations under the License.
<link rel="import" href="./core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.html">
<link rel="import" href="./core/gr-main-header/gr-main-header.html">
<link rel="import" href="./core/gr-router/gr-router.html">
<link rel="import" href="./core/gr-reporting/gr-reporting.html">
<link rel="import" href="./change-list/gr-change-list-view/gr-change-list-view.html">
<link rel="import" href="./change-list/gr-dashboard-view/gr-dashboard-view.html">
@@ -136,6 +137,7 @@ limitations under the License.
</gr-overlay>
<gr-error-manager></gr-error-manager>
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
<gr-reporting id="reporting"></gr-reporting>
</template>
<script src="gr-app.js"></script>
</dom-module>

View File

@@ -72,6 +72,7 @@
},
ready: function() {
this.$.reporting.appStarted();
this._viewState = {
changeView: {
changeNum: null,

View File

@@ -0,0 +1,53 @@
<!DOCTYPE html>
<!--
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-app</title>
<script src="../bower_components/webcomponentsjs/webcomponents.min.js"></script>
<script src="../bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="gr-app.html">
<test-fixture id="basic">
<template>
<gr-app></gr-app>
</template>
</test-fixture>
<script>
suite('gr-app tests', function() {
var sandbox;
var element;
setup(function(done) {
sandbox = sinon.sandbox.create();
stub('gr-reporting', {
appStarted: sandbox.stub(),
});
element = fixture('basic');
flush(done);
});
teardown(function() {
sandbox.restore();
});
test('reporting', function() {
assert.isTrue(element.$.reporting.appStarted.calledOnce);
});
});
</script>

View File

@@ -49,8 +49,8 @@ limitations under the License.
'diff/gr-diff-comment-thread/gr-diff-comment-thread_test.html',
'diff/gr-diff-comment/gr-diff-comment_test.html',
'diff/gr-diff-cursor/gr-diff-cursor_test.html',
'diff/gr-diff-highlight/gr-diff-highlight_test.html',
'diff/gr-diff-highlight/gr-annotation_test.html',
'diff/gr-diff-highlight/gr-diff-highlight_test.html',
'diff/gr-diff-preferences/gr-diff-preferences_test.html',
'diff/gr-diff-processor/gr-diff-processor_test.html',
'diff/gr-diff-selection/gr-diff-selection_test.html',
@@ -61,6 +61,7 @@ limitations under the License.
'diff/gr-ranged-comment-layer/gr-ranged-comment-layer_test.html',
'diff/gr-selection-action-box/gr-selection-action-box_test.html',
'diff/gr-syntax-layer/gr-syntax-layer_test.html',
'gr-app_test.html',
'settings/gr-account-info/gr-account-info_test.html',
'settings/gr-email-editor/gr-email-editor_test.html',
'settings/gr-group-list/gr-group-list_test.html',
@@ -69,10 +70,10 @@ limitations under the License.
'settings/gr-settings-view/gr-settings-view_test.html',
'settings/gr-ssh-editor/gr-ssh-editor_test.html',
'settings/gr-watched-projects-editor/gr-watched-projects-editor_test.html',
'shared/gr-autocomplete/gr-autocomplete_test.html',
'shared/gr-account-label/gr-account-label_test.html',
'shared/gr-account-link/gr-account-link_test.html',
'shared/gr-alert/gr-alert_test.html',
'shared/gr-autocomplete/gr-autocomplete_test.html',
'shared/gr-avatar/gr-avatar_test.html',
'shared/gr-change-star/gr-change-star_test.html',
'shared/gr-confirm-dialog/gr-confirm-dialog_test.html',