blob: c77f0785d9461b24f387c6399d6f86f346671b87 [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/performance_manager/scenarios/input_scenario_observer.h"
#include <atomic>
#include <memory>
#include <utility>
#include "base/memory/read_only_shared_memory_region.h"
#include "components/performance_manager/decorators/frame_input_state_decorator.h"
#include "components/performance_manager/embedder/scoped_global_scenario_memory.h"
#include "components/performance_manager/graph/frame_node_impl.h"
#include "components/performance_manager/graph/graph_impl.h"
#include "components/performance_manager/graph/page_node_impl.h"
#include "components/performance_manager/graph/process_node_impl.h"
#include "components/performance_manager/scenario_api/performance_scenarios.h"
#include "components/performance_manager/scenarios/browser_performance_scenarios.h"
#include "components/performance_manager/test_support/graph_test_harness.h"
#include "components/performance_manager/test_support/mock_graphs.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace performance_manager {
namespace {
using performance_scenarios::GetInputScenario;
using performance_scenarios::ScenarioScope;
class InputScenarioObserverTest : public GraphTestHarness {
public:
void OnGraphCreated(GraphImpl* graph) override {
graph->PassToGraph(std::make_unique<FrameInputStateDecorator>());
graph->PassToGraph(std::make_unique<InputScenarioObserver>());
}
protected:
FrameInputStateDecorator& frame_input_state() {
return *FrameInputStateDecorator::GetFromGraph(graph());
}
private:
ScopedGlobalScenarioMemory scenario_memory_;
};
InputScenario GlobalInputScenario() {
return GetInputScenario(ScenarioScope::kGlobal)
->load(std::memory_order_relaxed);
}
InputScenario CurrentProcessInputScenario() {
return GetInputScenario(ScenarioScope::kCurrentProcess)
->load(std::memory_order_relaxed);
}
TEST_F(InputScenarioObserverTest, FrameInputState) {
MockSinglePageWithMultipleProcessesGraph mock_graph(graph());
// Map in the read-only scenario memory for the first mock process as the
// "current process" state.
base::ReadOnlySharedMemoryRegion process_region =
GetSharedScenarioRegionForProcessNode(mock_graph.process.get());
ASSERT_TRUE(process_region.IsValid());
performance_scenarios::ScopedReadOnlyScenarioMemory process_scenario_memory(
ScenarioScope::kCurrentProcess, std::move(process_region));
EXPECT_EQ(GlobalInputScenario(), InputScenario::kNoInput);
EXPECT_EQ(CurrentProcessInputScenario(), InputScenario::kNoInput);
frame_input_state().UpdateInputScenario(mock_graph.frame.get(), true);
EXPECT_EQ(GlobalInputScenario(), InputScenario::kTyping);
EXPECT_EQ(CurrentProcessInputScenario(), InputScenario::kTyping);
frame_input_state().UpdateInputScenario(mock_graph.child_frame.get(), true);
EXPECT_EQ(GlobalInputScenario(), InputScenario::kTyping);
EXPECT_EQ(CurrentProcessInputScenario(), InputScenario::kTyping);
frame_input_state().UpdateInputScenario(mock_graph.frame.get(), false);
EXPECT_EQ(GlobalInputScenario(), InputScenario::kTyping);
// Only `child_frame`, which is hosted in `other_process`, still has input.
EXPECT_EQ(CurrentProcessInputScenario(), InputScenario::kNoInput);
frame_input_state().UpdateInputScenario(mock_graph.child_frame.get(), false);
EXPECT_EQ(CurrentProcessInputScenario(), InputScenario::kNoInput);
EXPECT_EQ(GlobalInputScenario(), InputScenario::kNoInput);
}
TEST_F(InputScenarioObserverTest, FrameNodeRemoved) {
MockSinglePageInSingleProcessGraph mock_graph(graph());
// Map in the read-only scenario memory for the mock process as the "current
// process" state.
base::ReadOnlySharedMemoryRegion process_region =
GetSharedScenarioRegionForProcessNode(mock_graph.process.get());
ASSERT_TRUE(process_region.IsValid());
performance_scenarios::ScopedReadOnlyScenarioMemory process_scenario_memory(
ScenarioScope::kCurrentProcess, std::move(process_region));
auto new_frame1 =
CreateFrameNodeAutoId(mock_graph.process.get(), mock_graph.page.get());
auto new_frame2 =
CreateFrameNodeAutoId(mock_graph.process.get(), mock_graph.page.get());
EXPECT_EQ(GlobalInputScenario(), InputScenario::kNoInput);
EXPECT_EQ(CurrentProcessInputScenario(), InputScenario::kNoInput);
frame_input_state().UpdateInputScenario(new_frame1.get(), true);
frame_input_state().UpdateInputScenario(new_frame2.get(), true);
EXPECT_EQ(GlobalInputScenario(), InputScenario::kTyping);
EXPECT_EQ(CurrentProcessInputScenario(), InputScenario::kTyping);
// Delete a frame receiving input. Another frame is still receiving input.
new_frame1.reset();
EXPECT_EQ(GlobalInputScenario(), InputScenario::kTyping);
EXPECT_EQ(CurrentProcessInputScenario(), InputScenario::kTyping);
// Delete the last frame receiving input.
new_frame2.reset();
EXPECT_EQ(GlobalInputScenario(), InputScenario::kNoInput);
EXPECT_EQ(CurrentProcessInputScenario(), InputScenario::kNoInput);
}
} // namespace
} // namespace performance_manager