blob: ec814f1ae7790f24922a624b9ea7dc4c97c71c10 [file] [log] [blame]
// Copyright 2022 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 <cstring>
#include <string_view>
#include "ipcz/ipcz.h"
#include "reference_drivers/blob.h"
#include "test/multinode_test.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "util/ref_counted.h"
namespace ipcz {
namespace {
using Blob = reference_drivers::Blob;
using BoxTestNode = test::TestNode;
using BoxTest = test::MultinodeTest<BoxTestNode>;
IpczDriverHandle CreateTestBlob(std::string_view message) {
return Blob::ReleaseAsHandle(MakeRefCounted<Blob>(message));
}
std::string GetBlobContents(IpczDriverHandle handle) {
Ref<Blob> blob = Blob::TakeFromHandle(handle);
return std::string(blob->message());
}
TEST_P(BoxTest, BoxAndUnbox) {
constexpr const char kMessage[] = "Hello, world?";
IpczDriverHandle blob_handle = CreateTestBlob(kMessage);
IpczHandle box;
EXPECT_EQ(IPCZ_RESULT_OK,
ipcz().Box(node(), blob_handle, IPCZ_NO_FLAGS, nullptr, &box));
blob_handle = IPCZ_INVALID_DRIVER_HANDLE;
EXPECT_EQ(IPCZ_RESULT_OK,
ipcz().Unbox(box, IPCZ_NO_FLAGS, nullptr, &blob_handle));
EXPECT_EQ(kMessage, GetBlobContents(blob_handle));
}
TEST_P(BoxTest, CloseBox) {
Ref<Blob> blob = MakeRefCounted<Blob>("meh");
Ref<Blob::RefCountedFlag> destroyed = blob->destruction_flag_for_testing();
IpczDriverHandle blob_handle = Blob::ReleaseAsHandle(std::move(blob));
IpczHandle box;
EXPECT_EQ(IPCZ_RESULT_OK,
ipcz().Box(node(), blob_handle, IPCZ_NO_FLAGS, nullptr, &box));
EXPECT_FALSE(destroyed->get());
EXPECT_EQ(IPCZ_RESULT_OK, ipcz().Close(box, IPCZ_NO_FLAGS, nullptr));
EXPECT_TRUE(destroyed->get());
}
TEST_P(BoxTest, Peek) {
constexpr const char kMessage[] = "Hello, world?";
IpczDriverHandle blob_handle = CreateTestBlob(kMessage);
IpczHandle box;
EXPECT_EQ(IPCZ_RESULT_OK,
ipcz().Box(node(), blob_handle, IPCZ_NO_FLAGS, nullptr, &box));
blob_handle = IPCZ_INVALID_DRIVER_HANDLE;
EXPECT_EQ(IPCZ_RESULT_OK,
ipcz().Unbox(box, IPCZ_UNBOX_PEEK, nullptr, &blob_handle));
EXPECT_EQ(IPCZ_RESULT_OK,
ipcz().Unbox(box, IPCZ_UNBOX_PEEK, nullptr, &blob_handle));
EXPECT_EQ(IPCZ_RESULT_OK,
ipcz().Unbox(box, IPCZ_UNBOX_PEEK, nullptr, &blob_handle));
Blob* blob = Blob::FromHandle(blob_handle);
EXPECT_EQ(kMessage, blob->message());
EXPECT_EQ(IPCZ_RESULT_OK,
ipcz().Unbox(box, IPCZ_NO_FLAGS, nullptr, &blob_handle));
Ref<Blob> released_blob = Blob::TakeFromHandle(blob_handle);
EXPECT_EQ(blob, released_blob.get());
}
constexpr const char kMessage1[] = "Hello, world?";
constexpr const char kMessage2[] = "Hello, world!";
MULTINODE_TEST_NODE(BoxTestNode, TransferBoxClient) {
IpczHandle b = ConnectToBroker();
std::string message;
IpczHandle box;
EXPECT_EQ(IPCZ_RESULT_OK, WaitToGet(b, &message, {&box, 1}));
EXPECT_EQ(kMessage2, message);
IpczDriverHandle blob_handle;
EXPECT_EQ(IPCZ_RESULT_OK,
ipcz().Unbox(box, IPCZ_NO_FLAGS, nullptr, &blob_handle));
EXPECT_EQ(kMessage1, GetBlobContents(blob_handle));
Close(b);
}
TEST_P(BoxTest, TransferBox) {
IpczHandle c = SpawnTestNode<TransferBoxClient>();
IpczDriverHandle blob_handle = CreateTestBlob(kMessage1);
IpczHandle box;
EXPECT_EQ(IPCZ_RESULT_OK,
ipcz().Box(node(), blob_handle, IPCZ_NO_FLAGS, nullptr, &box));
EXPECT_EQ(IPCZ_RESULT_OK, Put(c, kMessage2, {&box, 1}));
Close(c);
}
constexpr size_t TransferBoxBetweenNonBrokersNumIterations = 50;
MULTINODE_TEST_NODE(BoxTestNode, TransferBoxBetweenNonBrokersClient1) {
IpczHandle q;
IpczHandle b = ConnectToBroker();
EXPECT_EQ(IPCZ_RESULT_OK, WaitToGet(b, nullptr, {&q, 1}));
for (size_t i = 0; i < TransferBoxBetweenNonBrokersNumIterations; ++i) {
IpczDriverHandle blob_handle = CreateTestBlob(kMessage1);
IpczHandle box;
EXPECT_EQ(IPCZ_RESULT_OK,
ipcz().Box(node(), blob_handle, IPCZ_NO_FLAGS, nullptr, &box));
EXPECT_EQ(IPCZ_RESULT_OK, Put(q, kMessage2, {&box, 1}));
box = IPCZ_INVALID_DRIVER_HANDLE;
std::string message;
EXPECT_EQ(IPCZ_RESULT_OK, WaitToGet(q, &message, {&box, 1}));
EXPECT_EQ(kMessage1, message);
EXPECT_EQ(IPCZ_RESULT_OK,
ipcz().Unbox(box, IPCZ_NO_FLAGS, nullptr, &blob_handle));
EXPECT_EQ(kMessage2, GetBlobContents(blob_handle));
}
CloseAll({q, b});
}
MULTINODE_TEST_NODE(BoxTestNode, TransferBoxBetweenNonBrokersClient2) {
IpczHandle p;
IpczHandle b = ConnectToBroker();
EXPECT_EQ(IPCZ_RESULT_OK, WaitToGet(b, nullptr, {&p, 1}));
for (size_t i = 0; i < TransferBoxBetweenNonBrokersNumIterations; ++i) {
IpczHandle box;
IpczDriverHandle blob_handle;
std::string message;
EXPECT_EQ(IPCZ_RESULT_OK, WaitToGet(p, &message, {&box, 1}));
EXPECT_EQ(kMessage2, message);
EXPECT_EQ(IPCZ_RESULT_OK,
ipcz().Unbox(box, IPCZ_NO_FLAGS, nullptr, &blob_handle));
EXPECT_EQ(kMessage1, GetBlobContents(blob_handle));
blob_handle = CreateTestBlob(kMessage2);
EXPECT_EQ(IPCZ_RESULT_OK,
ipcz().Box(node(), blob_handle, IPCZ_NO_FLAGS, nullptr, &box));
EXPECT_EQ(IPCZ_RESULT_OK, Put(p, kMessage1, {&box, 1}));
}
CloseAll({p, b});
}
TEST_P(BoxTest, TransferBoxBetweenNonBrokers) {
IpczHandle c1 = SpawnTestNode<TransferBoxBetweenNonBrokersClient1>();
IpczHandle c2 = SpawnTestNode<TransferBoxBetweenNonBrokersClient2>();
// Create a new portal pair and send each end to one of the two non-brokers so
// they'll establish a direct link.
auto [q, p] = OpenPortals();
EXPECT_EQ(IPCZ_RESULT_OK, Put(c1, "", {&q, 1}));
EXPECT_EQ(IPCZ_RESULT_OK, Put(c2, "", {&p, 1}));
// Wait for the clients to finish their business and go away.
EXPECT_EQ(IPCZ_RESULT_OK, WaitForConditionFlags(c1, IPCZ_TRAP_PEER_CLOSED));
EXPECT_EQ(IPCZ_RESULT_OK, WaitForConditionFlags(c2, IPCZ_TRAP_PEER_CLOSED));
CloseAll({c1, c2});
}
INSTANTIATE_MULTINODE_TEST_SUITE_P(BoxTest);
} // namespace
} // namespace ipcz