Commit bec29220 authored by Julien Muchembled's avatar Julien Muchembled

coverage: add support for functional tests

parent 8eb14b01
...@@ -30,6 +30,7 @@ if filter(re.compile(r'--coverage$|-\w*c').match, sys.argv[1:]): ...@@ -30,6 +30,7 @@ if filter(re.compile(r'--coverage$|-\w*c').match, sys.argv[1:]):
# Start coverage as soon as possible. # Start coverage as soon as possible.
import coverage import coverage
coverage = coverage.Coverage() coverage = coverage.Coverage()
coverage.neotestrunner = []
coverage.start() coverage.start()
import neo import neo
...@@ -210,7 +211,7 @@ class TestRunner(BenchmarkRunner): ...@@ -210,7 +211,7 @@ class TestRunner(BenchmarkRunner):
def add_options(self, parser): def add_options(self, parser):
parser.add_option('-c', '--coverage', action='store_true', parser.add_option('-c', '--coverage', action='store_true',
help='Enable coverage (not working yet for functional tests)') help='Enable coverage')
parser.add_option('-f', '--functional', action='store_true', parser.add_option('-f', '--functional', action='store_true',
help='Functional tests') help='Functional tests')
parser.add_option('-u', '--unit', action='store_true', parser.add_option('-u', '--unit', action='store_true',
...@@ -267,6 +268,8 @@ Environment Variables: ...@@ -267,6 +268,8 @@ Environment Variables:
traceback.print_exc() traceback.print_exc()
if config.coverage: if config.coverage:
coverage.stop() coverage.stop()
if coverage.neotestrunner:
coverage.combine(coverage.neotestrunner)
coverage.save() coverage.save()
# build report # build report
self._successful = runner.wasSuccessful() self._successful = runner.wasSuccessful()
......
...@@ -42,6 +42,11 @@ from .. import ADDRESS_TYPE, DB_SOCKET, DB_USER, IP_VERSION_FORMAT_DICT, SSL, \ ...@@ -42,6 +42,11 @@ from .. import ADDRESS_TYPE, DB_SOCKET, DB_USER, IP_VERSION_FORMAT_DICT, SSL, \
from neo.client.Storage import Storage from neo.client.Storage import Storage
from neo.storage.database import buildDatabaseManager from neo.storage.database import buildDatabaseManager
try:
coverage = sys.modules['neo.scripts.runner'].coverage
except (AttributeError, KeyError):
coverage = None
command_dict = { command_dict = {
NodeTypes.MASTER: 'neomaster', NodeTypes.MASTER: 'neomaster',
NodeTypes.STORAGE: 'neostorage', NodeTypes.STORAGE: 'neostorage',
...@@ -111,6 +116,10 @@ class PortAllocator(object): ...@@ -111,6 +116,10 @@ class PortAllocator(object):
class NEOProcess(object): class NEOProcess(object):
_coverage_fd = None
_coverage_prefix = os.path.join(getTempDirectory(), 'coverage-')
_coverage_index = 0
pid = 0 pid = 0
def __init__(self, command, uuid, arg_dict): def __init__(self, command, uuid, arg_dict):
...@@ -136,12 +145,40 @@ class NEOProcess(object): ...@@ -136,12 +145,40 @@ class NEOProcess(object):
args.append(str(param)) args.append(str(param))
if with_uuid: if with_uuid:
args += '--uuid', str(self.uuid) args += '--uuid', str(self.uuid)
global coverage
if coverage:
cls = self.__class__
cls._coverage_index += 1
coverage_data_path = cls._coverage_prefix + str(cls._coverage_index)
self._coverage_fd, w = os.pipe()
def save_coverage(*args):
if coverage:
coverage.stop()
coverage.save()
if args:
os.close(w)
os.kill(os.getpid(), signal.SIGSTOP)
self.pid = os.fork() self.pid = os.fork()
if self.pid == 0: if self.pid:
# Wait that the signal to kill the child is set up.
os.close(w)
os.read(self._coverage_fd, 1)
if coverage:
coverage.neotestrunner.append(coverage_data_path)
else:
# Child # Child
try: try:
# release SQLite debug log # release SQLite debug log
logging.setup() logging.setup()
signal.signal(signal.SIGTERM, lambda *args: sys.exit())
if coverage:
coverage.stop()
from coverage import Coverage
coverage = Coverage(coverage_data_path)
coverage.start()
signal.signal(signal.SIGUSR2, save_coverage)
os.close(self._coverage_fd)
os.write(w, '\0')
sys.argv = [command] + args sys.argv = [command] + args
getattr(neo.scripts, command).main() getattr(neo.scripts, command).main()
status = 0 status = 0
...@@ -158,6 +195,7 @@ class NEOProcess(object): ...@@ -158,6 +195,7 @@ class NEOProcess(object):
# prevent child from killing anything (cf __del__), or # prevent child from killing anything (cf __del__), or
# running any other cleanup code normally done by the parent # running any other cleanup code normally done by the parent
try: try:
save_coverage()
os._exit(status) os._exit(status)
except: except:
print >>sys.stderr, status print >>sys.stderr, status
...@@ -166,6 +204,15 @@ class NEOProcess(object): ...@@ -166,6 +204,15 @@ class NEOProcess(object):
logging.info('pid %u: %s %s', logging.info('pid %u: %s %s',
self.pid, command, ' '.join(map(repr, args))) self.pid, command, ' '.join(map(repr, args)))
def child_coverage(self):
r = self._coverage_fd
if r is not None:
try:
os.read(r, 1)
finally:
os.close(r)
del self._coverage_fd
def kill(self, sig=signal.SIGTERM): def kill(self, sig=signal.SIGTERM):
if self.pid: if self.pid:
logging.info('kill pid %u', self.pid) logging.info('kill pid %u', self.pid)
...@@ -186,12 +233,14 @@ class NEOProcess(object): ...@@ -186,12 +233,14 @@ class NEOProcess(object):
# guaranteed way to handle them (other objects we would depend on # guaranteed way to handle them (other objects we would depend on
# might already have been deleted). # might already have been deleted).
pass pass
assert self._coverage_fd is None, self._coverage_fd
def wait(self, options=0): def wait(self):
if self.pid == 0: if self.pid == 0:
raise AlreadyStopped raise AlreadyStopped
result = os.WEXITSTATUS(os.waitpid(self.pid, options)[1]) result = os.WEXITSTATUS(os.waitpid(self.pid, 0)[1])
self.pid = 0 self.pid = 0
self.child_coverage()
if result: if result:
raise NodeProcessError('%r %r exited with status %r' % ( raise NodeProcessError('%r %r exited with status %r' % (
self.command, self.arg_dict, result)) self.command, self.arg_dict, result))
...@@ -376,10 +425,12 @@ class NEOCluster(object): ...@@ -376,10 +425,12 @@ class NEOCluster(object):
for process_list in self.process_dict.itervalues(): for process_list in self.process_dict.itervalues():
for process in process_list: for process in process_list:
try: try:
process.kill(signal.SIGSTOP) process.kill(signal.SIGUSR2)
stopped_list.append(process) stopped_list.append(process)
except AlreadyStopped: except AlreadyStopped:
pass pass
for process in stopped_list:
process.child_coverage()
error_list = [] error_list = []
for process in stopped_list: for process in stopped_list:
try: try:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment