1280 lines
35 KiB
C++
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
|