Merge pull request #2820 from kumpera/license-change-rebased
[mono.git] / mono / tests / test-runner.cs
index f1606197bacf480fa20aaeb9340bc47dd0389dea..ae52220e335db404bdfe5ff59d2c6d0aa701d702 100644 (file)
@@ -6,24 +6,7 @@
 //
 // Copyright (C) 2008 Novell, Inc (http://www.novell.com)
 //
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-// 
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-// 
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
 //
 using System;
 using System.IO;
@@ -32,6 +15,8 @@ using System.Diagnostics;
 using System.Collections.Generic;
 using System.Globalization;
 using System.Xml;
+using System.Text.RegularExpressions;
+using Mono.Unix.Native;
 
 //
 // This is a simple test runner with support for parallel execution
@@ -39,6 +24,9 @@ using System.Xml;
 
 public class TestRunner
 {
+       const string TEST_TIME_FORMAT = "mm\\:ss\\.fff";
+       const string ENV_TIMEOUT = "TEST_DRIVER_TIMEOUT_SEC";
+
        class ProcessData {
                public string test;
                public StreamWriter stdout, stderr;
@@ -55,8 +43,7 @@ public class TestRunner
                int timeout = 2 * 60; // in seconds
                int expectedExitCode = 0;
                string testsuiteName = null;
-
-               DateTime test_start_time = DateTime.UtcNow;
+               string inputFile = null;
 
                // FIXME: Add support for runtime arguments + env variables
 
@@ -69,7 +56,7 @@ public class TestRunner
                while (i < args.Length) {
                        if (args [i].StartsWith ("-")) {
                                if (args [i] == "-j") {
-                                       if (i + i >= args.Length) {
+                                       if (i + 1 >= args.Length) {
                                                Console.WriteLine ("Missing argument to -j command line option.");
                                                return 1;
                                        }
@@ -79,28 +66,28 @@ public class TestRunner
                                                concurrency = Int32.Parse (args [i + 1]);
                                        i += 2;
                                } else if (args [i] == "--timeout") {
-                                       if (i + i >= args.Length) {
+                                       if (i + 1 >= args.Length) {
                                                Console.WriteLine ("Missing argument to --timeout command line option.");
                                                return 1;
                                        }
                                        timeout = Int32.Parse (args [i + 1]);
                                        i += 2;
                                } else if (args [i] == "--disabled") {
-                                       if (i + i >= args.Length) {
+                                       if (i + 1 >= args.Length) {
                                                Console.WriteLine ("Missing argument to --disabled command line option.");
                                                return 1;
                                        }
                                        disabled_tests = args [i + 1];
                                        i += 2;
                                } else if (args [i] == "--runtime") {
-                                       if (i + i >= args.Length) {
+                                       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] == "--opt-sets") {
-                                       if (i + i >= args.Length) {
+                                       if (i + 1 >= args.Length) {
                                                Console.WriteLine ("Missing argument to --opt-sets command line option.");
                                                return 1;
                                        }
@@ -108,19 +95,26 @@ public class TestRunner
                                                opt_sets.Add (s);
                                        i += 2;
                                } else if (args [i] == "--expected-exit-code") {
-                                       if (i + i >= args.Length) {
+                                       if (i + 1 >= args.Length) {
                                                Console.WriteLine ("Missing argument to --expected-exit-code command line option.");
                                                return 1;
                                        }
                                        expectedExitCode = Int32.Parse (args [i + 1]);
                                        i += 2;
                                } else if (args [i] == "--testsuite-name") {
-                                       if (i + i >= args.Length) {
+                                       if (i + 1 >= args.Length) {
                                                Console.WriteLine ("Missing argument to --testsuite-name command line option.");
                                                return 1;
                                        }
                                        testsuiteName = args [i + 1];
                                        i += 2;
+                               } else if (args [i] == "--input-file") {
+                                       if (i + 1 >= args.Length) {
+                                               Console.WriteLine ("Missing argument to --input-file command line option.");
+                                               return 1;
+                                       }
+                                       inputFile = args [i + 1];
+                                       i += 2;
                                } else {
                                        Console.WriteLine ("Unknown command line option: '" + args [i] + "'.");
                                        return 1;
@@ -142,11 +136,16 @@ public class TestRunner
                                disabled [test] = test;
                }
 
-               // The remaining arguments are the tests
                var tests = new List<string> ();
-               for (int j = i; j < args.Length; ++j)
-                       if (!disabled.ContainsKey (args [j]))
-                               tests.Add (args [j]);
+
+               if (!String.IsNullOrEmpty (inputFile)) {
+                       tests.AddRange (File.ReadAllLines (inputFile));
+               } else {
+                       // The remaining arguments are the tests
+                       for (int j = i; j < args.Length; ++j)
+                               if (!disabled.ContainsKey (args [j]))
+                                       tests.Add (args [j]);
+               }
 
                var passed = new List<ProcessData> ();
                var failed = new List<ProcessData> ();
@@ -154,8 +153,7 @@ public class TestRunner
 
                object monitor = new object ();
 
-               if (concurrency != 1)
-                       Console.WriteLine ("Running tests: ");
+               Console.WriteLine ("Running tests: ");
 
                var test_info = new Queue<TestInfo> ();
                if (opt_sets.Count == 0) {
@@ -166,10 +164,19 @@ public class TestRunner
                                foreach (string s in tests)
                                        test_info.Enqueue (new TestInfo { test = s, opt_set = opt });
                        }
-               }               
+               }
+
+               /* compute the max length of test names, to have an optimal output width */
+               int output_width = -1;
+               foreach (TestInfo ti in test_info) {
+                       if (ti.test.Length > output_width)
+                               output_width = Math.Min (120, ti.test.Length);
+               }
 
                List<Thread> threads = new List<Thread> (concurrency);
 
+               DateTime test_start_time = DateTime.UtcNow;
+
                for (int j = 0; j < concurrency; ++j) {
                        Thread thread = new Thread (() => {
                                while (true) {
@@ -181,11 +188,12 @@ public class TestRunner
                                                ti = test_info.Dequeue ();
                                        }
 
+                                       var output = new StringWriter ();
+
                                        string test = ti.test;
                                        string opt_set = ti.opt_set;
 
-                                       if (concurrency == 1)
-                                               Console.Write ("Testing " + test + "... ");
+                                       output.Write (String.Format ("{{0,-{0}}} ", output_width), test);
 
                                        /* Spawn a new process */
                                        string process_args;
@@ -197,6 +205,7 @@ public class TestRunner
                                        info.UseShellExecute = false;
                                        info.RedirectStandardOutput = true;
                                        info.RedirectStandardError = true;
+                                       info.EnvironmentVariables[ENV_TIMEOUT] = timeout.ToString();
                                        Process p = new Process ();
                                        p.StartInfo = info;
 
@@ -231,6 +240,8 @@ public class TestRunner
                                                }
                                        };
 
+                                       var start = DateTime.UtcNow;
+
                                        p.Start ();
 
                                        p.BeginOutputReadLine ();
@@ -241,33 +252,39 @@ public class TestRunner
                                                        timedout.Add (data);
                                                }
 
-                                               if (concurrency == 1)
-                                                       Console.WriteLine ("timed out.");
-                                               else
-                                                       Console.Write (".");
+                                               // Force the process to print a thread dump
+                                               try {
+                                                       Syscall.kill (p.Id, Signum.SIGQUIT);
+                                                       Thread.Sleep (1000);
+                                               } catch {
+                                               }
+
+                                               output.Write ("timed out");
 
                                                p.Kill ();
                                        } else if (p.ExitCode != expectedExitCode) {
+                                               var end = DateTime.UtcNow;
+
                                                lock (monitor) {
                                                        failed.Add (data);
                                                }
 
-                                               if (concurrency == 1)
-                                                       Console.WriteLine ("failed.");
-                                               else
-                                                       Console.Write (".");
+                                               output.Write ("failed, time: {0}, exit code: {1}", (end - start).ToString (TEST_TIME_FORMAT), p.ExitCode);
                                        } else {
+                                               var end = DateTime.UtcNow;
+
                                                lock (monitor) {
                                                        passed.Add (data);
                                                }
 
-                                               if (concurrency == 1)
-                                                       Console.WriteLine ("passed.");
-                                               else
-                                                       Console.Write (".");
+                                               output.Write ("passed, time: {0}", (end - start).ToString (TEST_TIME_FORMAT));
                                        }
 
                                        p.Close ();
+
+                                       lock (monitor) {
+                                               Console.WriteLine (output.ToString ());
+                                       }
                                }
                        });
 
@@ -279,15 +296,16 @@ public class TestRunner
                for (int j = 0; j < threads.Count; ++j)
                        threads [j].Join ();
 
+               TimeSpan test_time = DateTime.UtcNow - test_start_time;
+
                int npassed = passed.Count;
                int nfailed = failed.Count;
                int ntimedout = timedout.Count;
 
-               TimeSpan test_time = DateTime.UtcNow - test_start_time;
                XmlWriterSettings xmlWriterSettings = new XmlWriterSettings ();
                xmlWriterSettings.NewLineOnAttributes = true;
                xmlWriterSettings.Indent = true;
-               using (XmlWriter writer = XmlWriter.Create (String.Format ("TestResults_{0}.xml", testsuiteName), xmlWriterSettings)) {
+               using (XmlWriter writer = XmlWriter.Create (String.Format ("TestResult-{0}.xml", testsuiteName), xmlWriterSettings)) {
                        // <?xml version="1.0" encoding="utf-8" standalone="no"?>
                        writer.WriteStartDocument ();
                        // <!--This file represents the results of running a test suite-->
@@ -406,6 +424,8 @@ public class TestRunner
                        writer.WriteEndDocument ();
                }
 
+               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);
@@ -446,8 +466,14 @@ public class TestRunner
 
        static string DumpPseudoTrace (string filename) {
                if (File.Exists (filename))
-                       return File.ReadAllText (filename);
+                       return FilterInvalidXmlChars (File.ReadAllText (filename));
                else
                        return string.Empty;
        }
+
+       static string FilterInvalidXmlChars (string text) {
+               // Spec at http://www.w3.org/TR/2008/REC-xml-20081126/#charsets says only the following chars are valid in XML:
+               // Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]      /* any Unicode character, excluding the surrogate blocks, FFFE, and FFFF. */
+               return Regex.Replace (text, @"[^\x09\x0A\x0D\x20-\uD7FF\uE000-\uFFFD\u10000-\u10FFFF]", "");
+       }
 }