* roottypes.cs: Rename from tree.cs.
[mono.git] / mcs / class / Microsoft.JScript / Test / Mozilla / runner.cs
1 using System;
2 using System.Text;
3 using System.Diagnostics;
4 using System.Collections;
5 using System.IO;
6 using System.Text.RegularExpressions;
7
8 namespace JSTestRunner {
9         class Program {
10                 struct Config {
11                         public bool is_win32;
12                         public string preamble;
13                         public string test_file;
14                         public string fail_file;
15                         public bool full_run;
16                         public string test_dir;
17                         public string compile_cmd;
18                         public string compile_args;
19                         public string run_cmd;
20                         public string run_args;
21                 }
22
23                 static Config config;
24                 static string preamble;
25                 static string [] fail_tests;
26                 static string [] tests;
27
28                 static void Main (string [] args)
29                 {
30                         LoadConfig ();
31                         ReadFiles ();
32
33                         // Can specify test to run as first argument
34                         if (args.Length == 1) {
35                                 string arg = args [0];
36         
37                                 if (arg == "--full-run" || arg == "-f")
38                                         config.full_run = true;
39                                 else
40                                         tests = new string [] { args [0] };
41                         }
42
43                         foreach (string test in tests) {
44                                 if (File.Exists (test))
45                                         RunTest (test);
46                                 else
47                                         Console.WriteLine ("WARNING: Test `{0}' doesn't exist.", test);
48                         }
49                 }
50
51                 static void LoadConfig ()
52                 {
53                         IDictionary env = Environment.GetEnvironmentVariables ();
54                         config = new Config ();
55                         config.is_win32 = Environment.OSVersion.ToString ().IndexOf ("Windows") != -1;
56                         
57                         config.preamble = env ["MJS_TEST_PREAMBLE"] as string;
58                         if (config.preamble == null)
59                                 config.preamble = "mjs.preamble";
60
61                         config.test_file = env ["MJS_TEST_FILE"] as string;
62                         if (config.test_file == null)
63                                 config.test_file = "mjs-most.tests";
64
65                         config.fail_file = env ["MJS_FAIL_FILE"] as string;
66                         if (config.fail_file == null)
67                                 config.fail_file = "mjs-most.fail";
68
69                         config.full_run = false;
70                         config.test_dir = env ["MJS_TEST_DIR"] as string;
71                         if (config.test_dir == null)
72                                 config.test_dir = config.is_win32 ? env ["TEMP"] as string : "."; 
73
74                         config.compile_cmd = env ["MJS_COMPILE_CMD"] as string;
75                         if (config.compile_cmd == null)
76                                 config.compile_cmd = config.is_win32 ? "bash" : "mjs";
77
78                         string compile_str = env ["MJS_COMPILE_ARGS"] as string;
79                         if (compile_str == null)
80                                 compile_str = config.is_win32 ? "mjs {0}" : "{0}";
81                         config.compile_args = String.Format (compile_str, "jstest.js");
82
83                         config.run_cmd = env ["MJS_RUN_CMD"] as string;
84                         if (config.run_cmd == null)
85                                 config.run_cmd = "mono";
86
87                         string run_str = env ["MJS_RUN_ARGS"] as string;
88                         if (run_str == null)
89                                 run_str = "{0}";
90                         config.run_args = String.Format (run_str, "jstest.exe");
91                 }
92
93                 static void ReadFiles ()
94                 {
95                         StreamReader preamble_io = File.OpenText (config.preamble);
96                         try {
97                                 preamble = preamble_io.ReadToEnd ();
98                         } finally {
99                                 preamble_io.Close ();
100                         }
101
102                         StreamReader fail_io = File.OpenText (config.fail_file);
103                         try {
104                                 fail_tests = FilterTextToLines (fail_io.ReadToEnd ());
105                         } finally {
106                                 fail_io.Close ();
107                         }
108
109                         StreamReader test_io = File.OpenText (config.test_file);
110                         try {
111                                 tests = FilterTextToLines (test_io.ReadToEnd ());
112                         } finally {
113                                 test_io.Close ();
114                         }
115                 }
116
117                 // Filters whitespace and comment lines
118                 static string [] FilterTextToLines (string text)
119                 {
120                         ArrayList result = new ArrayList ();
121                         foreach (string line in Regex.Split (text, @"\r?\n")) {
122                                 string t_line = line.Trim ();
123                                 if ((t_line != "") && (t_line [0] != '#'))
124                                         result.Add (t_line);
125                         }
126                         return (string []) result.ToArray (typeof (string));
127                 }
128
129                 class ReplaceEval {
130                         public StringBuilder eval_funs;
131                         int eval_fun_count = 0;
132
133                         public ReplaceEval ()
134                         {
135                                 eval_funs = new StringBuilder ();
136                         }
137
138                         public string replace_eval (Match match)
139                         {
140                                 string body = ProcessEscapes (match.Groups [2].Value);
141
142                                 // Try to insert a return statement before a semicolon or closing curly brace
143                                 string new_body = Regex.Replace (body, "^(.+)([;}])(.+)$", "$1$2 return $3", RegexOptions.Singleline);
144                                 // No return inserted yet? Prepend to whole body.
145                                 if (body == new_body)
146                                         new_body = "return " + body;
147
148                                 string fun_name = String.Format ("eval_fun_{0}", eval_fun_count++);
149                                 eval_funs.AppendFormat ("function {0}() {{ {1} }}{2}", fun_name, new_body, Environment.NewLine);
150
151                                 return fun_name + "()";
152                         }
153                 }
154
155                 static void RunTest (string test)
156                 {
157                         string target = Path.Combine (config.test_dir, "jstest.js");
158
159                         ReplaceEval repl_eval = new ReplaceEval ();
160                         MatchEvaluator replace_eval = new MatchEvaluator (repl_eval.replace_eval);
161
162                         StreamReader source_io = File.OpenText (test);
163                         string source;
164                         try {
165                                 source = source_io.ReadToEnd ();
166                         } finally {
167                                 source_io.Close ();
168                         }
169
170                         source = Regex.Replace (source, @"eval\s*\(([""'])(.+?[^\\]?)\1\s*\)", replace_eval).
171                                 Replace ("new TestCase", "TestCase");
172                         string eval_funs = repl_eval.eval_funs.ToString ();
173
174                         StreamWriter target_io = File.CreateText (target);
175                         try {
176                                 target_io.WriteLine (preamble);
177                                 target_io.WriteLine (eval_funs);
178                                 target_io.WriteLine (source);
179                         } finally {
180                                 target_io.Close ();
181                         }
182
183                         // Luckily, we don't have to deal with multiple threads here...
184                         string old_dir = Environment.CurrentDirectory;
185                         Environment.CurrentDirectory = config.test_dir;
186                         {
187                                 Console.WriteLine ();
188                                 Console.WriteLine ("Running {0}...", test);
189
190                                 ProcessStartInfo compiler_info = new ProcessStartInfo (config.compile_cmd, config.compile_args);
191                                 compiler_info.CreateNoWindow = true;
192                                 compiler_info.UseShellExecute = false;
193                                 compiler_info.RedirectStandardOutput = true;
194                                 compiler_info.RedirectStandardError = true;
195                                 Process compiler = Process.Start (compiler_info);
196                                 compiler.WaitForExit ();
197                                 Console.Write (compiler.StandardOutput.ReadToEnd ());
198                                 Console.Write (compiler.StandardError.ReadToEnd ());
199                                 if (compiler.ExitCode != 0 || !File.Exists ("jstest.exe")) {
200                                         Failed (test, String.Format ("compiler aborted with exit code {0}", compiler.ExitCode));
201                                         goto done;
202                                 }
203
204                                 ProcessStartInfo runtime_info = new ProcessStartInfo (config.run_cmd, config.run_args);
205                                 runtime_info.CreateNoWindow = true;
206                                 runtime_info.UseShellExecute = false;
207                                 runtime_info.RedirectStandardOutput = true;
208                                 runtime_info.RedirectStandardError = true;
209                                 Process runtime = Process.Start (runtime_info);
210                                 // For some reason we need the timeout here even if the runtime does not need longer than it...
211                                 runtime.WaitForExit (5000);
212
213                                 Console.Write (runtime.StandardOutput.ReadToEnd ());
214                                 Console.Write (runtime.StandardError.ReadToEnd ());
215                                 if (runtime.ExitCode != 0)
216                                         Failed (test, String.Format ("runtime aborted with exit code {0}", runtime.ExitCode));
217                                 File.Delete ("jstest.exe");
218                         }
219
220                         done:
221                         Environment.CurrentDirectory = old_dir;
222                 }
223
224                 static string ProcessEscapeMatch (Match match)
225                 {
226                         string escape = match.Groups [1].Value;
227
228                         switch (escape) {
229                         case "n":
230                                 return "\n";
231                         case "r":
232                                 return "\r";
233                         case "'":
234                                 return "'";
235                         case "\"":
236                                 return "\"";
237                         case "\\":
238                                 return "\\";
239                         default:
240                                 return "\\" + escape;
241                         }
242                 }
243
244                 static string ProcessEscapes (String text)
245                 {
246                         MatchEvaluator process = new MatchEvaluator (ProcessEscapeMatch);
247                         return Regex.Replace (text, @"\\(.)", process);
248                 }
249
250                 static void Failed (string test, string reason)
251                 {                       
252                         bool fail_expected = Array.IndexOf (fail_tests, test) != -1;
253                         Console.WriteLine ("Failed {0}: {1}!", test, reason);
254                         Console.WriteLine (); Console.WriteLine (); Console.WriteLine ();
255                         if (!fail_expected) {
256                                 Console.WriteLine ("CRITICAL: Unexpected failure of {0}. Aborting run.", test);
257                                 if (!config.full_run)
258                                         Environment.Exit (1);
259                         }
260                 }
261         }
262 }