A Discrete-Event Network Simulator
API
tc-flow-control-test-suite.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2017 Universita' degli Studi di Napoli Federico II
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation;
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16  *
17  * Author: Stefano Avallone <stavallo@unina.it>
18  *
19  */
20 
21 #include "ns3/config.h"
22 #include "ns3/data-rate.h"
23 #include "ns3/double.h"
24 #include "ns3/log.h"
25 #include "ns3/net-device-queue-interface.h"
26 #include "ns3/node-container.h"
27 #include "ns3/pointer.h"
28 #include "ns3/queue.h"
29 #include "ns3/simple-net-device-helper.h"
30 #include "ns3/simulator.h"
31 #include "ns3/string.h"
32 #include "ns3/test.h"
33 #include "ns3/traffic-control-helper.h"
34 #include "ns3/traffic-control-layer.h"
35 #include "ns3/uinteger.h"
36 
37 #include <algorithm>
38 #include <string>
39 
40 using namespace ns3;
41 
48 {
49  public:
56  ~QueueDiscTestItem() override;
57 
58  // Delete default constructor, copy constructor and assignment operator to avoid misuse
59  QueueDiscTestItem() = delete;
62 
63  void AddHeader() override;
64  bool Mark() override;
65 };
66 
68  : QueueDiscItem(p, Mac48Address(), 0)
69 {
70 }
71 
73 {
74 }
75 
76 void
78 {
79 }
80 
81 bool
83 {
84  return false;
85 }
86 
93 {
94  public:
102  TcFlowControlTestCase(QueueSizeUnit tt, uint32_t deviceQueueLength, uint32_t totalTxPackets);
103  ~TcFlowControlTestCase() override;
104 
105  private:
106  void DoRun() override;
112  void SendPackets(Ptr<Node> n, uint16_t nPackets);
119  void CheckPacketsInDeviceQueue(Ptr<NetDevice> dev, uint16_t nPackets, const std::string msg);
126  void CheckDeviceQueueStopped(Ptr<NetDevice> dev, bool value, const std::string msg);
133  void CheckPacketsInQueueDisc(Ptr<NetDevice> dev, uint16_t nPackets, const std::string msg);
136  uint32_t m_totalTxPackets;
137 };
138 
140  uint32_t deviceQueueLength,
141  uint32_t totalTxPackets)
142  : TestCase("Test the operation of the flow control mechanism"),
143  m_type(tt),
144  m_deviceQueueLength(deviceQueueLength),
145  m_totalTxPackets(totalTxPackets)
146 {
147 }
148 
150 {
151 }
152 
153 void
155 {
157  for (uint16_t i = 0; i < nPackets; i++)
158  {
159  tc->Send(n->GetDevice(0), Create<QueueDiscTestItem>(Create<Packet>(1000)));
160  }
161 }
162 
163 void
165  uint16_t nPackets,
166  const std::string msg)
167 {
168  PointerValue ptr;
169  dev->GetAttributeFailSafe("TxQueue", ptr);
170  Ptr<Queue<Packet>> queue = ptr.Get<Queue<Packet>>();
171  NS_TEST_EXPECT_MSG_EQ(queue->GetNPackets(), nPackets, msg);
172 }
173 
174 void
176  bool value,
177  const std::string msg)
178 {
179  Ptr<NetDeviceQueueInterface> ndqi = dev->GetObject<NetDeviceQueueInterface>();
180  NS_ASSERT_MSG(ndqi, "A device queue interface has not been aggregated to the device");
181  NS_TEST_EXPECT_MSG_EQ(ndqi->GetTxQueue(0)->IsStopped(), value, msg);
182 }
183 
184 void
186  uint16_t nPackets,
187  const std::string msg)
188 {
189  Ptr<TrafficControlLayer> tc = dev->GetNode()->GetObject<TrafficControlLayer>();
190  Ptr<QueueDisc> qdisc = tc->GetRootQueueDiscOnDevice(dev);
191  NS_TEST_EXPECT_MSG_EQ(qdisc->GetNPackets(), nPackets, msg);
192 }
193 
194 void
196 {
197  NodeContainer n;
198  n.Create(2);
199 
200  n.Get(0)->AggregateObject(CreateObject<TrafficControlLayer>());
201  n.Get(1)->AggregateObject(CreateObject<TrafficControlLayer>());
202 
203  SimpleNetDeviceHelper simple;
204 
205  NetDeviceContainer rxDevC = simple.Install(n.Get(1));
206 
207  simple.SetDeviceAttribute("DataRate", DataRateValue(DataRate("1Mb/s")));
208  simple.SetQueue("ns3::DropTailQueue",
209  "MaxSize",
213 
214  Ptr<NetDevice> txDev;
215  txDev =
216  simple.Install(n.Get(0), DynamicCast<SimpleChannel>(rxDevC.Get(0)->GetChannel())).Get(0);
217  txDev->SetMtu(2500);
218 
219  TrafficControlHelper tch = TrafficControlHelper::Default();
220  tch.Install(txDev);
221 
222  // transmit 10 packets at time 0
223  Simulator::Schedule(Time(Seconds(0)),
225  this,
226  n.Get(0),
228 
230  {
231  /*
232  * When the device queue is in packet mode, all the packets enqueued in the
233  * queue disc are correctly transmitted, even if the device queue is stopped
234  * when the last packet is received from the upper layers
235  *
236  * We have the following invariants:
237  * - totalPackets = txPackets + deviceQueuePackets + qdiscPackets
238  * - deviceQueuePackets = MIN(totalPackets - txPackets, deviceQueueLen)
239  * - qdiscPackets = MAX(totalPackets - txPackets - deviceQueuePackets, 0)
240  *
241  * The transmission of each packet takes 1000B/1Mbps = 8ms
242  *
243  * We check the values of deviceQueuePackets and qdiscPackets 1ms after each
244  * packet is transmitted (i.e. at 1ms, 9ms, 17ms, ...), as well as verifying
245  * that the device queue is stopped or not, as appropriate.
246  */
247 
248  uint32_t checkTimeMs = 0;
249  uint32_t deviceQueuePackets = 0;
250  uint32_t qdiscPackets = 0;
251 
252  uint32_t txPackets = 0;
253  for (txPackets = 1; txPackets <= m_totalTxPackets; txPackets++)
254  {
255  checkTimeMs = 8 * (txPackets - 1) + 1; // Check 1ms after each packet is sent
256  deviceQueuePackets = std::min(m_totalTxPackets - txPackets, m_deviceQueueLength);
257  qdiscPackets = std::max(m_totalTxPackets - txPackets - deviceQueuePackets, (uint32_t)0);
258  if (deviceQueuePackets == m_deviceQueueLength)
259  {
260  Simulator::Schedule(Time(MilliSeconds(checkTimeMs)),
262  this,
263  txDev,
264  true,
265  "The device queue must be stopped after " +
266  std::to_string(checkTimeMs) + "ms");
267  }
268  else
269  {
270  Simulator::Schedule(Time(MilliSeconds(checkTimeMs)),
272  this,
273  txDev,
274  false,
275  "The device queue must not be stopped after " +
276  std::to_string(checkTimeMs) + "ms");
277  }
278 
279  Simulator::Schedule(Time(MilliSeconds(checkTimeMs)),
281  this,
282  txDev,
283  deviceQueuePackets,
284  "There must be " + std::to_string(m_deviceQueueLength) +
285  " packets in the device after " + std::to_string(checkTimeMs) +
286  "ms");
287 
288  Simulator::Schedule(Time(MilliSeconds(checkTimeMs)),
290  this,
291  txDev,
292  qdiscPackets,
293  "There must be " + std::to_string(qdiscPackets) +
294  " packets in the queue disc after " +
295  std::to_string(checkTimeMs) + "ms");
296  }
297  }
298  else
299  {
300  // TODO: Make this test parametric as well, and add new test cases
301  /*
302  * When the device queue is in byte mode, all the packets enqueued in the
303  * queue disc are correctly transmitted, even if the device queue is stopped
304  * when the last packet is received from the upper layers
305  */
306 
307  // The transmission of each packet takes 1000B/1Mbps = 8ms
308  // After 1ms, we have 3 packets in the device queue (stopped) and 6 in the queue disc
309  Simulator::Schedule(Time(MilliSeconds(1)),
311  this,
312  txDev,
313  3,
314  "There must be 3 packets in the device queue after 1ms");
315  Simulator::Schedule(Time(MilliSeconds(1)),
317  this,
318  txDev,
319  true,
320  "The device queue must be stopped after 1ms");
321  Simulator::Schedule(Time(MilliSeconds(1)),
323  this,
324  txDev,
325  6,
326  "There must be 6 packets in the queue disc after 1ms");
327 
328  // After 9ms, we have 3 packets in the device queue (stopped) and 5 in the queue disc
329  Simulator::Schedule(Time(MilliSeconds(9)),
331  this,
332  txDev,
333  3,
334  "There must be 3 packets in the device queue after 9ms");
335  Simulator::Schedule(Time(MilliSeconds(9)),
337  this,
338  txDev,
339  true,
340  "The device queue must be stopped after 9ms");
341  Simulator::Schedule(Time(MilliSeconds(9)),
343  this,
344  txDev,
345  5,
346  "There must be 5 packets in the queue disc after 9ms");
347 
348  // After 17ms, we have 3 packets in the device queue (stopped) and 4 in the queue disc
349  Simulator::Schedule(Time(MilliSeconds(17)),
351  this,
352  txDev,
353  3,
354  "There must be 3 packets in the device queue after 17ms");
355  Simulator::Schedule(Time(MilliSeconds(17)),
357  this,
358  txDev,
359  true,
360  "The device queue must be stopped after 17ms");
361  Simulator::Schedule(Time(MilliSeconds(17)),
363  this,
364  txDev,
365  4,
366  "There must be 4 packets in the queue disc after 17ms");
367 
368  // After 25ms, we have 3 packets in the device queue (stopped) and 3 in the queue disc
369  Simulator::Schedule(Time(MilliSeconds(25)),
371  this,
372  txDev,
373  3,
374  "There must be 3 packets in the device queue after 25ms");
375  Simulator::Schedule(Time(MilliSeconds(25)),
377  this,
378  txDev,
379  true,
380  "The device queue must be stopped after 25ms");
381  Simulator::Schedule(Time(MilliSeconds(25)),
383  this,
384  txDev,
385  3,
386  "There must be 3 packets in the queue disc after 25ms");
387 
388  // After 33ms, we have 3 packets in the device queue (stopped) and 2 in the queue disc
389  Simulator::Schedule(Time(MilliSeconds(33)),
391  this,
392  txDev,
393  3,
394  "There must be 3 packets in the device queue after 33ms");
395  Simulator::Schedule(Time(MilliSeconds(33)),
397  this,
398  txDev,
399  true,
400  "The device queue must be stopped after 33ms");
401  Simulator::Schedule(Time(MilliSeconds(33)),
403  this,
404  txDev,
405  2,
406  "There must be 2 packets in the queue disc after 33ms");
407 
408  // After 41ms, we have 3 packets in the device queue (stopped) and 1 in the queue disc
409  Simulator::Schedule(Time(MilliSeconds(41)),
411  this,
412  txDev,
413  3,
414  "There must be 3 packets in the device queue after 41ms");
415  Simulator::Schedule(Time(MilliSeconds(41)),
417  this,
418  txDev,
419  true,
420  "The device queue must be stopped after 41ms");
421  Simulator::Schedule(Time(MilliSeconds(41)),
423  this,
424  txDev,
425  1,
426  "There must be 1 packet in the queue disc after 41ms");
427 
428  // After 49ms, we have 3 packets in the device queue (stopped) and the queue disc is empty
429  Simulator::Schedule(Time(MilliSeconds(49)),
431  this,
432  txDev,
433  3,
434  "There must be 3 packets in the device queue after 49ms");
435  Simulator::Schedule(Time(MilliSeconds(49)),
437  this,
438  txDev,
439  true,
440  "The device queue must be stopped after 49ms");
441  Simulator::Schedule(Time(MilliSeconds(49)),
443  this,
444  txDev,
445  0,
446  "The queue disc must be empty after 49ms");
447 
448  // After 57ms, we have 2 packets in the device queue (not stopped) and the queue disc is
449  // empty
450  Simulator::Schedule(Time(MilliSeconds(57)),
452  this,
453  txDev,
454  2,
455  "There must be 2 packets in the device queue after 57ms");
456  Simulator::Schedule(Time(MilliSeconds(57)),
458  this,
459  txDev,
460  false,
461  "The device queue must not be stopped after 57ms");
462  Simulator::Schedule(Time(MilliSeconds(57)),
464  this,
465  txDev,
466  0,
467  "The queue disc must be empty after 57ms");
468 
469  // After 81ms, all packets must have been transmitted (the device queue and the queue disc
470  // are empty)
471  Simulator::Schedule(Time(MilliSeconds(81)),
473  this,
474  txDev,
475  0,
476  "The device queue must be empty after 81ms");
477  Simulator::Schedule(Time(MilliSeconds(81)),
479  this,
480  txDev,
481  false,
482  "The device queue must not be stopped after 81ms");
483  Simulator::Schedule(Time(MilliSeconds(81)),
485  this,
486  txDev,
487  0,
488  "The queue disc must be empty after 81ms");
489  }
490 
491  Simulator::Run();
492  Simulator::Destroy();
493 }
494 
500 static class TcFlowControlTestSuite : public TestSuite
501 {
502  public:
504  : TestSuite("tc-flow-control", UNIT)
505  {
506  AddTestCase(new TcFlowControlTestCase(QueueSizeUnit::PACKETS, 1, 10), TestCase::QUICK);
507  AddTestCase(new TcFlowControlTestCase(QueueSizeUnit::PACKETS, 5, 10), TestCase::QUICK);
508  AddTestCase(new TcFlowControlTestCase(QueueSizeUnit::PACKETS, 9, 10), TestCase::QUICK);
509  AddTestCase(new TcFlowControlTestCase(QueueSizeUnit::PACKETS, 10, 10), TestCase::QUICK);
510  AddTestCase(new TcFlowControlTestCase(QueueSizeUnit::PACKETS, 11, 10), TestCase::QUICK);
511  AddTestCase(new TcFlowControlTestCase(QueueSizeUnit::PACKETS, 15, 10), TestCase::QUICK);
512  AddTestCase(new TcFlowControlTestCase(QueueSizeUnit::PACKETS, 1, 1), TestCase::QUICK);
513  AddTestCase(new TcFlowControlTestCase(QueueSizeUnit::PACKETS, 2, 1), TestCase::QUICK);
514  AddTestCase(new TcFlowControlTestCase(QueueSizeUnit::PACKETS, 5, 1), TestCase::QUICK);
515 
516  // TODO: Right now, this test only works for 5000B and 10 packets (it's hard coded). Should
517  // also be made parametric.
518  AddTestCase(new TcFlowControlTestCase(QueueSizeUnit::BYTES, 5000, 10), TestCase::QUICK);
519  }
#define min(a, b)
Definition: 80211b.c:41
#define max(a, b)
Definition: 80211b.c:42
Queue Disc Test Item.
bool Mark() override
Marks the packet as a substitute for dropping it, such as for Explicit Congestion Notification.
QueueDiscTestItem(const QueueDiscTestItem &)=delete
QueueDiscTestItem & operator=(const QueueDiscTestItem &)=delete
QueueDiscTestItem()=delete
void AddHeader() override
Add the header to the packet.
Traffic Control Flow Control Test Case.
uint32_t m_deviceQueueLength
the queue length of the device
void CheckPacketsInQueueDisc(Ptr< NetDevice > dev, uint16_t nPackets, const std::string msg)
Check if the queue disc stores the expected number of packets.
void DoRun() override
Implementation to actually run this TestCase.
void SendPackets(Ptr< Node > n, uint16_t nPackets)
Instruct a node to send a specified number of packets.
TcFlowControlTestCase(QueueSizeUnit tt, uint32_t deviceQueueLength, uint32_t totalTxPackets)
Constructor.
void CheckDeviceQueueStopped(Ptr< NetDevice > dev, bool value, const std::string msg)
Check if the device queue is in the expected status (stopped or not)
QueueSizeUnit m_type
the test type
void CheckPacketsInDeviceQueue(Ptr< NetDevice > dev, uint16_t nPackets, const std::string msg)
Check if the device queue stores the expected number of packets.
uint32_t m_totalTxPackets
the toal number of packets to transmit
Traffic Control Flow Control Test Suite.
an EUI-48 address
Definition: mac48-address.h:46
holds a vector of ns3::NetDevice pointers
Ptr< NetDevice > Get(uint32_t i) const
Get the Ptr<NetDevice> stored in this container at a given index.
Network device transmission queue interface.
keep track of a set of node pointers.
void Create(uint32_t n)
Create n nodes and append pointers to them to the end of this NodeContainer.
Ptr< Node > Get(uint32_t i) const
Get the Ptr<Node> stored in this container at a given index.
Ptr< NetDevice > GetDevice(uint32_t index) const
Retrieve the index-th NetDevice associated to this node.
Definition: node.cc:152
Ptr< T > GetObject() const
Get a pointer to the requested aggregated Object.
Definition: object.h:471
void AggregateObject(Ptr< Object > other)
Aggregate two Objects together.
Definition: object.cc:259
Hold objects of type Ptr<T>.
Definition: pointer.h:37
Ptr< T > Get() const
Definition: pointer.h:202
QueueDiscItem is the abstract base class for items that are stored in a queue disc.
Definition: queue-item.h:133
Template class for packet Queues.
Definition: queue.h:268
build a set of SimpleNetDevice objects
void SetQueue(std::string type, Ts &&... args)
Each net device must have a queue to pass packets through.
void SetDeviceAttribute(std::string n1, const AttributeValue &v1)
NetDeviceContainer Install(Ptr< Node > node) const
This method creates an ns3::SimpleChannel with the attributes configured by SimpleNetDeviceHelper::Se...
Hold variables of type string.
Definition: string.h:56
encapsulates test code
Definition: test.h:1060
void AddTestCase(TestCase *testCase, TestDuration duration=QUICK)
Add an individual child TestCase to this test suite.
Definition: test.cc:301
A suite of tests to run.
Definition: test.h:1256
@ UNIT
This test suite implements a Unit Test.
Definition: test.h:1265
Build a set of QueueDisc objects.
QueueDiscContainer Install(NetDeviceContainer c)
The Traffic Control layer aims at introducing an equivalent of the Linux Traffic Control infrastructu...
#define NS_ASSERT_MSG(condition, message)
At runtime, in debugging builds, if this condition is not true, the program prints the message to out...
Definition: assert.h:86
void(* DataRate)(DataRate oldValue, DataRate newValue)
TracedValue callback signature for DataRate.
Definition: data-rate.h:327
QueueSizeUnit
Enumeration of the operating modes of queues.
Definition: queue-size.h:44
@ BYTES
Use number of bytes for queue size.
Definition: queue-size.h:46
@ PACKETS
Use number of packets for queue size.
Definition: queue-size.h:45
#define NS_TEST_EXPECT_MSG_EQ(actual, limit, msg)
Test that an actual and expected (limit) value are equal and report if not.
Definition: test.h:251
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition: nstime.h:1326
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition: nstime.h:1338
TcFlowControlTestSuite g_tcFlowControlTestSuite
the test suite
NLOHMANN_BASIC_JSON_TPL_DECLARATION std::string to_string(const NLOHMANN_BASIC_JSON_TPL &j)
user-defined to_string function for JSON values
Definition: json.h:25255
void(* Time)(Time oldValue, Time newValue)
TracedValue callback signature for Time.
Definition: nstime.h:839
Every class exported by the ns3 library is enclosed in the ns3 namespace.
value
Definition: second.py:48