stacktrace_test.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. // Copyright 2009 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. goog.provide('goog.testing.stacktraceTest');
  15. goog.setTestOnly('goog.testing.stacktraceTest');
  16. goog.require('goog.functions');
  17. goog.require('goog.string');
  18. goog.require('goog.testing.ExpectedFailures');
  19. goog.require('goog.testing.PropertyReplacer');
  20. goog.require('goog.testing.StrictMock');
  21. goog.require('goog.testing.asserts');
  22. goog.require('goog.testing.jsunit');
  23. goog.require('goog.testing.stacktrace');
  24. goog.require('goog.testing.stacktrace.Frame');
  25. goog.require('goog.userAgent');
  26. var stubs = new goog.testing.PropertyReplacer();
  27. var expectedFailures;
  28. function setUpPage() {
  29. expectedFailures = new goog.testing.ExpectedFailures();
  30. }
  31. function setUp() {
  32. stubs.set(goog.testing.stacktrace, 'isClosureInspectorActive_', function() {
  33. return false;
  34. });
  35. }
  36. function tearDown() {
  37. stubs.reset();
  38. expectedFailures.handleTearDown();
  39. }
  40. function testParseStackFrameInV8() {
  41. var frameString = ' at Error (unknown source)';
  42. var frame = goog.testing.stacktrace.parseStackFrame_(frameString);
  43. var expected = new goog.testing.stacktrace.Frame('', 'Error', '', '');
  44. assertObjectEquals('exception name only', expected, frame);
  45. frameString = ' at Object.assert (file:///.../asserts.js:29:10)';
  46. frame = goog.testing.stacktrace.parseStackFrame_(frameString);
  47. expected = new goog.testing.stacktrace.Frame(
  48. 'Object', 'assert', '', 'file:///.../asserts.js:29:10');
  49. assertObjectEquals('context object + function name + url', expected, frame);
  50. frameString = ' at Object.x.y.z (/Users/bob/file.js:564:9)';
  51. frame = goog.testing.stacktrace.parseStackFrame_(frameString);
  52. expected = new goog.testing.stacktrace.Frame(
  53. 'Object.x.y', 'z', '', '/Users/bob/file.js:564:9');
  54. assertObjectEquals(
  55. 'nested context object + function name + url', expected, frame);
  56. frameString = ' at http://www.example.com/jsunit.js:117:13';
  57. frame = goog.testing.stacktrace.parseStackFrame_(frameString);
  58. expected = new goog.testing.stacktrace.Frame(
  59. '', '', '', 'http://www.example.com/jsunit.js:117:13');
  60. assertObjectEquals('url only', expected, frame);
  61. frameString = ' at [object Object].exec [as execute] (file:///foo)';
  62. frame = goog.testing.stacktrace.parseStackFrame_(frameString);
  63. expected = new goog.testing.stacktrace.Frame(
  64. '[object Object]', 'exec', 'execute', 'file:///foo');
  65. assertObjectEquals('function alias', expected, frame);
  66. frameString = ' at new Class (file:///foo)';
  67. frame = goog.testing.stacktrace.parseStackFrame_(frameString);
  68. expected =
  69. new goog.testing.stacktrace.Frame('', 'new Class', '', 'file:///foo');
  70. assertObjectEquals('constructor call', expected, frame);
  71. frameString = ' at new <anonymous> (file:///foo)';
  72. frame = goog.testing.stacktrace.parseStackFrame_(frameString);
  73. expected = new goog.testing.stacktrace.Frame(
  74. '', 'new <anonymous>', '', 'file:///foo');
  75. assertObjectEquals('anonymous constructor call', expected, frame);
  76. frameString = ' at Array.forEach (native)';
  77. frame = goog.testing.stacktrace.parseStackFrame_(frameString);
  78. expected = new goog.testing.stacktrace.Frame('Array', 'forEach', '', '');
  79. assertObjectEquals('native function call', expected, frame);
  80. frameString = ' at foo (eval at file://bar)';
  81. frame = goog.testing.stacktrace.parseStackFrame_(frameString);
  82. expected =
  83. new goog.testing.stacktrace.Frame('', 'foo', '', 'eval at file://bar');
  84. assertObjectEquals('eval', expected, frame);
  85. frameString = ' at foo.bar (closure/goog/foo.js:11:99)';
  86. frame = goog.testing.stacktrace.parseStackFrame_(frameString);
  87. expected = new goog.testing.stacktrace.Frame(
  88. 'foo', 'bar', '', 'closure/goog/foo.js:11:99');
  89. assertObjectEquals('Path without schema', expected, frame);
  90. // In the Chrome console, execute: console.log(eval('Error().stack')).
  91. frameString =
  92. ' at eval (eval at <anonymous> (unknown source), <anonymous>:1:1)';
  93. frame = goog.testing.stacktrace.parseStackFrame_(frameString);
  94. expected = new goog.testing.stacktrace.Frame(
  95. '', 'eval', '', 'eval at <anonymous> (unknown source), <anonymous>:1:1');
  96. assertObjectEquals('nested eval', expected, frame);
  97. }
  98. function testParseStackFrameInOpera() {
  99. var frameString = '@';
  100. var frame = goog.testing.stacktrace.parseStackFrame_(frameString);
  101. var expected = new goog.testing.stacktrace.Frame('', '', '', '');
  102. assertObjectEquals('empty frame', expected, frame);
  103. frameString = '@javascript:console.log(Error().stack):1';
  104. frame = goog.testing.stacktrace.parseStackFrame_(frameString);
  105. expected = new goog.testing.stacktrace.Frame(
  106. '', '', '', 'javascript:console.log(Error().stack):1');
  107. assertObjectEquals('javascript path only', expected, frame);
  108. frameString = '@file:///foo:42';
  109. frame = goog.testing.stacktrace.parseStackFrame_(frameString);
  110. expected = new goog.testing.stacktrace.Frame('', '', '', 'file:///foo:42');
  111. assertObjectEquals('path only', expected, frame);
  112. // (function go() { throw Error() })()
  113. // var c = go; c()
  114. frameString = 'go([arguments not available])@';
  115. frame = goog.testing.stacktrace.parseStackFrame_(frameString);
  116. expected = new goog.testing.stacktrace.Frame('', 'go', '', '');
  117. assertObjectEquals('name and empty path', expected, frame);
  118. frameString = 'go([arguments not available])@file:///foo:42';
  119. frame = goog.testing.stacktrace.parseStackFrame_(frameString);
  120. expected = new goog.testing.stacktrace.Frame('', 'go', '', 'file:///foo:42');
  121. assertObjectEquals('name and path', expected, frame);
  122. // (function() { throw Error() })()
  123. frameString =
  124. '<anonymous function>([arguments not available])@file:///foo:42';
  125. frame = goog.testing.stacktrace.parseStackFrame_(frameString);
  126. expected = new goog.testing.stacktrace.Frame('', '', '', 'file:///foo:42');
  127. assertObjectEquals('anonymous function', expected, frame);
  128. // var b = {foo: function() { throw Error() }}
  129. frameString = '<anonymous function: foo>()@file:///foo:42';
  130. frame = goog.testing.stacktrace.parseStackFrame_(frameString);
  131. expected = new goog.testing.stacktrace.Frame('', 'foo', '', 'file:///foo:42');
  132. assertObjectEquals('object literal function', expected, frame);
  133. // var c = {}; c.foo = function() { throw Error() }
  134. frameString = '<anonymous function: c.foo>()@file:///foo:42';
  135. frame = goog.testing.stacktrace.parseStackFrame_(frameString);
  136. expected =
  137. new goog.testing.stacktrace.Frame('c', 'foo', '', 'file:///foo:42');
  138. assertObjectEquals('named object literal function', expected, frame);
  139. frameString = '<anonymous function: Foo.prototype.bar>()@';
  140. frame = goog.testing.stacktrace.parseStackFrame_(frameString);
  141. expected = new goog.testing.stacktrace.Frame('Foo.prototype', 'bar', '', '');
  142. assertObjectEquals('prototype function', expected, frame);
  143. frameString = '<anonymous function: goog.Foo.prototype.bar>()@';
  144. frame = goog.testing.stacktrace.parseStackFrame_(frameString);
  145. expected =
  146. new goog.testing.stacktrace.Frame('goog.Foo.prototype', 'bar', '', '');
  147. assertObjectEquals('namespaced prototype function', expected, frame);
  148. }
  149. // All test strings are parsed with the conventional and long
  150. // frame algorithms.
  151. function testParseStackFrameInFirefox() {
  152. var frameString = 'Error("Assertion failed")@:0';
  153. var frame = goog.testing.stacktrace.parseStackFrame_(frameString);
  154. var expected = new goog.testing.stacktrace.Frame('', 'Error', '', '');
  155. assertObjectEquals('function name + arguments', expected, frame);
  156. frameString = '()@file:///foo:42';
  157. frame = goog.testing.stacktrace.parseStackFrame_(frameString);
  158. expected = new goog.testing.stacktrace.Frame('', '', '', 'file:///foo:42');
  159. assertObjectEquals('anonymous function', expected, frame);
  160. frameString = '@javascript:alert(0)';
  161. frame = goog.testing.stacktrace.parseStackFrame_(frameString);
  162. expected =
  163. new goog.testing.stacktrace.Frame('', '', '', 'javascript:alert(0)');
  164. assertObjectEquals('anonymous function', expected, frame);
  165. }
  166. // All test strings are parsed with the conventional and long
  167. // frame algorithms.
  168. function testParseStackFrameInFirefoxWithQualifiedName() {
  169. var frameString = 'ns.method@http://some.thing/a.js:1:2';
  170. var frame = goog.testing.stacktrace.parseStackFrame_(frameString);
  171. var expected = new goog.testing.stacktrace.Frame(
  172. '', 'ns.method', '', 'http://some.thing/a.js:1:2');
  173. assertObjectEquals('anonymous function', expected, frame);
  174. }
  175. function testCanonicalizeFrame() {
  176. var frame = new goog.testing.stacktrace.Frame(
  177. '<window>', 'foo', 'bar', 'http://x?a=1&b=2:1');
  178. assertEquals(
  179. 'canonical stack frame, everything is escaped', '&lt;window&gt;.foo ' +
  180. '[as bar] at http://x?a=1&amp;b=2:1',
  181. frame.toCanonicalString());
  182. }
  183. function testDeobfuscateFunctionName() {
  184. goog.testing.stacktrace.setDeobfuscateFunctionName(function(name) {
  185. return name.replace(/\$/g, '.');
  186. });
  187. var frame = new goog.testing.stacktrace.Frame('', 'a$b$c', 'd$e', '');
  188. assertEquals(
  189. 'deobfuscated function name', 'a.b.c [as d.e]',
  190. frame.toCanonicalString());
  191. }
  192. function testFramesToString() {
  193. var normalFrame = new goog.testing.stacktrace.Frame('', 'foo', '', '');
  194. var anonFrame = new goog.testing.stacktrace.Frame('', '', '', '');
  195. var frames = [normalFrame, anonFrame, null, anonFrame];
  196. var stack = goog.testing.stacktrace.framesToString_(frames);
  197. assertEquals('framesToString', '> foo\n> anonymous\n> (unknown)\n', stack);
  198. }
  199. function testFollowCallChain() {
  200. var func = function(var_args) {
  201. return goog.testing.stacktrace.followCallChain_();
  202. };
  203. // Created a fake type with a toString method.
  204. function LocalType(){};
  205. LocalType.prototype.toString = function() { return 'sg'; };
  206. // Create a mock with no expectations.
  207. var mock = new goog.testing.StrictMock(LocalType);
  208. mock.$replay();
  209. var frames = func(
  210. undefined, null, false, 0, '', {}, goog.nullFunction, mock,
  211. new LocalType);
  212. // Opera before version 10 doesn't support the caller attribute. In that
  213. // browser followCallChain_() returns empty stack trace.
  214. expectedFailures.expectFailureFor(
  215. goog.userAgent.OPERA && !goog.userAgent.isVersionOrHigher('10'));
  216. try {
  217. assertTrue('The stack trace consists of >=2 frames', frames.length >= 2);
  218. } catch (e) {
  219. expectedFailures.handleException(e);
  220. }
  221. if (frames.length >= 2) {
  222. assertEquals('innermost function is anonymous', '', frames[0].getName());
  223. // There are white space differences how browsers convert functions to
  224. // strings.
  225. assertEquals(
  226. 'test function name', 'testFollowCallChain', frames[1].getName());
  227. }
  228. mock.$verify();
  229. }
  230. // Create a stack trace string with one modest record and one long record,
  231. // Verify that all frames are parsed. The length of the long arg is set
  232. // to blow Firefox 3x's stack if put through a RegExp.
  233. function testParsingLongStackTrace() {
  234. var longArg =
  235. goog.string.buildString('(', goog.string.repeat('x', 1000000), ')');
  236. var stackTrace = goog.string.buildString(
  237. 'shortFrame()@:0\n', 'longFrame', longArg,
  238. '@http://google.com/somescript:0\n');
  239. var frames = goog.testing.stacktrace.parse_(stackTrace);
  240. assertEquals('number of returned frames', 2, frames.length);
  241. var expected = new goog.testing.stacktrace.Frame('', 'shortFrame', '', '');
  242. assertObjectEquals('short frame', expected, frames[0]);
  243. assertNull('exception no frame', frames[1]);
  244. }
  245. function testParseStackFrameInIE10() {
  246. var frameString = ' at foo (http://bar:4000/bar.js:150:3)';
  247. var frame = goog.testing.stacktrace.parseStackFrame_(frameString);
  248. var expected = new goog.testing.stacktrace.Frame(
  249. '', 'foo', '', 'http://bar:4000/bar.js:150:3');
  250. assertObjectEquals('name and path', expected, frame);
  251. frameString = ' at Anonymous function (http://bar:4000/bar.js:150:3)';
  252. frame = goog.testing.stacktrace.parseStackFrame_(frameString);
  253. expected = new goog.testing.stacktrace.Frame(
  254. '', 'Anonymous function', '', 'http://bar:4000/bar.js:150:3');
  255. assertObjectEquals('Anonymous function', expected, frame);
  256. frameString = ' at Global code (http://bar:4000/bar.js:150:3)';
  257. frame = goog.testing.stacktrace.parseStackFrame_(frameString);
  258. expected = new goog.testing.stacktrace.Frame(
  259. '', 'Global code', '', 'http://bar:4000/bar.js:150:3');
  260. assertObjectEquals('Global code', expected, frame);
  261. frameString = ' at foo (eval code:150:3)';
  262. frame = goog.testing.stacktrace.parseStackFrame_(frameString);
  263. expected =
  264. new goog.testing.stacktrace.Frame('', 'foo', '', 'eval code:150:3');
  265. assertObjectEquals('eval code', expected, frame);
  266. frameString = ' at eval code (eval code:150:3)';
  267. frame = goog.testing.stacktrace.parseStackFrame_(frameString);
  268. expected =
  269. new goog.testing.stacktrace.Frame('', 'eval code', '', 'eval code:150:3');
  270. assertObjectEquals('nested eval', expected, frame);
  271. }
  272. function testParseStackFrameInIE11() {
  273. var frameString = ' at a.b.c (Unknown script code:150:3)';
  274. var frame = goog.testing.stacktrace.parseStackFrame_(frameString);
  275. var expected = new goog.testing.stacktrace.Frame(
  276. '', 'a.b.c', '', 'Unknown script code:150:3');
  277. assertObjectEquals('name and path', expected, frame);
  278. }
  279. function testParseStackFrameInEdge() {
  280. frameString = ' at a.b.c (http://host.com:80/some/file.js:101:2)';
  281. frame = goog.testing.stacktrace.parseStackFrame_(frameString);
  282. expected = new goog.testing.stacktrace.Frame(
  283. '', 'a.b.c', '', 'http://host.com:80/some/file.js:101:2');
  284. assertObjectEquals(expected, frame);
  285. }
  286. // Verifies that retrieving the stack trace works when the 'stack' field of an
  287. // exception contains an array of CallSites instead of a string. This is the
  288. // case when running in a lightweight V8 runtime (for instance, in gjstests),
  289. // as opposed to a browser environment.
  290. function testGetStackFrameWithV8CallSites() {
  291. // A function to create V8 CallSites. Note that CallSite is an extern and thus
  292. // cannot be mocked with closure mocks.
  293. function createCallSite(functionName, fileName, lineNumber, colNumber) {
  294. return {
  295. getFunctionName: goog.functions.constant(functionName),
  296. getFileName: goog.functions.constant(fileName),
  297. getLineNumber: goog.functions.constant(lineNumber),
  298. getColumnNumber: goog.functions.constant(colNumber)
  299. };
  300. }
  301. // Mock the goog.testing.stacktrace.getStack_ function, which normally
  302. // triggers an exception for the purpose of reading and returning its stack
  303. // trace. Here, pretend that V8 provided an array of CallSites instead of the
  304. // string that browsers provide.
  305. stubs.set(goog.testing.stacktrace, 'getNativeStack_', function() {
  306. return [
  307. createCallSite('fn1', 'file1', 1, 2),
  308. createCallSite('fn2', 'file2', 3, 4), createCallSite('fn3', 'file3', 5, 6)
  309. ];
  310. });
  311. // Retrieve the stacktrace. This should translate the array of CallSites into
  312. // a single multi-line string.
  313. var stackTrace = goog.testing.stacktrace.get();
  314. // Make sure the stack trace was translated correctly.
  315. var frames = stackTrace.split('\n');
  316. assertEquals(frames[0], '> fn1 at file1:1:2');
  317. assertEquals(frames[1], '> fn2 at file2:3:4');
  318. assertEquals(frames[2], '> fn3 at file3:5:6');
  319. }