From 8e05e4a178e341991bbe446a9f2a686e553f604b Mon Sep 17 00:00:00 2001 From: Lee Miller Date: Mon, 11 Apr 2022 19:08:37 +0300 Subject: [PATCH] A test case for the network start checks that: - all the threads are started, - it opens connections and updates stats. A base class for partial run essentially mimics bitmessagemain. --- src/tests/partial.py | 35 ++++++++++++++++++ src/tests/test_network.py | 74 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 src/tests/partial.py create mode 100644 src/tests/test_network.py diff --git a/src/tests/partial.py b/src/tests/partial.py new file mode 100644 index 00000000..fd2d3f77 --- /dev/null +++ b/src/tests/partial.py @@ -0,0 +1,35 @@ +"""A test case for partial run class definition""" + +import os +import sys +import unittest + +from pybitmessage import pathmagic + + +class TestPartialRun(unittest.TestCase): + """ + A base class for test cases running some parts of the app, + e.g. separate threads or packages. + """ + + @classmethod + def setUpClass(cls): + cls.dirs = (os.path.abspath(os.curdir), pathmagic.setup()) + + import bmconfigparser + import state + + from debug import logger # noqa:F401 pylint: disable=unused-variable + + state.shutdown = 0 + cls.state = state + bmconfigparser.config = cls.config = bmconfigparser.BMConfigParser() + cls.config.read() + + @classmethod + def tearDownClass(cls): + cls.state.shutdown = 1 + # deactivate pathmagic + os.chdir(cls.dirs[0]) + sys.path.remove(cls.dirs[1]) diff --git a/src/tests/test_network.py b/src/tests/test_network.py new file mode 100644 index 00000000..e6a05717 --- /dev/null +++ b/src/tests/test_network.py @@ -0,0 +1,74 @@ +"""Test network module""" + +import threading +import time + +from .common import skip_python3 +from .partial import TestPartialRun + +skip_python3() + + +class TestNetwork(TestPartialRun): + """A test case for running the network subsystem""" + + @classmethod + def setUpClass(cls): + super(TestNetwork, cls).setUpClass() + + cls.state.maximumNumberOfHalfOpenConnections = 4 + + cls.config.set('bitmessagesettings', 'sendoutgoingconnections', 'True') + + # config variable is still used inside of the network ): + import network + from network import connectionpool, stats + + # beware of singleton + connectionpool.config = cls.config + cls.pool = network.BMConnectionPool() + cls.stats = stats + + network.start(cls.config, cls.state) + + def test_threads(self): + """Ensure all the network threads started""" + threads = { + "AddrBroadcaster", "Asyncore", "Downloader", "InvBroadcaster", + "Uploader"} + extra = ( + self.config.getint('threads', 'receive') + + self.config.safeGetBoolean('bitmessagesettings', 'udp')) + for thread in threading.enumerate(): + try: + threads.remove(thread.name) + except KeyError: + extra -= ( + thread.name == "Announcer" + or thread.name.startswith("ReceiveQueue_")) + + self.assertEqual(len(threads), 0) + self.assertEqual(extra, 0) + + def test_stats(self): + """Check that network starts connections and updates stats""" + pl = 0 + for _ in range(30): + if pl == 0: + pl = len(self.pool) + if ( + self.stats.receivedBytes() > 0 and self.stats.sentBytes() > 0 + and pl > 0 + # and len(self.stats.connectedHostsList()) > 0 + ): + break + time.sleep(1) + else: + self.fail('Have not started any connection in 30 sec') + + @classmethod + def tearDownClass(cls): + super(TestNetwork, cls).tearDownClass() + for thread in threading.enumerate(): + if thread.name == "Asyncore": + thread.stopThread()