package org.dynjs.runtime; import org.dynjs.exception.ThrowException; import org.dynjs.runtime.builtins.types.error.StackElement; import org.junit.Test; import static org.fest.assertions.Assertions.assertThat; public class StackTraceTest extends AbstractDynJSTestSupport { @Test public void testBasicConstructedError() { DynObject e = (DynObject) eval("function foo() { return new Error(); }; foo();"); String stack = (String) e.get(getContext(), "stack"); assertThat(stack).contains("Error\n"); assertThat(stack).contains("at foo (<eval>:1:17)\n"); } @Test public void testBasicConstructedTypeError() { DynObject e = (DynObject) eval("function foo() { return new TypeError(); }; foo()"); String stack = (String) e.get(getContext(), "stack"); assertThat(stack).contains("TypeError\n"); assertThat(stack).contains("at foo (<eval>:1:17)\n"); } @Test public void testBasicThrownError() { DynObject e = (DynObject) eval("function foo() { try { throw new Error(); } catch(e) { return e; } }; foo();"); String stack = (String) e.get(getContext(), "stack"); assertThat(stack).contains("Error\n"); assertThat(stack).contains("at foo (<eval>:1:55)\n"); } @Test public void testComplexStack() { try { eval("", "var x = { one: function() { bar() } };", "var y = { two: x.one };", "function foo() {", " throw TypeError();", "}", "function bar() {", " foo();", "}", "", "try {", " (function(){y.two();})();", "} catch(e) {", " throw e;", "}"); throw new AssertionError("Should have thrown"); } catch (ThrowException e) { JSObject o = (JSObject) e.getValue(); String stack = (String) o.get(getContext(), "stack"); assertThat(stack.contains("TypeError\n")).isTrue(); assertThat(stack.contains("at foo (<eval>:5:2)")).isTrue(); assertThat(stack.contains("at bar (<eval>:8:3)")).isTrue(); assertThat(stack.contains("at Object.one (<eval>:2:29)")).isTrue(); assertThat(stack.contains("at <anonymous> (<eval>:12:15)")).isTrue(); assertThat(stack.contains("at <eval> (<eval>:12:3)")).isTrue(); } } @Test public void simpleFunctionsAssignedToVariable() { try { eval(" var x = function() {", " throw new Error('dang');", "};", "x();"); } catch (ThrowException e) { JSObject o = (JSObject) e.getValue(); String stack = (String) o.get( getContext(), "stack" ); assertThat( stack.contains( "at x")).isTrue(); } } @Test public void testComplexStackWithMessage() { try { eval("", "var x = { one: function() { bar() } };", "var y = { two: x.one };", "function foo() {", " throw TypeError('dangit');", "}", "function bar() {", " foo();", "}", "", "try {", " y.two();", "} catch(e) {", " print(e.stack);", " throw e;", "}"); throw new AssertionError("Should have thrown"); } catch (ThrowException e) { JSObject o = (JSObject) e.getValue(); String stack = (String) o.get(getContext(), "stack"); assertThat(stack.contains("TypeError: dangit")).isTrue(); assertThat(stack.contains("at foo (<eval>:5:2)")).isTrue(); assertThat(stack.contains("at bar (<eval>:8:3)")).isTrue(); assertThat(stack.contains("at Object.one (<eval>:2:29)")).isTrue(); assertThat(stack.contains("at <eval> (<eval>:12:3)")).isTrue(); } } @Test public void testNativeStackTrace() { StackElement stackElement = (StackElement) eval("__prepareStackTrace = Error.prepareStackTrace;" + "Error.prepareStackTrace = function(e,s) { return s; };" + "var val = null;" + "try {" + " require('notexist.js');" + "} catch(e) {" + " val = e.stack[1];" + "}" + "Error.prepareStackTrace = __prepareStackTrace;" + "val;"); assertThat(stackElement).isNotNull(); assertThat(stackElement.isNative()).isTrue(); } @Test public void testStackElementIsTopLevel() { StackElement stackElement = (StackElement) eval("__prepareStackTrace = Error.prepareStackTrace;" + "Error.prepareStackTrace = function(e,s) { return s; };" + "var e = new Error('broken china');" + "var val = e.stack[0];" + "Error.prepareStackTrace = __prepareStackTrace;" + "val;"); assertThat(stackElement).isNotNull(); assertThat(stackElement.isTopLevel()).isTrue(); } @Test public void testStackElementIsNotTopLevel() { StackElement stackElement = (StackElement) eval("__prepareStackTrace = Error.prepareStackTrace;" + "Error.prepareStackTrace = function(e,s) { return s; };" + "var ErrorMaker = function() {};" + "ErrorMaker.prototype.makeIt = function() { return new Error('broken china'); };" + "var e = new ErrorMaker().makeIt();" + "var val = e.stack[0];" + "Error.prepareStackTrace = __prepareStackTrace;" + "val;"); assertThat(stackElement).isNotNull(); assertThat(stackElement.isTopLevel()).isFalse(); } @Test public void testStackElementIsConstructor() { StackElement stackElement = (StackElement) eval("__prepareStackTrace = Error.prepareStackTrace;" + "Error.prepareStackTrace = function(e,s) { return s; };" + "var ErrorMaker = function() { throw new Error('broken china'); };" + "try {" + " new ErrorMaker();" + "} catch(e) {" + " var val = e.stack[0];" + "}" + "Error.prepareStackTrace = __prepareStackTrace;" + "val;"); assertThat(stackElement).isNotNull(); assertThat(stackElement.isConstructor()).isTrue(); } @Test public void testStackElementIsNotConstructor() { StackElement stackElement = (StackElement) eval("__prepareStackTrace = Error.prepareStackTrace;" + "Error.prepareStackTrace = function(e,s) { return s; };" + "var e = new Error('broken china');" + "var val = e.stack[0];" + "Error.prepareStackTrace = __prepareStackTrace;" + "val;"); assertThat(stackElement).isNotNull(); assertThat(stackElement.isConstructor()).isFalse(); } @Test public void testStackElementIsEval() { StackElement stackElement = (StackElement) eval("__prepareStackTrace = Error.prepareStackTrace;" + "Error.prepareStackTrace = function(e,s) { return s; };" + "try {" + " eval('throw new Error()');" + "} catch(e) {" + " var val = e.stack[1];" + "}" + "Error.prepareStackTrace = __prepareStackTrace;" + "val;"); assertThat(stackElement).isNotNull(); assertThat((stackElement).isEval()).isTrue(); } @Test public void testStackElementEvalOrigin() { StackElement stackElement = (StackElement) eval("__prepareStackTrace = Error.prepareStackTrace;" + "Error.prepareStackTrace = function(e,s) { return s; };" + "try {" + " function thrower() {" + " eval('throw new Error()');" + " }" + " thrower();" + "} catch(e) {" + " print(__prepareStackTrace(e,e.stack));" + " var val = e.stack[1];" + "}" + "Error.prepareStackTrace = __prepareStackTrace;" + "val;"); assertThat(stackElement).isNotNull(); assertThat((stackElement).isEval()).isTrue(); StackElement origin = stackElement.getEvalOrigin(); assertThat(origin).isNotNull(); assertThat(origin.getFunctionName()).isEqualTo("thrower"); } @Test // FIXME public void testComplexStackAndPretendWeHaveFilename() { /* try { getRuntime().execute("\n" + "var x = { one: function() { bar() } };\n" + "var y = { two: x.one };\n" + "function foo() {\n" + " throw TypeError();\n" + "}\n" + "function bar() {\n" + " foo();\n" + "}\n" + "\n" + "try {\n" + " y.two();\n" + "} catch(e) {\n" + " //print(e.stack);\n" + " throw e;\n" + "}", "fakefile.js", 0); } catch (ThrowException e) { JSObject o = (JSObject) e.getValue(); String stack = (String) o.get(getContext(), "stack"); assertThat(stack.contains("TypeError\n")).isTrue(); assertThat(stack.contains("at foo (fakefile.js:5)")).isTrue(); assertThat(stack.contains("at bar (fakefile.js:8)")).isTrue(); assertThat(stack.contains("at Object.one (fakefile.js:2)")).isTrue(); assertThat(stack.contains("at <eval> (fakefile.js:12)")).isTrue(); } */ } }