1 var initialize_HeapSnapshotTest = function() {
3 InspectorTest.createHeapSnapshotMockFactories = function() {
5 InspectorTest.createJSHeapSnapshotMockObject = function()
11 _nodeEdgeCountOffset: 2,
17 _nodeTypes: ["hidden", "object"],
18 _edgeTypes: ["element", "property", "shortcut"],
19 _edgeShortcutType: -1,
23 // Represents the following graph:
24 // (numbers in parentheses indicate node offset)
26 // -> A (3) --ac- C (9) -ce- E(15)
30 // -> B (6) -bd- D (12)
40 2, 6, 3, // 0: shortcut 'a' to node 'A'
41 1, 7, 6, // 3: property 'b' to node 'B'
42 0, 1, 6, // 6: element '1' to node 'B'
43 1, 8, 9, // 9: property 'ac' to node 'C'
44 1, 9, 9, // 12: property 'bc' to node 'C'
45 1, 10, 12, // 15: property 'bd' to node 'D'
46 1, 11, 15], // 18: property 'ce' to node 'E'
47 _strings: ["", "A", "B", "C", "D", "E", "a", "b", "ac", "bc", "bd", "ce"],
48 _firstEdgeIndexes: [0, 6, 12, 18, 21, 21, 21],
49 createNode: WebInspector.JSHeapSnapshot.prototype.createNode,
50 createEdge: WebInspector.JSHeapSnapshot.prototype.createEdge,
51 createRetainingEdge: WebInspector.JSHeapSnapshot.prototype.createRetainingEdge
55 InspectorTest.createHeapSnapshotMockRaw = function()
57 // Effectively the same graph as in createJSHeapSnapshotMockObject,
58 // but having full set of fields.
60 // A triple in parentheses indicates node index, self size and
63 // --- A (14,2,2) --ac- C (40,4,10) -ce- E(57,6,6)
65 // "" (1,0,20) 1 --bc-----
67 // --- B (27,3,8) --bd- D (50,5,5)
72 node_fields: ["type", "name", "id", "self_size", "retained_size", "dominator", "edge_count"],
73 node_types: [["hidden", "object"], "", "", "", "", "", ""],
74 edge_fields: ["type", "name_or_index", "to_node"],
75 edge_types: [["element", "property", "shortcut"], "", ""]
80 0, 0, 1, 0, 20, 0, 2, // root (0)
81 1, 1, 2, 2, 2, 0, 2, // A (7)
82 1, 2, 3, 3, 8, 0, 2, // B (14)
83 1, 3, 4, 4, 10, 0, 1, // C (21)
84 1, 4, 5, 5, 5, 14, 0, // D (28)
85 1, 5, 6, 6, 6, 21, 0], // E (35)
88 2, 6, 7, // shortcut 'a' to node 'A'
89 1, 7, 14, // property 'b' to node 'B'
92 0, 1, 14, // element 1 to node 'B'
93 1, 8, 21, // property 'ac' to node 'C'
96 1, 9, 21, // property 'bc' to node 'C'
97 1, 10, 28, // property 'bd' to node 'D'
100 1, 11, 35], // property 'ce' to node 'E'
101 strings: ["", "A", "B", "C", "D", "E", "a", "b", "ac", "bc", "bd", "ce"]
105 InspectorTest._postprocessHeapSnapshotMock = function(mock)
107 mock.nodes = new Uint32Array(mock.nodes);
108 mock.edges = new Uint32Array(mock.edges);
112 InspectorTest.createHeapSnapshotMock = function()
114 return InspectorTest._postprocessHeapSnapshotMock(InspectorTest.createHeapSnapshotMockRaw());
117 InspectorTest.createHeapSnapshotMockWithDOM = function()
119 return InspectorTest._postprocessHeapSnapshotMock({
122 node_fields: ["type", "name", "id", "edge_count"],
123 node_types: [["hidden", "object", "synthetic"], "", "", ""],
124 edge_fields: ["type", "name_or_index", "to_node"],
125 edge_types: [["element", "hidden", "internal"], "", ""]
131 // A tree with Window objects.
133 // |----->Window--->A
135 // |----->Window--->B--->C
137 // (root) hidden --->D--internal / "native"-->N
139 // |----->E H internal
143 /* (root) */ 2, 0, 1, 4,
144 /* Window */ 1, 11, 2, 2,
145 /* Window */ 1, 11, 3, 3,
154 /* N */ 1, 10, 12, 0,
158 /* from (root) */ 0, 1, 4, 0, 2, 8, 0, 3, 12, 0, 4, 16,
159 /* from Window */ 0, 1, 20, 0, 2, 24,
160 /* from Window */ 0, 1, 24, 0, 2, 28, 1, 3, 32,
161 /* from F */ 0, 1, 36,
162 /* from B */ 0, 1, 40,
163 /* from D */ 2, 12, 44, 2, 1, 48
165 strings: ["", "A", "B", "C", "D", "E", "F", "G", "H", "M", "N", "Window", "native"]
169 InspectorTest.HeapNode = function(name, selfSize, type, id)
171 this._type = type || InspectorTest.HeapNode.Type.object;
173 this._selfSize = selfSize || 0;
174 this._builder = null;
176 this._edgesCount = 0;
180 InspectorTest.HeapNode.Type = {
186 "closure": "closure",
192 InspectorTest.HeapNode.prototype = {
193 linkNode: function(node, type, nameOrIndex)
196 throw new Error("parent node is not connected to a snapshot");
199 node._setBuilder(this._builder);
201 if (nameOrIndex === undefined)
202 nameOrIndex = this._edgesCount;
205 if (nameOrIndex in this._edges)
206 throw new Error("Can't add edge with the same nameOrIndex. nameOrIndex: " + nameOrIndex + " oldNodeName: " + this._edges[nameOrIndex]._name + " newNodeName: " + node._name);
207 this._edges[nameOrIndex] = new InspectorTest.HeapEdge(node, type, nameOrIndex);
210 _setBuilder: function(builder)
213 throw new Error("node reusing is prohibited");
215 this._builder = builder;
216 this._ordinal = this._builder._registerNode(this);
219 _serialize: function(rawSnapshot)
221 rawSnapshot.nodes.push(this._builder.lookupNodeType(this._type));
222 rawSnapshot.nodes.push(this._builder.lookupOrAddString(this._name));
223 // JS engine snapshot impementation generates monotonicaly increasing odd id for JS objects,
224 // and even ids based on a hash for native DOMObject groups.
225 rawSnapshot.nodes.push(this._id || this._ordinal * 2 + 1);
226 rawSnapshot.nodes.push(this._selfSize);
227 rawSnapshot.nodes.push(0); // retained_size
228 rawSnapshot.nodes.push(0); // dominator
229 rawSnapshot.nodes.push(Object.keys(this._edges).length); // edge_count
231 for (var i in this._edges)
232 this._edges[i]._serialize(rawSnapshot);
236 InspectorTest.HeapEdge = function(targetNode, type, nameOrIndex)
238 this._targetNode = targetNode;
240 this._nameOrIndex = nameOrIndex;
243 InspectorTest.HeapEdge.prototype = {
244 _serialize: function(rawSnapshot)
246 if (!this._targetNode._builder)
247 throw new Error("Inconsistent state of node: " + this._name + " no builder assigned");
248 var builder = this._targetNode._builder;
249 rawSnapshot.edges.push(builder.lookupEdgeType(this._type));
250 rawSnapshot.edges.push(typeof this._nameOrIndex === "string" ? builder.lookupOrAddString(this._nameOrIndex) : this._nameOrIndex);
251 rawSnapshot.edges.push(this._targetNode._ordinal * builder.nodeFieldsCount); // index
255 InspectorTest.HeapEdge.Type = {
256 "context": "context",
257 "element": "element",
258 "property": "property",
259 "internal": "internal",
261 "shortcut": "shortcut"
264 InspectorTest.HeapSnapshotBuilder = function()
267 this._string2id = {};
269 this.nodeFieldsCount = 7;
271 this._nodeTypesMap = {};
272 this._nodeTypesArray = [];
273 for (var nodeType in InspectorTest.HeapNode.Type) {
274 this._nodeTypesMap[nodeType] = this._nodeTypesArray.length
275 this._nodeTypesArray.push(nodeType);
278 this._edgeTypesMap = {};
279 this._edgeTypesArray = [];
280 for (var edgeType in InspectorTest.HeapEdge.Type) {
281 this._edgeTypesMap[edgeType] = this._edgeTypesArray.length
282 this._edgeTypesArray.push(edgeType);
285 this.rootNode = new InspectorTest.HeapNode("root", 0, "object");
286 this.rootNode._setBuilder(this);
289 InspectorTest.HeapSnapshotBuilder.prototype = {
290 generateSnapshot: function()
295 "node_fields": ["type","name","id","self_size","retained_size","dominator","edge_count"],
297 this._nodeTypesArray,
305 "edge_fields": ["type","name_or_index","to_node"],
307 this._edgeTypesArray,
318 for (var i = 0; i < this._nodes.length; ++i)
319 this._nodes[i]._serialize(rawSnapshot);
321 rawSnapshot.strings = this._strings.slice();
323 var meta = rawSnapshot.snapshot.meta;
324 rawSnapshot.snapshot.edge_count = rawSnapshot.edges.length / meta.edge_fields.length;
325 rawSnapshot.snapshot.node_count = rawSnapshot.nodes.length / meta.node_fields.length;
330 _registerNode: function(node)
332 this._nodes.push(node);
333 return this._nodes.length - 1;
336 lookupNodeType: function(typeName)
338 if (typeName === undefined)
339 throw new Error("wrong node type: " + typeName);
340 if (!typeName in this._nodeTypesMap)
341 throw new Error("wrong node type name: " + typeName);
342 return this._nodeTypesMap[typeName];
345 lookupEdgeType: function(typeName)
347 if (!typeName in this._edgeTypesMap)
348 throw new Error("wrong edge type name: " + typeName);
349 return this._edgeTypesMap[typeName];
352 lookupOrAddString: function(string)
354 if (string in this._string2id)
355 return this._string2id[string];
356 this._string2id[string] = this._strings.length;
357 this._strings.push(string);
358 return this._strings.length - 1;
362 InspectorTest.createHeapSnapshot = function(instanceCount, firstId)
364 // Mocking results of running the following code:
366 // function A() { this.a = this; }
367 // function B() { this.a = new A(); }
368 // for (var i = 0; i < instanceCount; ++i) window[i] = new B();
370 // Set A and B object sizes to pseudo random numbers. It is used in sorting tests.
373 function pseudoRandom(limit) {
374 seed = ((seed * 355109 + 153763) >> 2) & 0xffff;
378 var builder = new InspectorTest.HeapSnapshotBuilder();
379 var rootNode = builder.rootNode;
381 var gcRootsNode = new InspectorTest.HeapNode("(GC roots)");
382 rootNode.linkNode(gcRootsNode, InspectorTest.HeapEdge.Type.element);
384 var windowNode = new InspectorTest.HeapNode("Window", 20);
385 rootNode.linkNode(windowNode, InspectorTest.HeapEdge.Type.shortcut);
386 gcRootsNode.linkNode(windowNode, InspectorTest.HeapEdge.Type.element);
388 for (var i = 0; i < instanceCount; ++i) {
389 var sizeOfB = pseudoRandom(20) + 1;
390 var nodeB = new InspectorTest.HeapNode("B", sizeOfB, undefined, firstId++);
391 windowNode.linkNode(nodeB, InspectorTest.HeapEdge.Type.element);
392 var sizeOfA = pseudoRandom(50) + 1;
393 var nodeA = new InspectorTest.HeapNode("A", sizeOfA, undefined, firstId++);
394 nodeB.linkNode(nodeA, InspectorTest.HeapEdge.Type.property, "a");
395 nodeA.linkNode(nodeA, InspectorTest.HeapEdge.Type.property, "a");
397 return builder.generateSnapshot();
402 InspectorTest.createHeapSnapshotMockFactories();
405 InspectorTest.startProfilerTest = function(callback)
407 WebInspector.showPanel("profiles");
408 WebInspector.settings.showAdvancedHeapSnapshotProperties.set(true);
410 InspectorTest.addResult("Profiler was enabled.");
411 // We mock out HeapProfilerAgent -- as DRT runs in single-process mode, Inspector
412 // and test share the same heap. Taking a snapshot takes too long for a test,
413 // so we provide synthetic snapshots.
414 InspectorTest._panelReset = InspectorTest.override(WebInspector.panels.profiles, "_reset", function(){}, true);
415 InspectorTest.addSniffer(WebInspector.HeapSnapshotView.prototype, "show", InspectorTest._snapshotViewShown, true);
417 // Reduce the number of populated nodes to speed up testing.
418 WebInspector.HeapSnapshotContainmentDataGrid.prototype.defaultPopulateCount = function() { return 10; };
419 WebInspector.HeapSnapshotConstructorsDataGrid.prototype.defaultPopulateCount = function() { return 10; };
420 WebInspector.HeapSnapshotDiffDataGrid.prototype.defaultPopulateCount = function() { return 5; };
421 WebInspector.HeapSnapshotDominatorsDataGrid.prototype.defaultPopulateCount = function() { return 3; }
422 InspectorTest.addResult("Detailed heap profiles were enabled.");
423 InspectorTest.safeWrap(callback)();
426 InspectorTest.completeProfilerTest = function()
428 // There is no way to disable detailed heap profiles.
429 InspectorTest.addResult("");
430 InspectorTest.addResult("Profiler was disabled.");
431 InspectorTest.completeTest();
434 InspectorTest.runHeapSnapshotTestSuite = function(testSuite)
436 var testSuiteTests = testSuite.slice();
437 var completeTestStack;
441 if (!testSuiteTests.length) {
442 if (completeTestStack)
443 InspectorTest.addResult("FAIL: test already completed at " + completeTestStack);
444 InspectorTest.completeProfilerTest();
445 completeTestStack = new Error().stack;
449 var nextTest = testSuiteTests.shift();
450 InspectorTest.addResult("");
451 InspectorTest.addResult("Running: " + /function\s([^(]*)/.exec(nextTest)[1]);
452 InspectorTest._panelReset.call(WebInspector.panels.profiles);
453 InspectorTest.safeWrap(nextTest)(runner, runner);
456 InspectorTest.startProfilerTest(runner);
459 InspectorTest.assertColumnContentsEqual = function(reference, actual)
461 var length = Math.min(reference.length, actual.length);
462 for (var i = 0; i < length; ++i)
463 InspectorTest.assertEquals(reference[i], actual[i], "row " + i);
464 if (reference.length > length)
465 InspectorTest.addResult("extra rows in reference array:\n" + reference.slice(length).join("\n"));
466 else if (actual.length > length)
467 InspectorTest.addResult("extra rows in actual array:\n" + actual.slice(length).join("\n"));
470 InspectorTest.checkArrayIsSorted = function(contents, sortType, sortOrder)
472 function simpleComparator(a, b)
474 return a < b ? -1 : (a > b ? 1 : 0);
476 function parseSize(size)
478 if (size.substr(0, 1) === '"') size = JSON.parse(size);
479 // Remove thousands separator.
480 return parseInt(size.replace(/[\u2009,]/g, ""));
482 function extractField(data, field)
484 if (data.substr(0, 1) !== "{") return data;
485 data = JSON.parse(data);
487 InspectorTest.addResult("No " + field + " field in " + JSON.stringify(data));
490 function extractId(data)
492 return parseInt(extractField(data, "nodeId"));
495 text: function (data) { return extractField(data, "value"); },
496 number: function (data) { return parseInt(data, 10); },
497 size: function (data) { return parseSize(data); },
498 name: function (data) { return extractField(data, "name"); },
499 id: function (data) { return extractId(data); }
501 var acceptableComparisonResult = {
507 InspectorTest.addResult("Invalid sort type: " + sortType);
510 if (!acceptableComparisonResult) {
511 InspectorTest.addResult("Invalid sort order: " + sortOrder);
515 for (var i = 0; i < contents.length - 1; ++i) {
516 var a = extractor(contents[i]);
517 var b = extractor(contents[i + 1]);
518 var result = simpleComparator(a, b);
519 if (result !== 0 && result !== acceptableComparisonResult)
520 InspectorTest.addResult("Elements " + i + " and " + (i + 1) + " are out of order: " + a + " " + b + " (" + sortOrder + ")");
524 InspectorTest.clickColumn = function(column, callback)
526 callback = InspectorTest.safeWrap(callback);
527 var cell = this._currentGrid()._headerTableHeaders[column.identifier];
528 var event = { target: { enclosingNodeOrSelfWithNodeName: function() { return cell; } } };
530 function sortingComplete()
532 InspectorTest._currentGrid().removeEventListener("sorting complete", sortingComplete, this);
533 InspectorTest.assertEquals(column.identifier, this._currentGrid().sortColumnIdentifier(), "unexpected sorting");
534 column.sort = this._currentGrid().sortOrder();
535 function callCallback()
539 setTimeout(callCallback, 0);
541 InspectorTest._currentGrid().addEventListener("sorting complete", sortingComplete, this);
542 this._currentGrid()._clickInHeaderCell(event);
545 InspectorTest.clickRowAndGetRetainers = function(row, callback)
547 callback = InspectorTest.safeWrap(callback);
550 enclosingNodeOrSelfWithNodeName: function() { return row._element; },
554 this._currentGrid()._mouseDownInDataTable(event);
555 var rootNode = InspectorTest.currentProfileView().retainmentDataGrid.rootNode();
556 function populateComplete()
558 rootNode.removeEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, this);
561 rootNode.addEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, this);
564 InspectorTest.clickShowMoreButton = function(buttonName, row, callback)
566 callback = InspectorTest.safeWrap(callback);
567 var parent = row.parent;
568 function populateComplete()
570 parent.removeEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, this);
571 function callCallback()
575 setTimeout(callCallback, 0);
577 parent.addEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, this);
578 row[buttonName].click();
581 InspectorTest.columnContents = function(column, row)
583 // Make sure invisible nodes are removed from the view port.
584 this._currentGrid().updateVisibleNodes();
586 var parent = row || this._currentGrid().rootNode();
587 for (var node = parent.children[0]; node; node = node.traverseNextNode(true, parent, true)) {
588 if (!node.selectable)
590 var data = node.data[column.identifier];
591 if (typeof data === "object")
592 data = JSON.stringify(data);
598 InspectorTest.countDataRows = function(row, filter)
601 filter = filter || function(node) { return node.selectable; };
602 for (var node = row.children[0]; node; node = node.traverseNextNode(true, row, true)) {
609 InspectorTest.expandRow = function(row, callback)
611 callback = InspectorTest.safeWrap(callback);
612 function populateComplete()
614 row.removeEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, this);
615 function callCallback()
619 setTimeout(callCallback, 0);
621 row.addEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, this);
627 setTimeout(expand, 0);
631 InspectorTest.findAndExpandGCRoots = function(callback)
633 InspectorTest.findAndExpandRow("(GC roots)", callback);
636 InspectorTest.findAndExpandWindow = function(callback)
638 InspectorTest.findAndExpandRow("Window", callback);
641 InspectorTest.findAndExpandRow = function(name, callback)
643 callback = InspectorTest.safeWrap(callback);
644 function propertyMatcher(data)
646 return data.value === name;
648 var row = InspectorTest.findRow("object", propertyMatcher);
649 InspectorTest.assertEquals(true, !!row, '"' + name + '" row');
650 InspectorTest.expandRow(row, callback);
653 InspectorTest.findButtonsNode = function(row, startNode)
656 for (var node = startNode || row.children[0]; node; node = node.traverseNextNode(true, row, true)) {
657 if (!node.selectable && node.showNext)
663 InspectorTest.findRow = function(columnIdentifier, matcher, parent)
665 parent = parent || this._currentGrid().rootNode();
666 if (typeof matcher !== "function") {
668 matcher = function(x) { return x === value; };
670 for (var node = parent.children[0]; node; node = node.traverseNextNode(true, parent, true)) {
671 if (matcher(node.data[columnIdentifier]))
677 InspectorTest.findRow2 = function(matcher, parent)
679 parent = parent || this._currentGrid().rootNode();
680 for (var node = parent.children[0]; node; node = node.traverseNextNode(true, parent, true)) {
681 if (matcher(node.data))
687 InspectorTest.switchToView = function(title, callback)
689 callback = InspectorTest.safeWrap(callback);
690 var view = WebInspector.panels.profiles.visibleView;
691 view.changeView(title, callback);
694 InspectorTest.takeAndOpenSnapshot = function(generator, callback)
696 callback = InspectorTest.safeWrap(callback);
697 var snapshot = generator();
698 var profileType = WebInspector.ProfileTypeRegistry.instance.heapSnapshotProfileType;
699 function pushGeneratedSnapshot(reportProgress, callback)
701 var profile = profileType.profileBeingRecorded();
702 if (reportProgress) {
703 profileType.reportHeapSnapshotProgress(50, 100, false);
704 profileType.reportHeapSnapshotProgress(100, 100, true);
706 snapshot.snapshot.typeId = "HEAP";
707 profileType.addHeapSnapshotChunk(JSON.stringify(snapshot));
708 setTimeout(callback, 0);
710 InspectorTest.override(HeapProfilerAgent, "takeHeapSnapshot", pushGeneratedSnapshot);
711 InspectorTest._takeAndOpenSnapshotCallback = callback;
712 profileType._takeHeapSnapshot(function() { });
715 InspectorTest.viewColumns = function()
717 return InspectorTest._currentGrid()._columnsArray;
720 InspectorTest.currentProfileView = function()
722 return WebInspector.panels.profiles.visibleView;
725 InspectorTest._currentGrid = function()
727 return this.currentProfileView().dataGrid;
730 InspectorTest._snapshotViewShown = function()
732 if (InspectorTest._takeAndOpenSnapshotCallback) {
733 var callback = InspectorTest._takeAndOpenSnapshotCallback;
734 InspectorTest._takeAndOpenSnapshotCallback = null;
735 var dataGrid = this.dataGrid;
736 function sortingComplete()
738 dataGrid.removeEventListener("sorting complete", sortingComplete, null);
741 dataGrid.addEventListener("sorting complete", sortingComplete, null);