123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374 |
- // Copyright 2009 The Closure Library Authors. All Rights Reserved.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS-IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- goog.provide('goog.testing.stacktraceTest');
- goog.setTestOnly('goog.testing.stacktraceTest');
- goog.require('goog.functions');
- goog.require('goog.string');
- goog.require('goog.testing.ExpectedFailures');
- goog.require('goog.testing.PropertyReplacer');
- goog.require('goog.testing.StrictMock');
- goog.require('goog.testing.asserts');
- goog.require('goog.testing.jsunit');
- goog.require('goog.testing.stacktrace');
- goog.require('goog.testing.stacktrace.Frame');
- goog.require('goog.userAgent');
- var stubs = new goog.testing.PropertyReplacer();
- var expectedFailures;
- function setUpPage() {
- expectedFailures = new goog.testing.ExpectedFailures();
- }
- function setUp() {
- stubs.set(goog.testing.stacktrace, 'isClosureInspectorActive_', function() {
- return false;
- });
- }
- function tearDown() {
- stubs.reset();
- expectedFailures.handleTearDown();
- }
- function testParseStackFrameInV8() {
- var frameString = ' at Error (unknown source)';
- var frame = goog.testing.stacktrace.parseStackFrame_(frameString);
- var expected = new goog.testing.stacktrace.Frame('', 'Error', '', '');
- assertObjectEquals('exception name only', expected, frame);
- frameString = ' at Object.assert (file:///.../asserts.js:29:10)';
- frame = goog.testing.stacktrace.parseStackFrame_(frameString);
- expected = new goog.testing.stacktrace.Frame(
- 'Object', 'assert', '', 'file:///.../asserts.js:29:10');
- assertObjectEquals('context object + function name + url', expected, frame);
- frameString = ' at Object.x.y.z (/Users/bob/file.js:564:9)';
- frame = goog.testing.stacktrace.parseStackFrame_(frameString);
- expected = new goog.testing.stacktrace.Frame(
- 'Object.x.y', 'z', '', '/Users/bob/file.js:564:9');
- assertObjectEquals(
- 'nested context object + function name + url', expected, frame);
- frameString = ' at http://www.example.com/jsunit.js:117:13';
- frame = goog.testing.stacktrace.parseStackFrame_(frameString);
- expected = new goog.testing.stacktrace.Frame(
- '', '', '', 'http://www.example.com/jsunit.js:117:13');
- assertObjectEquals('url only', expected, frame);
- frameString = ' at [object Object].exec [as execute] (file:///foo)';
- frame = goog.testing.stacktrace.parseStackFrame_(frameString);
- expected = new goog.testing.stacktrace.Frame(
- '[object Object]', 'exec', 'execute', 'file:///foo');
- assertObjectEquals('function alias', expected, frame);
- frameString = ' at new Class (file:///foo)';
- frame = goog.testing.stacktrace.parseStackFrame_(frameString);
- expected =
- new goog.testing.stacktrace.Frame('', 'new Class', '', 'file:///foo');
- assertObjectEquals('constructor call', expected, frame);
- frameString = ' at new <anonymous> (file:///foo)';
- frame = goog.testing.stacktrace.parseStackFrame_(frameString);
- expected = new goog.testing.stacktrace.Frame(
- '', 'new <anonymous>', '', 'file:///foo');
- assertObjectEquals('anonymous constructor call', expected, frame);
- frameString = ' at Array.forEach (native)';
- frame = goog.testing.stacktrace.parseStackFrame_(frameString);
- expected = new goog.testing.stacktrace.Frame('Array', 'forEach', '', '');
- assertObjectEquals('native function call', expected, frame);
- frameString = ' at foo (eval at file://bar)';
- frame = goog.testing.stacktrace.parseStackFrame_(frameString);
- expected =
- new goog.testing.stacktrace.Frame('', 'foo', '', 'eval at file://bar');
- assertObjectEquals('eval', expected, frame);
- frameString = ' at foo.bar (closure/goog/foo.js:11:99)';
- frame = goog.testing.stacktrace.parseStackFrame_(frameString);
- expected = new goog.testing.stacktrace.Frame(
- 'foo', 'bar', '', 'closure/goog/foo.js:11:99');
- assertObjectEquals('Path without schema', expected, frame);
- // In the Chrome console, execute: console.log(eval('Error().stack')).
- frameString =
- ' at eval (eval at <anonymous> (unknown source), <anonymous>:1:1)';
- frame = goog.testing.stacktrace.parseStackFrame_(frameString);
- expected = new goog.testing.stacktrace.Frame(
- '', 'eval', '', 'eval at <anonymous> (unknown source), <anonymous>:1:1');
- assertObjectEquals('nested eval', expected, frame);
- }
- function testParseStackFrameInOpera() {
- var frameString = '@';
- var frame = goog.testing.stacktrace.parseStackFrame_(frameString);
- var expected = new goog.testing.stacktrace.Frame('', '', '', '');
- assertObjectEquals('empty frame', expected, frame);
- frameString = '@javascript:console.log(Error().stack):1';
- frame = goog.testing.stacktrace.parseStackFrame_(frameString);
- expected = new goog.testing.stacktrace.Frame(
- '', '', '', 'javascript:console.log(Error().stack):1');
- assertObjectEquals('javascript path only', expected, frame);
- frameString = '@file:///foo:42';
- frame = goog.testing.stacktrace.parseStackFrame_(frameString);
- expected = new goog.testing.stacktrace.Frame('', '', '', 'file:///foo:42');
- assertObjectEquals('path only', expected, frame);
- // (function go() { throw Error() })()
- // var c = go; c()
- frameString = 'go([arguments not available])@';
- frame = goog.testing.stacktrace.parseStackFrame_(frameString);
- expected = new goog.testing.stacktrace.Frame('', 'go', '', '');
- assertObjectEquals('name and empty path', expected, frame);
- frameString = 'go([arguments not available])@file:///foo:42';
- frame = goog.testing.stacktrace.parseStackFrame_(frameString);
- expected = new goog.testing.stacktrace.Frame('', 'go', '', 'file:///foo:42');
- assertObjectEquals('name and path', expected, frame);
- // (function() { throw Error() })()
- frameString =
- '<anonymous function>([arguments not available])@file:///foo:42';
- frame = goog.testing.stacktrace.parseStackFrame_(frameString);
- expected = new goog.testing.stacktrace.Frame('', '', '', 'file:///foo:42');
- assertObjectEquals('anonymous function', expected, frame);
- // var b = {foo: function() { throw Error() }}
- frameString = '<anonymous function: foo>()@file:///foo:42';
- frame = goog.testing.stacktrace.parseStackFrame_(frameString);
- expected = new goog.testing.stacktrace.Frame('', 'foo', '', 'file:///foo:42');
- assertObjectEquals('object literal function', expected, frame);
- // var c = {}; c.foo = function() { throw Error() }
- frameString = '<anonymous function: c.foo>()@file:///foo:42';
- frame = goog.testing.stacktrace.parseStackFrame_(frameString);
- expected =
- new goog.testing.stacktrace.Frame('c', 'foo', '', 'file:///foo:42');
- assertObjectEquals('named object literal function', expected, frame);
- frameString = '<anonymous function: Foo.prototype.bar>()@';
- frame = goog.testing.stacktrace.parseStackFrame_(frameString);
- expected = new goog.testing.stacktrace.Frame('Foo.prototype', 'bar', '', '');
- assertObjectEquals('prototype function', expected, frame);
- frameString = '<anonymous function: goog.Foo.prototype.bar>()@';
- frame = goog.testing.stacktrace.parseStackFrame_(frameString);
- expected =
- new goog.testing.stacktrace.Frame('goog.Foo.prototype', 'bar', '', '');
- assertObjectEquals('namespaced prototype function', expected, frame);
- }
- // All test strings are parsed with the conventional and long
- // frame algorithms.
- function testParseStackFrameInFirefox() {
- var frameString = 'Error("Assertion failed")@:0';
- var frame = goog.testing.stacktrace.parseStackFrame_(frameString);
- var expected = new goog.testing.stacktrace.Frame('', 'Error', '', '');
- assertObjectEquals('function name + arguments', expected, frame);
- frameString = '()@file:///foo:42';
- frame = goog.testing.stacktrace.parseStackFrame_(frameString);
- expected = new goog.testing.stacktrace.Frame('', '', '', 'file:///foo:42');
- assertObjectEquals('anonymous function', expected, frame);
- frameString = '@javascript:alert(0)';
- frame = goog.testing.stacktrace.parseStackFrame_(frameString);
- expected =
- new goog.testing.stacktrace.Frame('', '', '', 'javascript:alert(0)');
- assertObjectEquals('anonymous function', expected, frame);
- }
- // All test strings are parsed with the conventional and long
- // frame algorithms.
- function testParseStackFrameInFirefoxWithQualifiedName() {
- var frameString = 'ns.method@http://some.thing/a.js:1:2';
- var frame = goog.testing.stacktrace.parseStackFrame_(frameString);
- var expected = new goog.testing.stacktrace.Frame(
- '', 'ns.method', '', 'http://some.thing/a.js:1:2');
- assertObjectEquals('anonymous function', expected, frame);
- }
- function testCanonicalizeFrame() {
- var frame = new goog.testing.stacktrace.Frame(
- '<window>', 'foo', 'bar', 'http://x?a=1&b=2:1');
- assertEquals(
- 'canonical stack frame, everything is escaped', '<window>.foo ' +
- '[as bar] at http://x?a=1&b=2:1',
- frame.toCanonicalString());
- }
- function testDeobfuscateFunctionName() {
- goog.testing.stacktrace.setDeobfuscateFunctionName(function(name) {
- return name.replace(/\$/g, '.');
- });
- var frame = new goog.testing.stacktrace.Frame('', 'a$b$c', 'd$e', '');
- assertEquals(
- 'deobfuscated function name', 'a.b.c [as d.e]',
- frame.toCanonicalString());
- }
- function testFramesToString() {
- var normalFrame = new goog.testing.stacktrace.Frame('', 'foo', '', '');
- var anonFrame = new goog.testing.stacktrace.Frame('', '', '', '');
- var frames = [normalFrame, anonFrame, null, anonFrame];
- var stack = goog.testing.stacktrace.framesToString_(frames);
- assertEquals('framesToString', '> foo\n> anonymous\n> (unknown)\n', stack);
- }
- function testFollowCallChain() {
- var func = function(var_args) {
- return goog.testing.stacktrace.followCallChain_();
- };
- // Created a fake type with a toString method.
- function LocalType(){};
- LocalType.prototype.toString = function() { return 'sg'; };
- // Create a mock with no expectations.
- var mock = new goog.testing.StrictMock(LocalType);
- mock.$replay();
- var frames = func(
- undefined, null, false, 0, '', {}, goog.nullFunction, mock,
- new LocalType);
- // Opera before version 10 doesn't support the caller attribute. In that
- // browser followCallChain_() returns empty stack trace.
- expectedFailures.expectFailureFor(
- goog.userAgent.OPERA && !goog.userAgent.isVersionOrHigher('10'));
- try {
- assertTrue('The stack trace consists of >=2 frames', frames.length >= 2);
- } catch (e) {
- expectedFailures.handleException(e);
- }
- if (frames.length >= 2) {
- assertEquals('innermost function is anonymous', '', frames[0].getName());
- // There are white space differences how browsers convert functions to
- // strings.
- assertEquals(
- 'test function name', 'testFollowCallChain', frames[1].getName());
- }
- mock.$verify();
- }
- // Create a stack trace string with one modest record and one long record,
- // Verify that all frames are parsed. The length of the long arg is set
- // to blow Firefox 3x's stack if put through a RegExp.
- function testParsingLongStackTrace() {
- var longArg =
- goog.string.buildString('(', goog.string.repeat('x', 1000000), ')');
- var stackTrace = goog.string.buildString(
- 'shortFrame()@:0\n', 'longFrame', longArg,
- '@http://google.com/somescript:0\n');
- var frames = goog.testing.stacktrace.parse_(stackTrace);
- assertEquals('number of returned frames', 2, frames.length);
- var expected = new goog.testing.stacktrace.Frame('', 'shortFrame', '', '');
- assertObjectEquals('short frame', expected, frames[0]);
- assertNull('exception no frame', frames[1]);
- }
- function testParseStackFrameInIE10() {
- var frameString = ' at foo (http://bar:4000/bar.js:150:3)';
- var frame = goog.testing.stacktrace.parseStackFrame_(frameString);
- var expected = new goog.testing.stacktrace.Frame(
- '', 'foo', '', 'http://bar:4000/bar.js:150:3');
- assertObjectEquals('name and path', expected, frame);
- frameString = ' at Anonymous function (http://bar:4000/bar.js:150:3)';
- frame = goog.testing.stacktrace.parseStackFrame_(frameString);
- expected = new goog.testing.stacktrace.Frame(
- '', 'Anonymous function', '', 'http://bar:4000/bar.js:150:3');
- assertObjectEquals('Anonymous function', expected, frame);
- frameString = ' at Global code (http://bar:4000/bar.js:150:3)';
- frame = goog.testing.stacktrace.parseStackFrame_(frameString);
- expected = new goog.testing.stacktrace.Frame(
- '', 'Global code', '', 'http://bar:4000/bar.js:150:3');
- assertObjectEquals('Global code', expected, frame);
- frameString = ' at foo (eval code:150:3)';
- frame = goog.testing.stacktrace.parseStackFrame_(frameString);
- expected =
- new goog.testing.stacktrace.Frame('', 'foo', '', 'eval code:150:3');
- assertObjectEquals('eval code', expected, frame);
- frameString = ' at eval code (eval code:150:3)';
- frame = goog.testing.stacktrace.parseStackFrame_(frameString);
- expected =
- new goog.testing.stacktrace.Frame('', 'eval code', '', 'eval code:150:3');
- assertObjectEquals('nested eval', expected, frame);
- }
- function testParseStackFrameInIE11() {
- var frameString = ' at a.b.c (Unknown script code:150:3)';
- var frame = goog.testing.stacktrace.parseStackFrame_(frameString);
- var expected = new goog.testing.stacktrace.Frame(
- '', 'a.b.c', '', 'Unknown script code:150:3');
- assertObjectEquals('name and path', expected, frame);
- }
- function testParseStackFrameInEdge() {
- frameString = ' at a.b.c (http://host.com:80/some/file.js:101:2)';
- frame = goog.testing.stacktrace.parseStackFrame_(frameString);
- expected = new goog.testing.stacktrace.Frame(
- '', 'a.b.c', '', 'http://host.com:80/some/file.js:101:2');
- assertObjectEquals(expected, frame);
- }
- // Verifies that retrieving the stack trace works when the 'stack' field of an
- // exception contains an array of CallSites instead of a string. This is the
- // case when running in a lightweight V8 runtime (for instance, in gjstests),
- // as opposed to a browser environment.
- function testGetStackFrameWithV8CallSites() {
- // A function to create V8 CallSites. Note that CallSite is an extern and thus
- // cannot be mocked with closure mocks.
- function createCallSite(functionName, fileName, lineNumber, colNumber) {
- return {
- getFunctionName: goog.functions.constant(functionName),
- getFileName: goog.functions.constant(fileName),
- getLineNumber: goog.functions.constant(lineNumber),
- getColumnNumber: goog.functions.constant(colNumber)
- };
- }
- // Mock the goog.testing.stacktrace.getStack_ function, which normally
- // triggers an exception for the purpose of reading and returning its stack
- // trace. Here, pretend that V8 provided an array of CallSites instead of the
- // string that browsers provide.
- stubs.set(goog.testing.stacktrace, 'getNativeStack_', function() {
- return [
- createCallSite('fn1', 'file1', 1, 2),
- createCallSite('fn2', 'file2', 3, 4), createCallSite('fn3', 'file3', 5, 6)
- ];
- });
- // Retrieve the stacktrace. This should translate the array of CallSites into
- // a single multi-line string.
- var stackTrace = goog.testing.stacktrace.get();
- // Make sure the stack trace was translated correctly.
- var frames = stackTrace.split('\n');
- assertEquals(frames[0], '> fn1 at file1:1:2');
- assertEquals(frames[1], '> fn2 at file2:3:4');
- assertEquals(frames[2], '> fn3 at file3:5:6');
- }
|