summaryrefslogtreecommitdiff
path: root/www/wiki/extensions/MultimediaViewer/tests/qunit/mmv/logging/mmv.logging.PerformanceLogger.test.js
diff options
context:
space:
mode:
Diffstat (limited to 'www/wiki/extensions/MultimediaViewer/tests/qunit/mmv/logging/mmv.logging.PerformanceLogger.test.js')
-rw-r--r--www/wiki/extensions/MultimediaViewer/tests/qunit/mmv/logging/mmv.logging.PerformanceLogger.test.js341
1 files changed, 341 insertions, 0 deletions
diff --git a/www/wiki/extensions/MultimediaViewer/tests/qunit/mmv/logging/mmv.logging.PerformanceLogger.test.js b/www/wiki/extensions/MultimediaViewer/tests/qunit/mmv/logging/mmv.logging.PerformanceLogger.test.js
new file mode 100644
index 00000000..81a621f7
--- /dev/null
+++ b/www/wiki/extensions/MultimediaViewer/tests/qunit/mmv/logging/mmv.logging.PerformanceLogger.test.js
@@ -0,0 +1,341 @@
+/*
+ * This file is part of the MediaWiki extension MultimediaViewer.
+ *
+ * MultimediaViewer is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MultimediaViewer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with MultimediaViewer. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+( function ( mw, $ ) {
+ QUnit.module( 'mmv.logging.PerformanceLogger', QUnit.newMwEnvironment() );
+
+ function createFakeXHR( response ) {
+ return {
+ readyState: 0,
+ open: $.noop,
+ send: function () {
+ var xhr = this;
+
+ setTimeout( function () {
+ xhr.readyState = 4;
+ xhr.response = response;
+ if ( $.isFunction( xhr.onreadystatechange ) ) {
+ xhr.onreadystatechange();
+ }
+ }, 0 );
+ }
+ };
+ }
+
+ QUnit.test( 'recordEntry: basic', function ( assert ) {
+ var performance = new mw.mmv.logging.PerformanceLogger(),
+ fakeEventLog = { logEvent: this.sandbox.stub() },
+ type = 'gender',
+ total = 100,
+ // we'll be waiting for 4 promises to complete
+ asyncs = [ assert.async(), assert.async(), assert.async(), assert.async() ];
+
+ this.sandbox.stub( performance, 'loadDependencies' ).returns( $.Deferred().resolve() );
+ this.sandbox.stub( performance, 'isInSample' );
+ performance.setEventLog( fakeEventLog );
+
+ performance.isInSample.returns( false );
+
+ performance.recordEntry( type, total ).then( null, function () {
+ assert.strictEqual( fakeEventLog.logEvent.callCount, 0, 'No stats should be logged if not in sample' );
+ asyncs.pop()();
+ } );
+
+ performance.isInSample.returns( true );
+
+ performance.recordEntry( type, total ).then( null, function () {
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 0 ).args[ 0 ], 'MultimediaViewerNetworkPerformance', 'EventLogging schema is correct' );
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 0 ).args[ 1 ].type, type, 'type is correct' );
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 0 ).args[ 1 ].total, total, 'total is correct' );
+ assert.strictEqual( fakeEventLog.logEvent.callCount, 1, 'Stats should be logged' );
+ asyncs.pop()();
+ } );
+
+ performance.recordEntry( type, total, 'URL' ).then( null, function () {
+ assert.strictEqual( fakeEventLog.logEvent.callCount, 2, 'Stats should be logged' );
+ asyncs.pop()();
+ } );
+
+ performance.recordEntry( type, total, 'URL' ).then( null, function () {
+ assert.strictEqual( fakeEventLog.logEvent.callCount, 2, 'Stats should not be logged a second time for the same URL' );
+ asyncs.pop()();
+ } );
+ } );
+
+ QUnit.test( 'recordEntry: with Navigation Timing data', function ( assert ) {
+ var fakeRequest,
+ varnish1 = 'cp1061',
+ varnish2 = 'cp3006',
+ varnish3 = 'cp3005',
+ varnish1hits = 0,
+ varnish2hits = 2,
+ varnish3hits = 1,
+ xvarnish = '1754811951 1283049064, 1511828531, 1511828573 1511828528',
+ xcache = varnish1 + ' miss (0), ' + varnish2 + ' miss (2), ' + varnish3 + ' frontend hit (1), malformed(5)',
+ age = '12345',
+ contentLength = '23456',
+ urlHost = 'fail',
+ date = 'Tue, 04 Feb 2014 11:11:50 GMT',
+ timestamp = 1391512310,
+ url = 'https://' + urlHost + '/balls.jpg',
+ redirect = 500,
+ dns = 2,
+ tcp = 10,
+ request = 25,
+ response = 50,
+ cache = 1,
+ perfData = {
+ initiatorType: 'xmlhttprequest',
+ name: url,
+ duration: 12345,
+ redirectStart: 1000,
+ redirectEnd: 1500,
+ domainLookupStart: 2,
+ domainLookupEnd: 4,
+ connectStart: 50,
+ connectEnd: 60,
+ requestStart: 125,
+ responseStart: 150,
+ responseEnd: 200,
+ fetchStart: 1
+ },
+ country = 'FR',
+ type = 'image',
+ performance = new mw.mmv.logging.PerformanceLogger(),
+ status = 200,
+ metered = true,
+ bandwidth = 45.67,
+ fakeEventLog = { logEvent: this.sandbox.stub() },
+ done = assert.async();
+
+ this.sandbox.stub( performance, 'loadDependencies' ).returns( $.Deferred().resolve() );
+ performance.setEventLog( fakeEventLog );
+ performance.schemaSupportsCountry = this.sandbox.stub().returns( true );
+
+ this.sandbox.stub( performance, 'getWindowPerformance' ).returns( {
+ getEntriesByName: function () {
+ return [ perfData, {
+ initiatorType: 'bogus',
+ duration: 1234,
+ name: url
+ } ];
+ }
+ } );
+
+ this.sandbox.stub( performance, 'getNavigatorConnection' ).returns( { metered: metered, bandwidth: bandwidth } );
+ this.sandbox.stub( performance, 'isInSample' ).returns( true );
+
+ fakeRequest = {
+ getResponseHeader: function ( header ) {
+ switch ( header ) {
+ case 'X-Cache':
+ return xcache;
+ case 'X-Varnish':
+ return xvarnish;
+ case 'Age':
+ return age;
+ case 'Content-Length':
+ return contentLength;
+ case 'Date':
+ return date;
+ }
+ },
+ status: status
+ };
+
+ performance.setGeo( { country: country } );
+
+ performance.recordEntry( type, 100, url, fakeRequest ).then( null, function () {
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 0 ).args[ 0 ], 'MultimediaViewerNetworkPerformance', 'EventLogging schema is correct' );
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 0 ).args[ 1 ].type, type, 'type is correct' );
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 0 ).args[ 1 ].varnish1, varnish1, 'varnish1 is correct' );
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 0 ).args[ 1 ].varnish2, varnish2, 'varnish2 is correct' );
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 0 ).args[ 1 ].varnish3, varnish3, 'varnish3 is correct' );
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 0 ).args[ 1 ].varnish4, undefined, 'varnish4 is undefined' );
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 0 ).args[ 1 ].varnish1hits, varnish1hits, 'varnish1hits is correct' );
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 0 ).args[ 1 ].varnish2hits, varnish2hits, 'varnish2hits is correct' );
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 0 ).args[ 1 ].varnish3hits, varnish3hits, 'varnish3hits is correct' );
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 0 ).args[ 1 ].varnish4hits, undefined, 'varnish4hits is undefined' );
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 0 ).args[ 1 ].XVarnish, xvarnish, 'XVarnish is correct' );
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 0 ).args[ 1 ].XCache, xcache, 'XCache is correct' );
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 0 ).args[ 1 ].age, parseInt( age, 10 ), 'age is correct' );
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 0 ).args[ 1 ].contentLength, parseInt( contentLength, 10 ), 'contentLength is correct' );
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 0 ).args[ 1 ].contentHost, window.location.host, 'contentHost is correct' );
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 0 ).args[ 1 ].urlHost, urlHost, 'urlHost is correct' );
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 0 ).args[ 1 ].timestamp, timestamp, 'timestamp is correct' );
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 0 ).args[ 1 ].total, perfData.duration, 'total is correct' );
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 0 ).args[ 1 ].redirect, redirect, 'redirect is correct' );
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 0 ).args[ 1 ].dns, dns, 'dns is correct' );
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 0 ).args[ 1 ].tcp, tcp, 'tcp is correct' );
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 0 ).args[ 1 ].request, request, 'request is correct' );
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 0 ).args[ 1 ].response, response, 'response is correct' );
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 0 ).args[ 1 ].cache, cache, 'cache is correct' );
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 0 ).args[ 1 ].country, country, 'country is correct' );
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 0 ).args[ 1 ].isHttps, true, 'isHttps is correct' );
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 0 ).args[ 1 ].status, status, 'status is correct' );
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 0 ).args[ 1 ].metered, metered, 'metered is correct' );
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 0 ).args[ 1 ].bandwidth, Math.round( bandwidth ), 'bandwidth is correct' );
+ done();
+ } );
+ } );
+
+ QUnit.test( 'recordEntry: with async extra stats', function ( assert ) {
+ var performance = new mw.mmv.logging.PerformanceLogger(),
+ fakeEventLog = { logEvent: this.sandbox.stub() },
+ type = 'gender',
+ total = 100,
+ overriddenType = 'image',
+ foo = 'bar',
+ extraStatsPromise = $.Deferred(),
+ clock = this.sandbox.useFakeTimers();
+
+ this.sandbox.stub( performance, 'loadDependencies' ).returns( $.Deferred().resolve() );
+ this.sandbox.stub( performance, 'isInSample' );
+ performance.setEventLog( fakeEventLog );
+
+ performance.isInSample.returns( true );
+
+ performance.recordEntry( type, total, 'URL1', undefined, extraStatsPromise );
+
+ assert.strictEqual( fakeEventLog.logEvent.callCount, 0, 'Stats should not be logged if the promise hasn\'t completed yet' );
+
+ extraStatsPromise.reject();
+
+ extraStatsPromise.then( null, function () {
+ assert.strictEqual( fakeEventLog.logEvent.callCount, 1, 'Stats should be logged' );
+
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 0 ).args[ 0 ], 'MultimediaViewerNetworkPerformance', 'EventLogging schema is correct' );
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 0 ).args[ 1 ].type, type, 'type is correct' );
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 0 ).args[ 1 ].total, total, 'total is correct' );
+ } );
+
+ // make sure first promise is completed before recording another entry,
+ // to make sure data in fakeEventLog doesn't suffer race conditions
+ clock.tick( 10 );
+ clock.restore();
+
+ extraStatsPromise = $.Deferred();
+
+ performance.recordEntry( type, total, 'URL2', undefined, extraStatsPromise );
+
+ assert.strictEqual( fakeEventLog.logEvent.callCount, 1, 'Stats should not be logged if the promise hasn\'t been resolved yet' );
+
+ extraStatsPromise.resolve( { type: overriddenType, foo: foo } );
+
+ return extraStatsPromise.then( function () {
+ assert.strictEqual( fakeEventLog.logEvent.callCount, 2, 'Stats should be logged' );
+
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 1 ).args[ 0 ], 'MultimediaViewerNetworkPerformance', 'EventLogging schema is correct' );
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 1 ).args[ 1 ].type, overriddenType, 'type is correct' );
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 1 ).args[ 1 ].total, total, 'total is correct' );
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 1 ).args[ 1 ].foo, foo, 'extra stat is correct' );
+ } );
+ } );
+
+ QUnit.test( 'parseVarnishXCacheHeader', function ( assert ) {
+ var varnish1 = 'cp1061',
+ varnish2 = 'cp3006',
+ varnish3 = 'cp3005',
+ testString = varnish1 + ' miss (0), ' + varnish2 + ' miss (0), ' + varnish3 + ' frontend hit (1)',
+ performance = new mw.mmv.logging.PerformanceLogger(),
+ varnishXCache = performance.parseVarnishXCacheHeader( testString );
+
+ assert.strictEqual( varnishXCache.varnish1, varnish1, 'First varnish server name extracted' );
+ assert.strictEqual( varnishXCache.varnish2, varnish2, 'Second varnish server name extracted' );
+ assert.strictEqual( varnishXCache.varnish3, varnish3, 'Third varnish server name extracted' );
+ assert.strictEqual( varnishXCache.varnish4, undefined, 'Fourth varnish server is undefined' );
+ assert.strictEqual( varnishXCache.varnish1hits, 0, 'First varnish hit count extracted' );
+ assert.strictEqual( varnishXCache.varnish2hits, 0, 'Second varnish hit count extracted' );
+ assert.strictEqual( varnishXCache.varnish3hits, 1, 'Third varnish hit count extracted' );
+ assert.strictEqual( varnishXCache.varnish4hits, undefined, 'Fourth varnish hit count is undefined' );
+
+ testString = varnish1 + ' miss (36), ' + varnish2 + ' miss (2)';
+ varnishXCache = performance.parseVarnishXCacheHeader( testString );
+
+ assert.strictEqual( varnishXCache.varnish1, varnish1, 'First varnish server name extracted' );
+ assert.strictEqual( varnishXCache.varnish2, varnish2, 'Second varnish server name extracted' );
+ assert.strictEqual( varnishXCache.varnish3, undefined, 'Third varnish server is undefined' );
+ assert.strictEqual( varnishXCache.varnish1hits, 36, 'First varnish hit count extracted' );
+ assert.strictEqual( varnishXCache.varnish2hits, 2, 'Second varnish hit count extracted' );
+ assert.strictEqual( varnishXCache.varnish3hits, undefined, 'Third varnish hit count is undefined' );
+
+ varnishXCache = performance.parseVarnishXCacheHeader( 'garbage' );
+ assert.ok( $.isEmptyObject( varnishXCache ), 'Varnish cache results are empty' );
+ } );
+
+ QUnit.test( 'record()', function ( assert ) {
+ var type = 'foo',
+ url = 'http://example.com/',
+ response = {},
+ done = assert.async(),
+ performance = new mw.mmv.logging.PerformanceLogger();
+
+ performance.newXHR = function () { return createFakeXHR( response ); };
+
+ performance.recordEntryDelayed = function ( recordType, _, recordUrl, recordRequest ) {
+ assert.strictEqual( recordType, type, 'type is recorded correctly' );
+ assert.strictEqual( recordUrl, url, 'url is recorded correctly' );
+ assert.strictEqual( recordRequest.response, response, 'response is recorded correctly' );
+ done();
+ };
+
+ return performance.record( type, url ).done( function ( recordResponse ) {
+ assert.strictEqual( recordResponse, response, 'response is passed to callback' );
+ } );
+ } );
+
+ QUnit.test( 'record() with old browser', function ( assert ) {
+ var type = 'foo',
+ url = 'http://example.com/',
+ done = assert.async(),
+ performance = new mw.mmv.logging.PerformanceLogger();
+
+ performance.newXHR = function () { throw new Error( 'XMLHttpRequest? What\'s that?' ); };
+
+ performance.record( type, url ).fail( function () {
+ assert.ok( true, 'the promise is rejected when XMLHttpRequest is not supported' );
+ done();
+ } );
+ } );
+
+ QUnit.test( 'mw.mmv.logging.Api', function ( assert ) {
+ var api,
+ oldRecord = mw.mmv.logging.PerformanceLogger.prototype.recordJQueryEntryDelayed,
+ oldAjax = mw.Api.prototype.ajax,
+ ajaxCalled = false,
+ fakeJqxhr = {};
+
+ mw.Api.prototype.ajax = function () {
+ ajaxCalled = true;
+ return $.Deferred().resolve( {}, fakeJqxhr );
+ };
+
+ mw.mmv.logging.PerformanceLogger.prototype.recordJQueryEntryDelayed = function ( type, total, jqxhr ) {
+ assert.strictEqual( type, 'foo', 'type was passed correctly' );
+ assert.strictEqual( jqxhr, fakeJqxhr, 'jqXHR was passed correctly' );
+ };
+
+ api = new mw.mmv.logging.Api( 'foo' );
+
+ api.ajax();
+
+ assert.ok( ajaxCalled, 'parent ajax() function was called' );
+
+ mw.mmv.logging.PerformanceLogger.prototype.recordJQueryEntryDelayed = oldRecord;
+ mw.Api.prototype.ajax = oldAjax;
+ } );
+}( mediaWiki, jQuery ) );