using System.Collections.Generic;
using System.Globalization;
using System.Xml;
+using System.Text;
using System.Text.RegularExpressions;
+
+#if !MOBILE_STATIC
using Mono.Unix.Native;
+#endif
//
// This is a simple test runner with support for parallel execution
{
const string TEST_TIME_FORMAT = "mm\\:ss\\.fff";
const string ENV_TIMEOUT = "TEST_DRIVER_TIMEOUT_SEC";
+ const string MONO_PATH = "MONO_PATH";
class ProcessData {
public string test;
- public StreamWriter stdout, stderr;
- public string stdoutFile, stderrFile;
+ public StringBuilder stdout, stderr;
+ public string stdoutName, stderrName;
}
class TestInfo {
int concurrency = 1;
int timeout = 2 * 60; // in seconds
int expectedExitCode = 0;
+ bool verbose = false;
string testsuiteName = null;
string inputFile = null;
- // FIXME: Add support for runtime arguments + env variables
-
string disabled_tests = null;
string runtime = "mono";
+ string config = null;
+ string mono_path = null;
var opt_sets = new List<string> ();
+ string aot_run_flags = null;
+ string aot_build_flags = null;
+
// Process options
int i = 0;
while (i < args.Length) {
}
runtime = args [i + 1];
i += 2;
+ } else if (args [i] == "--config") {
+ if (i + 1 >= args.Length) {
+ Console.WriteLine ("Missing argument to --config command line option.");
+ return 1;
+ }
+ config = args [i + 1];
+ i += 2;
} else if (args [i] == "--opt-sets") {
if (i + 1 >= args.Length) {
Console.WriteLine ("Missing argument to --opt-sets command line option.");
}
inputFile = args [i + 1];
i += 2;
+ } else if (args [i] == "--runtime") {
+ if (i + 1 >= args.Length) {
+ Console.WriteLine ("Missing argument to --runtime command line option.");
+ return 1;
+ }
+ runtime = args [i + 1];
+ i += 2;
+ } else if (args [i] == "--mono-path") {
+ if (i + 1 >= args.Length) {
+ Console.WriteLine ("Missing argument to --mono-path command line option.");
+ return 1;
+ }
+ mono_path = args [i + 1].Substring(0, args [i + 1].Length);
+
+ i += 2;
+ } else if (args [i] == "--aot-run-flags") {
+ if (i + 1 >= args.Length) {
+ Console.WriteLine ("Missing argument to --aot-run-flags command line option.");
+ return 1;
+ }
+ aot_run_flags = args [i + 1].Substring(0, args [i + 1].Length);
+ i += 2;
+ } else if (args [i] == "--aot-build-flags") {
+ if (i + 1 >= args.Length) {
+ Console.WriteLine ("Missing argument to --aot-build-flags command line option.");
+ return 1;
+ }
+ aot_build_flags = args [i + 1].Substring(0, args [i + 1].Length);
+ i += 2;
+ } else if (args [i] == "--verbose") {
+ verbose = true;
+ i ++;
} else {
Console.WriteLine ("Unknown command line option: '" + args [i] + "'.");
return 1;
output_width = Math.Min (120, ti.test.Length);
}
+ if (aot_build_flags != null) {
+ Console.WriteLine("AOT compiling tests");
+
+ object aot_monitor = new object ();
+ var aot_queue = new Queue<String> (tests);
+
+ List<Thread> build_threads = new List<Thread> (concurrency);
+
+ for (int j = 0; j < concurrency; ++j) {
+ Thread thread = new Thread (() => {
+ while (true) {
+ String test_name;
+
+ lock (aot_monitor) {
+ if (aot_queue.Count == 0)
+ break;
+ test_name = aot_queue.Dequeue ();
+ }
+
+ string test_bitcode_output = test_name + "_bitcode_tmp";
+ string test_bitcode_arg = ",temp-path=" + test_bitcode_output;
+ string aot_args = aot_build_flags + test_bitcode_arg + " " + test_name;
+
+ Directory.CreateDirectory(test_bitcode_output);
+
+ ProcessStartInfo job = new ProcessStartInfo (runtime, aot_args);
+ job.UseShellExecute = false;
+ job.EnvironmentVariables[ENV_TIMEOUT] = timeout.ToString();
+ job.EnvironmentVariables[MONO_PATH] = mono_path;
+ Process compiler = new Process ();
+ compiler.StartInfo = job;
+
+ compiler.Start ();
+
+ if (!compiler.WaitForExit (timeout * 1000)) {
+ try {
+ compiler.Kill ();
+ } catch {
+ }
+ throw new Exception(String.Format("Timeout AOT compiling tests, output in {0}", test_bitcode_output));
+ } else if (compiler.ExitCode != 0) {
+ throw new Exception(String.Format("Error AOT compiling tests, output in {0}", test_bitcode_output));
+ } else {
+ Directory.Delete (test_bitcode_output, true);
+ }
+ }
+ });
+
+ thread.Start ();
+
+ build_threads.Add (thread);
+ }
+
+ for (int j = 0; j < build_threads.Count; ++j)
+ build_threads [j].Join ();
+
+ Console.WriteLine("Done compiling");
+ }
+
List<Thread> threads = new List<Thread> (concurrency);
DateTime test_start_time = DateTime.UtcNow;
string test = ti.test;
string opt_set = ti.opt_set;
- output.Write (String.Format ("{{0,-{0}}} ", output_width), test);
+ if (verbose) {
+ output.Write (String.Format ("{{0,-{0}}} ", output_width), test);
+ } else {
+ Console.Write (".");
+ }
+
+ string test_invoke;
+
+ if (aot_run_flags != null)
+ test_invoke = aot_run_flags + " " + test;
+ else
+ test_invoke = test;
/* Spawn a new process */
string process_args;
if (opt_set == null)
- process_args = test;
+ process_args = test_invoke;
else
- process_args = "-O=" + opt_set + " " + test;
+ process_args = "-O=" + opt_set + " " + test_invoke;
+
ProcessStartInfo info = new ProcessStartInfo (runtime, process_args);
info.UseShellExecute = false;
info.RedirectStandardOutput = true;
info.RedirectStandardError = true;
info.EnvironmentVariables[ENV_TIMEOUT] = timeout.ToString();
+ if (config != null)
+ info.EnvironmentVariables["MONO_CONFIG"] = config;
+ if (mono_path != null)
+ info.EnvironmentVariables[MONO_PATH] = mono_path;
Process p = new Process ();
p.StartInfo = info;
if (opt_set != null)
log_prefix = "." + opt_set.Replace ("-", "no").Replace (",", "_");
- data.stdoutFile = test + log_prefix + ".stdout";
- data.stdout = new StreamWriter (new FileStream (data.stdoutFile, FileMode.Create));
+ data.stdoutName = test + log_prefix + ".stdout";
+ data.stdout = new StringBuilder ();
- data.stderrFile = test + log_prefix + ".stderr";
- data.stderr = new StreamWriter (new FileStream (data.stderrFile, FileMode.Create));
+ data.stderrName = test + log_prefix + ".stderr";
+ data.stderr = new StringBuilder ();
p.OutputDataReceived += delegate (object sender, DataReceivedEventArgs e) {
if (e.Data != null) {
- data.stdout.WriteLine (e.Data);
- } else {
- data.stdout.Flush ();
- data.stdout.Close ();
+ data.stdout.AppendLine (e.Data);
}
};
p.ErrorDataReceived += delegate (object sender, DataReceivedEventArgs e) {
if (e.Data != null) {
- data.stderr.WriteLine (e.Data);
- } else {
- data.stderr.Flush ();
- data.stderr.Close ();
+ data.stderr.AppendLine (e.Data);
}
};
timedout.Add (data);
}
+#if !MOBILE_STATIC
// Force the process to print a thread dump
try {
Syscall.kill (p.Id, Signum.SIGQUIT);
Thread.Sleep (1000);
} catch {
}
+#endif
- output.Write ($"timed out ({timeout}s)");
+ if (verbose) {
+ output.Write ($"timed out ({timeout}s)");
+ }
try {
p.Kill ();
failed.Add (data);
}
- output.Write ("failed, time: {0}, exit code: {1}", (end - start).ToString (TEST_TIME_FORMAT), p.ExitCode);
+ if (verbose)
+ output.Write ("failed, time: {0}, exit code: {1}", (end - start).ToString (TEST_TIME_FORMAT), p.ExitCode);
} else {
var end = DateTime.UtcNow;
passed.Add (data);
}
- output.Write ("passed, time: {0}", (end - start).ToString (TEST_TIME_FORMAT));
+ if (verbose)
+ output.Write ("passed, time: {0}", (end - start).ToString (TEST_TIME_FORMAT));
}
p.Close ();
lock (monitor) {
- Console.WriteLine (output.ToString ());
+ if (verbose)
+ Console.WriteLine (output.ToString ());
}
}
});
XmlWriterSettings xmlWriterSettings = new XmlWriterSettings ();
xmlWriterSettings.NewLineOnAttributes = true;
xmlWriterSettings.Indent = true;
- using (XmlWriter writer = XmlWriter.Create (String.Format ("TestResult-{0}.xml", testsuiteName), xmlWriterSettings)) {
+
+ string xmlPath = String.Format ("TestResult-{0}.xml", testsuiteName);
+ using (XmlWriter writer = XmlWriter.Create (xmlPath, xmlWriterSettings)) {
// <?xml version="1.0" encoding="utf-8" standalone="no"?>
writer.WriteStartDocument ();
// <!--This file represents the results of running a test suite-->
writer.WriteAttributeString ("asserts", "1");
writer.WriteStartElement ("failure");
writer.WriteStartElement ("message");
- writer.WriteCData (DumpPseudoTrace (pd.stdoutFile));
+ writer.WriteCData (FilterInvalidXmlChars (pd.stdout.ToString ()));
writer.WriteEndElement ();
writer.WriteStartElement ("stack-trace");
- writer.WriteCData (DumpPseudoTrace (pd.stderrFile));
+ writer.WriteCData (FilterInvalidXmlChars (pd.stderr.ToString ()));
writer.WriteEndElement ();
writer.WriteEndElement ();
writer.WriteEndElement ();
writer.WriteAttributeString ("asserts", "1");
writer.WriteStartElement ("failure");
writer.WriteStartElement ("message");
- writer.WriteCData (DumpPseudoTrace (pd.stdoutFile));
+ writer.WriteCData (FilterInvalidXmlChars (pd.stdout.ToString ()));
writer.WriteEndElement ();
writer.WriteStartElement ("stack-trace");
- writer.WriteCData (DumpPseudoTrace (pd.stderrFile));
+ writer.WriteCData (FilterInvalidXmlChars (pd.stderr.ToString ()));
writer.WriteEndElement ();
writer.WriteEndElement ();
writer.WriteEndElement ();
// </test-results>
writer.WriteEndElement ();
writer.WriteEndDocument ();
+
+ string babysitterXmlList = Environment.GetEnvironmentVariable("MONO_BABYSITTER_NUNIT_XML_LIST_FILE");
+ if (!String.IsNullOrEmpty(babysitterXmlList)) {
+ try {
+ string fullXmlPath = Path.GetFullPath(xmlPath);
+ File.AppendAllText(babysitterXmlList, fullXmlPath + Environment.NewLine);
+ } catch (Exception e) {
+ Console.WriteLine("Attempted to record XML path to file {0} but failed.", babysitterXmlList);
+ }
+ }
}
- Console.WriteLine ();
- Console.WriteLine ("Time: {0}", test_time.ToString (TEST_TIME_FORMAT));
- Console.WriteLine ();
- Console.WriteLine ("{0,4} test(s) passed", npassed);
- Console.WriteLine ("{0,4} test(s) failed", nfailed);
- Console.WriteLine ("{0,4} test(s) timed out", ntimedout);
+ if (verbose) {
+ Console.WriteLine ();
+ Console.WriteLine ("Time: {0}", test_time.ToString (TEST_TIME_FORMAT));
+ Console.WriteLine ();
+ Console.WriteLine ("{0,4} test(s) passed", npassed);
+ Console.WriteLine ("{0,4} test(s) failed", nfailed);
+ Console.WriteLine ("{0,4} test(s) timed out", ntimedout);
+ } else {
+ Console.WriteLine ();
+ Console.WriteLine (String.Format ("{0} test(s) passed, {1} test(s) did not pass.", npassed, nfailed));
+ }
if (nfailed > 0) {
Console.WriteLine ();
foreach (ProcessData pd in failed) {
Console.WriteLine ();
Console.WriteLine (pd.test);
- DumpFile (pd.stdoutFile);
- DumpFile (pd.stderrFile);
+ DumpFile (pd.stdoutName, pd.stdout.ToString ());
+ DumpFile (pd.stderrName, pd.stderr.ToString ());
}
}
foreach (ProcessData pd in timedout) {
Console.WriteLine ();
Console.WriteLine (pd.test);
- DumpFile (pd.stdoutFile);
- DumpFile (pd.stderrFile);
+ DumpFile (pd.stdoutName, pd.stdout.ToString ());
+ DumpFile (pd.stderrName, pd.stderr.ToString ());
}
}
return (ntimedout == 0 && nfailed == 0) ? 0 : 1;
}
- static void DumpFile (string filename) {
- if (File.Exists (filename)) {
- Console.WriteLine ("=============== {0} ===============", filename);
- Console.WriteLine (File.ReadAllText (filename));
- Console.WriteLine ("=============== EOF ===============");
- }
- }
-
- static string DumpPseudoTrace (string filename) {
- if (File.Exists (filename))
- return FilterInvalidXmlChars (File.ReadAllText (filename));
- else
- return string.Empty;
+ static void DumpFile (string filename, string text) {
+ Console.WriteLine ("=============== {0} ===============", filename);
+ Console.WriteLine (text);
+ Console.WriteLine ("=============== EOF ===============");
}
static string FilterInvalidXmlChars (string text) {