webrtc_m130/net/dcsctp/rx/interleaved_reassembly_streams_test.cc
Victor Boivie b0acde349c dcsctp: Add handover test for interleaved streams
This test was missing, which made me believe that it wasn't supported as
the handover state only included SSN and not MID. But when adding tests,
I saw that the current implementation used the SSN field to handover the
MID information for ordered streams which is sufficient given the 32 bit
type used for that (SSNs are only 16 bits).

For unordered streams, there is no need to handover any state there are
no expected next MID for unordered streams (they can be received in any
order).

So, adding tests and removing the handover state I just added.

Bug: webrtc:41481008
Change-Id: If1799cb1def5bd9f585a87cff6d835f4a9053b4f
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/370121
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Commit-Queue: Victor Boivie <boivie@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#43495}
2024-12-04 14:10:32 +00:00

269 lines
10 KiB
C++

/*
* Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "net/dcsctp/rx/interleaved_reassembly_streams.h"
#include <cstdint>
#include <memory>
#include <utility>
#include "net/dcsctp/common/handover_testing.h"
#include "net/dcsctp/common/sequence_numbers.h"
#include "net/dcsctp/packet/chunk/forward_tsn_common.h"
#include "net/dcsctp/packet/chunk/iforward_tsn_chunk.h"
#include "net/dcsctp/packet/data.h"
#include "net/dcsctp/rx/reassembly_streams.h"
#include "net/dcsctp/testing/data_generator.h"
#include "rtc_base/gunit.h"
#include "test/gmock.h"
namespace dcsctp {
namespace {
using ::testing::ElementsAre;
using ::testing::Field;
using ::testing::MockFunction;
using ::testing::NiceMock;
using ::testing::Property;
class InterleavedReassemblyStreamsTest : public testing::Test {
protected:
UnwrappedTSN tsn(uint32_t value) { return tsn_.Unwrap(TSN(value)); }
InterleavedReassemblyStreamsTest() {}
DataGenerator gen_;
UnwrappedTSN::Unwrapper tsn_;
};
TEST_F(InterleavedReassemblyStreamsTest,
AddUnorderedMessageReturnsCorrectSize) {
NiceMock<MockFunction<ReassemblyStreams::OnAssembledMessage>> on_assembled;
InterleavedReassemblyStreams streams("", on_assembled.AsStdFunction());
EXPECT_EQ(streams.Add(tsn(1), gen_.Unordered({1}, "B")), 1);
EXPECT_EQ(streams.Add(tsn(2), gen_.Unordered({2, 3, 4})), 3);
EXPECT_EQ(streams.Add(tsn(3), gen_.Unordered({5, 6})), 2);
// Adding the end fragment should make it empty again.
EXPECT_EQ(streams.Add(tsn(4), gen_.Unordered({7}, "E")), -6);
}
TEST_F(InterleavedReassemblyStreamsTest,
AddSimpleOrderedMessageReturnsCorrectSize) {
NiceMock<MockFunction<ReassemblyStreams::OnAssembledMessage>> on_assembled;
InterleavedReassemblyStreams streams("", on_assembled.AsStdFunction());
EXPECT_EQ(streams.Add(tsn(1), gen_.Ordered({1}, "B")), 1);
EXPECT_EQ(streams.Add(tsn(2), gen_.Ordered({2, 3, 4})), 3);
EXPECT_EQ(streams.Add(tsn(3), gen_.Ordered({5, 6})), 2);
EXPECT_EQ(streams.Add(tsn(4), gen_.Ordered({7}, "E")), -6);
}
TEST_F(InterleavedReassemblyStreamsTest,
AddMoreComplexOrderedMessageReturnsCorrectSize) {
NiceMock<MockFunction<ReassemblyStreams::OnAssembledMessage>> on_assembled;
InterleavedReassemblyStreams streams("", on_assembled.AsStdFunction());
EXPECT_EQ(streams.Add(tsn(1), gen_.Ordered({1}, "B")), 1);
Data late = gen_.Ordered({2, 3, 4});
EXPECT_EQ(streams.Add(tsn(3), gen_.Ordered({5, 6})), 2);
EXPECT_EQ(streams.Add(tsn(4), gen_.Ordered({7}, "E")), 1);
EXPECT_EQ(streams.Add(tsn(5), gen_.Ordered({1}, "BE")), 1);
EXPECT_EQ(streams.Add(tsn(6), gen_.Ordered({5, 6}, "B")), 2);
EXPECT_EQ(streams.Add(tsn(7), gen_.Ordered({7}, "E")), 1);
EXPECT_EQ(streams.Add(tsn(2), std::move(late)), -8);
}
TEST_F(InterleavedReassemblyStreamsTest,
DeleteUnorderedMessageReturnsCorrectSize) {
NiceMock<MockFunction<ReassemblyStreams::OnAssembledMessage>> on_assembled;
InterleavedReassemblyStreams streams("", on_assembled.AsStdFunction());
EXPECT_EQ(streams.Add(tsn(1), gen_.Unordered({1}, "B")), 1);
EXPECT_EQ(streams.Add(tsn(2), gen_.Unordered({2, 3, 4})), 3);
EXPECT_EQ(streams.Add(tsn(3), gen_.Unordered({5, 6})), 2);
IForwardTsnChunk::SkippedStream skipped[] = {
IForwardTsnChunk::SkippedStream(IsUnordered(true), StreamID(1), MID(0))};
EXPECT_EQ(streams.HandleForwardTsn(tsn(3), skipped), 6u);
}
TEST_F(InterleavedReassemblyStreamsTest,
DeleteSimpleOrderedMessageReturnsCorrectSize) {
NiceMock<MockFunction<ReassemblyStreams::OnAssembledMessage>> on_assembled;
InterleavedReassemblyStreams streams("", on_assembled.AsStdFunction());
EXPECT_EQ(streams.Add(tsn(1), gen_.Ordered({1}, "B")), 1);
EXPECT_EQ(streams.Add(tsn(2), gen_.Ordered({2, 3, 4})), 3);
EXPECT_EQ(streams.Add(tsn(3), gen_.Ordered({5, 6})), 2);
IForwardTsnChunk::SkippedStream skipped[] = {
IForwardTsnChunk::SkippedStream(IsUnordered(false), StreamID(1), MID(0))};
EXPECT_EQ(streams.HandleForwardTsn(tsn(3), skipped), 6u);
}
TEST_F(InterleavedReassemblyStreamsTest,
DeleteManyOrderedMessagesReturnsCorrectSize) {
NiceMock<MockFunction<ReassemblyStreams::OnAssembledMessage>> on_assembled;
InterleavedReassemblyStreams streams("", on_assembled.AsStdFunction());
EXPECT_EQ(streams.Add(tsn(1), gen_.Ordered({1}, "B")), 1);
gen_.Ordered({2, 3, 4});
EXPECT_EQ(streams.Add(tsn(3), gen_.Ordered({5, 6})), 2);
EXPECT_EQ(streams.Add(tsn(4), gen_.Ordered({7}, "E")), 1);
EXPECT_EQ(streams.Add(tsn(5), gen_.Ordered({1}, "BE")), 1);
EXPECT_EQ(streams.Add(tsn(6), gen_.Ordered({5, 6}, "B")), 2);
EXPECT_EQ(streams.Add(tsn(7), gen_.Ordered({7}, "E")), 1);
// Expire all three messages
IForwardTsnChunk::SkippedStream skipped[] = {
IForwardTsnChunk::SkippedStream(IsUnordered(false), StreamID(1), MID(2))};
EXPECT_EQ(streams.HandleForwardTsn(tsn(8), skipped), 8u);
}
TEST_F(InterleavedReassemblyStreamsTest,
DeleteOrderedMessageDelivesTwoReturnsCorrectSize) {
NiceMock<MockFunction<ReassemblyStreams::OnAssembledMessage>> on_assembled;
InterleavedReassemblyStreams streams("", on_assembled.AsStdFunction());
EXPECT_EQ(streams.Add(tsn(1), gen_.Ordered({1}, "B")), 1);
gen_.Ordered({2, 3, 4});
EXPECT_EQ(streams.Add(tsn(3), gen_.Ordered({5, 6})), 2);
EXPECT_EQ(streams.Add(tsn(4), gen_.Ordered({7}, "E")), 1);
EXPECT_EQ(streams.Add(tsn(5), gen_.Ordered({1}, "BE")), 1);
EXPECT_EQ(streams.Add(tsn(6), gen_.Ordered({5, 6}, "B")), 2);
EXPECT_EQ(streams.Add(tsn(7), gen_.Ordered({7}, "E")), 1);
// The first ordered message expire, and the following two are delivered.
IForwardTsnChunk::SkippedStream skipped[] = {
IForwardTsnChunk::SkippedStream(IsUnordered(false), StreamID(1), MID(0))};
EXPECT_EQ(streams.HandleForwardTsn(tsn(4), skipped), 8u);
}
TEST_F(InterleavedReassemblyStreamsTest, CanReassembleFastPathUnordered) {
NiceMock<MockFunction<ReassemblyStreams::OnAssembledMessage>> on_assembled;
{
testing::InSequence s;
EXPECT_CALL(on_assembled,
Call(ElementsAre(tsn(1)),
Property(&DcSctpMessage::payload, ElementsAre(1))));
EXPECT_CALL(on_assembled,
Call(ElementsAre(tsn(3)),
Property(&DcSctpMessage::payload, ElementsAre(3))));
EXPECT_CALL(on_assembled,
Call(ElementsAre(tsn(2)),
Property(&DcSctpMessage::payload, ElementsAre(2))));
EXPECT_CALL(on_assembled,
Call(ElementsAre(tsn(4)),
Property(&DcSctpMessage::payload, ElementsAre(4))));
}
InterleavedReassemblyStreams streams("", on_assembled.AsStdFunction());
EXPECT_EQ(streams.Add(tsn(1), gen_.Unordered({1}, "BE")), 0);
EXPECT_EQ(streams.Add(tsn(3), gen_.Unordered({3}, "BE")), 0);
EXPECT_EQ(streams.Add(tsn(2), gen_.Unordered({2}, "BE")), 0);
EXPECT_EQ(streams.Add(tsn(4), gen_.Unordered({4}, "BE")), 0);
}
TEST_F(InterleavedReassemblyStreamsTest, CanReassembleFastPathOrdered) {
NiceMock<MockFunction<ReassemblyStreams::OnAssembledMessage>> on_assembled;
{
testing::InSequence s;
EXPECT_CALL(on_assembled,
Call(ElementsAre(tsn(1)),
Property(&DcSctpMessage::payload, ElementsAre(1))));
EXPECT_CALL(on_assembled,
Call(ElementsAre(tsn(2)),
Property(&DcSctpMessage::payload, ElementsAre(2))));
EXPECT_CALL(on_assembled,
Call(ElementsAre(tsn(3)),
Property(&DcSctpMessage::payload, ElementsAre(3))));
EXPECT_CALL(on_assembled,
Call(ElementsAre(tsn(4)),
Property(&DcSctpMessage::payload, ElementsAre(4))));
}
InterleavedReassemblyStreams streams("", on_assembled.AsStdFunction());
Data data1 = gen_.Ordered({1}, "BE");
Data data2 = gen_.Ordered({2}, "BE");
Data data3 = gen_.Ordered({3}, "BE");
Data data4 = gen_.Ordered({4}, "BE");
EXPECT_EQ(streams.Add(tsn(1), std::move(data1)), 0);
EXPECT_EQ(streams.Add(tsn(3), std::move(data3)), 1);
EXPECT_EQ(streams.Add(tsn(2), std::move(data2)), -1);
EXPECT_EQ(streams.Add(tsn(4), std::move(data4)), 0);
}
TEST_F(InterleavedReassemblyStreamsTest, CanHandoverOrderedStreams) {
InterleavedReassemblyStreams streams1("", [](auto...) {});
EXPECT_EQ(streams1.Add(tsn(1), gen_.Ordered({1}, "B")), 1);
EXPECT_EQ(streams1.GetHandoverReadiness(),
HandoverReadinessStatus(
HandoverUnreadinessReason::kOrderedStreamHasUnassembledChunks));
EXPECT_EQ(streams1.Add(tsn(2), gen_.Ordered({2, 3, 4}, "E")), -1);
EXPECT_TRUE(streams1.GetHandoverReadiness().IsReady());
DcSctpSocketHandoverState state;
streams1.AddHandoverState(state);
g_handover_state_transformer_for_test(&state);
MockFunction<ReassemblyStreams::OnAssembledMessage> on_assembled;
EXPECT_CALL(on_assembled,
Call(ElementsAre(tsn(3)),
Property(&DcSctpMessage::payload, ElementsAre(5, 6, 7, 8))));
InterleavedReassemblyStreams streams2("", on_assembled.AsStdFunction());
streams2.RestoreFromState(state);
Data data = gen_.Ordered({5, 6, 7, 8}, "BE");
EXPECT_EQ(data.mid, MID(1));
EXPECT_EQ(streams2.Add(tsn(3), std::move(data)), 0);
}
TEST_F(InterleavedReassemblyStreamsTest, CanHandoverUnorderedStreams) {
InterleavedReassemblyStreams streams1("", [](auto...) {});
EXPECT_EQ(streams1.Add(tsn(1), gen_.Unordered({1}, "B")), 1);
EXPECT_EQ(
streams1.GetHandoverReadiness(),
HandoverReadinessStatus(
HandoverUnreadinessReason::kUnorderedStreamHasUnassembledChunks));
EXPECT_EQ(streams1.Add(tsn(2), gen_.Unordered({2, 3, 4}, "E")), -1);
EXPECT_TRUE(streams1.GetHandoverReadiness().IsReady());
DcSctpSocketHandoverState state;
streams1.AddHandoverState(state);
g_handover_state_transformer_for_test(&state);
MockFunction<ReassemblyStreams::OnAssembledMessage> on_assembled;
EXPECT_CALL(on_assembled,
Call(ElementsAre(tsn(3)),
Property(&DcSctpMessage::payload, ElementsAre(5, 6, 7, 8))));
InterleavedReassemblyStreams streams2("", on_assembled.AsStdFunction());
streams2.RestoreFromState(state);
Data data = gen_.Unordered({5, 6, 7, 8}, "BE");
EXPECT_EQ(data.mid, MID(1));
EXPECT_EQ(streams2.Add(tsn(3), std::move(data)), 0);
}
} // namespace
} // namespace dcsctp