diff --git a/bindings/python/ns__init__.py b/bindings/python/ns__init__.py index 4d57d18fb..6089869a7 100644 --- a/bindings/python/ns__init__.py +++ b/bindings/python/ns__init__.py @@ -319,14 +319,6 @@ def load_modules(): setattr(cppyy.gbl.ns3, "addressFromIpv4Address", cppyy.gbl.addressFromIpv4Address) setattr(cppyy.gbl.ns3, "addressFromInetSocketAddress", cppyy.gbl.addressFromInetSocketAddress) setattr(cppyy.gbl.ns3, "addressFromPacketSocketAddress", cppyy.gbl.addressFromPacketSocketAddress) - cppyy.cppdef( - """using namespace ns3; CommandLine& getCommandLine(std::string filename){ static CommandLine g_cmd = CommandLine(filename); return g_cmd; };""") - setattr(cppyy.gbl.ns3, "getCommandLine", cppyy.gbl.getCommandLine) - cppyy.cppdef( - """using namespace ns3; template Callback ns3::MakeNullCallback(void);""") - cppyy.cppdef( - """using namespace ns3; Callback null_callback(){ return MakeNullCallback(); };""") - setattr(cppyy.gbl.ns3, "null_callback", cppyy.gbl.null_callback) cppyy.cppdef(""" using namespace ns3; diff --git a/doc/manual/source/python.rst b/doc/manual/source/python.rst index eb823efc7..989dc7a9f 100644 --- a/doc/manual/source/python.rst +++ b/doc/manual/source/python.rst @@ -159,29 +159,105 @@ For example, when handling command-line arguments, we could set additional param # Import the ns-3 C++ modules with Cppyy from ns import ns - # ns.cppyy.cppdef compiles the code defined in the block - # The defined types, values and functions are available in ns.cppyy.gbl - ns.cppyy.cppdef(""" - using namespace ns3; - - CommandLine& GetCommandLine(std::string filename, int& nCsma, bool& verbose) - { - static CommandLine cmd = CommandLine(filename); - cmd.AddValue("nCsma", "Number of extra CSMA nodes/devices", nCsma); - cmd.AddValue("verbose", "Tell echo applications to log if true", verbose); - return cmd; - } - """) - # To pass the addresses of the Python variables to c++, we need to use ctypes - from ctypes import c_int, c_bool - nCsma = c_int(3) + from ctypes import c_bool, c_int, c_double, c_char_p, create_string_buffer verbose = c_bool(True) + nCsma = c_int(3) + throughputKbps = c_double(3.1415) + BUFFLEN = 4096 + outputFileBuffer = create_string_buffer(b"default_output_file.xml", BUFFLEN) + outputFile = c_char_p(outputFileBuffer.raw) - # Pass the addresses of Python variables to C++ - cmd = ns.cppyy.gbl.GetCommandLine(__file__, nCsma, verbose) + # Cppyy will transform the ctype types into the appropriate reference or raw pointers + cmd = ns.CommandLine(__file__) + cmd.AddValue("verbose", "Tell echo applications to log if true", verbose) + cmd.AddValue("nCsma", "Number of extra CSMA nodes/devices", nCsma) + cmd.AddValue("throughputKbps", "Throughput of nodes", throughputKbps) + cmd.AddValue("outputFile", "Output file name", outputFile, BUFFLEN) cmd.Parse(sys.argv) + # Printing values of the different ctypes passed as arguments post parsing + print("Verbose:", verbose.value) + print("nCsma:", nCsma.value) + print("throughputKbps:", throughputKbps.value) + print("outputFile:", outputFile.value) + +Note that the variables are passed as references or raw pointers. Reassigning them on the Python side +(e.g. ``verbose = verbose.value``) can result in the Python garbage collector destroying the object +since its only reference has been overwritten, allowing the garbage collector to reclaim that memory space. +The C++ side will then have a dangling reference to the variable, which can be overwritten with +unexpected values, which can be read later, causing ns-3 to behave erratically due to the memory corruption. + +String values are problematic since Python and C++ string lifetimes are handled differently. +To workaround that, we need to use null-terminated C strings (``char*``) to exchange strings between +the bindings and ns-3 module libraries. However, C strings are particularly dangerous, since +overwriting the null-terminator can also result in memory corruption. When passing a C string, remember +to allocate a large buffer and perform bounds checking whenever possible. The CommandLine::AddValue +variant for ``char*`` performs bounds checking and aborts the execution in case the parsed value +does not fit in the buffer. Make sure to pass the complete size of the buffer, including the null terminator. + +There is an example below demonstrating how the memory corruption could happen in case there was +no bounds checking in CommandLine::AddValue variant for ``char*``. + +.. sourcecode:: python + + from ns import ns + from ctypes import c_char_p, c_char, create_string_buffer, byref, cast + + # The following buffer represent the memory contents + # of a program containing two adjacent C strings + # This could be the result of two subsequent variables + # on the stack or dynamically allocated + memoryContents = create_string_buffer(b"SHORT_STRING_CONTENTS\0"+b"DoNotWriteHere_"*5+b"\0") + lenShortString = len(b"SHORT_STRING_CONTENTS\0") + + # In the next lines, we pick pointers to these two C strings + shortStringBuffer = cast(byref(memoryContents, 0), c_char_p) + victimBuffer = cast(byref(memoryContents, lenShortString), c_char_p) + + cmd = ns.core.CommandLine(__file__) + # in the real implementation, the buffer size of 21+1 bytes containing SHORT_STRING_CONTENTS\0 is passed + cmd.AddValue("shortString", "", shortStringBuffer) + + print("Memory contents before the memory corruption") + print("Full Memory contents", memoryContents.raw) + print("shortStringBuffer contents: ", shortStringBuffer.value) + print("victimBuffer contents: ", victimBuffer.value) + + # The following block should print to the terminal. + # Note that the strings are correctly + # identified due to the null terminator (\x00) + # + # Memory contents before the memory corruption + # Full Memory contents b'SHORT_STRING_CONTENTS\x00DoNotWriteHere_DoNotWriteHere_DoNotWriteHere_DoNotWriteHere_DoNotWriteHere_\x00\x00' + # shortStringBuffer size=21, contents: b'SHORT_STRING_CONTENTS' + # victimBuffer size=75, contents: b'DoNotWriteHere_DoNotWriteHere_DoNotWriteHere_DoNotWriteHere_DoNotWriteHere_' + + # Write a very long string to a small buffer of size lenShortString = 22 + cmd.Parse(["python", "--shortString="+("OkToWrite"*lenShortString)[:lenShortString]+"CORRUPTED_"*3]) + + print("\n\nMemory contents after the memory corruption") + print("Full Memory contents", memoryContents.raw) + print("shortStringBuffer contents: ", shortStringBuffer.value) + print("victimBuffer contents: ", victimBuffer.value) + + # The following block should print to the terminal. + # + # Memory contents after the memory corruption + # Full Memory contents b'OkToWriteOkToWriteOkToCORRUPTED_CORRUPTED_CORRUPTED_\x00oNotWriteHere_DoNotWriteHere_DoNotWriteHere_\x00\x00' + # shortStringBuffer size=52, contents: b'OkToWriteOkToWriteOkToCORRUPTED_CORRUPTED_CORRUPTED_' + # victimBuffer size=30, contents: b'CORRUPTED_CORRUPTED_CORRUPTED_' + # + # Note that shortStringBuffer invaded the victimBuffer since the + # string being written was bigger than the shortStringBuffer. + # + # Since no bounds checks were performed, the adjacent memory got + # overwritten and both buffers are now corrupted. + # + # We also have a memory leak of the final block in the memory + # 'oNotWriteHere_DoNotWriteHere_DoNotWriteHere_\x00\x00', caused + # by the null terminator written at the middle of the victimBuffer. + If you find a segmentation violation, be sure to wait for the stacktrace provided by Cppyy and try to find the root cause of the issue. If you have multiple cores, the number of stacktraces will correspond to the number of threads being executed by Cppyy. To limit them, diff --git a/examples/tutorial/second.py b/examples/tutorial/second.py index 3d7f24e18..0eb60e4e5 100644 --- a/examples/tutorial/second.py +++ b/examples/tutorial/second.py @@ -26,37 +26,26 @@ import sys # // point-to-point | | | | # // ================ # // LAN 10.1.2.0 -ns.cppyy.cppdef(""" -using namespace ns3; -CommandLine& GetCommandLine(std::string filename, int& nCsma, bool& verbose) -{ - static CommandLine cmd = CommandLine(filename); - cmd.AddValue("nCsma", "Number of extra CSMA nodes/devices", nCsma); - cmd.AddValue("verbose", "Tell echo applications to log if true", verbose); - return cmd; -} -""") from ctypes import c_int, c_bool nCsma = c_int(3) verbose = c_bool(True) -cmd = ns.cppyy.gbl.GetCommandLine(__file__, nCsma, verbose) +cmd = ns.CommandLine(__file__) +cmd.AddValue("nCsma", "Number of extra CSMA nodes/devices", nCsma) +cmd.AddValue("verbose", "Tell echo applications to log if true", verbose) cmd.Parse(sys.argv) -nCsma = nCsma.value -verbose = verbose.value - -if verbose == "True": +if verbose.value: ns.core.LogComponentEnable("UdpEchoClientApplication", ns.core.LOG_LEVEL_INFO) ns.core.LogComponentEnable("UdpEchoServerApplication", ns.core.LOG_LEVEL_INFO) -nCsma = 1 if nCsma == 0 else nCsma +nCsma.value = 1 if nCsma.value == 0 else nCsma.value p2pNodes = ns.network.NodeContainer() p2pNodes.Create(2) csmaNodes = ns.network.NodeContainer() csmaNodes.Add(p2pNodes.Get(1)) -csmaNodes.Create(nCsma) +csmaNodes.Create(nCsma.value) pointToPoint = ns.point_to_point.PointToPointHelper() pointToPoint.SetDeviceAttribute("DataRate", ns.core.StringValue("5Mbps")) @@ -83,11 +72,11 @@ csmaInterfaces = address.Assign(csmaDevices) echoServer = ns.applications.UdpEchoServerHelper(9) -serverApps = echoServer.Install(csmaNodes.Get(nCsma)) +serverApps = echoServer.Install(csmaNodes.Get(nCsma.value)) serverApps.Start(ns.core.Seconds(1.0)) serverApps.Stop(ns.core.Seconds(10.0)) -echoClient = ns.applications.UdpEchoClientHelper(ns.addressFromIpv4Address(csmaInterfaces.GetAddress(nCsma)), 9) +echoClient = ns.applications.UdpEchoClientHelper(ns.addressFromIpv4Address(csmaInterfaces.GetAddress(nCsma.value)), 9) echoClient.SetAttribute("MaxPackets", ns.core.UintegerValue(1)) echoClient.SetAttribute("Interval", ns.core.TimeValue(ns.core.Seconds (1.0))) echoClient.SetAttribute("PacketSize", ns.core.UintegerValue(1024)) diff --git a/examples/tutorial/third.py b/examples/tutorial/third.py index 622162af6..d24402371 100644 --- a/examples/tutorial/third.py +++ b/examples/tutorial/third.py @@ -30,30 +30,28 @@ import sys # // ================ # // LAN 10.1.2.0 -cmd = ns.getCommandLine(__file__) -nCsma ="3" -verbose = "True" -nWifi = "3" -tracing = "False" +from ctypes import c_bool, c_int +nCsma = c_int(3) +verbose = c_bool(True) +nWifi = c_int(3) +tracing = c_bool(False) -cmd.AddValue("nCsma", "Number of extra CSMA nodes/devices", ns.null_callback(), nCsma) -cmd.AddValue("nWifi", "Number of wifi STA devices", ns.null_callback(), nWifi) -cmd.AddValue("verbose", "Tell echo applications to log if true", ns.null_callback(), verbose) -cmd.AddValue("tracing", "Enable pcap tracing", ns.null_callback(), tracing) +cmd = ns.CommandLine(__file__) +cmd.AddValue("nCsma", "Number of extra CSMA nodes/devices", nCsma) +cmd.AddValue("nWifi", "Number of wifi STA devices", nWifi) +cmd.AddValue("verbose", "Tell echo applications to log if true", verbose) +cmd.AddValue("tracing", "Enable pcap tracing", tracing) cmd.Parse(sys.argv) -nCsma = int(nCsma) -nWifi = int(nWifi) - # The underlying restriction of 18 is due to the grid position # allocator's configuration; the grid layout will exceed the # bounding box if more than 18 nodes are provided. -if nWifi > 18: +if nWifi.value > 18: print("nWifi should be 18 or less; otherwise grid layout exceeds the bounding box") sys.exit(1) -if verbose == "True": +if verbose.value: ns.core.LogComponentEnable("UdpEchoClientApplication", ns.core.LOG_LEVEL_INFO) ns.core.LogComponentEnable("UdpEchoServerApplication", ns.core.LOG_LEVEL_INFO) @@ -68,7 +66,7 @@ p2pDevices = pointToPoint.Install(p2pNodes) csmaNodes = ns.network.NodeContainer() csmaNodes.Add(p2pNodes.Get(1)) -csmaNodes.Create(nCsma) +csmaNodes.Create(nCsma.value) csma = ns.csma.CsmaHelper() csma.SetChannelAttribute("DataRate", ns.core.StringValue("100Mbps")) @@ -77,7 +75,7 @@ csma.SetChannelAttribute("Delay", ns.core.TimeValue(ns.core.NanoSeconds(6560))) csmaDevices = csma.Install(csmaNodes) wifiStaNodes = ns.network.NodeContainer() -wifiStaNodes.Create(nWifi) +wifiStaNodes.Create(nWifi.value) wifiApNode = p2pNodes.Get(0) channel = ns.wifi.YansWifiChannelHelper.Default() @@ -124,16 +122,16 @@ address.Assign(apDevices) echoServer = ns.applications.UdpEchoServerHelper(9) -serverApps = echoServer.Install(csmaNodes.Get(nCsma)) +serverApps = echoServer.Install(csmaNodes.Get(nCsma.value)) serverApps.Start(ns.core.Seconds(1.0)) serverApps.Stop(ns.core.Seconds(10.0)) -echoClient = ns.applications.UdpEchoClientHelper(ns.addressFromIpv4Address(csmaInterfaces.GetAddress(nCsma)), 9) +echoClient = ns.applications.UdpEchoClientHelper(ns.addressFromIpv4Address(csmaInterfaces.GetAddress(nCsma.value)), 9) echoClient.SetAttribute("MaxPackets", ns.core.UintegerValue(1)) echoClient.SetAttribute("Interval", ns.core.TimeValue(ns.core.Seconds (1.0))) echoClient.SetAttribute("PacketSize", ns.core.UintegerValue(1024)) -clientApps = echoClient.Install(wifiStaNodes.Get (nWifi - 1)) +clientApps = echoClient.Install(wifiStaNodes.Get (nWifi.value - 1)) clientApps.Start(ns.core.Seconds(2.0)) clientApps.Stop(ns.core.Seconds(10.0)) @@ -141,7 +139,7 @@ ns.internet.Ipv4GlobalRoutingHelper.PopulateRoutingTables() ns.core.Simulator.Stop(ns.core.Seconds(10.0)) -if tracing == "True": +if tracing.value: phy.SetPcapDataLinkType(phy.DLT_IEEE802_11_RADIO) pointToPoint.EnablePcapAll ("third") phy.EnablePcap ("third", apDevices.Get (0)) diff --git a/examples/wireless/mixed-wired-wireless.py b/examples/wireless/mixed-wired-wireless.py index 71b3d452e..c17f39d1a 100644 --- a/examples/wireless/mixed-wired-wireless.py +++ b/examples/wireless/mixed-wired-wireless.py @@ -68,12 +68,12 @@ def main(argv): # First, we initialize a few local variables that control some # simulation parameters. # - - cmd = ns.getCommandLine(__file__) - cmd.backboneNodes = "10" - cmd.infraNodes = "2" - cmd.lanNodes = "2" - cmd.stopTime = "20" + from ctypes import c_int, c_double + backboneNodes = c_int(10) + infraNodes = c_int(2) + lanNodes = c_int(2) + stopTime = c_double(20) + cmd = ns.CommandLine(__file__) # # Simulation defaults are typically set next, before command line @@ -88,10 +88,10 @@ def main(argv): # "--backboneNodes=20" # - cmd.AddValue("backboneNodes", "number of backbone nodes", ns.null_callback(), cmd.backboneNodes) - cmd.AddValue("infraNodes", "number of leaf nodes", ns.null_callback(), cmd.infraNodes) - cmd.AddValue("lanNodes", "number of LAN nodes", ns.null_callback(), cmd.lanNodes) - cmd.AddValue("stopTime", "simulation stop time(seconds)", ns.null_callback(), cmd.stopTime) + cmd.AddValue("backboneNodes", "number of backbone nodes", backboneNodes) + cmd.AddValue("infraNodes", "number of leaf nodes", infraNodes) + cmd.AddValue("lanNodes", "number of LAN nodes", lanNodes) + cmd.AddValue("stopTime", "simulation stop time(seconds)", stopTime) # # The system global variables and the local values added to the argument @@ -99,12 +99,7 @@ def main(argv): # cmd.Parse(argv) - backboneNodes = int(cmd.backboneNodes) - infraNodes = int(cmd.infraNodes) - lanNodes = int(cmd.lanNodes) - stopTime = int(cmd.stopTime) - - if (stopTime < 10): + if (stopTime.value < 10): print ("Use a simulation stop time >= 10 seconds") exit(1) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # / @@ -118,7 +113,7 @@ def main(argv): # Later we'll create the rest of the nodes we'll need. # backbone = ns.network.NodeContainer() - backbone.Create(backboneNodes) + backbone.Create(backboneNodes.value) # # Create the backbone wifi net devices and install them into the nodes in # our container @@ -179,7 +174,7 @@ def main(argv): # the "172.16 address space ipAddrs.SetBase(ns.network.Ipv4Address("172.16.0.0"), ns.network.Ipv4Mask("255.255.255.0")) - for i in range(backboneNodes): + for i in range(backboneNodes.value): print ("Configuring local area network for backbone node ", i) # # Create a container to manage the nodes of the LAN. We need @@ -187,7 +182,7 @@ def main(argv): # with all of the nodes including new and existing nodes # newLanNodes = ns.network.NodeContainer() - newLanNodes.Create(lanNodes - 1) + newLanNodes.Create(lanNodes.value - 1) # Now, create the container with all nodes on this link lan = ns.network.NodeContainer(ns.network.NodeContainer(backbone.Get(i)), newLanNodes) # @@ -236,7 +231,7 @@ def main(argv): # the "10.0" address space ipAddrs.SetBase(ns.network.Ipv4Address("10.0.0.0"), ns.network.Ipv4Mask("255.255.255.0")) tempRef = [] # list of references to be held to prevent garbage collection - for i in range(backboneNodes): + for i in range(backboneNodes.value): print ("Configuring wireless network for backbone node ", i) # # Create a container to manage the nodes of the LAN. We need @@ -244,7 +239,7 @@ def main(argv): # with all of the nodes including new and existing nodes # stas = ns.network.NodeContainer() - stas.Create(infraNodes - 1) + stas.Create(infraNodes.value - 1) # Now, create the container with all nodes on this link infra = ns.network.NodeContainer(ns.network.NodeContainer(backbone.Get(i)), stas) # @@ -313,8 +308,8 @@ def main(argv): print ("Create Applications.") port = 9 # Discard port(RFC 863) - appSource = ns.network.NodeList.GetNode(backboneNodes) - lastNodeIndex = backboneNodes + backboneNodes*(lanNodes - 1) + backboneNodes*(infraNodes - 1) - 1 + appSource = ns.network.NodeList.GetNode(backboneNodes.value) + lastNodeIndex = backboneNodes.value + backboneNodes.value*(lanNodes.value - 1) + backboneNodes.value*(infraNodes.value - 1) - 1 appSink = ns.network.NodeList.GetNode(lastNodeIndex) ns.cppyy.cppdef(""" @@ -329,7 +324,7 @@ def main(argv): onoff = ns.applications.OnOffHelper("ns3::UdpSocketFactory", genericAddress) apps = onoff.Install(ns.network.NodeContainer(appSource)) apps.Start(ns.core.Seconds(3)) - apps.Stop(ns.core.Seconds(stopTime - 1)) + apps.Stop(ns.core.Seconds(stopTime.value - 1)) # Create a packet sink to receive these packets sink = ns.applications.PacketSinkHelper("ns3::UdpSocketFactory", @@ -374,7 +369,7 @@ def main(argv): # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # print ("Run Simulation.") - ns.core.Simulator.Stop(ns.core.Seconds(stopTime)) + ns.core.Simulator.Stop(ns.core.Seconds(stopTime.value)) ns.core.Simulator.Run() ns.core.Simulator.Destroy() diff --git a/src/core/examples/sample-simulator.py b/src/core/examples/sample-simulator.py index 551e87c92..58f2650d2 100755 --- a/src/core/examples/sample-simulator.py +++ b/src/core/examples/sample-simulator.py @@ -103,14 +103,14 @@ ns.cppyy.cppdef(""" """) -def main(dummy_argv): - cmd = ns.getCommandLine(__file__) - cmd.Parse(dummy_argv) +def main(argv): + cmd = ns.CommandLine(__file__) + cmd.Parse(argv) model = ns.cppyy.gbl.MyModel() v = ns.CreateObject("UniformRandomVariable") - v.SetAttribute("Min", ns.core.DoubleValue (10)) - v.SetAttribute("Max", ns.core.DoubleValue (20)) + v.SetAttribute("Min", ns.core.DoubleValue(10)) + v.SetAttribute("Max", ns.core.DoubleValue(20)) ev = ns.cppyy.gbl.ExampleFunctionEvent(model) ns.core.Simulator.Schedule(ns.core.Seconds(10.0), ev) diff --git a/src/flow-monitor/examples/wifi-olsr-flowmon.py b/src/flow-monitor/examples/wifi-olsr-flowmon.py index a806066b2..23b0089cd 100644 --- a/src/flow-monitor/examples/wifi-olsr-flowmon.py +++ b/src/flow-monitor/examples/wifi-olsr-flowmon.py @@ -24,30 +24,21 @@ from ns import ns DISTANCE = 20 # (m) NUM_NODES_SIDE = 3 -ns.cppyy.cppdef(""" -using namespace ns3; - -CommandLine& GetCommandLine(std::string filename, int& NumNodesSide, bool& Plot, std::string Results) -{ - static CommandLine cmd = CommandLine(filename); - cmd.AddValue("NumNodesSide", "Grid side number of nodes (total number of nodes will be this number squared)", NumNodesSide); - cmd.AddValue("Results", "Write XML results to file", Results); - cmd.AddValue("Plot", "Plot the results using the matplotlib python module", Plot); - return cmd; -} -""") def main(argv): - from ctypes import c_int, c_bool + from ctypes import c_int, c_bool, c_char_p, create_string_buffer NumNodesSide = c_int(2) Plot = c_bool(False) - Results = "output.xml" - cmd = ns.cppyy.gbl.GetCommandLine(__file__, NumNodesSide, Plot, Results) - cmd.Parse(argv) + BUFFLEN = 4096 + ResultsBuffer = create_string_buffer(b"output.xml", BUFFLEN) + Results = c_char_p(ResultsBuffer.raw) - Plot = Plot.value - NumNodesSide = NumNodesSide.value + cmd = ns.CommandLine(__file__) + cmd.AddValue("NumNodesSide", "Grid side number of nodes (total number of nodes will be this number squared)", NumNodesSide) + cmd.AddValue("Results", "Write XML results to file", Results, BUFFLEN) + cmd.AddValue("Plot", "Plot the results using the matplotlib python module", Plot) + cmd.Parse(argv) wifi = ns.CreateObject("WifiHelper") wifiMac = ns.CreateObject("WifiMacHelper") @@ -80,10 +71,10 @@ def main(argv): addresses = [] nodes = [] - if NumNodesSide is None: + if NumNodesSide.value == 2: num_nodes_side = NUM_NODES_SIDE else: - num_nodes_side = NumNodesSide + num_nodes_side = NumNodesSide.value nodes = ns.NodeContainer(num_nodes_side*num_nodes_side) accumulator = 0 @@ -159,7 +150,7 @@ def main(argv): monitor.CheckForLostPackets() classifier = flowmon_helper.GetClassifier() - if Results is None: + if Results.value != b"output.xml": for flow_id, flow_stats in monitor.GetFlowStats(): t = classifier.FindFlow(flow_id) proto = {6: 'TCP', 17: 'UDP'} [t.protocol] @@ -167,10 +158,11 @@ def main(argv): (flow_id, proto, t.sourceAddress, t.sourcePort, t.destinationAddress, t.destinationPort)) print_stats(sys.stdout, flow_stats) else: - print (monitor.SerializeToXmlFile(Results, True, True)) + res = monitor.SerializeToXmlFile(Results.value.decode("utf-8"), True, True) + print (res) - if Plot: + if Plot.value: import pylab delays = [] for flow_id, flow_stats in monitor.GetFlowStats(): diff --git a/utils/python-unit-tests.py b/utils/python-unit-tests.py index a049db191..29bd30cbb 100644 --- a/utils/python-unit-tests.py +++ b/utils/python-unit-tests.py @@ -277,30 +277,39 @@ class TestSimulator(unittest.TestCase): ok, typeId1 = ns.LookupByNameFailSafe("ns3::__InvalidTypeName__") self.assertFalse(ok) -# def testCommandLine(self): -# """! Test command line -# @param self this object -# @return none -# """ -# cmd = ns.core.CommandLine(__file__) -# cmd.AddValue("Test1", "this is a test option") -# cmd.AddValue("Test2", "this is a test option") -# cmd.AddValue("Test3", "this is a test option", variable="test_xxx") -# cmd.Test1 = None -# cmd.Test2 = None -# cmd.test_xxx = None -# class Foo: -# pass -# foo = Foo() -# foo.test_foo = None -# cmd.AddValue("Test4", "this is a test option", variable="test_foo", namespace=foo) -# -# cmd.Parse(["python", "--Test1=value1", "--Test2=value2", "--Test3=123", "--Test4=xpto"]) -# -# self.assertEqual(cmd.Test1, "value1") -# self.assertEqual(cmd.Test2, "value2") -# self.assertEqual(cmd.test_xxx, "123") -# self.assertEqual(foo.test_foo, "xpto") + def testCommandLine(self): + """! Test command line + @param self this object + @return none + """ + from ctypes import c_bool, c_int, c_double, c_char_p, create_string_buffer + + test1 = c_bool(True) + test2 = c_int(42) + test3 = c_double(3.1415) + BUFFLEN = 40 + test4Buffer = create_string_buffer(b"this is a test option", BUFFLEN) + test4 = c_char_p(test4Buffer.raw) + + cmd = ns.core.CommandLine(__file__) + cmd.AddValue("Test1", "this is a test option", test1) + cmd.AddValue("Test2", "this is a test option", test2) + cmd.AddValue("Test3", "this is a test option", test3) + cmd.AddValue("Test4", "this is a test option", test4, BUFFLEN) + + cmd.Parse(["python"]) + self.assertEqual(test1.value, True) + self.assertEqual(test2.value, 42) + self.assertEqual(test3.value, 3.1415) + self.assertEqual(test4.value, b"this is a test option") + + cmd.Parse(["python", "--Test1=false", "--Test2=0", "--Test3=0.0"]) + self.assertEqual(test1.value, False) + self.assertEqual(test2.value, 0) + self.assertEqual(test3.value, 0.0) + + cmd.Parse(["python", "--Test4=new_string"]) + self.assertEqual(test4.value, b"new_string") def testSubclass(self): """! Test subclass