Merged PeterSurda/teardown-test1:
- reset BMConfigParser before reading config file - enable threads name check on posix systems - check singleton.lock in TestProcessProto.setUpClass()
This commit is contained in:
parent
b65f2d154a
commit
96a784b58b
|
@ -124,7 +124,16 @@ class BMConfigParser(ConfigParser.SafeConfigParser):
|
|||
return [
|
||||
x for x in BMConfigParser().sections() if x.startswith('BM-')]
|
||||
|
||||
def _reset(self):
|
||||
"""Reset current config. There doesn't appear to be a built in
|
||||
method for this"""
|
||||
sections = self.sections()
|
||||
for x in sections:
|
||||
self.remove_section(x)
|
||||
|
||||
def read(self, filenames):
|
||||
"""Read config and populate defaults"""
|
||||
self._reset()
|
||||
ConfigParser.ConfigParser.read(self, filenames)
|
||||
for section in self.sections():
|
||||
for option in self.options(section):
|
||||
|
|
|
@ -47,6 +47,7 @@ class TestProcessConfig(TestProcessProto):
|
|||
def test_config_defaults(self):
|
||||
"""Test settings in the generated config"""
|
||||
self._stop_process()
|
||||
self._kill_process()
|
||||
config = BMConfigParser()
|
||||
config.read(os.path.join(self.home, 'keys.dat'))
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ Common reusable code for tests and tests for pybitmessage process.
|
|||
import os
|
||||
import signal
|
||||
import subprocess # nosec
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
import unittest
|
||||
|
@ -25,7 +26,26 @@ class TestProcessProto(unittest.TestCase):
|
|||
it starts pybitmessage in setUpClass() and stops it in tearDownClass()
|
||||
"""
|
||||
_process_cmd = ['pybitmessage', '-d']
|
||||
_threads_count = 15
|
||||
_threads_count_min = 15
|
||||
_threads_count_max = 16
|
||||
_threads_names = [
|
||||
'PyBitmessage',
|
||||
'addressGenerato',
|
||||
'singleWorker',
|
||||
'SQL',
|
||||
'objectProcessor',
|
||||
'singleCleaner',
|
||||
'singleAPI',
|
||||
'Asyncore',
|
||||
'ReceiveQueue_0',
|
||||
'ReceiveQueue_1',
|
||||
'ReceiveQueue_2',
|
||||
'Announcer',
|
||||
'InvBroadcaster',
|
||||
'AddrBroadcaster',
|
||||
'Downloader',
|
||||
'Uploader'
|
||||
]
|
||||
_files = (
|
||||
'keys.dat', 'debug.log', 'messages.dat', 'knownnodes.dat',
|
||||
'.api_started', 'unittest.lock'
|
||||
|
@ -41,15 +61,28 @@ class TestProcessProto(unittest.TestCase):
|
|||
cls._cleanup_files()
|
||||
os.environ['BITMESSAGE_HOME'] = cls.home
|
||||
put_signal_file(cls.home, 'unittest.lock')
|
||||
starttime = int(time.time())
|
||||
subprocess.Popen(
|
||||
cls._process_cmd,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT) # nosec
|
||||
time.sleep(5)
|
||||
try:
|
||||
cls.pid = int(cls._get_readline('singleton.lock'))
|
||||
except TypeError:
|
||||
cls.flag = True
|
||||
return
|
||||
timeout = starttime + 30
|
||||
while time.time() <= timeout:
|
||||
try:
|
||||
if os.path.exists(os.path.join(cls.home,
|
||||
'singleton.lock')):
|
||||
pstat = os.stat(os.path.join(cls.home, 'singleton.lock'))
|
||||
if starttime <= pstat.st_mtime and pstat.st_size > 0:
|
||||
break
|
||||
except OSError:
|
||||
break
|
||||
time.sleep(1)
|
||||
if time.time() >= timeout:
|
||||
raise psutil.TimeoutExpired(
|
||||
"Timeout starting {}".format(cls._process_cmd))
|
||||
# wait a bit for the program to fully start
|
||||
# 10 sec should be enough
|
||||
time.sleep(10)
|
||||
cls.pid = int(cls._get_readline('singleton.lock'))
|
||||
cls.process = psutil.Process(cls.pid)
|
||||
|
||||
def setUp(self):
|
||||
|
@ -73,6 +106,18 @@ class TestProcessProto(unittest.TestCase):
|
|||
return False
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def _kill_process(cls, timeout=5):
|
||||
try:
|
||||
cls.process.send_signal(signal.SIGKILL)
|
||||
cls.process.wait(timeout)
|
||||
# Windows or already dead
|
||||
except (AttributeError, psutil.NoSuchProcess):
|
||||
return True
|
||||
# except psutil.TimeoutExpired propagates, it means something is very
|
||||
# wrong
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def _cleanup_files(cls):
|
||||
cleanup(cls.home, cls._files)
|
||||
|
@ -82,7 +127,6 @@ class TestProcessProto(unittest.TestCase):
|
|||
"""Ensures that pybitmessage stopped and removes files"""
|
||||
try:
|
||||
if not cls._stop_process():
|
||||
print(open(os.path.join(cls.home, 'debug.log'), 'rb').read())
|
||||
cls.process.kill()
|
||||
except (psutil.NoSuchProcess, AttributeError):
|
||||
pass
|
||||
|
@ -90,25 +134,49 @@ class TestProcessProto(unittest.TestCase):
|
|||
cls._cleanup_files()
|
||||
|
||||
def _test_threads(self):
|
||||
# only count for now
|
||||
# because of https://github.com/giampaolo/psutil/issues/613
|
||||
# PyBitmessage
|
||||
# - addressGenerator
|
||||
# - singleWorker
|
||||
# - SQL
|
||||
# - objectProcessor
|
||||
# - singleCleaner
|
||||
# - singleAPI
|
||||
# - Asyncore
|
||||
# - ReceiveQueue_0
|
||||
# - ReceiveQueue_1
|
||||
# - ReceiveQueue_2
|
||||
# - Announcer
|
||||
# - InvBroadcaster
|
||||
# - AddrBroadcaster
|
||||
# - Downloader
|
||||
self.assertEqual(
|
||||
len(self.process.threads()), self._threads_count)
|
||||
"""Test number and names of threads"""
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
self.longMessage = True
|
||||
|
||||
try:
|
||||
# using ps for posix platforms
|
||||
# because of https://github.com/giampaolo/psutil/issues/613
|
||||
thread_names = subprocess.check_output([
|
||||
"ps", "-L", "-o", "comm=", "--pid",
|
||||
str(self.process.pid)
|
||||
]).split()
|
||||
except: # pylint: disable=bare-except
|
||||
thread_names = []
|
||||
|
||||
running_threads = len(thread_names)
|
||||
if 0 < running_threads < 30: # adequacy check
|
||||
extra_threads = []
|
||||
missing_threads = []
|
||||
for thread_name in thread_names:
|
||||
if thread_name not in self._threads_names:
|
||||
extra_threads.append(thread_name)
|
||||
for thread_name in self._threads_names:
|
||||
if thread_name not in thread_names:
|
||||
missing_threads.append(thread_name)
|
||||
|
||||
msg = "Missing threads: {}, Extra threads: {}".format(
|
||||
",".join(missing_threads), ",".join(extra_threads))
|
||||
else:
|
||||
running_threads = self.process.num_threads()
|
||||
if sys.platform.startswith('win'):
|
||||
running_threads -= 1 # one extra thread on Windows!
|
||||
msg = "Unexpected running thread count"
|
||||
|
||||
self.assertGreaterEqual(
|
||||
running_threads,
|
||||
self._threads_count_min,
|
||||
msg)
|
||||
|
||||
self.assertLessEqual(
|
||||
running_threads,
|
||||
self._threads_count_max,
|
||||
msg)
|
||||
|
||||
|
||||
class TestProcessShutdown(TestProcessProto):
|
||||
|
@ -123,7 +191,14 @@ class TestProcessShutdown(TestProcessProto):
|
|||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
"""Special teardown because pybitmessage is already stopped"""
|
||||
cls._cleanup_files()
|
||||
try:
|
||||
if cls.process.is_running():
|
||||
cls.process.kill()
|
||||
cls.process.wait(5)
|
||||
except (psutil.TimeoutExpired, psutil.NoSuchProcess):
|
||||
pass
|
||||
finally:
|
||||
cls._cleanup_files()
|
||||
|
||||
|
||||
class TestProcess(TestProcessProto):
|
||||
|
|
Reference in New Issue
Block a user