diff --git a/.travis.yml b/.travis.yml index a1a314d9..a8e2fa86 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,5 +18,5 @@ install: - export PYTHONWARNINGS=all script: - python checkdeps.py - - xvfb-run src/bitmessagemain.py -t + - python src/bitmessagemain.py -t - python -bm tests diff --git a/requirements.txt b/requirements.txt index b12641c2..c7c599d5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,7 @@ coverage -python_prctl psutil pycrypto -six PyQt5;python_version>="3.7" +python_prctl;platform_system=="Linux" +six +xvfbwrapper;platform_system=="Linux" diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 2f46c6ec..dc7426ac 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -21,16 +21,13 @@ app_dir = pathmagic.setup() import depends depends.check_dependencies() -import ctypes import getopt import multiprocessing # Used to capture a Ctrl-C keypress so that Bitmessage can shutdown gracefully. import signal -import socket import threading import time import traceback -from struct import pack import defaults import shared @@ -39,7 +36,7 @@ import state from bmconfigparser import BMConfigParser from debug import logger # this should go before any threads from helper_startup import ( - adjustHalfOpenConnectionsLimit, start_proxyconfig) + adjustHalfOpenConnectionsLimit, fixSocket, start_proxyconfig) from inventory import Inventory # Network objects and threads from network import ( @@ -54,67 +51,6 @@ from threads import ( addressGenerator, objectProcessor, singleCleaner, singleWorker, sqlThread) -def _fixSocket(): - if sys.platform.startswith('linux'): - socket.SO_BINDTODEVICE = 25 - - if not sys.platform.startswith('win'): - return - - # Python 2 on Windows doesn't define a wrapper for - # socket.inet_ntop but we can make one ourselves using ctypes - if not hasattr(socket, 'inet_ntop'): - addressToString = ctypes.windll.ws2_32.WSAAddressToStringA - - def inet_ntop(family, host): - """Converting an IP address in packed - binary format to string format""" - if family == socket.AF_INET: - if len(host) != 4: - raise ValueError("invalid IPv4 host") - host = pack("hH4s8s", socket.AF_INET, 0, host, "\0" * 8) - elif family == socket.AF_INET6: - if len(host) != 16: - raise ValueError("invalid IPv6 host") - host = pack("hHL16sL", socket.AF_INET6, 0, 0, host, 0) - else: - raise ValueError("invalid address family") - buf = "\0" * 64 - lengthBuf = pack("I", len(buf)) - addressToString(host, len(host), None, buf, lengthBuf) - return buf[0:buf.index("\0")] - socket.inet_ntop = inet_ntop - - # Same for inet_pton - if not hasattr(socket, 'inet_pton'): - stringToAddress = ctypes.windll.ws2_32.WSAStringToAddressA - - def inet_pton(family, host): - """Converting an IP address in string format - to a packed binary format""" - buf = "\0" * 28 - lengthBuf = pack("I", len(buf)) - if stringToAddress(str(host), - int(family), - None, - buf, - lengthBuf) != 0: - raise socket.error("illegal IP address passed to inet_pton") - if family == socket.AF_INET: - return buf[4:8] - elif family == socket.AF_INET6: - return buf[8:24] - else: - raise ValueError("invalid address family") - socket.inet_pton = inet_pton - - # These sockopts are needed on for IPv6 support - if not hasattr(socket, 'IPPROTO_IPV6'): - socket.IPPROTO_IPV6 = 41 - if not hasattr(socket, 'IPV6_V6ONLY'): - socket.IPV6_V6ONLY = 27 - - def signal_handler(signum, frame): """Single handler for any signal sent to pybitmessage""" process = multiprocessing.current_process() @@ -151,7 +87,7 @@ class Main(object): def start(self): """Start main application""" # pylint: disable=too-many-statements,too-many-branches,too-many-locals - _fixSocket() + fixSocket() adjustHalfOpenConnectionsLimit() config = BMConfigParser() diff --git a/src/helper_startup.py b/src/helper_startup.py index 56bf87cb..c0c35fd9 100644 --- a/src/helper_startup.py +++ b/src/helper_startup.py @@ -3,12 +3,15 @@ Startup operations. """ # pylint: disable=too-many-branches,too-many-statements +import ctypes import logging import os import platform +import socket import sys import time from distutils.version import StrictVersion +from struct import pack try: import defaults @@ -304,6 +307,68 @@ def adjustHalfOpenConnectionsLimit(): state.maximumNumberOfHalfOpenConnections = 9 if is_limited else 64 +def fixSocket(): + """Add missing socket options and methods mainly on Windows""" + if sys.platform.startswith('linux'): + socket.SO_BINDTODEVICE = 25 + + if not sys.platform.startswith('win'): + return + + # Python 2 on Windows doesn't define a wrapper for + # socket.inet_ntop but we can make one ourselves using ctypes + if not hasattr(socket, 'inet_ntop'): + addressToString = ctypes.windll.ws2_32.WSAAddressToStringA + + def inet_ntop(family, host): + """Converting an IP address in packed + binary format to string format""" + if family == socket.AF_INET: + if len(host) != 4: + raise ValueError("invalid IPv4 host") + host = pack("hH4s8s", socket.AF_INET, 0, host, "\0" * 8) + elif family == socket.AF_INET6: + if len(host) != 16: + raise ValueError("invalid IPv6 host") + host = pack("hHL16sL", socket.AF_INET6, 0, 0, host, 0) + else: + raise ValueError("invalid address family") + buf = "\0" * 64 + lengthBuf = pack("I", len(buf)) + addressToString(host, len(host), None, buf, lengthBuf) + return buf[0:buf.index("\0")] + socket.inet_ntop = inet_ntop + + # Same for inet_pton + if not hasattr(socket, 'inet_pton'): + stringToAddress = ctypes.windll.ws2_32.WSAStringToAddressA + + def inet_pton(family, host): + """Converting an IP address in string format + to a packed binary format""" + buf = "\0" * 28 + lengthBuf = pack("I", len(buf)) + if stringToAddress(str(host), + int(family), + None, + buf, + lengthBuf) != 0: + raise socket.error("illegal IP address passed to inet_pton") + if family == socket.AF_INET: + return buf[4:8] + elif family == socket.AF_INET6: + return buf[8:24] + else: + raise ValueError("invalid address family") + socket.inet_pton = inet_pton + + # These sockopts are needed on for IPv6 support + if not hasattr(socket, 'IPPROTO_IPV6'): + socket.IPPROTO_IPV6 = 41 + if not hasattr(socket, 'IPV6_V6ONLY'): + socket.IPV6_V6ONLY = 27 + + def start_proxyconfig(): """Check socksproxytype and start any proxy configuration plugin""" if not get_plugin: diff --git a/src/tests/core.py b/src/tests/core.py index 92d66bc0..18685e13 100644 --- a/src/tests/core.py +++ b/src/tests/core.py @@ -3,6 +3,7 @@ Tests for core and those that do not work outside (because of import error for example) """ +import atexit import os import pickle # nosec import Queue @@ -416,8 +417,9 @@ def run(): suite = loader.loadTestsFromTestCase(TestCore) try: import bitmessageqt.tests + from xvfbwrapper import Xvfb except ImportError: - pass + Xvfb = None else: qt_tests = loader.loadTestsFromModule(bitmessageqt.tests) suite.addTests(qt_tests) @@ -428,4 +430,8 @@ def run(): sys.excepthook = keep_exc + if Xvfb: + vdisplay = Xvfb(width=1024, height=768) + vdisplay.start() + atexit.register(vdisplay.stop) return unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/src/tests/test_logger.py b/src/tests/test_logger.py index da0f9341..d6bf33ed 100644 --- a/src/tests/test_logger.py +++ b/src/tests/test_logger.py @@ -29,7 +29,7 @@ format=%(asctime)s {1} %(message)s class=FileHandler level=NOTSET formatter=default -args=('{0}', 'w') +args=({0!r}, 'w') [logger_root] level=DEBUG diff --git a/src/tests/test_process.py b/src/tests/test_process.py index 7f7691d1..bcec289f 100644 --- a/src/tests/test_process.py +++ b/src/tests/test_process.py @@ -192,6 +192,7 @@ class TestProcessShutdown(TestProcessProto): class TestProcess(TestProcessProto): """A test case for pybitmessage process""" + @unittest.skipIf(sys.platform[:5] != 'linux', 'probably needs prctl') def test_process_name(self): """Check PyBitmessage process name""" self.assertEqual(self.process.name(), 'PyBitmessage') diff --git a/src/tests/test_protocol.py b/src/tests/test_protocol.py index ee649481..d285d1df 100644 --- a/src/tests/test_protocol.py +++ b/src/tests/test_protocol.py @@ -6,11 +6,17 @@ import sys import unittest from pybitmessage import protocol, state +from pybitmessage.helper_startup import fixSocket class TestProtocol(unittest.TestCase): """Main protocol test case""" + @classmethod + def setUpClass(cls): + """Execute fixSocket() before start. Only for Windows?""" + fixSocket() + def test_checkIPv4Address(self): """Check the results of protocol.checkIPv4Address()""" token = 'HELLO'