blob: 454602d45aaa2467c4f85d2b70e8e840137618f4 [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 "ipcz/ipcz.h"
#include "test/multinode_test.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/synchronization/notification.h"
namespace ipcz {
namespace {
using QueueingTestNode = test::TestNode;
using QueueingTest = test::MultinodeTest<QueueingTestNode>;
MULTINODE_TEST_NODE(QueueingTestNode, RemoteQueueFeedbackClient) {
IpczHandle b = ConnectToBroker();
// Wait for the first parcel to arrive.
EXPECT_EQ(IPCZ_RESULT_OK,
WaitForConditions(b, {.flags = IPCZ_TRAP_ABOVE_MIN_LOCAL_PARCELS,
.min_local_parcels = 0}));
// Send and ack and wait for another parcel to arrive.
absl::Notification new_parcel_arrived;
EXPECT_EQ(IPCZ_RESULT_OK,
Trap(b, {.flags = IPCZ_TRAP_NEW_LOCAL_PARCEL},
[&](const IpczTrapEvent&) { new_parcel_arrived.Notify(); }));
EXPECT_EQ(IPCZ_RESULT_OK, Put(b, "ok"));
new_parcel_arrived.WaitForNotification();
std::string data;
EXPECT_EQ(IPCZ_RESULT_OK, Get(b, &data));
EXPECT_EQ("1234", data);
std::string ack;
EXPECT_EQ(IPCZ_RESULT_OK, WaitToGet(b, &ack));
EXPECT_EQ("ok", ack);
EXPECT_EQ(IPCZ_RESULT_OK, WaitForConditionFlags(b, IPCZ_TRAP_PEER_CLOSED));
Close(b);
}
TEST_P(QueueingTest, RemoteQueueFeedback) {
// Exercises operations which rely on feedback from the remote peer regarding
// its inbound parcel queue state.
IpczHandle c = SpawnTestNode<RemoteQueueFeedbackClient>();
// This trap can only be set while the remote portal appears to be non-empty.
const IpczTrapConditions all_bytes_consumed = {
.size = sizeof(all_bytes_consumed),
.flags = IPCZ_TRAP_BELOW_MAX_REMOTE_BYTES,
.max_remote_bytes = 1,
};
EXPECT_EQ(IPCZ_RESULT_FAILED_PRECONDITION,
Trap(c, all_bytes_consumed, [&](const auto&) {}));
// Send 4 bytes and wait for acknowledgement that the parcel was received.
std::string ack;
EXPECT_EQ(IPCZ_RESULT_OK, Put(c, "1234"));
EXPECT_EQ(IPCZ_RESULT_OK, WaitToGet(c, &ack));
EXPECT_EQ("ok", ack);
// Now these operations should always fail due to the specified limits.
EXPECT_EQ(IPCZ_RESULT_RESOURCE_EXHAUSTED,
PutWithLimits(c, {.max_queued_parcels = 1}, "meh"));
EXPECT_EQ(IPCZ_RESULT_RESOURCE_EXHAUSTED,
PutWithLimits(c, {.max_queued_bytes = 4}, "?"));
// Now we should be able to install traps for both queued parcels and bytes on
// the remote side.
absl::Notification consumed_parcels;
const IpczTrapConditions all_parcels_consumed = {
.size = sizeof(all_parcels_consumed),
.flags = IPCZ_TRAP_BELOW_MAX_REMOTE_PARCELS,
.max_remote_parcels = 1,
};
EXPECT_EQ(
IPCZ_RESULT_OK,
Trap(c, all_parcels_consumed, [&](const IpczTrapEvent& event) {
EXPECT_TRUE(event.condition_flags & IPCZ_TRAP_BELOW_MAX_REMOTE_PARCELS);
consumed_parcels.Notify();
}));
absl::Notification consumed_bytes;
EXPECT_EQ(
IPCZ_RESULT_OK,
Trap(c, all_bytes_consumed, [&](const IpczTrapEvent& event) {
EXPECT_TRUE(event.condition_flags & IPCZ_TRAP_BELOW_MAX_REMOTE_BYTES);
consumed_bytes.Notify();
}));
// Ack back to the client so it will read its queue. Then we can wait for both
// traps to notify.
EXPECT_EQ(IPCZ_RESULT_OK, Put(c, "ok"));
consumed_parcels.WaitForNotification();
consumed_bytes.WaitForNotification();
// And now this Put operation should succeed.
EXPECT_EQ(IPCZ_RESULT_OK,
PutWithLimits(c, {.max_queued_parcels = 1, .max_queued_bytes = 4},
"meh!"));
Close(c);
}
INSTANTIATE_MULTINODE_TEST_SUITE_P(QueueingTest);
} // namespace
} // namespace ipcz