diff --git a/spec/core/PrettyPrintSpec.js b/spec/core/PrettyPrintSpec.js index 7b7c21ab..9604ada1 100644 --- a/spec/core/PrettyPrintSpec.js +++ b/spec/core/PrettyPrintSpec.js @@ -63,6 +63,13 @@ describe("j$.pp", function () { } }); + it("should stringify immutable circular objects", function(){ + var frozenObject = {foo: {bar: 'baz'}}; + frozenObject.circular = frozenObject; + frozenObject = Object.freeze(frozenObject); + expect(j$.pp(frozenObject)).toEqual("{ foo : { bar : 'baz' }, circular : }"); + }); + it("should stringify RegExp objects properly", function() { expect(j$.pp(/x|y|z/)).toEqual("/x|y|z/"); }); diff --git a/spec/performance/large_object_test.js b/spec/performance/large_object_test.js new file mode 100644 index 00000000..7741decf --- /dev/null +++ b/spec/performance/large_object_test.js @@ -0,0 +1,36 @@ +describe('Printing a big object', function(){ + var bigObject; + function rand(upper) { + return Math.round(upper * Math.random()); + } + + function generateObject(level) { + var object = {}; + + for (var i = 0; i < 50; i++) { + var decide = rand(2); + switch (decide) { + case 0: + object["cycle" + i] = object; + break; + case 1: + object["number" + i] = rand(100); + break; + case 2: + if (level < 3) { + object["nesting" + i] = generateObject(level + 1); + } + break; + } + + } + + return object; + } + + it('takes a resonable amount of time', function(){ + bigObject = generateObject(0); + expect(j$.pp(bigObject)).toMatch(/cycle/); + }); +}); + diff --git a/src/core/PrettyPrinter.js b/src/core/PrettyPrinter.js index 621abac2..2898a3d1 100644 --- a/src/core/PrettyPrinter.js +++ b/src/core/PrettyPrinter.js @@ -2,6 +2,7 @@ getJasmineRequireObj().pp = function(j$) { function PrettyPrinter() { this.ppNestLevel_ = 0; + this.seen = []; } PrettyPrinter.prototype.format = function(value) { @@ -29,16 +30,16 @@ getJasmineRequireObj().pp = function(j$) { this.emitScalar('HTMLNode'); } else if (value instanceof Date) { this.emitScalar('Date(' + value + ')'); - } else if (value.__Jasmine_been_here_before__) { + } else if (j$.util.arrayContains(this.seen, value)) { this.emitScalar(''); } else if (j$.isArray_(value) || j$.isA_('Object', value)) { - value.__Jasmine_been_here_before__ = true; + this.seen.push(value); if (j$.isArray_(value)) { this.emitArray(value); } else { this.emitObject(value); } - delete value.__Jasmine_been_here_before__; + this.seen.pop(); } else { this.emitScalar(value.toString()); } @@ -50,7 +51,6 @@ getJasmineRequireObj().pp = function(j$) { PrettyPrinter.prototype.iterateObject = function(obj, fn) { for (var property in obj) { if (!Object.prototype.hasOwnProperty.call(obj, property)) { continue; } - if (property == '__Jasmine_been_here_before__') { continue; } fn(property, obj.__lookupGetter__ ? (!j$.util.isUndefined(obj.__lookupGetter__(property)) && obj.__lookupGetter__(property) !== null) : false); } diff --git a/src/core/util.js b/src/core/util.js index 5285b8d7..64ad1e33 100644 --- a/src/core/util.js +++ b/src/core/util.js @@ -30,5 +30,15 @@ getJasmineRequireObj().util = function() { return obj === void 0; }; + util.arrayContains = function(array, search) { + var i = array.length; + while (i--) { + if (array[i] == search) { + return true; + } + } + return false; + } + return util; };