EngineTests.cs 32 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157
  1. using System;
  2. using System.Globalization;
  3. using System.IO;
  4. using System.Reflection;
  5. using System.Threading;
  6. using Jint.Native.Number;
  7. using Jint.Parser;
  8. using Jint.Parser.Ast;
  9. using Jint.Runtime;
  10. using Xunit;
  11. using Xunit.Extensions;
  12. using System.Net;
  13. namespace Jint.Tests.Runtime
  14. {
  15. public class EngineTests : IDisposable
  16. {
  17. private readonly Engine _engine;
  18. public EngineTests()
  19. {
  20. _engine = new Engine()
  21. .SetValue("log", new Action<object>(Console.WriteLine))
  22. .SetValue("assert", new Action<bool>(Assert.True))
  23. ;
  24. }
  25. void IDisposable.Dispose()
  26. {
  27. }
  28. private void RunTest(string source)
  29. {
  30. _engine.Execute(source);
  31. }
  32. [Theory]
  33. [InlineData("Scratch.js")]
  34. public void ShouldInterpretScriptFile(string file)
  35. {
  36. const string prefix = "Jint.Tests.Runtime.Scripts.";
  37. var assembly = Assembly.GetExecutingAssembly();
  38. var scriptPath = prefix + file;
  39. using (var stream = assembly.GetManifestResourceStream(scriptPath))
  40. if (stream != null)
  41. using (var sr = new StreamReader(stream))
  42. {
  43. var source = sr.ReadToEnd();
  44. RunTest(source);
  45. }
  46. }
  47. [Theory]
  48. [InlineData(42d, "42")]
  49. [InlineData("Hello", "'Hello'")]
  50. public void ShouldInterpretLiterals(object expected, string source)
  51. {
  52. var engine = new Engine();
  53. var result = engine.Execute(source).GetCompletionValue().ToObject();
  54. Assert.Equal(expected, result);
  55. }
  56. [Fact]
  57. public void ShouldInterpretVariableDeclaration()
  58. {
  59. var engine = new Engine();
  60. var result = engine
  61. .Execute("var foo = 'bar'; foo;")
  62. .GetCompletionValue()
  63. .ToObject();
  64. Assert.Equal("bar", result);
  65. }
  66. [Theory]
  67. [InlineData(4d, "1 + 3")]
  68. [InlineData(-2d, "1 - 3")]
  69. [InlineData(3d, "1 * 3")]
  70. [InlineData(2d, "6 / 3")]
  71. [InlineData(9d, "15 & 9")]
  72. [InlineData(15d, "15 | 9")]
  73. [InlineData(6d, "15 ^ 9")]
  74. [InlineData(36d, "9 << 2")]
  75. [InlineData(2d, "9 >> 2")]
  76. [InlineData(4d, "19 >>> 2")]
  77. public void ShouldInterpretBinaryExpression(object expected, string source)
  78. {
  79. var engine = new Engine();
  80. var result = engine.Execute(source).GetCompletionValue().ToObject();
  81. Assert.Equal(expected, result);
  82. }
  83. [Fact]
  84. public void ShouldEvaluateHasOwnProperty()
  85. {
  86. RunTest(@"
  87. var x = {};
  88. x.Bar = 42;
  89. assert(x.hasOwnProperty('Bar'));
  90. ");
  91. }
  92. [Fact]
  93. public void FunctionConstructorsShouldCreateNewObjects()
  94. {
  95. RunTest(@"
  96. var Vehicle = function () {};
  97. var vehicle = new Vehicle();
  98. assert(vehicle != undefined);
  99. ");
  100. }
  101. [Fact]
  102. public void NewObjectsInheritFunctionConstructorProperties()
  103. {
  104. RunTest(@"
  105. var Vehicle = function () {};
  106. var vehicle = new Vehicle();
  107. Vehicle.prototype.wheelCount = 4;
  108. assert(vehicle.wheelCount == 4);
  109. assert((new Vehicle()).wheelCount == 4);
  110. ");
  111. }
  112. [Fact]
  113. public void PrototypeFunctionIsInherited()
  114. {
  115. RunTest(@"
  116. function Body(mass){
  117. this.mass = mass;
  118. }
  119. Body.prototype.offsetMass = function(dm) {
  120. this.mass += dm;
  121. return this;
  122. }
  123. var b = new Body(36);
  124. b.offsetMass(6);
  125. assert(b.mass == 42);
  126. ");
  127. }
  128. [Fact]
  129. public void FunctionConstructorCall()
  130. {
  131. RunTest(@"
  132. function Body(mass){
  133. this.mass = mass;
  134. }
  135. var john = new Body(36);
  136. assert(john.mass == 36);
  137. ");
  138. }
  139. [Fact]
  140. public void NewObjectsShouldUsePrivateProperties()
  141. {
  142. RunTest(@"
  143. var Vehicle = function (color) {
  144. this.color = color;
  145. };
  146. var vehicle = new Vehicle('tan');
  147. assert(vehicle.color == 'tan');
  148. ");
  149. }
  150. [Fact]
  151. public void FunctionConstructorsShouldDefinePrototypeChain()
  152. {
  153. RunTest(@"
  154. function Vehicle() {};
  155. var vehicle = new Vehicle();
  156. assert(vehicle.hasOwnProperty('constructor') == false);
  157. ");
  158. }
  159. [Fact]
  160. public void NewObjectsConstructorIsObject()
  161. {
  162. RunTest(@"
  163. var o = new Object();
  164. assert(o.constructor == Object);
  165. ");
  166. }
  167. [Fact]
  168. public void NewObjectsIntanceOfConstructorObject()
  169. {
  170. RunTest(@"
  171. var o = new Object();
  172. assert(o instanceof Object);
  173. ");
  174. }
  175. [Fact]
  176. public void NewObjectsConstructorShouldBeConstructorObject()
  177. {
  178. RunTest(@"
  179. var Vehicle = function () {};
  180. var vehicle = new Vehicle();
  181. assert(vehicle.constructor == Vehicle);
  182. ");
  183. }
  184. [Fact]
  185. public void NewObjectsIntanceOfConstructorFunction()
  186. {
  187. RunTest(@"
  188. var Vehicle = function () {};
  189. var vehicle = new Vehicle();
  190. assert(vehicle instanceof Vehicle);
  191. ");
  192. }
  193. [Fact]
  194. public void ShouldEvaluateForLoops()
  195. {
  196. RunTest(@"
  197. var foo = 0;
  198. for (var i = 0; i < 5; i++) {
  199. foo += i;
  200. }
  201. assert(foo == 10);
  202. ");
  203. }
  204. [Fact]
  205. public void ShouldEvaluateRecursiveFunctions()
  206. {
  207. RunTest(@"
  208. function fib(n) {
  209. if (n < 2) {
  210. return n;
  211. }
  212. return fib(n - 1) + fib(n - 2);
  213. }
  214. var result = fib(6);
  215. assert(result == 8);
  216. ");
  217. }
  218. [Fact]
  219. public void ShouldAccessObjectProperties()
  220. {
  221. RunTest(@"
  222. var o = {};
  223. o.Foo = 'bar';
  224. o.Baz = 42;
  225. o.Blah = o.Foo + o.Baz;
  226. assert(o.Blah == 'bar42');
  227. ");
  228. }
  229. [Fact]
  230. public void ShouldConstructArray()
  231. {
  232. RunTest(@"
  233. var o = [];
  234. assert(o.length == 0);
  235. ");
  236. }
  237. [Fact]
  238. public void ArrayPushShouldIncrementLength()
  239. {
  240. RunTest(@"
  241. var o = [];
  242. o.push(1);
  243. assert(o.length == 1);
  244. ");
  245. }
  246. [Fact]
  247. public void ArrayFunctionInitializesLength()
  248. {
  249. RunTest(@"
  250. assert(Array(3).length == 3);
  251. assert(Array('3').length == 1);
  252. ");
  253. }
  254. [Fact]
  255. public void ArrayIndexerIsAssigned()
  256. {
  257. RunTest(@"
  258. var n = 8;
  259. var o = Array(n);
  260. for (var i = 0; i < n; i++) o[i] = i;
  261. assert(o[0] == 0);
  262. assert(o[7] == 7);
  263. ");
  264. }
  265. [Fact]
  266. public void ArrayPopShouldDecrementLength()
  267. {
  268. RunTest(@"
  269. var o = [42, 'foo'];
  270. var pop = o.pop();
  271. assert(o.length == 1);
  272. assert(pop == 'foo');
  273. ");
  274. }
  275. [Fact]
  276. public void ArrayConstructor()
  277. {
  278. RunTest(@"
  279. var o = [];
  280. assert(o.constructor == Array);
  281. ");
  282. }
  283. [Fact]
  284. public void DateConstructor()
  285. {
  286. RunTest(@"
  287. var o = new Date();
  288. assert(o.constructor == Date);
  289. assert(o.hasOwnProperty('constructor') == false);
  290. ");
  291. }
  292. [Fact]
  293. public void ShouldConvertDateToNumber()
  294. {
  295. RunTest(@"
  296. assert(Number(new Date(0)) === 0);
  297. ");
  298. }
  299. [Fact]
  300. public void DatePrimitiveValueShouldBeNaN()
  301. {
  302. RunTest(@"
  303. assert(isNaN(Date.prototype.valueOf()));
  304. ");
  305. }
  306. [Fact]
  307. public void MathObjectIsDefined()
  308. {
  309. RunTest(@"
  310. var o = Math.abs(-1)
  311. assert(o == 1);
  312. ");
  313. }
  314. [Fact]
  315. public void VoidShouldReturnUndefined()
  316. {
  317. RunTest(@"
  318. assert(void 0 === undefined);
  319. var x = '1';
  320. assert(void x === undefined);
  321. x = 'x';
  322. assert (isNaN(void x) === true);
  323. x = new String('-1');
  324. assert (void x === undefined);
  325. ");
  326. }
  327. [Fact]
  328. public void TypeofObjectShouldReturnString()
  329. {
  330. RunTest(@"
  331. assert(typeof x === 'undefined');
  332. assert(typeof 0 === 'number');
  333. var x = 0;
  334. assert (typeof x === 'number');
  335. var x = new Object();
  336. assert (typeof x === 'object');
  337. ");
  338. }
  339. [Fact]
  340. public void MathAbsReturnsAbsolute()
  341. {
  342. RunTest(@"
  343. assert(1 == Math.abs(-1));
  344. ");
  345. }
  346. [Fact]
  347. public void NaNIsNan()
  348. {
  349. RunTest(@"
  350. var x = NaN;
  351. assert(isNaN(NaN));
  352. assert(isNaN(Math.abs(x)));
  353. ");
  354. }
  355. [Fact]
  356. public void ToNumberHandlesStringObject()
  357. {
  358. RunTest(@"
  359. x = new String('1');
  360. x *= undefined;
  361. assert(isNaN(x));
  362. ");
  363. }
  364. [Fact]
  365. public void FunctionScopesAreChained()
  366. {
  367. RunTest(@"
  368. var x = 0;
  369. function f1(){
  370. function f2(){
  371. return x;
  372. };
  373. return f2();
  374. var x = 1;
  375. }
  376. assert(f1() === undefined);
  377. ");
  378. }
  379. [Fact]
  380. public void EvalFunctionParseAndExecuteCode()
  381. {
  382. RunTest(@"
  383. var x = 0;
  384. eval('assert(x == 0)');
  385. ");
  386. }
  387. [Fact]
  388. public void ForInStatement()
  389. {
  390. RunTest(@"
  391. var x, y, str = '';
  392. for(var z in this) {
  393. str += z;
  394. }
  395. assert(str == 'xystrz');
  396. ");
  397. }
  398. [Fact]
  399. public void WithStatement()
  400. {
  401. RunTest(@"
  402. with (Math) {
  403. assert(cos(0) == 1);
  404. }
  405. ");
  406. }
  407. [Fact]
  408. public void ObjectExpression()
  409. {
  410. RunTest(@"
  411. var o = { x: 1 };
  412. assert(o.x == 1);
  413. ");
  414. }
  415. [Fact]
  416. public void StringFunctionCreatesString()
  417. {
  418. RunTest(@"
  419. assert(String(NaN) === 'NaN');
  420. ");
  421. }
  422. [Fact]
  423. public void ScopeChainInWithStatement()
  424. {
  425. RunTest(@"
  426. var x = 0;
  427. var myObj = {x : 'obj'};
  428. function f1(){
  429. var x = 1;
  430. function f2(){
  431. with(myObj){
  432. return x;
  433. }
  434. };
  435. return f2();
  436. }
  437. assert(f1() === 'obj');
  438. ");
  439. }
  440. [Fact]
  441. public void TryCatchBlockStatement()
  442. {
  443. RunTest(@"
  444. var x, y, z;
  445. try {
  446. x = 1;
  447. throw new TypeError();
  448. x = 2;
  449. }
  450. catch(e) {
  451. assert(x == 1);
  452. assert(e instanceof TypeError);
  453. y = 1;
  454. }
  455. finally {
  456. assert(x == 1);
  457. z = 1;
  458. }
  459. assert(x == 1);
  460. assert(y == 1);
  461. assert(z == 1);
  462. ");
  463. }
  464. [Fact]
  465. public void FunctionsCanBeAssigned()
  466. {
  467. RunTest(@"
  468. var sin = Math.sin;
  469. assert(sin(0) == 0);
  470. ");
  471. }
  472. [Fact]
  473. public void FunctionArgumentsIsDefined()
  474. {
  475. RunTest(@"
  476. function f() {
  477. assert(arguments.length > 0);
  478. }
  479. f(42);
  480. ");
  481. }
  482. [Fact]
  483. public void PrimitiveValueFunctions()
  484. {
  485. RunTest(@"
  486. var s = (1).toString();
  487. assert(s == '1');
  488. ");
  489. }
  490. [Theory]
  491. [InlineData(true, "'ab' == 'a' + 'b'")]
  492. public void OperatorsPrecedence(object expected, string source)
  493. {
  494. var engine = new Engine();
  495. var result = engine.Execute(source).GetCompletionValue().ToObject();
  496. Assert.Equal(expected, result);
  497. }
  498. [Fact]
  499. public void FunctionPrototypeShouldHaveApplyMethod()
  500. {
  501. RunTest(@"
  502. var numbers = [5, 6, 2, 3, 7];
  503. var max = Math.max.apply(null, numbers);
  504. assert(max == 7);
  505. ");
  506. }
  507. [Theory]
  508. [InlineData(double.NaN, "parseInt(NaN)")]
  509. [InlineData(double.NaN, "parseInt(null)")]
  510. [InlineData(double.NaN, "parseInt(undefined)")]
  511. [InlineData(double.NaN, "parseInt(new Boolean(true))")]
  512. [InlineData(double.NaN, "parseInt(Infinity)")]
  513. [InlineData(-1d, "parseInt(-1)")]
  514. [InlineData(-1d, "parseInt('-1')")]
  515. public void ShouldEvaluateParseInt(object expected, string source)
  516. {
  517. var engine = new Engine();
  518. var result = engine.Execute(source).GetCompletionValue().ToObject();
  519. Assert.Equal(expected, result);
  520. }
  521. [Fact]
  522. public void ShouldNotExecuteDebuggerStatement()
  523. {
  524. new Engine().Execute("debugger");
  525. }
  526. [Fact]
  527. public void ShouldThrowStatementCountOverflow()
  528. {
  529. Assert.Throws<StatementsCountOverflowException>(
  530. () => new Engine(cfg => cfg.MaxStatements(100)).Execute("while(true);")
  531. );
  532. }
  533. [Fact]
  534. public void ShouldThrowTimeout()
  535. {
  536. Assert.Throws<TimeoutException>(
  537. () => new Engine(cfg => cfg.TimeoutInterval(new TimeSpan(0, 0, 0, 0, 500))).Execute("while(true);")
  538. );
  539. }
  540. [Fact]
  541. public void CanDiscardRecursion()
  542. {
  543. var script = @"var factorial = function(n) {
  544. if (n>1) {
  545. return n * factorial(n - 1);
  546. }
  547. };
  548. var result = factorial(500);
  549. ";
  550. Assert.Throws<RecursionDepthOverflowException>(
  551. () => new Engine(cfg => cfg.LimitRecursion()).Execute(script)
  552. );
  553. }
  554. [Fact]
  555. public void ShouldDiscardHiddenRecursion()
  556. {
  557. var script = @"var renamedFunc;
  558. var exec = function(callback) {
  559. renamedFunc = callback;
  560. callback();
  561. };
  562. var result = exec(function() {
  563. renamedFunc();
  564. });
  565. ";
  566. Assert.Throws<RecursionDepthOverflowException>(
  567. () => new Engine(cfg => cfg.LimitRecursion()).Execute(script)
  568. );
  569. }
  570. [Fact]
  571. public void ShouldRecognizeAndDiscardChainedRecursion()
  572. {
  573. var script = @" var funcRoot, funcA, funcB, funcC, funcD;
  574. var funcRoot = function() {
  575. funcA();
  576. };
  577. var funcA = function() {
  578. funcB();
  579. };
  580. var funcB = function() {
  581. funcC();
  582. };
  583. var funcC = function() {
  584. funcD();
  585. };
  586. var funcD = function() {
  587. funcRoot();
  588. };
  589. funcRoot();
  590. ";
  591. Assert.Throws<RecursionDepthOverflowException>(
  592. () => new Engine(cfg => cfg.LimitRecursion()).Execute(script)
  593. );
  594. }
  595. [Fact]
  596. public void ShouldProvideCallChainWhenDiscardRecursion()
  597. {
  598. var script = @" var funcRoot, funcA, funcB, funcC, funcD;
  599. var funcRoot = function() {
  600. funcA();
  601. };
  602. var funcA = function() {
  603. funcB();
  604. };
  605. var funcB = function() {
  606. funcC();
  607. };
  608. var funcC = function() {
  609. funcD();
  610. };
  611. var funcD = function() {
  612. funcRoot();
  613. };
  614. funcRoot();
  615. ";
  616. RecursionDepthOverflowException exception = null;
  617. try
  618. {
  619. new Engine(cfg => cfg.LimitRecursion()).Execute(script);
  620. }
  621. catch (RecursionDepthOverflowException ex)
  622. {
  623. exception = ex;
  624. }
  625. Assert.NotNull(exception);
  626. Assert.Equal("funcRoot->funcA->funcB->funcC->funcD", exception.CallChain);
  627. Assert.Equal("funcRoot", exception.CallExpressionReference);
  628. }
  629. [Fact]
  630. public void ShouldAllowShallowRecursion()
  631. {
  632. var script = @"var factorial = function(n) {
  633. if (n>1) {
  634. return n * factorial(n - 1);
  635. }
  636. };
  637. var result = factorial(8);
  638. ";
  639. new Engine(cfg => cfg.LimitRecursion(20)).Execute(script);
  640. }
  641. [Fact]
  642. public void ShouldDiscardDeepRecursion()
  643. {
  644. var script = @"var factorial = function(n) {
  645. if (n>1) {
  646. return n * factorial(n - 1);
  647. }
  648. };
  649. var result = factorial(38);
  650. ";
  651. Assert.Throws<RecursionDepthOverflowException>(
  652. () => new Engine(cfg => cfg.LimitRecursion(20)).Execute(script)
  653. );
  654. }
  655. [Fact]
  656. public void ShouldConvertDoubleToStringWithoutLosingPrecision()
  657. {
  658. RunTest(@"
  659. assert(String(14.915832707045631) === '14.915832707045631');
  660. assert(String(-14.915832707045631) === '-14.915832707045631');
  661. assert(String(0.5) === '0.5');
  662. assert(String(0.00000001) === '1e-8');
  663. assert(String(0.000001) === '0.000001');
  664. assert(String(-1.0) === '-1');
  665. assert(String(30.0) === '30');
  666. assert(String(0.2388906159889881) === '0.2388906159889881');
  667. ");
  668. }
  669. [Fact]
  670. public void ShouldWriteNumbersUsingBases()
  671. {
  672. RunTest(@"
  673. assert(15.0.toString() === '15');
  674. assert(15.0.toString(2) === '1111');
  675. assert(15.0.toString(8) === '17');
  676. assert(15.0.toString(16) === 'f');
  677. assert(15.0.toString(17) === 'f');
  678. assert(15.0.toString(36) === 'f');
  679. assert(15.1.toString(36) === 'f.3llllllllkau6snqkpygsc3di');
  680. ");
  681. }
  682. [Fact]
  683. public void ShouldNotAlterSlashesInRegex()
  684. {
  685. RunTest(@"
  686. assert(new RegExp('/').toString() === '///');
  687. ");
  688. }
  689. [Fact]
  690. public void ShouldHandleEscapedSlashesInRegex()
  691. {
  692. RunTest(@"
  693. var regex = /[a-z]\/[a-z]/;
  694. assert(regex.test('a/b') === true);
  695. assert(regex.test('a\\/b') === false);
  696. ");
  697. }
  698. [Fact]
  699. public void ShouldComputeFractionInBase()
  700. {
  701. Assert.Equal("011", NumberPrototype.ToFractionBase(0.375, 2));
  702. Assert.Equal("14141414141414141414141414141414141414141414141414", NumberPrototype.ToFractionBase(0.375, 5));
  703. }
  704. [Fact]
  705. public void ShouldInvokeAFunctionValue()
  706. {
  707. RunTest(@"
  708. function add(x, y) { return x + y; }
  709. ");
  710. var add = _engine.GetValue("add");
  711. Assert.Equal(3, add.Invoke(1, 2));
  712. }
  713. [Fact]
  714. public void ShouldNotInvokeNonFunctionValue()
  715. {
  716. RunTest(@"
  717. var x= 10;
  718. ");
  719. var x = _engine.GetValue("x");
  720. Assert.Throws<ArgumentException>(() => x.Invoke(1, 2));
  721. }
  722. [Theory]
  723. [InlineData("0", 0, 16)]
  724. [InlineData("1", 1, 16)]
  725. [InlineData("100", 100, 10)]
  726. [InlineData("1100100", 100, 2)]
  727. [InlineData("2s", 100, 36)]
  728. [InlineData("2qgpckvng1s", 10000000000000000L, 36)]
  729. public void ShouldConvertNumbersToDifferentBase(string expected, long number, int radix)
  730. {
  731. var result = NumberPrototype.ToBase(number, radix);
  732. Assert.Equal(expected, result);
  733. }
  734. [Fact]
  735. public void JsonParserShouldParseNegativeNumber()
  736. {
  737. RunTest(@"
  738. var a = JSON.parse('{ ""x"":-1 }');
  739. assert(a.x === -1);
  740. var b = JSON.parse('{ ""x"": -1 }');
  741. assert(b.x === -1);
  742. ");
  743. }
  744. [Fact]
  745. public void JsonParserShouldDetectInvalidNegativeNumberSyntax()
  746. {
  747. RunTest(@"
  748. try {
  749. JSON.parse('{ ""x"": -.1 }'); // Not allowed
  750. assert(false);
  751. }
  752. catch(ex) {
  753. assert(ex instanceof SyntaxError);
  754. }
  755. ");
  756. RunTest(@"
  757. try {
  758. JSON.parse('{ ""x"": - 1 }'); // Not allowed
  759. assert(false);
  760. }
  761. catch(ex) {
  762. assert(ex instanceof SyntaxError);
  763. }
  764. ");
  765. }
  766. [Fact]
  767. public void ShouldBeCultureInvariant()
  768. {
  769. // decimals in french are separated by commas
  770. Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("fr-FR");
  771. var engine = new Engine();
  772. var result = engine.Execute("1.2 + 2.1").GetCompletionValue().AsNumber();
  773. Assert.Equal(3.3d, result);
  774. result = engine.Execute("JSON.parse('{\"x\" : 3.3}').x").GetCompletionValue().AsNumber();
  775. Assert.Equal(3.3d, result);
  776. }
  777. [Fact]
  778. public void ShouldGetTheLastSyntaxNode()
  779. {
  780. var engine = new Engine();
  781. engine.Execute("1.2");
  782. var result = engine.GetLastSyntaxNode();
  783. Assert.Equal(SyntaxNodes.Literal, result.Type);
  784. }
  785. [Fact]
  786. public void ShouldGetParseErrorLocation()
  787. {
  788. var engine = new Engine();
  789. try
  790. {
  791. engine.Execute("1.2+ new", new ParserOptions { Source = "jQuery.js" });
  792. }
  793. catch (ParserException e)
  794. {
  795. Assert.Equal(1, e.LineNumber);
  796. Assert.Equal(9, e.Column);
  797. Assert.Equal("jQuery.js", e.Source);
  798. }
  799. }
  800. [Fact]
  801. public void ParseShouldReturnNumber()
  802. {
  803. var engine = new Engine();
  804. var result = engine.Execute("Date.parse('1970-01-01');").GetCompletionValue().AsNumber();
  805. Assert.Equal(0, result);
  806. }
  807. [Fact]
  808. public void UtcShouldUseUtc()
  809. {
  810. const string customName = "Custom Time";
  811. var customTimeZone = TimeZoneInfo.CreateCustomTimeZone(customName, new TimeSpan(7, 11, 0), customName, customName, customName, null, false);
  812. var engine = new Engine(cfg => cfg.LocalTimeZone(customTimeZone));
  813. var result = engine.Execute("Date.UTC(1970,0,1)").GetCompletionValue().AsNumber();
  814. Assert.Equal(0, result);
  815. }
  816. [Fact]
  817. public void ShouldUseLocalTimeZoneOverride()
  818. {
  819. const string customName = "Custom Time";
  820. var customTimeZone = TimeZoneInfo.CreateCustomTimeZone(customName, new TimeSpan(0, 11, 0), customName, customName, customName, null, false);
  821. var engine = new Engine(cfg => cfg.LocalTimeZone(customTimeZone));
  822. var epochGetLocalMinutes = engine.Execute("var d = new Date(0); d.getMinutes();").GetCompletionValue().AsNumber();
  823. Assert.Equal(11, epochGetLocalMinutes);
  824. var localEpochGetUtcMinutes = engine.Execute("var d = new Date(1970,0,1); d.getUTCMinutes();").GetCompletionValue().AsNumber();
  825. Assert.Equal(-11, localEpochGetUtcMinutes);
  826. var parseLocalEpoch = engine.Execute("Date.parse('January 1, 1970');").GetCompletionValue().AsNumber();
  827. Assert.Equal(-11 * 60 * 1000, parseLocalEpoch);
  828. var epochToLocalString = engine.Execute("var d = new Date(0); d.toString();").GetCompletionValue().AsString();
  829. Assert.Equal("Thu Jan 01 1970 00:11:00 GMT", epochToLocalString);
  830. }
  831. [Theory]
  832. [InlineData("1970")]
  833. [InlineData("1970-01")]
  834. [InlineData("1970-01-01")]
  835. [InlineData("1970-01-01T00:00")]
  836. [InlineData("1970-01-01T00:00:00")]
  837. [InlineData("1970-01-01T00:00:00.000")]
  838. [InlineData("1970Z")]
  839. [InlineData("1970-1Z")]
  840. [InlineData("1970-1-1Z")]
  841. [InlineData("1970-1-1T0:0Z")]
  842. [InlineData("1970-1-1T0:0:0Z")]
  843. [InlineData("1970-1-1T0:0:0.0Z")]
  844. [InlineData("1970/1Z")]
  845. [InlineData("1970/1/1Z")]
  846. [InlineData("1970/1/1 0:0Z")]
  847. [InlineData("1970/1/1 0:0:0Z")]
  848. [InlineData("1970/1/1 0:0:0.0Z")]
  849. [InlineData("January 1, 1970 GMT")]
  850. [InlineData("1970-01-01T00:00:00.000-00:00")]
  851. public void ShouldParseAsUtc(string date)
  852. {
  853. const string customName = "Custom Time";
  854. var customTimeZone = TimeZoneInfo.CreateCustomTimeZone(customName, new TimeSpan(7, 11, 0), customName, customName, customName, null, false);
  855. var engine = new Engine(cfg => cfg.LocalTimeZone(customTimeZone));
  856. engine.SetValue("d", date);
  857. var result = engine.Execute("Date.parse(d);").GetCompletionValue().AsNumber();
  858. Assert.Equal(0, result);
  859. }
  860. [Theory]
  861. [InlineData("1970/01")]
  862. [InlineData("1970/01/01")]
  863. [InlineData("1970/01/01T00:00")]
  864. [InlineData("1970/01/01 00:00")]
  865. [InlineData("1970-1")]
  866. [InlineData("1970-1-1")]
  867. [InlineData("1970-1-1T0:0")]
  868. [InlineData("1970-1-1 0:0")]
  869. [InlineData("1970/1")]
  870. [InlineData("1970/1/1")]
  871. [InlineData("1970/1/1T0:0")]
  872. [InlineData("1970/1/1 0:0")]
  873. [InlineData("01-1970")]
  874. [InlineData("01-01-1970")]
  875. [InlineData("January 1, 1970")]
  876. [InlineData("1970-01-01T00:00:00.000+00:11")]
  877. public void ShouldParseAsLocalTime(string date)
  878. {
  879. const string customName = "Custom Time";
  880. var customTimeZone = TimeZoneInfo.CreateCustomTimeZone(customName, new TimeSpan(0, 11, 0), customName, customName, customName, null, false);
  881. var engine = new Engine(cfg => cfg.LocalTimeZone(customTimeZone)).SetValue("d", date);
  882. var result = engine.Execute("Date.parse(d);").GetCompletionValue().AsNumber();
  883. Assert.Equal(-11 * 60 * 1000, result);
  884. }
  885. [Fact]
  886. public void EmptyStringShouldMatchRegex()
  887. {
  888. RunTest(@"
  889. var regex = /^(?:$)/g;
  890. assert(''.match(regex) instanceof Array);
  891. ");
  892. }
  893. [Fact]
  894. public void ShouldExecuteHandlebars()
  895. {
  896. var url = "http://cdnjs.cloudflare.com/ajax/libs/handlebars.js/2.0.0/handlebars.js";
  897. var content = new WebClient().DownloadString(url);
  898. RunTest(content);
  899. RunTest(@"
  900. var source = 'Hello {{name}}';
  901. var template = Handlebars.compile(source);
  902. var context = {name: 'Paul'};
  903. var html = template(context);
  904. assert('Hello Paul' == html);
  905. ");
  906. }
  907. [Fact]
  908. public void DateParseReturnsNaN()
  909. {
  910. RunTest(@"
  911. var d = Date.parse('not a date');
  912. assert(isNaN(d));
  913. ");
  914. }
  915. [Fact]
  916. public void ShouldIgnoreHtmlComments()
  917. {
  918. RunTest(@"
  919. var d = Date.parse('not a date'); <!-- a comment -->
  920. assert(isNaN(d));
  921. ");
  922. }
  923. [Fact]
  924. public void DateShouldAllowEntireDotNetDateRange()
  925. {
  926. var engine = new Engine();
  927. var minValue = engine.Execute("new Date('0001-01-01T00:00:00.000')").GetCompletionValue().ToObject();
  928. Assert.Equal(new DateTime(1, 1, 1, 0, 0, 0, DateTimeKind.Utc), minValue);
  929. var maxValue = engine.Execute("new Date('9999-12-31T23:59:59.999')").GetCompletionValue().ToObject();
  930. Assert.Equal(new DateTime(9999, 12, 31, 23, 59, 59, 999, DateTimeKind.Utc), maxValue);
  931. }
  932. [Fact]
  933. public void ShouldConstructNewArrayWithInteger()
  934. {
  935. RunTest(@"
  936. var a = new Array(3);
  937. assert(a.length === 3);
  938. assert(a[0] == undefined);
  939. assert(a[1] == undefined);
  940. assert(a[2] == undefined);
  941. ");
  942. }
  943. [Fact]
  944. public void ShouldConstructNewArrayWithString()
  945. {
  946. RunTest(@"
  947. var a = new Array('foo');
  948. assert(a.length === 1);
  949. assert(a[0] === 'foo');
  950. ");
  951. }
  952. [Fact]
  953. public void ShouldThrowRangeExceptionWhenConstructedWithNonInteger()
  954. {
  955. RunTest(@"
  956. var result = false;
  957. try {
  958. var a = new Array(3.4);
  959. }
  960. catch(e) {
  961. result = e instanceof RangeError;
  962. }
  963. assert(result);
  964. ");
  965. }
  966. [Fact]
  967. public void ShouldInitializeArrayWithSingleIngegerValue()
  968. {
  969. RunTest(@"
  970. var a = [3];
  971. assert(a.length === 1);
  972. assert(a[0] === 3);
  973. ");
  974. }
  975. [Fact]
  976. public void ShouldInitializeJsonObjectArrayWithSingleIntegerValue()
  977. {
  978. RunTest(@"
  979. var x = JSON.parse('{ ""a"": [3] }');
  980. assert(x.a.length === 1);
  981. assert(x.a[0] === 3);
  982. ");
  983. }
  984. [Fact]
  985. public void ShouldInitializeJsonArrayWithSingleIntegerValue()
  986. {
  987. RunTest(@"
  988. var a = JSON.parse('[3]');
  989. assert(a.length === 1);
  990. assert(a[0] === 3);
  991. ");
  992. }
  993. }
  994. }