This repository has been archived by the owner. It is now read-only.
Permalink
Browse files

Add a basic performance test to the selenium suite. Must be manually …

…turned on for it to run.
  • Loading branch information...
1 parent cee4270 commit dfe0859f25e8fe1ce3cc0c4cb64abc516d01336a @shane-tomlinson shane-tomlinson committed Mar 15, 2013
@@ -7,17 +7,20 @@
var testsToIgnore = {
dev: [
- "public-terminals.js"
+ "public-terminals.js",
+ "can-interact-test.js"
],
stage: [
"frontend-qunit-test.js",
- "public-terminals.js"
+ "public-terminals.js",
+ "can-interact-test.js"
],
prod: [
"frontend-qunit-test.js",
- "public-terminals.js"
+ "public-terminals.js",
+ "can-interact-test.js"
]
};
@@ -0,0 +1,108 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * This module is responsible for sending collecting KPI results and saving
+ * performance information to performance_results.txt. The results are saved
+ * with this format:
+ *
+ * Test Name (cache [empty||full]), I:<time_ms>, D:<time_ms>
+ *
+ * I is the amount of time until the communication Iframe is ready.
+ * D is the amount of time until the user can interact with the Dialog.
+ *
+ * Several environment variables can be specified to control behavior.
+ *
+ * TEST_NAME - Name of test. Defaults to `Unnamed`
+ * KPIGGYBANK_URL - URL of the KPiggybank server.
+ * Defaults are found in urls.js
+ * OUTPUT_PATH - Where to save results. Defaults to
+ * `../performance_results.txt`
+ */
+
+const
+path = require('path'),
+url = require('url'),
+https = require('https'),
+fs = require('fs'),
+persona_urls = require('./urls.js');
+
+const TEST_NAME = process.env.TEST_NAME || 'Unnamed';
+const KPIGGYBANK_URL = process.env.KPIGGYBANK_URL || persona_urls.kpiggybank;
+const RESULTS_PATH = process.env.OUTPUT_PATH
+ ? path.resolve(path.join(__dirname, '..'), process.env.OUTPUT_PATH)
+ : path.join(__dirname, '..', 'performance_results.txt');
+
+exports.REQUEST_TIMEOUT = 5000;
+
+/**
+ * Save the last result sent to the KPI server to `RESULTS_PATH`.
+ *
+ * @method save
+ * @api public
+ */
+
+exports.save = function(cacheStatus, done) {
+ exports.getResult(function(err, result) {
+ if (err) return done(err);
+
+ exports.writeResult(cacheStatus, result, done);
+ });
+};
+
+
+exports.getResult = function(done) {
+ var kpiUrl = KPIGGYBANK_URL;
+ https.get({
+ method: 'GET',
+ hostname: url.parse(kpiUrl).hostname,
+ path: '/wsapi/interaction_data/last'
+ }, function(res) {
+ var data = '';
+ res.on('data', function(chunk) {
+ data += chunk;
+ });
+ res.on('end', function() {
+ try {
+ var result = JSON.parse(data);
+ if (!result) return done(new Error("no result"));
+ done && done(null, result.value);
+ } catch(e) {
+ done(e);
+ }
+ });
+ }).setTimeout(exports.REQUEST_TIMEOUT, function() {
+ done(new Error("timeout for " + kpiUrl));
+ });
+};
+
+exports.writeResult = function(cacheStatus, result, done) {
+ try {
+ var data = TEST_NAME
+ + " (" + cacheStatus + ")"
+ + ", I:" + result.ready_time_ms
+ + ", D:" + getCanInteractMS(result.event_stream)
+ + "\n";
+
+ fs.appendFile(RESULTS_PATH, data, function(err) {
+ if (err) return done(err);
+
+ done(null, result);
+ });
+ } catch(e) {
+ done(e);
+ }
+};
+
+function getCanInteract(eventStream) {
+ for (var i = 0, result; result = eventStream[i]; ++i) {
+ if (/can_interact/.test(result[0])) return result;
+ }
+}
+
+function getCanInteractMS(eventStream) {
+ var canInteractResult = getCanInteract(eventStream);
+ return (canInteractResult && canInteractResult[1]) || "undefined";
+}
+
@@ -8,21 +8,24 @@ const URLS = {
persona: 'https://login.dev.anosrep.org',
personatestuser: 'http://personatestuser.org/email/dev/',
myfavoritebeer: 'http://dev.myfavoritebeer.org',
- eyedeeme: 'https://eyedee.me'
+ eyedeeme: 'https://eyedee.me',
+ kpiggybank: 'https://piggy-stage.personatest.org/wsapi/interaction_data'
},
stage: {
"123done": 'http://beta.123done.org',
persona: 'https://login.anosrep.org',
personatestuser: 'http://personatestuser.org/email/stage/',
myfavoritebeer: 'http://beta.myfavoritebeer.org',
- eyedeeme: 'https://eyedee.me'
+ eyedeeme: 'https://eyedee.me',
+ kpiggybank: 'https://piggy-stage.personatest.org/wsapi/interaction_data'
},
prod: {
"123done": 'http://123done.org',
persona: 'https://login.persona.org',
personatestuser: 'http://personatestuser.org/email/prod/',
myfavoritebeer: 'http://myfavoritebeer.org',
- eyedeeme: 'https://eyedee.me'
+ eyedeeme: 'https://eyedee.me',
+ kpiggybank: 'https://piggy-stage.personatest.org/wsapi/interaction_data'
}
};
@@ -35,7 +38,8 @@ if (!URLS[env]) {
persona: 'https://'+personaURL,
personatestuser: 'http://personatestuser.org/email/custom/?browserid='+personaURL+'&verifier='+personaURL+'/verify',
myfavoritebeer: 'http://'+env+'.myfavoritebeer.org',
- eyedeeme: 'https://eyedee.me'
+ eyedeeme: 'https://eyedee.me',
+ kpiggybank: 'https://piggy-stage.personatest.org/wsapi/interaction_data'
};
}
@@ -0,0 +1,80 @@
+#!/usr/bin/env node
+/*jshint sub:true */
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const
+path = require('path'),
+assert = require('../../lib/asserts'),
+runner = require('../../lib/runner.js'),
+testSetup = require('../../lib/test-setup.js'),
+persona_urls = require('../../lib/urls.js'),
+performance = require('../../lib/performance.js'),
+CSS = require('../../pages/css.js'),
+config = require('../../../lib/configuration');
+
+var browser;
+
+runner.run(module, {
+ "setup all the things": function(done) {
+ testSetup.setup({ b:1 }, function(err, fix) {
+ if (fix) {
+ browser = fix.b[0];
+ }
+ done(err);
+ });
+ },
+
+ "start the session": function(done) {
+ testSetup.newBrowserSession(browser, done);
+ },
+
+ "load up 123done & open dialog": function(done) {
+ browser.chain({onError: done})
+ .get(persona_urls["123done"])
+ .wclick(CSS['123done.org'].signInButton)
+ .wwin(CSS['dialog'].windowName)
+ .wfind(CSS['dialog'].emailInput, done);
+ },
+
+ "load up 123done & reload the dialog to use primed cache": function(done) {
+ browser.chain({onError: done})
+ .wwin()
+ .refresh()
+ .wclick(CSS['123done.org'].signInButton)
+ .wwin(CSS['dialog'].windowName)
+ .wfind(CSS['dialog'].emailInput)
+ // give time for the interaction_data blob to be sent
+ .delay(performance.REQUEST_TIMEOUT, done);
+ },
+
+ "save cache emtpy results": function(done) {
+ performance.save("cache empty", done);
+ },
+
+ "load up 123done & reopen dialog to send KPIs": function(done) {
+ /* KPIs for one dialog session are sent in the next dialog session.
+ * hold on a second, go back to the main page, and reload the dialog
+ * so the KPIs are sent. */
+ browser.chain({onError: done})
+ .wwin()
+ .refresh()
+ .wclick(CSS['123done.org'].signInButton)
+ .wwin(CSS['dialog'].windowName)
+ .wfind(CSS['dialog'].emailInput)
+ // give time for the interaction_data blob to be sent
+ .delay(performance.REQUEST_TIMEOUT, done);
+ },
+
+ "save cache full results": function(done) {
+ browser.quit();
+ performance.save("cache full", done);
+ }
+},
+{
+ suiteName: path.basename(__filename),
+ cleanup: function(done) { testSetup.teardown(done);
+}});
+
@@ -22,7 +22,8 @@ BrowserID.Models.InteractionData = (function() {
'orphaned',
'new_account',
'email_type',
- 'rp_api'
+ 'rp_api',
+ 'ready_time_ms'
];
@@ -136,13 +136,13 @@ BrowserID.Modules.Dialog = (function() {
}
}
- function validateStartTime(startTime) {
- var parsedTime = parseInt(startTime, 10);
- if (typeof parsedTime !== "number" || isNaN(parsedTime)) {
- throw new Error("invalid value for start_time: " + startTime);
+ function validateNumber(title, numToValidate) {
+ var parsedNumber = parseInt(numToValidate, 10);
+ if (typeof parsedNumber !== "number" || isNaN(parsedNumber)) {
+ throw new Error("invalid value for " + title + ": " + numToValidate);
}
- return parsedTime;
+ return parsedNumber;
}
@@ -204,10 +204,16 @@ BrowserID.Modules.Dialog = (function() {
try {
var startTime = paramsFromRP.start_time;
if (startTime) {
- startTime = validateStartTime(startTime);
+ startTime = validateNumber("start_time", startTime);
self.publish("start_time", startTime);
}
+ var readyTimeMS = paramsFromRP.ready_time_ms;
+ if (readyTimeMS) {
+ readyTimeMS = validateNumber("ready_time_ms", readyTimeMS);
+ self.publish("kpi_data", { ready_time_ms: readyTimeMS });
+ }
+
self.publish("channel_established");
var rpAPI = paramsFromRP.rp_api;
@@ -128,6 +128,11 @@
var w;
+ // used in performance timing to figure out how long the
+ // communication_iframe takes to respond
+ var watchStartTime,
+ watchReadyTime;
+
// table of registered observers
var observers = {
login: null,
@@ -211,6 +216,7 @@
method: 'loaded',
success: function(){
// NOTE: Do not modify without reading GH-2017
+ watchReadyTime = new Date();
if (observers.ready) observers.ready();
}, error: function() {
}
@@ -275,6 +281,8 @@
}
function internalWatch(options) {
+ watchStartTime = new Date();
+
if (typeof options !== 'object') return;
if (options.onlogin && typeof options.onlogin !== 'function' ||
@@ -312,6 +320,14 @@
return rp_api;
}
+ function getReadyTime() {
+ if (watchStartTime && watchReadyTime) {
+ return watchReadyTime.getTime() - watchStartTime.getTime();
+ }
+
+ return null;
+ }
+
function internalRequest(options) {
checkDeprecated(options, "requiredEmail");
checkRenamed(options, "tosURL", "termsOfService");
@@ -326,6 +342,8 @@
}
options.rp_api = getRPAPI();
+ options.ready_time_ms = getReadyTime();
+
// reset the api_called in case the site implementor changes which api
// method called the next time around.
api_called = null;

0 comments on commit dfe0859

Please sign in to comment.