PageRenderTime 46ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/mono/tests/test-runner.cs

https://bitbucket.org/foobar22/mono
C# | 319 lines | 230 code | 48 blank | 41 comment | 64 complexity | 4657da6ded2ad623d46bde6fe0a2df18 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0, Unlicense, Apache-2.0, LGPL-2.0
  1. //
  2. // test-runner.cs
  3. //
  4. // Author:
  5. // Zoltan Varga (vargaz@gmail.com)
  6. //
  7. // Copyright (C) 2008 Novell, Inc (http://www.novell.com)
  8. //
  9. // Permission is hereby granted, free of charge, to any person obtaining
  10. // a copy of this software and associated documentation files (the
  11. // "Software"), to deal in the Software without restriction, including
  12. // without limitation the rights to use, copy, modify, merge, publish,
  13. // distribute, sublicense, and/or sell copies of the Software, and to
  14. // permit persons to whom the Software is furnished to do so, subject to
  15. // the following conditions:
  16. //
  17. // The above copyright notice and this permission notice shall be
  18. // included in all copies or substantial portions of the Software.
  19. //
  20. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  21. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  22. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  23. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  24. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  25. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  26. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  27. //
  28. using System;
  29. using System.IO;
  30. using System.Threading;
  31. using System.Diagnostics;
  32. using System.Collections.Generic;
  33. //
  34. // This is a simple test runner with support for parallel execution
  35. //
  36. public class TestRunner
  37. {
  38. class ProcessData {
  39. public string test;
  40. public StreamWriter stdout, stderr;
  41. }
  42. class TestInfo {
  43. public string test, opt_set;
  44. }
  45. public static int Main (String[] args) {
  46. // Defaults
  47. int concurrency = 1;
  48. int timeout = 2 * 60; // in seconds
  49. // FIXME: Add support for runtime arguments + env variables
  50. string disabled_tests = null;
  51. string runtime = "mono";
  52. var opt_sets = new List<string> ();
  53. // Process options
  54. int i = 0;
  55. while (i < args.Length) {
  56. if (args [i].StartsWith ("-")) {
  57. if (args [i] == "-j") {
  58. if (i + i >= args.Length) {
  59. Console.WriteLine ("Missing argument to -j command line option.");
  60. return 1;
  61. }
  62. if (args [i + 1] == "a")
  63. concurrency = Environment.ProcessorCount;
  64. else
  65. concurrency = Int32.Parse (args [i + 1]);
  66. i += 2;
  67. } else if (args [i] == "--timeout") {
  68. if (i + i >= args.Length) {
  69. Console.WriteLine ("Missing argument to --timeout command line option.");
  70. return 1;
  71. }
  72. timeout = Int32.Parse (args [i + 1]);
  73. i += 2;
  74. } else if (args [i] == "--disabled") {
  75. if (i + i >= args.Length) {
  76. Console.WriteLine ("Missing argument to --disabled command line option.");
  77. return 1;
  78. }
  79. disabled_tests = args [i + 1];
  80. i += 2;
  81. } else if (args [i] == "--runtime") {
  82. if (i + i >= args.Length) {
  83. Console.WriteLine ("Missing argument to --runtime command line option.");
  84. return 1;
  85. }
  86. runtime = args [i + 1];
  87. i += 2;
  88. } else if (args [i] == "--opt-sets") {
  89. if (i + i >= args.Length) {
  90. Console.WriteLine ("Missing argument to --opt-sets command line option.");
  91. return 1;
  92. }
  93. foreach (var s in args [i + 1].Split ())
  94. opt_sets.Add (s);
  95. i += 2;
  96. } else {
  97. Console.WriteLine ("Unknown command line option: '" + args [i] + "'.");
  98. return 1;
  99. }
  100. } else {
  101. break;
  102. }
  103. }
  104. var disabled = new Dictionary <string, string> ();
  105. if (disabled_tests != null) {
  106. foreach (string test in disabled_tests.Split ())
  107. disabled [test] = test;
  108. }
  109. // The remaining arguments are the tests
  110. var tests = new List<string> ();
  111. for (int j = i; j < args.Length; ++j)
  112. if (!disabled.ContainsKey (args [j]))
  113. tests.Add (args [j]);
  114. int npassed = 0;
  115. int nfailed = 0;
  116. var processes = new List<Process> ();
  117. var failed = new List<string> ();
  118. var process_data = new Dictionary<Process, ProcessData> ();
  119. object monitor = new object ();
  120. var terminated = new List<Process> ();
  121. if (concurrency != 1)
  122. Console.WriteLine ("Running tests: ");
  123. var test_info = new List<TestInfo> ();
  124. if (opt_sets.Count == 0) {
  125. foreach (string s in tests)
  126. test_info.Add (new TestInfo { test = s });
  127. } else {
  128. foreach (string opt in opt_sets) {
  129. foreach (string s in tests)
  130. test_info.Add (new TestInfo { test = s, opt_set = opt });
  131. }
  132. }
  133. foreach (TestInfo ti in test_info) {
  134. lock (monitor) {
  135. while (processes.Count == concurrency) {
  136. /* Wait for one process to terminate */
  137. Monitor.Wait (monitor);
  138. }
  139. /* Cleaup terminated processes */
  140. foreach (Process dead in terminated) {
  141. if (process_data [dead].stdout != null)
  142. process_data [dead].stdout.Close ();
  143. if (process_data [dead].stderr != null)
  144. process_data [dead].stderr.Close ();
  145. // This is needed to avoid CreateProcess failed errors :(
  146. dead.Close ();
  147. }
  148. terminated.Clear ();
  149. }
  150. string test = ti.test;
  151. string opt_set = ti.opt_set;
  152. if (concurrency == 1)
  153. Console.Write ("Testing " + test + "... ");
  154. /* Spawn a new process */
  155. string process_args;
  156. if (opt_set == null)
  157. process_args = test;
  158. else
  159. process_args = "-O=" + opt_set + " " + test;
  160. ProcessStartInfo info = new ProcessStartInfo (runtime, process_args);
  161. info.UseShellExecute = false;
  162. info.RedirectStandardOutput = true;
  163. info.RedirectStandardError = true;
  164. Process p = new Process ();
  165. p.StartInfo = info;
  166. p.EnableRaisingEvents = true;
  167. ProcessData data = new ProcessData ();
  168. data.test = test;
  169. p.Exited += delegate (object sender, EventArgs e) {
  170. // Anon methods share some of their state, so we can't use
  171. // variables which change during the loop (test, p)
  172. Process dead = (Process)sender;
  173. lock (monitor) {
  174. if (dead.ExitCode == 0) {
  175. if (concurrency == 1)
  176. Console.WriteLine ("passed.");
  177. else
  178. Console.Write (".");
  179. npassed ++;
  180. } else {
  181. if (concurrency == 1)
  182. Console.WriteLine ("failed.");
  183. else
  184. Console.Write ("F");
  185. failed.Add (process_data [dead].test);
  186. nfailed ++;
  187. }
  188. processes.Remove (dead);
  189. terminated.Add (dead);
  190. Monitor.Pulse (monitor);
  191. }
  192. };
  193. string log_prefix = "";
  194. if (opt_set != null)
  195. log_prefix = "." + opt_set.Replace ("-", "no").Replace (",", "_");
  196. data.stdout = new StreamWriter (new FileStream (test + log_prefix + ".stdout", FileMode.Create));
  197. data.stderr = new StreamWriter (new FileStream (test + log_prefix + ".stderr", FileMode.Create));
  198. p.OutputDataReceived += delegate (object sender, DataReceivedEventArgs e) {
  199. Process p2 = (Process)sender;
  200. StreamWriter fs;
  201. lock (monitor) {
  202. fs = process_data [p2].stdout;
  203. if (String.IsNullOrEmpty (e.Data))
  204. process_data [p2].stdout = null;
  205. }
  206. if (String.IsNullOrEmpty (e.Data))
  207. fs.Close ();
  208. else
  209. fs.WriteLine (e.Data);
  210. };
  211. p.ErrorDataReceived += delegate (object sender, DataReceivedEventArgs e) {
  212. Process p2 = (Process)sender;
  213. StreamWriter fs;
  214. lock (monitor) {
  215. fs = process_data [p2].stderr;
  216. if (String.IsNullOrEmpty (e.Data))
  217. process_data [p2].stderr = null;
  218. }
  219. if (String.IsNullOrEmpty (e.Data)) {
  220. fs.Close ();
  221. lock (monitor) {
  222. process_data [p2].stderr = null;
  223. }
  224. }
  225. else
  226. fs.WriteLine (e.Data);
  227. };
  228. lock (monitor) {
  229. processes.Add (p);
  230. process_data [p] = data;
  231. }
  232. p.Start ();
  233. p.BeginOutputReadLine ();
  234. p.BeginErrorReadLine ();
  235. }
  236. bool timed_out = false;
  237. /* Wait for all processes to terminate */
  238. while (true) {
  239. lock (monitor) {
  240. int nprocesses = processes.Count;
  241. if (nprocesses == 0)
  242. break;
  243. bool res = Monitor.Wait (monitor, 1000 * timeout);
  244. if (!res) {
  245. timed_out = true;
  246. break;
  247. }
  248. }
  249. }
  250. Console.WriteLine ();
  251. if (timed_out) {
  252. Console.WriteLine ("\nrunning tests timed out:\n");
  253. Console.WriteLine (npassed + nfailed);
  254. lock (monitor) {
  255. foreach (Process p in processes) {
  256. Console.WriteLine (process_data [p].test);
  257. }
  258. }
  259. return 1;
  260. }
  261. Console.WriteLine ("" + npassed + " test(s) passed. " + nfailed + " test(s) did not pass.");
  262. if (nfailed > 0) {
  263. Console.WriteLine ("\nFailed tests:\n");
  264. foreach (string s in failed)
  265. Console.WriteLine (s);
  266. return 1;
  267. } else {
  268. return 0;
  269. }
  270. }
  271. }