1660 lines
78 KiB
CMake
1660 lines
78 KiB
CMake
/*
|
|
* Program.cs
|
|
*
|
|
* This source file is part of the FoundationDB open source project
|
|
*
|
|
* Copyright 2013-2020 Apple Inc. and the FoundationDB project authors
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
using System.Threading;
|
|
using System.Xml.Linq;
|
|
using System.IO;
|
|
using System.Diagnostics;
|
|
using System.ComponentModel;
|
|
using System.Runtime.InteropServices;
|
|
using System.Xml;
|
|
using System.Runtime.Serialization.Json;
|
|
|
|
namespace SummarizeTest
|
|
{
|
|
static class Program
|
|
{
|
|
static Random random;
|
|
const int killSeconds = 60 * 30;
|
|
const int maxWarnings = 10;
|
|
const int maxStderrBytes = 1000;
|
|
const double unseedRatio = 0.05;
|
|
const double buggifyOnRatio = 0.8;
|
|
static string BINARY = "fdbserver" + (IsRunningOnMono() ? "" : ".exe");
|
|
static string PLUGIN = "FDBLibTLS." + (IsRunningOnMono() ? "so" : "dll");
|
|
static string OS_NAME = IsRunningOnMono() ? "linux" : "win";
|
|
|
|
static int Main(string[] args)
|
|
{
|
|
bool traceToStdout = false;
|
|
try
|
|
{
|
|
string joshuaSeed = System.Environment.GetEnvironmentVariable("JOSHUA_SEED");
|
|
byte[] seed = new byte[4];
|
|
new System.Security.Cryptography.RNGCryptoServiceProvider().GetBytes(seed);
|
|
random = new Random(joshuaSeed != null ? Convert.ToInt32(Int64.Parse(joshuaSeed) % 2147483648) : new BinaryReader(new MemoryStream(seed)).ReadInt32());
|
|
|
|
if (args.Length < 1)
|
|
return UsageMessage();
|
|
|
|
if (args[0] == "summarize")
|
|
{
|
|
if (args.Length < 3)
|
|
return UsageMessage();
|
|
string valgrindFileName = args.Length >= 4 ? args[3] : null;
|
|
string externalError = args.Length >= 5 ? args[4] : "";
|
|
traceToStdout = args.Length == 6 && args[5] == "true";
|
|
|
|
int unseed;
|
|
bool retryableError;
|
|
//SOMEDAY: This only works if a run generated just one trace file. We should change the summarize command to take multiple trace files
|
|
return Summarize(new string[]{ args[1] }, args[2], null, null, null, null, null, null, valgrindFileName, -1, out unseed, out retryableError, true,
|
|
traceToStdout: traceToStdout, externalError: externalError);
|
|
}
|
|
else if (args[0] == "remote")
|
|
{
|
|
if (args.Length < 6)
|
|
return UsageMessage();
|
|
|
|
return Remote(args[1], args[2], double.Parse(args[3]), int.Parse(args[4]), args[5], args.Length == 7 ? args[6] : "default");
|
|
}
|
|
else if (args[0] == "run")
|
|
{
|
|
try
|
|
{
|
|
if (args.Length < 6)
|
|
return UsageMessage();
|
|
|
|
string runDir = null;
|
|
if(args[1] != "temp") runDir = args[1];
|
|
|
|
bool useValgind = args.Length > 6 && args[6].ToLower() == "true";
|
|
int maxTries = (args.Length > 7) ? int.Parse(args[7]) : 3;
|
|
return Run(args[2], args[3], args[4], args[5], null, runDir, null, useValgind, maxTries);
|
|
}
|
|
catch(Exception e)
|
|
{
|
|
var xout = new XElement("TestHarnessError",
|
|
new XAttribute("Severity", (int)Magnesium.Severity.SevError),
|
|
new XAttribute("ErrorMessage", e.Message));
|
|
|
|
AppendXmlMessageToSummary(args[5], xout);
|
|
throw;
|
|
}
|
|
}
|
|
else if (args[0] == "replay")
|
|
{
|
|
if (args.Length != 6)
|
|
return UsageMessage();
|
|
|
|
string runDir = null;
|
|
if (args[1] != "temp") runDir = args[1];
|
|
|
|
return Replay(args[2], args[3], args[4], args[5], runDir);
|
|
}
|
|
else if (args[0] == "auto")
|
|
{
|
|
if (args.Length < 4)
|
|
return UsageMessage();
|
|
|
|
string runDir = null;
|
|
if (args[1] != "temp") runDir = args[1];
|
|
|
|
string cacheDir = Path.Combine(runDir, "test_harness_cache");
|
|
bool useValgrind = args.Length > 4 && args[4].ToLower() == "true";
|
|
int maxTries = (args.Length > 5) ? int.Parse(args[5]) : 3;
|
|
|
|
return Auto(args[2], Path.Combine(runDir, "runs"), args[3], cacheDir, useValgrind, maxTries);
|
|
}
|
|
else if (args[0] == "extract-errors")
|
|
{
|
|
if (args.Length != 3)
|
|
return UsageMessage();
|
|
|
|
return ExtractErrors(args[1], args[2]);
|
|
}
|
|
else if (args[0] == "joshua-run")
|
|
{
|
|
traceToStdout = true;
|
|
|
|
try
|
|
{
|
|
string oldBinaryFolder = (args.Length > 1) ? args[1] : Path.Combine("/opt", "joshua", "global_data", "oldBinaries");
|
|
bool useValgrind = args.Length > 2 && args[2].ToLower() == "true";
|
|
int maxTries = (args.Length > 3) ? int.Parse(args[3]) : 3;
|
|
bool buggifyEnabled = (args.Length > 4) ? bool.Parse(args[4]) : true;
|
|
bool faultInjectionEnabled = (args.Length > 5) ? bool.Parse(args[5]) : true;
|
|
return Run(Path.Combine("bin", BINARY), "", "tests", "summary.xml", "error.xml", "tmp", oldBinaryFolder, useValgrind, maxTries, true, Path.Combine("/app", "deploy", "runtime", ".tls_5_1", PLUGIN), buggifyEnabled, faultInjectionEnabled);
|
|
}
|
|
catch(Exception e)
|
|
{
|
|
var xout = new XElement("TestHarnessError",
|
|
new XAttribute("Severity", (int)Magnesium.Severity.SevError),
|
|
new XAttribute("ErrorMessage", e.ToString()));
|
|
|
|
AppendXmlMessageToSummary("summary.xml", xout, true);
|
|
throw;
|
|
}
|
|
}
|
|
else if (args[0] == "version")
|
|
{
|
|
return VersionInfo();
|
|
}
|
|
|
|
return UsageMessage();
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (!traceToStdout)
|
|
{
|
|
Console.WriteLine("Error:");
|
|
Console.WriteLine(e.ToString());
|
|
}
|
|
|
|
return 100;
|
|
}
|
|
}
|
|
|
|
static T Choice<T>(this Random random, IList<T> items)
|
|
{
|
|
return items[ random.Next(items.Count) ];
|
|
}
|
|
|
|
static bool IsRunningOnMono()
|
|
{
|
|
return Type.GetType("Mono.Runtime") != null;
|
|
}
|
|
|
|
static int Replay(string fdbserverName, string tlsPluginFile, string inputSummaryFileName, string outputSummaryFileName, string runDir)
|
|
{
|
|
using (var summaryFileIn = System.IO.File.Open(inputSummaryFileName,
|
|
System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.ReadWrite | System.IO.FileShare.Delete))
|
|
{
|
|
foreach (var test in Magnesium.XmlParser.Parse(summaryFileIn, inputSummaryFileName).OfType<Magnesium.TestPlan>())
|
|
{
|
|
if (test is Magnesium.Test) continue; // Only process the test plans
|
|
int unseed;
|
|
bool retryableError;
|
|
RunTest(fdbserverName, tlsPluginFile, outputSummaryFileName, null, test.randomSeed, test.Buggify, test.TestFile, runDir, test.TestUID, -1, out unseed, out retryableError, true, false);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Ver format - x.x.x (eg, 5.0.1, 4.2.11)
|
|
// Returns true is ver1 is greater than or equal to ver2
|
|
static bool versionGreaterThanOrEqual(string ver1, string ver2)
|
|
{
|
|
string[] tokens1 = ver1.Split('.');
|
|
string[] tokens2 = ver2.Split('.');
|
|
if (tokens1.Length != tokens2.Length || tokens2.Length != 3)
|
|
{
|
|
throw new ArgumentException("Invalid Version Format Version1: " + ver1 + " Version2: " + ver2);
|
|
}
|
|
int[] version1 = Array.ConvertAll(tokens1, int.Parse);
|
|
int[] version2 = Array.ConvertAll(tokens2, int.Parse);
|
|
return ((System.Collections.IStructuralComparable)version1).CompareTo(version2, System.Collections.Generic.Comparer<int>.Default) >= 0;
|
|
}
|
|
|
|
static bool versionLessThan(string ver1, string ver2) {
|
|
return !versionGreaterThanOrEqual(ver1, ver2);
|
|
}
|
|
|
|
static string getFdbserverVersion(string fdbserverName) {
|
|
using (var process = new System.Diagnostics.Process())
|
|
{
|
|
process.StartInfo.UseShellExecute = false;
|
|
process.StartInfo.RedirectStandardOutput = true;
|
|
process.StartInfo.FileName = fdbserverName;
|
|
process.StartInfo.Arguments = "--version";
|
|
process.StartInfo.RedirectStandardError = true;
|
|
|
|
process.Start();
|
|
var output = process.StandardOutput.ReadToEnd();
|
|
// If the process finished successfully, we call the parameterless WaitForExit to ensure that output buffers get flushed
|
|
process.WaitForExit();
|
|
|
|
var match = Regex.Match(output, @"v(\d+\.\d+\.\d+)");
|
|
if (match.Groups.Count < 1) return "";
|
|
return match.Groups[1].Value;
|
|
}
|
|
}
|
|
|
|
static int Run(string fdbserverName, string tlsPluginFile, string testFolder, string summaryFileName, string errorFileName, string runDir, string oldBinaryFolder, bool useValgrind, int maxTries, bool traceToStdout = false, string tlsPluginFile_5_1 = "", bool buggifyEnabled = true, bool faultInjectionEnabled = true)
|
|
{
|
|
int seed = random.Next(1000000000);
|
|
bool buggify = buggifyEnabled ? (random.NextDouble() < buggifyOnRatio) : false;
|
|
string testFile = null;
|
|
string testDir = "";
|
|
string oldServerName = "";
|
|
bool noSim = false;
|
|
|
|
if (Directory.Exists(testFolder))
|
|
{
|
|
int poolSize = 0;
|
|
if( Directory.Exists(Path.Combine(testFolder, "rare")) ) poolSize += 1;
|
|
if( Directory.Exists(Path.Combine(testFolder, "slow")) ) poolSize += 5;
|
|
if( Directory.Exists(Path.Combine(testFolder, "fast")) ) poolSize += 14;
|
|
if( Directory.Exists(Path.Combine(testFolder, "restarting")) ) poolSize += 1;
|
|
if( Directory.Exists(Path.Combine(testFolder, "noSim")) ) poolSize += 1;
|
|
|
|
if( poolSize == 0 ) {
|
|
Console.WriteLine("Passed folder ({0}) did not have a fast, slow, rare, restarting, or noSim sub-folder", testFolder);
|
|
return 1;
|
|
}
|
|
int selection = random.Next(poolSize);
|
|
int selectionWindow = 0;
|
|
|
|
if( Directory.Exists(Path.Combine(testFolder, "rare")) ) selectionWindow += 1;
|
|
if (selection < selectionWindow)
|
|
testDir = Path.Combine(testFolder, "rare");
|
|
else
|
|
{
|
|
if (Directory.Exists(Path.Combine(testFolder, "restarting"))) selectionWindow += 1;
|
|
if (selection < selectionWindow)
|
|
testDir = Path.Combine(testFolder, "restarting");
|
|
else
|
|
{
|
|
if (Directory.Exists(Path.Combine(testFolder, "noSim"))) selectionWindow += 1;
|
|
if (selection < selectionWindow)
|
|
{
|
|
testDir = Path.Combine(testFolder, "noSim");
|
|
noSim = true;
|
|
}
|
|
else
|
|
{
|
|
if (Directory.Exists(Path.Combine(testFolder, "slow"))) selectionWindow += 5;
|
|
if (selection < selectionWindow)
|
|
testDir = Path.Combine(testFolder, "slow");
|
|
else
|
|
testDir = Path.Combine(testFolder, "fast");
|
|
}
|
|
}
|
|
}
|
|
string[] files = Directory.GetFiles(testDir, "*", SearchOption.AllDirectories);
|
|
string[] uniqueFiles;
|
|
if (testDir.EndsWith("restarting"))
|
|
{
|
|
ISet<string> uniqueFileSet = new HashSet<String>();
|
|
foreach(string file in files) {
|
|
uniqueFileSet.Add(file.Substring(0, file.LastIndexOf("-"))); // all restarting tests end with -1.txt or -2.txt
|
|
}
|
|
uniqueFiles = uniqueFileSet.ToArray();
|
|
testFile = random.Choice(uniqueFiles);
|
|
// The on-disk format changed in 4.0.0, and 5.x can't load files from 3.x.
|
|
string oldBinaryVersionLowerBound = "4.0.0";
|
|
string lastFolderName = Path.GetFileName(Path.GetDirectoryName(testFile));
|
|
if (lastFolderName.Contains("from_") || lastFolderName.Contains("to_")) // Only perform upgrade/downgrade tests from certain versions
|
|
{
|
|
oldBinaryVersionLowerBound = lastFolderName.Split('_').ElementAt(1); // Assuming "from_*.*.*" appears first in the folder name
|
|
}
|
|
string oldBinaryVersionUpperBound = getFdbserverVersion(fdbserverName);
|
|
if (lastFolderName.Contains("until_")) // Specify upper bound for old binary; "until_*.*.*" is assumed at the end if present
|
|
{
|
|
string givenUpperBound = lastFolderName.Split('_').Last();
|
|
if (versionLessThan(givenUpperBound, oldBinaryVersionUpperBound)) {
|
|
oldBinaryVersionUpperBound = givenUpperBound;
|
|
}
|
|
}
|
|
if (versionGreaterThanOrEqual("4.0.0", oldBinaryVersionUpperBound)) {
|
|
// If the binary under test is from 3.x, then allow upgrade tests from 3.x binaries.
|
|
oldBinaryVersionLowerBound = "0.0.0";
|
|
}
|
|
string[] currentBinary = { fdbserverName };
|
|
IEnumerable<string> oldBinaries = Array.FindAll(
|
|
Directory.GetFiles(oldBinaryFolder),
|
|
x => versionGreaterThanOrEqual(Path.GetFileName(x).Split('-').Last(), oldBinaryVersionLowerBound)
|
|
&& versionLessThan(Path.GetFileName(x).Split('-').Last(), oldBinaryVersionUpperBound));
|
|
if (!lastFolderName.Contains("until_")) {
|
|
// only add current binary to the list of old binaries if "until_" is not specified in the folder name
|
|
// <version> in until_<version> should be less or equal to the current binary version
|
|
// otherwise, using "until_" makes no sense
|
|
// thus, by definition, if "until_" appears, we do not want to run with the current binary version
|
|
oldBinaries = oldBinaries.Concat(currentBinary);
|
|
}
|
|
List<string> oldBinariesList = oldBinaries.ToList<string>();
|
|
if (oldBinariesList.Count == 0) {
|
|
// In theory, restarting tests are named to have at least one old binary version to run
|
|
// But if none of the provided old binaries fall in the range, we just skip the test
|
|
Console.WriteLine("No available old binary version from {0} to {1}", oldBinaryVersionLowerBound, oldBinaryVersionUpperBound);
|
|
return 0;
|
|
} else {
|
|
oldServerName = random.Choice(oldBinariesList);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
uniqueFiles = Directory.GetFiles(testDir);
|
|
testFile = random.Choice(uniqueFiles);
|
|
}
|
|
}
|
|
else if (File.Exists(testFolder))
|
|
testFile = testFolder;
|
|
else
|
|
{
|
|
Console.WriteLine("Passed path ({0}) was not a folder or file", testFolder);
|
|
return 1;
|
|
}
|
|
|
|
int result = 0;
|
|
bool unseedCheck = random.NextDouble() < unseedRatio;
|
|
for (int i = 0; i < maxTries; ++i)
|
|
{
|
|
bool logOnRetryableError = i == maxTries - 1;
|
|
bool retryableError = false;
|
|
|
|
if (testDir.EndsWith("restarting"))
|
|
{
|
|
bool isDowngrade = Path.GetFileName(Path.GetDirectoryName(testFile)).Contains("to_");
|
|
string firstServerName = isDowngrade ? fdbserverName : oldServerName;
|
|
string secondServerName = isDowngrade ? oldServerName : fdbserverName;
|
|
int expectedUnseed = -1;
|
|
int unseed;
|
|
string uid = Guid.NewGuid().ToString();
|
|
bool useNewPlugin = (oldServerName == fdbserverName) || versionGreaterThanOrEqual(oldServerName.Split('-').Last(), "5.2.0");
|
|
bool useToml = File.Exists(testFile + "-1.toml");
|
|
string testFile1 = useToml ? testFile + "-1.toml" : testFile + "-1.txt";
|
|
result = RunTest(firstServerName, useNewPlugin ? tlsPluginFile : tlsPluginFile_5_1, summaryFileName, errorFileName, seed, buggify, testFile1, runDir, uid, expectedUnseed, out unseed, out retryableError, logOnRetryableError, useValgrind, false, true, oldServerName, traceToStdout, noSim, faultInjectionEnabled);
|
|
if (result == 0)
|
|
{
|
|
string testFile2 = useToml ? testFile + "-2.toml" : testFile + "-2.txt";
|
|
result = RunTest(secondServerName, tlsPluginFile, summaryFileName, errorFileName, seed+1, buggify, testFile2, runDir, uid, expectedUnseed, out unseed, out retryableError, logOnRetryableError, useValgrind, true, false, oldServerName, traceToStdout, noSim, faultInjectionEnabled);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int expectedUnseed = -1;
|
|
if (!useValgrind && unseedCheck)
|
|
{
|
|
result = RunTest(fdbserverName, tlsPluginFile, null, null, seed, buggify, testFile, runDir, Guid.NewGuid().ToString(), -1, out expectedUnseed, out retryableError, logOnRetryableError, false, false, false, "", traceToStdout, noSim, faultInjectionEnabled);
|
|
}
|
|
|
|
if (!retryableError)
|
|
{
|
|
int unseed;
|
|
result = RunTest(fdbserverName, tlsPluginFile, summaryFileName, errorFileName, seed, buggify, testFile, runDir, Guid.NewGuid().ToString(), expectedUnseed, out unseed, out retryableError, logOnRetryableError, useValgrind, false, false, "", traceToStdout, noSim, faultInjectionEnabled);
|
|
}
|
|
}
|
|
|
|
if (!retryableError)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private static int RunTest(string fdbserverName, string tlsPluginFile, string summaryFileName, string errorFileName, int seed,
|
|
bool buggify, string testFile, string runDir, string uid, int expectedUnseed, out int unseed, out bool retryableError, bool logOnRetryableError, bool useValgrind, bool restarting = false,
|
|
bool willRestart = false, string oldBinaryName = "", bool traceToStdout = false, bool noSim = false, bool faultInjectionEnabled = true)
|
|
{
|
|
unseed = -1;
|
|
|
|
retryableError = false;
|
|
string tempPath = Path.Combine(runDir != null ? Path.GetFullPath(runDir) : Path.GetTempPath(), uid);
|
|
string oldDir = Directory.GetCurrentDirectory();
|
|
|
|
try
|
|
{
|
|
fdbserverName = Path.GetFullPath(fdbserverName);
|
|
tlsPluginFile = (tlsPluginFile.Length > 0) ? Path.GetFullPath(tlsPluginFile) : "";
|
|
testFile = Path.GetFullPath(testFile);
|
|
var ok = 0;
|
|
|
|
if (summaryFileName != null)
|
|
summaryFileName = Path.GetFullPath(summaryFileName);
|
|
|
|
Directory.CreateDirectory(tempPath);
|
|
Directory.SetCurrentDirectory(tempPath);
|
|
|
|
if (!restarting) LogTestPlan(summaryFileName, testFile, seed, buggify, expectedUnseed != -1, uid, faultInjectionEnabled, oldBinaryName);
|
|
|
|
string valgrindOutputFile = null;
|
|
using (var process = new System.Diagnostics.Process())
|
|
{
|
|
ErrorOutputListener errorListener = new ErrorOutputListener();
|
|
process.StartInfo.UseShellExecute = false;
|
|
string tlsPluginArg = "";
|
|
if (tlsPluginFile.Length > 0) {
|
|
process.StartInfo.EnvironmentVariables["FDB_TLS_PLUGIN"] = tlsPluginFile;
|
|
tlsPluginArg = "--tls_plugin=" + tlsPluginFile;
|
|
}
|
|
process.StartInfo.RedirectStandardOutput = true;
|
|
string role = (noSim) ? "test" : "simulation";
|
|
var args = "";
|
|
string faultInjectionArg = string.IsNullOrEmpty(oldBinaryName) ? string.Format("-fi {0}", faultInjectionEnabled ? "on" : "off") : "";
|
|
if (willRestart && oldBinaryName.EndsWith("alpha6"))
|
|
{
|
|
args = string.Format("-Rs 1000000000 -r {0} {1} -s {2} -f \"{3}\" -b {4} {5} {6} --crash",
|
|
role, IsRunningOnMono() ? "" : "-q", seed, testFile, buggify ? "on" : "off", faultInjectionArg, tlsPluginArg);
|
|
}
|
|
else
|
|
{
|
|
args = string.Format("-Rs 1GB -r {0} {1} -s {2} -f \"{3}\" -b {4} {5} {6} --crash",
|
|
role, IsRunningOnMono() ? "" : "-q", seed, testFile, buggify ? "on" : "off", faultInjectionArg, tlsPluginArg);
|
|
}
|
|
if (restarting) args = args + " --restarting";
|
|
if (useValgrind && !willRestart)
|
|
{
|
|
valgrindOutputFile = string.Format("valgrind-{0}.xml", seed);
|
|
process.StartInfo.FileName = "valgrind";
|
|
// Add extra debug directory, if environment variables is defined
|
|
string valgrindDbgDir = System.Environment.GetEnvironmentVariable("FDB_VALGRIND_DBGPATH");
|
|
process.StartInfo.Arguments = (string.IsNullOrEmpty(valgrindDbgDir)) ? "" : string.Format("--extra-debuginfo-path={0} ", valgrindDbgDir);
|
|
|
|
process.StartInfo.Arguments +=
|
|
string.Format("--xml=yes --xml-file={0} -q {1} {2}", valgrindOutputFile, fdbserverName, args);
|
|
}
|
|
else
|
|
{
|
|
process.StartInfo.FileName = fdbserverName;
|
|
process.StartInfo.Arguments = args;
|
|
}
|
|
process.StartInfo.RedirectStandardError = true;
|
|
process.ErrorDataReceived += new DataReceivedEventHandler(errorListener.handleData);
|
|
|
|
if (!traceToStdout)
|
|
{
|
|
Console.WriteLine("Executing {0} {1} in {2} (buggify: {3}, valgrind: {4})",
|
|
process.StartInfo.FileName, process.StartInfo.Arguments, tempPath,
|
|
buggify ? "on" : "off",
|
|
useValgrind ? "on" : "off");
|
|
}
|
|
|
|
process.Start();
|
|
|
|
// SOMEDAY: Do we want to actually do anything with standard output or error?
|
|
// Using standarderror requires async read, see http://msdn.microsoft.com/en-us/library/system.diagnostics.process.standardoutput.aspx
|
|
//process.StandardOutput.ReadToEnd();
|
|
OutputCopier copier = new OutputCopier(process.StandardOutput);
|
|
Thread consoleThread = new Thread(new ThreadStart(copier.copyOutput));
|
|
consoleThread.Start();
|
|
|
|
MemoryChecker memChecker = new MemoryChecker(process);
|
|
Thread memCheckThread = new Thread(new ThreadStart(memChecker.monitorMemory));
|
|
memCheckThread.Start();
|
|
|
|
process.BeginErrorReadLine();
|
|
|
|
Stopwatch stopwatch = new Stopwatch();
|
|
stopwatch.Start();
|
|
|
|
var ms = 1000 * killSeconds;
|
|
if (useValgrind)
|
|
ms *= 20;
|
|
bool killed = !process.WaitForExit(ms); // Wait "killSeconds" seconds here
|
|
|
|
stopwatch.Stop();
|
|
|
|
if (!killed)
|
|
process.WaitForExit(); // If the process finished successfully, we call the parameterless WaitForExit to ensure that output buffers get flushed
|
|
else
|
|
{
|
|
// It seems that WaitForExit sometimes returns false well before the timeout has elapsed
|
|
// We won't report that as an error, but just in case the process didn't actually finish we attempt to
|
|
// kill it here
|
|
if (killed && ms - stopwatch.ElapsedMilliseconds > 10000)
|
|
killed = false;
|
|
|
|
if (!traceToStdout)
|
|
{
|
|
Console.WriteLine("Warning: Killing process after {0} seconds...", killSeconds);
|
|
}
|
|
|
|
try
|
|
{
|
|
process.Kill();
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (!traceToStdout)
|
|
{
|
|
Console.WriteLine("Warning: Process.Kill returned {0}", e);
|
|
}
|
|
}
|
|
|
|
int waitSeconds = 60 * 2;
|
|
process.WaitForExit(1000 * waitSeconds);
|
|
if (!process.HasExited)
|
|
{
|
|
if(!traceToStdout)
|
|
{
|
|
Console.WriteLine("Warning: Unable to kill process after {0} seconds...", waitSeconds);
|
|
}
|
|
|
|
var xout = new XElement("UnableToKillProcess",
|
|
new XAttribute("Severity", (int)Magnesium.Severity.SevWarnAlways));
|
|
|
|
AppendXmlMessageToSummary(summaryFileName, xout, traceToStdout, testFile, seed, buggify, expectedUnseed != -1, oldBinaryName, faultInjectionEnabled);
|
|
return 104;
|
|
}
|
|
}
|
|
if (!traceToStdout)
|
|
{
|
|
Console.WriteLine("Exit code is {0}", process.ExitCode);
|
|
}
|
|
memCheckThread.Join();
|
|
consoleThread.Join();
|
|
|
|
var traceFiles = Directory.GetFiles(tempPath, "trace*.*").Where(s => s.EndsWith(".xml") || s.EndsWith(".json")).ToArray();
|
|
// if no traces caused by the process failed then the result will include its stderr
|
|
if (process.ExitCode == 0 && traceFiles.Length == 0)
|
|
{
|
|
if (!traceToStdout)
|
|
{
|
|
Console.WriteLine("Warning: No trace file was generated, summary will not be affected.");
|
|
}
|
|
|
|
var xout = new XElement((useValgrind ? "NoTraceFileGeneratedLibPos" : "NoTraceFileGenerated"),
|
|
new XAttribute("Severity", (int)Magnesium.Severity.SevWarnAlways),
|
|
new XAttribute("Plugin", tlsPluginFile),
|
|
new XAttribute("MachineName", System.Environment.MachineName));
|
|
|
|
AppendXmlMessageToSummary(summaryFileName, xout, traceToStdout, testFile, seed, buggify, expectedUnseed != -1, oldBinaryName, faultInjectionEnabled);
|
|
ok = useValgrind ? 0 : 103;
|
|
}
|
|
else
|
|
{
|
|
var result = Summarize(traceFiles, summaryFileName, errorFileName, killed, errorListener.Errors,
|
|
process.ExitCode, memChecker.MaxMem, uid,
|
|
valgrindOutputFile == null ? null : Path.Combine(tempPath, valgrindOutputFile),
|
|
expectedUnseed, out unseed, out retryableError, logOnRetryableError, willRestart, restarting, oldBinaryName, traceToStdout);
|
|
|
|
if (result != 0)
|
|
{
|
|
ok = result;
|
|
}
|
|
}
|
|
|
|
if (willRestart) foreach (var f in traceFiles) File.Delete(f);
|
|
|
|
process.Close();
|
|
}
|
|
if (!traceToStdout)
|
|
{
|
|
Console.WriteLine("Done (unseed {0})", unseed);
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (!traceToStdout)
|
|
{
|
|
Console.WriteLine("Error in Run:");
|
|
Console.WriteLine(e);
|
|
}
|
|
|
|
var xout = new XElement("TestHarnessRunError",
|
|
new XAttribute("Severity", (int)Magnesium.Severity.SevError),
|
|
new XAttribute("ErrorMessage", e.Message));
|
|
|
|
AppendXmlMessageToSummary(summaryFileName, xout, traceToStdout, testFile, seed, buggify, expectedUnseed != -1, oldBinaryName, faultInjectionEnabled);
|
|
return 101;
|
|
}
|
|
finally
|
|
{
|
|
Directory.SetCurrentDirectory(oldDir);
|
|
if (!willRestart) Directory.Delete(tempPath, true);
|
|
}
|
|
}
|
|
|
|
class OutputCopier
|
|
{
|
|
private StreamReader reader;
|
|
|
|
public OutputCopier(StreamReader streamReader)
|
|
{
|
|
this.reader = streamReader;
|
|
}
|
|
|
|
public void copyOutput()
|
|
{
|
|
//Console.WriteLine(" Beginning console copy...");
|
|
while (!reader.EndOfStream)
|
|
{
|
|
reader.ReadLine();
|
|
//Console.WriteLine(" : " + s);
|
|
}
|
|
}
|
|
}
|
|
|
|
private class ErrorOutputListener
|
|
{
|
|
public List<string> Errors { get; set; }
|
|
int maxErrorLength;
|
|
int maxErrors;
|
|
bool errorsExceeded;
|
|
|
|
public ErrorOutputListener(int maxErrorLength = 1000, int maxErrors = 10)
|
|
{
|
|
Errors = new List<string>();
|
|
this.maxErrorLength = maxErrorLength;
|
|
this.maxErrors = maxErrors;
|
|
this.errorsExceeded = false;
|
|
}
|
|
|
|
public bool hasError = false;
|
|
public void handleData(object sendingProcess, DataReceivedEventArgs errLine)
|
|
{
|
|
if(!String.IsNullOrEmpty(errLine.Data))
|
|
{
|
|
if (errLine.Data.EndsWith("WARNING: ASan doesn't fully support makecontext/swapcontext functions and may produce false positives in some cases!")) {
|
|
// When running ASAN we expect to see this message. Boost coroutine should be using the correct asan annotations so that it shouldn't produce any false positives.
|
|
return;
|
|
}
|
|
if (errLine.Data.EndsWith("Warning: unimplemented fcntl command: 1036")) {
|
|
// Valgrind produces this warning when F_SET_RW_HINT is used
|
|
return;
|
|
}
|
|
|
|
hasError = true;
|
|
if(Errors.Count < maxErrors) {
|
|
if(errLine.Data.Length > maxErrorLength) {
|
|
Errors.Add(errLine.Data.Substring(0, maxErrorLength) + "...");
|
|
}
|
|
else {
|
|
Errors.Add(errLine.Data);
|
|
}
|
|
}
|
|
else if(!errorsExceeded) {
|
|
Errors.Add("TestHarness error limit exceeded");
|
|
errorsExceeded = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
class MemoryChecker
|
|
{
|
|
//private System.Diagnostics.Process process;
|
|
private long maxMem;
|
|
private Process process;
|
|
|
|
public long MaxMem
|
|
{
|
|
get { return maxMem; }
|
|
set { maxMem = value; }
|
|
}
|
|
|
|
public MemoryChecker(System.Diagnostics.Process process)
|
|
{
|
|
this.process = process;
|
|
this.maxMem = 0;
|
|
}
|
|
|
|
public void monitorMemory()
|
|
{
|
|
while (true)
|
|
{
|
|
try
|
|
{
|
|
process.Refresh();
|
|
if (process.HasExited)
|
|
return;
|
|
long mem = process.PrivateMemorySize64;
|
|
MaxMem = Math.Max(MaxMem, mem);
|
|
//Console.WriteLine(string.Format("Process used {0} bytes", MaxMem));
|
|
Thread.Sleep(1000);
|
|
}
|
|
catch
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void LogTestPlan(string summaryFileName, string testFileName, int randomSeed, bool buggify, bool testDeterminism, string uid, bool faultInjectionEnabled, string oldBinary="")
|
|
{
|
|
var xout = new XElement("TestPlan",
|
|
new XAttribute("TestUID", uid),
|
|
new XAttribute("RandomSeed", randomSeed),
|
|
new XAttribute("TestFile", testFileName),
|
|
new XAttribute("BuggifyEnabled", buggify ? "1" : "0"),
|
|
new XAttribute("FaultInjectionEnabled", faultInjectionEnabled ? "1" : "0"),
|
|
new XAttribute("DeterminismCheck", testDeterminism ? "1" : "0"),
|
|
new XAttribute("OldBinary", Path.GetFileName(oldBinary)));
|
|
AppendToSummary(summaryFileName, xout);
|
|
}
|
|
|
|
// Parses the valgrind XML file and returns a list of "what" tags for each error.
|
|
// All errors for which the "kind" tag starts with "Leak" are ignored
|
|
static string[] ParseValgrindOutput(string valgrindOutputFileName, bool traceToStdout)
|
|
{
|
|
if (!traceToStdout)
|
|
{
|
|
Console.WriteLine("Reading vXML file: " + valgrindOutputFileName);
|
|
}
|
|
|
|
ISet<string> whats = new HashSet<string>();
|
|
XElement xdoc = XDocument.Load(valgrindOutputFileName).Element("valgrindoutput");
|
|
foreach(var elem in xdoc.Elements()) {
|
|
if (elem.Name != "error")
|
|
continue;
|
|
string kind = elem.Element("kind").Value;
|
|
if(kind.StartsWith("Leak"))
|
|
continue;
|
|
whats.Add(elem.Element("what").Value);
|
|
}
|
|
return whats.ToArray();
|
|
}
|
|
|
|
delegate IEnumerable<Magnesium.Event> parseDelegate(System.IO.Stream stream, string file,
|
|
bool keepOriginalElement = false, double startTime = -1, double endTime = Double.MaxValue,
|
|
double samplingFactor = 1.0, Action<string> nonFatalErrorMessage = null);
|
|
|
|
static int Summarize(string[] traceFiles, string summaryFileName,
|
|
string errorFileName, bool? killed, List<string> outputErrors, int? exitCode, long? peakMemory,
|
|
string uid, string valgrindOutputFileName, int expectedUnseed, out int unseed, out bool retryableError, bool logOnRetryableError,
|
|
bool willRestart = false, bool restarted = false, string oldBinaryName = "", bool traceToStdout = false, string externalError = "")
|
|
{
|
|
unseed = -1;
|
|
retryableError = false;
|
|
List<string> errorList = new List<string>();
|
|
var xout = new XElement("Test");
|
|
if (uid != null)
|
|
xout.Add(new XAttribute("TestUID", uid));
|
|
bool ok = false;
|
|
string testFile = "Unknown";
|
|
int testsPassed = 0, testCount = -1, warnings = 0, errors = 0;
|
|
bool testBeginFound = false, testEndFound = false, error = false;
|
|
string firstRetryableError = "";
|
|
int stderrSeverity = (int)Magnesium.Severity.SevError;
|
|
|
|
Dictionary<KeyValuePair<string, Magnesium.Severity>, Magnesium.Severity> severityMap = new Dictionary<KeyValuePair<string, Magnesium.Severity>, Magnesium.Severity>();
|
|
Dictionary<Tuple<string, string>, bool> codeCoverage = new Dictionary<Tuple<string, string>, bool>();
|
|
|
|
foreach (var traceFileName in traceFiles)
|
|
{
|
|
if(!traceToStdout) {
|
|
Console.WriteLine("Summarizing {0}", traceFileName);
|
|
}
|
|
using (var traceFile = System.IO.File.Open(traceFileName,
|
|
System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.ReadWrite | System.IO.FileShare.Delete))
|
|
{
|
|
try
|
|
{
|
|
// Use Action to set this because IEnumerables with yield can't have an out variable
|
|
string nonFatalParseError = null;
|
|
parseDelegate parse;
|
|
if (traceFileName.EndsWith(".json"))
|
|
parse = Magnesium.JsonParser.Parse;
|
|
else
|
|
parse = Magnesium.XmlParser.Parse;
|
|
foreach (var ev in parse(traceFile, traceFileName, nonFatalErrorMessage: (x) => { nonFatalParseError = x; }))
|
|
{
|
|
Magnesium.Severity newSeverity;
|
|
if (severityMap.TryGetValue(new KeyValuePair<string, Magnesium.Severity>(ev.Type, ev.Severity), out newSeverity))
|
|
ev.Severity = newSeverity;
|
|
if (ev.Severity >= Magnesium.Severity.SevWarnAlways && ev.DDetails.ContainsKey("ErrorIsInjectedFault"))
|
|
ev.Severity = Magnesium.Severity.SevWarn;
|
|
|
|
if (ev.Type == "ProgramStart" && !testBeginFound
|
|
// Just in case the first ProgramStart seen is a Simulated one, ignore it
|
|
&& (!ev.DDetails.ContainsKey("Simulated") || ev.Details.Simulated != "1"))
|
|
{
|
|
xout.Add(
|
|
new XAttribute("RandomSeed", ev.Details.RandomSeed),
|
|
new XAttribute("SourceVersion", ev.Details.SourceVersion),
|
|
new XAttribute("Time", ev.Details.ActualTime),
|
|
new XAttribute("BuggifyEnabled", ev.Details.BuggifyEnabled),
|
|
new XAttribute("DeterminismCheck", expectedUnseed != -1 ? "1" : "0"),
|
|
new XAttribute("OldBinary", Path.GetFileName(oldBinaryName)));
|
|
testBeginFound = true;
|
|
if (ev.DDetails.ContainsKey("FaultInjectionEnabled"))
|
|
xout.Add(new XAttribute("FaultInjectionEnabled", ev.Details.FaultInjectionEnabled));
|
|
}
|
|
if (ev.Type == "Simulation")
|
|
{
|
|
xout.Add(
|
|
new XAttribute("TestFile", ev.Details.TestFile));
|
|
testFile = ev.Details.TestFile.Substring(ev.Details.TestFile.IndexOf("tests"));
|
|
}
|
|
if (ev.Type == "ActualRun")
|
|
{
|
|
xout.Add(
|
|
new XAttribute("TestFile", ev.Details.RunID));
|
|
}
|
|
if (ev.Type == "ElapsedTime" && !testEndFound)
|
|
{
|
|
testEndFound = true;
|
|
unseed = int.Parse(ev.Details.RandomUnseed);
|
|
if (expectedUnseed != -1 && expectedUnseed != unseed)
|
|
{
|
|
Magnesium.Severity severity;
|
|
if (!severityMap.TryGetValue(new KeyValuePair<string, Magnesium.Severity>("UnseedMismatch", Magnesium.Severity.SevError), out severity))
|
|
severity = Magnesium.Severity.SevError;
|
|
|
|
if (severity >= Magnesium.Severity.SevWarnAlways)
|
|
{
|
|
xout.Add(new XElement("UnseedMismatch",
|
|
new XAttribute("Unseed", unseed),
|
|
new XAttribute("ExpectedUnseed", expectedUnseed),
|
|
new XAttribute("Severity", (int)severity)));
|
|
if ( severity == Magnesium.Severity.SevError ) {
|
|
error = true;
|
|
errorList.Add("UnseedMismatch");
|
|
}
|
|
}
|
|
}
|
|
xout.Add(
|
|
new XAttribute("SimElapsedTime", ev.Details.SimTime),
|
|
new XAttribute("RealElapsedTime", ev.Details.RealTime),
|
|
new XAttribute("RandomUnseed", ev.Details.RandomUnseed));
|
|
}
|
|
if (ev.Severity == Magnesium.Severity.SevWarnAlways)
|
|
{
|
|
if (warnings < maxWarnings)
|
|
{
|
|
xout.Add(new XElement(ev.Type,
|
|
new XAttribute("Severity", (int)ev.Severity),
|
|
ev.DDetails
|
|
//.Where(kv => true)
|
|
.Select(kv => new XAttribute(kv.Key, kv.Value))));
|
|
}
|
|
warnings++;
|
|
}
|
|
if (ev.Severity >= Magnesium.Severity.SevError)
|
|
{
|
|
string errorString = ev.FormatTestError(true);
|
|
if (errorString.Contains("platform_error"))
|
|
{
|
|
if (!retryableError)
|
|
{
|
|
firstRetryableError = errorString;
|
|
}
|
|
retryableError = true;
|
|
}
|
|
if (errors < maxWarnings)
|
|
{
|
|
xout.Add(new XElement(ev.Type,
|
|
new XAttribute("Severity", (int)ev.Severity),
|
|
ev.DDetails
|
|
//.Where(kv => true)
|
|
.Select(kv => new XAttribute(kv.Key, kv.Value))));
|
|
errorList.Add(errorString);
|
|
}
|
|
errors++;
|
|
error = true;
|
|
}
|
|
if (ev.Type == "CodeCoverage" && !willRestart)
|
|
{
|
|
bool covered = true;
|
|
if(ev.DDetails.ContainsKey("Covered"))
|
|
{
|
|
covered = int.Parse(ev.Details.Covered) != 0;
|
|
}
|
|
|
|
var key = new Tuple<string, string>(ev.Details.File, ev.Details.Line);
|
|
if (covered || !codeCoverage.ContainsKey(key))
|
|
{
|
|
codeCoverage[key] = covered;
|
|
}
|
|
}
|
|
if (ev.Type == "FaultInjected" || (ev.Type == "BuggifySection" && ev.Details.Activated == "1"))
|
|
{
|
|
xout.Add(new XElement(ev.Type, new XAttribute("File", ev.Details.File), new XAttribute("Line", ev.Details.Line)));
|
|
}
|
|
if (ev.Type == "TestsExpectedToPass")
|
|
testCount = int.Parse(ev.Details.Count);
|
|
if (ev.Type == "TestResults" && ev.Details.Passed == "1")
|
|
testsPassed++;
|
|
if (ev.Type == "RemapEventSeverity")
|
|
severityMap[new KeyValuePair<string, Magnesium.Severity>(ev.Details.TargetEvent, (Magnesium.Severity)int.Parse(ev.Details.OriginalSeverity))] = (Magnesium.Severity)int.Parse(ev.Details.NewSeverity);
|
|
if (ev.Type == "StderrSeverity")
|
|
stderrSeverity = int.Parse(ev.Details.NewSeverity);
|
|
}
|
|
if (nonFatalParseError != null) {
|
|
xout.Add(new XElement("NonFatalParseError",
|
|
new XAttribute("Severity", (int)Magnesium.Severity.SevWarnAlways),
|
|
new XAttribute("ErrorMessage", nonFatalParseError)));
|
|
}
|
|
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (!traceToStdout)
|
|
{
|
|
Console.WriteLine("Error summarizing {0}: {1}", traceFileName, e);
|
|
}
|
|
|
|
error = true;
|
|
xout.Add(new XElement("SummarizationError",
|
|
new XAttribute("Severity", (int)Magnesium.Severity.SevError),
|
|
new XAttribute("ErrorMessage", e.Message)));
|
|
errorList.Add("SummarizationError " + e.Message);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (externalError.Length > 0) {
|
|
xout.Add(new XElement(externalError, new XAttribute("Severity", (int)Magnesium.Severity.SevError)));
|
|
}
|
|
|
|
foreach(var kv in codeCoverage)
|
|
{
|
|
var element = new XElement("CodeCoverage", new XAttribute("File", kv.Key.Item1), new XAttribute("Line", kv.Key.Item2));
|
|
if(!kv.Value)
|
|
{
|
|
element.Add(new XAttribute("Covered", "0"));
|
|
}
|
|
|
|
xout.Add(element);
|
|
}
|
|
|
|
if (warnings > maxWarnings)
|
|
{
|
|
//error = true;
|
|
xout.Add(new XElement("WarningLimitExceeded",
|
|
new XAttribute("Severity", (int)Magnesium.Severity.SevWarnAlways),
|
|
new XAttribute("WarningCount", warnings)));
|
|
}
|
|
if (errors > maxWarnings)
|
|
{
|
|
error = true;
|
|
xout.Add(new XElement("ErrorLimitExceeded",
|
|
new XAttribute("Severity", (int)Magnesium.Severity.SevError),
|
|
new XAttribute("ErrorCount", errors)));
|
|
errorList.Add("ErrorLimitExceeded");
|
|
}
|
|
if (killed == true)
|
|
{
|
|
if (!retryableError)
|
|
{
|
|
firstRetryableError = "ExternalTimeout";
|
|
}
|
|
|
|
retryableError = true;
|
|
error = true;
|
|
xout.Add(new XElement("ExternalTimeout", new XAttribute("Severity", (int)Magnesium.Severity.SevError)));
|
|
}
|
|
if (outputErrors != null)
|
|
{
|
|
int stderrBytes = 0;
|
|
foreach (string err in outputErrors)
|
|
{
|
|
if (stderrSeverity == (int)Magnesium.Severity.SevError)
|
|
{
|
|
error = true;
|
|
}
|
|
|
|
int remainingBytes = maxStderrBytes - stderrBytes;
|
|
if (remainingBytes > 0)
|
|
{
|
|
string outErr = (err.Length > remainingBytes) ? err.Substring(remainingBytes) + "..." : err;
|
|
|
|
xout.Add(new XElement("StdErrOutput",
|
|
new XAttribute("Severity", stderrSeverity),
|
|
new XAttribute("Output", outErr)));
|
|
}
|
|
|
|
stderrBytes += err.Length;
|
|
}
|
|
|
|
if (stderrBytes > maxStderrBytes)
|
|
{
|
|
xout.Add(new XElement("StdErrOutputTruncated",
|
|
new XAttribute("Severity", stderrSeverity),
|
|
new XAttribute("BytesRemaining", stderrBytes - maxStderrBytes)));
|
|
}
|
|
}
|
|
if (exitCode.HasValue && exitCode != 0)
|
|
{
|
|
error = true;
|
|
xout.Add(new XElement("ExitCode", new XAttribute("Code", exitCode.Value), new XAttribute("Severity", (int)Magnesium.Severity.SevError)));
|
|
errorList.Add(string.Format("ExitCode 0x{0:x}", exitCode.Value));
|
|
}
|
|
if (!testEndFound && !willRestart)
|
|
{
|
|
// We didn't terminate the test, but it didn't reach the end?
|
|
error = true;
|
|
xout.Add(new XElement("TestUnexpectedlyNotFinished"), new XAttribute("Severity", (int)Magnesium.Severity.SevError));
|
|
errorList.Add("TestUnexpectedlyNotFinished");
|
|
}
|
|
ok = testsPassed == testCount && testsPassed > 0 && !error;
|
|
xout.Add(
|
|
new XAttribute("Passed", testsPassed),
|
|
new XAttribute("Failed", testCount - testsPassed));
|
|
if (peakMemory.HasValue)
|
|
xout.Add(new XAttribute("PeakMemory", peakMemory.Value));
|
|
|
|
if (valgrindOutputFileName != null && valgrindOutputFileName.Length > 0)
|
|
{
|
|
try
|
|
{
|
|
// If there are any errors reported "ok" will be set to false
|
|
var whats = ParseValgrindOutput(valgrindOutputFileName, traceToStdout);
|
|
foreach (var what in whats)
|
|
{
|
|
xout.Add(new XElement("ValgrindError",
|
|
new XAttribute("Severity", (int)Magnesium.Severity.SevError),
|
|
new XAttribute("What", what)));
|
|
ok = false;
|
|
error = true;
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (!traceToStdout)
|
|
{
|
|
Console.WriteLine(e);
|
|
}
|
|
|
|
error = true;
|
|
xout.Add(new XElement("ValgrindParseError",
|
|
new XAttribute("Severity", (int)Magnesium.Severity.SevError),
|
|
new XAttribute("ErrorMessage", e.Message)));
|
|
errorList.Add("Failed to parse valgrind output: " + e.Message);
|
|
}
|
|
}
|
|
|
|
if (retryableError && !logOnRetryableError)
|
|
{
|
|
xout = new XElement("Test", xout.Attributes());
|
|
xout.Add(new XElement("RetryingError",
|
|
new XAttribute("Severity", (int)Magnesium.Severity.SevWarnAlways),
|
|
new XAttribute("What", firstRetryableError)));
|
|
}
|
|
else
|
|
{
|
|
xout.Add(new XAttribute("OK", ok || willRestart));
|
|
}
|
|
|
|
AppendToSummary(summaryFileName, xout, traceToStdout);
|
|
|
|
if ((!retryableError || logOnRetryableError) && errorFileName != null && (errorList.Count > 0 || !ok) && !willRestart)
|
|
{
|
|
var errorText = string.Join("\n\t", errorList
|
|
.Concat((!ok && errorList.Count == 0) ? new string[] { "Failed with no explanation" } : new string[] { })
|
|
.Distinct()
|
|
.ToArray());
|
|
AppendToFile(errorFileName, string.Format("Test {0} failed with:\n\t{1}\n", testFile, errorText));
|
|
}
|
|
if (!error) {
|
|
return 0;
|
|
}
|
|
else {
|
|
return 102;
|
|
}
|
|
}
|
|
|
|
static int ExtractErrors(string summaryFileName, string errorSummaryFileName)
|
|
{
|
|
Console.WriteLine("Extracting from {0}", summaryFileName);
|
|
List<XElement> xout = new List<XElement>();
|
|
var coverage = new Dictionary<Tuple<string,int>,Tuple<int,int>>();
|
|
using (var traceFile = System.IO.File.Open(summaryFileName,
|
|
System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.ReadWrite | System.IO.FileShare.Delete))
|
|
{
|
|
try
|
|
{
|
|
var events = Magnesium.XmlParser.Parse(traceFile, summaryFileName, true);
|
|
events = Magnesium.TraceLogUtil.IdentifyFailedTestPlans(events);
|
|
foreach (var ev in events)
|
|
{
|
|
Magnesium.Test t = ev as Magnesium.Test;
|
|
if (t != null)
|
|
{
|
|
foreach (var tev in t.events)
|
|
{
|
|
if (tev.Type == "CodeCoverage" || tev.Type == "FaultInjected")
|
|
{
|
|
var keyTuple = Tuple.Create(tev.Details.File, int.Parse(tev.Details.Line));
|
|
if (coverage.ContainsKey(keyTuple))
|
|
{
|
|
var old = coverage[keyTuple];
|
|
coverage[keyTuple] = Tuple.Create(old.Item1 + 1, old.Item2 + (t.ok ? 0 : 1));
|
|
}
|
|
else
|
|
{
|
|
coverage[keyTuple] = Tuple.Create(1, (t.ok ? 0 : 1));
|
|
}
|
|
}
|
|
}
|
|
if (!t.ok)
|
|
{
|
|
if (t.original != null)
|
|
{
|
|
foreach (var c in t.original.Elements("CodeCoverage"))
|
|
c.Remove();
|
|
foreach (var f in t.original.Elements("FaultInjected"))
|
|
f.Remove();
|
|
|
|
xout.Add(t.original);
|
|
}
|
|
else
|
|
{
|
|
xout.Add(new XElement("Test",
|
|
new XAttribute("Type", t.Type),
|
|
new XAttribute("Time", t.Time),
|
|
new XAttribute("Machine", t.Machine),
|
|
new XAttribute("TestUID", t.TestUID),
|
|
new XAttribute("TestFile", t.TestFile),
|
|
new XAttribute("randomSeed", t.randomSeed),
|
|
new XAttribute("Buggify", t.Buggify),
|
|
new XAttribute("DeterminismCheck", t.DeterminismCheck),
|
|
new XAttribute("OldBinary", t.OldBinary),
|
|
new XElement("TestNotSummarized",
|
|
new XAttribute("Severity", (int)Magnesium.Severity.SevWarnAlways)
|
|
)
|
|
)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Console.WriteLine("Error summarizing {0}: {1}", summaryFileName, e);
|
|
xout.Add(new XElement("SummarizationError",
|
|
new XAttribute("Severity", (int)Magnesium.Severity.SevError),
|
|
new XAttribute("ErrorMessage", e.Message)));
|
|
//failedTests.Add("SummarizationError " + e.Message);
|
|
}
|
|
}
|
|
|
|
foreach (var e in coverage)
|
|
{
|
|
xout.Add(new XElement("Event",
|
|
new XAttribute("Type", "CoverageSummary"),
|
|
new XAttribute("Time", 0),
|
|
new XAttribute("Machine", ""),
|
|
new XAttribute("File", e.Key.Item1),
|
|
new XAttribute("Line", e.Key.Item2),
|
|
new XAttribute("Covered", e.Value.Item1),
|
|
new XAttribute("Failed", e.Value.Item2)));
|
|
}
|
|
|
|
AppendToErrorSummary(errorSummaryFileName, xout);
|
|
return 0;
|
|
}
|
|
|
|
private static void AppendToErrorSummary(string summaryFileName, List<XElement> elements)
|
|
{
|
|
if (summaryFileName == null)
|
|
return;
|
|
takeLock(summaryFileName);
|
|
try {
|
|
foreach (XElement e in elements)
|
|
AppendToSummary(summaryFileName, e, false, false);
|
|
}
|
|
finally
|
|
{
|
|
releaseLock(summaryFileName);
|
|
}
|
|
}
|
|
|
|
private static void AppendToSummary(string summaryFileName, XElement xout, bool traceToStdout = false, bool shouldLock = true)
|
|
{
|
|
bool useXml = true;
|
|
if (summaryFileName != null && summaryFileName.EndsWith(".json")) {
|
|
useXml = false;
|
|
}
|
|
|
|
if (traceToStdout)
|
|
{
|
|
if (useXml) {
|
|
using (var wr = System.Xml.XmlWriter.Create(Console.OpenStandardOutput(), new System.Xml.XmlWriterSettings() { OmitXmlDeclaration = true, Encoding = new System.Text.UTF8Encoding(false) }))
|
|
xout.WriteTo(wr);
|
|
} else {
|
|
using (var wr = System.Runtime.Serialization.Json.JsonReaderWriterFactory.CreateJsonWriter(Console.OpenStandardOutput()))
|
|
xout.WriteTo(wr);
|
|
}
|
|
Console.WriteLine();
|
|
return;
|
|
}
|
|
|
|
if (summaryFileName == null)
|
|
return;
|
|
if (shouldLock)
|
|
takeLock(summaryFileName);
|
|
try
|
|
{
|
|
using (var f = System.IO.File.Open(summaryFileName, System.IO.FileMode.Append, System.IO.FileAccess.Write))
|
|
{
|
|
if (f.Length == 0)
|
|
{
|
|
byte[] bytes = Encoding.UTF8.GetBytes("<Trace>");
|
|
f.Write(bytes, 0, bytes.Length);
|
|
}
|
|
if (useXml) {
|
|
using (var wr = System.Xml.XmlWriter.Create(f, new System.Xml.XmlWriterSettings() { OmitXmlDeclaration = true }))
|
|
xout.Save(wr);
|
|
} else {
|
|
using (var wr = System.Runtime.Serialization.Json.JsonReaderWriterFactory.CreateJsonWriter(f))
|
|
xout.WriteTo(wr);
|
|
}
|
|
var endl = Encoding.UTF8.GetBytes(Environment.NewLine);
|
|
f.Write(endl, 0, endl.Length);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
if (shouldLock)
|
|
releaseLock(summaryFileName);
|
|
}
|
|
}
|
|
|
|
private static void AppendXmlMessageToSummary(string summaryFileName, XElement xout, bool traceToStdout = false, string testFile = null,
|
|
int? seed = null, bool? buggify = null, bool? determinismCheck = null, string oldBinaryName = null, bool? faultInjectionEnabled = null)
|
|
{
|
|
var test = new XElement("Test", xout);
|
|
if(testFile != null)
|
|
test.Add(new XAttribute("TestFile", testFile));
|
|
if(seed != null)
|
|
test.Add(new XAttribute("RandomSeed", seed));
|
|
if(buggify != null)
|
|
test.Add(new XAttribute("BuggifyEnabled", buggify.Value ? "1" : "0"));
|
|
if(faultInjectionEnabled != null)
|
|
test.Add(new XAttribute("FaultInjectionEnabled", faultInjectionEnabled.Value ? "1" : "0"));
|
|
if(determinismCheck != null)
|
|
test.Add(new XAttribute("DeterminismCheck", determinismCheck.Value ? "1" : "0"));
|
|
if(oldBinaryName != null)
|
|
test.Add(new XAttribute("OldBinary", Path.GetFileName(oldBinaryName)));
|
|
|
|
test.Add(xout);
|
|
AppendToSummary(summaryFileName, test, traceToStdout);
|
|
}
|
|
|
|
private static void AppendToFile(string fileName, string content)
|
|
{
|
|
if (fileName == null)
|
|
return;
|
|
takeLock(fileName);
|
|
try
|
|
{
|
|
using (var f = System.IO.File.Open(fileName, System.IO.FileMode.Append, System.IO.FileAccess.Write))
|
|
{
|
|
var endl = Encoding.UTF8.GetBytes(content);
|
|
f.Write(endl, 0, endl.Length);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
releaseLock(fileName);
|
|
}
|
|
}
|
|
|
|
static int Remote(string queue, string fdbRoot, double addHours, int testCount, string testTypes, string userScope)
|
|
{
|
|
queue = Path.GetFullPath(queue);
|
|
fdbRoot = Path.GetFullPath(fdbRoot);
|
|
var output = Path.Combine(queue, "archive");
|
|
var now = DateTime.Now;
|
|
string date = string.Format("{0}-{1:00}-{2:00}-{3:00}-{4:00}",
|
|
now.Year, now.Month, now.Day, now.Hour, now.Minute);
|
|
|
|
if (!Directory.Exists(queue))
|
|
Directory.CreateDirectory(queue);
|
|
|
|
int maxCount = 0;
|
|
foreach (var f in Directory.GetFiles(queue, String.Format("{0}-*.xml", date)))
|
|
{
|
|
var count = Int32.Parse(f.Split('-')[5]);
|
|
maxCount = Math.Max(maxCount, count);
|
|
}
|
|
maxCount++;
|
|
|
|
var suffix = String.Format("{0}-{1}-{2}", Environment.UserName, userScope, OS_NAME);
|
|
foreach (var f in Directory.GetFiles(queue, "*" + suffix + ".xml"))
|
|
File.Delete(f);
|
|
|
|
string label = String.Format("{0}-{1}-{2}", date, maxCount, suffix);
|
|
|
|
var testStaging = Path.Combine(output, label);
|
|
Directory.CreateDirectory(testStaging);
|
|
File.Create(Path.Combine(testStaging, "errors.txt"));
|
|
|
|
var release = Path.Combine(fdbRoot, "bin");
|
|
if(!IsRunningOnMono())
|
|
release = Path.Combine(release, "Release");
|
|
|
|
File.Copy(Path.Combine(release, BINARY), Path.Combine(testStaging, BINARY));
|
|
File.Copy(Path.Combine(fdbRoot, "tls-plugins", PLUGIN), Path.Combine(testStaging, PLUGIN));
|
|
|
|
if (IsRunningOnMono())
|
|
File.Copy(Path.Combine(release, BINARY + ".debug"), Path.Combine(testStaging, BINARY + ".debug"));
|
|
|
|
//using (var f = System.IO.File.Open(
|
|
// Path.Combine(testStaging, "summary.xml"),
|
|
// System.IO.FileMode.Create, System.IO.FileAccess.ReadWrite, System.IO.FileShare.Delete))
|
|
//{
|
|
// byte[] bytes = Encoding.UTF8.GetBytes("<Trace>");
|
|
// f.Write(bytes, 0, bytes.Length);
|
|
//}
|
|
|
|
var coverageFiles = Directory.GetFiles(release, "coverage*.xml");
|
|
foreach (var coverage in coverageFiles)
|
|
File.Copy(coverage, Path.Combine(testStaging, Path.GetFileName(coverage)));
|
|
|
|
Directory.CreateDirectory(Path.Combine(testStaging, "tests"));
|
|
|
|
if (testTypes == "fast" || testTypes == "all")
|
|
CopyAll(new DirectoryInfo(Path.Combine(fdbRoot, "tests", "fast")), new DirectoryInfo(Path.Combine(testStaging, "tests", "fast")));
|
|
if (testTypes == "restarting" || testTypes == "all")
|
|
{
|
|
CopyAll(new DirectoryInfo(Path.Combine(fdbRoot, "tests", "restarting")), new DirectoryInfo(Path.Combine(testStaging, "tests", "restarting")));
|
|
//CopyAll(new DirectoryInfo(Path.Combine(fdbRoot, "tests", "oldBinaries")), new DirectoryInfo(Path.Combine(testStaging, "tests", "oldBinaries")));
|
|
}
|
|
if (testTypes == "all")
|
|
{
|
|
CopyAll(new DirectoryInfo(Path.Combine(fdbRoot, "tests", "slow")), new DirectoryInfo(Path.Combine(testStaging, "tests", "slow")));
|
|
CopyAll(new DirectoryInfo(Path.Combine(fdbRoot, "tests", "rare")), new DirectoryInfo(Path.Combine(testStaging, "tests", "rare")));
|
|
}
|
|
|
|
|
|
if (testTypes != "fast" && testTypes != "all" && testTypes != "restarting")
|
|
{
|
|
FileInfo file = new FileInfo(testTypes);
|
|
Directory.CreateDirectory(Path.Combine(testStaging, "tests", file.Directory.Name));
|
|
file.CopyTo(Path.Combine(testStaging, "tests", file.Directory.Name, file.Name), true);
|
|
}
|
|
|
|
var e =
|
|
new XElement("TestDefinition",
|
|
new XElement("Duration",
|
|
new XAttribute("Hours", addHours)),
|
|
new XElement("TestCount", testCount));
|
|
new XDocument(e).Save(Path.Combine(queue, label + ".xml"));
|
|
File.Copy(Path.Combine(queue, label + ".xml"), Path.Combine(testStaging, label + ".xml"));
|
|
|
|
var summaryFile = Path.Combine(testStaging, "summary.xml");
|
|
Console.WriteLine(label);
|
|
|
|
if (!IsRunningOnMono())
|
|
{
|
|
using (var mProcess = new System.Diagnostics.Process())
|
|
{
|
|
mProcess.StartInfo.UseShellExecute = false;
|
|
mProcess.StartInfo.RedirectStandardOutput = true;
|
|
mProcess.StartInfo.FileName = "Magnesium.exe";
|
|
mProcess.StartInfo.Arguments = "Summary " + summaryFile;
|
|
mProcess.Start();
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
public static void CopyAll(DirectoryInfo source, DirectoryInfo target, Func<FileInfo, bool> predicate = null)
|
|
{
|
|
// Check if the target directory exists, if not, create it.
|
|
if (!Directory.Exists(target.FullName))
|
|
Directory.CreateDirectory(target.FullName);
|
|
|
|
// Copy each file into it's new directory.
|
|
foreach (FileInfo fi in source.GetFiles())
|
|
if (predicate == null || predicate(fi))
|
|
fi.CopyTo(Path.Combine(target.ToString(), fi.Name), true);
|
|
|
|
// Copy each subdirectory using recursion.
|
|
foreach (DirectoryInfo diSourceSubDir in source.GetDirectories())
|
|
CopyAll(diSourceSubDir, target.CreateSubdirectory(diSourceSubDir.Name), predicate);
|
|
}
|
|
|
|
static int Auto(string queueDirectory, string runDir, string shareDir, string cacheDir, bool useValgrind, int maxTries)
|
|
{
|
|
try
|
|
{
|
|
queueDirectory = Path.GetFullPath(queueDirectory);
|
|
while (true)
|
|
{
|
|
Test test = getTest(queueDirectory, runDir, shareDir, cacheDir);
|
|
Console.WriteLine("Running test {0}", test.label);
|
|
|
|
// run test
|
|
test.run(useValgrind, maxTries);
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Console.WriteLine("Error: {0}", e);
|
|
return 100;
|
|
}
|
|
}
|
|
|
|
class Test
|
|
{
|
|
public DateTime? testEnd;
|
|
public string queueDirectory;
|
|
public string label;
|
|
public string runDir;
|
|
public string inputDir;
|
|
public string outputDir;
|
|
public string oldBinaryDir;
|
|
public int testCount;
|
|
|
|
public Test(string queueDirectory, string label, string runDir, string shareDir, string cacheDir)
|
|
{
|
|
this.queueDirectory = queueDirectory;
|
|
this.label = label;
|
|
this.runDir = runDir;
|
|
var specFile = Path.Combine(queueDirectory, label + ".xml");
|
|
var testDef = XDocument.Load(specFile).Element("TestDefinition");
|
|
var testDuration = double.Parse(testDef.Element("Duration").Attribute("Hours").Value);
|
|
var testBegin = File.GetCreationTime(specFile);
|
|
testEnd = testDuration < 0 ? (DateTime?)null : testBegin.AddHours(testDuration);
|
|
|
|
testCount = int.Parse(testDef.Element("TestCount").Value);
|
|
outputDir = Path.Combine(queueDirectory, "archive", label);
|
|
Directory.CreateDirectory(outputDir);
|
|
|
|
string oldBinarySourceDir = Path.Combine(shareDir, "oldBinaries");
|
|
|
|
if (cacheDir != null)
|
|
{
|
|
inputDir = Path.Combine(cacheDir, "archive", label);
|
|
Directory.CreateDirectory(Path.Combine(cacheDir, "archive"));
|
|
|
|
if (!Directory.Exists(inputDir))
|
|
{
|
|
takeLock(inputDir);
|
|
if (!Directory.Exists(inputDir))
|
|
{
|
|
string tmpDir = Path.Combine(cacheDir, "archive.part", label + "." + Path.GetRandomFileName() + ".part");
|
|
Directory.CreateDirectory(tmpDir);
|
|
CopyAll(new DirectoryInfo(outputDir), new DirectoryInfo(tmpDir), (FileInfo file) =>
|
|
file.Name != "fdbserver.debug" &&
|
|
!file.Name.StartsWith("summary-") &&
|
|
file.Name != "errors.txt"
|
|
);
|
|
Directory.Move(tmpDir, inputDir);
|
|
}
|
|
releaseLock(inputDir);
|
|
}
|
|
|
|
oldBinaryDir = Path.Combine(cacheDir, "oldBinaries");
|
|
|
|
Directory.CreateDirectory(oldBinaryDir);
|
|
foreach (FileInfo fi in new DirectoryInfo(oldBinarySourceDir).GetFiles())
|
|
{
|
|
var targetName = Path.Combine(oldBinaryDir, fi.Name);
|
|
if (!File.Exists(targetName) || fi.LastWriteTimeUtc != File.GetLastWriteTimeUtc(targetName))
|
|
{
|
|
fi.CopyTo(targetName, true);
|
|
File.SetLastWriteTimeUtc(targetName, fi.LastWriteTimeUtc);
|
|
}
|
|
}
|
|
|
|
foreach (FileInfo fi in new DirectoryInfo(oldBinaryDir).GetFiles())
|
|
{
|
|
var targetName = Path.Combine(oldBinarySourceDir, fi.Name);
|
|
if (!File.Exists(targetName))
|
|
fi.Delete();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
inputDir = outputDir;
|
|
oldBinaryDir = oldBinarySourceDir;
|
|
}
|
|
//Console.WriteLine("TestEnd {0}, now {1}, duration {2}, done {3}", testEnd, DateTime.Now, testDuration, done());
|
|
}
|
|
public bool done()
|
|
{
|
|
return testEnd.HasValue && System.DateTime.Now > testEnd.Value;
|
|
}
|
|
public void run(bool useValgrind, int maxTries)
|
|
{
|
|
Run(Path.Combine(inputDir, BINARY),
|
|
Path.Combine(inputDir, PLUGIN),
|
|
Path.Combine(inputDir, "tests"),
|
|
Path.Combine(outputDir, "summary-" + Environment.MachineName + ".xml"),
|
|
Path.Combine(outputDir, "errors.txt"),
|
|
runDir,
|
|
oldBinaryDir,
|
|
useValgrind,
|
|
maxTries);
|
|
}
|
|
|
|
public void finalize()
|
|
{
|
|
try
|
|
{
|
|
Console.WriteLine("Deleting: {0}", Path.Combine(queueDirectory, label + ".xml"));
|
|
File.Delete(Path.Combine(queueDirectory, label + ".xml"));
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Console.WriteLine("Error deleting queue folder: {0}", e.Message);
|
|
}
|
|
}
|
|
}
|
|
|
|
static Test getTest(string parent, string runDir, string shareDir, string cacheDir)
|
|
{
|
|
while (true)
|
|
{
|
|
var testFiles = Directory.GetFiles(parent, String.Format("*{0}.xml", OS_NAME));
|
|
// (if no tests, wait, try again)
|
|
if (testFiles.Length != 0)
|
|
{
|
|
/*try
|
|
{*/
|
|
var testFile = testFiles[random.Next(testFiles.Length)];
|
|
var test = new Test(parent, Path.GetFileNameWithoutExtension(testFile), runDir, shareDir, cacheDir);
|
|
if ((test.testCount < 0 || UpdateTestTotals(Path.Combine(test.outputDir, "testCount"), test.testCount)) && !test.done())
|
|
return test;
|
|
else
|
|
test.finalize();
|
|
/*}
|
|
catch (Exception)
|
|
{
|
|
// retry opening a test
|
|
}*/
|
|
}
|
|
System.Threading.Thread.Sleep(1000);
|
|
}
|
|
}
|
|
|
|
static void takeLock(string targetFile)
|
|
{
|
|
// Console.WriteLine("Attempting to take lock on {0}", targetFile);
|
|
string lockFile = targetFile + ".lock";
|
|
while (true)
|
|
{
|
|
try
|
|
{
|
|
using (var f = System.IO.File.Open(lockFile, System.IO.FileMode.CreateNew))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
catch (System.IO.IOException e)
|
|
{
|
|
Console.WriteLine("Waiting for file lock: {0}", e.Message);
|
|
System.Threading.Thread.Sleep(250);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void releaseLock(string targetFile)
|
|
{
|
|
File.Delete(targetFile + ".lock");
|
|
}
|
|
|
|
private static bool UpdateTestTotals(string countFileName, int desiredTestCount)
|
|
{
|
|
takeLock(countFileName);
|
|
try
|
|
{
|
|
using (var f = System.IO.File.Open(countFileName, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.ReadWrite))
|
|
{
|
|
int currentCount = 0;
|
|
byte[] b;
|
|
|
|
if (f.Length != 0)
|
|
{
|
|
b = new byte[f.Length];
|
|
f.Read(b, 0, b.Length);
|
|
currentCount = int.Parse(Encoding.UTF8.GetString(b));
|
|
}
|
|
|
|
if (currentCount >= desiredTestCount)
|
|
return false;
|
|
|
|
f.SetLength(0);
|
|
b = Encoding.UTF8.GetBytes(String.Format("{0}", currentCount + 1));
|
|
f.Write(b, 0, b.Length);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
releaseLock(countFileName);
|
|
}
|
|
}
|
|
|
|
private static int VersionInfo()
|
|
{
|
|
Console.WriteLine("Version: 1.02");
|
|
|
|
Console.WriteLine("FDB Project Ver: " + "${CMAKE_PROJECT_VERSION}");
|
|
Console.WriteLine("FDB Version: " + "${CMAKE_PROJECT_VERSION_MAJOR}" + "." + "${CMAKE_PROJECT_VERSION_MINOR}");
|
|
Console.WriteLine("Source Version: " + "${CURRENT_GIT_VERSION}");
|
|
return 1;
|
|
}
|
|
|
|
private static int UsageMessage()
|
|
{
|
|
Console.WriteLine("Usage:");
|
|
Console.WriteLine(" TestHarness run [temp/runDir] [fdbserver[.exe]] [TLSplugin] [testfolder] [summary.xml] <useValgrind> <maxTries>");
|
|
Console.WriteLine(" TestHarness summarize [trace.xml] [summary.xml] <valgrind-XML-file> <external-error> <traceToStdOut>");
|
|
Console.WriteLine(" TestHarness replay [temp/runDir] [fdbserver[.exe]] [TLSplugin] [summary-in.xml] [summary-out.xml]");
|
|
Console.WriteLine(" TestHarness auto [temp/runDir] [directory] [shareDir] <useValgrind> <maxTries>");
|
|
Console.WriteLine(" TestHarness remote [queue folder] [root foundation folder] [duration in hours] [amount of tests] [all/fast/<test_path>] [scope]");
|
|
Console.WriteLine(" TestHarness extract-errors [summary-file] [error-summary-file]");
|
|
Console.WriteLine(" TestHarness joshua-run <useValgrind> <maxTries>");
|
|
VersionInfo();
|
|
return 1;
|
|
}
|
|
}
|
|
}
|