[gjs] package: Add checkSymbol() to check for symbol availability
- From: Philip Chimento <pchimento src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gjs] package: Add checkSymbol() to check for symbol availability
- Date: Thu, 11 May 2017 06:23:15 +0000 (UTC)
commit 588f4003bc0f54be2d9f26acbadfff3597d9312d
Author: Florian Müllner <fmuellner gnome org>
Date: Fri Mar 3 21:01:21 2017 +0100
package: Add checkSymbol() to check for symbol availability
GI allows to specify the API version to import, but not a minimum
version of said API. This can lead to hard to track down bugs at
runtime, in particular when the failure doesn't trigger a warning
or exception (for example when setting a non-existent GObject
property).
To address this, add a checkSymbols() method that test whether a
library can be imported and contains a particular symbol.
https://bugzilla.gnome.org/show_bug.cgi?id=779593
Makefile-test.am | 1 +
installed-tests/js/testPackage.js | 83 +++++++++++++++++++++++++++++++++++++
modules/package.js | 57 +++++++++++++++++++++++++
3 files changed, 141 insertions(+), 0 deletions(-)
---
diff --git a/Makefile-test.am b/Makefile-test.am
index ff3ff33..af2fd8f 100644
--- a/Makefile-test.am
+++ b/Makefile-test.am
@@ -237,6 +237,7 @@ common_jstests_files = \
installed-tests/js/testMainloop.js \
installed-tests/js/testMetaClass.js \
installed-tests/js/testNamespace.js \
+ installed-tests/js/testPackage.js \
installed-tests/js/testParamSpec.js \
installed-tests/js/testSignals.js \
installed-tests/js/testSystem.js \
diff --git a/installed-tests/js/testPackage.js b/installed-tests/js/testPackage.js
new file mode 100644
index 0000000..bcaf3db
--- /dev/null
+++ b/installed-tests/js/testPackage.js
@@ -0,0 +1,83 @@
+const Pkg = imports.package;
+
+describe('Package module', function () {
+ it('finds an existing library', function () {
+ expect(Pkg.checkSymbol('Regress', '1.0')).toEqual(true);
+ });
+
+ it('doesn\'t find a non-existent library', function () {
+ expect(Pkg.checkSymbol('Rägräss', '1.0')).toEqual(false);
+ });
+
+ it('finds a function', function () {
+ expect(Pkg.checkSymbol('Regress', '1.0', 'get_variant')).toEqual(true);
+ });
+
+ it('doesn\'t find a non-existent function', function () {
+ expect(Pkg.checkSymbol('Regress', '1.0', 'get_väriänt')).toEqual(false);
+ });
+
+ it('finds a class', function () {
+ expect(Pkg.checkSymbol('Regress', '1.0', 'TestObj')).toEqual(true);
+ });
+
+ it('doesn\'t find a non-existent class', function () {
+ expect(Pkg.checkSymbol('Regress', '1.0', 'TestNoObj')).toEqual(false);
+ });
+
+ it('finds a property', function () {
+ expect(Pkg.checkSymbol('Regress', '1.0', 'TestObj.bare')).toEqual(true);
+ });
+
+ it('doesn\'t find a non-existent property', function () {
+ expect(Pkg.checkSymbol('Regress', '1.0', 'TestObj.bäre')).toEqual(false);
+ });
+
+ it('finds a static function', function () {
+ expect(Pkg.checkSymbol('Regress', '1.0', 'TestObj.static_method')).toEqual(true);
+ });
+
+ it('doesn\'t find a non-existent static function', function () {
+ expect(Pkg.checkSymbol('Regress', '1.0', 'TestObj.stätic_methöd')).toEqual(false);
+ });
+
+ it('finds a method', function () {
+ expect(Pkg.checkSymbol('Regress', '1.0', 'TestObj.null_out')).toEqual(true);
+ });
+
+ it('doesn\'t find a non-existent method', function () {
+ expect(Pkg.checkSymbol('Regress', '1.0', 'TestObj.nüll_out')).toEqual(false);
+ });
+
+ it('finds an interface', function () {
+ expect(Pkg.checkSymbol('GIMarshallingTests', '1.0', 'Interface')).toEqual(true);
+ });
+
+ it('doesn\'t find a non-existent interface', function () {
+ expect(Pkg.checkSymbol('GIMarshallingTests', '1.0', 'Interfäce')).toEqual(false);
+ });
+
+ it('finds an interface method', function () {
+ expect(Pkg.checkSymbol('GIMarshallingTests', '1.0', 'Interface.test_int8_in')).toEqual(true);
+ });
+
+ it('doesn\'t find a non-existent interface method', function () {
+ expect(Pkg.checkSymbol('GIMarshallingTests', '1.0', 'Interface.test_int42_in')).toEqual(false);
+ });
+
+ it('finds an enum value', function () {
+ expect(Pkg.checkSymbol('Regress', '1.0', 'TestEnum.VALUE1')).toEqual(true);
+ });
+
+ it('doesn\'t find a non-existent enum value', function () {
+ expect(Pkg.checkSymbol('Regress', '1.0', 'TestEnum.value1')).toEqual(false);
+ });
+
+ it('finds a constant', function () {
+ expect(Pkg.checkSymbol('Regress', '1.0', 'BOOL_CONSTANT')).toEqual(true);
+ });
+
+ it('doesn\'t find a non-existent constant', function () {
+ expect(Pkg.checkSymbol('Regress', '1.0', 'BööL_CONSTANT')).toEqual(false);
+ });
+});
diff --git a/modules/package.js b/modules/package.js
index 93adce4..03fceaa 100644
--- a/modules/package.js
+++ b/modules/package.js
@@ -26,6 +26,7 @@
const GLib = imports.gi.GLib;
const GIRepository = imports.gi.GIRepository;
const Gio = imports.gi.Gio;
+const GObject = imports.gi.GObject;
const System = imports.system;
const Gettext = imports.gettext;
@@ -246,6 +247,62 @@ function require(libs) {
}
}
+/**
+ * checkSymbol:
+ * @lib: an external dependency to import
+ * @version: optional version of the dependency
+ * @symbol: optional symbol to check for
+ *
+ * Check whether an external GI typelib can be imported
+ * and provides @symbol.
+ *
+ * Symbols may refer to
+ * - global functions ('main_quit')
+ * - classes ('Window')
+ * - class / instance methods ('IconTheme.get_default' / 'IconTheme.has_icon')
+ * - GObject properties ('Window.default_height')
+ *
+ * Returns: %true if @lib can be imported and provides @symbol, %false otherwise
+ */
+function checkSymbol(lib, version, symbol) {
+ let Lib = null;
+
+ if (version)
+ imports.gi.versions[lib] = version;
+
+ try {
+ Lib = imports.gi[lib];
+ } catch(e) {
+ return false;
+ }
+
+ if (!symbol)
+ return true; // Done
+
+ let [klass, sym] = symbol.split('.');
+ if (klass === symbol) // global symbol
+ return (typeof Lib[symbol] !== 'undefined');
+
+ let obj = Lib[klass];
+ if (typeof obj === 'undefined')
+ return false;
+
+ if (typeof obj[sym] !== 'undefined' ||
+ (obj.prototype && typeof obj.prototype[sym] !== 'undefined'))
+ return true; // class- or object method
+
+ // GObject property
+ let pspec = null;
+ if (GObject.type_is_a(obj.$gtype, GObject.TYPE_INTERFACE)) {
+ let iface = GObject.type_default_interface_ref(obj.$gtype);
+ pspec = GObject.Object.interface_find_property(iface, sym);
+ } else if (GObject.type_is_a(obj.$gtype, GObject.TYPE_OBJECT)) {
+ pspec = GObject.Object.find_property.call(obj.$gtype, sym);
+ }
+
+ return (pspec !== null);
+}
+
function initGettext() {
Gettext.bindtextdomain(_pkgname, localedir);
Gettext.textdomain(_pkgname);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]