diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/Makefile.am | 23 | ||||
-rw-r--r-- | tests/Makefile.in | 41 | ||||
-rw-r--r-- | tests/compat_test_pygtk.py (renamed from tests/test_pygtkcompat.py) | 44 | ||||
-rwxr-xr-x | tests/runtests.py | 3 | ||||
-rw-r--r-- | tests/test_atoms.py | 31 | ||||
-rw-r--r-- | tests/test_everything.py | 405 | ||||
-rw-r--r-- | tests/test_gi.py | 382 | ||||
-rw-r--r-- | tests/test_glib.py | 186 | ||||
-rw-r--r-- | tests/test_gobject.py | 152 | ||||
-rw-r--r-- | tests/test_gtype.py | 53 | ||||
-rw-r--r-- | tests/test_interface.py | 4 | ||||
-rw-r--r-- | tests/test_iochannel.py | 424 | ||||
-rw-r--r-- | tests/test_mainloop.py | 18 | ||||
-rw-r--r-- | tests/test_option.py | 56 | ||||
-rw-r--r-- | tests/test_overrides.py | 29 | ||||
-rw-r--r-- | tests/test_overrides_gtk.py | 74 | ||||
-rw-r--r-- | tests/test_properties.py | 124 | ||||
-rw-r--r-- | tests/test_signal.py | 87 | ||||
-rw-r--r-- | tests/test_source.py | 223 | ||||
-rw-r--r-- | tests/test_subprocess.py | 133 | ||||
-rw-r--r-- | tests/test_thread.py | 2 | ||||
-rw-r--r-- | tests/test_uris.py | 16 | ||||
-rw-r--r-- | tests/testhelpermodule.c | 3 |
23 files changed, 2234 insertions, 279 deletions
diff --git a/tests/Makefile.am b/tests/Makefile.am index 7a619bf..a91c814 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -8,7 +8,7 @@ libgimarshallingtests_la_LDFLAGS = -module -avoid-version $(GLIB_LIBS) # This is a hack to make sure a shared library is built libgimarshallingtests.la: $(libgimarshallingtests_la_OBJECTS) $(libgimarshallingtests_la_DEPENDENCIES) - $(LINK) -rpath $(pkgpyexecdir) $(libgimarshallingtests_la_LDFLAGS) $(libgimarshallingtests_la_OBJECTS) $(libgimarhallingtests_la_LIBADD) $(LIBS) + $(AM_V_GEN) $(LINK) -rpath $(pkgpyexecdir) $(libgimarshallingtests_la_LDFLAGS) $(libgimarshallingtests_la_OBJECTS) $(libgimarhallingtests_la_LIBADD) $(LIBS) GIMarshallingTests-1.0.gir: libgimarshallingtests.la Makefile $(AM_V_GEN) g-ir-scanner --include=Gio-2.0 \ @@ -30,7 +30,7 @@ libregress_la_CFLAGS = $(GIO_CFLAGS) $(CAIRO_CFLAGS) libregress_la_LDFLAGS = -module -avoid-version $(GIO_LIBS) $(CAIRO_LIBS) libregress.la: $(libregress_la_OBJECTS) $(libregress_la_DEPENDENCIES) - $(LINK) -rpath $(pkgpyexecdir) $(libregress_la_LDFLAGS) $(libregress_la_OBJECTS) $(libregress_la_LIBADD) $(LIBS) + $(AM_V_GEN) $(LINK) -rpath $(pkgpyexecdir) $(libregress_la_LDFLAGS) $(libregress_la_OBJECTS) $(libregress_la_LIBADD) $(LIBS) # g-i doesn't ship these as shared libraries anymore; we build them here Regress-1.0.gir: libregress.la Makefile @@ -65,7 +65,7 @@ testhelper_la_SOURCES = \ # This is a hack to make sure a shared library is built testhelper.la: $(testhelper_la_OBJECTS) $(testhelper_la_DEPENDENCIES) - $(LINK) -rpath $(pkgpyexecdir) $(testhelper_la_LDFLAGS) $(testhelper_la_OBJECTS) $(testhelper_la_LIBADD) $(LIBS) + $(AM_V_GEN) $(LINK) -rpath $(pkgpyexecdir) $(testhelper_la_LDFLAGS) $(testhelper_la_OBJECTS) $(testhelper_la_LIBADD) $(LIBS) .la.so: @@ -86,7 +86,9 @@ EXTRA_DIST = \ org.gnome.test.gschema.xml \ test_glib.py \ test_gobject.py \ + test_gtype.py \ test_interface.py \ + test_iochannel.py \ test_mainloop.py \ test_option.py \ test_properties.py \ @@ -94,7 +96,6 @@ EXTRA_DIST = \ test_source.py \ test_subprocess.py \ test_thread.py \ - test_uris.py \ test_everything.py \ test_gi.py \ test_gdbus.py \ @@ -105,7 +106,7 @@ EXTRA_DIST = \ test_overrides_gdk.py \ test_overrides_gtk.py \ test_atoms.py \ - test_pygtkcompat.py \ + compat_test_pygtk.py \ gi/__init__.py \ gi/overrides/__init__.py \ gi/overrides/Regress.py \ @@ -121,15 +122,21 @@ RUN_TESTS_ENV_VARS= \ GI_TYPELIB_PATH=$(builddir):$$GI_TYPELIB_PATH \ XDG_DATA_DIRS=$$XDG_DATA_DIRS:/usr/share \ MALLOC_PERTURB_=85 \ + MALLOC_CHECK_=3 \ TESTS_BUILDDIR=$(builddir) +# pygtkcompat tests need to be run in a separate process as they +# clobber global name space check-local: $(LTLIBRARIES:.la=.so) $(test_typelibs) gschemas.compiled @echo " CHECK Pyflakes" @if type pyflakes >/dev/null 2>&1; then pyflakes $(top_srcdir); else echo "skipped, pyflakes not installed"; fi - @echo " CHECK PEP8" - @if type pep8 >/dev/null 2>&1; then pep8 --ignore=E501,E123,E124 --repeat --show-source $(top_srcdir); else echo "skipped, pep8 not installed"; fi + @if test -z "$$SKIP_PEP8"; then \ + echo " CHECK PEP8"; \ + if type pep8 >/dev/null 2>&1; then pep8 --ignore=E501,E123,E124 --repeat --show-source $(top_srcdir); else echo "skipped, pep8 not installed"; fi; \ + fi export `$(DBUS_LAUNCH)` && \ - $(RUN_TESTS_ENV_VARS) $(EXEC_NAME) $(PYTHON) -Wd -Werror::PendingDeprecationWarning -Werror::DeprecationWarning $(srcdir)/runtests.py; rc=$$?; \ + $(RUN_TESTS_ENV_VARS) $(EXEC_NAME) $(PYTHON) -Wd -Werror::PendingDeprecationWarning -Werror::DeprecationWarning -Werror::RuntimeWarning $(srcdir)/runtests.py; rc=$$?; \ + [ "$$rc" -ne 0 ] || [ -n "$$TEST_NAMES" ] || { TEST_NAMES=compat_test_pygtk $(RUN_TESTS_ENV_VARS) $(EXEC_NAME) $(PYTHON) -Wd -Werror::PendingDeprecationWarning -Werror::DeprecationWarning -Werror::RuntimeWarning $(srcdir)/runtests.py; rc=$$?; }; \ kill $$DBUS_SESSION_BUS_PID; \ exit $$rc diff --git a/tests/Makefile.in b/tests/Makefile.in index 9993ec7..60fc5cc 100644 --- a/tests/Makefile.in +++ b/tests/Makefile.in @@ -59,8 +59,10 @@ subdir = tests DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/as-ac-expand.m4 \ - $(top_srcdir)/m4/jhflags.m4 $(top_srcdir)/m4/python.m4 \ - $(top_srcdir)/configure.ac + $(top_srcdir)/m4/jhflags.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/python.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d @@ -149,6 +151,9 @@ CAIRO_LIBS = @CAIRO_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ +CODE_COVERAGE_CFLAGS = @CODE_COVERAGE_CFLAGS@ +CODE_COVERAGE_ENABLED = @CODE_COVERAGE_ENABLED@ +CODE_COVERAGE_LDFLAGS = @CODE_COVERAGE_LDFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ @@ -166,6 +171,7 @@ EXEEXT = @EXEEXT@ FFI_CFLAGS = @FFI_CFLAGS@ FFI_LIBS = @FFI_LIBS@ FGREP = @FGREP@ +GENHTML = @GENHTML@ GIO_CFLAGS = @GIO_CFLAGS@ GIO_LIBS = @GIO_LIBS@ GI_CFLAGS = @GI_CFLAGS@ @@ -185,6 +191,7 @@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ INTROSPECTION_COMPILER = @INTROSPECTION_COMPILER@ INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@ +LCOV = @LCOV@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBFFI_PC = @LIBFFI_PC@ @@ -194,7 +201,6 @@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ -MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ @@ -326,7 +332,9 @@ EXTRA_DIST = \ org.gnome.test.gschema.xml \ test_glib.py \ test_gobject.py \ + test_gtype.py \ test_interface.py \ + test_iochannel.py \ test_mainloop.py \ test_option.py \ test_properties.py \ @@ -334,7 +342,6 @@ EXTRA_DIST = \ test_source.py \ test_subprocess.py \ test_thread.py \ - test_uris.py \ test_everything.py \ test_gi.py \ test_gdbus.py \ @@ -345,7 +352,7 @@ EXTRA_DIST = \ test_overrides_gdk.py \ test_overrides_gtk.py \ test_atoms.py \ - test_pygtkcompat.py \ + compat_test_pygtk.py \ gi/__init__.py \ gi/overrides/__init__.py \ gi/overrides/Regress.py \ @@ -358,13 +365,14 @@ RUN_TESTS_ENV_VARS = \ GI_TYPELIB_PATH=$(builddir):$$GI_TYPELIB_PATH \ XDG_DATA_DIRS=$$XDG_DATA_DIRS:/usr/share \ MALLOC_PERTURB_=85 \ + MALLOC_CHECK_=3 \ TESTS_BUILDDIR=$(builddir) all: all-am .SUFFIXES: .SUFFIXES: .c .la .lo .o .obj .so -$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ @@ -389,9 +397,9 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) +$(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) +$(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): @@ -696,7 +704,7 @@ uninstall-am: # This is a hack to make sure a shared library is built libgimarshallingtests.la: $(libgimarshallingtests_la_OBJECTS) $(libgimarshallingtests_la_DEPENDENCIES) - $(LINK) -rpath $(pkgpyexecdir) $(libgimarshallingtests_la_LDFLAGS) $(libgimarshallingtests_la_OBJECTS) $(libgimarhallingtests_la_LIBADD) $(LIBS) + $(AM_V_GEN) $(LINK) -rpath $(pkgpyexecdir) $(libgimarshallingtests_la_LDFLAGS) $(libgimarshallingtests_la_OBJECTS) $(libgimarhallingtests_la_LIBADD) $(LIBS) GIMarshallingTests-1.0.gir: libgimarshallingtests.la Makefile $(AM_V_GEN) g-ir-scanner --include=Gio-2.0 \ @@ -710,7 +718,7 @@ GIMarshallingTests-1.0.typelib: GIMarshallingTests-1.0.gir Makefile $(AM_V_GEN) g-ir-compiler $< -o $@ @ENABLE_CAIRO_TRUE@libregress.la: $(libregress_la_OBJECTS) $(libregress_la_DEPENDENCIES) -@ENABLE_CAIRO_TRUE@ $(LINK) -rpath $(pkgpyexecdir) $(libregress_la_LDFLAGS) $(libregress_la_OBJECTS) $(libregress_la_LIBADD) $(LIBS) +@ENABLE_CAIRO_TRUE@ $(AM_V_GEN) $(LINK) -rpath $(pkgpyexecdir) $(libregress_la_LDFLAGS) $(libregress_la_OBJECTS) $(libregress_la_LIBADD) $(LIBS) # g-i doesn't ship these as shared libraries anymore; we build them here @ENABLE_CAIRO_TRUE@Regress-1.0.gir: libregress.la Makefile @@ -729,7 +737,7 @@ gschemas.compiled: org.gnome.test.gschema.xml # This is a hack to make sure a shared library is built testhelper.la: $(testhelper_la_OBJECTS) $(testhelper_la_DEPENDENCIES) - $(LINK) -rpath $(pkgpyexecdir) $(testhelper_la_LDFLAGS) $(testhelper_la_OBJECTS) $(testhelper_la_LIBADD) $(LIBS) + $(AM_V_GEN) $(LINK) -rpath $(pkgpyexecdir) $(testhelper_la_LDFLAGS) $(testhelper_la_OBJECTS) $(testhelper_la_LIBADD) $(LIBS) .la.so: test -L $@ || $(LN_S) .libs/$@ $@ @@ -739,13 +747,18 @@ all: $(LTLIBRARIES:.la=.so) clean-local: rm -f $(LTLIBRARIES:.la=.so) file.txt~ +# pygtkcompat tests need to be run in a separate process as they +# clobber global name space check-local: $(LTLIBRARIES:.la=.so) $(test_typelibs) gschemas.compiled @echo " CHECK Pyflakes" @if type pyflakes >/dev/null 2>&1; then pyflakes $(top_srcdir); else echo "skipped, pyflakes not installed"; fi - @echo " CHECK PEP8" - @if type pep8 >/dev/null 2>&1; then pep8 --ignore=E501,E123,E124 --repeat --show-source $(top_srcdir); else echo "skipped, pep8 not installed"; fi + @if test -z "$$SKIP_PEP8"; then \ + echo " CHECK PEP8"; \ + if type pep8 >/dev/null 2>&1; then pep8 --ignore=E501,E123,E124 --repeat --show-source $(top_srcdir); else echo "skipped, pep8 not installed"; fi; \ + fi export `$(DBUS_LAUNCH)` && \ - $(RUN_TESTS_ENV_VARS) $(EXEC_NAME) $(PYTHON) -Wd -Werror::PendingDeprecationWarning -Werror::DeprecationWarning $(srcdir)/runtests.py; rc=$$?; \ + $(RUN_TESTS_ENV_VARS) $(EXEC_NAME) $(PYTHON) -Wd -Werror::PendingDeprecationWarning -Werror::DeprecationWarning -Werror::RuntimeWarning $(srcdir)/runtests.py; rc=$$?; \ + [ "$$rc" -ne 0 ] || [ -n "$$TEST_NAMES" ] || { TEST_NAMES=compat_test_pygtk $(RUN_TESTS_ENV_VARS) $(EXEC_NAME) $(PYTHON) -Wd -Werror::PendingDeprecationWarning -Werror::DeprecationWarning -Werror::RuntimeWarning $(srcdir)/runtests.py; rc=$$?; }; \ kill $$DBUS_SESSION_BUS_PID; \ exit $$rc diff --git a/tests/test_pygtkcompat.py b/tests/compat_test_pygtk.py index 5fabf2a..10be6a3 100644 --- a/tests/test_pygtkcompat.py +++ b/tests/compat_test_pygtk.py @@ -12,10 +12,10 @@ try: from gi.repository import Gtk (Atk, Gtk, Pango) # pyflakes - import gi.pygtkcompat + import pygtkcompat - gi.pygtkcompat.enable() - gi.pygtkcompat.enable_gtk(version='3.0') + pygtkcompat.enable() + pygtkcompat.enable_gtk(version='3.0') import atk import pango @@ -28,54 +28,54 @@ except ImportError: @unittest.skipUnless(Gtk, 'Gtk not available') class TestATKCompat(unittest.TestCase): - def testObject(self): + def test_object(self): self.assertTrue(hasattr(atk, 'Object')) @unittest.skipUnless(Gtk, 'Gtk not available') class TestPangoCompat(unittest.TestCase): - def testLayout(self): + def test_layout(self): self.assertTrue(hasattr(pango, 'Layout')) @unittest.skipUnless(Gtk, 'Gtk not available') class TestPangoCairoCompat(unittest.TestCase): - def testErrorUnderlinePath(self): + def test_error_underline_path(self): self.assertTrue(hasattr(pangocairo, 'error_underline_path')) @unittest.skipUnless(Gtk, 'Gtk not available') class TestGTKCompat(unittest.TestCase): - def testButtons(self): + def test_buttons(self): self.assertEqual(Gdk._2BUTTON_PRESS, 5) self.assertEqual(Gdk.BUTTON_PRESS, 4) - def testEnums(self): + def test_enums(self): self.assertEqual(gtk.WINDOW_TOPLEVEL, Gtk.WindowType.TOPLEVEL) self.assertEqual(gtk.PACK_START, Gtk.PackType.START) - def testFlags(self): + def test_flags(self): self.assertEqual(gtk.EXPAND, Gtk.AttachOptions.EXPAND) self.assertEqual(gtk.gdk.SHIFT_MASK, Gdk.ModifierType.SHIFT_MASK) - def testKeysyms(self): + def test_keysyms(self): import gtk.keysyms self.assertEqual(gtk.keysyms.Escape, Gdk.KEY_Escape) self.assertTrue(gtk.keysyms._0, Gdk.KEY_0) - def testStyle(self): + def test_style(self): widget = gtk.Button() self.assertTrue(isinstance(widget.style.base[gtk.STATE_NORMAL], gtk.gdk.Color)) - def testAlignment(self): + def test_alignment(self): a = gtk.Alignment() self.assertEqual(a.props.xalign, 0.0) self.assertEqual(a.props.yalign, 0.0) self.assertEqual(a.props.xscale, 0.0) self.assertEqual(a.props.yscale, 0.0) - def testBox(self): + def test_box(self): box = gtk.Box() child = gtk.Button() @@ -94,7 +94,7 @@ class TestGTKCompat(unittest.TestCase): self.assertEqual(padding, 0) self.assertEqual(pack_type, gtk.PACK_END) - def testComboBoxEntry(self): + def test_combobox_entry(self): liststore = gtk.ListStore(int, str) liststore.append((1, 'One')) liststore.append((2, 'Two')) @@ -102,8 +102,10 @@ class TestGTKCompat(unittest.TestCase): # might cause a Pango warning, do not break on this old_mask = GLib.log_set_always_fatal( GLib.LogLevelFlags.LEVEL_CRITICAL | GLib.LogLevelFlags.LEVEL_ERROR) - combo = gtk.ComboBoxEntry(model=liststore) - GLib.log_set_always_fatal(old_mask) + try: + combo = gtk.ComboBoxEntry(model=liststore) + finally: + GLib.log_set_always_fatal(old_mask) combo.set_text_column(1) combo.set_active(0) self.assertEqual(combo.get_text_column(), 1) @@ -120,18 +122,18 @@ class TestGTKCompat(unittest.TestCase): self.assertEqual(combo.get_text_column(), 1) self.assertEqual(combo.get_child().get_text(), 'One') - def testSizeRequest(self): + def test_size_request(self): box = gtk.Box() self.assertEqual(box.size_request(), [0, 0]) - def testPixbuf(self): + def test_pixbuf(self): gtk.gdk.Pixbuf() - def testPixbufLoader(self): + def test_pixbuf_loader(self): loader = gtk.gdk.PixbufLoader('png') loader.close() - def testPixbufFormats(self): + def test_pixbuf_formats(self): formats = gtk.gdk.pixbuf_get_formats() self.assertEqual(type(formats[0]), dict) self.assertTrue('name' in formats[0]) @@ -139,7 +141,7 @@ class TestGTKCompat(unittest.TestCase): self.assertTrue('mime_types' in formats[0]) self.assertEqual(type(formats[0]['extensions']), list) - def testGdkWindow(self): + def test_gdk_window(self): w = gtk.Window() w.realize() self.assertEqual(w.get_window().get_origin(), (0, 0)) diff --git a/tests/runtests.py b/tests/runtests.py index c518fcc..9f3a7ec 100755 --- a/tests/runtests.py +++ b/tests/runtests.py @@ -34,6 +34,9 @@ if sys.version_info[:2] == (2, 6): unittest.TestCase.assertGreater = assertGreater unittest.TestCase.assertIsInstance = assertIsInstance +if sys.version_info[:2] == (2, 7): + unittest.TestCase.assertRaisesRegex = unittest.TestCase.assertRaisesRegexp + if '--help' in sys.argv: print("Usage: ./runtests.py <testfiles>") sys.exit(0) diff --git a/tests/test_atoms.py b/tests/test_atoms.py index a59d15a..1561dbd 100644 --- a/tests/test_atoms.py +++ b/tests/test_atoms.py @@ -13,6 +13,18 @@ class TestGdkAtom(unittest.TestCase): atom = Gdk.Atom.intern('my_string', False) self.assertEqual(atom.name(), 'my_string') + def test_str(self): + atom = Gdk.Atom.intern('my_string', False) + self.assertEqual(str(atom), 'my_string') + + self.assertEqual(str(Gdk.SELECTION_CLIPBOARD), 'CLIPBOARD') + + def test_repr(self): + atom = Gdk.Atom.intern('my_string', False) + self.assertEqual(repr(atom), 'Gdk.Atom<my_string>') + + self.assertEqual(repr(Gdk.SELECTION_CLIPBOARD), 'Gdk.Atom<CLIPBOARD>') + def test_in_single(self): a_selection = Gdk.Atom.intern('test_clipboard', False) clipboard = Gtk.Clipboard.get(a_selection) @@ -36,6 +48,25 @@ class TestGdkAtom(unittest.TestCase): self.assertTrue(Gtk.targets_include_image([a_jpeg], False)) self.assertTrue(Gtk.targets_include_image([a_jpeg, a_plain], False)) + def test_out_array(self): + a_selection = Gdk.Atom.intern('my_clipboard', False) + clipboard = Gtk.Clipboard.get(a_selection) + + # empty + (res, targets) = clipboard.wait_for_targets() + self.assertEqual(res, False) + self.assertEqual(targets, []) + + # text + clipboard.set_text('hello', 5) + (res, targets) = clipboard.wait_for_targets() + self.assertEqual(res, True) + self.assertNotEqual(targets, []) + self.assertEqual(type(targets[0]), Gdk.Atom) + names = [t.name() for t in targets] + self.assertFalse(None in names, names) + self.assertTrue('TEXT' in names, names) + def test_out_glist(self): display = Gdk.Display.get_default() dm = display.get_device_manager() diff --git a/tests/test_everything.py b/tests/test_everything.py index 4c4535f..f1f14b7 100644 --- a/tests/test_everything.py +++ b/tests/test_everything.py @@ -4,14 +4,10 @@ import unittest import traceback +import ctypes import warnings -import gc -gc - import sys -from sys import getrefcount -import copy try: import cairo has_cairo = True @@ -37,6 +33,17 @@ else: UNICHAR = "♥" +class RawGList(ctypes.Structure): + _fields_ = [('data', ctypes.c_void_p), + ('next', ctypes.c_void_p), + ('prev', ctypes.c_void_p)] + + @classmethod + def from_wrapped(cls, obj): + offset = sys.getsizeof(object()) # size of PyObject_HEAD + return ctypes.POINTER(cls).from_address(id(obj) + offset) + + @unittest.skipUnless(has_cairo, 'built without cairo support') class TestEverything(unittest.TestCase): @@ -73,6 +80,123 @@ class TestEverything(unittest.TestCase): self.assertEqual(surface.get_width(), 10) self.assertEqual(surface.get_height(), 10) + def test_bool(self): + self.assertEqual(Everything.test_boolean(False), False) + self.assertEqual(Everything.test_boolean(True), True) + self.assertEqual(Everything.test_boolean('hello'), True) + self.assertEqual(Everything.test_boolean(''), False) + + self.assertEqual(Everything.test_boolean_true(True), True) + self.assertEqual(Everything.test_boolean_false(False), False) + + def test_int8(self): + self.assertEqual(Everything.test_int8(GObject.G_MAXINT8), + GObject.G_MAXINT8) + self.assertEqual(Everything.test_int8(GObject.G_MININT8), + GObject.G_MININT8) + self.assertRaises(ValueError, Everything.test_int8, GObject.G_MAXINT8 + 1) + + self.assertEqual(Everything.test_uint8(GObject.G_MAXUINT8), + GObject.G_MAXUINT8) + self.assertEqual(Everything.test_uint8(0), 0) + self.assertRaises(ValueError, Everything.test_uint8, -1) + self.assertRaises(ValueError, Everything.test_uint8, GObject.G_MAXUINT8 + 1) + + def test_int16(self): + self.assertEqual(Everything.test_int16(GObject.G_MAXINT16), + GObject.G_MAXINT16) + self.assertEqual(Everything.test_int16(GObject.G_MININT16), + GObject.G_MININT16) + self.assertRaises(ValueError, Everything.test_int16, GObject.G_MAXINT16 + 1) + + self.assertEqual(Everything.test_uint16(GObject.G_MAXUINT16), + GObject.G_MAXUINT16) + self.assertEqual(Everything.test_uint16(0), 0) + self.assertRaises(ValueError, Everything.test_uint16, -1) + self.assertRaises(ValueError, Everything.test_uint16, GObject.G_MAXUINT16 + 1) + + def test_int32(self): + self.assertEqual(Everything.test_int32(GObject.G_MAXINT32), + GObject.G_MAXINT32) + self.assertEqual(Everything.test_int32(GObject.G_MININT32), + GObject.G_MININT32) + self.assertRaises(ValueError, Everything.test_int32, GObject.G_MAXINT32 + 1) + + self.assertEqual(Everything.test_uint32(GObject.G_MAXUINT32), + GObject.G_MAXUINT32) + self.assertEqual(Everything.test_uint32(0), 0) + self.assertRaises(ValueError, Everything.test_uint32, -1) + self.assertRaises(ValueError, Everything.test_uint32, GObject.G_MAXUINT32 + 1) + + def test_int64(self): + self.assertEqual(Everything.test_int64(GObject.G_MAXINT64), + GObject.G_MAXINT64) + self.assertEqual(Everything.test_int64(GObject.G_MININT64), + GObject.G_MININT64) + self.assertRaises(ValueError, Everything.test_int64, GObject.G_MAXINT64 + 1) + + self.assertEqual(Everything.test_uint64(GObject.G_MAXUINT64), + GObject.G_MAXUINT64) + self.assertEqual(Everything.test_uint64(0), 0) + self.assertRaises(ValueError, Everything.test_uint64, -1) + self.assertRaises(ValueError, Everything.test_uint64, GObject.G_MAXUINT64 + 1) + + def test_int(self): + self.assertEqual(Everything.test_int(GObject.G_MAXINT), + GObject.G_MAXINT) + self.assertEqual(Everything.test_int(GObject.G_MININT), + GObject.G_MININT) + self.assertRaises(ValueError, Everything.test_int, GObject.G_MAXINT + 1) + + self.assertEqual(Everything.test_uint(GObject.G_MAXUINT), + GObject.G_MAXUINT) + self.assertEqual(Everything.test_uint(0), 0) + self.assertRaises(ValueError, Everything.test_uint, -1) + self.assertRaises(ValueError, Everything.test_uint, GObject.G_MAXUINT + 1) + + def test_short(self): + self.assertEqual(Everything.test_short(GObject.G_MAXSHORT), + GObject.G_MAXSHORT) + self.assertEqual(Everything.test_short(GObject.G_MINSHORT), + GObject.G_MINSHORT) + self.assertRaises(ValueError, Everything.test_short, GObject.G_MAXSHORT + 1) + + self.assertEqual(Everything.test_ushort(GObject.G_MAXUSHORT), + GObject.G_MAXUSHORT) + self.assertEqual(Everything.test_ushort(0), 0) + self.assertRaises(ValueError, Everything.test_ushort, -1) + self.assertRaises(ValueError, Everything.test_ushort, GObject.G_MAXUSHORT + 1) + + def test_long(self): + self.assertEqual(Everything.test_long(GObject.G_MAXLONG), + GObject.G_MAXLONG) + self.assertEqual(Everything.test_long(GObject.G_MINLONG), + GObject.G_MINLONG) + self.assertRaises(ValueError, Everything.test_long, GObject.G_MAXLONG + 1) + + self.assertEqual(Everything.test_ulong(GObject.G_MAXULONG), + GObject.G_MAXULONG) + self.assertEqual(Everything.test_ulong(0), 0) + self.assertRaises(ValueError, Everything.test_ulong, -1) + self.assertRaises(ValueError, Everything.test_ulong, GObject.G_MAXULONG + 1) + + def test_size(self): + self.assertEqual(Everything.test_ssize(GObject.G_MAXSSIZE), + GObject.G_MAXSSIZE) + self.assertEqual(Everything.test_ssize(GObject.G_MINSSIZE), + GObject.G_MINSSIZE) + self.assertRaises(ValueError, Everything.test_ssize, GObject.G_MAXSSIZE + 1) + + self.assertEqual(Everything.test_size(GObject.G_MAXSIZE), + GObject.G_MAXSIZE) + self.assertEqual(Everything.test_size(0), 0) + self.assertRaises(ValueError, Everything.test_size, -1) + self.assertRaises(ValueError, Everything.test_size, GObject.G_MAXSIZE + 1) + + def test_timet(self): + self.assertEqual(Everything.test_timet(42), 42) + self.assertRaises(ValueError, Everything.test_timet, GObject.G_MAXUINT64 + 1) + def test_unichar(self): self.assertEqual("c", Everything.test_unichar("c")) @@ -82,6 +206,96 @@ class TestEverything(unittest.TestCase): self.assertRaises(TypeError, Everything.test_unichar, "") self.assertRaises(TypeError, Everything.test_unichar, "morethanonechar") + def test_float(self): + self.assertEqual(Everything.test_float(GObject.G_MAXFLOAT), + GObject.G_MAXFLOAT) + self.assertEqual(Everything.test_float(GObject.G_MINFLOAT), + GObject.G_MINFLOAT) + self.assertRaises(ValueError, Everything.test_float, GObject.G_MAXFLOAT * 2) + + def test_double(self): + self.assertEqual(Everything.test_double(GObject.G_MAXDOUBLE), + GObject.G_MAXDOUBLE) + self.assertEqual(Everything.test_double(GObject.G_MINDOUBLE), + GObject.G_MINDOUBLE) + self.assertRaises(ValueError, Everything.test_double, GObject.G_MAXDOUBLE * 2) + + (two, three) = Everything.test_multi_double_args(2.5) + self.assertAlmostEqual(two, 5.0) + self.assertAlmostEqual(three, 7.5) + + def test_value(self): + self.assertEqual(Everything.test_int_value_arg(GObject.G_MAXINT), GObject.G_MAXINT) + self.assertEqual(Everything.test_value_return(GObject.G_MAXINT), GObject.G_MAXINT) + + def test_variant(self): + v = Everything.test_gvariant_i() + self.assertEqual(v.get_type_string(), 'i') + self.assertEqual(v.get_int32(), 1) + + v = Everything.test_gvariant_s() + self.assertEqual(v.get_type_string(), 's') + self.assertEqual(v.get_string(), 'one') + + v = Everything.test_gvariant_v() + self.assertEqual(v.get_type_string(), 'v') + vi = v.get_variant() + self.assertEqual(vi.get_type_string(), 's') + self.assertEqual(vi.get_string(), 'contents') + + v = Everything.test_gvariant_as() + self.assertEqual(v.get_type_string(), 'as') + self.assertEqual(v.get_strv(), ['one', 'two', 'three']) + + v = Everything.test_gvariant_asv() + self.assertEqual(v.get_type_string(), 'a{sv}') + self.assertEqual(v.lookup_value('nosuchkey', None), None) + name = v.lookup_value('name', None) + self.assertEqual(name.get_string(), 'foo') + timeout = v.lookup_value('timeout', None) + self.assertEqual(timeout.get_int32(), 10) + + def test_string(self): + const_str = b'const \xe2\x99\xa5 utf8' + if sys.version_info >= (3, 0): + const_str = const_str.decode('UTF-8') + noconst_str = 'non' + const_str + + self.assertEqual(Everything.test_utf8_const_return(), const_str) + self.assertEqual(Everything.test_utf8_nonconst_return(), noconst_str) + self.assertEqual(Everything.test_utf8_out(), noconst_str) + + Everything.test_utf8_const_in(const_str) + self.assertEqual(Everything.test_utf8_inout(const_str), noconst_str) + + self.assertEqual(Everything.test_filename_return(), ['åäö', '/etc/fstab']) + + # returns g_utf8_strlen() in out argument + self.assertEqual(Everything.test_int_out_utf8(''), 0) + self.assertEqual(Everything.test_int_out_utf8('hello world'), 11) + self.assertEqual(Everything.test_int_out_utf8('åäö'), 3) + + self.assertEqual(Everything.test_utf8_out_out(), ('first', 'second')) + self.assertEqual(Everything.test_utf8_out_nonconst_return(), ('first', 'second')) + + def test_enum(self): + self.assertEqual(Everything.test_enum_param(Everything.TestEnum.VALUE1), 'value1') + self.assertEqual(Everything.test_enum_param(Everything.TestEnum.VALUE3), 'value3') + self.assertRaises(TypeError, Everything.test_enum_param, 'hello') + + # FIXME: ValueError: invalid enum value: 2147483648 + @unittest.expectedFailure + def test_enum_unsigned(self): + self.assertEqual(Everything.test_unsigned_enum_param(Everything.TestEnumUnsigned.VALUE1), 'value1') + self.assertEqual(Everything.test_unsigned_enum_param(Everything.TestEnumUnsigned.VALUE3), 'value3') + self.assertRaises(TypeError, Everything.test_unsigned_enum_param, 'hello') + + def test_flags(self): + result = Everything.global_get_flags_out() + # assert that it's not an int + self.assertEqual(type(result), Everything.TestFlags) + self.assertEqual(result, Everything.TestFlags.FLAG1 | Everything.TestFlags.FLAG3) + def test_floating(self): e = Everything.TestFloating() self.assertEqual(e.__grefcount__, 1) @@ -121,6 +335,9 @@ class TestEverything(unittest.TestCase): self.assertEqual(struct_b.nested_a.some_double, struct_b_clone.nested_a.some_double) self.assertEqual(struct_b.nested_a.some_enum, struct_b_clone.nested_a.some_enum) + struct_a = Everything.test_struct_a_parse('ignored') + self.assertEqual(struct_a.some_int, 23) + def test_wrong_type_of_arguments(self): try: Everything.test_int8() @@ -173,6 +390,34 @@ class TestEverything(unittest.TestCase): # test that there are no duplicates returned self.assertEqual(len(attr_list), len(set(attr_list))) + def test_array(self): + self.assertEqual(Everything.test_array_int_in([]), 0) + self.assertEqual(Everything.test_array_int_in([1, 5, -2]), 4) + self.assertEqual(Everything.test_array_int_out(), [0, 1, 2, 3, 4]) + self.assertEqual(Everything.test_array_int_full_out(), [0, 1, 2, 3, 4]) + self.assertEqual(Everything.test_array_int_none_out(), [1, 2, 3, 4, 5]) + self.assertEqual(Everything.test_array_int_inout([1, 5, 42, -8]), [6, 43, -7]) + + if sys.version_info >= (3, 0): + self.assertEqual(Everything.test_array_gint8_in(b'\x01\x03\x05'), 9) + self.assertEqual(Everything.test_array_gint8_in([1, 3, 5, -50]), -41) + self.assertEqual(Everything.test_array_gint16_in([256, 257, -1000, 10000]), 9513) + self.assertEqual(Everything.test_array_gint32_in([30000, 1, -2]), 29999) + self.assertEqual(Everything.test_array_gint64_in([2 ** 33, 2 ** 34]), 2 ** 33 + 2 ** 34) + + self.assertEqual(Everything.test_array_gtype_in( + [GObject.TYPE_STRING, GObject.TYPE_UINT64, GObject.TYPE_VARIANT]), + '[gchararray,guint64,GVariant,]') + + def test_array_fixed_size(self): + # fixed length of 5 + self.assertEqual(Everything.test_array_fixed_size_int_in([1, 2, -10, 5, 3]), 1) + self.assertRaises(ValueError, Everything.test_array_fixed_size_int_in, [1, 2, 3, 4]) + self.assertRaises(ValueError, Everything.test_array_fixed_size_int_in, [1, 2, 3, 4, 5, 6]) + + self.assertEqual(Everything.test_array_fixed_size_int_out(), [0, 1, 2, 3, 4]) + self.assertEqual(Everything.test_array_fixed_size_int_return(), [0, 1, 2, 3, 4]) + def test_ptrarray(self): # transfer container result = Everything.test_garray_container_return() @@ -184,7 +429,43 @@ class TestEverything(unittest.TestCase): self.assertEqual(result, ['regress']) result = None + def test_strv(self): + self.assertEqual(Everything.test_strv_out(), ['thanks', 'for', 'all', 'the', 'fish']) + self.assertEqual(Everything.test_strv_out_c(), ['thanks', 'for', 'all', 'the', 'fish']) + self.assertEqual(Everything.test_strv_out_container(), ['1', '2', '3']) + self.assertEqual(Everything.test_strv_outarg(), ['1', '2', '3']) + + self.assertEqual(Everything.test_strv_in_gvalue(), ['one', 'two', 'three']) + + Everything.test_strv_in(['1', '2', '3']) + + def test_glist(self): + self.assertEqual(Everything.test_glist_nothing_return(), ['1', '2', '3']) + self.assertEqual(Everything.test_glist_nothing_return2(), ['1', '2', '3']) + self.assertEqual(Everything.test_glist_container_return(), ['1', '2', '3']) + self.assertEqual(Everything.test_glist_everything_return(), ['1', '2', '3']) + + Everything.test_glist_nothing_in(['1', '2', '3']) + Everything.test_glist_nothing_in2(['1', '2', '3']) + + def test_gslist(self): + self.assertEqual(Everything.test_gslist_nothing_return(), ['1', '2', '3']) + self.assertEqual(Everything.test_gslist_nothing_return2(), ['1', '2', '3']) + self.assertEqual(Everything.test_gslist_container_return(), ['1', '2', '3']) + self.assertEqual(Everything.test_gslist_everything_return(), ['1', '2', '3']) + + Everything.test_gslist_nothing_in(['1', '2', '3']) + Everything.test_gslist_nothing_in2(['1', '2', '3']) + def test_hash_return(self): + expected = {'foo': 'bar', 'baz': 'bat', 'qux': 'quux'} + + self.assertEqual(Everything.test_ghash_null_return(), None) + self.assertEqual(Everything.test_ghash_nothing_return(), expected) + self.assertEqual(Everything.test_ghash_nothing_return(), expected) + self.assertEqual(Everything.test_ghash_container_return(), expected) + self.assertEqual(Everything.test_ghash_everything_return(), expected) + result = Everything.test_ghash_gvalue_return() self.assertEqual(result['integer'], 12) self.assertEqual(result['boolean'], True) @@ -194,6 +475,11 @@ class TestEverything(unittest.TestCase): self.assertEqual(result['enum'], Everything.TestEnum.VALUE2) result = None + # FIXME: CRITICAL **: Unsupported type ghash + def disabled_test_hash_return_nested(self): + self.assertEqual(Everything.test_ghash_nested_everything_return(), {}) + self.assertEqual(Everything.test_ghash_nested_everything_return2(), {}) + def test_hash_in(self): # specifying a simple string array for "strings" does not work due to # https://bugzilla.gnome.org/show_bug.cgi?id=666636 @@ -201,6 +487,11 @@ class TestEverything(unittest.TestCase): class GStrv(list): __gtype__ = GObject.TYPE_STRV + expected = {'foo': 'bar', 'baz': 'bat', 'qux': 'quux'} + + Everything.test_ghash_nothing_in(expected) + Everything.test_ghash_nothing_in2(expected) + data = {'integer': 12, 'boolean': True, 'string': 'some text', @@ -212,24 +503,24 @@ class TestEverything(unittest.TestCase): data = None def test_struct_gpointer(self): - l1 = GLib.List() - self.assertEqual(l1.data, None) - init_refcount = getrefcount(l1) + glist = GLib.List() + raw = RawGList.from_wrapped(glist) + + self.assertEqual(glist.data, None) + self.assertEqual(raw.contents.data, None) - l1.data = 'foo' - self.assertEqual(l1.data, 'foo') + glist.data = 123 + self.assertEqual(glist.data, 123) + self.assertEqual(raw.contents.data, 123) - l2 = l1 - self.assertEqual(l1.data, l2.data) - self.assertEqual(getrefcount(l1), init_refcount + 1) + glist.data = None + self.assertEqual(glist.data, None) + self.assertEqual(raw.contents.data, None) - l3 = copy.copy(l1) - l3.data = 'bar' - self.assertEqual(l1.data, 'foo') - self.assertEqual(l2.data, 'foo') - self.assertEqual(l3.data, 'bar') - self.assertEqual(getrefcount(l1), init_refcount + 1) - self.assertEqual(getrefcount(l3), init_refcount) + # Setting to anything other than an int should raise + self.assertRaises(TypeError, setattr, glist.data, 'nan') + self.assertRaises(TypeError, setattr, glist.data, object()) + self.assertRaises(TypeError, setattr, glist.data, 123.321) def test_struct_opaque(self): # we should get a sensible error message @@ -375,13 +666,37 @@ class TestCallbacks(unittest.TestCase): def callback(): TestCallbacks.called += 1 - return 0 + return TestCallbacks.called refcount = sys.getrefcount(callback) - Everything.test_multi_callback(callback) + result = Everything.test_multi_callback(callback) + # first callback should give 1, second 2, and the function sums them up + self.assertEqual(result, 3) self.assertEqual(TestCallbacks.called, 2) self.assertEqual(sys.getrefcount(callback), refcount) + # FIXME: TypeError: callback() takes 2 positional arguments but 4 were given + # does not remove the array length arguments + @unittest.expectedFailure + def test_callback_scope_call_array(self): + # This tests a callback that gets called multiple times from a + # single scope call in python with array arguments + TestCallbacks.callargs = [] + + # works with: + #def callback(one, one_length, two, two_length): + def callback(one, two): + TestCallbacks.callargs.append((one, two)) + return len(TestCallbacks.callargs) + + refcount = sys.getrefcount(callback) + result = Everything.test_array_callback(callback) + # first callback should give 1, second 2, and the function sums them up + self.assertEqual(result, 3) + self.assertEqual(TestCallbacks.callargs, + [([-1, 0, 1, 2], ['one', 'two', 'three'])] * 2) + self.assertEqual(sys.getrefcount(callback), refcount) + def test_callback_userdata(self): TestCallbacks.called = 0 @@ -556,6 +871,16 @@ class TestCallbacks(unittest.TestCase): @unittest.skipUnless(has_cairo, 'built without cairo support') class TestClosures(unittest.TestCase): + def test_no_arg(self): + def callback(): + self.called = True + return 42 + + self.called = False + result = Everything.test_closure(callback) + self.assertTrue(self.called) + self.assertEqual(result, 42) + def test_int_arg(self): def callback(num): self.called = True @@ -589,6 +914,22 @@ class TestClosures(unittest.TestCase): self.assertRaises(TypeError, Everything.test_closure_variant, callback, 'foo') self.assertFalse(self.called) + def test_variant_wrong_return_type(self): + def callback(variant): + return 'no_variant' + + # reset last error + sys.last_type = None + + # this does not directly raise an exception (see + # https://bugzilla.gnome.org/show_bug.cgi?id=616279) + result = Everything.test_closure_variant(callback, GLib.Variant('i', 42)) + # ... but the result shouldn't be a string + self.assertEqual(result, None) + # and the error should be shown + self.assertEqual(sys.last_type, TypeError) + self.assertTrue('return value' in str(sys.last_value), sys.last_value) + @unittest.skipUnless(has_cairo, 'built without cairo support') class TestProperties(unittest.TestCase): @@ -647,7 +988,27 @@ class TestProperties(unittest.TestCase): self.assertTrue(isinstance(object_.props.boxed, Everything.TestBoxed)) self.assertEqual(object_.props.boxed.some_int8, 42) + def test_boxed_alternative_constructor(self): + boxed = Everything.TestBoxed.new_alternative_constructor1(5) + self.assertEqual(boxed.some_int8, 5) + + boxed = Everything.TestBoxed.new_alternative_constructor2(5, 3) + self.assertEqual(boxed.some_int8, 8) + + boxed = Everything.TestBoxed.new_alternative_constructor3("-3") + self.assertEqual(boxed.some_int8, -3) + def test_boxed_equality(self): + boxed42 = Everything.TestBoxed.new_alternative_constructor1(42) + boxed5 = Everything.TestBoxed.new_alternative_constructor1(5) + boxed42_2 = Everything.TestBoxed.new_alternative_constructor2(41, 1) + + self.assertFalse(boxed42.equals(boxed5)) + self.assertTrue(boxed42.equals(boxed42_2)) + self.assertTrue(boxed42_2.equals(boxed42)) + self.assertTrue(boxed42.equals(boxed42)) + + def test_boxed_c_equality(self): boxed = Everything.TestBoxedC() # TestBoxedC uses refcounting, so we know that # the pointer is the same when copied diff --git a/tests/test_gi.py b/tests/test_gi.py index 51cb6f9..4837204 100644 --- a/tests/test_gi.py +++ b/tests/test_gi.py @@ -11,6 +11,7 @@ import os import locale import subprocess import gc +import weakref from io import StringIO, BytesIO import gi @@ -18,7 +19,7 @@ from gi.repository import GObject, GLib, Gio from gi.repository import GIMarshallingTests -from compathelper import _bytes +from compathelper import _bytes, _unicode if sys.version_info < (3, 0): CONSTANT_UTF8 = "const \xe2\x99\xa5 utf8" @@ -64,6 +65,15 @@ class TestConstant(unittest.TestCase): def test_constant_number(self): self.assertEqual(CONSTANT_NUMBER, GIMarshallingTests.CONSTANT_NUMBER) + def test_min_max_int(self): + self.assertEqual(GLib.MAXINT32, 2 ** 31 - 1) + self.assertEqual(GLib.MININT32, -2 ** 31) + self.assertEqual(GLib.MAXUINT32, 2 ** 32 - 1) + + self.assertEqual(GLib.MAXINT64, 2 ** 63 - 1) + self.assertEqual(GLib.MININT64, -2 ** 63) + self.assertEqual(GLib.MAXUINT64, 2 ** 64 - 1) + class TestBoolean(unittest.TestCase): @@ -325,8 +335,8 @@ class TestUInt64(unittest.TestCase): class TestShort(unittest.TestCase): - MAX = GObject.constants.G_MAXSHORT - MIN = GObject.constants.G_MINSHORT + MAX = GObject.G_MAXSHORT + MIN = GObject.G_MINSHORT def test_short_return(self): self.assertEqual(self.MAX, GIMarshallingTests.short_return_max()) @@ -358,7 +368,7 @@ class TestShort(unittest.TestCase): class TestUShort(unittest.TestCase): - MAX = GObject.constants.G_MAXUSHORT + MAX = GObject.G_MAXUSHORT def test_ushort_return(self): self.assertEqual(self.MAX, GIMarshallingTests.ushort_return()) @@ -384,8 +394,8 @@ class TestUShort(unittest.TestCase): class TestInt(unittest.TestCase): - MAX = GObject.constants.G_MAXINT - MIN = GObject.constants.G_MININT + MAX = GObject.G_MAXINT + MIN = GObject.G_MININT def test_int_return(self): self.assertEqual(self.MAX, GIMarshallingTests.int_return_max()) @@ -418,7 +428,7 @@ class TestInt(unittest.TestCase): class TestUInt(unittest.TestCase): - MAX = GObject.constants.G_MAXUINT + MAX = GObject.G_MAXUINT def test_uint_return(self): self.assertEqual(self.MAX, GIMarshallingTests.uint_return()) @@ -444,8 +454,8 @@ class TestUInt(unittest.TestCase): class TestLong(unittest.TestCase): - MAX = GObject.constants.G_MAXLONG - MIN = GObject.constants.G_MINLONG + MAX = GObject.G_MAXLONG + MIN = GObject.G_MINLONG def test_long_return(self): self.assertEqual(self.MAX, GIMarshallingTests.long_return_max()) @@ -477,7 +487,7 @@ class TestLong(unittest.TestCase): class TestULong(unittest.TestCase): - MAX = GObject.constants.G_MAXULONG + MAX = GObject.G_MAXULONG def test_ulong_return(self): self.assertEqual(self.MAX, GIMarshallingTests.ulong_return()) @@ -503,8 +513,8 @@ class TestULong(unittest.TestCase): class TestSSize(unittest.TestCase): - MAX = GObject.constants.G_MAXLONG - MIN = GObject.constants.G_MINLONG + MAX = GObject.G_MAXLONG + MIN = GObject.G_MINLONG def test_ssize_return(self): self.assertEqual(self.MAX, GIMarshallingTests.ssize_return_max()) @@ -536,7 +546,7 @@ class TestSSize(unittest.TestCase): class TestSize(unittest.TestCase): - MAX = GObject.constants.G_MAXULONG + MAX = GObject.G_MAXULONG def test_size_return(self): self.assertEqual(self.MAX, GIMarshallingTests.size_return()) @@ -560,10 +570,26 @@ class TestSize(unittest.TestCase): self.assertEqual(0, GIMarshallingTests.size_inout(Number(self.MAX))) +class TestTimet(unittest.TestCase): + + def test_time_t_return(self): + self.assertEqual(1234567890, GIMarshallingTests.time_t_return()) + + def test_time_t_in(self): + GIMarshallingTests.time_t_in(1234567890) + self.assertRaises(TypeError, GIMarshallingTests.time_t_in, "hello") + + def test_time_t_out(self): + self.assertEqual(1234567890, GIMarshallingTests.time_t_out()) + + def test_time_t_inout(self): + self.assertEqual(0, GIMarshallingTests.time_t_inout(1234567890)) + + class TestFloat(unittest.TestCase): - MAX = GObject.constants.G_MAXFLOAT - MIN = GObject.constants.G_MINFLOAT + MAX = GObject.G_MAXFLOAT + MIN = GObject.G_MINFLOAT def test_float_return(self): self.assertAlmostEqual(self.MAX, GIMarshallingTests.float_return()) @@ -582,8 +608,8 @@ class TestFloat(unittest.TestCase): class TestDouble(unittest.TestCase): - MAX = GObject.constants.G_MAXDOUBLE - MIN = GObject.constants.G_MINDOUBLE + MAX = GObject.G_MAXDOUBLE + MIN = GObject.G_MINDOUBLE def test_double_return(self): self.assertAlmostEqual(self.MAX, GIMarshallingTests.double_return()) @@ -662,6 +688,36 @@ class TestUtf8(unittest.TestCase): self.assertEqual("", GIMarshallingTests.utf8_full_inout(CONSTANT_UTF8)) +class TestFilename(unittest.TestCase): + def setUp(self): + self.workdir = tempfile.mkdtemp() + + def tearDown(self): + shutil.rmtree(self.workdir) + + def test_filename_in(self): + fname = os.path.join(self.workdir, _unicode('testäø.txt')) + self.assertRaises(GLib.GError, GLib.file_get_contents, fname) + + with open(fname.encode('UTF-8'), 'wb') as f: + f.write(b'hello world!\n\x01\x02') + + (result, contents) = GLib.file_get_contents(fname) + self.assertEqual(result, True) + self.assertEqual(contents, b'hello world!\n\x01\x02') + + def test_filename_out(self): + self.assertRaises(GLib.GError, GLib.Dir.make_tmp, 'test') + + dirname = GLib.Dir.make_tmp('testäø.XXXXXX') + self.assertTrue('/testäø.' in dirname, dirname) + self.assertTrue(os.path.isdir(dirname)) + os.rmdir(dirname) + + def test_filename_type_error(self): + self.assertRaises(TypeError, GLib.file_get_contents, 23) + + class TestArray(unittest.TestCase): def test_array_fixed_int_return(self): @@ -690,8 +746,16 @@ class TestArray(unittest.TestCase): def test_array_return(self): self.assertEqual([-1, 0, 1, 2], GIMarshallingTests.array_return()) + def test_array_return_etc(self): + self.assertEqual(([5, 0, 1, 9], 14), GIMarshallingTests.array_return_etc(5, 9)) + def test_array_in(self): GIMarshallingTests.array_in(Sequence([-1, 0, 1, 2])) + GIMarshallingTests.array_in_guint64_len(Sequence([-1, 0, 1, 2])) + GIMarshallingTests.array_in_guint8_len(Sequence([-1, 0, 1, 2])) + + def test_array_in_len_before(self): + GIMarshallingTests.array_in_len_before(Sequence([-1, 0, 1, 2])) def test_array_in_len_zero_terminated(self): GIMarshallingTests.array_in_len_zero_terminated(Sequence([-1, 0, 1, 2])) @@ -700,12 +764,22 @@ class TestArray(unittest.TestCase): GIMarshallingTests.array_uint8_in(Sequence([97, 98, 99, 100])) GIMarshallingTests.array_uint8_in(_bytes("abcd")) + def test_array_string_in(self): + GIMarshallingTests.array_string_in(['foo', 'bar']) + def test_array_out(self): self.assertEqual([-1, 0, 1, 2], GIMarshallingTests.array_out()) + def test_array_out_etc(self): + self.assertEqual(([-5, 0, 1, 9], 4), GIMarshallingTests.array_out_etc(-5, 9)) + def test_array_inout(self): self.assertEqual([-2, -1, 0, 1, 2], GIMarshallingTests.array_inout(Sequence([-1, 0, 1, 2]))) + def test_array_inout_etc(self): + self.assertEqual(([-5, -1, 0, 1, 9], 4), + GIMarshallingTests.array_inout_etc(-5, Sequence([-1, 0, 1, 2]), 9)) + def test_method_array_in(self): object_ = GIMarshallingTests.Object() object_.method_array_in(Sequence([-1, 0, 1, 2])) @@ -737,6 +811,29 @@ class TestArray(unittest.TestCase): GIMarshallingTests.array_struct_in([struct1, struct2, struct3]) + def test_array_boxed_struct_take_in(self): + struct1 = GIMarshallingTests.BoxedStruct() + struct1.long_ = 1 + struct2 = GIMarshallingTests.BoxedStruct() + struct2.long_ = 2 + struct3 = GIMarshallingTests.BoxedStruct() + struct3.long_ = 3 + + GIMarshallingTests.array_struct_take_in([struct1, struct2, struct3]) + + self.assertEqual(1, struct1.long_) + + @unittest.expectedFailure + def test_array_boxed_struct_return(self): + (struct1, struct2, struct3) = GIMarshallingTests.array_zero_terminated_return_struct() + self.assertEqual(GIMarshallingTests.BoxedStruct, type(struct1)) + self.assertEqual(GIMarshallingTests.BoxedStruct, type(struct2)) + self.assertEqual(GIMarshallingTests.BoxedStruct, type(struct3)) + # FIXME: gets bogus values + self.assertEqual(42, struct1.long_) + self.assertEqual(43, struct2.long_) + self.assertEqual(44, struct3.long_) + def test_array_simple_struct_in(self): struct1 = GIMarshallingTests.SimpleStruct() struct1.long_ = 1 @@ -777,6 +874,12 @@ class TestArray(unittest.TestCase): def test_array_zero_terminated_inout(self): self.assertEqual(['-1', '0', '1', '2'], GIMarshallingTests.array_zero_terminated_inout(['0', '1', '2'])) + def test_init_function(self): + self.assertEqual((True, []), GIMarshallingTests.init_function([])) + self.assertEqual((True, []), GIMarshallingTests.init_function(['hello'])) + self.assertEqual((True, ['hello']), + GIMarshallingTests.init_function(['hello', 'world'])) + class TestGStrv(unittest.TestCase): @@ -898,6 +1001,60 @@ class TestGPtrArray(unittest.TestCase): self.assertEqual(['-2', '-1', '0', '1'], GIMarshallingTests.gptrarray_utf8_full_inout(['0', '1', '2'])) +class TestGBytes(unittest.TestCase): + def test_gbytes_create(self): + b = GLib.Bytes.new(b'\x00\x01\xFF') + self.assertEqual(3, b.get_size()) + self.assertEqual(b'\x00\x01\xFF', b.get_data()) + + def test_gbytes_create_take(self): + b = GLib.Bytes.new_take(b'\x00\x01\xFF') + self.assertEqual(3, b.get_size()) + self.assertEqual(b'\x00\x01\xFF', b.get_data()) + + @unittest.skipUnless(hasattr(GIMarshallingTests, 'gbytes_full_return'), + 'too old gobject-introspection') + def test_gbytes_full_return(self): + b = GIMarshallingTests.gbytes_full_return() + self.assertEqual(4, b.get_size()) + self.assertEqual(b'\x00\x31\xFF\x33', b.get_data()) + + @unittest.skipUnless(hasattr(GIMarshallingTests, 'gbytes_full_return'), + 'too old gobject-introspection') + def test_gbytes_none_in(self): + b = GIMarshallingTests.gbytes_full_return() + GIMarshallingTests.gbytes_none_in(b) + + def test_compare(self): + a1 = GLib.Bytes.new(b'\x00\x01\xFF') + a2 = GLib.Bytes.new(b'\x00\x01\xFF') + b = GLib.Bytes.new(b'\x00\x01\xFE') + + self.assertTrue(a1.equal(a2)) + self.assertTrue(a2.equal(a1)) + self.assertFalse(a1.equal(b)) + self.assertFalse(b.equal(a2)) + + self.assertEqual(0, a1.compare(a2)) + self.assertEqual(1, a1.compare(b)) + self.assertEqual(-1, b.compare(a1)) + + +class TestGByteArray(unittest.TestCase): + def test_new(self): + ba = GLib.ByteArray.new() + self.assertEqual(b'', ba) + + ba = GLib.ByteArray.new_take(b'\x01\x02\xFF') + self.assertEqual(b'\x01\x02\xFF', ba) + + def test_bytearray_full_return(self): + self.assertEqual(b'\x001\xFF3', GIMarshallingTests.bytearray_full_return()) + + def test_bytearray_none_in(self): + GIMarshallingTests.bytearray_none_in(b'\x00\x31\xFF\x33') + + class TestGList(unittest.TestCase): def test_glist_int_none_return(self): @@ -1061,6 +1218,23 @@ class TestGValue(unittest.TestCase): value.set_int64(GObject.G_MAXINT64) GIMarshallingTests.gvalue_int64_in(value) + def test_gvalue_in_with_type(self): + value = GObject.Value() + value.init(GObject.TYPE_STRING) + value.set_string('foo') + GIMarshallingTests.gvalue_in_with_type(value, GObject.TYPE_STRING) + + value = GObject.Value() + value.init(GIMarshallingTests.Flags.__gtype__) + value.set_flags(GIMarshallingTests.Flags.VALUE1) + GIMarshallingTests.gvalue_in_with_type(value, GObject.TYPE_FLAGS) + + def test_gvalue_in_enum(self): + value = GObject.Value() + value.init(GIMarshallingTests.Enum.__gtype__) + value.set_enum(GIMarshallingTests.Enum.VALUE3) + GIMarshallingTests.gvalue_in_enum(value) + def test_gvalue_out(self): self.assertEqual(42, GIMarshallingTests.gvalue_out()) @@ -1085,6 +1259,88 @@ class TestGValue(unittest.TestCase): values = GIMarshallingTests.return_gvalue_flat_array() self.assertEqual(values, [42, '42', True]) + def test_gvalue_gobject_ref_counts(self): + # Tests a GObject held by a GValue + obj = GObject.Object() + ref = weakref.ref(obj) + grefcount = obj.__grefcount__ + + value = GObject.Value() + value.init(GObject.TYPE_OBJECT) + + # TYPE_OBJECT will inc ref count as it should + value.set_object(obj) + self.assertEqual(obj.__grefcount__, grefcount + 1) + + # multiple set_object should not inc ref count + value.set_object(obj) + self.assertEqual(obj.__grefcount__, grefcount + 1) + + # get_object will re-use the same wrapper as obj + res = value.get_object() + self.assertEqual(obj, res) + self.assertEqual(obj.__grefcount__, grefcount + 1) + + # multiple get_object should not inc ref count + res = value.get_object() + self.assertEqual(obj.__grefcount__, grefcount + 1) + + # deletion of the result and value holder should bring the + # refcount back to where we started + del res + del value + gc.collect() + self.assertEqual(obj.__grefcount__, grefcount) + + del obj + gc.collect() + self.assertEqual(ref(), None) + + def test_gvalue_boxed_ref_counts(self): + # Tests a boxed type wrapping a python object pointer (TYPE_PYOBJECT) + # held by a GValue + class Obj(object): + pass + + obj = Obj() + ref = weakref.ref(obj) + refcount = sys.getrefcount(obj) + + value = GObject.Value() + value.init(GObject.TYPE_PYOBJECT) + + # boxed TYPE_PYOBJECT will inc ref count as it should + value.set_boxed(obj) + self.assertEqual(sys.getrefcount(obj), refcount + 1) + + # multiple set_boxed should not inc ref count + value.set_boxed(obj) + self.assertEqual(sys.getrefcount(obj), refcount + 1) + + res = value.get_boxed() + self.assertEqual(obj, res) + self.assertEqual(sys.getrefcount(obj), refcount + 2) + + # multiple get_boxed should not inc ref count + res = value.get_boxed() + self.assertEqual(sys.getrefcount(obj), refcount + 2) + + # deletion of the result and value holder should bring the + # refcount back to where we started + del res + del value + gc.collect() + self.assertEqual(sys.getrefcount(obj), refcount) + + del obj + gc.collect() + self.assertEqual(ref(), None) + + # FIXME: crashes + def disabled_test_gvalue_flat_array_round_trip(self): + self.assertEqual([42, '42', True], + GIMarshallingTests.gvalue_flat_array_round_trip(42, '42', True)) + class TestGClosure(unittest.TestCase): @@ -1154,6 +1410,11 @@ class TestEnum(unittest.TestCase): self.assertRaises(TypeError, GIMarshallingTests.enum_in, 43) self.assertRaises(TypeError, GIMarshallingTests.enum_in, 'GIMarshallingTests.Enum.VALUE3') + def test_enum_return(self): + enum = GIMarshallingTests.enum_returnv() + self.assertTrue(isinstance(enum, GIMarshallingTests.Enum)) + self.assertEqual(enum, GIMarshallingTests.Enum.VALUE3) + def test_enum_out(self): enum = GIMarshallingTests.enum_out() self.assertTrue(isinstance(enum, GIMarshallingTests.Enum)) @@ -1200,6 +1461,11 @@ class TestGEnum(unittest.TestCase): self.assertRaises(TypeError, GIMarshallingTests.genum_in, 43) self.assertRaises(TypeError, GIMarshallingTests.genum_in, 'GIMarshallingTests.GEnum.VALUE3') + def test_genum_return(self): + genum = GIMarshallingTests.genum_returnv() + self.assertTrue(isinstance(genum, GIMarshallingTests.GEnum)) + self.assertEqual(genum, GIMarshallingTests.GEnum.VALUE3) + def test_genum_out(self): genum = GIMarshallingTests.genum_out() self.assertTrue(isinstance(genum, GIMarshallingTests.GEnum)) @@ -1241,6 +1507,11 @@ class TestGFlags(unittest.TestCase): self.assertRaises(TypeError, GIMarshallingTests.flags_in, 1 << 1) self.assertRaises(TypeError, GIMarshallingTests.flags_in, 'GIMarshallingTests.Flags.VALUE2') + def test_flags_return(self): + flags = GIMarshallingTests.flags_returnv() + self.assertTrue(isinstance(flags, GIMarshallingTests.Flags)) + self.assertEqual(flags, GIMarshallingTests.Flags.VALUE2) + def test_flags_out(self): flags = GIMarshallingTests.flags_out() self.assertTrue(isinstance(flags, GIMarshallingTests.Flags)) @@ -1281,6 +1552,11 @@ class TestNoTypeFlags(unittest.TestCase): self.assertRaises(TypeError, GIMarshallingTests.no_type_flags_in, 1 << 1) self.assertRaises(TypeError, GIMarshallingTests.no_type_flags_in, 'GIMarshallingTests.NoTypeFlags.VALUE2') + def test_flags_return(self): + flags = GIMarshallingTests.no_type_flags_returnv() + self.assertTrue(isinstance(flags, GIMarshallingTests.NoTypeFlags)) + self.assertEqual(flags, GIMarshallingTests.NoTypeFlags.VALUE2) + def test_flags_out(self): flags = GIMarshallingTests.no_type_flags_out() self.assertTrue(isinstance(flags, GIMarshallingTests.NoTypeFlags)) @@ -1738,13 +2014,8 @@ class TestPythonGObject(unittest.TestCase): self.assertEqual(object_.val, 87) def test_dynamic_module(self): - from gi.module import DynamicGObjectModule - self.assertTrue(isinstance(GObject, DynamicGObjectModule)) - # compare the same enum from both the pygobject attrs and gi GObject attrs - self.assertEqual(GObject.SIGNAL_ACTION, GObject.SignalFlags.ACTION) - # compare a static gobject attr with a dynamic GObject attr - import gi._gobject - self.assertEqual(GObject.GObject, gi._gobject.GObject) + from gi.module import DynamicModule + self.assertTrue(isinstance(GObject, DynamicModule)) def test_subobject_non_vfunc_do_method(self): class PythonObjectWithNonVFuncDoMethod: @@ -2198,11 +2469,12 @@ class TestPropertiesObject(unittest.TestCase): # wrong; this will raise an assertion critical which we need to ignore old_mask = GLib.log_set_always_fatal( GLib.LogLevelFlags.LEVEL_WARNING | GLib.LogLevelFlags.LEVEL_ERROR) - self.assertEqual(self.obj.props.some_char, 0) - self.obj.props.some_char = GObject.G_MAXINT8 - self.assertEqual(self.obj.props.some_char, GObject.G_MAXINT8) - - GLib.log_set_always_fatal(old_mask) + try: + self.assertEqual(self.obj.props.some_char, 0) + self.obj.props.some_char = GObject.G_MAXINT8 + self.assertEqual(self.obj.props.some_char, GObject.G_MAXINT8) + finally: + GLib.log_set_always_fatal(old_mask) obj = GIMarshallingTests.PropertiesObject(some_char=-42) self.assertEqual(obj.props.some_char, -42) @@ -2349,6 +2621,28 @@ class TestPropertiesObject(unittest.TestCase): obj = GIMarshallingTests.PropertiesObject(some_boxed_struct=struct1) self.assertEqual(obj.props.some_boxed_struct.long_, 1) + @unittest.skipUnless(hasattr(GIMarshallingTests.PropertiesObject, 'some_variant'), + 'too old gobject-introspection') + def test_variant(self): + self.assertEqual(self.obj.props.some_variant, None) + + self.obj.props.some_variant = GLib.Variant('o', '/myobj') + self.assertEqual(self.obj.props.some_variant.get_type_string(), 'o') + self.assertEqual(self.obj.props.some_variant.print_(False), "'/myobj'") + + self.obj.props.some_variant = None + self.assertEqual(self.obj.props.some_variant, None) + + obj = GIMarshallingTests.PropertiesObject(some_variant=GLib.Variant('b', True)) + self.assertEqual(obj.props.some_variant.get_type_string(), 'b') + self.assertEqual(obj.props.some_variant.get_boolean(), True) + + self.assertRaises(TypeError, setattr, self.obj.props, 'some_variant', 'foo') + self.assertRaises(TypeError, setattr, self.obj.props, 'some_variant', 23) + + self.assertEqual(obj.props.some_variant.get_type_string(), 'b') + self.assertEqual(obj.props.some_variant.get_boolean(), True) + class TestKeywords(unittest.TestCase): def test_method(self): @@ -2435,3 +2729,33 @@ class TestObjectInfo(unittest.TestCase): repo = gi.gi.Repository.get_default() info = repo.find_by_name('GObject', 'Object') self.assertFalse(info.get_abstract()) + + +class TestSignatureArgs(unittest.TestCase): + def test_split_args_multi_out(self): + in_args, out_args = gi.types.split_function_info_args(GIMarshallingTests.int_out_out.__info__) + self.assertEqual(len(in_args), 0) + self.assertEqual(len(out_args), 2) + self.assertEqual(out_args[0].get_pytype_hint(), 'int') + self.assertEqual(out_args[1].get_pytype_hint(), 'int') + + def test_split_args_inout(self): + in_args, out_args = gi.types.split_function_info_args(GIMarshallingTests.long_inout_max_min.__info__) + self.assertEqual(len(in_args), 1) + self.assertEqual(len(out_args), 1) + self.assertEqual(in_args[0].get_name(), out_args[0].get_name()) + self.assertEqual(in_args[0].get_pytype_hint(), out_args[0].get_pytype_hint()) + + def test_split_args_none(self): + obj = GIMarshallingTests.Object(int=33) + in_args, out_args = gi.types.split_function_info_args(obj.none_inout.__info__) + self.assertEqual(len(in_args), 1) + self.assertEqual(len(out_args), 1) + + def test_final_signature_with_full_inout(self): + self.assertEqual(GIMarshallingTests.Object.full_inout.__doc__, + 'full_inout(object:GIMarshallingTests.Object) -> object:GIMarshallingTests.Object') + + def test_overridden_doc_is_not_clobbered(self): + self.assertEqual(GIMarshallingTests.OverridesObject.method.__doc__, + 'Overridden doc string.') diff --git a/tests/test_glib.py b/tests/test_glib.py index a36eca6..1a45fd8 100644 --- a/tests/test_glib.py +++ b/tests/test_glib.py @@ -1,9 +1,15 @@ # -*- Mode: Python -*- +# encoding: UTF-8 import unittest import os.path +import warnings +import subprocess from gi.repository import GLib +from gi import PyGIDeprecationWarning + +from compathelper import _unicode, _bytes class TestGLib(unittest.TestCase): @@ -13,3 +19,183 @@ class TestGLib(unittest.TestCase): self.assertTrue(os.path.exists(bash_path)) self.assertEqual(GLib.find_program_in_path('non existing'), None) + + def test_markup_escape_text(self): + self.assertEqual(GLib.markup_escape_text(_unicode('a&bä')), 'a&bä') + self.assertEqual(GLib.markup_escape_text(_bytes('a&b\x05')), 'a&b') + + # with explicit length argument + self.assertEqual(GLib.markup_escape_text(_bytes('a\x05\x01\x02'), 2), 'a') + + def test_progname(self): + GLib.set_prgname('moo') + self.assertEqual(GLib.get_prgname(), 'moo') + + def test_appname(self): + GLib.set_application_name('moo') + self.assertEqual(GLib.get_application_name(), 'moo') + + def test_xdg_dirs(self): + self.assertTrue(os.path.isdir(GLib.get_user_data_dir())) + self.assertTrue(os.path.isdir(GLib.get_user_special_dir(GLib.USER_DIRECTORY_DESKTOP))) + # also works with backwards compatible enum names + self.assertEqual(GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_MUSIC), + GLib.get_user_special_dir(GLib.USER_DIRECTORY_MUSIC)) + + for d in GLib.get_system_config_dirs(): + self.assertTrue('/' in d, d) + for d in GLib.get_system_data_dirs(): + self.assertTrue('/' in d, d) + + def test_main_depth(self): + self.assertEqual(GLib.main_depth(), 0) + + def test_filenames(self): + self.assertEqual(GLib.filename_display_name('foo'), 'foo') + self.assertEqual(GLib.filename_display_basename('bar/foo'), 'foo') + + # this is locale dependent, so we cannot completely verify the result + res = GLib.filename_from_utf8(_unicode('aäb')) + self.assertTrue(isinstance(res, bytes)) + self.assertGreaterEqual(len(res), 3) + + # with explicit length argument + self.assertEqual(GLib.filename_from_utf8(_unicode('aäb'), 1), b'a') + + def test_uri_extract(self): + res = GLib.uri_list_extract_uris('''# some comment +http://example.com +https://my.org/q?x=1&y=2 + http://gnome.org/new''') + self.assertEqual(res, ['http://example.com', + 'https://my.org/q?x=1&y=2', + 'http://gnome.org/new']) + + def test_current_time(self): + with warnings.catch_warnings(record=True) as warn: + warnings.simplefilter('always') + tm = GLib.get_current_time() + self.assertTrue(issubclass(warn[0].category, PyGIDeprecationWarning)) + + self.assertTrue(isinstance(tm, float)) + self.assertGreater(tm, 1350000000.0) + + def test_main_loop(self): + # note we do not test run() here, as we use this in countless other + # tests + ml = GLib.MainLoop() + self.assertFalse(ml.is_running()) + + context = ml.get_context() + self.assertEqual(context, GLib.MainContext.default()) + self.assertTrue(context.is_owner() in [True, False]) + self.assertTrue(context.pending() in [True, False]) + self.assertFalse(context.iteration(False)) + + def test_main_loop_with_context(self): + context = GLib.MainContext() + ml = GLib.MainLoop(context) + self.assertFalse(ml.is_running()) + self.assertEqual(ml.get_context(), context) + + def test_main_context(self): + # constructor + context = GLib.MainContext() + self.assertTrue(context.is_owner() in [True, False]) + self.assertFalse(context.pending()) + self.assertFalse(context.iteration(False)) + + # GLib API + context = GLib.MainContext.default() + self.assertTrue(context.is_owner() in [True, False]) + self.assertTrue(context.pending() in [True, False]) + self.assertTrue(context.iteration(False) in [True, False]) + + # backwards compatible API + context = GLib.main_context_default() + self.assertTrue(context.is_owner() in [True, False]) + self.assertTrue(context.pending() in [True, False]) + self.assertTrue(context.iteration(False) in [True, False]) + + def test_io_add_watch_no_data(self): + (r, w) = os.pipe() + call_data = [] + + def cb(fd, condition): + call_data.append((fd, condition, os.read(fd, 1))) + return True + + # io_add_watch() takes an IOChannel, calling with an fd is deprecated + with warnings.catch_warnings(record=True) as warn: + warnings.simplefilter('always') + GLib.io_add_watch(r, GLib.IOCondition.IN, cb) + self.assertTrue(issubclass(warn[0].category, PyGIDeprecationWarning)) + + ml = GLib.MainLoop() + GLib.timeout_add(10, lambda: os.write(w, b'a') and False) + GLib.timeout_add(100, lambda: os.write(w, b'b') and False) + GLib.timeout_add(200, ml.quit) + ml.run() + + self.assertEqual(call_data, [(r, GLib.IOCondition.IN, b'a'), + (r, GLib.IOCondition.IN, b'b')]) + + def test_io_add_watch_with_data(self): + (r, w) = os.pipe() + call_data = [] + + def cb(fd, condition, data): + call_data.append((fd, condition, os.read(fd, 1), data)) + return True + + # io_add_watch() takes an IOChannel, calling with an fd is deprecated + with warnings.catch_warnings(record=True) as warn: + warnings.simplefilter('always') + GLib.io_add_watch(r, GLib.IOCondition.IN, cb, 'moo') + self.assertTrue(issubclass(warn[0].category, PyGIDeprecationWarning)) + + ml = GLib.MainLoop() + GLib.timeout_add(10, lambda: os.write(w, b'a') and False) + GLib.timeout_add(100, lambda: os.write(w, b'b') and False) + GLib.timeout_add(200, ml.quit) + ml.run() + + self.assertEqual(call_data, [(r, GLib.IOCondition.IN, b'a', 'moo'), + (r, GLib.IOCondition.IN, b'b', 'moo')]) + + def test_io_add_watch_pyfile(self): + call_data = [] + + cmd = subprocess.Popen('sleep 0.1; echo hello; sleep 0.2; echo world', + shell=True, stdout=subprocess.PIPE) + + def cb(file, condition): + call_data.append((file, condition, file.readline())) + return True + + # io_add_watch() takes an IOChannel, calling with a Python file is deprecated + with warnings.catch_warnings(record=True) as warn: + warnings.simplefilter('always') + GLib.io_add_watch(cmd.stdout, GLib.IOCondition.IN, cb) + self.assertTrue(issubclass(warn[0].category, PyGIDeprecationWarning)) + + ml = GLib.MainLoop() + GLib.timeout_add(400, ml.quit) + ml.run() + + cmd.wait() + + self.assertEqual(call_data, [(cmd.stdout, GLib.IOCondition.IN, b'hello\n'), + (cmd.stdout, GLib.IOCondition.IN, b'world\n')]) + + def test_glib_version(self): + (major, minor, micro) = GLib.glib_version + self.assertGreaterEqual(major, 2) + self.assertGreaterEqual(minor, 0) + self.assertGreaterEqual(micro, 0) + + def test_pyglib_version(self): + (major, minor, micro) = GLib.pyglib_version + self.assertGreaterEqual(major, 3) + self.assertGreaterEqual(minor, 7) + self.assertGreaterEqual(micro, 2) diff --git a/tests/test_gobject.py b/tests/test_gobject.py index a28a145..99f471b 100644 --- a/tests/test_gobject.py +++ b/tests/test_gobject.py @@ -1,37 +1,93 @@ # -*- Mode: Python -*- +import sys import gc import unittest +import warnings from gi.repository import GObject -import sys +from gi import PyGIDeprecationWarning +from gi.module import get_introspection_module +from gi._gobject import _gobject + import testhelper class TestGObjectAPI(unittest.TestCase): - def testGObjectModule(self): - obj = GObject.GObject() + def test_gobject_inheritance(self): + # GObject.Object is a class hierarchy as follows: + # overrides.Object -> introspection.Object -> static.GObject + GIObjectModule = get_introspection_module('GObject') + self.assertTrue(issubclass(GObject.Object, GIObjectModule.Object)) + self.assertTrue(issubclass(GIObjectModule.Object, _gobject.GObject)) + + self.assertEqual(_gobject.GObject.__gtype__, GObject.TYPE_OBJECT) + self.assertEqual(GIObjectModule.Object.__gtype__, GObject.TYPE_OBJECT) + self.assertEqual(GObject.Object.__gtype__, GObject.TYPE_OBJECT) + + # The pytype wrapper should hold the outer most Object class from overrides. + self.assertEqual(GObject.TYPE_OBJECT.pytype, GObject.Object) + + @unittest.skipIf(sys.version_info[:2] < (2, 7), 'Python 2.7 is required') + def test_gobject_unsupported_overrides(self): + obj = GObject.Object() + + with self.assertRaisesRegex(RuntimeError, 'Data access methods are unsupported.*'): + obj.get_data() + + with self.assertRaisesRegex(RuntimeError, 'This method is currently unsupported.*'): + obj.force_floating() + + def test_compat_api(self): + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter('always') + # GObject formerly exposed a lot of GLib's functions + self.assertEqual(GObject.markup_escape_text('foo'), 'foo') + + ml = GObject.MainLoop() + self.assertFalse(ml.is_running()) - self.assertEqual(obj.__module__, - 'gi._gobject._gobject') + context = GObject.main_context_default() + self.assertTrue(context.pending() in [False, True]) + + context = GObject.MainContext() + self.assertFalse(context.pending()) + + self.assertTrue(issubclass(w[0].category, PyGIDeprecationWarning)) + self.assertTrue('GLib.markup_escape_text' in str(w[0]), str(w[0])) + + self.assertLess(GObject.PRIORITY_HIGH, GObject.PRIORITY_DEFAULT) + + def test_min_max_int(self): + self.assertEqual(GObject.G_MAXINT16, 2 ** 15 - 1) + self.assertEqual(GObject.G_MININT16, -2 ** 15) + self.assertEqual(GObject.G_MAXUINT16, 2 ** 16 - 1) + + self.assertEqual(GObject.G_MAXINT32, 2 ** 31 - 1) + self.assertEqual(GObject.G_MININT32, -2 ** 31) + self.assertEqual(GObject.G_MAXUINT32, 2 ** 32 - 1) + + self.assertEqual(GObject.G_MAXINT64, 2 ** 63 - 1) + self.assertEqual(GObject.G_MININT64, -2 ** 63) + self.assertEqual(GObject.G_MAXUINT64, 2 ** 64 - 1) class TestReferenceCounting(unittest.TestCase): - def testRegularObject(self): + def test_regular_object(self): obj = GObject.GObject() self.assertEqual(obj.__grefcount__, 1) obj = GObject.new(GObject.GObject) self.assertEqual(obj.__grefcount__, 1) - def testFloating(self): + def test_floating(self): obj = testhelper.Floating() self.assertEqual(obj.__grefcount__, 1) obj = GObject.new(testhelper.Floating) self.assertEqual(obj.__grefcount__, 1) - def testOwnedByLibrary(self): + def test_owned_by_library(self): # Upon creation, the refcount of the object should be 2: # - someone already has a reference on the new object. # - the python wrapper should hold its own reference. @@ -44,7 +100,7 @@ class TestReferenceCounting(unittest.TestCase): obj.release() self.assertEqual(obj.__grefcount__, 1) - def testOwnedByLibraryOutOfScope(self): + def test_owned_by_library_out_of_scope(self): obj = testhelper.OwnedByLibrary() self.assertEqual(obj.__grefcount__, 2) @@ -62,7 +118,7 @@ class TestReferenceCounting(unittest.TestCase): obj.release() self.assertEqual(obj.__grefcount__, 1) - def testOwnedByLibraryUsingGObjectNew(self): + def test_owned_by_library_using_gobject_new(self): # Upon creation, the refcount of the object should be 2: # - someone already has a reference on the new object. # - the python wrapper should hold its own reference. @@ -75,7 +131,7 @@ class TestReferenceCounting(unittest.TestCase): obj.release() self.assertEqual(obj.__grefcount__, 1) - def testOwnedByLibraryOutOfScopeUsingGobjectNew(self): + def test_owned_by_library_out_of_scope_using_gobject_new(self): obj = GObject.new(testhelper.OwnedByLibrary) self.assertEqual(obj.__grefcount__, 2) @@ -93,7 +149,7 @@ class TestReferenceCounting(unittest.TestCase): obj.release() self.assertEqual(obj.__grefcount__, 1) - def testFloatingAndSunk(self): + def test_floating_and_sunk(self): # Upon creation, the refcount of the object should be 2: # - someone already has a reference on the new object. # - the python wrapper should hold its own reference. @@ -106,7 +162,7 @@ class TestReferenceCounting(unittest.TestCase): obj.release() self.assertEqual(obj.__grefcount__, 1) - def testFloatingAndSunkOutOfScope(self): + def test_floating_and_sunk_out_of_scope(self): obj = testhelper.FloatingAndSunk() self.assertEqual(obj.__grefcount__, 2) @@ -124,7 +180,7 @@ class TestReferenceCounting(unittest.TestCase): obj.release() self.assertEqual(obj.__grefcount__, 1) - def testFloatingAndSunkUsingGObjectNew(self): + def test_floating_and_sunk_using_gobject_new(self): # Upon creation, the refcount of the object should be 2: # - someone already has a reference on the new object. # - the python wrapper should hold its own reference. @@ -137,7 +193,7 @@ class TestReferenceCounting(unittest.TestCase): obj.release() self.assertEqual(obj.__grefcount__, 1) - def testFloatingAndSunkOutOfScopeUsingGObjectNew(self): + def test_floating_and_sunk_out_of_scope_using_gobject_new(self): obj = GObject.new(testhelper.FloatingAndSunk) self.assertEqual(obj.__grefcount__, 2) @@ -155,7 +211,7 @@ class TestReferenceCounting(unittest.TestCase): obj.release() self.assertEqual(obj.__grefcount__, 1) - def testUninitializedObject(self): + def test_uninitialized_object(self): class Obj(GObject.GObject): def __init__(self): x = self.__grefcount__ @@ -176,19 +232,19 @@ class TestPythonReferenceCounting(unittest.TestCase): # Newly created instances should alwayshave two references: one for # the GC, and one for the bound variable in the local scope. - def testNewInstanceHasTwoRefs(self): + def test_new_instance_has_two_refs(self): obj = GObject.GObject() self.assertEqual(sys.getrefcount(obj), 2) - def testNewInstanceHasTwoRefsUsingGObjectNew(self): + def test_new_instance_has_two_refs_using_gobject_new(self): obj = GObject.new(GObject.GObject) self.assertEqual(sys.getrefcount(obj), 2) - def testNewSubclassInstanceHasTwoRefs(self): + def test_new_subclass_instance_has_two_refs(self): obj = A() self.assertEqual(sys.getrefcount(obj), 2) - def testNewSubclassInstanceHasTwoRefsUsingGObjectNew(self): + def test_new_subclass_instance_has_two_refs_using_gobject_new(self): obj = GObject.new(A) self.assertEqual(sys.getrefcount(obj), 2) @@ -206,7 +262,7 @@ class TestContextManagers(unittest.TestCase): self.obj = self.ContextTestObject() self.handler = self.obj.connect('notify::prop', self.on_prop_set) - def testFreezeNotifyContext(self): + def test_freeze_notify_context(self): # Verify prop tracking list self.assertEqual(self.tracking, []) self.obj.props.prop = 1 @@ -215,22 +271,28 @@ class TestContextManagers(unittest.TestCase): self.assertEqual(self.tracking, [1, 2]) self.assertEqual(self.obj.__grefcount__, 1) - # Using the context manager the tracking list should not be affected - # and the GObject reference count should go up. + pyref_count = sys.getrefcount(self.obj) + + # Using the context manager the tracking list should not be affected. + # The GObject reference count should stay the same and the python + # object ref-count should go up. with self.obj.freeze_notify(): - self.assertEqual(self.obj.__grefcount__, 2) + self.assertEqual(self.obj.__grefcount__, 1) + self.assertEqual(sys.getrefcount(self.obj), pyref_count + 1) self.obj.props.prop = 3 self.assertEqual(self.obj.props.prop, 3) self.assertEqual(self.tracking, [1, 2]) # After the context manager, the prop should have been modified, - # the tracking list will be modified, and the GObject ref + # the tracking list will be modified, and the python object ref # count goes back down. + gc.collect() self.assertEqual(self.obj.props.prop, 3) self.assertEqual(self.tracking, [1, 2, 3]) self.assertEqual(self.obj.__grefcount__, 1) + self.assertEqual(sys.getrefcount(self.obj), pyref_count) - def testHandlerBlockContext(self): + def test_handler_block_context(self): # Verify prop tracking list self.assertEqual(self.tracking, []) self.obj.props.prop = 1 @@ -239,10 +301,14 @@ class TestContextManagers(unittest.TestCase): self.assertEqual(self.tracking, [1, 2]) self.assertEqual(self.obj.__grefcount__, 1) - # Using the context manager the tracking list should not be affected - # and the GObject reference count should go up. + pyref_count = sys.getrefcount(self.obj) + + # Using the context manager the tracking list should not be affected. + # The GObject reference count should stay the same and the python + # object ref-count should go up. with self.obj.handler_block(self.handler): - self.assertEqual(self.obj.__grefcount__, 2) + self.assertEqual(self.obj.__grefcount__, 1) + self.assertEqual(sys.getrefcount(self.obj), pyref_count + 1) self.obj.props.prop = 3 self.assertEqual(self.obj.props.prop, 3) self.assertEqual(self.tracking, [1, 2]) @@ -250,11 +316,13 @@ class TestContextManagers(unittest.TestCase): # After the context manager, the prop should have been modified # the tracking list should have stayed the same and the GObject ref # count goes back down. + gc.collect() self.assertEqual(self.obj.props.prop, 3) self.assertEqual(self.tracking, [1, 2]) self.assertEqual(self.obj.__grefcount__, 1) + self.assertEqual(sys.getrefcount(self.obj), pyref_count) - def testFreezeNotifyContextNested(self): + def test_freeze_notify_context_nested(self): self.assertEqual(self.tracking, []) with self.obj.freeze_notify(): self.obj.props.prop = 1 @@ -274,7 +342,7 @@ class TestContextManagers(unittest.TestCase): # and the last one sent. self.assertEqual(self.tracking, [3]) - def testHandlerBlockContextNested(self): + def test_handler_block_context_nested(self): self.assertEqual(self.tracking, []) with self.obj.handler_block(self.handler): self.obj.props.prop = 1 @@ -295,7 +363,7 @@ class TestContextManagers(unittest.TestCase): self.assertEqual(self.obj.props.prop, 3) self.assertEqual(self.tracking, []) - def testFreezeNotifyNormalUsageRefCounts(self): + def test_freeze_notify_normal_usage_ref_counts(self): # Ensure ref counts without using methods as context managers # maintain the same count. self.assertEqual(self.obj.__grefcount__, 1) @@ -304,14 +372,14 @@ class TestContextManagers(unittest.TestCase): self.obj.thaw_notify() self.assertEqual(self.obj.__grefcount__, 1) - def testHandlerBlockNormalUsageRefCounts(self): + def test_handler_block_normal_usage_ref_counts(self): self.assertEqual(self.obj.__grefcount__, 1) self.obj.handler_block(self.handler) self.assertEqual(self.obj.__grefcount__, 1) self.obj.handler_unblock(self.handler) self.assertEqual(self.obj.__grefcount__, 1) - def testFreezeNotifyContextError(self): + def test_freeze_notify_context_error(self): # Test an exception occurring within a freeze context exits the context try: with self.obj.freeze_notify(): @@ -329,7 +397,7 @@ class TestContextManagers(unittest.TestCase): self.obj.props.prop = 2 self.assertEqual(self.tracking, [1, 2]) - def testHandlerBlockContextError(self): + def test_handler_block_context_error(self): # Test an exception occurring within a handler block exits the context try: with self.obj.handler_block(self.handler): @@ -356,7 +424,7 @@ class TestPropertyBindings(unittest.TestCase): self.source = self.TestObject() self.target = self.TestObject() - def testDefaultBinding(self): + def test_default_binding(self): binding = self.source.bind_property('int_prop', self.target, 'int_prop', GObject.BindingFlags.DEFAULT) binding = binding # PyFlakes @@ -371,7 +439,7 @@ class TestPropertyBindings(unittest.TestCase): self.assertEqual(self.source.int_prop, 1) self.assertEqual(self.target.int_prop, 2) - def testBiDirectionalBinding(self): + def test_bidirectional_binding(self): binding = self.source.bind_property('int_prop', self.target, 'int_prop', GObject.BindingFlags.BIDIRECTIONAL) binding = binding # PyFlakes @@ -386,7 +454,7 @@ class TestPropertyBindings(unittest.TestCase): self.assertEqual(self.source.int_prop, 2) self.assertEqual(self.target.int_prop, 2) - def testTransformToOnly(self): + def test_transform_to_only(self): def transform_to(binding, value, user_data=None): self.assertEqual(user_data, 'test-data') return value * 2 @@ -404,7 +472,7 @@ class TestPropertyBindings(unittest.TestCase): self.assertEqual(self.source.int_prop, 1) self.assertEqual(self.target.int_prop, 1) - def testTransformFromOnly(self): + def test_transform_from_only(self): def transform_from(binding, value, user_data=None): self.assertEqual(user_data, None) return value * 2 @@ -422,7 +490,7 @@ class TestPropertyBindings(unittest.TestCase): self.assertEqual(self.source.int_prop, 2) self.assertEqual(self.target.int_prop, 1) - def testTransformBidrectional(self): + def test_transform_bidirectional(self): def transform_to(binding, value, user_data=None): self.assertEqual(user_data, 'test-data') return value * 2 @@ -445,7 +513,7 @@ class TestPropertyBindings(unittest.TestCase): self.assertEqual(self.source.int_prop, 2) self.assertEqual(self.target.int_prop, 4) - def testExplicitUnbindClearsConnection(self): + def test_explicit_unbind_clears_connection(self): self.assertEqual(self.source.int_prop, 0) self.assertEqual(self.target.int_prop, 0) @@ -465,7 +533,7 @@ class TestPropertyBindings(unittest.TestCase): # An already unbound BindingWeakRef will raise if unbind is attempted a second time. self.assertRaises(ValueError, binding.unbind) - def testReferenceCounts(self): + def test_reference_counts(self): self.assertEqual(self.source.__grefcount__, 1) self.assertEqual(self.target.__grefcount__, 1) diff --git a/tests/test_gtype.py b/tests/test_gtype.py new file mode 100644 index 0000000..dec716e --- /dev/null +++ b/tests/test_gtype.py @@ -0,0 +1,53 @@ +import unittest + +from gi.repository import GObject +from gi.repository import GIMarshallingTests + + +class CustomBase(GObject.GObject): + pass + + +class CustomChild(CustomBase, GIMarshallingTests.Interface): + pass + + +class TestTypeModuleLevelFunctions(unittest.TestCase): + def test_type_name(self): + self.assertEqual(GObject.type_name(GObject.TYPE_NONE), 'void') + self.assertEqual(GObject.type_name(GObject.TYPE_OBJECT), 'GObject') + self.assertEqual(GObject.type_name(GObject.TYPE_PYOBJECT), 'PyObject') + + def test_type_from_name(self): + # A complete test is not needed here since the TYPE_* defines are created + # using this method. + self.assertRaises(RuntimeError, GObject.type_from_name, '!NOT_A_REAL_TYPE!') + self.assertEqual(GObject.type_from_name('GObject'), GObject.TYPE_OBJECT) + self.assertEqual(GObject.type_from_name('GObject'), GObject.GObject.__gtype__) + + def test_type_is_a(self): + self.assertTrue(GObject.type_is_a(CustomBase, GObject.TYPE_OBJECT)) + self.assertTrue(GObject.type_is_a(CustomChild, CustomBase)) + self.assertTrue(GObject.type_is_a(CustomBase, GObject.GObject)) + self.assertTrue(GObject.type_is_a(CustomBase.__gtype__, GObject.TYPE_OBJECT)) + self.assertFalse(GObject.type_is_a(GObject.TYPE_OBJECT, CustomBase)) + self.assertFalse(GObject.type_is_a(CustomBase, int)) # invalid type + self.assertRaises(TypeError, GObject.type_is_a, CustomBase, 1) + self.assertRaises(TypeError, GObject.type_is_a, 2, GObject.TYPE_OBJECT) + self.assertRaises(TypeError, GObject.type_is_a, 1, 2) + + def test_type_children(self): + self.assertSequenceEqual(GObject.type_children(CustomBase), + [CustomChild.__gtype__]) + self.assertEqual(len(GObject.type_children(CustomChild)), 0) + + def test_type_interfaces(self): + self.assertEqual(len(GObject.type_interfaces(CustomBase)), 0) + self.assertEqual(len(GObject.type_interfaces(CustomChild)), 1) + self.assertSequenceEqual(GObject.type_interfaces(CustomChild), + [GIMarshallingTests.Interface.__gtype__]) + + def test_type_parent(self): + self.assertEqual(GObject.type_parent(CustomChild), CustomBase.__gtype__) + self.assertEqual(GObject.type_parent(CustomBase), GObject.TYPE_OBJECT) + self.assertRaises(RuntimeError, GObject.type_parent, GObject.GObject) diff --git a/tests/test_interface.py b/tests/test_interface.py index 2df61b1..dd01af8 100644 --- a/tests/test_interface.py +++ b/tests/test_interface.py @@ -37,12 +37,12 @@ GObject.type_register(MyObject) class TestIfaceImpl(unittest.TestCase): - def testReImplementInterface(self): + def test_reimplement_interface(self): m = MyUnknown() m.iface_method() self.assertEqual(m.called, True) - def testImplementInterface(self): + def test_implement_interface(self): m = MyObject() m.iface_method() self.assertEqual(m.called, True) diff --git a/tests/test_iochannel.py b/tests/test_iochannel.py new file mode 100644 index 0000000..0cc1b4b --- /dev/null +++ b/tests/test_iochannel.py @@ -0,0 +1,424 @@ +# -*- Mode: Python -*- +# encoding: UTF-8 +from __future__ import unicode_literals + +import unittest +import tempfile +import os.path +import fcntl +import shutil +import warnings + +from gi.repository import GLib +from gi import PyGIDeprecationWarning + +from compathelper import _unicode + + +class IOChannel(unittest.TestCase): + def setUp(self): + self.workdir = tempfile.mkdtemp() + + self.testutf8 = os.path.join(self.workdir, 'testutf8.txt') + with open(self.testutf8, 'wb') as f: + f.write('''hello ♥ world +second line + +À demain!'''.encode('UTF-8')) + + self.testlatin1 = os.path.join(self.workdir, 'testlatin1.txt') + with open(self.testlatin1, 'wb') as f: + f.write(b'''hell\xf8 world +second line + +\xc0 demain!''') + + self.testout = os.path.join(self.workdir, 'testout.txt') + + def tearDown(self): + shutil.rmtree(self.workdir) + + def test_file_readline_utf8(self): + ch = GLib.IOChannel(filename=self.testutf8) + self.assertEqual(ch.get_encoding(), 'UTF-8') + self.assertTrue(ch.get_close_on_unref()) + self.assertEqual(_unicode(ch.readline()), 'hello ♥ world\n') + self.assertEqual(ch.get_buffer_condition(), GLib.IOCondition.IN) + self.assertEqual(ch.readline(), 'second line\n') + self.assertEqual(ch.readline(), '\n') + self.assertEqual(_unicode(ch.readline()), 'À demain!') + self.assertEqual(ch.get_buffer_condition(), 0) + self.assertEqual(ch.readline(), '') + ch.close() + + def test_file_readline_latin1(self): + ch = GLib.IOChannel(filename=self.testlatin1, mode='r') + ch.set_encoding('latin1') + self.assertEqual(ch.get_encoding(), 'latin1') + self.assertEqual(_unicode(ch.readline()), 'hellø world\n') + self.assertEqual(ch.readline(), 'second line\n') + self.assertEqual(ch.readline(), '\n') + self.assertEqual(_unicode(ch.readline()), 'À demain!') + ch.close() + + def test_file_iter(self): + items = [] + ch = GLib.IOChannel(filename=self.testutf8) + for item in ch: + items.append(item) + self.assertEqual(len(items), 4) + self.assertEqual(_unicode(items[0]), 'hello ♥ world\n') + ch.close() + + def test_file_readlines(self): + ch = GLib.IOChannel(filename=self.testutf8) + lines = ch.readlines() + # Note, this really ought to be 4, but the static bindings add an extra + # empty one + self.assertGreaterEqual(len(lines), 4) + self.assertLessEqual(len(lines), 5) + self.assertEqual(_unicode(lines[0]), 'hello ♥ world\n') + self.assertEqual(_unicode(lines[3]), 'À demain!') + if len(lines) == 4: + self.assertEqual(lines[4], '') + + def test_file_read(self): + ch = GLib.IOChannel(filename=self.testutf8) + with open(self.testutf8, 'rb') as f: + self.assertEqual(ch.read(), f.read()) + + ch = GLib.IOChannel(filename=self.testutf8) + with open(self.testutf8, 'rb') as f: + self.assertEqual(ch.read(10), f.read(10)) + + ch = GLib.IOChannel(filename=self.testutf8) + with open(self.testutf8, 'rb') as f: + self.assertEqual(ch.read(max_count=15), f.read(15)) + + def test_seek(self): + ch = GLib.IOChannel(filename=self.testutf8) + ch.seek(2) + self.assertEqual(ch.read(3), b'llo') + + ch.seek(2, 0) # SEEK_SET + self.assertEqual(ch.read(3), b'llo') + + ch.seek(1, 1) # SEEK_CUR, skip the space + self.assertEqual(ch.read(3), b'\xe2\x99\xa5') + + ch.seek(2, 2) # SEEK_END + # FIXME: does not work currently + #self.assertEqual(ch.read(2), b'n!') + + # invalid whence value + self.assertRaises(ValueError, ch.seek, 0, 3) + + def test_file_write(self): + ch = GLib.IOChannel(filename=self.testout, mode='w') + ch.set_encoding('latin1') + ch.write('hellø world\n') + ch.close() + ch = GLib.IOChannel(filename=self.testout, mode='a') + ch.set_encoding('latin1') + ch.write('À demain!') + ch.close() + + with open(self.testout, 'rb') as f: + self.assertEqual(f.read().decode('latin1'), 'hellø world\nÀ demain!') + + def test_file_writelines(self): + ch = GLib.IOChannel(filename=self.testout, mode='w') + ch.writelines(['foo', 'bar\n', 'baz\n', 'end']) + ch.close() + + with open(self.testout, 'r') as f: + self.assertEqual(f.read(), 'foobar\nbaz\nend') + + def test_buffering(self): + writer = GLib.IOChannel(filename=self.testout, mode='w') + writer.set_encoding(None) + self.assertTrue(writer.get_buffered()) + self.assertGreater(writer.get_buffer_size(), 10) + + reader = GLib.IOChannel(filename=self.testout, mode='r') + + # does not get written immediately on buffering + writer.write('abc') + self.assertEqual(reader.read(), b'') + writer.flush() + self.assertEqual(reader.read(), b'abc') + + # does get written immediately without buffering + writer.set_buffered(False) + writer.write('def') + self.assertEqual(reader.read(), b'def') + + # writes after buffer overflow + writer.set_buffer_size(10) + writer.write('0123456789012') + self.assertTrue(reader.read().startswith(b'012')) + writer.flush() + reader.read() # ignore bits written after flushing + + # closing flushes + writer.set_buffered(True) + writer.write('ghi') + writer.close() + self.assertEqual(reader.read(), b'ghi') + reader.close() + + def test_fd_read(self): + (r, w) = os.pipe() + + ch = GLib.IOChannel(filedes=r) + ch.set_encoding(None) + ch.set_flags(ch.get_flags() | GLib.IOFlags.NONBLOCK) + self.assertNotEqual(ch.get_flags() | GLib.IOFlags.NONBLOCK, 0) + self.assertEqual(ch.read(), b'') + os.write(w, b'\x01\x02') + self.assertEqual(ch.read(), b'\x01\x02') + + # now test blocking case, after closing the write end + ch.set_flags(GLib.IOFlags(ch.get_flags() & ~GLib.IOFlags.NONBLOCK)) + os.write(w, b'\x03\x04') + os.close(w) + self.assertEqual(ch.read(), b'\x03\x04') + + ch.close() + + def test_fd_write(self): + (r, w) = os.pipe() + fcntl.fcntl(r, fcntl.F_SETFL, fcntl.fcntl(r, fcntl.F_GETFL) | os.O_NONBLOCK) + + ch = GLib.IOChannel(filedes=w, mode='w') + ch.set_encoding(None) + ch.set_buffered(False) + ch.write(b'\x01\x02') + self.assertEqual(os.read(r, 10), b'\x01\x02') + + # now test blocking case, after closing the write end + fcntl.fcntl(r, fcntl.F_SETFL, fcntl.fcntl(r, fcntl.F_GETFL) & ~os.O_NONBLOCK) + ch.write(b'\x03\x04') + ch.close() + self.assertEqual(os.read(r, 10), b'\x03\x04') + os.close(r) + + def test_deprecated_method_add_watch_no_data(self): + (r, w) = os.pipe() + + ch = GLib.IOChannel(filedes=r) + ch.set_encoding(None) + ch.set_flags(ch.get_flags() | GLib.IOFlags.NONBLOCK) + + cb_reads = [] + + def cb(channel, condition): + self.assertEqual(channel, ch) + self.assertEqual(condition, GLib.IOCondition.IN) + cb_reads.append(channel.read()) + return True + + # io_add_watch() method is deprecated, use GLib.io_add_watch + with warnings.catch_warnings(record=True) as warn: + warnings.simplefilter('always') + ch.add_watch(GLib.IOCondition.IN, cb) + self.assertTrue(issubclass(warn[0].category, PyGIDeprecationWarning)) + + ml = GLib.MainLoop() + + GLib.timeout_add(10, lambda: os.write(w, b'a') and False) + GLib.timeout_add(100, lambda: os.write(w, b'b') and False) + GLib.timeout_add(200, ml.quit) + ml.run() + + self.assertEqual(cb_reads, [b'a', b'b']) + + def test_deprecated_method_add_watch_data_priority(self): + (r, w) = os.pipe() + + ch = GLib.IOChannel(filedes=r) + ch.set_encoding(None) + ch.set_flags(ch.get_flags() | GLib.IOFlags.NONBLOCK) + + cb_reads = [] + + def cb(channel, condition, data): + self.assertEqual(channel, ch) + self.assertEqual(condition, GLib.IOCondition.IN) + self.assertEqual(data, 'hello') + cb_reads.append(channel.read()) + return True + + ml = GLib.MainLoop() + # io_add_watch() method is deprecated, use GLib.io_add_watch + with warnings.catch_warnings(record=True) as warn: + warnings.simplefilter('always') + id = ch.add_watch(GLib.IOCondition.IN, cb, 'hello', priority=GLib.PRIORITY_HIGH) + self.assertTrue(issubclass(warn[0].category, PyGIDeprecationWarning)) + + self.assertEqual(ml.get_context().find_source_by_id(id).priority, + GLib.PRIORITY_HIGH) + + GLib.timeout_add(10, lambda: os.write(w, b'a') and False) + GLib.timeout_add(100, lambda: os.write(w, b'b') and False) + GLib.timeout_add(200, ml.quit) + ml.run() + + self.assertEqual(cb_reads, [b'a', b'b']) + + def test_add_watch_no_data(self): + (r, w) = os.pipe() + + ch = GLib.IOChannel(filedes=r) + ch.set_encoding(None) + ch.set_flags(ch.get_flags() | GLib.IOFlags.NONBLOCK) + + cb_reads = [] + + def cb(channel, condition): + self.assertEqual(channel, ch) + self.assertEqual(condition, GLib.IOCondition.IN) + cb_reads.append(channel.read()) + return True + + id = GLib.io_add_watch(ch, GLib.PRIORITY_HIGH, GLib.IOCondition.IN, cb) + + ml = GLib.MainLoop() + self.assertEqual(ml.get_context().find_source_by_id(id).priority, + GLib.PRIORITY_HIGH) + GLib.timeout_add(10, lambda: os.write(w, b'a') and False) + GLib.timeout_add(100, lambda: os.write(w, b'b') and False) + GLib.timeout_add(200, ml.quit) + ml.run() + + self.assertEqual(cb_reads, [b'a', b'b']) + + def test_add_watch_with_data(self): + (r, w) = os.pipe() + + ch = GLib.IOChannel(filedes=r) + ch.set_encoding(None) + ch.set_flags(ch.get_flags() | GLib.IOFlags.NONBLOCK) + + cb_reads = [] + + def cb(channel, condition, data): + self.assertEqual(channel, ch) + self.assertEqual(condition, GLib.IOCondition.IN) + self.assertEqual(data, 'hello') + cb_reads.append(channel.read()) + return True + + id = GLib.io_add_watch(ch, GLib.PRIORITY_HIGH, GLib.IOCondition.IN, cb, 'hello') + + ml = GLib.MainLoop() + self.assertEqual(ml.get_context().find_source_by_id(id).priority, + GLib.PRIORITY_HIGH) + GLib.timeout_add(10, lambda: os.write(w, b'a') and False) + GLib.timeout_add(100, lambda: os.write(w, b'b') and False) + GLib.timeout_add(200, ml.quit) + ml.run() + + self.assertEqual(cb_reads, [b'a', b'b']) + + def test_add_watch_with_multi_data(self): + (r, w) = os.pipe() + + ch = GLib.IOChannel(filedes=r) + ch.set_encoding(None) + ch.set_flags(ch.get_flags() | GLib.IOFlags.NONBLOCK) + + cb_reads = [] + + def cb(channel, condition, data1, data2, data3): + self.assertEqual(channel, ch) + self.assertEqual(condition, GLib.IOCondition.IN) + self.assertEqual(data1, 'a') + self.assertEqual(data2, 'b') + self.assertEqual(data3, 'c') + cb_reads.append(channel.read()) + return True + + id = GLib.io_add_watch(ch, GLib.PRIORITY_HIGH, GLib.IOCondition.IN, cb, + 'a', 'b', 'c') + + ml = GLib.MainLoop() + self.assertEqual(ml.get_context().find_source_by_id(id).priority, + GLib.PRIORITY_HIGH) + GLib.timeout_add(10, lambda: os.write(w, b'a') and False) + GLib.timeout_add(100, lambda: os.write(w, b'b') and False) + GLib.timeout_add(200, ml.quit) + ml.run() + + self.assertEqual(cb_reads, [b'a', b'b']) + + def test_deprecated_add_watch_no_data(self): + (r, w) = os.pipe() + + ch = GLib.IOChannel(filedes=r) + ch.set_encoding(None) + ch.set_flags(ch.get_flags() | GLib.IOFlags.NONBLOCK) + + cb_reads = [] + + def cb(channel, condition): + self.assertEqual(channel, ch) + self.assertEqual(condition, GLib.IOCondition.IN) + cb_reads.append(channel.read()) + return True + + with warnings.catch_warnings(record=True) as warn: + warnings.simplefilter('always') + id = GLib.io_add_watch(ch, GLib.IOCondition.IN, cb, priority=GLib.PRIORITY_HIGH) + self.assertTrue(issubclass(warn[0].category, PyGIDeprecationWarning)) + + ml = GLib.MainLoop() + self.assertEqual(ml.get_context().find_source_by_id(id).priority, + GLib.PRIORITY_HIGH) + GLib.timeout_add(10, lambda: os.write(w, b'a') and False) + GLib.timeout_add(100, lambda: os.write(w, b'b') and False) + GLib.timeout_add(200, ml.quit) + ml.run() + + self.assertEqual(cb_reads, [b'a', b'b']) + + def test_deprecated_add_watch_with_data(self): + (r, w) = os.pipe() + + ch = GLib.IOChannel(filedes=r) + ch.set_encoding(None) + ch.set_flags(ch.get_flags() | GLib.IOFlags.NONBLOCK) + + cb_reads = [] + + def cb(channel, condition, data): + self.assertEqual(channel, ch) + self.assertEqual(condition, GLib.IOCondition.IN) + self.assertEqual(data, 'hello') + cb_reads.append(channel.read()) + return True + + with warnings.catch_warnings(record=True) as warn: + warnings.simplefilter('always') + id = GLib.io_add_watch(ch, GLib.IOCondition.IN, cb, 'hello', + priority=GLib.PRIORITY_HIGH) + self.assertTrue(issubclass(warn[0].category, PyGIDeprecationWarning)) + + ml = GLib.MainLoop() + self.assertEqual(ml.get_context().find_source_by_id(id).priority, + GLib.PRIORITY_HIGH) + GLib.timeout_add(10, lambda: os.write(w, b'a') and False) + GLib.timeout_add(100, lambda: os.write(w, b'b') and False) + GLib.timeout_add(200, ml.quit) + ml.run() + + self.assertEqual(cb_reads, [b'a', b'b']) + + def test_backwards_compat_flags(self): + self.assertEqual(GLib.IOCondition.IN, GLib.IO_IN) + self.assertEqual(GLib.IOFlags.NONBLOCK, GLib.IO_FLAG_NONBLOCK) + self.assertEqual(GLib.IOFlags.IS_SEEKABLE, GLib.IO_FLAG_IS_SEEKABLE) + self.assertEqual(GLib.IOStatus.NORMAL, GLib.IO_STATUS_NORMAL) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_mainloop.py b/tests/test_mainloop.py index 408a123..44197b3 100644 --- a/tests/test_mainloop.py +++ b/tests/test_mainloop.py @@ -34,7 +34,7 @@ class TestMainLoop(unittest.TestCase): raise Exception("deadbabe") loop = GLib.MainLoop() - GLib.child_watch_add(pid, child_died, loop) + GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, child_died, loop) os.close(pipe_r) os.write(pipe_w, _bytes("Y")) @@ -79,3 +79,19 @@ class TestMainLoop(unittest.TestCase): loop2.quit() finally: signal.signal(signal.SIGUSR1, orig_handler) + + def test_sigint(self): + pid = os.fork() + if pid == 0: + time.sleep(0.5) + os.kill(os.getppid(), signal.SIGINT) + os._exit(0) + + loop = GLib.MainLoop() + try: + loop.run() + self.fail('expected KeyboardInterrupt exception') + except KeyboardInterrupt: + pass + self.assertFalse(loop.is_running()) + os.waitpid(pid, 0) diff --git a/tests/test_option.py b/tests/test_option.py index 345d0ad..2900edd 100644 --- a/tests/test_option.py +++ b/tests/test_option.py @@ -10,17 +10,15 @@ try: except ImportError: from io import StringIO -# FIXME: we need a way to import the options module from a public module -from gi._glib.option import OptionParser, OptionGroup, OptionValueError, \ - make_option, BadOptionError +from gi.repository import GLib class TestOption(unittest.TestCase): EXCEPTION_MESSAGE = "This callback fails" def setUp(self): - self.parser = OptionParser("NAMES...", - description="Option unit test") + self.parser = GLib.option.OptionParser("NAMES...", + description="Option unit test") self.parser.add_option("-t", "--test", help="Unit test option", action="store_false", dest="test", default=True) self.parser.add_option("--g-fatal-warnings", @@ -32,22 +30,22 @@ class TestOption(unittest.TestCase): def option_callback(option, opt, value, parser): raise Exception(self.EXCEPTION_MESSAGE) - group = OptionGroup( + group = GLib.option.OptionGroup( "unittest", "Unit test options", "Show all unittest options", option_list=[ - make_option("-f", "-u", "--file", "--unit-file", - type="filename", - dest="unit_file", - help="Unit test option"), - make_option("--test-integer", - type="int", - dest="test_integer", - help="Unit integer option"), - make_option("--callback-failure-test", - action="callback", - callback=option_callback, - dest="test_integer", - help="Unit integer option"), + GLib.option.make_option("-f", "-u", "--file", "--unit-file", + type="filename", + dest="unit_file", + help="Unit test option"), + GLib.option.make_option("--test-integer", + type="int", + dest="test_integer", + help="Unit integer option"), + GLib.option.make_option("--callback-failure-test", + action="callback", + callback=option_callback, + dest="test_integer", + help="Unit integer option"), ]) group.add_option("-t", "--test", action="store_false", @@ -57,7 +55,7 @@ class TestOption(unittest.TestCase): self.parser.add_option_group(group) return group - def testParseArgs(self): + def test_parse_args(self): options, args = self.parser.parse_args( ["test_option.py"]) self.assertFalse(args) @@ -70,12 +68,12 @@ class TestOption(unittest.TestCase): ["test_option.py", "foo", "bar"]) self.assertEqual(args, []) - def testParseArgsDoubleDash(self): + def test_parse_args_double_dash(self): options, args = self.parser.parse_args( ["test_option.py", "--", "-xxx"]) #self.assertEqual(args, ["-xxx"]) - def testParseArgsGroup(self): + def test_parse_args_group(self): group = self._create_group() options, args = self.parser.parse_args( @@ -89,20 +87,20 @@ class TestOption(unittest.TestCase): self.assertEqual(group.values.unit_file, "test") self.assertFalse(args) - def testOptionValueError(self): + def test_option_value_error(self): self._create_group() - self.assertRaises(OptionValueError, self.parser.parse_args, + self.assertRaises(GLib.option.OptionValueError, self.parser.parse_args, ["test_option.py", "--test-integer=text"]) - def testBadOptionError(self): - self.assertRaises(BadOptionError, + def test_bad_option_error(self): + self.assertRaises(GLib.option.BadOptionError, self.parser.parse_args, ["test_option.py", "--unknwon-option"]) - def testOptionGroupConstructor(self): - self.assertRaises(TypeError, OptionGroup) + def test_option_group_constructor(self): + self.assertRaises(TypeError, GLib.option.OptionGroup) - def testStandardError(self): + def test_standard_error(self): self._create_group() sio = StringIO() old_stderr = sys.stderr diff --git a/tests/test_overrides.py b/tests/test_overrides.py index dd2aa6a..e1af1f1 100644 --- a/tests/test_overrides.py +++ b/tests/test_overrides.py @@ -4,6 +4,8 @@ import unittest import gi.overrides +import gi.module + try: from gi.repository import Regress Regress # pyflakes @@ -27,3 +29,30 @@ class TestRegistry(unittest.TestCase): # Regress override is in tests/gi/overrides, separate from gi/overrides # https://bugzilla.gnome.org/show_bug.cgi?id=680913 self.assertEqual(Regress.REGRESS_OVERRIDE, 42) + + +class TestModule(unittest.TestCase): + # Tests for gi.module + + def test_get_introspection_module_caching(self): + # This test attempts to minimize side effects by + # using a DynamicModule directly instead of going though: + # from gi.repository import Foo + + # Clear out introspection module cache before running this test. + old_modules = gi.module._introspection_modules + gi.module._introspection_modules = {} + + mod_name = 'GIMarshallingTests' + mod1 = gi.module.get_introspection_module(mod_name) + mod2 = gi.module.get_introspection_module(mod_name) + self.assertTrue(mod1 is mod2) + + # Using a DynamicModule will use get_introspection_module internally + # in its _load method. + mod_overridden = gi.module.DynamicModule(mod_name) + mod_overridden._load() + self.assertTrue(mod1 is mod_overridden._introspection_module) + + # Restore the previous cache + gi.module._introspection_modules = old_modules diff --git a/tests/test_overrides_gtk.py b/tests/test_overrides_gtk.py index ee41457..6148479 100644 --- a/tests/test_overrides_gtk.py +++ b/tests/test_overrides_gtk.py @@ -2,8 +2,6 @@ # vim: tabstop=4 shiftwidth=4 expandtab import unittest -import ctypes -import sys from compathelper import _unicode, _bytes @@ -18,19 +16,6 @@ except ImportError: Gtk = None -class RawTreeIter(ctypes.Structure): - """Class used for testing Gtk.TreeIter raw data.""" - _fields_ = [('stamp', ctypes.c_int), - ('user_data', ctypes.c_void_p), - ('user_data2', ctypes.c_void_p), - ('user_data3', ctypes.c_void_p)] - - @classmethod - def from_iter(cls, iter): - offset = sys.getsizeof(object()) # size of PyObject_HEAD - return ctypes.POINTER(cls).from_address(id(iter) + offset) - - @unittest.skipUnless(Gtk, 'Gtk not available') class TestGtk(unittest.TestCase): def test_container(self): @@ -1324,13 +1309,14 @@ class TestTreeView(unittest.TestCase): # will raise a Gtk-CRITICAL which we ignore for now old_mask = GLib.log_set_always_fatal( GLib.LogLevelFlags.LEVEL_WARNING | GLib.LogLevelFlags.LEVEL_ERROR) - view.set_cursor(store[1].path) - view.set_cursor(str(store[1].path)) + try: + view.set_cursor(store[1].path) + view.set_cursor(str(store[1].path)) - view.get_cell_area(store[1].path) - view.get_cell_area(str(store[1].path)) - - GLib.log_set_always_fatal(old_mask) + view.get_cell_area(store[1].path) + view.get_cell_area(str(store[1].path)) + finally: + GLib.log_set_always_fatal(old_mask) def test_tree_view_column(self): cell = Gtk.CellRendererText() @@ -1361,10 +1347,12 @@ class TestTreeView(unittest.TestCase): # might cause a Pango warning, do not break on this old_mask = GLib.log_set_always_fatal( GLib.LogLevelFlags.LEVEL_CRITICAL | GLib.LogLevelFlags.LEVEL_ERROR) - # causes the widget to get realized and cellN.props.text receive a - # value, otherwise it will be None. - tree.get_preferred_size() - GLib.log_set_always_fatal(old_mask) + try: + # causes the widget to get realized and cellN.props.text receive a + # value, otherwise it will be None. + tree.get_preferred_size() + finally: + GLib.log_set_always_fatal(old_mask) self.assertEqual(tree.get_column(0).get_title(), 'Head1') self.assertEqual(tree.get_column(1).get_title(), 'Head2') @@ -1396,10 +1384,12 @@ class TestTreeView(unittest.TestCase): # might cause a Pango warning, do not break on this old_mask = GLib.log_set_always_fatal( GLib.LogLevelFlags.LEVEL_CRITICAL | GLib.LogLevelFlags.LEVEL_ERROR) - # This will make cell.props.text receive a value, otherwise it - # will be None. - treeview.get_preferred_size() - GLib.log_set_always_fatal(old_mask) + try: + # This will make cell.props.text receive a value, otherwise it + # will be None. + treeview.get_preferred_size() + finally: + GLib.log_set_always_fatal(old_mask) self.assertTrue(cell.props.text in directors) @@ -1432,32 +1422,6 @@ class TestTreeView(unittest.TestCase): self.assertEqual(m, store) self.assertEqual(store.get_path(s), firstpath) - def test_tree_iter_user_data_int(self): - pyiter = Gtk.TreeIter() - rawiter = RawTreeIter.from_iter(pyiter) - - initial_ref_count = sys.getrefcount(1) - pyiter.user_data = 1 - - # verify setting int value increases refcount of the "1" object - self.assertEqual(sys.getrefcount(1), initial_ref_count + 1) - # verify the address of the '1' object is what user_data is actually set to. - self.assertEqual(id(1), rawiter.contents.user_data) - - def test_tree_iter_user_data_null(self): - pyiter = Gtk.TreeIter() - rawiter = RawTreeIter.from_iter(pyiter) - - self.assertEqual(pyiter.user_data, None) - self.assertEqual(rawiter.contents.user_data, None) - - # Setting user_data to None should not increase None's ref count. - # and the raw iters user_data should also come back as None/NULL. - initial_ref_count = sys.getrefcount(None) - pyiter.user_data = None - self.assertEqual(sys.getrefcount(None), initial_ref_count) - self.assertEqual(rawiter.contents.user_data, None) - @unittest.skipUnless(Gtk, 'Gtk not available') class TestTextBuffer(unittest.TestCase): diff --git a/tests/test_properties.py b/tests/test_properties.py index 490b1ae..ccb80a6 100644 --- a/tests/test_properties.py +++ b/tests/test_properties.py @@ -13,13 +13,14 @@ from gi.repository.GObject import \ TYPE_UINT64, TYPE_GTYPE, TYPE_INVALID, TYPE_NONE, TYPE_STRV, \ TYPE_INTERFACE, TYPE_CHAR, TYPE_UCHAR, TYPE_BOOLEAN, TYPE_FLOAT, \ TYPE_DOUBLE, TYPE_POINTER, TYPE_BOXED, TYPE_PARAM, TYPE_OBJECT, \ - TYPE_STRING, TYPE_PYOBJECT + TYPE_STRING, TYPE_PYOBJECT, TYPE_VARIANT from gi.repository.GObject import \ G_MININT, G_MAXINT, G_MAXUINT, G_MINLONG, G_MAXLONG, G_MAXULONG from gi.repository import Gio from gi.repository import GLib +from gi.repository import Regress from gi.repository import GIMarshallingTests from gi._gobject import propertyhelper @@ -60,6 +61,42 @@ class PropertyObject(GObject.GObject): strings = GObject.Property( type=TYPE_STRV, flags=PARAM_READWRITE | PARAM_CONSTRUCT) + variant = GObject.Property( + type=TYPE_VARIANT, flags=PARAM_READWRITE | PARAM_CONSTRUCT) + + variant_def = GObject.Property( + type=TYPE_VARIANT, flags=PARAM_READWRITE | PARAM_CONSTRUCT, + default=GLib.Variant('i', 42)) + + +class PropertyInheritanceObject(Regress.TestObj): + # override property from the base class, with a different type + string = GObject.Property(type=int) + + # a property entirely defined at the Python level + python_prop = GObject.Property(type=str) + + +class PropertySubClassObject(PropertyInheritanceObject): + # override property from the base class, with a different type + python_prop = GObject.Property(type=int) + + +class TestPropertyInheritanceObject(unittest.TestCase): + def test_override_gi_property(self): + self.assertNotEqual(Regress.TestObj.props.string.value_type, + PropertyInheritanceObject.props.string.value_type) + obj = PropertyInheritanceObject() + self.assertEqual(type(obj.props.string), int) + obj.props.string = 4 + self.assertEqual(obj.props.string, 4) + + def test_override_python_property(self): + obj = PropertySubClassObject() + self.assertEqual(type(obj.props.python_prop), int) + obj.props.python_prop = 5 + self.assertEqual(obj.props.python_prop, 5) + class TestPropertyObject(unittest.TestCase): def test_get_set(self): @@ -82,19 +119,24 @@ class TestPropertyObject(unittest.TestCase): def test_iteration(self): for obj in (PropertyObject.props, PropertyObject().props): + names = [] for pspec in obj: gtype = GType(pspec) self.assertEqual(gtype.parent.name, 'GParam') - self.assertTrue(pspec.name in ['normal', - 'construct', - 'construct-only', - 'uint64', - 'enum', - 'flags', - 'gtype', - 'strings', - 'boxed']) - self.assertEqual(len(obj), 9) + names.append(pspec.name) + + names.sort() + self.assertEqual(names, ['boxed', + 'construct', + 'construct-only', + 'enum', + 'flags', + 'gtype', + 'normal', + 'strings', + 'uint64', + 'variant', + 'variant-def']) def test_normal(self): obj = new(PropertyObject, normal="123") @@ -300,6 +342,52 @@ class TestPropertyObject(unittest.TestCase): self.assertRaises(TypeError, GObject.Property, type=TYPE_STRV, default=['hello', 1]) + def test_variant(self): + obj = new(PropertyObject) + + self.assertEqual(obj.props.variant, None) + self.assertEqual(obj.variant, None) + + obj.variant = GLib.Variant('s', 'hello') + self.assertEqual(obj.variant.print_(True), "'hello'") + + obj.variant = GLib.Variant('b', True) + self.assertEqual(obj.variant.print_(True), "true") + + obj.props.variant = GLib.Variant('y', 2) + self.assertEqual(obj.variant.print_(True), "byte 0x02") + + obj.variant = None + self.assertEqual(obj.variant, None) + + # set in constructor + obj = new(PropertyObject, variant=GLib.Variant('u', 5)) + self.assertEqual(obj.props.variant.print_(True), 'uint32 5') + + GObject.Property(type=TYPE_VARIANT, default=GLib.Variant('i', 1)) + + # incompatible types + self.assertRaises(TypeError, setattr, obj, 'variant', 'foo') + self.assertRaises(TypeError, setattr, obj, 'variant', 42) + + self.assertRaises(TypeError, GObject.Property, type=TYPE_VARIANT, + default='foo') + self.assertRaises(TypeError, GObject.Property, type=TYPE_VARIANT, + default=object()) + + def test_variant_default(self): + obj = new(PropertyObject) + + self.assertEqual(obj.props.variant_def.print_(True), '42') + self.assertEqual(obj.variant_def.print_(True), '42') + + obj.props.variant_def = GLib.Variant('y', 2) + self.assertEqual(obj.variant_def.print_(True), "byte 0x02") + + # set in constructor + obj = new(PropertyObject, variant_def=GLib.Variant('u', 5)) + self.assertEqual(obj.props.variant_def.print_(True), 'uint32 5') + def test_range(self): # kiwi code def max(c): @@ -689,14 +777,20 @@ class TestProperty(unittest.TestCase): del t self.assertEqual(sys.getrefcount(o), rc) - def test_doc_string_as_blurb(self): + def test_doc_strings(self): class C(GObject.GObject): + foo_blurbed = GObject.Property(type=int, blurb='foo_blurbed doc string') + @GObject.Property - def blurbed(self): - """blurbed doc string""" + def foo_getter(self): + """foo_getter doc string""" return 0 - self.assertEqual(C.blurbed.blurb, 'blurbed doc string') + self.assertEqual(C.foo_blurbed.blurb, 'foo_blurbed doc string') + self.assertEqual(C.foo_blurbed.__doc__, 'foo_blurbed doc string') + + self.assertEqual(C.foo_getter.blurb, 'foo_getter doc string') + self.assertEqual(C.foo_getter.__doc__, 'foo_getter doc string') def test_python_to_glib_type_mapping(self): tester = GObject.Property() diff --git a/tests/test_signal.py b/tests/test_signal.py index 8b9d8ce..fc8c835 100644 --- a/tests/test_signal.py +++ b/tests/test_signal.py @@ -4,7 +4,7 @@ import gc import unittest import sys -from gi.repository import GObject +from gi.repository import GObject, GLib from gi._gobject import signalhelper import testhelper from compathelper import _long @@ -49,11 +49,11 @@ class TestChaining(unittest.TestCase): assert args[2:] == (1, 2, 3) - def testChaining(self): + def test_chaining(self): self.inst.emit("my_signal", 42) assert self.inst.arg == 42 - def testChaining2(self): + def test_chaining2(self): inst2 = D() inst2.emit("my_signal", 44) assert inst2.arg == 44 @@ -63,18 +63,24 @@ class TestChaining(unittest.TestCase): class TestGSignalsError(unittest.TestCase): - def testInvalidType(self, *args): + def test_invalid_type(self, *args): def foo(): class Foo(GObject.GObject): __gsignals__ = None self.assertRaises(TypeError, foo) gc.collect() - def testInvalidName(self, *args): + def test_invalid_name(self, *args): def foo(): class Foo(GObject.GObject): __gsignals__ = {'not-exists': 'override'} - self.assertRaises(TypeError, foo) + # do not stumble over the warning thrown by GLib + old_mask = GLib.log_set_always_fatal(GLib.LogLevelFlags.LEVEL_CRITICAL | + GLib.LogLevelFlags.LEVEL_ERROR) + try: + self.assertRaises(TypeError, foo) + finally: + GLib.log_set_always_fatal(old_mask) gc.collect() @@ -294,7 +300,7 @@ class TestClosures(unittest.TestCase): self.assertEqual(inst.a, 1) gc.collect() - def testGString(self): + def test_gstring(self): class C(GObject.GObject): __gsignals__ = {'my_signal': (GObject.SignalFlags.RUN_LAST, GObject.TYPE_GSTRING, (GObject.TYPE_GSTRING,))} @@ -592,7 +598,8 @@ class TestSignalDecorator(unittest.TestCase): class TestSignalConnectors(unittest.TestCase): class CustomButton(GObject.GObject): - value = 0 + on_notify_called = False + value = GObject.Property(type=int) @GObject.Signal(arg_types=(int,)) def clicked(self, value): @@ -606,6 +613,16 @@ class TestSignalConnectors(unittest.TestCase): self.obj = obj self.value = value + def test_signal_notify(self): + def on_notify(obj, param): + obj.on_notify_called = True + + obj = self.CustomButton() + obj.connect('notify', on_notify) + self.assertFalse(obj.on_notify_called) + obj.notify('value') + self.assertTrue(obj.on_notify_called) + def test_signal_emit(self): # standard callback connection with different forms of emit. obj = self.CustomButton() @@ -734,5 +751,59 @@ class TestPython3Signals(unittest.TestCase): str) +class TestSignalModuleLevelFunctions(unittest.TestCase): + @unittest.skipIf(sys.version_info < (2, 7), 'Requires Python >= 2.7') + def test_signal_list_ids_with_invalid_type(self): + with self.assertRaisesRegex(TypeError, 'type must be instantiable or an interface.*'): + GObject.signal_list_ids(GObject.TYPE_INVALID) + + @unittest.skipIf(sys.version_info < (2, 7), 'Requires Python >= 2.7') + def test_signal_list_ids(self): + with self.assertRaisesRegex(TypeError, 'type must be instantiable or an interface.*'): + GObject.signal_list_ids(GObject.TYPE_INT) + + ids = GObject.signal_list_ids(C) + self.assertEqual(len(ids), 1) + # Note canonicalized names + self.assertEqual(GObject.signal_name(ids[0]), 'my-signal') + # There is no signal 0 in gobject + self.assertEqual(GObject.signal_name(0), None) + + @unittest.skipIf(sys.version_info < (2, 7), 'Requires Python >= 2.7') + def test_signal_lookup_with_invalid_type(self): + with self.assertRaisesRegex(TypeError, 'type must be instantiable or an interface.*'): + GObject.signal_lookup('NOT_A_SIGNAL_NAME', GObject.TYPE_INVALID) + + @unittest.skipIf(sys.version_info < (2, 7), 'Requires Python >= 2.7') + def test_signal_lookup(self): + ids = GObject.signal_list_ids(C) + self.assertEqual(ids[0], GObject.signal_lookup('my_signal', C)) + self.assertEqual(ids[0], GObject.signal_lookup('my-signal', C)) + + with self.assertRaisesRegex(TypeError, 'type must be instantiable or an interface.*'): + GObject.signal_lookup('NOT_A_SIGNAL_NAME', GObject.TYPE_INT) + + # Invalid signal names return 0 instead of raising + self.assertEqual(GObject.signal_lookup('NOT_A_SIGNAL_NAME', C), + 0) + + def test_signal_query(self): + my_signal_id, = GObject.signal_list_ids(C) + + # Form is: (id, name, gtype, arg_count, return_type, (arg_type1, ...)) + my_signal_expected_query_result = [my_signal_id, 'my-signal', C.__gtype__, + 1, GObject.TYPE_NONE, (GObject.TYPE_INT,)] + # signal_query(name, type) + self.assertSequenceEqual(GObject.signal_query('my-signal', C), + my_signal_expected_query_result) + # signal_query(signal_id) + self.assertSequenceEqual(GObject.signal_query(my_signal_id), + my_signal_expected_query_result) + # invalid query returns None instead of raising + self.assertEqual(GObject.signal_query(0), None) + self.assertEqual(GObject.signal_query('NOT_A_SIGNAL', C), + None) + + if __name__ == '__main__': unittest.main() diff --git a/tests/test_source.py b/tests/test_source.py index fe674cd..dda492a 100644 --- a/tests/test_source.py +++ b/tests/test_source.py @@ -1,8 +1,10 @@ # -*- Mode: Python -*- import unittest +import warnings -from gi.repository import GLib +from gi.repository import GLib, GObject +from gi import PyGIDeprecationWarning class Idle(GLib.Idle): @@ -43,25 +45,35 @@ class TestSource(unittest.TestCase): timeout.set_callback(self.timeout_callback, loop) timeout.attach() - def testSources(self): + def test_sources(self): loop = GLib.MainLoop() self.setup_timeout(loop) idle = Idle(loop) + self.assertEqual(idle.get_context(), None) idle.attach() + self.assertEqual(idle.get_context(), GLib.main_context_default()) self.pos = 0 m = MySource() + self.assertEqual(m.get_context(), None) m.set_callback(self.my_callback, loop) m.attach() + self.assertEqual(m.get_context(), GLib.main_context_default()) loop.run() - assert self.pos >= 0 and idle.count >= 0 + m.destroy() + idle.destroy() - def testSourcePrepare(self): + self.assertGreater(self.pos, 0) + self.assertGreaterEqual(idle.count, 0) + self.assertTrue(m.is_destroyed()) + self.assertTrue(idle.is_destroyed()) + + def test_source_prepare(self): # this test may not terminate if prepare() is wrapped incorrectly dispatched = [False] loop = GLib.MainLoop() @@ -89,7 +101,7 @@ class TestSource(unittest.TestCase): assert dispatched[0] - def testIsDestroyedSimple(self): + def test_is_destroyed_simple(self): s = GLib.Source() self.assertFalse(s.is_destroyed()) @@ -103,7 +115,7 @@ class TestSource(unittest.TestCase): s.destroy() self.assertTrue(s.is_destroyed()) - def testIsDestroyedContext(self): + def test_is_destroyed_context(self): def f(): c = GLib.MainContext() s = GLib.Source() @@ -113,12 +125,207 @@ class TestSource(unittest.TestCase): s = f() self.assertTrue(s.is_destroyed()) + def test_remove(self): + s = GLib.idle_add(dir) + self.assertEqual(GLib.source_remove(s), True) + # s is now removed, should fail now + self.assertEqual(GLib.source_remove(s), False) + + # accepts large source IDs (they are unsigned) + self.assertEqual(GLib.source_remove(GObject.G_MAXINT32), False) + self.assertEqual(GLib.source_remove(GObject.G_MAXINT32 + 1), False) + self.assertEqual(GLib.source_remove(GObject.G_MAXUINT32), False) + + def test_recurse_property(self): + s = GLib.Idle() + self.assertTrue(s.can_recurse in [False, True]) + s.can_recurse = False + self.assertFalse(s.can_recurse) + + def test_priority(self): + s = GLib.Idle() + self.assertEqual(s.priority, GLib.PRIORITY_DEFAULT_IDLE) + s.priority = GLib.PRIORITY_HIGH + self.assertEqual(s.priority, GLib.PRIORITY_HIGH) + + s = GLib.Idle(GLib.PRIORITY_LOW) + self.assertEqual(s.priority, GLib.PRIORITY_LOW) -class TestTimeout(unittest.TestCase): - def test504337(self): + s = GLib.Timeout(1, GLib.PRIORITY_LOW) + self.assertEqual(s.priority, GLib.PRIORITY_LOW) + + s = GLib.Source() + self.assertEqual(s.priority, GLib.PRIORITY_DEFAULT) + + def test_get_current_time(self): + # Note, deprecated API + s = GLib.Idle() + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter('always') + time = s.get_current_time() + self.assertTrue(issubclass(w[0].category, PyGIDeprecationWarning)) + + self.assertTrue(isinstance(time, float)) + # plausibility check, and check magnitude of result + self.assertGreater(time, 1300000000.0) + self.assertLess(time, 2000000000.0) + + def test_add_remove_poll(self): + # FIXME: very shallow test, only verifies the API signature + pollfd = GLib.PollFD(99, GLib.IOCondition.IN | GLib.IOCondition.HUP) + self.assertEqual(pollfd.fd, 99) + source = GLib.Source() + source.add_poll(pollfd) + source.remove_poll(pollfd) + + def test_out_of_scope_before_dispatch(self): + # https://bugzilla.gnome.org/show_bug.cgi?id=504337 GLib.Timeout(20) GLib.Idle() +class TestUserData(unittest.TestCase): + def test_idle_no_data(self): + ml = GLib.MainLoop() + + def cb(): + ml.quit() + id = GLib.idle_add(cb) + self.assertEqual(ml.get_context().find_source_by_id(id).priority, + GLib.PRIORITY_DEFAULT_IDLE) + ml.run() + + def test_timeout_no_data(self): + ml = GLib.MainLoop() + + def cb(): + ml.quit() + id = GLib.timeout_add(50, cb) + self.assertEqual(ml.get_context().find_source_by_id(id).priority, + GLib.PRIORITY_DEFAULT) + ml.run() + + def test_idle_data(self): + ml = GLib.MainLoop() + + def cb(data): + data['called'] = True + ml.quit() + data = {} + id = GLib.idle_add(cb, data) + self.assertEqual(ml.get_context().find_source_by_id(id).priority, + GLib.PRIORITY_DEFAULT_IDLE) + ml.run() + self.assertTrue(data['called']) + + def test_idle_multidata(self): + ml = GLib.MainLoop() + + def cb(data, data2): + data['called'] = True + data['data2'] = data2 + ml.quit() + data = {} + id = GLib.idle_add(cb, data, 'hello') + self.assertEqual(ml.get_context().find_source_by_id(id).priority, + GLib.PRIORITY_DEFAULT_IDLE) + ml.run() + self.assertTrue(data['called']) + self.assertEqual(data['data2'], 'hello') + + def test_timeout_data(self): + ml = GLib.MainLoop() + + def cb(data): + data['called'] = True + ml.quit() + data = {} + id = GLib.timeout_add(50, cb, data) + self.assertEqual(ml.get_context().find_source_by_id(id).priority, + GLib.PRIORITY_DEFAULT) + ml.run() + self.assertTrue(data['called']) + + def test_timeout_multidata(self): + ml = GLib.MainLoop() + + def cb(data, data2): + data['called'] = True + data['data2'] = data2 + ml.quit() + data = {} + id = GLib.timeout_add(50, cb, data, 'hello') + self.assertEqual(ml.get_context().find_source_by_id(id).priority, + GLib.PRIORITY_DEFAULT) + ml.run() + self.assertTrue(data['called']) + self.assertEqual(data['data2'], 'hello') + + def test_idle_no_data_priority(self): + ml = GLib.MainLoop() + + def cb(): + ml.quit() + id = GLib.idle_add(cb, priority=GLib.PRIORITY_HIGH) + self.assertEqual(ml.get_context().find_source_by_id(id).priority, + GLib.PRIORITY_HIGH) + ml.run() + + def test_timeout_no_data_priority(self): + ml = GLib.MainLoop() + + def cb(): + ml.quit() + id = GLib.timeout_add(50, cb, priority=GLib.PRIORITY_HIGH) + self.assertEqual(ml.get_context().find_source_by_id(id).priority, + GLib.PRIORITY_HIGH) + ml.run() + + def test_idle_data_priority(self): + ml = GLib.MainLoop() + + def cb(data): + data['called'] = True + ml.quit() + data = {} + id = GLib.idle_add(cb, data, priority=GLib.PRIORITY_HIGH) + self.assertEqual(ml.get_context().find_source_by_id(id).priority, + GLib.PRIORITY_HIGH) + ml.run() + self.assertTrue(data['called']) + + def test_timeout_data_priority(self): + ml = GLib.MainLoop() + + def cb(data): + data['called'] = True + ml.quit() + data = {} + id = GLib.timeout_add(50, cb, data, priority=GLib.PRIORITY_HIGH) + self.assertEqual(ml.get_context().find_source_by_id(id).priority, + GLib.PRIORITY_HIGH) + ml.run() + self.assertTrue(data['called']) + + def cb_no_data(self): + self.loop.quit() + + def test_idle_method_callback_no_data(self): + self.loop = GLib.MainLoop() + GLib.idle_add(self.cb_no_data) + self.loop.run() + + def cb_with_data(self, data): + data['called'] = True + self.loop.quit() + + def test_idle_method_callback_with_data(self): + self.loop = GLib.MainLoop() + data = {} + GLib.idle_add(self.cb_with_data, data) + self.loop.run() + self.assertTrue(data['called']) + + if __name__ == '__main__': unittest.main() diff --git a/tests/test_subprocess.py b/tests/test_subprocess.py index 69f5f4c..26308e8 100644 --- a/tests/test_subprocess.py +++ b/tests/test_subprocess.py @@ -1,24 +1,145 @@ # -*- Mode: Python -*- import sys +import os import unittest +import warnings from gi.repository import GLib +from gi import PyGIDeprecationWarning class TestProcess(unittest.TestCase): - def _child_watch_cb(self, pid, condition, data): - self.data = data - self.loop.quit() + def test_deprecated_child_watch_no_data(self): + def cb(pid, status): + self.status = status + self.loop.quit() - def testChildWatch(self): + self.status = None + self.loop = GLib.MainLoop() + argv = [sys.executable, '-c', 'import sys'] + pid, stdin, stdout, stderr = GLib.spawn_async( + argv, flags=GLib.SpawnFlags.DO_NOT_REAP_CHILD) + pid.close() + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter('always') + GLib.child_watch_add(pid, cb) + self.assertTrue(issubclass(w[0].category, PyGIDeprecationWarning)) + self.loop.run() + self.assertEqual(self.status, 0) + + def test_deprecated_child_watch_data_priority(self): + def cb(pid, status, data): + self.data = data + self.status = status + self.loop.quit() + + self.status = None + self.data = None + self.loop = GLib.MainLoop() + argv = [sys.executable, '-c', 'import sys'] + pid, stdin, stdout, stderr = GLib.spawn_async( + argv, flags=GLib.SpawnFlags.DO_NOT_REAP_CHILD) + pid.close() + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter('always') + id = GLib.child_watch_add(pid, cb, 12345, GLib.PRIORITY_HIGH) + self.assertTrue(issubclass(w[0].category, PyGIDeprecationWarning)) + self.assertEqual(self.loop.get_context().find_source_by_id(id).priority, + GLib.PRIORITY_HIGH) + self.loop.run() + self.assertEqual(self.data, 12345) + self.assertEqual(self.status, 0) + + def test_deprecated_child_watch_data_priority_kwargs(self): + def cb(pid, status, data): + self.data = data + self.status = status + self.loop.quit() + + self.status = None self.data = None self.loop = GLib.MainLoop() argv = [sys.executable, '-c', 'import sys'] pid, stdin, stdout, stderr = GLib.spawn_async( - argv, flags=GLib.SPAWN_DO_NOT_REAP_CHILD) + argv, flags=GLib.SpawnFlags.DO_NOT_REAP_CHILD) pid.close() - GLib.child_watch_add(pid, self._child_watch_cb, 12345) + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter('always') + id = GLib.child_watch_add(pid, cb, priority=GLib.PRIORITY_HIGH, data=12345) + self.assertTrue(issubclass(w[0].category, PyGIDeprecationWarning)) + self.assertEqual(self.loop.get_context().find_source_by_id(id).priority, + GLib.PRIORITY_HIGH) self.loop.run() self.assertEqual(self.data, 12345) + self.assertEqual(self.status, 0) + + def test_child_watch_no_data(self): + def cb(pid, status): + self.status = status + self.loop.quit() + + self.status = None + self.loop = GLib.MainLoop() + argv = [sys.executable, '-c', 'import sys'] + pid, stdin, stdout, stderr = GLib.spawn_async( + argv, flags=GLib.SpawnFlags.DO_NOT_REAP_CHILD) + pid.close() + id = GLib.child_watch_add(GLib.PRIORITY_HIGH, pid, cb) + self.assertEqual(self.loop.get_context().find_source_by_id(id).priority, + GLib.PRIORITY_HIGH) + self.loop.run() + self.assertEqual(self.status, 0) + + def test_child_watch_with_data(self): + def cb(pid, status, data): + self.status = status + self.data = data + self.loop.quit() + + self.data = None + self.status = None + self.loop = GLib.MainLoop() + argv = [sys.executable, '-c', 'import sys'] + pid, stdin, stdout, stderr = GLib.spawn_async( + argv, flags=GLib.SpawnFlags.DO_NOT_REAP_CHILD) + self.assertEqual(stdin, None) + self.assertEqual(stdout, None) + self.assertEqual(stderr, None) + pid.close() + id = GLib.child_watch_add(GLib.PRIORITY_HIGH, pid, cb, 12345) + self.assertEqual(self.loop.get_context().find_source_by_id(id).priority, + GLib.PRIORITY_HIGH) + self.loop.run() + self.assertEqual(self.data, 12345) + self.assertEqual(self.status, 0) + + def test_spawn_async_fds(self): + pid, stdin, stdout, stderr = GLib.spawn_async( + ['cat'], flags=GLib.SpawnFlags.SEARCH_PATH, standard_input=True, + standard_output=True, standard_error=True) + os.write(stdin, b'hello world!\n') + os.close(stdin) + out = os.read(stdout, 50) + os.close(stdout) + err = os.read(stderr, 50) + os.close(stderr) + pid.close() + self.assertEqual(out, b'hello world!\n') + self.assertEqual(err, b'') + + def test_spawn_async_envp(self): + pid, stdin, stdout, stderr = GLib.spawn_async( + ['sh', '-c', 'echo $TEST_VAR'], ['TEST_VAR=moo!'], + flags=GLib.SpawnFlags.SEARCH_PATH, standard_output=True) + self.assertEqual(stdin, None) + self.assertEqual(stderr, None) + out = os.read(stdout, 50) + os.close(stdout) + pid.close() + self.assertEqual(out, b'moo!\n') + + def test_backwards_compat_flags(self): + self.assertEqual(GLib.SpawnFlags.DO_NOT_REAP_CHILD, + GLib.SPAWN_DO_NOT_REAP_CHILD) diff --git a/tests/test_thread.py b/tests/test_thread.py index b707bc4..3d0557e 100644 --- a/tests/test_thread.py +++ b/tests/test_thread.py @@ -20,7 +20,7 @@ class TestThread(unittest.TestCase): self.obj.connect('from-thread', self.from_thread_cb) self.obj.emit('emit-signal') - def testExtensionModule(self): + def test_extension_module(self): GLib.idle_add(self.idle_cb) GLib.timeout_add(50, self.timeout_cb) self.main.run() diff --git a/tests/test_uris.py b/tests/test_uris.py deleted file mode 100644 index 143cc7b..0000000 --- a/tests/test_uris.py +++ /dev/null @@ -1,16 +0,0 @@ -import unittest - -from gi.repository import GLib - - -class TestUris(unittest.TestCase): - def testExtractUris(self): - uri_list_text = """# urn:isbn:0-201-08372-8 -http://www.huh.org/books/foo.html -http://www.huh.org/books/foo.pdf -ftp://ftp.foo.org/books/foo.txt -""" - uri_list = GLib.uri_list_extract_uris(uri_list_text) - assert uri_list[0] == "http://www.huh.org/books/foo.html" - assert uri_list[1] == "http://www.huh.org/books/foo.pdf" - assert uri_list[2] == "ftp://ftp.foo.org/books/foo.txt" diff --git a/tests/testhelpermodule.c b/tests/testhelpermodule.c index bc83e9d..16bb39e 100644 --- a/tests/testhelpermodule.c +++ b/tests/testhelpermodule.c @@ -602,12 +602,11 @@ PYGLIB_MODULE_START(testhelper, "testhelper") { PyObject *m, *d; - g_thread_init(NULL); pygobject_init(-1, -1, -1); d = PyModule_GetDict(module); - if ((m = PyImport_ImportModule("gi._gobject")) != NULL) { + if ((m = PyImport_ImportModule("gi._gobject._gobject")) != NULL) { PyObject *moddict = PyModule_GetDict(m); _PyGObject_Type = (PyTypeObject *)PyDict_GetItemString(moddict, "GObject"); |