tests: Add helper for glib based test binaries

This makes running glib based tests inside a dbusmock environment easier
and more beautiful (i.e. output is supressed unless an error occurs).

This helper has been submitted for inclusion in dbusmock. If it cannot$
live there in some form, then we should try to find a home in the GNOME$
project for it.$
This commit is contained in:
Benjamin Berg 2018-04-06 17:01:29 +02:00 committed by Georges Basile Stavracas Neto
parent 8c9be792f2
commit 7630bf963e

113
tests/shared/gtest.py Normal file
View file

@ -0,0 +1,113 @@
#!/usr/bin/python3
# Copyright © 2018 Red Hat, Inc
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.
#
# Authors: Benjamin Berg <bberg@redhat.com>
import os
import sys
import subprocess
import functools
class _GTestSingleProp(object):
"""Property which creates a bound method for calling the specified test."""
def __init__(self, test):
self.test = test
@staticmethod
def __func(self, test):
self._gtest_single(test)
def __get__(self, obj, cls):
bound_method = self.__func.__get__(obj, cls)
partial_method = functools.partial(bound_method, self.test)
partial_method.__doc__ = bound_method.__doc__
return partial_method
class _GTestMeta(type):
def __new__(cls, name, bases, namespace, **kwds):
result = type.__new__(cls, name, bases, dict(namespace))
if result.g_test_exe is not None:
try:
_GTestMeta.make_tests(result.g_test_exe, result)
except Exception as e:
print('')
print(e)
print('Error generating separate test funcs, will call binary once.')
result.test_all = result._gtest_all
return result
@staticmethod
def make_tests(exe, result):
env = os.environ.copy()
env['G_MESSAGES_DEBUG'] = ''
test = subprocess.Popen([exe, '-l'], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, env=env)
stdout, stderr = test.communicate()
if test.returncode != 0:
raise AssertionError('Execution of GTest executable to query the tests returned non-zero exit code!')
stdout = stdout.decode('utf-8')
for i, test in enumerate(stdout.split('\n')):
if not test:
continue
# Number it and make sure the function name is prefixed with 'test'.
# Keep the rest as is, we don't care about the fact that the function
# names cannot be typed in.
name = 'test_%03d_' % (i + 1) + test
setattr(result, name, _GTestSingleProp(test))
class GTest(metaclass = _GTestMeta):
"""Helper class to run GLib test. A test function will be created for each
test from the executable.
Use by using this class as a mixin and setting g_test_exe to an appropriate
value.
"""
#: The GTest based executable
g_test_exe = None
#: Timeout when running a single test
g_test_single_timeout = None
#: Timeout when running all tests in one go
g_test_all_timeout = None
def _gtest_single(self, test):
assert(test)
p = subprocess.Popen([self.g_test_exe, '-q', '-p', test], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
try:
stdout, stderr = p.communicate(timeout=self.g_test_single_timeout)
except subprocess.TimeoutExpired:
p.kill()
stdout, stderr = p.communicate()
stdout += b'\n\nTest was aborted due to timeout'
try:
stdout = stdout.decode('utf-8')
except UnicodeDecodeError:
pass
if p.returncode != 0:
self.fail(stdout)
def _gtest_all(self):
subprocess.check_call([self.g_test_exe], timeout=self.g_test_all_timeout)