Merge pull request #347 from JamesB7/master
[mono.git] / mcs / tools / compiler-tester / compiler-tester.cs
1 //
2 // compiler-tester.cs
3 //
4 // Author:
5 //   Marek Safar (marek.safar@gmail.com)
6 //
7 // Copyright (C) 2008, 2009 Novell, Inc (http://www.novell.com)
8 // Copyright (C) 2012 Xamarin Inc (http://www.xamarin.com)
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 // 
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 // 
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //
29
30 using System;
31 using System.IO;
32 using System.Diagnostics;
33 using System.Reflection;
34 using System.Text;
35 using System.Collections;
36 using System.Xml;
37 using System.Collections.Generic;
38 using Mono.CompilerServices.SymbolWriter;
39 using System.Globalization;
40
41 namespace TestRunner {
42
43         interface ITester
44         {
45                 string Output { get; }
46                 bool Invoke (string[] args);
47                 bool IsWarning (int warningNumber);
48         }
49
50         class ReflectionTester: ITester {
51                 MethodInfo ep;
52                 object[] method_arg;
53                 StringWriter output;
54                 int[] all_warnings;
55
56                 public ReflectionTester (Assembly a)
57                 {
58                         Type t = a.GetType ("Mono.CSharp.CompilerCallableEntryPoint");
59
60                         if (t == null)
61                                 Console.Error.WriteLine ("null, huh?");
62
63                         ep = t.GetMethod ("InvokeCompiler", 
64                                 BindingFlags.Static | BindingFlags.Public);
65                         if (ep == null)
66                                 throw new MissingMethodException ("static InvokeCompiler");
67                         method_arg = new object [2];
68
69                         PropertyInfo pi = t.GetProperty ("AllWarningNumbers");
70                         all_warnings = (int[])pi.GetValue (null, null);
71                         Array.Sort (all_warnings);
72                 }
73
74                 public string Output {
75                         get {
76                                 return output.GetStringBuilder ().ToString ();
77                         }
78                 }
79
80                 public bool Invoke(string[] args)
81                 {
82                         output = new StringWriter ();
83                         method_arg [0] = args;
84                         method_arg [1] = output;
85                         return (bool)ep.Invoke (null, method_arg);
86                 }
87
88                 public bool IsWarning (int warningNumber)
89                 {
90                         return Array.BinarySearch (all_warnings, warningNumber) >= 0;
91                 }
92         }
93
94 #if !NET_2_1
95         class ProcessTester: ITester
96         {
97                 ProcessStartInfo pi;
98                 string output;
99
100                 public ProcessTester (string p_path)
101                 {
102                         pi = new ProcessStartInfo ();
103                         pi.FileName = p_path;
104                         pi.CreateNoWindow = true;
105                         pi.WindowStyle = ProcessWindowStyle.Hidden;
106                         pi.RedirectStandardOutput = true;
107                         pi.RedirectStandardError = true;
108                         pi.UseShellExecute = false;
109                 }
110
111                 public string Output {
112                         get {
113                                 return output;
114                         }
115                 }
116
117                 public bool Invoke(string[] args)
118                 {
119                         StringBuilder sb = new StringBuilder ("/nologo ");
120                         foreach (string s in args) {
121                                 sb.Append (s);
122                                 sb.Append (" ");
123                         }
124                         pi.Arguments = sb.ToString ();
125                         Process p = Process.Start (pi);
126                         output = p.StandardError.ReadToEnd ();
127                         if (output.Length == 0)
128                             output = p.StandardOutput.ReadToEnd ();
129                         p.WaitForExit ();
130                         return p.ExitCode == 0;
131                 }
132
133                 public bool IsWarning (int warningNumber)
134                 {
135                         throw new NotImplementedException ();
136                 }
137         }
138 #endif
139
140         class TestCase : MarshalByRefObject
141         {
142                 public readonly string FileName;
143                 public readonly string[] CompilerOptions;
144                 public readonly string[] Dependencies;
145
146                 public TestCase (string filename, string[] options, string[] deps)
147                 {
148                         this.FileName = filename;
149                         this.CompilerOptions = options;
150                         this.Dependencies = deps;
151                 }
152         }
153
154         class PositiveTestCase : TestCase
155         {
156                 public class VerificationData : MarshalByRefObject
157                 {
158                         public class MethodData : MarshalByRefObject
159                         {
160                                 public MethodData (MethodBase mi, int il_size)
161                                 {
162                                         this.Type = mi.DeclaringType.ToString ();
163                                         this.MethodName = mi.ToString ();
164                                         this.MethodAttributes = (int) mi.Attributes;
165                                         this.ILSize = il_size;
166                                 }
167
168                                 public MethodData (string type_name, string method_name, int method_attributes, int il_size)
169                                 {
170                                         this.Type = type_name;
171                                         this.MethodName = method_name;
172                                         this.MethodAttributes = method_attributes;
173                                         this.ILSize = il_size;
174                                 }
175
176                                 public string Type;
177                                 public string MethodName;
178                                 public int ILSize;
179                                 public bool Checked;
180                                 public int MethodAttributes;
181                         }
182
183                         ArrayList methods;
184                         public bool IsNewSet;
185
186                         public VerificationData (string test_file)
187                         {
188                                 this.test_file = test_file;
189                         }
190
191                         string test_file;
192
193                         public static VerificationData FromFile (string name, XmlReader r)
194                         {
195                                 VerificationData tc = new VerificationData (name);
196                                 ArrayList methods = new ArrayList ();
197                                 r.Read ();
198                                 while (r.ReadToNextSibling ("type")) {
199                                         string type_name = r ["name"];
200                                         r.Read ();
201                                         while (r.ReadToNextSibling ("method")) {
202                                                 string m_name = r ["name"];
203                                                 int method_attrs = int.Parse (r["attrs"]);
204
205                                                 r.ReadToDescendant ("size");
206                                                 int il_size = r.ReadElementContentAsInt ();
207                                                 methods.Add (new MethodData (type_name, m_name, method_attrs, il_size));
208                                                 r.Read ();
209                                         }
210                                         r.Read ();
211                                 }
212
213                                 tc.methods = methods;
214                                 return tc;
215                         }
216
217                         public void WriteCodeInfoTo (XmlWriter w)
218                         {
219                                 w.WriteStartElement ("test");
220                                 w.WriteAttributeString ("name", test_file);
221
222                                 string type = null;
223                                 foreach (MethodData data in methods) {
224                                         if (!data.Checked)
225                                                 continue;
226
227                                         if (type != data.Type) {
228                                                 if (type != null)
229                                                         w.WriteEndElement ();
230
231                                                 type = data.Type;
232                                                 w.WriteStartElement ("type");
233                                                 w.WriteAttributeString ("name", type);
234                                         }
235
236                                         w.WriteStartElement ("method");
237                                         w.WriteAttributeString ("name", data.MethodName);
238                                         int v = data.MethodAttributes;
239                                         w.WriteAttributeString ("attrs", v.ToString ());
240                                         w.WriteStartElement ("size");
241                                         w.WriteValue (data.ILSize);
242                                         w.WriteEndElement ();
243                                         w.WriteEndElement ();
244                                 }
245
246                                 if (type != null)
247                                         w.WriteEndElement ();
248
249                                 w.WriteEndElement ();
250                         }
251
252                         public MethodData FindMethodData (string method_name, string declaring_type)
253                         {
254                                 if (methods == null)
255                                         return null;
256
257                                 foreach (MethodData md in methods) {
258                                         if (md.MethodName == method_name && md.Type == declaring_type)
259                                                 return md;
260                                 }
261
262                                 return null;
263                         }
264
265                         public void AddNewMethod (MethodBase mb, int il_size)
266                         {
267                                 if (methods == null)
268                                         methods = new ArrayList ();
269
270                                 MethodData md = new MethodData (mb, il_size);
271                                 md.Checked = true;
272                                 methods.Add (md);
273                         }
274                 }
275
276                 VerificationData verif_data;
277
278                 public PositiveTestCase (string filename, string [] options, string [] deps)
279                         : base (filename, options, deps)
280                 {
281                 }
282
283                 public void CreateNewTest ()
284                 {
285                         verif_data = new VerificationData (FileName);
286                         verif_data.IsNewSet = true;
287                 }
288
289                 public VerificationData VerificationProvider {
290                         set {
291                                 verif_data = value;
292                         }
293                         get {
294                                 return verif_data;
295                         }
296                 }
297         }
298
299         class Checker: MarshalByRefObject, IDisposable
300         {
301                 protected ITester tester;
302                 protected int success;
303                 protected int total;
304                 protected int ignored;
305                 protected int syntax_errors;
306                 string issue_file;
307                 StreamWriter log_file;
308                 protected string[] extra_compiler_options;
309                 // protected string[] compiler_options;
310                 // protected string[] dependencies;
311
312                 protected ArrayList tests = new ArrayList ();
313                 protected Hashtable test_hash = new Hashtable ();
314                 protected ArrayList regression = new ArrayList ();
315                 protected ArrayList know_issues = new ArrayList ();
316                 protected ArrayList ignore_list = new ArrayList ();
317                 protected ArrayList no_error_list = new ArrayList ();
318                 
319                 protected bool verbose;
320                 protected bool safe_execution;
321                         
322                 int total_known_issues;
323
324                 protected Checker (ITester tester)
325                 {
326                         this.tester = tester;
327                 }
328
329                 public string IssueFile {
330                         set {
331                                 this.issue_file = value;
332                                 ReadWrongErrors (issue_file);
333                         }
334                 }
335                 
336                 public string LogFile {
337                         set {
338                                 this.log_file = new StreamWriter (value, false);
339                         }
340                 }
341
342                 public bool Verbose {
343                         set {
344                                 verbose = value;
345                         }
346                 }
347
348                 public bool SafeExecution {
349                         set {
350                                 safe_execution = value;
351                         }
352                 }
353
354                 public string[] ExtraCompilerOptions {
355                         set {
356                                 extra_compiler_options = value;
357                         }
358                 }
359
360                 protected virtual bool GetExtraOptions (string file, out string[] compiler_options,
361                                                         out string[] dependencies)
362                 {
363                         int row = 0;
364                         compiler_options = null;
365                         dependencies = null;
366                         try {
367                                 using (StreamReader sr = new StreamReader (file)) {
368                                         String line;
369                                         while (row++ < 3 && (line = sr.ReadLine()) != null) {
370                                                 if (!AnalyzeTestFile (file, ref row, line, ref compiler_options,
371                                                                       ref dependencies))
372                                                         return false;
373                                         }
374                                 }
375                         } catch {
376                                 return false;
377                         }
378                         return true;
379                 }
380
381                 protected virtual bool AnalyzeTestFile (string file, ref int row, string line,
382                                                         ref string[] compiler_options,
383                                                         ref string[] dependencies)
384                 {
385                         const string options = "// Compiler options:";
386                         const string depends = "// Dependencies:";
387
388                         if (row == 1) {
389                                 compiler_options = null;
390                                 dependencies = null;
391                         }
392
393                         int index = line.IndexOf (options);
394                         if (index != -1) {
395                                 compiler_options = line.Substring (index + options.Length).Trim().Split (' ');
396                                 for (int i = 0; i < compiler_options.Length; i++)
397                                         compiler_options[i] = compiler_options[i].TrimStart ();
398                         }
399                         index = line.IndexOf (depends);
400                         if (index != -1) {
401                                 dependencies = line.Substring (index + depends.Length).Trim().Split (' ');
402                                 for (int i = 0; i < dependencies.Length; i++)
403                                         dependencies[i] = dependencies[i].TrimStart ();
404                         }
405
406                         return true;
407                 }
408
409                 public bool Do (string filename)
410                 {
411                         if (test_hash.Contains (filename))
412                                 return true;
413
414                         if (verbose)
415                                 Log (filename + "...\t");
416
417                         if (ignore_list.Contains (filename)) {
418                                 ++ignored;
419                                 LogFileLine (filename, "NOT TESTED");
420                                 return false;
421                         }
422
423                         string[] compiler_options, dependencies;
424                         if (!GetExtraOptions (filename, out compiler_options, out dependencies)) {
425                                 LogFileLine (filename, "ERROR");
426                                 return false;
427                         }
428
429                         if (extra_compiler_options != null) {
430                                 if (compiler_options == null)
431                                         compiler_options = extra_compiler_options;
432                                 else {
433                                         string[] new_options = new string [compiler_options.Length + extra_compiler_options.Length];
434                                         extra_compiler_options.CopyTo (new_options, 0);
435                                         compiler_options.CopyTo (new_options, extra_compiler_options.Length);
436                                         compiler_options = new_options;
437                                 }
438                         }
439
440                         TestCase test = CreateTestCase (filename, compiler_options, dependencies);
441                         test_hash.Add (filename, test);
442
443                         ++total;
444                         if (dependencies != null) {
445                                 foreach (string dependency in dependencies) {
446                                         if (!Do (dependency)) {
447                                                 LogFileLine (filename, "DEPENDENCY FAILED");
448                                                 return false;
449                                         }
450                                 }
451                         }
452
453                         tests.Add (test);
454
455                         return Check (test);
456                 }
457
458                 protected virtual bool Check (TestCase test)
459                 {
460                         string[] test_args;
461
462                         if (test.CompilerOptions != null) {
463                                 test_args = new string [2 + test.CompilerOptions.Length];
464                                 test.CompilerOptions.CopyTo (test_args, 0);
465                         } else {
466                                 test_args = new string [2];
467                         }
468                         test_args [test_args.Length - 2] = test.FileName;
469                         test_args [test_args.Length - 1] = "-debug";
470
471                         return tester.Invoke (test_args);
472                 }
473
474                 protected virtual TestCase CreateTestCase (string filename, string [] options, string [] deps)
475                 {
476                         return new TestCase (filename, options, deps);
477                 }
478
479                 void ReadWrongErrors (string file)
480                 {
481                         const string ignored = "IGNORE";
482                         const string no_error = "NO ERROR";
483
484                         using (StreamReader sr = new StreamReader (file)) {
485                                 string line;
486                                 while ((line = sr.ReadLine()) != null) {
487                                         if (line.StartsWith ("#"))
488                                                 continue;
489
490                                         ArrayList active_cont = know_issues;
491
492                                         if (line.IndexOf (ignored) > 0)
493                                                 active_cont = ignore_list;
494                                         else if (line.IndexOf (no_error) > 0)
495                                                 active_cont = no_error_list;
496
497                                         string file_name = line.Split (' ')[0];
498                                         if (file_name.Length == 0)
499                                                 continue;
500
501                                         active_cont.Add (file_name);
502                                 }
503                         }
504                         total_known_issues = know_issues.Count;
505                 }
506
507                 protected virtual void PrintSummary ()
508                 {
509                         LogLine ("Done" + Environment.NewLine);
510                         float rate = 0;
511                         if (total > 0)
512                                 rate = (float) (success) / (float)total;
513                         LogLine ("{0} test cases passed ({1:0.##%})", success, rate);
514
515                         if (syntax_errors > 0)
516                                 LogLine ("{0} test(s) ignored because of wrong syntax !", syntax_errors);
517                                 
518                         if (ignored > 0)
519                                 LogLine ("{0} test(s) ignored", ignored);
520                         
521                         if (total_known_issues - know_issues.Count > 0)
522                                 LogLine ("{0} known issue(s)", total_known_issues - know_issues.Count);
523
524                         know_issues.AddRange (no_error_list);
525                         if (know_issues.Count > 0) {
526                                 LogLine ("");
527                                 LogLine (issue_file + " contains {0} already fixed issues. Please remove", know_issues.Count);
528                                 foreach (string s in know_issues)
529                                         LogLine (s);
530                         }
531                         if (regression.Count > 0) {
532                                 LogLine ("");
533                                 LogLine ("The latest changes caused regression in {0} file(s)", regression.Count);
534                                 foreach (string s in regression)
535                                         LogLine (s);
536                         }
537                 }
538
539                 public int ResultCode
540                 {
541                         get {
542                                 return regression.Count == 0 ? 0 : 1;
543                         }
544                 }
545
546                 protected void Log (string msg, params object [] rest)
547                 {
548                         Console.Write (msg, rest);
549                         if (log_file != null)
550                                 log_file.Write (msg, rest);
551                 }
552
553                 protected void LogLine (string msg)
554                 {
555                         Console.WriteLine (msg);
556                         if (log_file != null)
557                                 log_file.WriteLine (msg);
558                 }
559
560                 protected void LogLine (string msg, params object [] rest)
561                 {
562                         Console.WriteLine (msg, rest);
563                         if (log_file != null)
564                                 log_file.WriteLine (msg, rest);
565                 }
566                 
567                 public void LogFileLine (string file, string msg, params object [] args)
568                 {
569                         string s = verbose ? 
570                                 string.Format (msg, args) :
571                                 file + "...\t" + string.Format (msg, args); 
572
573                         Console.WriteLine (s);
574                         if (log_file != null)
575                                 log_file.WriteLine (s);
576                 }
577
578                 #region IDisposable Members
579
580                 public void Dispose()
581                 {
582                         if (log_file != null)
583                                 log_file.Close ();
584                 }
585
586                 #endregion
587
588                 public virtual void Initialize ()
589                 {
590                 }
591
592                 public virtual void CleanUp ()
593                 {
594                         PrintSummary ();
595                 }
596         }
597
598         class PositiveChecker: Checker
599         {
600                 readonly string files_folder;
601                 readonly static object[] default_args = new object[1] { new string[] {} };
602                 string doc_output;
603                 string verif_file;
604                 bool update_verif_file;
605                 Hashtable verif_data;
606
607 #if !NET_2_1
608                 ProcessStartInfo pi;
609 #endif
610                 readonly string mono;
611
612                 public enum TestResult {
613                         CompileError,
614                         ExecError,
615                         LoadError,
616                         XmlError,
617                         Success,
618                         ILError,
619                         DebugError,
620                         MethodAttributesError
621                 }
622
623                 public PositiveChecker (ITester tester, string verif_file):
624                         base (tester)
625                 {
626                         files_folder = Directory.GetCurrentDirectory ();
627                         this.verif_file = verif_file;
628
629 #if !NET_2_1
630                         pi = new ProcessStartInfo ();
631                         pi.CreateNoWindow = true;
632                         pi.WindowStyle = ProcessWindowStyle.Hidden;
633                         pi.RedirectStandardOutput = true;
634                         pi.RedirectStandardError = true;
635                         pi.UseShellExecute = false;
636
637                         mono = Environment.GetEnvironmentVariable ("MONO_RUNTIME");
638                         if (mono != null) {
639                                 pi.FileName = mono;
640                         }
641 #endif
642                 }
643
644                 public bool UpdateVerificationDataFile {
645                         set {
646                                 update_verif_file = value;
647                         }
648                         get {
649                                 return update_verif_file;
650                         }
651                 }
652
653                 protected override bool GetExtraOptions(string file, out string[] compiler_options,
654                                                         out string[] dependencies) {
655                         if (!base.GetExtraOptions (file, out compiler_options, out dependencies))
656                                 return false;
657
658                         doc_output = null;
659                         if (compiler_options == null)
660                                 return true;
661
662                         foreach (string one_opt in compiler_options) {
663                                 if (one_opt.StartsWith ("-doc:")) {
664                                         doc_output = one_opt.Split (':', '/')[1];
665                                 }
666                         }
667                         return true;
668                 }
669
670                 class DomainTester : MarshalByRefObject
671                 {
672                         public bool CheckILSize (PositiveTestCase test, PositiveChecker checker, string file)
673                         {
674                                 Assembly assembly = Assembly.LoadFile (file);
675
676                                 bool success = true;
677                                 Type[] types = assembly.GetTypes ();
678                                 foreach (Type t in types) {
679
680                                         // Skip interfaces
681                                         if (!t.IsClass && !t.IsValueType)
682                                                 continue;
683
684                                         if (test.VerificationProvider == null) {
685                                                 if (!checker.UpdateVerificationDataFile)
686                                                         checker.LogFileLine (test.FileName, "Missing IL verification data");
687                                                 test.CreateNewTest ();
688                                         }
689
690                                         foreach (MemberInfo m in t.GetMembers (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly)) {
691                                                 MethodBase mi = m as MethodBase;
692                                                 if (mi == null)
693                                                         continue;
694
695                                                 if ((mi.Attributes & (MethodAttributes.PinvokeImpl)) != 0)
696                                                         continue;
697
698                                                 success &= CompareIL (mi, test, checker);
699                                         }
700                                 }
701
702                                 return success;
703                         }
704
705                         bool CompareIL (MethodBase mi, PositiveTestCase test, PositiveChecker checker)
706                         {
707                                 string m_name = mi.ToString ();
708                                 string decl_type = mi.DeclaringType.ToString ();
709                                 PositiveTestCase.VerificationData data_provider = test.VerificationProvider;
710
711                                 PositiveTestCase.VerificationData.MethodData md = data_provider.FindMethodData (m_name, decl_type);
712                                 if (md == null) {
713                                         data_provider.AddNewMethod (mi, GetILSize (mi));
714                                         if (!data_provider.IsNewSet) {
715                                                 checker.HandleFailure (test.FileName, PositiveChecker.TestResult.ILError, decl_type + ": " + m_name + " (new method?)");
716                                                 return false;
717                                         }
718
719                                         return true;
720                                 }
721
722                                 if (md.Checked) {
723                                         checker.HandleFailure (test.FileName, PositiveChecker.TestResult.ILError, decl_type + ": " + m_name + " has a duplicate");
724                                         return false;
725                                 }
726
727                                 md.Checked = true;
728
729                                 if (md.MethodAttributes != (int) mi.Attributes) {
730                                         checker.HandleFailure (test.FileName, PositiveChecker.TestResult.MethodAttributesError,
731                                                 string.Format ("{0} ({1} -> {2})", decl_type + ": " + m_name, md.MethodAttributes, mi.Attributes));
732                                 }
733
734                                 md.MethodAttributes = (int) mi.Attributes;
735
736                                 int il_size = GetILSize (mi);
737                                 if (md.ILSize == il_size)
738                                         return true;
739
740                                 if (md.ILSize > il_size) {
741                                         checker.LogFileLine (test.FileName, "{0} (code size reduction {1} -> {2})", decl_type + ": " + m_name, md.ILSize, il_size);
742                                         md.ILSize = il_size;
743                                         return true;
744                                 }
745
746                                 checker.HandleFailure (test.FileName, PositiveChecker.TestResult.ILError,
747                                         string.Format ("{0} (code size {1} -> {2})", decl_type + ": " + m_name, md.ILSize, il_size));
748
749                                 md.ILSize = il_size;
750
751                                 return false;
752                         }
753
754                         static int GetILSize (MethodBase mi)
755                         {
756                                 MethodBody body = mi.GetMethodBody ();
757                                 if (body != null)
758                                         return body.GetILAsByteArray ().Length;
759
760                                 return 0;
761                         }
762
763                         bool ExecuteFile (MethodInfo entry_point, string filename)
764                         {
765                                 TextWriter stdout = Console.Out;
766                                 TextWriter stderr = Console.Error;
767                                 Console.SetOut (TextWriter.Null);
768                                 Console.SetError (TextWriter.Null);
769                                 ParameterInfo[] pi = entry_point.GetParameters ();
770                                 object[] args = pi.Length == 0 ? null : default_args;
771
772                                 object result = null;
773                                 try {
774                                         try {
775                                                 result = entry_point.Invoke (null, args);
776                                         } finally {
777                                                 Console.SetOut (stdout);
778                                                 Console.SetError (stderr);
779                                         }
780                                 } catch (Exception e) {
781                                         throw new ApplicationException (e.ToString ());
782                                 }
783
784                                 if (result is int && (int) result != 0)
785                                         throw new ApplicationException ("Wrong return code: " + result.ToString ());
786
787                                 return true;
788                         }
789
790                         public bool Test (string file)
791                         {
792                                 Assembly assembly = Assembly.LoadFile (file);
793                                 return ExecuteFile (assembly.EntryPoint, file);
794                         }
795                 }
796
797                 protected override bool Check(TestCase test)
798                 {
799                         string filename = test.FileName;
800                         try {
801                                 if (!base.Check (test)) {
802                                         HandleFailure (filename, TestResult.CompileError, tester.Output);
803                                         return false;
804                                 }
805                         }
806                         catch (Exception e) {
807                                 if (e.InnerException != null)
808                                         e = e.InnerException;
809                                 
810                                 HandleFailure (filename, TestResult.CompileError, e.ToString ());
811                                 return false;
812                         }
813
814                         // Test setup
815                         if (filename.EndsWith ("-lib.cs") || filename.EndsWith ("-mod.cs")) {
816                                 if (verbose)
817                                         LogFileLine (filename, "OK");
818                                 --total;
819                                 return true;
820                         }
821
822                         string file = Path.Combine (files_folder, Path.GetFileNameWithoutExtension (filename) + ".exe");
823
824                         // Enable .dll only tests (no execution required)
825                         if (!File.Exists(file)) {
826                                 HandleFailure (filename, TestResult.Success, null);
827                                 return true;
828                         }
829
830                         AppDomain domain = null;
831 #if !NET_2_1
832                         if (safe_execution) {
833                                 // Create a new AppDomain, with the current directory as the base.
834                                 AppDomainSetup setupInfo = new AppDomainSetup ();
835                                 setupInfo.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
836                                 setupInfo.LoaderOptimization = LoaderOptimization.SingleDomain;
837                                 domain = AppDomain.CreateDomain (Path.GetFileNameWithoutExtension (file), null, setupInfo);
838                         }
839 #endif
840                         try {
841                                 DomainTester tester;
842                                 try {
843 #if !NET_2_1
844                                         if (domain != null)
845                                                 tester = (DomainTester) domain.CreateInstanceAndUnwrap (typeof (PositiveChecker).Assembly.FullName, typeof (DomainTester).FullName);
846                                         else
847 #endif
848                                                 tester = new DomainTester ();
849
850                                         if (!tester.Test (file))
851                                                 return false;
852
853                                 } catch (ApplicationException e) {
854                                         HandleFailure (filename, TestResult.ExecError, e.Message);
855                                         return false;
856                                 } catch (Exception e) {
857                                         HandleFailure (filename, TestResult.LoadError, e.ToString ());
858                                         return false;
859                                 }
860
861                                 if (doc_output != null) {
862                                         string ref_file = filename.Replace (".cs", "-ref.xml");
863                                         try {
864 #if !NET_2_1
865                                                 new XmlComparer ("doc").Compare (ref_file, doc_output);
866 #endif
867                                         } catch (Exception e) {
868                                                 HandleFailure (filename, TestResult.XmlError, e.Message);
869                                                 return false;
870                                         }
871                                 } else {
872                                         if (verif_file != null) {
873                                                 PositiveTestCase pt = (PositiveTestCase) test;
874                                                 pt.VerificationProvider = (PositiveTestCase.VerificationData) verif_data[filename];
875
876                                                 if (!tester.CheckILSize (pt, this, file))
877                                                         return false;
878                                         }
879
880                                         if (filename.StartsWith ("test-debug", StringComparison.OrdinalIgnoreCase)) {
881                                                 var mdb_file_name = file + ".mdb";
882                                                 MonoSymbolFile mdb_file = MonoSymbolFile.ReadSymbolFile (mdb_file_name);
883                                                 var mdb_xml_file = mdb_file_name + ".xml";
884                                                 ConvertSymbolFileToXml (mdb_file, mdb_xml_file);
885
886                                                 var ref_file = filename.Replace(".cs", "-ref.xml");
887                                                 try {
888                                                         new XmlComparer ("symbols").Compare (ref_file, mdb_xml_file);
889                                                 } catch (Exception e) {
890                                                         HandleFailure (filename, TestResult.DebugError, e.Message);
891                                                         return false;
892                                                 }
893                                         }
894
895                                 }
896                         } finally {
897                                 if (domain != null)
898                                         AppDomain.Unload (domain);
899                         }
900
901                         HandleFailure (filename, TestResult.Success, null);
902                         return true;
903                 }
904
905                 static void ConvertSymbolFileToXml (MonoSymbolFile symbolFile, string xmlFile)
906                 {
907                         using (XmlTextWriter writer = new XmlTextWriter (xmlFile, Encoding.UTF8)) {
908                                 writer.Formatting = Formatting.Indented;
909
910                                 writer.WriteStartDocument ();
911
912                                 writer.WriteStartElement ("symbols");
913
914                                 writer.WriteStartElement ("files");
915                                 foreach (var file in symbolFile.Sources) {
916                                         writer.WriteStartElement ("file");
917                                         writer.WriteAttributeString ("id", file.Index.ToString ());
918                                         writer.WriteAttributeString ("name", Path.GetFileName (file.FileName));
919                                         var checksum = file.Checksum;
920                                         if (checksum != null)
921                                                 writer.WriteAttributeString ("checksum", ChecksumToString (checksum));
922
923                                         writer.WriteEndElement ();
924                                 }
925                                 writer.WriteEndElement ();
926
927                                 writer.WriteStartElement ("methods");
928                                 foreach (var method in symbolFile.Methods) {
929                                         writer.WriteStartElement ("method");
930                                         writer.WriteAttributeString ("token", IntToHex (method.Token));
931
932                                         var il_entries = method.GetLineNumberTable ();
933                                         writer.WriteStartElement ("sequencepoints");
934                                         foreach (var entry in il_entries.LineNumbers) {
935                                                 writer.WriteStartElement ("entry");
936                                                 writer.WriteAttributeString ("il", IntToHex (entry.Offset));
937                                                 writer.WriteAttributeString ("row", entry.Row.ToString ());
938                                                 writer.WriteAttributeString ("col", entry.Column.ToString ());
939                                                 writer.WriteAttributeString ("file_ref", entry.File.ToString ());
940                                                 writer.WriteAttributeString ("hidden", BoolToString (entry.IsHidden));
941                                                 writer.WriteEndElement ();
942                                         }
943                                         writer.WriteEndElement ();
944
945                                         writer.WriteStartElement ("locals");
946                                         foreach (var local in method.GetLocals ()) {
947                                                 writer.WriteStartElement ("entry");
948                                                 writer.WriteAttributeString ("name", local.Name);
949                                                 writer.WriteAttributeString ("il_index", local.Index.ToString ());
950                                                 writer.WriteAttributeString ("scope_ref", local.BlockIndex.ToString ());
951                                                 writer.WriteEndElement ();
952                                         }
953                                         writer.WriteEndElement ();
954
955                                         writer.WriteStartElement ("scopes");
956                                         foreach (var scope in method.GetCodeBlocks ()) {
957                                                 writer.WriteStartElement ("entry");
958                                                 writer.WriteAttributeString ("index", scope.Index.ToString ());
959                                                 writer.WriteAttributeString ("start", IntToHex (scope.StartOffset));
960                                                 writer.WriteAttributeString ("end", IntToHex (scope.EndOffset));
961                                                 writer.WriteEndElement ();
962                                         }
963                                         writer.WriteEndElement ();
964
965                                         writer.WriteEndElement ();
966                                 }
967                                 writer.WriteEndElement ();
968
969                                 writer.WriteEndElement ();
970                                 writer.WriteEndDocument ();
971                         }
972                 }
973
974                 static string ChecksumToString (byte[] checksum)
975                 {
976                         var sb = new StringBuilder (checksum.Length * 2);
977                         for (int i = 0; i < checksum.Length; i++) {
978                                 sb.Append ("0123456789abcdef"[checksum[i] >> 4]);
979                                 sb.Append ("0123456789abcdef"[checksum[i] & 0x0F]);
980                         }
981
982                         return sb.ToString ();
983                 }
984
985                 static string IntToHex (int value)
986                 {
987                         return "0x" + value.ToString ("x", CultureInfo.InvariantCulture);
988                 }
989
990                 static string BoolToString (bool value)
991                 {
992                         return value ? "true" : "false";
993                 }
994
995                 protected override TestCase CreateTestCase (string filename, string [] options, string [] deps)
996                 {
997                         return new PositiveTestCase (filename, options, deps);
998                 }
999
1000                 public void HandleFailure (string file, TestResult status, string extra)
1001                 {
1002                         switch (status) {
1003                                 case TestResult.Success:
1004                                         success++;
1005                                         if (know_issues.Contains (file)) {
1006                                                 LogFileLine (file, "FIXED ISSUE");
1007                                                 return;
1008                                         }
1009                                         if (verbose)
1010                                                 LogFileLine (file, "OK");
1011                                         return;
1012
1013                                 case TestResult.CompileError:
1014                                         if (know_issues.Contains (file)) {
1015                                                 LogFileLine (file, "KNOWN ISSUE (Compilation error)");
1016                                                 know_issues.Remove (file);
1017                                                 return;
1018                                         }
1019                                         LogFileLine (file, "REGRESSION (SUCCESS -> COMPILATION ERROR)");
1020                                         break;
1021
1022                                 case TestResult.ExecError:
1023                                         if (know_issues.Contains (file)) {
1024                                                 LogFileLine (file, "KNOWN ISSUE (Execution error)");
1025                                                 know_issues.Remove (file);
1026                                                 return;
1027                                         }
1028                                         LogFileLine (file, "REGRESSION (SUCCESS -> EXECUTION ERROR)");
1029                                         break;
1030
1031                                 case TestResult.XmlError:
1032                                         if (know_issues.Contains (file)) {
1033                                                 LogFileLine (file, "KNOWN ISSUE (Xml comparision error)");
1034                                                 know_issues.Remove (file);
1035                                                 return;
1036                                         }
1037                                         LogFileLine (file, "REGRESSION (SUCCESS -> DOCUMENTATION ERROR)");
1038                                         break;
1039
1040                                 case TestResult.LoadError:
1041                                         LogFileLine (file, "REGRESSION (SUCCESS -> LOAD ERROR)");
1042                                         break;
1043
1044                                 case TestResult.MethodAttributesError:
1045                                 case TestResult.ILError:
1046                                         if (!update_verif_file) {
1047                                                 LogFileLine (file, "IL REGRESSION: " + extra);
1048                                         }
1049                                         extra = null;
1050                                         break;
1051
1052                                 case TestResult.DebugError:
1053                                         LogFileLine (file, "REGRESSION (SUCCESS -> SYMBOL FILE ERROR)");
1054                                         break;
1055                         }
1056
1057                         if (extra != null)
1058                                 LogLine ("{0}", extra);
1059
1060                         if (!regression.Contains (file))
1061                                 regression.Add (file);
1062                 }
1063
1064                 public override void Initialize ()
1065                 {
1066                         if (verif_file != null) {
1067                                 LoadVerificationData (verif_file);
1068                         }
1069
1070                         base.Initialize ();
1071                 }
1072
1073                 public override void CleanUp ()
1074                 {
1075                         base.CleanUp ();
1076
1077                         if (update_verif_file) {
1078                                 UpdateVerificationData (verif_file);
1079                         }
1080                 }
1081
1082                 void LoadVerificationData (string file)
1083                 {
1084                         verif_data = new Hashtable ();
1085
1086                         if (!File.Exists (file)) {
1087                                 LogLine ("Writing verification data to `{0}' ...", file);
1088                                 return;
1089                         }
1090
1091                         LogLine ("Loading verification data from `{0}' ...", file);
1092
1093                         using (XmlReader r = XmlReader.Create (file)) {
1094                                 r.ReadStartElement ("tests");
1095
1096                                 while (r.Read ()) {
1097                                         if (r.Name != "test")
1098                                                 continue;
1099
1100                                         string name = r.GetAttribute ("name");
1101                                         PositiveTestCase.VerificationData tc = PositiveTestCase.VerificationData.FromFile (name, r);
1102                                         verif_data.Add (name, tc);
1103                                 }
1104                         }
1105                 }
1106
1107                 void UpdateVerificationData (string file)
1108                 {
1109                         LogLine ("Updating verification data `{0}' ...", file);
1110
1111                         XmlWriterSettings s = new XmlWriterSettings ();
1112                         s.Indent = true;
1113                         using (XmlWriter w = XmlWriter.Create (new StreamWriter (file, false, Encoding.UTF8), s)) {
1114                                 w.WriteStartDocument ();
1115                                 w.WriteComment ("This file contains expected IL and metadata produced by compiler for each test");
1116                                 w.WriteStartElement ("tests");
1117                                 foreach (PositiveTestCase tc in tests) {
1118                                         if (tc.VerificationProvider != null)
1119                                                 tc.VerificationProvider.WriteCodeInfoTo (w);
1120                                 }
1121                                 w.WriteEndElement ();
1122                         }
1123                 }
1124         }
1125
1126         class NegativeChecker: Checker
1127         {
1128                 string expected_message;
1129                 string error_message;
1130                 bool check_msg;
1131                 bool check_error_line;
1132                 bool is_warning;
1133                 IDictionary wrong_warning;
1134
1135                 protected enum CompilerError {
1136                         Expected,
1137                         Wrong,
1138                         Missing,
1139                         WrongMessage,
1140                         MissingLocation,
1141                         Duplicate
1142                 }
1143
1144                 public NegativeChecker (ITester tester, bool check_msg):
1145                         base (tester)
1146                 {
1147                         this.check_msg = check_msg;
1148                         wrong_warning = new Hashtable ();
1149                 }
1150
1151                 protected override bool AnalyzeTestFile (string file, ref int row, string line,
1152                                                         ref string[] compiler_options,
1153                                                         ref string[] dependencies)
1154                 {
1155                         if (row == 1) {
1156                                 expected_message = null;
1157
1158                                 int index = line.IndexOf (':');
1159                                 if (index == -1 || index > 15) {
1160                                         LogFileLine (file, "IGNORING: Wrong test file syntax (missing error mesage text)");
1161                                         ++syntax_errors;
1162                                         base.AnalyzeTestFile (file, ref row, line, ref compiler_options,
1163                                                               ref dependencies);
1164                                         return false;
1165                                 }
1166
1167                                 expected_message = line.Substring (index + 1).Trim ();
1168                         }
1169
1170                         if (row == 2) {
1171                                 string filtered = line.Replace(" ", "");
1172
1173                                 // Some error tests require to have different error text for different runtimes.
1174                                 if (filtered.StartsWith ("//GMCS")) {
1175                                         row = 1;
1176                                         return AnalyzeTestFile(file, ref row, line, ref compiler_options, ref dependencies);
1177                                 }
1178
1179                                 check_error_line = !filtered.StartsWith ("//Line:0");
1180
1181                                 if (!filtered.StartsWith ("//Line:")) {
1182                                         LogFileLine (file, "IGNORING: Wrong test syntax (following line after an error messsage must have `// Line: xx' syntax");
1183                                         ++syntax_errors;
1184                                         return false;
1185                                 }
1186                         }
1187
1188                         if (!base.AnalyzeTestFile (file, ref row, line, ref compiler_options, ref dependencies))
1189                                 return false;
1190
1191                         is_warning = false;
1192                         if (compiler_options != null) {
1193                                 foreach (string s in compiler_options) {
1194                                         if (s.StartsWith ("-warnaserror") || s.StartsWith ("/warnaserror"))
1195                                                 is_warning = true;
1196                                 }
1197                         }
1198                         return true;
1199                 }
1200
1201
1202                 protected override bool Check (TestCase test)
1203                 {
1204                         string filename = test.FileName;
1205
1206                         int start_char = 0;
1207                         while (Char.IsLetter (filename, start_char))
1208                                 ++start_char;
1209
1210                         int end_char = filename.IndexOfAny (new char [] { '-', '.' } );
1211                         string expected = filename.Substring (start_char, end_char - start_char);
1212
1213                         try {
1214                                 if (base.Check (test)) {
1215                                         HandleFailure (filename, CompilerError.Missing);
1216                                         return false;
1217                                 }
1218                         }
1219                         catch (Exception e) {
1220                                 HandleFailure (filename, CompilerError.Missing);
1221                                 if (e.InnerException != null)
1222                                         e = e.InnerException;
1223                                 
1224                                 Log (e.ToString ());
1225                                 return false;
1226                         }
1227
1228                         int err_id = int.Parse (expected, System.Globalization.CultureInfo.InvariantCulture);
1229                         if (tester.IsWarning (err_id)) {
1230                                 if (!is_warning)
1231                                         wrong_warning [err_id] = true;
1232                         } else {
1233                                 if (is_warning)
1234                                         wrong_warning [err_id] = false;
1235                         }
1236
1237                         CompilerError result_code = GetCompilerError (expected, tester.Output);
1238                         if (HandleFailure (filename, result_code)) {
1239                                 success++;
1240                                 return true;
1241                         }
1242
1243                         if (result_code == CompilerError.Wrong)
1244                                 LogLine (tester.Output);
1245
1246                         return false;
1247                 }
1248
1249                 CompilerError GetCompilerError (string expected, string buffer)
1250                 {
1251                         const string error_prefix = "CS";
1252                         const string ignored_error = "error CS5001";
1253                         string tested_text = "error " + error_prefix + expected;
1254                         StringReader sr = new StringReader (buffer);
1255                         string line = sr.ReadLine ();
1256                         ArrayList ld = new ArrayList ();
1257                         CompilerError result = CompilerError.Missing;
1258                         while (line != null) {
1259                                 if (ld.Contains (line) && result == CompilerError.Expected) {
1260                                         if (line.IndexOf ("Location of the symbol related to previous") == -1)
1261                                                 return CompilerError.Duplicate;
1262                                 }
1263                                 ld.Add (line);
1264
1265                                 if (result != CompilerError.Expected) {
1266                                         if (line.IndexOf (tested_text) != -1) {
1267                                                 if (check_msg) {
1268                                                         int first = line.IndexOf (':');
1269                                                         int second = line.IndexOf (':', first + 1);
1270                                                         if (line.IndexOf ("Warning as Error: ", first, StringComparison.Ordinal) > 0) {
1271                                                                 if (check_error_line) {
1272                                                                         second = line.IndexOf (':', second + 1);
1273                                                                 }
1274                                                         } else if (second == -1 || !check_error_line) {
1275                                                                 second = first;
1276                                                         }
1277
1278                                                         string msg = line.Substring (second + 1).TrimEnd ('.').Trim ();
1279                                                         if (msg != expected_message && msg != expected_message.Replace ('`', '\'')) {
1280                                                                 error_message = msg;
1281                                                                 return CompilerError.WrongMessage;
1282                                                         }
1283
1284                                                         if (check_error_line && line.IndexOf (".cs(") == -1)
1285                                                                 return CompilerError.MissingLocation;
1286                                                 }
1287                                                 result = CompilerError.Expected;
1288                                         } else if (line.IndexOf (error_prefix) != -1 &&
1289                                                 line.IndexOf (ignored_error) == -1)
1290                                                 result = CompilerError.Wrong;
1291                                 }
1292
1293                                 line = sr.ReadLine ();
1294                         }
1295                         
1296                         return result;
1297                 }
1298
1299                 bool HandleFailure (string file, CompilerError status)
1300                 {
1301                         switch (status) {
1302                                 case CompilerError.Expected:
1303                                         if (know_issues.Contains (file) || no_error_list.Contains (file)) {
1304                                                 LogFileLine (file, "FIXED ISSUE");
1305                                                 return true;
1306                                         }
1307                                 
1308                                         if (verbose)
1309                                                 LogFileLine (file, "OK");
1310                                         return true;
1311
1312                                 case CompilerError.Wrong:
1313                                         if (know_issues.Contains (file)) {
1314                                                 LogFileLine (file, "KNOWN ISSUE (Wrong error reported)");
1315                                                 know_issues.Remove (file);
1316                                                 return false;
1317                                         }
1318                                         if (no_error_list.Contains (file)) {
1319                                                 LogFileLine (file, "REGRESSION (NO ERROR -> WRONG ERROR CODE)");
1320                                                 no_error_list.Remove (file);
1321                                         }
1322                                         else {
1323                                                 LogFileLine (file, "REGRESSION (CORRECT ERROR -> WRONG ERROR CODE)");
1324                                         }
1325                                         break;
1326
1327                                 case CompilerError.WrongMessage:
1328                                         if (know_issues.Contains (file)) {
1329                                                 LogFileLine (file, "KNOWN ISSUE (Wrong error message reported)");
1330                                                 know_issues.Remove (file);
1331                                                 return false;
1332                                         }
1333                                         if (no_error_list.Contains (file)) {
1334                                                 LogFileLine (file, "REGRESSION (NO ERROR -> WRONG ERROR MESSAGE)");
1335                                                 no_error_list.Remove (file);
1336                                         }
1337                                         else {
1338                                                 LogFileLine (file, "REGRESSION (CORRECT ERROR -> WRONG ERROR MESSAGE)");
1339                                                 LogLine ("Exp: {0}", expected_message);
1340                                                 LogLine ("Was: {0}", error_message);
1341                                         }
1342                                         break;
1343
1344                                 case CompilerError.Missing:
1345                                         if (no_error_list.Contains (file)) {
1346                                                 LogFileLine (file, "KNOWN ISSUE (No error reported)");
1347                                                 no_error_list.Remove (file);
1348                                                 return false;
1349                                         }
1350
1351                                         if (know_issues.Contains (file)) {
1352                                                 LogFileLine (file, "REGRESSION (WRONG ERROR -> NO ERROR)");
1353                                                 know_issues.Remove (file);
1354                                         }
1355                                         else {
1356                                                 LogFileLine (file, "REGRESSION (CORRECT ERROR -> NO ERROR)");
1357                                         }
1358
1359                                         break;
1360
1361                                 case CompilerError.MissingLocation:
1362                                         if (know_issues.Contains (file)) {
1363                                                 LogFileLine (file, "KNOWN ISSUE (Missing error location)");
1364                                                 know_issues.Remove (file);
1365                                                 return false;
1366                                         }
1367                                         if (no_error_list.Contains (file)) {
1368                                                 LogFileLine (file, "REGRESSION (NO ERROR -> MISSING ERROR LOCATION)");
1369                                                 no_error_list.Remove (file);
1370                                         }
1371                                         else {
1372                                                 LogFileLine (file, "REGRESSION (CORRECT ERROR -> MISSING ERROR LOCATION)");
1373                                         }
1374                                         break;
1375
1376                                 case CompilerError.Duplicate:
1377                                         // Will become an error soon
1378                                         LogFileLine (file, "WARNING: EXACTLY SAME ERROR HAS BEEN ISSUED MULTIPLE TIMES");
1379                                         return true;
1380                         }
1381
1382                         regression.Add (file);
1383                         return false;
1384                 }
1385
1386                 protected override void PrintSummary()
1387                 {
1388                         base.PrintSummary ();
1389
1390                         if (wrong_warning.Count > 0) {
1391                                 LogLine ("");
1392                                 LogLine ("List of incorectly defined warnings (they should be either defined in the compiler as a warning or a test-case has redundant `warnaserror' option)");
1393                                 LogLine ("");
1394                                 foreach (DictionaryEntry de in wrong_warning)
1395                                         LogLine ("CS{0:0000} : {1}", de.Key, (bool)de.Value ? "incorrect warning definition" : "missing warning definition");
1396                         }
1397                 }
1398
1399         }
1400
1401         class Tester {
1402
1403                 static int Main(string[] args)
1404                 {
1405                         string temp;
1406
1407                         if (GetOption ("help", args, false, out temp)) {
1408                                 Usage ();
1409                                 return 0;
1410                         }
1411
1412                         string compiler;
1413                         if (!GetOption ("compiler", args, true, out compiler)) {
1414                                 Usage ();
1415                                 return 1;
1416                         }
1417
1418                         ITester tester;
1419                         try {
1420                                 Console.WriteLine ("Loading " + compiler + " ...");
1421                                 tester = new ReflectionTester (Assembly.LoadFile (compiler));
1422                         } catch (Exception) {
1423                                 Console.Error.WriteLine ("Switching to command line mode (compiler entry point was not found)");
1424                                 if (!File.Exists (compiler)) {
1425                                         Console.Error.WriteLine ("ERROR: Tested compiler was not found");
1426                                         return 1;
1427                                 }
1428                                 tester = new ProcessTester (compiler);
1429                         }
1430
1431                         string mode;
1432                         if (!GetOption ("mode", args, true, out mode)) {
1433                                 Usage ();
1434                                 return 1;
1435                         }
1436
1437                         Checker checker;
1438                         bool positive;
1439                         switch (mode) {
1440                                 case "neg":
1441                                         checker = new NegativeChecker (tester, true);
1442                                         positive = false;
1443                                         break;
1444                                 case "pos":
1445                                         string iltest;
1446                                         GetOption ("il", args, false, out iltest);
1447                                         checker = new PositiveChecker (tester, iltest);
1448                                         positive = true;
1449
1450                                         if (iltest != null && GetOption ("update-il", args, false, out temp)) {
1451                                                 ((PositiveChecker) checker).UpdateVerificationDataFile = true;
1452                                         }
1453
1454                                         break;
1455                                 default:
1456                                         Console.Error.WriteLine ("Invalid -mode argument");
1457                                         return 1;
1458                         }
1459
1460
1461                         if (GetOption ("issues", args, true, out temp))
1462                                 checker.IssueFile = temp;
1463                         if (GetOption ("log", args, true, out temp))
1464                                 checker.LogFile = temp;
1465                         if (GetOption ("verbose", args, false, out temp))
1466                                 checker.Verbose = true;
1467                         if (GetOption ("safe-execution", args, false, out temp))
1468                                 checker.SafeExecution = true;
1469                         if (GetOption ("compiler-options", args, true, out temp)) {
1470                                 string[] extra = temp.Split (' ');
1471                                 checker.ExtraCompilerOptions = extra;
1472                         }
1473
1474                         string test_pattern;
1475                         if (!GetOption ("files", args, true, out test_pattern)) {
1476                                 Usage ();
1477                                 return 1;
1478                         }
1479
1480                         var files = new List<string> ();
1481                         switch (test_pattern) {
1482                         case "v1":
1483                                 files.AddRange (Directory.GetFiles (".", positive ? "test*.cs" : "cs*.cs"));
1484                                 break;
1485                         case "v2":
1486                                 files.AddRange (Directory.GetFiles (".", positive ? "gtest*.cs" : "gcs*.cs"));
1487                                 goto case "v1";
1488                         case "v4":
1489                                 files.AddRange (Directory.GetFiles (".", positive ? "dtest*.cs" : "dcs*.cs"));
1490                                 goto case "v2";
1491                         default:
1492                                 files.AddRange (Directory.GetFiles (".", test_pattern));
1493                                 break;
1494                         }
1495
1496                         if (files.Count == 0) {
1497                                 Console.Error.WriteLine ("No files matching `{0}' found", test_pattern);
1498                                 return 2;
1499                         }
1500
1501                         checker.Initialize ();
1502 /*
1503                         files.Sort ((a, b) => {
1504                                 if (a.EndsWith ("-lib.cs", StringComparison.Ordinal)) {
1505                                         if (!b.EndsWith ("-lib.cs", StringComparison.Ordinal))
1506                                                 return -1;
1507                                 } else if (b.EndsWith ("-lib.cs", StringComparison.Ordinal)) {
1508                                         if (!a.EndsWith ("-lib.cs", StringComparison.Ordinal))
1509                                                 return 1;
1510                                 }
1511
1512                                 return a.CompareTo (b);
1513                         });
1514 */
1515                         foreach (string s in files) {
1516                                 string filename = Path.GetFileName (s);
1517                                 if (Char.IsUpper (filename, 0)) { // Windows hack
1518                                         continue;
1519                                 }
1520
1521                                 if (filename.EndsWith ("-p2.cs"))
1522                                         continue;
1523                             
1524                                 checker.Do (filename);
1525                         }
1526
1527                         checker.CleanUp ();
1528
1529                         checker.Dispose ();
1530
1531                         return checker.ResultCode;
1532                 }
1533
1534                 static bool GetOption (string opt, string[] args, bool req_arg, out string value)
1535                 {
1536                         opt = "-" + opt;
1537                         foreach (string a in args) {
1538                                 if (a.StartsWith (opt)) {
1539                                         int sep = a.IndexOf (':');
1540                                         if (sep > 0) {
1541                                                 value = a.Substring (sep + 1);
1542                                         } else {
1543                                                 value = null;
1544                                                 if (req_arg) {
1545                                                         Console.Error.WriteLine ("Missing argument in option " + opt);
1546                                                         return false;
1547                                                 }
1548                                         }
1549
1550                                         return true;
1551                                 }
1552                         }
1553
1554                         value = null;
1555                         return false;
1556                 }
1557
1558                 static void Usage ()
1559                 {
1560                         Console.WriteLine (
1561                                 "Mono compiler tester, (C) 2009 Novell, Inc.\n" +
1562                                 "compiler-tester -mode:[pos|neg] -compiler:FILE -files:file-list [options]\n" +
1563                                 "   \n" +
1564                                 "   -compiler:FILE   The file which will be used to compiler tests\n" +
1565                                 "   -compiler-options:OPTIONS  Add global compiler options\n" +
1566                                 "   -il:IL-FILE      XML file with expected IL details for each test\n" +
1567                                 "   -issues:FILE     The list of expected failures\n" +
1568                                 "   -log:FILE        Writes any output also to the file\n" +
1569                                 "   -help            Lists all options\n" +
1570                                 "   -mode:[pos|neg]  Specifies compiler test mode\n" +
1571                                 "   -safe-execution  Runs compiled executables in separate app-domain\n" +
1572                                 "   -update-il       Updates IL-FILE to match compiler output\n" +
1573                                 "   -verbose         Prints more details during testing\n"
1574                                 );
1575                 }
1576         }
1577 }