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