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 InspectorTest._nextUid = 1;
437 var testSuiteTests = testSuite.slice();
441 if (!testSuiteTests.length) {
442 InspectorTest.completeProfilerTest();
446 var nextTest = testSuiteTests.shift();
447 InspectorTest.addResult("");
448 InspectorTest.addResult("Running: " + /function\s([^(]*)/.exec(nextTest)[1]);
449 InspectorTest._panelReset.call(WebInspector.panels.profiles);
450 InspectorTest.safeWrap(nextTest)(runner, runner);
453 InspectorTest.startProfilerTest(runner);
456 InspectorTest.assertColumnContentsEqual = function(reference, actual)
458 var length = Math.min(reference.length, actual.length);
459 for (var i = 0; i < length; ++i)
460 InspectorTest.assertEquals(reference[i], actual[i], "row " + i);
461 if (reference.length > length)
462 InspectorTest.addResult("extra rows in reference array:\n" + reference.slice(length).join("\n"));
463 else if (actual.length > length)
464 InspectorTest.addResult("extra rows in actual array:\n" + actual.slice(length).join("\n"));
467 InspectorTest.checkArrayIsSorted = function(contents, sortType, sortOrder)
469 function simpleComparator(a, b)
471 return a < b ? -1 : (a > b ? 1 : 0);
473 function parseSize(size)
475 if (size.substr(0, 1) === '"') size = JSON.parse(size);
476 // Remove thousands separator.
477 return parseInt(size.replace(/[\u2009,]/g, ""));
479 function extractField(data, field)
481 if (data.substr(0, 1) !== "{") return data;
482 data = JSON.parse(data);
484 InspectorTest.addResult("No " + field + " field in " + JSON.stringify(data));
487 function extractId(data)
489 return parseInt(extractField(data, "nodeId"));
492 text: function (data) { return extractField(data, "value"); },
493 number: function (data) { return parseInt(data, 10); },
494 size: function (data) { return parseSize(data); },
495 name: function (data) { return extractField(data, "name"); },
496 id: function (data) { return extractId(data); }
498 var acceptableComparisonResult = {
504 InspectorTest.addResult("Invalid sort type: " + sortType);
507 if (!acceptableComparisonResult) {
508 InspectorTest.addResult("Invalid sort order: " + sortOrder);
512 for (var i = 0; i < contents.length - 1; ++i) {
513 var a = extractor(contents[i]);
514 var b = extractor(contents[i + 1]);
515 var result = simpleComparator(a, b);
516 if (result !== 0 && result !== acceptableComparisonResult)
517 InspectorTest.addResult("Elements " + i + " and " + (i + 1) + " are out of order: " + a + " " + b + " (" + sortOrder + ")");
521 InspectorTest.clickColumn = function(column, callback)
523 callback = InspectorTest.safeWrap(callback);
524 var cell = this._currentGrid()._headerTableHeaders[column.identifier];
525 var event = { target: { enclosingNodeOrSelfWithNodeName: function() { return cell; } } };
527 function sortingComplete()
529 InspectorTest._currentGrid().removeEventListener("sorting complete", sortingComplete, this);
530 InspectorTest.assertEquals(column.identifier, this._currentGrid().sortColumnIdentifier(), "unexpected sorting");
531 column.sort = this._currentGrid().sortOrder();
532 function callCallback()
536 setTimeout(callCallback, 0);
538 InspectorTest._currentGrid().addEventListener("sorting complete", sortingComplete, this);
539 this._currentGrid()._clickInHeaderCell(event);
542 InspectorTest.clickRowAndGetRetainers = function(row, callback)
544 callback = InspectorTest.safeWrap(callback);
547 enclosingNodeOrSelfWithNodeName: function() { return row._element; },
551 this._currentGrid()._mouseDownInDataTable(event);
552 var rootNode = InspectorTest.currentProfileView().retainmentDataGrid.rootNode();
553 function populateComplete()
555 rootNode.removeEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, this);
558 rootNode.addEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, this);
561 InspectorTest.clickShowMoreButton = function(buttonName, row, callback)
563 callback = InspectorTest.safeWrap(callback);
564 var parent = row.parent;
565 function populateComplete()
567 parent.removeEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, this);
568 function callCallback()
572 setTimeout(callCallback, 0);
574 parent.addEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, this);
575 row[buttonName].click();
578 InspectorTest.columnContents = function(column, row)
580 // Make sure invisible nodes are removed from the view port.
581 this._currentGrid().updateVisibleNodes();
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 data = node.data[column.identifier];
588 if (typeof data === "object")
589 data = JSON.stringify(data);
595 InspectorTest.countDataRows = function(row, filter)
598 filter = filter || function(node) { return node.selectable; };
599 for (var node = row.children[0]; node; node = node.traverseNextNode(true, row, true)) {
606 InspectorTest.expandRow = function(row, callback)
608 callback = InspectorTest.safeWrap(callback);
609 function populateComplete()
611 row.removeEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, this);
612 function callCallback()
616 setTimeout(callCallback, 0);
618 row.addEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, this);
624 setTimeout(expand, 0);
628 InspectorTest.findAndExpandGCRoots = function(callback)
630 InspectorTest.findAndExpandRow("(GC roots)", callback);
633 InspectorTest.findAndExpandWindow = function(callback)
635 InspectorTest.findAndExpandRow("Window", callback);
638 InspectorTest.findAndExpandRow = function(name, callback)
640 callback = InspectorTest.safeWrap(callback);
641 function propertyMatcher(data)
643 return data.value === name;
645 var row = InspectorTest.findRow("object", propertyMatcher);
646 InspectorTest.assertEquals(true, !!row, '"' + name + '" row');
647 InspectorTest.expandRow(row, callback);
650 InspectorTest.findButtonsNode = function(row, startNode)
653 for (var node = startNode || row.children[0]; node; node = node.traverseNextNode(true, row, true)) {
654 if (!node.selectable && node.showNext)
660 InspectorTest.findRow = function(columnIdentifier, matcher, parent)
662 parent = parent || this._currentGrid().rootNode();
663 if (typeof matcher !== "function") {
665 matcher = function(x) { return x === value; };
667 for (var node = parent.children[0]; node; node = node.traverseNextNode(true, parent, true)) {
668 if (matcher(node.data[columnIdentifier]))
674 InspectorTest.findRow2 = function(matcher, parent)
676 parent = parent || this._currentGrid().rootNode();
677 for (var node = parent.children[0]; node; node = node.traverseNextNode(true, parent, true)) {
678 if (matcher(node.data))
684 InspectorTest.switchToView = function(title, callback)
686 callback = InspectorTest.safeWrap(callback);
687 var view = WebInspector.panels.profiles.visibleView;
688 view.changeView(title, callback);
691 InspectorTest.takeAndOpenSnapshot = function(generator, callback)
693 callback = InspectorTest.safeWrap(callback);
694 var uid = InspectorTest._nextUid++;
695 var snapshot = generator();
696 var profileType = WebInspector.panels.profiles.getProfileType(WebInspector.HeapSnapshotProfileType.TypeId);
697 var profile = new WebInspector.HeapProfileHeader(profileType, "Mock snapshot #" + uid, uid);
698 function pushGeneratedSnapshot(uid, callback)
700 snapshot.snapshot.typeId = "HEAP";
701 snapshot.snapshot.title = profile.title;
702 snapshot.snapshot.uid = profile.uid;
703 profileType.addHeapSnapshotChunk(uid, JSON.stringify(snapshot));
704 setTimeout(callback, 0);
706 InspectorTest.override(HeapProfilerAgent, "getHeapSnapshot", pushGeneratedSnapshot);
707 InspectorTest._takeAndOpenSnapshotCallback = callback;
708 profileType.addProfile(profile);
709 WebInspector.panels.profiles._showProfile(profile);
712 InspectorTest.viewColumns = function()
714 return InspectorTest._currentGrid()._columnsArray;
717 InspectorTest.currentProfileView = function()
719 return WebInspector.panels.profiles.visibleView;
722 InspectorTest._currentGrid = function()
724 return this.currentProfileView().dataGrid;
727 InspectorTest._snapshotViewShown = function()
729 if (InspectorTest._takeAndOpenSnapshotCallback) {
730 var callback = InspectorTest._takeAndOpenSnapshotCallback;
731 InspectorTest._takeAndOpenSnapshotCallback = null;
732 var dataGrid = this.dataGrid;
733 function sortingComplete()
735 dataGrid.removeEventListener("sorting complete", sortingComplete, null);
738 dataGrid.addEventListener("sorting complete", sortingComplete, null);