From ddd363ce386951272fa0b1e2d6936693f670a2a8 Mon Sep 17 00:00:00 2001 From: nightwing Date: Thu, 16 Feb 2017 16:53:58 +0000 Subject: [PATCH] fix test runner in the sdk --- .../test/framework.js | 2 +- .../test/hints_test.js | 1 - .../run-client-integration.phantom.js | 218 ++++++++++++++++++ test/lib/filefinder.js | 166 +++++++++++++ test/run-clientside-integration.js | 27 +++ 5 files changed, 412 insertions(+), 2 deletions(-) create mode 100644 test/client-integration/run-client-integration.phantom.js create mode 100644 test/lib/filefinder.js create mode 100644 test/run-clientside-integration.js diff --git a/plugins/c9.ide.language.javascript.infer/test/framework.js b/plugins/c9.ide.language.javascript.infer/test/framework.js index b59ac3f2..5bb020e9 100644 --- a/plugins/c9.ide.language.javascript.infer/test/framework.js +++ b/plugins/c9.ide.language.javascript.infer/test/framework.js @@ -70,7 +70,7 @@ function extractTypeAnnotations(code) { exports.buildTest = function(filename, exportSymbol) { return function(done) { - var code = fs.readFileSync(__dirname + "/" + filename, 'utf-8'); + var code = fs.readFileSync(__dirname + "/" + filename, 'utf-8').replace(/\r/g, ""); var builtins1 = fs.readFileSync(__dirname + "/../builtin.jst", 'utf-8'); var builtins2 = fs.readFileSync(__dirname + "/../builtin.custom.jst", 'utf-8'); var builtins3 = fs.readFileSync(__dirname + "/../builtin.nodejs.jst", 'utf-8'); diff --git a/plugins/c9.ide.language.javascript.infer/test/hints_test.js b/plugins/c9.ide.language.javascript.infer/test/hints_test.js index 0b8a1ccb..f945743f 100644 --- a/plugins/c9.ide.language.javascript.infer/test/hints_test.js +++ b/plugins/c9.ide.language.javascript.infer/test/hints_test.js @@ -6,6 +6,5 @@ require("c9/inline-mocha")(module); describe(__filename, function() { - setTimeout(4000); it("should analyze 'hints.js'", require('./framework').buildTest("hints.js")); }); \ No newline at end of file diff --git a/test/client-integration/run-client-integration.phantom.js b/test/client-integration/run-client-integration.phantom.js new file mode 100644 index 00000000..a8baafad --- /dev/null +++ b/test/client-integration/run-client-integration.phantom.js @@ -0,0 +1,218 @@ +"use strict"; + +/* global phantom */ + +var TEST_SUITE_URL = "http://localhost:8080/static/test.html?noui=true"; + +var page = require("webpage").create(); +var system = require("system"); +var pageErrors = []; +var hadError = 0; + +var address = system.args[1]; +var filter = system.args[2]; +var branch = system.args[3]; + +var logAll = false; +if (!address) console.log("# No address, running against: ", TEST_SUITE_URL); +if (filter) console.log("# Filter tests by ", filter); +if (!address) address = TEST_SUITE_URL; + +page.onConsoleMessage = function(msg) { + if (!/^PHANTOMJS:/.test(msg)) { + if (logAll) console.log(msg); + return; + } + + msg = msg.replace(/^PHANTOMJS: /, ""); + + if (/^exit/.test(msg)) { + showPageErrors(); + phantom.exit(hadError ? 1 : 0); + } + + if (/^screenshot/.test(msg)) { + var title = "out/cferror_" + /\w*$/m.exec(msg)[0] + ".png"; + return page.render(title); + } + + if (/^error #/.test(msg)) { + msg = linkifyErrorStack(msg); + } + + if (/^not ok/.test(msg)) { + hadError = true; + } + + console.log(msg); +}; + +function linkifyErrorStack(msg) { + function toGithubUrl(path) { + if (!branch) return path; + return path.replace(/\/?([^:]+):(\d+)/g, function(_, path, line) { + return "https://github.com/c9/newclient/blob/" + branch + "/" + path + "#L" + line; + }); + } + // change phantomjs stack to chrome format + msg = msg + .replace(/^(\s*)(?:([^@:\s]+)@|(https?:))/gm, "$1at $2 $3") + .replace(/^/gm, "\t\t").slice(1); + msg = msg.replace(/https?:\/\/[^/]+\/static(\/[^:]+[:\d]+)/g, function(_, path) { + if (/^\/plugins\//.test(path)) path = path; + else if (/^lib\/(events|path)/.test(path)) path = "plugins/c9.nodeapi" + path.slice(4); + else if (/^\/lib\/(ace|mocha)/.test(path)) path = "/node_modules" + path.slice(4); + else if (/^\/engine.io\/engine.io.js/.test(path)) path = "/node_modules" + path.replace(/engine.io/, "$&-client"); + else if (/^\/(vfs-socket|smith)\//.test(path)) path = "/node_modules" + path; + else if (/^\/(kaefer)\//.test(path)) path = "/node_modules" + path.replace("kaefer", "$&/lib"); + else return _; + + return toGithubUrl(path); + }); + + return msg; +} + +function showError(msg, trace) { + var msgStack = [msg]; + if (trace && trace.length) { + trace.forEach(function(t) { + msgStack.push((t.file || t.sourceURL) + ':' + t.line + (t.function ? ' (in function ' + t.function +')' : '')); + }); + } + console.log(linkifyErrorStack(msgStack.join('\n'))); +} + +page.onError = function(msg, trace) { + pageErrors.push({ + msg: msg, + trace: trace + }); + if (logAll) + showError(msg, trace); +}; + +function showPageErrors() { + var re = /--debug/; + if (system.args.some(re.test.bind(re))) + console.error(JSON.stringify(pageErrors, null, 2)); +} + +phantom.onError = function(msg, trace) { + showError(msg, trace); + phantom.exit(1); +}; + +page.open(address, function(status) { + if (status !== 'success') { + console.log('FAIL to load the address'); + phantom.exit(1); + } + + + page.evaluate(function(filterStr) { + var GLOBAL_TIMEOUT = 60 * 1000; + + function format(tpl) { + var values = Array.prototype.slice.call(arguments, 1); + return tpl.replace(/(%s)/g, values.shift.bind(values)); + } + + function takeScreenshot(name) { + log("screenshot %s", name.replace(/[^\w]/g, "_")); + } + + function log(msg) { + console.log("PHANTOMJS:", format.apply(null, arguments)); + } + + function each(things, runner, done) { + if (!things.length) return done(); + var thing = things.shift(); + + runner(thing, function(err) { + if (err) return done(err); + each(things, runner, done); + }); + } + + function filterTests(tests, filter) { + if (!filter) return tests; + var re = new RegExp(filter); + return tests.filter(re.test.bind(re)); + } + + function fail(count, test) { + log("not ok %s %s", count, test.title); + log("error # %s", test.error || "test.error is missing"); + } + + window.c9Test.onReady = function() { + log("# starting c9 tests"); + + var count = 0; + var failCount = 0; + var timeout = null; + + function runTest(name, next) { + var subtests = 0; + var failed = false; + + window.mocha._onSubTest = function(test) { + subtests++; + if (test.state == "failed") { + failed = true; + takeScreenshot(name + "_" + subtests); + } + }; + + watchForTimeout(name); + + window.c9Test.run(name, function(errors, out) { + log("# %s", name); + + out.tests.forEach(function(test, i) { + count++; + if (test.state == "passed") return log("ok %s %s", count, test.title); + if (test.state == "failed") return fail(count, test); + log("ok %s # SKIP %s", count, test.title); + }); + + if (failed) { + takeScreenshot(name + "_after"); + failCount++; + } + + done(); + }); + + function done() { + if (!timeout) return; // Don't call next twice, if timeout isn't set done has already been called + clearTimeout(timeout); + timeout = null; + next(); + } + + function watchForTimeout(name) { + timeout = window.setTimeout(function() { + count++; + fail(count, { + title: name, + error: "test file " + name + " timed out after " + (GLOBAL_TIMEOUT / 1000) + "s" + }); + failed = true; + done(); + }, GLOBAL_TIMEOUT); + } + } + + each(filterTests(window.c9Test.allFiles, filterStr), runTest, function() { + if (!count) log("not ok %s %s", count, "test found"); + console.log("%s..%s..%s", window.c9Test.allFiles.length, count, failCount); + log("exit"); + }); + + }; + }, filter); + +}); \ No newline at end of file diff --git a/test/lib/filefinder.js b/test/lib/filefinder.js new file mode 100644 index 00000000..9be51ab6 --- /dev/null +++ b/test/lib/filefinder.js @@ -0,0 +1,166 @@ +/* +filefinder - find files matching a specific file name pattern that are not found on a specified blacklist + +=example= + new filefinder("/tmp", "files", ".*_test.js", "/tmp/blacklist", function(err, res) { + console.log(util.inspect(res)); + }); + +--dirlist-- +one_test.js +two_test.js +three.js +four_test.js + +--blacklist-- +one_test.js +files/two_test.js +four + +--result-- +{ countOk: 2, + countPatternMis: 1, + countBl: 1, + list: [ 'files/four_test.js', 'files/one_test.js' ] } + +*/ + +var fs = require("fs"); +var util = require("util"); +var path = require("path"); + + +var DEBUGMODE = false; + + + +function filefinder(basedir, subdir, fnpattern, blacklistfile, cb) { + if (DEBUGMODE) console.log("\n\n\n\nfilefinder()"); + if (cb) cb(); +} + +filefinder.prototype.find = function(basedir, subdir, fnpattern, blacklistfile, cb) { + if (DEBUGMODE) console.log("\n\n\n\nfind(%s, %s, %s)", path.join(basedir, subdir), fnpattern, blacklistfile); + this.basedir = basedir; + this.subdir = subdir; + this.pattern = fnpattern; + this.blacklistfile = blacklistfile; + + this.flist = []; + this.countBlacklisted = 0; + this.countPatternMis = 0; + var _self = this; + + _self.getBlacklist(this.blacklistfile, function(err, blacklist) { + _self.treewalk(basedir, subdir, fnpattern, blacklist, _self.flist, function(err, res) { + // compose results object + var result = { + countOk: _self.flist.length, + countPatternMis: _self.countPatternMis, + countBl: _self.countBlacklisted, + list: _self.flist, + blacklist: blacklist + }; + cb(null, result); + }); + }); +}; + +filefinder.prototype.getBlacklist = function(filename, cb) { + if (DEBUGMODE) console.log("getBlacklist()"); + var _self = this; + _self.getFileInArray(filename, function(err, blacklist) { + if (err) return cb(new Error("error reading blacklist file")); + _self.arrayRemoveCrap(blacklist, function(err, blacklist) { + if (DEBUGMODE) console.log("--blacklist--\nlength: %s\n%s\n-------------", blacklist.length, util.inspect(blacklist)); + cb(null, blacklist); + }); + }); +}; + +// read a text file and make each line a member of an array +filefinder.prototype.getFileInArray = function(filename, cb) { + if (DEBUGMODE) console.log("getFileInArray()"); + var array = []; + fs.exists(filename, function (exists) { + if (!exists) return cb(null, array); + fs.readFile(filename, function(err, data) { + if(err) return cb(err); + array = data.toString().split("\n"); + cb(null, array); + }); + }); +}; + +filefinder.prototype.arrayRemoveCrap = function(array, cb) { + if (DEBUGMODE) console.log("arrayRemoveCrap()"); + + function removeComments(element, index, array) { + // clean-up whitespace, comments etc. + array[index] = array[index].replace(/\s*#.*|^\s*|\s*$/g, ''); + } + array.forEach(removeComments); + + array = array.filter(function(e) { + return e !== ""; + }); + + cb(null, array); +}; + + +filefinder.prototype.treewalk = function(basedir, subdir, fnpattern, blacklist, foundfilesarray, cb) { + var _self = this; + var fulldir = path.join(basedir, subdir); + if (DEBUGMODE) console.log(">treewalk (dir: %s, fnpattern: %s)", fulldir, fnpattern); + var results = []; + + fs.readdir(fulldir, function(err, list) { + if (err) return cb(err); + var i = 0; + + function next() { + var file = list[i++]; + if (!file) return cb(null, foundfilesarray.length); + var partName = path.join(subdir, file).replace(/\\/g, "/"); + var filepath = path.join(fulldir, file); + + // get file info + fs.stat(filepath, function(err, stat) { + if (stat && stat.isDirectory()) { + if (blacklist && blacklist.indexOf(partName + "/") !== -1) + return next(); + // directory, so recurse + _self.treewalk(basedir, partName, fnpattern, blacklist, foundfilesarray, function(err, res) { + results = results.concat(res); + next(); + }); + } + else { + // file found, matches pattern? + if (file.match(fnpattern) === null) { + _self.countPatternMis++; + return next(); + } + + // check if blacklisted + if (!blacklist || blacklist.indexOf(partName) == -1) { + if (DEBUGMODE) console.log("file found: %s", partName); + foundfilesarray.push(partName); + } + else { + _self.countBlacklisted++; + if (DEBUGMODE) console.log("File blacklisted: ", partName); + } + next(); + } + }); + } + next(); + }); +}; + +// export the class +module.exports = new filefinder(); + + diff --git a/test/run-clientside-integration.js b/test/run-clientside-integration.js new file mode 100644 index 00000000..115212a4 --- /dev/null +++ b/test/run-clientside-integration.js @@ -0,0 +1,27 @@ +"use strict"; + +var path = require("path"); +var spawn = require("child_process").spawn; +var phantomjs = require("phantomjs-prebuilt"); +var binPath = phantomjs.path; +var argv = require("optimist").usage("Usage: $0 ", { + "help": {alias: "h", description: "Display the usage", required: false}, + "branch": {description: "github branch for stacktrace", required: false}, + "filter": {description: "test pattern", required: false}, + "host": {description: "url of the test server", required: false}, +}).argv; + +var phantomRunner = path.join(__dirname, "client-integration/run-client-integration.phantom.js"); + +var args = [phantomRunner, argv.host || "", argv.filter || "", argv.branch || ""]; + +console.log("executing: %s %s", binPath, args.join(" ")); + +var child = spawn(binPath, args); + + +child.stdout.pipe(process.stdout); +child.stderr.pipe(process.stderr); +child.on("exit", function(code) { + process.exit(code); +});