[gjs: 2/3] installed-tests: Test both GTK3 and GTK4



commit 0db7f3a1e464e8bbc2b27feb24bc48b8ee49a1ba
Author: Florian Müllner <fmuellner gnome org>
Date:   Thu Apr 16 03:15:43 2020 +0200

    installed-tests: Test both GTK3 and GTK4
    
    The GTK4 tests are a copy of the existing tests with mostly minor
    changes to adapt to API changes. The notably exception are dropped
    tests for GdkAtom and container child properties, as those are no
    longer present in GTK4.

 installed-tests/js/{complex.ui => complex3.ui} |   0
 installed-tests/js/complex4.ui                 |  26 ++++
 installed-tests/js/jsunit.gresources.xml       |   3 +-
 installed-tests/js/meson.build                 |   6 +-
 installed-tests/js/{testGtk.js => testGtk3.js} |   4 +-
 installed-tests/js/testGtk4.js                 | 195 +++++++++++++++++++++++++
 installed-tests/js/testLegacyGtk.js            |   2 +-
 meson.build                                    |   4 +
 8 files changed, 235 insertions(+), 5 deletions(-)
---
diff --git a/installed-tests/js/complex.ui b/installed-tests/js/complex3.ui
similarity index 100%
rename from installed-tests/js/complex.ui
rename to installed-tests/js/complex3.ui
diff --git a/installed-tests/js/complex4.ui b/installed-tests/js/complex4.ui
new file mode 100644
index 00000000..f355785a
--- /dev/null
+++ b/installed-tests/js/complex4.ui
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="Gjs_MyComplexGtkSubclassFromResource" parent="GtkGrid">
+    <property name="margin_top">10</property>
+    <property name="margin_bottom">10</property>
+    <property name="margin_start">10</property>
+    <property name="margin_end">10</property>
+    <child>
+      <object class="GtkLabel" id="label-child">
+        <property name="label">Complex!</property>
+        <signal name="copy-clipboard" handler="templateCallback" swapped="no"/>
+      </object>
+    </child>
+    <child>
+      <object class="GtkLabel" id="label-child2">
+        <property name="label">Complex as well!</property>
+        <signal name="copy-clipboard" handler="boundCallback" object="label-child" swapped="no"/>
+      </object>
+    </child>
+    <child>
+      <object class="GtkLabel" id="internal-label-child">
+        <property name="label">Complex and internal!</property>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/installed-tests/js/jsunit.gresources.xml b/installed-tests/js/jsunit.gresources.xml
index 21002005..e0060c8c 100644
--- a/installed-tests/js/jsunit.gresources.xml
+++ b/installed-tests/js/jsunit.gresources.xml
@@ -1,7 +1,8 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <gresources>
   <gresource prefix="/org/gjs/jsunit">
-    <file preprocess="xml-stripblanks">complex.ui</file>
+    <file preprocess="xml-stripblanks">complex3.ui</file>
+    <file preprocess="xml-stripblanks">complex4.ui</file>
     <file>jasmine.js</file>
     <file>minijasmine.js</file>
     <file>modules/alwaysThrows.js</file>
diff --git a/installed-tests/js/meson.build b/installed-tests/js/meson.build
index 79ac4633..9c424418 100644
--- a/installed-tests/js/meson.build
+++ b/installed-tests/js/meson.build
@@ -122,10 +122,14 @@ endif
 
 if not get_option('skip_gtk_tests')
     jasmine_tests += [
-        'Gtk',
+        'Gtk3',
         'GObjectDestructionAccess',
         'LegacyGtk',
     ]
+
+    if have_gtk4
+        jasmine_tests += 'Gtk4'
+    endif
 endif
 
 installed_js_tests_dir = installed_tests_execdir / 'js'
diff --git a/installed-tests/js/testGtk.js b/installed-tests/js/testGtk3.js
similarity index 99%
rename from installed-tests/js/testGtk.js
rename to installed-tests/js/testGtk3.js
index c7200c27..288bdbdc 100644
--- a/installed-tests/js/testGtk.js
+++ b/installed-tests/js/testGtk3.js
@@ -65,7 +65,7 @@ MyComplexGtkSubclass.prototype.testChildrenExist = function () {
 };
 
 const MyComplexGtkSubclassFromResource = GObject.registerClass({
-    Template: 'resource:///org/gjs/jsunit/complex.ui',
+    Template: 'resource:///org/gjs/jsunit/complex3.ui',
     Children: ['label-child', 'label-child2'],
     InternalChildren: ['internal-label-child'],
 }, class MyComplexGtkSubclassFromResource extends Gtk.Grid {
@@ -187,7 +187,7 @@ describe('Gtk overrides', function () {
         w.destroy();
         System.gc();
 
-        GLib.test_assert_expected_messages_internal('Gjs', 'testGtk.js', 0,
+        GLib.test_assert_expected_messages_internal('Gjs', 'testGtk3.js', 0,
             'Gtk overrides avoid crashing and print a stack trace');
     });
 
diff --git a/installed-tests/js/testGtk4.js b/installed-tests/js/testGtk4.js
new file mode 100644
index 00000000..a9cff2ad
--- /dev/null
+++ b/installed-tests/js/testGtk4.js
@@ -0,0 +1,195 @@
+imports.gi.versions.Gtk = '4.0';
+
+const ByteArray = imports.byteArray;
+const {GLib, Gio, GObject, Gtk} = imports.gi;
+const System = imports.system;
+
+// This is ugly here, but usually it would be in a resource
+function createTemplate(className) {
+    return `
+<interface>
+  <template class="${className}" parent="GtkGrid">
+    <property name="margin_top">10</property>
+    <property name="margin_bottom">10</property>
+    <property name="margin_start">10</property>
+    <property name="margin_end">10</property>
+    <child>
+      <object class="GtkLabel" id="label-child">
+        <property name="label">Complex!</property>
+        <signal name="copy-clipboard" handler="templateCallback" swapped="no"/>
+      </object>
+    </child>
+    <child>
+      <object class="GtkLabel" id="label-child2">
+        <property name="label">Complex as well!</property>
+        <signal name="copy-clipboard" handler="boundCallback" object="label-child" swapped="no"/>
+      </object>
+    </child>
+    <child>
+      <object class="GtkLabel" id="internal-label-child">
+        <property name="label">Complex and internal!</property>
+      </object>
+    </child>
+  </template>
+</interface>`;
+}
+
+const MyComplexGtkSubclass = GObject.registerClass({
+    Template: ByteArray.fromString(createTemplate('Gjs_MyComplexGtkSubclass')),
+    Children: ['label-child', 'label-child2'],
+    InternalChildren: ['internal-label-child'],
+    CssName: 'complex-subclass',
+}, class MyComplexGtkSubclass extends Gtk.Grid {
+    templateCallback(widget) {
+        this.callbackEmittedBy = widget;
+    }
+
+    boundCallback(widget) {
+        widget.callbackBoundTo = this;
+    }
+});
+
+// Sadly, putting this in the body of the class will prevent calling
+// get_template_child, since MyComplexGtkSubclass will be bound to the ES6
+// class name without the GObject goodies in it
+MyComplexGtkSubclass.prototype.testChildrenExist = function () {
+    this._internalLabel = this.get_template_child(MyComplexGtkSubclass, 'label-child');
+    expect(this._internalLabel).toEqual(jasmine.anything());
+
+    expect(this.label_child2).toEqual(jasmine.anything());
+    expect(this._internal_label_child).toEqual(jasmine.anything());
+};
+
+const MyComplexGtkSubclassFromResource = GObject.registerClass({
+    Template: 'resource:///org/gjs/jsunit/complex4.ui',
+    Children: ['label-child', 'label-child2'],
+    InternalChildren: ['internal-label-child'],
+}, class MyComplexGtkSubclassFromResource extends Gtk.Grid {
+    testChildrenExist() {
+        expect(this.label_child).toEqual(jasmine.anything());
+        expect(this.label_child2).toEqual(jasmine.anything());
+        expect(this._internal_label_child).toEqual(jasmine.anything());
+    }
+
+    templateCallback(widget) {
+        this.callbackEmittedBy = widget;
+    }
+
+    boundCallback(widget) {
+        widget.callbackBoundTo = this;
+    }
+});
+
+const [templateFile, stream] = Gio.File.new_tmp(null);
+const baseStream = stream.get_output_stream();
+const out = new Gio.DataOutputStream({baseStream});
+out.put_string(createTemplate('Gjs_MyComplexGtkSubclassFromFile'), null);
+out.close(null);
+
+const MyComplexGtkSubclassFromFile = GObject.registerClass({
+    Template: templateFile.get_uri(),
+    Children: ['label-child', 'label-child2'],
+    InternalChildren: ['internal-label-child'],
+}, class MyComplexGtkSubclassFromFile extends Gtk.Grid {
+    testChildrenExist() {
+        expect(this.label_child).toEqual(jasmine.anything());
+        expect(this.label_child2).toEqual(jasmine.anything());
+        expect(this._internal_label_child).toEqual(jasmine.anything());
+    }
+
+    templateCallback(widget) {
+        this.callbackEmittedBy = widget;
+    }
+
+    boundCallback(widget) {
+        widget.callbackBoundTo = this;
+    }
+});
+
+const SubclassSubclass = GObject.registerClass(
+    class SubclassSubclass extends MyComplexGtkSubclass {});
+
+function validateTemplate(description, ClassName, pending = false) {
+    let suite = pending ? xdescribe : describe;
+    suite(description, function () {
+        let win, content;
+        beforeEach(function () {
+            win = new Gtk.Window();
+            content = new ClassName();
+            content.label_child.emit('copy-clipboard');
+            content.label_child2.emit('copy-clipboard');
+            win.add(content);
+        });
+
+        it('sets up internal and public template children', function () {
+            content.testChildrenExist();
+        });
+
+        it('sets up public template children with the correct widgets', function () {
+            expect(content.label_child.get_label()).toEqual('Complex!');
+            expect(content.label_child2.get_label()).toEqual('Complex as well!');
+        });
+
+        it('sets up internal template children with the correct widgets', function () {
+            expect(content._internal_label_child.get_label())
+                .toEqual('Complex and internal!');
+        });
+
+        it('connects template callbacks to the correct handler', function () {
+            expect(content.callbackEmittedBy).toBe(content.label_child);
+        });
+
+        it('binds template callbacks to the correct object', function () {
+            expect(content.label_child2.callbackBoundTo).toBe(content.label_child);
+        });
+
+        afterEach(function () {
+            win.destroy();
+        });
+    });
+}
+
+describe('Gtk overrides', function () {
+    beforeAll(function () {
+        Gtk.init();
+    });
+
+    afterAll(function () {
+        templateFile.delete(null);
+    });
+
+    validateTemplate('UI template', MyComplexGtkSubclass);
+    validateTemplate('UI template from resource', MyComplexGtkSubclassFromResource);
+    validateTemplate('UI template from file', MyComplexGtkSubclassFromFile);
+    validateTemplate('Class inheriting from template class', SubclassSubclass, true);
+
+    it('sets CSS names on classes', function () {
+        expect(Gtk.Widget.get_css_name.call(MyComplexGtkSubclass)).toEqual('complex-subclass');
+    });
+
+    it('avoid crashing when GTK vfuncs are called in garbage collection', function () {
+        GLib.test_expect_message('Gjs', GLib.LogLevelFlags.LEVEL_CRITICAL,
+            '*during garbage collection*');
+        GLib.test_expect_message('Gjs', GLib.LogLevelFlags.LEVEL_CRITICAL,
+            '*destroy*');
+
+        let BadLabel = GObject.registerClass(class BadLabel extends Gtk.Label {
+            vfunc_destroy() {}
+        });
+
+        let w = new Gtk.Window();
+        w.add(new BadLabel());
+
+        w.destroy();
+        System.gc();
+
+        GLib.test_assert_expected_messages_internal('Gjs', 'testGtk4.js', 0,
+            'Gtk overrides avoid crashing and print a stack trace');
+    });
+
+    it('can create a Gtk.TreeIter with accessible stamp field', function () {
+        const iter = new Gtk.TreeIter();
+        iter.stamp = 42;
+        expect(iter.stamp).toEqual(42);
+    });
+});
diff --git a/installed-tests/js/testLegacyGtk.js b/installed-tests/js/testLegacyGtk.js
index 21f861f0..198d5c3b 100644
--- a/installed-tests/js/testLegacyGtk.js
+++ b/installed-tests/js/testLegacyGtk.js
@@ -56,7 +56,7 @@ const MyComplexGtkSubclass = new Lang.Class({
 const MyComplexGtkSubclassFromResource = new Lang.Class({
     Name: 'MyComplexGtkSubclassFromResource',
     Extends: Gtk.Grid,
-    Template: 'resource:///org/gjs/jsunit/complex.ui',
+    Template: 'resource:///org/gjs/jsunit/complex3.ui',
     Children: ['label-child', 'label-child2'],
     InternalChildren: ['internal-label-child'],
 
diff --git a/meson.build b/meson.build
index 9fb16fa9..4b574038 100644
--- a/meson.build
+++ b/meson.build
@@ -606,6 +606,10 @@ if host_machine.system() != 'windows'
     subdir('test')
 endif
 
+if not get_option('skip_gtk_tests')
+    have_gtk4 = dependency('gtk4', required: false).found()
+endif
+
 subdir('installed-tests')
 
 valgrind_environment = environment()


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]