bindings, docs: pass strings via char* to CommandLine.AddValue

And supplement memory-management issues section
This commit is contained in:
Gabriel Ferreira
2022-10-14 22:13:55 -03:00
parent bf7407026a
commit 12373157a4
8 changed files with 193 additions and 142 deletions

View File

@@ -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<bool, std::string> ns3::MakeNullCallback<bool, std::string>(void);""")
cppyy.cppdef(
"""using namespace ns3; Callback<bool, std::string> null_callback(){ return MakeNullCallback<bool, std::string>(); };""")
setattr(cppyy.gbl.ns3, "null_callback", cppyy.gbl.null_callback)
cppyy.cppdef("""
using namespace ns3;

View File

@@ -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,

View File

@@ -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))

View File

@@ -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))

View File

@@ -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()

View File

@@ -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)

View File

@@ -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():

View File

@@ -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