blob: 3ce2862a215f2579deef107d2d7e9cc5632a1e96 [file] [log] [blame]
// Copyright 2019 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 "third_party/blink/public/common/messaging/message_port_descriptor.h"
#include "base/test/gtest_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace blink {
namespace {
ExecutionContext* kDummyEc = reinterpret_cast<ExecutionContext*>(0xBAADF00D);
class LenientMockInstrumentationDelegate
: public MessagePortDescriptor::InstrumentationDelegate {
public:
LenientMockInstrumentationDelegate() {
MessagePortDescriptor::SetInstrumentationDelegate(this);
}
~LenientMockInstrumentationDelegate() override {
MessagePortDescriptor::SetInstrumentationDelegate(nullptr);
}
MOCK_METHOD2(NotifyMessagePortPairCreated,
void(const base::UnguessableToken& port0_id,
const base::UnguessableToken& port1_id));
MOCK_METHOD3(NotifyMessagePortAttached,
void(const base::UnguessableToken& port_id,
uint64_t sequence_number,
ExecutionContext* execution_context));
MOCK_METHOD2(NotifyMessagePortAttachedToEmbedder,
void(const base::UnguessableToken& port_id,
uint64_t sequence_number));
MOCK_METHOD2(NotifyMessagePortDetached,
void(const base::UnguessableToken& port_id,
uint64_t sequence_number));
MOCK_METHOD2(NotifyMessagePortDestroyed,
void(const base::UnguessableToken& port_id,
uint64_t sequence_number));
};
using MockInstrumentationDelegate =
testing::StrictMock<LenientMockInstrumentationDelegate>;
using testing::_;
using testing::Invoke;
} // namespace
TEST(MessagePortDescriptorTest, InstrumentationAndSerializationWorks) {
MockInstrumentationDelegate delegate;
// A small struct for holding information gleaned about ports during their
// creation event. Allows verifying that other events are appropriately
// sequenced.
struct {
base::UnguessableToken token0;
base::UnguessableToken token1;
uint64_t seq0 = 1;
uint64_t seq1 = 1;
} created_data;
// Create a message handle descriptor pair and expect a notification.
EXPECT_CALL(delegate, NotifyMessagePortPairCreated(_, _))
.WillOnce(Invoke([&created_data](const base::UnguessableToken& port0_id,
const base::UnguessableToken& port1_id) {
created_data.token0 = port0_id;
created_data.token1 = port1_id;
}));
MessagePortDescriptorPair pair;
MessagePortDescriptor port0;
MessagePortDescriptor port1;
EXPECT_FALSE(port0.IsValid());
EXPECT_FALSE(port1.IsValid());
EXPECT_FALSE(port0.IsEntangled());
EXPECT_FALSE(port1.IsEntangled());
EXPECT_TRUE(port0.IsDefault());
EXPECT_TRUE(port1.IsDefault());
port0 = pair.TakePort0();
port1 = pair.TakePort1();
EXPECT_TRUE(port0.IsValid());
EXPECT_TRUE(port1.IsValid());
EXPECT_FALSE(port0.IsEntangled());
EXPECT_FALSE(port1.IsEntangled());
EXPECT_FALSE(port0.IsDefault());
EXPECT_FALSE(port1.IsDefault());
// Expect that the data received at creation matches the actual ports.
EXPECT_EQ(created_data.token0, port0.id());
EXPECT_EQ(created_data.seq0, port0.sequence_number());
EXPECT_EQ(created_data.token1, port1.id());
EXPECT_EQ(created_data.seq1, port1.sequence_number());
// Simulate that a handle is attached by taking the pipe handle.
EXPECT_CALL(delegate,
NotifyMessagePortAttached(created_data.token0,
created_data.seq0++, kDummyEc));
auto handle0 = port0.TakeHandleToEntangle(kDummyEc);
EXPECT_TRUE(port0.IsValid());
EXPECT_TRUE(port0.IsEntangled());
EXPECT_FALSE(port0.IsDefault());
// Simulate that the handle is detached by giving the pipe handle back.
EXPECT_CALL(delegate, NotifyMessagePortDetached(created_data.token0,
created_data.seq0++));
port0.GiveDisentangledHandle(std::move(handle0));
EXPECT_TRUE(port0.IsValid());
EXPECT_FALSE(port0.IsEntangled());
EXPECT_FALSE(port0.IsDefault());
// Tear down a handle explicitly.
EXPECT_CALL(delegate, NotifyMessagePortDestroyed(created_data.token1,
created_data.seq1++));
port1.Reset();
// And leave the other handle to be torn down in the destructor.
EXPECT_CALL(delegate, NotifyMessagePortDestroyed(created_data.token0,
created_data.seq0++));
}
TEST(MessagePortDescriptorTestDeathTest, InvalidUsage) {
static MessagePortDescriptor::InstrumentationDelegate* kDummyDelegate1 =
reinterpret_cast<MessagePortDescriptor::InstrumentationDelegate*>(
0xBAADF00D);
static MessagePortDescriptor::InstrumentationDelegate* kDummyDelegate2 =
reinterpret_cast<MessagePortDescriptor::InstrumentationDelegate*>(
0xDEADBEEF);
EXPECT_DCHECK_DEATH(
MessagePortDescriptor::SetInstrumentationDelegate(nullptr));
MessagePortDescriptor::SetInstrumentationDelegate(kDummyDelegate1);
// Setting the same or another delegate should explode.
EXPECT_DCHECK_DEATH(
MessagePortDescriptor::SetInstrumentationDelegate(kDummyDelegate1));
EXPECT_DCHECK_DEATH(
MessagePortDescriptor::SetInstrumentationDelegate(kDummyDelegate2));
// Unset the dummy delegate we installed so we don't receive notifications in
// the rest of the test.
MessagePortDescriptor::SetInstrumentationDelegate(nullptr);
// Trying to take properties of a default port descriptor should explode.
MessagePortDescriptor port0;
EXPECT_DCHECK_DEATH(port0.TakeHandleForSerialization());
EXPECT_DCHECK_DEATH(port0.TakeIdForSerialization());
EXPECT_DCHECK_DEATH(port0.TakeSequenceNumberForSerialization());
MessagePortDescriptorPair pair;
port0 = pair.TakePort0();
MessagePortDescriptor port1 = pair.TakePort1();
{
// Dismantle the port as if for serialization. Trying to take fields a
// second time should explode. A partially serialized object should also
// explode if
auto handle = port0.TakeHandleForSerialization();
EXPECT_DCHECK_DEATH(port0.TakeHandleForSerialization());
EXPECT_DCHECK_DEATH(port0.Reset());
auto id = port0.TakeIdForSerialization();
EXPECT_DCHECK_DEATH(port0.TakeIdForSerialization());
EXPECT_DCHECK_DEATH(port0.Reset());
auto sequence_number = port0.TakeSequenceNumberForSerialization();
EXPECT_DCHECK_DEATH(port0.TakeSequenceNumberForSerialization());
// This time reset should *not* explode, as the object has been fully taken
// for serialization.
port0.Reset();
// Reserializing with inconsistent state should explode.
// First try with any 1 of the 3 fields being invalid.
EXPECT_DCHECK_DEATH(port0.InitializeFromSerializedValues(
mojo::ScopedMessagePipeHandle(), id, sequence_number));
EXPECT_DCHECK_DEATH(port0.InitializeFromSerializedValues(
std::move(handle), base::UnguessableToken::Null(), sequence_number));
EXPECT_DCHECK_DEATH(
port0.InitializeFromSerializedValues(std::move(handle), id, 0));
// Next try with any 2 of the 3 fields being invalid.
EXPECT_DCHECK_DEATH(port0.InitializeFromSerializedValues(
std::move(handle), base::UnguessableToken::Null(), 0));
EXPECT_DCHECK_DEATH(port0.InitializeFromSerializedValues(
mojo::ScopedMessagePipeHandle(), id, 0));
EXPECT_DCHECK_DEATH(port0.InitializeFromSerializedValues(
mojo::ScopedMessagePipeHandle(), base::UnguessableToken::Null(),
sequence_number));
// Restoring the port with default state should work (all 3 fields invalid).
port0.InitializeFromSerializedValues(mojo::ScopedMessagePipeHandle(),
base::UnguessableToken::Null(), 0);
EXPECT_TRUE(port0.IsDefault());
// Restoring the port with full state should work (all 3 fields valid).
port0.InitializeFromSerializedValues(std::move(handle), id,
sequence_number);
}
// Entangle the port.
auto handle0 = port0.TakeHandleToEntangleWithEmbedder();
// Trying to entangle a second time should explode.
EXPECT_DCHECK_DEATH(port0.TakeHandleToEntangleWithEmbedder());
EXPECT_DCHECK_DEATH(port0.TakeHandleToEntangle(kDummyEc));
// Destroying a port descriptor that has been entangled should explode. The
// handle needs to be given back to the descriptor before its death, ensuring
// descriptors remain fully accounted for over their entire lifecycle.
EXPECT_DCHECK_DEATH(port0.Reset());
// Trying to assign while the handle is entangled should explode, as it
// amounts to destroying the existing descriptor.
EXPECT_DCHECK_DEATH(port0 = MessagePortDescriptor());
// Trying to reset an entangled port should explode.
EXPECT_DCHECK_DEATH(port0.Reset());
// Trying to serialize an entangled port should explode.
EXPECT_DCHECK_DEATH(port0.TakeHandleForSerialization());
EXPECT_DCHECK_DEATH(port0.TakeIdForSerialization());
EXPECT_DCHECK_DEATH(port0.TakeSequenceNumberForSerialization());
// Disentangle the port so it doesn't explode at teardown.
port0.GiveDisentangledHandle(std::move(handle0));
}
} // namespace blink