blob: 7e6662ec5c4ac89d1901f65627d3090b8799553e [file] [log] [blame]
// Copyright 2024 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 "quiche/quic/moqt/moqt_outgoing_queue.h"
#include <cstdint>
#include <utility>
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "quiche/quic/moqt/moqt_messages.h"
#include "quiche/quic/moqt/moqt_subscribe_windows.h"
#include "quiche/quic/test_tools/quic_test_utils.h"
#include "quiche/common/platform/api/quiche_expect_bug.h"
#include "quiche/common/platform/api/quiche_logging.h"
#include "quiche/common/platform/api/quiche_test.h"
namespace moqt {
namespace {
using ::quic::test::MemSliceFromString;
class TestMoqtOutgoingQueue : public MoqtOutgoingQueue {
public:
TestMoqtOutgoingQueue()
: MoqtOutgoingQueue(nullptr, FullTrackName{"test", "track"}) {}
void CallSubscribeForPast(const SubscribeWindow& window) {
absl::StatusOr<PublishPastObjectsCallback> callback =
OnSubscribeForPast(window);
QUICHE_CHECK_OK(callback.status());
(*std::move(callback))();
}
MOCK_METHOD(void, CloseStreamForGroup, (uint64_t group_id), (override));
MOCK_METHOD(void, PublishObject,
(uint64_t group_id, uint64_t object_id, absl::string_view payload,
bool close_stream),
(override));
};
TEST(MoqtOutgoingQueue, FirstObjectNotKeyframe) {
TestMoqtOutgoingQueue queue;
EXPECT_QUICHE_BUG(queue.AddObject(MemSliceFromString("a"), false),
"The first object");
}
TEST(MoqtOutgoingQueue, SingleGroup) {
TestMoqtOutgoingQueue queue;
{
testing::InSequence seq;
EXPECT_CALL(queue, PublishObject(0, 0, "a", false));
EXPECT_CALL(queue, PublishObject(0, 1, "b", false));
EXPECT_CALL(queue, PublishObject(0, 2, "c", false));
}
queue.AddObject(MemSliceFromString("a"), true);
queue.AddObject(MemSliceFromString("b"), false);
queue.AddObject(MemSliceFromString("c"), false);
}
TEST(MoqtOutgoingQueue, SingleGroupPastSubscribeFromZero) {
TestMoqtOutgoingQueue queue;
{
testing::InSequence seq;
EXPECT_CALL(queue, PublishObject(0, 0, "a", false));
EXPECT_CALL(queue, PublishObject(0, 1, "b", false));
EXPECT_CALL(queue, PublishObject(0, 2, "c", false));
EXPECT_CALL(queue, PublishObject(0, 0, "a", false));
EXPECT_CALL(queue, PublishObject(0, 1, "b", false));
EXPECT_CALL(queue, PublishObject(0, 2, "c", false));
}
queue.AddObject(MemSliceFromString("a"), true);
queue.AddObject(MemSliceFromString("b"), false);
queue.AddObject(MemSliceFromString("c"), false);
queue.CallSubscribeForPast(
SubscribeWindow(0, MoqtForwardingPreference::kGroup, 0, 0));
}
TEST(MoqtOutgoingQueue, SingleGroupPastSubscribeFromMidGroup) {
TestMoqtOutgoingQueue queue;
{
testing::InSequence seq;
EXPECT_CALL(queue, PublishObject(0, 0, "a", false));
EXPECT_CALL(queue, PublishObject(0, 1, "b", false));
EXPECT_CALL(queue, PublishObject(0, 2, "c", false));
EXPECT_CALL(queue, PublishObject(0, 1, "b", false));
EXPECT_CALL(queue, PublishObject(0, 2, "c", false));
}
queue.AddObject(MemSliceFromString("a"), true);
queue.AddObject(MemSliceFromString("b"), false);
queue.AddObject(MemSliceFromString("c"), false);
queue.CallSubscribeForPast(
SubscribeWindow(0, MoqtForwardingPreference::kGroup, 0, 1));
}
TEST(MoqtOutgoingQueue, TwoGroups) {
TestMoqtOutgoingQueue queue;
{
testing::InSequence seq;
EXPECT_CALL(queue, PublishObject(0, 0, "a", false));
EXPECT_CALL(queue, PublishObject(0, 1, "b", false));
EXPECT_CALL(queue, PublishObject(0, 2, "c", false));
EXPECT_CALL(queue, CloseStreamForGroup(0));
EXPECT_CALL(queue, PublishObject(1, 0, "d", false));
EXPECT_CALL(queue, PublishObject(1, 1, "e", false));
EXPECT_CALL(queue, PublishObject(1, 2, "f", false));
}
queue.AddObject(MemSliceFromString("a"), true);
queue.AddObject(MemSliceFromString("b"), false);
queue.AddObject(MemSliceFromString("c"), false);
queue.AddObject(MemSliceFromString("d"), true);
queue.AddObject(MemSliceFromString("e"), false);
queue.AddObject(MemSliceFromString("f"), false);
}
TEST(MoqtOutgoingQueue, TwoGroupsPastSubscribe) {
TestMoqtOutgoingQueue queue;
{
testing::InSequence seq;
EXPECT_CALL(queue, PublishObject(0, 0, "a", false));
EXPECT_CALL(queue, PublishObject(0, 1, "b", false));
EXPECT_CALL(queue, PublishObject(0, 2, "c", false));
EXPECT_CALL(queue, CloseStreamForGroup(0));
EXPECT_CALL(queue, PublishObject(1, 0, "d", false));
EXPECT_CALL(queue, PublishObject(1, 1, "e", false));
EXPECT_CALL(queue, PublishObject(1, 2, "f", false));
EXPECT_CALL(queue, PublishObject(0, 1, "b", false));
EXPECT_CALL(queue, PublishObject(0, 2, "c", true));
EXPECT_CALL(queue, PublishObject(1, 0, "d", false));
EXPECT_CALL(queue, PublishObject(1, 1, "e", false));
EXPECT_CALL(queue, PublishObject(1, 2, "f", false));
}
queue.AddObject(MemSliceFromString("a"), true);
queue.AddObject(MemSliceFromString("b"), false);
queue.AddObject(MemSliceFromString("c"), false);
queue.AddObject(MemSliceFromString("d"), true);
queue.AddObject(MemSliceFromString("e"), false);
queue.AddObject(MemSliceFromString("f"), false);
queue.CallSubscribeForPast(
SubscribeWindow(0, MoqtForwardingPreference::kGroup, 0, 1));
}
TEST(MoqtOutgoingQueue, FiveGroups) {
TestMoqtOutgoingQueue queue;
{
testing::InSequence seq;
EXPECT_CALL(queue, PublishObject(0, 0, "a", false));
EXPECT_CALL(queue, PublishObject(0, 1, "b", false));
EXPECT_CALL(queue, CloseStreamForGroup(0));
EXPECT_CALL(queue, PublishObject(1, 0, "c", false));
EXPECT_CALL(queue, PublishObject(1, 1, "d", false));
EXPECT_CALL(queue, CloseStreamForGroup(1));
EXPECT_CALL(queue, PublishObject(2, 0, "e", false));
EXPECT_CALL(queue, PublishObject(2, 1, "f", false));
EXPECT_CALL(queue, CloseStreamForGroup(2));
EXPECT_CALL(queue, PublishObject(3, 0, "g", false));
EXPECT_CALL(queue, PublishObject(3, 1, "h", false));
EXPECT_CALL(queue, CloseStreamForGroup(3));
EXPECT_CALL(queue, PublishObject(4, 0, "i", false));
EXPECT_CALL(queue, PublishObject(4, 1, "j", false));
}
queue.AddObject(MemSliceFromString("a"), true);
queue.AddObject(MemSliceFromString("b"), false);
queue.AddObject(MemSliceFromString("c"), true);
queue.AddObject(MemSliceFromString("d"), false);
queue.AddObject(MemSliceFromString("e"), true);
queue.AddObject(MemSliceFromString("f"), false);
queue.AddObject(MemSliceFromString("g"), true);
queue.AddObject(MemSliceFromString("h"), false);
queue.AddObject(MemSliceFromString("i"), true);
queue.AddObject(MemSliceFromString("j"), false);
}
TEST(MoqtOutgoingQueue, FiveGroupsPastSubscribe) {
TestMoqtOutgoingQueue queue;
{
testing::InSequence seq;
EXPECT_CALL(queue, PublishObject(0, 0, "a", false));
EXPECT_CALL(queue, PublishObject(0, 1, "b", false));
EXPECT_CALL(queue, CloseStreamForGroup(0));
EXPECT_CALL(queue, PublishObject(1, 0, "c", false));
EXPECT_CALL(queue, PublishObject(1, 1, "d", false));
EXPECT_CALL(queue, CloseStreamForGroup(1));
EXPECT_CALL(queue, PublishObject(2, 0, "e", false));
EXPECT_CALL(queue, PublishObject(2, 1, "f", false));
EXPECT_CALL(queue, CloseStreamForGroup(2));
EXPECT_CALL(queue, PublishObject(3, 0, "g", false));
EXPECT_CALL(queue, PublishObject(3, 1, "h", false));
EXPECT_CALL(queue, CloseStreamForGroup(3));
EXPECT_CALL(queue, PublishObject(4, 0, "i", false));
EXPECT_CALL(queue, PublishObject(4, 1, "j", false));
// Past SUBSCRIBE would only get the three most recent groups.
EXPECT_CALL(queue, PublishObject(2, 0, "e", false));
EXPECT_CALL(queue, PublishObject(2, 1, "f", true));
EXPECT_CALL(queue, PublishObject(3, 0, "g", false));
EXPECT_CALL(queue, PublishObject(3, 1, "h", true));
EXPECT_CALL(queue, PublishObject(4, 0, "i", false));
EXPECT_CALL(queue, PublishObject(4, 1, "j", false));
}
queue.AddObject(MemSliceFromString("a"), true);
queue.AddObject(MemSliceFromString("b"), false);
queue.AddObject(MemSliceFromString("c"), true);
queue.AddObject(MemSliceFromString("d"), false);
queue.AddObject(MemSliceFromString("e"), true);
queue.AddObject(MemSliceFromString("f"), false);
queue.AddObject(MemSliceFromString("g"), true);
queue.AddObject(MemSliceFromString("h"), false);
queue.AddObject(MemSliceFromString("i"), true);
queue.AddObject(MemSliceFromString("j"), false);
queue.CallSubscribeForPast(
SubscribeWindow(0, MoqtForwardingPreference::kGroup, 0, 0));
}
} // namespace
} // namespace moqt