blob: 0789bbbb82e07415f8f3f52bd1aa6b066fe644e4 [file] [log] [blame]
// Copyright (c) 2012 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.
#include "chrome/browser/memory/tab_manager_delegate_chromeos.h"
#include <algorithm>
#include <map>
#include <string>
#include <vector>
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
#include "base/logging.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/process/process_handle.h"
#include "chrome/browser/chromeos/arc/process/arc_process.h"
#include "chrome/browser/memory/tab_manager.h"
#include "chrome/browser/memory/tab_stats.h"
#include "chrome/common/url_constants.h"
#include "chromeos/dbus/fake_debug_daemon_client.h"
#include "components/arc/common/process.mojom.h"
#include "components/exo/shell_surface.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/aura/window.h"
#include "ui/wm/public/activation_client.h"
#include "url/gurl.h"
namespace memory {
using TabManagerDelegateTest = testing::Test;
namespace {
constexpr bool kIsFocused = true;
constexpr bool kNotFocused = false;
} // namespace
TEST_F(TabManagerDelegateTest, CandidatesSorted) {
std::vector<arc::ArcProcess> arc_processes;
arc_processes.emplace_back(1, 10, "focused", arc::mojom::ProcessState::TOP,
kIsFocused, 100);
arc_processes.emplace_back(2, 20, "visible1", arc::mojom::ProcessState::TOP,
kNotFocused, 200);
arc_processes.emplace_back(
3, 30, "service", arc::mojom::ProcessState::SERVICE, kNotFocused, 500);
arc_processes.emplace_back(4, 40, "visible2", arc::mojom::ProcessState::TOP,
kNotFocused, 150);
TabStats tab1, tab2, tab3, tab4, tab5;
tab1.tab_contents_id = 100;
tab1.is_pinned = true;
tab2.tab_contents_id = 200;
tab2.is_internal_page = true;
tab3.tab_contents_id = 300;
tab3.is_pinned = true;
tab3.is_media = true;
tab4.tab_contents_id = 400;
tab4.is_media = true;
tab5.tab_contents_id = 500;
tab5.is_app = true;
TabStatsList tab_list = {
tab1, tab2, tab3, tab4, tab5
};
std::vector<TabManagerDelegate::Candidate> candidates;
candidates = TabManagerDelegate::GetSortedCandidates(
tab_list, arc_processes);
EXPECT_EQ(9U, candidates.size());
// focused app.
EXPECT_EQ("focused", candidates[0].app()->process_name());
// visible app 1, last_activity_time larger than visible app 2.
EXPECT_EQ("visible1", candidates[1].app()->process_name());
// visible app 2, last_activity_time less than visible app 1.
EXPECT_EQ("visible2", candidates[2].app()->process_name());
// pinned and media.
EXPECT_EQ(300, candidates[3].tab()->tab_contents_id);
// media.
EXPECT_EQ(400, candidates[4].tab()->tab_contents_id);
// pinned.
EXPECT_EQ(100, candidates[5].tab()->tab_contents_id);
// chrome app.
EXPECT_EQ(500, candidates[6].tab()->tab_contents_id);
// internal page.
EXPECT_EQ(200, candidates[7].tab()->tab_contents_id);
// background service.
EXPECT_EQ("service", candidates[8].app()->process_name());
}
class MockTabManagerDelegate : public TabManagerDelegate {
public:
MockTabManagerDelegate(): TabManagerDelegate(nullptr) {
}
explicit MockTabManagerDelegate(TabManagerDelegate::MemoryStat* mem_stat)
: TabManagerDelegate(nullptr, mem_stat) {
}
// unit test.
std::vector<int> GetKilledArcProcesses() {
return killed_arc_processes_;
}
// unit test.
std::vector<int64_t> GetKilledTabs() {
return killed_tabs_;
}
// unit test.
void Clear() {
killed_arc_processes_.clear();
killed_tabs_.clear();
}
protected:
bool KillArcProcess(const int nspid) override {
killed_arc_processes_.push_back(nspid);
return true;
}
bool KillTab(int64_t tab_id) override {
killed_tabs_.push_back(tab_id);
return true;
}
chromeos::DebugDaemonClient* GetDebugDaemonClient() override {
return &debugd_client_;
}
private:
chromeos::FakeDebugDaemonClient debugd_client_;
std::vector<int> killed_arc_processes_;
std::vector<int64_t> killed_tabs_;
};
class MockMemoryStat : public TabManagerDelegate::MemoryStat {
public:
MockMemoryStat() {}
~MockMemoryStat() override {}
int TargetMemoryToFreeKB() override {
return target_memory_to_free_kb_;
}
int EstimatedMemoryFreedKB(base::ProcessHandle pid) override {
return process_pss_[pid];
}
// unittest.
void SetTargetMemoryToFreeKB(const int target) {
target_memory_to_free_kb_ = target;
}
// unittest.
void SetProcessPss(base::ProcessHandle pid, int pss) {
process_pss_[pid] = pss;
}
private:
int target_memory_to_free_kb_;
std::map<base::ProcessHandle, int> process_pss_;
};
TEST_F(TabManagerDelegateTest, SetOomScoreAdj) {
base::MessageLoop message_loop;
MockTabManagerDelegate tab_manager_delegate;
std::vector<arc::ArcProcess> arc_processes;
arc_processes.emplace_back(1, 10, "focused", arc::mojom::ProcessState::TOP,
kIsFocused, 100);
arc_processes.emplace_back(2, 20, "visible1", arc::mojom::ProcessState::TOP,
kNotFocused, 200);
arc_processes.emplace_back(
3, 30, "service", arc::mojom::ProcessState::SERVICE, kNotFocused, 500);
arc_processes.emplace_back(4, 40, "visible2", arc::mojom::ProcessState::TOP,
kNotFocused, 150);
TabStats tab1, tab2, tab3, tab4, tab5;
tab1.is_pinned = true;
tab1.renderer_handle = 11;
tab2.is_internal_page = true;
tab2.renderer_handle = 11;
tab3.is_pinned = true;
tab3.is_media = true;
tab3.renderer_handle = 12;
tab4.is_media = true;
tab4.renderer_handle = 12;
tab5.is_app = true;
tab5.renderer_handle = 12;
TabStatsList tab_list = {tab1, tab2, tab3, tab4, tab5};
// Sorted order:
// app "focused" pid: 10
// app "visible1" pid: 20
// app "visible2" pid: 40
// tab3 pid: 12
// tab4 pid: 12
// tab1 pid: 11
// tab5 pid: 12
// tab2 pid: 11
// app "service" pid: 30
tab_manager_delegate.AdjustOomPrioritiesImpl(tab_list, arc_processes);
auto& oom_score_map = tab_manager_delegate.oom_score_map_;
EXPECT_EQ(6U, oom_score_map.size());
// Higher priority part.
EXPECT_EQ(300, oom_score_map[10]);
EXPECT_EQ(344, oom_score_map[20]);
EXPECT_EQ(388, oom_score_map[40]);
EXPECT_EQ(431, oom_score_map[12]);
EXPECT_EQ(475, oom_score_map[11]);
// Lower priority part.
EXPECT_EQ(650, oom_score_map[30]);
}
TEST_F(TabManagerDelegateTest, KillMultipleProcesses) {
// Not owned.
MockMemoryStat* memory_stat = new MockMemoryStat();
// Instantiate the mock instance.
MockTabManagerDelegate tab_manager_delegate(memory_stat);
std::vector<arc::ArcProcess> arc_processes;
arc_processes.emplace_back(1, 10, "focused", arc::mojom::ProcessState::TOP,
kIsFocused, 100);
arc_processes.emplace_back(2, 20, "visible1", arc::mojom::ProcessState::TOP,
kNotFocused, 200);
arc_processes.emplace_back(
3, 30, "service", arc::mojom::ProcessState::SERVICE, kNotFocused, 500);
arc_processes.emplace_back(4, 40, "visible2", arc::mojom::ProcessState::TOP,
kNotFocused, 150);
TabStats tab1, tab2, tab3, tab4, tab5;
tab1.is_pinned = true;
tab1.renderer_handle = 11;
tab1.tab_contents_id = 1;
tab2.is_internal_page = true;
tab2.renderer_handle = 11;
tab2.tab_contents_id = 2;
tab3.is_pinned = true;
tab3.is_media = true;
tab3.renderer_handle = 12;
tab3.tab_contents_id = 3;
tab4.is_media = true;
tab4.renderer_handle = 12;
tab4.tab_contents_id = 4;
tab5.is_app = true;
tab5.renderer_handle = 12;
tab5.tab_contents_id = 5;
TabStatsList tab_list = {tab1, tab2, tab3, tab4, tab5};
// Sorted order:
// app "focused" pid: 10 nspid 1
// app "visible1" pid: 20 nspid 2
// app "visible2" pid: 40 nspid 4
// tab3 pid: 12 tab_contents_id 3
// tab4 pid: 12 tab_contents_id 4
// tab1 pid: 11 tab_contents_id 1
// tab5 pid: 12 tab_contents_id 5
// tab2 pid: 11 tab_contents_id 2
// app "service" pid: 30 nspid 3
memory_stat->SetTargetMemoryToFreeKB(250000);
// Entities to be killed.
memory_stat->SetProcessPss(30, 10000);
memory_stat->SetProcessPss(11, 200000);
memory_stat->SetProcessPss(12, 30000);
// Should not be used.
memory_stat->SetProcessPss(40, 50000);
memory_stat->SetProcessPss(20, 30000);
memory_stat->SetProcessPss(10, 100000);
tab_manager_delegate.LowMemoryKillImpl(tab_list, arc_processes);
auto killed_arc_processes = tab_manager_delegate.GetKilledArcProcesses();
auto killed_tabs = tab_manager_delegate.GetKilledTabs();
// Killed apps and their nspid.
EXPECT_EQ(1U, killed_arc_processes.size());
EXPECT_EQ(3, killed_arc_processes[0]);
// Killed tabs and their content id.
// Note that process with pid 11 is counted twice. But so far I don't have a
// good way to estimate the memory freed if multiple tabs share one process.
EXPECT_EQ(3U, killed_tabs.size());
EXPECT_EQ(2, killed_tabs[0]);
EXPECT_EQ(5, killed_tabs[1]);
EXPECT_EQ(1, killed_tabs[2]);
}
} // namespace memory