blob: a60b49efac17203ad259a0e32f347cfa17a00117 [file] [log] [blame]
// Copyright 2016 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 "modules/remoteplayback/RemotePlayback.h"
#include "bindings/core/v8/ExceptionState.h"
#include "bindings/core/v8/V8BindingForTesting.h"
#include "bindings/modules/v8/RemotePlaybackAvailabilityCallback.h"
#include "core/dom/UserGestureIndicator.h"
#include "core/html/HTMLMediaElement.h"
#include "core/html/HTMLVideoElement.h"
#include "core/testing/DummyPageHolder.h"
#include "modules/remoteplayback/HTMLMediaElementRemotePlayback.h"
#include "platform/testing/UnitTestHelpers.h"
#include "public/platform/modules/remoteplayback/WebRemotePlaybackState.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace blink {
class MockFunction : public ScriptFunction {
public:
static ::testing::StrictMock<MockFunction>* Create(
ScriptState* script_state) {
return new ::testing::StrictMock<MockFunction>(script_state);
}
v8::Local<v8::Function> Bind() { return BindToV8Function(); }
MOCK_METHOD1(Call, ScriptValue(ScriptValue));
protected:
explicit MockFunction(ScriptState* script_state)
: ScriptFunction(script_state) {}
};
class MockEventListener : public EventListener {
public:
MockEventListener() : EventListener(kCPPEventListenerType) {}
bool operator==(const EventListener& other) const final {
return this == &other;
}
MOCK_METHOD2(handleEvent, void(ExecutionContext* executionContext, Event*));
};
class RemotePlaybackTest : public ::testing::Test {
protected:
void SetUp() override {
was_remote_playback_backend_enabled_ =
RuntimeEnabledFeatures::remotePlaybackBackendEnabled();
// Pretend the backend is enabled by default to test the API with backend
// implemented.
RuntimeEnabledFeatures::setRemotePlaybackBackendEnabled(true);
}
void TearDown() override {
RuntimeEnabledFeatures::setRemotePlaybackBackendEnabled(
was_remote_playback_backend_enabled_);
}
void CancelPrompt(RemotePlayback* remote_playback) {
remote_playback->PromptCancelled();
}
void SetState(RemotePlayback* remote_playback, WebRemotePlaybackState state) {
remote_playback->StateChanged(state);
}
private:
bool was_remote_playback_backend_enabled_;
};
TEST_F(RemotePlaybackTest, PromptCancelledRejectsWithNotAllowedError) {
V8TestingScope scope;
auto page_holder = DummyPageHolder::Create();
HTMLMediaElement* element =
HTMLVideoElement::Create(page_holder->GetDocument());
RemotePlayback* remote_playback =
HTMLMediaElementRemotePlayback::remote(*element);
auto resolve = MockFunction::Create(scope.GetScriptState());
auto reject = MockFunction::Create(scope.GetScriptState());
EXPECT_CALL(*resolve, Call(::testing::_)).Times(0);
EXPECT_CALL(*reject, Call(::testing::_)).Times(1);
UserGestureIndicator indicator(UserGestureToken::Create(
&page_holder->GetDocument(), UserGestureToken::kNewGesture));
remote_playback->prompt(scope.GetScriptState())
.Then(resolve->Bind(), reject->Bind());
CancelPrompt(remote_playback);
// Runs pending promises.
v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
// Verify mock expectations explicitly as the mock objects are garbage
// collected.
::testing::Mock::VerifyAndClear(resolve);
::testing::Mock::VerifyAndClear(reject);
}
TEST_F(RemotePlaybackTest, PromptConnectedRejectsWhenCancelled) {
V8TestingScope scope;
auto page_holder = DummyPageHolder::Create();
HTMLMediaElement* element =
HTMLVideoElement::Create(page_holder->GetDocument());
RemotePlayback* remote_playback =
HTMLMediaElementRemotePlayback::remote(*element);
auto resolve = MockFunction::Create(scope.GetScriptState());
auto reject = MockFunction::Create(scope.GetScriptState());
EXPECT_CALL(*resolve, Call(::testing::_)).Times(0);
EXPECT_CALL(*reject, Call(::testing::_)).Times(1);
SetState(remote_playback, WebRemotePlaybackState::kConnected);
UserGestureIndicator indicator(UserGestureToken::Create(
&page_holder->GetDocument(), UserGestureToken::kNewGesture));
remote_playback->prompt(scope.GetScriptState())
.Then(resolve->Bind(), reject->Bind());
CancelPrompt(remote_playback);
// Runs pending promises.
v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
// Verify mock expectations explicitly as the mock objects are garbage
// collected.
::testing::Mock::VerifyAndClear(resolve);
::testing::Mock::VerifyAndClear(reject);
}
TEST_F(RemotePlaybackTest, PromptConnectedResolvesWhenDisconnected) {
V8TestingScope scope;
auto page_holder = DummyPageHolder::Create();
HTMLMediaElement* element =
HTMLVideoElement::Create(page_holder->GetDocument());
RemotePlayback* remote_playback =
HTMLMediaElementRemotePlayback::remote(*element);
auto resolve = MockFunction::Create(scope.GetScriptState());
auto reject = MockFunction::Create(scope.GetScriptState());
EXPECT_CALL(*resolve, Call(::testing::_)).Times(1);
EXPECT_CALL(*reject, Call(::testing::_)).Times(0);
SetState(remote_playback, WebRemotePlaybackState::kConnected);
UserGestureIndicator indicator(UserGestureToken::Create(
&page_holder->GetDocument(), UserGestureToken::kNewGesture));
remote_playback->prompt(scope.GetScriptState())
.Then(resolve->Bind(), reject->Bind());
SetState(remote_playback, WebRemotePlaybackState::kDisconnected);
// Runs pending promises.
v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
// Verify mock expectations explicitly as the mock objects are garbage
// collected.
::testing::Mock::VerifyAndClear(resolve);
::testing::Mock::VerifyAndClear(reject);
}
TEST_F(RemotePlaybackTest, StateChangeEvents) {
V8TestingScope scope;
auto page_holder = DummyPageHolder::Create();
HTMLMediaElement* element =
HTMLVideoElement::Create(page_holder->GetDocument());
RemotePlayback* remote_playback =
HTMLMediaElementRemotePlayback::remote(*element);
auto connecting_handler = new ::testing::StrictMock<MockEventListener>();
auto connect_handler = new ::testing::StrictMock<MockEventListener>();
auto disconnect_handler = new ::testing::StrictMock<MockEventListener>();
remote_playback->addEventListener(EventTypeNames::connecting,
connecting_handler);
remote_playback->addEventListener(EventTypeNames::connect, connect_handler);
remote_playback->addEventListener(EventTypeNames::disconnect,
disconnect_handler);
EXPECT_CALL(*connecting_handler, handleEvent(::testing::_, ::testing::_))
.Times(1);
EXPECT_CALL(*connect_handler, handleEvent(::testing::_, ::testing::_))
.Times(1);
EXPECT_CALL(*disconnect_handler, handleEvent(::testing::_, ::testing::_))
.Times(1);
SetState(remote_playback, WebRemotePlaybackState::kConnecting);
SetState(remote_playback, WebRemotePlaybackState::kConnecting);
SetState(remote_playback, WebRemotePlaybackState::kConnected);
SetState(remote_playback, WebRemotePlaybackState::kConnected);
SetState(remote_playback, WebRemotePlaybackState::kDisconnected);
SetState(remote_playback, WebRemotePlaybackState::kDisconnected);
// Verify mock expectations explicitly as the mock objects are garbage
// collected.
::testing::Mock::VerifyAndClear(connecting_handler);
::testing::Mock::VerifyAndClear(connect_handler);
::testing::Mock::VerifyAndClear(disconnect_handler);
}
TEST_F(RemotePlaybackTest,
DisableRemotePlaybackRejectsPromptWithInvalidStateError) {
V8TestingScope scope;
auto page_holder = DummyPageHolder::Create();
HTMLMediaElement* element =
HTMLVideoElement::Create(page_holder->GetDocument());
RemotePlayback* remote_playback =
HTMLMediaElementRemotePlayback::remote(*element);
MockFunction* resolve = MockFunction::Create(scope.GetScriptState());
MockFunction* reject = MockFunction::Create(scope.GetScriptState());
EXPECT_CALL(*resolve, Call(::testing::_)).Times(0);
EXPECT_CALL(*reject, Call(::testing::_)).Times(1);
UserGestureIndicator indicator(UserGestureToken::Create(
&page_holder->GetDocument(), UserGestureToken::kNewGesture));
remote_playback->prompt(scope.GetScriptState())
.Then(resolve->Bind(), reject->Bind());
HTMLMediaElementRemotePlayback::SetBooleanAttribute(
HTMLNames::disableremoteplaybackAttr, *element, true);
// Runs pending promises.
v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
// Verify mock expectations explicitly as the mock objects are garbage
// collected.
::testing::Mock::VerifyAndClear(resolve);
::testing::Mock::VerifyAndClear(reject);
}
TEST_F(RemotePlaybackTest, DisableRemotePlaybackCancelsAvailabilityCallbacks) {
V8TestingScope scope;
auto page_holder = DummyPageHolder::Create();
HTMLMediaElement* element =
HTMLVideoElement::Create(page_holder->GetDocument());
RemotePlayback* remote_playback =
HTMLMediaElementRemotePlayback::remote(*element);
MockFunction* callback_function =
MockFunction::Create(scope.GetScriptState());
RemotePlaybackAvailabilityCallback* availability_callback =
RemotePlaybackAvailabilityCallback::Create(scope.GetScriptState(),
callback_function->Bind());
// The initial call upon registering will not happen as it's posted on the
// message loop.
EXPECT_CALL(*callback_function, Call(::testing::_)).Times(0);
MockFunction* resolve = MockFunction::Create(scope.GetScriptState());
MockFunction* reject = MockFunction::Create(scope.GetScriptState());
EXPECT_CALL(*resolve, Call(::testing::_)).Times(1);
EXPECT_CALL(*reject, Call(::testing::_)).Times(0);
remote_playback
->watchAvailability(scope.GetScriptState(), availability_callback)
.Then(resolve->Bind(), reject->Bind());
HTMLMediaElementRemotePlayback::SetBooleanAttribute(
HTMLNames::disableremoteplaybackAttr, *element, true);
// Runs pending promises.
v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
// Verify mock expectations explicitly as the mock objects are garbage
// collected.
::testing::Mock::VerifyAndClear(resolve);
::testing::Mock::VerifyAndClear(reject);
::testing::Mock::VerifyAndClear(callback_function);
}
TEST_F(RemotePlaybackTest, PromptThrowsWhenBackendDisabled) {
RuntimeEnabledFeatures::setRemotePlaybackBackendEnabled(false);
V8TestingScope scope;
auto page_holder = DummyPageHolder::Create();
HTMLMediaElement* element =
HTMLVideoElement::Create(page_holder->GetDocument());
RemotePlayback* remote_playback =
HTMLMediaElementRemotePlayback::remote(*element);
auto resolve = MockFunction::Create(scope.GetScriptState());
auto reject = MockFunction::Create(scope.GetScriptState());
EXPECT_CALL(*resolve, Call(::testing::_)).Times(0);
EXPECT_CALL(*reject, Call(::testing::_)).Times(1);
UserGestureIndicator indicator(UserGestureToken::Create(
&page_holder->GetDocument(), UserGestureToken::kNewGesture));
remote_playback->prompt(scope.GetScriptState())
.Then(resolve->Bind(), reject->Bind());
// Runs pending promises.
v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
// Verify mock expectations explicitly as the mock objects are garbage
// collected.
::testing::Mock::VerifyAndClear(resolve);
::testing::Mock::VerifyAndClear(reject);
}
TEST_F(RemotePlaybackTest, WatchAvailabilityWorksWhenBackendDisabled) {
RuntimeEnabledFeatures::setRemotePlaybackBackendEnabled(false);
V8TestingScope scope;
auto page_holder = DummyPageHolder::Create();
HTMLMediaElement* element =
HTMLVideoElement::Create(page_holder->GetDocument());
RemotePlayback* remote_playback =
HTMLMediaElementRemotePlayback::remote(*element);
MockFunction* callback_function =
MockFunction::Create(scope.GetScriptState());
RemotePlaybackAvailabilityCallback* availability_callback =
RemotePlaybackAvailabilityCallback::Create(scope.GetScriptState(),
callback_function->Bind());
// The initial call upon registering will not happen as it's posted on the
// message loop.
EXPECT_CALL(*callback_function, Call(::testing::_)).Times(0);
MockFunction* resolve = MockFunction::Create(scope.GetScriptState());
MockFunction* reject = MockFunction::Create(scope.GetScriptState());
EXPECT_CALL(*resolve, Call(::testing::_)).Times(1);
EXPECT_CALL(*reject, Call(::testing::_)).Times(0);
remote_playback
->watchAvailability(scope.GetScriptState(), availability_callback)
.Then(resolve->Bind(), reject->Bind());
// Runs pending promises.
v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
// Verify mock expectations explicitly as the mock objects are garbage
// collected.
::testing::Mock::VerifyAndClear(resolve);
::testing::Mock::VerifyAndClear(reject);
::testing::Mock::VerifyAndClear(callback_function);
}
} // namespace blink