git://git.onelab.eu
/
nepi.git
/ commitdiff
commit
grep
author
committer
pickaxe
?
search:
re
summary
|
shortlog
|
log
|
commit
| commitdiff |
tree
raw
|
patch
|
inline
| side by side (from parent 1:
a1427c7
)
Fixing ns-3 DCE tests
author
Alina Quereilhac
<alina.quereilhac@inria.fr>
Mon, 30 Jun 2014 10:38:17 +0000
(12:38 +0200)
committer
Alina Quereilhac
<alina.quereilhac@inria.fr>
Mon, 30 Jun 2014 10:38:17 +0000
(12:38 +0200)
src/nepi/execution/resource.py
patch
|
blob
|
history
src/nepi/resources/linux/application.py
patch
|
blob
|
history
src/nepi/resources/linux/ns3/ns3pingdceapplication.py
patch
|
blob
|
history
src/nepi/resources/linux/ns3/ns3simulation.py
patch
|
blob
|
history
src/nepi/resources/ns3/ns3dceapplication.py
patch
|
blob
|
history
test/resources/linux/ns3/ns3dceapplication.py
patch
|
blob
|
history
test/resources/linux/ns3/ns3dceping.py
patch
|
blob
|
history
diff --git
a/src/nepi/execution/resource.py
b/src/nepi/execution/resource.py
index
61ba806
..
df359f0
100644
(file)
--- a/
src/nepi/execution/resource.py
+++ b/
src/nepi/execution/resource.py
@@
-1015,49
+1015,54
@@
class ResourceManager(Logger):
def do_fail(self):
self.set_failed()
def do_fail(self):
self.set_failed()
- def set_started(self):
+ def set_started(self
, time = None
):
""" Mark ResourceManager as STARTED """
""" Mark ResourceManager as STARTED """
- self.set_state(ResourceState.STARTED, "_start_time")
+ self.set_state(ResourceState.STARTED, "_start_time"
, time
)
self.debug("----- STARTED ---- ")
self.debug("----- STARTED ---- ")
- def set_stopped(self):
+ def set_stopped(self
, time = None
):
""" Mark ResourceManager as STOPPED """
""" Mark ResourceManager as STOPPED """
- self.set_state(ResourceState.STOPPED, "_stop_time")
+ self.set_state(ResourceState.STOPPED, "_stop_time"
, time
)
self.debug("----- STOPPED ---- ")
self.debug("----- STOPPED ---- ")
- def set_ready(self):
+ def set_ready(self
, time = None
):
""" Mark ResourceManager as READY """
""" Mark ResourceManager as READY """
- self.set_state(ResourceState.READY, "_ready_time")
+ self.set_state(ResourceState.READY, "_ready_time"
, time
)
self.debug("----- READY ---- ")
self.debug("----- READY ---- ")
- def set_released(self):
+ def set_released(self
, time = None
):
""" Mark ResourceManager as REALEASED """
""" Mark ResourceManager as REALEASED """
- self.set_state(ResourceState.RELEASED, "_release_time")
+ self.set_state(ResourceState.RELEASED, "_release_time"
, time
)
self.debug("----- RELEASED ---- ")
self.debug("----- RELEASED ---- ")
- def set_failed(self):
+ def set_failed(self
, time = None
):
""" Mark ResourceManager as FAILED """
""" Mark ResourceManager as FAILED """
- self.set_state(ResourceState.FAILED, "_failed_time")
+ self.set_state(ResourceState.FAILED, "_failed_time"
, time
)
self.debug("----- FAILED ---- ")
self.debug("----- FAILED ---- ")
- def set_discovered(self):
+ def set_discovered(self
, time = None
):
""" Mark ResourceManager as DISCOVERED """
""" Mark ResourceManager as DISCOVERED """
- self.set_state(ResourceState.DISCOVERED, "_discover_time")
+ self.set_state(ResourceState.DISCOVERED, "_discover_time"
, time
)
self.debug("----- DISCOVERED ---- ")
self.debug("----- DISCOVERED ---- ")
- def set_provisioned(self):
+ def set_provisioned(self
, time = None
):
""" Mark ResourceManager as PROVISIONED """
""" Mark ResourceManager as PROVISIONED """
- self.set_state(ResourceState.PROVISIONED, "_provision_time")
+ self.set_state(ResourceState.PROVISIONED, "_provision_time"
, time
)
self.debug("----- PROVISIONED ---- ")
self.debug("----- PROVISIONED ---- ")
- def set_state(self, state, state_time_attr):
+ def set_state(self, state, state_time_attr
, time = None
):
""" Set the state of the RM while keeping a trace of the time """
# Ensure that RM state will not change after released
if self._state == ResourceState.RELEASED:
return
""" Set the state of the RM while keeping a trace of the time """
# Ensure that RM state will not change after released
if self._state == ResourceState.RELEASED:
return
-
- setattr(self, state_time_attr, tnow())
+
+ time = time or tnow()
+ self.set_state_time(state, state_time_attr, time)
+
+ def set_state_time(self, state, state_time_attr, time):
+ """ Set the time for the RM state change """
+ setattr(self, state_time_attr, time)
self._state = state
class ResourceFactory(object):
self._state = state
class ResourceFactory(object):
diff --git
a/src/nepi/resources/linux/application.py
b/src/nepi/resources/linux/application.py
index
af6ae34
..
86cf9c9
100644
(file)
--- a/
src/nepi/resources/linux/application.py
+++ b/
src/nepi/resources/linux/application.py
@@
-360,6
+360,13
@@
class LinuxApplication(ResourceManager):
def execute_deploy_command(self, command, prefix="deploy"):
if command:
def execute_deploy_command(self, command, prefix="deploy"):
if command:
+ # replace application specific paths in the command
+ command = self.replace_paths(command)
+
+ # replace application specific paths in the environment
+ env = self.get("env")
+ env = env and self.replace_paths(env)
+
# Upload the command to a bash script and run it
# in background ( but wait until the command has
# finished to continue )
# Upload the command to a bash script and run it
# in background ( but wait until the command has
# finished to continue )
diff --git
a/src/nepi/resources/linux/ns3/ns3pingdceapplication.py
b/src/nepi/resources/linux/ns3/ns3pingdceapplication.py
index
3086249
..
1f5a678
100644
(file)
--- a/
src/nepi/resources/linux/ns3/ns3pingdceapplication.py
+++ b/
src/nepi/resources/linux/ns3/ns3pingdceapplication.py
@@
-198,7
+198,7
@@
class LinuxDcePing(LinuxNS3DceApplication):
"tar xvjf ${SRC}/iputils-s20101006.tar.bz2 && "
"cd iputils-s20101006/ && "
"sed -i 's/CFLAGS=/CFLAGS+=/g' Makefile && "
"tar xvjf ${SRC}/iputils-s20101006.tar.bz2 && "
"cd iputils-s20101006/ && "
"sed -i 's/CFLAGS=/CFLAGS+=/g' Makefile && "
- "make CFLAGS=-fPIC LDFLAGS=
-pie
ping && "
+ "make CFLAGS=-fPIC LDFLAGS=
'-pie -rdynamic'
ping && "
"cp ping ${BIN_DCE} && cd - "
" )"
)
"cp ping ${BIN_DCE} && cd - "
" )"
)
diff --git
a/src/nepi/resources/linux/ns3/ns3simulation.py
b/src/nepi/resources/linux/ns3/ns3simulation.py
index
cfef32c
..
28a4e10
100644
(file)
--- a/
src/nepi/resources/linux/ns3/ns3simulation.py
+++ b/
src/nepi/resources/linux/ns3/ns3simulation.py
@@
-311,10
+311,10
@@
class LinuxNS3Simulation(LinuxApplication, NS3Simulation):
self._client.start()
"""
self._client.start()
"""
- # XXX: IS THIS REALLY NEEDED??!!!
- # Wait until the Simulation is actually started
...
+ XXX: Is this necessary??
+ # Wait until the Simulation is actually started
before setting the state
is_running = False
is_running = False
- for i in xrange(
10
00):
+ for i in xrange(
2
00):
is_running = self.invoke(SIMULATOR_UUID, "isRunning")
is_finished = self.invoke(SIMULATOR_UUID, "isFinished")
is_running = self.invoke(SIMULATOR_UUID, "isRunning")
is_finished = self.invoke(SIMULATOR_UUID, "isFinished")
@@
-328,6
+328,7
@@
class LinuxNS3Simulation(LinuxApplication, NS3Simulation):
self.error(msg)
raise RuntimeError
"""
self.error(msg)
raise RuntimeError
"""
+
self.set_started()
else:
msg = " Failed to execute command '%s'" % command
self.set_started()
else:
msg = " Failed to execute command '%s'" % command
diff --git
a/src/nepi/resources/ns3/ns3dceapplication.py
b/src/nepi/resources/ns3/ns3dceapplication.py
index
1922bb1
..
2b0f974
100644
(file)
--- a/
src/nepi/resources/ns3/ns3dceapplication.py
+++ b/
src/nepi/resources/ns3/ns3dceapplication.py
@@
-21,7
+21,10
@@
from nepi.execution.attribute import Attribute, Flags, Types
from nepi.execution.resource import clsinit_copy, ResourceState, reschedule_delay
from nepi.resources.ns3.ns3application import NS3BaseApplication
from nepi.execution.resource import clsinit_copy, ResourceState, reschedule_delay
from nepi.resources.ns3.ns3application import NS3BaseApplication
+from nepi.resources.ns3.ns3wrapper import SIMULATOR_UUID
+
import os
import os
+import time
import threading
@clsinit_copy
import threading
@clsinit_copy
@@
-177,12
+180,28
@@
class NS3BaseDceApplication(NS3BaseApplication):
self._start_time = self.simulation.start_time
def _configure_traces(self):
self._start_time = self.simulation.start_time
def _configure_traces(self):
- # Preventing concurrent access to the DceApplicationHelper
+ # Waiting until dce application is actually started
+ is_running = False
+ for i in xrange(200):
+ is_running = self.simulation.invoke(self.uuid, "isAppRunning")
+ is_finished = self.simulation.invoke(SIMULATOR_UUID, "isFinished")
+
+ if is_running or is_finished:
+ break
+ else:
+ time.sleep(1)
+ else:
+ if not is_running:
+ msg = " Application did not start"
+ self.error(msg)
+ raise RuntimeError
+
+ # Using lock to prevent concurrent access to the DceApplicationHelper
# from different DceApplication RMs
with self.dce_application_lock:
pid = self.simulation.invoke(self.dce_application_helper_uuid,
"GetPid", self.uuid)
# from different DceApplication RMs
with self.dce_application_lock:
pid = self.simulation.invoke(self.dce_application_helper_uuid,
"GetPid", self.uuid)
-
+
node_id = self.simulation.invoke(self.node.uuid, "GetId")
self._trace_filename["stdout"] = "files-%s/var/log/%s/stdout" % (node_id, pid)
self._trace_filename["stderr"] = "files-%s/var/log/%s/stderr" % (node_id, pid)
node_id = self.simulation.invoke(self.node.uuid, "GetId")
self._trace_filename["stdout"] = "files-%s/var/log/%s/stdout" % (node_id, pid)
self._trace_filename["stderr"] = "files-%s/var/log/%s/stderr" % (node_id, pid)
diff --git
a/test/resources/linux/ns3/ns3dceapplication.py
b/test/resources/linux/ns3/ns3dceapplication.py
index
8f1c992
..
8528f69
100644
(file)
--- a/
test/resources/linux/ns3/ns3dceapplication.py
+++ b/
test/resources/linux/ns3/ns3dceapplication.py
@@
-22,6
+22,8
@@
from nepi.execution.ec import ExperimentController
from nepi.execution.trace import TraceAttr
from nepi.execution.ec import ExperimentController
from nepi.execution.trace import TraceAttr
+from test_utils import skipIfNotAlive
+
import os
import time
import unittest
import os
import time
import unittest
@@
-45,15
+47,12
@@
def add_ns3_node(ec, simu):
tcp = ec.register_resource("ns3::TcpL4Protocol")
ec.register_connection(node, tcp)
tcp = ec.register_resource("ns3::TcpL4Protocol")
ec.register_connection(node, tcp)
-
return node
return node
-def add_point2point_device(ec, node,
address = None, prefix = None
):
+def add_point2point_device(ec, node,
ip, prefix
):
dev = ec.register_resource("ns3::PointToPointNetDevice")
dev = ec.register_resource("ns3::PointToPointNetDevice")
- if address:
- ec.set(dev, "ip", address)
- if prefix:
- ec.set(dev, "prefix", prefix)
+ ec.set(dev, "ip", ip)
+ ec.set(dev, "prefix", prefix)
ec.register_connection(node, dev)
queue = ec.register_resource("ns3::DropTailQueue")
ec.register_connection(node, dev)
queue = ec.register_resource("ns3::DropTailQueue")
@@
-61,12
+60,10
@@
def add_point2point_device(ec, node, address = None, prefix = None):
return dev
return dev
-def add_csma_device(ec, node,
address = None, prefix = None
):
+def add_csma_device(ec, node,
ip, prefix
):
dev = ec.register_resource("ns3::CsmaNetDevice")
dev = ec.register_resource("ns3::CsmaNetDevice")
- if address:
- ec.set(dev, "ip", address)
- if prefix:
- ec.set(dev, "prefix", prefix)
+ ec.set(dev, "ip", ip)
+ ec.set(dev, "prefix", prefix)
ec.register_connection(node, dev)
queue = ec.register_resource("ns3::DropTailQueue")
ec.register_connection(node, dev)
queue = ec.register_resource("ns3::DropTailQueue")
@@
-74,13
+71,11
@@
def add_csma_device(ec, node, address = None, prefix = None):
return dev
return dev
-def add_wifi_device(ec, node,
address = None, prefix = None
,
+def add_wifi_device(ec, node,
ip, prefix
,
access_point = False):
dev = ec.register_resource("ns3::WifiNetDevice")
access_point = False):
dev = ec.register_resource("ns3::WifiNetDevice")
- if address:
- ec.set(dev, "ip", address)
- if prefix:
- ec.set(dev, "prefix", prefix)
+ ec.set(dev, "ip", ip)
+ ec.set(dev, "prefix", prefix)
ec.register_connection(node, dev)
phy = ec.register_resource("ns3::YansWifiPhy")
ec.register_connection(node, dev)
phy = ec.register_resource("ns3::YansWifiPhy")
@@
-137,22
+132,23
@@
def add_wifi_channel(ec):
class LinuxNS3DceApplicationTest(unittest.TestCase):
def setUp(self):
class LinuxNS3DceApplicationTest(unittest.TestCase):
def setUp(self):
- #self.fedora_host = "nepi2.pl.sophia.inria.fr"
- #self.fedora_host = "planetlabpc1.upf.edu"
- #self.fedora_user = "inria_nepi"
- #self.fedora_identity = "%s/.ssh/id_rsa_planetlab" % (os.environ['HOME'])
- self.fedora_host = "mimas.inria.fr"
- self.fedora_user = "aquereil"
- self.fedora_identity = "%s/.ssh/id_rsa" % (os.environ['HOME'])
-
- def test_dce_ping(self):
+ self.fedora_host = "nepi2.pl.sophia.inria.fr"
+ self.fedora_user = "inria_nepi"
+ self.fedora_identity = "%s/.ssh/id_rsa_planetlab" % (os.environ['HOME'])
+
+ @skipIfNotAlive
+ def t_dce_ping(self, host, user = None, identity = None):
ec = ExperimentController(exp_id = "test-dce-ping")
ec = ExperimentController(exp_id = "test-dce-ping")
-
+
node = ec.register_resource("LinuxNode")
node = ec.register_resource("LinuxNode")
- ec.set(node, "hostname", self.fedora_host)
- ec.set(node, "username", self.fedora_user)
- ec.set(node, "identity", self.fedora_identity)
- #ec.set(node, "cleanProcesses", True)
+ if host == "localhost":
+ ec.set(node, "hostname", host)
+ else:
+ ec.set(node, "hostname", host)
+ ec.set(node, "username", user)
+ ec.set(node, "identity", identity)
+
+ ec.set(node, "cleanProcesses", True)
#ec.set(node, "cleanHome", True)
simu = ec.register_resource("LinuxNS3Simulation")
#ec.set(node, "cleanHome", True)
simu = ec.register_resource("LinuxNS3Simulation")
@@
-182,7
+178,7
@@
class LinuxNS3DceApplicationTest(unittest.TestCase):
ec.set (ping, "build", "tar xvjf ${SRC}/iputils-s20101006.tar.bz2 && "
"cd iputils-s20101006/ && "
"sed -i 's/CFLAGS=/CFLAGS+=/g' Makefile && "
ec.set (ping, "build", "tar xvjf ${SRC}/iputils-s20101006.tar.bz2 && "
"cd iputils-s20101006/ && "
"sed -i 's/CFLAGS=/CFLAGS+=/g' Makefile && "
- "make CFLAGS=-fPIC LDFLAGS=
-pie
ping && "
+ "make CFLAGS=-fPIC LDFLAGS=
'-pie -rdynamic'
ping && "
"cp ping ${BIN_DCE} && cd - ")
ec.set (ping, "binary", "ping")
ec.set (ping, "stackSize", 1<<20)
"cp ping ${BIN_DCE} && cd - ")
ec.set (ping, "binary", "ping")
ec.set (ping, "stackSize", 1<<20)
@@
-213,13
+209,18
@@
class LinuxNS3DceApplicationTest(unittest.TestCase):
ec.shutdown()
ec.shutdown()
- def test_dce_ccn(self):
+ @skipIfNotAlive
+ def t_dce_ccn(self, host, user = None, identity = None):
ec = ExperimentController(exp_id = "test-dce-ccn")
ec = ExperimentController(exp_id = "test-dce-ccn")
-
+
node = ec.register_resource("LinuxNode")
node = ec.register_resource("LinuxNode")
- ec.set(node, "hostname", self.fedora_host)
- ec.set(node, "username", self.fedora_user)
- ec.set(node, "identity", self.fedora_identity)
+ if host == "localhost":
+ ec.set(node, "hostname", host)
+ else:
+ ec.set(node, "hostname", host)
+ ec.set(node, "username", user)
+ ec.set(node, "identity", identity)
+
#ec.set(node, "cleanProcesses", True)
#ec.set(node, "cleanHome", True)
#ec.set(node, "cleanProcesses", True)
#ec.set(node, "cleanHome", True)
@@
-247,6
+248,7
@@
class LinuxNS3DceApplicationTest(unittest.TestCase):
### create applications
ccnd1 = ec.register_resource("ns3::LinuxCCNDceApplication")
### create applications
ccnd1 = ec.register_resource("ns3::LinuxCCNDceApplication")
+ # NOTE THAT INSTALLATION MIGHT FAIL IF openjdk-6-jdk is not installed
ec.set(ccnd1, "depends", "libpcap0.8-dev openjdk-6-jdk ant1.8 autoconf "
"libssl-dev libexpat-dev libpcap-dev libecryptfs0 libxml2-utils auto"
"make gawk gcc g++ git-core pkg-config libpcre3-dev openjdk-6-jre-lib")
ec.set(ccnd1, "depends", "libpcap0.8-dev openjdk-6-jdk ant1.8 autoconf "
"libssl-dev libexpat-dev libpcap-dev libecryptfs0 libxml2-utils auto"
"make gawk gcc g++ git-core pkg-config libpcre3-dev openjdk-6-jre-lib")
@@
-254,7
+256,7
@@
class LinuxNS3DceApplicationTest(unittest.TestCase):
ec.set (ccnd1, "build", "tar zxf ${SRC}/ccnx-0.7.2.tar.gz && "
"cd ccnx-0.7.2 && "
" INSTALL_BASE=${BIN_DCE}/.. ./configure && "
ec.set (ccnd1, "build", "tar zxf ${SRC}/ccnx-0.7.2.tar.gz && "
"cd ccnx-0.7.2 && "
" INSTALL_BASE=${BIN_DCE}/.. ./configure && "
- " make MORE_LDLIBS=
-pie
&& "
+ " make MORE_LDLIBS=
'-pie -rdynamic'
&& "
" make install && "
" cp ${BIN_DCE}/../bin/ccn* ${BIN_DCE} && "
" cd -")
" make install && "
" cp ${BIN_DCE}/../bin/ccn* ${BIN_DCE} && "
" cd -")
@@
-338,5
+340,17
@@
class LinuxNS3DceApplicationTest(unittest.TestCase):
ec.shutdown()
ec.shutdown()
+ def test_dce_ping_fedora(self):
+ self.t_dce_ping(self.fedora_host, self.fedora_user, self.fedora_identity)
+
+ def test_dce_ping_local(self):
+ self.t_dce_ping("localhost")
+
+ def test_dce_ccn_fedora(self):
+ self.t_dce_ccn(self.fedora_host, self.fedora_user, self.fedora_identity)
+
+ def test_dce_ccn_local(self):
+ self.t_dce_ccn("localhost")
+
if __name__ == '__main__':
unittest.main()
if __name__ == '__main__':
unittest.main()
diff --git
a/test/resources/linux/ns3/ns3dceping.py
b/test/resources/linux/ns3/ns3dceping.py
index
31f9142
..
f57c4f0
100644
(file)
--- a/
test/resources/linux/ns3/ns3dceping.py
+++ b/
test/resources/linux/ns3/ns3dceping.py
@@
-22,6
+22,8
@@
from nepi.execution.ec import ExperimentController
from nepi.execution.trace import TraceAttr
from nepi.execution.ec import ExperimentController
from nepi.execution.trace import TraceAttr
+from test_utils import skipIfNotAlive
+
import os
import time
import unittest
import os
import time
import unittest
@@
-47,12
+49,10
@@
def add_ns3_node(ec, simu):
return node
return node
-def add_point2point_device(ec, node,
address = None, prefix = None
):
+def add_point2point_device(ec, node,
ip, prefix
):
dev = ec.register_resource("ns3::PointToPointNetDevice")
dev = ec.register_resource("ns3::PointToPointNetDevice")
- if address:
- ec.set(dev, "ip", address)
- if prefix:
- ec.set(dev, "prefix", prefix)
+ ec.set(dev, "ip", address)
+ ec.set(dev, "prefix", prefix)
ec.register_connection(node, dev)
queue = ec.register_resource("ns3::DropTailQueue")
ec.register_connection(node, dev)
queue = ec.register_resource("ns3::DropTailQueue")
@@
-62,25
+62,23
@@
def add_point2point_device(ec, node, address = None, prefix = None):
class LinuxNS3PingDceApplicationTest(unittest.TestCase):
def setUp(self):
class LinuxNS3PingDceApplicationTest(unittest.TestCase):
def setUp(self):
- #self.fedora_host = "nepi2.pl.sophia.inria.fr"
- #self.fedora_host = "planetlabpc1.upf.edu"
- #self.fedora_user = "inria_nepi"
- #self.fedora_identity = "%s/.ssh/id_rsa_planetlab" % (os.environ['HOME'])
- self.fedora_host = "mimas.inria.fr"
- self.fedora_user = "aquereil"
- self.fedora_identity = "%s/.ssh/id_rsa" % (os.environ['HOME'])
- self.fedora_host = "planetlab1.informatik.uni-goettingen.de"
+ self.fedora_host = "nepi2.pl.sophia.inria.fr"
self.fedora_user = "inria_nepi"
self.fedora_user = "inria_nepi"
- self.fedora_identity = "%s/.ssh/id_rsa_inria_twitter" % (os.environ['HOME'])
+ self.fedora_identity = "%s/.ssh/id_rsa_planetlab" % (os.environ['HOME'])
+
+ @skipIfNotAlive
+ def t_dce_ping(self, host, user = None, identity = None):
+ ec = ExperimentController(exp_id = "test-dce-ping-app")
- def test_dce_ping(self):
- ec = ExperimentController(exp_id = "test-dceping")
-
node = ec.register_resource("LinuxNode")
node = ec.register_resource("LinuxNode")
- ec.set(node, "hostname", self.fedora_host)
- ec.set(node, "username", self.fedora_user)
- ec.set(node, "identity", self.fedora_identity)
- #ec.set(node, "cleanProcesses", True)
+ if host == "localhost":
+ ec.set(node, "hostname", host)
+ else:
+ ec.set(node, "hostname", host)
+ ec.set(node, "username", user)
+ ec.set(node, "identity", identity)
+
+ ec.set(node, "cleanProcesses", True)
#ec.set(node, "cleanHome", True)
simu = ec.register_resource("LinuxNS3Simulation")
#ec.set(node, "cleanHome", True)
simu = ec.register_resource("LinuxNS3Simulation")
@@
-136,5
+134,11
@@
class LinuxNS3PingDceApplicationTest(unittest.TestCase):
ec.shutdown()
ec.shutdown()
+ def test_dce_ping_fedora(self):
+ self.t_dce_ping(self.fedora_host, self.fedora_user, self.fedora_identity)
+
+ def test_dce_ping_local(self):
+ self.t_dce_ping("localhost")
+
if __name__ == '__main__':
unittest.main()
if __name__ == '__main__':
unittest.main()