//
// Author:
// Marek Safar (marek.safar@gmail.com)
-//
-
//
// Copyright (C) 2008, 2009 Novell, Inc (http://www.novell.com)
+// Copyright (C) 2012 Xamarin Inc (http://www.xamarin.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
using System.Collections;
using System.Xml;
using System.Collections.Generic;
+using Mono.CompilerServices.SymbolWriter;
+using System.Globalization;
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
{
this.Type = mi.DeclaringType.ToString ();
this.MethodName = mi.ToString ();
+ this.MethodAttributes = (int) mi.Attributes;
this.ILSize = il_size;
}
- public MethodData (string type_name, string method_name, int il_size)
+ public MethodData (string type_name, string method_name, int method_attributes, int il_size)
{
this.Type = type_name;
this.MethodName = method_name;
+ this.MethodAttributes = method_attributes;
this.ILSize = il_size;
}
public string MethodName;
public int ILSize;
public bool Checked;
+ public int MethodAttributes;
}
ArrayList methods;
public VerificationData (string test_file)
{
-#if NET_2_0
this.test_file = test_file;
-#endif
}
-#if NET_2_0
string test_file;
public static VerificationData FromFile (string name, XmlReader r)
r.Read ();
while (r.ReadToNextSibling ("method")) {
string m_name = r ["name"];
+ int method_attrs = int.Parse (r["attrs"]);
r.ReadToDescendant ("size");
int il_size = r.ReadElementContentAsInt ();
- methods.Add (new MethodData (type_name, m_name, il_size));
+ methods.Add (new MethodData (type_name, m_name, method_attrs, il_size));
r.Read ();
}
r.Read ();
w.WriteStartElement ("method");
w.WriteAttributeString ("name", data.MethodName);
+ int v = data.MethodAttributes;
+ w.WriteAttributeString ("attrs", v.ToString ());
w.WriteStartElement ("size");
w.WriteValue (data.ILSize);
w.WriteEndElement ();
w.WriteEndElement ();
}
-#endif
public MethodData FindMethodData (string method_name, string declaring_type)
{
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;
if (verbose)
Log (filename + "...\t");
+ if (skip.Contains (filename)) {
+ return false;
+ }
+
if (ignore_list.Contains (filename)) {
++ignored;
LogFileLine (filename, "NOT TESTED");
string[] test_args;
if (test.CompilerOptions != null) {
- test_args = new string [2 + test.CompilerOptions.Length];
+ test_args = new string[2 + test.CompilerOptions.Length];
test.CompilerOptions.CopyTo (test_args, 0);
} else {
- test_args = new string [2];
+ test_args = new string[2];
}
- test_args [test_args.Length - 2] = test.FileName;
- test_args [test_args.Length - 1] = "-debug";
+ test_args[test_args.Length - 2] = test_args[0];
+ test_args[test_args.Length - 1] = "-debug";
+ test_args[0] = test.FileName;
return tester.Invoke (test_args);
}
{
const string ignored = "IGNORE";
const string no_error = "NO ERROR";
+ const string skip_tag = "SKIP";
using (StreamReader sr = new StreamReader (file)) {
string line;
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)
LoadError,
XmlError,
Success,
- ILError
+ ILError,
+ DebugError,
+ MethodAttributesError
}
public PositiveChecker (ITester tester, string verif_file):
md.Checked = true;
+ 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));
+ }
+
+ md.MethodAttributes = (int) mi.Attributes;
+
int il_size = GetILSize (mi);
if (md.ILSize == il_size)
return true;
if (md.ILSize > il_size) {
- checker.LogFileLine (test.FileName, "{0} (code size reduction {1} -> {2})", m_name, md.ILSize, il_size);
+ checker.LogFileLine (test.FileName, "{0} (code size reduction {1} -> {2})", decl_type + ": " + m_name, md.ILSize, il_size);
md.ILSize = il_size;
return true;
}
checker.HandleFailure (test.FileName, PositiveChecker.TestResult.ILError,
- string.Format ("{0} (code size {1} -> {2})", m_name, md.ILSize, il_size));
+ string.Format ("{0} (code size {1} -> {2})", decl_type + ": " + m_name, md.ILSize, il_size));
md.ILSize = il_size;
static int GetILSize (MethodBase mi)
{
-#if NET_2_0
MethodBody body = mi.GetMethodBody ();
if (body != null)
return body.GetILAsByteArray ().Length;
-#endif
+
return 0;
}
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) {
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 {
#endif
tester = new DomainTester ();
- if (!tester.Test (file))
+ if (!tester.Test (binaryFileName))
return false;
} catch (ApplicationException e) {
string ref_file = filename.Replace (".cs", "-ref.xml");
try {
#if !NET_2_1
- XmlComparer.Compare (ref_file, doc_output);
+ new XmlComparer ("doc").Compare (ref_file, doc_output);
#endif
} catch (Exception e) {
HandleFailure (filename, TestResult.XmlError, e.Message);
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)) {
+ 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");
+ try {
+ new XmlComparer ("symbols").Compare (ref_file, mdb_xml_file);
+ } catch (Exception e) {
+ HandleFailure (filename, TestResult.DebugError, e.Message);
+ return false;
+ }
+ }
+
}
} finally {
if (domain != null)
return true;
}
+ static void ConvertSymbolFileToXml (MonoSymbolFile symbolFile, string xmlFile)
+ {
+ using (XmlTextWriter writer = new XmlTextWriter (xmlFile, Encoding.UTF8)) {
+ writer.Formatting = Formatting.Indented;
+
+ writer.WriteStartDocument ();
+
+ writer.WriteStartElement ("symbols");
+
+ writer.WriteStartElement ("files");
+ foreach (var file in symbolFile.Sources) {
+ writer.WriteStartElement ("file");
+ writer.WriteAttributeString ("id", file.Index.ToString ());
+ writer.WriteAttributeString ("name", Path.GetFileName (file.FileName));
+ var checksum = file.Checksum;
+ if (checksum != null)
+ writer.WriteAttributeString ("checksum", ChecksumToString (checksum));
+
+ writer.WriteEndElement ();
+ }
+ writer.WriteEndElement ();
+
+ writer.WriteStartElement ("methods");
+ foreach (var method in symbolFile.Methods) {
+ writer.WriteStartElement ("method");
+ writer.WriteAttributeString ("token", IntToHex (method.Token));
+
+ var il_entries = method.GetLineNumberTable ();
+ writer.WriteStartElement ("sequencepoints");
+ foreach (var entry in il_entries.LineNumbers) {
+ 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 ();
+ }
+ writer.WriteEndElement ();
+
+ writer.WriteStartElement ("locals");
+ foreach (var local in method.GetLocals ()) {
+ writer.WriteStartElement ("entry");
+ writer.WriteAttributeString ("name", local.Name);
+ writer.WriteAttributeString ("il_index", local.Index.ToString ());
+ writer.WriteAttributeString ("scope_ref", local.BlockIndex.ToString ());
+ writer.WriteEndElement ();
+ }
+ writer.WriteEndElement ();
+
+ writer.WriteStartElement ("scopes");
+ foreach (var scope in method.GetCodeBlocks ()) {
+ writer.WriteStartElement ("entry");
+ writer.WriteAttributeString ("index", scope.Index.ToString ());
+ writer.WriteAttributeString ("start", IntToHex (scope.StartOffset));
+ writer.WriteAttributeString ("end", IntToHex (scope.EndOffset));
+ writer.WriteEndElement ();
+ }
+ writer.WriteEndElement ();
+
+ writer.WriteEndElement ();
+ }
+ writer.WriteEndElement ();
+
+ writer.WriteEndElement ();
+ writer.WriteEndDocument ();
+ }
+ }
+
+ static string ChecksumToString (byte[] checksum)
+ {
+ var sb = new StringBuilder (checksum.Length * 2);
+ for (int i = 0; i < checksum.Length; i++) {
+ sb.Append ("0123456789abcdef"[checksum[i] >> 4]);
+ sb.Append ("0123456789abcdef"[checksum[i] & 0x0F]);
+ }
+
+ return sb.ToString ();
+ }
+
+ static string IntToHex (int value)
+ {
+ return "0x" + value.ToString ("x", CultureInfo.InvariantCulture);
+ }
+
+ static string BoolToString (bool value)
+ {
+ return value ? "true" : "false";
+ }
+
protected override TestCase CreateTestCase (string filename, string [] options, string [] deps)
{
return new PositiveTestCase (filename, options, deps);
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:
case TestResult.ILError:
if (!update_verif_file) {
LogFileLine (file, "IL REGRESSION: " + extra);
}
extra = null;
break;
+
+ case TestResult.DebugError:
+ LogFileLine (file, "REGRESSION (SUCCESS -> SYMBOL FILE ERROR)");
+ break;
}
if (extra != null)
public override void Initialize ()
{
if (verif_file != null) {
-#if NET_2_0
LoadVerificationData (verif_file);
-#else
- throw new NotSupportedException ();
-#endif
}
base.Initialize ();
base.CleanUp ();
if (update_verif_file) {
-#if NET_2_0
UpdateVerificationData (verif_file);
-#else
- throw new NotSupportedException ();
-#endif
}
}
-#if NET_2_0
void LoadVerificationData (string file)
{
+ verif_data = new Hashtable ();
+
+ if (!File.Exists (file)) {
+ LogLine ("Writing verification data to `{0}' ...", file);
+ return;
+ }
+
LogLine ("Loading verification data from `{0}' ...", file);
using (XmlReader r = XmlReader.Create (file)) {
r.ReadStartElement ("tests");
- verif_data = new Hashtable ();
while (r.Read ()) {
if (r.Name != "test")
w.WriteEndElement ();
}
}
-#endif
}
class NegativeChecker: Checker
// Some error tests require to have different error text for different runtimes.
if (filtered.StartsWith ("//GMCS")) {
row = 1;
-#if !NET_2_0
- return true;
-#else
return AnalyzeTestFile(file, ref row, line, ref compiler_options, ref dependencies);
-#endif
}
check_error_line = !filtered.StartsWith ("//Line:0");
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;
((PositiveChecker) checker).UpdateVerificationDataFile = true;
}
+ break;
+ case "nunit":
+ positive = true;
+ checker = new NUnitChecker (tester);
break;
default:
Console.Error.WriteLine ("Invalid -mode argument");