PageRenderTime 46ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/Languages/Ruby/IronRuby.Tests/Helpers.cs

http://github.com/IronLanguages/main
C# | 459 lines | 335 code | 83 blank | 41 comment | 59 complexity | 670372d91baf47b6056ca66173bae3b9 MD5 | raw file
Possible License(s): CPL-1.0, BSD-3-Clause, ISC, GPL-2.0, MPL-2.0-no-copyleft-exception
  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Apache License, Version 2.0. A
  6. * copy of the license can be found in the License.html file at the root of this distribution. If
  7. * you cannot locate the Apache License, Version 2.0, please send an email to
  8. * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
  9. * by the terms of the Apache License, Version 2.0.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. *
  14. * ***************************************************************************/
  15. using System;
  16. using System.Linq;
  17. using System.Collections.Generic;
  18. using System.Diagnostics;
  19. using System.IO;
  20. using System.Dynamic;
  21. using System.Text;
  22. using System.Text.RegularExpressions;
  23. using Microsoft.Scripting;
  24. using Microsoft.Scripting.Generation;
  25. using Microsoft.Scripting.Hosting;
  26. using Microsoft.Scripting.Runtime;
  27. using Microsoft.Scripting.Utils;
  28. using IronRuby.Compiler;
  29. using IronRuby.Runtime;
  30. using IronRuby.Builtins;
  31. namespace IronRuby.Tests {
  32. class ErrorLogRecord {
  33. public string Message;
  34. public SourceSpan Span;
  35. public int Code;
  36. public Severity Severity;
  37. }
  38. class LoggingErrorSink : ErrorCounter {
  39. public List<ErrorLogRecord> Errors = new List<ErrorLogRecord>();
  40. private bool _suppressOutput;
  41. public LoggingErrorSink() {
  42. _suppressOutput = true;
  43. }
  44. public LoggingErrorSink(bool suppressOutput) {
  45. _suppressOutput = suppressOutput;
  46. }
  47. public override void Add(SourceUnit source, string/*!*/ message, SourceSpan span, int errorCode, Severity severity) {
  48. base.CountError(severity);
  49. if (!_suppressOutput) {
  50. Console.Error.WriteLine("{0}({1}:{2}): {3}: RB{4}: {5}", source.Path, span.Start.Line, span.Start.Column,
  51. severity, errorCode, message);
  52. }
  53. ErrorLogRecord info = new ErrorLogRecord();
  54. info.Message = message;
  55. info.Span = span;
  56. info.Code = errorCode;
  57. info.Severity = severity;
  58. Errors.Add(info);
  59. }
  60. }
  61. public class TestHelpers {
  62. public static int GetClassVersion([NotNull]RubyClass/*!*/ cls) {
  63. return cls.Version.Method;
  64. }
  65. }
  66. public partial class Tests {
  67. private readonly Driver/*!*/ _driver;
  68. private readonly Action[]/*!*/ _methods;
  69. public Action[] TestMethods { get { return _methods; } }
  70. public ScriptRuntime Runtime { get { return _driver.TestRuntime.ScriptRuntime; } }
  71. public ScriptEngine Engine { get { return _driver.TestRuntime.Engine; } }
  72. public RubyContext Context { get { return _driver.TestRuntime.Context; } }
  73. public string/*!*/ Eval(bool eval, string/*!*/ code) {
  74. if (eval) {
  75. return code.Replace("#<","eval %q{").Replace("#>", "}");
  76. } else {
  77. return code.Replace("#<", "").Replace("#>", "");
  78. }
  79. }
  80. public void UsingEval(Action<bool>/*!*/ test) {
  81. test(true);
  82. test(false);
  83. }
  84. public void LoadTestLibrary() {
  85. Context.ObjectClass.SetConstant("TestHelpers", Context.GetClass(typeof(TestHelpers)));
  86. }
  87. public void CompilerTest(string/*!*/ code) {
  88. CompilerTest(code, 0, 0);
  89. }
  90. public void CompilerTest(string/*!*/ code, int expectedCompilerWarningCount, int expectedRuntimeWarningCount) {
  91. LoggingErrorSink log = new LoggingErrorSink(true);
  92. CompilerTest(code, log);
  93. Assert(log.ErrorCount == 0 && log.FatalErrorCount == 0, "Compile time error");
  94. Assert(log.WarningCount == expectedCompilerWarningCount, "Wrong number of compile time errors/warnings");
  95. Assert(Context.RuntimeErrorSink.WarningCount == expectedRuntimeWarningCount, "Wrong number of runtime warnings");
  96. }
  97. public void CompilerTest(string/*!*/ code, ErrorSink/*!*/ sink) {
  98. Debug.Assert(code != null && sink != null);
  99. SourceUnit source;
  100. string name = _driver.TestRuntime.TestName;
  101. if (_driver.SaveToAssemblies) {
  102. string path = Path.Combine(Snippets.Shared.SnippetsDirectory, name + ".rb");
  103. Directory.CreateDirectory(Snippets.Shared.SnippetsDirectory);
  104. File.WriteAllText(path, code);
  105. source = _driver.TestRuntime.Context.CreateFileUnit(path);
  106. } else {
  107. source = _driver.TestRuntime.Context.CreateSnippet(code, name + ".rb", SourceCodeKind.File);
  108. }
  109. ScriptCode compiledCode = source.Compile(new RubyCompilerOptions(Context.RubyOptions), sink);
  110. if (compiledCode != null) {
  111. compiledCode.Run(new Scope());
  112. }
  113. }
  114. public List<Tokens> GetRubyTokens(ErrorSink log, string source) {
  115. return GetRubyTokens(log, source, false);
  116. }
  117. public List<Tokens> GetRubyTokens(ErrorSink log, string source, bool dumpTokens) {
  118. return GetRubyTokens(Context, log, source, dumpTokens, false);
  119. }
  120. public static List<Tokens> GetRubyTokens(RubyContext context, ErrorSink log, string source, bool dumpTokens, bool dumpReductions) {
  121. Parser parser = new Parser();
  122. List<Tokens> tokens = new List<Tokens>();
  123. if (dumpTokens) {
  124. parser.Tokenizer.EnableLogging(1, Console.Out);
  125. }
  126. parser.TokenSink = delegate(Tokens token, SourceSpan span) {
  127. tokens.Add(token);
  128. };
  129. #if DEBUG
  130. if (dumpReductions) {
  131. DefaultParserLogger.Attach(parser, Console.Out);
  132. }
  133. #endif
  134. parser.Parse(context.CreateSnippet(source, SourceCodeKind.File), new RubyCompilerOptions(), log);
  135. //tokenizer.Initialize(SourceUnit.CreateSnippet(RB, source));
  136. //List<Tokens> tokens = new List<Tokens>();
  137. //Tokens token;
  138. //while ((token = tokenizer.GetNextToken()) != Tokens.EOF) {
  139. // tokens.Add(token);
  140. //}
  141. return tokens;
  142. }
  143. #if !SILVERLIGHT
  144. private static int domainId = 0;
  145. private static AppDomain CreateDomain() {
  146. return AppDomain.CreateDomain("RemoteScripts" + domainId++);
  147. }
  148. #endif
  149. [Flags]
  150. enum OutputFlags {
  151. None = 0,
  152. Raw = 1,
  153. Match = 2
  154. }
  155. [DebuggerHiddenAttribute]
  156. private void AssertEquals<T>(object actual, T expected)
  157. where T : IEquatable<T> {
  158. Assert(actual is T && ((T)actual).Equals(expected));
  159. }
  160. [DebuggerHiddenAttribute]
  161. private void XAssertOutput(Action f, string expectedOutput) {
  162. Driver.ColorWrite(ConsoleColor.Yellow, "Assertion check skipped.");
  163. // just run the code
  164. f();
  165. }
  166. private void XAssertOutput(Action f, string expectedOutput, OutputFlags flags) {
  167. Driver.ColorWrite(ConsoleColor.Yellow, "Assertion check skipped.");
  168. // just run the code
  169. f();
  170. }
  171. [DebuggerHiddenAttribute]
  172. private void TestOutput(string code, string expectedOutput) {
  173. AssertOutput(() => CompilerTest(code), expectedOutput);
  174. }
  175. [DebuggerHiddenAttribute]
  176. private void XTestOutput(string code, string expectedOutput) {
  177. XAssertOutput(() => CompilerTest(code), expectedOutput);
  178. }
  179. [DebuggerHiddenAttribute]
  180. private void TestOutputWithEval(string code, string expectedOutput) {
  181. UsingEval((eval) => AssertOutput(() => CompilerTest(Eval(eval, code)), expectedOutput));
  182. }
  183. [DebuggerHiddenAttribute]
  184. private void XTestOutputWithEval(string code, string expectedOutput) {
  185. UsingEval((eval) => XAssertOutput(() => CompilerTest(Eval(eval, code)), expectedOutput));
  186. }
  187. [DebuggerHiddenAttribute]
  188. private void AssertOutput(Action f, string expectedOutput) {
  189. AssertOutput(f, expectedOutput, OutputFlags.None);
  190. }
  191. [DebuggerHiddenAttribute]
  192. private void AssertOutput(Action f, string expectedOutput, OutputFlags flags) {
  193. var error = CompareOutput(f, expectedOutput, flags);
  194. if (error != null) {
  195. Assert(false, error);
  196. }
  197. }
  198. private string CompareOutput(Action f, string expectedOutput, OutputFlags flags) {
  199. #if !SILVERLIGHT
  200. StringBuilder builder = new StringBuilder();
  201. using (StringWriter output = new StringWriter(builder)) {
  202. RedirectOutput(output, f);
  203. }
  204. string actualOutput = builder.ToString();
  205. if ((flags & OutputFlags.Raw) == 0) {
  206. actualOutput = actualOutput.Trim().Replace("\r", "");
  207. expectedOutput = expectedOutput.Trim().Replace("\r", "");
  208. }
  209. if ((flags & OutputFlags.Match) != 0) {
  210. Regex regex = new Regex(Regex.Escape(expectedOutput).Replace("\\*", ".*").Replace("\\?", "."));
  211. if (!regex.IsMatch(actualOutput)) {
  212. return String.Format("Unexpected output: \n\n'{0}'.", actualOutput);
  213. }
  214. } else {
  215. int i = 0;
  216. while (i < actualOutput.Length && i < expectedOutput.Length && actualOutput[i] == expectedOutput[i]) i++;
  217. if (actualOutput != expectedOutput) {
  218. return String.Format("Unexpected output: \n\n'{0}'.\n\nFirst difference ({1}):\nactual = '{2}'\nexpected = '{3}'\n",
  219. Escape(builder), i,
  220. (i < actualOutput.Length ? Escape(actualOutput[i]) : "<end>"),
  221. (i < expectedOutput.Length ? Escape(expectedOutput[i]) : "<end>")
  222. );
  223. }
  224. }
  225. #endif
  226. return null;
  227. }
  228. private static string Escape(char ch) {
  229. return ch.ToString().Replace("\r", "\\r").Replace("\n", "\\n").Replace("\t", "\\t");
  230. }
  231. private static string Escape(string str) {
  232. return str.Replace("\r", "\\r").Replace("\n", "\\n").Replace("\t", "\\t");
  233. }
  234. private static string Escape(StringBuilder str) {
  235. return str.Replace("\r", "\\r").Replace("\n", "\\n").Replace("\t", "\\t").ToString();
  236. }
  237. [DebuggerHidden]
  238. private void RedirectOutput(TextWriter/*!*/ output, Action f) {
  239. // TODO:
  240. if (Runtime == null) {
  241. f();
  242. } else {
  243. MemoryStream stream = new MemoryStream();
  244. Runtime.IO.SetOutput(stream, StringUtils.DefaultEncoding);
  245. Runtime.IO.SetErrorOutput(Console.OpenStandardError(), Console.Error);
  246. try {
  247. f();
  248. } finally {
  249. output.Write(StringUtils.DefaultEncoding.GetString(stream.ToArray()));
  250. Runtime.IO.RedirectToConsole();
  251. }
  252. }
  253. }
  254. [DebuggerHidden]
  255. public void AssertExceptionThrown<T>(Action f) where T : Exception {
  256. AssertExceptionThrown<T>(f, null);
  257. }
  258. [DebuggerHidden]
  259. public void AssertExceptionThrown<T>(Action f, Predicate<T> condition) where T : Exception {
  260. try {
  261. RedirectOutput(TextWriter.Null, f);
  262. } catch (T e) {
  263. if (condition != null) {
  264. Assert(condition(e), "Exception has been thrown but the condition doesn't hold");
  265. }
  266. return;
  267. } catch (Exception e) {
  268. Assert(false, "Expecting exception '" + typeof(T) + "', got '" + e.GetType() + "'.");
  269. }
  270. Assert(false, "Expecting exception '" + typeof(T) + "'.");
  271. }
  272. /// <summary>
  273. /// Asserts two values are equal
  274. /// </summary>
  275. [DebuggerHidden]
  276. public void AreEqual(object x, object y) {
  277. if (x == null && y == null) return;
  278. Assert(x != null && x.Equals(y), String.Format("values aren't equal: {0} and {1}", x, y));
  279. }
  280. /// <summary>
  281. /// Asserts two sets are equal.
  282. /// </summary>
  283. public void AreSetsEqual<T>(IEnumerable<T> x, IEnumerable<T> y) {
  284. if (x == null && y == null) return;
  285. var set = new HashSet<T>(x);
  286. set.SymmetricExceptWith(y);
  287. Assert(set.Count == 0);
  288. }
  289. /// <summary>
  290. /// Asserts two bags (lists with insignificant order) are equal.
  291. /// </summary>
  292. public void AreBagsEqual<T>(IEnumerable<T> x, IEnumerable<T> y) {
  293. if (x == null && y == null) return;
  294. var bag = new HashSet<Key<T, int>>(GroupWithCount(x));
  295. bag.SymmetricExceptWith(GroupWithCount(y));
  296. Assert(bag.Count == 0);
  297. }
  298. private IEnumerable<Key<T, int>> GroupWithCount<T>(IEnumerable<T> sequence) {
  299. Dictionary<T, int> groups = new Dictionary<T, int>();
  300. foreach (var item in sequence) {
  301. int count;
  302. if (groups.TryGetValue(item, out count)) {
  303. count++;
  304. } else {
  305. count = 1;
  306. }
  307. groups[item] = count;
  308. }
  309. foreach (var entry in groups) {
  310. yield return Key.Create(entry.Key, entry.Value);
  311. }
  312. }
  313. /// <summary>
  314. /// Asserts an condition it true
  315. /// </summary>
  316. [DebuggerHidden]
  317. public void Assert(bool condition, string msg) {
  318. if (!condition) {
  319. AssertBreak();
  320. _driver.AssertionFailed(msg);
  321. }
  322. }
  323. [DebuggerHidden]
  324. public void Assert(bool condition) {
  325. Assert(condition, "Assertion failed");
  326. }
  327. internal void AssertBreak() {
  328. if (Debugger.IsAttached) {
  329. Debugger.Break();
  330. }
  331. }
  332. #region Parser, Tokenizer
  333. public object TestCategorizer(ScriptEngine engine, object state, string/*!*/ src, params TokenInfo[]/*!*/ expected) {
  334. return TestCategorizer(engine, state, src, SourceLocation.MinValue, expected);
  335. }
  336. public object TestCategorizer(ScriptEngine engine, object state, string/*!*/ src, SourceLocation initial, params TokenInfo[]/*!*/ expected) {
  337. TokenCategorizer categorizer = engine.GetService<TokenCategorizer>();
  338. categorizer.Initialize(state, engine.CreateScriptSourceFromString(src), initial);
  339. IEnumerable<TokenInfo> actual = categorizer.ReadTokens(Int32.MaxValue);
  340. int i = 0;
  341. foreach (TokenInfo info in actual) {
  342. Assert(i < expected.Length);
  343. if (!info.Equals(expected[i])) {
  344. Assert(false);
  345. }
  346. i++;
  347. }
  348. Assert(i == expected.Length);
  349. TokenInfo t = categorizer.ReadToken();
  350. SourceLocation end = expected[expected.Length - 1].SourceSpan.End;
  351. Assert(t.Equals(new TokenInfo(new SourceSpan(end, end), TokenCategory.EndOfStream, TokenTriggers.None)));
  352. return categorizer.CurrentState;
  353. }
  354. #endregion
  355. #region Bugs
  356. // Helpers for tests which are currently failing. Using these helpers will ensure that when the bug is fixed,
  357. // you are forced to update the test case. In the meantime, it makes sure that the test can atleast be executed
  358. // and documents the incorrect result
  359. [DebuggerHidden]
  360. internal void AreEqualBug(object x, object y, object buggyResult) {
  361. // Once the bug is fixed, the result should be "y".
  362. AreEqual(x, buggyResult);
  363. }
  364. [DebuggerHidden]
  365. internal void AreEqualBug<T>(Action f, object y) where T : Exception {
  366. // Once the bug is fixed, the result should be "y" and no exception should be thrown
  367. AssertExceptionThrown<T>(f);
  368. }
  369. [DebuggerHidden]
  370. internal void AssertExceptionThrownBug<T>(Action f, object buggyResult) where T : Exception {
  371. // f should throw an exception once the bug is fixed
  372. f();
  373. }
  374. #endregion
  375. }
  376. }