Files
unison/src/network/test/packet-test-suite.cc
2025-02-06 16:53:40 +00:00

1280 lines
35 KiB
C++

/*
* Copyright (c) 2005,2006 INRIA
*
* SPDX-License-Identifier: GPL-2.0-only
*
* Author: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
*/
#include "ns3/packet-tag-list.h"
#include "ns3/packet.h"
#include "ns3/test.h"
#include <cstdarg>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <limits> // std:numeric_limits
#include <string>
using namespace ns3;
//-----------------------------------------------------------------------------
// Unit tests
//-----------------------------------------------------------------------------
namespace
{
/**
* @ingroup network-test
* @ingroup tests
*
* @brief Base class for Test tags
*
* @note Class internal to packet-test-suite.cc
*/
class ATestTagBase : public Tag
{
public:
ATestTagBase()
: m_error(false),
m_data(0)
{
}
/// Constructor
/// @param data Tag data
ATestTagBase(uint8_t data)
: m_error(false),
m_data(data)
{
}
/**
* Register this type.
* @return The TypeId.
*/
static TypeId GetTypeId()
{
static TypeId tid =
TypeId("ATestTagBase").SetParent<Tag>().SetGroupName("Network").HideFromDocumentation()
// No AddConstructor because this is an abstract class.
;
return tid;
}
/// Get the tag data.
/// @return the tag data.
int GetData() const
{
int result = (int)m_data;
return result;
}
bool m_error; //!< Error in the Tag
uint8_t m_data; //!< Tag data
};
/**
* @ingroup network-test
* @ingroup tests
*
* @brief Template class for Test tags
*
* @note Class internal to packet-test-suite.cc
*/
template <int N>
class ATestTag : public ATestTagBase
{
public:
/**
* Register this type.
* @return The TypeId.
*/
static TypeId GetTypeId()
{
std::ostringstream oss;
oss << "anon::ATestTag<" << N << ">";
static TypeId tid = TypeId(oss.str())
.SetParent<ATestTagBase>()
.SetGroupName("Network")
.HideFromDocumentation()
.AddConstructor<ATestTag<N>>();
return tid;
}
TypeId GetInstanceTypeId() const override
{
return GetTypeId();
}
uint32_t GetSerializedSize() const override
{
return N + sizeof(m_data);
}
void Serialize(TagBuffer buf) const override
{
buf.WriteU8(m_data);
for (uint32_t i = 0; i < N; ++i)
{
buf.WriteU8(N);
}
}
void Deserialize(TagBuffer buf) override
{
m_data = buf.ReadU8();
for (uint32_t i = 0; i < N; ++i)
{
uint8_t v = buf.ReadU8();
if (v != N)
{
m_error = true;
}
}
}
void Print(std::ostream& os) const override
{
os << N << "(" << m_data << ")";
}
ATestTag()
: ATestTagBase()
{
}
/// Constructor
/// @param data Tag data
ATestTag(uint8_t data)
: ATestTagBase(data)
{
}
};
// Previous versions of ns-3 limited the tag size to 20 bytes or less
// static const uint8_t LARGE_TAG_BUFFER_SIZE = 64;
#define LARGE_TAG_BUFFER_SIZE 64
/**
* @ingroup network-test
* @ingroup tests
*
* @brief Template class for Large Test tags
*
* @see Bug 2221: Expanding packet tag maximum size
*
* @note Class internal to packet-test-suite.cc
*/
class ALargeTestTag : public Tag
{
public:
ALargeTestTag()
{
for (uint8_t i = 0; i < (LARGE_TAG_BUFFER_SIZE - 1); i++)
{
m_data.push_back(i);
}
m_size = LARGE_TAG_BUFFER_SIZE;
}
/**
* Register this type.
* @return The TypeId.
*/
static TypeId GetTypeId()
{
static TypeId tid = TypeId("ALargeTestTag")
.SetParent<Tag>()
.SetGroupName("Network")
.HideFromDocumentation()
.AddConstructor<ALargeTestTag>();
return tid;
}
TypeId GetInstanceTypeId() const override
{
return GetTypeId();
}
uint32_t GetSerializedSize() const override
{
return (uint32_t)m_size;
}
void Serialize(TagBuffer buf) const override
{
buf.WriteU8(m_size);
for (uint8_t i = 0; i < (m_size - 1); ++i)
{
buf.WriteU8(m_data[i]);
}
}
void Deserialize(TagBuffer buf) override
{
m_size = buf.ReadU8();
for (uint8_t i = 0; i < (m_size - 1); ++i)
{
uint8_t v = buf.ReadU8();
m_data.push_back(v);
}
}
void Print(std::ostream& os) const override
{
os << "(" << (uint16_t)m_size << ")";
}
private:
uint8_t m_size; //!< Packet size
std::vector<uint8_t> m_data; //!< Tag data
};
/**
* @ingroup network-test
* @ingroup tests
*
* @brief Base class for Test headers
*
* @note Class internal to packet-test-suite.cc
*/
class ATestHeaderBase : public Header
{
public:
ATestHeaderBase()
: Header(),
m_error(false)
{
}
/**
* Register this type.
* @return The TypeId.
*/
static TypeId GetTypeId()
{
static TypeId tid = TypeId("ATestHeaderBase")
.SetParent<Header>()
.SetGroupName("Network")
.HideFromDocumentation()
// No AddConstructor because this is an abstract class.
;
return tid;
}
bool m_error; //!< Error in the Header
};
/**
* @ingroup network-test
* @ingroup tests
*
* @brief Template class for Test headers
*
* @note Class internal to packet-test-suite.cc
*/
template <int N>
class ATestHeader : public ATestHeaderBase
{
public:
/**
* Register this type.
* @return The TypeId.
*/
static TypeId GetTypeId()
{
std::ostringstream oss;
oss << "anon::ATestHeader<" << N << ">";
static TypeId tid = TypeId(oss.str())
.SetParent<ATestHeaderBase>()
.SetGroupName("Network")
.HideFromDocumentation()
.AddConstructor<ATestHeader<N>>();
return tid;
}
TypeId GetInstanceTypeId() const override
{
return GetTypeId();
}
uint32_t GetSerializedSize() const override
{
return N;
}
void Serialize(Buffer::Iterator iter) const override
{
for (uint32_t i = 0; i < N; ++i)
{
iter.WriteU8(N);
}
}
uint32_t Deserialize(Buffer::Iterator iter) override
{
for (uint32_t i = 0; i < N; ++i)
{
uint8_t v = iter.ReadU8();
if (v != N)
{
m_error = true;
}
}
return N;
}
void Print(std::ostream& os) const override
{
}
ATestHeader()
: ATestHeaderBase()
{
}
};
/**
* @ingroup network-test
* @ingroup tests
*
* @brief Base class for Test trailers
*
* @note Class internal to packet-test-suite.cc
*/
class ATestTrailerBase : public Trailer
{
public:
ATestTrailerBase()
: Trailer(),
m_error(false)
{
}
/**
* Register this type.
* @return The TypeId.
*/
static TypeId GetTypeId()
{
static TypeId tid = TypeId("ATestTrailerBase")
.SetParent<Trailer>()
.SetGroupName("Network")
.HideFromDocumentation()
// No AddConstructor because this is an abstract class.
;
return tid;
}
bool m_error; //!< Error in the Trailer
};
/**
* @ingroup network-test
* @ingroup tests
*
* @brief Template class for Test trailers
*
* @note Class internal to packet-test-suite.cc
*/
template <int N>
class ATestTrailer : public ATestTrailerBase
{
public:
/**
* Register this type.
* @return The TypeId.
*/
static TypeId GetTypeId()
{
std::ostringstream oss;
oss << "anon::ATestTrailer<" << N << ">";
static TypeId tid = TypeId(oss.str())
.SetParent<ATestTrailerBase>()
.SetGroupName("Network")
.HideFromDocumentation()
.AddConstructor<ATestTrailer<N>>();
return tid;
}
TypeId GetInstanceTypeId() const override
{
return GetTypeId();
}
uint32_t GetSerializedSize() const override
{
return N;
}
void Serialize(Buffer::Iterator iter) const override
{
iter.Prev(N);
for (uint32_t i = 0; i < N; ++i)
{
iter.WriteU8(N);
}
}
uint32_t Deserialize(Buffer::Iterator iter) override
{
iter.Prev(N);
for (uint32_t i = 0; i < N; ++i)
{
uint8_t v = iter.ReadU8();
if (v != N)
{
m_error = true;
}
}
return N;
}
void Print(std::ostream& os) const override
{
}
ATestTrailer()
: ATestTrailerBase()
{
}
};
/**
* @ingroup network-test
* @ingroup tests
*
* @brief Struct to hold the expected data in the packet
*
* @note Class internal to packet-test-suite.cc
*/
struct Expected
{
/**
* Constructor
* @param n_ Number of elements
* @param start_ Start
* @param end_ End
*/
Expected(uint32_t n_, uint32_t start_, uint32_t end_)
: n(n_),
start(start_),
end(end_),
data(0)
{
}
/**
* Constructor
* @param n_ Number of elements
* @param start_ Start
* @param end_ End
* @param data_ Data stored in tag
*/
Expected(uint32_t n_, uint32_t start_, uint32_t end_, uint8_t data_)
: n(n_),
start(start_),
end(end_),
data(data_)
{
}
uint32_t n; //!< Number of elements
uint32_t start; //!< Start
uint32_t end; //!< End
uint8_t data; //!< Optional data
};
} // namespace
// tag name, start, end
#define E(name, start, end) name, start, end
// tag name, start, end, data
#define E_DATA(name, start, end, data) name, start, end, data
// Check byte tags on a packet, checks name, start, end
#define CHECK(p, n, ...) DoCheck(p, n, __VA_ARGS__)
// Check byte tags on a packet, checks name, start, end, data
#define CHECK_DATA(p, n, ...) DoCheckData(p, n, __VA_ARGS__)
/**
* @ingroup network-test
* @ingroup tests
*
* Packet unit tests.
*/
class PacketTest : public TestCase
{
public:
PacketTest();
void DoRun() override;
private:
/**
* Checks the packet
* @param p The packet
* @param n The number of variable arguments
* @param ... The variable arguments
*/
void DoCheck(Ptr<const Packet> p, uint32_t n, ...);
/**
* Checks the packet and its data
* @param p The packet
* @param n The number of variable arguments
* @param ... The variable arguments
*/
void DoCheckData(Ptr<const Packet> p, uint32_t n, ...);
};
PacketTest::PacketTest()
: TestCase("Packet")
{
}
void
PacketTest::DoCheck(Ptr<const Packet> p, uint32_t n, ...)
{
std::vector<Expected> expected;
va_list ap;
va_start(ap, n);
for (uint32_t k = 0; k < n; ++k)
{
uint32_t N = va_arg(ap, uint32_t);
uint32_t start = va_arg(ap, uint32_t);
uint32_t end = va_arg(ap, uint32_t);
expected.emplace_back(N, start, end);
}
va_end(ap);
ByteTagIterator i = p->GetByteTagIterator();
uint32_t j = 0;
while (i.HasNext() && j < expected.size())
{
ByteTagIterator::Item item = i.Next();
Expected e = expected[j];
std::ostringstream oss;
oss << "anon::ATestTag<" << e.n << ">";
NS_TEST_EXPECT_MSG_EQ(item.GetTypeId().GetName(), oss.str(), "trivial");
NS_TEST_EXPECT_MSG_EQ(item.GetStart(), e.start, "trivial");
NS_TEST_EXPECT_MSG_EQ(item.GetEnd(), e.end, "trivial");
ATestTagBase* tag = dynamic_cast<ATestTagBase*>(item.GetTypeId().GetConstructor()());
NS_TEST_EXPECT_MSG_NE(tag, 0, "trivial");
item.GetTag(*tag);
NS_TEST_EXPECT_MSG_EQ(tag->m_error, false, "trivial");
delete tag;
j++;
}
NS_TEST_EXPECT_MSG_EQ(i.HasNext(), false, "Nothing left");
NS_TEST_EXPECT_MSG_EQ(j, expected.size(), "Size match");
}
void
PacketTest::DoCheckData(Ptr<const Packet> p, uint32_t n, ...)
{
std::vector<Expected> expected;
va_list ap;
va_start(ap, n);
for (uint32_t k = 0; k < n; ++k)
{
uint32_t N = va_arg(ap, uint32_t);
uint32_t start = va_arg(ap, uint32_t);
uint32_t end = va_arg(ap, uint32_t);
int data = va_arg(ap, int);
expected.emplace_back(N, start, end, data);
}
va_end(ap);
ByteTagIterator i = p->GetByteTagIterator();
uint32_t j = 0;
while (i.HasNext() && j < expected.size())
{
ByteTagIterator::Item item = i.Next();
Expected e = expected[j];
std::ostringstream oss;
oss << "anon::ATestTag<" << e.n << ">";
NS_TEST_EXPECT_MSG_EQ(item.GetTypeId().GetName(), oss.str(), "trivial");
NS_TEST_EXPECT_MSG_EQ(item.GetStart(), e.start, "trivial");
NS_TEST_EXPECT_MSG_EQ(item.GetEnd(), e.end, "trivial");
ATestTagBase* tag = dynamic_cast<ATestTagBase*>(item.GetTypeId().GetConstructor()());
NS_TEST_EXPECT_MSG_NE(tag, 0, "trivial");
item.GetTag(*tag);
NS_TEST_EXPECT_MSG_EQ(tag->m_error, false, "trivial");
NS_TEST_EXPECT_MSG_EQ(tag->GetData(), e.data, "trivial");
delete tag;
j++;
}
NS_TEST_EXPECT_MSG_EQ(i.HasNext(), false, "Nothing left");
NS_TEST_EXPECT_MSG_EQ(j, expected.size(), "Size match");
}
void
PacketTest::DoRun()
{
Ptr<Packet> pkt1 = Create<Packet>(reinterpret_cast<const uint8_t*>("hello"), 5);
Ptr<Packet> pkt2 = Create<Packet>(reinterpret_cast<const uint8_t*>(" world"), 6);
Ptr<Packet> packet = Create<Packet>();
packet->AddAtEnd(pkt1);
packet->AddAtEnd(pkt2);
NS_TEST_EXPECT_MSG_EQ(packet->GetSize(), 11, "trivial");
auto buf = new uint8_t[packet->GetSize()];
packet->CopyData(buf, packet->GetSize());
std::string msg = std::string(reinterpret_cast<const char*>(buf), packet->GetSize());
delete[] buf;
NS_TEST_EXPECT_MSG_EQ(msg, "hello world", "trivial");
Ptr<const Packet> p = Create<Packet>(1000);
p->AddByteTag(ATestTag<1>());
CHECK(p, 1, E(1, 0, 1000));
Ptr<const Packet> copy = p->Copy();
CHECK(copy, 1, E(1, 0, 1000));
p->AddByteTag(ATestTag<2>());
CHECK(p, 2, E(1, 0, 1000), E(2, 0, 1000));
CHECK(copy, 1, E(1, 0, 1000));
{
Packet c0 = *copy;
Packet c1 = *copy; // NOLINT(performance-unnecessary-copy-initialization)
c0 = c1;
CHECK(&c0, 1, E(1, 0, 1000));
CHECK(&c1, 1, E(1, 0, 1000));
CHECK(copy, 1, E(1, 0, 1000));
c0.AddByteTag(ATestTag<10>());
CHECK(&c0, 2, E(1, 0, 1000), E(10, 0, 1000));
CHECK(&c1, 1, E(1, 0, 1000));
CHECK(copy, 1, E(1, 0, 1000));
}
Ptr<Packet> frag0 = p->CreateFragment(0, 10);
Ptr<Packet> frag1 = p->CreateFragment(10, 90);
Ptr<const Packet> frag2 = p->CreateFragment(100, 900);
frag0->AddByteTag(ATestTag<3>());
CHECK(frag0, 3, E(1, 0, 10), E(2, 0, 10), E(3, 0, 10));
frag1->AddByteTag(ATestTag<4>());
CHECK(frag1, 3, E(1, 0, 90), E(2, 0, 90), E(4, 0, 90));
frag2->AddByteTag(ATestTag<5>());
CHECK(frag2, 3, E(1, 0, 900), E(2, 0, 900), E(5, 0, 900));
frag1->AddAtEnd(frag2);
CHECK(frag1,
6,
E(1, 0, 90),
E(2, 0, 90),
E(4, 0, 90),
E(1, 90, 990),
E(2, 90, 990),
E(5, 90, 990));
CHECK(frag0, 3, E(1, 0, 10), E(2, 0, 10), E(3, 0, 10));
frag0->AddAtEnd(frag1);
CHECK(frag0,
9,
E(1, 0, 10),
E(2, 0, 10),
E(3, 0, 10),
E(1, 10, 100),
E(2, 10, 100),
E(4, 10, 100),
E(1, 100, 1000),
E(2, 100, 1000),
E(5, 100, 1000));
// force caching a buffer of the right size.
frag0 = Create<Packet>(1000);
frag0->AddHeader(ATestHeader<10>());
frag0 = nullptr;
p = Create<Packet>(1000);
p->AddByteTag(ATestTag<20>());
CHECK(p, 1, E(20, 0, 1000));
frag0 = p->CreateFragment(10, 90);
CHECK(p, 1, E(20, 0, 1000));
CHECK(frag0, 1, E(20, 0, 90));
p = nullptr;
frag0->AddHeader(ATestHeader<10>());
CHECK(frag0, 1, E(20, 10, 100));
{
Ptr<Packet> tmp = Create<Packet>(100);
tmp->AddByteTag(ATestTag<20>());
CHECK(tmp, 1, E(20, 0, 100));
tmp->AddHeader(ATestHeader<10>());
CHECK(tmp, 1, E(20, 10, 110));
ATestHeader<10> h;
tmp->RemoveHeader(h);
CHECK(tmp, 1, E(20, 0, 100));
tmp->AddHeader(ATestHeader<10>());
CHECK(tmp, 1, E(20, 10, 110));
tmp = Create<Packet>(100);
tmp->AddByteTag(ATestTag<20>());
CHECK(tmp, 1, E(20, 0, 100));
tmp->AddTrailer(ATestTrailer<10>());
CHECK(tmp, 1, E(20, 0, 100));
ATestTrailer<10> t;
tmp->RemoveTrailer(t);
CHECK(tmp, 1, E(20, 0, 100));
tmp->AddTrailer(ATestTrailer<10>());
CHECK(tmp, 1, E(20, 0, 100));
}
{
Ptr<Packet> tmp = Create<Packet>(0);
tmp->AddHeader(ATestHeader<156>());
tmp->AddByteTag(ATestTag<20>());
CHECK(tmp, 1, E(20, 0, 156));
tmp->RemoveAtStart(120);
CHECK(tmp, 1, E(20, 0, 36));
Ptr<Packet> a = Create<Packet>(0);
a->AddAtEnd(tmp);
CHECK(a, 1, E(20, 0, 36));
}
{
Ptr<Packet> tmp = Create<Packet>(0);
tmp->AddByteTag(ATestTag<20>());
CHECK(tmp, 0, E(20, 0, 0));
}
{
Ptr<Packet> tmp = Create<Packet>(1000);
tmp->AddByteTag(ATestTag<20>());
CHECK(tmp, 1, E(20, 0, 1000));
tmp->RemoveAtStart(1000);
CHECK(tmp, 0, E(0, 0, 0));
Ptr<Packet> a = Create<Packet>(10);
a->AddByteTag(ATestTag<10>());
CHECK(a, 1, E(10, 0, 10));
tmp->AddAtEnd(a);
CHECK(tmp, 1, E(10, 0, 10));
}
{
Packet p;
ATestTag<10> a;
p.AddPacketTag(a);
NS_TEST_EXPECT_MSG_EQ(p.PeekPacketTag(a), true, "trivial");
ATestTag<11> b;
p.AddPacketTag(b);
NS_TEST_EXPECT_MSG_EQ(p.PeekPacketTag(b), true, "trivial");
NS_TEST_EXPECT_MSG_EQ(p.PeekPacketTag(a), true, "trivial");
Packet copy = p;
NS_TEST_EXPECT_MSG_EQ(copy.PeekPacketTag(b), true, "trivial");
NS_TEST_EXPECT_MSG_EQ(copy.PeekPacketTag(a), true, "trivial");
ATestTag<12> c;
NS_TEST_EXPECT_MSG_EQ(copy.PeekPacketTag(c), false, "trivial");
copy.AddPacketTag(c);
NS_TEST_EXPECT_MSG_EQ(copy.PeekPacketTag(c), true, "trivial");
NS_TEST_EXPECT_MSG_EQ(copy.PeekPacketTag(b), true, "trivial");
NS_TEST_EXPECT_MSG_EQ(copy.PeekPacketTag(a), true, "trivial");
NS_TEST_EXPECT_MSG_EQ(p.PeekPacketTag(c), false, "trivial");
copy.RemovePacketTag(b);
NS_TEST_EXPECT_MSG_EQ(copy.PeekPacketTag(b), false, "trivial");
NS_TEST_EXPECT_MSG_EQ(p.PeekPacketTag(b), true, "trivial");
p.RemovePacketTag(a);
NS_TEST_EXPECT_MSG_EQ(p.PeekPacketTag(a), false, "trivial");
NS_TEST_EXPECT_MSG_EQ(copy.PeekPacketTag(a), true, "trivial");
NS_TEST_EXPECT_MSG_EQ(p.PeekPacketTag(c), false, "trivial");
NS_TEST_EXPECT_MSG_EQ(copy.PeekPacketTag(c), true, "trivial");
p.RemoveAllPacketTags();
NS_TEST_EXPECT_MSG_EQ(p.PeekPacketTag(b), false, "trivial");
}
/* Test Serialization and Deserialization of Packet with PacketTag data */
{
Ptr<Packet> p1 = Create<Packet>(1000);
;
ATestTag<10> a1(65);
ATestTag<11> b1(66);
ATestTag<12> c1(67);
p1->AddPacketTag(a1);
p1->AddPacketTag(b1);
p1->AddPacketTag(c1);
uint32_t serializedSize = p1->GetSerializedSize();
auto buffer = new uint8_t[serializedSize + 16];
p1->Serialize(buffer, serializedSize);
Ptr<Packet> p2 = Create<Packet>(buffer, serializedSize, true);
delete[] buffer;
ATestTag<10> a2;
ATestTag<11> b2;
ATestTag<12> c2;
NS_TEST_EXPECT_MSG_EQ(p2->PeekPacketTag(a2), true, "trivial");
NS_TEST_EXPECT_MSG_EQ(a2.GetData(), 65, "trivial");
NS_TEST_EXPECT_MSG_EQ(p2->PeekPacketTag(b2), true, "trivial");
NS_TEST_EXPECT_MSG_EQ(b2.GetData(), 66, "trivial");
NS_TEST_EXPECT_MSG_EQ(p2->PeekPacketTag(c2), true, "trivial");
NS_TEST_EXPECT_MSG_EQ(c2.GetData(), 67, "trivial");
}
/* Test Serialization and Deserialization of Packet with ByteTag data */
{
Ptr<Packet> p1 = Create<Packet>(1000);
;
ATestTag<10> a1(65);
ATestTag<11> b1(66);
ATestTag<12> c1(67);
p1->AddByteTag(a1);
p1->AddByteTag(b1);
p1->AddByteTag(c1);
CHECK(p1, 3, E(10, 0, 1000), E(11, 0, 1000), E(12, 0, 1000));
uint32_t serializedSize = p1->GetSerializedSize();
auto buffer = new uint8_t[serializedSize];
p1->Serialize(buffer, serializedSize);
Ptr<Packet> p2 = Create<Packet>(buffer, serializedSize, true);
delete[] buffer;
CHECK_DATA(p2,
3,
E_DATA(10, 0, 1000, 65),
E_DATA(11, 0, 1000, 66),
E_DATA(12, 0, 1000, 67));
}
{
/// @internal
/// See \bugid{572}
Ptr<Packet> tmp = Create<Packet>(1000);
tmp->AddByteTag(ATestTag<20>());
CHECK(tmp, 1, E(20, 0, 1000));
tmp->AddHeader(ATestHeader<2>());
CHECK(tmp, 1, E(20, 2, 1002));
tmp->RemoveAtStart(1);
CHECK(tmp, 1, E(20, 1, 1001));
#if 0
tmp->PeekData ();
CHECK (tmp, 1, E (20, 1, 1001));
#endif
}
/* Test reducing tagged packet size and increasing it back. */
{
Ptr<Packet> tmp = Create<Packet>(0);
tmp->AddHeader(ATestHeader<100>());
tmp->AddByteTag(ATestTag<25>());
CHECK(tmp, 1, E(25, 0, 100));
tmp->RemoveAtStart(50);
CHECK(tmp, 1, E(25, 0, 50));
tmp->AddHeader(ATestHeader<50>());
CHECK(tmp, 1, E(25, 50, 100));
}
/* Similar test case, but using trailer instead of header. */
{
Ptr<Packet> tmp = Create<Packet>(0);
tmp->AddTrailer(ATestTrailer<100>());
tmp->AddByteTag(ATestTag<25>());
CHECK(tmp, 1, E(25, 0, 100));
tmp->RemoveAtEnd(50);
CHECK(tmp, 1, E(25, 0, 50));
tmp->AddTrailer(ATestTrailer<50>());
CHECK(tmp, 1, E(25, 0, 50));
}
/* Test reducing tagged packet size and increasing it by half. */
{
Ptr<Packet> tmp = Create<Packet>(0);
tmp->AddHeader(ATestHeader<100>());
tmp->AddByteTag(ATestTag<25>());
CHECK(tmp, 1, E(25, 0, 100));
tmp->RemoveAtStart(50);
CHECK(tmp, 1, E(25, 0, 50));
tmp->AddHeader(ATestHeader<25>());
CHECK(tmp, 1, E(25, 25, 75));
}
/* Similar test case, but using trailer instead of header. */
{
Ptr<Packet> tmp = Create<Packet>(0);
tmp->AddTrailer(ATestTrailer<100>());
tmp->AddByteTag(ATestTag<25>());
CHECK(tmp, 1, E(25, 0, 100));
tmp->RemoveAtEnd(50);
CHECK(tmp, 1, E(25, 0, 50));
tmp->AddTrailer(ATestTrailer<25>());
CHECK(tmp, 1, E(25, 0, 50));
}
/* Test AddPaddingAtEnd. */
{
Ptr<Packet> tmp = Create<Packet>(0);
tmp->AddTrailer(ATestTrailer<100>());
tmp->AddByteTag(ATestTag<25>());
CHECK(tmp, 1, E(25, 0, 100));
tmp->RemoveAtEnd(50);
CHECK(tmp, 1, E(25, 0, 50));
tmp->AddPaddingAtEnd(50);
CHECK(tmp, 1, E(25, 0, 50));
}
/* Test reducing tagged packet size and increasing it back,
* now using padding bytes to avoid triggering dirty state
* in virtual buffer
*/
{
Ptr<Packet> tmp = Create<Packet>(100);
tmp->AddByteTag(ATestTag<25>());
CHECK(tmp, 1, E(25, 0, 100));
tmp->RemoveAtEnd(50);
CHECK(tmp, 1, E(25, 0, 50));
tmp->AddPaddingAtEnd(50);
CHECK(tmp, 1, E(25, 0, 50));
}
/* Test ALargeTestTag */
{
Ptr<Packet> tmp = Create<Packet>(0);
ALargeTestTag a;
tmp->AddPacketTag(a);
}
}
/**
* @ingroup network-test
* @ingroup tests
*
* Packet Tag list unit tests.
*/
class PacketTagListTest : public TestCase
{
public:
PacketTagListTest();
~PacketTagListTest() override;
private:
void DoRun() override;
/**
* Checks against a reference PacketTagList
* @param ref Reference
* @param t List to test
* @param msg Message
* @param miss Expected miss/hit
*/
void CheckRef(const PacketTagList& ref, ATestTagBase& t, const char* msg, bool miss = false);
/**
* Checks against a reference PacketTagList
* @param ref Reference
* @param msg Message
* @param miss Expected miss/hit
*/
void CheckRefList(const PacketTagList& ref, const char* msg, int miss = 0);
/**
* Prints the remove time
* @param ref Reference.
* @param t List to test.
* @param msg Message - prints on cout if msg is not null.
* @return the ticks to remove the tags.
*/
int RemoveTime(const PacketTagList& ref, ATestTagBase& t, const char* msg = nullptr);
/**
* Prints the remove time
* @param verbose prints on cout if verbose is true.
* @return the ticks to remove the tags.
*/
int AddRemoveTime(const bool verbose = false);
};
PacketTagListTest::PacketTagListTest()
: TestCase("PacketTagListTest: ")
{
}
PacketTagListTest::~PacketTagListTest()
{
}
void
PacketTagListTest::CheckRef(const PacketTagList& ref, ATestTagBase& t, const char* msg, bool miss)
{
int expect = t.GetData(); // the value we should find
bool found = ref.Peek(t); // rewrites t with actual value
NS_TEST_EXPECT_MSG_EQ(found, !miss, msg << ": ref contains " << t.GetTypeId().GetName());
if (found)
{
NS_TEST_EXPECT_MSG_EQ(t.GetData(),
expect,
msg << ": ref " << t.GetTypeId().GetName() << " = " << expect);
}
}
// A set of tags with data value 1, to check COW
#define MAKE_TEST_TAGS \
ATestTag<1> t1(1); \
ATestTag<2> t2(1); \
ATestTag<3> t3(1); \
ATestTag<4> t4(1); \
ATestTag<5> t5(1); \
ATestTag<6> t6(1); \
ATestTag<7> t7(1); \
constexpr int TAG_LAST [[maybe_unused]] = 7; /* length of ref PacketTagList */
void
PacketTagListTest::CheckRefList(const PacketTagList& ptl, const char* msg, int miss /* = 0 */)
{
MAKE_TEST_TAGS;
CheckRef(ptl, t1, msg, miss == 1);
CheckRef(ptl, t2, msg, miss == 2);
CheckRef(ptl, t3, msg, miss == 3);
CheckRef(ptl, t4, msg, miss == 4);
CheckRef(ptl, t5, msg, miss == 5);
CheckRef(ptl, t6, msg, miss == 6);
CheckRef(ptl, t7, msg, miss == 7);
}
int
PacketTagListTest::RemoveTime(const PacketTagList& ref, ATestTagBase& t, const char* msg /* = 0 */)
{
const int reps = 10000;
std::vector<PacketTagList> ptv(reps, ref);
int start = clock();
for (int i = 0; i < reps; ++i)
{
ptv[i].Remove(t);
}
int stop = clock();
int delta = stop - start;
if (msg)
{
std::cout << GetName() << "remove time: " << msg << ": " << std::setw(8) << delta
<< " ticks to remove " << reps << " times" << std::endl;
}
return delta;
}
int
PacketTagListTest::AddRemoveTime(const bool verbose /* = false */)
{
const int reps = 100000;
PacketTagList ptl;
ATestTag<2> t(2);
int start = clock();
for (int i = 0; i < reps; ++i)
{
ptl.Add(t);
ptl.Remove(t);
}
int stop = clock();
int delta = stop - start;
if (verbose)
{
std::cout << GetName() << "add/remove time: " << std::setw(8) << delta
<< " ticks to add+remove " << reps << " times" << std::endl;
}
return delta;
}
void
PacketTagListTest::DoRun()
{
std::cout << GetName() << "begin" << std::endl;
MAKE_TEST_TAGS;
PacketTagList ref; // empty list
ref.Add(t1); // last
ref.Add(t2); // post merge
ref.Add(t3); // merge successor
ref.Add(t4); // merge
ref.Add(t5); // merge precursor
ref.Add(t6); // pre-merge
ref.Add(t7); // first
// Peek
{
std::cout << GetName() << "check Peek (missing tag) returns false" << std::endl;
ATestTag<10> t10;
NS_TEST_EXPECT_MSG_EQ(ref.Peek(t10), false, "missing tag");
}
// Copy ctor, assignment
{
std::cout << GetName() << "check copy and assignment" << std::endl;
{
// Test copy constructor
// NOLINTNEXTLINE(performance-unnecessary-copy-initialization)
PacketTagList ptl(ref);
CheckRefList(ref, "copy ctor orig");
CheckRefList(ptl, "copy ctor copy");
}
{
// Test copy constructor
// NOLINTNEXTLINE(performance-unnecessary-copy-initialization)
PacketTagList ptl = ref;
CheckRefList(ref, "assignment orig");
CheckRefList(ptl, "assignment copy");
}
}
// Removal
{
#define RemoveCheck(n) \
PacketTagList p##n = ref; \
p##n.Remove(t##n); \
CheckRefList(ref, "remove " #n " orig"); \
CheckRefList(p##n, "remove " #n " copy", n);
// Remove single tags from list
{
std::cout << GetName() << "check removal of each tag" << std::endl;
RemoveCheck(1);
RemoveCheck(2);
RemoveCheck(3);
RemoveCheck(4);
RemoveCheck(5);
RemoveCheck(6);
RemoveCheck(7);
}
// Remove in the presence of a merge
{
std::cout << GetName() << "check removal doesn't disturb merge " << std::endl;
PacketTagList ptl = ref;
ptl.Remove(t7);
ptl.Remove(t6);
ptl.Remove(t5);
PacketTagList mrg = ptl; // merged list
ATestTag<8> m5(1);
mrg.Add(m5); // ptl and mrg differ
ptl.Add(t5);
ptl.Add(t6);
ptl.Add(t7);
CheckRefList(ref, "post merge, orig");
CheckRefList(ptl, "post merge, long chain");
const char* msg = "post merge, short chain";
CheckRef(mrg, t1, msg, false);
CheckRef(mrg, t2, msg, false);
CheckRef(mrg, t3, msg, false);
CheckRef(mrg, t4, msg, false);
CheckRef(mrg, m5, msg, false);
}
#undef RemoveCheck
} // Removal
// Replace
{
std::cout << GetName() << "check replacing each tag" << std::endl;
#define ReplaceCheck(n) \
t##n.m_data = 2; \
{ \
PacketTagList p##n = ref; \
p##n.Replace(t##n); \
CheckRefList(ref, "replace " #n " orig"); \
CheckRef(p##n, t##n, "replace " #n " copy"); \
}
ReplaceCheck(1);
ReplaceCheck(2);
ReplaceCheck(3);
ReplaceCheck(4);
ReplaceCheck(5);
ReplaceCheck(6);
ReplaceCheck(7);
}
// Timing
{
std::cout << GetName() << "add+remove timing" << std::endl;
int flm = std::numeric_limits<int>::max();
const int nIterations = 100;
for (int i = 0; i < nIterations; ++i)
{
int now = AddRemoveTime();
if (now < flm)
{
flm = now;
}
}
std::cout << GetName() << "min add+remove time: " << std::setw(8) << flm << " ticks"
<< std::endl;
std::cout << GetName() << "remove timing" << std::endl;
// tags numbered from 1, so add one for (unused) entry at 0
std::vector<int> rmn(TAG_LAST + 1, std::numeric_limits<int>::max());
for (int i = 0; i < nIterations; ++i)
{
for (int j = 1; j <= TAG_LAST; ++j)
{
int now = 0;
switch (j)
{
case 7:
now = RemoveTime(ref, t7);
break;
case 6:
now = RemoveTime(ref, t6);
break;
case 5:
now = RemoveTime(ref, t5);
break;
case 4:
now = RemoveTime(ref, t4);
break;
case 3:
now = RemoveTime(ref, t3);
break;
case 2:
now = RemoveTime(ref, t2);
break;
case 1:
now = RemoveTime(ref, t1);
break;
} // switch
if (now < rmn[j])
{
rmn[j] = now;
}
} // for tag j
} // for iteration i
for (int j = TAG_LAST; j > 0; --j)
{
std::cout << GetName() << "min remove time: t" << j << ": " << std::setw(8) << rmn[j]
<< " ticks" << std::endl;
}
} // Timing
}
/**
* @ingroup network-test
* @ingroup tests
*
* @brief Packet TestSuite
*/
class PacketTestSuite : public TestSuite
{
public:
PacketTestSuite();
};
PacketTestSuite::PacketTestSuite()
: TestSuite("packet", Type::UNIT)
{
AddTestCase(new PacketTest, TestCase::Duration::QUICK);
AddTestCase(new PacketTagListTest, TestCase::Duration::QUICK);
}
static PacketTestSuite g_packetTestSuite; //!< Static variable for test initialization