Web Inspector: Redesign summary view / retaining tree contents
[WebKit-https.git] / LayoutTests / inspector / profiler / detailed-heapshots-test.js
1 var initialize_DetailedHeapshotTest = function() {
2
3 InspectorTest.startProfilerTest = function(callback)
4 {
5     WebInspector.showPanel("profiles");
6     WebInspector.settings.showHeapSnapshotObjectsHiddenProperties.set(true);
7
8     function profilerEnabled()
9     {
10         InspectorTest.addResult("Profiler was enabled.");
11         // We mock out ProfilerAgent -- as DRT runs in single-process mode, Inspector
12         // and test share the same heap. Taking a snapshot takes too long for a test,
13         // so we provide synthetic snapshots.
14         InspectorTest._panelReset = InspectorTest.override(WebInspector.panels.profiles, "_reset", function(){}, true);
15         InspectorTest.addSniffer(WebInspector.DetailedHeapshotView.prototype, "_updatePercentButton", InspectorTest._snapshotViewShown, true);
16
17         detailedHeapProfilesEnabled();
18     }
19
20     function detailedHeapProfilesEnabled()
21     {
22         // Reduce the number of populated nodes to speed up testing.
23         WebInspector.HeapSnapshotContainmentDataGrid.prototype._defaultPopulateCount = 10;
24         WebInspector.HeapSnapshotConstructorsDataGrid.prototype._defaultPopulateCount = 10;
25         WebInspector.HeapSnapshotDiffDataGrid.prototype._defaultPopulateCount = 5;
26         WebInspector.HeapSnapshotDominatorsDataGrid.prototype._defaultPopulateCount = 3;
27         InspectorTest.addResult("Detailed heap profiles were enabled.");
28         InspectorTest.safeWrap(callback)();
29     }
30
31     if (WebInspector.panels.profiles._profilerEnabled)
32         profilerEnabled();
33     else {
34         InspectorTest.addSniffer(WebInspector.panels.profiles, "_profilerWasEnabled", profilerEnabled);
35         WebInspector.panels.profiles._toggleProfiling(false);
36     }
37 };
38
39 InspectorTest.completeProfilerTest = function()
40 {
41     // There is no way to disable detailed heap profiles.
42
43     function completeTest()
44     {
45         InspectorTest.addResult("");
46         InspectorTest.addResult("Profiler was disabled.");
47         InspectorTest.completeTest();
48     }
49
50     var profilesPanel = WebInspector.panels.profiles;
51     if (!profilesPanel._profilerEnabled)
52         completeTest();
53     else {
54         InspectorTest.addSniffer(WebInspector.panels.profiles, "_profilerWasDisabled", completeTest);
55         profilesPanel._toggleProfiling(false);
56     }
57 };
58
59 InspectorTest.runDetailedHeapshotTestSuite = function(testSuite)
60 {
61     if (!Capabilities.heapProfilerPresent) {
62         InspectorTest.addResult("Heap profiler is disabled");
63         InspectorTest.completeTest();
64         return;
65     }
66
67     InspectorTest._nextUid = 1;
68     var testSuiteTests = testSuite.slice();
69
70     function runner()
71     {
72         if (!testSuiteTests.length) {
73             InspectorTest.completeProfilerTest();
74             return;
75         }
76
77         var nextTest = testSuiteTests.shift();
78         InspectorTest.addResult("");
79         InspectorTest.addResult("Running: " + /function\s([^(]*)/.exec(nextTest)[1]);
80         InspectorTest._panelReset.call(WebInspector.panels.profiles);
81         InspectorTest.safeWrap(nextTest)(runner, runner);
82     }
83
84     InspectorTest.startProfilerTest(runner);
85 };
86
87 InspectorTest.assertColumnContentsEqual = function(reference, actual)
88 {
89     var length = Math.min(reference.length, actual.length);
90     for (var i = 0; i < length; ++i)
91         InspectorTest.assertEquals(reference[i], actual[i], "row " + i);
92     if (reference.length > length)
93         InspectorTest.addResult("extra rows in reference array:\n" + reference.slice(length).join("\n"));
94     else if (actual.length > length)
95         InspectorTest.addResult("extra rows in actual array:\n" + actual.slice(length).join("\n"));
96 };
97
98 InspectorTest.checkArrayIsSorted = function(contents, sortType, sortOrder)
99 {
100     function simpleComparator(a, b)
101     {
102         return a < b ? -1 : (a > b ? 1 : 0);
103     }
104     function parseSize(size)
105     {
106         if (size.charAt(0) === ">")
107             size = size.substring(2);
108         var amount = parseFloat(size, 10);
109         var multiplier = {
110             "KB": 1024,
111             "MB": 1024 * 1024
112         }[size.substring(size.length - 2)];
113         return multiplier ? amount * multiplier : amount;
114     }
115     function extractName(data)
116     {
117         data = JSON.parse(data);
118         if (!data.name)
119             InspectorTest.addResult("No name field in " + JSON.stringify(data));
120         return parseInt(data.name, 10);
121     }
122     function extractId(data)
123     {
124         data = JSON.parse(data);
125         if (!data.nodeId)
126             InspectorTest.addResult("No nodeId field in " + JSON.stringify(data));
127         return parseInt(data.nodeId, 10);
128     }
129     var comparator = {
130         text: simpleComparator,
131         number: function (a, b) { return simpleComparator(parseInt(a, 10), parseInt(b, 10)); },
132         size: function (a, b) { return simpleComparator(parseSize(a), parseSize(b)); },
133         name: function (a, b) { return simpleComparator(extractName(a), extractName(b)); },
134         id: function (a, b) { return simpleComparator(extractId(a), extractId(b)); }
135     }[sortType];
136     var acceptableComparisonResult = {
137         ascending: -1,
138         descending: 1
139     }[sortOrder];
140
141     if (!comparator) {
142         InspectorTest.addResult("Invalid sort type: " + sortType);
143         return;
144     }
145     if (!acceptableComparisonResult) {
146         InspectorTest.addResult("Invalid sort order: " + sortOrder);
147         return;
148     }
149
150     for (var i = 0; i < contents.length - 1; ++i) {
151         var result = comparator(contents[i], contents[i + 1]);
152         if (result !== 0 && result !== acceptableComparisonResult)
153             InspectorTest.addResult("Elements " + i + " and " + (i + 1) + " are out of order: " + contents[i] + " " + contents[i + 1] + " (" + sortOrder + ")");
154     }
155 };
156
157 InspectorTest.clickColumn = function(column, callback)
158 {
159     callback = InspectorTest.safeWrap(callback);
160     var cell = this._currentGrid()._headerTableHeaders[column.identifier];
161     var event = { target: { enclosingNodeOrSelfWithNodeName: function() { return cell; } } };
162
163     function sortingComplete()
164     {
165         InspectorTest._currentGrid().removeEventListener("sorting complete", sortingComplete, this);
166         InspectorTest.assertEquals(column.identifier, this._currentGrid().sortColumnIdentifier, "unexpected sorting");
167         column.sort = this._currentGrid().sortOrder;
168         function callCallback()
169         {
170             callback(column);
171         }
172         setTimeout(callCallback, 0);
173     }
174     InspectorTest._currentGrid().addEventListener("sorting complete", sortingComplete, this);
175     this._currentGrid()._clickInHeaderCell(event);
176 };
177
178 InspectorTest.clickShowMoreButton = function(buttonName, row, callback)
179 {
180     callback = InspectorTest.safeWrap(callback);
181     var parent = row.parent;
182     function populateComplete()
183     {
184         parent.removeEventListener("populate complete", populateComplete, this);
185         function callCallback()
186         {
187             callback(parent);
188         }
189         setTimeout(callCallback, 0);
190     }
191     parent.addEventListener("populate complete", populateComplete, this);
192     row[buttonName].click();
193 };
194
195 InspectorTest.columnContents = function(column, row)
196 {
197     var result = [];
198     var parent = row || this._currentGrid();
199     for (var node = parent.children[0]; node; node = node.traverseNextNode(true, parent, true)) {
200         if (!node.selectable)
201             continue;
202         var data = node.data[column.identifier];
203         if (typeof data === "object")
204             data = JSON.stringify(data);
205         result.push(data);
206     }
207     return result;
208 };
209
210 InspectorTest.countDataRows = function(row, filter)
211 {
212     var result = 0;
213     filter = filter || function(node) { return node.selectable; };
214     for (var node = row.children[0]; node; node = node.traverseNextNode(true, row, true)) {
215         if (filter(node))
216             ++result;
217     }
218     return result;
219 };
220
221 InspectorTest.createHeapSnapshot = function(instanceCount, firstId)
222 {
223     // Mocking results of running the following code:
224     // 
225     // function A() { this.a = this; }
226     // function B(x) { this.a = new A(x); }
227     // for (var i = 0; i < instanceCount; ++i) new B();
228     // 
229     // Instances of A have 12 bytes size, instances of B has 16 bytes size.
230     var sizeOfA = 12;
231     var sizeOfB = 16;  
232
233     function generateNodes()
234     {
235         var nodes = [null];
236         // Push the 'meta-root' node.
237         nodes.push(0, 0, 1, 0, (sizeOfA + sizeOfB) * instanceCount, 1, 1, 4, 1, null);
238         // Push instances of A and B.
239         var indexesOfB = [];
240         var nextId = firstId || 5;
241         for (var i = 0; i < instanceCount; ++i) {
242             var indexOfA = nodes.length;
243             nodes.push(3, 1, nextId++, sizeOfA, sizeOfA, null, 1, 2, 3, indexOfA);
244             var indexOfB = nodes.length;
245             // Set dominator of A.
246             nodes[indexOfA + 5] = indexOfB;
247             nodes.push(3, 2, nextId++, sizeOfB, sizeOfB + sizeOfA, null, 1, 2, 3, indexOfA);
248             indexesOfB.push(indexOfB);
249         }
250         var indexOfGCRoots = nodes.length;
251         nodes.push(3, 4, 3, 0, (sizeOfA + sizeOfB) * instanceCount, 1, instanceCount);
252         // Set dominator of B.
253         for (var i = 0; i < instanceCount; ++i) {
254             nodes[indexesOfB[i] + 5] = indexOfGCRoots;
255         }
256         // Set (GC roots) as child of meta-root.
257         nodes[10] = indexOfGCRoots;
258         // Push instances of B as children of GC roots.
259         for (var i = 0; i < instanceCount; ++i) {
260             nodes.push(1, i + 1, indexesOfB[i]);
261         }
262         return nodes;
263     }
264
265     var result = {
266         "snapshot": {},
267         "nodes": generateNodes(),
268         "strings": ["", "A", "B", "a", "(GC roots)"]
269     };
270     result.nodes[0] = {
271         "fields":["type","name","id","self_size","retained_size","dominator","children_count","children"],
272         "types":[["hidden","array","string","object","code","closure","regexp","number","native"],"string","number","number","number","number","number",{
273             "fields":["type","name_or_index","to_node"],
274             "types":[["context","element","property","internal","hidden","shortcut"],"string_or_number","node"]}]};
275     return result;
276 };
277
278 InspectorTest.expandRow = function(row, callback)
279 {
280     callback = InspectorTest.safeWrap(callback);
281     function populateComplete()
282     {
283         row.removeEventListener("populate complete", populateComplete, this);
284         function callCallback()
285         {
286             callback(row);
287         }
288         setTimeout(callCallback, 0);
289     }
290     row.addEventListener("populate complete", populateComplete, this);
291     (function expand()
292     {
293         if (row.hasChildren)
294             row.expand();
295         else
296             setTimeout(expand, 0);
297     })();
298 };
299
300 InspectorTest.findAndExpandGCRoots = function(callback)
301 {
302     callback = InspectorTest.safeWrap(callback);
303     function propertyMatcher(data)
304     {
305         return data.value === "(GC roots)";
306     }
307     var gcRoots = InspectorTest.findRow("object", propertyMatcher);
308     InspectorTest.assertEquals(true, !!gcRoots, "GC roots row");
309     InspectorTest.expandRow(gcRoots, callback);
310 }
311
312 InspectorTest.findButtonsNode = function(row, startNode)
313 {
314     var result = 0;
315     for (var node = startNode || row.children[0]; node; node = node.traverseNextNode(true, row, true)) {
316         if (!node.selectable && node.showNext)
317             return node;
318     }
319     return null;
320 };
321
322 InspectorTest.findRow = function(columnIdentifier, matcher, parent)
323 {
324     parent = parent || this._currentGrid();
325     if (typeof matcher !== "function") {
326         var value = matcher;
327         matcher = function(x) { return x === value; };
328     }
329     for (var node = parent.children[0]; node; node = node.traverseNextNode(true, parent, true)) {
330         if (matcher(node.data[columnIdentifier]))
331             return node;
332     }
333     return null;
334 };
335
336 InspectorTest.findRow2 = function(matcher, parent)
337 {
338     parent = parent || this._currentGrid();
339     for (var node = parent.children[0]; node; node = node.traverseNextNode(true, parent, true)) {
340         if (matcher(node.data))
341             return node;
342     }
343     return null;
344 };
345
346 InspectorTest.switchToView = function(title, callback)
347 {
348     callback = InspectorTest.safeWrap(callback);
349     var view = WebInspector.panels.profiles.visibleView;
350     view.changeView(title, callback);
351 };
352
353 InspectorTest.takeAndOpenSnapshot = function(generator, callback)
354 {
355     callback = InspectorTest.safeWrap(callback);
356     var uid = InspectorTest._nextUid++;
357     var profile = { typeId: WebInspector.DetailedHeapshotProfileType.TypeId, uid: uid, title: UserInitiatedProfileName + "." + uid };
358     function pushGeneratedSnapshot(typeId, uid)
359     {
360         var snapshot = generator();
361         snapshot.snapshot.typeId = profile.typeId;
362         snapshot.snapshot.title = profile.title;
363         snapshot.snapshot.uid = profile.uid;
364         WebInspector.panels.profiles._addHeapSnapshotChunk(uid, JSON.stringify(snapshot));
365         WebInspector.panels.profiles._finishHeapSnapshot(uid);
366     }
367     InspectorTest.override(ProfilerAgent, "getProfile", pushGeneratedSnapshot);
368     InspectorTest._takeAndOpenSnapshotCallback = callback;
369     WebInspector.panels.profiles.addProfileHeader(profile);
370     WebInspector.panels.profiles.showProfile(profile);
371 };
372
373 InspectorTest.viewColumns = function()
374 {
375     return InspectorTest._currentGrid()._columnsArray;
376 };
377
378 InspectorTest._currentGrid = function()
379 {
380     return WebInspector.panels.profiles.visibleView.dataGrid;
381 };
382
383 InspectorTest._snapshotViewShown = function()
384 {
385     if (InspectorTest._takeAndOpenSnapshotCallback) {
386         var callback = InspectorTest._takeAndOpenSnapshotCallback;
387         InspectorTest._takeAndOpenSnapshotCallback = null;
388         var dataGrid = this.dataGrid;
389         function sortingComplete()
390         {
391             dataGrid.removeEventListener("sorting complete", sortingComplete, null);
392             callback();
393         }
394         dataGrid.addEventListener("sorting complete", sortingComplete, null);
395     }
396 };
397
398 };