blob: d2e8bb00aa8c1b60867c91706576e5af5f6c2405 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
(async function() {
TestRunner.addResult(`This test checks HeapSnapshots module.\n`);
await TestRunner.loadModule('heap_profiler_test_runner');
await TestRunner.showPanel('heap_profiler');
function createTestEnvironmentInWorker() {
if (!this.TestRunner)
TestRunner = {};
if (!this.HeapProfilerTestRunner)
HeapProfilerTestRunner = {};
TestRunner.assertEquals = function(expected, found, message) {
if (expected === found)
return;
var error;
if (message)
error = 'Failure (' + message + '):';
else
error = 'Failure:';
throw new Error(error + ' expected <' + expected + '> found <' + found + '>');
};
}
function runTestSuiteInWorker() {
var testSuite = [
function postOrderIndexBug() {
var builder = new HeapProfilerTestRunner.HeapSnapshotBuilder();
var node1 = new HeapProfilerTestRunner.HeapNode('Node1');
var node2 = new HeapProfilerTestRunner.HeapNode('Node2');
builder.rootNode.linkNode(node1, HeapProfilerTestRunner.HeapEdge.Type.internal);
builder.rootNode.linkNode(node2, HeapProfilerTestRunner.HeapEdge.Type.internal);
node2.linkNode(node1, HeapProfilerTestRunner.HeapEdge.Type.internal);
var snapshot = builder.createJSHeapSnapshot();
var postOrderIndexes = snapshot._buildPostOrderIndex().nodeOrdinal2PostOrderIndex;
var nodeOrdinals = snapshot._buildPostOrderIndex().postOrderIndex2NodeOrdinal;
TestRunner.assertEquals(
JSON.stringify(new Uint32Array([2, 0, 1])), JSON.stringify(postOrderIndexes), 'postOrderIndexes');
TestRunner.assertEquals(
JSON.stringify(new Uint32Array([1, 2, 0])), JSON.stringify(nodeOrdinals), 'nodeOrdinals');
},
function heapSnapshotNodeSimpleTest() {
var snapshot = HeapProfilerTestRunner.createJSHeapSnapshotMockObject();
var nodeRoot = snapshot.createNode(snapshot._rootNodeIndex);
TestRunner.assertEquals('', nodeRoot.name(), 'root name');
TestRunner.assertEquals('hidden', nodeRoot.type(), 'root type');
TestRunner.assertEquals(2, nodeRoot.edgesCount(), 'root edges');
var nodeE = snapshot.createNode(15);
TestRunner.assertEquals('E', nodeE.name(), 'E name');
TestRunner.assertEquals('object', nodeE.type(), 'E type');
TestRunner.assertEquals(0, nodeE.edgesCount(), 'E edges');
},
function heapSnapshotNodeIteratorTest() {
var snapshot = HeapProfilerTestRunner.createJSHeapSnapshotMockObject();
var nodeRoot = snapshot.createNode(snapshot._rootNodeIndex);
var iterator = new HeapSnapshotWorker.HeapSnapshotNodeIterator(nodeRoot);
var names = [];
for (; iterator.hasNext(); iterator.next())
names.push(iterator.item().name());
TestRunner.assertEquals(',A,B,C,D,E', names.join(','), 'node iterator');
},
function heapSnapshotEdgeSimpleTest() {
var snapshot = HeapProfilerTestRunner.createJSHeapSnapshotMockObject();
var nodeRoot = snapshot.createNode(snapshot._rootNodeIndex);
var edgeIterator = new HeapSnapshotWorker.HeapSnapshotEdgeIterator(nodeRoot);
TestRunner.assertEquals(true, edgeIterator.hasNext(), 'has edges');
var edge = edgeIterator.item();
TestRunner.assertEquals('shortcut', edge.type(), 'edge type');
TestRunner.assertEquals('a', edge.name(), 'edge name');
TestRunner.assertEquals('A', edge.node().name(), 'edge node name');
var edgesCount = 0;
for (; edgeIterator.hasNext(); edgeIterator.next())
++edgesCount;
TestRunner.assertEquals(nodeRoot.edgesCount(), edgesCount, 'edges count');
},
function heapSnapshotEdgeIteratorTest() {
var snapshot = HeapProfilerTestRunner.createJSHeapSnapshotMockObject();
var nodeRoot = snapshot.createNode(snapshot._rootNodeIndex);
var names = [];
for (var iterator = nodeRoot.edges(); iterator.hasNext(); iterator.next())
names.push(iterator.item().name());
TestRunner.assertEquals('a,b', names.join(','), 'edge iterator');
var nodeE = snapshot.createNode(15);
TestRunner.assertEquals(false, nodeE.edges().hasNext(), 'empty edge iterator');
},
function heapSnapshotNodeAndEdgeTest() {
var snapshotMock = HeapProfilerTestRunner.createJSHeapSnapshotMockObject();
var nodeRoot = snapshotMock.createNode(snapshotMock._rootNodeIndex);
var names = [];
function depthFirstTraversal(node) {
names.push(node.name());
for (var edges = node.edges(); edges.hasNext(); edges.next()) {
names.push(edges.item().name());
depthFirstTraversal(edges.item().node());
}
}
depthFirstTraversal(nodeRoot);
var reference = ',a,A,1,B,bc,C,ce,E,bd,D,ac,C,ce,E,b,B,bc,C,ce,E,bd,D';
TestRunner.assertEquals(reference, names.join(','), 'mock traversal');
// Now check against a real HeapSnapshot instance.
names = [];
var snapshot = new HeapSnapshotWorker.JSHeapSnapshot(
HeapProfilerTestRunner.createHeapSnapshotMock(), new HeapSnapshotWorker.HeapSnapshotProgress());
depthFirstTraversal(snapshot.rootNode());
TestRunner.assertEquals(reference, names.join(','), 'snapshot traversal');
},
function heapSnapshotSimpleTest() {
var snapshot = new HeapSnapshotWorker.JSHeapSnapshot(
HeapProfilerTestRunner.createHeapSnapshotMock(), new HeapSnapshotWorker.HeapSnapshotProgress());
TestRunner.assertEquals(6, snapshot.nodeCount, 'node count');
TestRunner.assertEquals(20, snapshot.totalSize, 'total size');
},
function heapSnapshotContainmentEdgeIndexesTest() {
var snapshot = new HeapSnapshotWorker.JSHeapSnapshot(
HeapProfilerTestRunner.createHeapSnapshotMock(), new HeapSnapshotWorker.HeapSnapshotProgress());
var actual = snapshot._firstEdgeIndexes;
var expected = [0, 6, 12, 18, 21, 21, 21];
TestRunner.assertEquals(expected.length, actual.length, 'Edge indexes size');
for (var i = 0; i < expected.length; ++i)
TestRunner.assertEquals(expected[i], actual[i], 'Edge indexes');
},
function heapSnapshotPostOrderIndexTest() {
var snapshot = new HeapSnapshotWorker.JSHeapSnapshot(
HeapProfilerTestRunner.createHeapSnapshotMock(), new HeapSnapshotWorker.HeapSnapshotProgress());
var postOrderIndex2NodeOrdinal = snapshot._buildPostOrderIndex().postOrderIndex2NodeOrdinal;
var expected = [5, 3, 4, 2, 1, 0];
for (var i = 0; i < expected.length; ++i)
TestRunner.assertEquals(expected[i], postOrderIndex2NodeOrdinal[i], 'Post ordered indexes');
},
function heapSnapshotDominatorsTreeTest() {
var snapshot = new HeapSnapshotWorker.JSHeapSnapshot(
HeapProfilerTestRunner.createHeapSnapshotMock(), new HeapSnapshotWorker.HeapSnapshotProgress());
var result = snapshot._buildPostOrderIndex();
var dominatorsTree =
snapshot._buildDominatorTree(result.postOrderIndex2NodeOrdinal, result.nodeOrdinal2PostOrderIndex);
var expected = [0, 0, 0, 0, 2, 3];
for (var i = 0; i < expected.length; ++i)
TestRunner.assertEquals(expected[i], dominatorsTree[i], 'Dominators Tree');
},
function heapSnapshotLocations() {
var snapshot = new HeapSnapshotWorker.JSHeapSnapshot(
HeapProfilerTestRunner.createHeapSnapshotMock(), new HeapSnapshotWorker.HeapSnapshotProgress());
const expected = new Map([
[0, new HeapSnapshotModel.Location(1, 2, 3)],
[18, new HeapSnapshotModel.Location(2, 3, 4)],
]);
expected.forEach((expected_location, index) => {
const location = snapshot.getLocation(index);
TestRunner.assertEquals(expected_location.scriptId, location.scriptId, 'Locations scriptId');
TestRunner.assertEquals(expected_location.lineNumber, location.lineNumber, 'Locations lineNumber');
TestRunner.assertEquals(expected_location.columnNumber, location.columnNumber, 'Locations columnNumber');
});
},
function heapSnapshotRetainedSizeTest() {
var snapshot = new HeapSnapshotWorker.JSHeapSnapshot(
HeapProfilerTestRunner.createHeapSnapshotMock(), new HeapSnapshotWorker.HeapSnapshotProgress());
var actualRetainedSizes = new Array(snapshot.nodeCount);
for (var nodeOrdinal = 0; nodeOrdinal < snapshot.nodeCount; ++nodeOrdinal)
actualRetainedSizes[nodeOrdinal] = snapshot._retainedSizes[nodeOrdinal];
var expectedRetainedSizes = [20, 2, 8, 10, 5, 6];
TestRunner.assertEquals(
JSON.stringify(expectedRetainedSizes), JSON.stringify(actualRetainedSizes), 'Retained sizes');
},
function heapSnapshotLargeRetainedSize(next) {
var builder = new HeapProfilerTestRunner.HeapSnapshotBuilder();
var node = builder.rootNode;
var iterations = 6;
var nodeSize = 1000 * 1000 * 1000;
for (var i = 0; i < 6; i++) {
var newNode = new HeapProfilerTestRunner.HeapNode('Node' + i, nodeSize);
node.linkNode(newNode, HeapProfilerTestRunner.HeapEdge.Type.element);
node = newNode;
}
var snapshot = builder.createJSHeapSnapshot();
TestRunner.assertEquals(
iterations * nodeSize, snapshot.rootNode().retainedSize(),
'Ensure that root node retained size supports values exceeding 2^32 bytes.');
},
function heapSnapshotDominatedNodesTest() {
var snapshot = new HeapSnapshotWorker.JSHeapSnapshot(
HeapProfilerTestRunner.createHeapSnapshotMock(), new HeapSnapshotWorker.HeapSnapshotProgress());
var expectedDominatedNodes = [21, 14, 7, 28, 35];
var actualDominatedNodes = snapshot._dominatedNodes;
TestRunner.assertEquals(expectedDominatedNodes.length, actualDominatedNodes.length, 'Dominated Nodes length');
for (var i = 0; i < expectedDominatedNodes.length; ++i)
TestRunner.assertEquals(expectedDominatedNodes[i], actualDominatedNodes[i], 'Dominated Nodes');
var expectedDominatedNodeIndex = [0, 3, 3, 4, 5, 5, 5];
var actualDominatedNodeIndex = snapshot._firstDominatedNodeIndex;
TestRunner.assertEquals(
expectedDominatedNodeIndex.length, actualDominatedNodeIndex.length, 'Dominated Nodes Index length');
for (var i = 0; i < expectedDominatedNodeIndex.length; ++i)
TestRunner.assertEquals(expectedDominatedNodeIndex[i], actualDominatedNodeIndex[i], 'Dominated Nodes Index');
},
function heapSnapshotPageOwnedTest(next) {
var builder = new HeapProfilerTestRunner.HeapSnapshotBuilder();
var rootNode = builder.rootNode;
var debuggerNode = new HeapProfilerTestRunner.HeapNode('Debugger');
rootNode.linkNode(debuggerNode, HeapProfilerTestRunner.HeapEdge.Type.element);
var windowNode = new HeapProfilerTestRunner.HeapNode('Window');
rootNode.linkNode(windowNode, HeapProfilerTestRunner.HeapEdge.Type.shortcut);
var pageOwnedNode = new HeapProfilerTestRunner.HeapNode('PageOwnedNode');
windowNode.linkNode(pageOwnedNode, HeapProfilerTestRunner.HeapEdge.Type.element);
debuggerNode.linkNode(pageOwnedNode, HeapProfilerTestRunner.HeapEdge.Type.property, 'debugger2pageOwnedNode');
var debuggerOwnedNode = new HeapProfilerTestRunner.HeapNode('debuggerOwnedNode');
debuggerNode.linkNode(debuggerOwnedNode, HeapProfilerTestRunner.HeapEdge.Type.element);
var snapshot = builder.createJSHeapSnapshot();
snapshot._flags = new Array(snapshot.nodeCount);
for (var i = 0; i < snapshot.nodeCount; ++i)
snapshot._flags[i] = 0;
snapshot._markPageOwnedNodes();
var expectedFlags = [0, 0, 4, 4, 0];
TestRunner.assertEquals(
JSON.stringify(expectedFlags), JSON.stringify(snapshot._flags),
'We are expecting that only window(third element) and PageOwnedNode(forth element) have flag === 4.');
},
function heapSnapshotRetainersTest() {
var snapshot = new HeapSnapshotWorker.JSHeapSnapshot(
HeapProfilerTestRunner.createHeapSnapshotMock(), new HeapSnapshotWorker.HeapSnapshotProgress());
var expectedRetainers = {'': [], 'A': [''], 'B': ['', 'A'], 'C': ['A', 'B'], 'D': ['B'], 'E': ['C']};
for (var nodes = snapshot._allNodes(); nodes.hasNext(); nodes.next()) {
var names = [];
for (var retainers = nodes.item().retainers(); retainers.hasNext(); retainers.next())
names.push(retainers.item().node().name());
names.sort();
TestRunner.assertEquals(
expectedRetainers[nodes.item().name()].join(','), names.join(','),
'retainers of "' + nodes.item().name() + '"');
}
},
function heapSnapshotAggregatesTest() {
var snapshot = new HeapSnapshotWorker.JSHeapSnapshot(
HeapProfilerTestRunner.createHeapSnapshotMock(), new HeapSnapshotWorker.HeapSnapshotProgress());
var expectedAggregates = {
'A': {count: 1, self: 2, maxRet: 2, type: 'object', name: 'A'},
'B': {count: 1, self: 3, maxRet: 8, type: 'object', name: 'B'},
'C': {count: 1, self: 4, maxRet: 10, type: 'object', name: 'C'},
'D': {count: 1, self: 5, maxRet: 5, type: 'object', name: 'D'},
'E': {count: 1, self: 6, maxRet: 6, type: 'object', name: 'E'}
};
var aggregates = snapshot.aggregates(false);
for (var name in aggregates) {
var aggregate = aggregates[name];
var expectedAggregate = expectedAggregates[name];
for (var parameter in expectedAggregate)
TestRunner.assertEquals(
expectedAggregate[parameter], aggregate[parameter], 'parameter ' + parameter + ' of "' + name + '"');
}
var expectedIndexes = {
// Index of corresponding node in the raw snapshot:
'A': [7], // 14
'B': [14], // 27
'C': [21], // 40
'D': [28], // 50
'E': [35] // 57
};
var indexes = snapshot.aggregates(true);
for (var name in aggregates) {
var aggregate = aggregates[name];
var expectedIndex = expectedIndexes[name];
TestRunner.assertEquals(expectedIndex.join(','), aggregate.idxs.join(','), 'indexes of "' + name + '"');
}
},
function heapSnapshotFlagsTest() {
var snapshot = new HeapSnapshotWorker.JSHeapSnapshot(
HeapProfilerTestRunner.createHeapSnapshotMockWithDOM(), new HeapSnapshotWorker.HeapSnapshotProgress());
var expectedCanBeQueried = {
'': false,
'A': true,
'B': true,
'C': true,
'D': true,
'E': false,
'F': false,
'G': false,
'H': false,
'M': false,
'N': false,
'Window': true
};
for (var nodes = snapshot._allNodes(); nodes.hasNext(); nodes.next()) {
var node = nodes.item();
TestRunner.assertEquals(
expectedCanBeQueried[node.name()], node.canBeQueried(), 'canBeQueried of "' + node.name() + '"');
}
},
function heapSnapshotNodesProviderTest() {
var snapshot = new HeapSnapshotWorker.JSHeapSnapshot(
HeapProfilerTestRunner.createHeapSnapshotMock(), new HeapSnapshotWorker.HeapSnapshotProgress());
var allNodeIndexes = [];
for (var i = 0; i < snapshot.nodes.length; i += snapshot._nodeFieldCount)
allNodeIndexes.push(i);
var provider = new HeapSnapshotWorker.HeapSnapshotNodesProvider(snapshot, allNodeIndexes);
// Sort by names in reverse order.
provider.sortAndRewind({fieldName1: 'name', ascending1: false, fieldName2: 'id', ascending2: false});
var range = provider.serializeItemsRange(0, 6);
TestRunner.assertEquals(6, range.totalLength, 'Node range total length');
TestRunner.assertEquals(0, range.startPosition, 'Node range start position');
TestRunner.assertEquals(6, range.endPosition, 'Node range end position');
var names = range.items.map(item => item.name);
TestRunner.assertEquals('E,D,C,B,A,', names.join(','), 'nodes provider names');
},
function heapSnapshotEdgesProviderTest() {
var snapshot = new HeapSnapshotWorker.JSHeapSnapshot(
HeapProfilerTestRunner.createHeapSnapshotMock(), new HeapSnapshotWorker.HeapSnapshotProgress());
function edgeFilter(edge) {
return edge.name() === 'b';
}
var provider = snapshot.createEdgesProviderForTest(snapshot.rootNodeIndex, edgeFilter);
provider.sortAndRewind({fieldName1: '!edgeName', ascending1: false, fieldName2: 'id', ascending2: false});
var range = provider.serializeItemsRange(0, 10);
TestRunner.assertEquals(1, range.totalLength, 'Edge range total length');
TestRunner.assertEquals(0, range.startPosition, 'Edge range start position');
TestRunner.assertEquals(1, range.endPosition, 'Edge range end position');
var names = range.items.map(function(item) {
return item.name;
});
TestRunner.assertEquals('b', names.join(','), 'edges provider names');
},
async function heapSnapshotLoaderTest() {
var source = HeapProfilerTestRunner.createHeapSnapshotMockRaw();
var sourceStringified = JSON.stringify(source);
var partSize = sourceStringified.length >> 3;
var loader = new HeapSnapshotWorker.HeapSnapshotLoader();
for (var i = 0, l = sourceStringified.length; i < l; i += partSize)
loader.write(sourceStringified.slice(i, i + partSize));
loader.close();
await 0; // Make sure loader parses the input.
var result = loader.buildSnapshot(false);
result.nodes = new Uint32Array(result.nodes);
result.containmentEdges = new Uint32Array(result.containmentEdges);
function assertSnapshotEquals(reference, actual) {
TestRunner.assertEquals(JSON.stringify(reference), JSON.stringify(actual));
}
assertSnapshotEquals(
new HeapSnapshotWorker.JSHeapSnapshot(
HeapProfilerTestRunner.createHeapSnapshotMock(), new HeapSnapshotWorker.HeapSnapshotProgress(), false),
result);
},
];
var result = [];
for (var i = 0; i < testSuite.length; i++) {
var test = testSuite[i];
result.push('');
result.push('Running: ' + test.name);
try {
test();
} catch (e) {
result.push('FAIL: ' + e);
}
}
return result.join('\n');
}
var proxy = new Profiler.HeapSnapshotWorkerProxy(function(eventName, arg) {
TestRunner.addResult('Unexpected event from worker: ' + eventName);
});
var source = '(' + createTestEnvironmentInWorker + ')();' +
'(' + HeapProfilerTestRunner.createHeapSnapshotMockFactories + ')();' +
'(' + runTestSuiteInWorker + ')();';
proxy.evaluateForTest(source, function(result) {
TestRunner.addResult(result);
TestRunner.completeTest();
});
})();