5 // Zoltan Varga (vargaz@gmail.com)
7 // Copyright (C) 2008 Novell, Inc (http://www.novell.com)
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:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
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.
30 using System
.Threading
;
31 using System
.Diagnostics
;
32 using System
.Collections
.Generic
;
35 // This is a simple test runner with support for parallel execution
38 public class TestRunner
42 public StreamWriter stdout
, stderr
;
45 public static int Main (String
[] args
) {
48 int timeout
= 2 * 60; // in seconds
50 // FIXME: Add support for runtime arguments + env variables
52 string disabled_tests
= null;
53 string runtime
= "mono";
57 while (i
< args
.Length
) {
58 if (args
[i
].StartsWith ("-")) {
59 if (args
[i
] == "-j") {
60 if (i
+ i
>= args
.Length
) {
61 Console
.WriteLine ("Missing argument to -j command line option.");
64 if (args
[i
+ 1] == "a")
65 concurrency
= Environment
.ProcessorCount
;
67 concurrency
= Int32
.Parse (args
[i
+ 1]);
69 } else if (args
[i
] == "--timeout") {
70 if (i
+ i
>= args
.Length
) {
71 Console
.WriteLine ("Missing argument to --timeout command line option.");
74 timeout
= Int32
.Parse (args
[i
+ 1]);
76 } else if (args
[i
] == "--disabled") {
77 if (i
+ i
>= args
.Length
) {
78 Console
.WriteLine ("Missing argument to --disabled command line option.");
81 disabled_tests
= args
[i
+ 1];
83 } else if (args
[i
] == "--runtime") {
84 if (i
+ i
>= args
.Length
) {
85 Console
.WriteLine ("Missing argument to --runtime command line option.");
88 runtime
= args
[i
+ 1];
91 Console
.WriteLine ("Unknown command line option: '" + args
[i
] + "'.");
99 var disabled
= new Dictionary
<string, string> ();
101 if (disabled_tests
!= null) {
102 foreach (string test
in disabled_tests
.Split ())
103 disabled
[test
] = test
;
106 // The remaining arguments are the tests
107 var tests
= new List
<string> ();
108 for (int j
= i
; j
< args
.Length
; ++j
)
109 if (!disabled
.ContainsKey (args
[j
]))
110 tests
.Add (args
[j
]);
115 var processes
= new List
<Process
> ();
116 var failed
= new List
<string> ();
117 var process_data
= new Dictionary
<Process
, ProcessData
> ();
119 object monitor
= new object ();
121 var terminated
= new List
<Process
> ();
123 if (concurrency
!= 1)
124 Console
.WriteLine ("Running tests: ");
126 foreach (string test
in tests
) {
128 while (processes
.Count
== concurrency
) {
129 /* Wait for one process to terminate */
130 Monitor
.Wait (monitor
);
133 /* Cleaup terminated processes */
134 foreach (Process dead
in terminated
) {
135 if (process_data
[dead
].stdout
!= null)
136 process_data
[dead
].stdout
.Close ();
137 if (process_data
[dead
].stderr
!= null)
138 process_data
[dead
].stderr
.Close ();
139 // This is needed to avoid CreateProcess failed errors :(
145 if (concurrency
== 1)
146 Console
.Write ("Testing " + test
+ "... ");
148 /* Spawn a new process */
149 ProcessStartInfo info
= new ProcessStartInfo (runtime
, test
);
150 info
.UseShellExecute
= false;
151 info
.RedirectStandardOutput
= true;
152 info
.RedirectStandardError
= true;
153 Process p
= new Process ();
155 p
.EnableRaisingEvents
= true;
157 ProcessData data
= new ProcessData ();
160 p
.Exited
+= delegate (object sender
, EventArgs e
) {
161 // Anon methods share some of their state, so we can't use
162 // variables which change during the loop (test, p)
163 Process dead
= (Process
)sender
;
166 if (dead
.ExitCode
== 0) {
167 if (concurrency
== 1)
168 Console
.WriteLine ("passed.");
173 if (concurrency
== 1)
174 Console
.WriteLine ("failed.");
177 failed
.Add (process_data
[dead
].test
);
180 processes
.Remove (dead
);
181 terminated
.Add (dead
);
182 Monitor
.Pulse (monitor
);
186 data
.stdout
= new StreamWriter (new FileStream (test
+ ".stdout", FileMode
.Create
));
188 data
.stderr
= new StreamWriter (new FileStream (test
+ ".stderr", FileMode
.Create
));
190 p
.OutputDataReceived
+= delegate (object sender
, DataReceivedEventArgs e
) {
191 Process p2
= (Process
)sender
;
196 fs
= process_data
[p2
].stdout
;
198 if (String
.IsNullOrEmpty (e
.Data
))
199 process_data
[p2
].stdout
= null;
202 if (String
.IsNullOrEmpty (e
.Data
))
205 fs
.WriteLine (e
.Data
);
208 p
.ErrorDataReceived
+= delegate (object sender
, DataReceivedEventArgs e
) {
209 Process p2
= (Process
)sender
;
214 fs
= process_data
[p2
].stderr
;
216 if (String
.IsNullOrEmpty (e
.Data
))
217 process_data
[p2
].stderr
= null;
221 if (String
.IsNullOrEmpty (e
.Data
)) {
225 process_data
[p2
].stderr
= null;
229 fs
.WriteLine (e
.Data
);
234 process_data
[p
] = data
;
238 p
.BeginOutputReadLine ();
239 p
.BeginErrorReadLine ();
242 bool timed_out
= false;
244 /* Wait for all processes to terminate */
247 int nprocesses
= processes
.Count
;
252 bool res
= Monitor
.Wait (monitor
, 1000 * timeout
);
260 Console
.WriteLine ();
263 Console
.WriteLine ("\nrunning tests timed out:\n");
264 Console
.WriteLine (npassed
+ nfailed
);
266 foreach (Process p
in processes
) {
267 Console
.WriteLine (process_data
[p
].test
);
273 Console
.WriteLine ("" + npassed
+ " test(s) passed. " + nfailed
+ " test(s) did not pass.");
275 Console
.WriteLine ("\nFailed tests:\n");
276 foreach (string s
in failed
)
277 Console
.WriteLine (s
);