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)
32 nodes: new Uint32Array([
39 containmentEdges: new Uint32Array([
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: new Uint32Array([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 (7,2,2) --ac- C (21,4,10) -ce- E(35,6,6)
65 // "" (0,0,20) 1 --bc-----
67 // --- B (14,3,8) --bd- D (28,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",
190 "synthetic": "synthetic"
193 InspectorTest.HeapNode.prototype = {
194 linkNode: function(node, type, nameOrIndex)
197 throw new Error("parent node is not connected to a snapshot");
200 node._setBuilder(this._builder);
202 if (nameOrIndex === undefined)
203 nameOrIndex = this._edgesCount;
206 if (nameOrIndex in this._edges)
207 throw new Error("Can't add edge with the same nameOrIndex. nameOrIndex: " + nameOrIndex + " oldNodeName: " + this._edges[nameOrIndex]._name + " newNodeName: " + node._name);
208 this._edges[nameOrIndex] = new InspectorTest.HeapEdge(node, type, nameOrIndex);
211 _setBuilder: function(builder)
214 throw new Error("node reusing is prohibited");
216 this._builder = builder;
217 this._ordinal = this._builder._registerNode(this);
220 _serialize: function(rawSnapshot)
222 rawSnapshot.nodes.push(this._builder.lookupNodeType(this._type));
223 rawSnapshot.nodes.push(this._builder.lookupOrAddString(this._name));
224 // JS engine snapshot impementation generates monotonicaly increasing odd id for JS objects,
225 // and even ids based on a hash for native DOMObject groups.
226 rawSnapshot.nodes.push(this._id || this._ordinal * 2 + 1);
227 rawSnapshot.nodes.push(this._selfSize);
228 rawSnapshot.nodes.push(0); // retained_size
229 rawSnapshot.nodes.push(0); // dominator
230 rawSnapshot.nodes.push(Object.keys(this._edges).length); // edge_count
232 for (var i in this._edges)
233 this._edges[i]._serialize(rawSnapshot);
237 InspectorTest.HeapEdge = function(targetNode, type, nameOrIndex)
239 this._targetNode = targetNode;
241 this._nameOrIndex = nameOrIndex;
244 InspectorTest.HeapEdge.prototype = {
245 _serialize: function(rawSnapshot)
247 if (!this._targetNode._builder)
248 throw new Error("Inconsistent state of node: " + this._name + " no builder assigned");
249 var builder = this._targetNode._builder;
250 rawSnapshot.edges.push(builder.lookupEdgeType(this._type));
251 rawSnapshot.edges.push(typeof this._nameOrIndex === "string" ? builder.lookupOrAddString(this._nameOrIndex) : this._nameOrIndex);
252 rawSnapshot.edges.push(this._targetNode._ordinal * builder.nodeFieldsCount); // index
256 InspectorTest.HeapEdge.Type = {
257 "context": "context",
258 "element": "element",
259 "property": "property",
260 "internal": "internal",
262 "shortcut": "shortcut"
265 InspectorTest.HeapSnapshotBuilder = function()
268 this._string2id = {};
270 this.nodeFieldsCount = 7;
272 this._nodeTypesMap = {};
273 this._nodeTypesArray = [];
274 for (var nodeType in InspectorTest.HeapNode.Type) {
275 this._nodeTypesMap[nodeType] = this._nodeTypesArray.length
276 this._nodeTypesArray.push(nodeType);
279 this._edgeTypesMap = {};
280 this._edgeTypesArray = [];
281 for (var edgeType in InspectorTest.HeapEdge.Type) {
282 this._edgeTypesMap[edgeType] = this._edgeTypesArray.length
283 this._edgeTypesArray.push(edgeType);
286 this.rootNode = new InspectorTest.HeapNode("root", 0, "object");
287 this.rootNode._setBuilder(this);
290 InspectorTest.HeapSnapshotBuilder.prototype = {
291 generateSnapshot: function()
296 "node_fields": ["type","name","id","self_size","retained_size","dominator","edge_count"],
298 this._nodeTypesArray,
306 "edge_fields": ["type","name_or_index","to_node"],
308 this._edgeTypesArray,
319 for (var i = 0; i < this._nodes.length; ++i)
320 this._nodes[i]._serialize(rawSnapshot);
322 rawSnapshot.strings = this._strings.slice();
324 var meta = rawSnapshot.snapshot.meta;
325 rawSnapshot.snapshot.edge_count = rawSnapshot.edges.length / meta.edge_fields.length;
326 rawSnapshot.snapshot.node_count = rawSnapshot.nodes.length / meta.node_fields.length;
331 createJSHeapSnapshot: function()
333 var parsedSnapshot = InspectorTest._postprocessHeapSnapshotMock(this.generateSnapshot());
334 return new WebInspector.JSHeapSnapshot(parsedSnapshot, new WebInspector.HeapSnapshotProgress());
337 _registerNode: function(node)
339 this._nodes.push(node);
340 return this._nodes.length - 1;
343 lookupNodeType: function(typeName)
345 if (typeName === undefined)
346 throw new Error("wrong node type: " + typeName);
347 if (!typeName in this._nodeTypesMap)
348 throw new Error("wrong node type name: " + typeName);
349 return this._nodeTypesMap[typeName];
352 lookupEdgeType: function(typeName)
354 if (!typeName in this._edgeTypesMap)
355 throw new Error("wrong edge type name: " + typeName);
356 return this._edgeTypesMap[typeName];
359 lookupOrAddString: function(string)
361 if (string in this._string2id)
362 return this._string2id[string];
363 this._string2id[string] = this._strings.length;
364 this._strings.push(string);
365 return this._strings.length - 1;
369 InspectorTest.createHeapSnapshot = function(instanceCount, firstId)
371 // Mocking results of running the following code:
373 // function A() { this.a = this; }
374 // function B() { this.a = new A(); }
375 // for (var i = 0; i < instanceCount; ++i) window[i] = new B();
377 // Set A and B object sizes to pseudo random numbers. It is used in sorting tests.
380 function pseudoRandom(limit) {
381 seed = ((seed * 355109 + 153763) >> 2) & 0xffff;
385 var builder = new InspectorTest.HeapSnapshotBuilder();
386 var rootNode = builder.rootNode;
388 var gcRootsNode = new InspectorTest.HeapNode("(GC roots)", 0, InspectorTest.HeapNode.Type.synthetic);
389 rootNode.linkNode(gcRootsNode, InspectorTest.HeapEdge.Type.element);
391 var windowNode = new InspectorTest.HeapNode("Window", 20);
392 rootNode.linkNode(windowNode, InspectorTest.HeapEdge.Type.shortcut);
393 gcRootsNode.linkNode(windowNode, InspectorTest.HeapEdge.Type.element);
395 for (var i = 0; i < instanceCount; ++i) {
396 var sizeOfB = pseudoRandom(20) + 1;
397 var nodeB = new InspectorTest.HeapNode("B", sizeOfB, undefined, firstId++);
398 windowNode.linkNode(nodeB, InspectorTest.HeapEdge.Type.element);
399 var sizeOfA = pseudoRandom(50) + 1;
400 var nodeA = new InspectorTest.HeapNode("A", sizeOfA, undefined, firstId++);
401 nodeB.linkNode(nodeA, InspectorTest.HeapEdge.Type.property, "a");
402 nodeA.linkNode(nodeA, InspectorTest.HeapEdge.Type.property, "a");
404 return builder.generateSnapshot();
409 InspectorTest.createHeapSnapshotMockFactories();
412 InspectorTest.startProfilerTest = function(callback)
414 WebInspector.inspectorView.showPanel("profiles");
415 WebInspector.settings.showAdvancedHeapSnapshotProperties.set(true);
417 InspectorTest.addResult("Profiler was enabled.");
418 // We mock out HeapProfilerAgent -- as DRT runs in single-process mode, Inspector
419 // and test share the same heap. Taking a snapshot takes too long for a test,
420 // so we provide synthetic snapshots.
421 InspectorTest._panelReset = InspectorTest.override(WebInspector.panels.profiles, "_reset", function(){}, true);
422 InspectorTest.addSniffer(WebInspector.HeapSnapshotView.prototype, "show", InspectorTest._snapshotViewShown, true);
424 // Reduce the number of populated nodes to speed up testing.
425 WebInspector.HeapSnapshotContainmentDataGrid.prototype.defaultPopulateCount = function() { return 10; };
426 WebInspector.HeapSnapshotConstructorsDataGrid.prototype.defaultPopulateCount = function() { return 10; };
427 WebInspector.HeapSnapshotDiffDataGrid.prototype.defaultPopulateCount = function() { return 5; };
428 InspectorTest.addResult("Detailed heap profiles were enabled.");
429 InspectorTest.safeWrap(callback)();
432 InspectorTest.completeProfilerTest = function()
434 // There is no way to disable detailed heap profiles.
435 InspectorTest.addResult("");
436 InspectorTest.addResult("Profiler was disabled.");
437 InspectorTest.completeTest();
440 InspectorTest.runHeapSnapshotTestSuite = function(testSuite)
442 var testSuiteTests = testSuite.slice();
443 var completeTestStack;
447 if (!testSuiteTests.length) {
448 if (completeTestStack)
449 InspectorTest.addResult("FAIL: test already completed at " + completeTestStack);
450 InspectorTest.completeProfilerTest();
451 completeTestStack = new Error().stack;
455 var nextTest = testSuiteTests.shift();
456 InspectorTest.addResult("");
457 InspectorTest.addResult("Running: " + /function\s([^(]*)/.exec(nextTest)[1]);
458 InspectorTest._panelReset.call(WebInspector.panels.profiles);
459 InspectorTest.safeWrap(nextTest)(runner, runner);
462 InspectorTest.startProfilerTest(runner);
465 InspectorTest.assertColumnContentsEqual = function(reference, actual)
467 var length = Math.min(reference.length, actual.length);
468 for (var i = 0; i < length; ++i)
469 InspectorTest.assertEquals(reference[i], actual[i], "row " + i);
470 if (reference.length > length)
471 InspectorTest.addResult("extra rows in reference array:\n" + reference.slice(length).join("\n"));
472 else if (actual.length > length)
473 InspectorTest.addResult("extra rows in actual array:\n" + actual.slice(length).join("\n"));
476 InspectorTest.checkArrayIsSorted = function(contents, sortType, sortOrder)
478 function simpleComparator(a, b)
480 return a < b ? -1 : (a > b ? 1 : 0);
482 function parseSize(size)
484 // Remove thousands separator.
485 return parseInt(size.replace(/[\u2009,]/g, ""), 10);
488 text: function (data) { data; },
489 number: function (data) { return parseInt(data, 10); },
491 name: function (data) { return data; },
492 id: function (data) { return parseInt(data, 10); }
496 InspectorTest.addResult("Invalid sort type: " + sortType);
500 var acceptableComparisonResult;
501 if (sortOrder === WebInspector.DataGrid.Order.Ascending) {
502 acceptableComparisonResult = -1;
503 } else if (sortOrder === WebInspector.DataGrid.Order.Descending) {
504 acceptableComparisonResult = 1;
506 InspectorTest.addResult("Invalid sort order: " + sortOrder);
510 for (var i = 0; i < contents.length - 1; ++i) {
511 var a = extractor(contents[i]);
512 var b = extractor(contents[i + 1]);
513 var result = simpleComparator(a, b);
514 if (result !== 0 && result !== acceptableComparisonResult) {
515 InspectorTest.addResult("Elements " + i + " and " + (i + 1) + " are out of order: " + a + " " + b + " (" + sortOrder + ")");
520 InspectorTest.clickColumn = function(column, callback)
522 callback = InspectorTest.safeWrap(callback);
523 var cell = this._currentGrid()._headerTableHeaders[column.identifier];
524 var event = { target: { enclosingNodeOrSelfWithNodeName: function() { return cell; } } };
526 function sortingComplete()
528 InspectorTest._currentGrid().removeEventListener(WebInspector.HeapSnapshotSortableDataGrid.Events.SortingComplete, sortingComplete, this);
529 InspectorTest.assertEquals(column.identifier, this._currentGrid().sortColumnIdentifier(), "unexpected sorting");
530 column.sort = this._currentGrid().sortOrder();
531 function callCallback()
535 setTimeout(callCallback, 0);
537 InspectorTest._currentGrid().addEventListener(WebInspector.HeapSnapshotSortableDataGrid.Events.SortingComplete, sortingComplete, this);
538 this._currentGrid()._clickInHeaderCell(event);
541 InspectorTest.clickRowAndGetRetainers = function(row, callback)
543 callback = InspectorTest.safeWrap(callback);
546 enclosingNodeOrSelfWithNodeName: function() { return row._element; },
550 this._currentGrid()._mouseDownInDataTable(event);
551 var rootNode = InspectorTest.currentProfileView()._retainmentDataGrid.rootNode();
552 function populateComplete()
554 rootNode.removeEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, this);
557 rootNode.addEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, this);
560 InspectorTest.clickShowMoreButton = function(buttonName, row, callback)
562 callback = InspectorTest.safeWrap(callback);
563 var parent = row.parent;
564 function populateComplete()
566 parent.removeEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, this);
567 function callCallback()
571 setTimeout(callCallback, 0);
573 parent.addEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, this);
574 row[buttonName].click();
577 InspectorTest.columnContents = function(column, row)
579 // Make sure invisible nodes are removed from the view port.
580 this._currentGrid().updateVisibleNodes();
581 var columnOrdinal = InspectorTest.viewColumns().indexOf(column);
583 var parent = row || this._currentGrid().rootNode();
584 for (var node = parent.children[0]; node; node = node.traverseNextNode(true, parent, true)) {
585 if (!node.selectable)
587 var content = node.element().children[columnOrdinal];
588 // Do not inlcude percents
589 if (content.firstElementChild)
590 content = content.firstElementChild;
591 result.push(content.textContent);
596 InspectorTest.countDataRows = function(row, filter)
599 filter = filter || function(node) { return node.selectable; };
600 for (var node = row.children[0]; node; node = node.traverseNextNode(true, row, true)) {
607 InspectorTest.expandRow = function(row, callback)
609 callback = InspectorTest.safeWrap(callback);
610 function populateComplete()
612 row.removeEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, this);
613 function callCallback()
617 setTimeout(callCallback, 0);
619 row.addEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, this);
625 setTimeout(expand, 0);
629 InspectorTest.findAndExpandGCRoots = function(callback)
631 InspectorTest.findAndExpandRow("(GC roots)", callback);
634 InspectorTest.findAndExpandWindow = function(callback)
636 InspectorTest.findAndExpandRow("Window", callback);
639 InspectorTest.findAndExpandRow = function(name, callback)
641 callback = InspectorTest.safeWrap(callback);
642 var row = InspectorTest.findRow(name);
643 InspectorTest.assertEquals(true, !!row, '"' + name + '" row');
644 InspectorTest.expandRow(row, callback);
647 InspectorTest.findButtonsNode = function(row, startNode)
650 for (var node = startNode || row.children[0]; node; node = node.traverseNextNode(true, row, true)) {
651 if (!node.selectable && node.showNext)
657 InspectorTest.findRow = function(name, parent)
661 return x._name === name;
663 return InspectorTest.findMatchingRow(matcher, parent);
666 InspectorTest.findMatchingRow = function(matcher, parent)
668 parent = parent || this._currentGrid().rootNode();
669 for (var node = parent.children[0]; node; node = node.traverseNextNode(true, parent, true)) {
676 InspectorTest.switchToView = function(title, callback)
678 callback = InspectorTest.safeWrap(callback);
679 var view = WebInspector.panels.profiles.visibleView;
680 view._changePerspectiveAndWait(title, callback);
681 // Increase the grid container height so the viewport don't limit the number of nodes.
682 InspectorTest._currentGrid().scrollContainer.style.height = "10000px";
685 InspectorTest.takeAndOpenSnapshot = function(generator, callback)
687 callback = InspectorTest.safeWrap(callback);
688 var snapshot = generator();
689 var profileType = WebInspector.ProfileTypeRegistry.instance.heapSnapshotProfileType;
690 function pushGeneratedSnapshot(reportProgress, callback)
692 var profile = profileType.profileBeingRecorded();
693 if (reportProgress) {
694 profileType._reportHeapSnapshotProgress({data: {done: 50, total: 100, finished: false}});
695 profileType._reportHeapSnapshotProgress({data: {done: 100, total: 100, finished: true}});
697 snapshot.snapshot.typeId = "HEAP";
698 profileType._addHeapSnapshotChunk({data: JSON.stringify(snapshot)});
699 setTimeout(callback, 0);
701 InspectorTest.override(HeapProfilerAgent, "takeHeapSnapshot", pushGeneratedSnapshot);
702 InspectorTest._takeAndOpenSnapshotCallback = callback;
703 profileType._takeHeapSnapshot(function() { });
706 InspectorTest.viewColumns = function()
708 return InspectorTest._currentGrid()._columnsArray;
711 InspectorTest.currentProfileView = function()
713 return WebInspector.panels.profiles.visibleView;
716 InspectorTest._currentGrid = function()
718 return this.currentProfileView()._dataGrid;
721 InspectorTest._snapshotViewShown = function()
723 if (InspectorTest._takeAndOpenSnapshotCallback) {
724 var callback = InspectorTest._takeAndOpenSnapshotCallback;
725 InspectorTest._takeAndOpenSnapshotCallback = null;
726 var dataGrid = this._dataGrid;
727 function sortingComplete()
729 dataGrid.removeEventListener(WebInspector.HeapSnapshotSortableDataGrid.Events.SortingComplete, sortingComplete, null);
732 dataGrid.addEventListener(WebInspector.HeapSnapshotSortableDataGrid.Events.SortingComplete, sortingComplete, null);