1 // Copyright 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "third_party/blink/renderer/modules/presentation/presentation_connection_callbacks.h"
6
7 #include <utility>
8
9 #include "mojo/public/cpp/bindings/pending_receiver.h"
10 #include "mojo/public/cpp/bindings/pending_remote.h"
11 #include "testing/gmock/include/gmock/gmock.h" // MockFunctionScope
12 #include "testing/gtest/include/gtest/gtest.h"
13 #include "third_party/blink/public/mojom/presentation/presentation.mojom-blink.h"
14 #include "third_party/blink/renderer/bindings/core/v8/script_function.h" // MockFunctionScope
15 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
16 #include "third_party/blink/renderer/bindings/core/v8/script_value.h" // MockFunctionScope
17 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
18 #include "third_party/blink/renderer/modules/presentation/presentation_connection.h"
19 #include "third_party/blink/renderer/modules/presentation/presentation_request.h"
20 #include "third_party/blink/renderer/platform/bindings/script_state.h" // MockFunctionScope
21 #include "third_party/blink/renderer/platform/heap/heap.h"
22 #include "third_party/blink/renderer/platform/heap/persistent.h" // MockFunctionScope
23 #include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
24 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" // MockFunctionScope
25 #include "third_party/blink/renderer/platform/wtf/vector.h" // MockFunctionScope
26
27 constexpr char kPresentationUrl[] = "https://example.com";
28 constexpr char kPresentationId[] = "xyzzy";
29
30 namespace blink {
31
32 using mojom::blink::PresentationConnectionResult;
33 using mojom::blink::PresentationConnectionResultPtr;
34 using mojom::blink::PresentationConnectionState;
35 using mojom::blink::PresentationError;
36 using mojom::blink::PresentationErrorType;
37 using mojom::blink::PresentationInfo;
38 using mojom::blink::PresentationInfoPtr;
39
40 namespace {
41
42 // TODO(crbug.com/1120218): Consolidate with PaymentRequestMockFunctionScope and
43 // move to shared folder
44 class MockFunctionScope {
45 STACK_ALLOCATED();
46
47 public:
MockFunctionScope(ScriptState * script_state)48 explicit MockFunctionScope(ScriptState* script_state)
49 : script_state_(script_state) {}
~MockFunctionScope()50 ~MockFunctionScope() {
51 v8::MicrotasksScope::PerformCheckpoint(script_state_->GetIsolate());
52 for (MockFunction* mock_function : mock_functions_)
53 testing::Mock::VerifyAndClearExpectations(mock_function);
54 }
55
ExpectCall()56 v8::Local<v8::Function> ExpectCall() {
57 mock_functions_.push_back(
58 MakeGarbageCollected<MockFunction>(script_state_));
59 EXPECT_CALL(*mock_functions_.back(), Call(testing::_));
60 return mock_functions_.back()->Bind();
61 }
62
ExpectNoCall()63 v8::Local<v8::Function> ExpectNoCall() {
64 mock_functions_.push_back(
65 MakeGarbageCollected<MockFunction>(script_state_));
66 EXPECT_CALL(*mock_functions_.back(), Call(testing::_)).Times(0);
67 return mock_functions_.back()->Bind();
68 }
69
70 private:
71 class MockFunction : public ScriptFunction {
72 public:
MockFunction(ScriptState * script_state)73 explicit MockFunction(ScriptState* script_state)
74 : ScriptFunction(script_state) {
75 ON_CALL(*this, Call(testing::_)).WillByDefault(testing::ReturnArg<0>());
76 }
Bind()77 v8::Local<v8::Function> Bind() { return BindToV8Function(); }
78 MOCK_METHOD1(Call, ScriptValue(ScriptValue));
79 };
80
81 ScriptState* script_state_;
82 Vector<Persistent<MockFunction>> mock_functions_;
83 };
84
MakeRequest(V8TestingScope * scope)85 static PresentationRequest* MakeRequest(V8TestingScope* scope) {
86 PresentationRequest* request =
87 PresentationRequest::Create(scope->GetExecutionContext(),
88 kPresentationUrl, scope->GetExceptionState());
89 EXPECT_FALSE(scope->GetExceptionState().HadException());
90 return request;
91 }
92
93 } // namespace
94
TEST(PresentationConnectionCallbacksTest,HandleSuccess)95 TEST(PresentationConnectionCallbacksTest, HandleSuccess) {
96 V8TestingScope scope;
97 MockFunctionScope funcs(scope.GetScriptState());
98 auto* resolver =
99 MakeGarbageCollected<ScriptPromiseResolver>(scope.GetScriptState());
100 resolver->Promise().Then(funcs.ExpectCall(), funcs.ExpectNoCall());
101
102 PresentationConnectionCallbacks callbacks(resolver, MakeRequest(&scope));
103
104 // No connection currently exists.
105 EXPECT_FALSE(callbacks.connection_);
106
107 mojo::PendingRemote<mojom::blink::PresentationConnection> connection_remote;
108 mojo::PendingReceiver<mojom::blink::PresentationConnection>
109 connection_receiver = connection_remote.InitWithNewPipeAndPassReceiver();
110 PresentationConnectionResultPtr result = PresentationConnectionResult::New(
111 PresentationInfo::New(url_test_helpers::ToKURL(kPresentationUrl),
112 kPresentationId),
113 std::move(connection_remote), std::move(connection_receiver));
114
115 callbacks.HandlePresentationResponse(std::move(result), nullptr);
116
117 // New connection was created.
118 ControllerPresentationConnection* connection = callbacks.connection_.Get();
119 ASSERT_TRUE(connection);
120 EXPECT_EQ(connection->GetState(), PresentationConnectionState::CONNECTING);
121 }
122
TEST(PresentationConnectionCallbacksTest,HandleReconnect)123 TEST(PresentationConnectionCallbacksTest, HandleReconnect) {
124 V8TestingScope scope;
125 MockFunctionScope funcs(scope.GetScriptState());
126 PresentationInfoPtr info = PresentationInfo::New(
127 url_test_helpers::ToKURL(kPresentationUrl), kPresentationId);
128 auto* resolver =
129 MakeGarbageCollected<ScriptPromiseResolver>(scope.GetScriptState());
130 resolver->Promise().Then(funcs.ExpectCall(), funcs.ExpectNoCall());
131
132 auto* connection = ControllerPresentationConnection::Take(
133 resolver, *info, MakeRequest(&scope));
134 // Connection must be closed for reconnection to succeed.
135 connection->close();
136
137 PresentationConnectionCallbacks callbacks(resolver, connection);
138
139 mojo::PendingRemote<mojom::blink::PresentationConnection> connection_remote;
140 mojo::PendingReceiver<mojom::blink::PresentationConnection>
141 connection_receiver = connection_remote.InitWithNewPipeAndPassReceiver();
142 PresentationConnectionResultPtr result = PresentationConnectionResult::New(
143 std::move(info), std::move(connection_remote),
144 std::move(connection_receiver));
145
146 callbacks.HandlePresentationResponse(std::move(result), nullptr);
147
148 // Previous connection was returned.
149 ControllerPresentationConnection* new_connection =
150 callbacks.connection_.Get();
151 EXPECT_EQ(connection, new_connection);
152 EXPECT_EQ(new_connection->GetState(),
153 PresentationConnectionState::CONNECTING);
154 }
155
TEST(PresentationConnectionCallbacksTest,HandleError)156 TEST(PresentationConnectionCallbacksTest, HandleError) {
157 V8TestingScope scope;
158 MockFunctionScope funcs(scope.GetScriptState());
159 auto* resolver =
160 MakeGarbageCollected<ScriptPromiseResolver>(scope.GetScriptState());
161 resolver->Promise().Then(funcs.ExpectNoCall(), funcs.ExpectCall());
162
163 PresentationConnectionCallbacks callbacks(resolver, MakeRequest(&scope));
164
165 // No connection currently exists.
166 EXPECT_FALSE(callbacks.connection_);
167
168 callbacks.HandlePresentationResponse(
169 nullptr,
170 PresentationError::New(PresentationErrorType::NO_PRESENTATION_FOUND,
171 "Something bad happened"));
172
173 // No connection was created.
174 EXPECT_FALSE(callbacks.connection_);
175 }
176
177 } // namespace blink
178