blob: 42ea4a56aa265c6de9adf56baaef577a6ada2a6f [file]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Processes API test for Chrome.
// browser_tests.exe --gtest_filter=ExtensionApiTest.Processes
const pass = chrome.test.callbackPass;
const fail = chrome.test.callbackFail;
const assertEq = chrome.test.assertEq;
const assertTrue = chrome.test.assertTrue;
const assertFalse = chrome.test.assertFalse;
const listenOnce = chrome.test.listenOnce;
const tabs = [];
let hangingTabProcess = -1;
function createTab(index, url) {
chrome.tabs.create({url: url}, pass(function(tab) {
tabs[index] = tab;
}));
}
const getProcessId = chrome.processes.getProcessIdForTab;
function pageUrl(letter) {
return chrome.runtime.getURL(letter + '.html');
}
function dumpProcess(process) {
console.info(`id ${process.id}`);
console.info(`osProcId ${process.osProcessId}`);
console.info(`type ${process.type}`);
console.info(`profile ${process.profile}`);
console.info(`tasks ${process.tasks}`);
for (let i = 0; i < process.tasks.length; ++i) {
console.info(`task[${i}].title ${process.tasks[i].title}`);
if ('tabId' in process.tasks[i]) {
console.info(`task[${i}].tabId ${process.tasks[i].tabId}`);
}
}
console.info(`cpu ${process.cpu}`);
console.info(`privMem ${process.privateMemory}`);
console.info(`network ${process.network}`);
console.info(`jsMemAlloc ${process.jsMemoryAllocated}`);
console.info(`jsMemUsed ${process.jsMemoryUsed}`);
console.info(`sqliteMem ${process.sqliteMemory}`);
console.info(`naclDebugPort ${process.naclDebugPort}`);
if ('imageCache' in process) {
console.info(`imageCache.size ${process.imageCache.size}`);
console.info(`imageCache.liveSize ${process.imageCache.liveSize}`);
}
if ('scriptCache' in process) {
console.info(`scriptCache.size ${process.scriptCache.size}`);
console.info(`scriptCache.liveSize ${process.scriptCache.liveSize}`);
}
if ('cssCache' in process) {
console.info(`cssCache.size ${process.cssCache.size}`);
console.info(`cssCache .liveSize ${process.cssCache.liveSize}`);
}
}
function validateProcessProperties(process, updating, memory_included) {
// Always present.
assertTrue('id' in process);
assertTrue('naclDebugPort' in process);
assertTrue('osProcessId' in process);
assertTrue('type' in process);
assertTrue('profile' in process);
assertTrue('tasks' in process);
assertTrue('title' in process.tasks[0]);
// Present if onUpdate(WithMemory) listener is registered.
assertEq(('cpu' in process), updating);
assertEq(('network' in process), updating);
// Present if memory details are requested.
assertEq(('privateMemory' in process), memory_included);
// sqliteMemory is only reported for the browser process
if (process.type === 'browser') {
assertEq(('sqliteMemory' in process), updating);
} else if (process.type === 'renderer') {
// The rest are not present in the browser process
assertEq(('jsMemoryAllocated' in process), updating);
assertEq(('jsMemoryUsed' in process), updating);
assertEq(('imageCache' in process), updating);
assertEq(('scriptCache' in process), updating);
assertEq(('cssCache' in process), updating);
}
}
chrome.test.runTests([
function setupProcessTests() {
// Open 4 tabs for test, then wait and create a 5th
createTab(0, 'about:blank');
createTab(1, pageUrl('a'));
createTab(2, pageUrl('b'));
createTab(3, 'chrome://newtab/');
// Wait for all loads to complete.
let completedCount = 0;
const onUpdatedCompleted = chrome.test.listenForever(
chrome.tabs.onUpdated,
function(changedTabId, changeInfo, changedTab) {
if (changedTab.status === 'complete') {
completedCount++;
// Once the NTP finishes loading, create another one. This ensures
// both NTPs end up in the same process.
if (changedTabId === tabs[3].id) {
createTab(4, 'chrome://newtab/');
}
}
// Once all tabs are done loading, continue with the next test.
if (completedCount === 5) {
onUpdatedCompleted();
}
},
);
},
function extensionPageInOwnProcess() {
getProcessId(tabs[0].id, pass(function(pid0) {
getProcessId(tabs[1].id, pass(function(pid1) {
// about:blank and extension page should not
// share a process
assertTrue(pid0 !== pid1);
}));
}));
},
function extensionPagesShareProcess() {
getProcessId(tabs[1].id, pass(function(pid1) {
getProcessId(tabs[2].id, pass(function(pid2) {
// Pages from same extension should share a
// process
assertEq(pid1, pid2);
}));
}));
},
function extensionPagesMatchTabs() {
getProcessId(
tabs[1].id, pass(function(pid1) {
getProcessId(
tabs[2].id, pass(function(pid2) {
// Pages from same extension should share a process
assertEq(pid1, pid2);
chrome.processes.getProcessInfo(pid1, false, function(pl1) {
chrome.processes.getProcessInfo(pid2, false, function(pl2) {
const proc1 = pl1[pid1];
const proc2 = pl2[pid2];
assertTrue(proc1.tasks.length === proc2.tasks.length);
for (let i = 0; i < proc1.tasks.length; ++i) {
assertEq(proc1.tasks[i], proc2.tasks[i]);
}
});
});
}));
}));
},
function newTabPageInOwnProcess() {
getProcessId(tabs[0].id, pass(function(pid0) {
getProcessId(tabs[3].id, pass(function(pid3) {
// NTP should not share a process with current
// tabs
assertTrue(pid0 !== pid3);
}));
}));
},
function newTabPagesShareProcess() {
getProcessId(tabs[3].id, pass(function(pid3) {
getProcessId(tabs[4].id, pass(function(pid4) {
// Multiple NTPs should share a process
assertEq(pid3, pid4);
}));
}));
},
function idsInUpdateEvent() {
listenOnce(chrome.processes.onUpdated, function(processes) {
// onUpdated should return a valid dictionary of processes,
// indexed by process ID.
const pids = Object.keys(processes);
// There should be at least 5 processes: 1 browser, 1 extension, and 3
// renderers (for the 5 tabs).
assertTrue(pids.length >= 5, 'Unexpected size of pids');
// Should be able to look up process object by ID.
assertTrue(processes[pids[0]].id === pids[0]);
assertTrue(processes[pids[0]].id !== processes[pids[1]].id);
getProcessId(
tabs[0].id, pass(function(pidTab0) {
// Process ID for tab 0 should be listed in pids.
assertTrue(processes[pidTab0] !== undefined, 'Undefined Process');
assertEq(
'renderer', processes[pidTab0].type, 'Tab0 is not renderer');
}));
});
},
function typesInUpdateEvent() {
listenOnce(chrome.processes.onUpdated, function(processes) {
// Check types: 1 browser, 3 renderers, and 1 extension
let browserCount = 0;
let rendererCount = 0;
let extensionCount = 0;
let otherCount = 0;
for (const pid in processes) {
switch (processes[pid].type) {
case 'browser':
browserCount++;
break;
case 'renderer':
rendererCount++;
break;
case 'extension':
extensionCount++;
break;
default:
otherCount++;
}
}
assertEq(1, browserCount);
assertTrue(rendererCount >= 3);
assertTrue(extensionCount >= 1);
});
},
function propertiesOfProcesses() {
listenOnce(chrome.processes.onUpdated, function(processes) {
for (const pid in processes) {
const process = processes[pid];
validateProcessProperties(process, true, false);
}
});
},
function propertiesOfProcessesWithMemory() {
listenOnce(chrome.processes.onUpdatedWithMemory, function(processes) {
for (const pid in processes) {
const process = processes[pid];
validateProcessProperties(process, true, true);
}
});
},
function terminateProcess() {
listenOnce(chrome.processes.onExited, function(processId, type, code) {
assertTrue(processId > 0);
});
getProcessId(tabs[4].id, function(pid0) {
chrome.processes.terminate(pid0, function(killed) {
chrome.test.assertTrue(killed);
});
});
},
function terminateProcessNonExisting() {
chrome.processes.terminate(31337, fail('Process not found: 31337.'));
},
function testOnCreated() {
listenOnce(chrome.processes.onCreated, function(process) {
assertTrue('id' in process, `process doesn't have id property`);
// We don't report the creation of the browser process, hence process.id
// is expected to be > 0.
assertTrue(process.id > 0, `id is not positive ${process.id}`);
});
createTab(5, 'chrome://newtab/');
},
// DISABLED: crbug.com/40352452
// Hangs consistently (On Windows).
/*
function testOnExited() {
listenOnce(chrome.processes.onExited,
function(processId, type, code) {
assertTrue(type >= 0 && type < 5);
});
chrome.tabs.create({url: 'http://google.com/'}, pass(function(tab) {
chrome.tabs.remove(tab.id);
}));
},
*/
function testGetProcessInfoList() {
getProcessId(tabs[0].id, pass(function(pidTab0) {
getProcessId(
tabs[1].id, pass(function(pidTab1) {
chrome.processes.getProcessInfo(
[pidTab0, pidTab1], false,
pass(function(processes) {
assertTrue(Object.keys(processes).length === 2);
}));
}));
}));
},
function testGetProcessInfoSingle() {
chrome.processes.getProcessInfo(
0, false, pass(function(processes) {
assertTrue(Object.keys(processes).length === 1);
}));
},
function testGetProcessInfo() {
chrome.processes.getProcessInfo(
[], false, pass(function(processes) {
assertTrue(Object.keys(processes).length >= 1);
for (const pid in processes) {
const process = processes[pid];
validateProcessProperties(process, false, false);
assertFalse('privateMemory' in process);
}
}));
},
function testGetProcessInfoWithMemory() {
chrome.processes.getProcessInfo(0, true, pass(function(processes) {
for (const pid in processes) {
const process = processes[pid];
validateProcessProperties(
process, false, true);
assertTrue('privateMemory' in process);
}
}));
},
function testOnUnresponsive() {
listenOnce(chrome.processes.onUnresponsive, function(process) {
assertTrue(process.id === hangingTabProcess);
// actually kill the process, just to make sure it won't hang the test
chrome.processes.terminate(process.id, function(killed) {
chrome.test.assertTrue(killed);
});
});
chrome.tabs.create({url: 'chrome://hang'}, function(tab) {
getProcessId(tab.id, function(pid0) {
hangingTabProcess = pid0;
});
chrome.tabs.update(tab.id, {url: 'chrome://flags'});
});
},
]);