| Index: third_party/WebKit/Source/core/html/AutoplayExperimentTest.cpp
|
| diff --git a/third_party/WebKit/Source/core/html/AutoplayExperimentTest.cpp b/third_party/WebKit/Source/core/html/AutoplayExperimentTest.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..5614a0fb54c04ed7ee30ce4a14b71c2564e378e2
|
| --- /dev/null
|
| +++ b/third_party/WebKit/Source/core/html/AutoplayExperimentTest.cpp
|
| @@ -0,0 +1,436 @@
|
| +// Copyright 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 "core/dom/Document.h"
|
| +#include "core/html/AutoplayExperimentHelper.h"
|
| +#include "platform/UserGestureIndicator.h"
|
| +#include "testing/gmock/include/gmock/gmock.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +
|
| +// msvc refuses to compile if we use all of ::testing, due to a conflict with
|
| +// WTF::NotNull. So, we just use what we need.
|
| +using ::testing::Return;
|
| +using ::testing::NiceMock;
|
| +using ::testing::_;
|
| +
|
| +namespace blink {
|
| +
|
| +class MockAutoplayClient : public AutoplayExperimentHelper::Client {
|
| +public:
|
| + enum ElementType { Video, Audio };
|
| +
|
| + MockAutoplayClient(const char* mode, ElementType type)
|
| + : m_mode(mode)
|
| + , m_duration(100)
|
| + {
|
| + // Set up default behaviors that the helper is allowed to use or cache
|
| + // during construction.
|
| +
|
| + ON_CALL(*this, autoplayExperimentMode())
|
| + .WillByDefault(Return(m_mode));
|
| +
|
| + // Use m_isVideo to answer these.
|
| + ON_CALL(*this, isHTMLVideoElement())
|
| + .WillByDefault(Return(type == Video));
|
| + ON_CALL(*this, isHTMLAudioElement())
|
| + .WillByDefault(Return(type == Audio));
|
| +
|
| + // Now set up some useful defaults.
|
| + // Remember that this are only evaluated once.
|
| + ON_CALL(*this, duration())
|
| + .WillByDefault(Return(m_duration));
|
| + ON_CALL(*this, currentTime())
|
| + .WillByDefault(Return(0));
|
| +
|
| + // Default to "not optimized for mobile" page.
|
| + ON_CALL(*this, isLegacyViewportType())
|
| + .WillByDefault(Return(false));
|
| +
|
| + // Other handy defaults.
|
| + ON_CALL(*this, ended())
|
| + .WillByDefault(Return(false));
|
| + ON_CALL(*this, pageVisibilityState())
|
| + .WillByDefault(Return(PageVisibilityStateVisible));
|
| + ON_CALL(*this, absoluteBoundingBoxRect())
|
| + .WillByDefault(Return(
|
| + IntRect(10, 10, 100, 100)));
|
| +
|
| + // Normally, the autoplay experiment should not modify lots of other
|
| + // state unless we explicitly expect it.
|
| + EXPECT_CALL(*this, setMuted(_))
|
| + .Times(0);
|
| + EXPECT_CALL(*this, removeUserGestureRequirement())
|
| + .Times(0);
|
| + EXPECT_CALL(*this, setRequestPositionUpdates(true))
|
| + .Times(0);
|
| + EXPECT_CALL(*this, recordAutoplayMetric(_))
|
| + .Times(0);
|
| + }
|
| +
|
| + virtual ~MockAutoplayClient() {}
|
| +
|
| + MOCK_CONST_METHOD0(currentTime, double());
|
| + MOCK_CONST_METHOD0(duration, double());
|
| + MOCK_CONST_METHOD0(ended, bool());
|
| + MOCK_CONST_METHOD0(muted, bool());
|
| + MOCK_METHOD1(setMuted, void(bool));
|
| + MOCK_METHOD0(playInternal, void());
|
| + MOCK_CONST_METHOD0(isUserGestureRequiredForPlay, bool());
|
| + MOCK_METHOD0(removeUserGestureRequirement, void());
|
| + MOCK_METHOD1(recordAutoplayMetric, void(AutoplayMetrics));
|
| + MOCK_METHOD0(shouldAutoplay, bool());
|
| + MOCK_CONST_METHOD0(isHTMLVideoElement, bool());
|
| + MOCK_CONST_METHOD0(isHTMLAudioElement, bool());
|
| + MOCK_METHOD0(isLegacyViewportType, bool());
|
| + MOCK_CONST_METHOD0(pageVisibilityState, PageVisibilityState());
|
| + MOCK_CONST_METHOD0(autoplayExperimentMode, String());
|
| + MOCK_METHOD1(setRequestPositionUpdates, void(bool));
|
| + MOCK_CONST_METHOD0(absoluteBoundingBoxRect, IntRect());
|
| +
|
| + const char* m_mode;
|
| + // const since changes to it won't affect the mocked value.
|
| + const double m_duration;
|
| +};
|
| +
|
| +class AutoplayExperimentTest : public ::testing::Test {
|
| +public:
|
| + AutoplayExperimentTest() {}
|
| +
|
| + ~AutoplayExperimentTest() {}
|
| +
|
| + bool isEligible()
|
| + {
|
| + return m_helper->isEligible();
|
| + }
|
| +
|
| + void setInterface(PassOwnPtrWillBeRawPtr<MockAutoplayClient> client)
|
| + {
|
| + m_client = client;
|
| +
|
| + // Set some defaults.
|
| + setUserGestureRequiredForPlay(true);
|
| + setShouldAutoplay(true);
|
| + setIsMuted(false);
|
| +
|
| + m_helper = AutoplayExperimentHelper::create(m_client.get());
|
| + }
|
| +
|
| +#if ENABLE(OILPAN)
|
| + void TearDown()
|
| + {
|
| + // Be sure that the mock is destructed before the test, so that any
|
| + // missing expectations are noticed. Otherwise, the test will pass
|
| + // and then missing expectations will show up in the log, without
|
| + // causing a test failure.
|
| + m_helper.clear();
|
| + m_client.clear();
|
| + Heap::collectAllGarbage();
|
| + }
|
| +#endif
|
| +
|
| + OwnPtrWillBePersistent<MockAutoplayClient> m_client;
|
| + OwnPtrWillBePersistent<AutoplayExperimentHelper> m_helper;
|
| +
|
| + // Mirror updatePlayState to transition to play.
|
| + void startPlayback()
|
| + {
|
| + EXPECT_CALL(*m_client, recordAutoplayMetric(AnyPlaybackStarted))
|
| + .Times(1);
|
| + m_helper->playbackStarted();
|
| + }
|
| +
|
| + void startPlaybackWithoutUserGesture()
|
| + {
|
| + EXPECT_FALSE(UserGestureIndicator::processingUserGesture());
|
| + startPlayback();
|
| + }
|
| +
|
| + void startPlaybackWithUserGesture()
|
| + {
|
| + UserGestureIndicator indicator(DefinitelyProcessingUserGesture);
|
| + EXPECT_TRUE(UserGestureIndicator::processingUserGesture());
|
| + startPlayback();
|
| + }
|
| +
|
| + void setUserGestureRequiredForPlay(bool required)
|
| + {
|
| + ON_CALL(*m_client, isUserGestureRequiredForPlay())
|
| + .WillByDefault(Return(required));
|
| + }
|
| +
|
| + void setShouldAutoplay(bool should)
|
| + {
|
| + ON_CALL(*m_client, shouldAutoplay())
|
| + .WillByDefault(Return(should));
|
| + }
|
| +
|
| + void setIsMuted(bool isMuted)
|
| + {
|
| + ON_CALL(*m_client, muted())
|
| + .WillByDefault(Return(isMuted));
|
| + }
|
| +
|
| + void pausePlaybackExpectingBailout(bool expectingAutoplay)
|
| + {
|
| + EXPECT_CALL(*m_client, recordAutoplayMetric(AnyPlaybackPaused))
|
| + .Times(1);
|
| + EXPECT_CALL(*m_client, recordAutoplayMetric(AnyPlaybackBailout))
|
| + .Times(1);
|
| + if (expectingAutoplay) {
|
| + EXPECT_CALL(*m_client, recordAutoplayMetric(AutoplayPaused))
|
| + .Times(1);
|
| + EXPECT_CALL(*m_client, recordAutoplayMetric(AutoplayBailout))
|
| + .Times(1);
|
| + }
|
| + m_helper->pauseMethodCalled();
|
| + m_helper->playbackStopped();
|
| + }
|
| +
|
| + void pausePlaybackNotExpectingBailout(bool expectingAutoplay)
|
| + {
|
| + EXPECT_CALL(*m_client, recordAutoplayMetric(AnyPlaybackPaused))
|
| + .Times(1);
|
| + if (expectingAutoplay)
|
| + EXPECT_CALL(*m_client, recordAutoplayMetric(AutoplayPaused)).Times(1);
|
| + EXPECT_CALL(*m_client, recordAutoplayMetric(AnyPlaybackBailout)).Times(0);
|
| + EXPECT_CALL(*m_client, recordAutoplayMetric(AutoplayBailout)).Times(0);
|
| +
|
| + // Move past the bailout point, but not to the end. That way, we don't
|
| + // have to set ended().
|
| + ON_CALL(*m_client, currentTime())
|
| + .WillByDefault(Return(m_client->m_duration-1));
|
| +
|
| + m_helper->pauseMethodCalled();
|
| + m_helper->playbackStopped();
|
| + }
|
| +
|
| + void endPlayback(bool expectingAutoplay)
|
| + {
|
| + EXPECT_CALL(*m_client, recordAutoplayMetric(AnyPlaybackComplete))
|
| + .Times(1);
|
| + if (expectingAutoplay)
|
| + EXPECT_CALL(*m_client, recordAutoplayMetric(AutoplayComplete)).Times(1);
|
| + ON_CALL(*m_client, currentTime())
|
| + .WillByDefault(Return(m_client->m_duration));
|
| + ON_CALL(*m_client, ended())
|
| + .WillByDefault(Return(true));
|
| + m_helper->playbackStopped();
|
| + }
|
| +
|
| + void moveIntoViewport()
|
| + {
|
| + m_helper->positionChanged(IntRect(0, 0, 200, 200));
|
| + m_helper->triggerAutoplayViewportCheckForTesting();
|
| + }
|
| +};
|
| +
|
| +TEST_F(AutoplayExperimentTest, IsNotEligibleWithEmptyMode)
|
| +{
|
| + setInterface(adoptPtrWillBeNoop(new NiceMock<MockAutoplayClient>("", MockAutoplayClient::Video)));
|
| + EXPECT_FALSE(isEligible());
|
| +}
|
| +
|
| +TEST_F(AutoplayExperimentTest, IsVideoEligibleForVideoMode)
|
| +{
|
| + // Video should be eligible in "forvideo" mode.
|
| + setInterface(adoptPtrWillBeNoop(new NiceMock<MockAutoplayClient>("enabled-forvideo", MockAutoplayClient::Video)));
|
| + EXPECT_TRUE(isEligible());
|
| +}
|
| +
|
| +TEST_F(AutoplayExperimentTest, IsAudioNotEligibleForVideoMode)
|
| +{
|
| + // Audio should not be eligible for video mode.
|
| + setInterface(adoptPtrWillBeNoop(new NiceMock<MockAutoplayClient>("enabled-forvideo", MockAutoplayClient::Audio)));
|
| + EXPECT_FALSE(isEligible());
|
| +}
|
| +
|
| +TEST_F(AutoplayExperimentTest, IsEligibleRequiresUserGesture)
|
| +{
|
| + setInterface(adoptPtrWillBeNoop(new NiceMock<MockAutoplayClient>("enabled-forvideo", MockAutoplayClient::Video)));
|
| + // If a user gesture is not required, then we're not eligible.
|
| + ON_CALL(*m_client, isUserGestureRequiredForPlay())
|
| + .WillByDefault(Return(false));
|
| + EXPECT_FALSE(isEligible());
|
| +}
|
| +
|
| +TEST_F(AutoplayExperimentTest, IsEligibleRequiresShouldAutoplay)
|
| +{
|
| + setInterface(adoptPtrWillBeNoop(new NiceMock<MockAutoplayClient>("enabled-forvideo", MockAutoplayClient::Video)));
|
| + // If we shouldn't autoplay, then we're not eligible.
|
| + ON_CALL(*m_client, shouldAutoplay())
|
| + .WillByDefault(Return(false));
|
| + EXPECT_FALSE(isEligible());
|
| +}
|
| +
|
| +TEST_F(AutoplayExperimentTest, IsAudioEligibleForAudioMode)
|
| +{
|
| + setInterface(adoptPtrWillBeNoop(new NiceMock<MockAutoplayClient>("enabled-foraudio", MockAutoplayClient::Audio)));
|
| + EXPECT_TRUE(isEligible());
|
| +}
|
| +
|
| +TEST_F(AutoplayExperimentTest, EligibleIfOptimizedForMobile)
|
| +{
|
| + setInterface(adoptPtrWillBeNoop(new NiceMock<MockAutoplayClient>("enabled-forvideo-ifmobile", MockAutoplayClient::Video)));
|
| + // Should not be eligible with our default of "not mobile".
|
| + EXPECT_FALSE(isEligible());
|
| +
|
| + ON_CALL(*m_client, isLegacyViewportType())
|
| + .WillByDefault(Return(true));
|
| + EXPECT_TRUE(isEligible());
|
| +}
|
| +
|
| +TEST_F(AutoplayExperimentTest, EligibleIfMuted)
|
| +{
|
| + setInterface(adoptPtrWillBeNoop(new NiceMock<MockAutoplayClient>("enabled-forvideo-ifmuted", MockAutoplayClient::Video)));
|
| + // Should not be eligible with our default of "not muted".
|
| + EXPECT_FALSE(isEligible());
|
| +
|
| + ON_CALL(*m_client, muted())
|
| + .WillByDefault(Return(true));
|
| + EXPECT_TRUE(isEligible());
|
| +}
|
| +
|
| +TEST_F(AutoplayExperimentTest, BecameReadyAutoplayThenBailout)
|
| +{
|
| + setInterface(adoptPtrWillBeNoop(new NiceMock<MockAutoplayClient>("enabled-forvideo", MockAutoplayClient::Video)));
|
| +
|
| + EXPECT_CALL(*m_client, removeUserGestureRequirement())
|
| + .Times(1);
|
| + EXPECT_CALL(*m_client, recordAutoplayMetric(AutoplayMediaFound))
|
| + .Times(1);
|
| + m_helper->becameReadyToPlay();
|
| +
|
| + EXPECT_CALL(*m_client, recordAutoplayMetric(GesturelessPlaybackStartedByAutoplayFlagImmediately))
|
| + .Times(1);
|
| + startPlaybackWithoutUserGesture();
|
| +
|
| + pausePlaybackExpectingBailout(true);
|
| +}
|
| +
|
| +TEST_F(AutoplayExperimentTest, BecameReadyAutoplayThenPause)
|
| +{
|
| + setInterface(adoptPtrWillBeNoop(new NiceMock<MockAutoplayClient>("enabled-forvideo", MockAutoplayClient::Video)));
|
| +
|
| + EXPECT_CALL(*m_client, removeUserGestureRequirement())
|
| + .Times(1);
|
| + EXPECT_CALL(*m_client, recordAutoplayMetric(AutoplayMediaFound))
|
| + .Times(1);
|
| + m_helper->becameReadyToPlay();
|
| +
|
| + EXPECT_CALL(*m_client, recordAutoplayMetric(GesturelessPlaybackStartedByAutoplayFlagImmediately))
|
| + .Times(1);
|
| + startPlaybackWithoutUserGesture();
|
| +
|
| + pausePlaybackNotExpectingBailout(true);
|
| +}
|
| +
|
| +TEST_F(AutoplayExperimentTest, BecameReadyAutoplayThenComplete)
|
| +{
|
| + setInterface(adoptPtrWillBeNoop(new NiceMock<MockAutoplayClient>("enabled-forvideo", MockAutoplayClient::Video)));
|
| +
|
| + EXPECT_CALL(*m_client, removeUserGestureRequirement())
|
| + .Times(1);
|
| + EXPECT_CALL(*m_client, recordAutoplayMetric(AutoplayMediaFound))
|
| + .Times(1);
|
| + m_helper->becameReadyToPlay();
|
| +
|
| + EXPECT_CALL(*m_client, recordAutoplayMetric(GesturelessPlaybackStartedByAutoplayFlagImmediately))
|
| + .Times(1);
|
| + startPlaybackWithoutUserGesture();
|
| +
|
| + // Now stop at the end.
|
| + endPlayback(true);
|
| +}
|
| +
|
| +TEST_F(AutoplayExperimentTest, NoUserGestureNeededShouldNotOverride)
|
| +{
|
| + // Make sure that we don't override the user gesture if it isn't needed.
|
| + setInterface(adoptPtrWillBeNoop(new NiceMock<MockAutoplayClient>("enabled-forvideo", MockAutoplayClient::Video)));
|
| + setUserGestureRequiredForPlay(false);
|
| +
|
| + // It is still autoplay media, though.
|
| + EXPECT_CALL(*m_client, recordAutoplayMetric(AutoplayMediaFound))
|
| + .Times(1);
|
| + m_helper->becameReadyToPlay();
|
| +
|
| + EXPECT_CALL(*m_client, recordAutoplayMetric(GesturelessPlaybackNotOverridden))
|
| + .Times(1);
|
| + startPlaybackWithoutUserGesture();
|
| +}
|
| +
|
| +TEST_F(AutoplayExperimentTest, NoAutoplayMetricsIfNoAutoplay)
|
| +{
|
| + // If playback is started while processing a user gesture, then nothing
|
| + // should be overridden or logged about autoplay.
|
| + setInterface(adoptPtrWillBeNoop(new NiceMock<MockAutoplayClient>("enabled-forvideo", MockAutoplayClient::Video)));
|
| + setUserGestureRequiredForPlay(false);
|
| + setShouldAutoplay(false);
|
| + startPlaybackWithUserGesture();
|
| +
|
| + // Expect bailout, but not from autoplay.
|
| + pausePlaybackExpectingBailout(false);
|
| +}
|
| +
|
| +TEST_F(AutoplayExperimentTest, PlayMethodThenBailout)
|
| +{
|
| + setInterface(adoptPtrWillBeNoop(new NiceMock<MockAutoplayClient>("enabled-forvideo", MockAutoplayClient::Video)));
|
| + setShouldAutoplay(false); // No autoplay attribute.
|
| +
|
| + EXPECT_CALL(*m_client, removeUserGestureRequirement())
|
| + .Times(1);
|
| + EXPECT_CALL(*m_client, recordAutoplayMetric(AutoplayMediaFound))
|
| + .Times(1);
|
| + m_helper->playMethodCalled();
|
| +
|
| + EXPECT_CALL(*m_client, recordAutoplayMetric(GesturelessPlaybackStartedByPlayMethodImmediately))
|
| + .Times(1);
|
| + startPlaybackWithoutUserGesture();
|
| +
|
| + pausePlaybackExpectingBailout(true);
|
| +}
|
| +
|
| +TEST_F(AutoplayExperimentTest, DeferAutoplayUntilMuted)
|
| +{
|
| + setInterface(adoptPtrWillBeNoop(new NiceMock<MockAutoplayClient>("enabled-forvideo-ifmuted", MockAutoplayClient::Video)));
|
| +
|
| + // Should not override the gesture requirement yet.
|
| + EXPECT_CALL(*m_client, recordAutoplayMetric(AutoplayMediaFound))
|
| + .Times(1);
|
| + m_helper->becameReadyToPlay();
|
| +
|
| + // When we toggle the muted attribute, it should start.
|
| + EXPECT_CALL(*m_client, removeUserGestureRequirement())
|
| + .Times(1);
|
| + EXPECT_CALL(*m_client, playInternal())
|
| + .Times(1);
|
| + setIsMuted(true);
|
| + m_helper->mutedChanged();
|
| +
|
| + // When playback starts (in response to playInternal()), it should also
|
| + // record why. 'After scroll' isn't the best name, but this isn't a common case.
|
| + EXPECT_CALL(*m_client, recordAutoplayMetric(GesturelessPlaybackStartedByAutoplayFlagAfterScroll))
|
| + .Times(1);
|
| + startPlaybackWithoutUserGesture();
|
| +}
|
| +
|
| +TEST_F(AutoplayExperimentTest, DeferPlaybackUntilInViewport)
|
| +{
|
| + setInterface(adoptPtrWillBeNoop(new NiceMock<MockAutoplayClient>("enabled-forvideo-ifviewport", MockAutoplayClient::Video)));
|
| +
|
| + // Should not override the gesture requirement yet.
|
| + EXPECT_CALL(*m_client, recordAutoplayMetric(AutoplayMediaFound))
|
| + .Times(1);
|
| + EXPECT_CALL(*m_client, setRequestPositionUpdates(true))
|
| + .Times(1);
|
| + m_helper->becameReadyToPlay();
|
| +
|
| + EXPECT_CALL(*m_client, removeUserGestureRequirement())
|
| + .Times(1);
|
| + EXPECT_CALL(*m_client, playInternal())
|
| + .Times(1);
|
| + EXPECT_CALL(*m_client, setRequestPositionUpdates(false))
|
| + .Times(1);
|
| + moveIntoViewport();
|
| +}
|
| +}
|
|
|