A Discrete-Event Network Simulator
API
python-unit-tests.py
Go to the documentation of this file.
1 #! /usr/bin/env python3
2 
3 # Copyright (C) 2008-2011 INESC Porto
4 
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
9 
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 
19 # Author: Gustavo J. A. M. Carneiro <gjc@inescporto.pt>
20 
21 import unittest
22 
23 try:
24  from ns import ns
25 except ModuleNotFoundError:
26  raise SystemExit(
27  "Error: ns3 Python module not found;"
28  " Python bindings may not be enabled"
29  " or your PYTHONPATH might not be properly configured"
30  )
31 import sys
32 
33 UINT32_MAX = 0xFFFFFFFF
34 
35 
36 
37 class TestSimulator(unittest.TestCase):
38 
46 
47  def testScheduleNow(self):
48  """! Test schedule now
49  @param self this object
50  @return None
51  """
52 
53  def callback(args: ns.cppyy.gbl.std.vector) -> None:
54  """! Callback function
55  @param args arguments
56  @return None
57  """
58  self._args_received_args_received = list(map(lambda x: x.decode("utf-8"), args))
59  self._cb_time_cb_time = ns.Simulator.Now()
60 
61  ns.Simulator.Destroy()
62  self._args_received_args_received = None
63  self._cb_time_cb_time = None
64  ns.cppyy.cppdef(
65  """
66  EventImpl* pythonMakeEvent(void (*f)(std::vector<std::string>), std::vector<std::string> l)
67  {
68  return MakeEvent(f, l);
69  }
70  """
71  )
72  event = ns.cppyy.gbl.pythonMakeEvent(callback, sys.argv)
73  ns.Simulator.ScheduleNow(event)
74  ns.Simulator.Run()
75  self.assertListEqual(self._args_received_args_received, sys.argv)
76  self.assertEqual(self._cb_time_cb_time.GetSeconds(), 0.0)
77 
78  def testSchedule(self):
79  """! Test schedule
80  @param self this object
81  @return None
82  """
83 
84  def callback(args: ns.cppyy.gbl.std.vector):
85  """! Callback function
86  @param args arguments
87  @return None
88  """
89  self._args_received_args_received = list(map(lambda x: x.decode("utf-8"), args))
90  self._cb_time_cb_time = ns.Simulator.Now()
91 
92  ns.Simulator.Destroy()
93  self._args_received_args_received = None
94  self._cb_time_cb_time = None
95  ns.cppyy.cppdef(
96  """
97  EventImpl* pythonMakeEvent2(void (*f)(std::vector<std::string>), std::vector<std::string> l)
98  {
99  return MakeEvent(f, l);
100  }
101  """
102  )
103  event = ns.cppyy.gbl.pythonMakeEvent2(callback, sys.argv)
104  ns.Simulator.Schedule(ns.Seconds(123), event)
105  ns.Simulator.Run()
106  self.assertListEqual(self._args_received_args_received, sys.argv)
107  self.assertEqual(self._cb_time_cb_time.GetSeconds(), 123.0)
108 
110  """! Test schedule destroy
111  @param self this object
112  @return None
113  """
114 
115  def callback(args: ns.cppyy.gbl.std.vector):
116  """! Callback function
117  @param args
118  @return None
119  """
120  self._args_received_args_received = list(map(lambda x: x.decode("utf-8"), args))
121  self._cb_time_cb_time = ns.Simulator.Now()
122 
123  ns.Simulator.Destroy()
124  self._args_received_args_received = None
125  self._cb_time_cb_time = None
126  ns.cppyy.cppdef("void null(){ return; }")
127  ns.Simulator.Schedule(ns.Seconds(123), ns.cppyy.gbl.null)
128  ns.cppyy.cppdef(
129  """
130  EventImpl* pythonMakeEvent3(void (*f)(std::vector<std::string>), std::vector<std::string> l)
131  {
132  return MakeEvent(f, l);
133  }
134  """
135  )
136  event = ns.cppyy.gbl.pythonMakeEvent3(callback, sys.argv)
137  ns.Simulator.ScheduleDestroy(event)
138  ns.Simulator.Run()
139  ns.Simulator.Destroy()
140  self.assertListEqual(self._args_received_args_received, sys.argv)
141  self.assertEqual(self._cb_time_cb_time.GetSeconds(), 123.0)
142 
144  """! Test schedule with context
145  @param self this object
146  @return None
147  """
148 
149  def callback(context, args: ns.cppyy.gbl.std.vector):
150  """! Callback
151  @param context the context
152  @param args the arguments
153  @return None
154  """
155  self._context_received_context_received = context
156  self._args_received_args_received = list(map(lambda x: x.decode("utf-8"), args))
157  self._cb_time_cb_time = ns.Simulator.Now()
158 
159  ns.Simulator.Destroy()
160  self._args_received_args_received = None
161  self._cb_time_cb_time = None
162  self._context_received_context_received = None
163  ns.cppyy.cppdef(
164  """
165  EventImpl* pythonMakeEvent4(void (*f)(uint32_t, std::vector<std::string>), uint32_t context, std::vector<std::string> l)
166  {
167  return MakeEvent(f, context, l);
168  }
169  """
170  )
171  event = ns.cppyy.gbl.pythonMakeEvent4(callback, 54321, sys.argv)
172  ns.Simulator.ScheduleWithContext(54321, ns.Seconds(123), event)
173  ns.Simulator.Run()
174  self.assertEqual(self._context_received_context_received, 54321)
175  self.assertListEqual(self._args_received_args_received, sys.argv)
176  self.assertEqual(self._cb_time_cb_time.GetSeconds(), 123.0)
177 
179  """! Test time comparison
180  @param self this object
181  @return None
182  """
183  self.assertTrue(ns.Seconds(123) == ns.Seconds(123))
184  self.assertTrue(ns.Seconds(123) >= ns.Seconds(123))
185  self.assertTrue(ns.Seconds(123) <= ns.Seconds(123))
186  self.assertTrue(ns.Seconds(124) > ns.Seconds(123))
187  self.assertTrue(ns.Seconds(123) < ns.Seconds(124))
188 
190  """! Test numeric operations
191  @param self this object
192  @return None
193  """
194  self.assertEqual(ns.Seconds(10) + ns.Seconds(5), ns.Seconds(15))
195  self.assertEqual(ns.Seconds(10) - ns.Seconds(5), ns.Seconds(5))
196 
197  v1 = ns.int64x64_t(5.0) * ns.int64x64_t(10)
198  self.assertEqual(v1, ns.int64x64_t(50))
199 
200  def testConfig(self):
201  """! Test configuration
202  @param self this object
203  @return None
204  """
205  ns.Config.SetDefault("ns3::OnOffApplication::PacketSize", ns.core.UintegerValue(123))
206  # hm.. no Config.Get?
207 
208  def testSocket(self):
209  """! Test socket
210  @param self
211  @return None
212  """
213  nc = ns.NodeContainer(1)
214  node = nc.Get(0)
215  internet = ns.CreateObject("InternetStackHelper")
216  internet.Install(node)
217  self._received_packet_received_packet = None
218 
219  def python_rx_callback(socket) -> None:
220  self._received_packet_received_packet = socket.Recv(maxSize=UINT32_MAX, flags=0)
221 
222  ns.cppyy.cppdef(
223  """
224  Callback<void,ns3::Ptr<ns3::Socket> > make_rx_callback_test_socket(void(*func)(Ptr<Socket>))
225  {
226  return MakeCallback(func);
227  }
228  """
229  )
230 
231  sink = ns.network.Socket.CreateSocket(
232  node, ns.core.TypeId.LookupByName("ns3::UdpSocketFactory")
233  )
234  sink.Bind(ns.network.InetSocketAddress(ns.network.Ipv4Address.GetAny(), 80).ConvertTo())
235  sink.SetRecvCallback(ns.cppyy.gbl.make_rx_callback_test_socket(python_rx_callback))
236 
237  source = ns.network.Socket.CreateSocket(
238  node, ns.core.TypeId.LookupByName("ns3::UdpSocketFactory")
239  )
240  source.SendTo(
241  ns.network.Packet(19),
242  0,
243  ns.network.InetSocketAddress(ns.network.Ipv4Address("127.0.0.1"), 80).ConvertTo(),
244  )
245 
246  ns.Simulator.Run()
247  self.assertTrue(self._received_packet_received_packet is not None)
248  self.assertEqual(self._received_packet_received_packet.GetSize(), 19)
249 
250  # Delete Ptr<>'s on the python side to let C++ clean them
251  del internet
252 
253  def testAttributes(self):
254  """! Test attributes function
255  @param self this object
256  @return None
257  """
258  # Templated class DropTailQueue<Packet> in C++
259  queue = ns.CreateObject("DropTailQueue<Packet>")
260  queueSizeValue = ns.network.QueueSizeValue(ns.network.QueueSize("500p"))
261  queue.SetAttribute("MaxSize", queueSizeValue)
262 
263  limit = ns.network.QueueSizeValue()
264  queue.GetAttribute("MaxSize", limit)
265  self.assertEqual(limit.Get(), ns.network.QueueSize("500p"))
266 
267 
268  mobility = ns.CreateObject("RandomWaypointMobilityModel")
269  ptr = ns.CreateObject("PointerValue")
270  mobility.GetAttribute("PositionAllocator", ptr)
271  self.assertEqual(ptr.GetObject(), ns.core.Ptr["Object"](ns.cppyy.nullptr))
272 
273  pos = ns.mobility.ListPositionAllocator()
274  ptr.SetObject(pos)
275  mobility.SetAttribute("PositionAllocator", ptr)
276 
277  ptr2 = ns.CreateObject("PointerValue")
278  mobility.GetAttribute("PositionAllocator", ptr2)
279  self.assertNotEqual(ptr.GetObject(), ns.core.Ptr["Object"](ns.cppyy.nullptr))
280 
281  # Delete Ptr<>'s on the python side to let C++ clean them
282  del queue, mobility, ptr, ptr2
283 
284  def testIdentity(self):
285  """! Test identify
286  @param self this object
287  @return None
288  """
289  csma = ns.CreateObject("CsmaNetDevice")
290  channel = ns.CreateObject("CsmaChannel")
291  csma.Attach(channel)
292 
293  c1 = csma.GetChannel()
294  c2 = csma.GetChannel()
295 
296  self.assertEqual(c1, c2)
297 
298  # Delete Ptr<>'s on the python side to let C++ clean them
299  del csma, channel
300 
301  def testTypeId(self):
302  """! Test type ID
303  @param self this object
304  @return None
305  """
306  ok, typeId1 = ns.LookupByNameFailSafe("ns3::UdpSocketFactory")
307  self.assertTrue(ok)
308  self.assertEqual(typeId1.GetName(), "ns3::UdpSocketFactory")
309 
310  ok, typeId1 = ns.LookupByNameFailSafe("ns3::__InvalidTypeName__")
311  self.assertFalse(ok)
312 
313  def testCommandLine(self):
314  """! Test command line
315  @param self this object
316  @return None
317  """
318  from ctypes import c_bool, c_char_p, c_double, c_int, create_string_buffer
319 
320  test1 = c_bool(True)
321  test2 = c_int(42)
322  test3 = c_double(3.1415)
323  BUFFLEN = 40 # noqa
324  test4Buffer = create_string_buffer(b"this is a test option", BUFFLEN)
325  test4 = c_char_p(test4Buffer.raw)
326 
327  cmd = ns.core.CommandLine(__file__)
328  cmd.AddValue("Test1", "this is a test option", test1)
329  cmd.AddValue("Test2", "this is a test option", test2)
330  cmd.AddValue["double"]("Test3", "this is a test option", test3)
331  cmd.AddValue("Test4", "this is a test option", test4, BUFFLEN)
332 
333  cmd.Parse(["python"])
334  self.assertEqual(test1.value, True)
335  self.assertEqual(test2.value, 42)
336  self.assertEqual(test3.value, 3.1415)
337  self.assertEqual(test4.value, b"this is a test option")
338 
339  cmd.Parse(["python", "--Test1=false", "--Test2=0", "--Test3=0.0"])
340  self.assertEqual(test1.value, False)
341  self.assertEqual(test2.value, 0)
342  self.assertEqual(test3.value, 0.0)
343 
344  cmd.Parse(["python", "--Test4=new_string"])
345  self.assertEqual(test4.value, b"new_string")
346 
347  def testSubclass(self):
348  """! Test subclass
349  @param self this object
350  @return None
351  """
352 
353 
354  class MyNode(ns.network.Node):
355  def GetLocalTime(self) -> ns.Time:
356  return ns.Seconds(10)
357 
358  node = MyNode()
359  forced_local_time = node.GetLocalTime()
360  self.assertEqual(forced_local_time, ns.Seconds(10))
361  del node
362 
364  """! Test python-based application
365  @param self this object
366  @return None
367  """
368  ns.Simulator.Destroy()
369 
370  nodes = ns.network.NodeContainer()
371  nodes.Create(2)
372 
373  pointToPoint = ns.point_to_point.PointToPointHelper()
374  pointToPoint.SetDeviceAttribute("DataRate", ns.core.StringValue("5Mbps"))
375  pointToPoint.SetChannelAttribute("Delay", ns.core.StringValue("2ms"))
376 
377  devices = pointToPoint.Install(nodes)
378 
379  stack = ns.internet.InternetStackHelper()
380  stack.Install(nodes)
381 
382  address = ns.internet.Ipv4AddressHelper()
383  address.SetBase(ns.network.Ipv4Address("10.1.1.0"), ns.network.Ipv4Mask("255.255.255.0"))
384 
385  interfaces = address.Assign(devices)
386 
387  ns.cppyy.cppdef(
388  """
389  namespace ns3
390  {
391  Callback<void,Ptr<Socket> > make_rx_callback(void(*func)(Ptr<Socket>))
392  {
393  return MakeCallback(func);
394  }
395  EventImpl* pythonMakeEventSend(void (*f)(Ptr<Socket>, Ptr<Packet>, Address&), Ptr<Socket> socket, Ptr<Packet> packet, Address address)
396  {
397  return MakeEvent(f, socket, packet, address);
398  }
399  }
400  """
401  )
402 
403 
404  class EchoServer(ns.applications.Application):
405  LOGGING = False
406  ECHO_PORT = 1234
407  socketToInstanceDict = {}
408 
409  def __init__(self, node: ns.Node, port=ECHO_PORT):
410  """! Constructor needs to call first the constructor to Application (super class)
411  @param self this object
412  @param node node where this application will be executed
413  @param port port to listen
414  return None
415  """
416  super().__init__()
417 
418  self.__python_owns____python_owns__ = False # Let C++ destroy this on Simulator::Destroy
419 
420  self.portport = port
421 
422  self.m_socketm_socket = ns.network.Socket.CreateSocket(
423  node, ns.core.TypeId.LookupByName("ns3::UdpSocketFactory")
424  )
425  self.m_socketm_socket.Bind(
426  ns.network.InetSocketAddress(
427  ns.network.Ipv4Address.GetAny(), self.portport
428  ).ConvertTo()
429  )
430  self.m_socketm_socket.SetRecvCallback(ns.make_rx_callback(EchoServer._Receive))
431  EchoServer.socketToInstanceDict[self.m_socketm_socket] = self
432 
433  def __del__(self):
434  """! Destructor
435  @param self this object
436  return None
437  """
438  del EchoServer.socketToInstanceDict[self.m_socketm_socket]
439 
440  def Send(self, packet: ns.Packet, address: ns.Address) -> None:
441  """! Function to send a packet to an address
442  @param self this object
443  @param packet packet to send
444  @param address destination address
445  return None
446  """
447  self.m_socketm_socket.SendTo(packet, 0, address)
448  if EchoServer.LOGGING:
449  inetAddress = ns.InetSocketAddress.ConvertFrom(address)
450  print(
451  "At time +{s}s server sent {b} bytes from {ip} port {port}".format(
452  s=ns.Simulator.Now().GetSeconds(),
453  b=packet.__deref__().GetSize(),
454  ip=inetAddress.GetIpv4(),
455  port=inetAddress.GetPort(),
456  ),
457  file=sys.stderr,
458  flush=True,
459  )
460 
461  def Receive(self):
462  """! Function to receive a packet from an address
463  @param self this object
464  @return None
465  """
466  address = ns.Address()
467  packet = self.m_socketm_socket.RecvFrom(address)
468  if EchoServer.LOGGING:
469  inetAddress = ns.InetSocketAddress.ConvertFrom(address)
470  print(
471  "At time +{s}s server received {b} bytes from {ip} port {port}".format(
472  s=ns.Simulator.Now().GetSeconds(),
473  b=packet.__deref__().GetSize(),
474  ip=inetAddress.GetIpv4(),
475  port=inetAddress.GetPort(),
476  ),
477  file=sys.stderr,
478  flush=True,
479  )
480  event = ns.pythonMakeEventSend(EchoServer._Send, self.m_socketm_socket, packet, address)
481  ns.Simulator.Schedule(ns.Seconds(1), event)
482 
483  @staticmethod
484  def _Send(socket: ns.Socket, packet: ns.Packet, address: ns.Address):
485  """! Static send function, which matches the output socket
486  to the EchoServer instance to call the instance Send function
487  @param socket socket from the instance that should send the packet
488  @param packet packet to send
489  @param address destination address
490  return None
491  """
492  instance = EchoServer.socketToInstanceDict[socket]
493  instance.Send(packet, address)
494 
495  @staticmethod
496  def _Receive(socket: ns.Socket) -> None:
497  """! Static receive function, which matches the input socket
498  to the EchoServer instance to call the instance Receive function
499  @param socket socket from the instance that should receive the packet
500  return None
501  """
502  instance = EchoServer.socketToInstanceDict[socket]
503  instance.Receive()
504 
505  echoServer = EchoServer(nodes.Get(1))
506  nodes.Get(1).AddApplication(echoServer)
507 
508  serverApps = ns.ApplicationContainer()
509  serverApps.Add(echoServer)
510  serverApps.Start(ns.core.Seconds(1.0))
511  serverApps.Stop(ns.core.Seconds(10.0))
512 
513  address = interfaces.GetAddress(1).ConvertTo()
514  echoClient = ns.applications.UdpEchoClientHelper(address, EchoServer.ECHO_PORT)
515  echoClient.SetAttribute("MaxPackets", ns.core.UintegerValue(10))
516  echoClient.SetAttribute("Interval", ns.core.TimeValue(ns.core.Seconds(1.0)))
517  echoClient.SetAttribute("PacketSize", ns.core.UintegerValue(101))
518 
519  clientApps = echoClient.Install(nodes.Get(0))
520  clientApps.Start(ns.core.Seconds(2.0))
521  clientApps.Stop(ns.core.Seconds(10.0))
522 
523  ns.Simulator.Run()
524  ns.Simulator.Destroy()
525 
526 
527 if __name__ == "__main__":
528  unittest.main(verbosity=1, failfast=True)
def testScheduleDestroy(self)
Test schedule destroy.
port
Listen port for the server.
def testCommandLine(self)
Test command line.
def testTimeNumericOperations(self)
Test numeric operations.
def testEchoServerApplication(self)
Test python-based application.
__python_owns__
EchoServer application class.
def testScheduleNow(self)
Test schedule now.
def testTypeId(self)
Test type ID.
def testSubclass(self)
Test subclass.
def testSchedule(self)
Test schedule.
def testScheduleWithContext(self)
Test schedule with context.
def testAttributes(self)
Test attributes function.
def testSocket(self)
Test socket.
def testTimeComparison(self)
Test time comparison.
def testConfig(self)
Test configuration.
m_socket
Socket used by the server to listen to port.
def testIdentity(self)
Test identify.
static void Send(Ptr< NetDevice > dev, int level, std::string emuMode)
Definition: fd-emu-send.cc:54
uint32_t GetSize(Ptr< const Packet > packet, const WifiMacHeader *hdr, bool isAmpdu)
Return the total size of the packet after WifiMacHeader and FCS trailer have been added.
Definition: wifi-utils.cc:132
#define list