Files
tubestation/browser/modules/test/unit/test_TabUnloader.js
Toshihito Kikuchi f1e6437e96 Bug 1729911 - Define minInactiveDurationInMS not to unload fresh tabs. r=NeilDeakin
Tab unloading should not unload "fresh" tabs i.e. tabs that were accessed
very recently, even though one of them was the least recently used tab.

To achieve it, this patch introduces the minimum inactive duration and we
prevent tabs that were accessed in the last period of that duration from
being unloaded.

This patch sets the default value to 10 minutes based on the historgram
`TAB_UNLOAD_TO_RELOAD` where the median was 60sec and the 75th percentile
was 1040sec.  This value can be overriden by the parameter of the TabUnloader's
APIs so that about:unloads and the tests can unload those fresh tabs.

Differential Revision: https://phabricator.services.mozilla.com/D125824
2021-10-20 23:22:57 +00:00

435 lines
11 KiB
JavaScript

/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
"use strict";
const { TabUnloader } = ChromeUtils.import(
"resource:///modules/TabUnloader.jsm"
);
let TestTabUnloaderMethods = {
isNonDiscardable(tab, weight) {
return /\bselected\b/.test(tab.keywords) ? weight : 0;
},
isParentProcess(tab, weight) {
return /\bparent\b/.test(tab.keywords) ? weight : 0;
},
isPinned(tab, weight) {
return /\bpinned\b/.test(tab.keywords) ? weight : 0;
},
isLoading(tab, weight) {
return /\bloading\b/.test(tab.keywords) ? weight : 0;
},
usingPictureInPicture(tab, weight) {
return /\bpictureinpicture\b/.test(tab.keywords) ? weight : 0;
},
playingMedia(tab, weight) {
return /\bmedia\b/.test(tab.keywords) ? weight : 0;
},
usingWebRTC(tab, weight) {
return /\bwebrtc\b/.test(tab.keywords) ? weight : 0;
},
getMinTabCount() {
// Use a low number for testing.
return 3;
},
getNow() {
return 100;
},
*iterateProcesses(tab) {
for (let process of tab.process.split(",")) {
yield Number(process);
}
},
async calculateMemoryUsage(processMap, tabs) {
let memory = tabs[0].memory;
for (let pid of processMap.keys()) {
processMap.get(pid).memory = memory ? memory[pid - 1] : 1;
}
},
};
let unloadTests = [
// Each item in the array represents one test. The test is a subarray
// containing an element per tab. This is a string of keywords that
// identify which criteria apply. The first part of the string may contain
// a number that represents the last visit time, where higher numbers
// are later. The last element in the subarray is special and identifies
// the expected order of the tabs sorted by weight. The first tab in
// this list is the one that is expected to selected to be discarded.
{ tabs: ["1 selected", "2", "3"], result: "1,2,0" },
{ tabs: ["1", "2 selected", "3"], result: "0,2,1" },
{ tabs: ["1 selected", "2", "3"], process: ["1", "2", "3"], result: "1,2,0" },
{
tabs: ["1 selected", "2 selected", "3 selected"],
process: ["1", "2", "3"],
result: "0,1,2",
},
{
tabs: ["1 selected", "2", "3"],
process: ["1,2,3", "2", "3"],
result: "1,2,0",
},
{
tabs: ["9", "8", "6", "5 selected", "2", "3", "4", "1"],
result: "7,4,5,6,2,1,0,3",
},
{
tabs: ["9", "8 pinned", "6", "5 selected", "2", "3 pinned", "4", "1"],
result: "7,4,6,2,0,5,1,3",
},
{
tabs: [
"9",
"8 pinned",
"6",
"5 selected pinned",
"2",
"3 pinned",
"4",
"1",
],
result: "7,4,6,2,0,5,1,3",
},
{
tabs: [
"9",
"8 pinned",
"6",
"5 selected pinned",
"2",
"3 selected pinned",
"4",
"1",
],
result: "7,4,6,2,0,1,5,3",
},
{
tabs: ["1", "2 selected", "3", "4 media", "5", "6"],
result: "0,2,4,5,1,3",
},
{
tabs: ["1 media", "2 selected media", "3", "4 media", "5", "6"],
result: "2,4,5,0,3,1",
},
{
tabs: ["1 media", "2 media pinned", "3", "4 media", "5 pinned", "6"],
result: "2,5,4,0,3,1",
},
{
tabs: [
"1 media",
"2 media pinned",
"3",
"4 media",
"5 media pinned",
"6 selected",
],
result: "2,0,3,5,1,4",
},
{
// Since TestTabUnloaderMethods.getNow() returns 100 and the test
// passes minInactiveDuration = 0 to TabUnloader.getSortedTabs(),
// tab 200 and 300 are excluded from the result.
tabs: ["300", "10", "50", "100", "200"],
result: "1,2,3",
},
{
tabs: ["1", "2", "3", "4", "5", "6"],
process: ["1", "2", "1", "1", "1", "1"],
result: "1,0,2,3,4,5",
},
{
tabs: ["1", "2 selected", "3", "4", "5", "6"],
process: ["1", "2", "1", "1", "1", "1"],
result: "0,2,3,4,5,1",
},
{
tabs: ["1", "2", "3", "4", "5", "6"],
process: ["1", "2", "2", "1", "1", "1"],
result: "0,1,2,3,4,5",
},
{
tabs: ["1", "2", "3", "4", "5", "6"],
process: ["1", "2", "3", "1", "1", "1"],
result: "1,0,2,3,4,5",
},
{
tabs: ["1", "2 media", "3", "4", "5", "6"],
process: ["1", "2", "3", "1", "1", "1"],
result: "2,0,3,4,5,1",
},
{
tabs: ["1", "2 media", "3", "4", "5", "6"],
process: ["1", "2", "3", "1", "1,2,3", "1"],
result: "0,2,3,4,5,1",
},
{
tabs: ["1", "2 media", "3", "4", "5", "6"],
process: ["1", "2", "3", "1", "1,4,5", "1"],
result: "2,0,3,4,5,1",
},
{
tabs: ["1", "2 media", "3 media", "4", "5 media", "6"],
process: ["1", "2", "3", "1", "1,4,5", "1"],
result: "0,3,5,1,2,4",
},
{
tabs: ["1", "2 media", "3 media", "4", "5 media", "6"],
process: ["1", "1", "3", "1", "1,4,5", "1"],
result: "0,3,5,1,2,4",
},
{
tabs: ["1", "2 media", "3 media", "4", "5 media", "6"],
process: ["1", "2", "3", "4", "1,4,5", "5"],
result: "0,3,5,1,2,4",
},
{
tabs: ["1", "2 media", "3 media", "4", "5 media", "6"],
process: ["1", "1", "3", "4", "1,4,5", "5"],
result: "0,3,5,1,2,4",
},
{
tabs: ["1", "2", "3", "4", "5", "6"],
process: ["1", "1", "1", "2", "1,3,4,5,6,7,8", "1"],
result: "0,1,2,3,4,5",
},
{
tabs: ["1", "2", "3", "4", "5", "6", "7", "8"],
process: ["1", "1", "1", "2", "1,3,4,5,6,7,8", "1", "1", "1"],
result: "4,0,3,1,2,5,6,7",
},
{
tabs: ["1", "2", "3", "4", "5 selected", "6"],
process: ["1", "1", "1", "2", "1,3,4,5,6,7,8", "1"],
result: "0,1,2,3,5,4",
},
{
tabs: ["1", "2", "3", "4", "5 media", "6"],
process: ["1", "1", "1", "2", "1,3,4,5,6,7,8", "1"],
result: "0,1,2,3,5,4",
},
{
tabs: ["1", "2", "3", "4", "5 media", "6", "7", "8"],
process: ["1", "1", "1", "2", "1,3,4,5,6,7,8", "1", "1", "1"],
result: "0,3,1,2,5,6,7,4",
},
{
tabs: ["1", "2", "3", "4", "5 media", "6", "7", "8"],
process: ["1", "1,3,4,5,6,7,8", "1", "1", "1", "1", "1", "1"],
result: "1,0,2,3,5,6,7,4",
},
{
tabs: ["1", "2", "3", "4", "5 media", "6", "7", "8"],
process: ["1", "1", "1,3,4,5,6,7,8", "1", "1", "1", "1", "1"],
result: "2,0,1,3,5,6,7,4",
},
{
tabs: ["1", "2", "3", "4", "5 media", "6", "7", "8"],
process: ["1", "1", "1,1,1,1,1,1,1", "1", "1", "1", "1,1,1,1,1", "1"],
result: "0,1,2,3,5,6,7,4",
},
{
tabs: ["1", "2", "3", "4", "5 media", "6", "7", "8"],
process: ["1", "1", "1,2,3,4,5", "1", "1", "1", "1,2,3,4,5", "1"],
result: "0,1,2,3,5,6,7,4",
},
{
tabs: ["1", "2", "3", "4", "5 media", "6", "7", "8"],
process: ["1", "1", "1,6", "1", "1", "1", "1,2,3,4,5", "1"],
result: "0,2,1,3,5,6,7,4",
},
{
tabs: ["1", "2", "3", "4", "5 media", "6", "7", "8"],
process: ["1", "1", "1,6", "1,7", "1,8", "1,9", "1,2,3,4,5", "1"],
result: "2,3,0,5,1,6,7,4",
},
{
tabs: ["1", "2", "3", "4", "5 media", "6", "7", "8"],
process: ["1,10,11", "1", "1,2", "1,7", "1,8", "1,9", "1,2,3,4,5", "1"],
result: "0,3,1,5,2,6,7,4",
},
{
tabs: [
"1 media",
"2 media",
"3 media",
"4 media",
"5 media",
"6",
"7",
"8",
],
process: ["1,10,11", "1", "1,2", "1,7", "1,8", "1,9", "1,2,3,4,5", "1"],
result: "6,5,7,0,1,2,3,4",
},
{
tabs: ["1", "2", "3"],
process: ["1", "2", "3"],
memory: ["100", "200", "300"],
result: "0,1,2",
},
{
tabs: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"],
process: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"],
memory: [
"100",
"200",
"300",
"400",
"500",
"600",
"700",
"800",
"900",
"1000",
],
result: "0,1,2,3,4,5,6,7,8,9",
},
{
tabs: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"],
process: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"],
memory: [
"100",
"900",
"300",
"500",
"400",
"700",
"600",
"1000",
"200",
"200",
],
result: "1,0,2,3,5,4,6,7,8,9",
},
{
tabs: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"],
process: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"],
memory: [
"1000",
"900",
"300",
"500",
"400",
"1000",
"600",
"1000",
"200",
"200",
],
result: "0,1,2,3,5,4,6,7,8,9",
},
{
tabs: ["1", "2", "3", "4", "5", "6"],
process: ["1", "2,7", "3", "4", "5", "6"],
memory: ["100", "200", "300", "400", "500", "600", "700"],
result: "1,0,2,3,4,5",
},
{
tabs: ["1", "2", "3", "4", "5", "6", "7", "8"],
process: ["1,6", "2,7", "3,8", "4,1,2", "5", "6", "7", "8"],
memory: ["100", "200", "300", "400", "500", "600", "700", "800"],
result: "2,3,0,1,4,5,6,7",
},
{
tabs: ["1", "2", "3", "4", "5", "6", "7", "8"],
process: ["1", "1", "1", "2", "1", "1", "1", "1"],
memory: ["700", "1000"],
result: "0,3,1,2,4,5,6,7",
},
{
tabs: ["1", "2", "3", "4", "5", "6", "7", "8"],
process: ["1", "1", "1", "1", "2,1", "2,1", "3", "3"],
memory: ["1000", "2000", "3000"],
result: "0,1,2,4,3,5,6,7",
},
{
tabs: ["1", "2", "3", "4", "5", "6", "7", "8"],
process: ["2", "2", "2", "2", "2,1", "2,1", "3", "3"],
memory: ["1000", "600", "1000"],
result: "0,1,2,4,3,5,6,7",
},
{
tabs: ["1", "2", "3", "4", "5", "6", "7", "8"],
process: ["1", "1", "1", "2", "2,1,1,1", "2,1", "3", "3"],
memory: ["1000", "1800", "1000"],
result: "0,1,3,2,4,5,6,7",
},
{
tabs: ["1", "2", "3", "4", "5", "6", "7", "8"],
process: ["1", "1", "1", "2", "2,1,1,1", "2,1", "3", "3"],
memory: ["4000", "1800", "1000"],
result: "0,1,2,4,3,5,6,7",
},
{
// The tab "1" contains 4 frames, but its uniqueCount is 1 because
// all of those frames are backed by the process "1". As a result,
// TabUnloader puts the tab "1" first based on the last access time.
tabs: ["1", "2", "3", "4", "5"],
process: ["1,1,1,1", "2", "3", "3", "3"],
memory: ["100", "100", "100"],
result: "0,1,2,3,4",
},
{
// The uniqueCount of the tab "1", "2", and "3" is 1, 2, and 3,
// respectively. As a result the first three tabs are sorted as 2,1,0.
tabs: ["1", "2", "3", "4", "5", "6"],
process: ["1,7,1,7,1,1,7,1", "7,3,7,2", "4,5,7,4,6,7", "7", "7", "7"],
memory: ["100", "100", "100", "100", "100", "100", "100"],
result: "2,1,0,3,4,5",
},
];
let globalBrowser = {
discardBrowser() {
return true;
},
};
add_task(async function doTests() {
for (let test of unloadTests) {
function* iterateTabs() {
let tabs = test.tabs;
for (let t = 0; t < tabs.length; t++) {
let tab = {
tab: {
originalIndex: t,
lastAccessed: Number(/^[0-9]+/.exec(tabs[t])[0]),
keywords: tabs[t],
process: "process" in test ? test.process[t] : "1",
},
memory: test.memory,
gBrowser: globalBrowser,
};
yield tab;
}
}
TestTabUnloaderMethods.iterateTabs = iterateTabs;
let expectedOrder = "";
const sortedTabs = await TabUnloader.getSortedTabs(
0,
TestTabUnloaderMethods
);
for (let tab of sortedTabs) {
if (expectedOrder) {
expectedOrder += ",";
}
expectedOrder += tab.tab.originalIndex;
}
Assert.equal(expectedOrder, test.result);
}
});