Merge pull request #2889 from mono/alexischr/canary-sizecalc-change-impact
[mono.git] / mcs / tools / compiler-tester / compiler-tester.cs
index 7d3f3442024686a5bdf3375146a47d5b864a9f52..677403c049258a084fb8bfca2ebcbee87201c0cb 100644 (file)
@@ -151,6 +151,130 @@ namespace TestRunner {
                }
        }
 
+       class NUnitChecker : PositiveChecker
+       {
+               class TestCaseEntry
+               {
+                       string name;
+                       string referenceFile;
+                       string executedMethod;
+                       bool has_return;
+
+                       public TestCaseEntry (string name, string referenceFile, MethodInfo executedMethod)
+                       {
+                               this.name = name.Replace ('-', '_');
+                               this.referenceFile = referenceFile;
+                               this.executedMethod = ConvertMethodInfoToText (executedMethod, out has_return);
+                       }
+
+                       public string Name
+                       {
+                               get
+                               {
+                                       return name;
+                               }
+                       }
+
+                       public string ReferenceFile {
+                               get {
+                                       return referenceFile;
+                               }
+                       }
+
+                       static string ConvertMethodInfoToText (MethodInfo mi, out bool hasReturn)
+                       {
+                               hasReturn = mi.ReturnType != typeof (void);
+                               string declaring = mi.DeclaringType.FullName.Replace ('+', '.');
+                               var param = mi.GetParameters ();
+                               if (param.Length == 0)
+                                       return declaring + "." + mi.Name + " ()";
+
+                               return declaring + "." + mi.Name + " (new string[0])";
+                       }
+
+                       public string GetTestFixture ()
+                       {
+                               var call = name + "::" + executedMethod;
+                               if (!has_return)
+                                       return call;
+
+                               return string.Format ("Assert.AreEqual (0, {0})", call); 
+                       }
+               }
+
+               List<TestCaseEntry> entries = new List<TestCaseEntry> ();
+
+               public NUnitChecker (ITester tester)
+                       : base (tester, null)
+               {
+               }
+
+               public override void CleanUp ()
+               {
+                       base.CleanUp ();
+
+                       StringBuilder aliases = new StringBuilder ();
+                       var src_dir = Path.Combine ("projects", "MonoTouch");
+                       string src_file = Path.Combine (src_dir, "tests.cs");
+
+                       using (var file = new StreamWriter (src_file, false)) {
+                               foreach (var e in entries) {
+                                       file.WriteLine ("extern alias {0};", e.Name);
+                                       aliases.AppendFormat ("    <Reference Include=\"{0}\">", Path.GetFileNameWithoutExtension (e.ReferenceFile));
+                                       aliases.Append (Environment.NewLine);
+                                       aliases.AppendFormat ("      <Aliases>{0}</Aliases>", e.Name);
+                                       aliases.Append (Environment.NewLine);
+                                       aliases.AppendFormat ("      <HintPath>..\\..\\{0}</HintPath>", Path.GetFileName (e.ReferenceFile));
+                                       aliases.Append (Environment.NewLine);
+                                       aliases.AppendLine ("    </Reference>");
+                               }
+
+                               file.WriteLine ();
+                               file.WriteLine ("using NUnit.Framework;");
+                               file.WriteLine ();
+                               file.WriteLine ("[TestFixture]");
+                               file.WriteLine ("public class Tests {");
+
+                               foreach (var e in entries) {
+                                       file.WriteLine ("\t[Test]");
+                                       file.WriteLine ("\tpublic void TestFile_{0} ()", e.Name);
+                                       file.WriteLine ("\t{");
+                                       file.WriteLine ("\t\t{0};", e.GetTestFixture ());
+                                       file.WriteLine ("\t}");
+                                       file.WriteLine ();
+                               }
+
+                               file.WriteLine ("}");
+                       }
+
+                       var input = File.ReadAllText (Path.Combine (src_dir, "MonoTouch.csproj.template"));
+                       input = input.Replace ("@GENERATED_REFERENCES", aliases.ToString ());
+                       input = input.Replace ("@TEST_SOURCEFILE", Path.GetFileName (src_file));
+
+                       File.WriteAllText (Path.Combine (src_dir, "MonoTouch.csproj"), input);
+                       return;
+               }
+
+               protected override bool ExecuteTestFile (TestCase test, string binaryFileName)
+               {
+                       Assembly assembly = Assembly.LoadFile (binaryFileName);
+                       var ep = assembly.EntryPoint;
+                       if (!ep.IsPublic) {
+                               HandleFailure (test.FileName, TestResult.LoadError, "Entry method is private");
+                               return false;
+                       }
+
+                       if (ep.DeclaringType.IsNestedPrivate || ep.DeclaringType.IsNestedFamily) {
+                               HandleFailure (test.FileName, TestResult.LoadError, "Entry method in hidden nested type");
+                               return false;
+                       }
+
+                       entries.Add (new TestCaseEntry (Path.GetFileNameWithoutExtension (test.FileName), binaryFileName, ep));
+                       HandleFailure (test.FileName, TestResult.Success, null);
+                       return true;
+               }
+       }
+
        class PositiveTestCase : TestCase
        {
                public class VerificationData : MarshalByRefObject
@@ -315,6 +439,7 @@ namespace TestRunner {
                protected ArrayList know_issues = new ArrayList ();
                protected ArrayList ignore_list = new ArrayList ();
                protected ArrayList no_error_list = new ArrayList ();
+               ArrayList skip = new ArrayList ();
                
                protected bool verbose;
                protected bool safe_execution;
@@ -414,6 +539,10 @@ namespace TestRunner {
                        if (verbose)
                                Log (filename + "...\t");
 
+                       if (skip.Contains (filename)) {
+                               return false;
+                       }
+
                        if (ignore_list.Contains (filename)) {
                                ++ignored;
                                LogFileLine (filename, "NOT TESTED");
@@ -460,13 +589,13 @@ namespace TestRunner {
                        string[] test_args;
 
                        if (test.CompilerOptions != null) {
-                               test_args = new string [2 + test.CompilerOptions.Length];
+                               test_args = new string[1 + test.CompilerOptions.Length];
                                test.CompilerOptions.CopyTo (test_args, 0);
                        } else {
-                               test_args = new string [2];
+                               test_args = new string[1];
                        }
-                       test_args [test_args.Length - 2] = test.FileName;
-                       test_args [test_args.Length - 1] = "-debug";
+                       test_args[test_args.Length - 1] = test_args[0];
+                       test_args[0] = test.FileName;
 
                        return tester.Invoke (test_args);
                }
@@ -480,6 +609,7 @@ namespace TestRunner {
                {
                        const string ignored = "IGNORE";
                        const string no_error = "NO ERROR";
+                       const string skip_tag = "SKIP";
 
                        using (StreamReader sr = new StreamReader (file)) {
                                string line;
@@ -493,6 +623,8 @@ namespace TestRunner {
                                                active_cont = ignore_list;
                                        else if (line.IndexOf (no_error) > 0)
                                                active_cont = no_error_list;
+                                       else if (line.Contains (skip_tag))
+                                               active_cont = skip;
 
                                        string file_name = line.Split (' ')[0];
                                        if (file_name.Length == 0)
@@ -564,11 +696,9 @@ namespace TestRunner {
                                log_file.WriteLine (msg, rest);
                }
                
-               public void LogFileLine (string file, string msg, params object [] args)
+               public void LogFileLine (string file, string msg)
                {
-                       string s = verbose ? 
-                               string.Format (msg, args) :
-                               file + "...\t" + string.Format (msg, args); 
+                       string s = verbose ? msg : file + "...\t" + msg;
 
                        Console.WriteLine (s);
                        if (log_file != null)
@@ -728,7 +858,7 @@ namespace TestRunner {
 
                                if (md.MethodAttributes != (int) mi.Attributes) {
                                        checker.HandleFailure (test.FileName, PositiveChecker.TestResult.MethodAttributesError,
-                                               string.Format ("{0} ({1} -> {2})", decl_type + ": " + m_name, md.MethodAttributes, mi.Attributes));
+                                               string.Format ("{0} ({1} -> {2})", decl_type + ": " + m_name, (MethodAttributes) md.MethodAttributes, mi.Attributes));
                                }
 
                                md.MethodAttributes = (int) mi.Attributes;
@@ -738,7 +868,7 @@ namespace TestRunner {
                                        return true;
 
                                if (md.ILSize > il_size) {
-                                       checker.LogFileLine (test.FileName, "{0} (code size reduction {1} -> {2})", decl_type + ": " + m_name, md.ILSize, il_size);
+                                       checker.LogFileLine (test.FileName, string.Format ("{0} (code size reduction {1} -> {2})", decl_type + ": " + m_name, md.ILSize, il_size));
                                        md.ILSize = il_size;
                                        return true;
                                }
@@ -827,6 +957,13 @@ namespace TestRunner {
                                return true;
                        }
 
+                       return ExecuteTestFile (test, file);
+               }
+
+               protected virtual bool ExecuteTestFile (TestCase test, string binaryFileName)
+               {
+                       string filename = test.FileName;
+
                        AppDomain domain = null;
 #if !NET_2_1
                        if (safe_execution) {
@@ -834,7 +971,7 @@ namespace TestRunner {
                                AppDomainSetup setupInfo = new AppDomainSetup ();
                                setupInfo.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
                                setupInfo.LoaderOptimization = LoaderOptimization.SingleDomain;
-                               domain = AppDomain.CreateDomain (Path.GetFileNameWithoutExtension (file), null, setupInfo);
+                               domain = AppDomain.CreateDomain (Path.GetFileNameWithoutExtension (binaryFileName), null, setupInfo);
                        }
 #endif
                        try {
@@ -847,7 +984,7 @@ namespace TestRunner {
 #endif
                                                tester = new DomainTester ();
 
-                                       if (!tester.Test (file))
+                                       if (!tester.Test (binaryFileName))
                                                return false;
 
                                } catch (ApplicationException e) {
@@ -873,13 +1010,14 @@ namespace TestRunner {
                                                PositiveTestCase pt = (PositiveTestCase) test;
                                                pt.VerificationProvider = (PositiveTestCase.VerificationData) verif_data[filename];
 
-                                               if (!tester.CheckILSize (pt, this, file))
+                                               if (!tester.CheckILSize (pt, this, binaryFileName))
                                                        return false;
                                        }
 
                                        if (filename.StartsWith ("test-debug", StringComparison.OrdinalIgnoreCase)) {
-                                               MonoSymbolFile mdb_file = MonoSymbolFile.ReadSymbolFile (file + ".mdb");
-                                               var mdb_xml_file = mdb_file.FileName + ".xml";
+                                               var mdb_file_name = binaryFileName + ".mdb";
+                                               MonoSymbolFile mdb_file = MonoSymbolFile.ReadSymbolFile (mdb_file_name);
+                                               var mdb_xml_file = mdb_file_name + ".xml";
                                                ConvertSymbolFileToXml (mdb_file, mdb_xml_file);
 
                                                var ref_file = filename.Replace(".cs", "-ref.xml");
@@ -934,6 +1072,7 @@ namespace TestRunner {
                                                writer.WriteStartElement ("entry");
                                                writer.WriteAttributeString ("il", IntToHex (entry.Offset));
                                                writer.WriteAttributeString ("row", entry.Row.ToString ());
+                                               writer.WriteAttributeString ("col", entry.Column.ToString ());
                                                writer.WriteAttributeString ("file_ref", entry.File.ToString ());
                                                writer.WriteAttributeString ("hidden", BoolToString (entry.IsHidden));
                                                writer.WriteEndElement ();
@@ -1036,7 +1175,11 @@ namespace TestRunner {
                                        break;
 
                                case TestResult.LoadError:
-                                       LogFileLine (file, "REGRESSION (SUCCESS -> LOAD ERROR)");
+                                       if (extra != null)
+                                               extra = ": " + extra;
+
+                                       LogFileLine (file, "REGRESSION (SUCCESS -> LOAD ERROR)" + extra);
+                                       extra = null;
                                        break;
 
                                case TestResult.MethodAttributesError:
@@ -1274,7 +1417,7 @@ namespace TestRunner {
                                                        }
 
                                                        string msg = line.Substring (second + 1).TrimEnd ('.').Trim ();
-                                                       if (msg != expected_message && msg != expected_message.Replace ('`', '\'')) {
+                                                       if (msg != expected_message && !TryToMatchErrorMessage (msg, expected_message)) {
                                                                error_message = msg;
                                                                return CompilerError.WrongMessage;
                                                        }
@@ -1294,6 +1437,21 @@ namespace TestRunner {
                        return result;
                }
 
+               static bool TryToMatchErrorMessage (string actual, string expected)
+               {
+                       actual = actual.Replace ("\\", "/");
+                       var path_mask_start = expected.IndexOf ("*PATH*", StringComparison.Ordinal);
+                       if (path_mask_start > 0 && actual.Length > path_mask_start) {
+                               var parts = expected.Split (new [] { "*PATH*" }, StringSplitOptions.None);
+                               foreach (var part in parts) {
+                                       if (!actual.Contains (part))
+                                               return false;
+                               }
+                       }
+
+                       return true;
+               }
+
                bool HandleFailure (string file, CompilerError status)
                {
                        switch (status) {
@@ -1417,18 +1575,13 @@ namespace TestRunner {
                        try {
                                Console.WriteLine ("Loading " + compiler + " ...");
                                tester = new ReflectionTester (Assembly.LoadFile (compiler));
-                       }
-                       catch (Exception) {
-#if NET_2_1
-                               throw;
-#else
+                       } catch (Exception) {
                                Console.Error.WriteLine ("Switching to command line mode (compiler entry point was not found)");
                                if (!File.Exists (compiler)) {
                                        Console.Error.WriteLine ("ERROR: Tested compiler was not found");
                                        return 1;
                                }
                                tester = new ProcessTester (compiler);
-#endif
                        }
 
                        string mode;
@@ -1454,6 +1607,10 @@ namespace TestRunner {
                                                ((PositiveChecker) checker).UpdateVerificationDataFile = true;
                                        }
 
+                                       break;
+                               case "nunit":
+                                       positive = true;
+                                       checker = new NUnitChecker (tester);
                                        break;
                                default:
                                        Console.Error.WriteLine ("Invalid -mode argument");