Commit 7cb13798 authored by Chris Hamilton's avatar Chris Hamilton Committed by Commit Bot

Make PageAlmostIdle transitions determinate.

This adds a final timeout to PageAlmostIdle transitions so that they always fire at some point.
This notification is intended to mean "initial load has finished", and it makes sense for this
to fire at some point. This simplifies logic in an upcoming CL which factors
observation logic out of the TabManager into a helper component.

BUG=749789

Change-Id: I1db03f991220eebd33251dc5258a2760042eedef
Reviewed-on: https://chromium-review.googlesource.com/929985
Commit-Queue: Chris Hamilton <chrisha@chromium.org>
Reviewed-by: default avatarlpy <lpy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#542614}
parent 1d625d9e
......@@ -33,6 +33,13 @@ class PageSignalGeneratorImpl : public CoordinationUnitGraphObserver,
// transitions.
static const base::TimeDelta kLoadedAndIdlingTimeout;
// The maximum amount of time post-DidStopLoading a page can be waiting for
// an idle state to occur before the page is simply considered loaded anyways.
// Since PageAlmostIdle is intended as an "initial loading complete" signal,
// it needs to eventually terminate. This is strictly greater than the
// kLoadedAndIdlingTimeout.
static const base::TimeDelta kWaitingForIdleTimeout;
PageSignalGeneratorImpl();
~PageSignalGeneratorImpl() override;
......@@ -61,12 +68,15 @@ class PageSignalGeneratorImpl : public CoordinationUnitGraphObserver,
const service_manager::BindSourceInfo& source_info);
private:
friend class PageSignalGeneratorImplTest;
FRIEND_TEST_ALL_PREFIXES(PageSignalGeneratorImplTest, IsLoading);
FRIEND_TEST_ALL_PREFIXES(PageSignalGeneratorImplTest, IsIdling);
FRIEND_TEST_ALL_PREFIXES(PageSignalGeneratorImplTest,
PageDataCorrectlyManaged);
FRIEND_TEST_ALL_PREFIXES(PageSignalGeneratorImplTest,
PageAlmostIdleTransitions);
PageAlmostIdleTransitionsNoTimeout);
FRIEND_TEST_ALL_PREFIXES(PageSignalGeneratorImplTest,
PageAlmostIdleTransitionsWithTimeout);
// The state transitions for the PageAlmostIdle signal. In general a page
// transitions through these states from top to bottom.
......@@ -96,8 +106,12 @@ class PageSignalGeneratorImpl : public CoordinationUnitGraphObserver,
// to UpdateLoadIdleState. Is reset to kLoadingNotStarted when a non-same
// document navigation is committed.
LoadIdleState load_idle_state;
// Marks the point in time when the transition to kLoadedAndIdling occurred.
// Used for gating the transition to kLoadedAndIdle.
// Marks the point in time when the DidStopLoading signal was received,
// transitioning to kLoadedAndNotIdling or kLoadedAndIdling. This is used as
// the basis for the kWaitingForIdleTimeout.
base::TimeTicks loading_stopped;
// Marks the point in time when the last transition to kLoadedAndIdling
// occurred. Used for gating the transition to kLoadedAndIdle.
base::TimeTicks idling_started;
// A one-shot timer used for transitioning between kLoadedAndIdling and
// kLoadedAndIdle.
......@@ -112,6 +126,9 @@ class PageSignalGeneratorImpl : public CoordinationUnitGraphObserver,
void UpdateLoadIdleStateProcess(
const ProcessCoordinationUnitImpl* process_cu);
// Helper function for transitioning to the final state.
void TransitionToLoadedAndIdle(const PageCoordinationUnitImpl* page_cu);
// Convenience accessors for state associated with a |page_cu|.
PageData* GetPageData(const PageCoordinationUnitImpl* page_cu);
bool IsLoading(const PageCoordinationUnitImpl* page_cu);
......
......@@ -4,18 +4,38 @@
#include "services/resource_coordinator/observers/page_signal_generator_impl.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/simple_test_tick_clock.h"
#include "services/resource_coordinator/coordination_unit/coordination_unit_test_harness.h"
#include "services/resource_coordinator/coordination_unit/frame_coordination_unit_impl.h"
#include "services/resource_coordinator/coordination_unit/mock_coordination_unit_graphs.h"
#include "services/resource_coordinator/coordination_unit/page_coordination_unit_impl.h"
#include "services/resource_coordinator/coordination_unit/process_coordination_unit_impl.h"
#include "services/resource_coordinator/public/cpp/resource_coordinator_features.h"
#include "services/resource_coordinator/resource_coordinator_clock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace resource_coordinator {
// A wrapper for a tick clock. ResourceCoordinatorClock wants TickClock
// ownership, but the test harness only provides a raw pointer.
class TickClockWrapper : public base::TickClock {
public:
explicit TickClockWrapper(base::TickClock* tick_clock)
: tick_clock_(tick_clock) {}
~TickClockWrapper() override {}
// base::TickClock implementation:
base::TimeTicks NowTicks() override { return tick_clock_->NowTicks(); }
private:
base::TickClock* tick_clock_;
DISALLOW_COPY_AND_ASSIGN(TickClockWrapper);
};
class MockPageSignalGeneratorImpl : public PageSignalGeneratorImpl {
public:
// Overridden from PageSignalGeneratorImpl.
......@@ -34,12 +54,23 @@ class MockPageSignalGeneratorImpl : public PageSignalGeneratorImpl {
class PageSignalGeneratorImplTest : public CoordinationUnitTestHarness {
protected:
void TearDown() override { ResourceCoordinatorClock::ResetClockForTesting(); }
MockPageSignalGeneratorImpl* page_signal_generator() {
return &page_signal_generator_;
}
void EnablePAI() {
feature_list_ = std::make_unique<base::test::ScopedFeatureList>();
feature_list_->InitAndEnableFeature(features::kPageAlmostIdle);
ASSERT_TRUE(resource_coordinator::IsPageAlmostIdleSignalEnabled());
}
void TestPageAlmostIdleTransitions(bool timeout);
private:
MockPageSignalGeneratorImpl page_signal_generator_;
std::unique_ptr<base::test::ScopedFeatureList> feature_list_;
};
TEST_F(PageSignalGeneratorImplTest,
......@@ -61,6 +92,7 @@ TEST_F(PageSignalGeneratorImplTest,
}
TEST_F(PageSignalGeneratorImplTest, IsLoading) {
EnablePAI();
MockSinglePageInSingleProcessCoordinationUnitGraph cu_graph;
auto* page_cu = cu_graph.page.get();
auto* psg = page_signal_generator();
......@@ -80,6 +112,7 @@ TEST_F(PageSignalGeneratorImplTest, IsLoading) {
}
TEST_F(PageSignalGeneratorImplTest, IsIdling) {
EnablePAI();
MockSinglePageInSingleProcessCoordinationUnitGraph cu_graph;
auto* frame_cu = cu_graph.frame.get();
auto* page_cu = cu_graph.page.get();
......@@ -114,6 +147,7 @@ TEST_F(PageSignalGeneratorImplTest, IsIdling) {
}
TEST_F(PageSignalGeneratorImplTest, PageDataCorrectlyManaged) {
EnablePAI();
MockSinglePageInSingleProcessCoordinationUnitGraph cu_graph;
auto* page_cu = cu_graph.page.get();
auto* psg = page_signal_generator();
......@@ -127,12 +161,11 @@ TEST_F(PageSignalGeneratorImplTest, PageDataCorrectlyManaged) {
EXPECT_EQ(0u, psg->page_data_.count(page_cu));
}
TEST_F(PageSignalGeneratorImplTest, PageAlmostIdleTransitions) {
void PageSignalGeneratorImplTest::TestPageAlmostIdleTransitions(bool timeout) {
EnablePAI();
ResourceCoordinatorClock::SetClockForTesting(
std::make_unique<base::SimpleTestTickClock>());
auto* test_clock = static_cast<base::SimpleTestTickClock*>(
ResourceCoordinatorClock::GetClockForTesting());
test_clock->Advance(base::TimeDelta::FromSeconds(1));
std::make_unique<TickClockWrapper>(task_env().GetMockTickClock()));
task_env().FastForwardBy(base::TimeDelta::FromSeconds(1));
MockSinglePageInSingleProcessCoordinationUnitGraph cu_graph;
auto* frame_cu = cu_graph.frame.get();
......@@ -185,24 +218,37 @@ TEST_F(PageSignalGeneratorImplTest, PageAlmostIdleTransitions) {
EXPECT_EQ(LIS::kLoadedAndIdling, page_data->load_idle_state);
EXPECT_TRUE(page_data->idling_timer.IsRunning());
// Go back to not idling. We should transition back to kLoadedNotIdling.
// Go back to not idling. We should transition back to kLoadedNotIdling, and
// a timer should still be running.
frame_cu->SetNetworkAlmostIdle(false);
EXPECT_EQ(LIS::kLoadedNotIdling, page_data->load_idle_state);
EXPECT_FALSE(page_data->idling_timer.IsRunning());
// Go back to idling.
frame_cu->SetNetworkAlmostIdle(true);
EXPECT_EQ(LIS::kLoadedAndIdling, page_data->load_idle_state);
EXPECT_TRUE(page_data->idling_timer.IsRunning());
// Let the timer evaluate. The final state transition should occur.
// TODO(chrisha): Expose the tick clock from the ScopedTaskEnvironment, and
// use that clock in the ResourceCoordinatorClock. Beats having two separate
// fake clocks that need to be manually kept in sync.
test_clock->Advance(PageSignalGeneratorImpl::kLoadedAndIdlingTimeout);
task_env().FastForwardUntilNoTasksRemain();
EXPECT_EQ(LIS::kLoadedAndIdle, page_data->load_idle_state);
EXPECT_FALSE(page_data->idling_timer.IsRunning());
base::TimeTicks start = ResourceCoordinatorClock::NowTicks();
if (timeout) {
// Let the timeout run down. The final state transition should occur.
task_env().FastForwardUntilNoTasksRemain();
base::TimeTicks end = ResourceCoordinatorClock::NowTicks();
base::TimeDelta elapsed = end - start;
EXPECT_LE(PageSignalGeneratorImpl::kLoadedAndIdlingTimeout, elapsed);
EXPECT_LE(PageSignalGeneratorImpl::kWaitingForIdleTimeout, elapsed);
EXPECT_EQ(LIS::kLoadedAndIdle, page_data->load_idle_state);
EXPECT_FALSE(page_data->idling_timer.IsRunning());
} else {
// Go back to idling.
frame_cu->SetNetworkAlmostIdle(true);
EXPECT_EQ(LIS::kLoadedAndIdling, page_data->load_idle_state);
EXPECT_TRUE(page_data->idling_timer.IsRunning());
// Let the idle timer evaluate. The final state transition should occur.
task_env().FastForwardUntilNoTasksRemain();
base::TimeTicks end = ResourceCoordinatorClock::NowTicks();
base::TimeDelta elapsed = end - start;
EXPECT_LE(PageSignalGeneratorImpl::kLoadedAndIdlingTimeout, elapsed);
EXPECT_GT(PageSignalGeneratorImpl::kWaitingForIdleTimeout, elapsed);
EXPECT_EQ(LIS::kLoadedAndIdle, page_data->load_idle_state);
EXPECT_FALSE(page_data->idling_timer.IsRunning());
}
// Firing other signals should not change the state at all.
proc_cu->SetMainThreadTaskLoadIsLow(false);
......@@ -218,4 +264,12 @@ TEST_F(PageSignalGeneratorImplTest, PageAlmostIdleTransitions) {
EXPECT_FALSE(page_data->idling_timer.IsRunning());
}
TEST_F(PageSignalGeneratorImplTest, PageAlmostIdleTransitionsNoTimeout) {
TestPageAlmostIdleTransitions(false);
}
TEST_F(PageSignalGeneratorImplTest, PageAlmostIdleTransitionsWithTimeout) {
TestPageAlmostIdleTransitions(true);
}
} // namespace resource_coordinator
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment