// Author:
// Marek Safar (marek.safar@gmail.com)
//
-
-//
-// Copyright (C) 2008 Novell, Inc (http://www.novell.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.Text;
using System.Collections;
using System.Xml;
+using System.Collections.Generic;
+using Mono.CompilerServices.SymbolWriter;
+using System.Globalization;
namespace TestRunner {
}
#endif
- class TestCase
+ class TestCase : MarshalByRefObject
{
public readonly string FileName;
public readonly string[] CompilerOptions;
class PositiveTestCase : TestCase
{
- public class VerificationData
+ public class VerificationData : MarshalByRefObject
{
- public class MethodData
+ public class MethodData : MarshalByRefObject
{
public MethodData (MethodBase mi, int il_size)
{
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)
w.WriteEndElement ();
}
-#endif
public MethodData FindMethodData (string method_name, string declaring_type)
{
return verif_data;
}
}
-
- public bool CompareIL (MethodBase mi, PositiveChecker checker)
- {
- string m_name = mi.ToString ();
- string decl_type = mi.DeclaringType.ToString ();
- VerificationData.MethodData md = verif_data.FindMethodData (m_name, decl_type);
- if (md == null) {
- verif_data.AddNewMethod (mi, GetILSize (mi));
- if (!verif_data.IsNewSet) {
- checker.HandleFailure (FileName, PositiveChecker.TestResult.ILError, decl_type + ": " + m_name + " (new method?)");
- return false;
- }
-
- return true;
- }
-
- if (md.Checked) {
- checker.HandleFailure (FileName, PositiveChecker.TestResult.ILError, decl_type + ": " + m_name + " has a duplicate");
- return false;
- }
-
- md.Checked = true;
-
- int il_size = GetILSize (mi);
- if (md.ILSize == il_size)
- return true;
-
- if (md.ILSize > il_size) {
- checker.LogFileLine (FileName, "{0} (code size reduction {1} -> {2})", m_name, md.ILSize, il_size);
- md.ILSize = il_size;
- return true;
- }
-
- checker.HandleFailure (FileName, PositiveChecker.TestResult.ILError,
- string.Format ("{0} (code size {1} -> {2})", m_name, md.ILSize, il_size));
-
- md.ILSize = il_size;
-
- return false;
- }
-
- static int GetILSize (MethodBase mi)
- {
-#if NET_2_0
- MethodBody body = mi.GetMethodBody ();
- if (body != null)
- return body.GetILAsByteArray ().Length;
-#endif
- return 0;
- }
}
- class Checker: IDisposable
+ class Checker: MarshalByRefObject, IDisposable
{
protected ITester tester;
protected int success;
protected ArrayList no_error_list = new ArrayList ();
protected bool verbose;
+ protected bool safe_execution;
int total_known_issues;
}
}
+ public bool SafeExecution {
+ set {
+ safe_execution = value;
+ }
+ }
+
public string[] ExtraCompilerOptions {
set {
extra_compiler_options = value;
if (test_hash.Contains (filename))
return true;
+ if (verbose)
+ Log (filename + "...\t");
+
if (ignore_list.Contains (filename)) {
++ignored;
LogFileLine (filename, "NOT TESTED");
public void LogFileLine (string file, string msg, params object [] args)
{
- string s = file + "...\t" + string.Format (msg, args);
+ string s = verbose ?
+ string.Format (msg, args) :
+ file + "...\t" + string.Format (msg, args);
+
Console.WriteLine (s);
if (log_file != null)
log_file.WriteLine (s);
LoadError,
XmlError,
Success,
- ILError
+ ILError,
+ DebugError
}
public PositiveChecker (ITester tester, string verif_file):
set {
update_verif_file = value;
}
+ get {
+ return update_verif_file;
+ }
}
protected override bool GetExtraOptions(string file, out string[] compiler_options,
return true;
}
+ class DomainTester : MarshalByRefObject
+ {
+ public bool CheckILSize (PositiveTestCase test, PositiveChecker checker, string file)
+ {
+ Assembly assembly = Assembly.LoadFile (file);
+
+ bool success = true;
+ Type[] types = assembly.GetTypes ();
+ foreach (Type t in types) {
+
+ // Skip interfaces
+ if (!t.IsClass && !t.IsValueType)
+ continue;
+
+ if (test.VerificationProvider == null) {
+ if (!checker.UpdateVerificationDataFile)
+ checker.LogFileLine (test.FileName, "Missing IL verification data");
+ test.CreateNewTest ();
+ }
+
+ foreach (MemberInfo m in t.GetMembers (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly)) {
+ MethodBase mi = m as MethodBase;
+ if (mi == null)
+ continue;
+
+ if ((mi.Attributes & (MethodAttributes.PinvokeImpl)) != 0)
+ continue;
+
+ success &= CompareIL (mi, test, checker);
+ }
+ }
+
+ return success;
+ }
+
+ bool CompareIL (MethodBase mi, PositiveTestCase test, PositiveChecker checker)
+ {
+ string m_name = mi.ToString ();
+ string decl_type = mi.DeclaringType.ToString ();
+ PositiveTestCase.VerificationData data_provider = test.VerificationProvider;
+
+ PositiveTestCase.VerificationData.MethodData md = data_provider.FindMethodData (m_name, decl_type);
+ if (md == null) {
+ data_provider.AddNewMethod (mi, GetILSize (mi));
+ if (!data_provider.IsNewSet) {
+ checker.HandleFailure (test.FileName, PositiveChecker.TestResult.ILError, decl_type + ": " + m_name + " (new method?)");
+ return false;
+ }
+
+ return true;
+ }
+
+ if (md.Checked) {
+ checker.HandleFailure (test.FileName, PositiveChecker.TestResult.ILError, decl_type + ": " + m_name + " has a duplicate");
+ return false;
+ }
+
+ md.Checked = true;
+
+ 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})", 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})", decl_type + ": " + m_name, md.ILSize, il_size));
+
+ md.ILSize = il_size;
+
+ return false;
+ }
+
+ static int GetILSize (MethodBase mi)
+ {
+ MethodBody body = mi.GetMethodBody ();
+ if (body != null)
+ return body.GetILAsByteArray ().Length;
+
+ return 0;
+ }
+
+ bool ExecuteFile (MethodInfo entry_point, string filename)
+ {
+ TextWriter stdout = Console.Out;
+ TextWriter stderr = Console.Error;
+ Console.SetOut (TextWriter.Null);
+ Console.SetError (TextWriter.Null);
+ ParameterInfo[] pi = entry_point.GetParameters ();
+ object[] args = pi.Length == 0 ? null : default_args;
+
+ object result = null;
+ try {
+ try {
+ result = entry_point.Invoke (null, args);
+ } finally {
+ Console.SetOut (stdout);
+ Console.SetError (stderr);
+ }
+ } catch (Exception e) {
+ throw new ApplicationException (e.ToString ());
+ }
+
+ if (result is int && (int) result != 0)
+ throw new ApplicationException ("Wrong return code: " + result.ToString ());
+
+ return true;
+ }
+
+ public bool Test (string file)
+ {
+ Assembly assembly = Assembly.LoadFile (file);
+ return ExecuteFile (assembly.EntryPoint, file);
+ }
+ }
+
protected override bool Check(TestCase test)
{
string filename = test.FileName;
return true;
}
- MethodInfo mi = null;
string file = Path.Combine (files_folder, Path.GetFileNameWithoutExtension (filename) + ".exe");
// Enable .dll only tests (no execution required)
return true;
}
- Assembly assembly = null;
- try {
- assembly = Assembly.LoadFile (file);
- mi = assembly.EntryPoint;
- }
- catch (FileNotFoundException) {
- if (File.Exists (file)) {
- Console.WriteLine ("APPDOMAIN LIMIT REACHED");
- }
- }
- catch (Exception e) {
- HandleFailure (filename, TestResult.LoadError, e.ToString ());
- return false;
+ AppDomain domain = null;
+#if !NET_2_1
+ if (safe_execution) {
+ // Create a new AppDomain, with the current directory as the base.
+ AppDomainSetup setupInfo = new AppDomainSetup ();
+ setupInfo.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
+ setupInfo.LoaderOptimization = LoaderOptimization.SingleDomain;
+ domain = AppDomain.CreateDomain (Path.GetFileNameWithoutExtension (file), null, setupInfo);
}
-
- if (!ExecuteFile (mi, file, filename))
- return false;
-
- if (doc_output != null) {
- string ref_file = filename.Replace (".cs", "-ref.xml");
+#endif
+ try {
+ DomainTester tester;
try {
#if !NET_2_1
- XmlComparer.Compare (ref_file, doc_output);
+ if (domain != null)
+ tester = (DomainTester) domain.CreateInstanceAndUnwrap (typeof (PositiveChecker).Assembly.FullName, typeof (DomainTester).FullName);
+ else
#endif
- }
- catch (Exception e) {
- HandleFailure (filename, TestResult.XmlError, e.Message);
+ tester = new DomainTester ();
+
+ if (!tester.Test (file))
+ return false;
+
+ } catch (ApplicationException e) {
+ HandleFailure (filename, TestResult.ExecError, e.Message);
+ return false;
+ } catch (Exception e) {
+ HandleFailure (filename, TestResult.LoadError, e.ToString ());
return false;
}
- } else {
- if (verif_file != null) {
- PositiveTestCase pt = (PositiveTestCase) test;
- pt.VerificationProvider = (PositiveTestCase.VerificationData) verif_data [filename];
- if (!CheckILSize (assembly, pt))
+
+ if (doc_output != null) {
+ string ref_file = filename.Replace (".cs", "-ref.xml");
+ try {
+#if !NET_2_1
+ new XmlComparer ("doc").Compare (ref_file, doc_output);
+#endif
+ } catch (Exception e) {
+ HandleFailure (filename, TestResult.XmlError, e.Message);
return false;
+ }
+ } else {
+ if (verif_file != null) {
+ PositiveTestCase pt = (PositiveTestCase) test;
+ pt.VerificationProvider = (PositiveTestCase.VerificationData) verif_data[filename];
+
+ if (!tester.CheckILSize (pt, this, file))
+ return false;
+ }
+
+ if (filename.StartsWith ("test-debug", StringComparison.OrdinalIgnoreCase)) {
+ MonoSymbolFile mdb_file = MonoSymbolFile.ReadSymbolFile (file + ".mdb");
+ var mdb_xml_file = mdb_file.FileName + ".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)
+ AppDomain.Unload (domain);
}
HandleFailure (filename, TestResult.Success, null);
return true;
}
- protected override TestCase CreateTestCase (string filename, string [] options, string [] deps)
- {
- return new PositiveTestCase (filename, options, deps);
- }
-
- bool CheckILSize (Assembly assembly, PositiveTestCase test)
+ static void ConvertSymbolFileToXml (MonoSymbolFile symbolFile, string xmlFile)
{
- bool success = true;
- Type[] types = assembly.GetTypes ();
- foreach (Type t in types) {
-
- // Skip interfaces
- if (!t.IsClass && !t.IsValueType)
- continue;
+ using (XmlTextWriter writer = new XmlTextWriter (xmlFile, Encoding.UTF8)) {
+ writer.Formatting = Formatting.Indented;
- if (test.VerificationProvider == null) {
- if (!update_verif_file)
- LogFileLine (test.FileName, "Missing IL verification data");
- test.CreateNewTest ();
- }
+ writer.WriteStartDocument ();
- foreach (MemberInfo m in t.GetMembers (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly)) {
- MethodBase mi = m as MethodBase;
- if (mi == null)
- continue;
+ writer.WriteStartElement ("symbols");
- if ((mi.Attributes & (MethodAttributes.PinvokeImpl)) != 0)
- continue;
+ 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));
+ 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 ("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 ();
- success &= test.CompareIL (mi, this);
+ writer.WriteEndElement ();
}
+ writer.WriteEndElement ();
+
+ writer.WriteEndElement ();
+ writer.WriteEndDocument ();
}
+ }
- return success;
+ static string IntToHex (int value)
+ {
+ return "0x" + value.ToString ("x", CultureInfo.InvariantCulture);
}
- bool ExecuteFile (MethodInfo entry_point, string exe_name, string filename)
+ static string BoolToString (bool value)
{
- TextWriter stdout = Console.Out;
- TextWriter stderr = Console.Error;
- Console.SetOut (TextWriter.Null);
- Console.SetError (TextWriter.Null);
- ParameterInfo[] pi = entry_point.GetParameters ();
- object[] args = pi.Length == 0 ? null : default_args;
-
- object result = null;
- try {
- try {
- result = entry_point.Invoke (null, args);
- } finally {
- Console.SetOut (stdout);
- Console.SetError (stderr);
- }
- }
- catch (Exception e) {
- HandleFailure (filename, TestResult.ExecError, e.ToString ());
- return false;
- }
+ return value ? "true" : "false";
+ }
- if (result is int && (int)result != 0) {
- HandleFailure (filename, TestResult.ExecError, "Wrong return code: " + result.ToString ());
- return false;
- }
- return true;
+ protected override TestCase CreateTestCase (string filename, string [] options, string [] deps)
+ {
+ return new PositiveTestCase (filename, options, deps);
}
public void HandleFailure (string file, TestResult status, string 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)
{
LogLine ("Loading verification data from `{0}' ...", file);
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");
ArrayList ld = new ArrayList ();
CompilerError result = CompilerError.Missing;
while (line != null) {
- if (ld.Contains (line)) {
+ if (ld.Contains (line) && result == CompilerError.Expected) {
if (line.IndexOf ("Location of the symbol related to previous") == -1)
return CompilerError.Duplicate;
}
if (check_msg) {
int first = line.IndexOf (':');
int second = line.IndexOf (':', first + 1);
- if (second == -1 || !check_error_line)
+ if (line.IndexOf ("Warning as Error: ", first, StringComparison.Ordinal) > 0) {
+ if (check_error_line) {
+ second = line.IndexOf (':', second + 1);
+ }
+ } else if (second == -1 || !check_error_line) {
second = first;
+ }
string msg = line.Substring (second + 1).TrimEnd ('.').Trim ();
if (msg != expected_message && msg != expected_message.Replace ('`', '\'')) {
}
Checker checker;
+ bool positive;
switch (mode) {
case "neg":
checker = new NegativeChecker (tester, true);
+ positive = false;
break;
case "pos":
string iltest;
GetOption ("il", args, false, out iltest);
checker = new PositiveChecker (tester, iltest);
+ positive = true;
if (iltest != null && GetOption ("update-il", args, false, out temp)) {
((PositiveChecker) checker).UpdateVerificationDataFile = true;
checker.LogFile = temp;
if (GetOption ("verbose", args, false, out temp))
checker.Verbose = true;
+ if (GetOption ("safe-execution", args, false, out temp))
+ checker.SafeExecution = true;
if (GetOption ("compiler-options", args, true, out temp)) {
string[] extra = temp.Split (' ');
checker.ExtraCompilerOptions = extra;
return 1;
}
- string [] files = Directory.GetFiles (".", test_pattern);
- if (files.Length == 0) {
+ var files = new List<string> ();
+ switch (test_pattern) {
+ case "v1":
+ files.AddRange (Directory.GetFiles (".", positive ? "test*.cs" : "cs*.cs"));
+ break;
+ case "v2":
+ files.AddRange (Directory.GetFiles (".", positive ? "gtest*.cs" : "gcs*.cs"));
+ goto case "v1";
+ case "v4":
+ files.AddRange (Directory.GetFiles (".", positive ? "dtest*.cs" : "dcs*.cs"));
+ goto case "v2";
+ default:
+ files.AddRange (Directory.GetFiles (".", test_pattern));
+ break;
+ }
+
+ if (files.Count == 0) {
Console.Error.WriteLine ("No files matching `{0}' found", test_pattern);
return 2;
}
checker.Initialize ();
+/*
+ files.Sort ((a, b) => {
+ if (a.EndsWith ("-lib.cs", StringComparison.Ordinal)) {
+ if (!b.EndsWith ("-lib.cs", StringComparison.Ordinal))
+ return -1;
+ } else if (b.EndsWith ("-lib.cs", StringComparison.Ordinal)) {
+ if (!a.EndsWith ("-lib.cs", StringComparison.Ordinal))
+ return 1;
+ }
+ return a.CompareTo (b);
+ });
+*/
foreach (string s in files) {
string filename = Path.GetFileName (s);
if (Char.IsUpper (filename, 0)) { // Windows hack
static void Usage ()
{
Console.WriteLine (
- "Mono compiler tester, (C) 2008 Novell, Inc.\n" +
+ "Mono compiler tester, (C) 2009 Novell, Inc.\n" +
"compiler-tester -mode:[pos|neg] -compiler:FILE -files:file-list [options]\n" +
" \n" +
" -compiler:FILE The file which will be used to compiler tests\n" +
" -log:FILE Writes any output also to the file\n" +
" -help Lists all options\n" +
" -mode:[pos|neg] Specifies compiler test mode\n" +
+ " -safe-execution Runs compiled executables in separate app-domain\n" +
" -update-il Updates IL-FILE to match compiler output\n" +
" -verbose Prints more details during testing\n"
);