1
0
Fork 0
arangodb/js/client/modules/@arangodb/test-utils.js

979 lines
31 KiB
JavaScript
Executable File

/* jshint strict: false, sub: true */
/* global print db arango */
'use strict';
// //////////////////////////////////////////////////////////////////////////////
// / DISCLAIMER
// /
// / Copyright 2016 ArangoDB GmbH, Cologne, Germany
// / 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 ArangoDB GmbH, Cologne, Germany
// /
// / @author Max Neunhoeffer
// //////////////////////////////////////////////////////////////////////////////
/* Modules: */
const _ = require('lodash');
const fs = require('fs');
const pu = require('@arangodb/process-utils');
const yaml = require('js-yaml');
const toArgv = require('internal').toArgv;
const time = require('internal').time;
const sleep = require('internal').sleep;
const download = require('internal').download;
const pathForTesting = require('internal').pathForTesting;
const platform = require('internal').platform;
/* Constants: */
// const BLUE = require('internal').COLORS.COLOR_BLUE;
// const CYAN = require('internal').COLORS.COLOR_CYAN;
// const GREEN = require('internal').COLORS.COLOR_GREEN;
const RED = require('internal').COLORS.COLOR_RED;
const RESET = require('internal').COLORS.COLOR_RESET;
// const YELLOW = require('internal').COLORS.COLOR_YELLOW;
let didSplitBuckets = false;
// //////////////////////////////////////////////////////////////////////////////
// / @brief get the items uniq to arr1 or arr2
// //////////////////////////////////////////////////////////////////////////////
function diffArray (arr1, arr2) {
return arr1.concat(arr2).filter(function (val) {
if (!(arr1.includes(val) && arr2.includes(val))) {
return val;
}
});
}
// //////////////////////////////////////////////////////////////////////////////
// / @brief build a unix path
// //////////////////////////////////////////////////////////////////////////////
function makePathUnix (path) {
return fs.join.apply(null, path.split('/'));
}
// //////////////////////////////////////////////////////////////////////////////
// / @brief build a generic path
// //////////////////////////////////////////////////////////////////////////////
function makePathGeneric (path) {
return fs.join.apply(null, path.split(fs.pathSeparator));
}
// //////////////////////////////////////////////////////////////////////////////
// / @brief runs a list of tests
// //////////////////////////////////////////////////////////////////////////////
function performTests (options, testList, testname, runFn, serverOptions, startStopHandlers) {
if (options.testBuckets && !didSplitBuckets) {
throw new Error("You parametrized to split buckets, but this testsuite doesn't support it!!!");
}
if (testList.length === 0) {
print('Testsuite is empty!');
return {
"ALLTESTS" : {
status: ((options.skipGrey || options.skipTimecritial || options.skipNondeterministic) && (options.test === undefined)),
skipped: true,
message: 'no testfiles were found!'
}
};
}
if (serverOptions === undefined) {
serverOptions = {};
}
let env = {};
let customInstanceInfos = {};
let healthCheck = function () {return true;};
if (startStopHandlers !== undefined && startStopHandlers.hasOwnProperty('healthCheck')) {
healthCheck = startStopHandlers.healthCheck;
}
if (startStopHandlers !== undefined && startStopHandlers.hasOwnProperty('preStart')) {
customInstanceInfos['preStart'] = startStopHandlers.preStart(options,
serverOptions,
customInstanceInfos,
startStopHandlers);
if (customInstanceInfos.preStart.state === false) {
return {
setup: {
status: false,
message: 'custom preStart failed!' + customInstanceInfos.preStart.message
},
shutdown: customInstanceInfos.preStart.shutdown
};
}
_.defaults(env, customInstanceInfos.preStart.env);
}
let instanceInfo = pu.startInstance(options.protocol, options, serverOptions, testname);
if (instanceInfo === false) {
if (startStopHandlers !== undefined && startStopHandlers.hasOwnProperty('startFailed')) {
customInstanceInfos['startFailed'] = startStopHandlers.startFailed(options,
serverOptions,
customInstanceInfos,
startStopHandlers);
}
return {
setup: {
status: false,
message: 'failed to start server!'
}
};
}
if (startStopHandlers !== undefined && startStopHandlers.hasOwnProperty('postStart')) {
customInstanceInfos['postStart'] = startStopHandlers.postStart(options,
serverOptions,
instanceInfo,
customInstanceInfos,
startStopHandlers);
if (customInstanceInfos.postStart.state === false) {
let shutdownStatus = customInstanceInfos.postStart.shutdown;
shutdownStatus = shutdownStatus && pu.shutdownInstance(instanceInfo, options);
return {
setup: {
status: false,
message: 'custom postStart failed: ' + customInstanceInfos.postStart.message,
shutdown: shutdownStatus
}
};
}
_.defaults(env, customInstanceInfos.postStart.env);
}
let results = { shutdown: true };
let continueTesting = true;
let serverDead = false;
let count = 0;
let forceTerminate = false;
let graphCount = 0;
for (let i = 0; i < testList.length; i++) {
let te = testList[i];
let filtered = {};
if (filterTestcaseByOptions(te, options, filtered)) {
let first = true;
let loopCount = 0;
count += 1;
let collectionsBefore = [];
if (!serverDead) {
try {
db._collections().forEach(collection => {
collectionsBefore.push(collection._name);
});
}
catch (x) {
results[te] = {
status: false,
message: 'failed to fetch the previously available collections: ' + x.message
};
continueTesting = false;
serverDead = true;
first = false;
}
}
while (first || options.loopEternal) {
if (!continueTesting) {
if (!results.hasOwnProperty('SKIPPED')) {
print('oops! Skipping remaining tests, server is gone.');
results['SKIPPED'] = {
status: false,
message: ""
};
results[te] = {
status: false,
message: 'server crashed'
};
} else {
if (results['SKIPPED'].message !== '') {
results['SKIPPED'].message += ', ';
}
results['SKIPPED'].message += te;
}
instanceInfo.exitStatus = 'server is gone.';
break;
}
print('\n' + Date() + ' ' + runFn.info + ': Trying', te, '...');
let reply = runFn(options, instanceInfo, te, env);
if (reply.hasOwnProperty('forceTerminate')) {
continueTesting = false;
forceTerminate = true;
continue;
} else if (reply.hasOwnProperty('status')) {
results[te] = reply;
if (results[te].status === false) {
options.cleanup = false;
}
if (!reply.status && !options.force) {
break;
}
} else {
results[te] = {
status: false,
message: reply
};
if (!options.force) {
break;
}
}
if (pu.arangod.check.instanceAlive(instanceInfo, options) &&
healthCheck(options, serverOptions, instanceInfo, customInstanceInfos, startStopHandlers)) {
continueTesting = true;
// Check whether some collections were left behind, and if mark test as failed.
let collectionsAfter = [];
try {
db._collections().forEach(collection => {
collectionsAfter.push(collection._name);
});
}
catch (x) {
results[te] = {
status: false,
message: 'failed to fetch the currently available collections: ' + x.message + '. Original test status: ' + JSON.stringify(results[te])
};
continueTesting = false;
continue;
}
let delta = diffArray(collectionsBefore, collectionsAfter).filter(function(name) {
return ! ((name[0] === '_') || (name === "compact") || (name === "election")
|| (name === "log")); // exclude system/agency collections from the comparison
return (name[0] !== '_'); // exclude system collections from the comparison
});
if (delta.length !== 0) {
results[te] = {
status: false,
message: 'Cleanup missing - test left over collections: ' + delta + '. Original test status: ' + JSON.stringify(results[te])
};
collectionsBefore = [];
try {
db._collections().forEach(collection => {
collectionsBefore.push(collection._name);
});
}
catch (x) {
results[te] = {
status: false,
message: 'failed to fetch the currently available delta collections: ' + x.message + '. Original test status: ' + JSON.stringify(results[te])
};
continueTesting = false;
continue;
}
}
let graphs;
try {
graphs = db._collection('_graphs');
}
catch (x) {
results[te] = {
status: false,
message: 'failed to fetch the graphs: ' + x.message + '. Original test status: ' + JSON.stringify(results[te])
};
continueTesting = false;
continue;
}
if (graphs && graphs.count() !== graphCount) {
results[te] = {
status: false,
message: 'Cleanup of graphs missing - found graph definitions: [ ' +
JSON.stringify(graphs.toArray()) +
' ] - Original test status: ' +
JSON.stringify(results[te])
};
graphCount = graphs.count();
}
} else {
serverDead = true;
continueTesting = false;
results[te] = {
status: false,
message: 'server is dead.'
};
}
if (startStopHandlers !== undefined && startStopHandlers.hasOwnProperty('alive')) {
customInstanceInfos['alive'] = startStopHandlers.alive(options,
serverOptions,
instanceInfo,
customInstanceInfos,
startStopHandlers);
if (customInstanceInfos.alive.state === false) {
continueTesting = false;
results.setup.message = 'custom alive check failed!';
}
collectionsBefore = [];
try {
db._collections().forEach(collection => {
collectionsBefore.push(collection._name);
});
}
catch (x) {
results[te] = {
status: false,
message: 'failed to fetch the currently available shutdown collections: ' + x.message + '. Original test status: ' + JSON.stringify(results[te])
};
continueTesting = false;
continue;
}
}
first = false;
if (options.loopEternal) {
if (loopCount % options.loopSleepWhen === 0) {
print('sleeping...');
sleep(options.loopSleepSec);
print('continuing.');
}
++loopCount;
}
}
} else {
if (options.extremeVerbosity) {
print('Skipped ' + te + ' because of ' + filtered.filter);
}
}
}
if (count === 0) {
results['ALLTESTS'] = {
status: true,
skipped: true
};
results.status = true;
print(RED + 'No testcase matched the filter.' + RESET);
}
print(Date() + ' Shutting down...');
if (startStopHandlers !== undefined && startStopHandlers.hasOwnProperty('preStop')) {
customInstanceInfos['preStop'] = startStopHandlers.preStop(options,
serverOptions,
instanceInfo,
customInstanceInfos,
startStopHandlers);
if (customInstanceInfos.preStop.state === false) {
if (!results.hasOwnProperty('setup')) {
results['setup'] = {};
}
results.setup['status'] = false;
results.setup['message'] = 'custom preStop failed!' + customInstanceInfos.preStop.message;
if (customInstanceInfos.preStop.hasOwnProperty('shutdown')) {
results.shutdown = results.shutdown && customInstanceInfos.preStop.shutdown;
}
}
}
// pass on JWT secret
let clonedOpts = _.clone(options);
if (serverOptions['server.jwt-secret'] && !clonedOpts['server.jwt-secret']) {
clonedOpts['server.jwt-secret'] = serverOptions['server.jwt-secret'];
}
results.shutdown = results.shutdown && pu.shutdownInstance(instanceInfo, clonedOpts, forceTerminate);
if (startStopHandlers !== undefined && startStopHandlers.hasOwnProperty('postStop')) {
customInstanceInfos['postStop'] = startStopHandlers.postStop(options,
serverOptions,
instanceInfo,
customInstanceInfos,
startStopHandlers);
if (customInstanceInfos.postStop.state === false) {
results.setup.status = false;
results.setup.message = 'custom postStop failed!';
}
}
print('done.');
return results;
}
// //////////////////////////////////////////////////////////////////////////////
// / @brief filter test-cases according to options
// //////////////////////////////////////////////////////////////////////////////
function filterTestcaseByOptions (testname, options, whichFilter) {
if (options.skipTest(testname, options)) {
whichFilter.filter = 'blacklist';
return false;
}
// These filters require a proper setup, Even if we filter by testcase:
if ((testname.indexOf('-mmfiles') !== -1) && options.storageEngine === 'rocksdb') {
whichFilter.filter = 'skip when running as rocksdb';
return false;
}
if ((testname.indexOf('-rocksdb') !== -1) && options.storageEngine === 'mmfiles') {
whichFilter.filter = 'skip when running as mmfiles';
return false;
}
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('-arangosearch') !== -1 && !options.arangosearch) {
whichFilter.filter = 'arangosearch';
return false;
}
// if we filter, we don't care about the other filters below:
if (options.hasOwnProperty('test') && (typeof (options.test) !== 'undefined')) {
whichFilter.filter = 'testcase';
return testname.search(options.test) >= 0;
}
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('-nondeterministic') !== -1 && options.skipNondeterministic) {
whichFilter.filter = 'nondeterministic';
return false;
}
if (testname.indexOf('-grey') !== -1 && options.skipGrey) {
whichFilter.filter = 'grey';
return false;
}
if (testname.indexOf('-grey') === -1 && options.onlyGrey) {
whichFilter.filter = 'grey';
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('-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) && options.valgrind) {
whichFilter.filter = 'skip in valgrind';
return false;
}
return true;
}
// //////////////////////////////////////////////////////////////////////////////
// / @brief split into buckets
// //////////////////////////////////////////////////////////////////////////////
function splitBuckets (options, cases) {
if (!options.testBuckets) {
return cases;
}
if (cases.length === 0) {
didSplitBuckets = true;
return cases;
}
didSplitBuckets = true;
let m = cases.length;
let n = options.testBuckets.split('/');
let r = parseInt(n[0]);
let s = parseInt(n[1]);
if (r < 1) {
r = 1;
}
if (r === 1) {
return cases;
}
if (s < 0) {
s = 0;
}
if (r <= s) {
s = r - 1;
}
let result = [];
cases.sort();
for (let i = s % m; i < cases.length; i = i + r) {
result.push(cases[i]);
}
return result;
}
function doOnePathInner (path) {
return _.filter(fs.list(makePathUnix(path)),
function (p) {
return (p.substr(-3) === '.js') || (p.substr(-3) === '.rb');;
})
.map(function (x) {
return fs.join(makePathUnix(path), x);
}).sort();
}
function scanTestPaths (paths, options) {
// add enterprise tests
if (global.ARANGODB_CLIENT_VERSION(true)['enterprise-version']) {
paths = paths.concat(paths.map(function(p) {
return 'enterprise/' + p;
}));
}
let allTestCases = [];
paths.forEach(function(p) {
allTestCases = allTestCases.concat(doOnePathInner(p));
});
let allFiltered = [];
let filteredTestCases = _.filter(allTestCases,
function (p) {
let whichFilter = {};
let rc = filterTestcaseByOptions(p, options, whichFilter);
if (!rc) {
allFiltered.push(p + " Filtered by: " + whichFilter.filter);
}
return rc;
});
if (filteredTestCases.length === 0) {
print("No testcase matched the filter: " + JSON.stringify(allFiltered));
return [];
}
return allTestCases;
}
// //////////////////////////////////////////////////////////////////////////////
// / @brief runs a remote unittest file using /_admin/execute
// //////////////////////////////////////////////////////////////////////////////
function runThere (options, instanceInfo, file) {
try {
let testCode;
if (file.indexOf('-spec') === -1) {
let testCase = JSON.stringify(options.testCase);
if (options.testCase === undefined) {
testCase = '"undefined"';
}
testCode = 'const runTest = require("jsunity").runTest; ' +
'return runTest(' + JSON.stringify(file) + ', true, ' + testCase + ');';
} else {
let mochaGrep = options.testCase ? ', ' + JSON.stringify(options.testCase) : '';
testCode = 'const runTest = require("@arangodb/mocha-runner"); ' +
'return runTest(' + JSON.stringify(file) + ', true' + mochaGrep + ');';
}
testCode = 'global.instanceInfo = ' + JSON.stringify(instanceInfo) + ';\n' + testCode;
let httpOptions = pu.makeAuthorizationHeaders(options);
httpOptions.method = 'POST';
if (options.isAsan) { options.oneTestTimeout *= 2; }
httpOptions.timeout = options.oneTestTimeout;
if (options.valgrind) {
httpOptions.timeout *= 2;
}
httpOptions.returnBodyOnError = true;
const reply = download(instanceInfo.url + '/_admin/execute?returnAsJSON=true',
testCode,
httpOptions);
if (!reply.error && reply.code === 200) {
return JSON.parse(reply.body);
} else {
if ((reply.code === 500) &&
reply.hasOwnProperty('message') &&
(
(reply.message.search('Request timeout reached') >= 0 ) ||
(reply.message.search('timeout during read') >= 0 ) ||
(reply.message.search('Connection closed by remote') >= 0 )
)) {
print(RED + Date() + " request timeout reached (" + reply.message +
"), aborting test execution" + RESET);
return {
status: false,
message: reply.message,
forceTerminate: true
};
} else if (reply.hasOwnProperty('body')) {
return {
status: false,
message: reply.body
};
} else {
return {
status: false,
message: yaml.safeDump(reply)
};
}
}
} catch (ex) {
return {
status: false,
message: ex.message || String(ex),
stack: ex.stack
};
}
}
runThere.info = 'runThere';
function readTestResult(path, rc, testCase) {
const jsonFN = fs.join(path, 'testresult.json');
let buf;
try {
buf = fs.read(jsonFN);
fs.remove(jsonFN);
} catch (x) {
let msg = 'failed to read ' + jsonFN + " - " + x;
print(RED + msg + RESET);
return {
failed: 1,
status: false,
message: msg,
duration: -1
};
}
let result;
try {
result = JSON.parse(buf);
} catch (x) {
let msg = 'failed to parse ' + jsonFN + "'" + buf + "' - " + x;
print(RED + msg + RESET);
return {
status: false,
message: msg,
duration: -1
};
}
if (Array.isArray(result)) {
if (result.length === 0) {
// spec-files - don't have parseable results.
rc.failed = rc.status ? 0 : 1;
return rc;
} else if ((result.length >= 1) &&
(typeof result[0] === 'object') &&
result[0].hasOwnProperty('status')) {
return result[0];
} else {
rc.failed = rc.status ? 0 : 1;
rc.message = "don't know howto handle '" + buf + "'";
return rc;
}
} else if (_.isObject(result)) {
if ((testCase !== undefined) && result.hasOwnProperty(testCase)) {
return result[testCase];
} else {
return result;
}
} else {
rc.failed = rc.status ? 0 : 1;
rc.message = "don't know howto handle '" + buf + "'";
return rc;
}
}
function writeTestResult(path, data) {
const jsonFN = fs.join(path, 'testresult.json');
fs.write(jsonFN, JSON.stringify(data));
}
// //////////////////////////////////////////////////////////////////////////////
// / @brief runs a local unittest file using arangosh
// //////////////////////////////////////////////////////////////////////////////
function runInArangosh (options, instanceInfo, file, addArgs) {
let args = pu.makeArgs.arangosh(options);
args['server.endpoint'] = instanceInfo.endpoint;
args['javascript.unit-tests'] = fs.join(pu.TOP_DIR, file);
args['javascript.unit-test-filter'] = options.testCase;
if (!options.verbose) {
args['log.level'] = 'warning';
}
if (addArgs !== undefined) {
args = Object.assign(args, addArgs);
}
require('internal').env.INSTANCEINFO = JSON.stringify(instanceInfo);
let rc = pu.executeAndWait(pu.ARANGOSH_BIN, toArgv(args), options, 'arangosh', instanceInfo.rootDir, false, options.coreCheck);
return readTestResult(instanceInfo.rootDir, rc, args['javascript.unit-tests']);
}
runInArangosh.info = 'arangosh';
// //////////////////////////////////////////////////////////////////////////////
// / @brief runs a local unittest file in the current arangosh
// //////////////////////////////////////////////////////////////////////////////
function runInLocalArangosh (options, instanceInfo, file, addArgs) {
let endpoint = arango.getEndpoint();
if (endpoint !== instanceInfo.endpoint) {
print(`runInLocalArangosh: Reconnecting to ${instanceInfo.endpoint} from ${endpoint}`);
arango.reconnect(instanceInfo.endpoint, '_system', 'root', '');
}
let testCode;
// \n's in testCode are required because of content could contain '//' at the very EOF
if (file.indexOf('-spec') === -1) {
let testCase = JSON.stringify(options.testCase);
if (options.testCase === undefined) {
testCase = '"undefined"';
}
testCode = 'const runTest = require("jsunity").runTest;\n ' +
'return runTest(' + JSON.stringify(file) + ', true, ' + testCase + ');\n';
} else {
let mochaGrep = options.testCase ? ', ' + JSON.stringify(options.testCase) : '';
testCode = 'const runTest = require("@arangodb/mocha-runner"); ' +
'return runTest(' + JSON.stringify(file) + ', true' + mochaGrep + ');\n';
}
let testFunc;
eval('testFunc = function () { \nglobal.instanceInfo = ' + JSON.stringify(instanceInfo) + ';\n' + testCode + "}");
try {
let result = testFunc();
return result;
}
catch (ex) {
return {
status: false,
message: "test has thrown! '" + file + "' - " + ex.message || String(ex),
stack: ex.stack
};
}
}
runInLocalArangosh.info = 'localarangosh';
// //////////////////////////////////////////////////////////////////////////////
// / @brief runs a unittest file using rspec
// //////////////////////////////////////////////////////////////////////////////
function camelize (str) {
return str.replace(/(?:^\w|[A-Z]|\b\w)/g, function (letter, index) {
return index === 0 ? letter.toLowerCase() : letter.toUpperCase();
}).replace(/\s+/g, '');
}
const parseRspecJson = function (testCase, res, totalDuration) {
let tName = camelize(testCase.description);
let status = (testCase.status === 'passed');
res[tName] = {
status: status,
message: testCase.full_description,
duration: totalDuration // RSpec doesn't offer per testcase time...
};
res.total++;
if (!status) {
const msg = yaml.safeDump(testCase)
.replace(/.*rspec\/core.*\n/gm, '')
.replace(/.*rspec\\core.*\n/gm, '')
.replace(/.*lib\/ruby.*\n/, '')
.replace(/.*- >-.*\n/gm, '')
.replace(/\n *`/gm, ' `');
print('RSpec test case falied: \n' + msg);
res[tName].message += '\n' + msg;
}
return status ? 0 : 1;
};
function runInRSpec (options, instanceInfo, file, addArgs) {
const tmpname = fs.join(instanceInfo.rootDir, 'testconfig.rb');
const jsonFN = fs.join(instanceInfo.rootDir, 'testresult.json');
let command;
let args;
let rspec;
let ssl = "0";
if (instanceInfo.protocol === 'ssl') {
ssl = "1";
}
let rspecConfig = '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 + '"\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' +
' c.add_setting :SKIP_TIMECRITICAL\n' +
' c.SKIP_TIMECRITICAL = ' + JSON.stringify(options.skipTimeCritical) + '\n' +
' c.add_setting :STORAGE_ENGINE\n' +
' c.STORAGE_ENGINE = ' + JSON.stringify(options.storageEngine) + '\n' +
'end\n';
fs.write(tmpname, rspecConfig);
if (options.hasOwnProperty('ruby')) {
let rx = new RegExp('ruby.exe$');
rspec = options.ruby.replace(rx, 'rspec');
command = options.ruby;
} else {
if (platform.substr(0, 3) !== 'win') {
command = 'rspec';
} else {
// Windows process utilties would apply `.exe` to rspec.
// However the file is called .bat and .exe cannot be found.
command = 'rspec.bat';
}
}
if (options.extremeVerbosity === true) {
print('rspecConfig: \n' + rspecConfig);
}
try {
fs.makeDirectory(pu.LOGS_DIR);
} catch (err) {}
args = ['--color',
'-I', fs.join('tests', 'arangodbRspecLib'),
'--format', 'd',
'--format', 'j',
'--out', jsonFN,
'--require', tmpname,
file
];
if (rspec !== undefined) {
args = [rspec].concat(args);
}
const res = pu.executeAndWait(command, args, options, 'rspec', instanceInfo.rootDir, false, false, options.oneTestTimeout);
if (!res.status && res.timeout) {
return {
total: 0,
failed: 1,
status: false,
forceTerminate: true,
message: res.message
};
}
let result = {
total: 0,
failed: 0,
status: res.status
};
try {
const jsonResult = JSON.parse(fs.read(jsonFN));
if (options.extremeVerbosity) {
print(yaml.safeDump(jsonResult));
}
for (let j = 0; j < jsonResult.examples.length; ++j) {
result.failed += parseRspecJson(
jsonResult.examples[j], result,
jsonResult.summary.duration);
}
result.duration = jsonResult.summary.duration;
} catch (x) {
result.failed = 1;
result.message = 'Failed to parse rspec results for: ' + file;
if (res.status === false) {
options.cleanup = false;
}
}
if (fs.exists(jsonFN)) fs.remove(jsonFN);
fs.remove(tmpname);
return result;
}
runInRSpec.info = 'runInRSpec';
exports.runThere = runThere;
exports.runInArangosh = runInArangosh;
exports.runInLocalArangosh = runInLocalArangosh;
exports.runInRSpec = runInRSpec;
exports.makePathUnix = makePathUnix;
exports.makePathGeneric = makePathGeneric;
exports.performTests = performTests;
exports.readTestResult = readTestResult;
exports.writeTestResult = writeTestResult;
exports.filterTestcaseByOptions = filterTestcaseByOptions;
exports.splitBuckets = splitBuckets;
exports.doOnePathInner = doOnePathInner;
exports.scanTestPaths = scanTestPaths;
exports.diffArray = diffArray;
exports.pathForTesting = pathForTesting;