258bb9a8a2bb78eb797aa15c6b78b3d30978c75b
[WebKit-https.git] / LayoutTests / inspector / unit-tests / heap-snapshot.html
1 <!doctype html>
2 <html>
3 <head>
4 <script src="../../http/tests/inspector/resources/inspector-test.js"></script>
5 <script>
6 function test()
7 {
8     // Simple HeapSnapshot representation.
9
10     WebInspector.TestHeapSnapshotNode = class TestHeapSnapshotNode
11     {
12         constructor(identifier, className, size, internal)
13         {
14             this.id = identifier;
15             this.className = className;
16             this.size = size; 
17             this.internal = internal;
18             this.gcRoot = false;
19             this.outgoingEdges = [];
20             this.incomingEdges = [];
21         }
22     }
23
24     WebInspector.TestHeapSnapshotEdge = class TestHeapSnapshotEdge
25     {
26         constructor(from, to, type, data)
27         {
28             this.from = from;
29             this.to = to;
30             this.type = type;
31             this.data = data;
32         }
33     };
34
35     WebInspector.TestHeapSnapshot = class TestHeapSnapshot
36     {
37         constructor(rootNode, nodes, nodeMap)
38         {
39             console.assert(rootNode instanceof WebInspector.TestHeapSnapshotNode);
40             console.assert(nodes.every((n) => n instanceof WebInspector.TestHeapSnapshotNode));
41
42             this.rootNode = rootNode;
43             this.nodes = nodes;
44             this.nodeMap = nodeMap;
45             this.totalSize = nodes.reduce((sum, node) => sum += node.size, 0);
46             this.totalObjectCount = nodes.length;
47         }
48
49         static fromPayload(payload)
50         {
51             let {version, nodes, nodeClassNames, edges, edgeTypes, edgeNames} = payload;
52             console.assert(version === 1, "Only know how to handle JavaScriptCore Heap Snapshot Format Version 1");
53
54             let nodeMap = new Map;
55
56             // Turn nodes into real nodes.
57             let processedNodes = [];
58             for (let i = 0, length = nodes.length; i < length;) {
59                 let id = nodes[i++];
60                 let size = nodes[i++];
61                 let classNameIndex = nodes[i++];
62                 let internal = nodes[i++];
63
64                 let node = new WebInspector.TestHeapSnapshotNode(id, nodeClassNames[classNameIndex], size, !!internal);
65                 nodeMap.set(id, node);
66                 processedNodes.push(node);
67             }
68
69             // Turn edges into real edges and set them on the nodes.
70             for (let i = 0, length = edges.length; i < length;) {
71                 let fromIdentifier = edges[i++];
72                 let toIdentifier = edges[i++];
73                 let edgeTypeIndex = edges[i++];
74                 let data = edges[i++];
75
76                 let from = nodeMap.get(fromIdentifier);
77                 let to = nodeMap.get(toIdentifier);
78                 let type = edgeTypes[edgeTypeIndex];
79                 if (type === "Property" || type === "Variable")
80                     data = edgeNames[data];
81
82                 let edge = new WebInspector.TestHeapSnapshotEdge(from, to, type, data);
83                 from.outgoingEdges.push(edge);
84                 to.incomingEdges.push(edge);
85             }
86
87             // Root node.
88             let rootNode = nodeMap.get(0);
89             console.assert(rootNode, "Node with identifier 0 is the synthetic <root> node.");
90             console.assert(rootNode.outgoingEdges.length > 0, "This had better have children!");
91             console.assert(rootNode.incomingEdges.length === 0, "This had better not have back references!");
92
93             // Mark GC roots.
94             let rootNodeEdges = rootNode.outgoingEdges;
95             for (let i = 0, length = rootNodeEdges.length; i < length; ++i)
96                 rootNodeEdges[i].to.gcRoot = true;
97
98             return new WebInspector.TestHeapSnapshot(rootNode, processedNodes, nodeMap);
99         }
100
101         // Public
102
103         instancesWithClassName(className)
104         {
105             let results = [];
106             for (let i = 0; i < this.nodes.length; ++i) {
107                 let node = this.nodes[i];
108                 if (node.className === className)
109                     results.push(node);
110             }
111             return results;
112         }
113     };
114
115     // ------
116
117     let suite = InspectorTest.createAsyncSuite("HeapSnapshot");
118
119     let snapshot = null;
120     let snapshotNodeForWindowObject = null;
121     let testSnapshot = null;
122     let testSnapshotNodeForWindowObject = null;
123
124     function compareNodes(node1, node2) {
125         return node1.id === node2.id
126             && node1.size === node2.size
127             && node1.className === node2.className
128             && node1.internal === node2.internal
129             && node1.gcRoot === node2.gcRoot;
130     }
131
132     suite.addTestCase({
133         name: "HeapSnapshotProxy data",
134         test: (resolve, reject) => {
135             HeapAgent.snapshot((error, timestamp, snapshotStringData) => {
136                 InspectorTest.expectThat(!error, "Should not have an error creating a snapshot.");
137                 testSnapshot = WebInspector.TestHeapSnapshot.fromPayload(JSON.parse(snapshotStringData));
138                 let workerProxy = WebInspector.HeapSnapshotWorkerProxy.singleton();
139                 workerProxy.createSnapshot(snapshotStringData, ({objectId, snapshot: serializedSnapshot}) => {
140                     snapshot = WebInspector.HeapSnapshotProxy.deserialize(objectId, serializedSnapshot);
141                     InspectorTest.assert(testSnapshot, "Created TestHeapSnapshot");
142                     InspectorTest.assert(snapshot, "Created HeapSnapshotProxy");
143                     InspectorTest.expectThat(snapshot.totalSize === testSnapshot.totalSize, "Snapshots totalSize should match.");
144                     InspectorTest.expectThat(snapshot.totalObjectCount === testSnapshot.totalObjectCount, "Snapshots totalObjectCount should match.");
145                     resolve();
146                 });
147             });
148         }
149     });
150
151     suite.addTestCase({
152         name: "HeapSnapshotProxy.prototype.instancesWithClassName",
153         test: (resolve, reject) => {
154             let windowObjects = testSnapshot.instancesWithClassName("Window")
155             let windowObjectCount = windowObjects.length;
156             let functionObjectCount = testSnapshot.instancesWithClassName("Function").length;
157             let stringCount = testSnapshot.instancesWithClassName("string").length;
158
159             snapshot.instancesWithClassName("Window", (windows) => {
160                 testSnapshotNodeForWindowObject = windowObjects[0]; // Used by later tests.
161                 snapshotNodeForWindowObject = windows[0]; // Used by later tests.
162
163                 InspectorTest.expectThat(windows.length > 0, "Should be at least 1 Window.");
164                 InspectorTest.expectThat(windows.length === windowObjectCount, "Window object count is expected.");
165                 InspectorTest.expectThat(windows.every((node) => node.className === "Window"), "Every className should be 'Window'.");
166             });
167
168             snapshot.instancesWithClassName("Function", (functions) => {
169                 InspectorTest.expectThat(functions.length > 0, "Should be at least 1 Function.");
170                 InspectorTest.expectThat(functions.length === functionObjectCount, "Function object count is expected.");
171                 InspectorTest.expectThat(functions.every((node) => node.className === "Function"), "Every className should be 'Function'.");
172             });
173
174             snapshot.instancesWithClassName("string", (strings) => {
175                 InspectorTest.expectThat(strings.length > 0, "Should be at least 1 string.");
176                 InspectorTest.expectThat(strings.length === stringCount, "string count is expected.");
177                 InspectorTest.expectThat(strings.every((node) => node.className === "string"), "Every className should be 'string'.");
178                 resolve();
179             });
180         }
181     });
182
183     suite.addTestCase({
184         name: "HeapSnapshotProxy.prototype.nodeWithIdentifier and HeapSnapshotNodeProxy data",
185         test: (resolve, reject) => {
186             snapshot.nodeWithIdentifier(testSnapshotNodeForWindowObject.id, (heapSnapshotNode) => {
187                 InspectorTest.expectThat(heapSnapshotNode.className === "Window", "Node className should be 'Window'.");
188                 InspectorTest.expectThat(heapSnapshotNode.id === testSnapshotNodeForWindowObject.id, "Node identifier should match.")
189                 InspectorTest.expectThat(heapSnapshotNode.size === testSnapshotNodeForWindowObject.size, "Node size should match.");
190                 InspectorTest.expectThat(heapSnapshotNode.internal === testSnapshotNodeForWindowObject.internal, "Node internal state should match.");
191                 InspectorTest.expectThat(heapSnapshotNode.gcRoot === testSnapshotNodeForWindowObject.gcRoot, "Node gcRoot state should match.");
192                 InspectorTest.expectThat(heapSnapshotNode.retainedSize >= heapSnapshotNode.size, "Node retainedSize should at least be the size.");
193                 resolve();
194             });
195         }
196     });
197
198     suite.addTestCase({
199         name: "HeapSnapshotProxy.prototype.allocationBucketCounts",
200         test: (resolve, reject) => {
201             let testSmall = 0, testMedium = 0, testLarge = 0;
202             const smallSize = 32, mediumSize = 128;
203             for (let {size} of testSnapshot.nodes) {
204                 if (size < smallSize)
205                     testSmall++;
206                 else if (size < mediumSize)
207                     testMedium++;
208                 else
209                     testLarge++;
210             }
211
212             snapshot.allocationBucketCounts([smallSize, mediumSize], (results) => {
213                 let [small, medium, large] = results;
214                 InspectorTest.expectThat(results.length === 3, "Result should have 3 buckets, for small/medium/large.");
215                 InspectorTest.expectThat(small === testSmall, "Small count should match.");
216                 InspectorTest.expectThat(medium === testMedium, "Medium count should match.");
217                 InspectorTest.expectThat(large === testLarge, "Large count should match.");
218                 resolve();
219             });
220         }
221     });
222
223     suite.addTestCase({
224         name: "HeapSnapshotNodeProxy.prototype.retainedNodes",
225         test: (resolve, reject) => {
226             let expectedNodes = testSnapshotNodeForWindowObject.outgoingEdges.map((edge) => edge.to);
227             expectedNodes.sort((a, b) => a.id - b.id);
228
229             snapshotNodeForWindowObject.retainedNodes((nodes) => {
230                 nodes.sort((a, b) => a.id - b.id);
231                 InspectorTest.assert(nodes.length > 0, "Test only makes since if there are retained nodes");
232                 InspectorTest.expectThat(nodes.length === expectedNodes.length, "Number of retained nodes should match.");
233                 InspectorTest.expectThat(nodes.every((node, i) => compareNodes(node, expectedNodes[i])), "Node values should match.");
234                 resolve();
235             });
236         }
237     });
238
239     suite.addTestCase({
240         name: "HeapSnapshotNodeProxy.prototype.retainers",
241         test: (resolve, reject) => {
242             let expectedNodes = testSnapshotNodeForWindowObject.incomingEdges.map((edge) => edge.from);
243             expectedNodes.sort((a, b) => a.id - b.id);
244
245             snapshotNodeForWindowObject.retainers((nodes) => {
246                 nodes.sort((a, b) => a.id - b.id);
247                 InspectorTest.assert(nodes.length > 0, "Test only makes since if there are retainer nodes");
248                 InspectorTest.expectThat(nodes.length === expectedNodes.length, "Number of retainer nodes should match.");
249                 InspectorTest.expectThat(nodes.every((node, i) => compareNodes(node, expectedNodes[i])), "Node values should match.");
250                 resolve();
251             });
252         }
253     });
254
255     suite.runTestCasesAndFinish();
256 }
257 </script>
258 </head>
259 <body onload="runTest()">
260     <p>Testing HeapSnapshot Worker and Proxy objects.</p>
261 </body>
262 </html>