test: Bring back the testsuite and test if the initialization override works

Summary:
Simplify the Makefile taking example on pitivi and copy several pitivi
testing files, simplifying them a bit for our use case

Reviewers: Mathieu_Du

Differential Revision: http://phabricator.freedesktop.org/D97
This commit is contained in:
Thibault Saunier 2015-04-15 19:57:43 +02:00
parent 6b32ccbbb2
commit d97662ad41
33 changed files with 205 additions and 282 deletions

View file

@ -1,4 +1,4 @@
SUBDIRS = common gi plugin
SUBDIRS = common gi plugin testsuite
# Examples and testsuite need to be ported to 1.0
#examples testsuite

View file

@ -152,5 +152,6 @@ AC_OUTPUT([
common/m4/Makefile
gi/Makefile
plugin/Makefile
testsuite/Makefile
gi/overrides/Makefile
])

View file

@ -1,90 +1,24 @@
# This is a hack to make sure a shared library is built
tests = \
test_adapter.py \
test_audio.py \
test_bin.py \
test_buffer.py \
test_caps.py \
test_element.py \
test_event.py \
test_ghostpad.py \
test_interface.py \
test_message.py \
test_pad.py \
test_pipeline.py \
test_registry.py \
test_struct.py \
test_segment.py \
test_taglist.py \
test_xml.py \
test_pbutils.py \
# Don't try to use wildcards to replace the list of tests below.
# http://www.gnu.org/software/automake/manual/automake.html#Wildcards
# Keep this list sorted!
tests = \
test_gst.py
EXTRA_DIST = $(tests) common.py cleanup.py runtests.py test-object.h python.supp gstpython.supp
EXTRA_DIST = \
__init__.py \
common.py \
runtests.py \
overrides_hack.py \
$(tests)
if HAVE_VALGRIND
check-valgrind:
$(PYTHON) cleanup.py
make valgrind
else
check-valgrind:
@true
endif
clean-local:
rm -rf *.pyc *.pyo
GSTSUPP = $(top_srcdir)/common/gst.supp
PYTHONSUPP = $(top_srcdir)/testsuite/python.supp
GSTPYTHONSUPP = $(top_srcdir)/testsuite/gstpython.supp
TESTS_ENVIRONMENT = LC_ALL=C
# gdb any given test_x.py by running make test_x.py.gdb
%.gdb: %
$(TESTS_ENVIRONMENT) \
gdb --args \
$(PYTHON) \
$*
# valgrind any given test_x.py by running make test_x.py.valgrind
%.valgrind: %
$(TESTS_ENVIRONMENT) \
G_DEBUG=gc-friendly G_SLICE=always-malloc \
$(VALGRIND_PATH) -q \
--suppressions=$(GSTSUPP) \
--suppressions=$(PYTHONSUPP) \
--suppressions=$(GSTPYTHONSUPP) \
--tool=memcheck --leak-check=full --trace-children=yes \
--leak-resolution=high --num-callers=50 \
$(PYTHON) \
$* 2>&1 | tee valgrind.log
@if grep "tely lost" valgrind.log; then \
rm valgrind.log; \
exit 1; \
fi
@rm valgrind.log
# valgrind any given test_x.py by running make test_x.py.valgrind
%.gen-suppressions: %
$(TESTS_ENVIRONMENT) \
G_DEBUG=gc-friendly G_SLICE=always-malloc \
$(VALGRIND_PATH) -q \
--suppressions=$(GSTSUPP) \
--suppressions=$(PYTHONSUPP) \
--suppressions=$(GSTPYTHONSUPP) \
--tool=memcheck --leak-check=full --trace-children=yes \
--leak-resolution=high --num-callers=50 \
--gen-suppressions=all \
$(PYTHON) \
$* 2>&1 | tee valgrind.log
@if grep "tely lost" valgrind.log; then \
rm valgrind.log; \
exit 1; \
fi
@rm valgrind.log
check-local:
$(PYTHON) $(srcdir)/runtests.py $(tests)
%.check: %
$(PYTHON) $(srcdir)/cleanup.py
$(PYTHON) $(srcdir)/runtests.py $*
@rm -fr *.pyc
%.forever: %
$(srcdir)/cleanup.py
@while true; do \

View file

@ -1,204 +1,138 @@
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
# -*- Mode: Python; py-indent-offset: 4 -*-
# vim: tabstop=4 shiftwidth=4 expandtab
#
# gst-python - Python bindings for GStreamer
# Copyright (C) 2002 David I. Lehn
# Copyright (C) 2004 Johan Dahlin
# Copyright (C) 2005 Edward Hervey
# Copyright (C) 2015 Thibault Saunier <thibault.saunier@collabora.com>
#
# This library is free software; you can redistribute it and/or
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
# License along with this program; if not, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
# 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 3, or (at your option)
# any later version.
"""
A collection of objects to use for testing
Copyied from pitivi
"""
try:
from dl import RTLD_LAZY, RTLD_GLOBAL
except ImportError:
# dl doesn't seem to be available on 64bit systems
try:
from DLFCN import RTLD_LAZY, RTLD_GLOBAL
except ImportError:
pass
import os
import sys
import gc
import unittest
import gi.overrides
gi.overrides
import pygtk
pygtk.require('2.0')
from gi.repository import Gst
import gobject
try:
gobject.threads_init()
except:
print "WARNING: gobject doesn't have threads_init, no threadsafety"
# Detect the version of pygobject
# In pygobject >= 2.13.0 the refcounting of objects has changed.
pgmaj,pgmin,pgmac = gobject.pygobject_version
if pgmaj >= 2 and pgmin >= 13:
pygobject_2_13 = True
else:
pygobject_2_13 = False
detect_leaks = os.environ.get("TEST_DETECT_LEAKS", "1") not in ("0", "")
# Don't insert before .
# sys.path.insert(1, os.path.join('..'))
# Load GST and make sure we load it from the current build
sys.setdlopenflags(RTLD_LAZY | RTLD_GLOBAL)
topbuilddir = os.path.abspath(os.path.join('..'))
topsrcdir = os.path.abspath(os.path.join('..'))
if topsrcdir.endswith('_build'):
topsrcdir = os.path.dirname(topsrcdir)
# gst's __init__.py is in topbuilddir/gst
path = os.path.abspath(os.path.join(topbuilddir, 'gst'))
import gst
file = gst.__file__
assert file.startswith(path), 'bad gst path: %s' % file
# gst's interfaces is in topbuilddir/gst
path = os.path.abspath(os.path.join(topbuilddir, 'gst'))
try:
import gst.interfaces
except ImportError:
# hack: we import it from our builddir/gst/.libs instead; ugly
import interfaces
gst.interfaces = interfaces
file = gst.interfaces.__file__
assert file.startswith(path), 'bad gst.interfaces path: %s' % file
# gst's tags is in topbuilddir/gst
path = os.path.abspath(os.path.join(topbuilddir, 'gst'))
try:
import gst.tag
except ImportError:
# hack: we import it from our builddir/gst/.libs instead; ugly
import tag
gst.tag = tag
file = gst.tag.__file__
assert file.startswith(path), 'bad gst.tag path: %s' % file
# gst's pbutils is in topbuilddir/gst
path = os.path.abspath(os.path.join(topbuilddir, 'gst'))
try:
import gst.pbutils
except ImportError:
# hack: we import it from our builddir/gst/.libs instead; ugly
import pbutils
gst.pbutils = pbutils
file = gst.pbutils.__file__
assert file.startswith(path), 'bad gst.pbutils path: %s' % file
# testhelper needs gstlibtoolimporter
import gstlibtoolimporter
gstlibtoolimporter.install()
import testhelper
gstlibtoolimporter.uninstall()
_stderr = None
def disable_stderr():
global _stderr
_stderr = file('/tmp/stderr', 'w+')
sys.stderr = os.fdopen(os.dup(2), 'w')
os.close(2)
os.dup(_stderr.fileno())
def enable_stderr():
global _stderr
os.close(2)
os.dup(sys.stderr.fileno())
_stderr.seek(0, 0)
data = _stderr.read()
_stderr.close()
os.remove('/tmp/stderr')
return data
def run_silent(function, *args, **kwargs):
disable_stderr()
try:
function(*args, **kwargs)
except Exception, exc:
enable_stderr()
raise exc
output = enable_stderr()
return output
class TestCase(unittest.TestCase):
_tracked_types = (Gst.MiniObject, Gst.Element, Gst.Pad, Gst.Caps)
_types = [gst.Object, gst.MiniObject]
def gctrack(self):
self.gccollect()
self._tracked = []
for obj in gc.get_objects():
if not isinstance(obj, self._tracked_types):
continue
self._tracked.append(obj)
def gccollect(self):
# run the garbage collector
ret = 0
gst.debug('garbage collecting')
while True:
c = gc.collect()
ret += c
if c == 0: break
gst.debug('done garbage collecting, %d objects' % ret)
if c == 0:
break
return ret
def gctrack(self):
# store all gst objects in the gc in a tracking dict
# call before doing any allocation in your test, from setUp
gst.debug('tracking gc GstObjects for types %r' % self._types)
self.gccollect()
self._tracked = {}
for c in self._types:
self._tracked[c] = [o for o in gc.get_objects() if isinstance(o, c)]
def gcverify(self):
# verify no new gst objects got added to the gc
# call after doing all cleanup in your test, from tearDown
gst.debug('verifying gc GstObjects for types %r' % self._types)
new = []
for c in self._types:
objs = [o for o in gc.get_objects() if isinstance(o, c)]
new.extend([o for o in objs if o not in self._tracked[c]])
leaked = []
for obj in gc.get_objects():
if not isinstance(obj, self._tracked_types) or \
obj in self._tracked:
continue
self.failIf(new, new)
#self.failIf(new, ["%r:%d" % (type(o), id(o)) for o in new])
leaked.append(obj)
# we collect again here to get rid of temporary objects created in the
# above loop
self.gccollect()
for elt in leaked:
print(elt)
for i in gc.get_referrers(elt):
print(" ", i)
self.assertFalse(leaked, leaked)
del self._tracked
def setUp(self):
"""
Override me by chaining up to me at the start of your setUp.
"""
# Using private variables is BAD ! this variable changed name in
# python 2.5
try:
methodName = self.__testMethodName
except:
methodName = self._testMethodName
gst.debug('%s.%s' % (self.__class__.__name__, methodName))
self.gctrack()
self._num_failures = len(getattr(self._result, 'failures', []))
self._num_errors = len(getattr(self._result, 'errors', []))
if detect_leaks:
self.gctrack()
def tearDown(self):
"""
Override me by chaining up to me at the end of your tearDown.
"""
# Using private variables is BAD ! this variable changed name in
# python 2.5
try:
methodName = self.__testMethodName
except:
methodName = self._testMethodName
gst.debug('%s.%s' % (self.__class__.__name__, methodName))
self.gccollect()
self.gcverify()
# don't barf gc info all over the console if we have already failed a
# test case
if (self._num_failures < len(getattr(self._result, 'failures', []))
or self._num_errors < len(getattr(self._result, 'failures', []))):
return
if detect_leaks:
self.gccollect()
self.gcverify()
# override run() to save a reference to the test result object
def run(self, result=None):
if not result:
result = self.defaultTestResult()
self._result = result
unittest.TestCase.run(self, result)
class SignalMonitor(object):
def __init__(self, obj, *signals):
self.signals = signals
self.connectToObj(obj)
def connectToObj(self, obj):
self.obj = obj
for signal in self.signals:
obj.connect(signal, self._signalCb, signal)
setattr(self, self._getSignalCounterName(signal), 0)
setattr(self, self._getSignalCollectName(signal), [])
def disconnectFromObj(self, obj):
obj.disconnect_by_func(self._signalCb)
del self.obj
def _getSignalCounterName(self, signal):
field = '%s_count' % signal.replace('-', '_')
return field
def _getSignalCollectName(self, signal):
field = '%s_collect' % signal.replace('-', '_')
return field
def _signalCb(self, obj, *args):
name = args[-1]
field = self._getSignalCounterName(name)
setattr(self, field, getattr(self, field, 0) + 1)
field = self._getSignalCollectName(name)
setattr(self, field, getattr(self, field, []) + [args[:-1]])

View file

@ -0,0 +1,11 @@
import gi.overrides
if not gi.overrides.__path__[0].endswith("gst-python/gi/overrides"):
local_overrides = None
# our overrides don't take precedence, let's fix it
for i, path in enumerate(gi.overrides.__path__):
if path.endswith("gst-python/gi/overrides"):
local_overrides = path
gi.overrides.__path__.remove(local_overrides)
gi.overrides.__path__.insert(0, local_overrides)

View file

@ -21,40 +21,50 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
import glob
import os
import sys
import unittest
SKIP_FILES = ['common', 'runtests']
os.environ['LC_MESSAGES'] = 'C'
def _testcases(filenames):
"""Yield testcases out of filenames."""
for filename in filenames:
if filename.endswith(".py"):
yield filename[:-3]
def gettestnames(which):
if not which:
dir = os.path.split(os.path.abspath(__file__))[0]
which = [os.path.basename(p) for p in glob.glob('%s/test_*.py' % dir)]
names = map(lambda x: x[:-3], which)
for f in SKIP_FILES:
if f in names:
names.remove(f)
return names
suite = unittest.TestSuite()
loader = unittest.TestLoader()
for name in gettestnames(sys.argv[1:]):
suite.addTest(loader.loadTestsFromName(name))
descriptions = 1
verbosity = 1
if os.environ.has_key('VERBOSE'):
descriptions = 2
verbosity = 2
def _tests_suite():
"""Pick which tests to run."""
testcase = os.getenv("TESTCASE")
if testcase:
testcases = [testcase]
else:
testcases = _testcases(sys.argv[1:])
loader = unittest.TestLoader()
return loader.loadTestsFromNames(testcases)
testRunner = unittest.TextTestRunner(descriptions=descriptions,
verbosity=verbosity)
result = testRunner.run(suite)
if result.failures or result.errors:
sys.exit(1)
def setup():
return
if __name__ == "__main__":
setup()
# Set verbosity.
descriptions = 1
verbosity = 1
if 'VERBOSE' in os.environ:
descriptions = 2
verbosity = 2
suite = _tests_suite()
if not list(suite):
raise Exception("No tests found")
# Run the tests.
testRunner = unittest.TextTestRunner(descriptions=descriptions,
verbosity=verbosity)
result = testRunner.run(suite)
if result.failures or result.errors:
sys.exit(1)

View file

@ -17,20 +17,53 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
from common import gst, TestCase, unittest
import overrides_hack
overrides_hack
from common import TestCase, unittest
from gi.repository import Gst
class TimeArgsTest(TestCase):
def testNoneTime(self):
self.assertRaises(TypeError, gst.TIME_ARGS, None)
self.assertRaises(TypeError, Gst.TIME_ARGS, None)
def testStringTime(self):
self.assertRaises(TypeError, gst.TIME_ARGS, "String")
self.assertRaises(TypeError, Gst.TIME_ARGS, "String")
def testClockTimeNone(self):
self.assertEquals(gst.TIME_ARGS(gst.CLOCK_TIME_NONE), 'CLOCK_TIME_NONE')
self.assertEquals(Gst.TIME_ARGS(Gst.CLOCK_TIME_NONE), 'CLOCK_TIME_NONE')
def testOneSecond(self):
self.assertEquals(gst.TIME_ARGS(gst.SECOND), '0:00:01.000000000')
self.assertEquals(Gst.TIME_ARGS(Gst.SECOND), '0:00:01.000000000')
class TestNotInitialized(TestCase):
def testNotInitialized(self):
with self.assertRaises(Gst.NotInitalized):
Gst.Caps.from_string("audio/x-raw")
with self.assertRaises(Gst.NotInitalized):
Gst.Structure.from_string("audio/x-raw")
with self.assertRaises(Gst.NotInitalized):
Gst.ElementFactory.make("identity", None)
def testNotDeinitialized(self):
Gst.init(None)
assert(Gst.Caps.from_string("audio/x-raw"))
assert(Gst.Structure.from_string("audio/x-raw"))
assert(Gst.ElementFactory.make("identity", None))
Gst.deinit()
with self.assertRaises(Gst.NotInitalized):
Gst.Caps.from_string("audio/x-raw")
with self.assertRaises(Gst.NotInitalized):
Gst.Structure.from_string("audio/x-raw")
with self.assertRaises(Gst.NotInitalized):
Gst.ElementFactory.make("identity", None)
if __name__ == "__main__":
unittest.main()