Merge "UI latency reporting"
This commit is contained in:
@@ -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>
|
||||
95
polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js
Normal file
95
polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js
Normal 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];
|
||||
},
|
||||
});
|
||||
})();
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
},
|
||||
|
||||
ready: function() {
|
||||
this.$.reporting.appStarted();
|
||||
this._viewState = {
|
||||
changeView: {
|
||||
changeNum: null,
|
||||
|
||||
53
polygerrit-ui/app/elements/gr-app_test.html
Normal file
53
polygerrit-ui/app/elements/gr-app_test.html
Normal 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>
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user