summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Macdonald <mamacdon@gmail.com>2014-11-14 14:58:37 -0500
committerMark Macdonald <mamacdon@gmail.com>2014-11-14 15:18:52 -0500
commit97d036a10212b294a33375756bed1ac02d05503b (patch)
tree0270cefb51d21cd26422a3625b7554d2c0feb715
parent1f663342de2fafa8018df34874b4de7c6e359ca4 (diff)
downloadorg.eclipse.orion.client-origin/saucefix.zip
org.eclipse.orion.client-origin/saucefix.tar.gz
org.eclipse.orion.client-origin/saucefix.tar.bz2
Better tolerance for bad Sauce resultsorigin/saucefix
-rw-r--r--releng/org.eclipse.orion.client.releng/test/Gruntfile.js97
-rw-r--r--releng/org.eclipse.orion.client.releng/test/package.json3
-rw-r--r--releng/org.eclipse.orion.client.releng/test/runtests.js4
-rw-r--r--releng/org.eclipse.orion.client.releng/test/test-helpers.js66
4 files changed, 115 insertions, 55 deletions
diff --git a/releng/org.eclipse.orion.client.releng/test/Gruntfile.js b/releng/org.eclipse.orion.client.releng/test/Gruntfile.js
index 14cc196..cc66449 100644
--- a/releng/org.eclipse.orion.client.releng/test/Gruntfile.js
+++ b/releng/org.eclipse.orion.client.releng/test/Gruntfile.js
@@ -43,9 +43,11 @@ module.exports = function(grunt) {
nodeUrl = require("url"),
archiver = require("archiver"),
fmt = require("util").format,
+ Q = require("q"),
nodeutil = require("util"),
zlib = require("zlib"),
- util = require(orionClient + "modules/orionode/build/utils")(grunt);
+ util = require(orionClient + "modules/orionode/build/utils")(grunt),
+ helpers = require("./test-helpers");
var config = util.loadBuildConfig(orionClient + "/releng/org.eclipse.orion.client.releng/builder/scripts/orion.build.js"),
bundles = util.parseBundles(config, { orionClient: orionClient }),
@@ -151,26 +153,7 @@ module.exports = function(grunt) {
grunt.registerTask("test", ["server", "sauce"]);
grunt.registerTask("default", "test");
- /**
- * For Hudson to parse out nice packages instead of (root), we have to add classname="packageName.className"
- * to the <testsuite> element, and prefix the "packageName." onto every <testcase>'s @classname. We also strip
- * out some problematic characters from the original classnames: [#?.]
- * @param {String} xml The xunit test result
- * @returns {String} The test result, fixed up
- */
- function nicerName(xml, sauceResult, testUrl) {
- function sanitize(s) {
- return s.replace(/[^A-Za-z0-9_\.]/g, "_");
- }
- testUrl = testUrl.replace(/(^\/)|(\.html$)/g, "");
- var platform = sanitize(sauceResult.platform.join(" ")),
- packageName = sanitize(fmt("%s.%s", platform, testUrl));
- return xml
- .replace(/(<testsuite\s+name="[^"]+")/g, fmt("$1 classname=\"%s\"", packageName))
- .replace(/<testcase classname="([^"]+)"/g, function(match, className) {
- return fmt("<testcase classname=\"%s.%s\"", packageName, className.replace(/[#?.]/g, "_"));
- });
- }
+
/**
* Called per browser, per test page, after a test job is complete. Not called for a job that times out.
@@ -178,42 +161,52 @@ module.exports = function(grunt) {
* @param {Function} callback Async CB to be invoked as callback(err, passOrFail) when done
*/
function onTestComplete(sauceResult, callback) {
- function error(msgOrError) {
- var e = nodeutil.isError(msgOrError) ? msgOrError : new Error(msgOrError);
- grunt.warn(e && e.stack);
- callback(e);
- }
grunt.verbose.write("Got test result: ");
- grunt.verbose.oklns(JSON.stringify(sauceResult));
+ grunt.verbose.write(sauceResult);
var mochaResult = sauceResult.result,
- id = sauceResult.id;
- if (!mochaResult)
- throw new Error(fmt("Test %s is missing 'result' field in response:\n%s", id, JSON.stringify(sauceResult)));
- var testurl = mochaResult.url || "",
- gzippedXml = mochaResult.xunit,
- filename = fmt("TEST-%s_%s.xml", testurl.replace(/[^A-Za-z0-9_\-]/g, "_"), id);
- if (/experienced an error/.test(sauceResult.message))
- error(sauceResult.message);
- if (!testurl)
- error(fmt("Test %s did not return its url. Ensure it is using sauce.js", id));
- if (!gzippedXml)
- error(fmt("Test %s did not return an xunit result. Ensure it is using sauce.js.", testurl));
-
- grunt.verbose.write("Inflating compressed xunit result...");
- zlib.gunzip(new Buffer(gzippedXml, "base64"), function(e, buffer) {
- if (e) {
- error(e);
+ id = sauceResult.id,
+ testPageUrl = sauceResult.testPageUrl,
+ filename = fmt("TEST-%s_%s.xml", testPageUrl.replace(/[^A-Za-z0-9_\-]/g, "_"), id);
+ Q.try(function() {
+ function throwError(msgOrError) {
+ var e = nodeutil.isError(msgOrError) ? msgOrError : new Error(msgOrError);
+ throw e;
}
- grunt.verbose.ok();
- var xunitReport = buffer.toString("utf8");
- grunt.verbose.write("Replacing xunit testsuite name...");
- xunitReport = nicerName(xunitReport, sauceResult, testurl);
- grunt.verbose.ok();
+ if (!mochaResult)
+ throwError(fmt("Test %s is missing 'result' field in response:\n%s", id, JSON.stringify(sauceResult)));
+
+ var testurl = testPageUrl || mochaResult.url,
+ gzippedXml = mochaResult.xunit;
+ if (/experienced an error/.test(sauceResult.message))
+ throwError(sauceResult.message);
+ if (!testurl)
+ throwError(fmt("Test %s did not return its url. Ensure it is using sauce.js", id));
+ if (!gzippedXml)
+ throwError(fmt("Test %s did not return an xunit result. Ensure it is using sauce.js.", testurl));
+
+ grunt.verbose.write("Inflating compressed xunit result...");
+ var deferred = Q.defer();
+ var zipresolver = deferred.makeNodeResolver(); // will resolve or reject `deferred`
+ zlib.gunzip(new Buffer(gzippedXml, "base64"), zipresolver);
+ return deferred.promise.then(function(buffer) {
+ grunt.verbose.ok();
+ var xunitReport = buffer.toString("utf8");
+ grunt.verbose.write("Replacing xunit testsuite name...");
+ xunitReport = helpers.xunit_cleanup(xunitReport, sauceResult, testurl);
+ grunt.verbose.ok();
+
+ helpers.xunit_write(grunt, nodePath.join(results, filename), xunitReport);
+ testFilenames.push(filename);
+ callback(undefined, true /*job pass*/);
+ });
+ })
+ .catch(function(error) {
+ // Something broke the test suite; output a generic xunit showing that it error'd
+ grunt.warn(error && error.stack);
- grunt.file.write(nodePath.join(results, filename), xunitReport);
+ helpers.xunit_write(grunt, nodePath.join(results, filename), helpers.xunit_suite_error(filename, error));
testFilenames.push(filename);
- grunt.verbose.ok();
- callback(undefined, true /*job pass*/);
+ callback(error); /*job fail*/
});
}
diff --git a/releng/org.eclipse.orion.client.releng/test/package.json b/releng/org.eclipse.orion.client.releng/test/package.json
index be22595..4a65d80 100644
--- a/releng/org.eclipse.orion.client.releng/test/package.json
+++ b/releng/org.eclipse.orion.client.releng/test/package.json
@@ -20,7 +20,8 @@
"grunt-cli": "~0.1.13",
"grunt-contrib-connect": "~0.7.1",
"grunt-curl": "~1.5.1",
- "grunt-saucelabs": "~8.3.2"
+ "grunt-saucelabs": "~8.3.2",
+ "q": "~1.1.1"
},
"results": "./target/test-reports/",
"urls": [
diff --git a/releng/org.eclipse.orion.client.releng/test/runtests.js b/releng/org.eclipse.orion.client.releng/test/runtests.js
index 0ae3848..35789e2 100644
--- a/releng/org.eclipse.orion.client.releng/test/runtests.js
+++ b/releng/org.eclipse.orion.client.releng/test/runtests.js
@@ -8,7 +8,7 @@
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
-/*jslint node:true*/
+/*eslint-env node*/
/**
* Performs the programatic equivalent of running `grunt --force --verbose` from the command line.
* Need this for CF where running grunt from the command line has.. issues
@@ -23,7 +23,7 @@ try {
force: true,
stack: true,
verbose: true,
- "no-color": !!(process.env.VCAP_APPLICATION) // CF logs can't deal with color codes
+ "no-color": ("VCAP_APPLICATION" in process.env) // CF logs can't deal with color codes
});
} catch(e) {
// Uncaught exceptions are getting swallowed in CF env, need to log explicitly
diff --git a/releng/org.eclipse.orion.client.releng/test/test-helpers.js b/releng/org.eclipse.orion.client.releng/test/test-helpers.js
new file mode 100644
index 0000000..a811e9a
--- /dev/null
+++ b/releng/org.eclipse.orion.client.releng/test/test-helpers.js
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2014 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License v1.0
+ * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution
+ * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html).
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+/*eslint-env node*/
+var fmt = require("util").format;
+
+function sanitizeClassName(s) {
+ return s.replace(/[^A-Za-z0-9_\.]/g, "_");
+}
+
+function sanitizeXmlAttr(s) {
+ return s.replace(/['"&]/g, "_");
+}
+
+/**
+ * For Hudson to parse out nice packages instead of (root), we have to add classname="packageName.className"
+ * to the <testsuite> element, and prefix the "packageName." onto every <testcase>'s @classname. We also strip
+ * out some problematic characters from the original classnames: [#?.]
+ * @param {String} xml The xunit test result
+ * @returns {String} The test result, fixed up
+ */
+exports.xunit_cleanup = function(xml, sauceResult, testUrl) {
+ testUrl = testUrl.replace(/(^\/)|(\.html$)/g, "");
+ var platform = sanitizeClassName(sauceResult.platform.join(" ")),
+ packageName = sanitizeClassName(fmt("%s.%s", platform, testUrl));
+ return xml
+ .replace(/(<testsuite\s+name="[^"]+")/g, fmt("$1 classname=\"%s\"", packageName))
+ .replace(/<testcase classname="([^"]+)"/g, function(match, className) {
+ return fmt("<testcase classname=\"%s.%s\"", packageName, className.replace(/[#?.]/g, "_"));
+ });
+};
+
+/**
+ * Returns a barebones xunit test suite mentioning the test url and error. This is useful for giving *something*
+ * to the Hudson build that indicates a failure. Otherwise unexpected errors might not be shown at all in the build.
+ * @returns {String} An xunit
+ */
+exports.xunit_suite_error = function(testurl, error) {
+ var classname = sanitizeClassName(testurl),
+ errorMessage = sanitizeXmlAttr(error.message);
+ var xml = ''
+ + fmt('<testsuite name="%s" classname="%s" tests="1" failures="0" errors="1" skipped="0" timestamp="%s" time="0">', testurl, classname, (new Date()).toUTCString())
+ + fmt('<testcase classname="SuiteFailure" name="SuiteFailure" time="0" message="%s">', errorMessage)
+ + fmt('<failure classname="SuiteFailure" name="SuiteFailure" time="0" message="%s">', errorMessage)
+ + '<![CDATA['
+ + error.stack
+ + ']]>'
+ + '</failure>'
+ + '</testcase>'
+ + '</testsuite>';
+ return xml;
+};
+
+exports.xunit_write = function(grunt, filepath, contents) {
+ grunt.verbose.write(fmt("Writing result file %s", filepath));
+ grunt.file.write(filepath, contents);
+ grunt.verbose.ok();
+};
+