Test262Test.cs 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Reflection;
  6. using System.Text.RegularExpressions;
  7. using Esprima;
  8. using Jint.Runtime;
  9. using Newtonsoft.Json.Linq;
  10. using Xunit;
  11. namespace Jint.Tests.Test262
  12. {
  13. public abstract class Test262Test
  14. {
  15. private static readonly string[] Sources;
  16. private static readonly string BasePath;
  17. private static readonly TimeZoneInfo _pacificTimeZone;
  18. private static readonly Dictionary<string, string> _skipReasons =
  19. new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
  20. static Test262Test()
  21. {
  22. //NOTE: The Date tests in test262 assume the local timezone is Pacific Standard Time
  23. _pacificTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time");
  24. var assemblyPath = new Uri(typeof(Test262Test).GetTypeInfo().Assembly.CodeBase).LocalPath;
  25. var assemblyDirectory = new FileInfo(assemblyPath).Directory;
  26. BasePath = assemblyDirectory.Parent.Parent.Parent.FullName;
  27. string[] files =
  28. {
  29. @"harness\sta.js",
  30. @"harness\assert.js",
  31. @"harness\propertyHelper.js",
  32. @"harness\compareArray.js",
  33. @"harness\decimalToHexString.js",
  34. };
  35. Sources = new string[files.Length];
  36. for (var i = 0; i < files.Length; i++)
  37. {
  38. Sources[i] = File.ReadAllText(Path.Combine(BasePath, files[i]));
  39. }
  40. var content = File.ReadAllText(Path.Combine(BasePath, "test/skipped.json"));
  41. var doc = JArray.Parse(content);
  42. foreach (var entry in doc.Values<JObject>())
  43. {
  44. _skipReasons[entry["source"].Value<string>()] = entry["reason"].Value<string>();
  45. }
  46. }
  47. protected void RunTestCode(string code, bool strict)
  48. {
  49. var engine = new Engine(cfg => cfg
  50. .LocalTimeZone(_pacificTimeZone)
  51. .Strict(strict));
  52. for (int i = 0; i < Sources.Length; ++i)
  53. {
  54. engine.Execute(Sources[i]);
  55. }
  56. string lastError = null;
  57. bool negative = code.IndexOf("negative:", StringComparison.Ordinal) > -1;
  58. try
  59. {
  60. engine.Execute(code);
  61. }
  62. catch (JavaScriptException j)
  63. {
  64. lastError = TypeConverter.ToString(j.Error);
  65. }
  66. catch (Exception e)
  67. {
  68. lastError = e.ToString();
  69. }
  70. if (negative)
  71. {
  72. Assert.NotNull(lastError);
  73. }
  74. else
  75. {
  76. Assert.Null(lastError);
  77. }
  78. }
  79. protected void RunTestInternal(SourceFile sourceFile)
  80. {
  81. RunTestCode(sourceFile.Code);
  82. }
  83. private void RunTestCode(string code)
  84. {
  85. if (code.IndexOf("onlyStrict", StringComparison.Ordinal) < 0)
  86. {
  87. RunTestCode(code, strict: false);
  88. }
  89. if (code.IndexOf("noStrict", StringComparison.Ordinal) < 0)
  90. {
  91. RunTestCode(code, strict: true);
  92. }
  93. }
  94. public static IEnumerable<object[]> SourceFiles(string pathPrefix, bool skipped)
  95. {
  96. var results = new List<object[]>();
  97. var fixturesPath = Path.Combine(BasePath, "test");
  98. var searchPath = Path.Combine(fixturesPath, pathPrefix);
  99. var files = Directory.GetFiles(searchPath, "*", SearchOption.AllDirectories);
  100. foreach (var file in files)
  101. {
  102. var name = file.Substring(fixturesPath.Length + 1).Replace("\\", "/");
  103. bool skip = _skipReasons.TryGetValue(name, out var reason);
  104. var code = skip ? "" : File.ReadAllText(file);
  105. var features = Regex.Match(code, "features: \\[(.+?)\\]");
  106. if (features.Success)
  107. {
  108. var items = features.Groups[1].Captures[0].Value.Split(",");
  109. foreach (var item in items.Select(x => x.Trim()))
  110. {
  111. switch (item)
  112. {
  113. // TODO implement
  114. case "cross-realm":
  115. skip = true;
  116. reason = "realms not implemented";
  117. break;
  118. case "tail-call-optimization":
  119. skip = true;
  120. reason = "tail-calls not implemented";
  121. break;
  122. case "class":
  123. skip = true;
  124. reason = "class keyword not implemented";
  125. break;
  126. case "Symbol.species":
  127. skip = true;
  128. reason = "Symbol.species not implemented";
  129. break;
  130. case "Proxy":
  131. skip = true;
  132. reason = "Proxies not implemented";
  133. break;
  134. case "object-spread":
  135. skip = true;
  136. reason = "Object spread not implemented";
  137. break;
  138. case "Symbol.unscopables":
  139. skip = true;
  140. reason = "Symbol.unscopables not implemented";
  141. break;
  142. case "Symbol.match":
  143. skip = true;
  144. reason = "Symbol.match not implemented";
  145. break;
  146. case "Symbol.matchAll":
  147. skip = true;
  148. reason = "Symbol.matchAll not implemented";
  149. break;
  150. case "Symbol.split":
  151. skip = true;
  152. reason = "Symbol.split not implemented";
  153. break;
  154. case "String.prototype.matchAll":
  155. skip = true;
  156. reason = "proposal stage";
  157. break;
  158. case "Symbol.search":
  159. skip = true;
  160. reason = "Symbol.search not implemented";
  161. break;
  162. case "Symbol.replace":
  163. skip = true;
  164. reason = "Symbol.replace not implemented";
  165. break;
  166. case "Symbol.toStringTag":
  167. skip = true;
  168. reason = "Symbol.toStringTag not implemented";
  169. break;
  170. case "BigInt":
  171. skip = true;
  172. reason = "BigInt not implemented";
  173. break;
  174. case "generators":
  175. skip = true;
  176. reason = "generators not implemented";
  177. break;
  178. case "destructuring-binding":
  179. skip = true;
  180. reason = "destructuring-binding not implemented";
  181. break;
  182. case "let":
  183. skip = true;
  184. reason = "let not implemented";
  185. break;
  186. case "async-functions":
  187. skip = true;
  188. reason = "async-functions not implemented";
  189. break;
  190. }
  191. }
  192. }
  193. if (name.StartsWith("built-ins/String/raw/"))
  194. {
  195. skip = true;
  196. reason = "requires template string";
  197. }
  198. if (code.IndexOf("SpecialCasing.txt") > -1)
  199. {
  200. skip = true;
  201. reason = "SpecialCasing.txt not implemented";
  202. }
  203. var sourceFile = new SourceFile(
  204. name,
  205. file,
  206. skip,
  207. reason,
  208. code);
  209. if (skipped == sourceFile.Skip)
  210. {
  211. results.Add(new object[]
  212. {
  213. sourceFile
  214. });
  215. }
  216. }
  217. return results;
  218. }
  219. }
  220. public class SourceFile
  221. {
  222. public SourceFile(
  223. string source,
  224. string fullPath,
  225. bool skip,
  226. string reason,
  227. string code)
  228. {
  229. Skip = skip;
  230. Source = source;
  231. Reason = reason;
  232. FullPath = fullPath;
  233. Code = code;
  234. }
  235. public string Source { get; }
  236. public bool Skip { get; }
  237. public string Reason { get; }
  238. public string FullPath { get; }
  239. public string Code { get; }
  240. public override string ToString()
  241. {
  242. return Source;
  243. }
  244. }
  245. }