1 // Copyright 2019 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/public/common/messaging/message_port_descriptor.h"
6
7 #include "base/test/gtest_util.h"
8 #include "testing/gmock/include/gmock/gmock.h"
9 #include "testing/gtest/include/gtest/gtest.h"
10
11 namespace blink {
12
13 namespace {
14
15 class LenientMockInstrumentationDelegate
16 : public MessagePortDescriptor::InstrumentationDelegate {
17 public:
LenientMockInstrumentationDelegate()18 LenientMockInstrumentationDelegate() {
19 MessagePortDescriptor::SetInstrumentationDelegate(this);
20 }
21
~LenientMockInstrumentationDelegate()22 ~LenientMockInstrumentationDelegate() override {
23 MessagePortDescriptor::SetInstrumentationDelegate(nullptr);
24 }
25
26 MOCK_METHOD2(NotifyMessagePortPairCreated,
27 void(const base::UnguessableToken& port0_id,
28 const base::UnguessableToken& port1_id));
29
30 MOCK_METHOD3(NotifyMessagePortAttached,
31 void(const base::UnguessableToken& port_id,
32 uint64_t sequence_number,
33 const base::UnguessableToken& execution_context_id));
34
35 MOCK_METHOD2(NotifyMessagePortAttachedToEmbedder,
36 void(const base::UnguessableToken& port_id,
37 uint64_t sequence_number));
38
39 MOCK_METHOD2(NotifyMessagePortDetached,
40 void(const base::UnguessableToken& port_id,
41 uint64_t sequence_number));
42
43 MOCK_METHOD2(NotifyMessagePortDestroyed,
44 void(const base::UnguessableToken& port_id,
45 uint64_t sequence_number));
46 };
47
48 using MockInstrumentationDelegate =
49 testing::StrictMock<LenientMockInstrumentationDelegate>;
50
51 using testing::_;
52 using testing::Invoke;
53
54 } // namespace
55
56 class MessagePortDescriptorTestHelper {
57 public:
Init(MessagePortDescriptor * port,mojo::ScopedMessagePipeHandle handle,base::UnguessableToken id,uint64_t sequence_number)58 static void Init(MessagePortDescriptor* port,
59 mojo::ScopedMessagePipeHandle handle,
60 base::UnguessableToken id,
61 uint64_t sequence_number) {
62 return port->Init(std::move(handle), id, sequence_number);
63 }
64
TakeHandle(MessagePortDescriptor * port)65 static mojo::ScopedMessagePipeHandle TakeHandle(MessagePortDescriptor* port) {
66 return port->TakeHandle();
67 }
68
TakeId(MessagePortDescriptor * port)69 static base::UnguessableToken TakeId(MessagePortDescriptor* port) {
70 return port->TakeId();
71 }
72
TakeSequenceNumber(MessagePortDescriptor * port)73 static uint64_t TakeSequenceNumber(MessagePortDescriptor* port) {
74 return port->TakeSequenceNumber();
75 }
76
TakeHandleToEntangle(MessagePortDescriptor * port,const base::UnguessableToken & execution_context_id)77 static mojo::ScopedMessagePipeHandle TakeHandleToEntangle(
78 MessagePortDescriptor* port,
79 const base::UnguessableToken& execution_context_id) {
80 return port->TakeHandleToEntangle(execution_context_id);
81 }
82
GiveDisentangledHandle(MessagePortDescriptor * port,mojo::ScopedMessagePipeHandle handle)83 static void GiveDisentangledHandle(MessagePortDescriptor* port,
84 mojo::ScopedMessagePipeHandle handle) {
85 return port->GiveDisentangledHandle(std::move(handle));
86 }
87 };
88
TEST(MessagePortDescriptorTest,InstrumentationAndSerializationWorks)89 TEST(MessagePortDescriptorTest, InstrumentationAndSerializationWorks) {
90 MockInstrumentationDelegate delegate;
91
92 // A small struct for holding information gleaned about ports during their
93 // creation event. Allows verifying that other events are appropriately
94 // sequenced.
95 struct {
96 base::UnguessableToken token0;
97 base::UnguessableToken token1;
98 uint64_t seq0 = 1;
99 uint64_t seq1 = 1;
100 } created_data;
101
102 // Create a message handle descriptor pair and expect a notification.
103 EXPECT_CALL(delegate, NotifyMessagePortPairCreated(_, _))
104 .WillOnce(Invoke([&created_data](const base::UnguessableToken& port0_id,
105 const base::UnguessableToken& port1_id) {
106 created_data.token0 = port0_id;
107 created_data.token1 = port1_id;
108 }));
109 MessagePortDescriptorPair pair;
110
111 MessagePortDescriptor port0;
112 MessagePortDescriptor port1;
113 EXPECT_FALSE(port0.IsValid());
114 EXPECT_FALSE(port1.IsValid());
115 EXPECT_FALSE(port0.IsEntangled());
116 EXPECT_FALSE(port1.IsEntangled());
117 EXPECT_TRUE(port0.IsDefault());
118 EXPECT_TRUE(port1.IsDefault());
119 port0 = pair.TakePort0();
120 port1 = pair.TakePort1();
121 EXPECT_TRUE(port0.IsValid());
122 EXPECT_TRUE(port1.IsValid());
123 EXPECT_FALSE(port0.IsEntangled());
124 EXPECT_FALSE(port1.IsEntangled());
125 EXPECT_FALSE(port0.IsDefault());
126 EXPECT_FALSE(port1.IsDefault());
127
128 // Expect that the data received at creation matches the actual ports.
129 EXPECT_EQ(created_data.token0, port0.id());
130 EXPECT_EQ(created_data.seq0, port0.sequence_number());
131 EXPECT_EQ(created_data.token1, port1.id());
132 EXPECT_EQ(created_data.seq1, port1.sequence_number());
133
134 // Simulate that a handle is attached by taking the pipe handle.
135 base::UnguessableToken dummy_ec = base::UnguessableToken::Create();
136 EXPECT_CALL(delegate,
137 NotifyMessagePortAttached(created_data.token0,
138 created_data.seq0++, dummy_ec));
139 auto handle0 =
140 MessagePortDescriptorTestHelper::TakeHandleToEntangle(&port0, dummy_ec);
141 EXPECT_TRUE(port0.IsValid());
142 EXPECT_TRUE(port0.IsEntangled());
143 EXPECT_FALSE(port0.IsDefault());
144
145 // Simulate that the handle is detached by giving the pipe handle back.
146 EXPECT_CALL(delegate, NotifyMessagePortDetached(created_data.token0,
147 created_data.seq0++));
148 MessagePortDescriptorTestHelper::GiveDisentangledHandle(&port0,
149 std::move(handle0));
150 EXPECT_TRUE(port0.IsValid());
151 EXPECT_FALSE(port0.IsEntangled());
152 EXPECT_FALSE(port0.IsDefault());
153
154 // Tear down a handle explicitly.
155 EXPECT_CALL(delegate, NotifyMessagePortDestroyed(created_data.token1,
156 created_data.seq1++));
157 port1.Reset();
158
159 // And leave the other handle to be torn down in the destructor.
160 EXPECT_CALL(delegate, NotifyMessagePortDestroyed(created_data.token0,
161 created_data.seq0++));
162 }
163
TEST(MessagePortDescriptorTest,InvalidUsageDeathTest)164 TEST(MessagePortDescriptorTest, InvalidUsageDeathTest) {
165 static MessagePortDescriptor::InstrumentationDelegate* kDummyDelegate1 =
166 reinterpret_cast<MessagePortDescriptor::InstrumentationDelegate*>(
167 0xBAADF00D);
168 static MessagePortDescriptor::InstrumentationDelegate* kDummyDelegate2 =
169 reinterpret_cast<MessagePortDescriptor::InstrumentationDelegate*>(
170 0xDEADBEEF);
171 EXPECT_DCHECK_DEATH(
172 MessagePortDescriptor::SetInstrumentationDelegate(nullptr));
173 MessagePortDescriptor::SetInstrumentationDelegate(kDummyDelegate1);
174 // Setting the same or another delegate should explode.
175 EXPECT_DCHECK_DEATH(
176 MessagePortDescriptor::SetInstrumentationDelegate(kDummyDelegate1));
177 EXPECT_DCHECK_DEATH(
178 MessagePortDescriptor::SetInstrumentationDelegate(kDummyDelegate2));
179 // Unset the dummy delegate we installed so we don't receive notifications in
180 // the rest of the test.
181 MessagePortDescriptor::SetInstrumentationDelegate(nullptr);
182
183 // Trying to take properties of a default port descriptor should explode.
184 MessagePortDescriptor port0;
185 EXPECT_DCHECK_DEATH(MessagePortDescriptorTestHelper::TakeHandle(&port0));
186 EXPECT_DCHECK_DEATH(MessagePortDescriptorTestHelper::TakeId(&port0));
187 EXPECT_DCHECK_DEATH(
188 MessagePortDescriptorTestHelper::TakeSequenceNumber(&port0));
189
190 MessagePortDescriptorPair pair;
191 port0 = pair.TakePort0();
192 MessagePortDescriptor port1 = pair.TakePort1();
193
194 {
195 // Dismantle the port as if for serialization.
196 auto handle = MessagePortDescriptorTestHelper::TakeHandle(&port0);
197 auto id = MessagePortDescriptorTestHelper::TakeId(&port0);
198 auto sequence_number =
199 MessagePortDescriptorTestHelper::TakeSequenceNumber(&port0);
200
201 // Reserializing with inconsistent state should explode.
202
203 // First try with any 1 of the 3 fields being invalid.
204 EXPECT_DCHECK_DEATH(MessagePortDescriptorTestHelper::Init(
205 &port0, mojo::ScopedMessagePipeHandle(), id, sequence_number));
206 EXPECT_DCHECK_DEATH(MessagePortDescriptorTestHelper::Init(
207 &port0, std::move(handle), base::UnguessableToken::Null(),
208 sequence_number));
209 EXPECT_DCHECK_DEATH(MessagePortDescriptorTestHelper::Init(
210 &port0, std::move(handle), id, 0));
211
212 // Next try with any 2 of the 3 fields being invalid.
213 EXPECT_DCHECK_DEATH(MessagePortDescriptorTestHelper::Init(
214 &port0, std::move(handle), base::UnguessableToken::Null(), 0));
215 EXPECT_DCHECK_DEATH(MessagePortDescriptorTestHelper::Init(
216 &port0, mojo::ScopedMessagePipeHandle(), id, 0));
217 EXPECT_DCHECK_DEATH(MessagePortDescriptorTestHelper::Init(
218 &port0, mojo::ScopedMessagePipeHandle(), base::UnguessableToken::Null(),
219 sequence_number));
220
221 // Restoring the port with default state should work (all 3 fields invalid).
222 MessagePortDescriptorTestHelper::Init(&port0,
223 mojo::ScopedMessagePipeHandle(),
224 base::UnguessableToken::Null(), 0);
225 EXPECT_TRUE(port0.IsDefault());
226
227 // Restoring the port with full state should work (all 3 fields valid).
228 MessagePortDescriptorTestHelper::Init(&port0, std::move(handle), id,
229 sequence_number);
230 }
231
232 // Entangle the port.
233 base::UnguessableToken dummy_ec = base::UnguessableToken::Create();
234 auto handle0 =
235 MessagePortDescriptorTestHelper::TakeHandleToEntangle(&port0, dummy_ec);
236
237 // Trying to entangle a second time should explode.
238 EXPECT_DCHECK_DEATH(
239 MessagePortDescriptorTestHelper::TakeHandleToEntangle(&port0, dummy_ec));
240
241 // Destroying a port descriptor that has been entangled should explode. The
242 // handle needs to be given back to the descriptor before its death, ensuring
243 // descriptors remain fully accounted for over their entire lifecycle.
244 EXPECT_DCHECK_DEATH(port0.Reset());
245
246 // Trying to assign while the handle is entangled should explode, as it
247 // amounts to destroying the existing descriptor.
248 EXPECT_DCHECK_DEATH(port0 = MessagePortDescriptor());
249
250 // Trying to disentangle with an empty port should explode.
251 mojo::ScopedMessagePipeHandle handle1;
252 EXPECT_DCHECK_DEATH(MessagePortDescriptorTestHelper::GiveDisentangledHandle(
253 &port0, std::move(handle1)));
254
255 // Trying to disentangle with the wrong port should explode.
256 handle1 =
257 MessagePortDescriptorTestHelper::TakeHandleToEntangle(&port1, dummy_ec);
258 EXPECT_DCHECK_DEATH(MessagePortDescriptorTestHelper::GiveDisentangledHandle(
259 &port0, std::move(handle1)));
260
261 // Disentangle the ports properly.
262 MessagePortDescriptorTestHelper::GiveDisentangledHandle(&port0,
263 std::move(handle0));
264 MessagePortDescriptorTestHelper::GiveDisentangledHandle(&port1,
265 std::move(handle1));
266 }
267
268 } // namespace blink
269