5 // Marek Safar (marek.safar@gmail.com)
9 // Copyright (C) 2008, 2009 Novell, Inc (http://www.novell.com)
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 using System
.Diagnostics
;
34 using System
.Reflection
;
36 using System
.Collections
;
38 using System
.Collections
.Generic
;
40 namespace TestRunner
{
44 string Output { get; }
45 bool Invoke (string[] args
);
46 bool IsWarning (int warningNumber
);
49 class ReflectionTester
: ITester
{
55 public ReflectionTester (Assembly a
)
57 Type t
= a
.GetType ("Mono.CSharp.CompilerCallableEntryPoint");
60 Console
.Error
.WriteLine ("null, huh?");
62 ep
= t
.GetMethod ("InvokeCompiler",
63 BindingFlags
.Static
| BindingFlags
.Public
);
65 throw new MissingMethodException ("static InvokeCompiler");
66 method_arg
= new object [2];
68 PropertyInfo pi
= t
.GetProperty ("AllWarningNumbers");
69 all_warnings
= (int[])pi
.GetValue (null, null);
70 Array
.Sort (all_warnings
);
73 public string Output
{
75 return output
.GetStringBuilder ().ToString ();
79 public bool Invoke(string[] args
)
81 output
= new StringWriter ();
82 method_arg
[0] = args
;
83 method_arg
[1] = output
;
84 return (bool)ep
.Invoke (null, method_arg
);
87 public bool IsWarning (int warningNumber
)
89 return Array
.BinarySearch (all_warnings
, warningNumber
) >= 0;
94 class ProcessTester
: ITester
99 public ProcessTester (string p_path
)
101 pi
= new ProcessStartInfo ();
102 pi
.FileName
= p_path
;
103 pi
.CreateNoWindow
= true;
104 pi
.WindowStyle
= ProcessWindowStyle
.Hidden
;
105 pi
.RedirectStandardOutput
= true;
106 pi
.RedirectStandardError
= true;
107 pi
.UseShellExecute
= false;
110 public string Output
{
116 public bool Invoke(string[] args
)
118 StringBuilder sb
= new StringBuilder ("/nologo ");
119 foreach (string s
in args
) {
123 pi
.Arguments
= sb
.ToString ();
124 Process p
= Process
.Start (pi
);
125 output
= p
.StandardError
.ReadToEnd ();
126 if (output
.Length
== 0)
127 output
= p
.StandardOutput
.ReadToEnd ();
129 return p
.ExitCode
== 0;
132 public bool IsWarning (int warningNumber
)
134 throw new NotImplementedException ();
139 class TestCase
: MarshalByRefObject
141 public readonly string FileName
;
142 public readonly string[] CompilerOptions
;
143 public readonly string[] Dependencies
;
145 public TestCase (string filename
, string[] options
, string[] deps
)
147 this.FileName
= filename
;
148 this.CompilerOptions
= options
;
149 this.Dependencies
= deps
;
153 class PositiveTestCase
: TestCase
155 public class VerificationData
: MarshalByRefObject
157 public class MethodData
: MarshalByRefObject
159 public MethodData (MethodBase mi
, int il_size
)
161 this.Type
= mi
.DeclaringType
.ToString ();
162 this.MethodName
= mi
.ToString ();
163 this.ILSize
= il_size
;
166 public MethodData (string type_name
, string method_name
, int il_size
)
168 this.Type
= type_name
;
169 this.MethodName
= method_name
;
170 this.ILSize
= il_size
;
174 public string MethodName
;
180 public bool IsNewSet
;
182 public VerificationData (string test_file
)
185 this.test_file
= test_file
;
192 public static VerificationData
FromFile (string name
, XmlReader r
)
194 VerificationData tc
= new VerificationData (name
);
195 ArrayList methods
= new ArrayList ();
197 while (r
.ReadToNextSibling ("type")) {
198 string type_name
= r
["name"];
200 while (r
.ReadToNextSibling ("method")) {
201 string m_name
= r
["name"];
203 r
.ReadToDescendant ("size");
204 int il_size
= r
.ReadElementContentAsInt ();
205 methods
.Add (new MethodData (type_name
, m_name
, il_size
));
211 tc
.methods
= methods
;
215 public void WriteCodeInfoTo (XmlWriter w
)
217 w
.WriteStartElement ("test");
218 w
.WriteAttributeString ("name", test_file
);
221 foreach (MethodData data
in methods
) {
225 if (type
!= data
.Type
) {
227 w
.WriteEndElement ();
230 w
.WriteStartElement ("type");
231 w
.WriteAttributeString ("name", type
);
234 w
.WriteStartElement ("method");
235 w
.WriteAttributeString ("name", data
.MethodName
);
236 w
.WriteStartElement ("size");
237 w
.WriteValue (data
.ILSize
);
238 w
.WriteEndElement ();
239 w
.WriteEndElement ();
243 w
.WriteEndElement ();
245 w
.WriteEndElement ();
249 public MethodData
FindMethodData (string method_name
, string declaring_type
)
254 foreach (MethodData md
in methods
) {
255 if (md
.MethodName
== method_name
&& md
.Type
== declaring_type
)
262 public void AddNewMethod (MethodBase mb
, int il_size
)
265 methods
= new ArrayList ();
267 MethodData md
= new MethodData (mb
, il_size
);
273 VerificationData verif_data
;
275 public PositiveTestCase (string filename
, string [] options
, string [] deps
)
276 : base (filename
, options
, deps
)
280 public void CreateNewTest ()
282 verif_data
= new VerificationData (FileName
);
283 verif_data
.IsNewSet
= true;
286 public VerificationData VerificationProvider
{
296 class Checker
: MarshalByRefObject
, IDisposable
298 protected ITester tester
;
299 protected int success
;
301 protected int ignored
;
302 protected int syntax_errors
;
304 StreamWriter log_file
;
305 protected string[] extra_compiler_options
;
306 // protected string[] compiler_options;
307 // protected string[] dependencies;
309 protected ArrayList tests
= new ArrayList ();
310 protected Hashtable test_hash
= new Hashtable ();
311 protected ArrayList regression
= new ArrayList ();
312 protected ArrayList know_issues
= new ArrayList ();
313 protected ArrayList ignore_list
= new ArrayList ();
314 protected ArrayList no_error_list
= new ArrayList ();
316 protected bool verbose
;
317 protected bool safe_execution
;
319 int total_known_issues
;
321 protected Checker (ITester tester
)
323 this.tester
= tester
;
326 public string IssueFile
{
328 this.issue_file
= value;
329 ReadWrongErrors (issue_file
);
333 public string LogFile
{
335 this.log_file
= new StreamWriter (value, false);
339 public bool Verbose
{
345 public bool SafeExecution
{
347 safe_execution
= value;
351 public string[] ExtraCompilerOptions
{
353 extra_compiler_options
= value;
357 protected virtual bool GetExtraOptions (string file
, out string[] compiler_options
,
358 out string[] dependencies
)
361 compiler_options
= null;
364 using (StreamReader sr
= new StreamReader (file
)) {
366 while (row
++ < 3 && (line
= sr
.ReadLine()) != null) {
367 if (!AnalyzeTestFile (file
, ref row
, line
, ref compiler_options
,
378 protected virtual bool AnalyzeTestFile (string file
, ref int row
, string line
,
379 ref string[] compiler_options
,
380 ref string[] dependencies
)
382 const string options
= "// Compiler options:";
383 const string depends
= "// Dependencies:";
386 compiler_options
= null;
390 int index
= line
.IndexOf (options
);
392 compiler_options
= line
.Substring (index
+ options
.Length
).Trim().Split (' ');
393 for (int i
= 0; i
< compiler_options
.Length
; i
++)
394 compiler_options
[i
] = compiler_options
[i
].TrimStart ();
396 index
= line
.IndexOf (depends
);
398 dependencies
= line
.Substring (index
+ depends
.Length
).Trim().Split (' ');
399 for (int i
= 0; i
< dependencies
.Length
; i
++)
400 dependencies
[i
] = dependencies
[i
].TrimStart ();
406 public bool Do (string filename
)
408 if (test_hash
.Contains (filename
))
412 Log (filename
+ "...\t");
414 if (ignore_list
.Contains (filename
)) {
416 LogFileLine (filename
, "NOT TESTED");
420 string[] compiler_options
, dependencies
;
421 if (!GetExtraOptions (filename
, out compiler_options
, out dependencies
)) {
422 LogFileLine (filename
, "ERROR");
426 if (extra_compiler_options
!= null) {
427 if (compiler_options
== null)
428 compiler_options
= extra_compiler_options
;
430 string[] new_options
= new string [compiler_options
.Length
+ extra_compiler_options
.Length
];
431 extra_compiler_options
.CopyTo (new_options
, 0);
432 compiler_options
.CopyTo (new_options
, extra_compiler_options
.Length
);
433 compiler_options
= new_options
;
437 TestCase test
= CreateTestCase (filename
, compiler_options
, dependencies
);
438 test_hash
.Add (filename
, test
);
441 if (dependencies
!= null) {
442 foreach (string dependency
in dependencies
) {
443 if (!Do (dependency
)) {
444 LogFileLine (filename
, "DEPENDENCY FAILED");
455 protected virtual bool Check (TestCase test
)
459 if (test
.CompilerOptions
!= null) {
460 test_args
= new string [2 + test
.CompilerOptions
.Length
];
461 test
.CompilerOptions
.CopyTo (test_args
, 0);
463 test_args
= new string [2];
465 test_args
[test_args
.Length
- 2] = test
.FileName
;
466 test_args
[test_args
.Length
- 1] = "-debug";
468 return tester
.Invoke (test_args
);
471 protected virtual TestCase
CreateTestCase (string filename
, string [] options
, string [] deps
)
473 return new TestCase (filename
, options
, deps
);
476 void ReadWrongErrors (string file
)
478 const string ignored
= "IGNORE";
479 const string no_error
= "NO ERROR";
481 using (StreamReader sr
= new StreamReader (file
)) {
483 while ((line
= sr
.ReadLine()) != null) {
484 if (line
.StartsWith ("#"))
487 ArrayList active_cont
= know_issues
;
489 if (line
.IndexOf (ignored
) > 0)
490 active_cont
= ignore_list
;
491 else if (line
.IndexOf (no_error
) > 0)
492 active_cont
= no_error_list
;
494 string file_name
= line
.Split (' ')[0];
495 if (file_name
.Length
== 0)
498 active_cont
.Add (file_name
);
501 total_known_issues
= know_issues
.Count
;
504 protected virtual void PrintSummary ()
506 LogLine ("Done" + Environment
.NewLine
);
509 rate
= (float) (success
) / (float)total
;
510 LogLine ("{0} test cases passed ({1:0.##%})", success
, rate
);
512 if (syntax_errors
> 0)
513 LogLine ("{0} test(s) ignored because of wrong syntax !", syntax_errors
);
516 LogLine ("{0} test(s) ignored", ignored
);
518 if (total_known_issues
- know_issues
.Count
> 0)
519 LogLine ("{0} known issue(s)", total_known_issues
- know_issues
.Count
);
521 know_issues
.AddRange (no_error_list
);
522 if (know_issues
.Count
> 0) {
524 LogLine (issue_file
+ " contains {0} already fixed issues. Please remove", know_issues
.Count
);
525 foreach (string s
in know_issues
)
528 if (regression
.Count
> 0) {
530 LogLine ("The latest changes caused regression in {0} file(s)", regression
.Count
);
531 foreach (string s
in regression
)
536 public int ResultCode
539 return regression
.Count
== 0 ? 0 : 1;
543 protected void Log (string msg
, params object [] rest
)
545 Console
.Write (msg
, rest
);
546 if (log_file
!= null)
547 log_file
.Write (msg
, rest
);
550 protected void LogLine (string msg
)
552 Console
.WriteLine (msg
);
553 if (log_file
!= null)
554 log_file
.WriteLine (msg
);
557 protected void LogLine (string msg
, params object [] rest
)
559 Console
.WriteLine (msg
, rest
);
560 if (log_file
!= null)
561 log_file
.WriteLine (msg
, rest
);
564 public void LogFileLine (string file
, string msg
, params object [] args
)
567 string.Format (msg
, args
) :
568 file
+ "...\t" + string.Format (msg
, args
);
570 Console
.WriteLine (s
);
571 if (log_file
!= null)
572 log_file
.WriteLine (s
);
575 #region IDisposable Members
577 public void Dispose()
579 if (log_file
!= null)
585 public virtual void Initialize ()
589 public virtual void CleanUp ()
595 class PositiveChecker
: Checker
597 readonly string files_folder
;
598 readonly static object[] default_args
= new object[1] { new string[] {}
};
601 bool update_verif_file
;
602 Hashtable verif_data
;
607 readonly string mono
;
609 public enum TestResult
{
618 public PositiveChecker (ITester tester
, string verif_file
):
621 files_folder
= Directory
.GetCurrentDirectory ();
622 this.verif_file
= verif_file
;
625 pi
= new ProcessStartInfo ();
626 pi
.CreateNoWindow
= true;
627 pi
.WindowStyle
= ProcessWindowStyle
.Hidden
;
628 pi
.RedirectStandardOutput
= true;
629 pi
.RedirectStandardError
= true;
630 pi
.UseShellExecute
= false;
632 mono
= Environment
.GetEnvironmentVariable ("MONO_RUNTIME");
639 public bool UpdateVerificationDataFile
{
641 update_verif_file
= value;
644 return update_verif_file
;
648 protected override bool GetExtraOptions(string file
, out string[] compiler_options
,
649 out string[] dependencies
) {
650 if (!base.GetExtraOptions (file
, out compiler_options
, out dependencies
))
654 if (compiler_options
== null)
657 foreach (string one_opt
in compiler_options
) {
658 if (one_opt
.StartsWith ("-doc:")) {
659 doc_output
= one_opt
.Split (':', '/')[1];
665 class DomainTester
: MarshalByRefObject
667 public bool CheckILSize (PositiveTestCase test
, PositiveChecker checker
, string file
)
669 Assembly assembly
= Assembly
.LoadFile (file
);
672 Type
[] types
= assembly
.GetTypes ();
673 foreach (Type t
in types
) {
676 if (!t
.IsClass
&& !t
.IsValueType
)
679 if (test
.VerificationProvider
== null) {
680 if (!checker
.UpdateVerificationDataFile
)
681 checker
.LogFileLine (test
.FileName
, "Missing IL verification data");
682 test
.CreateNewTest ();
685 foreach (MemberInfo m
in t
.GetMembers (BindingFlags
.Public
| BindingFlags
.NonPublic
| BindingFlags
.Static
| BindingFlags
.Instance
| BindingFlags
.DeclaredOnly
)) {
686 MethodBase mi
= m
as MethodBase
;
690 if ((mi
.Attributes
& (MethodAttributes
.PinvokeImpl
)) != 0)
693 success
&= CompareIL (mi
, test
, checker
);
700 bool CompareIL (MethodBase mi
, PositiveTestCase test
, PositiveChecker checker
)
702 string m_name
= mi
.ToString ();
703 string decl_type
= mi
.DeclaringType
.ToString ();
704 PositiveTestCase
.VerificationData data_provider
= test
.VerificationProvider
;
706 PositiveTestCase
.VerificationData
.MethodData md
= data_provider
.FindMethodData (m_name
, decl_type
);
708 data_provider
.AddNewMethod (mi
, GetILSize (mi
));
709 if (!data_provider
.IsNewSet
) {
710 checker
.HandleFailure (test
.FileName
, PositiveChecker
.TestResult
.ILError
, decl_type
+ ": " + m_name
+ " (new method?)");
718 checker
.HandleFailure (test
.FileName
, PositiveChecker
.TestResult
.ILError
, decl_type
+ ": " + m_name
+ " has a duplicate");
724 int il_size
= GetILSize (mi
);
725 if (md
.ILSize
== il_size
)
728 if (md
.ILSize
> il_size
) {
729 checker
.LogFileLine (test
.FileName
, "{0} (code size reduction {1} -> {2})", m_name
, md
.ILSize
, il_size
);
734 checker
.HandleFailure (test
.FileName
, PositiveChecker
.TestResult
.ILError
,
735 string.Format ("{0} (code size {1} -> {2})", m_name
, md
.ILSize
, il_size
));
742 static int GetILSize (MethodBase mi
)
745 MethodBody body
= mi
.GetMethodBody ();
747 return body
.GetILAsByteArray ().Length
;
752 bool ExecuteFile (MethodInfo entry_point
, string filename
)
754 TextWriter stdout
= Console
.Out
;
755 TextWriter stderr
= Console
.Error
;
756 Console
.SetOut (TextWriter
.Null
);
757 Console
.SetError (TextWriter
.Null
);
758 ParameterInfo
[] pi
= entry_point
.GetParameters ();
759 object[] args
= pi
.Length
== 0 ? null : default_args
;
761 object result
= null;
764 result
= entry_point
.Invoke (null, args
);
766 Console
.SetOut (stdout
);
767 Console
.SetError (stderr
);
769 } catch (Exception e
) {
770 throw new ApplicationException (e
.ToString ());
773 if (result
is int && (int) result
!= 0)
774 throw new ApplicationException ("Wrong return code: " + result
.ToString ());
779 public bool Test (string file
)
781 Assembly assembly
= Assembly
.LoadFile (file
);
782 return ExecuteFile (assembly
.EntryPoint
, file
);
786 protected override bool Check(TestCase test
)
788 string filename
= test
.FileName
;
790 if (!base.Check (test
)) {
791 HandleFailure (filename
, TestResult
.CompileError
, tester
.Output
);
795 catch (Exception e
) {
796 if (e
.InnerException
!= null)
797 e
= e
.InnerException
;
799 HandleFailure (filename
, TestResult
.CompileError
, e
.ToString ());
804 if (filename
.EndsWith ("-lib.cs") || filename
.EndsWith ("-mod.cs")) {
806 LogFileLine (filename
, "OK");
811 string file
= Path
.Combine (files_folder
, Path
.GetFileNameWithoutExtension (filename
) + ".exe");
813 // Enable .dll only tests (no execution required)
814 if (!File
.Exists(file
)) {
815 HandleFailure (filename
, TestResult
.Success
, null);
819 AppDomain domain
= null;
821 if (safe_execution
) {
822 // Create a new AppDomain, with the current directory as the base.
823 AppDomainSetup setupInfo
= new AppDomainSetup ();
824 setupInfo
.ApplicationBase
= AppDomain
.CurrentDomain
.BaseDirectory
;
825 setupInfo
.LoaderOptimization
= LoaderOptimization
.SingleDomain
;
826 domain
= AppDomain
.CreateDomain (Path
.GetFileNameWithoutExtension (file
), null, setupInfo
);
834 tester
= (DomainTester
) domain
.CreateInstanceAndUnwrap (typeof (PositiveChecker
).Assembly
.FullName
, typeof (DomainTester
).FullName
);
837 tester
= new DomainTester ();
839 if (!tester
.Test (file
))
842 } catch (ApplicationException e
) {
843 HandleFailure (filename
, TestResult
.ExecError
, e
.Message
);
845 } catch (Exception e
) {
846 HandleFailure (filename
, TestResult
.LoadError
, e
.ToString ());
850 if (doc_output
!= null) {
851 string ref_file
= filename
.Replace (".cs", "-ref.xml");
854 XmlComparer
.Compare (ref_file
, doc_output
);
856 } catch (Exception e
) {
857 HandleFailure (filename
, TestResult
.XmlError
, e
.Message
);
861 if (verif_file
!= null) {
862 PositiveTestCase pt
= (PositiveTestCase
) test
;
863 pt
.VerificationProvider
= (PositiveTestCase
.VerificationData
) verif_data
[filename
];
865 if (!tester
.CheckILSize (pt
, this, file
))
871 AppDomain
.Unload (domain
);
874 HandleFailure (filename
, TestResult
.Success
, null);
878 protected override TestCase
CreateTestCase (string filename
, string [] options
, string [] deps
)
880 return new PositiveTestCase (filename
, options
, deps
);
883 public void HandleFailure (string file
, TestResult status
, string extra
)
886 case TestResult
.Success
:
888 if (know_issues
.Contains (file
)) {
889 LogFileLine (file
, "FIXED ISSUE");
893 LogFileLine (file
, "OK");
896 case TestResult
.CompileError
:
897 if (know_issues
.Contains (file
)) {
898 LogFileLine (file
, "KNOWN ISSUE (Compilation error)");
899 know_issues
.Remove (file
);
902 LogFileLine (file
, "REGRESSION (SUCCESS -> COMPILATION ERROR)");
905 case TestResult
.ExecError
:
906 if (know_issues
.Contains (file
)) {
907 LogFileLine (file
, "KNOWN ISSUE (Execution error)");
908 know_issues
.Remove (file
);
911 LogFileLine (file
, "REGRESSION (SUCCESS -> EXECUTION ERROR)");
914 case TestResult
.XmlError
:
915 if (know_issues
.Contains (file
)) {
916 LogFileLine (file
, "KNOWN ISSUE (Xml comparision error)");
917 know_issues
.Remove (file
);
920 LogFileLine (file
, "REGRESSION (SUCCESS -> DOCUMENTATION ERROR)");
923 case TestResult
.LoadError
:
924 LogFileLine (file
, "REGRESSION (SUCCESS -> LOAD ERROR)");
927 case TestResult
.ILError
:
928 if (!update_verif_file
) {
929 LogFileLine (file
, "IL REGRESSION: " + extra
);
936 LogLine ("{0}", extra
);
938 if (!regression
.Contains (file
))
939 regression
.Add (file
);
942 public override void Initialize ()
944 if (verif_file
!= null) {
946 LoadVerificationData (verif_file
);
948 throw new NotSupportedException ();
955 public override void CleanUp ()
959 if (update_verif_file
) {
961 UpdateVerificationData (verif_file
);
963 throw new NotSupportedException ();
969 void LoadVerificationData (string file
)
971 LogLine ("Loading verification data from `{0}' ...", file
);
973 using (XmlReader r
= XmlReader
.Create (file
)) {
974 r
.ReadStartElement ("tests");
975 verif_data
= new Hashtable ();
978 if (r
.Name
!= "test")
981 string name
= r
.GetAttribute ("name");
982 PositiveTestCase
.VerificationData tc
= PositiveTestCase
.VerificationData
.FromFile (name
, r
);
983 verif_data
.Add (name
, tc
);
988 void UpdateVerificationData (string file
)
990 LogLine ("Updating verification data `{0}' ...", file
);
992 XmlWriterSettings s
= new XmlWriterSettings ();
994 using (XmlWriter w
= XmlWriter
.Create (new StreamWriter (file
, false, Encoding
.UTF8
), s
)) {
995 w
.WriteStartDocument ();
996 w
.WriteComment ("This file contains expected IL and metadata produced by compiler for each test");
997 w
.WriteStartElement ("tests");
998 foreach (PositiveTestCase tc
in tests
) {
999 if (tc
.VerificationProvider
!= null)
1000 tc
.VerificationProvider
.WriteCodeInfoTo (w
);
1002 w
.WriteEndElement ();
1008 class NegativeChecker
: Checker
1010 string expected_message
;
1011 string error_message
;
1013 bool check_error_line
;
1015 IDictionary wrong_warning
;
1017 protected enum CompilerError
{
1026 public NegativeChecker (ITester tester
, bool check_msg
):
1029 this.check_msg
= check_msg
;
1030 wrong_warning
= new Hashtable ();
1033 protected override bool AnalyzeTestFile (string file
, ref int row
, string line
,
1034 ref string[] compiler_options
,
1035 ref string[] dependencies
)
1038 expected_message
= null;
1040 int index
= line
.IndexOf (':');
1041 if (index
== -1 || index
> 15) {
1042 LogFileLine (file
, "IGNORING: Wrong test file syntax (missing error mesage text)");
1044 base.AnalyzeTestFile (file
, ref row
, line
, ref compiler_options
,
1049 expected_message
= line
.Substring (index
+ 1).Trim ();
1053 string filtered
= line
.Replace(" ", "");
1055 // Some error tests require to have different error text for different runtimes.
1056 if (filtered
.StartsWith ("//GMCS")) {
1061 return AnalyzeTestFile(file
, ref row
, line
, ref compiler_options
, ref dependencies
);
1065 check_error_line
= !filtered
.StartsWith ("//Line:0");
1067 if (!filtered
.StartsWith ("//Line:")) {
1068 LogFileLine (file
, "IGNORING: Wrong test syntax (following line after an error messsage must have `// Line: xx' syntax");
1074 if (!base.AnalyzeTestFile (file
, ref row
, line
, ref compiler_options
, ref dependencies
))
1078 if (compiler_options
!= null) {
1079 foreach (string s
in compiler_options
) {
1080 if (s
.StartsWith ("-warnaserror") || s
.StartsWith ("/warnaserror"))
1088 protected override bool Check (TestCase test
)
1090 string filename
= test
.FileName
;
1093 while (Char
.IsLetter (filename
, start_char
))
1096 int end_char
= filename
.IndexOfAny (new char [] { '-', '.' }
);
1097 string expected
= filename
.Substring (start_char
, end_char
- start_char
);
1100 if (base.Check (test
)) {
1101 HandleFailure (filename
, CompilerError
.Missing
);
1105 catch (Exception e
) {
1106 HandleFailure (filename
, CompilerError
.Missing
);
1107 if (e
.InnerException
!= null)
1108 e
= e
.InnerException
;
1110 Log (e
.ToString ());
1114 int err_id
= int.Parse (expected
, System
.Globalization
.CultureInfo
.InvariantCulture
);
1115 if (tester
.IsWarning (err_id
)) {
1117 wrong_warning
[err_id
] = true;
1120 wrong_warning
[err_id
] = false;
1123 CompilerError result_code
= GetCompilerError (expected
, tester
.Output
);
1124 if (HandleFailure (filename
, result_code
)) {
1129 if (result_code
== CompilerError
.Wrong
)
1130 LogLine (tester
.Output
);
1135 CompilerError
GetCompilerError (string expected
, string buffer
)
1137 const string error_prefix
= "CS";
1138 const string ignored_error
= "error CS5001";
1139 string tested_text
= "error " + error_prefix
+ expected
;
1140 StringReader sr
= new StringReader (buffer
);
1141 string line
= sr
.ReadLine ();
1142 ArrayList ld
= new ArrayList ();
1143 CompilerError result
= CompilerError
.Missing
;
1144 while (line
!= null) {
1145 if (ld
.Contains (line
) && result
== CompilerError
.Expected
) {
1146 if (line
.IndexOf ("Location of the symbol related to previous") == -1)
1147 return CompilerError
.Duplicate
;
1151 if (result
!= CompilerError
.Expected
) {
1152 if (line
.IndexOf (tested_text
) != -1) {
1154 int first
= line
.IndexOf (':');
1155 int second
= line
.IndexOf (':', first
+ 1);
1156 if (line
.IndexOf ("Warning as Error: ", first
, StringComparison
.Ordinal
) > 0) {
1157 if (check_error_line
) {
1158 second
= line
.IndexOf (':', second
+ 1);
1160 } else if (second
== -1 || !check_error_line
) {
1164 string msg
= line
.Substring (second
+ 1).TrimEnd ('.').Trim ();
1165 if (msg
!= expected_message
&& msg
!= expected_message
.Replace ('`', '\'')) {
1166 error_message
= msg
;
1167 return CompilerError
.WrongMessage
;
1170 if (check_error_line
&& line
.IndexOf (".cs(") == -1)
1171 return CompilerError
.MissingLocation
;
1173 result
= CompilerError
.Expected
;
1174 } else if (line
.IndexOf (error_prefix
) != -1 &&
1175 line
.IndexOf (ignored_error
) == -1)
1176 result
= CompilerError
.Wrong
;
1179 line
= sr
.ReadLine ();
1185 bool HandleFailure (string file
, CompilerError status
)
1188 case CompilerError
.Expected
:
1189 if (know_issues
.Contains (file
) || no_error_list
.Contains (file
)) {
1190 LogFileLine (file
, "FIXED ISSUE");
1195 LogFileLine (file
, "OK");
1198 case CompilerError
.Wrong
:
1199 if (know_issues
.Contains (file
)) {
1200 LogFileLine (file
, "KNOWN ISSUE (Wrong error reported)");
1201 know_issues
.Remove (file
);
1204 if (no_error_list
.Contains (file
)) {
1205 LogFileLine (file
, "REGRESSION (NO ERROR -> WRONG ERROR CODE)");
1206 no_error_list
.Remove (file
);
1209 LogFileLine (file
, "REGRESSION (CORRECT ERROR -> WRONG ERROR CODE)");
1213 case CompilerError
.WrongMessage
:
1214 if (know_issues
.Contains (file
)) {
1215 LogFileLine (file
, "KNOWN ISSUE (Wrong error message reported)");
1216 know_issues
.Remove (file
);
1219 if (no_error_list
.Contains (file
)) {
1220 LogFileLine (file
, "REGRESSION (NO ERROR -> WRONG ERROR MESSAGE)");
1221 no_error_list
.Remove (file
);
1224 LogFileLine (file
, "REGRESSION (CORRECT ERROR -> WRONG ERROR MESSAGE)");
1225 LogLine ("Exp: {0}", expected_message
);
1226 LogLine ("Was: {0}", error_message
);
1230 case CompilerError
.Missing
:
1231 if (no_error_list
.Contains (file
)) {
1232 LogFileLine (file
, "KNOWN ISSUE (No error reported)");
1233 no_error_list
.Remove (file
);
1237 if (know_issues
.Contains (file
)) {
1238 LogFileLine (file
, "REGRESSION (WRONG ERROR -> NO ERROR)");
1239 know_issues
.Remove (file
);
1242 LogFileLine (file
, "REGRESSION (CORRECT ERROR -> NO ERROR)");
1247 case CompilerError
.MissingLocation
:
1248 if (know_issues
.Contains (file
)) {
1249 LogFileLine (file
, "KNOWN ISSUE (Missing error location)");
1250 know_issues
.Remove (file
);
1253 if (no_error_list
.Contains (file
)) {
1254 LogFileLine (file
, "REGRESSION (NO ERROR -> MISSING ERROR LOCATION)");
1255 no_error_list
.Remove (file
);
1258 LogFileLine (file
, "REGRESSION (CORRECT ERROR -> MISSING ERROR LOCATION)");
1262 case CompilerError
.Duplicate
:
1263 // Will become an error soon
1264 LogFileLine (file
, "WARNING: EXACTLY SAME ERROR HAS BEEN ISSUED MULTIPLE TIMES");
1268 regression
.Add (file
);
1272 protected override void PrintSummary()
1274 base.PrintSummary ();
1276 if (wrong_warning
.Count
> 0) {
1278 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)");
1280 foreach (DictionaryEntry de
in wrong_warning
)
1281 LogLine ("CS{0:0000} : {1}", de
.Key
, (bool)de
.Value
? "incorrect warning definition" : "missing warning definition");
1289 static int Main(string[] args
)
1293 if (GetOption ("help", args
, false, out temp
)) {
1299 if (!GetOption ("compiler", args
, true, out compiler
)) {
1306 Console
.WriteLine ("Loading " + compiler
+ " ...");
1307 tester
= new ReflectionTester (Assembly
.LoadFile (compiler
));
1313 Console
.Error
.WriteLine ("Switching to command line mode (compiler entry point was not found)");
1314 if (!File
.Exists (compiler
)) {
1315 Console
.Error
.WriteLine ("ERROR: Tested compiler was not found");
1318 tester
= new ProcessTester (compiler
);
1323 if (!GetOption ("mode", args
, true, out mode
)) {
1332 checker
= new NegativeChecker (tester
, true);
1337 GetOption ("il", args
, false, out iltest
);
1338 checker
= new PositiveChecker (tester
, iltest
);
1341 if (iltest
!= null && GetOption ("update-il", args
, false, out temp
)) {
1342 ((PositiveChecker
) checker
).UpdateVerificationDataFile
= true;
1347 Console
.Error
.WriteLine ("Invalid -mode argument");
1352 if (GetOption ("issues", args
, true, out temp
))
1353 checker
.IssueFile
= temp
;
1354 if (GetOption ("log", args
, true, out temp
))
1355 checker
.LogFile
= temp
;
1356 if (GetOption ("verbose", args
, false, out temp
))
1357 checker
.Verbose
= true;
1358 if (GetOption ("safe-execution", args
, false, out temp
))
1359 checker
.SafeExecution
= true;
1360 if (GetOption ("compiler-options", args
, true, out temp
)) {
1361 string[] extra
= temp
.Split (' ');
1362 checker
.ExtraCompilerOptions
= extra
;
1365 string test_pattern
;
1366 if (!GetOption ("files", args
, true, out test_pattern
)) {
1371 var files
= new List
<string> ();
1372 switch (test_pattern
) {
1374 files
.AddRange (Directory
.GetFiles (".", positive
? "test*.cs" : "cs*.cs"));
1377 files
.AddRange (Directory
.GetFiles (".", positive
? "gtest*.cs" : "gcs*.cs"));
1380 files
.AddRange (Directory
.GetFiles (".", positive
? "dtest*.cs" : "dcs*.cs"));
1383 files
.AddRange (Directory
.GetFiles (".", test_pattern
));
1387 if (files
.Count
== 0) {
1388 Console
.Error
.WriteLine ("No files matching `{0}' found", test_pattern
);
1392 checker
.Initialize ();
1394 files.Sort ((a, b) => {
1395 if (a.EndsWith ("-lib.cs", StringComparison.Ordinal)) {
1396 if (!b.EndsWith ("-lib.cs", StringComparison.Ordinal))
1398 } else if (b.EndsWith ("-lib.cs", StringComparison.Ordinal)) {
1399 if (!a.EndsWith ("-lib.cs", StringComparison.Ordinal))
1403 return a.CompareTo (b);
1406 foreach (string s
in files
) {
1407 string filename
= Path
.GetFileName (s
);
1408 if (Char
.IsUpper (filename
, 0)) { // Windows hack
1412 if (filename
.EndsWith ("-p2.cs"))
1415 checker
.Do (filename
);
1422 return checker
.ResultCode
;
1425 static bool GetOption (string opt
, string[] args
, bool req_arg
, out string value)
1428 foreach (string a
in args
) {
1429 if (a
.StartsWith (opt
)) {
1430 int sep
= a
.IndexOf (':');
1432 value = a
.Substring (sep
+ 1);
1436 Console
.Error
.WriteLine ("Missing argument in option " + opt
);
1449 static void Usage ()
1452 "Mono compiler tester, (C) 2009 Novell, Inc.\n" +
1453 "compiler-tester -mode:[pos|neg] -compiler:FILE -files:file-list [options]\n" +
1455 " -compiler:FILE The file which will be used to compiler tests\n" +
1456 " -compiler-options:OPTIONS Add global compiler options\n" +
1457 " -il:IL-FILE XML file with expected IL details for each test\n" +
1458 " -issues:FILE The list of expected failures\n" +
1459 " -log:FILE Writes any output also to the file\n" +
1460 " -help Lists all options\n" +
1461 " -mode:[pos|neg] Specifies compiler test mode\n" +
1462 " -safe-execution Runs compiled executables in separate app-domain\n" +
1463 " -update-il Updates IL-FILE to match compiler output\n" +
1464 " -verbose Prints more details during testing\n"