blob: e64dab378cb8a937e1c851674ee3f72015f049dc [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ipcz/driver_transport.h"
#include <cstdint>
#include <functional>
#include <string>
#include <string_view>
#include <utility>
#include "ipcz/driver_memory.h"
#include "ipcz/driver_memory_mapping.h"
#include "ipcz/driver_object.h"
#include "ipcz/test_messages.h"
#include "test/mock_driver.h"
#include "test/test_transport_listener.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/base/macros.h"
#include "util/ref_counted.h"
namespace ipcz {
namespace {
using ::testing::_;
using ::testing::Return;
class DriverTransportTest : public testing::Test {
public:
DriverTransportTest() = default;
~DriverTransportTest() override = default;
test::MockDriver& driver() { return driver_; }
std::pair<Ref<DriverTransport>, Ref<DriverTransport>> CreateTransportPair(
IpczDriverHandle transport0,
IpczDriverHandle transport1) {
return {MakeRefCounted<DriverTransport>(
DriverObject(test::kMockDriver, transport0)),
MakeRefCounted<DriverTransport>(
DriverObject(test::kMockDriver, transport1))};
}
private:
::testing::StrictMock<test::MockDriver> driver_;
};
TEST_F(DriverTransportTest, Activation) {
constexpr IpczDriverHandle kTransport0 = 5;
constexpr IpczDriverHandle kTransport1 = 42;
auto [a, b] = CreateTransportPair(kTransport0, kTransport1);
IpczHandle ipcz_transport = IPCZ_INVALID_HANDLE;
IpczTransportActivityHandler activity_handler = nullptr;
EXPECT_CALL(driver(), ActivateTransport(kTransport1, _, _, _, _))
.WillOnce([&](IpczDriverHandle driver_transport, IpczHandle transport,
IpczTransportActivityHandler handler, uint32_t flags,
const void* options) {
ipcz_transport = transport;
activity_handler = handler;
return IPCZ_RESULT_OK;
})
.RetiresOnSaturation();
// And verify that the activity handler actually invokes the transport's
// Listener.
const std::string kTestMessage = "hihihihi";
bool received = false;
test::TestTransportListener listener(b);
listener.OnStringMessage([&](std::string_view message) {
EXPECT_EQ(kTestMessage, message);
received = true;
});
// Verify that activation of a DriverTransport feeds the driver an activity
// handler and valid ipcz handle to use when notifying ipcz of incoming
// communications.
EXPECT_NE(IPCZ_INVALID_HANDLE, ipcz_transport);
EXPECT_TRUE(activity_handler);
EXPECT_FALSE(received);
EXPECT_EQ(
IPCZ_RESULT_OK,
activity_handler(ipcz_transport, kTestMessage.data(), kTestMessage.size(),
nullptr, 0, IPCZ_NO_FLAGS, nullptr));
EXPECT_TRUE(received);
// Normal shutdown involves ipcz calling Deactivate() on the DriverTransport.
// This should result in a call to DeactivateTransport() on the driver.
EXPECT_CALL(driver(), DeactivateTransport(kTransport1, _, _))
.WillOnce(Return(IPCZ_RESULT_OK));
EXPECT_CALL(driver(), Close(kTransport1, _, _));
EXPECT_CALL(driver(), Close(kTransport0, _, _));
// The driver must also release its handle to ipcz' DriverTransport, which it
// does by an invocation of the activity handler like this. Without this, we'd
// be left with a dangling reference to the DriverTransport.
EXPECT_EQ(IPCZ_RESULT_OK,
activity_handler(ipcz_transport, nullptr, 0, nullptr, 0,
IPCZ_TRANSPORT_ACTIVITY_DEACTIVATED, nullptr));
}
TEST_F(DriverTransportTest, Error) {
constexpr IpczDriverHandle kTransport0 = 5;
constexpr IpczDriverHandle kTransport1 = 42;
auto [a, b] = CreateTransportPair(kTransport0, kTransport1);
IpczHandle ipcz_transport = IPCZ_INVALID_HANDLE;
IpczTransportActivityHandler activity_handler = nullptr;
EXPECT_CALL(driver(), ActivateTransport(kTransport1, _, _, _, _))
.WillOnce([&](IpczDriverHandle driver_transport, IpczHandle transport,
IpczTransportActivityHandler handler, uint32_t flags,
const void* options) {
ipcz_transport = transport;
activity_handler = handler;
return IPCZ_RESULT_OK;
})
.RetiresOnSaturation();
bool observed_error = false;
test::TestTransportListener listener(b);
listener.OnError([&] { observed_error = true; });
// Verify that a driver invoking the activity handler with
// IPCZ_TRANSPORT_ACTIVITY_ERROR results in an error notification on the
// DriverTransport's Listener. This implies deactivation on the ipcz side, so
// no call to Deactivate() is necessary.
EXPECT_FALSE(observed_error);
EXPECT_EQ(IPCZ_RESULT_OK,
activity_handler(ipcz_transport, nullptr, 0, nullptr, 0,
IPCZ_TRANSPORT_ACTIVITY_ERROR, nullptr));
EXPECT_TRUE(observed_error);
// Even after signaling an error, the driver must also signal deactivation on
// its side, to release the DriverTransport handle it holds.
EXPECT_EQ(IPCZ_RESULT_OK,
activity_handler(ipcz_transport, nullptr, 0, nullptr, 0,
IPCZ_TRANSPORT_ACTIVITY_DEACTIVATED, nullptr));
EXPECT_CALL(driver(), DeactivateTransport(kTransport1, _, _));
EXPECT_CALL(driver(), Close(kTransport1, _, _));
EXPECT_CALL(driver(), Close(kTransport0, _, _));
}
TEST_F(DriverTransportTest, Transmit) {
constexpr IpczDriverHandle kTransport0 = 5;
constexpr IpczDriverHandle kTransport1 = 42;
auto [a, b] = CreateTransportPair(kTransport0, kTransport1);
test::msg::BasicTestMessage message;
message.params().foo = 5;
message.params().bar = 7;
EXPECT_CALL(driver(), Transmit(kTransport0, message.data_view().data(),
message.data_view().size(), _, _, _, _))
.WillOnce(Return(IPCZ_RESULT_OK));
a->Transmit(message);
EXPECT_CALL(driver(), Close(kTransport1, _, _));
EXPECT_CALL(driver(), Close(kTransport0, _, _));
}
} // namespace
} // namespace ipcz