| Index: components/page_load_metrics/renderer/page_load_metrics_render_frame_observer_unittest.cc
|
| diff --git a/components/page_load_metrics/renderer/page_load_metrics_render_frame_observer_unittest.cc b/components/page_load_metrics/renderer/page_load_metrics_render_frame_observer_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..fa27d3d9edc0a3ac473026cd1b4668a85ac7a783
|
| --- /dev/null
|
| +++ b/components/page_load_metrics/renderer/page_load_metrics_render_frame_observer_unittest.cc
|
| @@ -0,0 +1,199 @@
|
| +// 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 "components/page_load_metrics/renderer/page_load_metrics_render_frame_observer.h"
|
| +
|
| +#include "base/memory/scoped_ptr.h"
|
| +#include "base/time/time.h"
|
| +#include "components/page_load_metrics/common/page_load_metrics_messages.h"
|
| +#include "ipc/ipc_message_macros.h"
|
| +#include "testing/gmock/include/gmock/gmock.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +
|
| +using testing::NiceMock;
|
| +using testing::Return;
|
| +using testing::StrictMock;
|
| +
|
| +namespace page_load_metrics {
|
| +namespace {
|
| +
|
| +// IPC interceptor class, which we use to verify that certain IPC
|
| +// messages get sent.
|
| +class MockIPCInterceptor {
|
| + public:
|
| + void OnMessage(const IPC::Message& message) {
|
| + IPC_BEGIN_MESSAGE_MAP(MockIPCInterceptor, message)
|
| + IPC_MESSAGE_HANDLER(PageLoadMetricsMsg_DOMContentLoadedEvent,
|
| + OnDOMContentLoadedEvent)
|
| + IPC_MESSAGE_HANDLER(PageLoadMetricsMsg_LoadEvent, OnLoadEvent)
|
| + IPC_MESSAGE_HANDLER(PageLoadMetricsMsg_FirstLayout, OnFirstLayout)
|
| + IPC_MESSAGE_UNHANDLED(ADD_FAILURE())
|
| + IPC_END_MESSAGE_MAP()
|
| + }
|
| +
|
| + MOCK_METHOD1(OnDOMContentLoadedEvent, void(base::TimeDelta));
|
| + MOCK_METHOD1(OnLoadEvent, void(base::TimeDelta));
|
| + MOCK_METHOD1(OnFirstLayout, void(base::TimeDelta));
|
| +};
|
| +
|
| +class MockTiming : public PageLoadMetricsRenderFrameObserver::Timing {
|
| + public:
|
| + MockTiming() {}
|
| + MOCK_CONST_METHOD0(GetNavigationStart, base::Time());
|
| + MOCK_CONST_METHOD0(GetDOMContentLoadedEvent, base::Time());
|
| + MOCK_CONST_METHOD0(GetLoadEvent, base::Time());
|
| + MOCK_CONST_METHOD0(GetFirstLayout, base::Time());
|
| +
|
| + // Creates an implementation of MockTiming with default return values.
|
| + static scoped_ptr<MockTiming> Create() {
|
| + scoped_ptr<NiceMock<MockTiming> > timing(new NiceMock<MockTiming>());
|
| + ON_CALL(*timing, GetNavigationStart()).WillByDefault(Return(base::Time()));
|
| + ON_CALL(*timing, GetDOMContentLoadedEvent()).WillByDefault(
|
| + Return(base::Time()));
|
| + ON_CALL(*timing, GetLoadEvent()).WillByDefault(Return(base::Time()));
|
| + ON_CALL(*timing, GetFirstLayout()).WillByDefault(Return(base::Time()));
|
| + return timing.Pass();
|
| + }
|
| +};
|
| +
|
| +// Implementation of the PageLoadMetricsRenderFrameObserver class we're testing,
|
| +// with the CreateTiming() and ShouldSendMetrics() methods stubbed out to make
|
| +// the rest of the class more testable.
|
| +class TestPageLoadMetricsRenderFrameObserver
|
| + : public PageLoadMetricsRenderFrameObserver {
|
| + public:
|
| + explicit TestPageLoadMetricsRenderFrameObserver()
|
| + : PageLoadMetricsRenderFrameObserver(nullptr) {}
|
| +
|
| + // We stub out CreateTiming so we can return MockTiming instances. The blink
|
| + // WebPerformance class which provides our timing data in production code is
|
| + // not unit test friendly, so we define the Timing abstraction to make the
|
| + // rest of PageLoadMetricsRenderFrameObserver testable.
|
| + scoped_ptr<Timing> CreateTiming() override {
|
| + if (timing_ == nullptr) {
|
| + ADD_FAILURE();
|
| + }
|
| + return timing_.Pass();
|
| + }
|
| +
|
| + // We intercept sent messages and dispatch them to a MockIPCInterceptor, which
|
| + // we use to verify that the expected IPC messages get sent.
|
| + virtual bool Send(IPC::Message* message) {
|
| + interceptor_.OnMessage(*message);
|
| + // Our base class deletes the message.
|
| + return PageLoadMetricsRenderFrameObserver::Send(message);
|
| + }
|
| +
|
| + // The production ShouldSendMetrics implementation depends on content and
|
| + // Blink. In order to reduce dependencies for this unit test, we stub out
|
| + // this method to always return true.
|
| + bool ShouldSendMetrics() override { return true; }
|
| +
|
| + // Provides a Timing instance that should be returned from CreateTiming().
|
| + // Ownership of the Timing instance is transferred to this
|
| + // TestPageLoadMetricsRenderFrameObserver.
|
| + void set_mock_timing(scoped_ptr<Timing> timing) { timing_ = timing.Pass(); }
|
| +
|
| + MockIPCInterceptor* ipc_interceptor() { return &interceptor_; }
|
| +
|
| + private:
|
| + StrictMock<MockIPCInterceptor> interceptor_;
|
| + scoped_ptr<Timing> timing_;
|
| +};
|
| +
|
| +typedef testing::Test PageLoadMetricsRenderFrameObserverTest;
|
| +
|
| +TEST_F(PageLoadMetricsRenderFrameObserverTest, NoMetrics) {
|
| + TestPageLoadMetricsRenderFrameObserver observer;
|
| + observer.DidCommitProvisionalLoad(true, false);
|
| +
|
| + observer.set_mock_timing(MockTiming::Create());
|
| + observer.DidFinishDocumentLoad();
|
| +}
|
| +
|
| +TEST_F(PageLoadMetricsRenderFrameObserverTest, SingleMetric) {
|
| + base::Time nav_start = base::Time::FromDoubleT(10);
|
| + base::Time first_layout = base::Time::FromDoubleT(12);
|
| +
|
| + TestPageLoadMetricsRenderFrameObserver observer;
|
| + observer.DidCommitProvisionalLoad(true, false);
|
| +
|
| + scoped_ptr<MockTiming> timing(MockTiming::Create());
|
| + EXPECT_CALL(*timing, GetNavigationStart()).WillRepeatedly(Return(nav_start));
|
| + EXPECT_CALL(*timing, GetFirstLayout()).WillRepeatedly(Return(first_layout));
|
| + observer.set_mock_timing(timing.Pass());
|
| +
|
| + // We expect the above MockTiming configuration to trigger the generation of
|
| + // only the first_layout metric.
|
| + EXPECT_CALL(*observer.ipc_interceptor(),
|
| + OnFirstLayout(first_layout - nav_start));
|
| + observer.DidFinishDocumentLoad();
|
| +}
|
| +
|
| +TEST_F(PageLoadMetricsRenderFrameObserverTest, MultipleMetrics) {
|
| + base::Time nav_start = base::Time::FromDoubleT(10);
|
| + base::Time first_layout = base::Time::FromDoubleT(12);
|
| + base::Time dom_event = base::Time::FromDoubleT(14);
|
| + base::Time load_event = base::Time::FromDoubleT(16);
|
| +
|
| + TestPageLoadMetricsRenderFrameObserver observer;
|
| + observer.DidCommitProvisionalLoad(true, false);
|
| +
|
| + scoped_ptr<MockTiming> timing(MockTiming::Create());
|
| + EXPECT_CALL(*timing, GetNavigationStart()).WillRepeatedly(Return(nav_start));
|
| + EXPECT_CALL(*timing, GetFirstLayout()).WillRepeatedly(Return(first_layout));
|
| + EXPECT_CALL(*timing, GetDOMContentLoadedEvent()).WillRepeatedly(
|
| + Return(dom_event));
|
| + observer.set_mock_timing(timing.Pass());
|
| +
|
| + // We expect the above MockTiming configuration to trigger the generation of
|
| + // the first layout and dom content metrics.
|
| + EXPECT_CALL(*observer.ipc_interceptor(),
|
| + OnFirstLayout(first_layout - nav_start));
|
| + EXPECT_CALL(*observer.ipc_interceptor(),
|
| + OnDOMContentLoadedEvent(dom_event - nav_start));
|
| + observer.DidFinishDocumentLoad();
|
| +
|
| + // 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.
|
| + testing::Mock::VerifyAndClearExpectations(observer.ipc_interceptor());
|
| +
|
| + timing = MockTiming::Create();
|
| + EXPECT_CALL(*timing, GetNavigationStart()).WillRepeatedly(Return(nav_start));
|
| + EXPECT_CALL(*timing, GetFirstLayout()).WillRepeatedly(Return(first_layout));
|
| + EXPECT_CALL(*timing, GetDOMContentLoadedEvent()).WillRepeatedly(
|
| + Return(dom_event));
|
| + EXPECT_CALL(*timing, GetLoadEvent()).WillRepeatedly(Return(load_event));
|
| + observer.set_mock_timing(timing.Pass());
|
| +
|
| + // The MockTiming above includes timing information for the first layout,
|
| + // dom content, and load metrics. However, since we've already generated
|
| + // the first layout and dom content metrics above, we only expect this
|
| + // invocation to generate the load metric.
|
| + EXPECT_CALL(*observer.ipc_interceptor(), OnLoadEvent(load_event - nav_start));
|
| + observer.DidFinishLoad();
|
| +
|
| + // Verify and reset the observer's expectations before moving on to the next
|
| + // part of the test.
|
| + testing::Mock::VerifyAndClearExpectations(observer.ipc_interceptor());
|
| +
|
| + timing = MockTiming::Create();
|
| + EXPECT_CALL(*timing, GetNavigationStart()).WillRepeatedly(Return(nav_start));
|
| + EXPECT_CALL(*timing, GetFirstLayout()).WillRepeatedly(Return(first_layout));
|
| + EXPECT_CALL(*timing, GetDOMContentLoadedEvent()).WillRepeatedly(
|
| + Return(dom_event));
|
| + EXPECT_CALL(*timing, GetLoadEvent()).WillRepeatedly(Return(load_event));
|
| + observer.set_mock_timing(timing.Pass());
|
| +
|
| + // The MockTiming 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.DidFinishLoad();
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +} // namespace page_load_metrics
|
|
|