blob: a41cb8944a8f41744cec70187480cd769a36a87d [file] [log] [blame]
// Copyright (c) 2015 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/renderer/page_load_metrics/metrics_render_frame_observer.h"
#include <memory>
#include <utility>
#include "base/memory/ptr_util.h"
#include "base/time/time.h"
#include "base/timer/mock_timer.h"
#include "chrome/common/page_load_metrics/page_load_timing.h"
#include "chrome/common/page_load_metrics/test/weak_mock_timer.h"
#include "chrome/renderer/page_load_metrics/fake_page_timing_sender.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace page_load_metrics {
// Implementation of the MetricsRenderFrameObserver class we're testing, with
// the GetTiming() method stubbed out to make the rest of the class more
// testable.
class TestMetricsRenderFrameObserver : public MetricsRenderFrameObserver,
public test::WeakMockTimerProvider {
public:
TestMetricsRenderFrameObserver() : MetricsRenderFrameObserver(nullptr) {}
std::unique_ptr<base::OneShotTimer> CreateTimer() override {
auto timer = std::make_unique<test::WeakMockTimer>();
SetMockTimer(timer->AsWeakPtr());
return std::move(timer);
}
std::unique_ptr<PageTimingSender> CreatePageTimingSender() override {
return base::WrapUnique<PageTimingSender>(
new FakePageTimingSender(&validator_));
}
void ExpectPageLoadTiming(const mojom::PageLoadTiming& timing) {
SetFakePageLoadTiming(timing);
validator_.ExpectPageLoadTiming(timing);
}
void ExpectCpuTiming(const base::TimeDelta& timing) {
validator_.ExpectCpuTiming(timing);
}
void SetFakePageLoadTiming(const mojom::PageLoadTiming& timing) {
EXPECT_EQ(nullptr, fake_timing_.get());
fake_timing_ = timing.Clone();
}
mojom::PageLoadTimingPtr GetTiming() const override {
EXPECT_NE(nullptr, fake_timing_.get());
return std::move(fake_timing_);
}
void VerifyExpectedTimings() const {
EXPECT_EQ(nullptr, fake_timing_.get());
validator_.VerifyExpectedTimings();
}
bool HasNoRenderFrame() const override { return false; }
private:
FakePageTimingSender::PageTimingValidator validator_;
mutable mojom::PageLoadTimingPtr fake_timing_;
};
typedef testing::Test MetricsRenderFrameObserverTest;
TEST_F(MetricsRenderFrameObserverTest, NoMetrics) {
TestMetricsRenderFrameObserver observer;
observer.DidChangePerformanceTiming();
ASSERT_EQ(nullptr, observer.GetMockTimer());
}
TEST_F(MetricsRenderFrameObserverTest, SingleMetric) {
base::Time nav_start = base::Time::FromDoubleT(10);
base::TimeDelta first_layout = base::TimeDelta::FromMillisecondsD(10);
TestMetricsRenderFrameObserver observer;
mojom::PageLoadTiming timing;
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = nav_start;
observer.ExpectPageLoadTiming(timing);
observer.DidStartNavigation(GURL(), base::nullopt);
observer.ReadyToCommitNavigation(nullptr);
observer.DidCommitProvisionalLoad(false, ui::PAGE_TRANSITION_LINK);
observer.GetMockTimer()->Fire();
timing.document_timing->first_layout = first_layout;
observer.ExpectPageLoadTiming(timing);
observer.DidChangePerformanceTiming();
observer.GetMockTimer()->Fire();
}
// Verify that when two CpuTimings come in, they're grouped into a single
// Message with the total being the sum of the two.
TEST_F(MetricsRenderFrameObserverTest, SingleCpuMetric) {
base::Time nav_start = base::Time::FromDoubleT(10);
TestMetricsRenderFrameObserver observer;
mojom::PageLoadTiming timing;
// Initialize the page and add the initial timing info to the expected.
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = nav_start;
observer.ExpectPageLoadTiming(timing);
observer.DidStartNavigation(GURL(), base::nullopt);
observer.ReadyToCommitNavigation(nullptr);
observer.DidCommitProvisionalLoad(false, ui::PAGE_TRANSITION_LINK);
// Send cpu timing updates and verify the expected result.
observer.DidChangeCpuTiming(base::TimeDelta::FromMilliseconds(110));
observer.DidChangeCpuTiming(base::TimeDelta::FromMilliseconds(50));
observer.ExpectCpuTiming(base::TimeDelta::FromMilliseconds(160));
observer.GetMockTimer()->Fire();
}
TEST_F(MetricsRenderFrameObserverTest, MultipleMetrics) {
base::Time nav_start = base::Time::FromDoubleT(10);
base::TimeDelta first_layout = base::TimeDelta::FromMillisecondsD(2);
base::TimeDelta dom_event = base::TimeDelta::FromMillisecondsD(2);
base::TimeDelta load_event = base::TimeDelta::FromMillisecondsD(2);
TestMetricsRenderFrameObserver observer;
mojom::PageLoadTiming timing;
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = nav_start;
observer.ExpectPageLoadTiming(timing);
observer.DidStartNavigation(GURL(), base::nullopt);
observer.ReadyToCommitNavigation(nullptr);
observer.DidCommitProvisionalLoad(false, ui::PAGE_TRANSITION_LINK);
observer.GetMockTimer()->Fire();
timing.document_timing->first_layout = first_layout;
timing.document_timing->dom_content_loaded_event_start = dom_event;
observer.ExpectPageLoadTiming(timing);
observer.DidChangePerformanceTiming();
observer.GetMockTimer()->Fire();
// At this point, we should have triggered the generation of two metrics.
// Verify and reset the observer's expectations before moving on to the next
// part of the test.
observer.VerifyExpectedTimings();
timing.document_timing->load_event_start = load_event;
observer.ExpectPageLoadTiming(timing);
observer.DidChangePerformanceTiming();
observer.GetMockTimer()->Fire();
// Verify and reset the observer's expectations before moving on to the next
// part of the test.
observer.VerifyExpectedTimings();
// The PageLoadTiming above includes timing information for the first layout,
// dom content, and load metrics. However, since we've already generated
// timing information for all of these metrics previously, we do not expect
// this invocation to generate any additional metrics.
observer.SetFakePageLoadTiming(timing);
observer.DidChangePerformanceTiming();
ASSERT_FALSE(observer.GetMockTimer()->IsRunning());
}
TEST_F(MetricsRenderFrameObserverTest, MultipleNavigations) {
base::Time nav_start = base::Time::FromDoubleT(10);
base::TimeDelta first_layout = base::TimeDelta::FromMillisecondsD(2);
base::TimeDelta dom_event = base::TimeDelta::FromMillisecondsD(2);
base::TimeDelta load_event = base::TimeDelta::FromMillisecondsD(2);
TestMetricsRenderFrameObserver observer;
mojom::PageLoadTiming timing;
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = nav_start;
observer.ExpectPageLoadTiming(timing);
observer.DidStartNavigation(GURL(), base::nullopt);
observer.ReadyToCommitNavigation(nullptr);
observer.DidCommitProvisionalLoad(false, ui::PAGE_TRANSITION_LINK);
observer.GetMockTimer()->Fire();
timing.document_timing->first_layout = first_layout;
timing.document_timing->dom_content_loaded_event_start = dom_event;
timing.document_timing->load_event_start = load_event;
observer.ExpectPageLoadTiming(timing);
observer.DidChangePerformanceTiming();
observer.GetMockTimer()->Fire();
// At this point, we should have triggered the generation of two metrics.
// Verify and reset the observer's expectations before moving on to the next
// part of the test.
observer.VerifyExpectedTimings();
base::Time nav_start_2 = base::Time::FromDoubleT(100);
base::TimeDelta first_layout_2 = base::TimeDelta::FromMillisecondsD(20);
base::TimeDelta dom_event_2 = base::TimeDelta::FromMillisecondsD(20);
base::TimeDelta load_event_2 = base::TimeDelta::FromMillisecondsD(20);
mojom::PageLoadTiming timing_2;
page_load_metrics::InitPageLoadTimingForTest(&timing_2);
timing_2.navigation_start = nav_start_2;
observer.SetMockTimer(nullptr);
observer.ExpectPageLoadTiming(timing_2);
observer.DidStartNavigation(GURL(), base::nullopt);
observer.ReadyToCommitNavigation(nullptr);
observer.DidCommitProvisionalLoad(false, ui::PAGE_TRANSITION_LINK);
observer.GetMockTimer()->Fire();
timing_2.document_timing->first_layout = first_layout_2;
timing_2.document_timing->dom_content_loaded_event_start = dom_event_2;
timing_2.document_timing->load_event_start = load_event_2;
observer.ExpectPageLoadTiming(timing_2);
observer.DidChangePerformanceTiming();
observer.GetMockTimer()->Fire();
}
} // namespace page_load_metrics