mirror of https://gitee.com/bigwinds/arangodb
fixed version of arangosh result evaluation refactoring - properly count objects (#10080)
This commit is contained in:
parent
e89d72d02c
commit
636b2e5d5f
|
@ -823,3 +823,21 @@ Choose the `Npcap Loopback Adapter` number - 1:
|
||||||
--sniffProgram c:/Programm Files/wireshark/tshark.exe
|
--sniffProgram c:/Programm Files/wireshark/tshark.exe
|
||||||
|
|
||||||
You can later on use Wireshark to inpsect the capture files.
|
You can later on use Wireshark to inpsect the capture files.
|
||||||
|
|
||||||
|
|
||||||
|
### Evaluating json test reports from previous testruns
|
||||||
|
|
||||||
|
All test results of testruns are dumped to a json file named `UNITTEST_RESULT.json` which can be used
|
||||||
|
for later analyzing of timings etc.
|
||||||
|
|
||||||
|
Currently available analyzers are:
|
||||||
|
|
||||||
|
- unitTestPrettyPrintResults - Prints a pretty summary and writes an ASCII representation into `out/testfailures.txt` (if any errors)
|
||||||
|
- saveToJunitXML - saves jUnit compatible XML files
|
||||||
|
- locateLongRunning - searches the 10 longest running tests from a testsuite
|
||||||
|
- locateShortServerLife - whether the servers lifetime for the tests isn't at least 10 times as long as startup/shutdown
|
||||||
|
- locateLongSetupTeardown - locate tests that may use a lot of time in setup/teardown
|
||||||
|
- yaml - dumps the json file as a yaml file
|
||||||
|
|
||||||
|
|
||||||
|
./scripts/examine_results.js -- 'yaml,locateLongRunning' --readFile out/UNITTEST_RESULT.json
|
||||||
|
|
|
@ -3,251 +3,72 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
|
const internal = require('internal');
|
||||||
|
const rp = require('@arangodb/result-processing');
|
||||||
|
|
||||||
const UnitTest = require('@arangodb/testing');
|
const unitTest = require('@arangodb/testing').unitTest;
|
||||||
|
const optionsDefaults = require('@arangodb/testing').optionsDefaults;
|
||||||
const internalMembers = UnitTest.internalMembers;
|
const makeDirectoryRecursive = require('fs').makeDirectoryRecursive;
|
||||||
const fs = require('fs');
|
const killRemainingProcesses = require('@arangodb/process-utils').killRemainingProcesses;
|
||||||
const internal = require('internal'); // js/common/bootstrap/modules/internal.js
|
|
||||||
const inspect = internal.inspect;
|
const inspect = internal.inspect;
|
||||||
const abortSignal = 6;
|
|
||||||
|
|
||||||
let testOutputDirectory;
|
|
||||||
|
|
||||||
function makePathGeneric (path) {
|
|
||||||
return path.split(fs.pathSeparator);
|
|
||||||
}
|
|
||||||
|
|
||||||
function xmlEscape (s) {
|
|
||||||
return s.replace(/[<>&"]/g, function (c) {
|
|
||||||
return '&' + {
|
|
||||||
'<': 'lt',
|
|
||||||
'>': 'gt',
|
|
||||||
'&': 'amp',
|
|
||||||
'"': 'quot'
|
|
||||||
}[c] + ';';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildXml () {
|
|
||||||
let xml = ['<?xml version="1.0" encoding="UTF-8"?>\n'];
|
|
||||||
|
|
||||||
xml.text = function (s) {
|
|
||||||
Array.prototype.push.call(this, s);
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
xml.elem = function (tagName, attrs, close) {
|
|
||||||
this.text('<').text(tagName);
|
|
||||||
attrs = attrs || {};
|
|
||||||
|
|
||||||
for (let a in attrs) {
|
|
||||||
if (attrs.hasOwnProperty(a)) {
|
|
||||||
this.text(' ').text(a).text('="')
|
|
||||||
.text(xmlEscape(String(attrs[a]))).text('"');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (close) {
|
|
||||||
this.text('/');
|
|
||||||
}
|
|
||||||
|
|
||||||
this.text('>\n');
|
|
||||||
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
return xml;
|
|
||||||
}
|
|
||||||
|
|
||||||
// //////////////////////////////////////////////////////////////////////////////
|
// //////////////////////////////////////////////////////////////////////////////
|
||||||
// @brief converts results to XML representation
|
// @brief runs the test using the test facilities in js/client/modules/@arangodb
|
||||||
// //////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
function resultsToXml (results, baseName, cluster, isRocksDb) {
|
|
||||||
let clprefix = '';
|
|
||||||
|
|
||||||
if (cluster) {
|
|
||||||
clprefix = 'CL_';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isRocksDb) {
|
|
||||||
clprefix += 'RX_';
|
|
||||||
} else {
|
|
||||||
clprefix += 'MM_';
|
|
||||||
}
|
|
||||||
|
|
||||||
const isSignificant = function (a, b) {
|
|
||||||
return (internalMembers.indexOf(b) === -1) && a.hasOwnProperty(b);
|
|
||||||
};
|
|
||||||
|
|
||||||
for (let resultName in results) {
|
|
||||||
if (isSignificant(results, resultName)) {
|
|
||||||
let run = results[resultName];
|
|
||||||
|
|
||||||
for (let runName in run) {
|
|
||||||
if (isSignificant(run, runName)) {
|
|
||||||
const xmlName = clprefix + resultName + '_' + runName;
|
|
||||||
const current = run[runName];
|
|
||||||
|
|
||||||
if (current.skipped) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let xml = buildXml();
|
|
||||||
let total = 0;
|
|
||||||
|
|
||||||
if (current.hasOwnProperty('total')) {
|
|
||||||
total = current.total;
|
|
||||||
}
|
|
||||||
|
|
||||||
let failuresFound = current.failed;
|
|
||||||
xml.elem('testsuite', {
|
|
||||||
errors: 0,
|
|
||||||
failures: failuresFound,
|
|
||||||
tests: total,
|
|
||||||
name: xmlName,
|
|
||||||
time: 0 + current.duration
|
|
||||||
});
|
|
||||||
|
|
||||||
let seen = false;
|
|
||||||
|
|
||||||
for (let oneTestName in current) {
|
|
||||||
if (isSignificant(current, oneTestName)) {
|
|
||||||
const oneTest = current[oneTestName];
|
|
||||||
const success = (oneTest.status === true);
|
|
||||||
|
|
||||||
seen = true;
|
|
||||||
|
|
||||||
xml.elem('testcase', {
|
|
||||||
name: clprefix + oneTestName,
|
|
||||||
time: 0 + oneTest.duration
|
|
||||||
}, success);
|
|
||||||
|
|
||||||
if (!success) {
|
|
||||||
xml.elem('failure');
|
|
||||||
xml.text('<![CDATA[' + oneTest.message + ']]>\n');
|
|
||||||
xml.elem('/failure');
|
|
||||||
xml.elem('/testcase');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!seen) {
|
|
||||||
if (failuresFound === 0) {
|
|
||||||
xml.elem('testcase', {
|
|
||||||
name: 'all_tests_in_' + xmlName,
|
|
||||||
time: 0 + current.duration
|
|
||||||
}, true);
|
|
||||||
} else {
|
|
||||||
xml.elem('testcase', {
|
|
||||||
name: 'all_tests_in_' + xmlName,
|
|
||||||
failures: failuresFound,
|
|
||||||
time: 0 + current.duration
|
|
||||||
}, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
xml.elem('/testsuite');
|
|
||||||
|
|
||||||
const fn = makePathGeneric(baseName + xmlName + '.xml').join('_');
|
|
||||||
|
|
||||||
fs.write(testOutputDirectory + fn, xml.join(''));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// //////////////////////////////////////////////////////////////////////////////
|
|
||||||
// @brief runs the test using testing.js
|
|
||||||
// //////////////////////////////////////////////////////////////////////////////
|
// //////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
function main (argv) {
|
function main (argv) {
|
||||||
start_pretty_print();
|
start_pretty_print();
|
||||||
|
|
||||||
// parse arguments
|
let testSuites = []; // e.g all, http_server, recovery, ...
|
||||||
let testSuits = []; // e.g all, http_server, recovery, ...
|
|
||||||
let options = {};
|
let options = {};
|
||||||
|
|
||||||
while (argv.length >= 1) {
|
while (argv.length >= 1) {
|
||||||
if (argv[0].slice(0, 1) === '{') { // stop parsing if there is a json document
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (argv[0].slice(0, 1) === '-') { // break parsing if we hit some -option
|
if (argv[0].slice(0, 1) === '-') { // break parsing if we hit some -option
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
testSuits.push(argv[0]); // append first arg to test suits
|
testSuites.push(argv[0]); // append first arg to test suits
|
||||||
argv = argv.slice(1); // and remove first arg (c++:pop_front/bash:shift)
|
argv = argv.slice(1); // and remove first arg (c++:pop_front/bash:shift)
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert arguments
|
|
||||||
if (argv.length >= 1) {
|
if (argv.length >= 1) {
|
||||||
try {
|
try {
|
||||||
if (argv[0].slice(0, 1) === '{') {
|
options = internal.parseArgv(argv, 0);
|
||||||
options = JSON.parse(argv[0]); // parse options form json
|
|
||||||
} else {
|
|
||||||
options = internal.parseArgv(argv, 0); // parse option with parseArgv function
|
|
||||||
}
|
|
||||||
} catch (x) {
|
} catch (x) {
|
||||||
print('failed to parse the json options: ' + x.message + '\n' + String(x.stack));
|
print('failed to parse the options: ' + x.message + '\n' + String(x.stack));
|
||||||
print('argv: ', argv);
|
print('argv: ', argv);
|
||||||
return -1;
|
throw x;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.hasOwnProperty('testOutput')) {
|
if (options.hasOwnProperty('testOutput')) {
|
||||||
testOutputDirectory = options.testOutput + '/';
|
options.testOutputDirectory = options.testOutput + '/';
|
||||||
} else {
|
|
||||||
testOutputDirectory = 'out/';
|
|
||||||
}
|
}
|
||||||
|
_.defaults(options, optionsDefaults);
|
||||||
options.testOutputDirectory = testOutputDirectory;
|
|
||||||
|
|
||||||
// force json reply
|
|
||||||
options.jsonReply = true;
|
|
||||||
|
|
||||||
// create output directory
|
// create output directory
|
||||||
try {
|
try {
|
||||||
fs.makeDirectoryRecursive(testOutputDirectory);
|
makeDirectoryRecursive(options.testOutputDirectory);
|
||||||
} catch (x) {
|
} catch (x) {
|
||||||
print("failed to create test directory - " + x.message);
|
print("failed to create test directory - " + x.message);
|
||||||
throw x;
|
throw x;
|
||||||
}
|
}
|
||||||
|
|
||||||
// by default we set this to error, so we always have a proper result for the caller
|
|
||||||
try {
|
try {
|
||||||
fs.write(testOutputDirectory + '/UNITTEST_RESULT_EXECUTIVE_SUMMARY.json', "false", true);
|
// safeguard: mark test as failed if we die for some reason
|
||||||
fs.write(testOutputDirectory + '/UNITTEST_RESULT_CRASHED.json', "true", true);
|
rp.writeDefaultReports(options, testSuites);
|
||||||
let testFailureText = 'testfailures.txt';
|
|
||||||
if (options.hasOwnProperty('testFailureText')) {
|
|
||||||
testFailureText = options.testFailureText;
|
|
||||||
}
|
|
||||||
fs.write(fs.join(testOutputDirectory, testFailureText),
|
|
||||||
"Incomplete testrun with these testsuites: '" + testSuits +
|
|
||||||
"'\nand these options: " + JSON.stringify(options) + "\n");
|
|
||||||
} catch (x) {
|
} catch (x) {
|
||||||
print('failed to write default test result: ' + x.message);
|
print('failed to write default test result: ' + x.message);
|
||||||
throw(x);
|
throw x;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.hasOwnProperty('cluster') && options.cluster) {
|
let result = {};
|
||||||
// cluster beats resilient single server
|
|
||||||
options.singleresilient = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.hasOwnProperty('blacklist')) {
|
|
||||||
UnitTest.loadBlacklist(options.blacklist);
|
|
||||||
}
|
|
||||||
|
|
||||||
// run the test and store the result
|
|
||||||
let res = {}; // result
|
|
||||||
try {
|
try {
|
||||||
// run tests
|
result = unitTest(testSuites, options);
|
||||||
res = UnitTest.unitTest(testSuits, options, testOutputDirectory) || {};
|
|
||||||
} catch (x) {
|
} catch (x) {
|
||||||
|
if (x.message === "USAGE ERROR") {
|
||||||
|
throw x;
|
||||||
|
}
|
||||||
print('caught exception during test execution!');
|
print('caught exception during test execution!');
|
||||||
|
|
||||||
if (x.message !== undefined) {
|
if (x.message !== undefined) {
|
||||||
|
@ -260,74 +81,38 @@ function main (argv) {
|
||||||
print(x);
|
print(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
print(JSON.stringify(res));
|
print(JSON.stringify(result));
|
||||||
|
throw x;
|
||||||
}
|
}
|
||||||
|
|
||||||
_.defaults(res, {
|
_.defaults(result, {
|
||||||
status: false,
|
status: false,
|
||||||
crashed: true
|
crashed: true
|
||||||
});
|
});
|
||||||
|
|
||||||
let running = require("internal").getExternalSpawned();
|
killRemainingProcesses(result);
|
||||||
let i = 0;
|
|
||||||
for (i = 0; i < running.length; i++) {
|
|
||||||
let status = require("internal").statusExternal(running[i].pid, false);
|
|
||||||
if (status.status === "TERMINATED") {
|
|
||||||
print("process exited without us joining it (marking crashy): " + JSON.stringify(running[i]) + JSON.stringify(status));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
print("Killing remaining process & marking crashy: " + JSON.stringify(running[i]));
|
|
||||||
print(require("internal").killExternal(running[i].pid, abortSignal));
|
|
||||||
}
|
|
||||||
res.crashed = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
// whether or not there was an error
|
|
||||||
try {
|
try {
|
||||||
fs.write(testOutputDirectory + '/UNITTEST_RESULT_EXECUTIVE_SUMMARY.json', String(res.status), true);
|
rp.writeReports(options, result);
|
||||||
fs.write(testOutputDirectory + '/UNITTEST_RESULT_CRASHED.json', String(res.crashed), true);
|
|
||||||
} catch (x) {
|
} catch (x) {
|
||||||
print('failed to write test result: ' + x.message);
|
print('failed to write test result: ' + x.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rp.dumpAllResults(options, result);
|
||||||
if (options.writeXmlReport) {
|
if (options.writeXmlReport) {
|
||||||
let j;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
j = JSON.stringify(res);
|
rp.analyze.saveToJunitXML(options, result);
|
||||||
} catch (err) {
|
|
||||||
j = inspect(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
fs.write(testOutputDirectory + '/UNITTEST_RESULT.json', j, true);
|
|
||||||
|
|
||||||
try {
|
|
||||||
let isCluster = false;
|
|
||||||
let isRocksDb = false;
|
|
||||||
let prefix = '';
|
|
||||||
if (options.hasOwnProperty('prefix')) {
|
|
||||||
prefix = options.prefix;
|
|
||||||
}
|
|
||||||
if (options.hasOwnProperty('cluster') && options.cluster) {
|
|
||||||
isCluster = true;
|
|
||||||
}
|
|
||||||
if (options.hasOwnProperty('storageEngine')) {
|
|
||||||
isRocksDb = (options.storageEngine === 'rocksdb');
|
|
||||||
}
|
|
||||||
|
|
||||||
resultsToXml(res, 'UNITTEST_RESULT_' + prefix, isCluster, isRocksDb);
|
|
||||||
} catch (x) {
|
} catch (x) {
|
||||||
print('exception while serializing status xml!');
|
print('exception while serializing status xml!');
|
||||||
print(x.message);
|
print(x.message);
|
||||||
print(x.stack);
|
print(x.stack);
|
||||||
print(inspect(res));
|
print(inspect(result));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// creates yaml like dump at the end
|
rp.analyze.unitTestPrettyPrintResults(options, result);
|
||||||
UnitTest.unitTestPrettyPrintResults(res, testOutputDirectory, options);
|
|
||||||
|
|
||||||
return res.status && running.length === 0;
|
return result.status;
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = main(ARGUMENTS);
|
let result = main(ARGUMENTS);
|
||||||
|
|
|
@ -66,7 +66,7 @@ RestStatus RestStatusHandler::execute() {
|
||||||
result.add("server", VPackValue("arango"));
|
result.add("server", VPackValue("arango"));
|
||||||
result.add("version", VPackValue(ARANGODB_VERSION));
|
result.add("version", VPackValue(ARANGODB_VERSION));
|
||||||
|
|
||||||
result.add("pid", VPackValue(Thread::currentProcessId()));
|
result.add("pid", VPackValue(static_cast<TRI_vpack_pid_t>(Thread::currentProcessId())));
|
||||||
|
|
||||||
#ifdef USE_ENTERPRISE
|
#ifdef USE_ENTERPRISE
|
||||||
result.add("license", VPackValue("enterprise"));
|
result.add("license", VPackValue("enterprise"));
|
||||||
|
|
|
@ -0,0 +1,116 @@
|
||||||
|
/* jshint strict: false, sub: true */
|
||||||
|
/* global print, 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 Wilfried Goesgens
|
||||||
|
// //////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
const internal = require('internal');
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
const download = internal.download;
|
||||||
|
const time = internal.time;
|
||||||
|
const sleep = internal.sleep;
|
||||||
|
|
||||||
|
const pu = require('@arangodb/process-utils');
|
||||||
|
|
||||||
|
const instanceInfo = JSON.parse(internal.env.INSTANCEINFO);
|
||||||
|
const options = JSON.parse(internal.env.OPTIONS);
|
||||||
|
const outfn = fs.join(instanceInfo.rootDir, 'stats.jsonl');
|
||||||
|
const opts = Object.assign(pu.makeAuthorizationHeaders(options),
|
||||||
|
{ method: 'GET' });
|
||||||
|
|
||||||
|
while(true) {
|
||||||
|
let state = {
|
||||||
|
state: true,
|
||||||
|
before: time(),
|
||||||
|
delta: [],
|
||||||
|
fails: []
|
||||||
|
};
|
||||||
|
let results = [];
|
||||||
|
for (let i = 0; i < 60; i++) {
|
||||||
|
const before = time();
|
||||||
|
let oneSet = { state: true };
|
||||||
|
results.push(oneSet);
|
||||||
|
instanceInfo.arangods.forEach(arangod => {
|
||||||
|
let serverId = arangod.role + '_' + arangod.port;
|
||||||
|
let beforeCall = time();
|
||||||
|
let procStats = pu.getProcessStats(arangod.pid);
|
||||||
|
if (arangod.role === "agent") {
|
||||||
|
let reply = download(arangod.url + '/_api/version', '', opts);
|
||||||
|
if (reply.hasOwnProperty('error') || reply.code !== 200) {
|
||||||
|
print("fail: " + JSON.stringify(reply) +
|
||||||
|
" - ps before: " + JSON.stringify(procStats) +
|
||||||
|
" - ps now: " + JSON.stringify(pu.getProcessStats(arangod.pid)));
|
||||||
|
state.state = false;
|
||||||
|
oneSet.state = false;
|
||||||
|
oneSet[serverId] = {
|
||||||
|
error: true,
|
||||||
|
start: beforeCall,
|
||||||
|
delta: time() - beforeCall
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
let statisticsReply = JSON.parse(reply.body);
|
||||||
|
oneSet[serverId] = {
|
||||||
|
error: false,
|
||||||
|
start: beforeCall,
|
||||||
|
delta: time() - beforeCall
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let reply = download(arangod.url + '/_admin/statistics', '', opts);
|
||||||
|
if (reply.hasOwnProperty('error') || reply.code !== 200) {
|
||||||
|
print("fail: " + JSON.stringify(reply) +
|
||||||
|
" - ps before: " + JSON.stringify(procStats) +
|
||||||
|
" - ps now: " + JSON.stringify(pu.getProcessStats(arangod.pid)));
|
||||||
|
state.state = false;
|
||||||
|
oneSet.state = false;
|
||||||
|
oneSet[serverId] = {
|
||||||
|
error: true,
|
||||||
|
start: beforeCall,
|
||||||
|
delta: time() - beforeCall
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
let statisticsReply = JSON.parse(reply.body);
|
||||||
|
oneSet[serverId] = {
|
||||||
|
error: false,
|
||||||
|
start: beforeCall,
|
||||||
|
delta: time() - beforeCall,
|
||||||
|
uptime: statisticsReply.server.uptime
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
state['delta'].push(time() - before);
|
||||||
|
if (state.delta > 1000) {
|
||||||
|
print("marking FAIL since it took to long");
|
||||||
|
state.state = false;
|
||||||
|
}
|
||||||
|
if (!oneSet.state) {
|
||||||
|
state.fails.push(oneSet);
|
||||||
|
}
|
||||||
|
sleep(1);
|
||||||
|
}
|
||||||
|
fs.append(outfn, JSON.stringify(state) + "\n");
|
||||||
|
|
||||||
|
}
|
|
@ -28,6 +28,7 @@
|
||||||
/* Modules: */
|
/* Modules: */
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
const rp = require('@arangodb/result-processing');
|
||||||
const yaml = require('js-yaml');
|
const yaml = require('js-yaml');
|
||||||
const internal = require('internal');
|
const internal = require('internal');
|
||||||
const crashUtils = require('@arangodb/crash-utils');
|
const crashUtils = require('@arangodb/crash-utils');
|
||||||
|
@ -39,6 +40,7 @@ const executeExternal = internal.executeExternal;
|
||||||
const executeExternalAndWait = internal.executeExternalAndWait;
|
const executeExternalAndWait = internal.executeExternalAndWait;
|
||||||
const killExternal = internal.killExternal;
|
const killExternal = internal.killExternal;
|
||||||
const statusExternal = internal.statusExternal;
|
const statusExternal = internal.statusExternal;
|
||||||
|
const statisticsExternal = internal.statisticsExternal;
|
||||||
const base64Encode = internal.base64Encode;
|
const base64Encode = internal.base64Encode;
|
||||||
const testPort = internal.testPort;
|
const testPort = internal.testPort;
|
||||||
const download = internal.download;
|
const download = internal.download;
|
||||||
|
@ -234,14 +236,22 @@ function setupBinaries (builddir, buildType, configDir) {
|
||||||
BIN_DIR = fs.join(TOP_DIR, BIN_DIR);
|
BIN_DIR = fs.join(TOP_DIR, BIN_DIR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!fs.exists(BIN_DIR)) {
|
||||||
|
BIN_DIR = fs.join(TOP_DIR, 'bin');
|
||||||
|
}
|
||||||
|
|
||||||
UNITTESTS_DIR = fs.join(fs.join(builddir, 'tests'));
|
UNITTESTS_DIR = fs.join(fs.join(builddir, 'tests'));
|
||||||
if (!fs.exists(UNITTESTS_DIR)) {
|
if (!fs.exists(UNITTESTS_DIR)) {
|
||||||
UNITTESTS_DIR = fs.join(TOP_DIR, UNITTESTS_DIR);
|
UNITTESTS_DIR = fs.join(TOP_DIR, UNITTESTS_DIR);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buildType !== '') {
|
if (buildType !== '') {
|
||||||
BIN_DIR = fs.join(BIN_DIR, buildType);
|
if (fs.exists(fs.join(BIN_DIR, buildType))) {
|
||||||
UNITTESTS_DIR = fs.join(UNITTESTS_DIR, buildType);
|
BIN_DIR = fs.join(BIN_DIR, buildType);
|
||||||
|
}
|
||||||
|
if (fs.exists(fs.join(UNITTESTS_DIR, buildType))) {
|
||||||
|
UNITTESTS_DIR = fs.join(UNITTESTS_DIR, buildType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ARANGOBACKUP_BIN = fs.join(BIN_DIR, 'arangobackup' + executableExt);
|
ARANGOBACKUP_BIN = fs.join(BIN_DIR, 'arangobackup' + executableExt);
|
||||||
|
@ -587,6 +597,88 @@ function killWithCoreDump (options, instanceInfo) {
|
||||||
instanceInfo.exitStatus = killExternal(instanceInfo.pid, abortSignal);
|
instanceInfo.exitStatus = killExternal(instanceInfo.pid, abortSignal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// //////////////////////////////////////////////////////////////////////////////
|
||||||
|
// / @brief aggregates information from /proc about the SUT
|
||||||
|
// //////////////////////////////////////////////////////////////////////////////
|
||||||
|
function getProcessStats(pid) {
|
||||||
|
let processStats = statisticsExternal(pid);
|
||||||
|
if (platform === 'linux') {
|
||||||
|
let pidStr = "" + pid;
|
||||||
|
let ioraw;
|
||||||
|
try {
|
||||||
|
ioraw = fs.readBuffer(fs.join('/', 'proc', pidStr, 'io'));
|
||||||
|
} catch (x) {
|
||||||
|
print(x.stack);
|
||||||
|
throw x;
|
||||||
|
}
|
||||||
|
let lineStart = 0;
|
||||||
|
let maxBuffer = ioraw.length;
|
||||||
|
for (let j = 0; j < maxBuffer; j++) {
|
||||||
|
if (ioraw[j] === 10) { // \n
|
||||||
|
const line = ioraw.asciiSlice(lineStart, j);
|
||||||
|
lineStart = j + 1;
|
||||||
|
let x = line.split(":");
|
||||||
|
processStats[x[0]] = parseInt(x[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return processStats;
|
||||||
|
}
|
||||||
|
|
||||||
|
function initProcessStats(instanceInfo) {
|
||||||
|
instanceInfo.arangods.forEach((arangod) => {
|
||||||
|
arangod.stats = getProcessStats(arangod.pid);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDeltaProcessStats(instanceInfo) {
|
||||||
|
let deltaStats = {};
|
||||||
|
instanceInfo.arangods.forEach((arangod) => {
|
||||||
|
let newStats = getProcessStats(arangod.pid);
|
||||||
|
let myDeltaStats = {};
|
||||||
|
for (let key in arangod.stats) {
|
||||||
|
myDeltaStats[key] = newStats[key] - arangod.stats[key];
|
||||||
|
}
|
||||||
|
deltaStats[arangod.pid + '_' + arangod.role] = myDeltaStats;
|
||||||
|
arangod.stats = newStats;
|
||||||
|
});
|
||||||
|
return deltaStats;
|
||||||
|
}
|
||||||
|
|
||||||
|
function summarizeStats(deltaStats) {
|
||||||
|
let sumStats = {};
|
||||||
|
for (let instance in deltaStats) {
|
||||||
|
for (let key in deltaStats[instance]) {
|
||||||
|
if (!sumStats.hasOwnProperty(key)) {
|
||||||
|
sumStats[key] = 0;
|
||||||
|
}
|
||||||
|
sumStats[key] += deltaStats[instance][key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sumStats;
|
||||||
|
}
|
||||||
|
|
||||||
|
// //////////////////////////////////////////////////////////////////////////////
|
||||||
|
// / @brief if we forgot about processes, this safe guard will clean up and mark failed
|
||||||
|
// //////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
function killRemainingProcesses(results) {
|
||||||
|
let running = internal.getExternalSpawned();
|
||||||
|
results.status = results.status && (running.length === 0);
|
||||||
|
let i = 0;
|
||||||
|
for (i = 0; i < running.length; i++) {
|
||||||
|
let status = require("internal").statusExternal(running[i].pid, false);
|
||||||
|
if (status.status === "TERMINATED") {
|
||||||
|
print("process exited without us joining it (marking crashy): " + JSON.stringify(running[i]) + JSON.stringify(status));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
print("Killing remaining process & marking crashy: " + JSON.stringify(running[i]));
|
||||||
|
print(killExternal(running[i].pid, abortSignal));
|
||||||
|
}
|
||||||
|
results.crashed = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// //////////////////////////////////////////////////////////////////////////////
|
// //////////////////////////////////////////////////////////////////////////////
|
||||||
// / @brief executes a command and waits for result
|
// / @brief executes a command and waits for result
|
||||||
// //////////////////////////////////////////////////////////////////////////////
|
// //////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -1039,7 +1131,12 @@ function abortSurvivors(arangod, options) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkInstanceAlive (instanceInfo, options) {
|
function checkInstanceAlive (instanceInfo, options) {
|
||||||
if (options.activefailover && instanceInfo.hasOwnProperty('authOpts')) {
|
if (options.activefailover &&
|
||||||
|
instanceInfo.hasOwnProperty('authOpts') &&
|
||||||
|
(instanceInfo.url !== instanceInfo.agencyUrl)
|
||||||
|
) {
|
||||||
|
// only detect a leader after we actually know one has been started.
|
||||||
|
// the agency won't tell us anything about leaders.
|
||||||
let d = detectCurrentLeader(instanceInfo);
|
let d = detectCurrentLeader(instanceInfo);
|
||||||
if (instanceInfo.endpoint !== d.endpoint) {
|
if (instanceInfo.endpoint !== d.endpoint) {
|
||||||
print(Date() + ' failover has happened, leader is no more! Marking Crashy!');
|
print(Date() + ' failover has happened, leader is no more! Marking Crashy!');
|
||||||
|
@ -1270,6 +1367,16 @@ function shutdownInstance (instanceInfo, options, forceTerminate) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.cluster && instanceInfo.hasOwnProperty('clusterHealthMonitor')) {
|
||||||
|
try {
|
||||||
|
instanceInfo.clusterHealthMonitor['kill'] = killExternal(instanceInfo.clusterHealthMonitor.pid);
|
||||||
|
instanceInfo.clusterHealthMonitor['statusExternal'] = statusExternal(instanceInfo.clusterHealthMonitor.pid, true);
|
||||||
|
}
|
||||||
|
catch (x) {
|
||||||
|
print(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Shut down all non-agency servers:
|
// Shut down all non-agency servers:
|
||||||
const n = instanceInfo.arangods.length;
|
const n = instanceInfo.arangods.length;
|
||||||
|
|
||||||
|
@ -1431,7 +1538,6 @@ function detectCurrentLeader(instanceInfo) {
|
||||||
throw "Leader is not selected";
|
throw "Leader is not selected";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
opts['method'] = 'GET';
|
opts['method'] = 'GET';
|
||||||
reply = download(instanceInfo.url + '/_api/cluster/endpoints', '', opts);
|
reply = download(instanceInfo.url + '/_api/cluster/endpoints', '', opts);
|
||||||
let res;
|
let res;
|
||||||
|
@ -1452,9 +1558,6 @@ function detectCurrentLeader(instanceInfo) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkClusterAlive(options, instanceInfo, addArgs) {
|
function checkClusterAlive(options, instanceInfo, addArgs) {
|
||||||
// disabled because not in use (jslint)
|
|
||||||
// let coordinatorUrl = instanceInfo.url
|
|
||||||
// let response
|
|
||||||
let httpOptions = makeAuthorizationHeaders(options);
|
let httpOptions = makeAuthorizationHeaders(options);
|
||||||
httpOptions.method = 'POST';
|
httpOptions.method = 'POST';
|
||||||
httpOptions.returnBodyOnError = true;
|
httpOptions.returnBodyOnError = true;
|
||||||
|
@ -1512,6 +1615,7 @@ function checkClusterAlive(options, instanceInfo, addArgs) {
|
||||||
throw new Error('cluster startup timed out after 10 minutes!');
|
throw new Error('cluster startup timed out after 10 minutes!');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
print("Determining server IDs");
|
print("Determining server IDs");
|
||||||
instanceInfo.arangods.forEach(arangod => {
|
instanceInfo.arangods.forEach(arangod => {
|
||||||
// agents don't support the ID call...
|
// agents don't support the ID call...
|
||||||
|
@ -1524,6 +1628,19 @@ function checkClusterAlive(options, instanceInfo, addArgs) {
|
||||||
arangod.id = res['id'];
|
arangod.id = res['id'];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if ((options.cluster || options.agency) &&
|
||||||
|
!instanceInfo.hasOwnProperty('clusterHealthMonitor') &&
|
||||||
|
!options.disableClusterMonitor) {
|
||||||
|
print("spawning cluster health inspector");
|
||||||
|
internal.env.INSTANCEINFO = JSON.stringify(instanceInfo);
|
||||||
|
internal.env.OPTIONS = JSON.stringify(options);
|
||||||
|
let args = makeArgsArangosh(options);
|
||||||
|
args['javascript.execute'] = fs.join('js', 'client', 'modules', '@arangodb', 'clusterstats.js');
|
||||||
|
const argv = toArgv(args);
|
||||||
|
instanceInfo.clusterHealthMonitor = executeExternal(ARANGOSH_BIN, argv);
|
||||||
|
instanceInfo.clusterHealthMonitorFile = fs.join(instanceInfo.rootDir, 'stats.jsonl');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// //////////////////////////////////////////////////////////////////////////////
|
// //////////////////////////////////////////////////////////////////////////////
|
||||||
// / @brief starts an instance
|
// / @brief starts an instance
|
||||||
|
@ -1710,6 +1827,7 @@ function launchFinalize(options, instanceInfo, startTime) {
|
||||||
}
|
}
|
||||||
print(processInfo.join('\n') + '\n');
|
print(processInfo.join('\n') + '\n');
|
||||||
internal.sleep(options.sleepBeforeStart);
|
internal.sleep(options.sleepBeforeStart);
|
||||||
|
initProcessStats(instanceInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
// //////////////////////////////////////////////////////////////////////////////
|
// //////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -1848,6 +1966,9 @@ function startInstanceAgency (instanceInfo, protocol, options, addArgs, rootDir)
|
||||||
instanceInfo.role = 'agent';
|
instanceInfo.role = 'agent';
|
||||||
print(Date() + ' Agency Endpoint: ' + instanceInfo.endpoint);
|
print(Date() + ' Agency Endpoint: ' + instanceInfo.endpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkClusterAlive(options, instanceInfo, addArgs);
|
||||||
|
|
||||||
return instanceInfo;
|
return instanceInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1988,6 +2109,12 @@ function reStartInstance(options, instanceInfo, moreArgs) {
|
||||||
launchFinalize(options, instanceInfo, startTime);
|
launchFinalize(options, instanceInfo, startTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function aggregateFatalErrors(currentTest) {
|
||||||
|
if (serverCrashedLocal) {
|
||||||
|
rp.addFailRunsMessage(currentTest, serverFailMessagesLocal);
|
||||||
|
serverFailMessagesLocal = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
// exports.analyzeServerCrash = analyzeServerCrash;
|
// exports.analyzeServerCrash = analyzeServerCrash;
|
||||||
exports.makeArgs = {
|
exports.makeArgs = {
|
||||||
arangod: makeArgsArangod,
|
arangod: makeArgsArangod,
|
||||||
|
@ -2008,6 +2135,7 @@ exports.coverageEnvironment = coverageEnvironment;
|
||||||
|
|
||||||
exports.executeArangod = executeArangod;
|
exports.executeArangod = executeArangod;
|
||||||
exports.executeAndWait = executeAndWait;
|
exports.executeAndWait = executeAndWait;
|
||||||
|
exports.killRemainingProcesses = killRemainingProcesses;
|
||||||
exports.stopProcdump = stopProcdump;
|
exports.stopProcdump = stopProcdump;
|
||||||
|
|
||||||
exports.createBaseConfig = createBaseConfigBuilder;
|
exports.createBaseConfig = createBaseConfigBuilder;
|
||||||
|
@ -2021,14 +2149,17 @@ exports.run = {
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.shutdownInstance = shutdownInstance;
|
exports.shutdownInstance = shutdownInstance;
|
||||||
|
exports.getProcessStats = getProcessStats;
|
||||||
|
exports.getDeltaProcessStats = getDeltaProcessStats;
|
||||||
|
exports.summarizeStats = summarizeStats;
|
||||||
exports.startArango = startArango;
|
exports.startArango = startArango;
|
||||||
exports.startInstance = startInstance;
|
exports.startInstance = startInstance;
|
||||||
exports.reStartInstance = reStartInstance;
|
exports.reStartInstance = reStartInstance;
|
||||||
exports.setupBinaries = setupBinaries;
|
exports.setupBinaries = setupBinaries;
|
||||||
exports.executableExt = executableExt;
|
exports.executableExt = executableExt;
|
||||||
exports.serverCrashed = serverCrashedLocal;
|
exports.serverCrashed = serverCrashedLocal;
|
||||||
exports.serverFailMessages = serverFailMessagesLocal;
|
|
||||||
|
|
||||||
|
exports.aggregateFatalErrors = aggregateFatalErrors;
|
||||||
exports.cleanupDBDirectoriesAppend = cleanupDBDirectoriesAppend;
|
exports.cleanupDBDirectoriesAppend = cleanupDBDirectoriesAppend;
|
||||||
exports.cleanupDBDirectories = cleanupDBDirectories;
|
exports.cleanupDBDirectories = cleanupDBDirectories;
|
||||||
exports.cleanupLastDirectory = cleanupLastDirectory;
|
exports.cleanupLastDirectory = cleanupLastDirectory;
|
||||||
|
|
|
@ -0,0 +1,769 @@
|
||||||
|
/* jshint strict: false, sub: true */
|
||||||
|
/* global print */
|
||||||
|
'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 Wilfried Goesgens
|
||||||
|
// //////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// //////////////////////////////////////////////////////////////////////////////
|
||||||
|
// / @brief internal members of the results
|
||||||
|
// //////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
const internal = require('internal');
|
||||||
|
const inspect = internal.inspect;
|
||||||
|
const fs = require('fs');
|
||||||
|
const pu = require('@arangodb/process-utils');
|
||||||
|
const cu = require('@arangodb/crash-utils');
|
||||||
|
const yaml = require('js-yaml');
|
||||||
|
|
||||||
|
/* Constants: */
|
||||||
|
const BLUE = internal.COLORS.COLOR_BLUE;
|
||||||
|
const CYAN = internal.COLORS.COLOR_CYAN;
|
||||||
|
const GREEN = internal.COLORS.COLOR_GREEN;
|
||||||
|
const RED = internal.COLORS.COLOR_RED;
|
||||||
|
const RESET = internal.COLORS.COLOR_RESET;
|
||||||
|
const YELLOW = internal.COLORS.COLOR_YELLOW;
|
||||||
|
|
||||||
|
const internalMembers = [
|
||||||
|
'code',
|
||||||
|
'error',
|
||||||
|
'status',
|
||||||
|
'duration',
|
||||||
|
'failed',
|
||||||
|
'total',
|
||||||
|
'crashed',
|
||||||
|
'ok',
|
||||||
|
'message',
|
||||||
|
'suiteName',
|
||||||
|
'processStats',
|
||||||
|
'startupTime',
|
||||||
|
'testDuration',
|
||||||
|
'shutdownTime',
|
||||||
|
'totalSetUp',
|
||||||
|
'totalTearDown',
|
||||||
|
'setUpDuration',
|
||||||
|
'setUpAllDuration',
|
||||||
|
'teardownAllDuration'
|
||||||
|
];
|
||||||
|
|
||||||
|
function skipInternalMember (r, a) {
|
||||||
|
return !r.hasOwnProperty(a) || internalMembers.indexOf(a) !== -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function makePathGeneric (path) {
|
||||||
|
return path.split(fs.pathSeparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
let failedRuns = {
|
||||||
|
};
|
||||||
|
|
||||||
|
function gatherFailed(result) {
|
||||||
|
return Object.values(result).reduce(function(prev, testCase) {
|
||||||
|
if (testCase instanceof Object) {
|
||||||
|
return prev + !testCase.status;
|
||||||
|
} else {
|
||||||
|
return prev;
|
||||||
|
}
|
||||||
|
}, 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function gatherStatus(result) {
|
||||||
|
return Object.values(result).reduce(function(prev, testCase) {
|
||||||
|
if (testCase instanceof Object) {
|
||||||
|
return prev && testCase.status;
|
||||||
|
} else {
|
||||||
|
return prev;
|
||||||
|
}
|
||||||
|
}, true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// //////////////////////////////////////////////////////////////////////////////
|
||||||
|
// / @brief pretty prints the result
|
||||||
|
// //////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
function fancyTimeFormat(time)
|
||||||
|
{
|
||||||
|
// Hours, minutes and seconds
|
||||||
|
var hrs = ~~(time / 3600);
|
||||||
|
var mins = ~~((time % 3600) / 60);
|
||||||
|
var secs = ~~time % 60;
|
||||||
|
|
||||||
|
// Output like "1:01" or "4:03:59" or "123:03:59"
|
||||||
|
var ret = "";
|
||||||
|
|
||||||
|
if (hrs > 0) {
|
||||||
|
ret += "" + hrs + ":" + (mins < 10 ? "0" : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
ret += "" + mins + ":" + (secs < 10 ? "0" : "");
|
||||||
|
ret += "" + secs;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function iterateTestResults(options, results, state, handlers) {
|
||||||
|
let bucketName = "";
|
||||||
|
if (options.testBuckets) {
|
||||||
|
let n = options.testBuckets.split('/');
|
||||||
|
bucketName = "_" + n[1];
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
/* jshint forin: false */
|
||||||
|
// i.e. shell_server_aql
|
||||||
|
for (let testRunName in results) {
|
||||||
|
if (skipInternalMember(results, testRunName)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let testRun = results[testRunName];
|
||||||
|
|
||||||
|
if (handlers.hasOwnProperty('testRun')) {
|
||||||
|
handlers.testRun(options, state, testRun, testRunName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// i.e. tests/js/server/aql/aql-cross.js
|
||||||
|
for (let testSuiteName in testRun) {
|
||||||
|
if (skipInternalMember(testRun, testSuiteName)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let testSuite = testRun[testSuiteName];
|
||||||
|
|
||||||
|
if (handlers.hasOwnProperty('testSuite')) {
|
||||||
|
handlers.testSuite(options, state, testSuite, testSuiteName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// i.e. testDocument1
|
||||||
|
for (let testName in testSuite) {
|
||||||
|
if (skipInternalMember(testSuite, testName)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let testCase = testSuite[testName];
|
||||||
|
|
||||||
|
if (handlers.hasOwnProperty('testCase')) {
|
||||||
|
handlers.testCase(options, state, testCase, testName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (handlers.hasOwnProperty('endTestSuite')) {
|
||||||
|
handlers.endTestSuite(options, state, testSuite, testSuiteName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (handlers.hasOwnProperty('endTestRun')) {
|
||||||
|
handlers.endTestRun(options, state, testRun, testRunName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (x) {
|
||||||
|
print("Exception occured in running tests: " + x.message + "\n" + x.stack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function locateFailState(options, results) {
|
||||||
|
let failedStates = {
|
||||||
|
state: true,
|
||||||
|
failCount: 0,
|
||||||
|
thisFailedTestCount: 0,
|
||||||
|
runFailedTestCount: 0,
|
||||||
|
currentSuccess: true,
|
||||||
|
failedTests: {},
|
||||||
|
thisFailedTests: []
|
||||||
|
};
|
||||||
|
iterateTestResults(options, results, failedStates, {
|
||||||
|
testRun: function(options, state, testRun, testRunName) {
|
||||||
|
if (!testRun.state) {
|
||||||
|
state.state = false;
|
||||||
|
state.thisFailedTestCount = testRun.failed;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
testCase: function(options, state, testCase, testCaseName) {
|
||||||
|
if (!testCase.state) {
|
||||||
|
state.state = false;
|
||||||
|
state.runFailedTestCount ++;
|
||||||
|
state.thisFailedTests.push(testCaseName);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
endTestRun: function(options, state, testRun, testRunName) {
|
||||||
|
if (state.thisFailedTestCount !== 0) {
|
||||||
|
if (state.thisFailedTestCount !== state.runFailedTestCount) {
|
||||||
|
// todo error message
|
||||||
|
}
|
||||||
|
state.failedTests[testRunName] = state.thisFailedTests;
|
||||||
|
state.failCount += state.runFailedTestCount;
|
||||||
|
|
||||||
|
state.thisFailedTests = [];
|
||||||
|
state.thisFailedTestCount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// //////////////////////////////////////////////////////////////////////////////
|
||||||
|
// @brief converts results to XML representation
|
||||||
|
// //////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
function saveToJunitXML(options, results) {
|
||||||
|
function xmlEscape (s) {
|
||||||
|
return s.replace(/[<>&"]/g, function (c) {
|
||||||
|
return '&' + {
|
||||||
|
'<': 'lt',
|
||||||
|
'>': 'gt',
|
||||||
|
'&': 'amp',
|
||||||
|
'"': 'quot'
|
||||||
|
}[c] + ';';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildXml () {
|
||||||
|
let xml = ['<?xml version="1.0" encoding="UTF-8"?>\n'];
|
||||||
|
|
||||||
|
xml.text = function (s) {
|
||||||
|
Array.prototype.push.call(this, s);
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
xml.elem = function (tagName, attrs, close) {
|
||||||
|
this.text('<').text(tagName);
|
||||||
|
attrs = attrs || {};
|
||||||
|
|
||||||
|
for (let a in attrs) {
|
||||||
|
if (attrs.hasOwnProperty(a)) {
|
||||||
|
this.text(' ').text(a).text('="')
|
||||||
|
.text(xmlEscape(String(attrs[a]))).text('"');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (close) {
|
||||||
|
this.text('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.text('>\n');
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
return xml;
|
||||||
|
}
|
||||||
|
let xmlState = {
|
||||||
|
xml: undefined,
|
||||||
|
xmlName: '',
|
||||||
|
testRunName: '',
|
||||||
|
seenTestCases: false,
|
||||||
|
};
|
||||||
|
let prefix = options.cluster ? 'CL_' : '' +
|
||||||
|
(options.storageEngine === 'rocksdb') ? 'RX_': 'MM_';
|
||||||
|
iterateTestResults(options, results, xmlState, {
|
||||||
|
testRun: function(options, state, testRun, testRunName) {state.testRunName = testRunName;},
|
||||||
|
testSuite: function(options, state, testSuite, testSuiteName) {
|
||||||
|
let total = 0;
|
||||||
|
state.seenTestCases = false;
|
||||||
|
state.xml = buildXml();
|
||||||
|
state.xmlName = prefix + state.testRunName + '_' + makePathGeneric(testSuiteName).join('_');
|
||||||
|
if (testSuite.hasOwnProperty('total')) {
|
||||||
|
total = testSuite.total;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.xml.elem('testsuite', {
|
||||||
|
errors: 0,
|
||||||
|
failures: testSuite.failed,
|
||||||
|
tests: total,
|
||||||
|
name: state.xmlName,
|
||||||
|
time: 0 + testSuite.duration
|
||||||
|
});
|
||||||
|
|
||||||
|
},
|
||||||
|
testCase: function(options, state, testCase, testCaseName) {
|
||||||
|
const success = (testCase.status === true);
|
||||||
|
|
||||||
|
state.xml.elem('testcase', {
|
||||||
|
name: prefix + testCaseName,
|
||||||
|
time: 0 + testCase.duration
|
||||||
|
}, success);
|
||||||
|
|
||||||
|
state.seenTestCases = true;
|
||||||
|
if (!success) {
|
||||||
|
state.xml.elem('failure');
|
||||||
|
state.xml.text('<![CDATA[' + testCase.message + ']]>\n');
|
||||||
|
state.xml.elem('/failure');
|
||||||
|
state.xml.elem('/testcase');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
endTestSuite: function(options, state, testSuite, testSuiteName) {
|
||||||
|
if (!state.seenTestCases) {
|
||||||
|
if (testSuite.failed === 0) {
|
||||||
|
state.xml.elem('testcase', {
|
||||||
|
name: 'all_tests_in_' + state.xmlName,
|
||||||
|
time: 0 + testSuite.duration
|
||||||
|
}, true);
|
||||||
|
} else {
|
||||||
|
state.xml.elem('testcase', {
|
||||||
|
name: 'all_tests_in_' + state.xmlName,
|
||||||
|
failures: testSuite.failuresFound,
|
||||||
|
time: 0 + testSuite.duration
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state.xml.elem('/testsuite');
|
||||||
|
fs.write(fs.join(options.testOutputDirectory,
|
||||||
|
'UNITTEST_RESULT_' + state.xmlName + '.xml'),
|
||||||
|
state.xml.join(''));
|
||||||
|
|
||||||
|
},
|
||||||
|
endTestRun: function(options, state, testRun, testRunName) {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function unitTestPrettyPrintResults (options, results) {
|
||||||
|
let onlyFailedMessages = '';
|
||||||
|
let failedMessages = '';
|
||||||
|
let SuccessMessages = '';
|
||||||
|
let failedSuiteCount = 0;
|
||||||
|
let failedTestsCount = 0;
|
||||||
|
let successCases = {};
|
||||||
|
let failedCases = {};
|
||||||
|
let failsOfOneSuite = {};
|
||||||
|
let failsOfOneSuiteCount = 0;
|
||||||
|
let bucketName = "";
|
||||||
|
let testRunStatistics = "";
|
||||||
|
let isSuccess = true;
|
||||||
|
let suiteSuccess = true;
|
||||||
|
|
||||||
|
if (options.testBuckets) {
|
||||||
|
let n = options.testBuckets.split('/');
|
||||||
|
bucketName = "_" + n[1];
|
||||||
|
}
|
||||||
|
function testCaseMessage (test) {
|
||||||
|
if (typeof test.message === 'object' && test.message.hasOwnProperty('body')) {
|
||||||
|
return test.message.body;
|
||||||
|
} else {
|
||||||
|
return test.message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let failedStates = {
|
||||||
|
state: true,
|
||||||
|
failCount: 0,
|
||||||
|
thisFailedTestCount: 0,
|
||||||
|
runFailedTestCount: 0,
|
||||||
|
currentSuccess: true,
|
||||||
|
failedTests: {},
|
||||||
|
thisFailedTests: []
|
||||||
|
};
|
||||||
|
|
||||||
|
iterateTestResults(options, results, failedStates, {
|
||||||
|
testRun: function(options, state, testRun, testRunName) {
|
||||||
|
},
|
||||||
|
testSuite: function(options, state, testSuite, testSuiteName) {
|
||||||
|
if (testSuite.status) {
|
||||||
|
successCases[testSuiteName] = testSuite;
|
||||||
|
} else {
|
||||||
|
++failedSuiteCount;
|
||||||
|
isSuccess = false;
|
||||||
|
if (testSuite.hasOwnProperty('message')) {
|
||||||
|
failedCases[testSuiteName] = {
|
||||||
|
test: testCaseMessage(testSuite)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
testCase: function(options, state, testCase, testCaseName) {
|
||||||
|
if (!testCase.status) {
|
||||||
|
failsOfOneSuite[testCaseName] = testCaseMessage(testCase);
|
||||||
|
failsOfOneSuiteCount ++;
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
endTestSuite: function(options, state, testSuite, testSuiteName) {
|
||||||
|
failedTestsCount++;
|
||||||
|
if (failsOfOneSuiteCount !== 0) {
|
||||||
|
failedCases[testSuiteName] = failsOfOneSuite;
|
||||||
|
failsOfOneSuite = {};
|
||||||
|
failsOfOneSuiteCount = 0;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
endTestRun: function(options, state, testRun, testRunName) {
|
||||||
|
if (isSuccess) {
|
||||||
|
SuccessMessages += '* Test "' + testRunName + bucketName + '"\n';
|
||||||
|
|
||||||
|
for (let name in successCases) {
|
||||||
|
if (!successCases.hasOwnProperty(name)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let details = successCases[name];
|
||||||
|
|
||||||
|
if (details.skipped) {
|
||||||
|
SuccessMessages += YELLOW + ' [SKIPPED] ' + name + RESET + '\n';
|
||||||
|
} else {
|
||||||
|
SuccessMessages += GREEN + ' [SUCCESS] ' + name + RESET + '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let m = '* Test "' + testRunName + bucketName + '"\n';
|
||||||
|
onlyFailedMessages += m;
|
||||||
|
failedMessages += m;
|
||||||
|
|
||||||
|
for (let name in successCases) {
|
||||||
|
if (!successCases.hasOwnProperty(name)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let details = successCases[name];
|
||||||
|
|
||||||
|
if (details.skipped) {
|
||||||
|
m = ' [SKIPPED] ' + name;
|
||||||
|
failedMessages += YELLOW + m + RESET + '\n';
|
||||||
|
onlyFailedMessages += m + '\n';
|
||||||
|
} else {
|
||||||
|
failedMessages += GREEN + ' [SUCCESS] ' + name + RESET + '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let name in failedCases) {
|
||||||
|
if (!failedCases.hasOwnProperty(name)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
m = ' [FAILED] ' + name;
|
||||||
|
failedMessages += RED + m + RESET + '\n\n';
|
||||||
|
onlyFailedMessages += m + '\n\n';
|
||||||
|
|
||||||
|
let details = failedCases[name];
|
||||||
|
|
||||||
|
let count = 0;
|
||||||
|
for (let one in details) {
|
||||||
|
if (!details.hasOwnProperty(one)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count > 0) {
|
||||||
|
failedMessages += '\n';
|
||||||
|
onlyFailedMessages += '\n';
|
||||||
|
}
|
||||||
|
m = ' "' + one + '" failed: ' + details[one];
|
||||||
|
failedMessages += RED + m + RESET + '\n\n';
|
||||||
|
onlyFailedMessages += m + '\n\n';
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
isSuccess = true;
|
||||||
|
successCases = {};
|
||||||
|
failedCases = {};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let color,statusMessage;
|
||||||
|
let crashedText = '';
|
||||||
|
let crashText = '';
|
||||||
|
let failText = '';
|
||||||
|
if (results.status === true) {
|
||||||
|
color = GREEN;
|
||||||
|
statusMessage = 'Success';
|
||||||
|
} else {
|
||||||
|
color = RED;
|
||||||
|
statusMessage = 'Fail';
|
||||||
|
}
|
||||||
|
if (results.crashed === true) {
|
||||||
|
color = RED;
|
||||||
|
for (let failed in failedRuns) {
|
||||||
|
crashedText += ' [' + failed + '] : ' + failedRuns[failed].replace(/^/mg, ' ');
|
||||||
|
}
|
||||||
|
crashedText += "\nMarking crashy!";
|
||||||
|
crashText = color + crashedText + RESET;
|
||||||
|
}
|
||||||
|
if (results.status !== true) {
|
||||||
|
failText = '\n Suites failed: ' + failedSuiteCount + ' Tests Failed: ' + failedTestsCount;
|
||||||
|
onlyFailedMessages += failText + '\n';
|
||||||
|
failText = RED + failText + RESET;
|
||||||
|
}
|
||||||
|
if (cu.GDB_OUTPUT !== '') {
|
||||||
|
// write more verbose failures to the testFailureText file
|
||||||
|
onlyFailedMessages += '\n\n' + cu.GDB_OUTPUT;
|
||||||
|
}
|
||||||
|
print(`
|
||||||
|
${YELLOW}================================================================================'
|
||||||
|
TEST RESULTS
|
||||||
|
================================================================================${RESET}
|
||||||
|
${SuccessMessages}
|
||||||
|
${failedMessages}${color} * Overall state: ${statusMessage}${RESET}${crashText}${failText}`);
|
||||||
|
|
||||||
|
onlyFailedMessages;
|
||||||
|
if (crashedText !== '') {
|
||||||
|
onlyFailedMessages += '\n' + crashedText;
|
||||||
|
}
|
||||||
|
fs.write(options.testOutputDirectory + options.testFailureText, onlyFailedMessages);
|
||||||
|
}
|
||||||
|
|
||||||
|
function locateLongRunning(options, results) {
|
||||||
|
let testRunStatistics = "";
|
||||||
|
let sortedByDuration = [];
|
||||||
|
|
||||||
|
let failedStates = {
|
||||||
|
state: true,
|
||||||
|
failCount: 0,
|
||||||
|
thisFailedTestCount: 0,
|
||||||
|
runFailedTestCount: 0,
|
||||||
|
currentSuccess: true,
|
||||||
|
failedTests: {},
|
||||||
|
thisFailedTests: []
|
||||||
|
};
|
||||||
|
iterateTestResults(options, results, failedStates, {
|
||||||
|
testRun: function(options, state, testRun, testRunName) {
|
||||||
|
let startup = testRun['startupTime'];
|
||||||
|
let testDuration = testRun['testDuration'];
|
||||||
|
let shutdown = testRun['shutdownTime'];
|
||||||
|
let color = GREEN;
|
||||||
|
if (((startup + shutdown) * 10) > testDuration) {
|
||||||
|
color = RED;
|
||||||
|
}
|
||||||
|
|
||||||
|
testRunStatistics += `${color}${testRunName} - startup [${startup}] => run [${testDuration}] => shutdown [${shutdown}]${RESET}
|
||||||
|
`;
|
||||||
|
},
|
||||||
|
testSuite: function(options, state, testSuite, testSuiteName) {
|
||||||
|
if (testSuite.hasOwnProperty('duration') && testSuite.duration !== 0) {
|
||||||
|
sortedByDuration.push(
|
||||||
|
{
|
||||||
|
testName: testSuiteName,
|
||||||
|
duration: testSuite.duration,
|
||||||
|
test: testSuite,
|
||||||
|
count: Object.keys(testSuite).filter(testCase => skipInternalMember(testSuite, testCase)).length
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
print(RED + "This test doesn't have a duration: " + testSuiteName + "\n" + JSON.stringify(testSuite) + RESET);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
testCase: function(options, state, testCase, testCaseName) {
|
||||||
|
},
|
||||||
|
endTestSuite: function(options, state, testSuite, testSuiteName) {
|
||||||
|
},
|
||||||
|
endTestRun: function(options, state, testRun, testRunName) {
|
||||||
|
sortedByDuration.sort(function(a, b) {
|
||||||
|
return a.duration - b.duration;
|
||||||
|
});
|
||||||
|
let results = {};
|
||||||
|
for (let i = sortedByDuration.length - 1; (i >= 0) && (i > sortedByDuration.length - 11); i --) {
|
||||||
|
let key = " - " + fancyTimeFormat(sortedByDuration[i].duration / 1000) + " - " +
|
||||||
|
sortedByDuration[i].count + " - " +
|
||||||
|
sortedByDuration[i].testName.replace('/\\/g', '/');
|
||||||
|
let testCases = [];
|
||||||
|
let thisTestSuite = sortedByDuration[i];
|
||||||
|
for (let testName in thisTestSuite.test) {
|
||||||
|
if (skipInternalMember(thisTestSuite.test, testName)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let test = thisTestSuite.test[testName];
|
||||||
|
let duration = 0;
|
||||||
|
if (test.hasOwnProperty('duration')) {
|
||||||
|
duration += test.duration;
|
||||||
|
}
|
||||||
|
if (test.hasOwnProperty('setUpDuration')) {
|
||||||
|
duration += test.setUpDuration;
|
||||||
|
}
|
||||||
|
if (test.hasOwnProperty('tearDownDuration')) {
|
||||||
|
duration += test.tearDownDuration;
|
||||||
|
}
|
||||||
|
testCases.push({
|
||||||
|
testName: testName,
|
||||||
|
duration: duration
|
||||||
|
});
|
||||||
|
}
|
||||||
|
testCases.sort(function(a, b) {
|
||||||
|
return a.duration - b.duration;
|
||||||
|
});
|
||||||
|
|
||||||
|
let statistics = [];
|
||||||
|
for (let j = Object.keys(testCases).length - 1; (j >= 0) && (j > Object.keys(testCases).length - 11); j --) {
|
||||||
|
statistics.push(fancyTimeFormat(testCases[j].duration / 1000) + " - " + testCases[j].testName);
|
||||||
|
}
|
||||||
|
results[key] = {
|
||||||
|
'processStatistics': pu.sumarizeStats(thisTestSuite.test['processStats']),
|
||||||
|
'stats': statistics
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
testRunStatistics += yaml.safeDump(results);
|
||||||
|
sortedByDuration = [];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
print(testRunStatistics);
|
||||||
|
}
|
||||||
|
|
||||||
|
function locateLongSetupTeardown(options, results) {
|
||||||
|
let testRunStatistics = " Setup | Run | tests | setupAll | suite name\n";
|
||||||
|
let sortedByDuration = [];
|
||||||
|
let failedStates = {};
|
||||||
|
|
||||||
|
iterateTestResults(options, results, failedStates, {
|
||||||
|
testRun: function(options, state, testRun, testRunName) {
|
||||||
|
},
|
||||||
|
testSuite: function(options, state, testSuite, testSuiteName) {
|
||||||
|
let setupAllDuration = 0;
|
||||||
|
if (testSuite.hasOwnProperty('setUpAllDuration')) {
|
||||||
|
Object.keys(testSuite.setUpAllDuration).forEach(testName => {
|
||||||
|
setupAllDuration += testSuite.setUpAllDuration[testName];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let hasSetupAll = setupAllDuration !== 0;
|
||||||
|
|
||||||
|
if (testSuite.hasOwnProperty('totalSetUp') &&
|
||||||
|
testSuite.hasOwnProperty('totalTearDown')) {
|
||||||
|
sortedByDuration.push({
|
||||||
|
testName: testSuiteName,
|
||||||
|
setupTearDown: testSuite.totalSetUp + testSuite.totalTearDown,
|
||||||
|
duration: testSuite.duration,
|
||||||
|
hasSetupAll: hasSetupAll,
|
||||||
|
count: Object.keys(testSuite).filter(testCase => ! skipInternalMember(testSuite, testCase)).length,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
print(RED + "This test doesn't have a duration: " + testSuiteName + "\n" + JSON.stringify(testSuite) + RESET);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
testCase: function(options, state, testCase, testCaseName) {
|
||||||
|
},
|
||||||
|
endTestSuite: function(options, state, testSuite, testSuiteName) {
|
||||||
|
},
|
||||||
|
endTestRun: function(options, state, testRun, testRunName) {
|
||||||
|
sortedByDuration.sort(function(a, b) {
|
||||||
|
return a.setupTearDown - b.setupTearDown;
|
||||||
|
});
|
||||||
|
let results = [];
|
||||||
|
for (let i = sortedByDuration.length - 1; (i >= 0) && (i > sortedByDuration.length - 11); i --) {
|
||||||
|
let key = " " +
|
||||||
|
fancyTimeFormat(sortedByDuration[i].setupTearDown / 1000) + " | " +
|
||||||
|
fancyTimeFormat(sortedByDuration[i].duration / 1000) + " | " +
|
||||||
|
sortedByDuration[i].count + " | " +
|
||||||
|
sortedByDuration[i].hasSetupAll + " | " +
|
||||||
|
sortedByDuration[i].testName.replace('/\\/g', '/');
|
||||||
|
results.push(key);
|
||||||
|
}
|
||||||
|
testRunStatistics += yaml.safeDump(results);
|
||||||
|
sortedByDuration = [];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
print(testRunStatistics);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function locateShortServerLife(options, results) {
|
||||||
|
let rc = true;
|
||||||
|
let testRunStatistics = "";
|
||||||
|
let sortedByDuration = [];
|
||||||
|
|
||||||
|
let failedStates = {
|
||||||
|
state: true,
|
||||||
|
failCount: 0,
|
||||||
|
thisFailedTestCount: 0,
|
||||||
|
runFailedTestCount: 0,
|
||||||
|
currentSuccess: true,
|
||||||
|
failedTests: {},
|
||||||
|
thisFailedTests: []
|
||||||
|
};
|
||||||
|
iterateTestResults(options, results, failedStates, {
|
||||||
|
testRun: function(options, state, testRun, testRunName) {
|
||||||
|
if (testRun.hasOwnProperty('startupTime') &&
|
||||||
|
testRun.hasOwnProperty('testDuration') &&
|
||||||
|
testRun.hasOwnProperty('shutdownTime')) {
|
||||||
|
let startup = testRun['startupTime'];
|
||||||
|
let testDuration = testRun['testDuration'];
|
||||||
|
let shutdown = testRun['shutdownTime'];
|
||||||
|
let color = GREEN;
|
||||||
|
let OK = "YES";
|
||||||
|
if (((startup + shutdown) * 10) > testDuration) {
|
||||||
|
color = RED;
|
||||||
|
OK = "NOP";
|
||||||
|
rc = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
testRunStatistics += `${color}[${OK}] ${testRunName} - startup [${startup}] => run [${testDuration}] => shutdown [${shutdown}]${RESET}`;
|
||||||
|
} else {
|
||||||
|
testRunStatistics += `${YELLOW}${testRunName} doesn't have server start statistics${RESET}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
print(testRunStatistics);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// //////////////////////////////////////////////////////////////////////////////
|
||||||
|
// @brief simple status representations
|
||||||
|
// //////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
function writeDefaultReports(options, testSuites) {
|
||||||
|
fs.write(options.testOutputDirectory + '/UNITTEST_RESULT_EXECUTIVE_SUMMARY.json', "false", true);
|
||||||
|
fs.write(options.testOutputDirectory + '/UNITTEST_RESULT_CRASHED.json', "true", true);
|
||||||
|
let testFailureText = 'testfailures.txt';
|
||||||
|
if (options.hasOwnProperty('testFailureText')) {
|
||||||
|
testFailureText = options.testFailureText;
|
||||||
|
}
|
||||||
|
fs.write(fs.join(options.testOutputDirectory, testFailureText),
|
||||||
|
"Incomplete testrun with these testsuites: '" + testSuites +
|
||||||
|
"'\nand these options: " + JSON.stringify(options) + "\n");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeReports(options, results) {
|
||||||
|
fs.write(options.testOutputDirectory + '/UNITTEST_RESULT_EXECUTIVE_SUMMARY.json', String(results.status), true);
|
||||||
|
fs.write(options.testOutputDirectory + '/UNITTEST_RESULT_CRASHED.json', String(results.crashed), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function dumpAllResults(options, results) {
|
||||||
|
let j;
|
||||||
|
|
||||||
|
try {
|
||||||
|
j = JSON.stringify(results);
|
||||||
|
} catch (err) {
|
||||||
|
j = inspect(results);
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.write(options.testOutputDirectory + '/UNITTEST_RESULT.json', j, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function addFailRunsMessage(testcase, message) {
|
||||||
|
failedRuns[testcase] = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
function yamlDumpResults(options, results) {
|
||||||
|
try {
|
||||||
|
print(yaml.safeDump(JSON.parse(JSON.stringify(results))));
|
||||||
|
} catch (err) {
|
||||||
|
print(RED + 'cannot dump results: ' + String(err) + RESET);
|
||||||
|
print(RED + require('internal').inspect(results) + RESET);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.gatherStatus = gatherStatus;
|
||||||
|
exports.gatherFailed = gatherFailed;
|
||||||
|
exports.yamlDumpResults = yamlDumpResults;
|
||||||
|
exports.addFailRunsMessage = addFailRunsMessage;
|
||||||
|
exports.dumpAllResults = dumpAllResults;
|
||||||
|
exports.writeDefaultReports = writeDefaultReports;
|
||||||
|
exports.writeReports = writeReports;
|
||||||
|
|
||||||
|
exports.analyze = {
|
||||||
|
unitTestPrettyPrintResults: unitTestPrettyPrintResults,
|
||||||
|
saveToJunitXML: saveToJunitXML,
|
||||||
|
locateLongRunning: locateLongRunning,
|
||||||
|
locateShortServerLife: locateShortServerLife,
|
||||||
|
locateLongSetupTeardown: locateLongSetupTeardown,
|
||||||
|
yaml: yamlDumpResults
|
||||||
|
};
|
|
@ -103,7 +103,7 @@ function performTests (options, testList, testname, runFn, serverOptions, startS
|
||||||
let env = {};
|
let env = {};
|
||||||
let customInstanceInfos = {};
|
let customInstanceInfos = {};
|
||||||
let healthCheck = function () {return true;};
|
let healthCheck = function () {return true;};
|
||||||
|
let beforeStart = time();
|
||||||
if (startStopHandlers !== undefined && startStopHandlers.hasOwnProperty('healthCheck')) {
|
if (startStopHandlers !== undefined && startStopHandlers.hasOwnProperty('healthCheck')) {
|
||||||
healthCheck = startStopHandlers.healthCheck;
|
healthCheck = startStopHandlers.healthCheck;
|
||||||
}
|
}
|
||||||
|
@ -162,7 +162,11 @@ function performTests (options, testList, testname, runFn, serverOptions, startS
|
||||||
_.defaults(env, customInstanceInfos.postStart.env);
|
_.defaults(env, customInstanceInfos.postStart.env);
|
||||||
}
|
}
|
||||||
|
|
||||||
let results = { shutdown: true };
|
let testrunStart = time();
|
||||||
|
let results = {
|
||||||
|
shutdown: true,
|
||||||
|
startupTime: testrunStart - beforeStart
|
||||||
|
};
|
||||||
let continueTesting = true;
|
let continueTesting = true;
|
||||||
let serverDead = false;
|
let serverDead = false;
|
||||||
let count = 0;
|
let count = 0;
|
||||||
|
@ -234,6 +238,7 @@ function performTests (options, testList, testname, runFn, serverOptions, startS
|
||||||
continue;
|
continue;
|
||||||
} else if (reply.hasOwnProperty('status')) {
|
} else if (reply.hasOwnProperty('status')) {
|
||||||
results[te] = reply;
|
results[te] = reply;
|
||||||
|
results[te]['processStats'] = pu.getDeltaProcessStats(instanceInfo);
|
||||||
|
|
||||||
if (results[te].status === false) {
|
if (results[te].status === false) {
|
||||||
options.cleanup = false;
|
options.cleanup = false;
|
||||||
|
@ -399,7 +404,8 @@ function performTests (options, testList, testname, runFn, serverOptions, startS
|
||||||
results.status = true;
|
results.status = true;
|
||||||
print(RED + 'No testcase matched the filter.' + RESET);
|
print(RED + 'No testcase matched the filter.' + RESET);
|
||||||
}
|
}
|
||||||
|
let shutDownStart = time();
|
||||||
|
results['testDuration'] = shutDownStart - testrunStart;
|
||||||
print(Date() + ' Shutting down...');
|
print(Date() + ' Shutting down...');
|
||||||
if (startStopHandlers !== undefined && startStopHandlers.hasOwnProperty('preStop')) {
|
if (startStopHandlers !== undefined && startStopHandlers.hasOwnProperty('preStop')) {
|
||||||
customInstanceInfos['preStop'] = startStopHandlers.preStop(options,
|
customInstanceInfos['preStop'] = startStopHandlers.preStop(options,
|
||||||
|
@ -426,6 +432,8 @@ function performTests (options, testList, testname, runFn, serverOptions, startS
|
||||||
}
|
}
|
||||||
results.shutdown = results.shutdown && pu.shutdownInstance(instanceInfo, clonedOpts, forceTerminate);
|
results.shutdown = results.shutdown && pu.shutdownInstance(instanceInfo, clonedOpts, forceTerminate);
|
||||||
|
|
||||||
|
loadClusterTestStabilityInfo(results, instanceInfo);
|
||||||
|
|
||||||
if (startStopHandlers !== undefined && startStopHandlers.hasOwnProperty('postStop')) {
|
if (startStopHandlers !== undefined && startStopHandlers.hasOwnProperty('postStop')) {
|
||||||
customInstanceInfos['postStop'] = startStopHandlers.postStop(options,
|
customInstanceInfos['postStop'] = startStopHandlers.postStop(options,
|
||||||
serverOptions,
|
serverOptions,
|
||||||
|
@ -437,6 +445,8 @@ function performTests (options, testList, testname, runFn, serverOptions, startS
|
||||||
results.setup.message = 'custom postStop failed!';
|
results.setup.message = 'custom postStop failed!';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
results['shutdownTime'] = time() - shutDownStart;
|
||||||
|
|
||||||
print('done.');
|
print('done.');
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
|
@ -447,11 +457,6 @@ function performTests (options, testList, testname, runFn, serverOptions, startS
|
||||||
// //////////////////////////////////////////////////////////////////////////////
|
// //////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
function filterTestcaseByOptions (testname, options, whichFilter) {
|
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:
|
// These filters require a proper setup, Even if we filter by testcase:
|
||||||
if ((testname.indexOf('-mmfiles') !== -1) && options.storageEngine === 'rocksdb') {
|
if ((testname.indexOf('-mmfiles') !== -1) && options.storageEngine === 'rocksdb') {
|
||||||
whichFilter.filter = 'skip when running as rocksdb';
|
whichFilter.filter = 'skip when running as rocksdb';
|
||||||
|
@ -621,7 +626,7 @@ function scanTestPaths (paths, options) {
|
||||||
}
|
}
|
||||||
return rc;
|
return rc;
|
||||||
});
|
});
|
||||||
if (filteredTestCases.length === 0) {
|
if ((filteredTestCases.length === 0) && (options.extremeVerbosity !== 'silence')) {
|
||||||
print("No testcase matched the filter: " + JSON.stringify(allFiltered));
|
print("No testcase matched the filter: " + JSON.stringify(allFiltered));
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
@ -629,6 +634,41 @@ function scanTestPaths (paths, options) {
|
||||||
return allTestCases;
|
return allTestCases;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function loadClusterTestStabilityInfo(results, instanceInfo){
|
||||||
|
try {
|
||||||
|
if (instanceInfo.hasOwnProperty('clusterHealthMonitorFile')) {
|
||||||
|
let status = true;
|
||||||
|
let slow = [];
|
||||||
|
let buf = fs.readBuffer(instanceInfo.clusterHealthMonitorFile);
|
||||||
|
let lineStart = 0;
|
||||||
|
let maxBuffer = buf.length;
|
||||||
|
|
||||||
|
for (let j = 0; j < maxBuffer; j++) {
|
||||||
|
if (buf[j] === 10) { // \n
|
||||||
|
const line = buf.asciiSlice(lineStart, j);
|
||||||
|
lineStart = j + 1;
|
||||||
|
let val = JSON.parse(line);
|
||||||
|
if (val.state === false) {
|
||||||
|
slow.push(val);
|
||||||
|
status = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!status) {
|
||||||
|
print('found ' + slow.length + ' slow lines!');
|
||||||
|
results.status = false;
|
||||||
|
results.message += 'found ' + slow.length + ' slow lines!';
|
||||||
|
} else {
|
||||||
|
print('everything fast!');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(x) {
|
||||||
|
print(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// //////////////////////////////////////////////////////////////////////////////
|
// //////////////////////////////////////////////////////////////////////////////
|
||||||
// / @brief runs a remote unittest file using /_admin/execute
|
// / @brief runs a remote unittest file using /_admin/execute
|
||||||
// //////////////////////////////////////////////////////////////////////////////
|
// //////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -704,7 +744,7 @@ function runThere (options, instanceInfo, file) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
runThere.info = 'runThere';
|
runThere.info = 'runInArangod';
|
||||||
|
|
||||||
function readTestResult(path, rc, testCase) {
|
function readTestResult(path, rc, testCase) {
|
||||||
const jsonFN = fs.join(path, 'testresult.json');
|
const jsonFN = fs.join(path, 'testresult.json');
|
||||||
|
@ -791,7 +831,7 @@ function runInArangosh (options, instanceInfo, file, addArgs) {
|
||||||
let rc = pu.executeAndWait(pu.ARANGOSH_BIN, toArgv(args), options, 'arangosh', instanceInfo.rootDir, options.coreCheck);
|
let rc = pu.executeAndWait(pu.ARANGOSH_BIN, toArgv(args), options, 'arangosh', instanceInfo.rootDir, options.coreCheck);
|
||||||
return readTestResult(instanceInfo.rootDir, rc, args['javascript.unit-tests']);
|
return readTestResult(instanceInfo.rootDir, rc, args['javascript.unit-tests']);
|
||||||
}
|
}
|
||||||
runInArangosh.info = 'arangosh';
|
runInArangosh.info = 'runInExternalArangosh';
|
||||||
|
|
||||||
// //////////////////////////////////////////////////////////////////////////////
|
// //////////////////////////////////////////////////////////////////////////////
|
||||||
// / @brief runs a local unittest file in the current arangosh
|
// / @brief runs a local unittest file in the current arangosh
|
||||||
|
@ -834,7 +874,7 @@ function runInLocalArangosh (options, instanceInfo, file, addArgs) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
runInLocalArangosh.info = 'localarangosh';
|
runInLocalArangosh.info = 'runInLocalArangosh';
|
||||||
|
|
||||||
// //////////////////////////////////////////////////////////////////////////////
|
// //////////////////////////////////////////////////////////////////////////////
|
||||||
// / @brief runs a unittest file using rspec
|
// / @brief runs a unittest file using rspec
|
||||||
|
@ -977,7 +1017,7 @@ function runInRSpec (options, instanceInfo, file, addArgs) {
|
||||||
fs.remove(tmpname);
|
fs.remove(tmpname);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
runInRSpec.info = 'runInRSpec';
|
runInRSpec.info = 'runInLocalRSpec';
|
||||||
|
|
||||||
exports.runThere = runThere;
|
exports.runThere = runThere;
|
||||||
exports.runInArangosh = runInArangosh;
|
exports.runInArangosh = runInArangosh;
|
||||||
|
|
|
@ -23,8 +23,26 @@
|
||||||
// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||||
//
|
//
|
||||||
// @author Max Neunhoeffer
|
// @author Max Neunhoeffer
|
||||||
|
// @author Wilfried Goesgnes
|
||||||
// /////////////////////////////////////////////////////////////////////////////
|
// /////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
const _ = require('lodash');
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
const pu = require('@arangodb/process-utils');
|
||||||
|
const rp = require('@arangodb/result-processing');
|
||||||
|
const cu = require('@arangodb/crash-utils');
|
||||||
|
const tu = require('@arangodb/test-utils');
|
||||||
|
const internal = require('internal');
|
||||||
|
const platform = internal.platform;
|
||||||
|
|
||||||
|
const BLUE = internal.COLORS.COLOR_BLUE;
|
||||||
|
const CYAN = internal.COLORS.COLOR_CYAN;
|
||||||
|
const GREEN = internal.COLORS.COLOR_GREEN;
|
||||||
|
const RED = internal.COLORS.COLOR_RED;
|
||||||
|
const RESET = internal.COLORS.COLOR_RESET;
|
||||||
|
const YELLOW = internal.COLORS.COLOR_YELLOW;
|
||||||
|
|
||||||
let functionsDocumentation = {
|
let functionsDocumentation = {
|
||||||
'all': 'run all tests (marked with [x])',
|
'all': 'run all tests (marked with [x])',
|
||||||
'find': 'searches all testcases, and eventually filters them by `--test`, ' +
|
'find': 'searches all testcases, and eventually filters them by `--test`, ' +
|
||||||
|
@ -39,8 +57,6 @@ let optionsDocumentation = [
|
||||||
' The following properties of `options` are defined:',
|
' The following properties of `options` are defined:',
|
||||||
'',
|
'',
|
||||||
' - `testOutput`: set the output directory for testresults, defaults to `out`',
|
' - `testOutput`: set the output directory for testresults, defaults to `out`',
|
||||||
' - `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',
|
' - `force`: if set to true the tests are continued even if one fails',
|
||||||
'',
|
'',
|
||||||
" - `skipLogAnalysis`: don't try to crawl the server logs",
|
" - `skipLogAnalysis`: don't try to crawl the server logs",
|
||||||
|
@ -77,15 +93,17 @@ let optionsDocumentation = [
|
||||||
' - `agencySupervision`: run supervision in agency',
|
' - `agencySupervision`: run supervision in agency',
|
||||||
' - `oneTestTimeout`: how long a single testsuite (.js, .rb) should run',
|
' - `oneTestTimeout`: how long a single testsuite (.js, .rb) should run',
|
||||||
' - `isAsan`: doubles oneTestTimeot value if set to true (for ASAN-related builds)',
|
' - `isAsan`: doubles oneTestTimeot value if set to true (for ASAN-related builds)',
|
||||||
' - `test`: path to single test to execute for "single" test target',
|
' - `test`: path to single test to execute for "single" test target, ',
|
||||||
' - `cleanup`: if set to true (the default), the cluster data files',
|
' or pattern to filter for other suites',
|
||||||
' and logs are removed after termination of the test.',
|
' - `cleanup`: if set to false the data files',
|
||||||
|
' and logs are not removed after termination of the test.',
|
||||||
'',
|
'',
|
||||||
' - `protocol`: the protocol to talk to the server - [tcp (default), ssl, unix]',
|
' - `protocol`: the protocol to talk to the server - [tcp (default), ssl, unix]',
|
||||||
' - `sniff`: if we should try to launch tcpdump / windump for a testrun',
|
' - `sniff`: if we should try to launch tcpdump / windump for a testrun',
|
||||||
' false / true / sudo',
|
' false / true / sudo',
|
||||||
' - `sniffDevice`: the device tcpdump / tshark should use',
|
' - `sniffDevice`: the device tcpdump / tshark should use',
|
||||||
' - `sniffProgram`: specify your own programm',
|
' - `sniffProgram`: specify your own programm',
|
||||||
|
'',
|
||||||
' - `build`: the directory containing the binaries',
|
' - `build`: the directory containing the binaries',
|
||||||
' - `buildType`: Windows build type (Debug, Release), leave empty on linux',
|
' - `buildType`: Windows build type (Debug, Release), leave empty on linux',
|
||||||
' - `configDir`: the directory containing the config files, defaults to',
|
' - `configDir`: the directory containing the config files, defaults to',
|
||||||
|
@ -94,6 +112,8 @@ let optionsDocumentation = [
|
||||||
' - `dumpAgencyOnError`: if we should create an agency dump if an error occurs',
|
' - `dumpAgencyOnError`: if we should create an agency dump if an error occurs',
|
||||||
' - `prefix`: prefix for the tests in the xml reports',
|
' - `prefix`: prefix for the tests in the xml reports',
|
||||||
'',
|
'',
|
||||||
|
' - `disableClusterMonitor`: if set to false, an arangosh is started that will send',
|
||||||
|
' keepalive requests to all cluster instances, and report on error',
|
||||||
' - `disableMonitor`: if set to true on windows, procdump will not be attached.',
|
' - `disableMonitor`: if set to true on windows, procdump will not be attached.',
|
||||||
' - `rr`: if set to true arangod instances are run with rr',
|
' - `rr`: if set to true arangod instances are run with rr',
|
||||||
' - `exceptionFilter`: on windows you can use this to abort tests on specific exceptions',
|
' - `exceptionFilter`: on windows you can use this to abort tests on specific exceptions',
|
||||||
|
@ -135,7 +155,7 @@ const optionsDefaults = {
|
||||||
'agencyWaitForSync': false,
|
'agencyWaitForSync': false,
|
||||||
'agencySupervision': true,
|
'agencySupervision': true,
|
||||||
'build': '',
|
'build': '',
|
||||||
'buildType': '',
|
'buildType': (platform.substr(0, 3) === 'win') ? 'RelWithDebInfo':'',
|
||||||
'cleanup': true,
|
'cleanup': true,
|
||||||
'cluster': false,
|
'cluster': false,
|
||||||
'concurrency': 3,
|
'concurrency': 3,
|
||||||
|
@ -150,7 +170,6 @@ const optionsDefaults = {
|
||||||
'force': true,
|
'force': true,
|
||||||
'getSockStat': false,
|
'getSockStat': false,
|
||||||
'arangosearch':true,
|
'arangosearch':true,
|
||||||
'jsonReply': false,
|
|
||||||
'loopEternal': false,
|
'loopEternal': false,
|
||||||
'loopSleepSec': 1,
|
'loopSleepSec': 1,
|
||||||
'loopSleepWhen': 1,
|
'loopSleepWhen': 1,
|
||||||
|
@ -181,6 +200,7 @@ const optionsDefaults = {
|
||||||
'storageEngine': 'rocksdb',
|
'storageEngine': 'rocksdb',
|
||||||
'test': undefined,
|
'test': undefined,
|
||||||
'testBuckets': undefined,
|
'testBuckets': undefined,
|
||||||
|
'testOutputDirectory': 'out/',
|
||||||
'useReconnect': true,
|
'useReconnect': true,
|
||||||
'username': 'root',
|
'username': 'root',
|
||||||
'valgrind': false,
|
'valgrind': false,
|
||||||
|
@ -193,266 +213,14 @@ const optionsDefaults = {
|
||||||
'testFailureText': 'testfailures.txt',
|
'testFailureText': 'testfailures.txt',
|
||||||
'testCase': undefined,
|
'testCase': undefined,
|
||||||
'disableMonitor': false,
|
'disableMonitor': false,
|
||||||
|
'disableClusterMonitor': true,
|
||||||
'sleepBeforeStart' : 0,
|
'sleepBeforeStart' : 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
const _ = require('lodash');
|
let globalStatus = true;
|
||||||
const fs = require('fs');
|
|
||||||
const yaml = require('js-yaml');
|
|
||||||
|
|
||||||
const pu = require('@arangodb/process-utils');
|
let allTests = [];
|
||||||
const cu = require('@arangodb/crash-utils');
|
let testFuncs = {};
|
||||||
const tu = require('@arangodb/test-utils');
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
// //////////////////////////////////////////////////////////////////////////////
|
|
||||||
// / @brief test functions for all
|
|
||||||
// //////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
let failedRuns = {
|
|
||||||
};
|
|
||||||
|
|
||||||
let allTests = [
|
|
||||||
];
|
|
||||||
|
|
||||||
// /////////////////////////////////////////////////////////////////////////////
|
|
||||||
// blacklisting
|
|
||||||
// /////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
let useBlacklist = false;
|
|
||||||
|
|
||||||
let blacklistTests = {};
|
|
||||||
|
|
||||||
function skipTest(type, name) {
|
|
||||||
let ntype = type.toUpperCase();
|
|
||||||
return useBlacklist && !!blacklistTests[ntype + ":" + name];
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadBlacklist(name) {
|
|
||||||
let content = fs.read("BLACKLIST");
|
|
||||||
let a = _.filter(
|
|
||||||
_.map(content.split("\n"),
|
|
||||||
function(x) {return x.trim();}),
|
|
||||||
function(x) {return x.length > 0 && x[0] !== '#';});
|
|
||||||
|
|
||||||
for (let i = 0; i < a.length; ++i) {
|
|
||||||
blacklistTests[a[i]] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
useBlacklist = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// //////////////////////////////////////////////////////////////////////////////
|
|
||||||
// / @brief TEST: all
|
|
||||||
// //////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
let testFuncs = {
|
|
||||||
'all': function () {}
|
|
||||||
};
|
|
||||||
|
|
||||||
// //////////////////////////////////////////////////////////////////////////////
|
|
||||||
// / @brief internal members of the results
|
|
||||||
// //////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
const internalMembers = [
|
|
||||||
'code',
|
|
||||||
'error',
|
|
||||||
'status',
|
|
||||||
'duration',
|
|
||||||
'failed',
|
|
||||||
'total',
|
|
||||||
'crashed',
|
|
||||||
'ok',
|
|
||||||
'message',
|
|
||||||
'suiteName'
|
|
||||||
];
|
|
||||||
|
|
||||||
// //////////////////////////////////////////////////////////////////////////////
|
|
||||||
// / @brief pretty prints the result
|
|
||||||
// //////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
function testCaseMessage (test) {
|
|
||||||
if (typeof test.message === 'object' && test.message.hasOwnProperty('body')) {
|
|
||||||
return test.message.body;
|
|
||||||
} else {
|
|
||||||
return test.message;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function unitTestPrettyPrintResults (res, testOutputDirectory, options) {
|
|
||||||
function skipInternalMember (r, a) {
|
|
||||||
return !r.hasOwnProperty(a) || internalMembers.indexOf(a) !== -1;
|
|
||||||
}
|
|
||||||
print(YELLOW + '================================================================================');
|
|
||||||
print('TEST RESULTS');
|
|
||||||
print('================================================================================\n' + RESET);
|
|
||||||
|
|
||||||
let failedSuite = 0;
|
|
||||||
let failedTests = 0;
|
|
||||||
|
|
||||||
let onlyFailedMessages = '';
|
|
||||||
let failedMessages = '';
|
|
||||||
let SuccessMessages = '';
|
|
||||||
let bucketName = "";
|
|
||||||
if (options.testBuckets) {
|
|
||||||
let n = options.testBuckets.split('/');
|
|
||||||
bucketName = "_" + n[1];
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
/* jshint forin: false */
|
|
||||||
for (let testrunName in res) {
|
|
||||||
if (skipInternalMember(res, testrunName)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let testrun = res[testrunName];
|
|
||||||
|
|
||||||
let successCases = {};
|
|
||||||
let failedCases = {};
|
|
||||||
let isSuccess = true;
|
|
||||||
|
|
||||||
for (let testName in testrun) {
|
|
||||||
if (skipInternalMember(testrun, testName)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let test = testrun[testName];
|
|
||||||
|
|
||||||
if (test.status) {
|
|
||||||
successCases[testName] = test;
|
|
||||||
} else {
|
|
||||||
isSuccess = false;
|
|
||||||
++failedSuite;
|
|
||||||
|
|
||||||
if (test.hasOwnProperty('message')) {
|
|
||||||
++failedTests;
|
|
||||||
failedCases[testName] = {
|
|
||||||
test: testCaseMessage(test)
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
let fails = failedCases[testName] = {};
|
|
||||||
|
|
||||||
for (let oneName in test) {
|
|
||||||
if (skipInternalMember(test, oneName)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let oneTest = test[oneName];
|
|
||||||
|
|
||||||
if (!oneTest.status) {
|
|
||||||
++failedTests;
|
|
||||||
fails[oneName] = testCaseMessage(oneTest);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isSuccess) {
|
|
||||||
SuccessMessages += '* Test "' + testrunName + bucketName + '"\n';
|
|
||||||
|
|
||||||
for (let name in successCases) {
|
|
||||||
if (!successCases.hasOwnProperty(name)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let details = successCases[name];
|
|
||||||
|
|
||||||
if (details.skipped) {
|
|
||||||
SuccessMessages += YELLOW + ' [SKIPPED] ' + name + RESET + '\n';
|
|
||||||
} else {
|
|
||||||
SuccessMessages += GREEN + ' [SUCCESS] ' + name + RESET + '\n';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let m = '* Test "' + testrunName + bucketName + '"\n';
|
|
||||||
onlyFailedMessages += m;
|
|
||||||
failedMessages += m;
|
|
||||||
|
|
||||||
for (let name in successCases) {
|
|
||||||
if (!successCases.hasOwnProperty(name)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let details = successCases[name];
|
|
||||||
|
|
||||||
if (details.skipped) {
|
|
||||||
failedMessages += YELLOW + ' [SKIPPED] ' + name + RESET + '\n';
|
|
||||||
onlyFailedMessages += ' [SKIPPED] ' + name + '\n';
|
|
||||||
} else {
|
|
||||||
failedMessages += GREEN + ' [SUCCESS] ' + name + RESET + '\n';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let name in failedCases) {
|
|
||||||
if (!failedCases.hasOwnProperty(name)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
failedMessages += RED + ' [FAILED] ' + name + RESET + '\n\n';
|
|
||||||
onlyFailedMessages += ' [FAILED] ' + name + '\n\n';
|
|
||||||
|
|
||||||
let details = failedCases[name];
|
|
||||||
|
|
||||||
let count = 0;
|
|
||||||
for (let one in details) {
|
|
||||||
if (!details.hasOwnProperty(one)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count > 0) {
|
|
||||||
failedMessages += '\n';
|
|
||||||
onlyFailedMessages += '\n';
|
|
||||||
}
|
|
||||||
failedMessages += RED + ' "' + one + '" failed: ' + details[one] + RESET + '\n\n';
|
|
||||||
onlyFailedMessages += ' "' + one + '" failed: ' + details[one] + '\n\n';
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
print(SuccessMessages);
|
|
||||||
print(failedMessages);
|
|
||||||
/* jshint forin: true */
|
|
||||||
|
|
||||||
let color = (!res.crashed && res.status === true) ? GREEN : RED;
|
|
||||||
let crashText = '';
|
|
||||||
let crashedText = '';
|
|
||||||
if (res.crashed === true) {
|
|
||||||
for (let failed in failedRuns) {
|
|
||||||
crashedText += ' [' + failed + '] : ' + failedRuns[failed].replace(/^/mg, ' ');
|
|
||||||
}
|
|
||||||
crashedText += "\nMarking crashy!";
|
|
||||||
crashText = RED + crashedText + RESET;
|
|
||||||
}
|
|
||||||
print('\n' + color + '* Overall state: ' + ((res.status === true) ? 'Success' : 'Fail') + RESET + crashText);
|
|
||||||
|
|
||||||
let failText = '';
|
|
||||||
if (res.status !== true) {
|
|
||||||
failText = ' Suites failed: ' + failedSuite + ' Tests Failed: ' + failedTests;
|
|
||||||
print(color + failText + RESET);
|
|
||||||
}
|
|
||||||
|
|
||||||
failedMessages = onlyFailedMessages;
|
|
||||||
if (crashedText !== '') {
|
|
||||||
failedMessages += '\n' + crashedText;
|
|
||||||
}
|
|
||||||
if (cu.GDB_OUTPUT !== '' || failText !== '') {
|
|
||||||
failedMessages += '\n\n' + cu.GDB_OUTPUT + failText + '\n';
|
|
||||||
}
|
|
||||||
fs.write(testOutputDirectory + options.testFailureText, failedMessages);
|
|
||||||
} catch (x) {
|
|
||||||
print('exception caught while pretty printing result: ');
|
|
||||||
print(x.message);
|
|
||||||
print(JSON.stringify(res));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// //////////////////////////////////////////////////////////////////////////////
|
// //////////////////////////////////////////////////////////////////////////////
|
||||||
// / @brief print usage information
|
// / @brief print usage information
|
||||||
|
@ -501,7 +269,9 @@ function findTestCases(options) {
|
||||||
let allTestFiles = {};
|
let allTestFiles = {};
|
||||||
for (let testSuiteName in allTestPaths) {
|
for (let testSuiteName in allTestPaths) {
|
||||||
var myList = [];
|
var myList = [];
|
||||||
let files = tu.scanTestPaths(allTestPaths[testSuiteName], options);
|
var _opts = _.clone(options);
|
||||||
|
_opts.extremeVerbosity = 'silence';
|
||||||
|
let files = tu.scanTestPaths(allTestPaths[testSuiteName], _opts);
|
||||||
if (options.hasOwnProperty('test') && (typeof (options.test) !== 'undefined')) {
|
if (options.hasOwnProperty('test') && (typeof (options.test) !== 'undefined')) {
|
||||||
for (let j = 0; j < files.length; j++) {
|
for (let j = 0; j < files.length; j++) {
|
||||||
let foo = {};
|
let foo = {};
|
||||||
|
@ -557,7 +327,6 @@ function findTest(options) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function autoTest(options) {
|
function autoTest(options) {
|
||||||
if (!options.hasOwnProperty('test') || (typeof (options.test) === 'undefined')) {
|
if (!options.hasOwnProperty('test') || (typeof (options.test) === 'undefined')) {
|
||||||
return {
|
return {
|
||||||
|
@ -620,16 +389,13 @@ function loadTestSuites () {
|
||||||
throw x;
|
throw x;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
testFuncs['all'] = allTests;
|
||||||
testFuncs['find'] = findTest;
|
testFuncs['find'] = findTest;
|
||||||
testFuncs['auto'] = autoTest;
|
testFuncs['auto'] = autoTest;
|
||||||
}
|
}
|
||||||
|
|
||||||
let globalStatus = true;
|
function translateTestList(cases) {
|
||||||
|
|
||||||
function iterateTests(cases, options, jsonReply) {
|
|
||||||
// tests to run
|
|
||||||
let caselist = [];
|
let caselist = [];
|
||||||
|
|
||||||
const expandWildcard = ( name ) => {
|
const expandWildcard = ( name ) => {
|
||||||
if (!name.endsWith('*')) {
|
if (!name.endsWith('*')) {
|
||||||
return name;
|
return name;
|
||||||
|
@ -640,29 +406,18 @@ function iterateTests(cases, options, jsonReply) {
|
||||||
|
|
||||||
for (let n = 0; n < cases.length; ++n) {
|
for (let n = 0; n < cases.length; ++n) {
|
||||||
let splitted = expandWildcard(cases[n]).split(/[,;|]/);
|
let splitted = expandWildcard(cases[n]).split(/[,;|]/);
|
||||||
|
|
||||||
for (let m = 0; m < splitted.length; ++m) {
|
for (let m = 0; m < splitted.length; ++m) {
|
||||||
let which = splitted[m];
|
let which = splitted[m];
|
||||||
|
|
||||||
if (which === 'all') {
|
if (testFuncs.hasOwnProperty(which)) {
|
||||||
caselist = caselist.concat(allTests);
|
|
||||||
} else if (testFuncs.hasOwnProperty(which)) {
|
|
||||||
caselist.push(which);
|
caselist.push(which);
|
||||||
} else {
|
} else {
|
||||||
print('Unknown test "' + which + '"\nKnown tests are: ' + Object.keys(testFuncs).sort().join(', '));
|
print('Unknown test "' + which + '"\nKnown tests are: ' + Object.keys(testFuncs).sort().join(', '));
|
||||||
|
throw new Error("USAGE ERROR");
|
||||||
return {
|
|
||||||
status: false
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Expand meta tests like ldap, all
|
||||||
let results = {};
|
|
||||||
let cleanup = true;
|
|
||||||
|
|
||||||
// real ugly hack. there are some suites which are just placeholders
|
|
||||||
// for other suites
|
|
||||||
caselist = (function() {
|
caselist = (function() {
|
||||||
let flattened = [];
|
let flattened = [];
|
||||||
for (let n = 0; n < caselist.length; ++n) {
|
for (let n = 0; n < caselist.length; ++n) {
|
||||||
|
@ -675,12 +430,27 @@ function iterateTests(cases, options, jsonReply) {
|
||||||
}
|
}
|
||||||
return flattened;
|
return flattened;
|
||||||
})();
|
})();
|
||||||
|
if (cases === undefined || cases.length === 0) {
|
||||||
|
printUsage();
|
||||||
|
|
||||||
|
print('\nFATAL: "which" is undefined\n');
|
||||||
|
throw new Error("USAGE ERROR");
|
||||||
|
}
|
||||||
|
return caselist;
|
||||||
|
}
|
||||||
|
|
||||||
|
function iterateTests(cases, options) {
|
||||||
|
// tests to run
|
||||||
|
let caselist = [];
|
||||||
|
|
||||||
|
let results = {};
|
||||||
|
let cleanup = true;
|
||||||
|
|
||||||
|
caselist = translateTestList(cases);
|
||||||
// running all tests
|
// running all tests
|
||||||
for (let n = 0; n < caselist.length; ++n) {
|
for (let n = 0; n < caselist.length; ++n) {
|
||||||
const currentTest = caselist[n];
|
const currentTest = caselist[n];
|
||||||
var localOptions = _.cloneDeep(options);
|
var localOptions = _.cloneDeep(options);
|
||||||
localOptions.skipTest = skipTest;
|
|
||||||
let printTestName = currentTest;
|
let printTestName = currentTest;
|
||||||
if (options.testBuckets) {
|
if (options.testBuckets) {
|
||||||
printTestName += " - " + options.testBuckets;
|
printTestName += " - " + options.testBuckets;
|
||||||
|
@ -697,34 +467,23 @@ function iterateTests(cases, options, jsonReply) {
|
||||||
let status = true;
|
let status = true;
|
||||||
let shutdownSuccess = true;
|
let shutdownSuccess = true;
|
||||||
|
|
||||||
if (skipTest("SUITE", currentTest)) {
|
result = testFuncs[currentTest](localOptions);
|
||||||
result = {
|
// grrr...normalize structure
|
||||||
failed: 0,
|
delete result.status;
|
||||||
status: true,
|
delete result.failed;
|
||||||
crashed: false,
|
delete result.crashed;
|
||||||
};
|
if (result.hasOwnProperty('shutdown')) {
|
||||||
|
shutdownSuccess = result['shutdown'];
|
||||||
print(YELLOW + "[SKIPPED] " + currentTest + RESET + "\n");
|
delete result.shutdown;
|
||||||
} else {
|
|
||||||
result = testFuncs[currentTest](localOptions);
|
|
||||||
// grrr...normalize structure
|
|
||||||
delete result.status;
|
|
||||||
delete result.failed;
|
|
||||||
delete result.crashed;
|
|
||||||
if (result.hasOwnProperty('shutdown')) {
|
|
||||||
shutdownSuccess = result['shutdown'];
|
|
||||||
delete result.shutdown;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = Object.values(result).every(testCase => testCase.status === true);
|
|
||||||
let failed = Object.values(result).reduce((prev, testCase) => prev + !testCase.status, 0);
|
|
||||||
if (!status) {
|
|
||||||
globalStatus = false;
|
|
||||||
}
|
|
||||||
result.failed = failed;
|
|
||||||
result.status = status;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
status = rp.gatherStatus(result);
|
||||||
|
let failed = rp.gatherFailed(result);
|
||||||
|
if (!status) {
|
||||||
|
globalStatus = false;
|
||||||
|
}
|
||||||
|
result.failed = failed;
|
||||||
|
result.status = status;
|
||||||
results[currentTest] = result;
|
results[currentTest] = result;
|
||||||
|
|
||||||
if (status && localOptions.cleanup && shutdownSuccess ) {
|
if (status && localOptions.cleanup && shutdownSuccess ) {
|
||||||
|
@ -732,11 +491,7 @@ function iterateTests(cases, options, jsonReply) {
|
||||||
} else {
|
} else {
|
||||||
cleanup = false;
|
cleanup = false;
|
||||||
}
|
}
|
||||||
|
pu.aggregateFatalErrors(currentTest);
|
||||||
if (pu.serverCrashed) {
|
|
||||||
failedRuns[currentTest] = pu.serverFailMessages;
|
|
||||||
pu.serverFailMessages = "";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
results.status = globalStatus;
|
results.status = globalStatus;
|
||||||
|
@ -755,14 +510,8 @@ function iterateTests(cases, options, jsonReply) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.extremeVerbosity === true) {
|
if (options.extremeVerbosity === true) {
|
||||||
try {
|
rp.yamlDumpResults(options, results);
|
||||||
print(yaml.safeDump(JSON.parse(JSON.stringify(results))));
|
|
||||||
} catch (err) {
|
|
||||||
print(RED + 'cannot dump results: ' + String(err) + RESET);
|
|
||||||
print(RED + require('internal').inspect(results) + RESET);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -779,20 +528,10 @@ function unitTest (cases, options) {
|
||||||
if (typeof options !== 'object') {
|
if (typeof options !== 'object') {
|
||||||
options = {};
|
options = {};
|
||||||
}
|
}
|
||||||
loadTestSuites();
|
loadTestSuites(options);
|
||||||
|
// testsuites may register more defaults...
|
||||||
_.defaults(options, optionsDefaults);
|
_.defaults(options, optionsDefaults);
|
||||||
|
|
||||||
if (cases === undefined || cases.length === 0) {
|
|
||||||
printUsage();
|
|
||||||
|
|
||||||
print('FATAL: "which" is undefined\n');
|
|
||||||
|
|
||||||
return {
|
|
||||||
status: false,
|
|
||||||
crashed: false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
pu.setupBinaries(options.build, options.buildType, options.configDir);
|
pu.setupBinaries(options.build, options.buildType, options.configDir);
|
||||||
}
|
}
|
||||||
|
@ -808,25 +547,18 @@ function unitTest (cases, options) {
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const jsonReply = options.jsonReply;
|
|
||||||
delete options.jsonReply;
|
|
||||||
|
|
||||||
let results = iterateTests(cases, options, jsonReply);
|
if ((cases.length === 1) && cases[0] === 'auto') {
|
||||||
|
return autoTest(options);
|
||||||
if (jsonReply === true) {
|
|
||||||
return results;
|
|
||||||
} else {
|
} else {
|
||||||
return globalStatus;
|
return iterateTests(cases, options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// /////////////////////////////////////////////////////////////////////////////
|
// /////////////////////////////////////////////////////////////////////////////
|
||||||
// exports
|
// exports
|
||||||
// /////////////////////////////////////////////////////////////////////////////
|
// /////////////////////////////////////////////////////////////////////////////
|
||||||
|
exports.optionsDefaults = optionsDefaults;
|
||||||
exports.unitTest = unitTest;
|
exports.unitTest = unitTest;
|
||||||
|
|
||||||
exports.internalMembers = internalMembers;
|
|
||||||
exports.testFuncs = testFuncs;
|
exports.testFuncs = testFuncs;
|
||||||
exports.unitTestPrettyPrintResults = unitTestPrettyPrintResults;
|
|
||||||
exports.loadBlacklist = loadBlacklist;
|
|
||||||
|
|
|
@ -50,7 +50,6 @@ function agency (options) {
|
||||||
|
|
||||||
options.agency = true;
|
options.agency = true;
|
||||||
options.cluster = false;
|
options.cluster = false;
|
||||||
|
|
||||||
let results = tu.performTests(options, testCases, 'agency', tu.runInArangosh);
|
let results = tu.performTests(options, testCases, 'agency', tu.runInArangosh);
|
||||||
|
|
||||||
options.agency = saveAgency;
|
options.agency = saveAgency;
|
||||||
|
|
|
@ -74,6 +74,7 @@ function auditLog(onServer) {
|
||||||
'server.authentication': 'true',
|
'server.authentication': 'true',
|
||||||
'server.jwt-secret': 'haxxmann',
|
'server.jwt-secret': 'haxxmann',
|
||||||
'log.level': 'audit-authentication=info',
|
'log.level': 'audit-authentication=info',
|
||||||
|
'log.force-direct': true
|
||||||
};
|
};
|
||||||
|
|
||||||
print(CYAN + 'Audit log server tests...' + RESET);
|
print(CYAN + 'Audit log server tests...' + RESET);
|
||||||
|
|
|
@ -99,89 +99,79 @@ function config (options) {
|
||||||
print('absolute config tests');
|
print('absolute config tests');
|
||||||
print('--------------------------------------------------------------------------------');
|
print('--------------------------------------------------------------------------------');
|
||||||
|
|
||||||
if (options.skipTest('TEST', 'config.absolute')) {
|
// we append one cleanup directory for the invoking logic...
|
||||||
print(YELLOW + "[SKIPPED] config.absolute" + RESET + "\n");
|
let dummyDir = fs.join(fs.getTempPath(), 'configdummy');
|
||||||
results.absolute.skipped = true;
|
fs.makeDirectory(dummyDir);
|
||||||
} else {
|
pu.cleanupDBDirectoriesAppend(dummyDir);
|
||||||
// we append one cleanup directory for the invoking logic...
|
|
||||||
let dummyDir = fs.join(fs.getTempPath(), 'configdummy');
|
|
||||||
fs.makeDirectory(dummyDir);
|
|
||||||
pu.cleanupDBDirectoriesAppend(dummyDir);
|
|
||||||
|
|
||||||
let startTime = time();
|
let startTime = time();
|
||||||
|
|
||||||
for (let i = 0; i < ts.length; i++) {
|
for (let i = 0; i < ts.length; i++) {
|
||||||
const test = ts[i];
|
const test = ts[i];
|
||||||
print(CYAN + 'checking "' + test + '"' + RESET);
|
print(CYAN + 'checking "' + test + '"' + RESET);
|
||||||
|
|
||||||
const args = {
|
const args = {
|
||||||
'configuration': fs.join(pu.CONFIG_ARANGODB_DIR, test + '.conf'),
|
'configuration': fs.join(pu.CONFIG_ARANGODB_DIR, test + '.conf'),
|
||||||
'flatCommands': ['--check-configuration']
|
'flatCommands': ['--check-configuration']
|
||||||
};
|
};
|
||||||
|
|
||||||
const run = fs.join(pu.BIN_DIR, test);
|
const run = fs.join(pu.BIN_DIR, test);
|
||||||
|
|
||||||
results.absolute[test] = pu.executeAndWait(run, toArgv(args), options, test, rootDir, options.coreCheck);
|
results.absolute[test] = pu.executeAndWait(run, toArgv(args), options, test, rootDir, options.coreCheck);
|
||||||
|
|
||||||
if (!results.absolute[test].status) {
|
if (!results.absolute[test].status) {
|
||||||
results.absolute.status = false;
|
results.absolute.status = false;
|
||||||
results.absolute.failed += 1;
|
results.absolute.failed += 1;
|
||||||
results.failed += 1;
|
results.failed += 1;
|
||||||
}
|
|
||||||
|
|
||||||
results.absolute.total++;
|
|
||||||
|
|
||||||
if (options.verbose) {
|
|
||||||
print('Args for [' + test + ']:');
|
|
||||||
print(yaml.safeDump(args));
|
|
||||||
print('Result: ' + results.absolute[test].status);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
results.absolute.duration = time() - startTime;
|
results.absolute.total++;
|
||||||
|
|
||||||
|
if (options.verbose) {
|
||||||
|
print('Args for [' + test + ']:');
|
||||||
|
print(yaml.safeDump(args));
|
||||||
|
print('Result: ' + results.absolute[test].status);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
results.absolute.duration = time() - startTime;
|
||||||
|
|
||||||
print('\n--------------------------------------------------------------------------------');
|
print('\n--------------------------------------------------------------------------------');
|
||||||
print('relative config tests');
|
print('relative config tests');
|
||||||
print('--------------------------------------------------------------------------------');
|
print('--------------------------------------------------------------------------------');
|
||||||
|
|
||||||
if (options.skipTest('TEST', 'config.relative')) {
|
startTime = time();
|
||||||
print(YELLOW + "[SKIPPED] config.relative" + RESET + "\n");
|
|
||||||
results.relative.skipped = true;
|
|
||||||
} else {
|
|
||||||
let startTime = time();
|
|
||||||
|
|
||||||
for (let i = 0; i < ts.length; i++) {
|
for (let i = 0; i < ts.length; i++) {
|
||||||
const test = ts[i];
|
const test = ts[i];
|
||||||
print(CYAN + 'checking "' + test + '"' + RESET);
|
print(CYAN + 'checking "' + test + '"' + RESET);
|
||||||
|
|
||||||
const args = {
|
const args = {
|
||||||
'configuration': fs.join(pu.CONFIG_RELATIVE_DIR, test + '.conf'),
|
'configuration': fs.join(pu.CONFIG_RELATIVE_DIR, test + '.conf'),
|
||||||
'flatCommands': ['--check-configuration']
|
'flatCommands': ['--check-configuration']
|
||||||
};
|
};
|
||||||
|
|
||||||
const run = fs.join(pu.BIN_DIR, test);
|
const run = fs.join(pu.BIN_DIR, test);
|
||||||
|
|
||||||
results.relative[test] = pu.executeAndWait(run, toArgv(args), options, test, rootDir, options.coreCheck);
|
results.relative[test] = pu.executeAndWait(run, toArgv(args), options, test, rootDir, options.coreCheck);
|
||||||
|
|
||||||
if (!results.relative[test].status) {
|
if (!results.relative[test].status) {
|
||||||
results.failed += 1;
|
results.failed += 1;
|
||||||
results.relative.failed += 1;
|
results.relative.failed += 1;
|
||||||
results.relative.status = false;
|
results.relative.status = false;
|
||||||
}
|
|
||||||
|
|
||||||
results.relative.total++;
|
|
||||||
|
|
||||||
if (options.verbose) {
|
|
||||||
print('Args for (relative) [' + test + ']:');
|
|
||||||
print(yaml.safeDump(args));
|
|
||||||
print('Result: ' + results.relative[test].status);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
results.relative.duration = time() - startTime;
|
results.relative.total++;
|
||||||
|
|
||||||
|
if (options.verbose) {
|
||||||
|
print('Args for (relative) [' + test + ']:');
|
||||||
|
print(yaml.safeDump(args));
|
||||||
|
print('Result: ' + results.relative[test].status);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
results.relative.duration = time() - startTime;
|
||||||
|
|
||||||
print();
|
print();
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
|
|
|
@ -134,10 +134,10 @@ function success (options) {
|
||||||
duration: 2,
|
duration: 2,
|
||||||
failed: 1,
|
failed: 1,
|
||||||
failTest: {
|
failTest: {
|
||||||
status: false,
|
status: true,
|
||||||
total: 1,
|
total: 1,
|
||||||
duration: 1,
|
duration: 1,
|
||||||
message: 'this testcase will always success.'
|
message: 'this testcase will always succeed.'
|
||||||
},
|
},
|
||||||
failSuccessTest: {
|
failSuccessTest: {
|
||||||
status: true,
|
status: true,
|
||||||
|
|
|
@ -560,6 +560,14 @@ global.DEFINE_MODULE('internal', (function () {
|
||||||
delete global.SYS_PROCESS_JSON_FILE;
|
delete global.SYS_PROCESS_JSON_FILE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// //////////////////////////////////////////////////////////////////////////////
|
||||||
|
// / @brief statisticsExternal
|
||||||
|
// //////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
if (global.SYS_PROCESS_STATISTICS_EXTERNAL) {
|
||||||
|
exports.statisticsExternal = global.SYS_PROCESS_STATISTICS_EXTERNAL;
|
||||||
|
delete global.SYS_PROCESS_STATISTICS_EXTERNAL;
|
||||||
|
}
|
||||||
// //////////////////////////////////////////////////////////////////////////////
|
// //////////////////////////////////////////////////////////////////////////////
|
||||||
// / @brief executeExternal
|
// / @brief executeExternal
|
||||||
// //////////////////////////////////////////////////////////////////////////////
|
// //////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -756,7 +764,18 @@ global.DEFINE_MODULE('internal', (function () {
|
||||||
} else if (!isNaN(argv[i + 1])) {
|
} else if (!isNaN(argv[i + 1])) {
|
||||||
ret[option] = parseInt(argv[i + 1]);
|
ret[option] = parseInt(argv[i + 1]);
|
||||||
} else {
|
} else {
|
||||||
ret[option] = argv[i + 1];
|
if (ret.hasOwnProperty(option)) {
|
||||||
|
if (Array.isArray(ret[option])) {
|
||||||
|
ret[option].push(argv[i + 1]);
|
||||||
|
} else {
|
||||||
|
ret[option] = [
|
||||||
|
ret[option],
|
||||||
|
argv[i + 1]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ret[option] = argv[i + 1];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,8 @@ var TEARDOWNS = 0;
|
||||||
var TOTALSETUPS = 0;
|
var TOTALSETUPS = 0;
|
||||||
var TOTALTEARDOWNS = 0;
|
var TOTALTEARDOWNS = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var jsUnity = require('./jsunity/jsunity').jsUnity;
|
var jsUnity = require('./jsunity/jsunity').jsUnity;
|
||||||
var STARTTEST = 0.0;
|
var STARTTEST = 0.0;
|
||||||
var ENDTEST = 0.0;
|
var ENDTEST = 0.0;
|
||||||
|
@ -141,6 +143,15 @@ jsUnity.results.end = function (passed, failed, duration) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
jsUnity.results.beginSetUpAll = function(index) {
|
||||||
|
SETUPS = jsUnity.env.getDate();
|
||||||
|
};
|
||||||
|
|
||||||
|
jsUnity.results.endSetUpAll = function(index) {
|
||||||
|
RESULTS.setUpAllDuration = jsUnity.env.getDate() - SETUPS;
|
||||||
|
TOTALSETUPS += RESULTS.setUpAllDuration;
|
||||||
|
};
|
||||||
|
|
||||||
jsUnity.results.beginSetUp = function(index, testName) {
|
jsUnity.results.beginSetUp = function(index, testName) {
|
||||||
if (testCount === 0)
|
if (testCount === 0)
|
||||||
{
|
{
|
||||||
|
@ -171,6 +182,15 @@ jsUnity.results.endTeardown = function(index, testName) {
|
||||||
TOTALTEARDOWNS += RESULTS[testName].tearDownDuration;
|
TOTALTEARDOWNS += RESULTS[testName].tearDownDuration;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
jsUnity.results.beginTeardownAll = function(index) {
|
||||||
|
TEARDOWNS = jsUnity.env.getDate();
|
||||||
|
};
|
||||||
|
|
||||||
|
jsUnity.results.endTeardownAll = function(index) {
|
||||||
|
RESULTS.teardownAllDuration = jsUnity.env.getDate() - TEARDOWNS;
|
||||||
|
TOTALTEARDOWNS += RESULTS.teardownAllDuration;
|
||||||
|
};
|
||||||
|
|
||||||
// //////////////////////////////////////////////////////////////////////////////
|
// //////////////////////////////////////////////////////////////////////////////
|
||||||
// / @brief runs a test with context
|
// / @brief runs a test with context
|
||||||
// //////////////////////////////////////////////////////////////////////////////
|
// //////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -255,11 +275,15 @@ function Run (testsuite) {
|
||||||
PASSED += result.passed;
|
PASSED += result.passed;
|
||||||
FAILED += result.failed;
|
FAILED += result.failed;
|
||||||
DURATION += result.duration;
|
DURATION += result.duration;
|
||||||
|
|
||||||
|
|
||||||
let duplicates = [];
|
let duplicates = [];
|
||||||
for (var attrname in RESULTS) {
|
for (var attrname in RESULTS) {
|
||||||
if (RESULTS.hasOwnProperty(attrname)) {
|
if (typeof(RESULTS[attrname]) === 'number') {
|
||||||
|
if (!COMPLETE.hasOwnProperty(attrname)) {
|
||||||
|
COMPLETE[attrname] = { };
|
||||||
|
}
|
||||||
|
COMPLETE[attrname][suite.suiteName] = RESULTS[attrname];
|
||||||
|
} else if (RESULTS.hasOwnProperty(attrname)) {
|
||||||
if (COMPLETE.hasOwnProperty(attrname)) {
|
if (COMPLETE.hasOwnProperty(attrname)) {
|
||||||
print("Duplicate testsuite '" + attrname + "' - already have: " + JSON.stringify(COMPLETE[attrname]) + "");
|
print("Duplicate testsuite '" + attrname + "' - already have: " + JSON.stringify(COMPLETE[attrname]) + "");
|
||||||
duplicates.push(attrname);
|
duplicates.push(attrname);
|
||||||
|
|
|
@ -378,6 +378,10 @@ var jsUnity = exports.jsUnity = (function () {
|
||||||
jsUnity.log.info(plural(total, "test") + " found");
|
jsUnity.log.info(plural(total, "test") + " found");
|
||||||
},
|
},
|
||||||
|
|
||||||
|
beginSetUpAll: function(index, testName) {},
|
||||||
|
|
||||||
|
endSetUpAll: function(index, testName) {},
|
||||||
|
|
||||||
beginSetUp: function(index, testName) {},
|
beginSetUp: function(index, testName) {},
|
||||||
|
|
||||||
endSetUp: function(index, testName) {},
|
endSetUp: function(index, testName) {},
|
||||||
|
@ -399,6 +403,10 @@ var jsUnity = exports.jsUnity = (function () {
|
||||||
|
|
||||||
endTeardown: function(index, testName) {},
|
endTeardown: function(index, testName) {},
|
||||||
|
|
||||||
|
beginTeardownAll: function(index, testName) {},
|
||||||
|
|
||||||
|
endTeardownAll: function(index, testName) {},
|
||||||
|
|
||||||
end: function (passed, failed, duration) {
|
end: function (passed, failed, duration) {
|
||||||
jsUnity.log.info(plural(passed, "test") + " passed");
|
jsUnity.log.info(plural(passed, "test") + " passed");
|
||||||
jsUnity.log.info(plural(failed, "test") + " failed");
|
jsUnity.log.info(plural(failed, "test") + " failed");
|
||||||
|
@ -507,7 +515,9 @@ var jsUnity = exports.jsUnity = (function () {
|
||||||
var runSuite;
|
var runSuite;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
this.results.beginSetUpAll(suite.scope);
|
||||||
setUpAll(suite.suiteName);
|
setUpAll(suite.suiteName);
|
||||||
|
this.results.endSetUpAll(suite.scope);
|
||||||
runSuite = true;
|
runSuite = true;
|
||||||
} catch (setUpAllError) {
|
} catch (setUpAllError) {
|
||||||
runSuite = false;
|
runSuite = false;
|
||||||
|
@ -583,7 +593,9 @@ var jsUnity = exports.jsUnity = (function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
this.results.beginTeardownAll(suite.scope);
|
||||||
tearDownAll(suite.suiteName);
|
tearDownAll(suite.suiteName);
|
||||||
|
this.results.endTeardownAll(suite.scope);
|
||||||
} catch (tearDownAllError) {
|
} catch (tearDownAllError) {
|
||||||
if (tearDownAllError.stack !== undefined) {
|
if (tearDownAllError.stack !== undefined) {
|
||||||
this.results.fail(0, suite.suiteName,
|
this.results.fail(0, suite.suiteName,
|
||||||
|
|
|
@ -173,6 +173,18 @@ ExternalProcess::~ExternalProcess() {
|
||||||
ExternalProcessStatus::ExternalProcessStatus()
|
ExternalProcessStatus::ExternalProcessStatus()
|
||||||
: _status(TRI_EXT_NOT_STARTED), _exitStatus(0), _errorMessage() {}
|
: _status(TRI_EXT_NOT_STARTED), _exitStatus(0), _errorMessage() {}
|
||||||
|
|
||||||
|
static ExternalProcess* TRI_LookupSpawnedProcess(TRI_pid_t pid) {
|
||||||
|
{
|
||||||
|
MUTEX_LOCKER(mutexLocker, ExternalProcessesLock);
|
||||||
|
auto found = std::find_if(ExternalProcesses.begin(), ExternalProcesses.end(),
|
||||||
|
[pid](const ExternalProcess * m) -> bool { return m->_pid == pid; });
|
||||||
|
if (found != ExternalProcesses.end()) {
|
||||||
|
return *found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief creates pipe pair
|
/// @brief creates pipe pair
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -655,13 +667,14 @@ static time_t _FileTime_to_POSIX(FILETIME* ft) {
|
||||||
return (ts - 116444736000000000) / 10000000;
|
return (ts - 116444736000000000) / 10000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
ProcessInfo TRI_ProcessInfoSelf() {
|
ProcessInfo TRI_ProcessInfoH(HANDLE processHandle, TRI_pid_t pid) {
|
||||||
ProcessInfo result;
|
ProcessInfo result;
|
||||||
|
|
||||||
PROCESS_MEMORY_COUNTERS_EX pmc;
|
PROCESS_MEMORY_COUNTERS_EX pmc;
|
||||||
pmc.cb = sizeof(PROCESS_MEMORY_COUNTERS_EX);
|
pmc.cb = sizeof(PROCESS_MEMORY_COUNTERS_EX);
|
||||||
// compiler warning wird in kauf genommen!c
|
// compiler warning wird in kauf genommen!c
|
||||||
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms684874(v=vs.85).aspx
|
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms684874(v=vs.85).aspx
|
||||||
if (GetProcessMemoryInfo(GetCurrentProcess(), (PPROCESS_MEMORY_COUNTERS)&pmc, pmc.cb)) {
|
if (GetProcessMemoryInfo(processHandle, (PPROCESS_MEMORY_COUNTERS)&pmc, pmc.cb)) {
|
||||||
result._majorPageFaults = pmc.PageFaultCount;
|
result._majorPageFaults = pmc.PageFaultCount;
|
||||||
// there is not any corresponce to minflt in linux
|
// there is not any corresponce to minflt in linux
|
||||||
result._minorPageFaults = 0;
|
result._minorPageFaults = 0;
|
||||||
|
@ -686,7 +699,7 @@ ProcessInfo TRI_ProcessInfoSelf() {
|
||||||
}
|
}
|
||||||
/// computing times
|
/// computing times
|
||||||
FILETIME creationTime, exitTime, kernelTime, userTime;
|
FILETIME creationTime, exitTime, kernelTime, userTime;
|
||||||
if (GetProcessTimes(GetCurrentProcess(), &creationTime, &exitTime, &kernelTime, &userTime)) {
|
if (GetProcessTimes(processHandle, &creationTime, &exitTime, &kernelTime, &userTime)) {
|
||||||
// see remarks in
|
// see remarks in
|
||||||
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms683223(v=vs.85).aspx
|
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms683223(v=vs.85).aspx
|
||||||
// value in seconds
|
// value in seconds
|
||||||
|
@ -697,8 +710,7 @@ ProcessInfo TRI_ProcessInfoSelf() {
|
||||||
// the function '_FileTime_to_POSIX' should be called
|
// the function '_FileTime_to_POSIX' should be called
|
||||||
}
|
}
|
||||||
/// computing number of threads
|
/// computing number of threads
|
||||||
DWORD myPID = GetCurrentProcessId();
|
HANDLE snapShot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, pid);
|
||||||
HANDLE snapShot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, myPID);
|
|
||||||
|
|
||||||
if (snapShot != INVALID_HANDLE_VALUE) {
|
if (snapShot != INVALID_HANDLE_VALUE) {
|
||||||
THREADENTRY32 te32;
|
THREADENTRY32 te32;
|
||||||
|
@ -706,16 +718,35 @@ ProcessInfo TRI_ProcessInfoSelf() {
|
||||||
if (Thread32First(snapShot, &te32)) {
|
if (Thread32First(snapShot, &te32)) {
|
||||||
result._numberThreads++;
|
result._numberThreads++;
|
||||||
while (Thread32Next(snapShot, &te32)) {
|
while (Thread32Next(snapShot, &te32)) {
|
||||||
if (te32.th32OwnerProcessID == myPID) {
|
if (te32.th32OwnerProcessID == pid) {
|
||||||
result._numberThreads++;
|
result._numberThreads++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
LOG_TOPIC("66667", ERR, arangodb::Logger::FIXME) << "failed to acquire thread from snapshot - " << GetLastError();
|
||||||
|
}
|
||||||
CloseHandle(snapShot);
|
CloseHandle(snapShot);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
LOG_TOPIC("66668", ERR, arangodb::Logger::FIXME) << "failed to acquire process threads count - " << GetLastError();
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ProcessInfo TRI_ProcessInfoSelf() {
|
||||||
|
return TRI_ProcessInfoH(GetCurrentProcess(), GetCurrentProcessId());
|
||||||
|
}
|
||||||
|
|
||||||
|
ProcessInfo TRI_ProcessInfo(TRI_pid_t pid) {
|
||||||
|
auto external = TRI_LookupSpawnedProcess(pid);
|
||||||
|
if (external != nullptr) {
|
||||||
|
return TRI_ProcessInfoH(external->_process, pid);
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -882,7 +913,7 @@ ProcessInfo TRI_ProcessInfo(TRI_pid_t pid) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
#ifndef _WIN32
|
||||||
ProcessInfo TRI_ProcessInfo(TRI_pid_t pid) {
|
ProcessInfo TRI_ProcessInfo(TRI_pid_t pid) {
|
||||||
ProcessInfo result;
|
ProcessInfo result;
|
||||||
|
|
||||||
|
@ -890,7 +921,7 @@ ProcessInfo TRI_ProcessInfo(TRI_pid_t pid) {
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -983,17 +1014,7 @@ ExternalProcessStatus TRI_CheckExternalProcess(ExternalId pid, bool wait, uint32
|
||||||
status._status = TRI_EXT_NOT_FOUND;
|
status._status = TRI_EXT_NOT_FOUND;
|
||||||
status._exitStatus = 0;
|
status._exitStatus = 0;
|
||||||
|
|
||||||
ExternalProcess* external = nullptr;
|
auto external = TRI_LookupSpawnedProcess(pid._pid);
|
||||||
{
|
|
||||||
MUTEX_LOCKER(mutexLocker, ExternalProcessesLock);
|
|
||||||
|
|
||||||
for (auto& it : ExternalProcesses) {
|
|
||||||
if (it->_pid == pid._pid) {
|
|
||||||
external = it;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (external == nullptr) {
|
if (external == nullptr) {
|
||||||
status._errorMessage =
|
status._errorMessage =
|
||||||
|
|
|
@ -38,6 +38,8 @@
|
||||||
|
|
||||||
#define TRI_pid_t pid_t
|
#define TRI_pid_t pid_t
|
||||||
|
|
||||||
|
#define TRI_vpack_pid_t int
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief thread library identifier
|
/// @brief thread library identifier
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -31,7 +31,9 @@
|
||||||
/// @brief process identifier
|
/// @brief process identifier
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#define TRI_pid_t int
|
#define TRI_pid_t DWORD
|
||||||
|
|
||||||
|
#define TRI_vpack_pid_t int
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief thread identifier
|
/// @brief thread identifier
|
||||||
|
|
|
@ -2821,13 +2821,12 @@ static void JS_Output(v8::FunctionCallbackInfo<v8::Value> const& args) {
|
||||||
/// @END_EXAMPLE_ARANGOSH_OUTPUT
|
/// @END_EXAMPLE_ARANGOSH_OUTPUT
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
static void JS_ProcessStatistics(v8::FunctionCallbackInfo<v8::Value> const& args) {
|
static void ProcessStatisticsToV8(v8::FunctionCallbackInfo<v8::Value> const& args, ProcessInfo const& info) {
|
||||||
TRI_V8_TRY_CATCH_BEGIN(isolate)
|
TRI_V8_TRY_CATCH_BEGIN(isolate)
|
||||||
v8::HandleScope scope(isolate);
|
v8::HandleScope scope(isolate);
|
||||||
|
|
||||||
v8::Handle<v8::Object> result = v8::Object::New(isolate);
|
v8::Handle<v8::Object> result = v8::Object::New(isolate);
|
||||||
|
|
||||||
ProcessInfo info = TRI_ProcessInfoSelf();
|
|
||||||
double rss = (double)info._residentSize;
|
double rss = (double)info._residentSize;
|
||||||
double rssp = 0;
|
double rssp = 0;
|
||||||
|
|
||||||
|
@ -2855,6 +2854,36 @@ static void JS_ProcessStatistics(v8::FunctionCallbackInfo<v8::Value> const& args
|
||||||
TRI_V8_TRY_CATCH_END
|
TRI_V8_TRY_CATCH_END
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void JS_ProcessStatisticsSelf(v8::FunctionCallbackInfo<v8::Value> const& args) {
|
||||||
|
ProcessStatisticsToV8(args, TRI_ProcessInfoSelf());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void JS_ProcessStatisticsExternal(v8::FunctionCallbackInfo<v8::Value> const& args) {
|
||||||
|
TRI_V8_TRY_CATCH_BEGIN(isolate);
|
||||||
|
v8::HandleScope scope(isolate);
|
||||||
|
|
||||||
|
if (args.Length() != 1) {
|
||||||
|
TRI_V8_THROW_EXCEPTION_USAGE(
|
||||||
|
"statisticsExternal(<external-identifier>)");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& server = application_features::ApplicationServer::server();
|
||||||
|
V8SecurityFeature& v8security = server.getFeature<V8SecurityFeature>();
|
||||||
|
|
||||||
|
if (v8security.isInternalModuleHardened(isolate)) {
|
||||||
|
TRI_V8_THROW_EXCEPTION_MESSAGE(
|
||||||
|
TRI_ERROR_FORBIDDEN,
|
||||||
|
"not allowed to execute or modify state of external processes");
|
||||||
|
}
|
||||||
|
|
||||||
|
ExternalId pid;
|
||||||
|
|
||||||
|
pid._pid = static_cast<TRI_pid_t>(TRI_ObjectToUInt64(isolate, args[0], true));
|
||||||
|
|
||||||
|
ProcessStatisticsToV8(args, TRI_ProcessInfo(pid._pid));
|
||||||
|
TRI_V8_TRY_CATCH_END
|
||||||
|
}
|
||||||
|
|
||||||
static void JS_GetPid(v8::FunctionCallbackInfo<v8::Value> const& args) {
|
static void JS_GetPid(v8::FunctionCallbackInfo<v8::Value> const& args) {
|
||||||
TRI_V8_TRY_CATCH_BEGIN(isolate)
|
TRI_V8_TRY_CATCH_BEGIN(isolate)
|
||||||
v8::HandleScope scope(isolate);
|
v8::HandleScope scope(isolate);
|
||||||
|
@ -4355,11 +4384,7 @@ static void JS_StatusExternal(v8::FunctionCallbackInfo<v8::Value> const& args) {
|
||||||
|
|
||||||
ExternalId pid;
|
ExternalId pid;
|
||||||
|
|
||||||
#ifndef _WIN32
|
|
||||||
pid._pid = static_cast<TRI_pid_t>(TRI_ObjectToUInt64(isolate, args[0], true));
|
pid._pid = static_cast<TRI_pid_t>(TRI_ObjectToUInt64(isolate, args[0], true));
|
||||||
#else
|
|
||||||
pid._pid = static_cast<DWORD>(TRI_ObjectToUInt64(isolate, args[0], true));
|
|
||||||
#endif
|
|
||||||
bool wait = false;
|
bool wait = false;
|
||||||
if (args.Length() >= 2) {
|
if (args.Length() >= 2) {
|
||||||
wait = TRI_ObjectToBoolean(isolate, args[1]);
|
wait = TRI_ObjectToBoolean(isolate, args[1]);
|
||||||
|
@ -4550,6 +4575,11 @@ static void JS_KillExternal(v8::FunctionCallbackInfo<v8::Value> const& args) {
|
||||||
#else
|
#else
|
||||||
pid._pid = static_cast<DWORD>(TRI_ObjectToUInt64(isolate, args[0], true));
|
pid._pid = static_cast<DWORD>(TRI_ObjectToUInt64(isolate, args[0], true));
|
||||||
#endif
|
#endif
|
||||||
|
if (pid._pid == 0) {
|
||||||
|
TRI_V8_THROW_EXCEPTION_MESSAGE(
|
||||||
|
TRI_ERROR_FORBIDDEN,
|
||||||
|
"not allowed to kill 0");
|
||||||
|
}
|
||||||
|
|
||||||
// return the result
|
// return the result
|
||||||
v8::Handle<v8::Object> result = v8::Object::New(isolate);
|
v8::Handle<v8::Object> result = v8::Object::New(isolate);
|
||||||
|
@ -5540,6 +5570,11 @@ void TRI_InitV8Utils(v8::Isolate* isolate, v8::Handle<v8::Context> context,
|
||||||
TRI_AddGlobalFunctionVocbase(
|
TRI_AddGlobalFunctionVocbase(
|
||||||
isolate, TRI_V8_ASCII_STRING(isolate, "SYS_SPLIT_WORDS_ICU"), JS_SplitWordlist);
|
isolate, TRI_V8_ASCII_STRING(isolate, "SYS_SPLIT_WORDS_ICU"), JS_SplitWordlist);
|
||||||
|
|
||||||
|
TRI_AddGlobalFunctionVocbase(isolate,
|
||||||
|
TRI_V8_ASCII_STRING(isolate,
|
||||||
|
"SYS_PROCESS_STATISTICS_EXTERNAL"),
|
||||||
|
JS_ProcessStatisticsExternal);
|
||||||
|
|
||||||
TRI_AddGlobalFunctionVocbase(isolate,
|
TRI_AddGlobalFunctionVocbase(isolate,
|
||||||
TRI_V8_ASCII_STRING(isolate,
|
TRI_V8_ASCII_STRING(isolate,
|
||||||
"SYS_GET_EXTERNAL_SPAWNED"),
|
"SYS_GET_EXTERNAL_SPAWNED"),
|
||||||
|
@ -5578,7 +5613,7 @@ void TRI_InitV8Utils(v8::Isolate* isolate, v8::Handle<v8::Context> context,
|
||||||
TRI_AddGlobalFunctionVocbase(isolate,
|
TRI_AddGlobalFunctionVocbase(isolate,
|
||||||
TRI_V8_ASCII_STRING(isolate,
|
TRI_V8_ASCII_STRING(isolate,
|
||||||
"SYS_PROCESS_STATISTICS"),
|
"SYS_PROCESS_STATISTICS"),
|
||||||
JS_ProcessStatistics);
|
JS_ProcessStatisticsSelf);
|
||||||
TRI_AddGlobalFunctionVocbase(isolate,
|
TRI_AddGlobalFunctionVocbase(isolate,
|
||||||
TRI_V8_ASCII_STRING(isolate, "SYS_GET_PID"), JS_GetPid);
|
TRI_V8_ASCII_STRING(isolate, "SYS_GET_PID"), JS_GetPid);
|
||||||
TRI_AddGlobalFunctionVocbase(isolate, TRI_V8_ASCII_STRING(isolate, "SYS_RAND"), JS_Rand);
|
TRI_AddGlobalFunctionVocbase(isolate, TRI_V8_ASCII_STRING(isolate, "SYS_RAND"), JS_Rand);
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
#!bin/arangosh --javascript.execute
|
||||||
|
/* jshint globalstrict:false, unused:false */
|
||||||
|
/* global print, start_pretty_print, ARGUMENTS */
|
||||||
|
'use strict';
|
||||||
|
const _ = require('lodash');
|
||||||
|
const fs = require('fs');
|
||||||
|
const internal = require('internal');
|
||||||
|
const rp = require('@arangodb/result-processing');
|
||||||
|
|
||||||
|
const optionsDefaults = require('@arangodb/testing').optionsDefaults;
|
||||||
|
|
||||||
|
/* Constants: */
|
||||||
|
const BLUE = internal.COLORS.COLOR_BLUE;
|
||||||
|
const CYAN = internal.COLORS.COLOR_CYAN;
|
||||||
|
const GREEN = internal.COLORS.COLOR_GREEN;
|
||||||
|
const RED = internal.COLORS.COLOR_RED;
|
||||||
|
const RESET = internal.COLORS.COLOR_RESET;
|
||||||
|
const YELLOW = internal.COLORS.COLOR_YELLOW;
|
||||||
|
|
||||||
|
function main (argv) {
|
||||||
|
start_pretty_print();
|
||||||
|
|
||||||
|
let analyzers = []; // e.g all, http_server, recovery, ...
|
||||||
|
let options = {};
|
||||||
|
|
||||||
|
while (argv.length >= 1) {
|
||||||
|
if (argv[0].slice(0, 1) === '-') { // break parsing if we hit some -option
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
analyzers = _.merge(analyzers, argv[0].split(','));
|
||||||
|
|
||||||
|
argv = argv.slice(1); // and remove first arg (c++:pop_front/bash:shift)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argv.length >= 1) {
|
||||||
|
try {
|
||||||
|
options = internal.parseArgv(argv, 0);
|
||||||
|
} catch (x) {
|
||||||
|
print('failed to parse the options: ' + x.message + '\n' + String(x.stack));
|
||||||
|
print('argv: ', argv);
|
||||||
|
throw x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!options.hasOwnProperty('readFile')) {
|
||||||
|
print("need a file to load!");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
let readFile;
|
||||||
|
if (Array.isArray(options.readFile)) {
|
||||||
|
readFile = options.readFile;
|
||||||
|
} else {
|
||||||
|
readFile = [ options.readFile ];
|
||||||
|
}
|
||||||
|
let rc = readFile.forEach(file => {
|
||||||
|
let ret = true;
|
||||||
|
let resultsDump;
|
||||||
|
try {
|
||||||
|
resultsDump = fs.read(file);
|
||||||
|
} catch (ex) {
|
||||||
|
print(RED + "File not found [" + file + "]: " + ex.message + RESET + "\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let results;
|
||||||
|
try {
|
||||||
|
results = JSON.parse(resultsDump);
|
||||||
|
}
|
||||||
|
catch (ex) {
|
||||||
|
print(RED + "Failed to parse " + options.readFile + " - " + ex.message + RESET + "\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_.defaults(options, optionsDefaults);
|
||||||
|
try {
|
||||||
|
print(YELLOW + "Analyzing: " + file + RESET);
|
||||||
|
ret = ret && analyzers.forEach(function(which) {
|
||||||
|
rp.analyze[which](options, results);
|
||||||
|
});
|
||||||
|
} catch (ex) {
|
||||||
|
print("Failed to analyze [" + file + "]: " + ex.message + RESET + "\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
});
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = main(ARGUMENTS);
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
process.exit(1);
|
||||||
|
}
|
Loading…
Reference in New Issue