5370226c5dcee01e701bd68c8465975dccd61b6e
[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.value)
126             InspectorTest.addResult("No value field in " + JSON.stringify(data));
127         var indexOfAt = data.value.indexOf("@");
128         if (indexOfAt === -1)
129             InspectorTest.addResult("Can't find @ in " + data.value);
130         return parseInt(data.value.substring(indexOfAt + 1), 10);
131     }
132     var comparator = {
133         text: simpleComparator,
134         number: function (a, b) { return simpleComparator(parseInt(a, 10), parseInt(b, 10)); },
135         size: function (a, b) { return simpleComparator(parseSize(a), parseSize(b)); },
136         name: function (a, b) { return simpleComparator(extractName(a), extractName(b)); },
137         id: function (a, b) { return simpleComparator(extractId(a), extractId(b)); }
138     }[sortType];
139     var acceptableComparisonResult = {
140         ascending: -1,
141         descending: 1
142     }[sortOrder];
143
144     if (!comparator) {
145         InspectorTest.addResult("Invalid sort type: " + sortType);
146         return;
147     }
148     if (!acceptableComparisonResult) {
149         InspectorTest.addResult("Invalid sort order: " + sortOrder);
150         return;
151     }
152
153     for (var i = 0; i < contents.length - 1; ++i) {
154         var result = comparator(contents[i], contents[i + 1]);
155         if (result !== 0 && result !== acceptableComparisonResult)
156             InspectorTest.addResult("Elements " + i + " and " + (i + 1) + " are out of order: " + contents[i] + " " + contents[i + 1] + " (" + sortOrder + ")");
157     }
158 };
159
160 InspectorTest.clickColumn = function(column, callback)
161 {
162     callback = InspectorTest.safeWrap(callback);
163     var cell = this._currentGrid()._headerTableHeaders[column.identifier];
164     var event = { target: { enclosingNodeOrSelfWithNodeName: function() { return cell; } } };
165
166     function sortingComplete()
167     {
168         InspectorTest._currentGrid().removeEventListener("sorting complete", sortingComplete, this);
169         InspectorTest.assertEquals(column.identifier, this._currentGrid().sortColumnIdentifier, "unexpected sorting");
170         column.sort = this._currentGrid().sortOrder;
171         function callCallback()
172         {
173             callback(column);
174         }
175         setTimeout(callCallback, 0);
176     }
177     InspectorTest._currentGrid().addEventListener("sorting complete", sortingComplete, this);
178     this._currentGrid()._clickInHeaderCell(event);
179 };
180
181 InspectorTest.clickShowMoreButton = function(buttonName, row, callback)
182 {
183     callback = InspectorTest.safeWrap(callback);
184     var parent = row.parent;
185     function populateComplete()
186     {
187         parent.removeEventListener("populate complete", populateComplete, this);
188         function callCallback()
189         {
190             callback(parent);
191         }
192         setTimeout(callCallback, 0);
193     }
194     parent.addEventListener("populate complete", populateComplete, this);
195     row[buttonName].click();
196 };
197
198 InspectorTest.columnContents = function(column, row)
199 {
200     var result = [];
201     var parent = row || this._currentGrid();
202     for (var node = parent.children[0]; node; node = node.traverseNextNode(true, parent, true)) {
203         if (!node.selectable)
204             continue;
205         var data = node.data[column.identifier];
206         if (typeof data === "object")
207             data = JSON.stringify(data);
208         result.push(data);
209     }
210     return result;
211 };
212
213 InspectorTest.countDataRows = function(row, filter)
214 {
215     var result = 0;
216     filter = filter || function(node) { return node.selectable; };
217     for (var node = row.children[0]; node; node = node.traverseNextNode(true, row, true)) {
218         if (filter(node))
219             ++result;
220     }
221     return result;
222 };
223
224 InspectorTest.createHeapSnapshot = function(instanceCount, firstId)
225 {
226     // Mocking results of running the following code:
227     // 
228     // function A() { this.a = this; }
229     // function B(x) { this.a = new A(x); }
230     // for (var i = 0; i < instanceCount; ++i) new B();
231     // 
232     // Instances of A have 12 bytes size, instances of B has 16 bytes size.
233     var sizeOfA = 12;
234     var sizeOfB = 16;  
235
236     function generateNodes()
237     {
238         var nodes = [null];
239         // Push the 'meta-root' node.
240         nodes.push(0, 0, 1, 0, (sizeOfA + sizeOfB) * instanceCount, 1, 1, 4, 1, null);
241         // Push instances of A and B.
242         var indexesOfB = [];
243         var nextId = firstId || 5;
244         for (var i = 0; i < instanceCount; ++i) {
245             var indexOfA = nodes.length;
246             nodes.push(3, 1, nextId++, sizeOfA, sizeOfA, null, 1, 2, 3, indexOfA);
247             var indexOfB = nodes.length;
248             // Set dominator of A.
249             nodes[indexOfA + 5] = indexOfB;
250             nodes.push(3, 2, nextId++, sizeOfB, sizeOfB + sizeOfA, null, 1, 2, 3, indexOfA);
251             indexesOfB.push(indexOfB);
252         }
253         var indexOfGCRoots = nodes.length;
254         nodes.push(3, 4, 3, 0, (sizeOfA + sizeOfB) * instanceCount, 1, instanceCount);
255         // Set dominator of B.
256         for (var i = 0; i < instanceCount; ++i) {
257             nodes[indexesOfB[i] + 5] = indexOfGCRoots;
258         }
259         // Set (GC roots) as child of meta-root.
260         nodes[10] = indexOfGCRoots;
261         // Push instances of B as children of GC roots.
262         for (var i = 0; i < instanceCount; ++i) {
263             nodes.push(1, i + 1, indexesOfB[i]);
264         }
265         return nodes;
266     }
267
268     var result = {
269         "snapshot": {},
270         "nodes": generateNodes(),
271         "strings": ["", "A", "B", "a", "(GC roots)"]
272     };
273     result.nodes[0] = {
274         "fields":["type","name","id","self_size","retained_size","dominator","children_count","children"],
275         "types":[["hidden","array","string","object","code","closure","regexp","number","native"],"string","number","number","number","number","number",{
276             "fields":["type","name_or_index","to_node"],
277             "types":[["context","element","property","internal","hidden","shortcut"],"string_or_number","node"]}]};
278     return result;
279 };
280
281 InspectorTest.expandRow = function(row, callback)
282 {
283     callback = InspectorTest.safeWrap(callback);
284     function populateComplete()
285     {
286         row.removeEventListener("populate complete", populateComplete, this);
287         function callCallback()
288         {
289             callback(row);
290         }
291         setTimeout(callCallback, 0);
292     }
293     row.addEventListener("populate complete", populateComplete, this);
294     (function expand()
295     {
296         if (row.hasChildren)
297             row.expand();
298         else
299             setTimeout(expand, 0);
300     })();
301 };
302
303 InspectorTest.findAndExpandGCRoots = function(callback)
304 {
305     callback = InspectorTest.safeWrap(callback);
306     function propertyMatcher(data)
307     {
308         return data.value === "(GC roots): @3";
309     }
310     var gcRoots = InspectorTest.findRow("object", propertyMatcher);
311     InspectorTest.assertEquals(true, !!gcRoots, "GC roots row");
312     InspectorTest.expandRow(gcRoots, callback);
313 }
314
315 InspectorTest.findButtonsNode = function(row, startNode)
316 {
317     var result = 0;
318     for (var node = startNode || row.children[0]; node; node = node.traverseNextNode(true, row, true)) {
319         if (!node.selectable && node.showNext)
320             return node;
321     }
322     return null;
323 };
324
325 InspectorTest.findRow = function(columnIdentifier, matcher, parent)
326 {
327     parent = parent || this._currentGrid();
328     if (typeof matcher !== "function") {
329         var value = matcher;
330         matcher = function(x) { return x === value; };
331     }
332     for (var node = parent.children[0]; node; node = node.traverseNextNode(true, parent, true)) {
333         if (matcher(node.data[columnIdentifier]))
334             return node;
335     }
336     return null;
337 };
338
339 InspectorTest.findRow2 = function(matcher, parent)
340 {
341     parent = parent || this._currentGrid();
342     for (var node = parent.children[0]; node; node = node.traverseNextNode(true, parent, true)) {
343         if (matcher(node.data))
344             return node;
345     }
346     return null;
347 };
348
349 InspectorTest.switchToView = function(title, callback)
350 {
351     callback = InspectorTest.safeWrap(callback);
352     var view = WebInspector.panels.profiles.visibleView;
353     view.changeView(title, callback);
354 };
355
356 InspectorTest.takeAndOpenSnapshot = function(generator, callback)
357 {
358     callback = InspectorTest.safeWrap(callback);
359     var uid = InspectorTest._nextUid++;
360     var profile = { typeId: WebInspector.DetailedHeapshotProfileType.TypeId, uid: uid, title: UserInitiatedProfileName + "." + uid };
361     function pushGeneratedSnapshot(typeId, uid)
362     {
363         var snapshot = generator();
364         snapshot.snapshot.typeId = profile.typeId;
365         snapshot.snapshot.title = profile.title;
366         snapshot.snapshot.uid = profile.uid;
367         WebInspector.panels.profiles._addHeapSnapshotChunk(uid, JSON.stringify(snapshot));
368         WebInspector.panels.profiles._finishHeapSnapshot(uid);
369     }
370     InspectorTest.override(ProfilerAgent, "getProfile", pushGeneratedSnapshot);
371     InspectorTest._takeAndOpenSnapshotCallback = callback;
372     WebInspector.panels.profiles.addProfileHeader(profile);
373     WebInspector.panels.profiles.showProfile(profile);
374 };
375
376 InspectorTest.viewColumns = function()
377 {
378     return InspectorTest._currentGrid()._columnsArray;
379 };
380
381 InspectorTest._currentGrid = function()
382 {
383     return WebInspector.panels.profiles.visibleView.dataGrid;
384 };
385
386 InspectorTest._snapshotViewShown = function()
387 {
388     if (InspectorTest._takeAndOpenSnapshotCallback) {
389         var callback = InspectorTest._takeAndOpenSnapshotCallback;
390         InspectorTest._takeAndOpenSnapshotCallback = null;
391         var dataGrid = this.dataGrid;
392         function sortingComplete()
393         {
394             dataGrid.removeEventListener("sorting complete", sortingComplete, null);
395             callback();
396         }
397         dataGrid.addEventListener("sorting complete", sortingComplete, null);
398     }
399 };
400
401 };