blob: adcd514e3e77ae8a8fa0d247167828d98acdff9d [file] [log] [blame]
/*
* 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/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::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);
}
} // namespace
} // namespace dcsctp