/*jshint strict: false, sub: true */ //////////////////////////////////////////////////////////////////////////////// /// @brief General unittest framework /// /// @file /// /// DISCLAIMER /// /// Copyright 2014 triagens GmbH, Cologne, Germany /// /// 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. /// /// Copyright holder is triAGENS GmbH, Cologne, Germany /// /// @author Max Neunhoeffer /// @author Copyright 2014, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief framework to perform unittests /// /// This function gets one or two arguments, the first describes which tests /// to perform and the second is an options object. For `which` the following /// values are allowed: /// Empty will give you a complete list. //////////////////////////////////////////////////////////////////////////////// var functionsDocumentation = { 'all' : " do all tests (marked with [x])", "shell_server_perf" : "bulk tests intended to get an overview of executiontime needed.", "single_client" : "run one test suite isolated via the arangosh; options required\n" + " Run without to get more detail", "single_server" : "run one test suite on the server; options required\n" + " Run without to get more detail", "single_localserver": "run on this very instance of arangod, don't fork a new one.\n" }; var optionsDocumentation = [ '', ' The following properties of `options` are defined:', '', ' - `jsonReply`: if set a json is returned which the caller has to ', ' present the user', ' - `force`: if set to true the tests are continued even if one fails', ' - `skipBoost`: if set to true the boost unittests are skipped', ' - `skipGeo`: if set to true the geo index tests are skipped', ' - `skipGraph`: if set to true the graph tests are skipped', ' - `skipAql`: if set to true the AQL tests are skipped', ' - `skipArangoB`: if set to true benchmark tests are skipped', ' - `skipArangoBNonConnKeepAlive`: if set to true benchmark tests are skipped', ' - `skipRanges`: if set to true the ranges tests are skipped', ' - `skipTimeCritical`: if set to true, time critical tests will be skipped.', ' - `skipMemoryIntense`: tests using lots of resources will be skipped.', ' - `skipAuth : testing authentication will be skipped.', ' - `skipSsl`: omit the ssl_server rspec tests.', ' - `skipLogAnalysis`: don\'t try to crawl the server logs', ' - `skipConfig`: omit the noisy configuration tests', ' - `skipFoxxQueues`: omit the test for the foxx queues', ' - `skipNightly`: omit the nightly tests', ' - `onlyNightly`: execute only the nightly tests', '', ' - `cluster`: if set to true the tests are run with the coordinator', ' of a small local cluster', ' - `clusterNodes`: number of DB-Servers to use', ' - valgrindHosts - configure which clustercomponents to run using valgrintd', ' Coordinator - flag to run Coordinator with valgrind', ' DBServer - flag to run DBServers with valgrind', ' - `test`: path to single test to execute for "single" test target', ' - `cleanup`: if set to true (the default), the cluster data files', ' and logs are removed after termination of the test.', ' - `jasmineReportFormat`: this option is passed on to the `format`', ' option of the Jasmine options object, only for Jasmine tests.', '', ' - benchargs : additional commandline arguments to arangob', '', ' - `valgrind`: if set to true the arangods are run with the valgrind', ' memory checker', ' - `valgrindXmlFileBase`: string to prepend to the xml report name', ' - `valgrindargs`: list of commandline parameters to add to valgrind', '', ' - `extraargs`: list of extra commandline arguments to add to arangod', ' - `extremeVerbosity`: if set to true, then there will be more test run', ' output, especially for cluster tests.', ' - `portOffset`: move our base port by n ports up', '' ]; var _ = require("underscore"); var cleanupDirectories = []; var testFuncs = {'all': function(){}}; var print = require("internal").print; var time = require("internal").time; var fs = require("fs"); var download = require("internal").download; var wait = require("internal").wait; var executeExternal = require("internal").executeExternal; var killExternal = require("internal").killExternal; var statusExternal = require("internal").statusExternal; var executeExternalAndWait = require("internal").executeExternalAndWait; var base64Encode = require("internal").base64Encode; var yaml = require("js-yaml"); var PortFinder = require("@arangodb/cluster").PortFinder; var Planner = require("@arangodb/cluster").Planner; var Kickstarter = require("@arangodb/cluster").Kickstarter; var endpointToURL = require("@arangodb/cluster/planner").endpointToURL; var toArgv = require("internal").toArgv; var serverCrashed = false; var optionsDefaults = { "cluster": false, "valgrind": false, "force": true, "skipBoost": false, "skipGeo": false, "skipTimeCritical": false, "skipNightly": true, "onlyNightly": false, "skipMemoryIntense": false, "skipAql": false, "skipArangoB": false, "skipArangoBNonConnKeepAlive": false, "skipRanges": false, "skipLogAnalysis": false, "username": "root", "password": "", "test": undefined, "cleanup": true, "jsonReply": false, "portOffset": 0, "valgrindargs": [], "valgrindXmlFileBase" : "", "extraargs": [], "coreDirectory": "/var/tmp", "writeXmlReport": true, "extremeVerbosity": false }; var allTests = [ "config", "boost", "shell_server", "shell_server_aql", "http_server", "ssl_server", "shell_client", "dump", "arangob", "arangosh", "importing", "upgrade", "authentication", "authentication_parameters" ]; function printUsage () { print(); print("Usage: UnitTest( which, options )"); print(); print(' where "which" is one of:\n'); var i; var checkAll; var oneFunctionDocumentation; for (i in testFuncs) { if (testFuncs.hasOwnProperty(i)) { if (functionsDocumentation.hasOwnProperty(i)) { oneFunctionDocumentation = ' - ' + functionsDocumentation[i]; } else { oneFunctionDocumentation = ''; } if (allTests.indexOf(i) !== -1) { checkAll = '[x]'; } else { checkAll = ' '; } print(' ' + checkAll + ' '+ i + ' ' + oneFunctionDocumentation); } } for (i in optionsDocumentation) { if (optionsDocumentation.hasOwnProperty(i)) { print(optionsDocumentation[i]); } } } function filterTestcaseByOptions (testname, options, whichFilter) { if (options.hasOwnProperty('test') && (typeof(options.test) !== 'undefined')) { whichFilter.filter = "testcase"; return testname === options.test; } if ((testname.indexOf("-cluster") !== -1) && !options.cluster) { whichFilter.filter = 'noncluster'; return false; } if (testname.indexOf("-noncluster") !== -1 && options.cluster) { whichFilter.filter = 'cluster'; return false; } if (testname.indexOf("-timecritical") !== -1 && options.skipTimeCritical) { whichFilter.filter = 'timecritical'; return false; } if (testname.indexOf("-nightly") !== -1 && options.skipNightly && ! options.onlyNightly) { whichFilter.filter = 'skip nightly'; return false; } if (testname.indexOf("-geo") !== -1 && options.skipGeo) { whichFilter.filter = 'geo'; return false; } if (testname.indexOf("-graph") !== -1 && options.skipGraph) { whichFilter.filter = 'graph'; return false; } if (testname.indexOf("-disabled") !== -1) { whichFilter.filter = 'disabled'; return false; } if (testname.indexOf("replication") !== -1) { whichFilter.filter = 'replication'; return false; } if ((testname.indexOf("-memoryintense") !== -1) && options.skipMemoryIntense) { whichFilter.filter = 'memoryintense'; return false; } if (testname.indexOf("-nightly") === -1 && options.onlyNightly) { whichFilter.filter = 'only nightly'; return false; } if ((testname.indexOf("-novalgrind") !== -1) && (typeof(options.valgrind) === 'string')) { whichFilter.filter = "skip in valgrind"; return false; } return true; } function findTopDir () { var topDir = fs.normalize(fs.makeAbsolute(".")); if (! fs.exists("3rdParty") && ! fs.exists("arangod") && ! fs.exists("arangosh") && ! fs.exists("UnitTests")) { throw "Must be in ArangoDB topdir to execute unit tests."; } return topDir; } function makeTestingArgs (appDir) { var topDir = findTopDir(); fs.makeDirectoryRecursive(appDir, true); return { "configuration": "none", "server.keyfile": fs.join(topDir, "UnitTests", "server.pem"), "database.maximal-journal-size": "1048576", "database.force-sync-properties": "false", "javascript.app-path": appDir, "javascript.startup-directory": fs.join(topDir, "js"), "server.threads": "20", "javascript.v8-contexts": "5", "server.disable-authentication": "true", "server.allow-use-database": "true" }; } function makeTestingArgsClient (options) { var topDir = findTopDir(); return { "configuration": "none", "javascript.startup-directory": fs.join(topDir, "js"), "server.username": options.username, "server.password": options.password, "flatCommands": ["--no-colors", "--quiet"] }; } function makeAuthorizationHeaders (options) { return {"headers": {"Authorization": "Basic " + base64Encode(options.username+":"+ options.password)}}; } function startInstance (protocol, options, addArgs, testname, tmpDir) { // protocol must be one of ["tcp", "ssl", "unix"] var startTime = time(); var topDir = findTopDir(); var instanceInfo = {}; instanceInfo.topDir = topDir; var tmpDataDir = tmpDir || fs.getTempFile(); var appDir; instanceInfo.flatTmpDataDir = tmpDataDir; tmpDataDir = fs.join(tmpDataDir, testname); appDir = fs.join(tmpDataDir, "apps"); fs.makeDirectoryRecursive(tmpDataDir); instanceInfo.tmpDataDir = tmpDataDir; var optionsExtraArgs = {}; if (typeof(options.extraargs) === 'object') { optionsExtraArgs = options.extraargs; } var endpoint; var pos; var valgrindopts = {}; if (typeof(options.valgrindargs) === 'object') { valgrindopts = options.valgrindargs; } var valgrindHosts = ''; if (typeof(options.valgrindHosts) === 'object') { if (options.valgrindHosts.Coordinator === true) { valgrindHosts += 'Coordinator'; } if (options.valgrindHosts.DBServer === true) { valgrindHosts += 'DBserver'; } } var dispatcher; if (options.cluster) { var clusterNodes = 2; if (options.clusterNodes) { clusterNodes = options.clusterNodes; } var extraargs = makeTestingArgs(appDir); extraargs = _.extend(extraargs, optionsExtraArgs); if (addArgs !== undefined) { extraargs = _.extend(extraargs, addArgs); } dispatcher = {"endpoint":"tcp://127.0.0.1:", "arangodExtraArgs": toArgv(extraargs), "username": "root", "password": ""}; print("Temporary cluster data and logs are in", tmpDataDir); var runInValgrind = ""; var valgrindXmlFileBase = ""; if (typeof(options.valgrind) === 'string') { runInValgrind = options.valgrind; valgrindXmlFileBase = options.valgrindXmlFileBase; } var p = new Planner({"numberOfDBservers" : clusterNodes, "numberOfCoordinators" : 1, "dispatchers" : {"me": dispatcher}, "dataPath" : tmpDataDir, "logPath" : tmpDataDir, "useSSLonCoordinators" : protocol === "ssl", "valgrind" : runInValgrind, "valgrindopts" : toArgv(valgrindopts, true), "valgrindXmlFileBase" : valgrindXmlFileBase + '_cluster', "valgrindTestname" : testname, "valgrindHosts" : valgrindHosts, "extremeVerbosity" : options.extremeVerbosity }); instanceInfo.kickstarter = new Kickstarter(p.getPlan()); var rc = instanceInfo.kickstarter.launch(); if (rc.error) { print("Cluster startup failed: " + rc.errorMessage); return false; } var runInfo = instanceInfo.kickstarter.runInfo; var j = runInfo.length - 1; while (j > 0 && runInfo[j].isStartServers === undefined) { j--; } if ((runInfo.length === 0) || (runInfo[0].error === true)) { var error = new Error(); error.errorMessage = yaml.safeDump(runInfo); throw error; } var roles = runInfo[j].roles; var endpoints = runInfo[j].endpoints; pos = roles.indexOf("Coordinator"); endpoint = endpoints[pos]; } else { // single instance mode // We use the PortFinder to find a free port for our subinstance, // to this end, we have to fake a dummy dispatcher: dispatcher = {endpoint: "tcp://127.0.0.1:", avoidPorts: {}, id: "me"}; var pf = new PortFinder([8529 + options.portOffset],dispatcher); var port = pf.next(); instanceInfo.port = port; endpoint = protocol+"://127.0.0.1:"+port; var args = makeTestingArgs(appDir); args["server.endpoint"] = endpoint; args["database.directory"] = fs.join(tmpDataDir,"data"); fs.makeDirectoryRecursive(fs.join(tmpDataDir,"data")); args["log.file"] = fs.join(tmpDataDir,"log"); if (protocol === "ssl") { args["server.keyfile"] = fs.join("UnitTests","server.pem"); } args = _.extend(args, optionsExtraArgs); if (addArgs !== undefined) { args = _.extend(args, addArgs); } if (typeof(options.valgrind) === 'string') { var run = fs.join("bin","arangod"); var testfn = options.valgrindXmlFileBase; if (testfn.length > 0 ) { testfn += '_' ; } testfn += testname; valgrindopts["xml-file"] = testfn + '.%p.xml'; valgrindopts["log-file"] = testfn + '.%p.valgrind.log'; // Sequence: Valgrind arguments; binary to run; options to binary: var newargs=toArgv(valgrindopts, true).concat([run]).concat(toArgv(args)); var cmdline = options.valgrind; instanceInfo.pid = executeExternal(cmdline, newargs); } else { instanceInfo.pid = executeExternal(fs.join("bin","arangod"), toArgv(args)); } } // Wait until the server/coordinator is up: var count = 0; var url = endpointToURL(endpoint); instanceInfo.url = url; instanceInfo.endpoint = endpoint; while (true) { wait(0.5, false); var r = download(url+"/_api/version", "", makeAuthorizationHeaders(options)); if (! r.error && r.code === 200) { break; } count ++; if (count % 60 === 0) { if (! checkInstanceAlive(instanceInfo, options)) { print("startup failed! bailing out!"); return false; } } } print("up and running in " + (time () - startTime) + " seconds"); if (!options.cluster && (require("internal").platform.substr(0,3) === 'win')) { var procdumpArgs = [ '-accepteula', '-e', '-ma', instanceInfo.pid.pid, fs.join(tmpDataDir, 'core.dmp') ]; try { instanceInfo.monitor = executeExternal('procdump', procdumpArgs); } catch (x) { print("failed to start procdump - is it installed?"); throw x; } } return instanceInfo; } function readImportantLogLines(logPath) { var i, j; var importantLines = {}; var list=fs.list(logPath); var jasmineTest = fs.join("jasmine", "core"); for (i = 0; i < list.length; i++) { var fnLines = []; if (list[i].slice(0,3) === 'log') { var buf = fs.readBuffer(fs.join(logPath,list[i])); var lineStart = 0; var maxBuffer = buf.length; for (j = 0; j < maxBuffer; j++) { if (buf[j] === 10) { // \n var line = buf.asciiSlice(lineStart, j); // filter out regular INFO lines, and test related messages if ((line.search(" INFO ") < 0) && (line.search("WARNING about to execute:") < 0) && (line.search(jasmineTest) < 0)) { fnLines.push(line); } lineStart = j + 1; } } } if (fnLines.length > 0) { importantLines[list[i]] = fnLines; } } return importantLines; } function analyzeCoreDump(instanceInfo, options, storeArangodPath, pid) { var command; command = '('; command += "printf 'bt full\\n thread apply all bt\\n';"; command += "sleep 10;"; command += "echo quit;"; command += "sleep 2"; command += ") | gdb " + storeArangodPath + " " + options.coreDirectory + "/*core*" + pid + '*'; var args = ['-c', command]; print(JSON.stringify(args)); executeExternalAndWait("/bin/bash", args); } function analyzeCoreDumpWindows(instanceInfo) { var coreFN = instanceInfo.tmpDataDir + "\\" + "core.dmp"; if (!fs.exists(coreFN)) { print("core file "+ coreFN + " not found?"); return; } var dbgCmds = [ "kp", // print backtrace with arguments "dv", // analyze local variables (if) "!analyze -v", // print verbose analysis "q" //quit the debugger ]; var args = [ "-z", coreFN, "-c", dbgCmds.join("; ") ]; print("running cdb " + JSON.stringify(args)); executeExternalAndWait("cdb", args); } function checkInstanceAlive(instanceInfo, options) { var storeArangodPath; if (options.cluster === false) { if (instanceInfo.hasOwnProperty('exitStatus')) { return false; } var res = statusExternal(instanceInfo.pid, false); var ret = res.status === "RUNNING"; if (! ret) { print("ArangoD with PID " + instanceInfo.pid.pid + " gone:"); print(instanceInfo); if (res.hasOwnProperty('signal') && ((res.signal === 11) || (res.signal === 6) || // Windows sometimes has random numbers in signal... (require("internal").platform.substr(0,3) === 'win') ) ) { storeArangodPath = "/var/tmp/arangod_" + instanceInfo.pid.pid; print("Core dump written; copying arangod to " + instanceInfo.tmpDataDir + " for later analysis."); res.gdbHint = "Run debugger with 'gdb " + storeArangodPath + " " + options.coreDirectory + "/core*" + instanceInfo.pid.pid + "*'"; if (require("internal").platform.substr(0,3) === 'win') { // Windows: wait for procdump to do its job... statusExternal(instanceInfo.monitor, true); analyzeCoreDumpWindows(instanceInfo); } else { fs.copyFile("bin/arangod", storeArangodPath); analyzeCoreDump(instanceInfo, options, storeArangodPath, instanceInfo.pid.pid); } } instanceInfo.exitStatus = res; } if (!ret) { print("marking crashy"); serverCrashed = true; } return ret; } var ClusterFit = true; for (var part in instanceInfo.kickstarter.runInfo) { if (instanceInfo.kickstarter.runInfo[part].hasOwnProperty("pids")) { for (var pid in instanceInfo.kickstarter.runInfo[part].pids) { if (instanceInfo.kickstarter.runInfo[part].pids.hasOwnProperty(pid)) { var checkpid = instanceInfo.kickstarter.runInfo[part].pids[pid]; var ress = statusExternal(checkpid, false); if (ress.hasOwnProperty('signal') && ((ress.signal === 11) || (ress.signal === 6))) { storeArangodPath = "/var/tmp/arangod_" + checkpid.pid; print("Core dump written; copying arangod to " + storeArangodPath + " for later analysis."); ress.gdbHint = "Run debugger with 'gdb " + storeArangodPath + " /var/tmp/core*" + checkpid.pid + "*'"; if (require("internal").platform.substr(0,3) === 'win') { // Windows: wait for procdump to do its job... statusExternal(instanceInfo.monitor, true); analyzeCoreDumpWindows(instanceInfo); } else { fs.copyFile("bin/arangod", storeArangodPath); analyzeCoreDump(instanceInfo, options, storeArangodPath, checkpid.pid); } instanceInfo.exitStatus = ress; ClusterFit = false; } } } } } if (ClusterFit && instanceInfo.kickstarter.isHealthy()) { return true; } else { print("marking crashy"); serverCrashed = true; return false; } } function waitOnServerForGC(instanceInfo, options, waitTime) { try { print("waiting " + waitTime + " for server GC"); var r; var t; t = 'require("internal").wait(' + waitTime + ', true);'; var o = makeAuthorizationHeaders(options); o.method = "POST"; o.timeout = waitTime * 10; o.returnBodyOnError = true; r = download(instanceInfo.url + "/_admin/execute?returnAsJSON=true",t,o); print("waiting " + waitTime + " for server GC - done."); if (! r.error && r.code === 200) { r = JSON.parse(r.body); } else { return { status: false, message: r.body }; } return r; } catch (e) { return { status: false, message: e.message || String(e), stack: e.stack }; } } function shutdownInstance (instanceInfo, options) { if (!checkInstanceAlive(instanceInfo, options)) { print("Server already dead, doing nothing. This shouldn't happen?"); } if (options.valgrind) { waitOnServerForGC(instanceInfo, options, 60); } if (options.cluster) { var rc = instanceInfo.kickstarter.shutdown(); if (!options.skipLogAnalysis) { instanceInfo.importantLogLines = readImportantLogLines(instanceInfo.tmpDataDir); } if (options.cleanup) { instanceInfo.kickstarter.cleanup(); } if (rc.error) { for (var i = 0; i < rc.results.length; i++ ) { if (rc.results[i].hasOwnProperty('isStartServers') && (rc.results[i].isStartServers === true)) { for (var serverState in rc.results[i].serverStates) { if (rc.results[i].serverStates.hasOwnProperty(serverState)){ if ((rc.results[i].serverStates[serverState].status === "NOT-FOUND") || (rc.results[i].serverStates[serverState].hasOwnProperty('signal'))) { print("Server " + serverState + " shut down with:\n" + yaml.safeDump(rc.results[i].serverStates[serverState]) + " marking run as crashy."); print("marking crashy"); serverCrashed = true; } } } } } } } else { if (typeof(instanceInfo.exitStatus) === 'undefined') { download(instanceInfo.url+"/_admin/shutdown","", makeAuthorizationHeaders(options)); print("Waiting for server shut down"); var count = 0; var bar = "["; while (1) { instanceInfo.exitStatus = statusExternal(instanceInfo.pid, false); if (instanceInfo.exitStatus.status === "RUNNING") { count ++; if (typeof(options.valgrind) === 'string') { wait(1); continue; } if (count % 10 ===0) { bar = bar + "#"; } if (count > 600) { print("forcefully terminating " + yaml.safeDump(instanceInfo.pid) + " after 600 s grace period; marking crashy."); serverCrashed = true; killExternal(instanceInfo.pid); break; } else { wait(1); } } else if (instanceInfo.exitStatus.status !== "TERMINATED") { if (instanceInfo.exitStatus.hasOwnProperty('signal')) { print("Server shut down with : " + yaml.safeDump(instanceInfo.exitStatus) + " marking build as crashy."); serverCrashed = true; break; } if (require("internal").platform.substr(0,3) === 'win') { // Windows: wait for procdump to do its job... statusExternal(instanceInfo.monitor, true); } } else { print("Server shutdown: Success."); break; // Success. } } if (count > 10) { print("long Server shutdown: " + bar + ']'); } } else { print("Server already dead, doing nothing."); } if (!options.skipLogAnalysis) { instanceInfo.importantLogLines = readImportantLogLines(instanceInfo.tmpDataDir); } } cleanupDirectories = cleanupDirectories.concat(instanceInfo.tmpDataDir, instanceInfo.flatTmpDataDir); } function checkBodyForJsonToParse(request) { if (request.hasOwnProperty('body')) { var parsedBody = JSON.parse(request.hasOwnProperty('body')); request.body = parsedBody; } } function cleanupDBDirectories(options) { if (options.cleanup) { while (cleanupDirectories.length) { var cleanupDirectory = cleanupDirectories.shift(); // Avoid attempting to remove the same directory multiple times if (cleanupDirectories.indexOf(cleanupDirectory) === -1) { fs.removeDirectoryRecursive(cleanupDirectory, true); } } } } function makePathUnix (path) { return fs.join.apply(null,path.split("/")); } function makePathGeneric (path) { return fs.join.apply(null,path.split(fs.pathSeparator)); } var foundTests = false; var tests_shell_common; var tests_shell_server_only; var tests_shell_client_only; var tests_shell_server; var tests_shell_client; var tests_shell_server_aql; var tests_shell_server_aql_extended; var tests_shell_server_aql_performance; function findTests () { if (foundTests) { return; } tests_shell_common = _.filter(fs.list(makePathUnix("js/common/tests")), function (p) { return p.substr(0,6) === "shell-" && p.substr(-3) === ".js"; }).map( function(x) { return fs.join(makePathUnix("js/common/tests"),x); }).sort(); tests_shell_server_only = _.filter(fs.list(makePathUnix("js/server/tests")), function (p) { return p.substr(0,6) === "shell-" && p.substr(-3) === ".js"; }).map( function(x) { return fs.join(makePathUnix("js/server/tests"),x); }).sort(); tests_shell_client_only = _.filter(fs.list(makePathUnix("js/client/tests")), function (p) { return p.substr(0,6) === "shell-" && p.substr(-3) === ".js"; }).map( function(x) { return fs.join(makePathUnix("js/client/tests"),x); }).sort(); tests_shell_server_aql = _.filter(fs.list(makePathUnix("js/server/tests")), function (p) { return p.substr(0,4) === "aql-" && p.substr(-3) === ".js" && p.indexOf("ranges-combined") === -1; }).map( function(x) { return fs.join(makePathUnix("js/server/tests"),x); }).sort(); tests_shell_server_aql_extended = _.filter(fs.list(makePathUnix("js/server/tests")), function (p) { return p.substr(0,4) === "aql-" && p.substr(-3) === ".js" && p.indexOf("ranges-combined") !== -1; }).map( function(x) { return fs.join(makePathUnix("js/server/tests"),x); }).sort(); tests_shell_server_aql_performance = _.filter(fs.list(makePathUnix("js/server/perftests")), function (p) { return p.substr(-3) === ".js"; }).map( function(x) { return fs.join(makePathUnix("js/server/perftests"),x); }).sort(); tests_shell_server = tests_shell_common.concat(tests_shell_server_only); tests_shell_client = tests_shell_common.concat(tests_shell_client_only); foundTests = true; } function runThere (options, instanceInfo, file) { try { var r; var t; if (file.indexOf("-spec") === -1) { t = 'var runTest = require("jsunity").runTest; '+ 'return runTest(' + JSON.stringify(file) + ', true);'; } else { var jasmineReportFormat = options.jasmineReportFormat || 'progress'; t = 'var executeTestSuite = require("jasmine").executeTestSuite; '+ 'try {' + 'return { status: executeTestSuite([' + JSON.stringify(file) + '], {"format": '+ JSON.stringify(jasmineReportFormat) + '}), message: "Success"};'+ ' } catch (e) {' + 'return {' + ' status: false,' + ' message: e.message || String(e) || "unknown",' + ' stack: e.stack' + '};' + '}'; } var o = makeAuthorizationHeaders(options); o.method = "POST"; o.timeout = 3600; o.returnBodyOnError = true; r = download(instanceInfo.url + "/_admin/execute?returnAsJSON=true",t,o); if (! r.error && r.code === 200) { r = JSON.parse(r.body); } else { return { status: false, message: r.body }; } return r; } catch (e) { return { status: false, message: e.message || String(e), stack: e.stack }; } } function runHere (options, instanceInfo, file) { var result; try { if (file.indexOf("-spec") === -1) { var runTest = require("jsunity").runTest; result = runTest(file, true); } else { var jasmineReportFormat = options.jasmineReportFormat || 'progress'; var executeTestSuite = require("jasmine").executeTestSuite; try { result = executeTestSuite([ file ], { format: jasmineReportFormat }); } catch (e) { result = { status: false, message: e.message || String(e) || "unknown", stack: e.stack }; return result; } } if (file.indexOf("-spec") !== -1) { result = { status: result, message: '' }; } } catch (err) { result = err; } return result; } function executeAndWait (cmd, args) { var startTime = time(); var res = executeExternalAndWait(cmd, args); var deltaTime = time() - startTime; var errorMessage = ' - '; if (res.status === "TERMINATED") { print("Finished: " + res.status + " exit code: " + res.exit + " Time elapsed: " + deltaTime); if (res.exit === 0) { return { status: true, message: "", duration: deltaTime}; } else { return { status: false, message: "exit code was " + res.exit, duration: deltaTime}; } } else if (res.status === "ABORTED") { if (typeof(res.errorMessage) !== 'undefined') { errorMessage += res.errorMessage; } print("Finished: " + res.status + " Signal: " + res.signal + " Time elapsed: " + deltaTime + errorMessage); return { status: false, message: "irregular termination: " + res.status + " exit signal: " + res.signal + errorMessage, duration: deltaTime }; } else { if (typeof(res.errorMessage) !== 'undefined') { errorMessage += res.errorMessage; } print("Finished: " + res.status + " exit code: " + res.signal + " Time elapsed: " + deltaTime + errorMessage); return { status: false, message: "irregular termination: " + res.status + " exit code: " + res.exit + errorMessage, duration: deltaTime }; } } function runInArangosh (options, instanceInfo, file, addArgs) { var args = makeTestingArgsClient(options); var topDir = findTopDir(); args["server.endpoint"] = instanceInfo.endpoint; args["javascript.unit-tests"] = fs.join(topDir, file); if (addArgs !== undefined) { args = _.extend(args, addArgs); } var arangosh = fs.join("bin","arangosh"); var result; var rc = executeAndWait(arangosh, toArgv(args)); try { result = JSON.parse(fs.read("testresult.json")); } catch(x) { return rc; } if ((typeof(result[0]) === 'object') && result[0].hasOwnProperty('status')) { return result[0]; } else { // Jasmine tests... return rc; } } function runArangoshCmd (options, instanceInfo, addArgs, cmds) { var args = makeTestingArgsClient(options); args["server.endpoint"] = instanceInfo.endpoint; if (addArgs !== undefined) { args = _.extend(args, addArgs); } var argv = toArgv(args).concat(cmds); var arangosh = fs.join("bin", "arangosh"); return executeAndWait(arangosh, argv); } function performTests(options, testList, testname, remote) { var instanceInfo; if (remote) { instanceInfo = startInstance("tcp", options, [], testname); if (instanceInfo === false) { return {status: false, message: "failed to start server!"}; } } var results = {}; var i; var te; var continueTesting = true; var filtered = {}; if (testList.length === 0) { print("Testsuite is empty!"); return {"EMPTY TESTSUITE": {status: false, message: "no testsuites found!"}}; } for (i = 0; i < testList.length; i++) { te = testList[i]; if (filterTestcaseByOptions(te, options, filtered)) { if (! continueTesting) { print('oops!'); print("Skipping, " + te + " server is gone."); results[te] = {status: false, message: instanceInfo.exitStatus}; instanceInfo.exitStatus = "server is gone."; break; } print("\n" + Date() + " arangod: Trying",te,"..."); var r; if (remote) { r = runThere(options, instanceInfo, te); } else { r = runHere(options, instanceInfo, te); } if (r.hasOwnProperty('status')) { results[te] = r; if (results[te].status === false) { options.cleanup = false; } if (! r.status && ! options.force) { break; } } else { results[te] = {status: false, message: r}; if (! options.force) { break; } } if (remote) { continueTesting = checkInstanceAlive(instanceInfo, options); } } else { if (options.extremeVerbosity) { print("Skipped " + te + " because of " + filtered.filter); } } } if (remote) { print("Shutting down..."); shutdownInstance(instanceInfo,options); } print("done."); if ((!options.skipLogAnalysis) && instanceInfo.hasOwnProperty('importantLogLines') && Object.keys(instanceInfo.importantLogLines).length > 0) { print("Found messages in the server logs: \n" + yaml.safeDump(instanceInfo.importantLogLines)); } return results; } function single_usage (testsuite, list) { print("single_" + testsuite + ": No test specified!\n Available tests:"); var filelist = ""; for (var fileNo in list) { if (/\.js$/.test(list[fileNo])) { filelist += " " + list[fileNo]; } } print(filelist); print("usage: single_" + testsuite + " '{\"test\":\"\"}'"); print(" where is one from the list above."); return { status: false, message: "No test specified!"}; } testFuncs.single_server = function (options) { var result = { }; options.writeXmlReport = false; if (options.test !== undefined) { var instanceInfo = startInstance("tcp", options, [], "single_server"); if (instanceInfo === false) { return {status: false, message: "failed to start server!"}; } var te = options.test; print("\n" + Date() + " arangod: Trying",te,"..."); result = {}; var r = runThere(options, instanceInfo, makePathGeneric(te)); if (r.hasOwnProperty('status')) { result[te] = r; if (result[te].status === false) { options.cleanup = false; } } print("Shutting down..."); if (result[te].status === false) { options.cleanup = false; } shutdownInstance(instanceInfo,options); print("done."); if ((!options.skipLogAnalysis) && instanceInfo.hasOwnProperty('importantLogLines') && Object.keys(instanceInfo.importantLogLines).length > 0) { print("Found messages in the server logs: \n" + yaml.safeDump(instanceInfo.importantLogLines)); } return result; } else { findTests(); return single_usage("server", tests_shell_server); } }; testFuncs.single_localserver = function (options) { var result = { }; options.writeXmlReport = false; if (options.test !== undefined) { var instanceInfo; var te = options.test; print("\nArangod: Trying",te,"..."); result = {}; result[te] = runHere(options, instanceInfo, makePathGeneric(te)); if (result[te].status === false) { options.cleanup = false; } return result; } else { findTests(); return single_usage("localserver", tests_shell_server); } }; testFuncs.single_client = function (options) { var result = { }; options.writeXmlReport = false; if (options.test !== undefined) { var instanceInfo = startInstance("tcp", options, [], "single_client"); if (instanceInfo === false) { return {status: false, message: "failed to start server!"}; } var te = options.test; print("\n" + Date() + " arangosh: Trying ",te,"..."); result[te] = runInArangosh(options, instanceInfo, te); print("Shutting down..."); if (result[te].status === false) { options.cleanup = false; } shutdownInstance(instanceInfo,options); print("done."); if ((!options.skipLogAnalysis) && instanceInfo.hasOwnProperty('importantLogLines') && Object.keys(instanceInfo.importantLogLines).length > 0) { print("Found messages in the server logs: \n" + yaml.safeDump(instanceInfo.importantLogLines)); } return result; } else { findTests(); return single_usage("client", tests_shell_client); } }; testFuncs.shell_server_perf = function(options) { findTests(); return performTests(options, tests_shell_server_aql_performance, 'shell_server_perf', true); }; testFuncs.shell_server = function (options) { findTests(); return performTests(options, tests_shell_server, 'shell_server', true); }; testFuncs.shell_server_only = function (options) { findTests(); return performTests(options, tests_shell_server_only, 'shell_server_only', true); }; testFuncs.shell_server_aql = function(options) { findTests(); if (! options.skipAql) { if (options.skipRanges) { return performTests(options, tests_shell_server_aql, 'shell_server_aql_skipranges', true); } else { return performTests(options, tests_shell_server_aql.concat( tests_shell_server_aql_extended), 'shell_server_aql', true); } } return "skipped"; }; testFuncs.shell_server_aql_local = function(options) { if (!options.hasOwnProperty('cluster')) { print('need to specify whether this is a coordinator or not! Add "Cluster":true/false to the options.'); return; } findTests(); if (! options.skipAql) { if (options.skipRanges) { unitTestPrettyPrintResults( performTests(options, tests_shell_server_aql, 'shell_server_aql_skipranges', false)); } else { unitTestPrettyPrintResults( performTests(options, tests_shell_server_aql.concat( tests_shell_server_aql_extended), 'shell_server_aql', false)); } } }; testFuncs.shell_client = function(options) { findTests(); var instanceInfo = startInstance("tcp", options, [], "shell_client"); var results = {}; var i; var te; var continueTesting = true; var filtered = {}; if (instanceInfo === false) { return {status: false, message: "failed to start server!"}; } for (i = 0; i < tests_shell_client.length; i++) { te = tests_shell_client[i]; if (filterTestcaseByOptions(te, options, filtered)) { if (!continueTesting) { print("Skipping, " + te + " server is gone."); results[te] = {status: false, message: instanceInfo.exitStatus}; instanceInfo.exitStatus = "server is gone."; break; } print("\narangosh: Trying",te,"..."); var r = runInArangosh(options, instanceInfo, te); results[te] = r; if (r.status !== true) { options.cleanup = false; if (! options.force) { break; } } continueTesting = checkInstanceAlive(instanceInfo, options); } else { if (options.extremeVerbosity) { print("Skipped " + te + " because of " + filtered.filter); } } } print("Shutting down..."); shutdownInstance(instanceInfo, options); print("done."); if ((!options.skipLogAnalysis) && instanceInfo.hasOwnProperty('importantLogLines') && Object.keys(instanceInfo.importantLogLines).length > 0) { print("Found messages in the server logs: \n" + yaml.safeDump(instanceInfo.importantLogLines)); } return results; }; testFuncs.config = function (options) { if (options.skipConfig) { return {}; } var topDir = findTopDir(); var results = {}; var ts = ["arangod", "arangob", "arangodump", "arangoimp", "arangorestore", "arangosh"]; var args; var t; var i; print("--------------------------------------------------------------------------------"); print("Absolut config tests"); print("--------------------------------------------------------------------------------"); for (i = 0; i < ts.length; i++) { t = ts[i]; args = { "configuration" : fs.join(topDir,"etc","arangodb",t+".conf"), "flatCommands" : ["--help"] }; results[t] = executeAndWait(fs.join(topDir,"bin",t), toArgv(args)); print("Args for [" + t + "]:"); print(yaml.safeDump(args)); print("Result: " + results[t].status); } print("--------------------------------------------------------------------------------"); print("relative config tests"); print("--------------------------------------------------------------------------------"); for (i = 0; i < ts.length; i++) { t = ts[i]; args = { "configuration" : fs.join(topDir,"etc","relative",t+".conf"), "flatCommands" : ["--help"] }; results[t+"_rel"] = executeAndWait(fs.join(topDir,"bin",t), toArgv(args)); print("Args for (relative) [" + t + "]:"); print(yaml.safeDump(args)); print("Result: " + results[t + "_rel"].status); } return results; }; testFuncs.boost = function (options) { var topDir = findTopDir(); var results = {}; if (! options.skipBoost) { results.basics = executeAndWait(fs.join(topDir,"UnitTests","basics_suite"), ["--show_progress"]); } if (! options.skipGeo) { results.geo_suite = executeAndWait( fs.join(topDir,"UnitTests","geo_suite"), ["--show_progress"]); } return results; }; function rubyTests (options, ssl) { var instanceInfo; if (ssl) { instanceInfo = startInstance("ssl", options, [], "ssl_server"); } else { instanceInfo = startInstance("tcp", options, [], "http_server"); } if (instanceInfo === false) { return {status: false, message: "failed to start server!"}; } var tmpname = fs.getTempFile()+".rb"; fs.write(tmpname,'RSpec.configure do |c|\n'+ ' c.add_setting :ARANGO_SERVER\n'+ ' c.ARANGO_SERVER = "' + instanceInfo.endpoint.substr(6) + '"\n'+ ' c.add_setting :ARANGO_SSL\n'+ ' c.ARANGO_SSL = "' + (ssl ? '1' : '0') + '"\n'+ ' c.add_setting :ARANGO_USER\n'+ ' c.ARANGO_USER = "' + options.username + '"\n'+ ' c.add_setting :ARANGO_PASSWORD\n'+ ' c.ARANGO_PASSWORD = "' + options.password + '"\n'+ 'end\n'); var logsdir = fs.join(findTopDir(),"logs"); try { fs.makeDirectory(logsdir); } catch (err) { } var files = fs.list(fs.join("UnitTests","HttpInterface")); var filtered = {}; var result = {}; var args; var i; var continueTesting = true; var command; if (require("internal").platform.substr(0,3) === 'win') { command = "rspec.bat"; } else { command = "rspec"; } for (i = 0; i < files.length; i++) { var te = files[i]; if (te.substr(0,4) === "api-" && te.substr(-3) === ".rb") { if (filterTestcaseByOptions(te, options, filtered)) { args = ["--color", "-I", fs.join("UnitTests","HttpInterface"), "--format", "d", "--require", tmpname, fs.join("UnitTests","HttpInterface", te)]; if (! continueTesting) { print("Skipping " + te + " server is gone."); result[te] = {status: false, message: instanceInfo.exitStatus}; instanceInfo.exitStatus = "server is gone."; break; } print("\n"+ Date() + " rspec Trying ",te,"..."); result[te] = executeAndWait(command, args); if (result[te].status === false) { options.cleanup = false; if (!options.force) { break; } } continueTesting = checkInstanceAlive(instanceInfo, options); } else { if (options.extremeVerbosity) { print("Skipped " + te + " because of " + filtered.filter); } } } } print("Shutting down..."); fs.remove(tmpname); shutdownInstance(instanceInfo,options); print("done."); if ((!options.skipLogAnalysis) && instanceInfo.hasOwnProperty('importantLogLines') && Object.keys(instanceInfo.importantLogLines).length > 0) { print("Found messages in the server logs: \n" + yaml.safeDump(instanceInfo.importantLogLines)); } return result; } testFuncs.http_server = function (options) { return rubyTests(options, false); }; testFuncs.ssl_server = function (options) { if (options.hasOwnProperty('skipSsl')) { return {}; } return rubyTests(options, true); }; function runArangoImp (options, instanceInfo, what) { var topDir = findTopDir(); var args = { "server.username": options.username, "server.password": options.password, "server.endpoint": instanceInfo.endpoint, "file": fs.join(topDir,what.data), "collection": what.coll, "type": what.type }; if (what.create !== undefined) { args["create-collection"] = what.create; } if (what.backslash !== undefined) { args["backslash-escape"] = what.backslash; } if (what.separator !== undefined) { args["separator"] = what.separator; } var arangoimp = fs.join("bin","arangoimp"); return executeAndWait(arangoimp, toArgv(args)); } function runArangoDumpRestore (options, instanceInfo, which, database) { var args = { "configuration": "none", "server.username": options.username, "server.password": options.password, "server.endpoint": instanceInfo.endpoint, "server.database": database }; var exe; if (which === "dump") { args["output-directory"] = fs.join(instanceInfo.tmpDataDir,"dump"); exe = fs.join("bin","arangodump"); } else { args["create-database"] = "true"; args["input-directory"] = fs.join(instanceInfo.tmpDataDir,"dump"); exe = fs.join("bin","arangorestore"); } return executeAndWait(exe, toArgv(args)); } function runArangoBenchmark (options, instanceInfo, cmds) { var args = { "configuration": "none", "server.username": options.username, "server.password": options.password, "server.endpoint": instanceInfo.endpoint, //"server.request-timeout": 1200 // default now. "server.connect-timeout": 10 // 5s default }; args = _.extend(args, cmds); if (!args.hasOwnProperty('verbose')) { args.quiet = true; } var exe = fs.join("bin","arangob"); return executeAndWait(exe, toArgv(args)); } testFuncs.arangosh = function (options) { var failed = 0; var args = makeTestingArgsClient(options); var arangosh = fs.join("bin","arangosh"); print("Starting arangosh with exception throwing script:"); args["javascript.execute-string"] = "throw('foo')"; var rc = executeExternalAndWait(arangosh, toArgv(args)); var failSuccess = (rc.hasOwnProperty('exit') && rc.exit === 1); if (!failSuccess) { failed += 1; } print("Starting arangosh with regular terminating script:"); args["javascript.execute-string"] = ";"; rc = executeExternalAndWait(arangosh, toArgv(args)); var successSuccess = (rc.hasOwnProperty('exit') && rc.exit === 0); if (!successSuccess) { failed += 1; } return [ { "suiteName": "ArangoshExitCodeTest", "testArangoshExitCodeFail": { "status": failSuccess, "duration": 0 }, "testArangoshExitCodeSuccess": { "status": successSuccess, "duration": 0 }, "duration": 0, "status": failSuccess && successSuccess, "failed": failed, "total": 2 } ]; }; var impTodo = [ {id: "json1", data: makePathUnix("UnitTests/import-1.json"), coll: "UnitTestsImportJson1", type: "json", create: undefined}, {id: "json2", data: makePathUnix("UnitTests/import-2.json"), coll: "UnitTestsImportJson2", type: "json", create: undefined}, {id: "json3", data: makePathUnix("UnitTests/import-3.json"), coll: "UnitTestsImportJson3", type: "json", create: undefined}, {id: "json4", data: makePathUnix("UnitTests/import-4.json"), coll: "UnitTestsImportJson4", type: "json", create: undefined}, {id: "json5", data: makePathUnix("UnitTests/import-5.json"), coll: "UnitTestsImportJson5", type: "json", create: undefined}, {id: "csv1", data: makePathUnix("UnitTests/import-1.csv"), coll: "UnitTestsImportCsv1", type: "csv", create: "true"}, {id: "csv2", data: makePathUnix("UnitTests/import-2.csv"), coll: "UnitTestsImportCsv2", type: "csv", create: "true"}, {id: "csv3", data: makePathUnix("UnitTests/import-3.csv"), coll: "UnitTestsImportCsv3", type: "csv", create: "true"}, {id: "csv4", data: makePathUnix("UnitTests/import-4.csv"), coll: "UnitTestsImportCsv4", type: "csv", create: "true", separator: ";", backslash: true}, {id: "csv5", data: makePathUnix("UnitTests/import-5.csv"), coll: "UnitTestsImportCsv5", type: "csv", create: "true", separator: ";", backslash: true}, {id: "tsv1", data: makePathUnix("UnitTests/import-1.tsv"), coll: "UnitTestsImportTsv1", type: "tsv", create: "true"}, {id: "tsv2", data: makePathUnix("UnitTests/import-2.tsv"), coll: "UnitTestsImportTsv2", type: "tsv", create: "true"}, {id: "edge", data: makePathUnix("UnitTests/import-edges.json"), coll: "UnitTestsImportEdge", type: "json", create: "false"} ]; testFuncs.importing = function (options) { if (options.cluster) { if (options.extremeVerbosity) { print("Skipped because of cluster."); } return {"importing": { "status" : true, "message": "skipped because of cluster", "skipped": true } }; } var instanceInfo = startInstance("tcp", options, [ ], "importing"); if (instanceInfo === false) { return {status: false, message: "failed to start server!"}; } var result = {}; try { var r = runInArangosh(options, instanceInfo, makePathUnix("js/server/tests/import-setup.js")); result.setup = r; if (r.status !== true) { throw "banana"; } var i; for (i = 0; i < impTodo.length; i++) { r = runArangoImp(options, instanceInfo, impTodo[i]); result[impTodo[i].id] = r; if (r.status !== true && !options.force) { throw "banana"; } } r = runInArangosh(options, instanceInfo, makePathUnix("js/server/tests/import.js")); result.check = r; r = runInArangosh(options, instanceInfo, makePathUnix("js/server/tests/import-teardown.js")); result.teardown = r; } catch (banana) { print("A banana of the following form was caught:",yaml.safeDump(banana)); } print("Shutting down..."); shutdownInstance(instanceInfo,options); print("done."); if ((!options.skipLogAnalysis) && instanceInfo.hasOwnProperty('importantLogLines') && Object.keys(instanceInfo.importantLogLines).length > 0) { print("Found messages in the server logs: \n" + yaml.safeDump(instanceInfo.importantLogLines)); } return result; }; testFuncs.upgrade = function (options) { if (options.cluster) { return true; } var result = {}; var tmpDataDir = fs.getTempFile(); fs.makeDirectoryRecursive(tmpDataDir); var appDir = fs.join(tmpDataDir, "app"); // We use the PortFinder to find a free port for our subinstance, // to this end, we have to fake a dummy dispatcher: var dispatcher = {endpoint: "tcp://127.0.0.1:", avoidPorts: [], id: "me"}; var pf = new PortFinder([8529],dispatcher); var port = pf.next(); var args = makeTestingArgs(appDir); var endpoint = "tcp://127.0.0.1:"+port; args["server.endpoint"] = endpoint; args["database.directory"] = fs.join(tmpDataDir,"data"); fs.makeDirectoryRecursive(fs.join(tmpDataDir,"data")); var argv = toArgv(args).concat(["--upgrade"]); result.first = executeAndWait(fs.join("bin","arangod"), argv); if (result.first !== 0 && !options.force) { print("not removing " + tmpDataDir); return result; } result.second = executeAndWait(fs.join("bin","arangod"), argv); cleanupDirectories.push(tmpDataDir); return result; }; testFuncs.foxx_manager = function (options) { print("foxx_manager tests..."); var instanceInfo = startInstance("tcp", options, [], "foxx_manager"); var results = {}; if (instanceInfo === false) { return {status: false, message: "failed to start server!"}; } results.update = runArangoshCmd(options, instanceInfo, {"configuration": "etc/relative/foxx-manager.conf"}, ["update"]); if (results.update.status === true || options.force) { results.search = runArangoshCmd(options, instanceInfo, {"configuration": "etc/relative/foxx-manager.conf"}, ["search","itzpapalotl"]); } print("Shutting down..."); shutdownInstance(instanceInfo,options); print("done."); if ((!options.skipLogAnalysis) && instanceInfo.hasOwnProperty('importantLogLines') && Object.keys(instanceInfo.importantLogLines).length > 0) { print("Found messages in the server logs: \n" + yaml.safeDump(instanceInfo.importantLogLines)); } return results; }; // TODO write test for 2.6-style queues // testFuncs.queue_legacy = function (options) { // if (options.skipFoxxQueues) { // print("skipping test of legacy queue job types"); // return {}; // } // var startTime; // var queueAppMountPath = '/test-queue-legacy'; // print("Testing legacy queue job types"); // var instanceInfo = startInstance("tcp", options, [], "queue_legacy"); // if (instanceInfo === false) { // return {status: false, message: "failed to start server!"}; // } // var data = { // naive: {_key: 'potato', hello: 'world'}, // forced: {_key: 'tomato', hello: 'world'}, // plain: {_key: 'banana', hello: 'world'} // }; // var results = {}; // results.install = runArangoshCmd(options, instanceInfo, { // "configuration": "etc/relative/foxx-manager.conf" // }, [ // "install", // "js/common/test-data/apps/queue-legacy-test", // queueAppMountPath // ]); // print("Restarting without foxx-queues-warmup-exports..."); // shutdownInstance(instanceInfo, options); // instanceInfo = startInstance("tcp", options, { // "server.foxx-queues-warmup-exports": "false" // }, "queue_legacy", instanceInfo.flatTmpDataDir); // if (instanceInfo === false) { // return {status: false, message: "failed to restart server!"}; // } // print("done."); // var res, body; // startTime = time(); // try { // res = download( // instanceInfo.url + queueAppMountPath + '/', // JSON.stringify(data.naive), // {method: 'POST'} // ); // body = JSON.parse(res.body); // results.naive = {status: body.success === false, message: JSON.stringify({body: res.body, code: res.code})}; // } catch (e) { // results.naive = {status: true, message: JSON.stringify({body: res.body, code: res.code})}; // } // results.naive.duration = time() - startTime; // startTime = time(); // try { // res = download( // instanceInfo.url + queueAppMountPath + '/?allowUnknown=true', // JSON.stringify(data.forced), // {method: 'POST'} // ); // body = JSON.parse(res.body); // results.forced = ( // body.success // ? {status: true} // : {status: false, message: body.error, stacktrace: body.stacktrace} // ); // } catch (e) { // results.forced = {status: false, message: JSON.stringify({body: res.body, code: res.code})}; // } // results.forced.duration = time() - startTime; // print("Restarting with foxx-queues-warmup-exports..."); // shutdownInstance(instanceInfo, options); // instanceInfo = startInstance("tcp", options, { // "server.foxx-queues-warmup-exports": "true" // }, "queue_legacy", instanceInfo.flatTmpDataDir); // if (instanceInfo === false) { // return {status: false, message: "failed to restart server!"}; // } // print("done."); // startTime = time(); // try { // res = download(instanceInfo.url + queueAppMountPath + '/', JSON.stringify(data.plain), {method: 'POST'}); // body = JSON.parse(res.body); // results.plain = ( // body.success // ? {status: true} // : {status: false, message: JSON.stringify({body: res.body, code: res.code})} // ); // } catch (e) { // results.plain = {status: false, message: JSON.stringify({body: res.body, code: res.code})}; // } // results.plain.duration = time() - startTime; // startTime = time(); // try { // for (var i = 0; i < 60; i++) { // wait(1); // res = download(instanceInfo.url + queueAppMountPath + '/'); // body = JSON.parse(res.body); // if (body.length === 2) { // break; // } // } // results.final = ( // body.length === 2 && body[0]._key === data.forced._key && body[1]._key === data.plain._key // ? {status: true} // : {status: false, message: JSON.stringify({body: res.body, code: res.code})} // ); // } catch (e) { // results.final = {status: false, message: JSON.stringify({body: res.body, code: res.code})}; // } // results.final.duration = time() - startTime; // results.uninstall = runArangoshCmd(options, instanceInfo, { // "configuration": "etc/relative/foxx-manager.conf" // }, [ // "uninstall", // queueAppMountPath // ]); // print("Shutting down..."); // shutdownInstance(instanceInfo, options); // print("done."); // if ((!options.skipLogAnalysis) && // instanceInfo.hasOwnProperty('importantLogLines') && // Object.keys(instanceInfo.importantLogLines).length > 0) { // print("Found messages in the server logs: \n" + yaml.safeDump(instanceInfo.importantLogLines)); // } // return results; // }; testFuncs.dump = function (options) { var cluster; if (options.cluster) { cluster = "-cluster"; } else { cluster = ""; } print("dump tests..."); var instanceInfo = startInstance("tcp",options, [], "dump"); if (instanceInfo === false) { return {status: false, message: "failed to start server!"}; } var results = {}; print(Date() + ": Setting up"); results.setup = runInArangosh(options, instanceInfo, makePathUnix("js/server/tests/dump-setup"+cluster+".js")); if (checkInstanceAlive(instanceInfo, options) && (results.setup.status === true)) { print(Date() + ": Dump and Restore - dump"); results.dump = runArangoDumpRestore(options, instanceInfo, "dump", "UnitTestsDumpSrc"); if (checkInstanceAlive(instanceInfo, options)) { print(Date() + ": Dump and Restore - restore"); results.restore = runArangoDumpRestore(options, instanceInfo, "restore", "UnitTestsDumpDst"); if (checkInstanceAlive(instanceInfo, options)) { print(Date() + ": Dump and Restore - dump 2"); results.test = runInArangosh(options, instanceInfo, makePathUnix("js/server/tests/dump"+cluster+".js"), { "server.database": "UnitTestsDumpDst"}); if (checkInstanceAlive(instanceInfo, options)) { print(Date() + ": Dump and Restore - teardown"); results.tearDown = runInArangosh(options, instanceInfo, makePathUnix("js/server/tests/dump-teardown"+cluster+".js")); } } } } print("Shutting down..."); shutdownInstance(instanceInfo,options); print("done."); if ((!options.skipLogAnalysis) && instanceInfo.hasOwnProperty('importantLogLines') && Object.keys(instanceInfo.importantLogLines).length > 0) { print("Found messages in the server logs: \n" + yaml.safeDump(instanceInfo.importantLogLines)); } return results; }; var benchTodo = [ {"requests":"10000", "concurrency":"2", "test":"version", "keep-alive":"false"}, {"requests":"10000", "concurrency":"2", "test":"version", "async":"true"}, {"requests":"20000", "concurrency":"1", "test":"version", "async":"true"}, {"requests":"100000","concurrency":"2", "test":"shapes", "batch-size":"16", "complexity":"2"}, {"requests":"100000","concurrency":"2", "test":"shapes-append", "batch-size":"16", "complexity":"4"}, {"requests":"100000","concurrency":"2", "test":"random-shapes", "batch-size":"16", "complexity":"2"}, {"requests":"1000", "concurrency":"2", "test":"version", "batch-size":"16"}, {"requests":"100", "concurrency":"1", "test":"version", "batch-size": "0"}, {"requests":"100", "concurrency":"2", "test":"document", "batch-size":"10", "complexity":"1"}, {"requests":"2000", "concurrency":"2", "test":"crud", "complexity":"1"}, {"requests":"4000", "concurrency":"2", "test":"crud-append", "complexity":"4"}, {"requests":"4000", "concurrency":"2", "test":"edge", "complexity":"4"}, {"requests":"5000", "concurrency":"2", "test":"hash", "complexity":"1"}, {"requests":"5000", "concurrency":"2", "test":"skiplist", "complexity":"1"}, {"requests":"500", "concurrency":"3", "test":"aqltrx", "complexity":"1"}, {"requests":"100", "concurrency":"3", "test":"counttrx"}, {"requests":"500", "concurrency":"3", "test":"multitrx"} ]; testFuncs.arangob = function (options) { print(options); print(options.skipArangoB); if (options.skipArangoB === true) { print("skipping Benchmark tests!"); return {}; } print("arangob tests..."); var instanceInfo = startInstance("tcp",options, [], "arangob"); if (instanceInfo === false) { return {status: false, message: "failed to start server!"}; } var results = {}; var i,r; var continueTesting = true; for (i = 0; i < benchTodo.length; i++) { if ((options.skipArangoBNonConnKeepAlive) && benchTodo[i].hasOwnProperty('keep-alive') && (benchTodo[i]['keep-alive'] === "false")) { benchTodo[i]['keep-alive'] = true; } // On the cluster we do not yet have working transaction functionality: if (! options.cluster || (benchTodo[i].test !== "counttrx" && benchTodo[i].test !== "multitrx")) { if (!continueTesting) { print("Skipping " + benchTodo[i] + ", server is gone."); results[i] = {status: false, message: instanceInfo.exitStatus}; instanceInfo.exitStatus = "server is gone."; break; } var args = benchTodo[i]; if (options.hasOwnProperty('benchargs')) { args = _.extend(args, options.benchargs); } r = runArangoBenchmark(options, instanceInfo, args); results[i] = r; continueTesting = checkInstanceAlive(instanceInfo, options); if (r.status !== true && !options.force) { break; } } } print("Shutting down..."); shutdownInstance(instanceInfo,options); print("done."); if ((!options.skipLogAnalysis) && instanceInfo.hasOwnProperty('importantLogLines') && Object.keys(instanceInfo.importantLogLines).length > 0) { print("Found messages in the server logs: \n" + yaml.safeDump(instanceInfo.importantLogLines)); } return results; }; testFuncs.authentication = function (options) { if (options.skipAuth === true) { print("skipping Authentication tests!"); return {}; } print("Authentication tests..."); var instanceInfo = startInstance("tcp", options, {"server.disable-authentication": "false"}, "authentication"); if (instanceInfo === false) { return {status: false, message: "failed to start server!"}; } var results = {}; results.auth = runInArangosh(options, instanceInfo, fs.join("js","client","tests","auth.js")); print("Shutting down..."); shutdownInstance(instanceInfo,options); print("done."); if ((!options.skipLogAnalysis) && instanceInfo.hasOwnProperty('importantLogLines') && Object.keys(instanceInfo.importantLogLines).length > 0) { print("Found messages in the server logs: \n" + yaml.safeDump(instanceInfo.importantLogLines)); } return results; }; var urlsTodo = [ "/_api/", "/_api", "/_api/version", "/_admin/html", "/_admin/html/", "/test", "/the-big-fat-fox" ]; var authTestServerParams = [ {"server.disable-authentication": "false", "server.authenticate-system-only": "false"}, {"server.disable-authentication": "false", "server.authenticate-system-only": "true"}, {"server.disable-authentication": "true", "server.authenticate-system-only": "true"} ]; var authTestNames = ["Full", "SystemAuth", "None"]; var authTestExpectRC = [ [401, 401, 401, 401, 401, 401, 401], [401, 401, 401, 401, 401, 404, 404], [404, 404, 200, 301, 301, 404, 404] ]; testFuncs.authentication_parameters = function (options) { if (options.skipAuth === true) { print("skipping Authentication with parameters tests!"); return {}; } print("Authentication with parameters tests..."); var results = {}; var continueTesting = true; var downloadOptions = { followRedirects:false, returnBodyOnError:true }; if (typeof(options.valgrind) === 'string') { downloadOptions.timeout = 300; } var test; for (test = 0; test < 3; test++) { var all_ok = true; var instanceInfo = startInstance("tcp", options, authTestServerParams[test], "authentication_parameters_" + authTestNames[test]); if (instanceInfo === false) { return {status: false, message: authTestNames[test] + ": failed to start server!"}; } var r; var i; var testName = 'auth_' + authTestNames[test]; print(Date() + " Starting " + authTestNames[test] + " test"); results[testName] = {}; for (i = 0; i < urlsTodo.length; i++) { print(" URL: " + instanceInfo.url + urlsTodo[i]); if (!continueTesting) { print("Skipping " + urlsTodo[i] + ", server is gone."); results[testName][urlsTodo[i]] = { status: false, message: instanceInfo.exitStatus }; instanceInfo.exitStatus = "server is gone."; all_ok = false; break; } r = download(instanceInfo.url + urlsTodo[i],"", downloadOptions); if (r.code === authTestExpectRC[test][i]) { results[testName][urlsTodo[i]] = { status: true, message: ""}; } else { checkBodyForJsonToParse(r); results[testName][urlsTodo[i]] = { status: false, message: "we expected " + authTestExpectRC[test][i] + " and we got " + r.code + " Full Status: " + yaml.safeDump(r) }; all_ok = false; } continueTesting = checkInstanceAlive(instanceInfo, options); } results[testName].status = all_ok; print("Shutting down " + authTestNames[test] + " test..."); shutdownInstance(instanceInfo, options); if ((!options.skipLogAnalysis) && instanceInfo.hasOwnProperty('importantLogLines') && (instanceInfo.importantLogLines.length > 0)) { print("Found messages in the server logs: \n" + yaml.safeDump(instanceInfo.importantLogLines)); } print("done with " + authTestNames[test] + " test."); } return results; }; var internalMembers = [ "code", "error", "status", "duration", "failed", "total", "crashed", "all_ok", "ok", "message", "suiteName" ]; function unitTestPrettyPrintResults(r) { var testrun; var test; var key; var oneTest; var testFail = 0; var testSuiteFail = 0; var success = ""; var fail = ""; try { for (testrun in r) { if (r.hasOwnProperty(testrun) && (internalMembers.indexOf(testrun) === -1)) { var isSuccess = true; var oneOutput = ""; oneOutput = "* Testrun: " + testrun + "\n"; var successTests = {}; for (test in r[testrun]) { if (r[testrun].hasOwnProperty(test) && (internalMembers.indexOf(test) === -1)) { if (r[testrun][test].status) { var where = test.lastIndexOf(fs.pathSeparator); var which; if (where < 0 ) { which = 'Unittests'; } else { which = test.substring(0, where); test = test.substring(where + 1, test.length); } if (!successTests.hasOwnProperty(which)) { successTests[which]=[]; } successTests[which].push(test); } else { testSuiteFail++; if (r[testrun][test].hasOwnProperty('message')) { isSuccess = false; oneOutput += " [ Fail ] " + test + ": Whole testsuite failed!\n"; if (typeof r[testrun][test].message === "object" && r[testrun][test].message.hasOwnProperty('body')) { oneOutput += r[testrun][test].message.body + "\n"; } else { oneOutput += r[testrun][test].message + "\n"; } } else { isSuccess = false; oneOutput += " [ Fail ] " + test + "\n"; for (oneTest in r[testrun][test]) { if (r[testrun][test].hasOwnProperty(oneTest) && (internalMembers.indexOf(oneTest) === -1) && (! r[testrun][test][oneTest].status)) { testFail++; oneOutput += " -> " + oneTest + " Failed; Verbose message:\n"; oneOutput += r[testrun][test][oneTest].message + "\n"; } } } } } } if (successTests !== "") { for (key in successTests) { if (successTests.hasOwnProperty(key)) { oneOutput = " [Success] " + key + " / [" + successTests[key].join(', ') + ']\n' + oneOutput; } } } if (isSuccess) { success += oneOutput; } else { fail += oneOutput; } } } if (success !== "") { print(success); } if (fail !== "") { print(fail); } print("Overall state: " + ((r.all_ok === true) ? "Success" : "Fail")); if (r.all_ok !== true) { print(" Suites failed: " + testSuiteFail + " Tests Failed: " + testFail); } if (r.crashed === true) { print(" We had at least one unclean shutdown of an arangod during the testrun."); } } catch (x) { print("exception caught while pretty printing result: "); print(x.message); print(JSON.stringify(r)); } } function UnitTest (which, options) { var allok = true; var results = {}; if (typeof options !== "object") { options = {}; } _.defaults(options, optionsDefaults); if (which === undefined) { printUsage(); return; } var jsonReply = options.jsonReply; delete options.jsonReply; var i; var ok; if (which === "all") { var n; for (n = 0; n < allTests.length; n++) { print("Doing test", allTests[n], "with options", options); results[allTests[n]] = r = testFuncs[allTests[n]](options); ok = true; for (i in r) { if (r.hasOwnProperty(i)) { if (r[i].status !== true) { ok = false; } } } r.ok = ok; if (!ok) { allok = false; } results.all_ok = allok; } results.all_ok = allok; results.crashed = serverCrashed; if (allok) { cleanupDBDirectories(options); } else { print("since some tests weren't successfully, not cleaning up: \n" + yaml.safeDump(cleanupDirectories)); } if (jsonReply === true ) { return results; } else { unitTestPrettyPrintResults(results); return allok; } } else if (!testFuncs.hasOwnProperty(which)) { printUsage(); throw 'Unknown test "' + which + '"'; } else { var r; results[which] = r = testFuncs[which](options); // print("Testresult:", yaml.safeDump(r)); ok = true; for (i in r) { if (r.hasOwnProperty(i) && (which !== "single" || i !== "test")) { if (r[i].status !== true) { ok = false; allok = false; } } } r.ok = ok; results.all_ok = ok; results.crashed = serverCrashed; if (allok) { cleanupDBDirectories(options); } else { print("since some tests weren't successfully, not cleaning up: \n" + yaml.safeDump(cleanupDirectories)); } if (jsonReply === true ) { return results; } else { unitTestPrettyPrintResults(results); return allok; } } } exports.internalMembers = internalMembers; exports.testFuncs = testFuncs; exports.UnitTest = UnitTest; exports.unitTestPrettyPrintResults = unitTestPrettyPrintResults; // ----------------------------------------------------------------------------- // --SECTION-- END-OF-FILE // ----------------------------------------------------------------------------- // Local Variables: // mode: outline-minor // outline-regexp: "\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @\\}\\)" // End: