cjs-128.0/0000775000175000017500000000000014771557763011255 5ustar fabiofabiocjs-128.0/debian/0000775000175000017500000000000014771557763012477 5ustar fabiofabiocjs-128.0/installed-tests/0000775000175000017500000000000014771557763014374 5ustar fabiofabiocjs-128.0/installed-tests/meson.build0000664000175000017500000000555114771557763016544 0ustar fabiofabio# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2019 Philip Chimento # SPDX-FileCopyrightText: 2019 Chun-wei Fan ### Installed tests ############################################################ # Simple shell script tests # simple_tests = [] tests_dependencies = [ cjs_console, cjs_private_typelib, ] # The test scripts need to be ported from shell scripts # for clang-cl builds, which do not use BASH-style shells if cxx.get_argument_syntax() != 'msvc' simple_tests += [ 'CommandLine', 'CommandLineModules', 'Warnings', ] endif foreach test : simple_tests test_file = files('scripts' / 'test@0@.sh'.format(test)) test(test, test_file, env: tests_environment, protocol: 'tap', suite: 'Scripts', depends: tests_dependencies) test_description_subst = { 'name': 'test@0@.sh'.format(test), 'installed_tests_execdir': prefix / installed_tests_execdir, } configure_file(configuration: test_description_subst, input: 'script.test.in', output: 'test@0@.sh.test'.format(test), install: get_option('installed_tests'), install_dir: installed_tests_metadir) if get_option('installed_tests') install_data(test_file, install_dir: installed_tests_execdir / 'scripts') endif endforeach # Jasmine tests # subdir('js') # Debugger script tests # debugger_tests = [ 'backtrace', 'breakpoint', 'continue', 'delete', 'detach', 'down-up', 'finish', 'frame', 'keys', 'lastvalues', 'list', 'next', 'print', 'quit', 'return', 'set', 'step', 'throw', 'throw-ignored', 'until', ] debugger_test_driver = find_program(files('debugger-test.sh')) if get_option('installed_tests') install_data('debugger-test.sh', install_dir: installed_tests_execdir) endif foreach test : debugger_tests test_file = files('debugger' / '@0@.debugger'.format(test)) test('@0@ command'.format(test), debugger_test_driver, args: test_file, env: tests_environment, protocol: 'tap', suite: 'Debugger', depends: tests_dependencies) test_description_subst = { 'name': '@0@.debugger'.format(test), 'installed_tests_execdir': prefix / installed_tests_execdir, } configure_file(configuration: test_description_subst, input: 'debugger.test.in', output: '@0@.test'.format(test), install: get_option('installed_tests'), install_dir: installed_tests_metadir) if get_option('installed_tests') install_data(test_file, install_dir: installed_tests_execdir / 'debugger') install_data('debugger' / '@0@.debugger.js'.format(test), 'debugger' / '@0@.debugger.output'.format(test), install_dir: installed_tests_execdir / 'debugger') endif endforeach cjs-128.0/installed-tests/script.test.in0000664000175000017500000000027314771557763017210 0ustar fabiofabio# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2013 Red Hat, Inc. [Test] Type=session Exec=sh @prefix@/@installed_tests_execdir@/scripts/@name@ Output=TAP cjs-128.0/installed-tests/debugger-test.sh0000664000175000017500000000137514771557763017477 0ustar fabiofabio#!/bin/sh # SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2018 Philip Chimento if test "$GJS_USE_UNINSTALLED_FILES" = "1"; then gjs="$TOP_BUILDDIR/cjs-console" else gjs=cjs-console fi echo 1..1 DEBUGGER_SCRIPT="$1" JS_SCRIPT="$1.js" EXPECTED_OUTPUT="$1.output" THE_DIFF=$("$gjs" -d "$JS_SCRIPT" < "$DEBUGGER_SCRIPT" | sed \ -e "s#$1#$(basename $1)#g" \ -e "s/0x[0-9a-f]\{4,16\}/0xADDR/g" \ | diff -u "$EXPECTED_OUTPUT" -) EXITCODE=$? if test -n "$THE_DIFF"; then echo "not ok 1 - $1" echo "$THE_DIFF" | while read line; do echo "#$line"; done else if test $EXITCODE -ne 0; then echo "not ok 1 - $1 # command failed" exit 1 fi echo "ok 1 - $1" fi cjs-128.0/installed-tests/.eslintrc.yml0000664000175000017500000000023614771557763017021 0ustar fabiofabio--- # SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2020 Evan Welsh rules: jsdoc/require-jsdoc: 'off' cjs-128.0/installed-tests/minijasmine.test.in0000664000175000017500000000035614771557763020211 0ustar fabiofabio# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2016 Philip Chimento [Test] Type=session Exec=@installed_tests_execdir@/minijasmine @installed_tests_execdir@/js/@name@ Output=TAP cjs-128.0/installed-tests/js/0000775000175000017500000000000014771557763015010 5ustar fabiofabiocjs-128.0/installed-tests/js/testMainloop.js0000664000175000017500000000641114771557763020026 0ustar fabiofabio/* eslint-disable no-restricted-properties */ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2008 litl, LLC const Mainloop = imports.mainloop; describe('Mainloop.timeout_add()', function () { let runTenTimes, runOnlyOnce, neverRun, neverRunSource; beforeAll(function (done) { let count = 0; runTenTimes = jasmine.createSpy('runTenTimes').and.callFake(() => { if (count === 10) { done(); return false; } count += 1; return true; }); runOnlyOnce = jasmine.createSpy('runOnlyOnce').and.returnValue(false); neverRun = jasmine.createSpy('neverRun').and.throwError(); Mainloop.timeout_add(10, runTenTimes); Mainloop.timeout_add(10, runOnlyOnce); neverRunSource = Mainloop.timeout_add(90000, neverRun); }); it('runs a timeout function', function () { expect(runOnlyOnce).toHaveBeenCalledTimes(1); }); it('runs a timeout function until it returns false', function () { expect(runTenTimes).toHaveBeenCalledTimes(11); }); it('runs a timeout function after an initial timeout', function () { expect(neverRun).not.toHaveBeenCalled(); }); afterAll(function () { Mainloop.source_remove(neverRunSource); }); }); describe('Mainloop.idle_add()', function () { let runOnce, runTwice, neverRuns, quitAfterManyRuns; beforeAll(function (done) { runOnce = jasmine.createSpy('runOnce').and.returnValue(false); runTwice = jasmine.createSpy('runTwice').and.returnValues([true, false]); neverRuns = jasmine.createSpy('neverRuns').and.throwError(); let count = 0; quitAfterManyRuns = jasmine.createSpy('quitAfterManyRuns').and.callFake(() => { count += 1; if (count > 10) { done(); return false; } return true; }); Mainloop.idle_add(runOnce); Mainloop.idle_add(runTwice); let neverRunsId = Mainloop.idle_add(neverRuns); Mainloop.idle_add(quitAfterManyRuns); Mainloop.source_remove(neverRunsId); }); it('runs an idle function', function () { expect(runOnce).toHaveBeenCalledTimes(1); }); it('continues to run idle functions that return true', function () { expect(runTwice).toHaveBeenCalledTimes(2); expect(quitAfterManyRuns).toHaveBeenCalledTimes(11); }); it('does not run idle functions if removed', function () { expect(neverRuns).not.toHaveBeenCalled(); }); it('can remove idle functions while they are being invoked', function (done) { let removeId = Mainloop.idle_add(() => { Mainloop.source_remove(removeId); done(); return false; }); }); // Add an idle before exit, then never run main loop again. // This is to test that we remove idle callbacks when the associated // JSContext is blown away. The leak check in minijasmine will // fail if the idle function is not garbage collected. it('does not leak idle callbacks', function () { Mainloop.idle_add(() => { fail('This should never have been called'); return true; }); }); }); cjs-128.0/installed-tests/js/testIntrospection.js0000664000175000017500000002236714771557763021120 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2008, 2018 Red Hat, Inc. // SPDX-FileCopyrightText: 2017 Philip Chimento // SPDX-FileCopyrightText: 2020 Ole Jørgen Brønner // Various tests having to do with how introspection is implemented in GJS imports.gi.versions.Gdk = '3.0'; imports.gi.versions.Gtk = '3.0'; const {Gdk, Gio, GLib, GObject, Gtk} = imports.gi; const System = imports.system; describe('GLib.DestroyNotify parameter', function () { it('throws when encountering a GDestroyNotify not associated with a callback', function () { // should throw when called, not when the function object is created expect(() => Gio.MemoryInputStream.new_from_data).not.toThrow(); // the 'destroy' argument applies to the data, which is not supported in // gobject-introspection expect(() => Gio.MemoryInputStream.new_from_data('foobar')) .toThrowError(/destroy/); }); }); describe('Unsafe integer marshalling', function () { it('warns when conversion is lossy', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, '*cannot be safely stored*'); GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, '*cannot be safely stored*'); GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, '*cannot be safely stored*'); void GLib.MININT64; void GLib.MAXINT64; void GLib.MAXUINT64; GLib.test_assert_expected_messages_internal('Cjs', 'testEverythingBasic.js', 0, 'Limits warns when conversion is lossy'); }); }); describe('Marshalling empty flat arrays of structs', function () { let widget; let gtkEnabled; beforeAll(function () { gtkEnabled = GLib.getenv('ENABLE_GTK') === 'yes'; if (!gtkEnabled) return; Gtk.init(null); }); beforeEach(function () { if (!gtkEnabled) { pending('GTK disabled'); return; } widget = new Gtk.Label(); }); it('accepts null', function () { widget.drag_dest_set(0, null, Gdk.DragAction.COPY); }); it('accepts an empty array', function () { widget.drag_dest_set(0, [], Gdk.DragAction.COPY); }); }); describe('Constructor', function () { it('throws when constructor called without new', function () { expect(() => Gio.AppLaunchContext()) .toThrowError(/Constructor called as normal method/); }); }); describe('Enum classes', function () { it('enum has a $gtype property', function () { expect(Gio.BusType.$gtype).toBeDefined(); }); it('enum $gtype property is enumerable', function () { expect('$gtype' in Gio.BusType).toBeTruthy(); }); }); describe('GError domains', function () { it('Number converts error to quark', function () { expect(Gio.ResolverError.quark()).toEqual(Number(Gio.ResolverError)); }); }); describe('Object properties on GtkBuilder-constructed objects', function () { let o1; let gtkEnabled; beforeAll(function () { gtkEnabled = GLib.getenv('ENABLE_GTK') === 'yes'; if (!gtkEnabled) return; Gtk.init(null); }); beforeEach(function () { if (!gtkEnabled) { pending('GTK disabled'); return; } const ui = ` Click me `; let builder = Gtk.Builder.new_from_string(ui, -1); o1 = builder.get_object('button'); }); it('are found on the GObject itself', function () { expect(o1.label).toBe('Click me'); }); it('are found on the GObject\'s parents', function () { expect(o1.visible).toBeFalsy(); }); it('are found on the GObject\'s interfaces', function () { expect(o1.action_name).toBeNull(); }); }); describe('Garbage collection of introspected objects', function () { // This tests a regression that would very rarely crash, but // when run under valgrind this code would show use-after-free. it('collects objects properly with signals connected', function (done) { function orphanObject() { let obj = new GObject.Object(); obj.connect('notify', () => {}); } orphanObject(); System.gc(); GLib.idle_add(GLib.PRIORITY_LOW, () => done()); }); // This tests a race condition that would crash; it should warn instead it('handles setting a property from C on an object whose JS wrapper has been collected', function (done) { class SomeObject extends GObject.Object { static [GObject.properties] = { 'screenfull': GObject.ParamSpec.boolean('screenfull', '', '', GObject.ParamFlags.READWRITE, false), }; static { GObject.registerClass(this); } } GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, '*property screenfull*'); const settings = new Gio.Settings({schemaId: 'org.cinnamon.CjsTest'}); let obj = new SomeObject(); settings.bind('fullscreen', obj, 'screenfull', Gio.SettingsBindFlags.DEFAULT); const handler = settings.connect('changed::fullscreen', () => { obj.run_dispose(); obj = null; settings.disconnect(handler); GLib.idle_add(GLib.PRIORITY_LOW, () => { GLib.test_assert_expected_messages_internal('Cjs', 'testIntrospection.js', 0, 'Warn about setting property on disposed JS object'); done(); }); }); settings.set_boolean('fullscreen', !settings.get_boolean('fullscreen')); settings.reset('fullscreen'); }); }); describe('Gdk.Atom', function () { it('is presented as string', function () { expect(Gdk.Atom.intern('CLIPBOARD', false)).toBe('CLIPBOARD'); expect(Gdk.Atom.intern('NONE', false)).toBe(null); }); }); describe('Complete enumeration (boxed types)', function () { it('enumerates all properties', function () { // Note: this test breaks down if other code access all the methods of Rectangle const rect = new Gdk.Rectangle(); const names = Object.getOwnPropertyNames(Object.getPrototypeOf(rect)); const expectAtLeast = ['equal', 'intersect', 'union', 'x', 'y', 'width', 'height']; expect(names).toEqual(jasmine.arrayContaining(expectAtLeast)); }); }); describe('Complete enumeration of GIRepositoryNamespace (new_enumerate)', function () { it('enumerates all properties (sampled)', function () { const names = Object.getOwnPropertyNames(Gdk); // Note: properties which has been accessed are listed without new_enumerate hook const expectAtLeast = ['KEY_ybelowdot', 'EventSequence', 'ByteOrder', 'Window']; expect(names).toEqual(jasmine.arrayContaining(expectAtLeast)); }); it('all enumerated properties are defined', function () { const names = Object.keys(Gdk); expect(() => { // Access each enumerated property to check it can be defined. names.forEach(name => Gdk[name]); }).not.toThrowError(/API of type .* not implemented, cannot define .*/); }); }); describe('Backwards compatibility for GLib/Gio platform specific GIRs', function () { // Only test this if GioUnix is available const skip = imports.gi.versions.GioUnix !== '2.0'; it('GioUnix objects are looked up in GioUnix, not Gio', function () { if (skip) { pending('GioUnix required for this test'); return; } GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, '*Gio.UnixMountMonitor*'); const monitor = Gio.UnixMountMonitor.get(); expect(monitor.toString()).toContain('GIName:GioUnix.MountMonitor'); GLib.test_assert_expected_messages_internal('Cjs', 'testIntrospection.js', 0, 'Expected deprecation message for Gio.Unix -> GioUnix'); }); it("doesn't print the message if the type isn't resolved directly", function () { if (skip) { pending('GioUnix required for this test'); return; } const launcher = new Gio.SubprocessLauncher({flags: Gio.SubprocessFlags.STDOUT_PIPE}); const proc = launcher.spawnv(['ls', '/dev/null']); expect(proc.get_stdout_pipe().toString()).toContain('GIName:GioUnix.InputStream'); }); it('has some exceptions', function () { expect(Gio.UnixConnection.toString()).toContain('Gio_UnixConnection'); const credentialsMessage = new Gio.UnixCredentialsMessage(); expect(credentialsMessage.toString()).toContain('GIName:Gio.UnixCredentials'); const fdList = new Gio.UnixFDList(); expect(fdList.toString()).toContain('GIName:Gio.UnixFDList'); const socketAddress = Gio.UnixSocketAddress.new_with_type('', Gio.UnixSocketAddressType.ANONYMOUS); expect(socketAddress.toString()).toContain('GIName:Gio.UnixSocketAddress'); }); }); cjs-128.0/installed-tests/js/testImporter2.js0000664000175000017500000000322614771557763020134 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2023 Philip Chimento // This test is in a separate file instead of testImporter.js, because it tests // loading overrides for g-i modules, and in the original file we have literally // run out of g-i modules to override -- at least, the ones that we can assume // will be present on any system where GJS is compiled. describe('GI importer', function () { describe('on failure', function () { // For these tests, we provide special overrides files to sabotage the // import, at the path resource:///org/cjs/jsunit/modules/badOverrides2. let oldSearchPath; beforeAll(function () { oldSearchPath = imports.overrides.searchPath.slice(); imports.overrides.searchPath = ['resource:///org/cjs/jsunit/modules/badOverrides2']; }); afterAll(function () { imports.overrides.searchPath = oldSearchPath; }); it("throws an exception when the overrides _init isn't a function", function () { expect(() => imports.gi.GIMarshallingTests).toThrowError(/_init/); }); it('throws an exception when the overrides _init is null', function () { expect(() => imports.gi.Gio).toThrowError(/_init/); }); it('throws an exception when the overrides _init is undefined', function () { expect(() => imports.gi.Regress).toThrowError(/_init/); }); it('throws an exception when the overrides _init is missing', function () { expect(() => imports.gi.WarnLib).toThrowError(/_init/); }); }); }); cjs-128.0/installed-tests/js/testGIMarshalling.js0000664000175000017500000025772614771557763020752 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2010 Collabora, Ltd. // SPDX-FileCopyrightText: 2010 litl, LLC // SPDX-FileCopyrightText: 2010 Giovanni Campagna // SPDX-FileCopyrightText: 2011 Red Hat, Inc. // SPDX-FileCopyrightText: 2016 Endless Mobile, Inc. // SPDX-FileCopyrightText: 2019 Philip Chimento // Load overrides for GIMarshallingTests imports.overrides.searchPath.unshift('resource:///org/cjs/jsunit/modules/overrides'); const GIMarshallingTests = imports.gi.GIMarshallingTests; // We use Gio and GLib to have some objects that we know exist const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; // Some helpers to cut down on repetitive marshalling tests. // - options.omit: the test doesn't exist, don't create a test case // - options.skip: the test does exist, but doesn't pass, either unsupported or // a bug in GJS. Create the test case and mark it pending function testReturnValue(root, value, {omit, skip, funcName = `${root}_return`} = {}) { if (omit) return; it('marshals as a return value', function () { if (skip) pending(skip); expect(GIMarshallingTests[funcName]()).toEqual(value); }); } function testInParameter(root, value, {omit, skip, funcName = `${root}_in`} = {}) { if (omit) return; it('marshals as an in parameter', function () { if (skip) pending(skip); expect(() => GIMarshallingTests[funcName](value)).not.toThrow(); }); } function testOutParameter(root, value, {omit, skip, funcName = `${root}_out`} = {}) { if (omit) return; it('marshals as an out parameter', function () { if (skip) pending(skip); expect(GIMarshallingTests[funcName]()).toEqual(value); }); } function testUninitializedOutParameter(root, defaultValue, {omit, skip, funcName = `${root}_out_uninitialized`} = {}) { if (omit) return; it("picks a reasonable default value when the function doesn't set the out parameter", function () { if (skip) pending(skip); const [success, defaultVal] = GIMarshallingTests[funcName](); expect(success).toBeFalse(); expect(defaultVal).toEqual(defaultValue); }); } function testInoutParameter(root, inValue, outValue, {omit, skip, funcName = `${root}_inout`} = {}) { if (omit) return; it('marshals as an inout parameter', function () { if (skip) pending(skip); expect(GIMarshallingTests[funcName](inValue)).toEqual(outValue); }); } function testSimpleMarshalling(root, value, inoutValue, defaultValue, options = {}) { testReturnValue(root, value, options.returnv); testInParameter(root, value, options.in); testOutParameter(root, value, options.out); testUninitializedOutParameter(root, defaultValue, options.uninitOut); testInoutParameter(root, value, inoutValue, options.inout); } function testTransferMarshalling(root, value, inoutValue, defaultValue, options = {}) { describe('with transfer none', function () { testSimpleMarshalling(`${root}_none`, value, inoutValue, defaultValue, options.none); }); describe('with transfer full', function () { const fullOptions = { in: { omit: true, // this case is not in the test suite }, inout: { skip: 'https://gitlab.gnome.org/GNOME/gobject-introspection/issues/192', }, }; Object.assign(fullOptions, options.full); testSimpleMarshalling(`${root}_full`, value, inoutValue, defaultValue, fullOptions); }); } function testContainerMarshalling(root, value, inoutValue, defaultValue, options = {}) { testTransferMarshalling(root, value, inoutValue, defaultValue, options); describe('with transfer container', function () { const containerOptions = { in: { omit: true, // this case is not in the test suite }, inout: { skip: 'https://gitlab.gnome.org/GNOME/gjs/issues/44', }, }; Object.assign(containerOptions, options.container); testSimpleMarshalling(`${root}_container`, value, inoutValue, defaultValue, containerOptions); }); } // Integer limits, defined without reference to GLib (because the GLib.MAXINT8 // etc. constants are also subject to marshalling) const Limits = { int8: { min: -(2 ** 7), max: 2 ** 7 - 1, umax: 2 ** 8 - 1, }, int16: { min: -(2 ** 15), max: 2 ** 15 - 1, umax: 2 ** 16 - 1, }, int32: { min: -(2 ** 31), max: 2 ** 31 - 1, umax: 2 ** 32 - 1, }, int64: { min: -(2 ** 63), max: 2 ** 63 - 1, umax: 2 ** 64 - 1, bit64: true, // note: unsafe, values will not be accurate! }, short: {}, int: {}, long: {}, ssize: { utype: 'size', }, }; const BigIntLimits = { int64: { min: -(2n ** 63n), max: 2n ** 63n - 1n, umax: 2n ** 64n - 1n, }, }; Object.assign(Limits.short, Limits.int16); Object.assign(Limits.int, Limits.int32); // Platform dependent sizes; expand definitions as needed if (GLib.SIZEOF_LONG === 8) { Object.assign(Limits.long, Limits.int64); BigIntLimits.long = Object.assign({}, BigIntLimits.int64); } else { Object.assign(Limits.long, Limits.int32); } if (GLib.SIZEOF_SSIZE_T === 8) { Object.assign(Limits.ssize, Limits.int64); BigIntLimits.ssize = Object.assign({utype: 'size'}, BigIntLimits.int64); } else { Object.assign(Limits.ssize, Limits.int32); } // Functions for dealing with tests that require or return unsafe 64-bit ints, // until we get BigInts. // Sometimes tests pass if we are comparing two inaccurate values in JS with // each other. That's fine for now. Then we just have to suppress the warnings. function warn64(is64bit, func, ...args) { if (is64bit) { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, '*cannot be safely stored*'); } const retval = func(...args); if (is64bit) { GLib.test_assert_expected_messages_internal('Cjs', 'testGIMarshalling.js', 0, 'Ignore message'); } return retval; } // Other times we compare an inaccurate value marshalled from JS into C, with an // accurate value in C. Those tests we have to skip. function skip64(is64bit) { if (is64bit) pending('https://gitlab.gnome.org/GNOME/gjs/issues/271'); } describe('Boolean', function () { [true, false].forEach(bool => { describe(`${bool}`, function () { testSimpleMarshalling('boolean', bool, !bool, false, { returnv: { funcName: `boolean_return_${bool}`, }, in: { funcName: `boolean_in_${bool}`, }, out: { funcName: `boolean_out_${bool}`, }, uninitOut: { omit: true, }, inout: { funcName: `boolean_inout_${bool}_${!bool}`, }, }); }); }); testUninitializedOutParameter('boolean', false); }); describe('Integer', function () { Object.entries(Limits).forEach(([type, {min, max, umax, bit64, utype = `u${type}`}]) => { describe(`${type}-typed`, function () { it('marshals signed value as a return value', function () { expect(warn64(bit64, GIMarshallingTests[`${type}_return_max`])).toEqual(max); expect(warn64(bit64, GIMarshallingTests[`${type}_return_min`])).toEqual(min); }); it('marshals signed value as an in parameter', function () { skip64(bit64); expect(() => GIMarshallingTests[`${type}_in_max`](max)).not.toThrow(); expect(() => GIMarshallingTests[`${type}_in_min`](min)).not.toThrow(); }); it('marshals signed value as an out parameter', function () { expect(warn64(bit64, GIMarshallingTests[`${type}_out_max`])).toEqual(max); expect(warn64(bit64, GIMarshallingTests[`${type}_out_min`])).toEqual(min); }); testUninitializedOutParameter(type, 0); it('marshals as an inout parameter', function () { skip64(bit64); expect(GIMarshallingTests[`${type}_inout_max_min`](max)).toEqual(min); expect(GIMarshallingTests[`${type}_inout_min_max`](min)).toEqual(max); }); it('marshals unsigned value as a return value', function () { expect(warn64(bit64, GIMarshallingTests[`${utype}_return`])).toEqual(umax); }); it('marshals unsigned value as an in parameter', function () { skip64(bit64); expect(() => GIMarshallingTests[`${utype}_in`](umax)).not.toThrow(); }); it('marshals unsigned value as an out parameter', function () { expect(warn64(bit64, GIMarshallingTests[`${utype}_out`])).toEqual(umax); }); testUninitializedOutParameter(utype, 0); it('marshals unsigned value as an inout parameter', function () { skip64(bit64); expect(GIMarshallingTests[`${utype}_inout`](umax)).toEqual(0); }); }); }); }); describe('BigInt', function () { Object.entries(BigIntLimits).forEach(([type, {min, max, umax, utype = `u${type}`}]) => { describe(`${type}-typed`, function () { it('marshals signed value as an in parameter', function () { expect(() => GIMarshallingTests[`${type}_in_max`](max)).not.toThrow(); expect(() => GIMarshallingTests[`${type}_in_min`](min)).not.toThrow(); }); it('marshals unsigned value as an in parameter', function () { expect(() => GIMarshallingTests[`${utype}_in`](umax)).not.toThrow(); }); }); }); }); describe('Floating point', function () { const FloatLimits = { float: { min: 2 ** -126, max: (2 - 2 ** -23) * 2 ** 127, }, double: { // GLib.MINDOUBLE is the minimum normal value, which is not the same // as the minimum denormal value Number.MIN_VALUE min: 2 ** -1022, max: Number.MAX_VALUE, }, }; Object.entries(FloatLimits).forEach(([type, {min, max}]) => { describe(`${type}-typed`, function () { it('marshals value as a return value', function () { expect(GIMarshallingTests[`${type}_return`]()).toBeCloseTo(max, 10); }); testInParameter(type, max); it('marshals value as an out parameter', function () { expect(GIMarshallingTests[`${type}_out`]()).toBeCloseTo(max, 10); }); testUninitializedOutParameter(type, 0); it('marshals value as an inout parameter', function () { expect(GIMarshallingTests[`${type}_inout`](max)).toBeCloseTo(min, 10); }); it('can handle noncanonical NaN', function () { expect(GIMarshallingTests[`${type}_noncanonical_nan_out`]()).toBeNaN(); }); }); }); }); describe('time_t', function () { testSimpleMarshalling('time_t', 1234567890, 0, 0); }); describe('GType', function () { describe('void', function () { testSimpleMarshalling('gtype', GObject.TYPE_NONE, GObject.TYPE_INT, null); }); describe('string', function () { testSimpleMarshalling('gtype_string', GObject.TYPE_STRING, null, null, { inout: {omit: true}, uninitOut: {omit: true}, }); }); it('can be implicitly converted from a GObject type alias', function () { expect(() => GIMarshallingTests.gtype_in(GObject.VoidType)).not.toThrow(); }); it('can be implicitly converted from a JS type', function () { expect(() => GIMarshallingTests.gtype_string_in(String)).not.toThrow(); }); }); describe('UTF-8 string', function () { testTransferMarshalling('utf8', 'const ♥ utf8', '', null, { full: { uninitOut: {omit: true}, // covered by utf8_dangling_out() test below }, }); it('marshals value as a byte array', function () { expect(() => GIMarshallingTests.utf8_as_uint8array_in('const ♥ utf8')).not.toThrow(); }); it('makes a default out value for a broken C function', function () { expect(GIMarshallingTests.utf8_dangling_out()).toBeNull(); }); }); describe('In-out array in the style of gtk_init()', function () { it('marshals null', function () { const [, newArray] = GIMarshallingTests.init_function(null); expect(newArray).toEqual([]); }); it('marshals an inout empty array', function () { const [ret, newArray] = GIMarshallingTests.init_function([]); expect(ret).toBeTrue(); expect(newArray).toEqual([]); }); it('marshals an inout array', function () { const [ret, newArray] = GIMarshallingTests.init_function(['--foo', '--bar']); expect(ret).toBeTrue(); expect(newArray).toEqual(['--foo']); }); }); describe('Fixed-size C array', function () { describe('of ints', function () { testReturnValue('array_fixed_int', [-1, 0, 1, 2]); testInParameter('array_fixed_int', [-1, 0, 1, 2]); testOutParameter('array_fixed', [-1, 0, 1, 2]); testOutParameter('array_fixed_caller_allocated', [-1, 0, 1, 2]); testInoutParameter('array_fixed', [-1, 0, 1, 2], [2, 1, 0, -1]); }); describe('of shorts', function () { testReturnValue('array_fixed_short', [-1, 0, 1, 2]); testInParameter('array_fixed_short', [-1, 0, 1, 2]); }); it('marshals a struct array as an out parameter', function () { expect(GIMarshallingTests.array_fixed_out_struct()).toEqual([ jasmine.objectContaining({long_: 7, int8: 6}), jasmine.objectContaining({long_: 6, int8: 7}), ]); }); it('marshals a fixed-size struct array as caller allocated out param', function () { expect(GIMarshallingTests.array_fixed_caller_allocated_struct_out()).toEqual([ jasmine.objectContaining({long_: -2, int8: -1}), jasmine.objectContaining({long_: 1, int8: 2}), jasmine.objectContaining({long_: 3, int8: 4}), jasmine.objectContaining({long_: 5, int8: 6}), ]); }); }); describe('C array with length', function () { function createStructArray(StructType = GIMarshallingTests.BoxedStruct) { return [1, 2, 3].map(num => { let struct = new StructType(); struct.long_ = num; return struct; }); } testSimpleMarshalling('array', [-1, 0, 1, 2], [-2, -1, 0, 1, 2], []); it('can be returned along with other arguments', function () { let [array, sum] = GIMarshallingTests.array_return_etc(9, 5); expect(sum).toEqual(14); expect(array).toEqual([9, 0, 1, 5]); }); it('can be passed to a function with its length parameter before it', function () { expect(() => GIMarshallingTests.array_in_len_before([-1, 0, 1, 2])) .not.toThrow(); }); it('can be passed to a function with zero terminator', function () { expect(() => GIMarshallingTests.array_in_len_zero_terminated([-1, 0, 1, 2])) .not.toThrow(); }); describe('of strings', function () { testInParameter('array_string', ['foo', 'bar']); }); it('marshals a byte array as an in parameter', function () { expect(() => GIMarshallingTests.array_uint8_in('abcd')).not.toThrow(); expect(() => GIMarshallingTests.array_uint8_in([97, 98, 99, 100])).not.toThrow(); expect(() => GIMarshallingTests.array_uint8_in(new TextEncoder().encode('abcd'))) .not.toThrow(); }); describe('of signed 64-bit ints', function () { testInParameter('array_int64', [-1, 0, 1, 2]); }); describe('of unsigned 64-bit ints', function () { testInParameter('array_uint64', [-1, 0, 1, 2]); }); describe('of unichars', function () { testInParameter('array_unichar', 'const ♥ utf8'); testOutParameter('array_unichar', 'const ♥ utf8'); it('marshals from an array of codepoints', function () { const codepoints = [...'const ♥ utf8'].map(c => c.codePointAt(0)); expect(() => GIMarshallingTests.array_unichar_in(codepoints)).not.toThrow(); }); }); describe('of booleans', function () { testInParameter('array_bool', [true, false, true, true]); testOutParameter('array_bool', [true, false, true, true]); it('marshals from an array of numbers', function () { expect(() => GIMarshallingTests.array_bool_in([-1, 0, 1, 2])).not.toThrow(); }); }); describe('of boxed structs', function () { testInParameter('array_struct', createStructArray()); describe('passed by value', function () { testInParameter('array_struct_value', createStructArray(), { skip: 'https://gitlab.gnome.org/GNOME/gjs/issues/44', }); }); }); describe('of simple structs', function () { testInParameter('array_simple_struct', createStructArray(GIMarshallingTests.SimpleStruct), { skip: 'https://gitlab.gnome.org/GNOME/gjs/issues/44', }); }); it('marshals two arrays with the same length parameter', function () { const keys = ['one', 'two', 'three']; const values = [1, 2, 3]; expect(() => GIMarshallingTests.multi_array_key_value_in(keys, values)).not.toThrow(); }); // Run twice to ensure that copies are correctly made for (transfer full) it('copies correctly on transfer full', function () { let array = createStructArray(); expect(() => { GIMarshallingTests.array_struct_take_in(array); GIMarshallingTests.array_struct_take_in(array); }).not.toThrow(); }); describe('of enums', function () { testInParameter('array_enum', [ GIMarshallingTests.Enum.VALUE1, GIMarshallingTests.Enum.VALUE2, GIMarshallingTests.Enum.VALUE3, ]); }); describe('of flags', function () { testInParameter('array_flags', [ GIMarshallingTests.Flags.VALUE1, GIMarshallingTests.Flags.VALUE2, GIMarshallingTests.Flags.VALUE3, ]); }); it('marshals an array with a 64-bit length parameter', function () { expect(() => GIMarshallingTests.array_in_guint64_len([-1, 0, 1, 2])).not.toThrow(); }); it('marshals an array with an 8-bit length parameter', function () { expect(() => GIMarshallingTests.array_in_guint8_len([-1, 0, 1, 2])).not.toThrow(); }); it('can be an in-out argument', function () { const array = GIMarshallingTests.array_inout([-1, 0, 1, 2]); expect(array).toEqual([-2, -1, 0, 1, 2]); }); it('can be an in-out argument with in length', function () { if (!GIMarshallingTests.array_inout_length_in) pending('https://gitlab.gnome.org/GNOME/gobject-introspection/-/merge_requests/407'); const array = GIMarshallingTests.array_inout_length_in([-1, 0, 1, 2]); expect(array).toEqual([-2, -1, 1, 2]); }); xit('can be an out argument with in-out length', function () { const array = GIMarshallingTests.array_out_length_inout(5); expect(array).toEqual([-2, -4, -6, 8, -10, -12]); }).pend('https://gitlab.gnome.org/GNOME/gjs/-/issues/560'); it('cannot be an out argument with in-out length', function () { // TODO(3v1n0): remove this test when fixing // https://gitlab.gnome.org/GNOME/gjs/-/issues/560 if (!GIMarshallingTests.array_out_length_inout) pending('https://gitlab.gnome.org/GNOME/gobject-introspection/-/merge_requests/407'); expect(() => GIMarshallingTests.array_out_length_inout(5)).toThrow(); }); xit('can be an in-out argument with out length', function () { const array = GIMarshallingTests.array_inout_length_out([-1, 0, 1, 2]); expect(array).toEqual([-2, -1, 0, 1, 2]); }).pend('https://gitlab.gnome.org/GNOME/gjs/-/issues/560'); it('cannot be an in-out argument with out length', function () { // TODO(3v1n0): remove this test when fixing // https://gitlab.gnome.org/GNOME/gjs/-/issues/560 if (!GIMarshallingTests.array_inout_length_out) pending('https://gitlab.gnome.org/GNOME/gobject-introspection/-/merge_requests/407'); expect(() => GIMarshallingTests.array_inout_length_out([-1, 0, 1, 2])).toThrow(); }); xit('can be an out argument with in length', function () { const array = GIMarshallingTests.array_out_length_in([-1, 0, 1, 2]); expect(array).toEqual([-2, 0, -2, -4]); }).pend('https://gitlab.gnome.org/GNOME/gjs/-/issues/560'); it('cannot be an out argument with in length', function () { // TODO(3v1n0): remove this test when fixing // https://gitlab.gnome.org/GNOME/gjs/-/issues/560 if (!GIMarshallingTests.array_out_length_in) pending('https://gitlab.gnome.org/GNOME/gobject-introspection/-/merge_requests/407'); expect(() => GIMarshallingTests.array_out_length_in([-1, 0, 1, 2])).toThrow(); }); it('can be an out argument along with other arguments', function () { let [array, sum] = GIMarshallingTests.array_out_etc(9, 5); expect(sum).toEqual(14); expect(array).toEqual([9, 0, 1, 5]); }); it('can be an in-out argument along with other arguments', function () { let [array, sum] = GIMarshallingTests.array_inout_etc(9, [-1, 0, 1, 2], 5); expect(sum).toEqual(14); expect(array).toEqual([9, -1, 0, 1, 5]); }); it('does not interpret an unannotated integer as a length parameter', function () { expect(() => GIMarshallingTests.array_in_nonzero_nonlen(42, 'abcd')).not.toThrow(); }); }); describe('Zero-terminated C array', function () { describe('of strings', function () { testSimpleMarshalling('array_zero_terminated', ['0', '1', '2'], ['-1', '0', '1', '2'], null); }); it('marshals null as a zero-terminated array return value', function () { expect(GIMarshallingTests.array_zero_terminated_return_null()).toEqual(null); }); it('marshals an array of structs as a return value', function () { let structArray = GIMarshallingTests.array_zero_terminated_return_struct(); expect(structArray.map(e => e.long_)).toEqual([42, 43, 44]); }); it('marshals an array of unichars as a return value', function () { expect(GIMarshallingTests.array_zero_terminated_return_unichar()) .toEqual('const ♥ utf8'); }); describe('of GLib.Variants', function () { let variantArray; beforeEach(function () { variantArray = [ new GLib.Variant('i', 27), new GLib.Variant('s', 'Hello'), ]; }); ['none', 'container', 'full'].forEach(transfer => { it(`marshals as a transfer-${transfer} in and out parameter`, function () { const returnedArray = GIMarshallingTests[`array_gvariant_${transfer}_in`](variantArray); expect(returnedArray.map(v => v.deepUnpack())).toEqual([27, 'Hello']); }); }); }); }); describe('GArray', function () { describe('of ints with transfer none', function () { testReturnValue('garray_int_none', [-1, 0, 1, 2]); testInParameter('garray_int_none', [-1, 0, 1, 2]); }); it('marshals int64s as a transfer-none return value', function () { expect(warn64(true, GIMarshallingTests.garray_uint64_none_return)) .toEqual([0, Limits.int64.umax]); }); describe('of strings', function () { testContainerMarshalling('garray_utf8', ['0', '1', '2'], ['-2', '-1', '0', '1'], null); it('marshals as a transfer-full caller-allocated out parameter', function () { expect(GIMarshallingTests.garray_utf8_full_out_caller_allocated()) .toEqual(['0', '1', '2']); }).pend('https://gitlab.gnome.org/GNOME/gjs/issues/106'); // https://gitlab.gnome.org/GNOME/gjs/-/issues/344 // the test should be replaced with the one above when issue // https://gitlab.gnome.org/GNOME/gjs/issues/106 is fixed. it('marshals as a transfer-full caller-allocated out parameter throws errors', function () { // should throw when called, not when the function object is created expect(() => GIMarshallingTests.garray_utf8_full_out_caller_allocated).not.toThrow(); expect(() => GIMarshallingTests.garray_utf8_full_out_caller_allocated()) .toThrowError(/type array.*\(out caller-allocates\)/); }); }); it('marshals boxed structs as a transfer-full return value', function () { expect(GIMarshallingTests.garray_boxed_struct_full_return().map(e => e.long_)) .toEqual([42, 43, 44]); }); describe('of booleans with transfer none', function () { testInParameter('garray_bool_none', [-1, 0, 1, 2]); }); describe('of unichars', function () { it('can be passed in with transfer none', function () { expect(() => GIMarshallingTests.garray_unichar_none_in('const \u2665 utf8')) .not.toThrow(); expect(() => GIMarshallingTests.garray_unichar_none_in([0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x2665, 0x20, 0x75, 0x74, 0x66, 0x38])).not.toThrow(); }); }); }); describe('GPtrArray', function () { describe('of strings', function () { testContainerMarshalling('gptrarray_utf8', ['0', '1', '2'], ['-2', '-1', '0', '1'], null); }); describe('of structs', function () { it('can be returned with transfer full', function () { expect(GIMarshallingTests.gptrarray_boxed_struct_full_return().map(e => e.long_)) .toEqual([42, 43, 44]); }); }); }); describe('GByteArray', function () { const refByteArray = Uint8Array.from([0, 49, 0xFF, 51]); testReturnValue('bytearray_full', refByteArray); it('can be passed in with transfer none', function () { expect(() => GIMarshallingTests.bytearray_none_in(refByteArray)) .not.toThrow(); expect(() => GIMarshallingTests.bytearray_none_in([0, 49, 0xFF, 51])) .not.toThrow(); }); }); describe('GBytes', function () { const refByteArray = Uint8Array.from([0, 49, 0xFF, 51]); it('marshals as a transfer-full return value', function () { expect(GIMarshallingTests.gbytes_full_return().toArray()).toEqual(refByteArray); }); it('can be created from an array and passed in', function () { let bytes = GLib.Bytes.new([0, 49, 0xFF, 51]); expect(() => GIMarshallingTests.gbytes_none_in(bytes)).not.toThrow(); }); it('can be created by returning from a function and passed in', function () { var bytes = GIMarshallingTests.gbytes_full_return(); expect(() => GIMarshallingTests.gbytes_none_in(bytes)).not.toThrow(); expect(bytes.toArray()).toEqual(refByteArray); }); it('can be implicitly converted from a Uint8Array', function () { expect(() => GIMarshallingTests.gbytes_none_in(refByteArray)) .not.toThrow(); }); it('can be created from a string and is encoded in UTF-8', function () { let bytes = GLib.Bytes.new('const \u2665 utf8'); expect(() => GIMarshallingTests.utf8_as_uint8array_in(bytes.toArray())) .not.toThrow(); }); it('cannot be passed to a function expecting a byte array', function () { let bytes = GLib.Bytes.new([97, 98, 99, 100]); expect(() => GIMarshallingTests.array_uint8_in(bytes.toArray())).not.toThrow(); expect(() => GIMarshallingTests.array_uint8_in(bytes)).toThrow(); }); }); describe('GStrv', function () { testSimpleMarshalling('gstrv', ['0', '1', '2'], ['-1', '0', '1', '2'], null); }); describe('Array of GStrv', function () { ['length', 'fixed', 'zero_terminated'].forEach(arrayKind => ['none', 'container', 'full'].forEach(transfer => { const testFunction = returnMode => { const commonName = 'array_of_gstrv_transfer'; const funcName = [arrayKind, commonName, transfer, returnMode].join('_'); const func = GIMarshallingTests[funcName]; if (!func) pending('https://gitlab.gnome.org/GNOME/gobject-introspection/-/merge_requests/407'); return func; }; ['out', 'return'].forEach(returnMode => it(`${arrayKind} ${returnMode} transfer ${transfer}`, function () { const func = testFunction(returnMode); expect(func()).toEqual([ ['0', '1', '2'], ['3', '4', '5'], ['6', '7', '8'], ]); })); it(`${arrayKind} in transfer ${transfer}`, function () { const func = testFunction('in'); if (transfer === 'container') pending('https://gitlab.gnome.org/GNOME/gjs/-/issues/44'); expect(() => func([ ['0', '1', '2'], ['3', '4', '5'], ['6', '7', '8'], ])).not.toThrow(); }); it(`${arrayKind} inout transfer ${transfer}`, function () { const func = testFunction('inout'); if (transfer === 'container') pending('https://gitlab.gnome.org/GNOME/gjs/-/issues/44'); const expectedReturn = [ ['-1', '0', '1', '2'], ['-1', '3', '4', '5'], ['-1', '6', '7', '8'], ]; if (arrayKind !== 'fixed') expectedReturn.push(['-1', '9', '10', '11']); expect(func([ ['0', '1', '2'], ['3', '4', '5'], ['6', '7', '8'], ])).toEqual(expectedReturn); }); })); }); ['GList', 'GSList'].forEach(listKind => { const list = listKind.toLowerCase(); describe(listKind, function () { describe('of ints with transfer none', function () { testReturnValue(`${list}_int_none`, [-1, 0, 1, 2]); testInParameter(`${list}_int_none`, [-1, 0, 1, 2]); }); if (listKind === 'GList') { describe('of unsigned 32-bit ints with transfer none', function () { testReturnValue('glist_uint32_none', [0, Limits.int32.umax]); testInParameter('glist_uint32_none', [0, Limits.int32.umax]); }); } describe('of strings', function () { testContainerMarshalling(`${list}_utf8`, ['0', '1', '2'], ['-2', '-1', '0', '1'], []); }); }); }); describe('GHashTable', function () { const numberDict = { '-1': -0.1, 0: 0, 1: 0.1, 2: 0.2, }; describe('with integer values', function () { const intDict = { '-1': 1, 0: 0, 1: -1, 2: -2, }; testReturnValue('ghashtable_int_none', intDict); testInParameter('ghashtable_int_none', intDict); }); describe('with string values', function () { const stringDict = { '-1': '1', 0: '0', 1: '-1', 2: '-2', }; const stringDictOut = { '-1': '1', 0: '0', 1: '1', }; testContainerMarshalling('ghashtable_utf8', stringDict, stringDictOut, null); }); describe('with double values', function () { testInParameter('ghashtable_double', numberDict); }); describe('with float values', function () { testInParameter('ghashtable_float', numberDict); }); describe('with 64-bit int values', function () { const int64Dict = { '-1': -1, 0: 0, 1: 1, 2: 0x100000000, }; testInParameter('ghashtable_int64', int64Dict); }); describe('with unsigned 64-bit int values', function () { const uint64Dict = { '-1': 0x100000000, 0: 0, 1: 1, 2: 2, }; testInParameter('ghashtable_uint64', uint64Dict); }); it('symbol keys are ignored', function () { const symbolDict = { [Symbol('foo')]: 2, '-1': 1, 0: 0, 1: -1, 2: -2, }; expect(() => GIMarshallingTests.ghashtable_int_none_in(symbolDict)).not.toThrow(); }); }); describe('GValue', function () { testSimpleMarshalling('gvalue', 42, '42', null, { inout: { skip: 'https://gitlab.gnome.org/GNOME/gobject-introspection/issues/192', }, }); it('can handle noncanonical float NaN', function () { expect(GIMarshallingTests.gvalue_noncanonical_nan_float()).toBeNaN(); }); it('can handle noncanonical double NaN', function () { expect(GIMarshallingTests.gvalue_noncanonical_nan_double()).toBeNaN(); }); it('marshals as an int64 in parameter', function () { expect(() => GIMarshallingTests.gvalue_int64_in(BigIntLimits.int64.max)) .not.toThrow(); }); it('type objects can be converted from primitive-like types', function () { expect(() => GIMarshallingTests.gvalue_in_with_type(42, GObject.Int)) .not.toThrow(); expect(() => GIMarshallingTests.gvalue_in_with_type(42.5, GObject.Double)) .not.toThrow(); expect(() => GIMarshallingTests.gvalue_in_with_type(42.5, Number)) .not.toThrow(); }); it('can be passed into a function and modified', function () { expect(() => GIMarshallingTests.gvalue_in_with_modification(42)).not.toThrow(); // Let's assume this test doesn't expect that the modified number makes // it back to the caller; it is not possible to "modify" a JS primitive. // // See the "as a boxed type" test below for passing an explicit GObject.Value }); it('can be passed into a function as a boxed type and modified', function () { const value = new GObject.Value(); value.init(GObject.TYPE_INT); value.set_int(42); expect(() => GIMarshallingTests.gvalue_in_with_modification(value)).not.toThrow(); expect(value.get_int()).toBe(24); }); xit('enum can be passed into a function and packed', function () { expect(() => GIMarshallingTests.gvalue_in_enum(GIMarshallingTests.Enum.VALUE3)) .not.toThrow(); }).pend("we don't know to pack enums in a GValue as enum and not int"); it('enum can be passed into a function as a boxed type and packed', function () { const value = new GObject.Value(); // GIMarshallingTests.Enum is a native enum. value.init(GObject.TYPE_ENUM); value.set_enum(GIMarshallingTests.Enum.VALUE3); expect(() => GIMarshallingTests.gvalue_in_enum(value)) .not.toThrow(); }); xit('flags can be passed into a function and packed', function () { expect(() => GIMarshallingTests.gvalue_in_flags(GIMarshallingTests.Flags.VALUE3)) .not.toThrow(); }).pend("we don't know to pack flags in a GValue as flags and not gint"); it('flags can be passed into a function as a boxed type and packed', function () { const value = new GObject.Value(); value.init(GIMarshallingTests.Flags); value.set_flags(GIMarshallingTests.Flags.VALUE3); expect(() => GIMarshallingTests.gvalue_in_flags(value)) .not.toThrow(); }); it('marshals as an int64 out parameter', function () { expect(GIMarshallingTests.gvalue_int64_out()).toEqual(Limits.int64.max); }); it('marshals as a caller-allocated out parameter', function () { expect(GIMarshallingTests.gvalue_out_caller_allocates()).toEqual(42); }); it('array can be passed into a function and packed', function () { expect(() => GIMarshallingTests.gvalue_flat_array([42, '42', true])) .not.toThrow(); }); it('array of boxed type GValues can be passed into a function', function () { const value0 = new GObject.Value(); value0.init(GObject.TYPE_INT); value0.set_int(42); const value1 = new GObject.Value(); value1.init(String); value1.set_string('42'); const value2 = new GObject.Value(); value2.init(Boolean); value2.set_boolean(true); const values = [value0, value1, value2]; expect(() => GIMarshallingTests.gvalue_flat_array(values)) .not.toThrow(); }); it('array of uninitialized boxed GValues', function () { const values = Array(3).fill().map(() => new GObject.Value()); expect(() => GIMarshallingTests.gvalue_flat_array(values)).toThrow(); }); it('array can be passed as an out argument and unpacked', function () { expect(GIMarshallingTests.return_gvalue_flat_array()) .toEqual([42, '42', true]); }); it('array can be passed as an out argument and unpacked when zero-terminated', function () { expect(GIMarshallingTests.return_gvalue_zero_terminated_array()) .toEqual([42, '42', true]); }); xit('array can roundtrip with GValues intact', function () { expect(GIMarshallingTests.gvalue_flat_array_round_trip(42, '42', true)) .toEqual([42, '42', true]); }).pend('https://gitlab.gnome.org/GNOME/gjs/issues/272'); it('can have its type inferred from primitive values', function () { expect(() => GIMarshallingTests.gvalue_in_with_type(42, GObject.TYPE_INT)) .not.toThrow(); expect(() => GIMarshallingTests.gvalue_in_with_type(42.5, GObject.TYPE_DOUBLE)) .not.toThrow(); expect(() => GIMarshallingTests.gvalue_in_with_type('42', GObject.TYPE_STRING)) .not.toThrow(); expect(() => GIMarshallingTests.gvalue_in_with_type(GObject.TYPE_GTYPE, GObject.TYPE_GTYPE)) .not.toThrow(); }); // supplementary tests for gvalue_in_with_type() it('can have its type inferred as a GObject type', function () { expect(() => GIMarshallingTests.gvalue_in_with_type(new Gio.SimpleAction(), Gio.SimpleAction)) .not.toThrow(); }); it('can have its type inferred as a superclass', function () { let action = new Gio.SimpleAction(); expect(() => GIMarshallingTests.gvalue_in_with_type(action, GObject.Object)) .not.toThrow(); expect(() => GIMarshallingTests.gvalue_in_with_type(action, GObject.TYPE_OBJECT)) .not.toThrow(); }); it('can have its type inferred as an interface that it implements', function () { expect(() => GIMarshallingTests.gvalue_in_with_type(new Gio.SimpleAction(), Gio.SimpleAction)) .not.toThrow(); }); it('can have its type inferred as a boxed type', function () { let keyfile = new GLib.KeyFile(); expect(() => GIMarshallingTests.gvalue_in_with_type(keyfile, GLib.KeyFile)) .not.toThrow(); expect(() => GIMarshallingTests.gvalue_in_with_type(keyfile, GObject.TYPE_BOXED)) .not.toThrow(); let struct = new GIMarshallingTests.BoxedStruct(); expect(() => GIMarshallingTests.gvalue_in_with_type(struct, GIMarshallingTests.BoxedStruct)) .not.toThrow(); }); it('can have its type inferred as GVariant', function () { let variant = GLib.Variant.new('u', 42); expect(() => GIMarshallingTests.gvalue_in_with_type(variant, GLib.Variant)) .not.toThrow(); expect(() => GIMarshallingTests.gvalue_in_with_type(variant, GObject.TYPE_VARIANT)) .not.toThrow(); }); it('can have its type inferred as a union type', function () { let union = GIMarshallingTests.union_returnv(); expect(() => GIMarshallingTests.gvalue_in_with_type(union, GIMarshallingTests.Union)) .not.toThrow(); }); it('can have its type inferred as a GParamSpec', function () { let paramSpec = GObject.ParamSpec.string('my-param', '', '', GObject.ParamFlags.READABLE, ''); expect(() => GIMarshallingTests.gvalue_in_with_type(paramSpec, GObject.TYPE_PARAM)) .not.toThrow(); }); it('can deal with a GValue packed in a GValue', function () { const innerValue = new GObject.Value(); innerValue.init(Number); innerValue.set_double(42); expect(() => GIMarshallingTests.gvalue_in_with_type(innerValue, Number)) .not.toThrow(); const value = new GObject.Value(); value.init(GObject.Value); value.set_boxed(innerValue); expect(() => GIMarshallingTests.gvalue_in_with_type(value, GObject.Value)) .not.toThrow(); }); // See testCairo.js for a test of GIMarshallingTests.gvalue_in_with_type() // on Cairo foreign structs, since it will be skipped if compiling without // Cairo support. }); describe('Callback', function () { describe('GClosure', function () { testInParameter('gclosure', () => 42); xit('marshals a GClosure as a return value', function () { // Currently a GObject.Closure instance is returned, upon which it's // not possible to call invoke() because that method takes a bare // pointer as an argument. expect(GIMarshallingTests.gclosure_return()()).toEqual(42); }).pend('https://gitlab.gnome.org/GNOME/gjs/issues/80'); }); it('marshals a return value', function () { expect(GIMarshallingTests.callback_return_value_only(() => 42)) .toEqual(42); }); it('marshals one out parameter', function () { expect(GIMarshallingTests.callback_one_out_parameter(() => 43)) .toEqual(43); }); it('marshals multiple out parameters', function () { expect(GIMarshallingTests.callback_multiple_out_parameters(() => [44, 45])) .toEqual([44, 45]); }); it('marshals a return value and one out parameter', function () { expect(GIMarshallingTests.callback_return_value_and_one_out_parameter(() => [46, 47])) .toEqual([46, 47]); }); it('marshals a return value and multiple out parameters', function () { expect(GIMarshallingTests.callback_return_value_and_multiple_out_parameters(() => [48, 49, 50])) .toEqual([48, 49, 50]); }); xit('marshals an array out parameter', function () { expect(GIMarshallingTests.callback_array_out_parameter(() => [50, 51])) .toEqual([50, 51]); }).pend('Function not added to gobject-introspection test suite yet'); it('marshals a callback parameter that can be called from C', function () { expect(GIMarshallingTests.callback_owned_boxed(box => { expect(box.long_).toEqual(1); box.long_ = 52; })).toEqual(52); }); }); describe('Raw pointers', function () { it('gets an allocated return value', function () { expect(GIMarshallingTests.pointer_in_return(null)).toBeFalsy(); }); it('can be roundtripped at least if the pointer is null', function () { expect(GIMarshallingTests.pointer_in_return(null)).toBeNull(); }); }); describe('Registered enum type', function () { testSimpleMarshalling('genum', GIMarshallingTests.GEnum.VALUE3, GIMarshallingTests.GEnum.VALUE1, 0, { returnv: { funcName: 'genum_returnv', }, }); }); describe('Bare enum type', function () { testSimpleMarshalling('enum', GIMarshallingTests.Enum.VALUE3, GIMarshallingTests.Enum.VALUE1, 0, { returnv: { funcName: 'enum_returnv', }, }); }); describe('Registered flags type', function () { testSimpleMarshalling('flags', GIMarshallingTests.Flags.VALUE2, GIMarshallingTests.Flags.VALUE1, 0, { returnv: { funcName: 'flags_returnv', }, }); it('accepts zero', function () { expect(() => GIMarshallingTests.flags_in_zero(0)).not.toThrow(); }); }); describe('Bare flags type', function () { testSimpleMarshalling('no_type_flags', GIMarshallingTests.NoTypeFlags.VALUE2, GIMarshallingTests.NoTypeFlags.VALUE1, 0, { returnv: { funcName: 'no_type_flags_returnv', }, }); it('accepts zero', function () { expect(() => GIMarshallingTests.no_type_flags_in_zero(0)).not.toThrow(); }); }); describe('Simple struct', function () { it('marshals as a return value', function () { expect(GIMarshallingTests.simple_struct_returnv()).toEqual(jasmine.objectContaining({ long_: 6, int8: 7, })); }); it('marshals as the this-argument of a method', function () { const struct = new GIMarshallingTests.SimpleStruct({ long_: 6, int8: 7, }); expect(() => struct.inv()).not.toThrow(); // was this supposed to be static? expect(() => struct.method()).not.toThrow(); }); }); describe('Pointer struct', function () { it('marshals as a return value', function () { expect(GIMarshallingTests.pointer_struct_returnv()).toEqual(jasmine.objectContaining({ long_: 42, })); }); it('marshals as the this-argument of a method', function () { const struct = new GIMarshallingTests.PointerStruct({ long_: 42, }); expect(() => struct.inv()).not.toThrow(); }); }); describe('Boxed struct', function () { it('marshals as a return value', function () { expect(GIMarshallingTests.boxed_struct_returnv()).toEqual(jasmine.objectContaining({ long_: 42, string_: 'hello', g_strv: ['0', '1', '2'], })); }); it('marshals as the this-argument of a method', function () { const struct = new GIMarshallingTests.BoxedStruct({ long_: 42, }); expect(() => struct.inv()).not.toThrow(); }); it('marshals as an out parameter', function () { expect(GIMarshallingTests.boxed_struct_out()).toEqual(jasmine.objectContaining({ long_: 42, })); }); it('marshals as an inout parameter', function () { const struct = new GIMarshallingTests.BoxedStruct({ long_: 42, }); expect(GIMarshallingTests.boxed_struct_inout(struct)).toEqual(jasmine.objectContaining({ long_: 0, })); }); }); describe('Union', function () { let union; beforeEach(function () { union = GIMarshallingTests.union_returnv(); }); xit('marshals as a return value', function () { expect(union.long_).toEqual(42); }).pend('https://gitlab.gnome.org/GNOME/gjs/issues/273'); it('marshals as the this-argument of a method', function () { expect(() => union.inv()).not.toThrow(); // was this supposed to be static? expect(() => union.method()).not.toThrow(); }); }); describe('GObject', function () { it('has a static method that can be called', function () { expect(() => GIMarshallingTests.Object.static_method()).not.toThrow(); }); it('has a method that can be called', function () { const o = new GIMarshallingTests.Object({int: 42}); expect(() => o.method()).not.toThrow(); }); it('has an overridden method that can be called', function () { const o = new GIMarshallingTests.Object({int: 0}); expect(() => o.overridden_method()).not.toThrow(); }); it('can be created from a static constructor', function () { const o = GIMarshallingTests.Object.new(42); expect(o.int).toEqual(42); }); it('can have a static constructor that fails', function () { expect(() => GIMarshallingTests.Object.new_fail(42)).toThrow(); }); describe('method', function () { let o; beforeEach(function () { o = new GIMarshallingTests.Object(); }); it('marshals an int array as an in parameter', function () { expect(() => o.method_array_in([-1, 0, 1, 2])).not.toThrow(); }); it('marshals an int array as an out parameter', function () { expect(o.method_array_out()).toEqual([-1, 0, 1, 2]); }); it('marshals an int array as an inout parameter', function () { expect(o.method_array_inout([-1, 0, 1, 2])).toEqual([-2, -1, 0, 1, 2]); }); it('marshals an int array as a return value', function () { expect(o.method_array_return()).toEqual([-1, 0, 1, 2]); }); it('with default implementation can be called', function () { o = new GIMarshallingTests.Object({int: 42}); o.method_with_default_implementation(43); expect(o.int).toEqual(43); }); }); ['none', 'full'].forEach(transfer => { ['return', 'out'].forEach(mode => { it(`marshals as a ${mode} parameter with transfer ${transfer}`, function () { expect(GIMarshallingTests.Object[`${transfer}_${mode}`]().int).toEqual(0); }); }); it(`marshals as an inout parameter with transfer ${transfer}`, function () { const o = new GIMarshallingTests.Object({int: 42}); expect(GIMarshallingTests.Object[`${transfer}_inout`](o).int).toEqual(0); }); }); it('marshals as a this value with transfer none', function () { const o = new GIMarshallingTests.Object({int: 42}); expect(() => o.none_in()).not.toThrow(); }); }); let VFuncTester = GObject.registerClass(class VFuncTester extends GIMarshallingTests.Object { vfunc_method_int8_in(i) { this.int = i; } vfunc_method_int8_out() { return 40; } vfunc_method_int8_arg_and_out_caller(i) { return i + 3; } vfunc_method_int8_arg_and_out_callee(i) { return i + 4; } vfunc_method_str_arg_out_ret(s) { return [`Called with ${s}`, 41]; } vfunc_method_with_default_implementation(i) { this.int = i + 2; } // vfunc_vfunc_with_callback(callback) { // this.int = callback(41); // } vfunc_vfunc_return_value_only() { return 42; } vfunc_vfunc_one_out_parameter() { return 43; } vfunc_vfunc_multiple_out_parameters() { return [44, 45]; } vfunc_vfunc_return_value_and_one_out_parameter() { return [46, 47]; } vfunc_vfunc_return_value_and_multiple_out_parameters() { return [48, 49, 50]; } vfunc_vfunc_array_out_parameter() { return [50, 51]; } vfunc_vfunc_caller_allocated_out_parameter() { return 52; } vfunc_vfunc_meth_with_err(x) { switch (x) { case -1: return true; case 0: undefined.throwTypeError(); break; case 1: void referenceError; // eslint-disable-line no-undef break; case 2: throw new Gio.IOErrorEnum({ code: Gio.IOErrorEnum.FAILED, message: 'I FAILED, but the test passed!', }); case 3: throw new GLib.SpawnError({ code: GLib.SpawnError.TOO_BIG, message: 'This test is Too Big to Fail', }); case 4: throw null; // eslint-disable-line no-throw-literal case 5: throw undefined; // eslint-disable-line no-throw-literal case 6: throw 42; // eslint-disable-line no-throw-literal case 7: throw true; // eslint-disable-line no-throw-literal case 8: throw 'a string'; // eslint-disable-line no-throw-literal case 9: throw 42n; // eslint-disable-line no-throw-literal case 10: throw Symbol('a symbol'); case 11: throw {plain: 'object'}; // eslint-disable-line no-throw-literal case 12: // eslint-disable-next-line no-throw-literal throw {name: 'TypeError', message: 'an error message'}; case 13: // eslint-disable-next-line no-throw-literal throw {name: 1, message: 'an error message'}; case 14: // eslint-disable-next-line no-throw-literal throw {name: 'TypeError', message: false}; } } vfunc_vfunc_return_enum() { return GIMarshallingTests.Enum.VALUE2; } vfunc_vfunc_out_enum() { return GIMarshallingTests.Enum.VALUE3; } vfunc_vfunc_return_flags() { return GIMarshallingTests.Flags.VALUE2; } vfunc_vfunc_out_flags() { return GIMarshallingTests.Flags.VALUE3; } vfunc_vfunc_return_object_transfer_none() { if (!this._returnObject) this._returnObject = new GIMarshallingTests.Object({int: 53}); return this._returnObject; } vfunc_vfunc_return_object_transfer_full() { return new GIMarshallingTests.Object({int: 54}); } vfunc_vfunc_out_object_transfer_none() { if (!this._outObject) this._outObject = new GIMarshallingTests.Object({int: 55}); return this._outObject; } vfunc_vfunc_out_object_transfer_full() { return new GIMarshallingTests.Object({int: 56}); } vfunc_vfunc_in_object_transfer_none(object) { void object; } vfunc_vfunc_in_object_transfer_full(object) { this._inObject = object; } }); try { VFuncTester = GObject.registerClass(class VFuncTesterInOut extends VFuncTester { vfunc_vfunc_one_inout_parameter(input) { return input * 5; } vfunc_vfunc_multiple_inout_parameters(inputA, inputB) { return [inputA * 5, inputB * -1]; } vfunc_vfunc_return_value_and_one_inout_parameter(input) { return [49, input * 5]; } vfunc_vfunc_return_value_and_multiple_inout_parameters(inputA, inputB) { return [49, inputA * 5, inputB * -1]; } }); } catch {} describe('Virtual function', function () { let tester; beforeEach(function () { tester = new VFuncTester(); }); it('marshals an in argument', function () { tester.method_int8_in(39); expect(tester.int).toEqual(39); }); it('marshals an out argument', function () { expect(tester.method_int8_out()).toEqual(40); }); it('marshals a POD out argument', function () { expect(tester.method_int8_arg_and_out_caller(39)).toEqual(42); }); it('marshals a callee-allocated pointer out argument', function () { expect(tester.method_int8_arg_and_out_callee(38)).toEqual(42); }); it('marshals a string out argument and return value', function () { expect(tester.method_str_arg_out_ret('a string')).toEqual(['Called with a string', 41]); expect(tester.method_str_arg_out_ret('a 2nd string')).toEqual(['Called with a 2nd string', 41]); }); it('can override a default implementation in JS', function () { tester.method_with_default_implementation(40); expect(tester.int).toEqual(42); }); xit('marshals a callback', function () { tester.call_vfunc_with_callback(); expect(tester.int).toEqual(41); }).pend('callback parameters to vfuncs not supported'); it('marshals a return value', function () { expect(tester.vfunc_return_value_only()).toEqual(42); }); it('marshals one out parameter', function () { expect(tester.vfunc_one_out_parameter()).toEqual(43); }); it('marshals multiple out parameters', function () { expect(tester.vfunc_multiple_out_parameters()).toEqual([44, 45]); }); it('marshals a return value and one out parameter', function () { expect(tester.vfunc_return_value_and_one_out_parameter()) .toEqual([46, 47]); }); it('marshals a return value and multiple out parameters', function () { expect(tester.vfunc_return_value_and_multiple_out_parameters()) .toEqual([48, 49, 50]); }); it('marshals one inout parameter', function () { expect(tester.vfunc_one_inout_parameter(10)).toEqual(50); }); it('marshals multiple inout parameters', function () { expect(tester.vfunc_multiple_inout_parameters(10, 5)).toEqual([50, -5]); }); it('marshals a return value and one inout parameter', function () { expect(tester.vfunc_return_value_and_one_inout_parameter(10)) .toEqual([49, 50]); }); it('marshals a return value and multiple inout parameters', function () { expect(tester.vfunc_return_value_and_multiple_inout_parameters(10, -51)) .toEqual([49, 50, 51]); }); it('marshals an array out parameter', function () { expect(tester.vfunc_array_out_parameter()).toEqual([50, 51]); }); it('marshals a caller-allocated GValue out parameter', function () { expect(tester.vfunc_caller_allocated_out_parameter()).toEqual(52); }); it('marshals an error out parameter when no error', function () { expect(tester.vfunc_meth_with_error(-1)).toBeTruthy(); }); it('marshals an error out parameter with a JavaScript exception', function () { expect(() => tester.vfunc_meth_with_error(0)).toThrowError(TypeError); expect(() => tester.vfunc_meth_with_error(1)).toThrowError(ReferenceError); }); it('marshals an error out parameter with a GError exception', function () { try { tester.vfunc_meth_with_error(2); fail('Exception should be thrown'); } catch (e) { expect(e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED)).toBeTruthy(); expect(e.message).toEqual('I FAILED, but the test passed!'); } try { tester.vfunc_meth_with_error(3); fail('Exception should be thrown'); } catch (e) { expect(e.matches(GLib.SpawnError, GLib.SpawnError.TOO_BIG)).toBeTruthy(); expect(e.message).toEqual('This test is Too Big to Fail'); } }); it('marshals an error out parameter with a primitive value', function () { expect(() => tester.vfunc_meth_with_error(4)).toThrowError(/null/); expect(() => tester.vfunc_meth_with_error(5)).toThrowError(/undefined/); expect(() => tester.vfunc_meth_with_error(6)).toThrowError(/42/); expect(() => tester.vfunc_meth_with_error(7)).toThrowError(/true/); expect(() => tester.vfunc_meth_with_error(8)).toThrowError(/"a string"/); expect(() => tester.vfunc_meth_with_error(9)).toThrowError(/42n/); expect(() => tester.vfunc_meth_with_error(10)).toThrowError(/Symbol\("a symbol"\)/); }); it('marshals an error out parameter with a plain object', function () { expect(() => tester.vfunc_meth_with_error(11)).toThrowError(/Object/); expect(() => tester.vfunc_meth_with_error(12)).toThrowError(TypeError, /an error message/); expect(() => tester.vfunc_meth_with_error(13)).toThrowError(/Object/); expect(() => tester.vfunc_meth_with_error(14)).toThrowError(Error, /Object/); }); it('marshals an enum return value', function () { expect(tester.vfunc_return_enum()).toEqual(GIMarshallingTests.Enum.VALUE2); }); it('marshals an enum out parameter', function () { expect(tester.vfunc_out_enum()).toEqual(GIMarshallingTests.Enum.VALUE3); }); it('marshals a flags return value', function () { expect(tester.vfunc_return_flags()).toEqual(GIMarshallingTests.Flags.VALUE2); }); it('marshals a flags out parameter', function () { expect(tester.vfunc_out_flags()).toEqual(GIMarshallingTests.Flags.VALUE3); }); // These tests check what the refcount is of the returned objects; see // comments in gimarshallingtests.c. // Objects that are exposed in JS always have at least one reference (the // toggle reference.) JS never owns more than one reference. There may be // other references owned on the C side. // In any case the refs should not be floating. We never have floating refs // in JS. function testVfuncRefcount(mode, transfer, expectedRefcount, options = {}, ...args) { it(`marshals an object ${mode} parameter with transfer ${transfer}`, function () { if (options.skip) pending(options.skip); const [refcount, floating] = tester[`get_ref_info_for_vfunc_${mode}_object_transfer_${transfer}`](...args); expect(floating).toBeFalsy(); expect(refcount).toEqual(expectedRefcount); }); } // Running in extra-gc mode can drop the JS reference, since it is not // actually stored anywhere reachable from user code. However, we cannot // force the extra GC under normal conditions because it occurs in the // middle of C++ code. const skipExtraGC = {}; const zeal = GLib.getenv('JS_GC_ZEAL'); if (zeal && zeal.startsWith('2,')) skipExtraGC.skip = 'Skip during extra-gc.'; // 1 reference = the object is owned only by JS. // 2 references = the object is owned by JS and the vfunc caller. testVfuncRefcount('return', 'none', 1); testVfuncRefcount('return', 'full', 2, skipExtraGC); testVfuncRefcount('out', 'none', 1); testVfuncRefcount('out', 'full', 2, skipExtraGC); testVfuncRefcount('in', 'none', 2, skipExtraGC, GIMarshallingTests.Object); testVfuncRefcount('in', 'full', 1, { skip: 'https://gitlab.gnome.org/GNOME/gjs/issues/275', }, GIMarshallingTests.Object); }); const WrongVFuncTester = GObject.registerClass(class WrongVFuncTester extends GIMarshallingTests.Object { vfunc_vfunc_return_value_only() { } vfunc_vfunc_one_out_parameter() { } vfunc_vfunc_multiple_out_parameters() { } vfunc_vfunc_return_value_and_one_out_parameter() { } vfunc_vfunc_return_value_and_multiple_out_parameters() { } vfunc_vfunc_array_out_parameter() { } vfunc_vfunc_caller_allocated_out_parameter() { } vfunc_vfunc_return_enum() { } vfunc_vfunc_out_enum() { } vfunc_vfunc_return_flags() { } vfunc_vfunc_out_flags() { } vfunc_vfunc_return_object_transfer_none() { } vfunc_vfunc_return_object_transfer_full() { } vfunc_vfunc_out_object_transfer_none() { } vfunc_vfunc_out_object_transfer_full() { } vfunc_vfunc_in_object_transfer_none() { } }); describe('Wrong virtual functions', function () { let tester; beforeEach(function () { tester = new WrongVFuncTester(); }); it('marshals a return value', function () { expect(tester.vfunc_return_value_only()).toBeUndefined(); }).pend('https://gitlab.gnome.org/GNOME/gjs/issues/311'); it('marshals one out parameter', function () { expect(tester.vfunc_one_out_parameter()).toBeUndefined(); }).pend('https://gitlab.gnome.org/GNOME/gjs/issues/311'); it('marshals multiple out parameters', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'JS ERROR: Error: *vfunc_vfunc_multiple_out_parameters*Array*'); expect(tester.vfunc_multiple_out_parameters()).toEqual([0, 0]); GLib.test_assert_expected_messages_internal('Cjs', 'testGIMarshalling.js', 0, 'testVFuncReturnWrongValue'); }); it('marshals a return value and one out parameter', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'JS ERROR: Error: *vfunc_return_value_and_one_out_parameter*Array*'); expect(tester.vfunc_return_value_and_one_out_parameter()).toEqual([0, 0]); GLib.test_assert_expected_messages_internal('Cjs', 'testGIMarshalling.js', 0, 'testVFuncReturnWrongValue'); }); it('marshals a return value and multiple out parameters', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'JS ERROR: Error: *vfunc_return_value_and_multiple_out_parameters*Array*'); expect(tester.vfunc_return_value_and_multiple_out_parameters()).toEqual([0, 0, 0]); GLib.test_assert_expected_messages_internal('Cjs', 'testGIMarshalling.js', 0, 'testVFuncReturnWrongValue'); }); it('marshals an array out parameter', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'JS ERROR: Error: Expected type gfloat for Argument*undefined*'); expect(tester.vfunc_array_out_parameter()).toEqual(null); GLib.test_assert_expected_messages_internal('Cjs', 'testGIMarshalling.js', 0, 'testVFuncReturnWrongValue'); }); it('marshals an enum return value', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'JS ERROR: Error: Expected type enum for Return*undefined*'); expect(tester.vfunc_return_enum()).toEqual(0); GLib.test_assert_expected_messages_internal('Cjs', 'testGIMarshalling.js', 0, 'testVFuncReturnWrongValue'); }); it('marshals an enum out parameter', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'JS ERROR: Error: Expected type enum for Argument*undefined*'); expect(tester.vfunc_out_enum()).toEqual(0); GLib.test_assert_expected_messages_internal('Cjs', 'testGIMarshalling.js', 0, 'testVFuncReturnWrongValue'); }); it('marshals a flags return value', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'JS ERROR: Error: Expected type flags for Return*undefined*'); expect(tester.vfunc_return_flags()).toEqual(0); GLib.test_assert_expected_messages_internal('Cjs', 'testGIMarshalling.js', 0, 'testVFuncReturnWrongValue'); }); it('marshals a flags out parameter', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'JS ERROR: Error: Expected type flags for Argument*undefined*'); expect(tester.vfunc_out_flags()).toEqual(0); GLib.test_assert_expected_messages_internal('Cjs', 'testGIMarshalling.js', 0, 'testVFuncReturnWrongValue'); }); }); describe('Inherited GObject', function () { ['SubObject', 'SubSubObject'].forEach(klass => { describe(klass, function () { it('has a parent method that can be called', function () { const o = new GIMarshallingTests[klass]({int: 42}); expect(() => o.method()).not.toThrow(); }); it('has a method that can be called', function () { const o = new GIMarshallingTests[klass]({int: 0}); expect(() => o.sub_method()).not.toThrow(); }); it('has an overridden method that can be called', function () { const o = new GIMarshallingTests[klass]({int: 0}); expect(() => o.overwritten_method()).not.toThrow(); }); it('has a method with default implementation that can be called', function () { const o = new GIMarshallingTests[klass]({int: 42}); o.method_with_default_implementation(43); expect(o.int).toEqual(43); }); }); }); }); describe('Interface', function () { it('can be returned', function () { let ifaceImpl = new GIMarshallingTests.InterfaceImpl(); let itself = ifaceImpl.get_as_interface(); expect(ifaceImpl).toEqual(itself); }); it('can call an interface vfunc in C', function () { let ifaceImpl = new GIMarshallingTests.InterfaceImpl(); expect(() => ifaceImpl.test_int8_in(42)).not.toThrow(); expect(() => GIMarshallingTests.test_interface_test_int8_in(ifaceImpl, 42)) .not.toThrow(); }); it('can implement a C interface', function () { const I2Impl = GObject.registerClass({ Implements: [GIMarshallingTests.Interface2], }, class I2Impl extends GObject.Object {}); expect(() => new I2Impl()).not.toThrow(); }); it('can implement a C interface with a vfunc', function () { const I3Impl = GObject.registerClass({ Implements: [GIMarshallingTests.Interface3], }, class I3Impl extends GObject.Object { vfunc_test_variant_array_in(variantArray) { this.stuff = variantArray.map(v => { const bit64 = this.bigInt && (v.is_of_type(new GLib.VariantType('t')) || v.is_of_type(new GLib.VariantType('x'))); return warn64(bit64, () => v.deepUnpack()); }); } }); const i3 = new I3Impl(); i3.test_variant_array_in([ new GLib.Variant('b', true), new GLib.Variant('s', 'hello'), new GLib.Variant('i', 42), new GLib.Variant('t', 43), new GLib.Variant('x', 44), ]); expect(i3.stuff).toEqual([true, 'hello', 42, 43, 44]); i3.bigInt = true; i3.test_variant_array_in([ new GLib.Variant('x', BigIntLimits.int64.min), new GLib.Variant('x', BigIntLimits.int64.max), new GLib.Variant('t', BigIntLimits.int64.umax), ]); expect(i3.stuff).toEqual([ Limits.int64.min, Limits.int64.max, Limits.int64.umax, ]); }); }); describe('Configurations of return values', function () { it('can handle two out parameters', function () { expect(GIMarshallingTests.int_out_out()).toEqual([6, 7]); }); it('can handle three in and three out parameters', function () { expect(GIMarshallingTests.int_three_in_three_out(1, 2, 3)).toEqual([1, 2, 3]); }); it('can handle a return value and an out parameter', function () { expect(GIMarshallingTests.int_return_out()).toEqual([6, 7]); }); it('can handle four in parameters, two of which are nullable', function () { expect(() => GIMarshallingTests.int_two_in_utf8_two_in_with_allow_none(1, 2, '3', '4')) .not.toThrow(); expect(() => GIMarshallingTests.int_two_in_utf8_two_in_with_allow_none(1, 2, '3', null)) .not.toThrow(); expect(() => GIMarshallingTests.int_two_in_utf8_two_in_with_allow_none(1, 2, null, '4')) .not.toThrow(); expect(() => GIMarshallingTests.int_two_in_utf8_two_in_with_allow_none(1, 2, null, null)) .not.toThrow(); }); it('can handle three in parameters, one of which is nullable and one not', function () { expect(() => GIMarshallingTests.int_one_in_utf8_two_in_one_allows_none(1, '2', '3')) .not.toThrow(); expect(() => GIMarshallingTests.int_one_in_utf8_two_in_one_allows_none(1, null, '3')) .not.toThrow(); expect(() => GIMarshallingTests.int_one_in_utf8_two_in_one_allows_none(1, '2', null)) .toThrow(); }); it('can handle an array in parameter and two nullable in parameters', function () { expect(() => GIMarshallingTests.array_in_utf8_two_in([-1, 0, 1, 2], '1', '2')) .not.toThrow(); expect(() => GIMarshallingTests.array_in_utf8_two_in([-1, 0, 1, 2], '1', null)) .not.toThrow(); expect(() => GIMarshallingTests.array_in_utf8_two_in([-1, 0, 1, 2], null, '2')) .not.toThrow(); expect(() => GIMarshallingTests.array_in_utf8_two_in([-1, 0, 1, 2], null, null)) .not.toThrow(); }); it('can handle an array in parameter and two nullable in parameters, mixed with the array length', function () { expect(() => GIMarshallingTests.array_in_utf8_two_in_out_of_order('1', [-1, 0, 1, 2], '2')) .not.toThrow(); expect(() => GIMarshallingTests.array_in_utf8_two_in_out_of_order('1', [-1, 0, 1, 2], null)) .not.toThrow(); expect(() => GIMarshallingTests.array_in_utf8_two_in_out_of_order(null, [-1, 0, 1, 2], '2')) .not.toThrow(); expect(() => GIMarshallingTests.array_in_utf8_two_in_out_of_order(null, [-1, 0, 1, 2], null)) .not.toThrow(); }); }); describe('GError', function () { it('marshals a GError** signature as an exception', function () { expect(() => GIMarshallingTests.gerror()).toThrow(); }); it('marshals a GError** at the end of the signature as an exception', function () { expect(() => GIMarshallingTests.gerror_array_in([-1, 0, 1, 2])).toThrowMatching(e => e.matches(GLib.quark_from_static_string(GIMarshallingTests.CONSTANT_GERROR_DOMAIN), GIMarshallingTests.CONSTANT_GERROR_CODE) && e.message === GIMarshallingTests.CONSTANT_GERROR_MESSAGE); }); it('marshals a GError** elsewhere in the signature as an out parameter', function () { expect(GIMarshallingTests.gerror_out()).toEqual([ jasmine.any(GLib.Error), 'we got an error, life is shit', ]); }); it('marshals a GError** elsewhere in the signature as an out parameter with transfer none', function () { expect(GIMarshallingTests.gerror_out_transfer_none()).toEqual([ jasmine.any(GLib.Error), 'we got an error, life is shit', ]); }); it('marshals GError as a return value', function () { expect(GIMarshallingTests.gerror_return()).toEqual(jasmine.any(GLib.Error)); }); }); describe('Overrides', function () { it('can add constants', function () { expect(GIMarshallingTests.OVERRIDES_CONSTANT).toEqual(7); }); it('can override a struct method', function () { const struct = new GIMarshallingTests.OverridesStruct(); expect(struct.method()).toEqual(6); }); it('returns the overridden struct', function () { const obj = GIMarshallingTests.OverridesStruct.returnv(); expect(obj).toBeInstanceOf(GIMarshallingTests.OverridesStruct); }); it('can override an object constructor', function () { const obj = new GIMarshallingTests.OverridesObject(42); expect(obj.num).toEqual(42); }); it('can override an object method', function () { const obj = new GIMarshallingTests.OverridesObject(); expect(obj.method()).toEqual(6); }); it('returns the overridden object', function () { const obj = GIMarshallingTests.OverridesObject.returnv(); expect(obj).toBeInstanceOf(GIMarshallingTests.OverridesObject); }); }); describe('Filename', function () { testReturnValue('filename_list', []); }); describe('GObject.ParamSpec', function () { const pspec = GObject.ParamSpec.boolean('mybool', 'My Bool', 'My boolean property', GObject.ParamFlags.READWRITE, true); testInParameter('param_spec', pspec, { funcName: 'param_spec_in_bool', }); const expectedProps = { name: 'test-param', nick: 'test', blurb: 'This is a test', default_value: '42', flags: GObject.ParamFlags.READABLE, value_type: GObject.TYPE_STRING, }; testReturnValue('param_spec', jasmine.objectContaining(expectedProps)); testOutParameter('param_spec', jasmine.objectContaining(expectedProps)); }); describe('GObject properties', function () { let obj; beforeEach(function () { obj = new GIMarshallingTests.PropertiesObject(); }); function testPropertyGetSet(type, value1, value2, skip = false) { const snakeCase = `some_${type}`; const paramCase = snakeCase.replaceAll('_', '-'); const camelCase = snakeCase.replace(/(_\w)/g, match => match.toUpperCase().replace('_', '')); [snakeCase, paramCase, camelCase].forEach(propertyName => { it(`gets and sets a ${type} property as ${propertyName}`, function () { if (skip) pending(skip); const handler = jasmine.createSpy(`handle-${paramCase}`); const id = obj.connect(`notify::${paramCase}`, handler); obj[propertyName] = value1; expect(obj[propertyName]).toEqual(value1); expect(handler).toHaveBeenCalledTimes(1); obj[propertyName] = value2; expect(obj[propertyName]).toEqual(value2); expect(handler).toHaveBeenCalledTimes(2); obj.disconnect(id); }); }); } function testPropertyGetSetBigInt(type, value1, value2) { const snakeCase = `some_${type}`; const paramCase = snakeCase.replaceAll('_', '-'); it(`gets and sets a ${type} property with a bigint`, function () { const handler = jasmine.createSpy(`handle-${paramCase}`); const id = obj.connect(`notify::${paramCase}`, handler); obj[snakeCase] = value1; expect(handler).toHaveBeenCalledTimes(1); expect(obj[snakeCase]).toEqual(Number(value1)); obj[snakeCase] = value2; expect(handler).toHaveBeenCalledTimes(2); expect(obj[snakeCase]).toEqual(Number(value2)); obj.disconnect(id); }); } testPropertyGetSet('boolean', true, false); testPropertyGetSet('char', 42, 64); testPropertyGetSet('uchar', 42, 64); testPropertyGetSet('int', 42, 64); testPropertyGetSet('uint', 42, 64); testPropertyGetSet('long', 42, 64); testPropertyGetSet('ulong', 42, 64); testPropertyGetSet('int64', 42, 64); testPropertyGetSet('int64', Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER); testPropertyGetSetBigInt('int64', BigIntLimits.int64.min, BigIntLimits.int64.max); testPropertyGetSet('uint64', 42, 64); testPropertyGetSetBigInt('uint64', BigIntLimits.int64.max, BigIntLimits.int64.umax); testPropertyGetSet('string', 'Cjs', 'is cool!'); testPropertyGetSet('string', 'and supports', null); it('get and sets out-of-range values throws', function () { expect(() => { obj.some_int64 = Limits.int64.max; }).toThrowError(/out of range/); expect(() => { obj.some_int64 = BigIntLimits.int64.max + 1n; }).toThrowError(/out of range/); expect(() => { obj.some_int64 = BigIntLimits.int64.min - 1n; }).toThrowError(/out of range/); expect(() => { obj.some_int64 = BigIntLimits.int64.umax; }).toThrowError(/out of range/); expect(() => { obj.some_int64 = -BigIntLimits.int64.umax; }).toThrowError(/out of range/); expect(() => { obj.some_uint64 = Limits.int64.min; }).toThrowError(/out of range/); expect(() => { obj.some_uint64 = BigIntLimits.int64.umax + 100n; }).toThrowError(/out of range/); }); it('gets and sets a float property', function () { const handler = jasmine.createSpy('handle-some-float'); const id = obj.connect('notify::some-float', handler); obj.some_float = Math.E; expect(handler).toHaveBeenCalledTimes(1); expect(obj.some_float).toBeCloseTo(Math.E); obj.some_float = Math.PI; expect(handler).toHaveBeenCalledTimes(2); expect(obj.some_float).toBeCloseTo(Math.PI); obj.disconnect(id); }); it('gets and sets a double property', function () { const handler = jasmine.createSpy('handle-some-double'); const id = obj.connect('notify::some-double', handler); obj.some_double = Math.E; expect(handler).toHaveBeenCalledTimes(1); expect(obj.some_double).toBeCloseTo(Math.E); obj.some_double = Math.PI; expect(handler).toHaveBeenCalledTimes(2); expect(obj.some_double).toBeCloseTo(Math.PI); obj.disconnect(id); }); testPropertyGetSet('strv', ['0', '1', '2'], []); testPropertyGetSet('boxed_struct', new GIMarshallingTests.BoxedStruct(), new GIMarshallingTests.BoxedStruct({long_: 42})); testPropertyGetSet('boxed_struct', new GIMarshallingTests.BoxedStruct(), null); testPropertyGetSet('boxed_glist', null, null); testPropertyGetSet('gvalue', 42, 'foo'); testPropertyGetSetBigInt('gvalue', BigIntLimits.int64.umax, BigIntLimits.int64.min); testPropertyGetSet('variant', new GLib.Variant('b', true), new GLib.Variant('s', 'hello')); testPropertyGetSet('variant', new GLib.Variant('x', BigIntLimits.int64.min), new GLib.Variant('x', BigIntLimits.int64.max)); testPropertyGetSet('variant', new GLib.Variant('t', BigIntLimits.int64.max), new GLib.Variant('t', BigIntLimits.int64.umax)); testPropertyGetSet('object', new GObject.Object(), new GIMarshallingTests.Object({int: 42})); testPropertyGetSet('object', new GIMarshallingTests.PropertiesObject({ 'some-int': 23, 'some-string': '👾', }), null); testPropertyGetSet('flags', GIMarshallingTests.Flags.VALUE2, GIMarshallingTests.Flags.VALUE1 | GIMarshallingTests.Flags.VALUE2); testPropertyGetSet('enum', GIMarshallingTests.GEnum.VALUE2, GIMarshallingTests.GEnum.VALUE3); testPropertyGetSet('byte_array', Uint8Array.of(1, 2, 3), new TextEncoder().encode('👾')); testPropertyGetSet('byte_array', Uint8Array.of(3, 2, 1), null); it('gets a read-only property', function () { expect(obj.some_readonly).toEqual(42); }); it('throws when setting a read-only property', function () { expect(() => (obj.some_readonly = 35)).toThrow(); }); it('allows to set/get deprecated properties', function () { if (!GObject.Object.find_property.call( GIMarshallingTests.PropertiesObject, 'some-deprecated-int')) pending('https://gitlab.gnome.org/GNOME/gobject-introspection/-/merge_requests/410'); GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, '*GObject property*.some-deprecated-int is deprecated*'); obj.some_deprecated_int = 35; GLib.test_assert_expected_messages_internal('Cjs', 'testGIMarshalling.js', 0, 'testAllowToSetGetDeprecatedProperties'); GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, '*GObject property*.some-deprecated-int is deprecated*'); expect(obj.some_deprecated_int).toBe(35); GLib.test_assert_expected_messages_internal('Cjs', 'testGIMarshalling.js', 0, 'testAllowToSetGetDeprecatedProperties'); }); const JSOverridingProperty = GObject.registerClass( class Overriding extends GIMarshallingTests.PropertiesObject { constructor(params) { super(params); this.intValue = 55; this.stringValue = 'a string'; } set some_int(v) { this.intValue = v; } get someInt() { return this.intValue; } set someString(v) { this.stringValue = v; } get someString() { return this.stringValue; } }); it('can be overridden from JS', function () { const intHandler = jasmine.createSpy('handle-some-int'); const stringHandler = jasmine.createSpy('handle-some-string'); const overriding = new JSOverridingProperty({ 'someInt': 45, 'someString': 'other string', }); const ids = []; ids.push(overriding.connect('notify::some-int', intHandler)); ids.push(overriding.connect('notify::some-string', stringHandler)); expect(overriding['some-int']).toBe(45); expect(overriding.someInt).toBe(55); expect(overriding.some_int).toBeUndefined(); expect(overriding.intValue).toBe(55); expect(overriding.someString).toBe('a string'); expect(overriding.some_string).toBe('other string'); expect(intHandler).not.toHaveBeenCalled(); expect(stringHandler).not.toHaveBeenCalled(); overriding.some_int = 35; expect(overriding['some-int']).toBe(45); expect(overriding.some_int).toBeUndefined(); expect(overriding.someInt).toBe(35); expect(overriding.intValue).toBe(35); expect(intHandler).not.toHaveBeenCalled(); overriding.someInt = 85; expect(overriding['some-int']).toBe(45); expect(overriding.someInt).toBe(35); expect(overriding.some_int).toBeUndefined(); expect(overriding.intValue).toBe(35); expect(intHandler).not.toHaveBeenCalled(); overriding['some-int'] = 123; expect(overriding['some-int']).toBe(123); expect(overriding.someInt).toBe(35); expect(overriding.some_int).toBeUndefined(); expect(overriding.intValue).toBe(35); expect(intHandler).toHaveBeenCalledTimes(1); overriding['some-string'] = '🐧'; expect(overriding['some-string']).toBe('🐧'); expect(overriding.some_string).toBe('🐧'); expect(overriding.someString).toBe('a string'); expect(overriding.stringValue).toBe('a string'); expect(stringHandler).toHaveBeenCalledTimes(1); overriding.some_string = '🍕'; expect(overriding['some-string']).toBe('🍕'); expect(overriding.some_string).toBe('🍕'); expect(overriding.someString).toBe('a string'); expect(overriding.stringValue).toBe('a string'); expect(stringHandler).toHaveBeenCalledTimes(2); overriding.someString = '🍝'; expect(overriding['some-string']).toBe('🍕'); expect(overriding.some_string).toBe('🍕'); expect(overriding.someString).toBe('🍝'); expect(overriding.stringValue).toBe('🍝'); expect(stringHandler).toHaveBeenCalledTimes(2); ids.forEach(id => overriding.disconnect(id)); }); }); describe('GObject signals', function () { let obj; beforeEach(function () { obj = new GIMarshallingTests.SignalsObject(); }); function testSignalEmission(type, transfer, value, skip = false) { it(`checks emission of signal with ${type} argument and transfer ${transfer}`, function () { if (skip) pending(skip); const signalCallback = jasmine.createSpy('signalCallback'); if (transfer !== 'none') type += `-${transfer}`; const signalName = `some_${type}`; const funcName = `emit_${type}`.replaceAll('-', '_'); const signalId = obj.connect(signalName, signalCallback); obj[funcName](); obj.disconnect(signalId); expect(signalCallback).toHaveBeenCalledOnceWith(obj, value); }); } ['none', 'container', 'none'].forEach(transfer => { testSignalEmission('boxed-gptrarray-utf8', transfer, ['0', '1', '2']); testSignalEmission('boxed-gptrarray-boxed-struct', transfer, [ new GIMarshallingTests.BoxedStruct({long_: 42}), new GIMarshallingTests.BoxedStruct({long_: 43}), new GIMarshallingTests.BoxedStruct({long_: 44}), ]); testSignalEmission('hash-table-utf8-int', transfer, { '-1': 1, '0': 0, '1': -1, '2': -2, }); }); ['none', 'full'].forEach(transfer => { let skip = false; if (transfer === 'full') skip = 'https://gitlab.gnome.org/GNOME/gobject-introspection/-/issues/470'; testSignalEmission('boxed-struct', transfer, jasmine.objectContaining({ long_: 99, string_: 'a string', g_strv: ['foo', 'bar', 'baz'], }), skip); }); it('with not-ref-counted boxed types with transfer full are properly handled', function () { // When using JS side only we can handle properly the problems of // https://gitlab.gnome.org/GNOME/gobject-introspection/-/issues/470 const callbackFunc = jasmine.createSpy('callbackFunc'); const signalId = obj.connect('some-boxed-struct-full', callbackFunc); obj.emit('some-boxed-struct-full', new GIMarshallingTests.BoxedStruct({long_: 44})); obj.disconnect(signalId); expect(callbackFunc).toHaveBeenCalledOnceWith(obj, new GIMarshallingTests.BoxedStruct({long_: 44})); }); }); cjs-128.0/installed-tests/js/testAsyncMainloop.js0000664000175000017500000000141514771557763021023 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2022 Evan Welsh import GLib from 'gi://GLib'; import {acquireMainloop} from 'resource:///org/cjs/jsunit/minijasmine.js'; describe('Async mainloop', function () { let loop, quit; beforeEach(function () { loop = new GLib.MainLoop(null, false); quit = jasmine.createSpy('quit').and.callFake(() => { loop.quit(); return GLib.SOURCE_REMOVE; }); }); it('resolves when main loop exits', async function () { const release = acquireMainloop(); GLib.timeout_add(GLib.PRIORITY_DEFAULT, 50, quit); await loop.runAsync(); expect(quit).toHaveBeenCalled(); release(); }); }); cjs-128.0/installed-tests/js/testGLibLogWriter.js0000664000175000017500000000546114771557763020730 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2021 Evan Welsh // eslint-disable-next-line /// import GLib from 'gi://GLib'; import {arrayLikeWithExactContents} from './matchers.js'; function encodedString(str) { const encoder = new TextEncoder(); const encoded = encoder.encode(str); return arrayLikeWithExactContents(encoded); } describe('GLib Structured logging handler', function () { /** @type {jasmine.Spy<(_level: any, _fields: any) => any>} */ let writer_func; beforeAll(function () { writer_func = jasmine.createSpy( 'Log test writer func', function (_level, _fields) { return GLib.LogWriterOutput.HANDLED; } ); writer_func.and.callThrough(); GLib.log_set_writer_func(writer_func); }); beforeEach(function () { writer_func.calls.reset(); }); it('writes a message', function () { GLib.log_structured('Gjs-Console', GLib.LogLevelFlags.LEVEL_MESSAGE, { MESSAGE: 'a message', }); expect(writer_func).toHaveBeenCalledWith( GLib.LogLevelFlags.LEVEL_MESSAGE, jasmine.objectContaining({MESSAGE: encodedString('a message')}) ); }); it('writes a warning', function () { GLib.log_structured('Gjs-Console', GLib.LogLevelFlags.LEVEL_WARNING, { MESSAGE: 'a warning', }); expect(writer_func).toHaveBeenCalledWith( GLib.LogLevelFlags.LEVEL_WARNING, jasmine.objectContaining({MESSAGE: encodedString('a warning')}) ); }); it('preserves a custom string field', function () { GLib.log_structured('Gjs-Console', GLib.LogLevelFlags.LEVEL_MESSAGE, { MESSAGE: 'with a custom field', GJS_CUSTOM_FIELD: 'a custom value', }); expect(writer_func).toHaveBeenCalledWith( GLib.LogLevelFlags.LEVEL_MESSAGE, jasmine.objectContaining({ MESSAGE: encodedString('with a custom field'), GJS_CUSTOM_FIELD: encodedString('a custom value'), }) ); }); it('preserves a custom byte array field', function () { GLib.log_structured('Gjs-Console', GLib.LogLevelFlags.LEVEL_MESSAGE, { MESSAGE: 'with a custom field', GJS_CUSTOM_FIELD: new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7]), }); expect(writer_func).toHaveBeenCalledWith( GLib.LogLevelFlags.LEVEL_MESSAGE, jasmine.objectContaining({ MESSAGE: encodedString('with a custom field'), GJS_CUSTOM_FIELD: arrayLikeWithExactContents([ 0, 1, 2, 3, 4, 5, 6, 7, ]), }) ); }); }); cjs-128.0/installed-tests/js/testGtk4.js0000664000175000017500000003106214771557763017061 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2013 Giovanni Campagna imports.gi.versions.Gdk = '4.0'; imports.gi.versions.Gtk = '4.0'; const {Gdk, Gio, GObject, Gtk, GLib, CjsTestTools} = imports.gi; const System = imports.system; // Set up log writer for tests to override const writerFunc = jasmine.createSpy('log writer', () => GLib.LogWriterOutput.UNHANDLED); writerFunc.and.callThrough(); GLib.log_set_writer_func(writerFunc); // This is ugly here, but usually it would be in a resource function createTemplate(className) { return ` `; } const MyComplexGtkSubclass = GObject.registerClass({ Template: new TextEncoder().encode(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; } testChildrenExist() { 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/cjs/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 MyComplexGtkSubclassFromString = GObject.registerClass({ Template: createTemplate('Gjs_MyComplexGtkSubclassFromString'), Children: ['label-child', 'label-child2'], InternalChildren: ['internal-label-child'], }, class MyComplexGtkSubclassFromString 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 {}); const CustomActionWidget = GObject.registerClass( class CustomActionWidget extends Gtk.Widget { static _classInit(klass) { klass = Gtk.Widget._classInit(klass); Gtk.Widget.install_action.call(klass, 'custom.action', null, widget => (widget.action = 42)); return klass; } }); 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.set_child(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 string', MyComplexGtkSubclassFromString); validateTemplate('UI template from file', MyComplexGtkSubclassFromFile); validateTemplate('Class inheriting from template class', SubclassSubclass, true); it('ensures signal handlers are callable', function () { const ClassWithUncallableHandler = GObject.registerClass({ Template: createTemplate('Gjs_ClassWithUncallableHandler'), Children: ['label-child', 'label-child2'], InternalChildren: ['internal-label-child'], }, class ClassWithUncallableHandler extends Gtk.Grid { templateCallback() {} get boundCallback() { return 'who ya gonna call?'; } }); // The exception is thrown inside a vfunc with a GError out parameter, // and Gtk logs a critical. writerFunc.calls.reset(); writerFunc.and.callFake((level, fields) => { const decoder = new TextDecoder('utf-8'); const domain = decoder.decode(fields?.GLIB_DOMAIN); const message = decoder.decode(fields?.MESSAGE); expect(level).toBe(GLib.LogLevelFlags.LEVEL_CRITICAL); expect(domain).toBe('Gtk'); expect(message).toMatch('is not a function'); return GLib.LogWriterOutput.HANDLED; }); void new ClassWithUncallableHandler(); expect(writerFunc).toHaveBeenCalled(); writerFunc.and.callThrough(); }); it('rejects unsupported template URIs', function () { expect(() => { return GObject.registerClass({ Template: 'https://gnome.org', }, class GtkTemplateInvalid extends Gtk.Widget { }); }).toThrowError(TypeError, /Invalid template URI/); }); it('sets CSS names on classes', function () { expect(Gtk.Widget.get_css_name.call(MyComplexGtkSubclass)).toEqual('complex-subclass'); }); it('static inheritance works', function () { expect(MyComplexGtkSubclass.get_css_name()).toEqual('complex-subclass'); }); it('can create a Gtk.TreeIter with accessible stamp field', function () { const iter = new Gtk.TreeIter(); iter.stamp = 42; expect(iter.stamp).toEqual(42); }); it('can create a Gtk.CustomSorter with callback', function () { const sortFunc = jasmine.createSpy('sortFunc').and.returnValue(1); const model = Gtk.StringList.new(['hello', 'world']); const sorter = Gtk.CustomSorter.new(sortFunc); void Gtk.SortListModel.new(model, sorter); expect(sortFunc).toHaveBeenCalledOnceWith(jasmine.any(Gtk.StringObject), jasmine.any(Gtk.StringObject)); }); it('can change the callback of a Gtk.CustomSorter', function () { const model = Gtk.StringList.new(['hello', 'world']); const sorter = Gtk.CustomSorter.new(null); void Gtk.SortListModel.new(model, sorter); const sortFunc = jasmine.createSpy('sortFunc').and.returnValue(1); sorter.set_sort_func(sortFunc); expect(sortFunc).toHaveBeenCalledOnceWith(jasmine.any(Gtk.StringObject), jasmine.any(Gtk.StringObject)); sortFunc.calls.reset(); sorter.set_sort_func(null); expect(sortFunc).not.toHaveBeenCalled(); }); }); describe('Gtk 4 regressions', function () { it('Gdk.Event fundamental type should not crash', function () { expect(() => new Gdk.Event()).toThrowError(/Couldn't find a constructor/); }); xit('Actions added via Gtk.WidgetClass.add_action() should not crash', function () { const custom = new CustomActionWidget(); custom.activate_action('custom.action', null); expect(custom.action).toEqual(42); }).pend('https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/3796'); it('Gdk.NoSelection section returns valid start/end values', function () { if (!Gtk.NoSelection.prototype.get_section) pending('Gtk 4.12 is required'); let result; try { result = new Gtk.NoSelection().get_section(0); } catch (err) { if (err.message.includes('not introspectable')) pending('This version of GTK has the annotation bug'); throw err; } expect(result).toEqual([0, GLib.MAXUINT32]); }); function createSurface(shouldStash) { // Create a Gdk.Surface that is unreachable after this function ends const display = Gdk.Display.get_default(); const surface = Gdk.Surface.new_toplevel(display); if (shouldStash) CjsTestTools.save_object(surface); } it('Gdk.Surface is destroyed properly', function () { createSurface(false); System.gc(); }); it('Gdk.Surface is not destroyed if a ref is held from C', function () { createSurface(true); System.gc(); const surface = CjsTestTools.steal_saved(); expect(surface.is_destroyed()).toBeFalsy(); }); }); class LeakTestWidget extends Gtk.Button { buttonClicked() {} } GObject.registerClass({ Template: new TextEncoder().encode(` `), }, LeakTestWidget); describe('Gtk template signal', function () { beforeAll(function () { Gtk.init(); }); function asyncIdle() { return new Promise(resolve => { GLib.idle_add(GLib.PRIORITY_DEFAULT, () => { resolve(); return GLib.SOURCE_REMOVE; }); }); } it('does not leak', async function () { const weakRef = new WeakRef(new LeakTestWidget()); await asyncIdle(); // It takes two GC cycles to free the widget, because of the tardy sweep // problem (https://gitlab.gnome.org/GNOME/gjs/-/issues/217) System.gc(); System.gc(); expect(weakRef.deref()).toBeUndefined(); }); }); cjs-128.0/installed-tests/js/meson.build0000664000175000017500000001201214771557763017146 0ustar fabiofabio# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2019 Philip Chimento # SPDX-FileCopyrightText: 2019 Chun-wei Fan ### Jasmine tests ############################################################## jsunit_resources_files = gnome.compile_resources('jsunit-resources', 'jsunit.gresources.xml', c_name: 'jsunit_resources') minijasmine = executable('minijasmine', '../minijasmine.cpp', jsunit_resources_files, dependencies: libcjs_dep, cpp_args: [ '-DINSTTESTDIR="@0@"'.format(prefix / installed_tests_execdir), ], include_directories: top_include, install: get_option('installed_tests'), install_dir: installed_tests_execdir) subdir('libgjstesttools') jasmine_tests = [ 'self', 'Cairo', 'Exceptions', 'Format', 'Fundamental', 'Gettext', 'GIMarshalling', 'Gio', 'GLib', 'GObject', 'GObjectClass', 'GObjectInterface', 'GObjectValue', 'GTypeClass', 'Importer', 'Importer2', 'Introspection', 'Lang', 'LegacyByteArray', 'LegacyClass', 'LegacyGObject', 'Mainloop', 'Namespace', 'Package', 'ParamSpec', 'Print', 'Promise', 'Regress', 'Signals', 'System', 'Tweener', 'WarnLib', ] if not get_option('skip_gtk_tests') jasmine_tests += [ 'Gtk3', 'GObjectDestructionAccess', 'LegacyGtk', ] endif installed_js_tests_dir = installed_tests_execdir / 'js' gschemas_compiled = gnome.compile_schemas( depend_files: 'org.cinnamon.CjsTest.gschema.xml') tests_dependencies = [ gschemas_compiled, cjs_private_typelib, gjstest_tools_typelib, gi_tests.get_variable('gimarshallingtests_typelib'), gi_tests.get_variable('regress_typelib'), gi_tests.get_variable('warnlib_typelib'), ] foreach test : jasmine_tests test_file = files('test@0@.js'.format(test)) test(test, minijasmine, args: test_file, depends: tests_dependencies, env: tests_environment, protocol: 'tap', suite: 'JS') test_description_subst = { 'name': 'test@0@.js'.format(test), 'installed_tests_execdir': prefix / installed_tests_execdir, } configure_file(configuration: test_description_subst, input: '../minijasmine.test.in', output: 'test@0@.test'.format(test), install: get_option('installed_tests'), install_dir: installed_tests_metadir) if get_option('installed_tests') install_data(test_file, install_dir: installed_js_tests_dir) endif endforeach if get_option('installed_tests') install_subdir('modules', install_dir: installed_js_tests_dir) endif # testGDBus.js and testGtk4.js are separate, because they can be skipped, and # during build should be run using dbus-run-session dbus_tests = ['GDBus'] if not get_option('skip_gtk_tests') have_gtk4 = dependency('gtk4', required: false).found() if have_gtk4 # FIXME: find out why GTK4 tries to acquire a message bus dbus_tests += 'Gtk4' endif endif bus_config = files('../../test/test-bus.conf') foreach test : dbus_tests test_file = files('test@0@.js'.format(test)) if not get_option('skip_dbus_tests') test(test, dbus_run_session, args: ['--config-file', bus_config, '--', minijasmine, test_file], env: tests_environment, protocol: 'tap', suite: 'dbus', depends: tests_dependencies) endif dbus_test_description_subst = { 'name': 'test@0@.js'.format(test), 'installed_tests_execdir': prefix / installed_tests_execdir, } configure_file( configuration: dbus_test_description_subst, input: '../minijasmine.test.in', output: 'test@0@.test'.format(test), install: get_option('installed_tests'), install_dir: installed_tests_metadir) if get_option('installed_tests') install_data(test_file, install_dir: installed_js_tests_dir) endif endforeach # tests using ES modules are also separate because they need an extra # minijasmine flag modules_tests = [ 'Async', 'CairoModule', 'Console', 'ESModules', 'AsyncMainloop', 'Encoding', 'GLibLogWriter', 'Global', 'Timers', 'WeakRef', ] foreach test : modules_tests test_file = files('test@0@.js'.format(test)) test(test, minijasmine, args: [test_file, '-m'], env: tests_environment, protocol: 'tap', suite: 'JS') esm_test_description_subst = { 'name': 'test@0@.js'.format(test), 'installed_tests_execdir': prefix / installed_tests_execdir, } configure_file(configuration: esm_test_description_subst, input: '../minijasmine-module.test.in', output: 'test@0@.test'.format(test), install: get_option('installed_tests'), install_dir: installed_tests_metadir) if get_option('installed_tests') install_data(test_file, install_dir: installed_js_tests_dir) endif endforeach if get_option('installed_tests') install_data('matchers.js', install_dir: installed_js_tests_dir) endif cjs-128.0/installed-tests/js/testAsync.js0000664000175000017500000000415314771557763017326 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2021 Evan Welsh import GLib from 'gi://GLib'; import GObject from 'gi://GObject'; const PRIORITIES = [ 'PRIORITY_LOW', 'PRIORITY_HIGH', 'PRIORITY_DEFAULT', 'PRIORITY_HIGH_IDLE', 'PRIORITY_DEFAULT_IDLE', ]; describe('Async microtasks resolves before', function () { // Generate test suites with different types of Sources const tests = [ { description: 'idle task with', createSource: () => GLib.idle_source_new(), }, { description: '0-second timeout task with', // A timeout of 0 tests if microtasks (promises) run before // non-idle tasks which would normally execute "next" in the loop createSource: () => GLib.timeout_source_new(0), }, ]; for (const {description, createSource} of tests) { describe(description, function () { const CORRECT_TASKS = [ 'async 1', 'async 2', 'source task', ]; for (const priority of PRIORITIES) { it(`priority set to GLib.${priority}`, function (done) { const tasks = []; const source = createSource(); source.set_priority(GLib[priority]); GObject.source_set_closure(source, () => { tasks.push('source task'); expect(tasks).toEqual(jasmine.arrayWithExactContents(CORRECT_TASKS)); done(); source.destroy(); return GLib.SOURCE_REMOVE; }); source.attach(null); (async () => { // Without an await async functions execute // synchronously tasks.push(await 'async 1'); })().then(() => { tasks.push('async 2'); }); }); } }); } }); cjs-128.0/installed-tests/js/testRegress.js0000664000175000017500000023631314771557763017670 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2008 litl, LLC // SPDX-FileCopyrightText: 2008 Red Hat, Inc. const Regress = imports.gi.Regress; // We use Gio to have some objects that we know exist imports.gi.versions.Gtk = '3.0'; const GLib = imports.gi.GLib; const Gio = imports.gi.Gio; const GObject = imports.gi.GObject; function expectWarn64(callable) { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, '*cannot be safely stored*'); const ret = callable(); GLib.test_assert_expected_messages_internal('Cjs', 'testRegress.js', 0, 'Ignore message'); return ret; } const bit64Types = ['uint64', 'int64']; if (GLib.SIZEOF_LONG === 8) bit64Types.push('long', 'ulong'); if (GLib.SIZEOF_SIZE_T === 8) bit64Types.push('size'); if (GLib.SIZEOF_SSIZE_T === 8) bit64Types.push('ssize'); describe('Life, the Universe and Everything', function () { it('includes null return value', function () { expect(Regress.test_return_allow_none()).toBeNull(); expect(Regress.test_return_nullable()).toBeNull(); }); it('includes booleans', function () { expect(Regress.test_boolean(false)).toBe(false); expect(Regress.test_boolean(true)).toBe(true); expect(Regress.test_boolean_true(true)).toBe(true); expect(Regress.test_boolean_false(false)).toBe(false); }); [8, 16, 32, 64].forEach(bits => { it(`includes ${bits}-bit integers`, function () { const method = `test_int${bits}`; expect(Regress[method](42)).toBe(42); expect(Regress[method](-42)).toBe(-42); expect(Regress[method](undefined)).toBe(0); expect(Regress[method](42.42)).toBe(42); expect(Regress[method](-42.42)).toBe(-42); if (bits >= 64) { expect(Regress[method](42n)).toBe(42); expect(Regress[method](-42n)).toBe(-42); } else { expect(() => Regress[method](42n)).toThrow(); expect(() => Regress[method](-42n)).toThrow(); } }); it(`includes unsigned ${bits}-bit integers`, function () { const method = `test_uint${bits}`; expect(Regress[method](42)).toBe(42); expect(Regress[method](undefined)).toBe(0); expect(Regress[method](42.42)).toBe(42); if (bits >= 64) expect(Regress[method](42n)).toEqual(42); else expect(() => Regress[method](42n)).toThrow(); }); }); ['short', 'int', 'long', 'ssize', 'float', 'double'].forEach(type => { it(`includes ${type}s`, function () { const method = `test_${type}`; expect(Regress[method](42)).toBe(42); expect(Regress[method](-42)).toBe(-42); if (['float', 'double'].includes(type)) { expect(Regress[method](undefined)).toBeNaN(); expect(Regress[method](42.42)).toBeCloseTo(42.42); expect(Regress[method](-42.42)).toBeCloseTo(-42.42); } else { expect(Regress[method](undefined)).toBe(0); expect(Regress[method](42.42)).toBe(42); expect(Regress[method](-42.42)).toBe(-42); } if (bit64Types.includes(type)) { expect(Regress[method](42n)).toBe(42); expect(Regress[method](-42n)).toBe(-42); } else { expect(() => Regress[method](42n)).toThrow(); expect(() => Regress[method](-42n)).toThrow(); } }); }); ['ushort', 'uint', 'ulong', 'size'].forEach(type => { it(`includes ${type}s`, function () { const method = `test_${type}`; expect(Regress[method](42)).toBe(42); expect(Regress[method](undefined)).toBe(0); expect(Regress[method](42.42)).toBe(42); if (bit64Types.includes(type)) expect(Regress[method](42n)).toBe(42); else expect(() => Regress[method](42n)).toThrow(); }); }); describe('No implicit conversion to unsigned', function () { ['uint8', 'uint16', 'uint32', 'uint64', 'uint', 'size'].forEach(type => { it(`for ${type}`, function () { expect(() => Regress[`test_${type}`](-42)).toThrowError(/out of range/); if (bit64Types.includes(type)) expect(() => Regress[`test_${type}`](-42n)).toThrowError(/out of range/); else expect(() => Regress[`test_${type}`](-42n)).toThrow(); }); }); }); describe('Infinity and NaN', function () { ['int8', 'int16', 'int32', 'int64', 'short', 'int', 'long', 'ssize'].forEach(type => { it(`converts to 0 for ${type}`, function () { expect(Regress[`test_${type}`](Infinity)).toBe(0); expect(Regress[`test_${type}`](-Infinity)).toBe(0); expect(Regress[`test_${type}`](NaN)).toBe(0); }); }); ['uint8', 'uint16', 'uint32', 'uint64', 'ushort', 'uint', 'ulong', 'size'].forEach(type => { it(`converts to 0 for ${type}`, function () { expect(Regress[`test_${type}`](Infinity)).toBe(0); expect(Regress[`test_${type}`](NaN)).toBe(0); }); }); ['float', 'double'].forEach(type => { it(`not for ${type}`, function () { expect(Regress[`test_${type}`](Infinity)).toBe(Infinity); expect(Regress[`test_${type}`](-Infinity)).toBe(-Infinity); expect(Regress[`test_${type}`](NaN)).toBeNaN(); }); }); }); describe('(u)int64 numeric values', function () { const minInt64 = -(2n ** 63n); const maxInt64 = 2n ** 63n - 1n; const maxUint64 = 2n ** 64n - 1n; ['uint64', 'int64', 'long', 'ulong', 'size', 'ssize'].forEach(type => { if (!bit64Types.includes(type)) return; const signed = ['int64', 'long', 'ssize'].includes(type); const limits = { min: signed ? minInt64 : 0n, max: signed ? maxInt64 : maxUint64, }; const testFunc = Regress[`test_${type}`]; it(`can use numeric limits for ${type}`, function () { expect(expectWarn64(() => testFunc(limits.max))) .toEqual(Number(limits.max)); if (signed) { expect(expectWarn64(() => testFunc(limits.min))) .toEqual(Number(limits.min)); } }); }); }); it('includes wide characters', function () { expect(Regress.test_unichar('c')).toBe('c'); expect(Regress.test_unichar('')).toBe(''); expect(Regress.test_unichar('\u2665')).toBe('\u2665'); }); it('includes time_t', function () { const now = Math.floor(new Date().getTime() / 1000); const bounced = Math.floor(Regress.test_timet(now)); expect(bounced).toEqual(now); }); it('includes GTypes', function () { expect(Regress.test_gtype(GObject.TYPE_NONE)).toBe(GObject.TYPE_NONE); expect(Regress.test_gtype(String)).toBe(GObject.TYPE_STRING); expect(Regress.test_gtype(GObject.Object)).toBe(GObject.Object.$gtype); }); it('closures', function () { const callback = jasmine.createSpy('callback').and.returnValue(42); expect(Regress.test_closure(callback)).toEqual(42); expect(callback).toHaveBeenCalledWith(); }); it('closures with one argument', function () { const callback = jasmine.createSpy('callback') .and.callFake(someValue => someValue); expect(Regress.test_closure_one_arg(callback, 42)).toEqual(42); expect(callback).toHaveBeenCalledWith(42); }); it('closure with GLib.Variant argument', function () { const callback = jasmine.createSpy('callback') .and.returnValue(new GLib.Variant('s', 'hello')); const variant = new GLib.Variant('i', 42); expect(Regress.test_closure_variant(callback, variant).deepUnpack()) .toEqual('hello'); expect(callback).toHaveBeenCalledWith(variant); }); describe('GValue marshalling', function () { it('integer in', function () { expect(Regress.test_int_value_arg(42)).toEqual(42); }); it('integer out', function () { expect(Regress.test_value_return(42)).toEqual(42); }); }); // See testCairo.js for the following tests, since that will be skipped if // we are building without Cairo support: // Regress.test_cairo_context_full_return() // Regress.test_cairo_context_none_in() // Regress.test_cairo_surface_none_return() // Regress.test_cairo_surface_full_return() // Regress.test_cairo_surface_none_in() // Regress.test_cairo_surface_full_out() // Regress.TestObj.emit_sig_with_foreign_struct() it('integer GLib.Variant', function () { const ivar = Regress.test_gvariant_i(); expect(ivar.get_type_string()).toEqual('i'); expect(ivar.unpack()).toEqual(1); }); it('string GLib.Variant', function () { const svar = Regress.test_gvariant_s(); expect(String.fromCharCode(svar.classify())).toEqual('s'); expect(svar.unpack()).toEqual('one'); }); it('dictionary GLib.Variant', function () { const asvvar = Regress.test_gvariant_asv(); expect(asvvar.recursiveUnpack()).toEqual({name: 'foo', timeout: 10}); }); it('variant GLib.Variant', function () { const vvar = Regress.test_gvariant_v(); expect(vvar.unpack()).toEqual(jasmine.any(GLib.Variant)); expect(vvar.recursiveUnpack()).toEqual('contents'); }); it('string array GLib.Variant', function () { const asvar = Regress.test_gvariant_as(); expect(asvar.deepUnpack()).toEqual(['one', 'two', 'three']); }); describe('UTF-8 strings', function () { const CONST_STR = 'const ♥ utf8'; const NONCONST_STR = 'nonconst ♥ utf8'; it('as return types', function () { expect(Regress.test_utf8_const_return()).toEqual(CONST_STR); expect(Regress.test_utf8_nonconst_return()).toEqual(NONCONST_STR); }); it('as in parameters', function () { Regress.test_utf8_const_in(CONST_STR); }); it('as out parameters', function () { expect(Regress.test_utf8_out()).toEqual(NONCONST_STR); }); xit('as in-out parameters', function () { expect(Regress.test_utf8_inout(CONST_STR)).toEqual(NONCONST_STR); }).pend('https://gitlab.gnome.org/GNOME/gobject-introspection/issues/192'); }); it('return values in filename encoding', function () { const filenames = Regress.test_filename_return(); expect(filenames).toEqual(['\u00e5\u00e4\u00f6', '/etc/fstab']); }); describe('Various configurations of arguments', function () { it('in after out', function () { const str = 'hello'; const len = Regress.test_int_out_utf8(str); expect(len).toEqual(str.length); }); it('multiple number args', function () { const [times2, times3] = Regress.test_multi_double_args(2.5); expect(times2).toEqual(5); expect(times3).toEqual(7.5); }); it('multiple string out parameters', function () { const [first, second] = Regress.test_utf8_out_out(); expect(first).toEqual('first'); expect(second).toEqual('second'); }); it('strings as return value and output parameter', function () { const [first, second] = Regress.test_utf8_out_nonconst_return(); expect(first).toEqual('first'); expect(second).toEqual('second'); }); it('nullable string in parameter', function () { expect(() => Regress.test_utf8_null_in(null)).not.toThrow(); }); it('nullable string out parameter', function () { expect(Regress.test_utf8_null_out()).toBeNull(); }); }); ['int', 'gint8', 'gint16', 'gint32', 'gint64'].forEach(inttype => { it(`arrays of ${inttype} in`, function () { expect(Regress[`test_array_${inttype}_in`]([1, 2, 3, 4])).toEqual(10); }); }); it('implicit conversions from strings to int arrays', function () { expect(Regress.test_array_gint8_in('\x01\x02\x03\x04')).toEqual(10); expect(Regress.test_array_gint16_in('\x01\x02\x03\x04')).toEqual(10); expect(Regress.test_array_gint16_in('\u0100\u0200\u0300\u0400')).toEqual(2560); }); it('out arrays of integers', function () { expect(Regress.test_array_int_out()).toEqual([0, 1, 2, 3, 4]); }); xit('inout arrays of integers', function () { expect(Regress.test_array_int_inout([0, 1, 2, 3, 4])).toEqual([2, 3, 4, 5]); }).pend('https://gitlab.gnome.org/GNOME/gobject-introspection/issues/192'); describe('String arrays', function () { it('marshalling in', function () { expect(Regress.test_strv_in(['1', '2', '3'])).toBeTruthy(); expect(Regress.test_strv_in(['4', '5', '6'])).toBeFalsy(); // Ensure that primitives throw without SEGFAULT expect(() => Regress.test_strv_in(1)).toThrow(); expect(() => Regress.test_strv_in('')).toThrow(); expect(() => Regress.test_strv_in(false)).toThrow(); // Second two are deliberately not strings expect(() => Regress.test_strv_in(['1', 2, 3])).toThrow(); }); it('marshalling out', function () { expect(Regress.test_strv_out()) .toEqual(['thanks', 'for', 'all', 'the', 'fish']); }); it('marshalling return value with container transfer', function () { expect(Regress.test_strv_out_container()).toEqual(['1', '2', '3']); }); it('marshalling out parameter with container transfer', function () { expect(Regress.test_strv_outarg()).toEqual(['1', '2', '3']); }); }); it('GType arrays', function () { expect(Regress.test_array_gtype_in([Gio.SimpleAction, Gio.Icon, GObject.TYPE_BOXED])) .toEqual('[GSimpleAction,GIcon,GBoxed,]'); expect(() => Regress.test_array_gtype_in(42)).toThrow(); expect(() => Regress.test_array_gtype_in([undefined])).toThrow(); // 80 is G_TYPE_OBJECT, but we don't want it to work expect(() => Regress.test_array_gtype_in([80])).toThrow(); }); describe('Fixed arrays of integers', function () { it('marshals as an in parameter', function () { expect(Regress.test_array_fixed_size_int_in([1, 2, 3, 4])).toEqual(10); }); it('marshals as an out parameter', function () { expect(Regress.test_array_fixed_size_int_out()).toEqual([0, 1, 2, 3, 4]); }); it('marshals as a return value', function () { expect(Regress.test_array_fixed_size_int_return()).toEqual([0, 1, 2, 3, 4]); }); }); it('integer array with static length', function () { const arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; expect(() => Regress.test_array_static_in_int(arr)).not.toThrow(); }); it("string array that's const in C", function () { expect(Regress.test_strv_out_c()).toEqual(['thanks', 'for', 'all', 'the', 'fish']); }); describe('arrays of integers with length parameter', function () { it('marshals as a return value with transfer full', function () { expect(Regress.test_array_int_full_out()).toEqual([0, 1, 2, 3, 4]); }); it('marshals as a return value with transfer none', function () { expect(Regress.test_array_int_none_out()).toEqual([1, 2, 3, 4, 5]); }); it('marshalls as a nullable in parameter', function () { expect(() => Regress.test_array_int_null_in(null)).not.toThrow(); }); it('marshals as a nullable return value', function () { expect(Regress.test_array_int_null_out()).toEqual([]); }); }); ['glist', 'gslist'].forEach(list => { describe(`${list} types`, function () { const STR_LIST = ['1', '2', '3']; it('return with transfer-none', function () { expect(Regress[`test_${list}_nothing_return`]()).toEqual(STR_LIST); expect(Regress[`test_${list}_nothing_return2`]()).toEqual(STR_LIST); }); it('return with transfer-container', function () { expect(Regress[`test_${list}_container_return`]()).toEqual(STR_LIST); }); it('return with transfer-full', function () { expect(Regress[`test_${list}_everything_return`]()).toEqual(STR_LIST); }); it('in with transfer-none', function () { Regress[`test_${list}_nothing_in`](STR_LIST); Regress[`test_${list}_nothing_in2`](STR_LIST); }); it('nullable in', function () { expect(() => Regress[`test_${list}_null_in`]([])).not.toThrow(); }); it('nullable out', function () { expect(Regress[`test_${list}_null_out`]()).toEqual([]); }); xit('in with transfer-container', function () { Regress[`test_${list}_container_in`](STR_LIST); }).pend('Function not added to gobject-introspection test suite yet'); }); }); it('GList of GTypes in with transfer container', function () { expect(() => Regress.test_glist_gtype_container_in([Regress.TestObj, Regress.TestSubObj])) .not.toThrow(); }); describe('GHash type', function () { const EXPECTED_HASH = {baz: 'bat', foo: 'bar', qux: 'quux'}; it('null GHash out', function () { expect(Regress.test_ghash_null_return()).toBeNull(); }); it('out GHash', function () { expect(Regress.test_ghash_nothing_return()).toEqual(EXPECTED_HASH); expect(Regress.test_ghash_nothing_return2()).toEqual(EXPECTED_HASH); }); const GVALUE_HASH_TABLE = { 'integer': 12, 'boolean': true, 'string': 'some text', 'strings': ['first', 'second', 'third'], 'flags': Regress.TestFlags.FLAG1 | Regress.TestFlags.FLAG3, 'enum': Regress.TestEnum.VALUE2, }; it('with GValue value type out', function () { expect(Regress.test_ghash_gvalue_return()).toEqual(GVALUE_HASH_TABLE); }); xit('with GValue value type in', function () { expect(() => Regress.test_ghash_gvalue_in(GVALUE_HASH_TABLE)).not.toThrow(); }).pend('https://gitlab.gnome.org/GNOME/gjs/issues/272'); it('marshals as a return value with transfer container', function () { expect(Regress.test_ghash_container_return()).toEqual(EXPECTED_HASH); }); it('marshals as a return value with transfer full', function () { expect(Regress.test_ghash_everything_return()).toEqual(EXPECTED_HASH); }); it('null GHash in', function () { Regress.test_ghash_null_in(null); }); it('null GHashTable out', function () { expect(Regress.test_ghash_null_out()).toBeNull(); }); it('in GHash', function () { Regress.test_ghash_nothing_in(EXPECTED_HASH); Regress.test_ghash_nothing_in2(EXPECTED_HASH); }); it('nested GHash', function () { const EXPECTED_NESTED_HASH = {wibble: EXPECTED_HASH}; expect(Regress.test_ghash_nested_everything_return()) .toEqual(EXPECTED_NESTED_HASH); expect(Regress.test_ghash_nested_everything_return2()) .toEqual(EXPECTED_NESTED_HASH); }); }); describe('GArray', function () { it('marshals as a return value with transfer container', function () { expect(Regress.test_garray_container_return()).toEqual(['regress']); }); it('marshals as a return value with transfer full', function () { expect(Regress.test_garray_full_return()).toEqual(['regress']); }); }); it('enum that references its own members has correct values', function () { expect(Regress.TestReferenceEnum.ZERO).toEqual(4); expect(Regress.TestReferenceEnum.ONE).toEqual(2); expect(Regress.TestReferenceEnum.TWO).toEqual(54); expect(Regress.TestReferenceEnum.THREE).toEqual(4); expect(Regress.TestReferenceEnum.FOUR).toEqual(216); expect(Regress.TestReferenceEnum.FIVE).toEqual(-217); }); it('unregistered enum works', function () { expect(Regress.TestEnumNoGEnum.EVALUE1).toEqual(0); expect(Regress.TestEnumNoGEnum.EVALUE2).toEqual(42); expect(Regress.TestEnumNoGEnum.EVALUE3).toEqual('0'.charCodeAt()); }); it('value is not added to enum with #define', function () { expect(Regress.TestEnumNoGEnum.EVALUE_DEPRECATED).not.toBeDefined(); }); it('enum parameter', function () { expect(Regress.test_enum_param(Regress.TestEnum.VALUE1)).toEqual('value1'); expect(Regress.test_enum_param(Regress.TestEnum.VALUE3)).toEqual('value3'); }); it('unsigned enum parameter', function () { expect(Regress.test_unsigned_enum_param(Regress.TestEnumUnsigned.VALUE1)) .toEqual('value1'); expect(Regress.test_unsigned_enum_param(Regress.TestEnumUnsigned.VALUE2)) .toEqual('value2'); }); it('flags parameter', function () { expect(Regress.global_get_flags_out()).toEqual(Regress.TestFlags.FLAG1 | Regress.TestFlags.FLAG3); }); describe('Simple introspected struct', function () { let struct; beforeEach(function () { struct = new Regress.TestStructA(); struct.some_int = 42; struct.some_int8 = 43; struct.some_double = 42.5; struct.some_enum = Regress.TestEnum.VALUE3; }); it('sets fields correctly', function () { expect(struct.some_int).toEqual(42); expect(struct.some_int8).toEqual(43); expect(struct.some_double).toEqual(42.5); expect(struct.some_enum).toEqual(Regress.TestEnum.VALUE3); }); it('can clone', function () { const b = struct.clone(); expect(b.some_int).toEqual(42); expect(b.some_int8).toEqual(43); expect(b.some_double).toEqual(42.5); expect(b.some_enum).toEqual(Regress.TestEnum.VALUE3); }); it('can be modified by a method', function () { const c = Regress.TestStructA.parse('foobar'); expect(c.some_int).toEqual(23); }); describe('constructors', function () { beforeEach(function () { struct = new Regress.TestStructA({ some_int: 42, some_int8: 43, some_double: 42.5, some_enum: Regress.TestEnum.VALUE3, }); }); it('"copies" an object from a hash of field values', function () { expect(struct.some_int).toEqual(42); expect(struct.some_int8).toEqual(43); expect(struct.some_double).toEqual(42.5); expect(struct.some_enum).toEqual(Regress.TestEnum.VALUE3); }); it('catches bad field names', function () { expect(() => new Regress.TestStructA({junk: 42})).toThrow(); }); it('copies an object from another object of the same type', function () { const copy = new Regress.TestStructA(struct); expect(copy.some_int).toEqual(42); expect(copy.some_int8).toEqual(43); expect(copy.some_double).toEqual(42.5); expect(copy.some_enum).toEqual(Regress.TestEnum.VALUE3); }); }); }); it('out arrays of structs', function () { const array = Regress.test_array_struct_out(); const ints = array.map(struct => struct.some_int); expect(ints).toEqual([22, 33, 44]); }); describe('Introspected nested struct', function () { let struct; beforeEach(function () { struct = new Regress.TestStructB(); struct.some_int8 = 43; struct.nested_a.some_int8 = 66; }); it('sets fields correctly', function () { expect(struct.some_int8).toEqual(43); expect(struct.nested_a.some_int8).toEqual(66); }); it('can clone', function () { const b = struct.clone(); expect(b.some_int8).toEqual(43); expect(b.nested_a.some_int8).toEqual(66); }); }); // Bare GObject pointer, not currently supported (and possibly not ever) xdescribe('Struct with non-basic member', function () { it('sets fields correctly', function () { const struct = new Regress.TestStructC(); struct.another_int = 43; struct.obj = new GObject.Object(); expect(struct.another_int).toEqual(43); expect(struct.obj).toEqual(jasmine.any(GObject.Object)); }); }); describe('Struct with annotated fields', function () { xit('sets fields correctly', function () { const testObjList = [new Regress.TestObj(), new Regress.TestObj()]; const testStructList = [new Regress.TestStructA(), new Regress.TestStructA()]; const struct = new Regress.TestStructD(); struct.array1 = testStructList; struct.array2 = testObjList; struct.field = testObjList[0]; struct.list = testObjList; struct.garray = testObjList; expect(struct.array1).toEqual(testStructList); expect(struct.array2).toEqual(testObjList); expect(struct.field).toEqual(testObjList[0]); expect(struct.list).toEqual(testObjList); expect(struct.garray).toEqual(testObjList); }).pend('https://gitlab.gnome.org/GNOME/gjs/issues/83'); }); describe('Struct with array of anonymous unions', function () { xit('sets fields correctly', function () { const struct = new Regress.TestStructE(); struct.some_type = GObject.Object.$gtype; for (let ix = 0; ix < 1; ix++) { struct.some_union[ix].v_int = 42; struct.some_union[ix].v_uint = 43; struct.some_union[ix].v_long = 44; struct.some_union[ix].v_ulong = 45; struct.some_union[ix].v_int64 = 46; struct.some_union[ix].v_uint64 = 47; struct.some_union[ix].v_float = 48.5; struct.some_union[ix].v_double = 49.5; struct.some_union[ix].v_pointer = null; } expect(struct.some_type).toEqual(GObject.Object.$gtype); for (let ix = 0; ix < 1; ix++) { expect(struct.some_union[ix].v_int).toEqual(42); expect(struct.some_union[ix].v_uint).toEqual(43); expect(struct.some_union[ix].v_long).toEqual(44); expect(struct.some_union[ix].v_ulong).toEqual(45); expect(struct.some_union[ix].v_int64).toEqual(46); expect(struct.some_union[ix].v_uint64).toEqual(47); expect(struct.some_union[ix].v_float).toEqual(48.5); expect(struct.some_union[ix].v_double).toEqual(49.5); expect(struct.some_union[ix].v_pointer).toBeNull(); } }).pend('https://gitlab.gnome.org/GNOME/gjs/issues/273'); }); // Bare int pointers, not currently supported (and possibly not ever) xdescribe('Struct with const/volatile members', function () { it('sets fields correctly', function () { const struct = new Regress.TestStructF(); struct.ref_count = 1; struct.data1 = null; struct.data2 = null; struct.data3 = null; struct.data4 = null; struct.data5 = null; struct.data6 = null; struct.data7 = 42; expect(struct.ref_count).toEqual(1); expect(struct.data1).toBeNull(); expect(struct.data2).toBeNull(); expect(struct.data3).toBeNull(); expect(struct.data4).toBeNull(); expect(struct.data5).toBeNull(); expect(struct.data6).toBeNull(); expect(struct.data7).toEqual(42); }); }); describe('Introspected simple boxed struct', function () { let struct; beforeEach(function () { struct = new Regress.TestSimpleBoxedA(); struct.some_int = 42; struct.some_int8 = 43; struct.some_double = 42.5; struct.some_enum = Regress.TestEnum.VALUE3; }); it('sets fields correctly', function () { expect(struct.some_int).toEqual(42); expect(struct.some_int8).toEqual(43); expect(struct.some_double).toEqual(42.5); expect(struct.some_enum).toEqual(Regress.TestEnum.VALUE3); }); it('can be passed to a method', function () { const other = new Regress.TestSimpleBoxedA({ some_int: 42, some_int8: 43, some_double: 42.5, }); expect(other.equals(struct)).toBeTruthy(); }); it('can be returned from a method', function () { const other = Regress.TestSimpleBoxedA.const_return(); expect(other.some_int).toEqual(5); expect(other.some_int8).toEqual(6); expect(other.some_double).toEqual(7); }); describe('constructors', function () { beforeEach(function () { struct = new Regress.TestSimpleBoxedA({ some_int: 42, some_int8: 43, some_double: 42.5, some_enum: Regress.TestEnum.VALUE3, }); }); it('"copies" an object from a hash of field values', function () { expect(struct.some_int).toEqual(42); expect(struct.some_int8).toEqual(43); expect(struct.some_double).toEqual(42.5); expect(struct.some_enum).toEqual(Regress.TestEnum.VALUE3); }); it('catches bad field names', function () { expect(() => new Regress.TestSimpleBoxedA({junk: 42})).toThrow(); }); it('copies an object from another object of the same type', function () { const copy = new Regress.TestSimpleBoxedA(struct); expect(copy).toEqual(jasmine.any(Regress.TestSimpleBoxedA)); expect(copy.some_int).toEqual(42); expect(copy.some_int8).toEqual(43); expect(copy.some_double).toEqual(42.5); expect(copy.some_enum).toEqual(Regress.TestEnum.VALUE3); }); }); }); describe('Introspected boxed nested struct', function () { let struct; beforeEach(function () { struct = new Regress.TestSimpleBoxedB(); struct.some_int8 = 42; struct.nested_a.some_int = 43; }); it('reads fields and nested fields', function () { expect(struct.some_int8).toEqual(42); expect(struct.nested_a.some_int).toEqual(43); }); it('assigns nested struct field from an instance', function () { struct.nested_a = new Regress.TestSimpleBoxedA({some_int: 53}); expect(struct.nested_a.some_int).toEqual(53); }); it('assigns nested struct field directly from a hash of field values', function () { struct.nested_a = {some_int: 63}; expect(struct.nested_a.some_int).toEqual(63); }); describe('constructors', function () { it('constructs with a nested hash of field values', function () { const simple2 = new Regress.TestSimpleBoxedB({ some_int8: 42, nested_a: { some_int: 43, some_int8: 44, some_double: 43.5, }, }); expect(simple2.some_int8).toEqual(42); expect(simple2.nested_a.some_int).toEqual(43); expect(simple2.nested_a.some_int8).toEqual(44); expect(simple2.nested_a.some_double).toEqual(43.5); }); it('copies an object from another object of the same type', function () { const copy = new Regress.TestSimpleBoxedB(struct); expect(copy.some_int8).toEqual(42); expect(copy.nested_a.some_int).toEqual(43); }); }); }); describe('Introspected boxed types', function () { describe('Opaque', function () { it('constructs from a default constructor', function () { const boxed = new Regress.TestBoxed(); expect(boxed).toEqual(jasmine.any(Regress.TestBoxed)); }); it('sets fields correctly', function () { const boxed = new Regress.TestBoxed(); boxed.some_int8 = 42; expect(boxed.some_int8).toEqual(42); }); it('constructs from a static constructor', function () { const boxed = Regress.TestBoxed.new_alternative_constructor1(42); expect(boxed.some_int8).toEqual(42); }); it('constructs from a static constructor with different args', function () { const boxed = Regress.TestBoxed.new_alternative_constructor2(40, 2); expect(boxed.some_int8).toEqual(42); }); it('constructs from a static constructor with differently typed args', function () { const boxed = Regress.TestBoxed.new_alternative_constructor3('42'); expect(boxed.some_int8).toEqual(42); }); it('constructs from a another object of the same type', function () { const boxed = new Regress.TestBoxed({some_int8: 42}); const copy = new Regress.TestBoxed(boxed); expect(copy.some_int8).toEqual(42); expect(copy.equals(boxed)).toBeTruthy(); }); it('ensures methods are named correctly', function () { const boxed = new Regress.TestBoxed(); expect(boxed.s_not_a_method).not.toBeDefined(); expect(boxed.not_a_method).not.toBeDefined(); expect(() => Regress.test_boxeds_not_a_method(boxed)).not.toThrow(); }); it('ensures static methods are named correctly', function () { expect(Regress.TestBoxed.s_not_a_static).not.toBeDefined(); expect(Regress.TestBoxed.not_a_static).not.toBeDefined(); expect(Regress.test_boxeds_not_a_static).not.toThrow(); }); }); describe('Simple', function () { it('sets fields correctly', function () { const boxed = new Regress.TestBoxedB(); boxed.some_int8 = 7; boxed.some_long = 5; expect(boxed.some_int8).toEqual(7); expect(boxed.some_long).toEqual(5); }); it('constructs from a static constructor', function () { const boxed = Regress.TestBoxedB.new(7, 5); expect(boxed.some_int8).toEqual(7); expect(boxed.some_long).toEqual(5); }); it('constructs from another object of the same type', function () { const boxed = Regress.TestBoxedB.new(7, 5); const copy = new Regress.TestBoxedB(boxed); expect(copy.some_int8).toEqual(7); expect(copy.some_long).toEqual(5); }); // Regress.TestBoxedB has a constructor that takes multiple arguments, // but since it is directly allocatable, we keep the old style of // passing an hash of fields. The two real world structs that have this // behavior are Clutter.Color and Clutter.ActorBox. it('constructs in backwards compatibility mode', function () { const boxed = new Regress.TestBoxedB({some_int8: 7, some_long: 5}); expect(boxed.some_int8).toEqual(7); expect(boxed.some_long).toEqual(5); }); }); describe('Refcounted', function () { it('constructs from a default constructor', function () { const boxed = new Regress.TestBoxedC(); expect(boxed.another_thing).toEqual(42); }); it('constructs from another object of the same type', function () { const boxed = new Regress.TestBoxedC({another_thing: 43}); const copy = new Regress.TestBoxedC(boxed); expect(copy.another_thing).toEqual(43); }); }); describe('Private', function () { it('constructs using a custom constructor', function () { const boxed = new Regress.TestBoxedD('abcd', 8); expect(boxed.get_magic()).toEqual(12); }); it('constructs from another object of the same type', function () { const boxed = new Regress.TestBoxedD('abcd', 8); const copy = new Regress.TestBoxedD(boxed); expect(copy.get_magic()).toEqual(12); }); it('does not construct with a default constructor', function () { expect(() => new Regress.TestBoxedD()).toThrow(); }); }); xit('methods take priority over fields in a name conflict', function () { const boxed = new Regress.TestBoxedC({name_conflict: true}); expect(boxed.name_conflict).not.toBeTrue(); expect(boxed.name_conflict()).toBeTrue(); }).pend('https://gitlab.gnome.org/GNOME/gobject-introspection/-/merge_requests/454'); }); describe('wrong type for GBoxed', function () { let simpleBoxed, wrongObject, wrongBoxed; beforeEach(function () { simpleBoxed = new Regress.TestSimpleBoxedA(); wrongObject = new Gio.SimpleAction(); wrongBoxed = new GLib.KeyFile(); }); // simpleBoxed.equals expects a Everything.TestSimpleBoxedA it('function does not accept a GObject of the wrong type', function () { expect(() => simpleBoxed.equals(wrongObject)).toThrow(); }); it('function does not accept a GBoxed of the wrong type', function () { expect(() => simpleBoxed.equals(wrongBoxed)).toThrow(); }); it('function does accept a GBoxed of the correct type', function () { expect(simpleBoxed.equals(simpleBoxed)).toBeTruthy(); }); it('method cannot be called on a GObject', function () { expect(() => Regress.TestSimpleBoxedA.prototype.copy.call(wrongObject)) .toThrow(); }); it('method cannot be called on a GBoxed of the wrong type', function () { expect(() => Regress.TestSimpleBoxedA.prototype.copy.call(wrongBoxed)) .toThrow(); }); it('method can be called on correct GBoxed type', function () { expect(() => Regress.TestSimpleBoxedA.prototype.copy.call(simpleBoxed)) .not.toThrow(); }); }); describe('Introspected GObject', function () { let o; beforeEach(function () { o = new Regress.TestObj({ // These properties have backing public fields with different names int: 42, float: 3.1416, double: 2.71828, }); }); it('can access fields with simple types', function () { // Compare the values gotten through the GObject property getters to the // values of the backing fields expect(o.some_int8).toEqual(o.int); expect(o.some_float).toEqual(o.float); expect(o.some_double).toEqual(o.double); }); it('cannot access fields with complex types (GI limitation)', function () { expect(() => o.parent_instance).toThrow(); expect(() => o.function_ptr).toThrow(); }); it('throws when setting a read-only field', function () { expect(() => (o.some_int8 = 41)).toThrow(); }); it('has normal Object methods', function () { o.ownprop = 'foo'; // eslint-disable-next-line no-prototype-builtins expect(o.hasOwnProperty('ownprop')).toBeTruthy(); }); it('sets write-only properties', function () { expect(o.int).not.toEqual(0); o.write_only = true; expect(o.int).toEqual(0); }); it('gives undefined for write-only properties', function () { expect(o.write_only).not.toBeDefined(); }); it('constructs from constructors annotated with (constructor)', function () { expect(Regress.TestObj.new(o)).toEqual(jasmine.any(Regress.TestObj)); expect(Regress.TestObj.constructor()).toEqual(jasmine.any(Regress.TestObj)); }); it('static methods', function () { const v = Regress.TestObj.new_from_file('/enoent'); expect(v).toEqual(jasmine.any(Regress.TestObj)); }); describe('GProperty', function () { let t, boxed, hashTable, hashTable2, list2, string, gtype, byteArray; const list = null; const int = 42; const double = Math.PI; const double2 = Math.E; beforeEach(function () { boxed = new Regress.TestBoxed({some_int8: 127}); hashTable = {a: 1, b: 2}; hashTable2 = {c: 3, d: 4}; list2 = ['j', 'k', 'l']; string = 'cauliflower'; gtype = GObject.Object.$gtype; byteArray = Uint8Array.from('abcd', c => c.charCodeAt(0)); t = new Regress.TestObj({ boxed, // hashTable, list, // pptrarray: list, // hashTableOld: hashTable, listOld: list, int, float: double, double, string, gtype, // byteArray, }); }); it('Boxed type', function () { expect(t.boxed.some_int8).toBe(127); const boxed2 = new Regress.TestBoxed({some_int8: 31}); t.boxed = boxed2; expect(t.boxed.some_int8).toBe(31); }); xit('Hash table', function () { expect(t.hashTable).toBe(hashTable); t.hashTable = hashTable2; expect(t.hashTable).toBe(hashTable2); }).pend('https://gitlab.gnome.org/GNOME/gjs/-/issues/83'); xit('List', function () { expect(t.list).toBe(list); t.list = list2; expect(t.list).toBe(list2); }).pend('https://gitlab.gnome.org/GNOME/gjs/-/issues/83'); xit('Pointer array', function () { expect(t.pptrarray).toBe(list); t.pptrarray = list2; expect(t.pptrarray).toBe(list2); }).pend('https://gitlab.gnome.org/GNOME/gjs/-/issues/83'); xit('Hash table with old-style annotation', function () { expect(t.hashTableOld).toBe(hashTable); t.hashTableOld = hashTable2; expect(t.hashTableOld).toBe(hashTable2); }).pend('https://gitlab.gnome.org/GNOME/gjs/-/issues/83'); xit('List with old-style annotation', function () { expect(t.listOld).toBe(list); t.listOld = list2; expect(t.listOld).toBe(list2); }).pend('https://gitlab.gnome.org/GNOME/gjs/-/issues/83'); it('Integer', function () { expect(t.int).toBe(int); t.int = 35; expect(t.int).toBe(35); }); it('Float', function () { expect(t.float).toBeCloseTo(double); t.float = double2; expect(t.float).toBeCloseTo(double2); }); it('Double', function () { expect(t.double).toBeCloseTo(double); t.double = double2; expect(t.double).toBeCloseTo(double2); }); it('String', function () { expect(t.string).toBe(string); t.string = 'string2'; expect(t.string).toBe('string2'); }); xit('GType object', function () { expect(t.gtype).toBe(gtype); const gtype2 = GObject.InitiallyUnowned.$gtype; t.gtype = gtype2; expect(t.gtype).toBe(gtype2); }).pend('https://gitlab.gnome.org/GNOME/gjs/-/issues/83'); xit('Byte array', function () { expect(t.byteArray).toBe(byteArray); const byteArray2 = Uint8Array.from('efgh', c => c.charCodeAt(0)); t.byteArray = byteArray2; expect(t.byteArray).toBe(byteArray2); }).pend('https://gitlab.gnome.org/GNOME/gjs/-/issues/276'); }); describe('Object-valued GProperty', function () { let o1, t1, t2; beforeEach(function () { o1 = new GObject.Object(); t1 = new Regress.TestObj({bare: o1}); t2 = new Regress.TestSubObj(); t2.bare = o1; }); it('marshals correctly in the getter', function () { expect(t1.bare).toBe(o1); }); it('marshals correctly when inherited', function () { expect(t2.bare).toBe(o1); }); it('marshals into setter function', function () { const o2 = new GObject.Object(); t2.set_bare(o2); expect(t2.bare).toBe(o2); }); it('marshals null', function () { t2.unset_bare(); expect(t2.bare).toBeNull(); }); }); describe('Signal connection', function () { it('calls correct handlers with correct arguments', function () { const handler = jasmine.createSpy('handler'); const handlerId = o.connect('test', handler); handler.and.callFake(() => o.disconnect(handlerId)); o.emit('test'); expect(handler).toHaveBeenCalledTimes(1); expect(handler).toHaveBeenCalledWith(o); handler.calls.reset(); o.emit('test'); expect(handler).not.toHaveBeenCalled(); }); it('throws errors for invalid signals', function () { expect(() => o.connect('invalid-signal', () => {})).toThrow(); expect(() => o.emit('invalid-signal')).toThrow(); }); it('signal handler with static scope arg gets arg passed by reference', function () { const b = new Regress.TestSimpleBoxedA({ some_int: 42, some_int8: 43, some_double: 42.5, some_enum: Regress.TestEnum.VALUE3, }); o.connect('test-with-static-scope-arg', (signalObject, signalArg) => { signalArg.some_int = 44; }); o.emit('test-with-static-scope-arg', b); expect(b.some_int).toEqual(44); }); it('signal with object gets correct arguments', function (done) { o.connect('sig-with-obj', (self, objectParam) => { expect(objectParam.int).toEqual(3); done(); }); o.emit_sig_with_obj(); }); it('signal with object with gets correct arguments from JS', function (done) { o.connect('sig-with-obj', (self, objectParam) => { expect(objectParam.int).toEqual(33); done(); }); const testObj = new Regress.TestObj({int: 33}); o.emit('sig-with-obj', testObj); }); it('signal with object with full transport gets correct arguments', function (done) { o.connect('sig-with-obj-full', (self, objectParam) => { expect(objectParam.int).toEqual(5); done(); }); o.emit_sig_with_obj_full(); }); it('signal with object with full transport gets correct arguments from JS', function (done) { o.connect('sig-with-obj-full', (self, objectParam) => { expect(objectParam.int).toEqual(55); done(); }); const testObj = new Regress.TestObj({int: 55}); o.emit('sig-with-obj-full', testObj); }); // See testCairo.js for a test of // Regress.TestObj::sig-with-foreign-struct. xit('signal with int64 gets correct value', function (done) { o.connect('sig-with-int64-prop', (self, number) => { expect(number).toEqual(GLib.MAXINT64); done(); return GLib.MAXINT64; }); o.emit_sig_with_int64(); }).pend('https://gitlab.gnome.org/GNOME/gjs/issues/271'); xit('signal with uint64 gets correct value', function (done) { o.connect('sig-with-uint64-prop', (self, number) => { expect(number).toEqual(GLib.MAXUINT64); done(); return GLib.MAXUINT64; }); o.emit_sig_with_uint64(); }).pend('https://gitlab.gnome.org/GNOME/gjs/issues/271'); xit('signal with array parameter is properly handled', function (done) { o.connect('sig-with-array-prop', (signalObj, signalArray, shouldBeUndefined) => { expect(signalObj).toBe(o); expect(shouldBeUndefined).not.toBeDefined(); expect(signalArray).toEqual([0, 1, 2, 3, 4, 5]); done(); }); o.emit('sig-with-array-prop', [0, 1, 2, 3, 4, 5]); }).pend('Not yet implemented'); xit('signal with hash parameter is properly handled', function (done) { o.connect('sig-with-hash-prop', (signalObj, signalArray, shouldBeUndefined) => { expect(signalObj).toBe(o); expect(shouldBeUndefined).not.toBeDefined(); expect(signalArray).toEqual([0, 1, 2, 3, 4, 5]); done(); }); o.emit('sig-with-hash-prop', {'0': 1}); }).pend('Not yet implemented'); it('signal with array len parameter is not passed correct array and no length arg', function (done) { o.connect('sig-with-array-len-prop', (signalObj, signalArray, shouldBeUndefined) => { expect(shouldBeUndefined).not.toBeDefined(); expect(signalArray).toEqual([0, 1, 2, 3, 4]); done(); }); o.emit_sig_with_array_len_prop(); }); it('signal with GStrv parameter is properly handled', function (done) { o.connect('sig-with-strv', (signalObj, signalArray, shouldBeUndefined) => { expect(signalObj).toBe(o); expect(shouldBeUndefined).not.toBeDefined(); expect(signalArray).toEqual(['a', 'bb', 'ccc']); done(); }); o.emit('sig-with-strv', ['a', 'bb', 'ccc']); }); it('signal with GStrv parameter and transfer full is properly handled from JS', function (done) { o.connect('sig-with-strv-full', (signalObj, signalArray, shouldBeUndefined) => { expect(signalObj).toBe(o); expect(shouldBeUndefined).not.toBeDefined(); expect(signalArray).toEqual(['a', 'bb', 'ccc']); done(); }); o.emit('sig-with-strv-full', ['a', 'bb', 'ccc']); }); xit('signal with GStrv parameter and transfer full is properly handled', function (done) { o.connect('sig-with-strv-full', (signalObj, signalArray, shouldBeUndefined) => { expect(signalObj).toBe(o); expect(shouldBeUndefined).not.toBeDefined(); expect(signalArray).toEqual(['foo', 'bar', 'baz']); done(); }); o.emit_sig_with_gstrv_full(); }).pend('https://gitlab.gnome.org/GNOME/gobject-introspection/-/issues/470'); xit('signal with int array ret parameter is properly handled', function (done) { o.connect('sig-with-intarray-ret', (signalObj, signalInt, shouldBeUndefined) => { expect(signalObj).toBe(o); expect(shouldBeUndefined).not.toBeDefined(); expect(signalInt).toEqual(5); const ret = []; for (let i = 0; i < signalInt; ++i) ret.push(i); done(); }); expect(o.emit('sig-with-intarray-ret', 5)).toBe([0, 1, 2, 3, 4]); }).pend('Not yet implemented'); xit('can pass parameter to signal with array len parameter via emit', function (done) { o.connect('sig-with-array-len-prop', (signalObj, signalArray) => { expect(signalArray).toEqual([0, 1, 2, 3, 4]); done(); }); o.emit('sig-with-array-len-prop', [0, 1, 2, 3, 4]); }).pend('Not yet implemented'); xit('can pass null to signal with array len parameter', function () { const handler = jasmine.createSpy('handler'); o.connect('sig-with-array-len-prop', handler); o.emit('sig-with-array-len-prop', null); expect(handler).toHaveBeenCalledWith([jasmine.any(Object), null]); }).pend('Not yet implemented'); xit('signal with int in-out parameter', function () { const handler = jasmine.createSpy('handler').and.callFake(() => 43); o.connect('sig-with-inout-int', handler); o.emit_sig_with_inout_int(); expect(handler.toHaveBeenCalledWith([jasmine.any(Object), 42])); }).pend('Not yet implemented'); it('GError signal with GError set', function (done) { o.connect('sig-with-gerror', (obj, e) => { expect(e).toEqual(jasmine.any(Gio.IOErrorEnum)); expect(e.domain).toEqual(Gio.io_error_quark()); expect(e.code).toEqual(Gio.IOErrorEnum.FAILED); done(); }); o.emit_sig_with_error(); }); it('GError signal with no GError set', function (done) { o.connect('sig-with-gerror', (obj, e) => { expect(e).toBeNull(); done(); }); o.emit_sig_with_null_error(); }); it('GError signal with no GError set from js', function (done) { o.connect('sig-with-gerror', (obj, e) => { expect(e).toBeNull(); done(); }); o.emit('sig-with-gerror', null); }); it('GError signal with no GError set from js', function (done) { o.connect('sig-with-gerror', (obj, e) => { expect(e).toEqual(jasmine.any(Gio.IOErrorEnum)); expect(e.domain).toEqual(Gio.io_error_quark()); expect(e.code).toEqual(Gio.IOErrorEnum.EXISTS); done(); }); o.emit('sig-with-gerror', new GLib.Error(Gio.IOErrorEnum, Gio.IOErrorEnum.EXISTS, 'We support this!')); }); }); it('can call an instance method', function () { expect(o.instance_method()).toEqual(-1); }); it('can call a transfer-full instance method', function () { expect(() => o.instance_method_full()).not.toThrow(); }); it('can call a static method', function () { expect(Regress.TestObj.static_method(5)).toEqual(5); }); it('can call a method annotated with (method)', function () { expect(() => o.forced_method()).not.toThrow(); }); describe('Object torture signature', function () { it('0', function () { const [y, z, q] = o.torture_signature_0(42, 'foo', 7); expect(Math.floor(y)).toEqual(42); expect(z).toEqual(84); expect(q).toEqual(10); }); it('1 fail', function () { expect(() => o.torture_signature_1(42, 'foo', 7)).toThrow(); }); it('1 success', function () { const [, y, z, q] = o.torture_signature_1(11, 'barbaz', 8); expect(Math.floor(y)).toEqual(11); expect(z).toEqual(22); expect(q).toEqual(14); }); }); describe('Introspected function length', function () { it('skips over instance parameters of methods', function () { expect(o.set_bare.length).toEqual(1); }); it('skips over out and GError parameters', function () { expect(o.torture_signature_1.length).toEqual(3); }); it('does not skip over inout parameters', function () { expect(o.skip_return_val.length).toEqual(5); }); xit('skips over return value annotated with skip', function () { const [b, d, sum] = o.skip_return_val(1, 2, 3, 4, 5); expect(b).toEqual(2); expect(d).toEqual(4); expect(sum).toEqual(54); const retval = o.skip_return_val_no_out(1); expect(retval).not.toBeDefined(); }).pend('https://gitlab.gnome.org/GNOME/gjs/issues/59'); xit('skips over parameters annotated with skip', function () { expect(o.skip_param.length).toEqual(4); const [success, b, d, sum] = o.skip_param(1, 2, 3, 4); expect(success).toBeTruthy(); expect(b).toEqual(2); expect(d).toEqual(3); expect(sum).toEqual(43); }).pend('https://gitlab.gnome.org/GNOME/gjs/issues/59'); xit('skips over out parameters annotated with skip', function () { const [success, d, sum] = o.skip_out_param(1, 2, 3, 4, 5); expect(success).toBeTruthy(); expect(d).toEqual(4); expect(sum).toEqual(54); }).pend('https://gitlab.gnome.org/GNOME/gjs/issues/59'); xit('skips over inout parameters annotated with skip', function () { expect(o.skip_inout_param.length).toEqual(4); const [success, b, sum] = o.skip_inout_param(1, 2, 3, 4); expect(success).toBeTruthy(); expect(b).toEqual(2); expect(sum).toEqual(43); }).pend('https://gitlab.gnome.org/GNOME/gjs/issues/59'); it('gives number of arguments for static methods', function () { expect(Regress.TestObj.new_from_file.length).toEqual(1); }); it('skips over destroy-notify and user-data parameters', function () { expect(Regress.TestObj.new_callback.length).toEqual(1); }); }); it('virtual function', function () { expect(o.do_matrix('meaningless string')).toEqual(42); }); describe('wrong type for GObject', function () { let wrongObject, wrongBoxed, subclassObject; beforeEach(function () { wrongObject = new Gio.SimpleAction(); wrongBoxed = new GLib.KeyFile(); subclassObject = new Regress.TestSubObj(); }); // Regress.func_obj_null_in expects a Regress.TestObj it('function does not accept a GObject of the wrong type', function () { expect(() => Regress.func_obj_null_in(wrongObject)).toThrow(); }); it('function does not accept a GBoxed instead of GObject', function () { expect(() => Regress.func_obj_null_in(wrongBoxed)).toThrow(); }); it('function does not accept returned GObject of the wrong type', function () { const wrongReturnedObject = Gio.File.new_for_path('/'); expect(() => Regress.func_obj_null_in(wrongReturnedObject)).toThrow(); }); it('function accepts GObject of subclass of expected type', function () { expect(() => Regress.func_obj_null_in(subclassObject)).not.toThrow(); }); it('method cannot be called on a GObject of the wrong type', function () { expect(() => Regress.TestObj.prototype.instance_method.call(wrongObject)) .toThrow(); }); it('method cannot be called on a GBoxed', function () { expect(() => Regress.TestObj.prototype.instance_method.call(wrongBoxed)) .toThrow(); }); it('method can be called on a GObject of subclass of expected type', function () { expect(() => Regress.TestObj.prototype.instance_method.call(subclassObject)) .not.toThrow(); }); }); it('marshals a null object in', function () { expect(() => Regress.func_obj_null_in(null)).not.toThrow(); expect(() => Regress.func_obj_nullable_in(null)).not.toThrow(); }); it('marshals a null object out', function () { expect(Regress.TestObj.null_out()).toBeNull(); }); it('marshals a gpointer with a type annotation in', function () { const o2 = new GObject.Object(); expect(() => o.not_nullable_typed_gpointer_in(o2)).not.toThrow(); }); it('marshals a gpointer with an element-type annotation in', function () { expect(() => o.not_nullable_element_typed_gpointer_in([1, 2])).not.toThrow(); }); // This test is not meant to be normative; a GObject behaving like this is // doing something unsupported. However, we have been handling this so far // in a certain way, and we don't want to break user code because of badly // behaved libraries. This test ensures that any change to the behaviour // must be intentional. it('resolves properties when they are shadowed by methods', function () { expect(o.name_conflict).toEqual(42); expect(o.name_conflict).not.toEqual(jasmine.any(Function)); }); }); it('marshals a fixed-size array of objects out', function () { expect(Regress.test_array_fixed_out_objects()).toEqual([ jasmine.any(Regress.TestObj), jasmine.any(Regress.TestObj), ]); }); describe('Inherited GObject', function () { let subobj; beforeEach(function () { subobj = new Regress.TestSubObj({ int: 42, float: Math.PI, double: Math.E, boolean: true, }); }); it('can read fields from a parent class', function () { // see "can access fields with simple types" above expect(subobj.some_int8).toEqual(subobj.int); expect(subobj.some_float).toEqual(subobj.float); expect(subobj.some_double).toEqual(subobj.double); }); it('can be constructed from a static constructor', function () { expect(Regress.TestSubObj.new).not.toThrow(); }); it('can call an instance method that overrides the parent class', function () { expect(subobj.instance_method()).toEqual(0); }); it('can have its own properties', function () { expect(subobj.boolean).toBeTruthy(); subobj.boolean = false; expect(subobj.boolean).toBeFalsy(); }); }); describe('Overridden properties on interfaces', function () { it('set and get properly', function () { const o = new Regress.TestSubObj(); o.number = 4; expect(o.number).toEqual(4); }); it('default properly', function () { const o = new Regress.TestSubObj(); expect(o.number).toBeDefined(); expect(o.number).toEqual(0); }); it('construct properly', function () { const o = new Regress.TestSubObj({number: 4}); expect(o.number).toEqual(4); }); }); describe('Fundamental type', function () { it('constructs a subtype of a fundamental type', function () { expect(() => new Regress.TestFundamentalSubObject('plop')).not.toThrow(); }); it('constructs a subtype of a hidden (no introspection data) fundamental type', function () { expect(() => Regress.test_create_fundamental_hidden_class_instance()).not.toThrow(); }); }); it('callbacks', function () { const callback = jasmine.createSpy('callback').and.returnValue(42); expect(Regress.test_callback(callback)).toEqual(42); }); it('null / undefined callback', function () { expect(Regress.test_callback(null)).toEqual(0); expect(() => Regress.test_callback(undefined)).toThrow(); }); it('callback called more than once', function () { const callback = jasmine.createSpy('callback').and.returnValue(21); expect(Regress.test_multi_callback(callback)).toEqual(42); expect(callback).toHaveBeenCalledTimes(2); }); it('null callback called more than once', function () { expect(Regress.test_multi_callback(null)).toEqual(0); }); it('array callbacks', function () { const callback = jasmine.createSpy('callback').and.returnValue(7); expect(Regress.test_array_callback(callback)).toEqual(14); expect(callback).toHaveBeenCalledWith([-1, 0, 1, 2], ['one', 'two', 'three']); }); it('null array callback', function () { expect(() => Regress.test_array_callback(null)).toThrow(); }); xit('callback with inout array', function () { const callback = jasmine.createSpy('callback').and.callFake(arr => arr.slice(1)); expect(Regress.test_array_inout_callback(callback)).toEqual(3); expect(callback).toHaveBeenCalledWith([-2, -1, 0, 1, 2], [-1, 0, 1, 2]); }); // assertion failed, "Use gjs_value_from_explicit_array() for arrays with length param"" ['simple', 'noptr'].forEach(type => { it(`${type} callback`, function () { const callback = jasmine.createSpy('callback'); Regress[`test_${type}_callback`](callback); expect(callback).toHaveBeenCalled(); }); it(`null ${type} callback`, function () { expect(() => Regress[`test_${type}_callback`](null)).not.toThrow(); }); }); it('gobject-introspected function as callback parameter', function () { const expected = GLib.get_num_processors(); expect(Regress.test_callback(GLib.get_num_processors)).toEqual(expected); }); it('callback with user data', function () { const callback = jasmine.createSpy('callback').and.returnValue(7); expect(Regress.test_callback_user_data(callback)).toEqual(7); expect(callback).toHaveBeenCalled(); }); it('callback with transfer-full return value', function () { const callback = jasmine.createSpy('callback') .and.returnValue(Regress.TestObj.new_from_file('/enoent')); Regress.test_callback_return_full(callback); expect(callback).toHaveBeenCalled(); }); it('callback with destroy-notify', function () { const callback1 = jasmine.createSpy('callback').and.returnValue(42); const callback2 = jasmine.createSpy('callback').and.returnValue(58); expect(Regress.test_callback_destroy_notify(callback1)).toEqual(42); expect(callback1).toHaveBeenCalledTimes(1); expect(Regress.test_callback_destroy_notify(callback2)).toEqual(58); expect(callback2).toHaveBeenCalledTimes(1); expect(Regress.test_callback_thaw_notifications()).toEqual(100); expect(callback1).toHaveBeenCalledTimes(2); expect(callback2).toHaveBeenCalledTimes(2); }); xit('callback with destroy-notify and no user data', function () { const callback1 = jasmine.createSpy('callback').and.returnValue(42); const callback2 = jasmine.createSpy('callback').and.returnValue(58); expect(Regress.test_callback_destroy_notify_no_user_data(callback1)).toEqual(42); expect(callback1).toHaveBeenCalledTimes(1); expect(Regress.test_callback_destroy_notify_no_user_data(callback2)).toEqual(58); expect(callback2).toHaveBeenCalledTimes(1); expect(Regress.test_callback_thaw_notifications()).toEqual(100); expect(callback1).toHaveBeenCalledTimes(2); expect(callback2).toHaveBeenCalledTimes(2); }).pend('Callback with destroy-notify and no user data not currently supported'); // If this is ever supported, then replace it with the above test. it('callback with destroy-notify and no user data throws error', function () { // should throw when called, not when the function object is created expect(() => Regress.test_callback_destroy_notify_no_user_data).not.toThrow(); expect(() => Regress.test_callback_destroy_notify_no_user_data(() => {})) .toThrowError(/no user data/); }); it('async callback', function () { Regress.test_callback_async(() => 44); expect(Regress.test_callback_thaw_async()).toEqual(44); }); it('Gio.AsyncReadyCallback', function (done) { Regress.test_async_ready_callback((obj, res) => { expect(obj).toBeNull(); expect(res).toEqual(jasmine.any(Gio.SimpleAsyncResult)); done(); }); }); it('instance method taking a callback', function () { const o = new Regress.TestObj(); const callback = jasmine.createSpy('callback'); o.instance_method_callback(callback); expect(callback).toHaveBeenCalled(); }); it('static method taking a callback', function () { const callback = jasmine.createSpy('callback'); Regress.TestObj.static_method_callback(callback); expect(callback).toHaveBeenCalled(); }); it('constructor taking a callback', function () { const callback = jasmine.createSpy('callback').and.returnValue(42); void Regress.TestObj.new_callback(callback); expect(callback).toHaveBeenCalled(); expect(Regress.test_callback_thaw_notifications()).toEqual(42); expect(callback).toHaveBeenCalledTimes(2); }); it('hash table passed to callback', function () { const hashtable = { a: 1, b: 2, c: 3, }; const callback = jasmine.createSpy('callback'); Regress.test_hash_table_callback(hashtable, callback); expect(callback).toHaveBeenCalledWith(hashtable); }); it('GError callback', function (done) { Regress.test_gerror_callback(e => { expect(e).toEqual(jasmine.any(Gio.IOErrorEnum)); expect(e.domain).toEqual(Gio.io_error_quark()); expect(e.code).toEqual(Gio.IOErrorEnum.NOT_SUPPORTED); done(); }); }); it('null GError callback', function () { const callback = jasmine.createSpy('callback'); Regress.test_null_gerror_callback(callback); expect(callback).toHaveBeenCalledWith(null); }); it('owned GError callback', function (done) { Regress.test_owned_gerror_callback(e => { expect(e).toEqual(jasmine.any(Gio.IOErrorEnum)); expect(e.domain).toEqual(Gio.io_error_quark()); expect(e.code).toEqual(Gio.IOErrorEnum.PERMISSION_DENIED); done(); }); }); describe('Introspected interface', function () { const Implementor = GObject.registerClass({ Implements: [Regress.TestInterface], Properties: { number: GObject.ParamSpec.override('number', Regress.TestInterface), }, }, class Implementor extends GObject.Object { get number() { return 5; } }); it('correctly emits interface signals', function () { const obj = new Implementor(); const handler = jasmine.createSpy('handler').and.callFake(() => {}); obj.connect('interface-signal', handler); obj.emit_signal(); expect(handler).toHaveBeenCalled(); }); }); describe('GObject with nonstandard prefix', function () { let o; beforeEach(function () { o = new Regress.TestWi8021x(); }); it('sets and gets properties', function () { expect(o.testbool).toBeTruthy(); o.testbool = false; expect(o.testbool).toBeFalsy(); }); it('constructs via a static constructor', function () { expect(Regress.TestWi8021x.new()).toEqual(jasmine.any(Regress.TestWi8021x)); }); it('calls methods', function () { expect(o.get_testbool()).toBeTruthy(); o.set_testbool(false); expect(o.get_testbool()).toBeFalsy(); }); it('calls a static method', function () { expect(Regress.TestWi8021x.static_method(21)).toEqual(42); }); }); describe('GObject.InitiallyUnowned', function () { it('constructs', function () { expect(new Regress.TestFloating()).toEqual(jasmine.any(Regress.TestFloating)); }); it('constructs via a static constructor', function () { expect(Regress.TestFloating.new()).toEqual(jasmine.any(Regress.TestFloating)); }); }); it('torture signature 0', function () { const [y, z, q] = Regress.test_torture_signature_0(42, 'foo', 7); expect(Math.floor(y)).toEqual(42); expect(z).toEqual(84); expect(q).toEqual(10); }); it('torture signature 1 fail', function () { expect(() => Regress.test_torture_signature_1(42, 'foo', 7)).toThrow(); }); it('torture signature 1 success', function () { const [, y, z, q] = Regress.test_torture_signature_1(11, 'barbaz', 8); expect(Math.floor(y)).toEqual(11); expect(z).toEqual(22); expect(q).toEqual(14); }); it('torture signature 2', function () { const [y, z, q] = Regress.test_torture_signature_2(42, () => 0, 'foo', 7); expect(Math.floor(y)).toEqual(42); expect(z).toEqual(84); expect(q).toEqual(10); }); describe('GValue boxing and unboxing', function () { it('date in', function () { const date = Regress.test_date_in_gvalue(); expect(date.get_year()).toEqual(1984); expect(date.get_month()).toEqual(GLib.DateMonth.DECEMBER); expect(date.get_day()).toEqual(5); }); it('strv in', function () { expect(Regress.test_strv_in_gvalue()).toEqual(['one', 'two', 'three']); }); it('correctly converts a NULL strv in a GValue to an empty array', function () { expect(Regress.test_null_strv_in_gvalue()).toEqual([]); }); }); it("code coverage for documentation tests that don't do anything", function () { expect(() => { Regress.test_multiline_doc_comments(); Regress.test_nested_parameter(5); Regress.test_versioning(); }).not.toThrow(); }); it('marshals an aliased type', function () { // GLib.PtrArray is not introspectable, so neither is an alias of it // Regress.introspectable_via_alias(new GLib.PtrArray()); expect(Regress.aliased_caller_alloc()).toEqual(jasmine.any(Regress.TestBoxed)); }); it('deals with a fixed-size array in a struct', function () { const struct = new Regress.TestStructFixedArray(); struct.frob(); expect(struct.just_int).toEqual(7); expect(struct.array).toEqual([42, 43, 44, 45, 46, 47, 48, 49, 50, 51]); }); it('marshals a fixed-size int array as a gpointer', function () { expect(() => Regress.has_parameter_named_attrs(0, Array(32).fill(42))).not.toThrow(); }); it('deals with a fixed-size and also zero-terminated array in a struct', function () { const x = new Regress.LikeXklConfigItem(); x.set_name('foo'); expect(x.name).toEqual([...'foo'].map(c => c.codePointAt()).concat(Array(29).fill(0))); x.set_name('*'.repeat(33)); expect(x.name).toEqual(Array(31).fill('*'.codePointAt()).concat([0])); }); it('marshals a transfer-floating GLib.Variant', function () { expect(Regress.get_variant().unpack()).toEqual(42); }); describe('Flat array of structs', function () { it('out parameter with transfer none', function () { const expected = [111, 222, 333].map(some_int => jasmine.objectContaining({some_int})); expect(Regress.test_array_struct_out_none()).toEqual(expected); }); it('out parameter with transfer container', function () { const expected = [11, 13, 17, 19, 23].map(some_int => jasmine.objectContaining({some_int})); expect(Regress.test_array_struct_out_container()).toEqual(expected); }); it('out parameter with transfer full', function () { const expected = [2, 3, 5, 7].map(some_int => jasmine.objectContaining({some_int})); expect(Regress.test_array_struct_out_full_fixed()).toEqual(expected); }); xit('caller-allocated out parameter', function () { // With caller-allocated array in, there's no way to supply the // length. This happens in GLib.MainContext.query() expect(Regress.test_array_struct_out_caller_alloc()).toEqual([]); }).pend('Not supported'); it('transfer-full in parameter', function () { const array = [201, 202].map(some_int => new Regress.TestStructA({some_int})); expect(() => Regress.test_array_struct_in_full(array)).not.toThrow(); }); it('transfer-none in parameter', function () { const array = [301, 302, 303].map(some_int => new Regress.TestStructA({some_int})); expect(() => Regress.test_array_struct_in_none(array)).not.toThrow(); }); }); }); cjs-128.0/installed-tests/js/testGObjectValue.js0000664000175000017500000001607214771557763020566 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2020 Marco Trevisan const {GLib, GObject, GIMarshallingTests, Regress} = imports.gi; const SIGNED_TYPES = ['schar', 'int', 'int64', 'long']; const UNSIGNED_TYPES = ['char', 'uchar', 'uint', 'uint64', 'ulong']; const FLOATING_TYPES = ['double', 'float']; const NUMERIC_TYPES = [...SIGNED_TYPES, ...UNSIGNED_TYPES, ...FLOATING_TYPES]; const SPECIFIC_TYPES = ['gtype', 'boolean', 'string', 'param', 'variant', 'boxed', 'gvalue']; const INSTANCED_TYPES = ['object', 'instance']; const ALL_TYPES = [...NUMERIC_TYPES, ...SPECIFIC_TYPES, ...INSTANCED_TYPES]; describe('GObject value (GValue)', function () { let v; beforeEach(function () { v = new GObject.Value(); }); function getDefaultContentByType(type) { if (SIGNED_TYPES.includes(type)) return -((Math.random() * 100 | 0) + 1); if (UNSIGNED_TYPES.includes(type)) return -getDefaultContentByType('int') + 2; if (FLOATING_TYPES.includes(type)) return getDefaultContentByType('uint') + 0.5; if (type === 'string') return `Hello GValue! ${getDefaultContentByType('uint')}`; if (type === 'boolean') return !!(getDefaultContentByType('int') % 2); if (type === 'gtype') return getGType(ALL_TYPES[Math.random() * ALL_TYPES.length | 0]); if (type === 'boxed' || type === 'boxed-struct') { return new GIMarshallingTests.BoxedStruct({ long_: getDefaultContentByType('long'), // string_: getDefaultContentByType('string'), not supported }); } if (type === 'object') { const wasCreatingObject = this.creatingObject; this.creatingObject = true; const props = ALL_TYPES.filter(e => (e !== 'object' || !wasCreatingObject) && e !== 'boxed' && e !== 'gtype' && e !== 'instance' && e !== 'param' && e !== 'schar').concat([ 'boxed-struct', ]).reduce((ac, a) => ({ ...ac, [`some-${a}`]: getDefaultContentByType(a), }), {}); delete this.creatingObject; return new GIMarshallingTests.PropertiesObject(props); } if (type === 'param') { return GObject.ParamSpec.string('test-param', '', getDefaultContentByType('string'), GObject.ParamFlags.READABLE, ''); } if (type === 'variant') { return new GLib.Variant('a{sv}', { pasta: new GLib.Variant('s', 'Carbonara (con guanciale)'), pizza: new GLib.Variant('s', 'Verace'), randomString: new GLib.Variant('s', getDefaultContentByType('string')), }); } if (type === 'gvalue') { const value = new GObject.Value(); const valueType = NUMERIC_TYPES[Math.random() * NUMERIC_TYPES.length | 0]; value.init(getGType(valueType)); setContent(value, valueType, getDefaultContentByType(valueType)); return value; } if (type === 'instance') return new Regress.TestFundamentalSubObject(getDefaultContentByType('string')); throw new Error(`No default content set for type ${type}`); } function getGType(type) { if (type === 'schar') return GObject.TYPE_CHAR; if (type === 'boxed' || type === 'gvalue' || type === 'instance') return getDefaultContentByType(type).constructor.$gtype; return GObject[`TYPE_${type.toUpperCase()}`]; } function getContent(gvalue, type) { if (type === 'gvalue') type = 'boxed'; if (type === 'instance') return GIMarshallingTests.gvalue_round_trip(gvalue); return gvalue[`get_${type}`](); } function setContent(gvalue, type, content) { if (type === 'gvalue') type = 'boxed'; if (type === 'instance') pending('https://gitlab.gnome.org/GNOME/gjs/-/issues/402'); return gvalue[`set_${type}`](content); } function skipUnsupported(type) { if (type === 'boxed') pending('https://gitlab.gnome.org/GNOME/gjs/-/issues/402'); if (type === 'gvalue') pending('https://gitlab.gnome.org/GNOME/gjs/-/issues/272'); } ALL_TYPES.forEach(type => { const gtype = getGType(type); it(`initializes ${type}`, function () { v.init(gtype); }); it(`${type} is compatible with itself`, function () { expect(GObject.Value.type_compatible(gtype, gtype)).toBeTruthy(); }); it(`${type} is transformable to itself`, function () { expect(GObject.Value.type_transformable(gtype, gtype)).toBeTruthy(); }); describe('initialized', function () { let randomContent; beforeEach(function () { v.init(gtype); randomContent = getDefaultContentByType(type); }); it(`sets and gets ${type}`, function () { skipUnsupported(type); setContent(v, type, randomContent); expect(getContent(v, type)).toEqual(randomContent); }); it(`can be passed to a function and returns a ${type}`, function () { skipUnsupported(type); setContent(v, type, randomContent); expect(GIMarshallingTests.gvalue_round_trip(v)).toEqual(randomContent); expect(GIMarshallingTests.gvalue_copy(v)).toEqual(randomContent); }); it(`copies ${type}`, function () { skipUnsupported(type); setContent(v, type, randomContent); const other = new GObject.Value(); other.init(gtype); v.copy(other); expect(getContent(other, type)).toEqual(randomContent); }); }); it(`can be marshalled and un-marshalled from JS ${type}`, function () { if (['gtype', 'gvalue'].includes(type)) pending('Not supported - always implicitly converted'); const content = getDefaultContentByType(type); expect(GIMarshallingTests.gvalue_round_trip(content)).toEqual(content); }); }); ['int', 'uint', 'boolean', 'gtype', ...FLOATING_TYPES].forEach(type => { it(`can be marshalled and un-marshalled from JS gtype of ${type}`, function () { const gtype = getGType(type); expect(GIMarshallingTests.gvalue_round_trip(gtype).constructor.$gtype).toEqual(gtype); }); }); INSTANCED_TYPES.forEach(type => { it(`initializes from instance of ${type}`, function () { skipUnsupported(type); const instance = getDefaultContentByType(type); v.init_from_instance(instance); expect(getContent(v, type)).toEqual(instance); }); }); afterEach(function () { v.unset(); }); }); cjs-128.0/installed-tests/js/complex4.ui0000664000175000017500000000213214771557763017100 0ustar fabiofabio cjs-128.0/installed-tests/js/testParamSpec.js0000664000175000017500000000407314771557763020125 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2011 Red Hat, Inc. const Regress = imports.gi.Regress; const GObject = imports.gi.GObject; let name = 'foo-property'; let nick = 'Foo property'; let blurb = 'This is the foo property'; let flags = GObject.ParamFlags.READABLE; function testParamSpec(type, params, defaultValue) { describe(`GObject.ParamSpec.${type}`, function () { let paramSpec; beforeEach(function () { paramSpec = GObject.ParamSpec[type](name, nick, blurb, flags, ...params); }); it('has the correct name strings', function () { expect(paramSpec.name).toEqual(name); expect(paramSpec._nick).toEqual(nick); expect(paramSpec._blurb).toEqual(blurb); }); it('has the correct flags', function () { expect(paramSpec.flags).toEqual(flags); }); it('has the correct default value', function () { expect(paramSpec.default_value).toEqual(defaultValue); }); }); } testParamSpec('string', ['Default Value'], 'Default Value'); testParamSpec('int', [-100, 100, -42], -42); testParamSpec('uint', [20, 100, 42], 42); testParamSpec('int64', [0x4000, 0xffffffff, 0x2266bbff], 0x2266bbff); testParamSpec('uint64', [0, 0xffffffff, 0x2266bbff], 0x2266bbff); testParamSpec('enum', [Regress.TestEnum, Regress.TestEnum.VALUE2], Regress.TestEnum.VALUE2); testParamSpec('flags', [Regress.TestFlags, Regress.TestFlags.FLAG2], Regress.TestFlags.FLAG2); testParamSpec('object', [GObject.Object], null); testParamSpec('jsobject', [], null); describe('GObject.ParamSpec object', function () { it("doesn't crash when resolving a non-string property", function () { let paramSpec = GObject.ParamSpec.string(name, nick, blurb, flags, ''); expect(paramSpec[0]).not.toBeDefined(); }); it('has correct object tag', function () { const paramSpec = GObject.ParamSpec.string(name, nick, blurb, flags, ''); expect(paramSpec.toString()).toEqual('[object GObject_ParamSpec]'); }); }); cjs-128.0/installed-tests/js/testLegacyGObject.js0000664000175000017500000007332614771557763020723 0ustar fabiofabio// -*- mode: js; indent-tabs-mode: nil -*- /* eslint-disable no-restricted-properties */ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2011 Giovanni Campagna // SPDX-FileCopyrightText: 2015 Endless Mobile, Inc. imports.gi.versions.Gtk = '3.0'; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const Gtk = imports.gi.Gtk; const Lang = imports.lang; const Mainloop = imports.mainloop; const MyObject = new GObject.Class({ Name: 'MyObject', Properties: { 'readwrite': GObject.ParamSpec.string('readwrite', 'ParamReadwrite', 'A read write parameter', GObject.ParamFlags.READWRITE, ''), 'readonly': GObject.ParamSpec.string('readonly', 'ParamReadonly', 'A readonly parameter', GObject.ParamFlags.READABLE, ''), 'construct': GObject.ParamSpec.string('construct', 'ParamConstructOnly', 'A readwrite construct-only parameter', GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, 'default'), }, Signals: { 'empty': { }, 'minimal': {param_types: [GObject.TYPE_INT, GObject.TYPE_INT]}, 'full': { flags: GObject.SignalFlags.RUN_LAST, accumulator: GObject.AccumulatorType.FIRST_WINS, return_type: GObject.TYPE_INT, param_types: [], }, 'run-last': {flags: GObject.SignalFlags.RUN_LAST}, 'detailed': { flags: GObject.SignalFlags.RUN_FIRST | GObject.SignalFlags.DETAILED, param_types: [GObject.TYPE_STRING], }, }, _init(props) { // check that it's safe to set properties before // chaining up (priv is NULL at this point, remember) this._readwrite = 'foo'; this._readonly = 'bar'; this._constructProp = null; this._constructCalled = false; this.parent(props); }, get readwrite() { return this._readwrite; }, set readwrite(val) { if (val === 'ignore') return; this._readwrite = val; }, get readonly() { return this._readonly; }, set readonly(val) { // this should never be called this._readonly = 'bogus'; }, get construct() { return this._constructProp; }, set construct(val) { this._constructProp = val; }, notify_prop() { this._readonly = 'changed'; this.notify('readonly'); }, emit_empty() { this.emit('empty'); }, emit_minimal(one, two) { this.emit('minimal', one, two); }, emit_full() { return this.emit('full'); }, emit_detailed() { this.emit('detailed::one'); this.emit('detailed::two'); }, emit_run_last(callback) { this._run_last_callback = callback; this.emit('run-last'); }, on_run_last() { this._run_last_callback(); }, on_empty() { this.empty_called = true; }, on_full() { this.full_default_handler_called = true; return 79; }, }); const MyApplication = new Lang.Class({ Name: 'MyApplication', Extends: Gio.Application, Signals: {'custom': {param_types: [GObject.TYPE_INT]}}, _init(params) { this.parent(params); }, emit_custom(n) { this.emit('custom', n); }, }); const MyInitable = new Lang.Class({ Name: 'MyInitable', Extends: GObject.Object, Implements: [Gio.Initable], _init(params) { this.parent(params); this.inited = false; }, vfunc_init(cancellable) { // error? if (!(cancellable instanceof Gio.Cancellable)) throw new Error('Bad argument'); this.inited = true; }, }); const Derived = new Lang.Class({ Name: 'Derived', Extends: MyObject, _init() { this.parent({readwrite: 'yes'}); }, }); const OddlyNamed = new Lang.Class({ Name: 'Legacy.OddlyNamed', Extends: MyObject, }); const MyCustomInit = new Lang.Class({ Name: 'MyCustomInit', Extends: GObject.Object, _init() { this.foo = false; this.parent(); }, _instance_init() { this.foo = true; }, }); describe('GObject class', function () { let myInstance; beforeEach(function () { myInstance = new MyObject(); }); it('constructs with default values for properties', function () { expect(myInstance.readwrite).toEqual('foo'); expect(myInstance.readonly).toEqual('bar'); expect(myInstance.construct).toEqual('default'); }); it('constructs with a hash of property values', function () { let myInstance2 = new MyObject({readwrite: 'baz', construct: 'asdf'}); expect(myInstance2.readwrite).toEqual('baz'); expect(myInstance2.readonly).toEqual('bar'); expect(myInstance2.construct).toEqual('asdf'); }); const ui = ` baz quz `; it('constructs with property values from Gtk.Builder', function () { let builder = Gtk.Builder.new_from_string(ui, -1); let myInstance3 = builder.get_object('MyObject'); expect(myInstance3.readwrite).toEqual('baz'); expect(myInstance3.readonly).toEqual('bar'); expect(myInstance3.construct).toEqual('quz'); }); it('does not allow changing CONSTRUCT_ONLY properties', function () { myInstance.construct = 'val'; expect(myInstance.construct).toEqual('default'); }); it('has a name', function () { expect(MyObject.name).toEqual('MyObject'); }); // the following would (should) cause a CRITICAL: // myInstance.readonly = 'val'; it('has a notify signal', function () { let notifySpy = jasmine.createSpy('notifySpy'); myInstance.connect('notify::readonly', notifySpy); myInstance.notify_prop(); myInstance.notify_prop(); expect(notifySpy).toHaveBeenCalledTimes(2); }); it('can define its own signals', function () { let emptySpy = jasmine.createSpy('emptySpy'); myInstance.connect('empty', emptySpy); myInstance.emit_empty(); expect(emptySpy).toHaveBeenCalled(); expect(myInstance.empty_called).toBeTruthy(); }); it('passes emitted arguments to signal handlers', function () { let minimalSpy = jasmine.createSpy('minimalSpy'); myInstance.connect('minimal', minimalSpy); myInstance.emit_minimal(7, 5); expect(minimalSpy).toHaveBeenCalledWith(myInstance, 7, 5); }); it('can return values from signals', function () { let fullSpy = jasmine.createSpy('fullSpy').and.returnValue(42); myInstance.connect('full', fullSpy); let result = myInstance.emit_full(); expect(fullSpy).toHaveBeenCalled(); expect(result).toEqual(42); }); it('does not call first-wins signal handlers after one returns a value', function () { let neverCalledSpy = jasmine.createSpy('neverCalledSpy'); myInstance.connect('full', () => 42); myInstance.connect('full', neverCalledSpy); myInstance.emit_full(); expect(neverCalledSpy).not.toHaveBeenCalled(); expect(myInstance.full_default_handler_called).toBeFalsy(); }); it('gets the return value of the default handler', function () { let result = myInstance.emit_full(); expect(myInstance.full_default_handler_called).toBeTruthy(); expect(result).toEqual(79); }); it('calls run-last default handler last', function () { let stack = []; let runLastSpy = jasmine.createSpy('runLastSpy') .and.callFake(() => { stack.push(1); }); myInstance.connect('run-last', runLastSpy); myInstance.emit_run_last(() => { stack.push(2); }); expect(stack).toEqual([1, 2]); }); it("can inherit from something that's not GObject.Object", function () { // ...and still get all the goodies of GObject.Class let instance = new MyApplication({application_id: 'org.gjs.Application'}); let customSpy = jasmine.createSpy('customSpy'); instance.connect('custom', customSpy); instance.emit_custom(73); expect(customSpy).toHaveBeenCalledWith(instance, 73); }); it('can implement an interface', function () { let instance = new MyInitable(); expect(instance.constructor.implements(Gio.Initable)).toBeTruthy(); }); it('can implement interface vfuncs', function () { let instance = new MyInitable(); expect(instance.inited).toBeFalsy(); instance.init(new Gio.Cancellable()); expect(instance.inited).toBeTruthy(); }); it('can be a subclass', function () { let derived = new Derived(); expect(derived instanceof Derived).toBeTruthy(); expect(derived instanceof MyObject).toBeTruthy(); expect(derived.readwrite).toEqual('yes'); }); it('can have any valid Lang.Class name', function () { let obj = new OddlyNamed(); expect(obj instanceof OddlyNamed).toBeTruthy(); expect(obj instanceof MyObject).toBeTruthy(); }); it('calls its _instance_init() function while chaining up in constructor', function () { let instance = new MyCustomInit(); expect(instance.foo).toBeTruthy(); }); it('can have an interface-valued property', function () { const InterfacePropObject = new Lang.Class({ Name: 'InterfacePropObject', Extends: GObject.Object, Properties: { 'file': GObject.ParamSpec.object('file', 'File', 'File', GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, Gio.File.$gtype), }, }); let file = Gio.File.new_for_path('dummy'); expect(() => new InterfacePropObject({file})).not.toThrow(); }); it('can override a property from the parent class', function () { const OverrideObject = new Lang.Class({ Name: 'OverrideObject', Extends: MyObject, Properties: { 'readwrite': GObject.ParamSpec.override('readwrite', MyObject), }, get readwrite() { return this._subclass_readwrite; }, set readwrite(val) { this._subclass_readwrite = `subclass${val}`; }, }); let obj = new OverrideObject(); obj.readwrite = 'foo'; expect(obj.readwrite).toEqual('subclassfoo'); }); it('cannot override a non-existent property', function () { expect(() => new Lang.Class({ Name: 'BadOverride', Extends: GObject.Object, Properties: { 'nonexistent': GObject.ParamSpec.override('nonexistent', GObject.Object), }, })).toThrow(); }); it('handles gracefully forgetting to override a C property', function () { GLib.test_expect_message('GLib-GObject', GLib.LogLevelFlags.LEVEL_CRITICAL, "*Object class Gjs_ForgottenOverride doesn't implement property " + "'anchors' from interface 'GTlsFileDatabase'*"); // This is a random interface in Gio with a read-write property const ForgottenOverride = new Lang.Class({ Name: 'ForgottenOverride', Extends: Gio.TlsDatabase, Implements: [Gio.TlsFileDatabase], }); const obj = new ForgottenOverride(); expect(obj.anchors).not.toBeDefined(); expect(() => (obj.anchors = 'foo')).not.toThrow(); expect(obj.anchors).toEqual('foo'); GLib.test_assert_expected_messages_internal('Cjs', 'testGObjectClass.js', 0, 'testGObjectClassForgottenOverride'); }); it('handles gracefully overriding a C property but forgetting the accessors', function () { // This is a random interface in Gio with a read-write property const ForgottenAccessors = new Lang.Class({ Name: 'ForgottenAccessors', Extends: Gio.TlsDatabase, Implements: [Gio.TlsFileDatabase], Properties: { 'anchors': GObject.ParamSpec.override('anchors', Gio.TlsFileDatabase), }, }); const obj = new ForgottenAccessors(); expect(obj.anchors).toBeNull(); obj.anchors = 'foo'; const ForgottenAccessors2 = new Lang.Class({ Name: 'ForgottenAccessors2', Extends: ForgottenAccessors, }); const obj2 = new ForgottenAccessors2(); expect(obj2.anchors).toBeNull(); obj2.anchors = 'foo'; }); }); const AnInterface = new Lang.Interface({ Name: 'AnInterface', }); const GObjectImplementingLangInterface = new Lang.Class({ Name: 'GObjectImplementingLangInterface', Extends: GObject.Object, Implements: [AnInterface], }); const AGObjectInterface = new Lang.Interface({ Name: 'AGObjectInterface', GTypeName: 'ArbitraryGTypeName', Requires: [GObject.Object], Properties: { 'interface-prop': GObject.ParamSpec.string('interface-prop', 'Interface property', 'Must be overridden in implementation', GObject.ParamFlags.READABLE, 'foobar'), }, Signals: { 'interface-signal': {}, }, requiredG: Lang.Interface.UNIMPLEMENTED, optionalG() { return 'AGObjectInterface.optionalG()'; }, }); const InterfaceRequiringGObjectInterface = new Lang.Interface({ Name: 'InterfaceRequiringGObjectInterface', Requires: [AGObjectInterface], optionalG() { return `InterfaceRequiringGObjectInterface.optionalG()\n${ AGObjectInterface.optionalG(this)}`; }, }); const GObjectImplementingGObjectInterface = new Lang.Class({ Name: 'GObjectImplementingGObjectInterface', Extends: GObject.Object, Implements: [AGObjectInterface], Properties: { 'interface-prop': GObject.ParamSpec.override('interface-prop', AGObjectInterface), 'class-prop': GObject.ParamSpec.string('class-prop', 'Class property', 'A property that is not on the interface', GObject.ParamFlags.READABLE, 'meh'), }, Signals: { 'class-signal': {}, }, get interface_prop() { return 'foobar'; }, get class_prop() { return 'meh'; }, requiredG() {}, optionalG() { return AGObjectInterface.optionalG(this); }, }); const MinimalImplementationOfAGObjectInterface = new Lang.Class({ Name: 'MinimalImplementationOfAGObjectInterface', Extends: GObject.Object, Implements: [AGObjectInterface], Properties: { 'interface-prop': GObject.ParamSpec.override('interface-prop', AGObjectInterface), }, requiredG() {}, }); const ImplementationOfTwoInterfaces = new Lang.Class({ Name: 'ImplementationOfTwoInterfaces', Extends: GObject.Object, Implements: [AGObjectInterface, InterfaceRequiringGObjectInterface], Properties: { 'interface-prop': GObject.ParamSpec.override('interface-prop', AGObjectInterface), }, requiredG() {}, optionalG() { return InterfaceRequiringGObjectInterface.optionalG(this); }, }); describe('GObject interface', function () { it('class can implement a Lang.Interface', function () { let obj; expect(() => { obj = new GObjectImplementingLangInterface(); }).not.toThrow(); expect(obj.constructor.implements(AnInterface)).toBeTruthy(); }); it('throws when an interface requires a GObject interface but not GObject.Object', function () { expect(() => new Lang.Interface({ Name: 'GObjectInterfaceNotRequiringGObject', GTypeName: 'GTypeNameNotRequiringGObject', Requires: [Gio.Initable], })).toThrow(); }); it('can be implemented by a GObject class along with a JS interface', function () { const ObjectImplementingLangInterfaceAndCInterface = new Lang.Class({ Name: 'ObjectImplementingLangInterfaceAndCInterface', Extends: GObject.Object, Implements: [AnInterface, Gio.Initable], }); let obj; expect(() => { obj = new ObjectImplementingLangInterfaceAndCInterface(); }).not.toThrow(); expect(obj.constructor.implements(AnInterface)).toBeTruthy(); expect(obj.constructor.implements(Gio.Initable)).toBeTruthy(); }); it('is an instance of the interface classes', function () { expect(AGObjectInterface instanceof Lang.Interface).toBeTruthy(); expect(AGObjectInterface instanceof GObject.Interface).toBeTruthy(); }); it('cannot be instantiated', function () { expect(() => new AGObjectInterface()).toThrow(); }); it('has a name', function () { expect(AGObjectInterface.name).toEqual('AGObjectInterface'); }); it('reports its type name', function () { expect(AGObjectInterface.$gtype.name).toEqual('ArbitraryGTypeName'); }); it('can be implemented by a GObject class', function () { let obj; expect(() => { obj = new GObjectImplementingGObjectInterface(); }).not.toThrow(); expect(obj.constructor.implements(AGObjectInterface)).toBeTruthy(); }); it('is implemented by a GObject class with the correct class object', function () { let obj = new GObjectImplementingGObjectInterface(); expect(obj.constructor).toEqual(GObjectImplementingGObjectInterface); expect(obj.constructor.name) .toEqual('GObjectImplementingGObjectInterface'); }); it('can be implemented by a class also implementing a Lang.Interface', function () { const GObjectImplementingBothKindsOfInterface = new Lang.Class({ Name: 'GObjectImplementingBothKindsOfInterface', Extends: GObject.Object, Implements: [AnInterface, AGObjectInterface], Properties: { 'interface-prop': GObject.ParamSpec.override('interface-prop', AGObjectInterface), }, required() {}, requiredG() {}, }); let obj; expect(() => { obj = new GObjectImplementingBothKindsOfInterface(); }).not.toThrow(); expect(obj.constructor.implements(AnInterface)).toBeTruthy(); expect(obj.constructor.implements(AGObjectInterface)).toBeTruthy(); }); it('can have its required function implemented', function () { expect(() => { let obj = new GObjectImplementingGObjectInterface(); obj.requiredG(); }).not.toThrow(); }); it('must have its required function implemented', function () { expect(() => new Lang.Class({ Name: 'BadObject', Extends: GObject.Object, Implements: [AGObjectInterface], Properties: { 'interface-prop': GObject.ParamSpec.override('interface-prop', AGObjectInterface), }, })).toThrow(); }); it("doesn't have to have its optional function implemented", function () { let obj; expect(() => { obj = new MinimalImplementationOfAGObjectInterface(); }).not.toThrow(); expect(obj.constructor.implements(AGObjectInterface)).toBeTruthy(); }); it('can have its optional function deferred to by the implementation', function () { let obj = new MinimalImplementationOfAGObjectInterface(); expect(obj.optionalG()).toEqual('AGObjectInterface.optionalG()'); }); it('can have its function chained up to', function () { let obj = new GObjectImplementingGObjectInterface(); expect(obj.optionalG()).toEqual('AGObjectInterface.optionalG()'); }); it('can require another interface', function () { let obj; expect(() => { obj = new ImplementationOfTwoInterfaces(); }).not.toThrow(); expect(obj.constructor.implements(AGObjectInterface)).toBeTruthy(); expect(obj.constructor.implements(InterfaceRequiringGObjectInterface)) .toBeTruthy(); }); it('can chain up to another interface', function () { let obj = new ImplementationOfTwoInterfaces(); expect(obj.optionalG()) .toEqual('InterfaceRequiringGObjectInterface.optionalG()\nAGObjectInterface.optionalG()'); }); it("defers to the last interface's optional function", function () { const MinimalImplementationOfTwoInterfaces = new Lang.Class({ Name: 'MinimalImplementationOfTwoInterfaces', Extends: GObject.Object, Implements: [AGObjectInterface, InterfaceRequiringGObjectInterface], Properties: { 'interface-prop': GObject.ParamSpec.override('interface-prop', AGObjectInterface), }, requiredG() {}, }); let obj = new MinimalImplementationOfTwoInterfaces(); expect(obj.optionalG()) .toEqual('InterfaceRequiringGObjectInterface.optionalG()\nAGObjectInterface.optionalG()'); }); it('must be implemented by a class that implements all required interfaces', function () { expect(() => new Lang.Class({ Name: 'BadObject', Implements: [InterfaceRequiringGObjectInterface], required() {}, })).toThrow(); }); it('must be implemented by a class that implements required interfaces in correct order', function () { expect(() => new Lang.Class({ Name: 'BadObject', Implements: [InterfaceRequiringGObjectInterface, AGObjectInterface], required() {}, })).toThrow(); }); it('can require an interface from C', function () { const InitableInterface = new Lang.Interface({ Name: 'InitableInterface', Requires: [GObject.Object, Gio.Initable], }); expect(() => new Lang.Class({ Name: 'BadObject', Implements: [InitableInterface], })).toThrow(); }); it('can define signals on the implementing class', function () { function quitLoop() { Mainloop.quit('signal'); } let obj = new GObjectImplementingGObjectInterface(); let interfaceSignalSpy = jasmine.createSpy('interfaceSignalSpy') .and.callFake(quitLoop); let classSignalSpy = jasmine.createSpy('classSignalSpy') .and.callFake(quitLoop); obj.connect('interface-signal', interfaceSignalSpy); obj.connect('class-signal', classSignalSpy); GLib.idle_add(GLib.PRIORITY_DEFAULT, () => { obj.emit('interface-signal'); return GLib.SOURCE_REMOVE; }); Mainloop.run('signal'); GLib.idle_add(GLib.PRIORITY_DEFAULT, () => { obj.emit('class-signal'); return GLib.SOURCE_REMOVE; }); Mainloop.run('signal'); expect(interfaceSignalSpy).toHaveBeenCalled(); expect(classSignalSpy).toHaveBeenCalled(); }); it('can define properties on the implementing class', function () { let obj = new GObjectImplementingGObjectInterface(); expect(obj.interface_prop).toEqual('foobar'); expect(obj.class_prop).toEqual('meh'); }); it('must have its properties overridden', function () { // Failing to override an interface property doesn't raise an error but // instead logs a critical warning. GLib.test_expect_message('GLib-GObject', GLib.LogLevelFlags.LEVEL_CRITICAL, "Object class * doesn't implement property 'interface-prop' from " + "interface 'ArbitraryGTypeName'"); new Lang.Class({ Name: 'MyNaughtyObject', Extends: GObject.Object, Implements: [AGObjectInterface], requiredG() {}, }); // g_test_assert_expected_messages() is a macro, not introspectable GLib.test_assert_expected_messages_internal('Cjs', 'testGObjectInterface.js', 416, 'testGObjectMustOverrideInterfaceProperties'); }); // This makes sure that we catch the case where the metaclass (e.g. // GtkWidgetClass) doesn't specify a meta-interface. In that case we get the // meta-interface from the metaclass's parent. it('gets the correct type for its metaclass', function () { const MyMeta = new Lang.Class({ Name: 'MyMeta', Extends: GObject.Class, }); const MyMetaObject = new MyMeta({ Name: 'MyMetaObject', }); const MyMetaInterface = new Lang.Interface({ Name: 'MyMetaInterface', Requires: [MyMetaObject], }); expect(MyMetaInterface instanceof GObject.Interface).toBeTruthy(); }); it('can be implemented by a class as well as its parent class', function () { const SubObject = new Lang.Class({ Name: 'SubObject', Extends: GObjectImplementingGObjectInterface, }); let obj = new SubObject(); expect(obj.constructor.implements(AGObjectInterface)).toBeTruthy(); expect(obj.interface_prop).toEqual('foobar'); // override not needed }); it('can be reimplemented by a subclass of a class that already implements it', function () { const SubImplementer = new Lang.Class({ Name: 'SubImplementer', Extends: GObjectImplementingGObjectInterface, Implements: [AGObjectInterface], }); let obj = new SubImplementer(); expect(obj.constructor.implements(AGObjectInterface)).toBeTruthy(); expect(obj.interface_prop).toEqual('foobar'); // override not needed }); }); const LegacyInterface1 = new Lang.Interface({ Name: 'LegacyInterface1', Requires: [GObject.Object], Signals: {'legacy-iface1-signal': {}}, }); const LegacyInterface2 = new Lang.Interface({ Name: 'LegacyInterface2', Requires: [GObject.Object], Signals: {'legacy-iface2-signal': {}}, }); const Legacy = new Lang.Class({ Name: 'Legacy', Extends: GObject.Object, Implements: [LegacyInterface1], Properties: { 'property': GObject.ParamSpec.int('property', 'Property', 'A magic property', GObject.ParamFlags.READWRITE, 0, 100, 0), 'override-property': GObject.ParamSpec.int('override-property', 'Override property', 'Another magic property', GObject.ParamFlags.READWRITE, 0, 100, 0), }, Signals: { 'signal': {}, }, _init(someval) { this.constructorCalledWith = someval; this.parent(); }, instanceMethod() {}, chainUpToMe() {}, overrideMe() {}, get property() { return this._property + 1; }, set property(value) { this._property = value - 2; }, get overrideProperty() { return this._overrideProperty + 1; }, set overrideProperty(value) { this._overrideProperty = value - 2; }, }); Legacy.staticMethod = function () {}; const Shiny = GObject.registerClass({ Implements: [LegacyInterface2], Properties: { 'override-property': GObject.ParamSpec.override('override-property', Legacy), }, }, class Shiny extends Legacy { chainUpToMe() { super.chainUpToMe(); } overrideMe() {} get overrideProperty() { return this._overrideProperty + 2; } set overrideProperty(value) { this._overrideProperty = value - 1; } }); describe('ES6 GObject class inheriting from GObject.Class', function () { let instance; beforeEach(function () { spyOn(Legacy, 'staticMethod'); spyOn(Legacy.prototype, 'instanceMethod'); spyOn(Legacy.prototype, 'chainUpToMe'); spyOn(Legacy.prototype, 'overrideMe'); instance = new Shiny(); }); it('calls a static method on the parent class', function () { Shiny.staticMethod(); expect(Legacy.staticMethod).toHaveBeenCalled(); }); it('calls a method on the parent class', function () { instance.instanceMethod(); expect(Legacy.prototype.instanceMethod).toHaveBeenCalled(); }); it("passes arguments to the parent class's constructor", function () { instance = new Shiny(42); expect(instance.constructorCalledWith).toEqual(42); }); it('chains up to a method on the parent class', function () { instance.chainUpToMe(); expect(Legacy.prototype.chainUpToMe).toHaveBeenCalled(); }); it('overrides a method on the parent class', function () { instance.overrideMe(); expect(Legacy.prototype.overrideMe).not.toHaveBeenCalled(); }); it('sets and gets a property from the parent class', function () { instance.property = 42; expect(instance.property).toEqual(41); }); it('overrides a property from the parent class', function () { instance.overrideProperty = 42; expect(instance.overrideProperty).toEqual(43); }); it('inherits a signal from the parent class', function () { let signalSpy = jasmine.createSpy('signalSpy'); expect(() => { instance.connect('signal', signalSpy); instance.emit('signal'); }).not.toThrow(); expect(signalSpy).toHaveBeenCalled(); }); it('inherits legacy interfaces from the parent', function () { expect(() => instance.emit('legacy-iface1-signal')).not.toThrow(); expect(instance instanceof LegacyInterface1).toBeTruthy(); }); it('can implement a legacy interface itself', function () { expect(() => instance.emit('legacy-iface2-signal')).not.toThrow(); expect(instance instanceof LegacyInterface2).toBeTruthy(); }); }); cjs-128.0/installed-tests/js/testLegacyClass.js0000664000175000017500000005557014771557763020454 0ustar fabiofabio// -*- mode: js; indent-tabs-mode: nil -*- /* eslint-disable no-restricted-properties */ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2011 Jasper St. Pierre // SPDX-FileCopyrightText: 2011 Giovanni Campagna // SPDX-FileCopyrightText: 2015 Endless Mobile, Inc. const Lang = imports.lang; const NormalClass = new Lang.Class({ Name: 'NormalClass', _init() { this.one = 1; }, }); let Subclassed = []; const MetaClass = new Lang.Class({ Name: 'MetaClass', Extends: Lang.Class, _init(params) { Subclassed.push(params.Name); this.parent(params); if (params.Extended) { this.prototype.dynamic_method = this.wrapFunction('dynamic_method', function () { return 73; }); this.DYNAMIC_CONSTANT = 2; } }, }); const CustomMetaOne = new MetaClass({ Name: 'CustomMetaOne', Extends: NormalClass, Extended: false, _init() { this.parent(); this.two = 2; }, }); const CustomMetaTwo = new MetaClass({ Name: 'CustomMetaTwo', Extends: NormalClass, Extended: true, _init() { this.parent(); this.two = 2; }, }); // This should inherit CustomMeta, even though // we use Lang.Class const CustomMetaSubclass = new Lang.Class({ Name: 'CustomMetaSubclass', Extends: CustomMetaOne, Extended: true, _init() { this.parent(); this.three = 3; }, }); describe('A metaclass', function () { it('has its constructor called each time a class is created with it', function () { expect(Subclassed).toEqual(['CustomMetaOne', 'CustomMetaTwo', 'CustomMetaSubclass']); }); it('is an instance of Lang.Class', function () { expect(NormalClass instanceof Lang.Class).toBeTruthy(); expect(MetaClass instanceof Lang.Class).toBeTruthy(); }); it('produces instances that are instances of itself and Lang.Class', function () { expect(CustomMetaOne instanceof Lang.Class).toBeTruthy(); expect(CustomMetaOne instanceof MetaClass).toBeTruthy(); }); it('can dynamically define properties in its constructor', function () { expect(CustomMetaTwo.DYNAMIC_CONSTANT).toEqual(2); expect(CustomMetaOne.DYNAMIC_CONSTANT).not.toBeDefined(); }); describe('instance', function () { let instanceOne, instanceTwo; beforeEach(function () { instanceOne = new CustomMetaOne(); instanceTwo = new CustomMetaTwo(); }); it('gets all the properties from its class and metaclass', function () { expect(instanceOne).toEqual(jasmine.objectContaining({one: 1, two: 2})); expect(instanceTwo).toEqual(jasmine.objectContaining({one: 1, two: 2})); }); it('gets dynamically defined properties from metaclass', function () { expect(() => instanceOne.dynamic_method()).toThrow(); expect(instanceTwo.dynamic_method()).toEqual(73); }); }); it('can be instantiated with Lang.Class but still get the appropriate metaclass', function () { expect(CustomMetaSubclass instanceof MetaClass).toBeTruthy(); expect(CustomMetaSubclass.DYNAMIC_CONSTANT).toEqual(2); let instance = new CustomMetaSubclass(); expect(instance).toEqual(jasmine.objectContaining({one: 1, two: 2, three: 3})); expect(instance.dynamic_method()).toEqual(73); }); it('can be detected with Lang.getMetaClass', function () { expect(Lang.getMetaClass({ Extends: CustomMetaOne, })).toBe(MetaClass); }); }); const MagicBase = new Lang.Class({ Name: 'MagicBase', _init(a, buffer) { if (buffer) buffer.push(a); this.a = a; }, foo(a, buffer) { buffer.push(a); return a * 3; }, bar(a) { return a * 5; }, }); const Magic = new Lang.Class({ Name: 'Magic', Extends: MagicBase, _init(a, b, buffer) { this.parent(a, buffer); if (buffer) buffer.push(b); this.b = b; }, foo(a, b, buffer) { let val = this.parent(a, buffer); buffer.push(b); return val * 2; }, bar(a, buffer) { this.foo(a, 2 * a, buffer); return this.parent(a); }, }); const Accessor = new Lang.Class({ Name: 'AccessorMagic', _init(val) { this._val = val; }, get value() { return this._val; }, set value(val) { if (val !== 42) throw TypeError('Value is not a magic number'); this._val = val; }, }); const AbstractBase = new Lang.Class({ Name: 'AbstractBase', Abstract: true, _init() { this.foo = 42; }, }); describe('Class framework', function () { it('calls _init constructors', function () { let newMagic = new MagicBase('A'); expect(newMagic.a).toEqual('A'); }); it('calls parent constructors', function () { let buffer = []; let newMagic = new Magic('a', 'b', buffer); expect(buffer).toEqual(['a', 'b']); buffer = []; let val = newMagic.foo(10, 20, buffer); expect(buffer).toEqual([10, 20]); expect(val).toEqual(10 * 6); }); it('sets the right constructor properties', function () { expect(Magic.prototype.constructor).toBe(Magic); let newMagic = new Magic(); expect(newMagic.constructor).toBe(Magic); }); it('sets up instanceof correctly', function () { let newMagic = new Magic(); expect(newMagic instanceof Magic).toBeTruthy(); expect(newMagic instanceof MagicBase).toBeTruthy(); }); it('has a name', function () { expect(Magic.name).toEqual('Magic'); }); it('reports a sensible value for toString()', function () { let newMagic = new MagicBase(); expect(newMagic.toString()).toEqual('[object MagicBase]'); }); it('allows overriding toString()', function () { const ToStringOverride = new Lang.Class({ Name: 'ToStringOverride', toString() { let oldToString = this.parent(); return `${oldToString}; hello`; }, }); let override = new ToStringOverride(); expect(override.toString()).toEqual('[object ToStringOverride]; hello'); }); it('is not configurable', function () { let newMagic = new MagicBase(); delete newMagic.foo; expect(newMagic.foo).toBeDefined(); }); it('allows accessors for properties', function () { let newAccessor = new Accessor(11); expect(newAccessor.value).toEqual(11); expect(() => (newAccessor.value = 12)).toThrow(); newAccessor.value = 42; expect(newAccessor.value).toEqual(42); }); it('raises an exception when creating an abstract class', function () { expect(() => new AbstractBase()).toThrow(); }); it('inherits properties from abstract base classes', function () { const AbstractImpl = new Lang.Class({ Name: 'AbstractImpl', Extends: AbstractBase, _init() { this.parent(); this.bar = 42; }, }); let newAbstract = new AbstractImpl(); expect(newAbstract.foo).toEqual(42); expect(newAbstract.bar).toEqual(42); }); it('inherits constructors from abstract base classes', function () { const AbstractImpl = new Lang.Class({ Name: 'AbstractImpl', Extends: AbstractBase, }); let newAbstract = new AbstractImpl(); expect(newAbstract.foo).toEqual(42); }); it('allows ES6 classes to inherit from abstract base classes', function () { class AbstractImpl extends AbstractBase {} let newAbstract = new AbstractImpl(); expect(newAbstract.foo).toEqual(42); }); it('lets methods call other methods without clobbering __caller__', function () { let newMagic = new Magic(); let buffer = []; let res = newMagic.bar(10, buffer); expect(buffer).toEqual([10, 20]); expect(res).toEqual(50); }); it('allows custom return values from constructors', function () { const CustomConstruct = new Lang.Class({ Name: 'CustomConstruct', _construct(one, two) { return [one, two]; }, }); let instance = new CustomConstruct(1, 2); expect(Array.isArray(instance)).toBeTruthy(); expect(instance instanceof CustomConstruct).toBeFalsy(); expect(instance).toEqual([1, 2]); }); it('allows symbol-named methods', function () { const SymbolClass = new Lang.Class({ Name: 'SymbolClass', *[Symbol.iterator]() { yield* [1, 2, 3]; }, }); let instance = new SymbolClass(); expect([...instance]).toEqual([1, 2, 3]); }); }); const AnInterface = new Lang.Interface({ Name: 'AnInterface', required: Lang.Interface.UNIMPLEMENTED, optional() { return 'AnInterface.optional()'; }, optionalGeneric() { return 'AnInterface.optionalGeneric()'; }, argumentGeneric(arg) { return `AnInterface.argumentGeneric(${arg})`; }, usesThis() { return this._interfacePrivateMethod(); }, _interfacePrivateMethod() { return 'interface private method'; }, get some_prop() { return 'AnInterface.some_prop getter'; }, set some_prop(value) { this.some_prop_setter_called = true; }, }); const InterfaceRequiringOtherInterface = new Lang.Interface({ Name: 'InterfaceRequiringOtherInterface', Requires: [AnInterface], optional(...args) { return `InterfaceRequiringOtherInterface.optional()\n${ AnInterface.prototype.optional.apply(this, args)}`; }, optionalGeneric() { return `InterfaceRequiringOtherInterface.optionalGeneric()\n${ AnInterface.optionalGeneric(this)}`; }, }); const ObjectImplementingAnInterface = new Lang.Class({ Name: 'ObjectImplementingAnInterface', Implements: [AnInterface], _init() { this.parent(); }, required() {}, optional(...args) { return AnInterface.prototype.optional.apply(this, args); }, optionalGeneric() { return AnInterface.optionalGeneric(this); }, argumentGeneric(arg) { return AnInterface.argumentGeneric(this, `${arg} (hello from class)`); }, }); const InterfaceRequiringClassAndInterface = new Lang.Interface({ Name: 'InterfaceRequiringClassAndInterface', Requires: [ObjectImplementingAnInterface, InterfaceRequiringOtherInterface], }); const MinimalImplementationOfAnInterface = new Lang.Class({ Name: 'MinimalImplementationOfAnInterface', Implements: [AnInterface], required() {}, }); const ImplementationOfTwoInterfaces = new Lang.Class({ Name: 'ImplementationOfTwoInterfaces', Implements: [AnInterface, InterfaceRequiringOtherInterface], required() {}, optional(...args) { return InterfaceRequiringOtherInterface.prototype.optional.apply(this, args); }, optionalGeneric() { return InterfaceRequiringOtherInterface.optionalGeneric(this); }, }); describe('An interface', function () { it('is an instance of Lang.Interface', function () { expect(AnInterface instanceof Lang.Interface).toBeTruthy(); expect(InterfaceRequiringOtherInterface instanceof Lang.Interface).toBeTruthy(); }); it('has a name', function () { expect(AnInterface.name).toEqual('AnInterface'); }); it('cannot be instantiated', function () { expect(() => new AnInterface()).toThrow(); }); it('can be implemented by a class', function () { let obj; expect(() => { obj = new ObjectImplementingAnInterface(); }).not.toThrow(); expect(obj.constructor.implements(AnInterface)).toBeTruthy(); }); it("can be implemented by a class's superclass", function () { const ChildWhoseParentImplementsAnInterface = new Lang.Class({ Name: 'ChildWhoseParentImplementsAnInterface', Extends: ObjectImplementingAnInterface, }); let obj = new ChildWhoseParentImplementsAnInterface(); expect(obj.constructor.implements(AnInterface)).toBeTruthy(); }); it("doesn't disturb a class's constructor", function () { let obj = new ObjectImplementingAnInterface(); expect(obj.constructor).toEqual(ObjectImplementingAnInterface); }); it('can have its required method implemented', function () { let implementer = new ObjectImplementingAnInterface(); expect(() => implementer.required()).not.toThrow(); }); it('must have a name', function () { expect(() => new Lang.Interface({ required: Lang.Interface.UNIMPLEMENTED, })).toThrow(); }); it('must have its required methods implemented', function () { expect(() => new Lang.Class({ Name: 'MyBadObject', Implements: [AnInterface], })).toThrow(); }); it('does not have to have its optional methods implemented', function () { let obj; expect(() => (obj = new MinimalImplementationOfAnInterface())).not.toThrow(); expect(obj.constructor.implements(AnInterface)).toBeTruthy(); }); it('can have its optional method deferred to by the implementation', function () { let obj = new MinimalImplementationOfAnInterface(); expect(obj.optional()).toEqual('AnInterface.optional()'); }); it('can be chained up to by a class', function () { let obj = new ObjectImplementingAnInterface(); expect(obj.optional()).toEqual('AnInterface.optional()'); }); it('can include arguments when being chained up to by a class', function () { let obj = new ObjectImplementingAnInterface(); expect(obj.argumentGeneric('arg')) .toEqual('AnInterface.argumentGeneric(arg (hello from class))'); }); it('can have its property getter deferred to', function () { let obj = new ObjectImplementingAnInterface(); expect(obj.some_prop).toEqual('AnInterface.some_prop getter'); }); it('can have its property setter deferred to', function () { let obj = new ObjectImplementingAnInterface(); obj.some_prop = 'foobar'; expect(obj.some_prop_setter_called).toBeTruthy(); }); it('can have its property getter overridden', function () { const ObjectWithGetter = new Lang.Class({ Name: 'ObjectWithGetter', Implements: [AnInterface], required() {}, get some_prop() { return 'ObjectWithGetter.some_prop getter'; }, }); let obj = new ObjectWithGetter(); expect(obj.some_prop).toEqual('ObjectWithGetter.some_prop getter'); }); it('can have its property setter overridden', function () { const ObjectWithSetter = new Lang.Class({ Name: 'ObjectWithSetter', Implements: [AnInterface], required() {}, set some_prop(value) { /* setter without getter */// jshint ignore:line this.overridden_some_prop_setter_called = true; }, }); let obj = new ObjectWithSetter(); obj.some_prop = 'foobar'; expect(obj.overridden_some_prop_setter_called).toBeTruthy(); expect(obj.some_prop_setter_called).not.toBeDefined(); }); it('can require another interface', function () { let obj; expect(() => { obj = new ImplementationOfTwoInterfaces(); }).not.toThrow(); expect(obj.constructor.implements(AnInterface)).toBeTruthy(); expect(obj.constructor.implements(InterfaceRequiringOtherInterface)).toBeTruthy(); }); it('can have empty requires', function () { expect(() => new Lang.Interface({ Name: 'InterfaceWithEmptyRequires', Requires: [], })).not.toThrow(); }); it('can chain up to another interface', function () { let obj = new ImplementationOfTwoInterfaces(); expect(obj.optional()) .toEqual('InterfaceRequiringOtherInterface.optional()\nAnInterface.optional()'); }); it('can be chained up to with a generic', function () { let obj = new ObjectImplementingAnInterface(); expect(obj.optionalGeneric()).toEqual('AnInterface.optionalGeneric()'); }); it('can chain up to another interface with a generic', function () { let obj = new ImplementationOfTwoInterfaces(); expect(obj.optionalGeneric()) .toEqual('InterfaceRequiringOtherInterface.optionalGeneric()\nAnInterface.optionalGeneric()'); }); it('has its optional function defer to that of the last interface', function () { const MinimalImplementationOfTwoInterfaces = new Lang.Class({ Name: 'MinimalImplementationOfTwoInterfaces', Implements: [AnInterface, InterfaceRequiringOtherInterface], required() {}, }); let obj = new MinimalImplementationOfTwoInterfaces(); expect(obj.optionalGeneric()) .toEqual('InterfaceRequiringOtherInterface.optionalGeneric()\nAnInterface.optionalGeneric()'); }); it('must have all its required interfaces implemented', function () { expect(() => new Lang.Class({ Name: 'ObjectWithNotEnoughInterfaces', Implements: [InterfaceRequiringOtherInterface], required() {}, })).toThrow(); }); it('must have all its required interfaces implemented in the correct order', function () { expect(() => new Lang.Class({ Name: 'ObjectWithInterfacesInWrongOrder', Implements: [InterfaceRequiringOtherInterface, AnInterface], required() {}, })).toThrow(); }); it('can have its implementation on a parent class', function () { let obj; expect(() => { const ObjectInheritingFromInterfaceImplementation = new Lang.Class({ Name: 'ObjectInheritingFromInterfaceImplementation', Extends: ObjectImplementingAnInterface, Implements: [InterfaceRequiringOtherInterface], }); obj = new ObjectInheritingFromInterfaceImplementation(); }).not.toThrow(); expect(obj.constructor.implements(AnInterface)).toBeTruthy(); expect(obj.constructor.implements(InterfaceRequiringOtherInterface)).toBeTruthy(); }); it('can require its implementor to be a subclass of some class', function () { let obj; expect(() => { const ObjectImplementingInterfaceRequiringParentObject = new Lang.Class({ Name: 'ObjectImplementingInterfaceRequiringParentObject', Extends: ObjectImplementingAnInterface, Implements: [InterfaceRequiringOtherInterface, InterfaceRequiringClassAndInterface], }); obj = new ObjectImplementingInterfaceRequiringParentObject(); }).not.toThrow(); expect(obj.constructor.implements(AnInterface)).toBeTruthy(); expect(obj.constructor.implements(InterfaceRequiringOtherInterface)).toBeTruthy(); expect(obj.constructor.implements(InterfaceRequiringClassAndInterface)).toBeTruthy(); }); it('must be implemented by an object which subclasses the required class', function () { expect(() => new Lang.Class({ Name: 'ObjectWithoutRequiredParent', Implements: [AnInterface, InterfaceRequiringOtherInterface, InterfaceRequiringClassAndInterface], required() {}, })).toThrow(); }); it('can have methods that call others of its methods', function () { let obj = new ObjectImplementingAnInterface(); expect(obj.usesThis()).toEqual('interface private method'); }); it('is implemented by a subclass of a class that implements it', function () { const SubObject = new Lang.Class({ Name: 'SubObject', Extends: ObjectImplementingAnInterface, }); let obj = new SubObject(); expect(obj.constructor.implements(AnInterface)).toBeTruthy(); }); it('can be reimplemented by a subclass of a class that implements it', function () { const SubImplementer = new Lang.Class({ Name: 'SubImplementer', Extends: ObjectImplementingAnInterface, Implements: [AnInterface], }); let obj = new SubImplementer(); expect(obj.constructor.implements(AnInterface)).toBeTruthy(); expect(() => obj.required()).not.toThrow(); }); it('tells what it is with toString()', function () { expect(AnInterface.toString()).toEqual('[interface Interface for AnInterface]'); }); }); describe('ES6 class inheriting from Lang.Class', function () { let Shiny, Legacy; beforeEach(function () { Legacy = new Lang.Class({ Name: 'Legacy', _init(someval) { this.constructorCalledWith = someval; }, instanceMethod() {}, chainUpToMe() {}, overrideMe() {}, get property() { return this._property + 1; }, set property(value) { this._property = value - 2; }, }); Legacy.staticMethod = function () {}; spyOn(Legacy, 'staticMethod'); spyOn(Legacy.prototype, 'instanceMethod'); spyOn(Legacy.prototype, 'chainUpToMe'); spyOn(Legacy.prototype, 'overrideMe'); Shiny = class extends Legacy { chainUpToMe() { super.chainUpToMe(); } overrideMe() {} }; }); it('calls a static method on the parent class', function () { Shiny.staticMethod(); expect(Legacy.staticMethod).toHaveBeenCalled(); }); it('calls a method on the parent class', function () { let instance = new Shiny(); instance.instanceMethod(); expect(Legacy.prototype.instanceMethod).toHaveBeenCalled(); }); it("passes arguments to the parent class's constructor", function () { let instance = new Shiny(42); expect(instance.constructorCalledWith).toEqual(42); }); it('chains up to a method on the parent class', function () { let instance = new Shiny(); instance.chainUpToMe(); expect(Legacy.prototype.chainUpToMe).toHaveBeenCalled(); }); it('overrides a method on the parent class', function () { let instance = new Shiny(); instance.overrideMe(); expect(Legacy.prototype.overrideMe).not.toHaveBeenCalled(); }); it('sets and gets a property from the parent class', function () { let instance = new Shiny(); instance.property = 42; expect(instance.property).toEqual(41); }); }); cjs-128.0/installed-tests/js/testWeakRef.js0000664000175000017500000000473114771557763017577 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2023 Philip Chimento import System from 'system'; const PromiseInternal = imports._promiseNative; describe('WeakRef', function () { it('works', function () { let obj = {}; const weakRef = new WeakRef(obj); expect(weakRef.deref()).toBe(obj); obj = null; // Do not use this in real code to process microtasks. This is only for // making the test execute synchronously. Instead, in real code, return // control to the event loop, e.g. with setTimeout(). PromiseInternal.drainMicrotaskQueue(); System.gc(); expect(weakRef.deref()).not.toBeDefined(); }); }); describe('FinalizationRegistry', function () { let registry, callback; beforeEach(function () { callback = jasmine.createSpy('FinalizationRegistry callback'); registry = new FinalizationRegistry(callback); }); it('works', function () { let obj = {}; registry.register(obj, 'marker'); obj = null; System.gc(); PromiseInternal.drainMicrotaskQueue(); expect(callback).toHaveBeenCalledOnceWith('marker'); }); it('works if a microtask is enqueued from the callback', function () { let obj = {}; let secondCallback = jasmine.createSpy('async callback'); callback.and.callFake(function () { return Promise.resolve().then(secondCallback); }); registry.register(obj); obj = null; System.gc(); PromiseInternal.drainMicrotaskQueue(); expect(callback).toHaveBeenCalled(); expect(secondCallback).toHaveBeenCalled(); }); it('works if the object is collected in a microtask', async function () { let obj = {}; registry.register(obj, 'marker'); await Promise.resolve(); obj = null; System.gc(); await Promise.resolve(); expect(callback).toHaveBeenCalled(); }); it('works if another collection is queued from the callback', function () { let obj = {}; let obj2 = {}; callback.and.callFake(function () { obj2 = null; System.gc(); }); registry.register(obj, 'marker'); registry.register(obj2, 'marker2'); obj = null; System.gc(); PromiseInternal.drainMicrotaskQueue(); expect(callback).toHaveBeenCalledTimes(2); }); }); cjs-128.0/installed-tests/js/testConsole.js0000664000175000017500000002770214771557763017660 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2021 Evan Welsh // eslint-disable-next-line /// import GLib from 'gi://GLib'; import {DEFAULT_LOG_DOMAIN} from 'console'; import {decodedStringMatching} from './matchers.js'; function objectContainingLogMessage( message, domain = DEFAULT_LOG_DOMAIN, fields = {}, messageMatcher = decodedStringMatching ) { return jasmine.objectContaining({ MESSAGE: messageMatcher(message), GLIB_DOMAIN: decodedStringMatching(domain), ...fields, }); } function matchStackTrace(log, sourceFile = null, encoding = 'utf-8') { const matcher = jasmine.stringMatching(log); const stackLineMatcher = jasmine.stringMatching(/^[\w./<]*@.*:\d+:\d+/); const sourceMatcher = sourceFile ? jasmine.stringMatching(RegExp( String.raw`^[\w]*@(file|resource):\/\/\/.*\/${sourceFile}\.js:\d+:\d+$`)) : stackLineMatcher; return { asymmetricMatch(compareTo) { const decoder = new TextDecoder(encoding); const decoded = decoder.decode(new Uint8Array(Array.from(compareTo))); const lines = decoded.split('\n').filter(l => !!l.length); if (!matcher.asymmetricMatch(lines[0])) return false; if (!sourceMatcher.asymmetricMatch(lines[1])) return false; return lines.slice(2).every(l => stackLineMatcher.asymmetricMatch(l)); }, jasmineToString() { return ``; }, }; } describe('console', function () { /** @type {jasmine.Spy<(_level: any, _fields: any) => any>} */ let writer_func; /** * @param {RegExp | string} message _ * @param {*} [logLevel] _ * @param {*} [domain] _ * @param {*} [fields] _ */ function expectLog( message, logLevel = GLib.LogLevelFlags.LEVEL_MESSAGE, domain = DEFAULT_LOG_DOMAIN, fields = {} ) { if (logLevel < GLib.LogLevelFlags.LEVEL_WARNING) { const [_, currentFile] = new Error().stack.split('\n').at(0).match( /^[^@]*@(.*):\d+:\d+$/); fields = { ...fields, CODE_FILE: decodedStringMatching(currentFile), }; } expect(writer_func).toHaveBeenCalledOnceWith( logLevel, objectContainingLogMessage(message, domain, fields) ); // Always reset the calls, so that we can assert at the end that no // unexpected messages were logged writer_func.calls.reset(); } beforeAll(function () { writer_func = jasmine.createSpy( 'Console test writer func', function (level, _fields) { if (level === GLib.LogLevelFlags.ERROR) return GLib.LogWriterOutput.UNHANDLED; return GLib.LogWriterOutput.HANDLED; } ); writer_func.and.callThrough(); // @ts-expect-error The existing binding doesn't accept any parameters because // it is a raw pointer. GLib.log_set_writer_func(writer_func); }); beforeEach(function () { writer_func.calls.reset(); }); it('has correct object tag', function () { expect(console.toString()).toBe('[object console]'); }); it('logs a message', function () { console.log('a log'); expect(writer_func).toHaveBeenCalledOnceWith( GLib.LogLevelFlags.LEVEL_MESSAGE, objectContainingLogMessage('a log') ); writer_func.calls.reset(); }); it('logs an empty object correctly', function () { const emptyObject = {}; console.log(emptyObject); expectLog('{}'); }); it('logs an object with custom constructor name', function () { function CustomObject() {} const customInstance = new CustomObject(); console.log(customInstance); expectLog('CustomObject {}'); }); it('logs an object with undefined constructor', function () { const objectWithUndefinedConstructor = Object.create(null); console.log(objectWithUndefinedConstructor); expectLog('{}'); }); it('logs an object with Symbol.toStringTag and __name__', function () { console.log(GLib); expectLog('[GIRepositoryNamespace GLib]'); }); it('logs a warning', function () { console.warn('a warning'); expect(writer_func).toHaveBeenCalledOnceWith( GLib.LogLevelFlags.LEVEL_WARNING, objectContainingLogMessage('a warning') ); writer_func.calls.reset(); }); it('logs an informative message', function () { console.info('an informative message'); expect(writer_func).toHaveBeenCalledOnceWith( GLib.LogLevelFlags.LEVEL_INFO, objectContainingLogMessage('an informative message') ); writer_func.calls.reset(); }); it('traces a line', function () { // eslint-disable-next-line max-statements-per-line console.trace('a trace'); const error = new Error(); const [_, currentFile, errorLine] = error.stack.split('\n').at(0).match( /^[^@]*@(.*):(\d+):\d+$/); expect(writer_func).toHaveBeenCalledOnceWith( GLib.LogLevelFlags.LEVEL_MESSAGE, objectContainingLogMessage('a trace', DEFAULT_LOG_DOMAIN, { CODE_FILE: decodedStringMatching(currentFile), CODE_LINE: decodedStringMatching(errorLine), }, message => matchStackTrace(message, 'testConsole')) ); writer_func.calls.reset(); }); it('traces a empty message', function () { console.trace(); const [_, currentFile] = new Error().stack.split('\n').at(0).match( /^[^@]*@(.*):\d+:\d+$/); expect(writer_func).toHaveBeenCalledOnceWith( GLib.LogLevelFlags.LEVEL_MESSAGE, objectContainingLogMessage('Trace', DEFAULT_LOG_DOMAIN, { CODE_FILE: decodedStringMatching(currentFile), }, message => matchStackTrace(message, 'testConsole')) ); writer_func.calls.reset(); }); it('asserts a true condition', function () { console.assert(true, 'no printed'); expect(writer_func).not.toHaveBeenCalled(); writer_func.calls.reset(); }); it('asserts a false condition', function () { console.assert(false); expectLog('Assertion failed', GLib.LogLevelFlags.LEVEL_CRITICAL); writer_func.calls.reset(); }); it('asserts a false condition with message', function () { console.assert(false, 'asserts false is not true'); expectLog('asserts false is not true', GLib.LogLevelFlags.LEVEL_CRITICAL); writer_func.calls.reset(); }); describe('clear()', function () { it('can be called', function () { console.clear(); }); it('resets indentation', function () { console.group('a group'); expectLog('a group'); console.log('a log'); expectLog(' a log'); console.clear(); console.log('a log'); expectLog('a log'); }); }); describe('table()', function () { it('logs at least something', function () { console.table(['title', 1, 2, 3]); expectLog(/title/); }); }); // %s - string // %d or %i - integer // %f - float // %o - "optimal" object formatting // %O - "generic" object formatting // %c - "CSS" formatting (unimplemented by GJS) describe('string replacement', function () { const functions = { log: GLib.LogLevelFlags.LEVEL_MESSAGE, warn: GLib.LogLevelFlags.LEVEL_WARNING, info: GLib.LogLevelFlags.LEVEL_INFO, error: GLib.LogLevelFlags.LEVEL_CRITICAL, trace: GLib.LogLevelFlags.LEVEL_MESSAGE, }; Object.entries(functions).forEach(([fn, level]) => { it(`console.${fn}() supports %s`, function () { console[fn]('Does this %s substitute correctly?', 'modifier'); expectLog('Does this modifier substitute correctly?', level); }); it(`console.${fn}() supports %d`, function () { console[fn]('Does this %d substitute correctly?', 10); expectLog('Does this 10 substitute correctly?', level); }); it(`console.${fn}() supports %i`, function () { console[fn]('Does this %i substitute correctly?', 26); expectLog('Does this 26 substitute correctly?', level); }); it(`console.${fn}() supports %f`, function () { console[fn]('Does this %f substitute correctly?', 27.56331); expectLog('Does this 27.56331 substitute correctly?', level); }); it(`console.${fn}() supports %o`, function () { console[fn]('Does this %o substitute correctly?', new Error()); expectLog(/Does this Error\n.*substitute correctly\?/s, level); }); it(`console.${fn}() supports %O`, function () { console[fn]('Does this %O substitute correctly?', new Error()); expectLog('Does this {} substitute correctly?', level); }); it(`console.${fn}() ignores %c`, function () { console[fn]('Does this %c substitute correctly?', 'modifier'); expectLog('Does this substitute correctly?', level); }); it(`console.${fn}() supports mixing substitutions`, function () { console[fn]( 'Does this %s and the %f substitute correctly alongside %d?', 'string', 3.14, 14 ); expectLog( 'Does this string and the 3.14 substitute correctly alongside 14?', level ); }); it(`console.${fn}() supports invalid numbers`, function () { console[fn]( 'Does this support parsing %i incorrectly?', 'a string' ); expectLog('Does this support parsing NaN incorrectly?', level); }); it(`console.${fn}() supports missing substitutions`, function () { console[fn]('Does this support a missing %s substitution?'); expectLog( 'Does this support a missing %s substitution?', level ); }); }); }); describe('time()', function () { it('ends correctly', function (done) { console.time('testing time'); // console.time logs nothing. expect(writer_func).not.toHaveBeenCalled(); setTimeout(() => { console.timeLog('testing time'); expectLog(/testing time: (.*)ms/); console.timeEnd('testing time'); expectLog(/testing time: (.*)ms/); console.timeLog('testing time'); expectLog( "No time log found for label: 'testing time'.", GLib.LogLevelFlags.LEVEL_WARNING ); done(); }, 10); }); it("doesn't log initially", function (done) { console.time('testing time'); // console.time logs nothing. expect(writer_func).not.toHaveBeenCalled(); setTimeout(() => { console.timeEnd('testing time'); expectLog(/testing time: (.*)ms/); done(); }, 10); }); afterEach(function () { // Ensure we only got the log lines that we expected expect(writer_func).not.toHaveBeenCalled(); }); }); }); cjs-128.0/installed-tests/js/testESModules.js0000664000175000017500000001742114771557763020113 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2020 Evan Welsh import gettext from 'gettext'; import {ngettext as N_} from 'gettext'; import gi from 'gi'; import Gio from 'gi://Gio'; import system from 'system'; import {exit} from 'system'; import $ from 'resource:///org/cjs/jsunit/modules/exports.js'; import {NamedExport, data} from 'resource:///org/cjs/jsunit/modules/exports.js'; import metaProperties from 'resource:///org/cjs/jsunit/modules/importmeta.js'; // These imports should all refer to the same module and import it only once import 'resource:///org/cjs/jsunit/modules/sideEffect.js'; import 'resource://org/cjs/jsunit/modules/sideEffect.js'; import 'resource:///org/cjs/jsunit/modules/../modules/sideEffect.js'; // Imports with query parameters should not fail and be imported uniquely import 'resource:///org/cjs/jsunit/modules/sideEffect3.js?foo=bar&maple=syrup'; // these should resolve to the same after being canonicalized import 'resource://org/cjs/jsunit/modules/./sideEffect3.js?etag=1'; import 'resource:///org/cjs/jsunit/modules/sideEffect3.js?etag=1'; import greeting1 from 'resource:///org/cjs/jsunit/modules/greet.js?greeting=Hello&name=Test%20Code'; import greeting2 from 'resource:///org/cjs/jsunit/modules/greet.js?greeting=Bonjour&name=Code%20de%20Test'; describe('ES module imports', function () { it('default import', function () { expect($).toEqual(5); }); it('named import', function () { expect(NamedExport).toEqual('Hello, World'); }); it('GObject introspection import', function () { expect(gi.require('GObject').toString()).toEqual('[object GIRepositoryNamespace]'); }); it('import with version parameter', function () { expect(gi.require('GObject', '2.0')).toBe(gi.require('GObject')); expect(imports.gi.versions['GObject']).toBe('2.0'); }); it('import again with other version parameter', function () { expect(() => gi.require('GObject', '1.75')).toThrow(); expect(imports.gi.versions['GObject']).toBe('2.0'); }); it('import for the first time with wrong version', function () { expect(() => gi.require('Gtk', '1.75')).toThrow(); expect(imports.gi.versions['Gtk']).not.toBeDefined(); }); it('import with another version after a failed import', function () { expect(gi.require('Gtk', '3.0').toString()).toEqual('[object GIRepositoryNamespace]'); expect(imports.gi.versions['Gtk']).toBe('3.0'); }); it('import nonexistent module', function () { expect(() => gi.require('PLib')).toThrow(); expect(imports.gi.versions['PLib']).not.toBeDefined(); }); it('GObject introspection import via URL scheme', function () { expect(Gio.toString()).toEqual('[object GIRepositoryNamespace]'); expect(imports.gi.versions['Gio']).toBe('2.0'); }); it('import.meta.url', function () { expect(import.meta.url).toMatch(/\/installed-tests\/(gjs\/)?js\/testESModules\.js$/); }); it('finds files relative to import.meta.url', function () { // this data is loaded inside exports.js relative to import.meta.url expect(data).toEqual(Uint8Array.from('test data\n', c => c.codePointAt())); }); it('does not expose internal import.meta properties to userland modules', function () { expect(metaProperties).toEqual(['url']); }); it('treats equivalent URIs as equal and does not load the module again', function () { expect(globalThis.leakyState).toEqual(1); }); it('can load modules with query parameters uniquely', function () { expect(globalThis.queryLeakyState).toEqual(2); }); it('passes query parameters to imported modules in import.meta.uri', function () { expect(greeting1).toEqual('Hello, Test Code'); expect(greeting2).toEqual('Bonjour, Code de Test'); }); }); describe('Builtin ES modules', function () { it('gettext default import', function () { expect(typeof gettext.ngettext).toBe('function'); }); it('gettext named import', function () { expect(typeof N_).toBe('function'); }); it('gettext named dynamic import', async function () { const localGettext = await import('gettext'); expect(typeof localGettext.ngettext).toEqual('function'); }); it('gettext dynamic import matches static import', async function () { const localGettext = await import('gettext'); expect(localGettext.default).toEqual(gettext); }); it('system default import', function () { expect(typeof system.exit).toBe('function'); }); it('system named import', function () { expect(typeof exit).toBe('function'); expect(exit).toBe(system.exit); }); it('system dynamic import matches static import', async function () { const localSystem = await import('system'); expect(localSystem.default).toEqual(system); }); it('system named dynamic import', async function () { const localSystem = await import('system'); expect(typeof localSystem.exit).toBe('function'); }); }); describe('Dynamic imports', function () { let module; beforeEach(async function () { try { module = await import('resource:///org/cjs/jsunit/modules/say.js'); } catch (err) { logError(err); fail(); } }); it('default import', function () { expect(module.default()).toEqual('default export'); }); it('named import', function () { expect(module.say('hi')).toEqual('<( hi )'); }); it('dynamic gi import matches static', async function () { expect((await import('gi://Gio')).default).toEqual(Gio); }); it('treats equivalent URIs as equal and does not load the module again', async function () { delete globalThis.leakyState; await import('resource:///org/cjs/jsunit/modules/sideEffect2.js'); await import('resource://org/cjs/jsunit/modules/sideEffect2.js'); await import('resource:///org/cjs/jsunit/modules/../modules/sideEffect2.js'); expect(globalThis.leakyState).toEqual(1); }); it('treats query parameters uniquely for absolute URIs', async function () { delete globalThis.queryLeakyState; await import('resource:///org/cjs/jsunit/modules/sideEffect3.js?maple=syrup'); expect(globalThis.queryLeakyState).toEqual(1); }); it('treats query parameters uniquely for relative URIs', async function () { delete globalThis.queryLeakyState; await import('resource:///org/cjs/jsunit/modules/sideEffect4.js'); expect(globalThis.queryLeakyState).toEqual(1); }); it('does not show internal stack frames in an import error', async function () { try { await import('resource:///org/cjs/jsunit/modules/doesNotExist.js'); fail('should not be reached'); } catch (e) { expect(e.name).toBe('ImportError'); expect(e.stack).not.toMatch('internal/'); } }); it('does not show internal stack frames in a module that throws an error', async function () { try { await import('resource:///org/cjs/jsunit/modules/alwaysThrows.js'); fail('should not be reached'); } catch (e) { expect(e.constructor).toBe(Error); expect(e.stack).not.toMatch('internal/'); } }); it('does not show internal stack frames in a module that fails to parse', async function () { try { // invalid JS await import('resource:///org/cjs/jsunit/modules/data.txt'); fail('should not be reached'); } catch (e) { expect(e.constructor).toBe(SyntaxError); expect(e.stack).not.toMatch('internal/'); } }); }); cjs-128.0/installed-tests/js/testGDBus.js0000664000175000017500000010010314771557763017205 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2008 litl, LLC const {Gio, CjsTestTools, GLib} = imports.gi; // Adapter for compatibility with pre-GLib-2.80 let GioUnix; if (imports.gi.versions.GioUnix === '2.0') { GioUnix = imports.gi.GioUnix; } else { GioUnix = { InputStream: Gio.UnixInputStream, }; } /* The methods list with their signatures. * * *** NOTE: If you add stuff here, you need to update the Test class below. */ var TestIface = ` `; const PROP_READ_ONLY_INITIAL_VALUE = Math.random(); const PROP_READ_WRITE_INITIAL_VALUE = 58; const PROP_WRITE_ONLY_INITIAL_VALUE = 'Initial value'; /* Test is the actual object exporting the dbus methods */ class Test { constructor() { this._propReadOnly = PROP_READ_ONLY_INITIAL_VALUE; this._propWriteOnly = PROP_WRITE_ONLY_INITIAL_VALUE; this._propReadWrite = PROP_READ_WRITE_INITIAL_VALUE; this._impl = Gio.DBusExportedObject.wrapJSObject(TestIface, this); this._impl.export(Gio.DBus.session, '/org/cinnamon/cjs/Test'); } frobateStuff() { return {hello: new GLib.Variant('s', 'world')}; } nonJsonFrobateStuff(i) { if (i === 42) return '42 it is!'; else return 'Oops'; } alwaysThrowException() { throw Error('Exception!'); } thisDoesNotExist() { /* We'll remove this later! */ } noInParameter() { return 'Yes!'; } multipleInArgs(a, b, c, d, e) { return `${a} ${b} ${c} ${d} ${e}`; } emitPropertyChanged(name, value) { this._impl.emit_property_changed(name, value); } emitSignal() { this._impl.emit_signal('signalFoo', GLib.Variant.new('(s)', ['foobar'])); } noReturnValue() { /* Empty! */ } /* The following two functions have identical return values * in JS, but the bus message will be different. * multipleOutValues is "sss", while oneArrayOut is "as" */ multipleOutValues() { return ['Hello', 'World', '!']; } oneArrayOut() { return ['Hello', 'World', '!']; } /* Same thing again. In this case multipleArrayOut is "asas", * while arrayOfArrayOut is "aas". */ multipleArrayOut() { return [['Hello', 'World'], ['World', 'Hello']]; } arrayOfArrayOut() { return [['Hello', 'World'], ['World', 'Hello']]; } arrayOutBadSig() { return Symbol('Hello World!'); } byteArrayEcho(binaryString) { return binaryString; } byteEcho(aByte) { return aByte; } dictEcho(dict) { return dict; } /* This one is implemented asynchronously. Returns * the input arguments */ echoAsync(parameters, invocation) { var [someString, someInt] = parameters; GLib.idle_add(GLib.PRIORITY_DEFAULT, function () { invocation.return_value(new GLib.Variant('(si)', [someString, someInt])); return false; }); } // double get PropReadOnly() { return this._propReadOnly; } // string set PropWriteOnly(value) { this._propWriteOnly = value; } // variant get PropReadWrite() { return new GLib.Variant('s', this._propReadWrite.toString()); } set PropReadWrite(value) { this._propReadWrite = value.deepUnpack(); } get PropPrePacked() { return new GLib.Variant('a{sv}', { member: GLib.Variant.new_string('value'), }); } structArray() { return [[128, 123456], [42, 654321]]; } fdIn(fdIndex, fdList) { const fd = fdList.get(fdIndex); const stream = new GioUnix.InputStream({fd, closeFd: true}); const bytes = stream.read_bytes(4096, null); return bytes; } // Same as fdIn(), but implemented asynchronously fdIn2Async([fdIndex], invocation, fdList) { const fd = fdList.get(fdIndex); const stream = new GioUnix.InputStream({fd, closeFd: true}); stream.read_bytes_async(4096, GLib.PRIORITY_DEFAULT, null, (obj, res) => { const bytes = obj.read_bytes_finish(res); invocation.return_value(new GLib.Variant('(ay)', [bytes])); }); } fdOut(bytes) { const fd = CjsTestTools.open_bytes(bytes); const fdList = Gio.UnixFDList.new_from_array([fd]); return [0, fdList]; } fdOut2Async([bytes], invocation) { GLib.idle_add(GLib.PRIORITY_DEFAULT, function () { const fd = CjsTestTools.open_bytes(bytes); const fdList = Gio.UnixFDList.new_from_array([fd]); invocation.return_value_with_unix_fd_list(new GLib.Variant('(h)', [0]), fdList); return GLib.SOURCE_REMOVE; }); } } const ProxyClass = Gio.DBusProxy.makeProxyWrapper(TestIface); describe('Exported DBus object', function () { let ownNameID; var test; var proxy; let loop; const expectedBytes = new TextEncoder().encode('some bytes'); function waitForServerProperty(property, value = undefined, timeout = 500) { let waitId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, timeout, () => { waitId = 0; throw new Error(`Timeout waiting for property ${property} expired`); }); while (waitId && (!test[property] || value !== undefined && test[property] !== value)) loop.get_context().iteration(true); if (waitId) GLib.source_remove(waitId); expect(waitId).not.toBe(0); return test[property]; } beforeAll(function () { loop = new GLib.MainLoop(null, false); test = new Test(); ownNameID = Gio.DBus.session.own_name('org.cinnamon.cjs.Test', Gio.BusNameOwnerFlags.NONE, name => { log(`Acquired name ${name}`); loop.quit(); }, name => { log(`Lost name ${name}`); }); loop.run(); new ProxyClass(Gio.DBus.session, 'org.cinnamon.cjs.Test', '/org/cinnamon/cjs/Test', (obj, error) => { expect(error).toBeNull(); proxy = obj; expect(proxy).not.toBeNull(); loop.quit(); }, Gio.DBusProxyFlags.NONE); loop.run(); }); afterAll(function () { // Not really needed, but if we don't cleanup // memory checking will complain Gio.DBus.session.unown_name(ownNameID); }); beforeEach(function () { loop = new GLib.MainLoop(null, false); }); it('can call a remote method', function () { proxy.frobateStuffRemote({}, ([result], excp) => { expect(excp).toBeNull(); expect(result.hello.deepUnpack()).toEqual('world'); loop.quit(); }); loop.run(); }); it('can call a method with async/await', async function () { const [{hello}] = await proxy.frobateStuffAsync({}); expect(hello.deepUnpack()).toEqual('world'); }); it('can initiate a proxy with promise and call a method with async/await', async function () { const asyncProxy = await ProxyClass.newAsync(Gio.DBus.session, 'org.cinnamon.cjs.Test', '/org/cinnamon/cjs/Test'); expect(asyncProxy).toBeInstanceOf(Gio.DBusProxy); const [{hello}] = await asyncProxy.frobateStuffAsync({}); expect(hello.deepUnpack()).toEqual('world'); }); it('can call a remote method when not using makeProxyWrapper', function () { let info = Gio.DBusNodeInfo.new_for_xml(TestIface); let iface = info.interfaces[0]; let otherProxy = null; Gio.DBusProxy.new_for_bus(Gio.BusType.SESSION, Gio.DBusProxyFlags.DO_NOT_AUTO_START, iface, 'org.cinnamon.cjs.Test', '/org/cinnamon/cjs/Test', iface.name, null, (o, res) => { otherProxy = Gio.DBusProxy.new_for_bus_finish(res); loop.quit(); }); loop.run(); otherProxy.frobateStuffRemote({}, ([result], excp) => { expect(excp).toBeNull(); expect(result.hello.deepUnpack()).toEqual('world'); loop.quit(); }); loop.run(); }); /* excp must be exactly the exception thrown by the remote method (more or less) */ it('can handle an exception thrown by a remote method', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: Exception in method call: alwaysThrowException: *'); proxy.alwaysThrowExceptionRemote({}, function (result, excp) { expect(excp).not.toBeNull(); loop.quit(); }); loop.run(); }); it('can handle an exception thrown by a method with async/await', async function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: Exception in method call: alwaysThrowException: *'); await expectAsync(proxy.alwaysThrowExceptionAsync({})).toBeRejected(); }); it('can still destructure the return value when an exception is thrown', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: Exception in method call: alwaysThrowException: *'); // This test will not fail, but instead if the functionality is not // implemented correctly it will hang. The exception in the function // argument destructuring will not propagate across the FFI boundary // and the main loop will never quit. // https://bugzilla.gnome.org/show_bug.cgi?id=729015 proxy.alwaysThrowExceptionRemote({}, function ([a, b, c], excp) { expect(a).not.toBeDefined(); expect(b).not.toBeDefined(); expect(c).not.toBeDefined(); void excp; loop.quit(); }); loop.run(); }); it('throws an exception when trying to call a method that does not exist', function () { /* First remove the method from the object! */ delete Test.prototype.thisDoesNotExist; proxy.thisDoesNotExistRemote(function (result, excp) { expect(excp).not.toBeNull(); loop.quit(); }); loop.run(); }); it('throws an exception when trying to call an async method that does not exist', async function () { delete Test.prototype.thisDoesNotExist; await expectAsync(proxy.thisDoesNotExistAsync()).toBeRejected(); }); it('can pass a parameter to a remote method that is not a JSON object', function () { proxy.nonJsonFrobateStuffRemote(42, ([result], excp) => { expect(result).toEqual('42 it is!'); expect(excp).toBeNull(); loop.quit(); }); loop.run(); }); it('can pass a parameter to a method with async/await that is not a JSON object', async function () { const [result] = await proxy.nonJsonFrobateStuffAsync(1); expect(result).toEqual('Oops'); }); it('can call a remote method with no in parameter', function () { proxy.noInParameterRemote(([result], excp) => { expect(result).toEqual('Yes!'); expect(excp).toBeNull(); loop.quit(); }); loop.run(); }); it('can call an async/await method with no in parameter', async function () { const [result] = await proxy.noInParameterAsync(); expect(result).toEqual('Yes!'); }); it('can call a remote method with multiple in parameters', function () { proxy.multipleInArgsRemote(1, 2, 3, 4, 5, ([result], excp) => { expect(result).toEqual('1 2 3 4 5'); expect(excp).toBeNull(); loop.quit(); }); loop.run(); }); it('can call an async/await method with multiple in parameters', async function () { const [result] = await proxy.multipleInArgsAsync(1, 2, 3, 4, 5); expect(result).toEqual('1 2 3 4 5'); }); it('can call a remote method with no return value', function () { proxy.noReturnValueRemote(([result], excp) => { expect(result).not.toBeDefined(); expect(excp).toBeNull(); loop.quit(); }); loop.run(); }); it('can call an async/await method with no return value', async function () { const [result] = await proxy.noReturnValueAsync(); expect(result).not.toBeDefined(); }); it('can emit a DBus signal', function () { let handler = jasmine.createSpy('signalFoo'); let id = proxy.connectSignal('signalFoo', handler); handler.and.callFake(() => proxy.disconnectSignal(id)); proxy.emitSignalRemote(([result], excp) => { expect(result).not.toBeDefined(); expect(excp).toBeNull(); expect(handler).toHaveBeenCalledTimes(1); expect(handler).toHaveBeenCalledWith(jasmine.anything(), jasmine.anything(), ['foobar']); loop.quit(); }); loop.run(); }); it('can emit a DBus signal with async/await', async function () { const handler = jasmine.createSpy('signalFoo'); const id = proxy.connectSignal('signalFoo', handler); handler.and.callFake(() => proxy.disconnectSignal(id)); const [result] = await proxy.emitSignalAsync(); expect(result).not.toBeDefined(); expect(handler).toHaveBeenCalledTimes(1); expect(handler).toHaveBeenCalledWith(jasmine.anything(), jasmine.anything(), ['foobar']); }); it('can call a remote method with multiple return values', function () { proxy.multipleOutValuesRemote(function (result, excp) { expect(result).toEqual(['Hello', 'World', '!']); expect(excp).toBeNull(); loop.quit(); }); loop.run(); }); it('can call an async/await method with multiple return values', async function () { const results = await proxy.multipleOutValuesAsync(); expect(results).toEqual(['Hello', 'World', '!']); }); it('does not coalesce one array into the array of return values', function () { proxy.oneArrayOutRemote(([result], excp) => { expect(result).toEqual(['Hello', 'World', '!']); expect(excp).toBeNull(); loop.quit(); }); loop.run(); }); it('does not coalesce one array into the array of return values with async/await', async function () { const [result] = await proxy.oneArrayOutAsync(); expect(result).toEqual(['Hello', 'World', '!']); }); it('does not coalesce an array of arrays into the array of return values', function () { proxy.arrayOfArrayOutRemote(([[a1, a2]], excp) => { expect(a1).toEqual(['Hello', 'World']); expect(a2).toEqual(['World', 'Hello']); expect(excp).toBeNull(); loop.quit(); }); loop.run(); }); it('does not coalesce an array of arrays into the array of return values with async/await', async function () { const [[a1, a2]] = await proxy.arrayOfArrayOutAsync(); expect(a1).toEqual(['Hello', 'World']); expect(a2).toEqual(['World', 'Hello']); }); it('can return multiple arrays from a remote method', function () { proxy.multipleArrayOutRemote(([a1, a2], excp) => { expect(a1).toEqual(['Hello', 'World']); expect(a2).toEqual(['World', 'Hello']); expect(excp).toBeNull(); loop.quit(); }); loop.run(); }); it('can return multiple arrays from an async/await method', async function () { const [a1, a2] = await proxy.multipleArrayOutAsync(); expect(a1).toEqual(['Hello', 'World']); expect(a2).toEqual(['World', 'Hello']); }); it('handles a bad signature by throwing an exception', function () { proxy.arrayOutBadSigRemote(function (result, excp) { expect(excp).not.toBeNull(); loop.quit(); }); loop.run(); }); it('handles a bad signature in async/await by rejecting the promise', async function () { await expectAsync(proxy.arrayOutBadSigAsync()).toBeRejected(); }); it('can call a remote method that is implemented asynchronously', function () { let someString = 'Hello world!'; let someInt = 42; proxy.echoRemote(someString, someInt, function (result, excp) { expect(excp).toBeNull(); expect(result).toEqual([someString, someInt]); loop.quit(); }); loop.run(); }); it('can call an async/await method that is implemented asynchronously', async function () { const someString = 'Hello world!'; const someInt = 42; const results = await proxy.echoAsync(someString, someInt); expect(results).toEqual([someString, someInt]); }); it('can send and receive bytes from a remote method', function () { let someBytes = [0, 63, 234]; someBytes.forEach(b => { proxy.byteEchoRemote(b, ([result], excp) => { expect(excp).toBeNull(); expect(result).toEqual(b); loop.quit(); }); loop.run(); }); }); it('can send and receive bytes from an async/await method', async function () { let someBytes = [0, 63, 234]; await Promise.allSettled(someBytes.map(async b => { const [byte] = await proxy.byteEchoAsync(b); expect(byte).toEqual(b); })); }); it('can call a remote method that returns an array of structs', function () { proxy.structArrayRemote(([result], excp) => { expect(excp).toBeNull(); expect(result).toEqual([[128, 123456], [42, 654321]]); loop.quit(); }); loop.run(); }); it('can call an async/await method that returns an array of structs', async function () { const [result] = await proxy.structArrayAsync(); expect(result).toEqual([[128, 123456], [42, 654321]]); }); it('can send and receive dicts from a remote method', function () { let someDict = { aDouble: new GLib.Variant('d', 10), // should be an integer after round trip anInteger: new GLib.Variant('i', 10.5), // should remain a double aDoubleBeforeAndAfter: new GLib.Variant('d', 10.5), }; proxy.dictEchoRemote(someDict, ([result], excp) => { expect(excp).toBeNull(); expect(result).not.toBeNull(); // verify the fractional part was dropped off int expect(result['anInteger'].deepUnpack()).toEqual(10); // and not dropped off a double expect(result['aDoubleBeforeAndAfter'].deepUnpack()).toEqual(10.5); // check without type conversion expect(result['aDouble'].deepUnpack()).toBe(10.0); loop.quit(); }); loop.run(); }); it('can send and receive dicts from an async/await method', async function () { // See notes in test above const [result] = await proxy.dictEchoAsync({ aDouble: new GLib.Variant('d', 10), anInteger: new GLib.Variant('i', 10.5), aDoubleBeforeAndAfter: new GLib.Variant('d', 10.5), }); expect(result).not.toBeNull(); expect(result['anInteger'].deepUnpack()).toEqual(10); expect(result['aDoubleBeforeAndAfter'].deepUnpack()).toEqual(10.5); expect(result['aDouble'].deepUnpack()).toBe(10.0); }); it('can call a remote method with a Unix FD', function (done) { const fd = CjsTestTools.open_bytes(expectedBytes); const fdList = Gio.UnixFDList.new_from_array([fd]); proxy.fdInRemote(0, fdList, ([bytes], exc, outFdList) => { expect(exc).toBeNull(); expect(outFdList).toBeNull(); expect(bytes).toEqual(expectedBytes); done(); }); }); it('can call an async/await method with a Unix FD', async function () { const fd = CjsTestTools.open_bytes(expectedBytes); const fdList = Gio.UnixFDList.new_from_array([fd]); const [bytes, outFdList] = await proxy.fdInAsync(0, fdList); expect(outFdList).not.toBeDefined(); expect(bytes).toEqual(expectedBytes); }); it('can call an asynchronously implemented remote method with a Unix FD', function (done) { const fd = CjsTestTools.open_bytes(expectedBytes); const fdList = Gio.UnixFDList.new_from_array([fd]); proxy.fdIn2Remote(0, fdList, ([bytes], exc, outFdList) => { expect(exc).toBeNull(); expect(outFdList).toBeNull(); expect(bytes).toEqual(expectedBytes); done(); }); }); it('can call an asynchronously implemented async/await method with a Unix FD', async function () { const fd = CjsTestTools.open_bytes(expectedBytes); const fdList = Gio.UnixFDList.new_from_array([fd]); const [bytes, outFdList] = await proxy.fdIn2Async(0, fdList); expect(outFdList).not.toBeDefined(); expect(bytes).toEqual(expectedBytes); }); function readBytesFromFdSync(fd) { const stream = new GioUnix.InputStream({fd, closeFd: true}); const bytes = stream.read_bytes(4096, null); return bytes.toArray(); } it('can call a remote method that returns a Unix FD', function (done) { proxy.fdOutRemote(expectedBytes, ([fdIndex], exc, outFdList) => { expect(exc).toBeNull(); const bytes = readBytesFromFdSync(outFdList.get(fdIndex)); expect(bytes).toEqual(expectedBytes); done(); }); }); it('can call an async/await method that returns a Unix FD', async function () { const [fdIndex, outFdList] = await proxy.fdOutAsync(expectedBytes); const bytes = readBytesFromFdSync(outFdList.get(fdIndex)); expect(bytes).toEqual(expectedBytes); }); it('can call an asynchronously implemented remote method that returns a Unix FD', function (done) { proxy.fdOut2Remote(expectedBytes, ([fdIndex], exc, outFdList) => { expect(exc).toBeNull(); const bytes = readBytesFromFdSync(outFdList.get(fdIndex)); expect(bytes).toEqual(expectedBytes); done(); }); }); it('can call an asynchronously implemented asyc/await method that returns a Unix FD', async function () { const [fdIndex, outFdList] = await proxy.fdOut2Async(expectedBytes); const bytes = readBytesFromFdSync(outFdList.get(fdIndex)); expect(bytes).toEqual(expectedBytes); }); it('throws an exception when not passing a Gio.UnixFDList to a method that requires one', function () { expect(() => proxy.fdInRemote(0, () => {})).toThrow(); }); it('rejects the promise when not passing a Gio.UnixFDList to an async method that requires one', async function () { await expectAsync(proxy.fdInAsync(0)).toBeRejected(); }); it('throws an exception when passing a handle out of range of a Gio.UnixFDList', function () { const fdList = new Gio.UnixFDList(); expect(() => proxy.fdInRemote(0, fdList, () => {})).toThrow(); }); it('rejects the promise when async passing a handle out of range of a Gio.UnixFDList', async function () { const fdList = new Gio.UnixFDList(); await expectAsync(proxy.fdInAsync(0, fdList)).toBeRejected(); }); it('Has defined properties', function () { expect(Object.hasOwn(proxy, 'PropReadWrite')).toBeTruthy(); expect(Object.hasOwn(proxy, 'PropReadOnly')).toBeTruthy(); expect(Object.hasOwn(proxy, 'PropWriteOnly')).toBeTruthy(); expect(Object.hasOwn(proxy, 'PropPrePacked')).toBeTruthy(); }); it('reading readonly property works', function () { expect(proxy.PropReadOnly).toEqual(PROP_READ_ONLY_INITIAL_VALUE); }); it('reading readwrite property works', function () { expect(proxy.PropReadWrite).toEqual( GLib.Variant.new_string(PROP_READ_WRITE_INITIAL_VALUE.toString())); }); it('reading writeonly throws an error', function () { expect(() => proxy.PropWriteOnly).toThrowError('Property PropWriteOnly is not readable'); }); it('Setting a readwrite property works', function () { let testStr = 'GjsVariantValue'; expect(() => { proxy.PropReadWrite = GLib.Variant.new_string(testStr); }).not.toThrow(); expect(proxy.PropReadWrite.deepUnpack()).toEqual(testStr); expect(waitForServerProperty('_propReadWrite', testStr)).toEqual(testStr); }); it('Setting a writeonly property works', function () { let testValue = Math.random().toString(); expect(() => { proxy.PropWriteOnly = testValue; }).not.toThrow(); expect(() => proxy.PropWriteOnly).toThrow(); expect(waitForServerProperty('_propWriteOnly', testValue)).toEqual(testValue); }); it('Setting a readonly property throws an error', function () { let testValue = Math.random().toString(); expect(() => { proxy.PropReadOnly = testValue; }).toThrowError('Property PropReadOnly is not writable'); expect(proxy.PropReadOnly).toBe(PROP_READ_ONLY_INITIAL_VALUE); }); it('Reading a property that prepacks the return value works', function () { expect(proxy.PropPrePacked.member).toBeInstanceOf(GLib.Variant); expect(proxy.PropPrePacked.member.get_type_string()).toEqual('s'); }); it('Marking a property as invalidated works', function () { let changedProps = {}; let invalidatedProps = []; proxy.connect('g-properties-changed', (proxy_, changed, invalidated) => { changedProps = changed.deepUnpack(); invalidatedProps = invalidated; loop.quit(); }); test.emitPropertyChanged('PropReadOnly', null); loop.run(); expect(changedProps).not.toContain('PropReadOnly'); expect(invalidatedProps).toContain('PropReadOnly'); }); }); describe('DBus Proxy wrapper', function () { let loop; let wasPromise; let writerFunc; beforeAll(function () { loop = new GLib.MainLoop(null, false); wasPromise = Gio.DBusProxy.prototype._original_init_async instanceof Function; Gio._promisify(Gio.DBusProxy.prototype, 'init_async'); writerFunc = jasmine.createSpy( 'log writer func', (level, fields) => { const decoder = new TextDecoder('utf-8'); const domain = decoder.decode(fields?.GLIB_DOMAIN); const message = `${decoder.decode(fields?.MESSAGE)}\n`; level |= GLib.LogLevelFlags.FLAG_RECURSION; GLib.log_default_handler(domain, level, message, null); return GLib.LogWriterOutput.HANDLED; }); writerFunc.and.callThrough(); GLib.log_set_writer_func(writerFunc); }); beforeEach(function () { writerFunc.calls.reset(); }); it('init failures are reported in sync mode', function () { const cancellable = new Gio.Cancellable(); cancellable.cancel(); expect(() => new ProxyClass(Gio.DBus.session, 'org.cinnamon.cjs.Test', '/org/cinnamon/cjs/Test', Gio.DBusProxyFlags.NONE, cancellable)).toThrow(); }); it('init failures are reported in async mode', function () { const cancellable = new Gio.Cancellable(); cancellable.cancel(); const initDoneSpy = jasmine.createSpy( 'init finish func', () => loop.quit()); initDoneSpy.and.callThrough(); new ProxyClass(Gio.DBus.session, 'org.cinnamon.cjs.Test', '/org/cinnamon/cjs/Test', initDoneSpy, cancellable, Gio.DBusProxyFlags.NONE); loop.run(); expect(initDoneSpy).toHaveBeenCalledTimes(1); const {args: callArgs} = initDoneSpy.calls.mostRecent(); expect(callArgs.at(0)).toBeNull(); expect(callArgs.at(1).matches( Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)).toBeTrue(); }); it('can init a proxy asynchronously when promisified', function () { new ProxyClass(Gio.DBus.session, 'org.cinnamon.cjs.Test', '/org/cinnamon/cjs/Test', () => loop.quit(), Gio.DBusProxyFlags.NONE); loop.run(); expect(writerFunc).not.toHaveBeenCalled(); }); it('can create a proxy from a promise', async function () { const proxyPromise = ProxyClass.newAsync(Gio.DBus.session, 'org.cinnamon.cjs.Test', '/org/cinnamon/cjs/Test'); await expectAsync(proxyPromise).toBeResolved(); }); it('can create fail a proxy from a promise', async function () { const cancellable = new Gio.Cancellable(); cancellable.cancel(); const proxyPromise = ProxyClass.newAsync(Gio.DBus.session, 'org.cinnamon.cjs.Test', '/org/cinnamon/cjs/Test', cancellable); await expectAsync(proxyPromise).toBeRejected(); }); afterAll(function () { if (!wasPromise) { // Remove stuff added by Gio._promisify, this can be not needed in future // nor should break other tests, but we didn't want to depend on those. expect(Gio.DBusProxy.prototype._original_init_async).toBeInstanceOf(Function); Gio.DBusProxy.prototype.init_async = Gio.DBusProxy.prototype._original_init_async; delete Gio.DBusProxy.prototype._original_init_async; } }); }); cjs-128.0/installed-tests/js/testExceptions.js0000664000175000017500000002371314771557763020375 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2013 Red Hat, Inc. // SPDX-FileCopyrightText: 2015 Endless Mobile, Inc. const {GIMarshallingTests, Gio, GLib, GObject} = imports.gi; const Foo = GObject.registerClass({ Properties: { 'prop': GObject.ParamSpec.string('prop', '', '', GObject.ParamFlags.READWRITE, ''), }, }, class Foo extends GObject.Object { set prop(v) { throw new Error('set'); } get prop() { throw new Error('get'); } }); const Bar = GObject.registerClass({ Properties: { 'prop': GObject.ParamSpec.string('prop', '', '', GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT, ''), }, }, class Bar extends GObject.Object {}); describe('Exceptions', function () { it('are thrown from property setter', function () { let foo = new Foo(); expect(() => (foo.prop = 'bar')).toThrowError(/set/); }); it('are thrown from property getter', function () { let foo = new Foo(); expect(() => foo.prop).toThrowError(/get/); }); // FIXME: In the next cases the errors aren't thrown but logged it('are logged from constructor', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'JS ERROR: Error: set*'); new Foo({prop: 'bar'}); GLib.test_assert_expected_messages_internal('Cjs', 'testExceptions.js', 0, 'testExceptionInPropertySetterFromConstructor'); }); it('are logged from property setter with binding', function () { let foo = new Foo(); let bar = new Bar(); bar.bind_property('prop', foo, 'prop', GObject.BindingFlags.DEFAULT); GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'JS ERROR: Error: set*'); // wake up the binding so that g_object_set() is called on foo bar.notify('prop'); GLib.test_assert_expected_messages_internal('Cjs', 'testExceptions.js', 0, 'testExceptionInPropertySetterWithBinding'); }); it('are logged from property getter with binding', function () { let foo = new Foo(); let bar = new Bar(); foo.bind_property('prop', bar, 'prop', GObject.BindingFlags.DEFAULT); GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'JS ERROR: Error: get*'); // wake up the binding so that g_object_get() is called on foo foo.notify('prop'); GLib.test_assert_expected_messages_internal('Cjs', 'testExceptions.js', 0, 'testExceptionInPropertyGetterWithBinding'); }); }); describe('logError', function () { afterEach(function () { GLib.test_assert_expected_messages_internal('Cjs', 'testExceptions.js', 0, 'testGErrorMessages'); }); it('logs a warning for a GError', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: Gio.IOErrorEnum: *'); try { let file = Gio.file_new_for_path("\\/,.^!@&$_don't exist"); file.read(null); } catch (e) { logError(e); } }); it('logs a warning with a message if given', function marker() { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: Gio.IOErrorEnum: a message\nmarker@*'); try { throw new Gio.IOErrorEnum({message: 'a message', code: 0}); } catch (e) { logError(e); } }); it('also logs an error for a created GError that is not thrown', function marker() { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: Gio.IOErrorEnum: a message\nmarker@*'); logError(new Gio.IOErrorEnum({message: 'a message', code: 0})); }); it('logs an error created with the GLib.Error constructor', function marker() { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: Gio.IOErrorEnum: a message\nmarker@*'); logError(new GLib.Error(Gio.IOErrorEnum, 0, 'a message')); }); it('logs the quark for a JS-created GError type', function marker() { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: GLib.Error my-error: a message\nmarker@*'); logError(new GLib.Error(GLib.quark_from_string('my-error'), 0, 'a message')); }); it('logs with stack for a GError created from a C struct', function marker() { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: GLib.Error gi-marshalling-tests-gerror-domain: gi-marshalling-tests-gerror-message\nmarker@*'); logError(GIMarshallingTests.gerror_return()); }); // Now with prefix it('logs an error with a prefix if given', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: prefix: Gio.IOErrorEnum: *'); try { let file = Gio.file_new_for_path("\\/,.^!@&$_don't exist"); file.read(null); } catch (e) { logError(e, 'prefix'); } }); it('logs an error with prefix and message', function marker() { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: prefix: Gio.IOErrorEnum: a message\nmarker@*'); try { throw new Gio.IOErrorEnum({message: 'a message', code: 0}); } catch (e) { logError(e, 'prefix'); } }); describe('Syntax Error', function () { function throwSyntaxError() { Reflect.parse('!@#$%^&'); } it('logs a SyntaxError', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: SyntaxError:*'); try { throwSyntaxError(); } catch (e) { logError(e); } }); it('logs a stack trace with the SyntaxError', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: SyntaxError:*throwSyntaxError@*'); try { throwSyntaxError(); } catch (e) { logError(e); } }); }); it('logs an error with cause', function marker() { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: Error: an error\nmarker@*Caused by: Gio.IOErrorEnum: another error\nmarker2@*'); function marker2() { return new Gio.IOErrorEnum({message: 'another error', code: 0}); } logError(new Error('an error', {cause: marker2()})); }); it('logs a GError with cause', function marker() { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: Gio.IOErrorEnum: an error\nmarker@*Caused by: Error: another error\nmarker2@*'); function marker2() { return new Error('another error'); } const e = new Gio.IOErrorEnum({message: 'an error', code: 0}); e.cause = marker2(); logError(e); }); it('logs an error with non-object cause', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: Error: an error\n*Caused by: 3'); logError(new Error('an error', {cause: 3})); }); it('logs an error with a cause tree', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: Error: one\n*Caused by: Error: two\n*Caused by: Error: three\n*'); const three = new Error('three'); const two = new Error('two', {cause: three}); logError(new Error('one', {cause: two})); }); it('logs an error with cyclical causes', function () { // We cannot assert here with GLib.test_expect_message that the * at the // end of the string doesn't match more causes, but at least the idea is // that it shouldn't go into an infinite loop GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: Error: one\n*Caused by: Error: two\n*'); const one = new Error('one'); one.cause = new Error('two', {cause: one}); logError(one); }); }); describe('Exception from function with too few arguments', function () { it('contains the full function name', function () { expect(() => GLib.get_locale_variants()) .toThrowError(/GLib\.get_locale_variants/); }); it('contains the full method name', function () { let file = Gio.File.new_for_path('foo'); expect(() => file.read()).toThrowError(/Gio\.File\.read/); }); }); describe('thrown GError', function () { let err; beforeEach(function () { try { let file = Gio.file_new_for_path("\\/,.^!@&$_don't exist"); file.read(null); } catch (x) { err = x; } }); it('is an instance of error enum type', function () { expect(err).toEqual(jasmine.any(Gio.IOErrorEnum)); }); it('matches error domain and code', function () { expect(err.matches(Gio.io_error_quark(), Gio.IOErrorEnum.NOT_FOUND)) .toBeTruthy(); }); it('has properties for domain and code', function () { expect(err.domain).toEqual(Gio.io_error_quark()); expect(err.code).toEqual(Gio.IOErrorEnum.NOT_FOUND); }); }); describe('GError.new_literal', function () { it('constructs a valid GLib.Error', function () { const e = GLib.Error.new_literal( Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED, 'message'); expect(e instanceof GLib.Error).toBeTruthy(); expect(e.code).toEqual(Gio.IOErrorEnum.FAILED); expect(e.message).toEqual('message'); }); it('does not accept invalid domains', function () { expect(() => GLib.Error.new_literal(0, 0, 'message')) .toThrowError(/0 is not a valid domain/); }); }); cjs-128.0/installed-tests/js/testCairo.js0000664000175000017500000003072114771557763017306 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2010 litl, LLC imports.gi.versions.Gdk = '3.0'; imports.gi.versions.Gtk = '3.0'; const Cairo = imports.cairo; const {Gdk, GIMarshallingTests, GLib, Gtk, Regress} = imports.gi; function _ts(obj) { return obj.toString().slice(8, -1); } describe('Cairo', function () { let cr, surface; beforeEach(function () { surface = new Cairo.ImageSurface(Cairo.Format.ARGB32, 10, 10); cr = new Cairo.Context(surface); }); describe('context', function () { it('has the right type', function () { expect(cr instanceof Cairo.Context).toBeTruthy(); }); it('reports its target surface', function () { expect(_ts(cr.getTarget())).toEqual('ImageSurface'); }); it('can set its source to a pattern', function () { let pattern = Cairo.SolidPattern.createRGB(1, 2, 3); cr.setSource(pattern); expect(_ts(cr.getSource())).toEqual('SolidPattern'); }); it('can set its antialias', function () { cr.setAntialias(Cairo.Antialias.NONE); expect(cr.getAntialias()).toEqual(Cairo.Antialias.NONE); }); it('can set its fill rule', function () { cr.setFillRule(Cairo.FillRule.EVEN_ODD); expect(cr.getFillRule()).toEqual(Cairo.FillRule.EVEN_ODD); }); it('can set its line cap', function () { cr.setLineCap(Cairo.LineCap.ROUND); expect(cr.getLineCap()).toEqual(Cairo.LineCap.ROUND); }); it('can set its line join', function () { cr.setLineJoin(Cairo.LineJoin.ROUND); expect(cr.getLineJoin()).toEqual(Cairo.LineJoin.ROUND); }); it('can set its line width', function () { cr.setLineWidth(1138); expect(cr.getLineWidth()).toEqual(1138); }); it('can set its miter limit', function () { cr.setMiterLimit(42); expect(cr.getMiterLimit()).toEqual(42); }); it('can set its operator', function () { cr.setOperator(Cairo.Operator.IN); expect(cr.getOperator()).toEqual(Cairo.Operator.IN); }); it('can set its tolerance', function () { cr.setTolerance(144); expect(cr.getTolerance()).toEqual(144); }); it('has a rectangle as clip extents', function () { expect(cr.clipExtents().length).toEqual(4); }); it('has a rectangle as fill extents', function () { expect(cr.fillExtents().length).toEqual(4); }); it('has a rectangle as stroke extents', function () { expect(cr.strokeExtents().length).toEqual(4); }); it('has zero dashes initially', function () { expect(cr.getDashCount()).toEqual(0); }); it('transforms user to device coordinates', function () { expect(cr.userToDevice(0, 0).length).toEqual(2); }); it('transforms user to device distance', function () { expect(cr.userToDeviceDistance(0, 0).length).toEqual(2); }); it('transforms device to user coordinates', function () { expect(cr.deviceToUser(0, 0).length).toEqual(2); }); it('transforms device to user distance', function () { expect(cr.deviceToUserDistance(0, 0).length).toEqual(2); }); it('computes text extents', function () { expect(cr.textExtents('')).toEqual({ xBearing: 0, yBearing: 0, width: 0, height: 0, xAdvance: 0, yAdvance: 0, }); expect(cr.textExtents('trailing spaces ')).toEqual({ xBearing: jasmine.any(Number), yBearing: jasmine.any(Number), width: jasmine.any(Number), height: jasmine.any(Number), xAdvance: jasmine.any(Number), yAdvance: 0, }); }); it('can call various, otherwise untested, methods without crashing', function () { expect(() => { cr.save(); cr.restore(); cr.setSourceSurface(surface, 0, 0); cr.pushGroup(); cr.popGroup(); cr.pushGroupWithContent(Cairo.Content.COLOR); cr.popGroupToSource(); cr.setSourceRGB(1, 2, 3); cr.setSourceRGBA(1, 2, 3, 4); cr.clip(); cr.clipPreserve(); cr.fill(); cr.fillPreserve(); let pattern = Cairo.SolidPattern.createRGB(1, 2, 3); cr.mask(pattern); cr.maskSurface(surface, 0, 0); cr.paint(); cr.paintWithAlpha(1); cr.setDash([1, 0.5], 1); cr.stroke(); cr.strokePreserve(); cr.inFill(0, 0); cr.inStroke(0, 0); cr.copyPage(); cr.showPage(); cr.translate(10, 10); cr.scale(10, 10); cr.rotate(180); cr.identityMatrix(); cr.showText('foobar'); cr.moveTo(0, 0); cr.setDash([], 1); cr.lineTo(1, 0); cr.lineTo(1, 1); cr.lineTo(0, 1); cr.closePath(); let path = cr.copyPath(); cr.fill(); cr.appendPath(path); cr.stroke(); }).not.toThrow(); }); it('has methods when created from a C function', function () { if (GLib.getenv('ENABLE_GTK') !== 'yes') { pending('GTK disabled'); return; } Gtk.init(null); let win = new Gtk.OffscreenWindow(); let da = new Gtk.DrawingArea(); win.add(da); da.realize(); cr = Gdk.cairo_create(da.window); expect(cr.save).toBeDefined(); expect(cr.getTarget()).toBeDefined(); }); }); describe('pattern', function () { it('has typechecks', function () { expect(() => cr.setSource({})).toThrow(); expect(() => cr.setSource(surface)).toThrow(); }); }); describe('solid pattern', function () { it('can be created from RGB static method', function () { let p1 = Cairo.SolidPattern.createRGB(1, 2, 3); expect(_ts(p1)).toEqual('SolidPattern'); cr.setSource(p1); expect(_ts(cr.getSource())).toEqual('SolidPattern'); }); it('can be created from RGBA static method', function () { let p2 = Cairo.SolidPattern.createRGBA(1, 2, 3, 4); expect(_ts(p2)).toEqual('SolidPattern'); cr.setSource(p2); expect(_ts(cr.getSource())).toEqual('SolidPattern'); }); }); describe('surface pattern', function () { it('can be created and added as a source', function () { let p1 = new Cairo.SurfacePattern(surface); expect(_ts(p1)).toEqual('SurfacePattern'); cr.setSource(p1); expect(_ts(cr.getSource())).toEqual('SurfacePattern'); }); }); describe('linear gradient', function () { it('can be created and added as a source', function () { let p1 = new Cairo.LinearGradient(1, 2, 3, 4); expect(_ts(p1)).toEqual('LinearGradient'); cr.setSource(p1); expect(_ts(cr.getSource())).toEqual('LinearGradient'); }); }); describe('radial gradient', function () { it('can be created and added as a source', function () { let p1 = new Cairo.RadialGradient(1, 2, 3, 4, 5, 6); expect(_ts(p1)).toEqual('RadialGradient'); cr.setSource(p1); expect(_ts(cr.getSource())).toEqual('RadialGradient'); }); }); describe('path', function () { it('has typechecks', function () { expect(() => cr.appendPath({})).toThrow(); expect(() => cr.appendPath(surface)).toThrow(); }); }); describe('surface', function () { it('has typechecks', function () { expect(() => new Cairo.Context({})).toThrow(); const pattern = new Cairo.SurfacePattern(surface); expect(() => new Cairo.Context(pattern)).toThrow(); }); it('can access the device scale', function () { let [x, y] = surface.getDeviceScale(); expect(x).toEqual(1); expect(y).toEqual(1); surface.setDeviceScale(1.2, 1.2); [x, y] = surface.getDeviceScale(); expect(x).toEqual(1.2); expect(y).toEqual(1.2); }); it('can access the device offset', function () { let [x, y] = surface.getDeviceOffset(); expect(x).toEqual(0); expect(y).toEqual(0); surface.setDeviceOffset(50, 50); [x, y] = surface.getDeviceOffset(); expect(x).toEqual(50); expect(y).toEqual(50); }); it('can be finalized', function () { expect(() => { let _surface = new Cairo.ImageSurface(Cairo.Format.ARGB32, 10, 10); let _cr = new Cairo.Context(_surface); _surface.finish(); _cr.stroke(); }).toThrow(); expect(() => { let _surface = new Cairo.ImageSurface(Cairo.Format.ARGB32, 10, 10); _surface.finish(); _surface.flush(); }).not.toThrow(); }); }); describe('GI test suite', function () { describe('for context', function () { it('can be marshalled as a return value', function () { const outCr = Regress.test_cairo_context_full_return(); const outSurface = outCr.getTarget(); expect(outSurface.getFormat()).toEqual(Cairo.Format.ARGB32); expect(outSurface.getWidth()).toEqual(10); expect(outSurface.getHeight()).toEqual(10); }); it('can be marshalled as an in parameter', function () { expect(() => Regress.test_cairo_context_none_in(cr)).not.toThrow(); }); }); describe('for surface', function () { ['none', 'full'].forEach(transfer => { it(`can be marshalled as a transfer-${transfer} return value`, function () { const outSurface = Regress[`test_cairo_surface_${transfer}_return`](); expect(outSurface.getFormat()).toEqual(Cairo.Format.ARGB32); expect(outSurface.getWidth()).toEqual(10); expect(outSurface.getHeight()).toEqual(10); }); }); it('can be marshalled as an in parameter', function () { expect(() => Regress.test_cairo_surface_none_in(surface)).not.toThrow(); }); it('can be marshalled as an out parameter', function () { const outSurface = Regress.test_cairo_surface_full_out(); expect(outSurface.getFormat()).toEqual(Cairo.Format.ARGB32); expect(outSurface.getWidth()).toEqual(10); expect(outSurface.getHeight()).toEqual(10); }); }); it('can be marshalled through a signal handler', function () { let o = new Regress.TestObj(); let foreignSpy = jasmine.createSpy('sig-with-foreign-struct'); o.connect('sig-with-foreign-struct', foreignSpy); o.emit_sig_with_foreign_struct(); expect(foreignSpy).toHaveBeenCalledWith(o, cr); }); it('can have its type inferred as a foreign struct', function () { expect(() => GIMarshallingTests.gvalue_in_with_type(cr, Cairo.Context)) .not.toThrow(); expect(() => GIMarshallingTests.gvalue_in_with_type(surface, Cairo.Surface)) .not.toThrow(); }); }); }); describe('Cairo imported via GI', function () { const giCairo = imports.gi.cairo; it('has the same functionality as imports.cairo', function () { const surface = new giCairo.ImageSurface(Cairo.Format.ARGB32, 1, 1); void new giCairo.Context(surface); }); it('has boxed types from the GIR file', function () { void new giCairo.RectangleInt(); }); }); cjs-128.0/installed-tests/js/testPrint.js0000664000175000017500000001572614771557763017355 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2020 Philip Chimento // SPDX-FileCopyrightText: 2022 Nasah Kuma imports.gi.versions.Gdk = '3.0'; const Gdk = imports.gi.Gdk; const ByteArray = imports.byteArray; const {getPrettyPrintFunction} = imports._print; let prettyPrint = getPrettyPrintFunction(globalThis); describe('print', function () { it('can be spied upon', function () { spyOn(globalThis, 'print'); print('foo'); expect(print).toHaveBeenCalledWith('foo'); }); }); describe('printerr', function () { it('can be spied upon', function () { spyOn(globalThis, 'printerr'); printerr('foo'); expect(printerr).toHaveBeenCalledWith('foo'); }); }); describe('log', function () { it('can be spied upon', function () { spyOn(globalThis, 'log'); log('foo'); expect(log).toHaveBeenCalledWith('foo'); }); }); describe('logError', function () { it('can be spied upon', function () { spyOn(globalThis, 'logError'); logError('foo', 'bar'); expect(logError).toHaveBeenCalledWith('foo', 'bar'); }); }); describe('prettyPrint', function () { it('property value primitive', function () { expect( prettyPrint({greeting: 'hi'}) ).toBe('{ greeting: "hi" }'); }); it('property value is object reference', function () { let obj = {a: 5}; obj.b = obj; expect( prettyPrint(obj) ).toBe('{ a: 5, b: [Circular] }'); }); it('more than one property', function () { expect( prettyPrint({a: 1, b: 2, c: 3}) ).toBe('{ a: 1, b: 2, c: 3 }'); }); it('add property value after property value object reference', function () { let obj = {a: 5}; obj.b = obj; obj.c = 4; expect( prettyPrint(obj) ).toBe('{ a: 5, b: [Circular], c: 4 }'); }); it('array', function () { expect( prettyPrint([1, 2, 3, 4, 5]) ).toBe('[1, 2, 3, 4, 5]'); }); it('property value array', function () { expect( prettyPrint({arr: [1, 2, 3, 4, 5]}) ).toBe('{ arr: [1, 2, 3, 4, 5] }'); }); it('array reference is the only array element', function () { let arr = []; arr.push(arr); expect( prettyPrint(arr) ).toBe('[[Circular]]'); }); it('array reference is one of multiple array elements', function () { let arr = []; arr.push(4); arr.push(arr); arr.push(5); expect( prettyPrint(arr) ).toBe('[4, [Circular], 5]'); }); it('nested array', function () { expect( prettyPrint([1, 2, [3, 4], 5]) ).toBe('[1, 2, [3, 4], 5]'); }); it('property value nested array', function () { expect( prettyPrint({arr: [1, 2, [3, 4], 5]}) ).toBe('{ arr: [1, 2, [3, 4], 5] }'); }); it('function', function () { expect( prettyPrint(function sum(a, b) { return a + b; }) ).toBe('[ Function: sum ]'); }); it('property value function', function () { expect( prettyPrint({ sum: function sum(a, b) { return a + b; }, }) ).toBe('{ sum: [ Function: sum ] }'); }); it('date', function () { expect( prettyPrint(new Date(Date.UTC(2018, 11, 24, 10, 33, 30))) ).toBe('2018-12-24T10:33:30.000Z'); }); it('property value date', function () { expect( prettyPrint({date: new Date(Date.UTC(2018, 11, 24, 10, 33, 30))}) ).toBe('{ date: 2018-12-24T10:33:30.000Z }'); }); it('toString is overridden on object', function () { expect( prettyPrint(new Gdk.Rectangle()) ).toMatch(/\[boxed instance wrapper GIName:.*\]/); }); it('string tag supplied', function () { expect( prettyPrint(Gdk) ).toMatch('[object GIRepositoryNamespace]'); }); it('symbol', function () { expect(prettyPrint(Symbol('foo'))).toEqual('Symbol("foo")'); }); it('property key symbol', function () { expect(prettyPrint({[Symbol('foo')]: 'symbol'})) .toEqual('{ [Symbol("foo")]: "symbol" }'); }); it('property value symbol', function () { expect(prettyPrint({symbol: Symbol('foo')})) .toEqual('{ symbol: Symbol("foo") }'); }); it('registered symbol', function () { expect(prettyPrint(Symbol.for('foo'))).toEqual('Symbol.for("foo")'); }); it('property key registered symbol', function () { expect(prettyPrint({[Symbol.for('foo')]: 'symbol'})) .toEqual('{ [Symbol.for("foo")]: "symbol" }'); }); it('property value registered symbol', function () { expect(prettyPrint({symbol: Symbol.for('foo')})) .toEqual('{ symbol: Symbol.for("foo") }'); }); it('well-known symbol', function () { expect(prettyPrint(Symbol.hasInstance)).toEqual('Symbol.hasInstance'); }); it('property key well-known symbol', function () { expect(prettyPrint({[Symbol.iterator]: 'symbol'})) .toEqual('{ [Symbol.iterator]: "symbol" }'); }); it('property value well-known symbol', function () { expect(prettyPrint({symbol: Symbol.hasInstance})) .toEqual('{ symbol: Symbol.hasInstance }'); }); it('undefined', function () { expect(prettyPrint(undefined)).toEqual('undefined'); }); it('null', function () { expect(prettyPrint(null)).toEqual('null'); }); it('nested null', function () { expect(prettyPrint({'foo': null})).toEqual('{ foo: null }'); }); it('imports root in object', function () { expect(prettyPrint({'foo': imports})) .toEqual('{ foo: [GjsFileImporter root] }'); }); describe('TypedArrays', () => { [ Int8Array, Uint8Array, Uint16Array, Uint8ClampedArray, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array, ].forEach(constructor => { it(constructor.name, function () { const arr = new constructor([1, 2, 3]); expect(prettyPrint(arr)) .toEqual('[1, 2, 3]'); }); }); [BigInt64Array, BigUint64Array].forEach(constructor => { it(constructor.name, function () { const arr = new constructor([1n, 2n, 3n]); expect(prettyPrint(arr)) .toEqual('[1, 2, 3]'); }); }); }); it('Uint8Array returned from introspected function', function () { let a = ByteArray.fromString('⅜'); expect(prettyPrint(a)).toEqual('[226, 133, 156]'); }); }); cjs-128.0/installed-tests/js/testWarnLib.js0000664000175000017500000000265514771557763017614 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2012 Red Hat, Inc. // SPDX-FileCopyrightText: 2019 Philip Chimento // File with tests from the WarnLib-1.0.gir test suite from GI const {Gio, GObject, WarnLib} = imports.gi; describe('WarnLib', function () { // Calling matches() on an unpaired error used to JSUnit.assert: // https://bugzilla.gnome.org/show_bug.cgi?id=689482 it('bug 689482', function () { try { WarnLib.throw_unpaired(); fail(); } catch (e) { expect(e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND)).toBeFalsy(); } }); const WhateverImpl = GObject.registerClass({ Implements: [WarnLib.Whatever], }, class WhateverImpl extends GObject.Object { vfunc_do_moo(x) { expect(x).toEqual(5); this.mooCalled = true; } vfunc_do_boo(x) { expect(x).toEqual(6); this.booCalled = true; } }); it('calls vfuncs with unnamed parameters', function () { const o = new WhateverImpl(); o.do_moo(5, null); o.do_boo(6, null); expect(o.mooCalled).toBeTruthy(); // spies don't work on vfuncs expect(o.booCalled).toBeTruthy(); }); it('handles enum members that start with a digit', function () { expect(WarnLib.NumericEnum['1ST']).toEqual(1); }); }); cjs-128.0/installed-tests/js/testTimers.js0000664000175000017500000002667414771557763017530 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2018-2019 the Deno authors. All rights reserved. // SPDX-FileCopyrightText: 2022 Evan Welsh // Derived from https://github.com/denoland/deno/blob/eda6e58520276786bd87e411d0284eb56d9686a6/cli/tests/unit/timers_test.ts import GLib from 'gi://GLib'; function deferred() { let resolve_; let reject_; function resolve() { resolve_(); } function reject() { reject_(); } const promise = new Promise((res, rej) => { resolve_ = res; reject_ = rej; }); return { promise, resolve, reject, }; } /** * @param {number} ms the number of milliseconds to wait * @returns {Promise} */ function waitFor(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } /** * @param {(resolve?: () => void, reject?: () => void) => void} callback a callback to call with handlers once the promise executes * @returns {jasmine.AsyncMatchers} */ function expectPromise(callback) { return expectAsync( new Promise((resolve, reject) => { callback(resolve, reject); }) ); } describe('Timers', () => { it('times out successfully', async function () { const startTime = GLib.get_monotonic_time(); const ms = 500; let count = 0; let endTime; await expectPromise(resolve => { setTimeout(() => { endTime = GLib.get_monotonic_time(); count++; resolve(); }, ms); }).toBeResolved(); expect(count).toBe(1); expect(endTime - startTime).toBeGreaterThanOrEqual(ms); return 5; }); it('has correct timeout args', async function () { const arg = 1; await expectPromise(resolve => { setTimeout( (a, b, c) => { expect(a).toBe(arg); expect(b).toBe(arg.toString()); expect(c).toEqual(jasmine.arrayWithExactContents([arg])); resolve(); }, 10, arg, arg.toString(), [arg] ); }).toBeResolved(); }); it('cancels successfully', async function () { let count = 0; const timeout = setTimeout(() => { count++; }, 1); // Cancelled, count should not increment clearTimeout(timeout); await waitFor(600); expect(count).toBe(0); }); it('cancels multiple correctly', async function () { const uncalled = jasmine.createSpy('uncalled'); // Set timers and cancel them in the same order. const t1 = setTimeout(uncalled, 10); const t2 = setTimeout(uncalled, 10); const t3 = setTimeout(uncalled, 10); clearTimeout(t1); clearTimeout(t2); clearTimeout(t3); // Set timers and cancel them in reverse order. const t4 = setTimeout(uncalled, 20); const t5 = setTimeout(uncalled, 20); const t6 = setTimeout(uncalled, 20); clearTimeout(t6); clearTimeout(t5); clearTimeout(t4); // Sleep until we're certain that the cancelled timers aren't gonna fire. await waitFor(50); expect(uncalled).not.toHaveBeenCalled(); }); it('cancels invalid silent fail', async function () { // Expect no panic const {promise, resolve} = deferred(); let count = 0; const id = setTimeout(() => { count++; // Should have no effect clearTimeout(id); resolve(); }, 500); await promise; expect(count).toBe(1); // Should silently fail (no panic) clearTimeout(2147483647); }); it('interval success', async function () { const {promise, resolve} = deferred(); let count = 0; const id = setInterval(() => { count++; clearInterval(id); resolve(); }, 100); await promise; // Clear interval clearInterval(id); // count should increment twice expect(count).toBe(1); }); it('cancels interval successfully', async function () { let count = 0; const id = setInterval(() => { count++; }, 1); clearInterval(id); await waitFor(500); expect(count).toBe(0); }); it('ordering interval', async function () { const timers = []; let timeouts = 0; function onTimeout() { ++timeouts; for (let i = 1; i < timers.length; i++) clearTimeout(timers[i]); } for (let i = 0; i < 10; i++) timers[i] = setTimeout(onTimeout, 1); await waitFor(500); expect(timeouts).toBe(1); }); it('cancel invalid silent fail', function () { // Should silently fail (no panic) clearInterval(2147483647); }); it('callback this', async function () { const {promise, resolve} = deferred(); const obj = { foo() { expect(this).toBe(window); resolve(); }, }; setTimeout(obj.foo, 1); await promise; }); it('bind this', function () { function noop() { } const thisCheckPassed = [null, undefined, window, globalThis]; const thisCheckFailed = [ 0, '', true, false, {}, [], 'foo', () => { }, Object.prototype, ]; thisCheckPassed.forEach(thisArg => { expect(() => { setTimeout.call(thisArg, noop, 1); }).not.toThrow(); }); thisCheckFailed.forEach(thisArg => { expect(() => { setTimeout.call(thisArg, noop, 1); }).toThrowError(TypeError); }); }); it('function names match spec', function testFunctionName() { expect(clearTimeout.name).toBe('clearTimeout'); expect(clearInterval.name).toBe('clearInterval'); }); it('argument lengths match spec', function testFunctionParamsLength() { expect(setTimeout.length).toBe(1); expect(setInterval.length).toBe(1); expect(clearTimeout.length).toBe(0); expect(clearInterval.length).toBe(0); }); it('clear and interval are unique functions', function clearTimeoutAndClearIntervalNotBeEquals() { expect(clearTimeout).not.toBe(clearInterval); }); // Based on https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/ // and https://github.com/web-platform-tests/wpt/blob/7b0ebaccc62b566a1965396e5be7bb2bc06f841f/html/webappapis/scripting/event-loops/task_microtask_ordering.html it('microtask ordering', async function () { const executionOrder = []; const expectedExecutionOrder = [ 'promise', 'timeout and promise', 'timeout', 'callback', ]; await expectPromise(resolve => { function execute(label) { executionOrder.push(label); if (executionOrder.length === expectedExecutionOrder.length) resolve(); } setTimeout(() => { execute('timeout'); }); setTimeout(() => { Promise.resolve().then(() => { execute('timeout and promise'); }); }); Promise.resolve().then(() => { execute('promise'); }); execute('callback'); }).toBeResolved(); expect(executionOrder).toEqual( jasmine.arrayWithExactContents(expectedExecutionOrder) ); }); it('nested microtask ordering', async function () { const executionOrder = []; const expectedExecutionOrder = [ 'promise 1', 'promise 2', 'promise 3', 'promise 4', 'promise 4 > nested promise', 'promise 4 > returned promise', 'timeout 1', 'timeout 2', 'timeout 3', 'timeout 4', 'promise 2 > nested timeout', 'promise 3 > nested timeout', 'promise 3 > nested timeout > nested promise', 'timeout 1 > nested timeout', 'timeout 2 > nested timeout', 'timeout 2 > nested timeout > nested promise', 'timeout 3 > nested timeout', 'timeout 3 > nested timeout > promise', 'timeout 3 > nested timeout > promise > nested timeout', ]; await expectPromise(resolve => { function execute(label) { executionOrder.push(label); } setTimeout(() => { execute('timeout 1'); setTimeout(() => { execute('timeout 1 > nested timeout'); }); }); setTimeout(() => { execute('timeout 2'); setTimeout(() => { execute('timeout 2 > nested timeout'); Promise.resolve().then(() => { execute('timeout 2 > nested timeout > nested promise'); }); }); }); setTimeout(() => { execute('timeout 3'); setTimeout(() => { execute('timeout 3 > nested timeout'); Promise.resolve().then(() => { execute('timeout 3 > nested timeout > promise'); setTimeout(() => { execute( 'timeout 3 > nested timeout > promise > nested timeout' ); // The most deeply nested setTimeout will be the last to resolve // because all queued promises should resolve prior to timeouts // and timeouts execute in order resolve(); }); }); }); }); setTimeout(() => { execute('timeout 4'); }); Promise.resolve().then(() => { execute('promise 1'); }); Promise.resolve().then(() => { execute('promise 2'); setTimeout(() => { execute('promise 2 > nested timeout'); }); }); Promise.resolve().then(() => { execute('promise 3'); setTimeout(() => { execute('promise 3 > nested timeout'); Promise.resolve().then(() => { execute('promise 3 > nested timeout > nested promise'); }); }); }); Promise.resolve().then(() => { execute('promise 4'); Promise.resolve().then(() => { execute('promise 4 > nested promise'); }); return Promise.resolve().then(() => { execute('promise 4 > returned promise'); }); }); }).toBeResolved(); expect(executionOrder).toEqual( jasmine.arrayWithExactContents(expectedExecutionOrder) ); }); }); cjs-128.0/installed-tests/js/testGio.js0000664000175000017500000004053614771557763016774 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2017 Patrick Griffis // SPDX-FileCopyrightText: 2019 Philip Chimento const {GLib, Gio, GObject} = imports.gi; const Foo = GObject.registerClass({ Properties: { boolval: GObject.ParamSpec.boolean('boolval', '', '', GObject.ParamFlags.READWRITE, false), }, }, class Foo extends GObject.Object { _init(value) { super._init(); this.value = value; } }); describe('ListStore iterator', function () { let list; beforeEach(function () { list = new Gio.ListStore({item_type: Foo}); for (let i = 0; i < 100; i++) list.append(new Foo(i)); }); it('ListStore iterates', function () { let i = 0; for (let f of list) expect(f.value).toBe(i++); }); }); function compareFunc(a, b) { return a.value - b.value; } describe('Sorting in ListStore', function () { let list; beforeEach(function () { list = new Gio.ListStore({ item_type: Foo, }); }); it('test insert_sorted', function () { for (let i = 10; i > 0; i--) list.insert_sorted(new Foo(i), compareFunc); let i = 1; for (let f of list) expect(f.value).toBe(i++); }); it('test sort', function () { for (let i = 10; i > 0; i--) list.append(new Foo(i)); list.sort(compareFunc); let i = 1; for (let f of list) expect(f.value).toBe(i++); }); }); describe('Promisify function', function () { it("doesn't crash when async function is not defined", function () { expect(() => Gio._promisify(Gio.Subprocess.prototype, 'commuicate_utf8_async', 'communicate_utf8_finish')).toThrowError(/commuicate_utf8_async/); }); it("doesn't crash when finish function is not defined", function () { expect(() => Gio._promisify(Gio.Subprocess.prototype, 'communicate_utf8_async', 'commuicate_utf8_finish')).toThrowError(/commuicate_utf8_finish/); }); it('promisifies functions', async function () { Gio._promisify(Gio.File.prototype, 'query_info_async'); const file = Gio.File.new_for_path('.'); const fileInfo = await file.query_info_async(Gio.FILE_ATTRIBUTE_STANDARD_TYPE, Gio.FileQueryInfoFlags.NONE, GLib.PRIORITY_DEFAULT, null); expect(fileInfo.get_file_type()).not.toBe(Gio.FileType.UNKNOWN); }); it('preserves old behavior', function (done) { Gio._promisify(Gio.File.prototype, 'query_info_async'); const file = Gio.File.new_for_path('.'); file.query_info_async(Gio.FILE_ATTRIBUTE_STANDARD_TYPE, Gio.FileQueryInfoFlags.NONE, GLib.PRIORITY_DEFAULT, null, (_, res) => { const fileInfo = file.query_info_finish(res); expect(fileInfo.get_file_type()).not.toBe(Gio.FileType.UNKNOWN); done(); }); }); it('can guess the finish function', function () { expect(() => Gio._promisify(Gio._LocalFilePrototype, 'read_async')).not.toThrow(); expect(() => Gio._promisify(Gio.DBus, 'get')).not.toThrow(); }); }); describe('Gio.Settings overrides', function () { it("doesn't crash when forgetting to specify a schema ID", function () { expect(() => new Gio.Settings()).toThrowError(/schema/); }); it("doesn't crash when specifying a schema ID that isn't installed", function () { expect(() => new Gio.Settings({schemaId: 'com.example.ThisDoesntExist'})) .toThrowError(/schema/); }); it("doesn't crash when forgetting to specify a schema path", function () { expect(() => new Gio.Settings({schemaId: 'org.cinnamon.CjsTest.Sub'})) .toThrowError(/schema/); }); it("doesn't crash when specifying conflicting schema paths", function () { expect(() => new Gio.Settings({ schemaId: 'org.cinnamon.CjsTest', path: '/conflicting/path/', })).toThrowError(/schema/); }); it('can construct with a settings schema object', function () { const source = Gio.SettingsSchemaSource.get_default(); const settingsSchema = source.lookup('org.cinnamon.CjsTest', false); expect(() => new Gio.Settings({settingsSchema})).not.toThrow(); }); it('throws proper error message when settings schema is specified with a wrong type', function () { expect(() => new Gio.Settings({ settings_schema: 'string.path', }).toThrowError('is not of type Gio.SettingsSchema')); }); describe('with existing schema', function () { const KINDS = ['boolean', 'double', 'enum', 'flags', 'int', 'int64', 'string', 'strv', 'uint', 'uint64', 'value']; let settings; beforeEach(function () { settings = new Gio.Settings({schemaId: 'org.cinnamon.CjsTest'}); }); it("doesn't crash when resetting a nonexistent key", function () { expect(() => settings.reset('foobar')).toThrowError(/key/); }); it("doesn't crash when checking a nonexistent key", function () { KINDS.forEach(kind => { expect(() => settings[`get_${kind}`]('foobar')).toThrowError(/key/); }); }); it("doesn't crash when setting a nonexistent key", function () { KINDS.forEach(kind => { expect(() => settings[`set_${kind}`]('foobar', null)).toThrowError(/key/); }); }); it("doesn't crash when checking writable for a nonexistent key", function () { expect(() => settings.is_writable('foobar')).toThrowError(/key/); }); it("doesn't crash when getting the user value for a nonexistent key", function () { expect(() => settings.get_user_value('foobar')).toThrowError(/key/); }); it("doesn't crash when getting the default value for a nonexistent key", function () { expect(() => settings.get_default_value('foobar')).toThrowError(/key/); }); it("doesn't crash when binding a nonexistent key", function () { const foo = new Foo(); expect(() => settings.bind('foobar', foo, 'boolval', Gio.SettingsBindFlags.GET)) .toThrowError(/key/); expect(() => settings.bind_writable('foobar', foo, 'boolval', false)) .toThrowError(/key/); }); it("doesn't crash when creating actions for a nonexistent key", function () { expect(() => settings.create_action('foobar')).toThrowError(/key/); }); it("doesn't crash when checking info about a nonexistent key", function () { expect(() => settings.settings_schema.get_key('foobar')).toThrowError(/key/); }); it("doesn't crash when getting a nonexistent sub-schema", function () { expect(() => settings.get_child('foobar')).toThrowError(/foobar/); }); it('still works with correct keys', function () { const KEYS = ['window-size', 'maximized', 'fullscreen']; KEYS.forEach(key => expect(settings.is_writable(key)).toBeTruthy()); expect(() => { settings.set_value('window-size', new GLib.Variant('(ii)', [100, 100])); settings.set_boolean('maximized', true); settings.set_boolean('fullscreen', true); }).not.toThrow(); expect(settings.get_value('window-size').deepUnpack()).toEqual([100, 100]); expect(settings.get_boolean('maximized')).toEqual(true); expect(settings.get_boolean('fullscreen')).toEqual(true); expect(() => { KEYS.forEach(key => settings.reset(key)); }).not.toThrow(); KEYS.forEach(key => expect(settings.get_user_value(key)).toBeNull()); expect(settings.get_default_value('window-size').deepUnpack()).toEqual([-1, -1]); expect(settings.get_default_value('maximized').deepUnpack()).toEqual(false); expect(settings.get_default_value('fullscreen').deepUnpack()).toEqual(false); const foo = new Foo({boolval: true}); settings.bind('maximized', foo, 'boolval', Gio.SettingsBindFlags.GET); expect(foo.boolval).toBeFalsy(); Gio.Settings.unbind(foo, 'boolval'); settings.bind_writable('maximized', foo, 'boolval', false); expect(foo.boolval).toBeTruthy(); expect(settings.create_action('maximized')).not.toBeNull(); expect(settings.settings_schema.get_key('fullscreen')).not.toBeNull(); const sub = settings.get_child('sub'); expect(sub.get_uint('marine')).toEqual(10); }); }); }); describe('Gio.content_type_set_mime_dirs', function () { it('can be called with NULL argument', function () { expect(() => Gio.content_type_set_mime_dirs(null)).not.toThrow(); }); }); describe('Gio.add_action_entries override', function () { it('registers each entry as an action', function () { const app = new Gio.Application(); const entries = [ { name: 'foo', parameter_type: 's', }, { name: 'bar', state: 'false', }, ]; app.add_action_entries(entries); expect(app.lookup_action('foo').name).toEqual(entries[0].name); expect(app.lookup_action('foo').parameter_type.dup_string()).toEqual(entries[0].parameter_type); expect(app.lookup_action('bar').name).toEqual(entries[1].name); expect(app.lookup_action('bar').state.print(true)).toEqual(entries[1].state); }); it('connects and binds the activate handler', function (done) { const app = new Gio.Application(); let action; const entries = [ { name: 'foo', parameter_type: 's', activate() { expect(this).toBe(action); done(); }, }, ]; app.add_action_entries(entries); action = app.lookup_action('foo'); action.activate(new GLib.Variant('s', 'hello')); }); it('connects and binds the change_state handler', function (done) { const app = new Gio.Application(); let action; const entries = [ { name: 'bar', state: 'false', change_state() { expect(this).toBe(action); done(); }, }, ]; app.add_action_entries(entries); action = app.lookup_action('bar'); action.change_state(new GLib.Variant('b', 'true')); }); it('throw an error if the parameter_type is invalid', function () { const app = new Gio.Application(); const entries = [ { name: 'foo', parameter_type: '(((', }, ]; expect(() => app.add_action_entries(entries)).toThrow(); }); it('throw an error if the state is invalid', function () { const app = new Gio.Application(); const entries = [ { name: 'bar', state: 'foo', }, ]; expect(() => app.add_action_entries(entries)).toThrow(); }); }); describe('Gio.InputStream.prototype.createSyncIterator', function () { it('iterates synchronously', function () { const [file] = Gio.File.new_tmp(null); file.replace_contents('hello ㊙ world', null, false, Gio.FileCreateFlags.REPLACE_DESTINATION, null); let totalRead = 0; for (const value of file.read(null).createSyncIterator(2)) { expect(value).toBeInstanceOf(GLib.Bytes); totalRead += value.get_size(); } expect(totalRead).toBe(15); }); }); describe('Gio.InputStream.prototype.createAsyncIterator', function () { it('iterates asynchronously', async function () { const [file] = Gio.File.new_tmp(null); file.replace_contents('hello ㊙ world', null, false, Gio.FileCreateFlags.REPLACE_DESTINATION, null); let totalRead = 0; for await (const value of file.read(null).createAsyncIterator(2)) { expect(value).toBeInstanceOf(GLib.Bytes); totalRead += value.get_size(); } expect(totalRead).toBe(15); }); }); describe('Gio.FileEnumerator overrides', function () { it('iterates synchronously', function () { const dir = Gio.File.new_for_path('.'); let count = 0; for (const value of dir.enumerate_children( 'standard::name', Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS, null )) { expect(value).toBeInstanceOf(Gio.FileInfo); count++; } expect(count).toBeGreaterThan(0); }); it('iterates asynchronously', async function () { const dir = Gio.File.new_for_path('.'); let count = 0; for await (const value of dir.enumerate_children( 'standard::name', Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS, null )) { expect(value).toBeInstanceOf(Gio.FileInfo); count++; } expect(count).toBeGreaterThan(0); }); }); describe('Non-introspectable file attribute overrides', function () { let numExpectedWarnings, file, info; const flags = [Gio.FileQueryInfoFlags.NONE, null]; function expectWarnings(count) { numExpectedWarnings = count; for (let c = 0; c < count; c++) { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, '*not introspectable*'); } } function assertWarnings(testName) { for (let c = 0; c < numExpectedWarnings; c++) { GLib.test_assert_expected_messages_internal('Cjs', 'testGio.js', 0, `test Gio.${testName}`); } numExpectedWarnings = 0; } beforeEach(function () { numExpectedWarnings = 0; [file] = Gio.File.new_tmp('XXXXXX'); info = file.query_info('standard::*', ...flags); }); it('invalid means unsetting the attribute', function () { expectWarnings(2); expect(() => file.set_attribute('custom::remove', Gio.FileAttributeType.INVALID, null, ...flags)) .toThrowError(/not introspectable/); expect(() => info.set_attribute('custom::remove', Gio.FileAttributeType.INVALID)).not.toThrow(); assertWarnings(); }); it('works for boolean', function () { expectWarnings(2); expect(() => file.set_attribute(Gio.FILE_ATTRIBUTE_STANDARD_IS_HIDDEN, Gio.FileAttributeType.BOOLEAN, false, ...flags)) .toThrowError(/not introspectable/); expect(() => info.set_attribute(Gio.FILE_ATTRIBUTE_STANDARD_IS_HIDDEN, Gio.FileAttributeType.BOOLEAN, false)) .not.toThrow(); assertWarnings(); }); it('works for uint32', function () { expectWarnings(2); expect(() => file.set_attribute(Gio.FILE_ATTRIBUTE_TIME_MODIFIED_USEC, Gio.FileAttributeType.UINT32, 123456, ...flags)) .not.toThrow(); expect(() => info.set_attribute(Gio.FILE_ATTRIBUTE_TIME_MODIFIED_USEC, Gio.FileAttributeType.UINT32, 654321)) .not.toThrow(); assertWarnings(); }); it('works for uint64', function () { expectWarnings(2); expect(() => file.set_attribute(Gio.FILE_ATTRIBUTE_TIME_MODIFIED, Gio.FileAttributeType.UINT64, Date.now() / 1000, ...flags)) .not.toThrow(); expect(() => info.set_attribute(Gio.FILE_ATTRIBUTE_TIME_MODIFIED, Gio.FileAttributeType.UINT64, Date.now() / 1000)) .not.toThrow(); assertWarnings(); }); it('works for object', function () { expectWarnings(2); const icon = Gio.ThemedIcon.new_from_names(['list-add-symbolic']); expect(() => file.set_attribute(Gio.FILE_ATTRIBUTE_STANDARD_ICON, Gio.FileAttributeType.OBJECT, icon, ...flags)) .toThrowError(/not introspectable/); expect(() => info.set_attribute(Gio.FILE_ATTRIBUTE_STANDARD_ICON, Gio.FileAttributeType.OBJECT, icon)) .not.toThrow(); assertWarnings(); }); afterEach(function () { file.delete_async(GLib.PRIORITY_DEFAULT, null, (obj, res) => obj.delete_finish(res)); }); }); cjs-128.0/installed-tests/js/testGTypeClass.js0000664000175000017500000000411514771557763020265 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2012 Red Hat, Inc. // SPDX-FileCopyrightText: 2013 Giovanni Campagna // We use Gio to have some objects that we know exist const Gio = imports.gi.Gio; const GObject = imports.gi.GObject; describe('Looking up param specs', function () { let p1, p2; beforeEach(function () { let findProperty = GObject.Object.find_property; p1 = findProperty.call(Gio.ThemedIcon, 'name'); p2 = findProperty.call(Gio.SimpleAction, 'enabled'); }); it('works', function () { expect(p1 instanceof GObject.ParamSpec).toBeTruthy(); expect(p2 instanceof GObject.ParamSpec).toBeTruthy(); }); it('gives the correct name', function () { expect(p1.name).toEqual('name'); expect(p2.name).toEqual('enabled'); }); it('gives the default value if present', function () { expect(p2.default_value).toBeTruthy(); }); }); describe('GType object', function () { it('has a name', function () { expect(GObject.TYPE_NONE.name).toEqual('void'); expect(GObject.TYPE_STRING.name).toEqual('gchararray'); }); it('has a read-only name', function () { try { GObject.TYPE_STRING.name = 'foo'; } catch (e) { } expect(GObject.TYPE_STRING.name).toEqual('gchararray'); }); it('has an undeletable name', function () { try { delete GObject.TYPE_STRING.name; } catch (e) { } expect(GObject.TYPE_STRING.name).toEqual('gchararray'); }); it('has a string representation', function () { expect(GObject.TYPE_NONE.toString()).toEqual("[object GType for 'void']"); expect(GObject.TYPE_STRING.toString()).toEqual("[object GType for 'gchararray']"); }); }); describe('GType marshalling', function () { it('marshals the invalid GType object into JS null', function () { expect(GObject.type_from_name('NonexistentType')).toBeNull(); expect(GObject.type_parent(GObject.TYPE_STRING)).toBeNull(); }); }); cjs-128.0/installed-tests/js/org.cinnamon.CjsTest.gschema.xml0000664000175000017500000000135014771557763023105 0ustar fabiofabio (-1, -1) false false 10 cjs-128.0/installed-tests/js/testTweener.js0000664000175000017500000002774114771557763017672 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2008 litl, LLC // SPDX-FileCopyrightText: 2009 Red Hat, Inc. const Tweener = imports.tweener.tweener; function installFrameTicker() { // Set up Tweener to have a "frame pulse" that the Jasmine clock functions // can influence let ticker = { FRAME_RATE: 50, _init() { }, start() { this._currentTime = 0; this._timeoutID = setInterval(() => { this._currentTime += 1000 / this.FRAME_RATE; this.emit('prepare-frame'); }, Math.floor(1000 / this.FRAME_RATE)); }, stop() { if ('_timeoutID' in this) { clearInterval(this._timeoutID); delete this._timeoutID; } this._currentTime = 0; }, getTime() { return this._currentTime; }, }; imports.signals.addSignalMethods(ticker); Tweener.setFrameTicker(ticker); } describe('Tweener', function () { beforeAll(function () { jasmine.clock().install(); installFrameTicker(); }); afterAll(function () { jasmine.clock().uninstall(); }); let start, update, overwrite, complete; beforeEach(function () { start = jasmine.createSpy('start'); update = jasmine.createSpy('update'); overwrite = jasmine.createSpy('overwrite'); complete = jasmine.createSpy('complete'); }); it('runs a simple tween', function () { var objectA = { x: 0, y: 0, }; var objectB = { x: 0, y: 0, }; Tweener.addTween(objectA, {x: 10, y: 10, time: 1, transition: 'linear'}); Tweener.addTween(objectB, {x: 10, y: 10, time: 1, delay: 0.5, transition: 'linear'}); jasmine.clock().tick(1001); expect(objectA.x).toEqual(10); expect(objectA.y).toEqual(10); expect(objectB.x).toEqual(5); expect(objectB.y).toEqual(5); }); it('calls callbacks during the tween', function () { Tweener.addTween({}, { time: 0.1, onStart: start, onUpdate: update, onComplete: complete, }); jasmine.clock().tick(101); expect(start).toHaveBeenCalled(); expect(update).toHaveBeenCalled(); expect(complete).toHaveBeenCalled(); }); it('can pause tweens', function () { var objectA = { foo: 0, }; var objectB = { bar: 0, }; var objectC = { baaz: 0, }; Tweener.addTween(objectA, {foo: 100, time: 0.1}); Tweener.addTween(objectC, {baaz: 100, time: 0.1}); Tweener.addTween(objectB, {bar: 100, time: 0.1}); Tweener.pauseTweens(objectA); // This should do nothing expect(Tweener.pauseTweens(objectB, 'quux')).toBeFalsy(); /* Pause and resume should be equal to doing nothing */ Tweener.pauseTweens(objectC, 'baaz'); Tweener.resumeTweens(objectC, 'baaz'); jasmine.clock().tick(101); expect(objectA.foo).toEqual(0); expect(objectB.bar).toEqual(100); expect(objectC.baaz).toEqual(100); }); it('can remove tweens', function () { var object = { foo: 0, bar: 0, baaz: 0, }; Tweener.addTween(object, {foo: 50, time: 0.1}); Tweener.addTween(object, {bar: 50, time: 0.1}); Tweener.addTween(object, {baaz: 50, time: 0.1}); /* The Tween on property foo should still be run after removing the other two */ Tweener.removeTweens(object, 'bar', 'baaz'); jasmine.clock().tick(101); expect(object.foo).toEqual(50); expect(object.bar).toEqual(0); expect(object.baaz).toEqual(0); }); it('overrides a tween with another one acting on the same object and property at the same time', function () { var objectA = { foo: 0, }; Tweener.addTween(objectA, {foo: 100, time: 0.1}); Tweener.addTween(objectA, {foo: 0, time: 0.1}); jasmine.clock().tick(101); expect(objectA.foo).toEqual(0); }); it('does not override a tween with another one acting not at the same time', function () { var objectB = { bar: 0, }; /* In this case both tweens should be executed, as they don't * act on the object at the same time (the second one has a * delay equal to the running time of the first one) */ Tweener.addTween(objectB, {bar: 100, time: 0.1}); Tweener.addTween(objectB, {bar: 150, time: 0.1, delay: 0.1}); jasmine.clock(0).tick(201); expect(objectB.bar).toEqual(150); }); it('can pause and resume all tweens', function () { var objectA = { foo: 0, }; var objectB = { bar: 0, }; Tweener.addTween(objectA, {foo: 100, time: 0.1}); Tweener.addTween(objectB, {bar: 100, time: 0.1}); Tweener.pauseAllTweens(); jasmine.clock().tick(10); Tweener.resumeAllTweens(); jasmine.clock().tick(101); expect(objectA.foo).toEqual(100); expect(objectB.bar).toEqual(100); }); it('can remove all tweens', function () { var objectA = { foo: 0, }; var objectB = { bar: 0, }; Tweener.addTween(objectA, {foo: 100, time: 0.1}); Tweener.addTween(objectB, {bar: 100, time: 0.1}); Tweener.removeAllTweens(); jasmine.clock().tick(200); expect(objectA.foo).toEqual(0); expect(objectB.bar).toEqual(0); }); it('runs a tween with a time of 0 immediately', function () { var object = { foo: 100, }; Tweener.addTween(object, {foo: 50, time: 0, delay: 0}); Tweener.addTween(object, { foo: 200, time: 0.1, onStart: () => { /* The immediate tween should set it to 50 before we run */ expect(object.foo).toEqual(50); }, }); jasmine.clock().tick(101); expect(object.foo).toEqual(200); }); it('can call a callback a certain number of times', function () { var object = { foo: 0, }; Tweener.addCaller(object, { onUpdate: () => { object.foo += 1; }, count: 10, time: 0.1, }); jasmine.clock().tick(101); expect(object.foo).toEqual(10); }); it('can count the number of tweens on an object', function () { var object = { foo: 0, bar: 0, baaz: 0, quux: 0, }; expect(Tweener.getTweenCount(object)).toEqual(0); Tweener.addTween(object, {foo: 100, time: 0.1}); expect(Tweener.getTweenCount(object)).toEqual(1); Tweener.addTween(object, {bar: 100, time: 0.1}); expect(Tweener.getTweenCount(object)).toEqual(2); Tweener.addTween(object, {baaz: 100, time: 0.1}); expect(Tweener.getTweenCount(object)).toEqual(3); Tweener.addTween(object, {quux: 100, time: 0.1}); expect(Tweener.getTweenCount(object)).toEqual(4); Tweener.removeTweens(object, 'bar', 'baaz'); expect(Tweener.getTweenCount(object)).toEqual(2); }); it('can register special properties', function () { Tweener.registerSpecialProperty( 'negative_x', function (obj) { return -obj.x; }, function (obj, val) { obj.x = -val; } ); var objectA = { x: 0, y: 0, }; Tweener.addTween(objectA, {negative_x: 10, y: 10, time: 1, transition: 'linear'}); jasmine.clock().tick(1001); expect(objectA.x).toEqual(-10); expect(objectA.y).toEqual(10); }); it('can register special modifiers for properties', function () { Tweener.registerSpecialPropertyModifier('discrete', discreteModifier, discreteGet); function discreteModifier(props) { return props.map(function (prop) { return {name: prop, parameters: null}; }); } function discreteGet(begin, end, time) { return Math.floor(begin + time * (end - begin)); } var objectA = { x: 0, y: 0, xFraction: false, yFraction: false, }; Tweener.addTween(objectA, { x: 10, y: 10, time: 1, discrete: ['x'], transition: 'linear', onUpdate() { if (objectA.x !== Math.floor(objectA.x)) objectA.xFraction = true; if (objectA.y !== Math.floor(objectA.y)) objectA.yFraction = true; }, }); jasmine.clock().tick(1001); expect(objectA.x).toEqual(10); expect(objectA.y).toEqual(10); expect(objectA.xFraction).toBeFalsy(); expect(objectA.yFraction).toBeTruthy(); }); it('can split properties into more than one special property', function () { Tweener.registerSpecialPropertySplitter( 'xnegy', function (val) { return [{name: 'x', value: val}, {name: 'y', value: -val}]; } ); var objectA = { x: 0, y: 0, }; Tweener.addTween(objectA, {xnegy: 10, time: 1, transition: 'linear'}); jasmine.clock().tick(1001); expect(objectA.x).toEqual(10); expect(objectA.y).toEqual(-10); }); it('calls an overwrite callback when a tween is replaced', function () { var object = { a: 0, b: 0, c: 0, d: 0, }; var tweenA = { a: 10, b: 10, c: 10, d: 10, time: 0.1, onStart: start, onOverwrite: overwrite, onComplete: complete, }; var tweenB = { a: 20, b: 20, c: 20, d: 20, time: 0.1, onStart: start, onOverwrite: overwrite, onComplete: complete, }; Tweener.addTween(object, tweenA); Tweener.addTween(object, tweenB); jasmine.clock().tick(101); expect(start).toHaveBeenCalledTimes(1); expect(overwrite).toHaveBeenCalledTimes(1); expect(complete).toHaveBeenCalledTimes(1); }); it('can still overwrite a tween after it has started', function () { var object = { a: 0, b: 0, c: 0, d: 0, }; var tweenA = { a: 10, b: 10, c: 10, d: 10, time: 0.1, onStart: () => { start(); Tweener.addTween(object, tweenB); }, onOverwrite: overwrite, onComplete: complete, }; var tweenB = { a: 20, b: 20, c: 20, d: 20, time: 0.1, onStart: start, onOverwrite: overwrite, onComplete: complete, }; Tweener.addTween(object, tweenA); jasmine.clock().tick(121); expect(start).toHaveBeenCalledTimes(2); expect(overwrite).toHaveBeenCalledTimes(1); expect(complete).toHaveBeenCalledTimes(1); }); it('stays within min and max values', function () { var objectA = { x: 0, y: 0, }; var objectB = { x: 0, y: 0, }; Tweener.addTween(objectA, {x: 300, y: 300, time: 1, max: 255, transition: 'linear'}); Tweener.addTween(objectB, {x: -200, y: -200, time: 1, delay: 0.5, min: 0, transition: 'linear'}); jasmine.clock().tick(1001); expect(objectA.x).toEqual(255); expect(objectA.y).toEqual(255); expect(objectB.x).toEqual(0); expect(objectB.y).toEqual(0); }); }); cjs-128.0/installed-tests/js/testself.js0000664000175000017500000000360114771557763017177 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2008 litl, LLC describe('Test harness internal consistency', function () { it('', function () { var someUndefined; var someNumber = 1; var someOtherNumber = 42; var someString = 'hello'; var someOtherString = 'world'; expect(true).toBeTruthy(); expect(false).toBeFalsy(); expect(someNumber).toEqual(someNumber); expect(someString).toEqual(someString); expect(someNumber).not.toEqual(someOtherNumber); expect(someString).not.toEqual(someOtherString); expect(null).toBeNull(); expect(someNumber).not.toBeNull(); expect(someNumber).toBeDefined(); expect(someUndefined).not.toBeDefined(); expect(0 / 0).toBeNaN(); expect(someNumber).not.toBeNaN(); expect(() => { throw new Error(); }).toThrow(); expect(() => expect(true).toThrow()).toThrow(); expect(() => true).not.toThrow(); }); describe('awaiting', function () { it('a Promise resolves', async function () { await Promise.resolve(); expect(true).toBe(true); }); async function nested() { await Promise.resolve(); } it('a nested async function resolves', async function () { await nested(); expect(true).toBe(true); }); }); }); describe('SpiderMonkey features check', function () { it('Intl API was compiled into SpiderMonkey', function () { expect(Intl).toBeDefined(); }); it('WeakRef is enabled', function () { expect(WeakRef).toBeDefined(); }); it('class static blocks are enabled', function () { class Test { static { Test.x = 4; } } expect(Test.x).toBe(4); }); }); cjs-128.0/installed-tests/js/testFormat.js0000664000175000017500000000417314771557763017503 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2013 Red Hat, Inc. // eslint-disable-next-line no-restricted-properties const Format = imports.format; String.prototype.format = Format.format; describe('imports.format', function () { it('escapes % with another % character', function () { expect('%d%%'.format(10)).toEqual('10%'); }); it('formats a single string argument', function () { expect('%s'.format('Foo')).toEqual('Foo'); }); it('formats two string arguments', function () { expect('%s %s'.format('Foo', 'Bar')).toEqual('Foo Bar'); }); it('formats two swapped string arguments', function () { expect('%2$s %1$s'.format('Foo', 'Bar')).toEqual('Bar Foo'); }); it('formats a number in base 10', function () { expect('%d'.format(42)).toEqual('42'); }); it('formats a number in base 16', function () { expect('%x'.format(42)).toEqual('2a'); }); it('formats a floating point number with no precision', function () { expect('%f'.format(0.125)).toEqual('0.125'); }); it('formats a floating point number with precision 2', function () { expect('%.2f'.format(0.125)).toEqual('0.13'); }); it('pads with zeroes', function () { let zeroFormat = '%04d'; expect(zeroFormat.format(1)).toEqual('0001'); expect(zeroFormat.format(10)).toEqual('0010'); expect(zeroFormat.format(100)).toEqual('0100'); }); it('pads with spaces', function () { let spaceFormat = '%4d'; expect(spaceFormat.format(1)).toEqual(' 1'); expect(spaceFormat.format(10)).toEqual(' 10'); expect(spaceFormat.format(100)).toEqual(' 100'); }); it('throws an error when given incorrect modifiers for the conversion type', function () { expect(() => '%z'.format(42)).toThrow(); expect(() => '%.2d'.format(42)).toThrow(); expect(() => '%Ix'.format(42)).toThrow(); }); it('throws an error when incorrectly instructed to swap arguments', function () { expect(() => '%2$d %d %1$d'.format(1, 2, 3)).toThrow(); }); }); cjs-128.0/installed-tests/js/.eslintrc.yml0000664000175000017500000000327314771557763017441 0ustar fabiofabio--- # SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2018 Philip Chimento env: jasmine: true rules: no-restricted-globals: - error - name: fdescribe message: Do not commit fdescribe(). Use describe() instead. - name: fit message: Do not commit fit(). Use it() instead. no-restricted-syntax: - error - selector: CallExpression[callee.name="it"] > ArrowFunctionExpression message: Arrow functions can mess up some Jasmine APIs. Use function () instead - selector: CallExpression[callee.name="beforeEach"] > ArrowFunctionExpression message: Arrow functions can mess up some Jasmine APIs. Use function () instead - selector: CallExpression[callee.name="afterEach"] > ArrowFunctionExpression message: Arrow functions can mess up some Jasmine APIs. Use function () instead - selector: CallExpression[callee.name="beforeAll"] > ArrowFunctionExpression message: Arrow functions can mess up some Jasmine APIs. Use function () instead - selector: CallExpression[callee.name="afterAll"] > ArrowFunctionExpression message: Arrow functions can mess up some Jasmine APIs. Use function () instead overrides: - files: - matchers.js - minijasmine.js - minijasmine-executor.js - testAsync.js - testAsyncMainloop.js - testCairoModule.js - testConsole.js - testESModules.js - testEncoding.js - testGLibLogWriter.js - testTimers.js - testWeakRef.js - modules/importmeta.js - modules/exports.js - modules/greet.js - modules/say.js - modules/sideEffect4.js parserOptions: sourceType: module cjs-128.0/installed-tests/js/libgjstesttools/0000775000175000017500000000000014771557763020243 5ustar fabiofabiocjs-128.0/installed-tests/js/libgjstesttools/meson.build0000664000175000017500000000240414771557763022405 0ustar fabiofabio# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2021 Marco Trevisan libgjstesttools_extra_cpp_args = [] if cc.get_argument_syntax() == 'msvc' # We need to ensure the symbols in the test DLLs export in clang-cl builds libgjstesttools_extra_cpp_args += ['-DGJS_TEST_TOOL_EXTERN=__declspec(dllexport)extern'] endif gjstest_tools_sources = [ 'gjs-test-tools.cpp', 'gjs-test-tools.h', ] libgjstesttools = library('gjstesttools', sources: gjstest_tools_sources, include_directories: top_include, dependencies: libcjs_dep, cpp_args: libcjs_cpp_args + libgjstesttools_extra_cpp_args, install: get_option('installed_tests'), install_dir: installed_tests_execdir) gjstest_tools_gir = gnome.generate_gir(libgjstesttools, includes: ['GObject-2.0', 'Gio-2.0'], sources: gjstest_tools_sources, namespace: 'CjsTestTools', nsversion: '1.0', symbol_prefix: 'gjs_test_tools_', fatal_warnings: get_option('werror'), install: get_option('installed_tests'), install_gir: false, install_dir_typelib: installed_tests_execdir) gjstest_tools_typelib = gjstest_tools_gir[1] libgjstesttools_dep = declare_dependency( link_with: libgjstesttools, include_directories: include_directories('.')) cjs-128.0/installed-tests/js/libgjstesttools/gjs-test-tools.cpp0000664000175000017500000002512614771557763023653 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2021 Marco Trevisan #include "installed-tests/js/libgjstesttools/gjs-test-tools.h" #include #include #include "cjs/jsapi-util.h" #ifdef G_OS_UNIX # include # include /* for FD_CLOEXEC */ # include # include /* for close, write */ # include /* for g_unix_open_pipe */ #endif static std::atomic s_tmp_object = nullptr; static GWeakRef s_tmp_weak; static std::unordered_set s_finalized_objects; static std::mutex s_finalized_objects_lock; struct FinalizedObjectsLocked { FinalizedObjectsLocked() : hold(s_finalized_objects_lock) {} std::unordered_set* operator->() { return &s_finalized_objects; } std::lock_guard hold; }; void gjs_test_tools_init() {} void gjs_test_tools_reset() { gjs_test_tools_clear_saved(); g_weak_ref_set(&s_tmp_weak, nullptr); FinalizedObjectsLocked()->clear(); } void gjs_test_tools_ref(GObject* object) { g_object_ref(object); } void gjs_test_tools_unref(GObject* object) { g_object_unref(object); } // clang-format off static G_DEFINE_QUARK(gjs-test-utils::finalize, finalize); // clang-format on static void monitor_object_finalization(GObject* object) { g_object_steal_qdata(object, finalize_quark()); g_object_set_qdata_full(object, finalize_quark(), object, [](void* data) { FinalizedObjectsLocked()->insert(static_cast(data)); }); } void gjs_test_tools_delayed_ref(GObject* object, int interval) { g_timeout_add( interval, [](void *data) { g_object_ref(G_OBJECT(data)); return G_SOURCE_REMOVE; }, object); } void gjs_test_tools_delayed_unref(GObject* object, int interval) { g_timeout_add( interval, [](void *data) { g_object_unref(G_OBJECT(data)); return G_SOURCE_REMOVE; }, object); } void gjs_test_tools_delayed_dispose(GObject* object, int interval) { g_timeout_add( interval, [](void *data) { g_object_run_dispose(G_OBJECT(data)); return G_SOURCE_REMOVE; }, object); } void gjs_test_tools_save_object(GObject* object) { g_object_ref(object); gjs_test_tools_save_object_unreffed(object); } void gjs_test_tools_save_object_unreffed(GObject* object) { GObject* expected = nullptr; g_assert_true(s_tmp_object.compare_exchange_strong(expected, object)); } void gjs_test_tools_clear_saved() { if (!FinalizedObjectsLocked()->count(s_tmp_object)) { auto* object = s_tmp_object.exchange(nullptr); g_clear_object(&object); } else { s_tmp_object = nullptr; } } void gjs_test_tools_ref_other_thread(GObject* object, GError** error) { auto* thread = g_thread_try_new("ref_object", g_object_ref, object, error); if (thread) g_thread_join(thread); // cppcheck-suppress memleak } typedef enum { REF = 1 << 0, UNREF = 1 << 1, } RefType; typedef struct { GObject* object; RefType ref_type; int delay; } RefThreadData; static RefThreadData* ref_thread_data_new(GObject* object, int interval, RefType ref_type) { auto* ref_data = g_new(RefThreadData, 1); ref_data->object = object; ref_data->delay = interval; ref_data->ref_type = ref_type; monitor_object_finalization(object); return ref_data; } static void* ref_thread_func(void* data) { GjsAutoPointer ref_data = static_cast(data); if (FinalizedObjectsLocked()->count(ref_data->object)) return nullptr; if (ref_data->delay > 0) g_usleep(ref_data->delay); if (FinalizedObjectsLocked()->count(ref_data->object)) return nullptr; if (ref_data->ref_type & REF) g_object_ref(ref_data->object); if (!(ref_data->ref_type & UNREF)) { return ref_data->object; } else if (ref_data->ref_type & REF) { g_usleep(ref_data->delay); if (FinalizedObjectsLocked()->count(ref_data->object)) return nullptr; } if (ref_data->object != s_tmp_object) g_object_steal_qdata(ref_data->object, finalize_quark()); g_object_unref(ref_data->object); return nullptr; } void gjs_test_tools_unref_other_thread(GObject* object, GError** error) { auto* thread = g_thread_try_new("unref_object", ref_thread_func, ref_thread_data_new(object, -1, UNREF), error); if (thread) g_thread_join(thread); // cppcheck-suppress memleak } /** * gjs_test_tools_delayed_ref_other_thread: * Returns: (transfer full) */ GThread* gjs_test_tools_delayed_ref_other_thread(GObject* object, int interval, GError** error) { return g_thread_try_new("ref_object", ref_thread_func, ref_thread_data_new(object, interval, REF), error); } /** * gjs_test_tools_delayed_unref_other_thread: * Returns: (transfer full) */ GThread* gjs_test_tools_delayed_unref_other_thread(GObject* object, int interval, GError** error) { return g_thread_try_new("unref_object", ref_thread_func, ref_thread_data_new(object, interval, UNREF), error); } /** * gjs_test_tools_delayed_ref_unref_other_thread: * Returns: (transfer full) */ GThread* gjs_test_tools_delayed_ref_unref_other_thread(GObject* object, int interval, GError** error) { return g_thread_try_new( "ref_unref_object", ref_thread_func, ref_thread_data_new(object, interval, static_cast(REF | UNREF)), error); } void gjs_test_tools_run_dispose_other_thread(GObject* object, GError** error) { auto* thread = g_thread_try_new( "run_dispose", [](void* object) -> void* { g_object_run_dispose(G_OBJECT(object)); return nullptr; }, object, error); // cppcheck-suppress leakNoVarFunctionCall g_thread_join(thread); // cppcheck-suppress memleak } /** * gjs_test_tools_get_saved: * Returns: (transfer full) */ GObject* gjs_test_tools_get_saved() { if (FinalizedObjectsLocked()->count(s_tmp_object)) s_tmp_object = nullptr; return s_tmp_object.exchange(nullptr); } /** * gjs_test_tools_steal_saved: * Returns: (transfer none) */ GObject* gjs_test_tools_steal_saved() { return gjs_test_tools_get_saved(); } void gjs_test_tools_save_weak(GObject* object) { g_weak_ref_set(&s_tmp_weak, object); } /** * gjs_test_tools_peek_saved: * Returns: (transfer none) */ GObject* gjs_test_tools_peek_saved() { if (FinalizedObjectsLocked()->count(s_tmp_object)) return nullptr; return s_tmp_object; } int gjs_test_tools_get_saved_ref_count() { GObject* saved = gjs_test_tools_peek_saved(); return saved ? saved->ref_count : 0; } /** * gjs_test_tools_get_weak: * Returns: (transfer full) */ GObject* gjs_test_tools_get_weak() { return static_cast(g_weak_ref_get(&s_tmp_weak)); } /** * gjs_test_tools_get_weak_other_thread: * Returns: (transfer full) */ GObject* gjs_test_tools_get_weak_other_thread(GError** error) { auto* thread = g_thread_try_new( "weak_get", [](void*) -> void* { return gjs_test_tools_get_weak(); }, NULL, error); if (!thread) return nullptr; return static_cast( // cppcheck-suppress leakNoVarFunctionCall g_thread_join(thread)); } /** * gjs_test_tools_get_disposed: * Returns: (transfer none) */ GObject* gjs_test_tools_get_disposed(GObject* object) { g_object_run_dispose(G_OBJECT(object)); return object; } #ifdef G_OS_UNIX // Adapted from glnx_throw_errno_prefix() static gboolean throw_errno_prefix(GError** error, const char* prefix) { int errsv = errno; g_set_error_literal(error, G_IO_ERROR, g_io_error_from_errno(errsv), g_strerror(errsv)); g_prefix_error(error, "%s: ", prefix); errno = errsv; return FALSE; } #endif /* G_OS_UNIX */ /** * gjs_open_bytes: * @bytes: bytes to send to the pipe * @error: Return location for a #GError, or %NULL * * Creates a pipe and sends @bytes to it, such that it is suitable for passing * to g_subprocess_launcher_take_fd(). * * Returns: file descriptor, or -1 on error */ int gjs_test_tools_open_bytes(GBytes* bytes, GError** error) { int pipefd[2], result; size_t count; const void* buf; ssize_t bytes_written; g_return_val_if_fail(bytes, -1); g_return_val_if_fail(error == NULL || *error == NULL, -1); #ifdef G_OS_UNIX if (!g_unix_open_pipe(pipefd, FD_CLOEXEC, error)) return -1; buf = g_bytes_get_data(bytes, &count); bytes_written = write(pipefd[1], buf, count); if (bytes_written < 0) { throw_errno_prefix(error, "write"); return -1; } if ((size_t)bytes_written != count) g_warning("%s: %zu bytes sent, only %zd bytes written", __func__, count, bytes_written); result = close(pipefd[1]); if (result == -1) { throw_errno_prefix(error, "close"); return -1; } return pipefd[0]; #else g_error("%s is currently supported on UNIX only", __func__); #endif } /** * gjs_test_tools_new_unaligned_bytes: * @len: Length of buffer to allocate * * Creates a data buffer at a location 1 byte away from an 8-byte alignment * boundary, to make sure that tests fail when SpiderMonkey enforces an * alignment restriction on embedder data. * * The buffer is filled with repeated 0x00-0x07 bytes containing the least * significant 3 bits of that byte's address. * * Returns: (transfer full): a #GBytes */ GBytes* gjs_test_tools_new_unaligned_bytes(size_t len) { auto* buffer = static_cast(g_aligned_alloc0(1, len + 1, 8)); for (size_t ix = 0; ix < len + 1; ix++) { buffer[ix] = reinterpret_cast(buffer + ix) & 0x07; } return g_bytes_new_with_free_func(buffer + 1, len, g_aligned_free, buffer); } alignas(8) static const char static_bytes[] = "hello"; /** * gjs_test_tools_new_static_bytes: * * Returns a buffer that lives in static storage. * * Returns: (transfer full): a #GBytes */ GBytes* gjs_test_tools_new_static_bytes() { return g_bytes_new_static(static_bytes, 6); } cjs-128.0/installed-tests/js/libgjstesttools/gjs-test-tools.h0000664000175000017500000000531214771557763023313 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2021 Marco Trevisan #pragma once #ifndef GJS_TEST_TOOL_EXTERN # define GJS_TEST_TOOL_EXTERN #endif #include #include #include G_BEGIN_DECLS GJS_TEST_TOOL_EXTERN void gjs_test_tools_init(void); GJS_TEST_TOOL_EXTERN void gjs_test_tools_reset(void); GJS_TEST_TOOL_EXTERN void gjs_test_tools_ref(GObject* object); GJS_TEST_TOOL_EXTERN void gjs_test_tools_unref(GObject* object); GJS_TEST_TOOL_EXTERN void gjs_test_tools_delayed_ref(GObject* object, int interval); GJS_TEST_TOOL_EXTERN void gjs_test_tools_delayed_unref(GObject* object, int interval); GJS_TEST_TOOL_EXTERN void gjs_test_tools_delayed_dispose(GObject* object, int interval); GJS_TEST_TOOL_EXTERN void gjs_test_tools_ref_other_thread(GObject* object, GError** error); GJS_TEST_TOOL_EXTERN GThread* gjs_test_tools_delayed_ref_other_thread(GObject* object, int interval, GError** error); GJS_TEST_TOOL_EXTERN void gjs_test_tools_unref_other_thread(GObject* object, GError** error); GJS_TEST_TOOL_EXTERN GThread* gjs_test_tools_delayed_unref_other_thread(GObject* object, int interval, GError** error); GJS_TEST_TOOL_EXTERN GThread* gjs_test_tools_delayed_ref_unref_other_thread(GObject* object, int interval, GError** error); GJS_TEST_TOOL_EXTERN void gjs_test_tools_run_dispose_other_thread(GObject* object, GError** error); GJS_TEST_TOOL_EXTERN void gjs_test_tools_save_object(GObject* object); GJS_TEST_TOOL_EXTERN void gjs_test_tools_save_object_unreffed(GObject* object); GJS_TEST_TOOL_EXTERN GObject* gjs_test_tools_get_saved(); GJS_TEST_TOOL_EXTERN GObject* gjs_test_tools_steal_saved(); GJS_TEST_TOOL_EXTERN GObject* gjs_test_tools_peek_saved(); GJS_TEST_TOOL_EXTERN int gjs_test_tools_get_saved_ref_count(); GJS_TEST_TOOL_EXTERN void gjs_test_tools_clear_saved(); GJS_TEST_TOOL_EXTERN void gjs_test_tools_save_weak(GObject* object); GJS_TEST_TOOL_EXTERN GObject* gjs_test_tools_get_weak(); GJS_TEST_TOOL_EXTERN GObject* gjs_test_tools_get_weak_other_thread(GError** error); GJS_TEST_TOOL_EXTERN GObject* gjs_test_tools_get_disposed(GObject* object); GJS_TEST_TOOL_EXTERN int gjs_test_tools_open_bytes(GBytes* bytes, GError** error); GJS_TEST_TOOL_EXTERN GBytes* gjs_test_tools_new_unaligned_bytes(size_t len); GJS_TEST_TOOL_EXTERN GBytes* gjs_test_tools_new_static_bytes(); G_END_DECLS cjs-128.0/installed-tests/js/testLegacyGtk.js0000664000175000017500000001156114771557763020124 0ustar fabiofabio// -*- mode: js; indent-tabs-mode: nil -*- /* eslint-disable no-restricted-properties */ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2013 Giovanni Campagna imports.gi.versions.Gtk = '3.0'; const {GLib, Gtk} = imports.gi; const Lang = imports.lang; const System = imports.system; const template = ` `; const MyComplexGtkSubclass = new Lang.Class({ Name: 'MyComplexGtkSubclass', Extends: Gtk.Grid, Template: new TextEncoder().encode(template), Children: ['label-child', 'label-child2'], InternalChildren: ['internal-label-child'], CssName: 'complex-subclass', testChildrenExist() { 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 = new Lang.Class({ Name: 'MyComplexGtkSubclassFromResource', Extends: Gtk.Grid, Template: 'resource:///org/cjs/jsunit/complex3.ui', Children: ['label-child', 'label-child2'], InternalChildren: ['internal-label-child'], testChildrenExist() { expect(this.label_child).toEqual(jasmine.anything()); expect(this.label_child2).toEqual(jasmine.anything()); expect(this._internal_label_child).toEqual(jasmine.anything()); }, templateCallback() {}, boundCallback() {}, }); function validateTemplate(description, ClassName) { describe(description, function () { let win, content; beforeEach(function () { win = new Gtk.Window({type: Gtk.WindowType.TOPLEVEL}); content = new ClassName(); 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!'); }); afterEach(function () { win.destroy(); }); }); } describe('Legacy Gtk overrides', function () { beforeAll(function () { Gtk.init(null); }); validateTemplate('UI template', MyComplexGtkSubclass); validateTemplate('UI template from resource', MyComplexGtkSubclassFromResource); it('sets CSS names on classes', function () { expect(Gtk.Widget.get_css_name.call(MyComplexGtkSubclass)).toEqual('complex-subclass'); }); function asyncIdle() { return new Promise(resolve => { GLib.idle_add(GLib.PRIORITY_DEFAULT, () => { resolve(); return GLib.SOURCE_REMOVE; }); }); } it('does not leak instance when connecting template signal', async function () { const LeakTestWidget = new Lang.Class({ Name: 'LeakTestWidget', Extends: Gtk.Button, Template: new TextEncoder().encode(` `), buttonClicked() {}, }); const weakRef = new WeakRef(new LeakTestWidget()); await asyncIdle(); // It takes two GC cycles to free the widget, because of the tardy sweep // problem (https://gitlab.gnome.org/GNOME/gjs/-/issues/217) System.gc(); System.gc(); expect(weakRef.deref()).toBeUndefined(); }); }); cjs-128.0/installed-tests/js/testGObjectClass.js0000664000175000017500000020143714771557763020560 0ustar fabiofabio// -*- mode: js; indent-tabs-mode: nil -*- // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2011 Giovanni Campagna const System = imports.system; imports.gi.versions.Gtk = '3.0'; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const Gtk = imports.gi.Gtk; const MyObject = GObject.registerClass({ Properties: { 'readwrite': GObject.ParamSpec.string('readwrite', 'ParamReadwrite', 'A read write parameter', GObject.ParamFlags.READWRITE, ''), 'readonly': GObject.ParamSpec.string('readonly', 'ParamReadonly', 'A readonly parameter', GObject.ParamFlags.READABLE, ''), 'construct': GObject.ParamSpec.string('construct', 'ParamConstructOnly', 'A readwrite construct-only parameter', GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, 'default'), }, Signals: { 'empty': {}, 'minimal': {param_types: [GObject.TYPE_INT, GObject.TYPE_INT]}, 'full': { flags: GObject.SignalFlags.RUN_LAST, accumulator: GObject.AccumulatorType.FIRST_WINS, return_type: GObject.TYPE_INT, param_types: [], }, 'run-last': {flags: GObject.SignalFlags.RUN_LAST}, 'detailed': { flags: GObject.SignalFlags.RUN_FIRST | GObject.SignalFlags.DETAILED, param_types: [GObject.TYPE_STRING], }, }, }, class MyObject extends GObject.Object { get readwrite() { if (typeof this._readwrite === 'undefined') return 'foo'; return this._readwrite; } set readwrite(val) { if (val === 'ignore') return; this._readwrite = val; } get readonly() { if (typeof this._readonly === 'undefined') return 'bar'; return this._readonly; } set readonly(val) { // this should never be called void val; this._readonly = 'bogus'; } get construct() { if (typeof this._constructProp === 'undefined') return null; return this._constructProp; } set construct(val) { this._constructProp = val; } notifyProp() { this._readonly = 'changed'; this.notify('readonly'); } emitEmpty() { this.emit('empty'); } emitMinimal(one, two) { this.emit('minimal', one, two); } emitFull() { return this.emit('full'); } emitDetailed() { this.emit('detailed::one'); this.emit('detailed::two'); } emitRunLast(callback) { this._run_last_callback = callback; this.emit('run-last'); } on_run_last() { this._run_last_callback(); } on_empty() { this.empty_called = true; } on_full() { this.full_default_handler_called = true; return 79; } }); const MyObjectWithCustomConstructor = GObject.registerClass({ Properties: { 'readwrite': GObject.ParamSpec.string('readwrite', 'ParamReadwrite', 'A read write parameter', GObject.ParamFlags.READWRITE, ''), 'readonly': GObject.ParamSpec.string('readonly', 'ParamReadonly', 'A readonly parameter', GObject.ParamFlags.READABLE, ''), 'construct': GObject.ParamSpec.string('construct', 'ParamConstructOnly', 'A readwrite construct-only parameter', GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, ''), }, Signals: { 'empty': {}, 'minimal': {param_types: [GObject.TYPE_INT, GObject.TYPE_INT]}, 'full': { flags: GObject.SignalFlags.RUN_LAST, accumulator: GObject.AccumulatorType.FIRST_WINS, return_type: GObject.TYPE_INT, param_types: [], }, 'run-last': {flags: GObject.SignalFlags.RUN_LAST}, 'detailed': { flags: GObject.SignalFlags.RUN_FIRST | GObject.SignalFlags.DETAILED, param_types: [GObject.TYPE_STRING], }, }, }, class MyObjectWithCustomConstructor extends GObject.Object { _readwrite; _readonly; _constructProp; constructor({readwrite = 'foo', readonly = 'bar', construct = 'default'} = {}) { super(); this._constructProp = construct; this._readwrite = readwrite; this._readonly = readonly; } get readwrite() { return this._readwrite; } set readwrite(val) { if (val === 'ignore') return; this._readwrite = val; } get readonly() { return this._readonly; } set readonly(val) { // this should never be called void val; this._readonly = 'bogus'; } get construct() { return this._constructProp; } notifyProp() { this._readonly = 'changed'; this.notify('readonly'); } emitEmpty() { this.emit('empty'); } emitMinimal(one, two) { this.emit('minimal', one, two); } emitFull() { return this.emit('full'); } emitDetailed() { this.emit('detailed::one'); this.emit('detailed::two'); } emitRunLast(callback) { this._run_last_callback = callback; this.emit('run-last'); } on_run_last() { this._run_last_callback(); } on_empty() { this.empty_called = true; } on_full() { this.full_default_handler_called = true; return 79; } }); const MyAbstractObject = GObject.registerClass({ GTypeFlags: GObject.TypeFlags.ABSTRACT, }, class MyAbstractObject extends GObject.Object { }); const MyFinalObject = GObject.registerClass({ GTypeFlags: GObject.TypeFlags.FINAL, }, class extends GObject.Object { }); const MyApplication = GObject.registerClass({ Signals: {'custom': {param_types: [GObject.TYPE_INT]}}, }, class MyApplication extends Gio.Application { emitCustom(n) { this.emit('custom', n); } }); const MyInitable = GObject.registerClass({ Implements: [Gio.Initable], }, class MyInitable extends GObject.Object { vfunc_init(cancellable) { if (!(cancellable instanceof Gio.Cancellable)) throw new Error('Bad argument'); this.inited = true; } }); const Derived = GObject.registerClass(class Derived extends MyObject { _init() { super._init({readwrite: 'yes'}); } }); const DerivedWithCustomConstructor = GObject.registerClass(class DerivedWithCustomConstructor extends MyObjectWithCustomConstructor { constructor() { super({readwrite: 'yes'}); } }); const ObjectWithDefaultConstructor = GObject.registerClass(class ObjectWithDefaultConstructor extends GObject.Object {}); const Cla$$ = GObject.registerClass(class Cla$$ extends MyObject {}); const MyCustomInit = GObject.registerClass(class MyCustomInit extends GObject.Object { _instance_init() { this.foo = true; } }); const NoName = GObject.registerClass(class extends GObject.Object {}); describe('GObject class with decorator', function () { let myInstance; beforeEach(function () { myInstance = new MyObject(); }); it('throws an error when not used with a GObject-derived class', function () { class Foo {} expect(() => GObject.registerClass(class Bar extends Foo {})).toThrow(); }); it('throws an error when used with an abstract class', function () { expect(() => new MyAbstractObject()).toThrow(); }); it('throws if final class is inherited from', function () { try { GObject.registerClass(class extends MyFinalObject {}); fail(); } catch (e) { expect(e.message).toEqual('Cannot inherit from a final type'); } }); it('constructs with default values for properties', function () { expect(myInstance.readwrite).toEqual('foo'); expect(myInstance.readonly).toEqual('bar'); expect(myInstance.construct).toEqual('default'); }); it('constructs with a hash of property values', function () { let myInstance2 = new MyObject({readwrite: 'baz', construct: 'asdf'}); expect(myInstance2.readwrite).toEqual('baz'); expect(myInstance2.readonly).toEqual('bar'); expect(myInstance2.construct).toEqual('asdf'); }); it('warns if more than one argument passed to the default constructor', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_MESSAGE, '*Too many arguments*'); new ObjectWithDefaultConstructor({}, 'this is ignored', 123); GLib.test_assert_expected_messages_internal('Cjs', 'testGObjectClass.js', 0, 'testGObjectClassTooManyArguments'); }); it('throws an error if the first argument to the default constructor is not a property hash', function () { expect(() => new MyObject('this is wrong')).toThrow(); }); it('does not accept a property hash that is not a plain object', function () { expect(() => new MyObject(new GObject.Object())).toThrow(); }); const ui = ` baz quz `; it('constructs with property values from Gtk.Builder', function () { let builder = Gtk.Builder.new_from_string(ui, -1); let myInstance3 = builder.get_object('MyObject'); expect(myInstance3.readwrite).toEqual('baz'); expect(myInstance3.readonly).toEqual('bar'); expect(myInstance3.construct).toEqual('quz'); }); it('does not allow changing CONSTRUCT_ONLY properties', function () { myInstance.construct = 'val'; expect(myInstance.construct).toEqual('default'); }); it('has a name', function () { expect(MyObject.name).toEqual('MyObject'); }); // the following would (should) cause a CRITICAL: // myInstance.readonly = 'val'; it('has a notify signal', function () { let notifySpy = jasmine.createSpy('notifySpy'); myInstance.connect('notify::readonly', notifySpy); myInstance.notifyProp(); myInstance.notifyProp(); expect(notifySpy).toHaveBeenCalledTimes(2); }); function asyncIdle() { return new Promise(resolve => { GLib.idle_add(GLib.PRIORITY_DEFAULT, () => { resolve(); return GLib.SOURCE_REMOVE; }); }); } it('disconnects connect_object signals on destruction', async function () { let callback = jasmine.createSpy('callback'); callback.myInstance = myInstance; const instance2 = new MyObject(); instance2.connect_object('empty', callback, myInstance, 0); instance2.emitEmpty(); instance2.emitEmpty(); expect(callback).toHaveBeenCalledTimes(2); const weakRef = new WeakRef(myInstance); myInstance = null; callback = null; await asyncIdle(); System.gc(); expect(weakRef.deref()).toBeUndefined(); }); it('can define its own signals', function () { let emptySpy = jasmine.createSpy('emptySpy'); myInstance.connect('empty', emptySpy); myInstance.emitEmpty(); expect(emptySpy).toHaveBeenCalled(); expect(myInstance.empty_called).toBeTruthy(); }); it('passes emitted arguments to signal handlers', function () { let minimalSpy = jasmine.createSpy('minimalSpy'); myInstance.connect('minimal', minimalSpy); myInstance.emitMinimal(7, 5); expect(minimalSpy).toHaveBeenCalledWith(myInstance, 7, 5); }); it('can return values from signals', function () { let fullSpy = jasmine.createSpy('fullSpy').and.returnValue(42); myInstance.connect('full', fullSpy); let result = myInstance.emitFull(); expect(fullSpy).toHaveBeenCalled(); expect(result).toEqual(42); }); it('does not call first-wins signal handlers after one returns a value', function () { let neverCalledSpy = jasmine.createSpy('neverCalledSpy'); myInstance.connect('full', () => 42); myInstance.connect('full', neverCalledSpy); myInstance.emitFull(); expect(neverCalledSpy).not.toHaveBeenCalled(); expect(myInstance.full_default_handler_called).toBeFalsy(); }); it('gets the return value of the default handler', function () { let result = myInstance.emitFull(); expect(myInstance.full_default_handler_called).toBeTruthy(); expect(result).toEqual(79); }); it('calls run-last default handler last', function () { let stack = []; let runLastSpy = jasmine.createSpy('runLastSpy') .and.callFake(() => { stack.push(1); }); myInstance.connect('run-last', runLastSpy); myInstance.emitRunLast(() => { stack.push(2); }); expect(stack).toEqual([1, 2]); }); it("can inherit from something that's not GObject.Object", function () { // ...and still get all the goodies of GObject.Class let instance = new MyApplication({application_id: 'org.gjs.Application'}); let customSpy = jasmine.createSpy('customSpy'); instance.connect('custom', customSpy); instance.emitCustom(73); expect(customSpy).toHaveBeenCalledWith(instance, 73); }); it('can implement an interface', function () { let instance = new MyInitable(); expect(instance instanceof Gio.Initable).toBeTruthy(); expect(instance instanceof Gio.AsyncInitable).toBeFalsy(); // Old syntax, backwards compatible expect(instance.constructor.implements(Gio.Initable)).toBeTruthy(); expect(instance.constructor.implements(Gio.AsyncInitable)).toBeFalsy(); }); it('can implement interface vfuncs', function () { let instance = new MyInitable(); expect(instance.inited).toBeFalsy(); instance.init(new Gio.Cancellable()); expect(instance.inited).toBeTruthy(); }); it('can be a subclass', function () { let derived = new Derived(); expect(derived instanceof Derived).toBeTruthy(); expect(derived instanceof MyObject).toBeTruthy(); expect(derived.readwrite).toEqual('yes'); }); it('can have any valid class name', function () { let obj = new Cla$$(); expect(obj instanceof Cla$$).toBeTruthy(); expect(obj instanceof MyObject).toBeTruthy(); }); it('handles anonymous class expressions', function () { const obj = new NoName(); expect(obj instanceof NoName).toBeTruthy(); const NoName2 = GObject.registerClass(class extends GObject.Object {}); const obj2 = new NoName2(); expect(obj2 instanceof NoName2).toBeTruthy(); }); it('calls its _instance_init() function while chaining up in constructor', function () { let instance = new MyCustomInit(); expect(instance.foo).toBeTruthy(); }); it('can have an interface-valued property', function () { const InterfacePropObject = GObject.registerClass({ Properties: { 'file': GObject.ParamSpec.object('file', 'File', 'File', GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, Gio.File.$gtype), }, }, class InterfacePropObject extends GObject.Object {}); let file = Gio.File.new_for_path('dummy'); expect(() => new InterfacePropObject({file})).not.toThrow(); }); it('can have an int64 property', function () { const PropInt64 = GObject.registerClass({ Properties: { 'int64': GObject.ParamSpec.int64('int64', 'int64', 'int64', GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, GLib.MININT64_BIGINT, GLib.MAXINT64_BIGINT, 0), }, }, class PropInt64 extends GObject.Object {}); let int64 = GLib.MAXINT64_BIGINT - 5n; let obj = new PropInt64({int64}); expect(obj.int64).toEqual(Number(int64)); int64 = GLib.MININT64_BIGINT + 555n; obj = new PropInt64({int64}); expect(obj.int64).toEqual(Number(int64)); }); it('can have a default int64 property', function () { const defaultValue = GLib.MAXINT64_BIGINT - 1000n; const PropInt64Init = GObject.registerClass({ Properties: { 'int64': GObject.ParamSpec.int64('int64', 'int64', 'int64', GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, GLib.MININT64_BIGINT, GLib.MAXINT64_BIGINT, defaultValue), }, }, class PropDefaultInt64Init extends GObject.Object {}); const obj = new PropInt64Init(); expect(obj.int64).toEqual(Number(defaultValue)); }); it('can have an uint64 property', function () { const PropUint64 = GObject.registerClass({ Properties: { 'uint64': GObject.ParamSpec.uint64('uint64', 'uint64', 'uint64', GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, 0, GLib.MAXUINT64_BIGINT, 0), }, }, class PropUint64 extends GObject.Object {}); const uint64 = GLib.MAXUINT64_BIGINT - 5n; const obj = new PropUint64({uint64}); expect(obj.uint64).toEqual(Number(uint64)); }); it('can have a default uint64 property', function () { const defaultValue = GLib.MAXUINT64_BIGINT; const PropUint64Init = GObject.registerClass({ Properties: { 'uint64': GObject.ParamSpec.uint64('uint64', 'uint64', 'uint64', GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, 0n, GLib.MAXUINT64_BIGINT, defaultValue), }, }, class PropDefaultUint64Init extends GObject.Object {}); const obj = new PropUint64Init(); expect(obj.uint64).toEqual(Number(defaultValue)); }); it('can override a property from the parent class', function () { const OverrideObject = GObject.registerClass({ Properties: { 'readwrite': GObject.ParamSpec.override('readwrite', MyObject), }, }, class OverrideObject extends MyObject { get readwrite() { return this._subclass_readwrite; } set readwrite(val) { this._subclass_readwrite = `subclass${val}`; } }); let obj = new OverrideObject(); obj.readwrite = 'foo'; expect(obj.readwrite).toEqual('subclassfoo'); }); it('cannot override a non-existent property', function () { expect(() => GObject.registerClass({ Properties: { 'nonexistent': GObject.ParamSpec.override('nonexistent', GObject.Object), }, }, class BadOverride extends GObject.Object {})).toThrow(); }); it('handles gracefully forgetting to override a C property', function () { GLib.test_expect_message('GLib-GObject', GLib.LogLevelFlags.LEVEL_CRITICAL, "*Object class Gjs_ForgottenOverride doesn't implement property " + "'anchors' from interface 'GTlsFileDatabase'*"); // This is a random interface in Gio with a read-write property const ForgottenOverride = GObject.registerClass({ Implements: [Gio.TlsFileDatabase], }, class ForgottenOverride extends Gio.TlsDatabase {}); const obj = new ForgottenOverride(); expect(obj.anchors).not.toBeDefined(); expect(() => (obj.anchors = 'foo')).not.toThrow(); expect(obj.anchors).toEqual('foo'); GLib.test_assert_expected_messages_internal('Cjs', 'testGObjectClass.js', 0, 'testGObjectClassForgottenOverride'); }); it('handles gracefully overriding a C property but forgetting the accessors', function () { // This is a random interface in Gio with a read-write property const ForgottenAccessors = GObject.registerClass({ Implements: [Gio.TlsFileDatabase], Properties: { 'anchors': GObject.ParamSpec.override('anchors', Gio.TlsFileDatabase), }, }, class ForgottenAccessors extends Gio.TlsDatabase {}); const obj = new ForgottenAccessors(); expect(obj.anchors).toBeNull(); // the property's default value obj.anchors = 'foo'; expect(obj.anchors).toEqual('foo'); const ForgottenAccessors2 = GObject.registerClass(class ForgottenAccessors2 extends ForgottenAccessors {}); const obj2 = new ForgottenAccessors2(); expect(obj2.anchors).toBeNull(); obj2.anchors = 'foo'; expect(obj2.anchors).toEqual('foo'); }); it('does not pollute the wrong prototype with GObject properties', function () { const MyCustomCharset = GObject.registerClass(class MyCustomCharset extends Gio.CharsetConverter { _init() { super._init(); void this.from_charset; } }); const MySecondCustomCharset = GObject.registerClass(class MySecondCustomCharset extends GObject.Object { _init() { super._init(); this.from_charset = 'another value'; } }); expect(() => new MyCustomCharset() && new MySecondCustomCharset()).not.toThrow(); }); it('resolves properties from interfaces', function () { const mon = Gio.NetworkMonitor.get_default(); expect(mon.network_available).toBeDefined(); expect(mon.networkAvailable).toBeDefined(); expect(mon['network-available']).toBeDefined(); }); it('has a toString() defintion', function () { expect(myInstance.toString()).toMatch( /\[object instance wrapper GType:Gjs_MyObject jsobj@0x[a-f0-9]+ native@0x[a-f0-9]+\]/); expect(new Derived().toString()).toMatch( /\[object instance wrapper GType:Gjs_Derived jsobj@0x[a-f0-9]+ native@0x[a-f0-9]+\]/); }); it('does not clobber native parent interface vfunc definitions', function () { const resetImplementationSpy = jasmine.createSpy('vfunc_reset'); expect(() => { // This is a random interface in Gio with a virtual function GObject.registerClass({ // Forgotten interface // Implements: [Gio.Converter], }, class MyZlibConverter extends Gio.ZlibCompressor { vfunc_reset() { resetImplementationSpy(); } }); }).toThrowError('Gjs_MyZlibConverter does not implement Gio.Converter, add Gio.Converter to your implements array'); let potentiallyClobbered = new Gio.ZlibCompressor(); potentiallyClobbered.reset(); expect(resetImplementationSpy).not.toHaveBeenCalled(); }); it('does not clobber dynamic parent interface vfunc definitions', function () { const resetImplementationSpy = jasmine.createSpy('vfunc_reset'); const MyJSConverter = GObject.registerClass({ Implements: [Gio.Converter], }, class MyJSConverter extends GObject.Object { vfunc_reset() { } }); expect(() => { GObject.registerClass({ // Forgotten interface // Implements: [Gio.Converter], }, class MyBadConverter extends MyJSConverter { vfunc_reset() { resetImplementationSpy(); } }); }).toThrowError('Gjs_MyBadConverter does not implement Gio.Converter, add Gio.Converter to your implements array'); let potentiallyClobbered = new MyJSConverter(); potentiallyClobbered.reset(); expect(resetImplementationSpy).not.toHaveBeenCalled(); }); }); describe('GObject class with custom constructor', function () { let myInstance; beforeEach(function () { myInstance = new MyObjectWithCustomConstructor(); }); it('throws an error when not used with a GObject-derived class', function () { class Foo {} expect(() => GObject.registerClass(class Bar extends Foo {})).toThrow(); }); it('constructs with default values for properties', function () { expect(myInstance.readwrite).toEqual('foo'); expect(myInstance.readonly).toEqual('bar'); expect(myInstance.construct).toEqual('default'); }); it('has a toString() defintion', function () { expect(myInstance.toString()).toMatch( /\[object instance wrapper GType:Gjs_MyObjectWithCustomConstructor jsobj@0x[a-f0-9]+ native@0x[a-f0-9]+\]/); }); it('constructs with a hash of property values', function () { let myInstance2 = new MyObjectWithCustomConstructor({readwrite: 'baz', construct: 'asdf'}); expect(myInstance2.readwrite).toEqual('baz'); expect(myInstance2.readonly).toEqual('bar'); console.log(Object.getOwnPropertyDescriptor(myInstance2, 'construct')); expect(myInstance2.construct).toEqual('asdf'); }); it('accepts a property hash that is not a plain object', function () { expect(() => new MyObjectWithCustomConstructor(new GObject.Object())).not.toThrow(); }); const ui = ` baz quz `; it('constructs with property values from Gtk.Builder', function () { let builder = Gtk.Builder.new_from_string(ui, -1); let myInstance3 = builder.get_object('MyObject'); expect(myInstance3.readwrite).toEqual('baz'); expect(myInstance3.readonly).toEqual('bar'); expect(myInstance3.construct).toEqual('quz'); }); it('does not allow changing CONSTRUCT_ONLY properties', function () { myInstance.construct = 'val'; expect(myInstance.construct).toEqual('default'); }); it('has a name', function () { expect(MyObjectWithCustomConstructor.name).toEqual('MyObjectWithCustomConstructor'); }); it('has a notify signal', function () { let notifySpy = jasmine.createSpy('notifySpy'); myInstance.connect('notify::readonly', notifySpy); myInstance.notifyProp(); myInstance.notifyProp(); expect(notifySpy).toHaveBeenCalledTimes(2); }); it('can define its own signals', function () { let emptySpy = jasmine.createSpy('emptySpy'); myInstance.connect('empty', emptySpy); myInstance.emitEmpty(); expect(emptySpy).toHaveBeenCalled(); expect(myInstance.empty_called).toBeTruthy(); }); it('passes emitted arguments to signal handlers', function () { let minimalSpy = jasmine.createSpy('minimalSpy'); myInstance.connect('minimal', minimalSpy); myInstance.emitMinimal(7, 5); expect(minimalSpy).toHaveBeenCalledWith(myInstance, 7, 5); }); it('can return values from signals', function () { let fullSpy = jasmine.createSpy('fullSpy').and.returnValue(42); myInstance.connect('full', fullSpy); let result = myInstance.emitFull(); expect(fullSpy).toHaveBeenCalled(); expect(result).toEqual(42); }); it('does not call first-wins signal handlers after one returns a value', function () { let neverCalledSpy = jasmine.createSpy('neverCalledSpy'); myInstance.connect('full', () => 42); myInstance.connect('full', neverCalledSpy); myInstance.emitFull(); expect(neverCalledSpy).not.toHaveBeenCalled(); expect(myInstance.full_default_handler_called).toBeFalsy(); }); it('gets the return value of the default handler', function () { let result = myInstance.emitFull(); expect(myInstance.full_default_handler_called).toBeTruthy(); expect(result).toEqual(79); }); it('calls run-last default handler last', function () { let stack = []; let runLastSpy = jasmine.createSpy('runLastSpy') .and.callFake(() => { stack.push(1); }); myInstance.connect('run-last', runLastSpy); myInstance.emitRunLast(() => { stack.push(2); }); expect(stack).toEqual([1, 2]); }); it('can be a subclass', function () { let derived = new DerivedWithCustomConstructor(); expect(derived instanceof DerivedWithCustomConstructor).toBeTruthy(); expect(derived instanceof MyObjectWithCustomConstructor).toBeTruthy(); expect(derived.readwrite).toEqual('yes'); }); it('can override a property from the parent class', function () { const OverrideObjectWithCustomConstructor = GObject.registerClass({ Properties: { 'readwrite': GObject.ParamSpec.override('readwrite', MyObjectWithCustomConstructor), }, }, class OverrideObjectWithCustomConstructor extends MyObjectWithCustomConstructor { get readwrite() { return this._subclass_readwrite; } set readwrite(val) { this._subclass_readwrite = `subclass${val}`; } }); let obj = new OverrideObjectWithCustomConstructor(); obj.readwrite = 'foo'; expect(obj.readwrite).toEqual('subclassfoo'); }); }); describe('GObject virtual function', function () { it('can have its property read', function () { expect(GObject.Object.prototype.vfunc_constructed).toBeTruthy(); }); it('can have its property overridden with an anonymous function', function () { let callback; let key = 'vfunc_constructed'; class _SimpleTestClass1 extends GObject.Object {} if (GObject.Object.prototype.vfunc_constructed) { let parentFunc = GObject.Object.prototype.vfunc_constructed; _SimpleTestClass1.prototype[key] = function (...args) { parentFunc.call(this, ...args); callback('123'); }; } else { _SimpleTestClass1.prototype[key] = function () { callback('abc'); }; } callback = jasmine.createSpy('callback'); const SimpleTestClass1 = GObject.registerClass({GTypeName: 'SimpleTestClass1'}, _SimpleTestClass1); new SimpleTestClass1(); expect(callback).toHaveBeenCalledWith('123'); }); it('can access the parent prototype with super()', function () { let callback; class _SimpleTestClass2 extends GObject.Object { vfunc_constructed() { super.vfunc_constructed(); callback('vfunc_constructed'); } } callback = jasmine.createSpy('callback'); const SimpleTestClass2 = GObject.registerClass({GTypeName: 'SimpleTestClass2'}, _SimpleTestClass2); new SimpleTestClass2(); expect(callback).toHaveBeenCalledWith('vfunc_constructed'); }); it('handles non-existing properties', function () { const _SimpleTestClass3 = class extends GObject.Object {}; _SimpleTestClass3.prototype.vfunc_doesnt_exist = function () {}; if (GObject.Object.prototype.vfunc_doesnt_exist) fail('Virtual function should not exist'); expect(() => GObject.registerClass({GTypeName: 'SimpleTestClass3'}, _SimpleTestClass3)).toThrow(); }); it('gracefully bails out when overriding an unsupported vfunc type', function () { expect(() => GObject.registerClass({ Implements: [Gio.AsyncInitable], }, class Foo extends GObject.Object { vfunc_init_async() {} })).toThrow(); }); it('are defined also for static virtual functions', function () { const CustomEmptyGIcon = GObject.registerClass({ Implements: [Gio.Icon], }, class CustomEmptyGIcon extends GObject.Object {}); expect(Gio.Icon.deserialize).toBeInstanceOf(Function); expect(CustomEmptyGIcon.deserialize).toBe(Gio.Icon.deserialize); expect(Gio.Icon.new_for_string).toBeInstanceOf(Function); expect(CustomEmptyGIcon.new_for_string).toBe(Gio.Icon.new_for_string); }); }); describe('GObject creation using base classes without registered GType', function () { it('fails when trying to instantiate a class that inherits from a GObject type', function () { const BadInheritance = class extends GObject.Object {}; const BadDerivedInheritance = class extends Derived {}; expect(() => new BadInheritance()).toThrowError(/Tried to construct an object without a GType/); expect(() => new BadDerivedInheritance()).toThrowError(/Tried to construct an object without a GType/); }); it('fails when trying to register a GObject class that inherits from a non-GObject type', function () { const BadInheritance = class extends GObject.Object {}; expect(() => GObject.registerClass(class BadInheritanceDerived extends BadInheritance {})) .toThrowError(/Object 0x[a-f0-9]+ is not a subclass of GObject_Object, it's a Object/); }); }); describe('Register GType name', function () { beforeAll(function () { expect(GObject.gtypeNameBasedOnJSPath).toBeFalsy(); }); afterEach(function () { GObject.gtypeNameBasedOnJSPath = false; }); it('uses the class name', function () { const GTypeTestAutoName = GObject.registerClass( class GTypeTestAutoName extends GObject.Object { }); expect(GTypeTestAutoName.$gtype.name).toEqual( 'Gjs_GTypeTestAutoName'); }); it('uses the sanitized class name', function () { const GTypeTestAutoName = GObject.registerClass( class GTypeTestAutoCla$$Name extends GObject.Object { }); expect(GTypeTestAutoName.$gtype.name).toEqual( 'Gjs_GTypeTestAutoCla__Name'); }); it('use the file path and class name', function () { GObject.gtypeNameBasedOnJSPath = true; const GTypeTestAutoName = GObject.registerClass( class GTypeTestAutoName extends GObject.Object {}); /* Update this test if the file is moved */ expect(GTypeTestAutoName.$gtype.name).toEqual( 'Gjs_js_testGObjectClass_GTypeTestAutoName'); }); it('use the file path and sanitized class name', function () { GObject.gtypeNameBasedOnJSPath = true; const GTypeTestAutoName = GObject.registerClass( class GTypeTestAutoCla$$Name extends GObject.Object { }); /* Update this test if the file is moved */ expect(GTypeTestAutoName.$gtype.name).toEqual( 'Gjs_js_testGObjectClass_GTypeTestAutoCla__Name'); }); it('use provided class name', function () { const GtypeClass = GObject.registerClass({ GTypeName: 'GTypeTestManualName', }, class extends GObject.Object {}); expect(GtypeClass.$gtype.name).toEqual('GTypeTestManualName'); }); it('sanitizes user provided class name', function () { let gtypeName = 'GType$Test/WithLòt\'s of*bad§chars!'; let expectedSanitized = 'GType_Test_WithL_t_s_of_bad_chars_'; GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, `*RangeError: Provided GType name '${gtypeName}' is not valid; ` + `automatically sanitized to '${expectedSanitized}'*`); const GtypeClass = GObject.registerClass({ GTypeName: gtypeName, }, class extends GObject.Object {}); GLib.test_assert_expected_messages_internal('Cjs', 'testGObjectClass.js', 0, 'testGObjectRegisterClassSanitize'); expect(GtypeClass.$gtype.name).toEqual(expectedSanitized); }); }); describe('Signal handler matching', function () { let o, handleEmpty, emptyId, handleDetailed, detailedId, handleDetailedOne, detailedOneId, handleDetailedTwo, detailedTwoId, handleNotifyTwo, notifyTwoId, handleMinimalOrFull, minimalId, fullId; beforeEach(function () { o = new MyObject(); handleEmpty = jasmine.createSpy('handleEmpty'); emptyId = o.connect('empty', handleEmpty); handleDetailed = jasmine.createSpy('handleDetailed'); detailedId = o.connect('detailed', handleDetailed); handleDetailedOne = jasmine.createSpy('handleDetailedOne'); detailedOneId = o.connect('detailed::one', handleDetailedOne); handleDetailedTwo = jasmine.createSpy('handleDetailedTwo'); detailedTwoId = o.connect('detailed::two', handleDetailedTwo); handleNotifyTwo = jasmine.createSpy('handleNotifyTwo'); notifyTwoId = o.connect('notify::two', handleNotifyTwo); handleMinimalOrFull = jasmine.createSpy('handleMinimalOrFull'); minimalId = o.connect('minimal', handleMinimalOrFull); fullId = o.connect('full', handleMinimalOrFull); }); it('finds handlers by signal ID', function () { expect(GObject.signal_handler_find(o, {signalId: 'empty'})).toEqual(emptyId); // when more than one are connected, returns an arbitrary one expect([detailedId, detailedOneId, detailedTwoId]) .toContain(GObject.signal_handler_find(o, {signalId: 'detailed'})); }); it('finds handlers by signal detail', function () { expect(GObject.signal_handler_find(o, {detail: 'one'})).toEqual(detailedOneId); // when more than one are connected, returns an arbitrary one expect([detailedTwoId, notifyTwoId]) .toContain(GObject.signal_handler_find(o, {detail: 'two'})); }); it('finds handlers by callback', function () { expect(GObject.signal_handler_find(o, {func: handleEmpty})).toEqual(emptyId); expect(GObject.signal_handler_find(o, {func: handleDetailed})).toEqual(detailedId); expect(GObject.signal_handler_find(o, {func: handleDetailedOne})).toEqual(detailedOneId); expect(GObject.signal_handler_find(o, {func: handleDetailedTwo})).toEqual(detailedTwoId); expect(GObject.signal_handler_find(o, {func: handleNotifyTwo})).toEqual(notifyTwoId); // when more than one are connected, returns an arbitrary one expect([minimalId, fullId]) .toContain(GObject.signal_handler_find(o, {func: handleMinimalOrFull})); }); it('finds handlers by a combination of parameters', function () { expect(GObject.signal_handler_find(o, {signalId: 'detailed', detail: 'two'})) .toEqual(detailedTwoId); expect(GObject.signal_handler_find(o, {signalId: 'detailed', func: handleDetailed})) .toEqual(detailedId); }); it('blocks a handler by callback', function () { expect(GObject.signal_handlers_block_matched(o, {func: handleEmpty})).toEqual(1); o.emitEmpty(); expect(handleEmpty).not.toHaveBeenCalled(); expect(GObject.signal_handlers_unblock_matched(o, {func: handleEmpty})).toEqual(1); o.emitEmpty(); expect(handleEmpty).toHaveBeenCalled(); }); it('blocks multiple handlers by callback', function () { expect(GObject.signal_handlers_block_matched(o, {func: handleMinimalOrFull})).toEqual(2); o.emitMinimal(); o.emitFull(); expect(handleMinimalOrFull).not.toHaveBeenCalled(); expect(GObject.signal_handlers_unblock_matched(o, {func: handleMinimalOrFull})).toEqual(2); o.emitMinimal(); o.emitFull(); expect(handleMinimalOrFull).toHaveBeenCalledTimes(2); }); it('blocks handlers by a combination of parameters', function () { expect(GObject.signal_handlers_block_matched(o, {signalId: 'detailed', func: handleDetailed})) .toEqual(1); o.emit('detailed', ''); o.emit('detailed::one', ''); expect(handleDetailed).not.toHaveBeenCalled(); expect(handleDetailedOne).toHaveBeenCalled(); expect(GObject.signal_handlers_unblock_matched(o, {signalId: 'detailed', func: handleDetailed})) .toEqual(1); o.emit('detailed', ''); o.emit('detailed::one', ''); expect(handleDetailed).toHaveBeenCalled(); }); it('disconnects a handler by callback', function () { expect(GObject.signal_handlers_disconnect_matched(o, {func: handleEmpty})).toEqual(1); o.emitEmpty(); expect(handleEmpty).not.toHaveBeenCalled(); }); it('blocks multiple handlers by callback', function () { expect(GObject.signal_handlers_disconnect_matched(o, {func: handleMinimalOrFull})).toEqual(2); o.emitMinimal(); o.emitFull(); expect(handleMinimalOrFull).not.toHaveBeenCalled(); }); it('blocks handlers by a combination of parameters', function () { expect(GObject.signal_handlers_disconnect_matched(o, {signalId: 'detailed', func: handleDetailed})) .toEqual(1); o.emit('detailed', ''); o.emit('detailed::one', ''); expect(handleDetailed).not.toHaveBeenCalled(); expect(handleDetailedOne).toHaveBeenCalled(); }); it('blocks a handler by callback, convenience method', function () { expect(GObject.signal_handlers_block_by_func(o, handleEmpty)).toEqual(1); o.emitEmpty(); expect(handleEmpty).not.toHaveBeenCalled(); expect(GObject.signal_handlers_unblock_by_func(o, handleEmpty)).toEqual(1); o.emitEmpty(); expect(handleEmpty).toHaveBeenCalled(); }); it('disconnects a handler by callback, convenience method', function () { expect(GObject.signal_handlers_disconnect_by_func(o, handleEmpty)).toEqual(1); o.emitEmpty(); expect(handleEmpty).not.toHaveBeenCalled(); }); it('does not support disconnecting a handler by callback data', function () { expect(() => GObject.signal_handlers_disconnect_by_data(o, null)).toThrow(); }); }); describe('Property bindings', function () { const ObjectWithProperties = GObject.registerClass({ Properties: { 'string': GObject.ParamSpec.string('string', 'String', 'String property', GObject.ParamFlags.READWRITE, ''), 'bool': GObject.ParamSpec.boolean('bool', 'Bool', 'Bool property', GObject.ParamFlags.READWRITE, true), }, }, class ObjectWithProperties extends GObject.Object {}); let a, b; beforeEach(function () { a = new ObjectWithProperties(); b = new ObjectWithProperties(); }); it('can bind properties of the same type', function () { a.bind_property('string', b, 'string', GObject.BindingFlags.NONE); a.string = 'foo'; expect(a.string).toEqual('foo'); expect(b.string).toEqual('foo'); }); it('can use custom mappings to bind properties of different types', function () { a.bind_property_full('bool', b, 'string', GObject.BindingFlags.NONE, (bind, source) => [true, `${source}`], null); a.bool = true; expect(a.bool).toEqual(true); expect(b.string).toEqual('true'); }); it('can be set up as a group', function () { if (GObject.BindingGroup === undefined) pending('GLib version too old'); const group = new GObject.BindingGroup({source: a}); group.bind('string', b, 'string', GObject.BindingFlags.NONE); a.string = 'foo'; expect(a.string).toEqual('foo'); expect(b.string).toEqual('foo'); }); it('can be set up as a group with custom mappings', function () { if (GObject.BindingGroup === undefined) pending('GLib version too old'); const group = new GObject.BindingGroup({source: a}); group.bind_full('bool', b, 'string', GObject.BindingFlags.NONE, (bind, source) => [true, `${source}`], null); a.bool = true; expect(a.bool).toEqual(true); expect(b.string).toEqual('true'); }); }); describe('Auto accessor generation', function () { const AutoAccessors = GObject.registerClass({ Properties: { 'simple': GObject.ParamSpec.int('simple', 'Simple', 'Short-named property', GObject.ParamFlags.READWRITE, 0, 100, 24), 'long-long-name': GObject.ParamSpec.int('long-long-name', 'Long long name', 'Long-named property', GObject.ParamFlags.READWRITE, 0, 100, 48), 'construct': GObject.ParamSpec.int('construct', 'Construct', 'Construct', GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT, 0, 100, 96), 'construct-only': GObject.ParamSpec.int('construct-only', 'Construct only', 'Construct-only property', GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, 0, 100, 80), 'construct-only-with-setter': GObject.ParamSpec.int('construct-only-with-setter', 'Construct only with setter', 'Construct-only property with a setter method', GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, 0, 100, 80), 'construct-only-was-invalid-in-turkish': GObject.ParamSpec.int( 'construct-only-was-invalid-in-turkish', 'Camel name in Turkish', 'Camel-cased property that was wrongly transformed in Turkish', GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, 0, 100, 55), 'snake-name': GObject.ParamSpec.int('snake-name', 'Snake name', 'Snake-cased property', GObject.ParamFlags.READWRITE, 0, 100, 36), 'camel-name': GObject.ParamSpec.int('camel-name', 'Camel name', 'Camel-cased property', GObject.ParamFlags.READWRITE, 0, 100, 72), 'kebab-name': GObject.ParamSpec.int('kebab-name', 'Kebab name', 'Kebab-cased property', GObject.ParamFlags.READWRITE, 0, 100, 12), 'readonly': GObject.ParamSpec.int('readonly', 'Readonly', 'Readonly property', GObject.ParamFlags.READABLE, 0, 100, 54), 'writeonly': GObject.ParamSpec.int('writeonly', 'Writeonly', 'Writeonly property', GObject.ParamFlags.WRITABLE, 0, 100, 60), 'missing-getter': GObject.ParamSpec.int('missing-getter', 'Missing getter', 'Missing a getter', GObject.ParamFlags.READWRITE, 0, 100, 18), 'missing-setter': GObject.ParamSpec.int('missing-setter', 'Missing setter', 'Missing a setter', GObject.ParamFlags.READWRITE, 0, 100, 42), }, }, class AutoAccessors extends GObject.Object { _init(props = {}) { this._constructOnlySetterCalled = 0; super._init(props); this._snakeNameGetterCalled = 0; this._snakeNameSetterCalled = 0; this._camelNameGetterCalled = 0; this._camelNameSetterCalled = 0; this._kebabNameGetterCalled = 0; this._kebabNameSetterCalled = 0; } get snake_name() { this._snakeNameGetterCalled++; return 42; } set snake_name(value) { this._snakeNameSetterCalled++; } get camelName() { this._camelNameGetterCalled++; return 42; } set camelName(value) { this._camelNameSetterCalled++; } get ['kebab-name']() { this._kebabNameGetterCalled++; return 42; } set ['kebab-name'](value) { this._kebabNameSetterCalled++; } set missing_getter(value) { this._missingGetter = value; } get missing_setter() { return 42; } get construct_only_with_setter() { return this._constructOnlyValue; } set constructOnlyWithSetter(value) { this._constructOnlySetterCalled++; this._constructOnlyValue = value; } }); let a; beforeEach(function () { a = new AutoAccessors(); }); it('get and set the property', function () { a.simple = 1; expect(a.simple).toEqual(1); a['long-long-name'] = 1; expect(a['long-long-name']).toEqual(1); a.construct = 1; expect(a.construct).toEqual(1); }); it("initial value is the param spec's default value", function () { expect(a.simple).toEqual(24); expect(a.long_long_name).toEqual(48); expect(a.longLongName).toEqual(48); expect(a['long-long-name']).toEqual(48); expect(a.construct).toEqual(96); expect(a.construct_only).toEqual(80); expect(a.constructOnly).toEqual(80); expect(a['construct-only']).toEqual(80); }); it('set properties at construct time', function () { a = new AutoAccessors({ simple: 1, longLongName: 1, construct: 1, 'construct-only': 1, 'construct-only-with-setter': 2, }); expect(a.simple).toEqual(1); expect(a.long_long_name).toEqual(1); expect(a.longLongName).toEqual(1); expect(a['long-long-name']).toEqual(1); expect(a.construct).toEqual(1); expect(a.construct_only).toEqual(1); expect(a.constructOnly).toEqual(1); expect(a['construct-only']).toEqual(1); expect(a.constructOnlyWithSetter).toEqual(2); expect(a.construct_only_with_setter).toEqual(2); expect(a['construct-only-with-setter']).toEqual(2); expect(a._constructOnlySetterCalled).toEqual(1); }); it('set properties at construct time with locale', function () { const {gettext: Gettext} = imports; const prevLocale = Gettext.setlocale(Gettext.LocaleCategory.ALL, null); Gettext.setlocale(Gettext.LocaleCategory.ALL, 'tr_TR'); a = new AutoAccessors({ 'construct-only-was-invalid-in-turkish': 35, }); Gettext.setlocale(Gettext.LocaleCategory.ALL, prevLocale); expect(a.constructOnlyWasInvalidInTurkish).toEqual(35); expect(a.construct_only_was_invalid_in_turkish).toEqual(35); expect(a['construct-only-was-invalid-in-turkish']).toEqual(35); }); it('notify when the property changes', function () { const notify = jasmine.createSpy('notify'); a.connect('notify::simple', notify); a.simple = 1; expect(notify).toHaveBeenCalledTimes(1); notify.calls.reset(); a.simple = 1; expect(notify).not.toHaveBeenCalled(); }); it('copies accessors for camel and kebab if snake accessors given', function () { a.snakeName = 42; expect(a.snakeName).toEqual(42); a['snake-name'] = 42; expect(a['snake-name']).toEqual(42); expect(a._snakeNameGetterCalled).toEqual(2); expect(a._snakeNameSetterCalled).toEqual(2); }); it('copies accessors for snake and kebab if camel accessors given', function () { a.camel_name = 42; expect(a.camel_name).toEqual(42); a['camel-name'] = 42; expect(a['camel-name']).toEqual(42); expect(a._camelNameGetterCalled).toEqual(2); expect(a._camelNameSetterCalled).toEqual(2); }); it('copies accessors for snake and camel if kebab accessors given', function () { a.kebabName = 42; expect(a.kebabName).toEqual(42); a.kebab_name = 42; expect(a.kebab_name).toEqual(42); expect(a._kebabNameGetterCalled).toEqual(2); expect(a._kebabNameSetterCalled).toEqual(2); }); it('readonly getter throws', function () { expect(() => a.readonly).toThrowError(/getter/); }); it('writeonly setter throws', function () { expect(() => (a.writeonly = 1)).toThrowError(/setter/); }); it('getter throws when setter defined', function () { expect(() => a.missingGetter).toThrowError(/getter/); }); it('setter throws when getter defined', function () { expect(() => (a.missingSetter = 1)).toThrowError(/setter/); }); }); const MyObjectWithJSObjectProperty = GObject.registerClass({ Properties: { 'jsobj-prop': GObject.ParamSpec.jsobject('jsobj-prop', 'jsobj-prop', 'jsobj-prop', GObject.ParamFlags.CONSTRUCT | GObject.ParamFlags.READWRITE), }, }, class MyObjectWithJSObjectProperty extends GObject.Object { }); describe('GObject class with JSObject property', function () { it('assigns a valid JSObject on construct', function () { let date = new Date(); let obj = new MyObjectWithJSObjectProperty({jsobj_prop: date}); expect(obj.jsobj_prop).toEqual(date); expect(obj.jsobj_prop).not.toEqual(new Date(0)); expect(() => obj.jsobj_prop.setFullYear(1985)).not.toThrow(); expect(obj.jsobj_prop.getFullYear()).toEqual(1985); }); it('Set null with an empty JSObject on construct', function () { expect(new MyObjectWithJSObjectProperty().jsobj_prop).toBeNull(); expect(new MyObjectWithJSObjectProperty({}).jsobj_prop).toBeNull(); }); it('assigns a null JSObject on construct', function () { expect(new MyObjectWithJSObjectProperty({jsobj_prop: null}).jsobj_prop) .toBeNull(); }); it('assigns a JSObject Array on construct', function () { expect(() => new MyObjectWithJSObjectProperty({jsobj_prop: [1, 2, 3]})) .not.toThrow(); }); it('assigns a Function on construct', function () { expect(() => new MyObjectWithJSObjectProperty({ jsobj_prop: () => true, })).not.toThrow(); }); it('throws an error when using a boolean value on construct', function () { expect(() => new MyObjectWithJSObjectProperty({jsobj_prop: true})) .toThrowError(/JSObject expected/); }); it('throws an error when using an int value on construct', function () { expect(() => new MyObjectWithJSObjectProperty({jsobj_prop: 1})) .toThrowError(/JSObject expected/); }); it('throws an error when using a numeric value on construct', function () { expect(() => new MyObjectWithJSObjectProperty({jsobj_prop: Math.PI})) .toThrowError(/JSObject expected/); }); it('throws an error when using a string value on construct', function () { expect(() => new MyObjectWithJSObjectProperty({jsobj_prop: 'string'})) .toThrowError(/JSObject expected/); }); it('throws an error when using an undefined value on construct', function () { expect(() => new MyObjectWithJSObjectProperty({jsobj_prop: undefined})).toThrow(); }); it('property value survives when GObject wrapper is collected', function () { const MyConverter = GObject.registerClass({ Properties: { testprop: GObject.ParamSpec.jsobject('testprop', 'testprop', 'Test property', GObject.ParamFlags.CONSTRUCT | GObject.ParamFlags.READWRITE), }, Implements: [Gio.Converter], }, class MyConverter extends GObject.Object {}); function stashObject() { const base = new Gio.MemoryInputStream(); const converter = new MyConverter({testprop: [1, 2, 3]}); return Gio.ConverterInputStream.new(base, converter); } const stream = stashObject(); System.gc(); expect(stream.get_converter().testprop).toEqual([1, 2, 3]); }); }); const MyObjectWithJSObjectSignals = GObject.registerClass({ Signals: { 'send-object': {param_types: [GObject.TYPE_JSOBJECT]}, 'send-many-objects': { param_types: [GObject.TYPE_JSOBJECT, GObject.TYPE_JSOBJECT, GObject.TYPE_JSOBJECT], }, 'get-object': { flags: GObject.SignalFlags.RUN_LAST, accumulator: GObject.AccumulatorType.FIRST_WINS, return_type: GObject.TYPE_JSOBJECT, param_types: [GObject.TYPE_JSOBJECT], }, }, }, class MyObjectWithJSObjectSignals extends GObject.Object { emitObject(obj) { this.emit('send-object', obj); } }); describe('GObject class with JSObject signals', function () { let myInstance; beforeEach(function () { myInstance = new MyObjectWithJSObjectSignals(); }); it('emits signal with null JSObject parameter', function () { let customSpy = jasmine.createSpy('sendObjectSpy'); myInstance.connect('send-object', customSpy); myInstance.emitObject(null); expect(customSpy).toHaveBeenCalledWith(myInstance, null); }); it('emits signal with JSObject parameter', function () { let customSpy = jasmine.createSpy('sendObjectSpy'); myInstance.connect('send-object', customSpy); let obj = { foo: [1, 2, 3], sub: {a: {}, 'b': this}, desc: 'test', date: new Date(), }; myInstance.emitObject(obj); expect(customSpy).toHaveBeenCalledWith(myInstance, obj); }); it('emits signal with multiple JSObject parameters', function () { let customSpy = jasmine.createSpy('sendManyObjectsSpy'); myInstance.connect('send-many-objects', customSpy); let obj = { foo: [9, 8, 7, 'a', 'b', 'c'], sub: {a: {}, 'b': this}, desc: 'test', date: new RegExp('\\w+'), }; myInstance.emit('send-many-objects', obj, obj.foo, obj.sub); expect(customSpy).toHaveBeenCalledWith(myInstance, obj, obj.foo, obj.sub); }); it('re-emits signal with same JSObject parameter', function () { let obj = { foo: [9, 8, 7, 'a', 'b', 'c'], sub: {a: {}, 'b': this}, func: arg => { return {ret: [arg]}; }, }; myInstance.connect('send-many-objects', (instance, func, args, foo) => { expect(instance).toEqual(myInstance); expect(System.addressOf(instance)).toEqual(System.addressOf(myInstance)); expect(foo).toEqual(obj.foo); expect(System.addressOf(foo)).toEqual(System.addressOf(obj.foo)); expect(func(args).ret[0]).toEqual(args); }); myInstance.connect('send-object', (instance, param) => { expect(instance).toEqual(myInstance); expect(System.addressOf(instance)).toEqual(System.addressOf(myInstance)); expect(param).toEqual(obj); expect(System.addressOf(param)).toEqual(System.addressOf(obj)); expect(() => instance.emit('send-many-objects', param.func, param, param.foo)) .not.toThrow(); }); myInstance.emit('send-object', obj); }); it('throws an error when using a boolean value as parameter', function () { expect(() => myInstance.emit('send-object', true)) .toThrowError(/JSObject expected/); expect(() => myInstance.emit('send-many-objects', ['a'], true, {})) .toThrowError(/JSObject expected/); }); it('throws an error when using an int value as parameter', function () { expect(() => myInstance.emit('send-object', 1)) .toThrowError(/JSObject expected/); expect(() => myInstance.emit('send-many-objects', ['a'], 1, {})) .toThrowError(/JSObject expected/); }); it('throws an error when using a numeric value as parameter', function () { expect(() => myInstance.emit('send-object', Math.PI)) .toThrowError(/JSObject expected/); expect(() => myInstance.emit('send-many-objects', ['a'], Math.PI, {})) .toThrowError(/JSObject expected/); }); it('throws an error when using a string value as parameter', function () { expect(() => myInstance.emit('send-object', 'string')) .toThrowError(/JSObject expected/); expect(() => myInstance.emit('send-many-objects', ['a'], 'string', {})) .toThrowError(/JSObject expected/); }); it('throws an error when using an undefined value as parameter', function () { expect(() => myInstance.emit('send-object', undefined)) .toThrowError(/JSObject expected/); expect(() => myInstance.emit('send-many-objects', ['a'], undefined, {})) .toThrowError(/JSObject expected/); }); it('returns a JSObject', function () { let data = { foo: [9, 8, 7, 'a', 'b', 'c'], sub: {a: {}, 'b': this}, func: arg => { return {ret: [arg]}; }, }; let id = myInstance.connect('get-object', () => { return data; }); expect(myInstance.emit('get-object', {})).toBe(data); myInstance.disconnect(id); myInstance.connect('get-object', (instance, input) => { if (input) { if (typeof input === 'function') input(); return input; } class SubObject { constructor() { this.pi = Math.PI; } method() {} gobject() { return GObject.Object; } get data() { return data; } } return new SubObject(); }); expect(myInstance.emit('get-object', null).constructor.name).toBe('SubObject'); expect(myInstance.emit('get-object', null).data).toBe(data); expect(myInstance.emit('get-object', null).pi).toBe(Math.PI); expect(() => myInstance.emit('get-object', null).method()).not.toThrow(); expect(myInstance.emit('get-object', null).gobject()).toBe(GObject.Object); expect(new (myInstance.emit('get-object', null).gobject())() instanceof GObject.Object) .toBeTruthy(); expect(myInstance.emit('get-object', data)).toBe(data); expect(myInstance.emit('get-object', jasmine.createSpy('callMeSpy'))) .toHaveBeenCalled(); }); it('returns null when returning undefined', function () { myInstance.connect('get-object', () => { return undefined; }); expect(myInstance.emit('get-object', {})).toBeNull(); }); it('returns null when not returning', function () { myInstance.connect('get-object', () => { }); expect(myInstance.emit('get-object', {})).toBeNull(); }); // These tests are intended to throw an error, but currently errors cannot // be caught from signal handlers, so we check for logged messages instead it('throws an error when returning a boolean value', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, '*JSObject expected*'); myInstance.connect('get-object', () => true); myInstance.emit('get-object', {}); GLib.test_assert_expected_messages_internal('Cjs', 'testGObjectClass.js', 0, 'throws an error when returning a boolean value'); }); it('throws an error when returning an int value', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, '*JSObject expected*'); myInstance.connect('get-object', () => 1); myInstance.emit('get-object', {}); GLib.test_assert_expected_messages_internal('Cjs', 'testGObjectClass.js', 0, 'throws an error when returning a boolean value'); }); it('throws an error when returning a numeric value', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, '*JSObject expected*'); myInstance.connect('get-object', () => Math.PI); myInstance.emit('get-object', {}); GLib.test_assert_expected_messages_internal('Cjs', 'testGObjectClass.js', 0, 'throws an error when returning a boolean value'); }); it('throws an error when returning a string value', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, '*JSObject expected*'); myInstance.connect('get-object', () => 'string'); myInstance.emit('get-object', {}); GLib.test_assert_expected_messages_internal('Cjs', 'testGObjectClass.js', 0, 'throws an error when returning a boolean value'); }); }); describe('GObject class with int64 properties', function () { const MyInt64Class = GObject.registerClass(class MyInt64Class extends GObject.Object { static [GObject.properties] = { 'int64': GObject.ParamSpec.int64('int64', 'int64', 'int64', GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE | GObject.ParamFlags.CONSTRUCT, // GLib.MAXINT64 exceeds JS' ability to safely represent an integer GLib.MININT32 * 2, GLib.MAXINT32 * 2, 0), }; }); it('can set an int64 property', function () { const instance = new MyInt64Class({ int64: GLib.MAXINT32, }); expect(instance.int64).toBe(GLib.MAXINT32); instance.int64 = GLib.MAXINT32 + 1; expect(instance.int64).toBe(GLib.MAXINT32 + 1); }); it('can construct with int64 property', function () { const instance = new MyInt64Class({ int64: GLib.MAXINT32 + 1, }); expect(instance.int64).toBe(GLib.MAXINT32 + 1); }); }); cjs-128.0/installed-tests/js/testGObject.js0000664000175000017500000001405314771557763017566 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2013 Giovanni Campagna // SPDX-FileCopyrightText: 2018 Red Hat, Inc. // This is where overrides in modules/core/overrides/GObject.js are tested, // except for the class machinery, interface machinery, and GObject.ParamSpec, // which are big enough to get their own files. const {GLib, GObject} = imports.gi; const {system: System} = imports; const TestObj = GObject.registerClass({ Properties: { int: GObject.ParamSpec.int('int', '', '', GObject.ParamFlags.READWRITE, 0, GLib.MAXINT32, 0), string: GObject.ParamSpec.string('string', '', '', GObject.ParamFlags.READWRITE, ''), }, Signals: { test: {}, }, }, class TestObj extends GObject.Object {}); describe('GObject overrides', function () { it('GObject.set()', function () { const o = new TestObj(); o.set({string: 'Answer', int: 42}); expect(o.string).toBe('Answer'); expect(o.int).toBe(42); }); describe('Signal alternative syntax', function () { let o, handler; beforeEach(function () { handler = jasmine.createSpy('handler'); o = new TestObj(); const handlerId = GObject.signal_connect(o, 'test', handler); handler.and.callFake(() => GObject.signal_handler_disconnect(o, handlerId)); GObject.signal_emit_by_name(o, 'test'); }); it('handler is called with the right object', function () { expect(handler).toHaveBeenCalledTimes(1); expect(handler).toHaveBeenCalledWith(o); }); it('disconnected handler is not called', function () { handler.calls.reset(); GObject.signal_emit_by_name(o, 'test'); expect(handler).not.toHaveBeenCalled(); }); }); it('toString() shows the native object address', function () { const o = new TestObj(); const address = System.addressOfGObject(o); expect(o.toString()).toMatch( new RegExp(`[object instance wrapper .* jsobj@0x[a-f0-9]+ native@${address}`)); }); }); describe('GObject should', function () { const types = ['gpointer', 'GBoxed', 'GParam', 'GInterface', 'GObject', 'GVariant']; types.forEach(type => { it(`be able to create a GType object for ${type}`, function () { const gtype = GObject.Type(type); expect(gtype.name).toEqual(type); }); }); it('be able to query signals', function () { const query = GObject.signal_query(1); expect(query instanceof GObject.SignalQuery).toBeTruthy(); expect(query.param_types).not.toBeNull(); expect(Array.isArray(query.param_types)).toBeTruthy(); expect(query.signal_id).toBe(1); }); }); describe('GObject.Object.new()', function () { const gon = GObject.Object.new; it('can be called with a property bag', function () { const o = gon(TestObj, { string: 'Answer', int: 42, }); expect(o.string).toBe('Answer'); expect(o.int).toBe(42); }); it('can be called to construct an object without setting properties', function () { const o1 = gon(TestObj); expect(o1.string).toBe(''); expect(o1.int).toBe(0); const o2 = gon(TestObj, {}); expect(o2.string).toBe(''); expect(o2.int).toBe(0); }); it('complains about wrong types', function () { expect(() => gon(TestObj, { string: 42, int: 'Answer', })).toThrow(); }); it('complains about wrong properties', function () { expect(() => gon(TestObj, {foo: 'bar'})).toThrow(); }); it('can construct C GObjects as well', function () { const o = gon(GObject.Object, {}); expect(o.constructor.$gtype.name).toBe('GObject'); }); }); describe('GObject.Object.new_with_properties()', function () { const gonwp = GObject.Object.new_with_properties; it('can be called with two arrays', function () { const o = gonwp(TestObj, ['string', 'int'], ['Answer', 42]); expect(o.string).toBe('Answer'); expect(o.int).toBe(42); }); it('can be called to construct an object without setting properties', function () { const o = gonwp(TestObj, [], []); expect(o.string).toBe(''); expect(o.int).toBe(0); }); it('complains about various incorrect usages', function () { expect(() => gonwp(TestObj)).toThrow(); expect(() => gonwp(TestObj, ['string', 'int'])).toThrow(); expect(() => gonwp(TestObj, ['string', 'int'], ['Answer'])).toThrow(); expect(() => gonwp(TestObj, {}, ['Answer', 42])).toThrow(); }); it('complains about wrong types', function () { expect(() => gonwp(TestObj, ['string', 'int'], [42, 'Answer'])).toThrow(); }); it('complains about wrong properties', function () { expect(() => gonwp(TestObj, ['foo'], ['bar'])).toThrow(); }); it('can construct C GObjects as well', function () { const o = gonwp(GObject.Object, [], []); expect(o.constructor.$gtype.name).toBe('GObject'); }); }); describe('Unsupported methods', function () { let o; beforeEach(function () { o = new GObject.Object(); }); it('throws on data stashing methods', function () { expect(() => o.get_data('foo')).toThrow(); expect(() => o.get_qdata(1)).toThrow(); expect(() => o.set_data('foo', 'bar')).toThrow(); expect(() => o.steal_data('foo')).toThrow(); expect(() => o.steal_qdata(1)).toThrow(); }); it('throws on refcounting methods', function () { const refcount = System.refcount(o); const floating = o.is_floating(); expect(() => o.ref()).toThrow(); expect(() => o.unref()).toThrow(); expect(() => o.ref_sink()).toThrow(); expect(() => o.force_floating()).toThrow(); expect(System.refcount(o)).toBe(refcount); expect(o.is_floating()).toBe(floating); }); }); cjs-128.0/installed-tests/js/complex3.ui0000664000175000017500000000242214771557763017101 0ustar fabiofabio cjs-128.0/installed-tests/js/minijasmine.js0000664000175000017500000001074714771557763017662 0ustar fabiofabio#!/usr/bin/env -S gjs -m // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2016 Philip Chimento import GLib from 'gi://GLib'; function _filterStack(stack) { if (!stack) return 'No stack'; return stack.split('\n') .filter(stackLine => stackLine.indexOf('resource:///org/cjs/jsunit') === -1) .join('\n'); } let jasmineRequire = imports.jasmine.getJasmineRequireObj(); let jasmineCore = jasmineRequire.core(jasmineRequire); export let environment = jasmineCore.getEnv(); environment.configure({ random: false, }); export const mainloop = GLib.MainLoop.new(null, false); export let retval = 0; export let errorsOutput = []; // Install Jasmine API on the global object let jasmineInterface = jasmineRequire.interface(jasmineCore, environment); Object.assign(globalThis, jasmineInterface); // Reporter that outputs according to the Test Anything Protocol // See http://testanything.org/tap-specification.html class TapReporter { constructor() { this._failedSuites = []; this._specCount = 0; } jasmineStarted(info) { print(`1..${info.totalSpecsDefined}`); } jasmineDone() { this._failedSuites.forEach(failure => { failure.failedExpectations.forEach(result => { print('not ok - An error was thrown outside a test'); print(`# ${result.message}`); }); }); mainloop.quit(); } suiteDone(result) { if (result.failedExpectations && result.failedExpectations.length > 0) { globalThis._jasmineRetval = 1; this._failedSuites.push(result); } if (result.status === 'disabled') print('# Suite was disabled:', result.fullName); } specStarted() { this._specCount++; } specDone(result) { let tapReport; if (result.status === 'failed') { globalThis._jasmineRetval = 1; tapReport = 'not ok'; } else { tapReport = 'ok'; } tapReport += ` ${this._specCount} ${result.fullName}`; if (result.status === 'pending' || result.status === 'disabled' || result.status === 'excluded') { let reason = result.pendingReason || result.status; tapReport += ` # SKIP ${reason}`; } print(tapReport); // Print additional diagnostic info on failure if (result.status === 'failed' && result.failedExpectations) { result.failedExpectations.forEach(failedExpectation => { const output = []; const messageLines = failedExpectation.message.split('\n'); output.push(`Message: ${messageLines.shift()}`); output.push(...messageLines.map(str => ` ${str}`)); output.push('Stack:'); let stackTrace = _filterStack(failedExpectation.stack).trim(); output.push(...stackTrace.split('\n').map(str => ` ${str}`)); if (errorsOutput.length) { errorsOutput.push( Array(GLib.getenv('COLUMNS') || 80).fill('―').join('')); } errorsOutput.push(`Test: ${result.fullName}`); errorsOutput.push(...output); print(output.map(l => `# ${l}`).join('\n')); }); } } } environment.addReporter(new TapReporter()); // If we're running the tests in certain JS_GC_ZEAL modes or Valgrind, then some // will time out if the CI machine is under a certain load. In that case // increase the default timeout. const gcZeal = GLib.getenv('JS_GC_ZEAL'); const valgrind = GLib.getenv('VALGRIND'); if (valgrind || (gcZeal && (gcZeal === '2' || gcZeal.startsWith('2,') || gcZeal === '4'))) jasmine.DEFAULT_TIMEOUT_INTERVAL *= 5; /** * The Promise (or null) that minijasmine-executor locks on * to avoid exiting prematurely */ export let mainloopLock = null; /** * Stops the main loop but prevents the minijasmine-executor from * exiting. This is used for testing the main loop itself. * * @returns a callback which returns control of the main loop to * minijasmine-executor */ export function acquireMainloop() { let resolve; mainloopLock = new Promise(_resolve => (resolve = _resolve)); if (!mainloop.is_running()) throw new Error("Main loop was stopped already, can't acquire"); mainloop.quit(); return () => { mainloopLock = null; resolve(true); }; } cjs-128.0/installed-tests/js/modules/0000775000175000017500000000000014771557763016460 5ustar fabiofabiocjs-128.0/installed-tests/js/modules/mutualImport/0000775000175000017500000000000014771557763021162 5ustar fabiofabiocjs-128.0/installed-tests/js/modules/mutualImport/a.js0000664000175000017500000000053214771557763021740 0ustar fabiofabio/* exported getCount, getCountViaB, incrementCount */ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2008 Red Hat, Inc. const B = imports.mutualImport.b; let count = 0; function incrementCount() { count++; } function getCount() { return count; } function getCountViaB() { return B.getCount(); } cjs-128.0/installed-tests/js/modules/mutualImport/b.js0000664000175000017500000000032114771557763021735 0ustar fabiofabio/* exported getCount */ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2008 Red Hat, Inc. const A = imports.mutualImport.a; function getCount() { return A.getCount(); } cjs-128.0/installed-tests/js/modules/modunicode.js0000664000175000017500000000027014771557763021143 0ustar fabiofabio/* exported uval */ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2012 Red Hat, Inc. // This file is written in UTF-8. var uval = 'const ♥ utf8'; cjs-128.0/installed-tests/js/modules/importmeta.js0000664000175000017500000000046714771557763021206 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2021 Philip Chimento if (typeof import.meta.importSync !== 'undefined') throw new Error('internal import meta property should not be visible in userland'); export default Object.keys(import.meta); cjs-128.0/installed-tests/js/modules/sideEffect.js0000664000175000017500000000027014771557763021056 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2023 Philip Chimento globalThis.leakyState ??= 0; globalThis.leakyState++; cjs-128.0/installed-tests/js/modules/sideEffect3.js0000664000175000017500000000026614771557763021146 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2024 Gary Li globalThis.queryLeakyState ??= 0; globalThis.queryLeakyState++; cjs-128.0/installed-tests/js/modules/exports.js0000664000175000017500000000061514771557763020524 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2020 Evan Welsh import Gio from 'gi://Gio'; export default 5; export const NamedExport = 'Hello, World'; const thisFile = Gio.File.new_for_uri(import.meta.url); const dataFile = thisFile.get_parent().resolve_relative_path('data.txt'); export const [, data] = dataFile.load_contents(null); cjs-128.0/installed-tests/js/modules/subErrorInit/0000775000175000017500000000000014771557763021107 5ustar fabiofabiocjs-128.0/installed-tests/js/modules/subErrorInit/__init__.js0000664000175000017500000000022514771557763023203 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2021 Evan Welsh throw Error('a bad init!'); cjs-128.0/installed-tests/js/modules/badOverrides/0000775000175000017500000000000014771557763021071 5ustar fabiofabiocjs-128.0/installed-tests/js/modules/badOverrides/.eslintrc.yml0000664000175000017500000000041014771557763023510 0ustar fabiofabio--- # SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2018 Philip Chimento rules: no-throw-literal: 'off' # these are intended to be bad code no-unused-vars: - error - varsIgnorePattern: ^_init$ cjs-128.0/installed-tests/js/modules/badOverrides/Gio.js0000664000175000017500000000030014771557763022136 0ustar fabiofabio// Sabotage the import of imports.gi.Gio! // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2016 Philip Chimento var _init = '💩'; cjs-128.0/installed-tests/js/modules/badOverrides/Regress.js0000664000175000017500000000032714771557763023043 0ustar fabiofabio// Sabotage the import of imports.gi.Regress! // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2016 Philip Chimento function _init() { throw '💩'; } cjs-128.0/installed-tests/js/modules/badOverrides/GIMarshallingTests.js0000664000175000017500000000031114771557763025126 0ustar fabiofabio// Sabotage the import of imports.gi.GIMarshallingTests! // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2016 Philip Chimento throw '💩'; cjs-128.0/installed-tests/js/modules/badOverrides/WarnLib.js0000664000175000017500000000031114771557763022760 0ustar fabiofabio// Sabotage the import of imports.gi.WarnLib! // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2016 Philip Chimento k$^s^%$#^*($%jdghdsfjkgd cjs-128.0/installed-tests/js/modules/lexicalScope.js0000664000175000017500000000172414771557763021435 0ustar fabiofabio/* exported a, b, c */ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2017 Philip Chimento // Tests bindings in the global scope (var) and lexical environment (let, const) // This should be exported as a property when importing this module: var a = 1; // These should not be exported, but for compatibility we will pretend they are // for the time being: let b = 2; const c = 3; // It's not clear whether this should be exported in ES6, but for compatibility // it should be: this.d = 4; // Modules should have access to standard properties on the global object. if (typeof imports === 'undefined') throw new Error('fail the import'); // This should probably not be supported in the future, but I'm not sure how // we can phase it out compatibly. The module should also have access to // properties that the importing code defines. if (typeof expectMe === 'undefined') throw new Error('fail the import'); cjs-128.0/installed-tests/js/modules/alwaysThrows.js0000664000175000017500000000025414771557763021526 0ustar fabiofabio// line 0 // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2008 litl, LLC throw new Error('This is an error that always happens on line 3'); cjs-128.0/installed-tests/js/modules/data.txt0000664000175000017500000000001214771557763020123 0ustar fabiofabiotest data cjs-128.0/installed-tests/js/modules/foobar.js0000664000175000017500000000062414771557763020270 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2008 litl, LLC // simple test module (used by testImporter.js) /* eslint no-redeclare: ["error", { "builtinGlobals": false }] */ // for toString /* exported bar, foo, testToString, toString */ var foo = 'This is foo'; var bar = 'This is bar'; var toString = x => x; function testToString(x) { return toString(x); } cjs-128.0/installed-tests/js/modules/subA/0000775000175000017500000000000014771557763017352 5ustar fabiofabiocjs-128.0/installed-tests/js/modules/subA/subB/0000775000175000017500000000000014771557763020245 5ustar fabiofabiocjs-128.0/installed-tests/js/modules/subA/subB/foobar.js0000664000175000017500000000033414771557763022053 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2008 litl, LLC // simple test module (used by testImporter.js) /* exported bar, foo */ var foo = 'This is foo'; var bar = 'This is bar'; cjs-128.0/installed-tests/js/modules/subA/subB/baz.js0000664000175000017500000000000014771557763021345 0ustar fabiofabiocjs-128.0/installed-tests/js/modules/subA/subB/__init__.js0000664000175000017500000000064114771557763022343 0ustar fabiofabio/* exported ImporterClass, testImporterFunction */ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2009 litl, LLC function testImporterFunction() { return '__init__ function tested'; } function ImporterClass() { this._init(); } ImporterClass.prototype = { _init() { this._a = '__init__ class tested'; }, testMethod() { return this._a; }, }; cjs-128.0/installed-tests/js/modules/greet.js0000664000175000017500000000055614771557763020132 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2024 Philip Chimento import GLib from 'gi://GLib'; const uri = GLib.Uri.parse(import.meta.url, GLib.UriFlags.NONE); const {greeting, name} = GLib.Uri.parse_params(uri.get_query(), -1, '&', GLib.UriParamsFlags.NONE); export default `${greeting}, ${name}`; cjs-128.0/installed-tests/js/modules/dynamic.js0000664000175000017500000000033214771557763020440 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2021 Evan Welsh /* exported test */ async function test() { const {say} = await import('./say.js'); return say('I did it!'); } cjs-128.0/installed-tests/js/modules/badOverrides2/0000775000175000017500000000000014771557763021153 5ustar fabiofabiocjs-128.0/installed-tests/js/modules/badOverrides2/.eslintrc.yml0000664000175000017500000000041014771557763023572 0ustar fabiofabio--- # SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2018 Philip Chimento rules: no-throw-literal: 'off' # these are intended to be bad code no-unused-vars: - error - varsIgnorePattern: ^_init$ cjs-128.0/installed-tests/js/modules/badOverrides2/Gio.js0000664000175000017500000000027614771557763022234 0ustar fabiofabio// Sabotage the import of imports.gi.Gio! // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2023 Philip Chimento var _init = null; cjs-128.0/installed-tests/js/modules/badOverrides2/Regress.js0000664000175000017500000000027314771557763023125 0ustar fabiofabio// Sabotage the import of imports.gi.Regress! // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2023 Philip Chimento var _init; cjs-128.0/installed-tests/js/modules/badOverrides2/GIMarshallingTests.js0000664000175000017500000000036214771557763025216 0ustar fabiofabio// Sabotage the import of imports.gi.GIMarshallingTests! // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2023 Philip Chimento var _init = {thingsThatThisObjectIsNot: ['callable']}; cjs-128.0/installed-tests/js/modules/badOverrides2/WarnLib.js0000664000175000017500000000025714771557763023053 0ustar fabiofabio// Sabotage the import of imports.gi.WarnLib! // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2023 Philip Chimento cjs-128.0/installed-tests/js/modules/say.js0000664000175000017500000000036614771557763017617 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2020 Philip Chimento export function say(str) { return `<( ${str} )`; } export default function () { return 'default export'; } cjs-128.0/installed-tests/js/modules/subBadInit/0000775000175000017500000000000014771557763020504 5ustar fabiofabiocjs-128.0/installed-tests/js/modules/subBadInit/__init__.js0000664000175000017500000000021614771557763022600 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2021 Evan Welsh this is gobbledygook cjs-128.0/installed-tests/js/modules/overrides/0000775000175000017500000000000014771557763020462 5ustar fabiofabiocjs-128.0/installed-tests/js/modules/overrides/.eslintrc.yml0000664000175000017500000000031114771557763023101 0ustar fabiofabio--- # SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2018 Philip Chimento rules: no-unused-vars: - error - varsIgnorePattern: ^_init$ cjs-128.0/installed-tests/js/modules/overrides/GIMarshallingTests.js0000664000175000017500000000174114771557763024527 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2019 Philip Chimento function _init() { const GIMarshallingTests = this; GIMarshallingTests.OVERRIDES_CONSTANT = 7; GIMarshallingTests.OverridesStruct.prototype._real_method = GIMarshallingTests.OverridesStruct.prototype.method; GIMarshallingTests.OverridesStruct.prototype.method = function () { return this._real_method() / 7; }; GIMarshallingTests.OverridesObject.prototype._realInit = GIMarshallingTests.OverridesObject.prototype._init; GIMarshallingTests.OverridesObject.prototype._init = function (num, ...args) { this._realInit(...args); this.num = num; }; GIMarshallingTests.OverridesObject.prototype._realMethod = GIMarshallingTests.OverridesObject.prototype.method; GIMarshallingTests.OverridesObject.prototype.method = function () { return this._realMethod() / 7; }; } cjs-128.0/installed-tests/js/modules/sideEffect2.js0000664000175000017500000000027014771557763021140 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2023 Philip Chimento globalThis.leakyState ??= 0; globalThis.leakyState++; cjs-128.0/installed-tests/js/modules/encodings.json0000664000175000017500000002316314771557763021331 0ustar fabiofabio[ { "encodings": [ { "labels": [ "unicode-1-1-utf-8", "unicode11utf8", "unicode20utf8", "utf-8", "utf8", "x-unicode20utf8" ], "name": "UTF-8" } ], "heading": "The Encoding" }, { "encodings": [ { "labels": [ "866", "cp866", "csibm866", "ibm866" ], "name": "IBM866" }, { "labels": [ "csisolatin2", "iso-8859-2", "iso-ir-101", "iso8859-2", "iso88592", "iso_8859-2", "iso_8859-2:1987", "l2", "latin2" ], "name": "ISO-8859-2" }, { "labels": [ "csisolatin3", "iso-8859-3", "iso-ir-109", "iso8859-3", "iso88593", "iso_8859-3", "iso_8859-3:1988", "l3", "latin3" ], "name": "ISO-8859-3" }, { "labels": [ "csisolatin4", "iso-8859-4", "iso-ir-110", "iso8859-4", "iso88594", "iso_8859-4", "iso_8859-4:1988", "l4", "latin4" ], "name": "ISO-8859-4" }, { "labels": [ "csisolatincyrillic", "cyrillic", "iso-8859-5", "iso-ir-144", "iso8859-5", "iso88595", "iso_8859-5", "iso_8859-5:1988" ], "name": "ISO-8859-5" }, { "labels": [ "arabic", "asmo-708", "csiso88596e", "csiso88596i", "csisolatinarabic", "ecma-114", "iso-8859-6", "iso-8859-6-e", "iso-8859-6-i", "iso-ir-127", "iso8859-6", "iso88596", "iso_8859-6", "iso_8859-6:1987" ], "name": "ISO-8859-6" }, { "labels": [ "csisolatingreek", "ecma-118", "elot_928", "greek", "greek8", "iso-8859-7", "iso-ir-126", "iso8859-7", "iso88597", "iso_8859-7", "iso_8859-7:1987", "sun_eu_greek" ], "name": "ISO-8859-7" }, { "labels": [ "csiso88598e", "csisolatinhebrew", "hebrew", "iso-8859-8", "iso-8859-8-e", "iso-ir-138", "iso8859-8", "iso88598", "iso_8859-8", "iso_8859-8:1988", "visual" ], "name": "ISO-8859-8" }, { "labels": [ "csiso88598i", "iso-8859-8-i", "logical" ], "name": "ISO-8859-8-I" }, { "labels": [ "csisolatin6", "iso-8859-10", "iso-ir-157", "iso8859-10", "iso885910", "l6", "latin6" ], "name": "ISO-8859-10" }, { "labels": [ "iso-8859-13", "iso8859-13", "iso885913" ], "name": "ISO-8859-13" }, { "labels": [ "iso-8859-14", "iso8859-14", "iso885914" ], "name": "ISO-8859-14" }, { "labels": [ "csisolatin9", "iso-8859-15", "iso8859-15", "iso885915", "iso_8859-15", "l9" ], "name": "ISO-8859-15" }, { "labels": [ "iso-8859-16" ], "name": "ISO-8859-16" }, { "labels": [ "cskoi8r", "koi", "koi8", "koi8-r", "koi8_r" ], "name": "KOI8-R" }, { "labels": [ "koi8-ru", "koi8-u" ], "name": "KOI8-U" }, { "labels": [ "csmacintosh", "mac", "macintosh", "x-mac-roman" ], "name": "macintosh" }, { "labels": [ "dos-874", "iso-8859-11", "iso8859-11", "iso885911", "tis-620", "windows-874" ], "name": "windows-874" }, { "labels": [ "cp1250", "windows-1250", "x-cp1250" ], "name": "windows-1250" }, { "labels": [ "cp1251", "windows-1251", "x-cp1251" ], "name": "windows-1251" }, { "labels": [ "ansi_x3.4-1968", "ascii", "cp1252", "cp819", "csisolatin1", "ibm819", "iso-8859-1", "iso-ir-100", "iso8859-1", "iso88591", "iso_8859-1", "iso_8859-1:1987", "l1", "latin1", "us-ascii", "windows-1252", "x-cp1252" ], "name": "windows-1252" }, { "labels": [ "cp1253", "windows-1253", "x-cp1253" ], "name": "windows-1253" }, { "labels": [ "cp1254", "csisolatin5", "iso-8859-9", "iso-ir-148", "iso8859-9", "iso88599", "iso_8859-9", "iso_8859-9:1989", "l5", "latin5", "windows-1254", "x-cp1254" ], "name": "windows-1254" }, { "labels": [ "cp1255", "windows-1255", "x-cp1255" ], "name": "windows-1255" }, { "labels": [ "cp1256", "windows-1256", "x-cp1256" ], "name": "windows-1256" }, { "labels": [ "cp1257", "windows-1257", "x-cp1257" ], "name": "windows-1257" }, { "labels": [ "cp1258", "windows-1258", "x-cp1258" ], "name": "windows-1258" }, { "labels": [ "x-mac-cyrillic", "x-mac-ukrainian" ], "name": "x-mac-cyrillic" } ], "heading": "Legacy single-byte encodings" }, { "encodings": [ { "labels": [ "chinese", "csgb2312", "csiso58gb231280", "gb2312", "gb_2312", "gb_2312-80", "gbk", "iso-ir-58", "x-gbk" ], "name": "GBK" }, { "labels": [ "gb18030" ], "name": "gb18030" } ], "heading": "Legacy multi-byte Chinese (simplified) encodings" }, { "encodings": [ { "labels": [ "big5", "big5-hkscs", "cn-big5", "csbig5", "x-x-big5" ], "name": "Big5" } ], "heading": "Legacy multi-byte Chinese (traditional) encodings" }, { "encodings": [ { "labels": [ "cseucpkdfmtjapanese", "euc-jp", "x-euc-jp" ], "name": "EUC-JP" }, { "labels": [ "csiso2022jp", "iso-2022-jp" ], "name": "ISO-2022-JP" }, { "labels": [ "csshiftjis", "ms932", "ms_kanji", "shift-jis", "shift_jis", "sjis", "windows-31j", "x-sjis" ], "name": "Shift_JIS" } ], "heading": "Legacy multi-byte Japanese encodings" }, { "encodings": [ { "labels": [ "cseuckr", "csksc56011987", "euc-kr", "iso-ir-149", "korean", "ks_c_5601-1987", "ks_c_5601-1989", "ksc5601", "ksc_5601", "windows-949" ], "name": "EUC-KR" } ], "heading": "Legacy multi-byte Korean encodings" }, { "encodings": [ { "labels": [ "csiso2022kr", "hz-gb-2312", "iso-2022-cn", "iso-2022-cn-ext", "iso-2022-kr", "replacement" ], "name": "replacement" }, { "labels": [ "unicodefffe", "utf-16be" ], "name": "UTF-16BE" }, { "labels": [ "csunicode", "iso-10646-ucs-2", "ucs-2", "unicode", "unicodefeff", "utf-16", "utf-16le" ], "name": "UTF-16LE" }, { "labels": [ "x-user-defined" ], "name": "x-user-defined" } ], "heading": "Legacy miscellaneous encodings" } ] cjs-128.0/installed-tests/js/modules/sideEffect4.js0000664000175000017500000000033014771557763021137 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2024 Gary Li const doImport = async () => { await import('./sideEffect3.js?bar=baz'); }; await doImport(); cjs-128.0/installed-tests/js/minijasmine-executor.js0000664000175000017500000000322414771557763021506 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2016 Philip Chimento // SPDX-FileCopyrightText: 2022 Evan Welsh import * as system from 'system'; import GLib from 'gi://GLib'; import { environment, retval, errorsOutput, mainloop, mainloopLock, } from './minijasmine.js'; // environment.execute() queues up all the tests and runs them // asynchronously. This should start after the main loop starts, otherwise // we will hit the main loop only after several tests have already run. For // consistency we should guarantee that there is a main loop running during // all tests. GLib.idle_add(GLib.PRIORITY_DEFAULT, function () { try { environment.execute(); } catch (e) { print('Bail out! Exception occurred inside Jasmine:', e); mainloop.quit(); system.exit(1); } return GLib.SOURCE_REMOVE; }); // Keep running the main loop while mainloopLock is not null and resolves true. // This happens when testing the main loop itself, in testAsyncMainloop.js. We // don't want to exit minijasmine when the inner loop exits. do { // Run the mainloop // This rule is to encourage parallelizing async // operations, in this case we don't want that. // eslint-disable-next-line no-await-in-loop await mainloop.runAsync(); // eslint-disable-next-line no-await-in-loop } while (await mainloopLock); // On exit, check the return value and print any errors which occurred if (retval !== 0) { printerr(errorsOutput.join('\n')); print('# Test script failed; see test log for assertions'); system.exit(retval); } cjs-128.0/installed-tests/js/jasmine.js0000664000175000017500000076243214771557763017012 0ustar fabiofabio// SPDX-License-Identifier: MIT // SPDX-FileCopyrightText: 2008-2020 Pivotal Labs // eslint-disable-next-line no-unused-vars var getJasmineRequireObj = (function(jasmineGlobal) { var jasmineRequire; if ( typeof module !== 'undefined' && module.exports && typeof exports !== 'undefined' ) { if (typeof global !== 'undefined') { jasmineGlobal = global; } else { jasmineGlobal = {}; } jasmineRequire = exports; } else { if ( typeof window !== 'undefined' && typeof window.toString === 'function' && window.toString() === '[object GjsGlobal]' ) { jasmineGlobal = window; } jasmineRequire = jasmineGlobal.jasmineRequire = {}; } function getJasmineRequire() { return jasmineRequire; } getJasmineRequire().core = function(jRequire) { var j$ = {}; jRequire.base(j$, jasmineGlobal); j$.util = jRequire.util(j$); j$.errors = jRequire.errors(); j$.formatErrorMsg = jRequire.formatErrorMsg(); j$.Any = jRequire.Any(j$); j$.Anything = jRequire.Anything(j$); j$.CallTracker = jRequire.CallTracker(j$); j$.MockDate = jRequire.MockDate(); j$.getClearStack = jRequire.clearStack(j$); j$.Clock = jRequire.Clock(); j$.DelayedFunctionScheduler = jRequire.DelayedFunctionScheduler(j$); j$.Env = jRequire.Env(j$); j$.StackTrace = jRequire.StackTrace(j$); j$.ExceptionFormatter = jRequire.ExceptionFormatter(j$); j$.ExpectationFilterChain = jRequire.ExpectationFilterChain(); j$.Expector = jRequire.Expector(j$); j$.Expectation = jRequire.Expectation(j$); j$.buildExpectationResult = jRequire.buildExpectationResult(j$); j$.JsApiReporter = jRequire.JsApiReporter(j$); j$.asymmetricEqualityTesterArgCompatShim = jRequire.asymmetricEqualityTesterArgCompatShim( j$ ); j$.makePrettyPrinter = jRequire.makePrettyPrinter(j$); j$.pp = j$.makePrettyPrinter(); j$.MatchersUtil = jRequire.MatchersUtil(j$); j$.matchersUtil = new j$.MatchersUtil({ customTesters: [], pp: j$.pp }); j$.ObjectContaining = jRequire.ObjectContaining(j$); j$.ArrayContaining = jRequire.ArrayContaining(j$); j$.ArrayWithExactContents = jRequire.ArrayWithExactContents(j$); j$.MapContaining = jRequire.MapContaining(j$); j$.SetContaining = jRequire.SetContaining(j$); j$.QueueRunner = jRequire.QueueRunner(j$); j$.ReportDispatcher = jRequire.ReportDispatcher(j$); j$.Spec = jRequire.Spec(j$); j$.Spy = jRequire.Spy(j$); j$.SpyFactory = jRequire.SpyFactory(j$); j$.SpyRegistry = jRequire.SpyRegistry(j$); j$.SpyStrategy = jRequire.SpyStrategy(j$); j$.StringMatching = jRequire.StringMatching(j$); j$.UserContext = jRequire.UserContext(j$); j$.Suite = jRequire.Suite(j$); j$.Timer = jRequire.Timer(); j$.TreeProcessor = jRequire.TreeProcessor(); j$.version = jRequire.version(); j$.Order = jRequire.Order(); j$.DiffBuilder = jRequire.DiffBuilder(j$); j$.NullDiffBuilder = jRequire.NullDiffBuilder(j$); j$.ObjectPath = jRequire.ObjectPath(j$); j$.MismatchTree = jRequire.MismatchTree(j$); j$.GlobalErrors = jRequire.GlobalErrors(j$); j$.Truthy = jRequire.Truthy(j$); j$.Falsy = jRequire.Falsy(j$); j$.Empty = jRequire.Empty(j$); j$.NotEmpty = jRequire.NotEmpty(j$); j$.matchers = jRequire.requireMatchers(jRequire, j$); j$.asyncMatchers = jRequire.requireAsyncMatchers(jRequire, j$); return j$; }; return getJasmineRequire; })(this); getJasmineRequireObj().requireMatchers = function(jRequire, j$) { var availableMatchers = [ 'nothing', 'toBe', 'toBeCloseTo', 'toBeDefined', 'toBeInstanceOf', 'toBeFalse', 'toBeFalsy', 'toBeGreaterThan', 'toBeGreaterThanOrEqual', 'toBeLessThan', 'toBeLessThanOrEqual', 'toBeNaN', 'toBeNegativeInfinity', 'toBeNull', 'toBePositiveInfinity', 'toBeTrue', 'toBeTruthy', 'toBeUndefined', 'toContain', 'toEqual', 'toHaveSize', 'toHaveBeenCalled', 'toHaveBeenCalledBefore', 'toHaveBeenCalledOnceWith', 'toHaveBeenCalledTimes', 'toHaveBeenCalledWith', 'toHaveClass', 'toMatch', 'toThrow', 'toThrowError', 'toThrowMatching' ], matchers = {}; for (var i = 0; i < availableMatchers.length; i++) { var name = availableMatchers[i]; matchers[name] = jRequire[name](j$); } return matchers; }; getJasmineRequireObj().base = function(j$, jasmineGlobal) { j$.unimplementedMethod_ = function() { throw new Error('unimplemented method'); }; /** * Maximum object depth the pretty printer will print to. * Set this to a lower value to speed up pretty printing if you have large objects. * @name jasmine.MAX_PRETTY_PRINT_DEPTH * @since 1.3.0 */ j$.MAX_PRETTY_PRINT_DEPTH = 8; /** * Maximum number of array elements to display when pretty printing objects. * This will also limit the number of keys and values displayed for an object. * Elements past this number will be ellipised. * @name jasmine.MAX_PRETTY_PRINT_ARRAY_LENGTH * @since 2.7.0 */ j$.MAX_PRETTY_PRINT_ARRAY_LENGTH = 50; /** * Maximum number of characters to display when pretty printing objects. * Characters past this number will be ellipised. * @name jasmine.MAX_PRETTY_PRINT_CHARS * @since 2.9.0 */ j$.MAX_PRETTY_PRINT_CHARS = 1000; /** * Default number of milliseconds Jasmine will wait for an asynchronous spec to complete. * @name jasmine.DEFAULT_TIMEOUT_INTERVAL * @since 1.3.0 */ j$.DEFAULT_TIMEOUT_INTERVAL = 5000; j$.getGlobal = function() { return jasmineGlobal; }; /** * Get the currently booted Jasmine Environment. * * @name jasmine.getEnv * @since 1.3.0 * @function * @return {Env} */ j$.getEnv = function(options) { var env = (j$.currentEnv_ = j$.currentEnv_ || new j$.Env(options)); //jasmine. singletons in here (setTimeout blah blah). return env; }; j$.isArray_ = function(value) { return j$.isA_('Array', value); }; j$.isObject_ = function(value) { return ( !j$.util.isUndefined(value) && value !== null && j$.isA_('Object', value) ); }; j$.isString_ = function(value) { return j$.isA_('String', value); }; j$.isNumber_ = function(value) { return j$.isA_('Number', value); }; j$.isFunction_ = function(value) { return j$.isA_('Function', value); }; j$.isAsyncFunction_ = function(value) { return j$.isA_('AsyncFunction', value); }; j$.isTypedArray_ = function(value) { return ( j$.isA_('Float32Array', value) || j$.isA_('Float64Array', value) || j$.isA_('Int16Array', value) || j$.isA_('Int32Array', value) || j$.isA_('Int8Array', value) || j$.isA_('Uint16Array', value) || j$.isA_('Uint32Array', value) || j$.isA_('Uint8Array', value) || j$.isA_('Uint8ClampedArray', value) ); }; j$.isA_ = function(typeName, value) { return j$.getType_(value) === '[object ' + typeName + ']'; }; j$.isError_ = function(value) { if (value instanceof Error) { return true; } if (value && value.constructor && value.constructor.constructor) { var valueGlobal = value.constructor.constructor('return this'); if (j$.isFunction_(valueGlobal)) { valueGlobal = valueGlobal(); } if (valueGlobal.Error && value instanceof valueGlobal.Error) { return true; } } return false; }; j$.isAsymmetricEqualityTester_ = function(obj) { return obj ? j$.isA_('Function', obj.asymmetricMatch) : false; }; j$.getType_ = function(value) { return Object.prototype.toString.apply(value); }; j$.isDomNode = function(obj) { // Node is a function, because constructors return typeof jasmineGlobal.Node !== 'undefined' ? obj instanceof jasmineGlobal.Node : obj !== null && typeof obj === 'object' && typeof obj.nodeType === 'number' && typeof obj.nodeName === 'string'; // return obj.nodeType > 0; }; j$.isMap = function(obj) { return ( obj !== null && typeof obj !== 'undefined' && typeof jasmineGlobal.Map !== 'undefined' && obj.constructor === jasmineGlobal.Map ); }; j$.isSet = function(obj) { return ( obj !== null && typeof obj !== 'undefined' && typeof jasmineGlobal.Set !== 'undefined' && obj.constructor === jasmineGlobal.Set ); }; j$.isWeakMap = function(obj) { return ( obj !== null && typeof obj !== 'undefined' && typeof jasmineGlobal.WeakMap !== 'undefined' && obj.constructor === jasmineGlobal.WeakMap ); }; j$.isDataView = function(obj) { return ( obj !== null && typeof obj !== 'undefined' && typeof jasmineGlobal.DataView !== 'undefined' && obj.constructor === jasmineGlobal.DataView ); }; j$.isPromise = function(obj) { return ( typeof jasmineGlobal.Promise !== 'undefined' && !!obj && obj.constructor === jasmineGlobal.Promise ); }; j$.isPromiseLike = function(obj) { return !!obj && j$.isFunction_(obj.then); }; j$.fnNameFor = function(func) { if (func.name) { return func.name; } var matches = func.toString().match(/^\s*function\s*(\w+)\s*\(/) || func.toString().match(/^\s*\[object\s*(\w+)Constructor\]/); return matches ? matches[1] : ''; }; /** * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if the actual value being compared is an instance of the specified class/constructor. * @name jasmine.any * @since 1.3.0 * @function * @param {Constructor} clazz - The constructor to check against. */ j$.any = function(clazz) { return new j$.Any(clazz); }; /** * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if the actual value being compared is not `null` and not `undefined`. * @name jasmine.anything * @since 2.2.0 * @function */ j$.anything = function() { return new j$.Anything(); }; /** * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if the actual value being compared is `true` or anything truthy. * @name jasmine.truthy * @since 3.1.0 * @function */ j$.truthy = function() { return new j$.Truthy(); }; /** * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if the actual value being compared is `null`, `undefined`, `0`, `false` or anything falsey. * @name jasmine.falsy * @since 3.1.0 * @function */ j$.falsy = function() { return new j$.Falsy(); }; /** * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if the actual value being compared is empty. * @name jasmine.empty * @since 3.1.0 * @function */ j$.empty = function() { return new j$.Empty(); }; /** * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if the actual value being compared is not empty. * @name jasmine.notEmpty * @since 3.1.0 * @function */ j$.notEmpty = function() { return new j$.NotEmpty(); }; /** * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if the actual value being compared contains at least the keys and values. * @name jasmine.objectContaining * @since 1.3.0 * @function * @param {Object} sample - The subset of properties that _must_ be in the actual. */ j$.objectContaining = function(sample) { return new j$.ObjectContaining(sample); }; /** * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if the actual value is a `String` that matches the `RegExp` or `String`. * @name jasmine.stringMatching * @since 2.2.0 * @function * @param {RegExp|String} expected */ j$.stringMatching = function(expected) { return new j$.StringMatching(expected); }; /** * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if the actual value is an `Array` that contains at least the elements in the sample. * @name jasmine.arrayContaining * @since 2.2.0 * @function * @param {Array} sample */ j$.arrayContaining = function(sample) { return new j$.ArrayContaining(sample); }; /** * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if the actual value is an `Array` that contains all of the elements in the sample in any order. * @name jasmine.arrayWithExactContents * @since 2.8.0 * @function * @param {Array} sample */ j$.arrayWithExactContents = function(sample) { return new j$.ArrayWithExactContents(sample); }; /** * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if every key/value pair in the sample passes the deep equality comparison * with at least one key/value pair in the actual value being compared * @name jasmine.mapContaining * @since 3.5.0 * @function * @param {Map} sample - The subset of items that _must_ be in the actual. */ j$.mapContaining = function(sample) { return new j$.MapContaining(sample); }; /** * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if every item in the sample passes the deep equality comparison * with at least one item in the actual value being compared * @name jasmine.setContaining * @since 3.5.0 * @function * @param {Set} sample - The subset of items that _must_ be in the actual. */ j$.setContaining = function(sample) { return new j$.SetContaining(sample); }; j$.isSpy = function(putativeSpy) { if (!putativeSpy) { return false; } return ( putativeSpy.and instanceof j$.SpyStrategy && putativeSpy.calls instanceof j$.CallTracker ); }; }; getJasmineRequireObj().util = function(j$) { var util = {}; util.inherit = function(childClass, parentClass) { var Subclass = function() {}; Subclass.prototype = parentClass.prototype; childClass.prototype = new Subclass(); }; util.htmlEscape = function(str) { if (!str) { return str; } return str .replace(/&/g, '&') .replace(//g, '>'); }; util.argsToArray = function(args) { var arrayOfArgs = []; for (var i = 0; i < args.length; i++) { arrayOfArgs.push(args[i]); } return arrayOfArgs; }; util.isUndefined = function(obj) { return obj === void 0; }; util.arrayContains = function(array, search) { var i = array.length; while (i--) { if (array[i] === search) { return true; } } return false; }; util.clone = function(obj) { if (Object.prototype.toString.apply(obj) === '[object Array]') { return obj.slice(); } var cloned = {}; for (var prop in obj) { if (obj.hasOwnProperty(prop)) { cloned[prop] = obj[prop]; } } return cloned; }; util.cloneArgs = function(args) { var clonedArgs = []; var argsAsArray = j$.util.argsToArray(args); for (var i = 0; i < argsAsArray.length; i++) { var str = Object.prototype.toString.apply(argsAsArray[i]), primitives = /^\[object (Boolean|String|RegExp|Number)/; // All falsey values are either primitives, `null`, or `undefined. if (!argsAsArray[i] || str.match(primitives)) { clonedArgs.push(argsAsArray[i]); } else { clonedArgs.push(j$.util.clone(argsAsArray[i])); } } return clonedArgs; }; util.getPropertyDescriptor = function(obj, methodName) { var descriptor, proto = obj; do { descriptor = Object.getOwnPropertyDescriptor(proto, methodName); proto = Object.getPrototypeOf(proto); } while (!descriptor && proto); return descriptor; }; util.objectDifference = function(obj, toRemove) { var diff = {}; for (var key in obj) { if (util.has(obj, key) && !util.has(toRemove, key)) { diff[key] = obj[key]; } } return diff; }; util.has = function(obj, key) { return Object.prototype.hasOwnProperty.call(obj, key); }; util.errorWithStack = function errorWithStack() { // Don't throw and catch if we don't have to, because it makes it harder // for users to debug their code with exception breakpoints. var error = new Error(); if (error.stack) { return error; } // But some browsers (e.g. Phantom) only provide a stack trace if we throw. try { throw new Error(); } catch (e) { return e; } }; function callerFile() { var trace = new j$.StackTrace(util.errorWithStack()); return trace.frames[2].file; } util.jasmineFile = (function() { var result; return function() { if (!result) { result = callerFile(); } return result; }; })(); function StopIteration() {} StopIteration.prototype = Object.create(Error.prototype); StopIteration.prototype.constructor = StopIteration; // useful for maps and sets since `forEach` is the only IE11-compatible way to iterate them util.forEachBreakable = function(iterable, iteratee) { function breakLoop() { throw new StopIteration(); } try { iterable.forEach(function(value, key) { iteratee(breakLoop, value, key, iterable); }); } catch (error) { if (!(error instanceof StopIteration)) throw error; } }; return util; }; getJasmineRequireObj().Spec = function(j$) { function Spec(attrs) { this.expectationFactory = attrs.expectationFactory; this.asyncExpectationFactory = attrs.asyncExpectationFactory; this.resultCallback = attrs.resultCallback || function() {}; this.id = attrs.id; this.description = attrs.description || ''; this.queueableFn = attrs.queueableFn; this.beforeAndAfterFns = attrs.beforeAndAfterFns || function() { return { befores: [], afters: [] }; }; this.userContext = attrs.userContext || function() { return {}; }; this.onStart = attrs.onStart || function() {}; this.getSpecName = attrs.getSpecName || function() { return ''; }; this.expectationResultFactory = attrs.expectationResultFactory || function() {}; this.queueRunnerFactory = attrs.queueRunnerFactory || function() {}; this.catchingExceptions = attrs.catchingExceptions || function() { return true; }; this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure; this.timer = attrs.timer || new j$.Timer(); if (!this.queueableFn.fn) { this.pend(); } /** * @typedef SpecResult * @property {Int} id - The unique id of this spec. * @property {String} description - The description passed to the {@link it} that created this spec. * @property {String} fullName - The full description including all ancestors of this spec. * @property {Expectation[]} failedExpectations - The list of expectations that failed during execution of this spec. * @property {Expectation[]} passedExpectations - The list of expectations that passed during execution of this spec. * @property {Expectation[]} deprecationWarnings - The list of deprecation warnings that occurred during execution this spec. * @property {String} pendingReason - If the spec is {@link pending}, this will be the reason. * @property {String} status - Once the spec has completed, this string represents the pass/fail status of this spec. * @property {number} duration - The time in ms used by the spec execution, including any before/afterEach. * @property {Object} properties - User-supplied properties, if any, that were set using {@link Env#setSpecProperty} */ this.result = { id: this.id, description: this.description, fullName: this.getFullName(), failedExpectations: [], passedExpectations: [], deprecationWarnings: [], pendingReason: '', duration: null, properties: null }; } Spec.prototype.addExpectationResult = function(passed, data, isError) { var expectationResult = this.expectationResultFactory(data); if (passed) { this.result.passedExpectations.push(expectationResult); } else { this.result.failedExpectations.push(expectationResult); if (this.throwOnExpectationFailure && !isError) { throw new j$.errors.ExpectationFailed(); } } }; Spec.prototype.setSpecProperty = function(key, value) { this.result.properties = this.result.properties || {}; this.result.properties[key] = value; }; Spec.prototype.expect = function(actual) { return this.expectationFactory(actual, this); }; Spec.prototype.expectAsync = function(actual) { return this.asyncExpectationFactory(actual, this); }; Spec.prototype.execute = function(onComplete, excluded, failSpecWithNoExp) { var self = this; var onStart = { fn: function(done) { self.timer.start(); self.onStart(self, done); } }; var complete = { fn: function(done) { self.queueableFn.fn = null; self.result.status = self.status(excluded, failSpecWithNoExp); self.result.duration = self.timer.elapsed(); self.resultCallback(self.result, done); } }; var fns = this.beforeAndAfterFns(); var regularFns = fns.befores.concat(this.queueableFn); var runnerConfig = { isLeaf: true, queueableFns: regularFns, cleanupFns: fns.afters, onException: function() { self.onException.apply(self, arguments); }, onComplete: function() { onComplete( self.result.status === 'failed' && new j$.StopExecutionError('spec failed') ); }, userContext: this.userContext() }; if (this.markedPending || excluded === true) { runnerConfig.queueableFns = []; runnerConfig.cleanupFns = []; } runnerConfig.queueableFns.unshift(onStart); runnerConfig.cleanupFns.push(complete); this.queueRunnerFactory(runnerConfig); }; Spec.prototype.onException = function onException(e) { if (Spec.isPendingSpecException(e)) { this.pend(extractCustomPendingMessage(e)); return; } if (e instanceof j$.errors.ExpectationFailed) { return; } this.addExpectationResult( false, { matcherName: '', passed: false, expected: '', actual: '', error: e }, true ); }; Spec.prototype.pend = function(message) { this.markedPending = true; if (message) { this.result.pendingReason = message; } }; Spec.prototype.getResult = function() { this.result.status = this.status(); return this.result; }; Spec.prototype.status = function(excluded, failSpecWithNoExpectations) { if (excluded === true) { return 'excluded'; } if (this.markedPending) { return 'pending'; } if ( this.result.failedExpectations.length > 0 || (failSpecWithNoExpectations && this.result.failedExpectations.length + this.result.passedExpectations.length === 0) ) { return 'failed'; } return 'passed'; }; Spec.prototype.getFullName = function() { return this.getSpecName(this); }; Spec.prototype.addDeprecationWarning = function(deprecation) { if (typeof deprecation === 'string') { deprecation = { message: deprecation }; } this.result.deprecationWarnings.push( this.expectationResultFactory(deprecation) ); }; var extractCustomPendingMessage = function(e) { var fullMessage = e.toString(), boilerplateStart = fullMessage.indexOf(Spec.pendingSpecExceptionMessage), boilerplateEnd = boilerplateStart + Spec.pendingSpecExceptionMessage.length; return fullMessage.substr(boilerplateEnd); }; Spec.pendingSpecExceptionMessage = '=> marked Pending'; Spec.isPendingSpecException = function(e) { return !!( e && e.toString && e.toString().indexOf(Spec.pendingSpecExceptionMessage) !== -1 ); }; return Spec; }; if (typeof window == void 0 && typeof exports == 'object') { /* globals exports */ exports.Spec = jasmineRequire.Spec; } /*jshint bitwise: false*/ getJasmineRequireObj().Order = function() { function Order(options) { this.random = 'random' in options ? options.random : true; var seed = (this.seed = options.seed || generateSeed()); this.sort = this.random ? randomOrder : naturalOrder; function naturalOrder(items) { return items; } function randomOrder(items) { var copy = items.slice(); copy.sort(function(a, b) { return jenkinsHash(seed + a.id) - jenkinsHash(seed + b.id); }); return copy; } function generateSeed() { return String(Math.random()).slice(-5); } // Bob Jenkins One-at-a-Time Hash algorithm is a non-cryptographic hash function // used to get a different output when the key changes slightly. // We use your return to sort the children randomly in a consistent way when // used in conjunction with a seed function jenkinsHash(key) { var hash, i; for (hash = i = 0; i < key.length; ++i) { hash += key.charCodeAt(i); hash += hash << 10; hash ^= hash >> 6; } hash += hash << 3; hash ^= hash >> 11; hash += hash << 15; return hash; } } return Order; }; getJasmineRequireObj().Env = function(j$) { /** * _Note:_ Do not construct this directly, Jasmine will make one during booting. * @name Env * @since 2.0.0 * @classdesc The Jasmine environment * @constructor */ function Env(options) { options = options || {}; var self = this; var global = options.global || j$.getGlobal(); var customPromise; var totalSpecsDefined = 0; var realSetTimeout = global.setTimeout; var realClearTimeout = global.clearTimeout; var clearStack = j$.getClearStack(global); this.clock = new j$.Clock( global, function() { return new j$.DelayedFunctionScheduler(); }, new j$.MockDate(global) ); var runnableResources = {}; var currentSpec = null; var currentlyExecutingSuites = []; var currentDeclarationSuite = null; var hasFailures = false; /** * This represents the available options to configure Jasmine. * Options that are not provided will use their default values * @interface Configuration * @since 3.3.0 */ var config = { /** * Whether to randomize spec execution order * @name Configuration#random * @since 3.3.0 * @type Boolean * @default true */ random: true, /** * Seed to use as the basis of randomization. * Null causes the seed to be determined randomly at the start of execution. * @name Configuration#seed * @since 3.3.0 * @type function * @default null */ seed: null, /** * Whether to stop execution of the suite after the first spec failure * @name Configuration#failFast * @since 3.3.0 * @type Boolean * @default false */ failFast: false, /** * Whether to fail the spec if it ran no expectations. By default * a spec that ran no expectations is reported as passed. Setting this * to true will report such spec as a failure. * @name Configuration#failSpecWithNoExpectations * @since 3.5.0 * @type Boolean * @default false */ failSpecWithNoExpectations: false, /** * Whether to cause specs to only have one expectation failure. * @name Configuration#oneFailurePerSpec * @since 3.3.0 * @type Boolean * @default false */ oneFailurePerSpec: false, /** * Function to use to filter specs * @name Configuration#specFilter * @since 3.3.0 * @type function * @default true */ specFilter: function() { return true; }, /** * Whether or not reporters should hide disabled specs from their output. * Currently only supported by Jasmine's HTMLReporter * @name Configuration#hideDisabled * @since 3.3.0 * @type Boolean * @default false */ hideDisabled: false, /** * Set to provide a custom promise library that Jasmine will use if it needs * to create a promise. If not set, it will default to whatever global Promise * library is available (if any). * @name Configuration#Promise * @since 3.5.0 * @type function * @default undefined */ Promise: undefined }; var currentSuite = function() { return currentlyExecutingSuites[currentlyExecutingSuites.length - 1]; }; var currentRunnable = function() { return currentSpec || currentSuite(); }; var globalErrors = null; var installGlobalErrors = function() { if (globalErrors) { return; } globalErrors = new j$.GlobalErrors(); globalErrors.install(); }; if (!options.suppressLoadErrors) { installGlobalErrors(); globalErrors.pushListener(function( message, filename, lineno, colNo, err ) { topSuite.result.failedExpectations.push({ passed: false, globalErrorType: 'load', message: message, stack: err && err.stack, filename: filename, lineno: lineno }); }); } /** * Configure your jasmine environment * @name Env#configure * @since 3.3.0 * @argument {Configuration} configuration * @function */ this.configure = function(configuration) { if (configuration.specFilter) { config.specFilter = configuration.specFilter; } if (configuration.hasOwnProperty('random')) { config.random = !!configuration.random; } if (configuration.hasOwnProperty('seed')) { config.seed = configuration.seed; } if (configuration.hasOwnProperty('failFast')) { config.failFast = configuration.failFast; } if (configuration.hasOwnProperty('failSpecWithNoExpectations')) { config.failSpecWithNoExpectations = configuration.failSpecWithNoExpectations; } if (configuration.hasOwnProperty('oneFailurePerSpec')) { config.oneFailurePerSpec = configuration.oneFailurePerSpec; } if (configuration.hasOwnProperty('hideDisabled')) { config.hideDisabled = configuration.hideDisabled; } // Don't use hasOwnProperty to check for Promise existence because Promise // can be initialized to undefined, either explicitly or by using the // object returned from Env#configuration. In particular, Karma does this. if (configuration.Promise) { if ( typeof configuration.Promise.resolve === 'function' && typeof configuration.Promise.reject === 'function' ) { customPromise = configuration.Promise; } else { throw new Error( 'Custom promise library missing `resolve`/`reject` functions' ); } } }; /** * Get the current configuration for your jasmine environment * @name Env#configuration * @since 3.3.0 * @function * @returns {Configuration} */ this.configuration = function() { var result = {}; for (var property in config) { result[property] = config[property]; } return result; }; Object.defineProperty(this, 'specFilter', { get: function() { self.deprecated( 'Getting specFilter directly from Env is deprecated and will be removed in a future version of Jasmine, please check the specFilter option from `configuration`' ); return config.specFilter; }, set: function(val) { self.deprecated( 'Setting specFilter directly on Env is deprecated and will be removed in a future version of Jasmine, please use the specFilter option in `configure`' ); config.specFilter = val; } }); this.setDefaultSpyStrategy = function(defaultStrategyFn) { if (!currentRunnable()) { throw new Error( 'Default spy strategy must be set in a before function or a spec' ); } runnableResources[ currentRunnable().id ].defaultStrategyFn = defaultStrategyFn; }; this.addSpyStrategy = function(name, fn) { if (!currentRunnable()) { throw new Error( 'Custom spy strategies must be added in a before function or a spec' ); } runnableResources[currentRunnable().id].customSpyStrategies[name] = fn; }; this.addCustomEqualityTester = function(tester) { if (!currentRunnable()) { throw new Error( 'Custom Equalities must be added in a before function or a spec' ); } runnableResources[currentRunnable().id].customEqualityTesters.push( tester ); }; this.addMatchers = function(matchersToAdd) { if (!currentRunnable()) { throw new Error( 'Matchers must be added in a before function or a spec' ); } var customMatchers = runnableResources[currentRunnable().id].customMatchers; for (var matcherName in matchersToAdd) { customMatchers[matcherName] = matchersToAdd[matcherName]; } }; this.addAsyncMatchers = function(matchersToAdd) { if (!currentRunnable()) { throw new Error( 'Async Matchers must be added in a before function or a spec' ); } var customAsyncMatchers = runnableResources[currentRunnable().id].customAsyncMatchers; for (var matcherName in matchersToAdd) { customAsyncMatchers[matcherName] = matchersToAdd[matcherName]; } }; this.addCustomObjectFormatter = function(formatter) { if (!currentRunnable()) { throw new Error( 'Custom object formatters must be added in a before function or a spec' ); } runnableResources[currentRunnable().id].customObjectFormatters.push( formatter ); }; j$.Expectation.addCoreMatchers(j$.matchers); j$.Expectation.addAsyncCoreMatchers(j$.asyncMatchers); var nextSpecId = 0; var getNextSpecId = function() { return 'spec' + nextSpecId++; }; var nextSuiteId = 0; var getNextSuiteId = function() { return 'suite' + nextSuiteId++; }; var makePrettyPrinter = function() { var customObjectFormatters = runnableResources[currentRunnable().id].customObjectFormatters; return j$.makePrettyPrinter(customObjectFormatters); }; var makeMatchersUtil = function() { var customEqualityTesters = runnableResources[currentRunnable().id].customEqualityTesters; return new j$.MatchersUtil({ customTesters: customEqualityTesters, pp: makePrettyPrinter() }); }; var expectationFactory = function(actual, spec) { var customEqualityTesters = runnableResources[spec.id].customEqualityTesters; return j$.Expectation.factory({ matchersUtil: makeMatchersUtil(), customEqualityTesters: customEqualityTesters, customMatchers: runnableResources[spec.id].customMatchers, actual: actual, addExpectationResult: addExpectationResult }); function addExpectationResult(passed, result) { return spec.addExpectationResult(passed, result); } }; function recordLateExpectation(runable, runableType, result) { var delayedExpectationResult = {}; Object.keys(result).forEach(function(k) { delayedExpectationResult[k] = result[k]; }); delayedExpectationResult.passed = false; delayedExpectationResult.globalErrorType = 'lateExpectation'; delayedExpectationResult.message = runableType + ' "' + runable.getFullName() + '" ran a "' + result.matcherName + '" expectation after it finished.\n'; if (result.message) { delayedExpectationResult.message += 'Message: "' + result.message + '"\n'; } delayedExpectationResult.message += 'Did you forget to return or await the result of expectAsync?'; topSuite.result.failedExpectations.push(delayedExpectationResult); } var asyncExpectationFactory = function(actual, spec, runableType) { return j$.Expectation.asyncFactory({ matchersUtil: makeMatchersUtil(), customEqualityTesters: runnableResources[spec.id].customEqualityTesters, customAsyncMatchers: runnableResources[spec.id].customAsyncMatchers, actual: actual, addExpectationResult: addExpectationResult }); function addExpectationResult(passed, result) { if (currentRunnable() !== spec) { recordLateExpectation(spec, runableType, result); } return spec.addExpectationResult(passed, result); } }; var suiteAsyncExpectationFactory = function(actual, suite) { return asyncExpectationFactory(actual, suite, 'Suite'); }; var specAsyncExpectationFactory = function(actual, suite) { return asyncExpectationFactory(actual, suite, 'Spec'); }; var defaultResourcesForRunnable = function(id, parentRunnableId) { var resources = { spies: [], customEqualityTesters: [], customMatchers: {}, customAsyncMatchers: {}, customSpyStrategies: {}, defaultStrategyFn: undefined, customObjectFormatters: [] }; if (runnableResources[parentRunnableId]) { resources.customEqualityTesters = j$.util.clone( runnableResources[parentRunnableId].customEqualityTesters ); resources.customMatchers = j$.util.clone( runnableResources[parentRunnableId].customMatchers ); resources.customAsyncMatchers = j$.util.clone( runnableResources[parentRunnableId].customAsyncMatchers ); resources.defaultStrategyFn = runnableResources[parentRunnableId].defaultStrategyFn; } runnableResources[id] = resources; }; var clearResourcesForRunnable = function(id) { spyRegistry.clearSpies(); delete runnableResources[id]; }; var beforeAndAfterFns = function(suite) { return function() { var befores = [], afters = []; while (suite) { befores = befores.concat(suite.beforeFns); afters = afters.concat(suite.afterFns); suite = suite.parentSuite; } return { befores: befores.reverse(), afters: afters }; }; }; var getSpecName = function(spec, suite) { var fullName = [spec.description], suiteFullName = suite.getFullName(); if (suiteFullName !== '') { fullName.unshift(suiteFullName); } return fullName.join(' '); }; // TODO: we may just be able to pass in the fn instead of wrapping here var buildExpectationResult = j$.buildExpectationResult, exceptionFormatter = new j$.ExceptionFormatter(), expectationResultFactory = function(attrs) { attrs.messageFormatter = exceptionFormatter.message; attrs.stackFormatter = exceptionFormatter.stack; return buildExpectationResult(attrs); }; /** * Sets whether Jasmine should throw an Error when an expectation fails. * This causes a spec to only have one expectation failure. * @name Env#throwOnExpectationFailure * @since 2.3.0 * @function * @param {Boolean} value Whether to throw when a expectation fails * @deprecated Use the `oneFailurePerSpec` option with {@link Env#configure} */ this.throwOnExpectationFailure = function(value) { this.deprecated( 'Setting throwOnExpectationFailure directly on Env is deprecated and will be removed in a future version of Jasmine, please use the oneFailurePerSpec option in `configure`' ); this.configure({ oneFailurePerSpec: !!value }); }; this.throwingExpectationFailures = function() { this.deprecated( 'Getting throwingExpectationFailures directly from Env is deprecated and will be removed in a future version of Jasmine, please check the oneFailurePerSpec option from `configuration`' ); return config.oneFailurePerSpec; }; /** * Set whether to stop suite execution when a spec fails * @name Env#stopOnSpecFailure * @since 2.7.0 * @function * @param {Boolean} value Whether to stop suite execution when a spec fails * @deprecated Use the `failFast` option with {@link Env#configure} */ this.stopOnSpecFailure = function(value) { this.deprecated( 'Setting stopOnSpecFailure directly is deprecated and will be removed in a future version of Jasmine, please use the failFast option in `configure`' ); this.configure({ failFast: !!value }); }; this.stoppingOnSpecFailure = function() { this.deprecated( 'Getting stoppingOnSpecFailure directly from Env is deprecated and will be removed in a future version of Jasmine, please check the failFast option from `configuration`' ); return config.failFast; }; /** * Set whether to randomize test execution order * @name Env#randomizeTests * @since 2.4.0 * @function * @param {Boolean} value Whether to randomize execution order * @deprecated Use the `random` option with {@link Env#configure} */ this.randomizeTests = function(value) { this.deprecated( 'Setting randomizeTests directly is deprecated and will be removed in a future version of Jasmine, please use the random option in `configure`' ); config.random = !!value; }; this.randomTests = function() { this.deprecated( 'Getting randomTests directly from Env is deprecated and will be removed in a future version of Jasmine, please check the random option from `configuration`' ); return config.random; }; /** * Set the random number seed for spec randomization * @name Env#seed * @since 2.4.0 * @function * @param {Number} value The seed value * @deprecated Use the `seed` option with {@link Env#configure} */ this.seed = function(value) { this.deprecated( 'Setting seed directly is deprecated and will be removed in a future version of Jasmine, please use the seed option in `configure`' ); if (value) { config.seed = value; } return config.seed; }; this.hidingDisabled = function(value) { this.deprecated( 'Getting hidingDisabled directly from Env is deprecated and will be removed in a future version of Jasmine, please check the hideDisabled option from `configuration`' ); return config.hideDisabled; }; /** * @name Env#hideDisabled * @since 3.2.0 * @function */ this.hideDisabled = function(value) { this.deprecated( 'Setting hideDisabled directly is deprecated and will be removed in a future version of Jasmine, please use the hideDisabled option in `configure`' ); config.hideDisabled = !!value; }; this.deprecated = function(deprecation) { var runnable = currentRunnable() || topSuite; runnable.addDeprecationWarning(deprecation); if ( typeof console !== 'undefined' && typeof console.error === 'function' ) { console.error('DEPRECATION:', deprecation); } }; var queueRunnerFactory = function(options, args) { var failFast = false; if (options.isLeaf) { failFast = config.oneFailurePerSpec; } else if (!options.isReporter) { failFast = config.failFast; } options.clearStack = options.clearStack || clearStack; options.timeout = { setTimeout: realSetTimeout, clearTimeout: realClearTimeout }; options.fail = self.fail; options.globalErrors = globalErrors; options.completeOnFirstError = failFast; options.onException = options.onException || function(e) { (currentRunnable() || topSuite).onException(e); }; options.deprecated = self.deprecated; new j$.QueueRunner(options).execute(args); }; var topSuite = new j$.Suite({ env: this, id: getNextSuiteId(), description: 'Jasmine__TopLevel__Suite', expectationFactory: expectationFactory, asyncExpectationFactory: suiteAsyncExpectationFactory, expectationResultFactory: expectationResultFactory }); defaultResourcesForRunnable(topSuite.id); currentDeclarationSuite = topSuite; this.topSuite = function() { return topSuite; }; /** * This represents the available reporter callback for an object passed to {@link Env#addReporter}. * @interface Reporter * @see custom_reporter */ var reporter = new j$.ReportDispatcher( [ /** * `jasmineStarted` is called after all of the specs have been loaded, but just before execution starts. * @function * @name Reporter#jasmineStarted * @param {JasmineStartedInfo} suiteInfo Information about the full Jasmine suite that is being run * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. * @see async */ 'jasmineStarted', /** * When the entire suite has finished execution `jasmineDone` is called * @function * @name Reporter#jasmineDone * @param {JasmineDoneInfo} suiteInfo Information about the full Jasmine suite that just finished running. * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. * @see async */ 'jasmineDone', /** * `suiteStarted` is invoked when a `describe` starts to run * @function * @name Reporter#suiteStarted * @param {SuiteResult} result Information about the individual {@link describe} being run * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. * @see async */ 'suiteStarted', /** * `suiteDone` is invoked when all of the child specs and suites for a given suite have been run * * While jasmine doesn't require any specific functions, not defining a `suiteDone` will make it impossible for a reporter to know when a suite has failures in an `afterAll`. * @function * @name Reporter#suiteDone * @param {SuiteResult} result * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. * @see async */ 'suiteDone', /** * `specStarted` is invoked when an `it` starts to run (including associated `beforeEach` functions) * @function * @name Reporter#specStarted * @param {SpecResult} result Information about the individual {@link it} being run * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. * @see async */ 'specStarted', /** * `specDone` is invoked when an `it` and its associated `beforeEach` and `afterEach` functions have been run. * * While jasmine doesn't require any specific functions, not defining a `specDone` will make it impossible for a reporter to know when a spec has failed. * @function * @name Reporter#specDone * @param {SpecResult} result * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. * @see async */ 'specDone' ], queueRunnerFactory ); this.execute = function(runnablesToRun) { installGlobalErrors(); if (!runnablesToRun) { if (focusedRunnables.length) { runnablesToRun = focusedRunnables; } else { runnablesToRun = [topSuite.id]; } } var order = new j$.Order({ random: config.random, seed: config.seed }); var processor = new j$.TreeProcessor({ tree: topSuite, runnableIds: runnablesToRun, queueRunnerFactory: queueRunnerFactory, failSpecWithNoExpectations: config.failSpecWithNoExpectations, nodeStart: function(suite, next) { currentlyExecutingSuites.push(suite); defaultResourcesForRunnable(suite.id, suite.parentSuite.id); reporter.suiteStarted(suite.result, next); suite.startTimer(); }, nodeComplete: function(suite, result, next) { if (suite !== currentSuite()) { throw new Error('Tried to complete the wrong suite'); } clearResourcesForRunnable(suite.id); currentlyExecutingSuites.pop(); if (result.status === 'failed') { hasFailures = true; } suite.endTimer(); reporter.suiteDone(result, next); }, orderChildren: function(node) { return order.sort(node.children); }, excludeNode: function(spec) { return !config.specFilter(spec); } }); if (!processor.processTree().valid) { throw new Error( 'Invalid order: would cause a beforeAll or afterAll to be run multiple times' ); } var jasmineTimer = new j$.Timer(); jasmineTimer.start(); /** * Information passed to the {@link Reporter#jasmineStarted} event. * @typedef JasmineStartedInfo * @property {Int} totalSpecsDefined - The total number of specs defined in this suite. * @property {Order} order - Information about the ordering (random or not) of this execution of the suite. */ reporter.jasmineStarted( { totalSpecsDefined: totalSpecsDefined, order: order }, function() { currentlyExecutingSuites.push(topSuite); processor.execute(function() { clearResourcesForRunnable(topSuite.id); currentlyExecutingSuites.pop(); var overallStatus, incompleteReason; if (hasFailures || topSuite.result.failedExpectations.length > 0) { overallStatus = 'failed'; } else if (focusedRunnables.length > 0) { overallStatus = 'incomplete'; incompleteReason = 'fit() or fdescribe() was found'; } else if (totalSpecsDefined === 0) { overallStatus = 'incomplete'; incompleteReason = 'No specs found'; } else { overallStatus = 'passed'; } /** * Information passed to the {@link Reporter#jasmineDone} event. * @typedef JasmineDoneInfo * @property {OverallStatus} overallStatus - The overall result of the suite: 'passed', 'failed', or 'incomplete'. * @property {Int} totalTime - The total time (in ms) that it took to execute the suite * @property {IncompleteReason} incompleteReason - Explanation of why the suite was incomplete. * @property {Order} order - Information about the ordering (random or not) of this execution of the suite. * @property {Expectation[]} failedExpectations - List of expectations that failed in an {@link afterAll} at the global level. * @property {Expectation[]} deprecationWarnings - List of deprecation warnings that occurred at the global level. */ reporter.jasmineDone( { overallStatus: overallStatus, totalTime: jasmineTimer.elapsed(), incompleteReason: incompleteReason, order: order, failedExpectations: topSuite.result.failedExpectations, deprecationWarnings: topSuite.result.deprecationWarnings }, function() {} ); }); } ); }; /** * Add a custom reporter to the Jasmine environment. * @name Env#addReporter * @since 2.0.0 * @function * @param {Reporter} reporterToAdd The reporter to be added. * @see custom_reporter */ this.addReporter = function(reporterToAdd) { reporter.addReporter(reporterToAdd); }; /** * Provide a fallback reporter if no other reporters have been specified. * @name Env#provideFallbackReporter * @since 2.5.0 * @function * @param {Reporter} reporterToAdd The reporter * @see custom_reporter */ this.provideFallbackReporter = function(reporterToAdd) { reporter.provideFallbackReporter(reporterToAdd); }; /** * Clear all registered reporters * @name Env#clearReporters * @since 2.5.2 * @function */ this.clearReporters = function() { reporter.clearReporters(); }; var spyFactory = new j$.SpyFactory( function getCustomStrategies() { var runnable = currentRunnable(); if (runnable) { return runnableResources[runnable.id].customSpyStrategies; } return {}; }, function getDefaultStrategyFn() { var runnable = currentRunnable(); if (runnable) { return runnableResources[runnable.id].defaultStrategyFn; } return undefined; }, function getPromise() { return customPromise || global.Promise; } ); var spyRegistry = new j$.SpyRegistry({ currentSpies: function() { if (!currentRunnable()) { throw new Error( 'Spies must be created in a before function or a spec' ); } return runnableResources[currentRunnable().id].spies; }, createSpy: function(name, originalFn) { return self.createSpy(name, originalFn); } }); this.allowRespy = function(allow) { spyRegistry.allowRespy(allow); }; this.spyOn = function() { return spyRegistry.spyOn.apply(spyRegistry, arguments); }; this.spyOnProperty = function() { return spyRegistry.spyOnProperty.apply(spyRegistry, arguments); }; this.spyOnAllFunctions = function() { return spyRegistry.spyOnAllFunctions.apply(spyRegistry, arguments); }; this.createSpy = function(name, originalFn) { if (arguments.length === 1 && j$.isFunction_(name)) { originalFn = name; name = originalFn.name; } return spyFactory.createSpy(name, originalFn); }; this.createSpyObj = function(baseName, methodNames, propertyNames) { return spyFactory.createSpyObj(baseName, methodNames, propertyNames); }; var ensureIsFunction = function(fn, caller) { if (!j$.isFunction_(fn)) { throw new Error( caller + ' expects a function argument; received ' + j$.getType_(fn) ); } }; var ensureIsFunctionOrAsync = function(fn, caller) { if (!j$.isFunction_(fn) && !j$.isAsyncFunction_(fn)) { throw new Error( caller + ' expects a function argument; received ' + j$.getType_(fn) ); } }; function ensureIsNotNested(method) { var runnable = currentRunnable(); if (runnable !== null && runnable !== undefined) { throw new Error( "'" + method + "' should only be used in 'describe' function" ); } } var suiteFactory = function(description) { var suite = new j$.Suite({ env: self, id: getNextSuiteId(), description: description, parentSuite: currentDeclarationSuite, timer: new j$.Timer(), expectationFactory: expectationFactory, asyncExpectationFactory: suiteAsyncExpectationFactory, expectationResultFactory: expectationResultFactory, throwOnExpectationFailure: config.oneFailurePerSpec }); return suite; }; this.describe = function(description, specDefinitions) { ensureIsNotNested('describe'); ensureIsFunction(specDefinitions, 'describe'); var suite = suiteFactory(description); if (specDefinitions.length > 0) { throw new Error('describe does not expect any arguments'); } if (currentDeclarationSuite.markedPending) { suite.pend(); } addSpecsToSuite(suite, specDefinitions); return suite; }; this.xdescribe = function(description, specDefinitions) { ensureIsNotNested('xdescribe'); ensureIsFunction(specDefinitions, 'xdescribe'); var suite = suiteFactory(description); suite.pend(); addSpecsToSuite(suite, specDefinitions); return suite; }; var focusedRunnables = []; this.fdescribe = function(description, specDefinitions) { ensureIsNotNested('fdescribe'); ensureIsFunction(specDefinitions, 'fdescribe'); var suite = suiteFactory(description); suite.isFocused = true; focusedRunnables.push(suite.id); unfocusAncestor(); addSpecsToSuite(suite, specDefinitions); return suite; }; function addSpecsToSuite(suite, specDefinitions) { var parentSuite = currentDeclarationSuite; parentSuite.addChild(suite); currentDeclarationSuite = suite; var declarationError = null; try { specDefinitions.call(suite); } catch (e) { declarationError = e; } if (declarationError) { suite.onException(declarationError); } currentDeclarationSuite = parentSuite; } function findFocusedAncestor(suite) { while (suite) { if (suite.isFocused) { return suite.id; } suite = suite.parentSuite; } return null; } function unfocusAncestor() { var focusedAncestor = findFocusedAncestor(currentDeclarationSuite); if (focusedAncestor) { for (var i = 0; i < focusedRunnables.length; i++) { if (focusedRunnables[i] === focusedAncestor) { focusedRunnables.splice(i, 1); break; } } } } var specFactory = function(description, fn, suite, timeout) { totalSpecsDefined++; var spec = new j$.Spec({ id: getNextSpecId(), beforeAndAfterFns: beforeAndAfterFns(suite), expectationFactory: expectationFactory, asyncExpectationFactory: specAsyncExpectationFactory, resultCallback: specResultCallback, getSpecName: function(spec) { return getSpecName(spec, suite); }, onStart: specStarted, description: description, expectationResultFactory: expectationResultFactory, queueRunnerFactory: queueRunnerFactory, userContext: function() { return suite.clonedSharedUserContext(); }, queueableFn: { fn: fn, timeout: timeout || 0 }, throwOnExpectationFailure: config.oneFailurePerSpec, timer: new j$.Timer() }); return spec; function specResultCallback(result, next) { clearResourcesForRunnable(spec.id); currentSpec = null; if (result.status === 'failed') { hasFailures = true; } reporter.specDone(result, next); } function specStarted(spec, next) { currentSpec = spec; defaultResourcesForRunnable(spec.id, suite.id); reporter.specStarted(spec.result, next); } }; this.it = function(description, fn, timeout) { ensureIsNotNested('it'); // it() sometimes doesn't have a fn argument, so only check the type if // it's given. if (arguments.length > 1 && typeof fn !== 'undefined') { ensureIsFunctionOrAsync(fn, 'it'); } var spec = specFactory(description, fn, currentDeclarationSuite, timeout); if (currentDeclarationSuite.markedPending) { spec.pend(); } currentDeclarationSuite.addChild(spec); return spec; }; this.xit = function(description, fn, timeout) { ensureIsNotNested('xit'); // xit(), like it(), doesn't always have a fn argument, so only check the // type when needed. if (arguments.length > 1 && typeof fn !== 'undefined') { ensureIsFunctionOrAsync(fn, 'xit'); } var spec = this.it.apply(this, arguments); spec.pend('Temporarily disabled with xit'); return spec; }; this.fit = function(description, fn, timeout) { ensureIsNotNested('fit'); ensureIsFunctionOrAsync(fn, 'fit'); var spec = specFactory(description, fn, currentDeclarationSuite, timeout); currentDeclarationSuite.addChild(spec); focusedRunnables.push(spec.id); unfocusAncestor(); return spec; }; /** * Sets a user-defined property that will be provided to reporters as part of the properties field of {@link SpecResult} * @name Env#setSpecProperty * @since 3.6.0 * @function * @param {String} key The name of the property * @param {*} value The value of the property */ this.setSpecProperty = function(key, value) { if (!currentRunnable() || currentRunnable() == currentSuite()) { throw new Error( "'setSpecProperty' was used when there was no current spec" ); } currentRunnable().setSpecProperty(key, value); }; /** * Sets a user-defined property that will be provided to reporters as part of the properties field of {@link SuiteResult} * @name Env#setSuiteProperty * @since 3.6.0 * @function * @param {String} key The name of the property * @param {*} value The value of the property */ this.setSuiteProperty = function(key, value) { if (!currentSuite()) { throw new Error( "'setSuiteProperty' was used when there was no current suite" ); } currentSuite().setSuiteProperty(key, value); }; this.expect = function(actual) { if (!currentRunnable()) { throw new Error( "'expect' was used when there was no current spec, this could be because an asynchronous test timed out" ); } return currentRunnable().expect(actual); }; this.expectAsync = function(actual) { if (!currentRunnable()) { throw new Error( "'expectAsync' was used when there was no current spec, this could be because an asynchronous test timed out" ); } return currentRunnable().expectAsync(actual); }; this.beforeEach = function(beforeEachFunction, timeout) { ensureIsNotNested('beforeEach'); ensureIsFunctionOrAsync(beforeEachFunction, 'beforeEach'); currentDeclarationSuite.beforeEach({ fn: beforeEachFunction, timeout: timeout || 0 }); }; this.beforeAll = function(beforeAllFunction, timeout) { ensureIsNotNested('beforeAll'); ensureIsFunctionOrAsync(beforeAllFunction, 'beforeAll'); currentDeclarationSuite.beforeAll({ fn: beforeAllFunction, timeout: timeout || 0 }); }; this.afterEach = function(afterEachFunction, timeout) { ensureIsNotNested('afterEach'); ensureIsFunctionOrAsync(afterEachFunction, 'afterEach'); afterEachFunction.isCleanup = true; currentDeclarationSuite.afterEach({ fn: afterEachFunction, timeout: timeout || 0 }); }; this.afterAll = function(afterAllFunction, timeout) { ensureIsNotNested('afterAll'); ensureIsFunctionOrAsync(afterAllFunction, 'afterAll'); currentDeclarationSuite.afterAll({ fn: afterAllFunction, timeout: timeout || 0 }); }; this.pending = function(message) { var fullMessage = j$.Spec.pendingSpecExceptionMessage; if (message) { fullMessage += message; } throw fullMessage; }; this.fail = function(error) { if (!currentRunnable()) { throw new Error( "'fail' was used when there was no current spec, this could be because an asynchronous test timed out" ); } var message = 'Failed'; if (error) { message += ': '; if (error.message) { message += error.message; } else if (j$.isString_(error)) { message += error; } else { // pretty print all kind of objects. This includes arrays. message += makePrettyPrinter()(error); } } currentRunnable().addExpectationResult(false, { matcherName: '', passed: false, expected: '', actual: '', message: message, error: error && error.message ? error : null }); if (config.oneFailurePerSpec) { throw new Error(message); } }; this.cleanup_ = function() { if (globalErrors) { globalErrors.uninstall(); } }; } return Env; }; getJasmineRequireObj().JsApiReporter = function(j$) { /** * @name jsApiReporter * @classdesc {@link Reporter} added by default in `boot.js` to record results for retrieval in javascript code. An instance is made available as `jsApiReporter` on the global object. * @class * @hideconstructor */ function JsApiReporter(options) { var timer = options.timer || new j$.Timer(), status = 'loaded'; this.started = false; this.finished = false; this.runDetails = {}; this.jasmineStarted = function() { this.started = true; status = 'started'; timer.start(); }; var executionTime; this.jasmineDone = function(runDetails) { this.finished = true; this.runDetails = runDetails; executionTime = timer.elapsed(); status = 'done'; }; /** * Get the current status for the Jasmine environment. * @name jsApiReporter#status * @since 2.0.0 * @function * @return {String} - One of `loaded`, `started`, or `done` */ this.status = function() { return status; }; var suites = [], suites_hash = {}; this.suiteStarted = function(result) { suites_hash[result.id] = result; }; this.suiteDone = function(result) { storeSuite(result); }; /** * Get the results for a set of suites. * * Retrievable in slices for easier serialization. * @name jsApiReporter#suiteResults * @since 2.1.0 * @function * @param {Number} index - The position in the suites list to start from. * @param {Number} length - Maximum number of suite results to return. * @return {SuiteResult[]} */ this.suiteResults = function(index, length) { return suites.slice(index, index + length); }; function storeSuite(result) { suites.push(result); suites_hash[result.id] = result; } /** * Get all of the suites in a single object, with their `id` as the key. * @name jsApiReporter#suites * @since 2.0.0 * @function * @return {Object} - Map of suite id to {@link SuiteResult} */ this.suites = function() { return suites_hash; }; var specs = []; this.specDone = function(result) { specs.push(result); }; /** * Get the results for a set of specs. * * Retrievable in slices for easier serialization. * @name jsApiReporter#specResults * @since 2.0.0 * @function * @param {Number} index - The position in the specs list to start from. * @param {Number} length - Maximum number of specs results to return. * @return {SpecResult[]} */ this.specResults = function(index, length) { return specs.slice(index, index + length); }; /** * Get all spec results. * @name jsApiReporter#specs * @since 2.0.0 * @function * @return {SpecResult[]} */ this.specs = function() { return specs; }; /** * Get the number of milliseconds it took for the full Jasmine suite to run. * @name jsApiReporter#executionTime * @since 2.0.0 * @function * @return {Number} */ this.executionTime = function() { return executionTime; }; } return JsApiReporter; }; getJasmineRequireObj().Any = function(j$) { function Any(expectedObject) { if (typeof expectedObject === 'undefined') { throw new TypeError( 'jasmine.any() expects to be passed a constructor function. ' + 'Please pass one or use jasmine.anything() to match any object.' ); } this.expectedObject = expectedObject; } Any.prototype.asymmetricMatch = function(other) { if (this.expectedObject == String) { return typeof other == 'string' || other instanceof String; } if (this.expectedObject == Number) { return typeof other == 'number' || other instanceof Number; } if (this.expectedObject == Function) { return typeof other == 'function' || other instanceof Function; } if (this.expectedObject == Object) { return other !== null && typeof other == 'object'; } if (this.expectedObject == Boolean) { return typeof other == 'boolean'; } /* jshint -W122 */ /* global Symbol */ if (typeof Symbol != 'undefined' && this.expectedObject == Symbol) { return typeof other == 'symbol'; } /* jshint +W122 */ return other instanceof this.expectedObject; }; Any.prototype.jasmineToString = function() { return ''; }; return Any; }; getJasmineRequireObj().Anything = function(j$) { function Anything() {} Anything.prototype.asymmetricMatch = function(other) { return !j$.util.isUndefined(other) && other !== null; }; Anything.prototype.jasmineToString = function() { return ''; }; return Anything; }; getJasmineRequireObj().ArrayContaining = function(j$) { function ArrayContaining(sample) { this.sample = sample; } ArrayContaining.prototype.asymmetricMatch = function(other, matchersUtil) { if (!j$.isArray_(this.sample)) { throw new Error('You must provide an array to arrayContaining, not ' + j$.pp(this.sample) + '.'); } // If the actual parameter is not an array, we can fail immediately, since it couldn't // possibly be an "array containing" anything. However, we also want an empty sample // array to match anything, so we need to double-check we aren't in that case if (!j$.isArray_(other) && this.sample.length > 0) { return false; } for (var i = 0; i < this.sample.length; i++) { var item = this.sample[i]; if (!matchersUtil.contains(other, item)) { return false; } } return true; }; ArrayContaining.prototype.jasmineToString = function (pp) { return ''; }; return ArrayContaining; }; getJasmineRequireObj().ArrayWithExactContents = function(j$) { function ArrayWithExactContents(sample) { this.sample = sample; } ArrayWithExactContents.prototype.asymmetricMatch = function(other, matchersUtil) { if (!j$.isArray_(this.sample)) { throw new Error('You must provide an array to arrayWithExactContents, not ' + j$.pp(this.sample) + '.'); } if (this.sample.length !== other.length) { return false; } for (var i = 0; i < this.sample.length; i++) { var item = this.sample[i]; if (!matchersUtil.contains(other, item)) { return false; } } return true; }; ArrayWithExactContents.prototype.jasmineToString = function(pp) { return ''; }; return ArrayWithExactContents; }; getJasmineRequireObj().Empty = function (j$) { function Empty() {} Empty.prototype.asymmetricMatch = function (other) { if (j$.isString_(other) || j$.isArray_(other) || j$.isTypedArray_(other)) { return other.length === 0; } if (j$.isMap(other) || j$.isSet(other)) { return other.size === 0; } if (j$.isObject_(other)) { return Object.keys(other).length === 0; } return false; }; Empty.prototype.jasmineToString = function () { return ''; }; return Empty; }; getJasmineRequireObj().Falsy = function(j$) { function Falsy() {} Falsy.prototype.asymmetricMatch = function(other) { return !other; }; Falsy.prototype.jasmineToString = function() { return ''; }; return Falsy; }; getJasmineRequireObj().MapContaining = function(j$) { function MapContaining(sample) { if (!j$.isMap(sample)) { throw new Error('You must provide a map to `mapContaining`, not ' + j$.pp(sample)); } this.sample = sample; } MapContaining.prototype.asymmetricMatch = function(other, matchersUtil) { if (!j$.isMap(other)) return false; var hasAllMatches = true; j$.util.forEachBreakable(this.sample, function(breakLoop, value, key) { // for each key/value pair in `sample` // there should be at least one pair in `other` whose key and value both match var hasMatch = false; j$.util.forEachBreakable(other, function(oBreakLoop, oValue, oKey) { if ( matchersUtil.equals(oKey, key) && matchersUtil.equals(oValue, value) ) { hasMatch = true; oBreakLoop(); } }); if (!hasMatch) { hasAllMatches = false; breakLoop(); } }); return hasAllMatches; }; MapContaining.prototype.jasmineToString = function(pp) { return ''; }; return MapContaining; }; getJasmineRequireObj().NotEmpty = function (j$) { function NotEmpty() {} NotEmpty.prototype.asymmetricMatch = function (other) { if (j$.isString_(other) || j$.isArray_(other) || j$.isTypedArray_(other)) { return other.length !== 0; } if (j$.isMap(other) || j$.isSet(other)) { return other.size !== 0; } if (j$.isObject_(other)) { return Object.keys(other).length !== 0; } return false; }; NotEmpty.prototype.jasmineToString = function () { return ''; }; return NotEmpty; }; getJasmineRequireObj().ObjectContaining = function(j$) { function ObjectContaining(sample) { this.sample = sample; } function getPrototype(obj) { if (Object.getPrototypeOf) { return Object.getPrototypeOf(obj); } if (obj.constructor.prototype == obj) { return null; } return obj.constructor.prototype; } function hasProperty(obj, property) { if (!obj || typeof(obj) !== 'object') { return false; } if (Object.prototype.hasOwnProperty.call(obj, property)) { return true; } return hasProperty(getPrototype(obj), property); } ObjectContaining.prototype.asymmetricMatch = function(other, matchersUtil) { if (typeof(this.sample) !== 'object') { throw new Error('You must provide an object to objectContaining, not \''+this.sample+'\'.'); } if (typeof(other) !== 'object') { return false; } for (var property in this.sample) { if (!hasProperty(other, property) || !matchersUtil.equals(this.sample[property], other[property])) { return false; } } return true; }; ObjectContaining.prototype.valuesForDiff_ = function(other, pp) { if (!j$.isObject_(other)) { return { self: this.jasmineToString(pp), other: other }; } var filteredOther = {}; Object.keys(this.sample).forEach(function (k) { // eq short-circuits comparison of objects that have different key sets, // so include all keys even if undefined. filteredOther[k] = other[k]; }); return { self: this.sample, other: filteredOther }; }; ObjectContaining.prototype.jasmineToString = function(pp) { return ''; }; return ObjectContaining; }; getJasmineRequireObj().SetContaining = function(j$) { function SetContaining(sample) { if (!j$.isSet(sample)) { throw new Error('You must provide a set to `setContaining`, not ' + j$.pp(sample)); } this.sample = sample; } SetContaining.prototype.asymmetricMatch = function(other, matchersUtil) { if (!j$.isSet(other)) return false; var hasAllMatches = true; j$.util.forEachBreakable(this.sample, function(breakLoop, item) { // for each item in `sample` there should be at least one matching item in `other` // (not using `matchersUtil.contains` because it compares set members by reference, // not by deep value equality) var hasMatch = false; j$.util.forEachBreakable(other, function(oBreakLoop, oItem) { if (matchersUtil.equals(oItem, item)) { hasMatch = true; oBreakLoop(); } }); if (!hasMatch) { hasAllMatches = false; breakLoop(); } }); return hasAllMatches; }; SetContaining.prototype.jasmineToString = function(pp) { return ''; }; return SetContaining; }; getJasmineRequireObj().StringMatching = function(j$) { function StringMatching(expected) { if (!j$.isString_(expected) && !j$.isA_('RegExp', expected)) { throw new Error('Expected is not a String or a RegExp'); } this.regexp = new RegExp(expected); } StringMatching.prototype.asymmetricMatch = function(other) { return this.regexp.test(other); }; StringMatching.prototype.jasmineToString = function() { return ''; }; return StringMatching; }; getJasmineRequireObj().Truthy = function(j$) { function Truthy() {} Truthy.prototype.asymmetricMatch = function(other) { return !!other; }; Truthy.prototype.jasmineToString = function() { return ''; }; return Truthy; }; getJasmineRequireObj().asymmetricEqualityTesterArgCompatShim = function(j$) { /* Older versions of Jasmine passed an array of custom equality testers as the second argument to each asymmetric equality tester's `asymmetricMatch` method. Newer versions will pass a `MatchersUtil` instance. The asymmetricEqualityTesterArgCompatShim allows for a graceful migration from the old interface to the new by "being" both an array of custom equality testers and a `MatchersUtil` at the same time. This code should be removed in the next major release. */ var likelyArrayProps = [ 'concat', 'constructor', 'copyWithin', 'entries', 'every', 'fill', 'filter', 'find', 'findIndex', 'flat', 'flatMap', 'forEach', 'includes', 'indexOf', 'join', 'keys', 'lastIndexOf', 'length', 'map', 'pop', 'push', 'reduce', 'reduceRight', 'reverse', 'shift', 'slice', 'some', 'sort', 'splice', 'toLocaleString', 'toSource', 'toString', 'unshift', 'values' ]; function asymmetricEqualityTesterArgCompatShim( matchersUtil, customEqualityTesters ) { var self = Object.create(matchersUtil), props, i, k; copy(self, customEqualityTesters, 'length'); for (i = 0; i < customEqualityTesters.length; i++) { copy(self, customEqualityTesters, i); } var props = arrayProps(); for (i = 0; i < props.length; i++) { k = props[i]; if (k !== 'length') { copy(self, Array.prototype, k); } } return self; } function copy(dest, src, propName) { Object.defineProperty(dest, propName, { get: function() { return src[propName]; } }); } function arrayProps() { var props, a, k; if (!Object.getOwnPropertyDescriptors) { return likelyArrayProps.filter(function(k) { return Array.prototype.hasOwnProperty(k); }); } props = Object.getOwnPropertyDescriptors(Array.prototype); // eslint-disable-line compat/compat a = []; for (k in props) { a.push(k); } return a; } return asymmetricEqualityTesterArgCompatShim; }; getJasmineRequireObj().CallTracker = function(j$) { /** * @namespace Spy#calls * @since 2.0.0 */ function CallTracker() { var calls = []; var opts = {}; this.track = function(context) { if (opts.cloneArgs) { context.args = j$.util.cloneArgs(context.args); } calls.push(context); }; /** * Check whether this spy has been invoked. * @name Spy#calls#any * @since 2.0.0 * @function * @return {Boolean} */ this.any = function() { return !!calls.length; }; /** * Get the number of invocations of this spy. * @name Spy#calls#count * @since 2.0.0 * @function * @return {Integer} */ this.count = function() { return calls.length; }; /** * Get the arguments that were passed to a specific invocation of this spy. * @name Spy#calls#argsFor * @since 2.0.0 * @function * @param {Integer} index The 0-based invocation index. * @return {Array} */ this.argsFor = function(index) { var call = calls[index]; return call ? call.args : []; }; /** * Get the raw calls array for this spy. * @name Spy#calls#all * @since 2.0.0 * @function * @return {Spy.callData[]} */ this.all = function() { return calls; }; /** * Get all of the arguments for each invocation of this spy in the order they were received. * @name Spy#calls#allArgs * @since 2.0.0 * @function * @return {Array} */ this.allArgs = function() { var callArgs = []; for (var i = 0; i < calls.length; i++) { callArgs.push(calls[i].args); } return callArgs; }; /** * Get the first invocation of this spy. * @name Spy#calls#first * @since 2.0.0 * @function * @return {ObjecSpy.callData} */ this.first = function() { return calls[0]; }; /** * Get the most recent invocation of this spy. * @name Spy#calls#mostRecent * @since 2.0.0 * @function * @return {ObjecSpy.callData} */ this.mostRecent = function() { return calls[calls.length - 1]; }; /** * Reset this spy as if it has never been called. * @name Spy#calls#reset * @since 2.0.0 * @function */ this.reset = function() { calls = []; }; /** * Set this spy to do a shallow clone of arguments passed to each invocation. * @name Spy#calls#saveArgumentsByValue * @since 2.5.0 * @function */ this.saveArgumentsByValue = function() { opts.cloneArgs = true; }; } return CallTracker; }; getJasmineRequireObj().clearStack = function(j$) { var maxInlineCallCount = 10; function messageChannelImpl(global, setTimeout) { var channel = new global.MessageChannel(), head = {}, tail = head; var taskRunning = false; channel.port1.onmessage = function() { head = head.next; var task = head.task; delete head.task; if (taskRunning) { global.setTimeout(task, 0); } else { try { taskRunning = true; task(); } finally { taskRunning = false; } } }; var currentCallCount = 0; return function clearStack(fn) { currentCallCount++; if (currentCallCount < maxInlineCallCount) { tail = tail.next = { task: fn }; channel.port2.postMessage(0); } else { currentCallCount = 0; setTimeout(fn); } }; } function getClearStack(global) { var currentCallCount = 0; var realSetTimeout = global.setTimeout; var setTimeoutImpl = function clearStack(fn) { Function.prototype.apply.apply(realSetTimeout, [global, [fn, 0]]); }; if (j$.isFunction_(global.setImmediate)) { var realSetImmediate = global.setImmediate; return function(fn) { currentCallCount++; if (currentCallCount < maxInlineCallCount) { realSetImmediate(fn); } else { currentCallCount = 0; setTimeoutImpl(fn); } }; } else if (!j$.util.isUndefined(global.MessageChannel)) { return messageChannelImpl(global, setTimeoutImpl); } else { return setTimeoutImpl; } } return getClearStack; }; getJasmineRequireObj().Clock = function() { /* global process */ var NODE_JS = typeof process !== 'undefined' && process.versions && typeof process.versions.node === 'string'; /** * _Note:_ Do not construct this directly, Jasmine will make one during booting. You can get the current clock with {@link jasmine.clock}. * @class Clock * @classdesc Jasmine's mock clock is used when testing time dependent code. */ function Clock(global, delayedFunctionSchedulerFactory, mockDate) { var self = this, realTimingFunctions = { setTimeout: global.setTimeout, clearTimeout: global.clearTimeout, setInterval: global.setInterval, clearInterval: global.clearInterval }, fakeTimingFunctions = { setTimeout: setTimeout, clearTimeout: clearTimeout, setInterval: setInterval, clearInterval: clearInterval }, installed = false, delayedFunctionScheduler, timer; self.FakeTimeout = FakeTimeout; /** * Install the mock clock over the built-in methods. * @name Clock#install * @since 2.0.0 * @function * @return {Clock} */ self.install = function() { if (!originalTimingFunctionsIntact()) { throw new Error( 'Jasmine Clock was unable to install over custom global timer functions. Is the clock already installed?' ); } replace(global, fakeTimingFunctions); timer = fakeTimingFunctions; delayedFunctionScheduler = delayedFunctionSchedulerFactory(); installed = true; return self; }; /** * Uninstall the mock clock, returning the built-in methods to their places. * @name Clock#uninstall * @since 2.0.0 * @function */ self.uninstall = function() { delayedFunctionScheduler = null; mockDate.uninstall(); replace(global, realTimingFunctions); timer = realTimingFunctions; installed = false; }; /** * Execute a function with a mocked Clock * * The clock will be {@link Clock#install|install}ed before the function is called and {@link Clock#uninstall|uninstall}ed in a `finally` after the function completes. * @name Clock#withMock * @since 2.3.0 * @function * @param {Function} closure The function to be called. */ self.withMock = function(closure) { this.install(); try { closure(); } finally { this.uninstall(); } }; /** * Instruct the installed Clock to also mock the date returned by `new Date()` * @name Clock#mockDate * @since 2.1.0 * @function * @param {Date} [initialDate=now] The `Date` to provide. */ self.mockDate = function(initialDate) { mockDate.install(initialDate); }; self.setTimeout = function(fn, delay, params) { return Function.prototype.apply.apply(timer.setTimeout, [ global, arguments ]); }; self.setInterval = function(fn, delay, params) { return Function.prototype.apply.apply(timer.setInterval, [ global, arguments ]); }; self.clearTimeout = function(id) { return Function.prototype.call.apply(timer.clearTimeout, [global, id]); }; self.clearInterval = function(id) { return Function.prototype.call.apply(timer.clearInterval, [global, id]); }; /** * Tick the Clock forward, running any enqueued timeouts along the way * @name Clock#tick * @since 1.3.0 * @function * @param {int} millis The number of milliseconds to tick. */ self.tick = function(millis) { if (installed) { delayedFunctionScheduler.tick(millis, function(millis) { mockDate.tick(millis); }); } else { throw new Error( 'Mock clock is not installed, use jasmine.clock().install()' ); } }; return self; function originalTimingFunctionsIntact() { return ( global.setTimeout === realTimingFunctions.setTimeout && global.clearTimeout === realTimingFunctions.clearTimeout && global.setInterval === realTimingFunctions.setInterval && global.clearInterval === realTimingFunctions.clearInterval ); } function replace(dest, source) { for (var prop in source) { dest[prop] = source[prop]; } } function setTimeout(fn, delay) { if (!NODE_JS) { return delayedFunctionScheduler.scheduleFunction( fn, delay, argSlice(arguments, 2) ); } var timeout = new FakeTimeout(); delayedFunctionScheduler.scheduleFunction( fn, delay, argSlice(arguments, 2), false, timeout ); return timeout; } function clearTimeout(id) { return delayedFunctionScheduler.removeFunctionWithId(id); } function setInterval(fn, interval) { if (!NODE_JS) { return delayedFunctionScheduler.scheduleFunction( fn, interval, argSlice(arguments, 2), true ); } var timeout = new FakeTimeout(); delayedFunctionScheduler.scheduleFunction( fn, interval, argSlice(arguments, 2), true, timeout ); return timeout; } function clearInterval(id) { return delayedFunctionScheduler.removeFunctionWithId(id); } function argSlice(argsObj, n) { return Array.prototype.slice.call(argsObj, n); } } /** * Mocks Node.js Timeout class */ function FakeTimeout() {} FakeTimeout.prototype.ref = function() { return this; }; FakeTimeout.prototype.unref = function() { return this; }; return Clock; }; getJasmineRequireObj().DelayedFunctionScheduler = function(j$) { function DelayedFunctionScheduler() { var self = this; var scheduledLookup = []; var scheduledFunctions = {}; var currentTime = 0; var delayedFnCount = 0; var deletedKeys = []; self.tick = function(millis, tickDate) { millis = millis || 0; var endTime = currentTime + millis; runScheduledFunctions(endTime, tickDate); currentTime = endTime; }; self.scheduleFunction = function( funcToCall, millis, params, recurring, timeoutKey, runAtMillis ) { var f; if (typeof funcToCall === 'string') { /* jshint evil: true */ f = function() { return eval(funcToCall); }; /* jshint evil: false */ } else { f = funcToCall; } millis = millis || 0; timeoutKey = timeoutKey || ++delayedFnCount; runAtMillis = runAtMillis || currentTime + millis; var funcToSchedule = { runAtMillis: runAtMillis, funcToCall: f, recurring: recurring, params: params, timeoutKey: timeoutKey, millis: millis }; if (runAtMillis in scheduledFunctions) { scheduledFunctions[runAtMillis].push(funcToSchedule); } else { scheduledFunctions[runAtMillis] = [funcToSchedule]; scheduledLookup.push(runAtMillis); scheduledLookup.sort(function(a, b) { return a - b; }); } return timeoutKey; }; self.removeFunctionWithId = function(timeoutKey) { deletedKeys.push(timeoutKey); for (var runAtMillis in scheduledFunctions) { var funcs = scheduledFunctions[runAtMillis]; var i = indexOfFirstToPass(funcs, function(func) { return func.timeoutKey === timeoutKey; }); if (i > -1) { if (funcs.length === 1) { delete scheduledFunctions[runAtMillis]; deleteFromLookup(runAtMillis); } else { funcs.splice(i, 1); } // intervals get rescheduled when executed, so there's never more // than a single scheduled function with a given timeoutKey break; } } }; return self; function indexOfFirstToPass(array, testFn) { var index = -1; for (var i = 0; i < array.length; ++i) { if (testFn(array[i])) { index = i; break; } } return index; } function deleteFromLookup(key) { var value = Number(key); var i = indexOfFirstToPass(scheduledLookup, function(millis) { return millis === value; }); if (i > -1) { scheduledLookup.splice(i, 1); } } function reschedule(scheduledFn) { self.scheduleFunction( scheduledFn.funcToCall, scheduledFn.millis, scheduledFn.params, true, scheduledFn.timeoutKey, scheduledFn.runAtMillis + scheduledFn.millis ); } function forEachFunction(funcsToRun, callback) { for (var i = 0; i < funcsToRun.length; ++i) { callback(funcsToRun[i]); } } function runScheduledFunctions(endTime, tickDate) { tickDate = tickDate || function() {}; if (scheduledLookup.length === 0 || scheduledLookup[0] > endTime) { tickDate(endTime - currentTime); return; } do { deletedKeys = []; var newCurrentTime = scheduledLookup.shift(); tickDate(newCurrentTime - currentTime); currentTime = newCurrentTime; var funcsToRun = scheduledFunctions[currentTime]; delete scheduledFunctions[currentTime]; forEachFunction(funcsToRun, function(funcToRun) { if (funcToRun.recurring) { reschedule(funcToRun); } }); forEachFunction(funcsToRun, function(funcToRun) { if (j$.util.arrayContains(deletedKeys, funcToRun.timeoutKey)) { // skip a timeoutKey deleted whilst we were running return; } funcToRun.funcToCall.apply(null, funcToRun.params || []); }); deletedKeys = []; } while ( scheduledLookup.length > 0 && // checking first if we're out of time prevents setTimeout(0) // scheduled in a funcToRun from forcing an extra iteration currentTime !== endTime && scheduledLookup[0] <= endTime ); // ran out of functions to call, but still time left on the clock if (currentTime !== endTime) { tickDate(endTime - currentTime); } } } return DelayedFunctionScheduler; }; getJasmineRequireObj().errors = function() { function ExpectationFailed() {} ExpectationFailed.prototype = new Error(); ExpectationFailed.prototype.constructor = ExpectationFailed; return { ExpectationFailed: ExpectationFailed }; }; getJasmineRequireObj().ExceptionFormatter = function(j$) { var ignoredProperties = [ 'name', 'message', 'stack', 'fileName', 'sourceURL', 'line', 'lineNumber', 'column', 'description', 'jasmineMessage' ]; function ExceptionFormatter(options) { var jasmineFile = (options && options.jasmineFile) || j$.util.jasmineFile(); this.message = function(error) { var message = ''; if (error.jasmineMessage) { message += error.jasmineMessage; } else if (error.name && error.message) { message += error.name + ': ' + error.message; } else if (error.message) { message += error.message; } else { message += error.toString() + ' thrown'; } if (error.fileName || error.sourceURL) { message += ' in ' + (error.fileName || error.sourceURL); } if (error.line || error.lineNumber) { message += ' (line ' + (error.line || error.lineNumber) + ')'; } return message; }; this.stack = function(error) { if (!error || !error.stack) { return null; } var stackTrace = new j$.StackTrace(error); var lines = filterJasmine(stackTrace); var result = ''; if (stackTrace.message) { lines.unshift(stackTrace.message); } result += formatProperties(error); result += lines.join('\n'); return result; }; function filterJasmine(stackTrace) { var result = [], jasmineMarker = stackTrace.style === 'webkit' ? '' : ' at '; stackTrace.frames.forEach(function(frame) { if (frame.file && frame.file !== jasmineFile) { result.push(frame.raw); } else if (result[result.length - 1] !== jasmineMarker) { result.push(jasmineMarker); } }); return result; } function formatProperties(error) { if (!(error instanceof Object)) { return; } var result = {}; var empty = true; for (var prop in error) { if (j$.util.arrayContains(ignoredProperties, prop)) { continue; } result[prop] = error[prop]; empty = false; } if (!empty) { return 'error properties: ' + j$.pp(result) + '\n'; } return ''; } } return ExceptionFormatter; }; getJasmineRequireObj().Expectation = function(j$) { /** * Matchers that come with Jasmine out of the box. * @namespace matchers */ function Expectation(options) { this.expector = new j$.Expector(options); var customMatchers = options.customMatchers || {}; for (var matcherName in customMatchers) { this[matcherName] = wrapSyncCompare( matcherName, customMatchers[matcherName] ); } } /** * Add some context for an {@link expect} * @function * @name matchers#withContext * @since 3.3.0 * @param {String} message - Additional context to show when the matcher fails * @return {matchers} */ Expectation.prototype.withContext = function withContext(message) { return addFilter(this, new ContextAddingFilter(message)); }; /** * Invert the matcher following this {@link expect} * @member * @name matchers#not * @since 1.3.0 * @type {matchers} * @example * expect(something).not.toBe(true); */ Object.defineProperty(Expectation.prototype, 'not', { get: function() { return addFilter(this, syncNegatingFilter); } }); /** * Asynchronous matchers. * @namespace async-matchers */ function AsyncExpectation(options) { var global = options.global || j$.getGlobal(); this.expector = new j$.Expector(options); if (!global.Promise) { throw new Error( 'expectAsync is unavailable because the environment does not support promises.' ); } var customAsyncMatchers = options.customAsyncMatchers || {}; for (var matcherName in customAsyncMatchers) { this[matcherName] = wrapAsyncCompare( matcherName, customAsyncMatchers[matcherName] ); } } /** * Add some context for an {@link expectAsync} * @function * @name async-matchers#withContext * @since 3.3.0 * @param {String} message - Additional context to show when the async matcher fails * @return {async-matchers} */ AsyncExpectation.prototype.withContext = function withContext(message) { return addFilter(this, new ContextAddingFilter(message)); }; /** * Invert the matcher following this {@link expectAsync} * @member * @name async-matchers#not * @type {async-matchers} * @example * await expectAsync(myPromise).not.toBeResolved(); * @example * return expectAsync(myPromise).not.toBeResolved(); */ Object.defineProperty(AsyncExpectation.prototype, 'not', { get: function() { return addFilter(this, asyncNegatingFilter); } }); function wrapSyncCompare(name, matcherFactory) { return function() { var result = this.expector.compare(name, matcherFactory, arguments); this.expector.processResult(result); }; } function wrapAsyncCompare(name, matcherFactory) { return function() { var self = this; // Capture the call stack here, before we go async, so that it will contain // frames that are relevant to the user instead of just parts of Jasmine. var errorForStack = j$.util.errorWithStack(); return this.expector .compare(name, matcherFactory, arguments) .then(function(result) { self.expector.processResult(result, errorForStack); }); }; } function addCoreMatchers(prototype, matchers, wrapper) { for (var matcherName in matchers) { var matcher = matchers[matcherName]; prototype[matcherName] = wrapper(matcherName, matcher); } } function addFilter(source, filter) { var result = Object.create(source); result.expector = source.expector.addFilter(filter); return result; } function negatedFailureMessage(result, matcherName, args, matchersUtil) { if (result.message) { if (j$.isFunction_(result.message)) { return result.message(); } else { return result.message; } } args = args.slice(); args.unshift(true); args.unshift(matcherName); return matchersUtil.buildFailureMessage.apply(matchersUtil, args); } function negate(result) { result.pass = !result.pass; return result; } var syncNegatingFilter = { selectComparisonFunc: function(matcher) { function defaultNegativeCompare() { return negate(matcher.compare.apply(null, arguments)); } return matcher.negativeCompare || defaultNegativeCompare; }, buildFailureMessage: negatedFailureMessage }; var asyncNegatingFilter = { selectComparisonFunc: function(matcher) { function defaultNegativeCompare() { return matcher.compare.apply(this, arguments).then(negate); } return matcher.negativeCompare || defaultNegativeCompare; }, buildFailureMessage: negatedFailureMessage }; function ContextAddingFilter(message) { this.message = message; } ContextAddingFilter.prototype.modifyFailureMessage = function(msg) { var nl = msg.indexOf('\n'); if (nl === -1) { return this.message + ': ' + msg; } else { return this.message + ':\n' + indent(msg); } }; function indent(s) { return s.replace(/^/gm, ' '); } return { factory: function(options) { return new Expectation(options || {}); }, addCoreMatchers: function(matchers) { addCoreMatchers(Expectation.prototype, matchers, wrapSyncCompare); }, asyncFactory: function(options) { return new AsyncExpectation(options || {}); }, addAsyncCoreMatchers: function(matchers) { addCoreMatchers(AsyncExpectation.prototype, matchers, wrapAsyncCompare); } }; }; getJasmineRequireObj().ExpectationFilterChain = function() { function ExpectationFilterChain(maybeFilter, prev) { this.filter_ = maybeFilter; this.prev_ = prev; } ExpectationFilterChain.prototype.addFilter = function(filter) { return new ExpectationFilterChain(filter, this); }; ExpectationFilterChain.prototype.selectComparisonFunc = function(matcher) { return this.callFirst_('selectComparisonFunc', arguments).result; }; ExpectationFilterChain.prototype.buildFailureMessage = function( result, matcherName, args, matchersUtil ) { return this.callFirst_('buildFailureMessage', arguments).result; }; ExpectationFilterChain.prototype.modifyFailureMessage = function(msg) { var result = this.callFirst_('modifyFailureMessage', arguments).result; return result || msg; }; ExpectationFilterChain.prototype.callFirst_ = function(fname, args) { var prevResult; if (this.prev_) { prevResult = this.prev_.callFirst_(fname, args); if (prevResult.found) { return prevResult; } } if (this.filter_ && this.filter_[fname]) { return { found: true, result: this.filter_[fname].apply(this.filter_, args) }; } return { found: false }; }; return ExpectationFilterChain; }; //TODO: expectation result may make more sense as a presentation of an expectation. getJasmineRequireObj().buildExpectationResult = function(j$) { function buildExpectationResult(options) { var messageFormatter = options.messageFormatter || function() {}, stackFormatter = options.stackFormatter || function() {}; /** * @typedef Expectation * @property {String} matcherName - The name of the matcher that was executed for this expectation. * @property {String} message - The failure message for the expectation. * @property {String} stack - The stack trace for the failure if available. * @property {Boolean} passed - Whether the expectation passed or failed. * @property {Object} expected - If the expectation failed, what was the expected value. * @property {Object} actual - If the expectation failed, what actual value was produced. */ var result = { matcherName: options.matcherName, message: message(), stack: stack(), passed: options.passed }; if (!result.passed) { result.expected = options.expected; result.actual = options.actual; if (options.error && !j$.isString_(options.error)) { if ('code' in options.error) { result.code = options.error.code; } if ( options.error.code === 'ERR_ASSERTION' && options.expected === '' && options.actual === '' ) { result.expected = options.error.expected; result.actual = options.error.actual; result.matcherName = 'assert ' + options.error.operator; } } } return result; function message() { if (options.passed) { return 'Passed.'; } else if (options.message) { return options.message; } else if (options.error) { return messageFormatter(options.error); } return ''; } function stack() { if (options.passed) { return ''; } var error = options.error; if (!error) { if (options.errorForStack) { error = options.errorForStack; } else if (options.stack) { error = options; } else { try { throw new Error(message()); } catch (e) { error = e; } } } return stackFormatter(error); } } return buildExpectationResult; }; getJasmineRequireObj().Expector = function(j$) { function Expector(options) { this.matchersUtil = options.matchersUtil || { buildFailureMessage: function() {} }; this.customEqualityTesters = options.customEqualityTesters || []; this.actual = options.actual; this.addExpectationResult = options.addExpectationResult || function() {}; this.filters = new j$.ExpectationFilterChain(); } Expector.prototype.instantiateMatcher = function( matcherName, matcherFactory, args ) { this.matcherName = matcherName; this.args = Array.prototype.slice.call(args, 0); this.expected = this.args.slice(0); this.args.unshift(this.actual); var matcher = matcherFactory(this.matchersUtil, this.customEqualityTesters); var comparisonFunc = this.filters.selectComparisonFunc(matcher); return comparisonFunc || matcher.compare; }; Expector.prototype.buildMessage = function(result) { var self = this; if (result.pass) { return ''; } var msg = this.filters.buildFailureMessage( result, this.matcherName, this.args, this.matchersUtil, defaultMessage ); return this.filters.modifyFailureMessage(msg || defaultMessage()); function defaultMessage() { if (!result.message) { var args = self.args.slice(); args.unshift(false); args.unshift(self.matcherName); return self.matchersUtil.buildFailureMessage.apply( self.matchersUtil, args ); } else if (j$.isFunction_(result.message)) { return result.message(); } else { return result.message; } } }; Expector.prototype.compare = function(matcherName, matcherFactory, args) { var matcherCompare = this.instantiateMatcher( matcherName, matcherFactory, args ); return matcherCompare.apply(null, this.args); }; Expector.prototype.addFilter = function(filter) { var result = Object.create(this); result.filters = this.filters.addFilter(filter); return result; }; Expector.prototype.processResult = function(result, errorForStack) { var message = this.buildMessage(result); if (this.expected.length === 1) { this.expected = this.expected[0]; } this.addExpectationResult(result.pass, { matcherName: this.matcherName, passed: result.pass, message: message, error: errorForStack ? undefined : result.error, errorForStack: errorForStack || undefined, actual: this.actual, expected: this.expected // TODO: this may need to be arrayified/sliced }); }; return Expector; }; getJasmineRequireObj().formatErrorMsg = function() { function generateErrorMsg(domain, usage) { var usageDefinition = usage ? '\nUsage: ' + usage : ''; return function errorMsg(msg) { return domain + ' : ' + msg + usageDefinition; }; } return generateErrorMsg; }; getJasmineRequireObj().GlobalErrors = function(j$) { function GlobalErrors(global) { var handlers = []; global = global || j$.getGlobal(); var onerror = function onerror() { var handler = handlers[handlers.length - 1]; if (handler) { handler.apply(null, Array.prototype.slice.call(arguments, 0)); } else { throw arguments[0]; } }; this.originalHandlers = {}; this.jasmineHandlers = {}; this.installOne_ = function installOne_(errorType, jasmineMessage) { function taggedOnError(error) { error.jasmineMessage = jasmineMessage + ': ' + error; var handler = handlers[handlers.length - 1]; if (handler) { handler(error); } else { throw error; } } this.originalHandlers[errorType] = global.process.listeners(errorType); this.jasmineHandlers[errorType] = taggedOnError; global.process.removeAllListeners(errorType); global.process.on(errorType, taggedOnError); this.uninstall = function uninstall() { var errorTypes = Object.keys(this.originalHandlers); for (var iType = 0; iType < errorTypes.length; iType++) { var errorType = errorTypes[iType]; global.process.removeListener( errorType, this.jasmineHandlers[errorType] ); for (var i = 0; i < this.originalHandlers[errorType].length; i++) { global.process.on(errorType, this.originalHandlers[errorType][i]); } delete this.originalHandlers[errorType]; delete this.jasmineHandlers[errorType]; } }; }; this.install = function install() { if ( global.process && global.process.listeners && j$.isFunction_(global.process.on) ) { this.installOne_('uncaughtException', 'Uncaught exception'); this.installOne_('unhandledRejection', 'Unhandled promise rejection'); } else { var originalHandler = global.onerror; global.onerror = onerror; var browserRejectionHandler = function browserRejectionHandler(event) { if (j$.isError_(event.reason)) { event.reason.jasmineMessage = 'Unhandled promise rejection: ' + event.reason; onerror(event.reason); } else { onerror('Unhandled promise rejection: ' + event.reason); } }; if (global.addEventListener) { global.addEventListener( 'unhandledrejection', browserRejectionHandler ); } this.uninstall = function uninstall() { global.onerror = originalHandler; if (global.removeEventListener) { global.removeEventListener( 'unhandledrejection', browserRejectionHandler ); } }; } }; this.pushListener = function pushListener(listener) { handlers.push(listener); }; this.popListener = function popListener() { handlers.pop(); }; } return GlobalErrors; }; /* eslint-disable compat/compat */ getJasmineRequireObj().toBePending = function(j$) { /** * Expect a promise to be pending, ie. the promise is neither resolved nor rejected. * @function * @async * @name async-matchers#toBePending * @since 3.6 * @example * await expectAsync(aPromise).toBePending(); */ return function toBePending() { return { compare: function(actual) { if (!j$.isPromiseLike(actual)) { throw new Error('Expected toBePending to be called on a promise.'); } var want = {}; return Promise.race([actual, Promise.resolve(want)]).then( function(got) { return {pass: want === got}; }, function() { return {pass: false}; } ); } }; }; }; getJasmineRequireObj().toBeRejected = function(j$) { /** * Expect a promise to be rejected. * @function * @async * @name async-matchers#toBeRejected * @since 3.1.0 * @example * await expectAsync(aPromise).toBeRejected(); * @example * return expectAsync(aPromise).toBeRejected(); */ return function toBeRejected() { return { compare: function(actual) { if (!j$.isPromiseLike(actual)) { throw new Error('Expected toBeRejected to be called on a promise.'); } return actual.then( function() { return {pass: false}; }, function() { return {pass: true}; } ); } }; }; }; getJasmineRequireObj().toBeRejectedWith = function(j$) { /** * Expect a promise to be rejected with a value equal to the expected, using deep equality comparison. * @function * @async * @name async-matchers#toBeRejectedWith * @since 3.3.0 * @param {Object} expected - Value that the promise is expected to be rejected with * @example * await expectAsync(aPromise).toBeRejectedWith({prop: 'value'}); * @example * return expectAsync(aPromise).toBeRejectedWith({prop: 'value'}); */ return function toBeRejectedWith(matchersUtil) { return { compare: function(actualPromise, expectedValue) { if (!j$.isPromiseLike(actualPromise)) { throw new Error('Expected toBeRejectedWith to be called on a promise.'); } function prefix(passed) { return 'Expected a promise ' + (passed ? 'not ' : '') + 'to be rejected with ' + matchersUtil.pp(expectedValue); } return actualPromise.then( function() { return { pass: false, message: prefix(false) + ' but it was resolved.' }; }, function(actualValue) { if (matchersUtil.equals(actualValue, expectedValue)) { return { pass: true, message: prefix(true) + '.' }; } else { return { pass: false, message: prefix(false) + ' but it was rejected with ' + matchersUtil.pp(actualValue) + '.' }; } } ); } }; }; }; getJasmineRequireObj().toBeRejectedWithError = function(j$) { /** * Expect a promise to be rejected with a value matched to the expected * @function * @async * @name async-matchers#toBeRejectedWithError * @since 3.5.0 * @param {Error} [expected] - `Error` constructor the object that was thrown needs to be an instance of. If not provided, `Error` will be used. * @param {RegExp|String} [message] - The message that should be set on the thrown `Error` * @example * await expectAsync(aPromise).toBeRejectedWithError(MyCustomError, 'Error message'); * await expectAsync(aPromise).toBeRejectedWithError(MyCustomError, /Error message/); * await expectAsync(aPromise).toBeRejectedWithError(MyCustomError); * await expectAsync(aPromise).toBeRejectedWithError('Error message'); * return expectAsync(aPromise).toBeRejectedWithError(/Error message/); */ return function toBeRejectedWithError(matchersUtil) { return { compare: function(actualPromise, arg1, arg2) { if (!j$.isPromiseLike(actualPromise)) { throw new Error('Expected toBeRejectedWithError to be called on a promise.'); } var expected = getExpectedFromArgs(arg1, arg2, matchersUtil); return actualPromise.then( function() { return { pass: false, message: 'Expected a promise to be rejected but it was resolved.' }; }, function(actualValue) { return matchError(actualValue, expected, matchersUtil); } ); } }; }; function matchError(actual, expected, matchersUtil) { if (!j$.isError_(actual)) { return fail(expected, 'rejected with ' + matchersUtil.pp(actual)); } if (!(actual instanceof expected.error)) { return fail(expected, 'rejected with type ' + j$.fnNameFor(actual.constructor)); } var actualMessage = actual.message; if (actualMessage === expected.message || typeof expected.message === 'undefined') { return pass(expected); } if (expected.message instanceof RegExp && expected.message.test(actualMessage)) { return pass(expected); } return fail(expected, 'rejected with ' + matchersUtil.pp(actual)); } function pass(expected) { return { pass: true, message: 'Expected a promise not to be rejected with ' + expected.printValue + ', but it was.' }; } function fail(expected, message) { return { pass: false, message: 'Expected a promise to be rejected with ' + expected.printValue + ' but it was ' + message + '.' }; } function getExpectedFromArgs(arg1, arg2, matchersUtil) { var error, message; if (isErrorConstructor(arg1)) { error = arg1; message = arg2; } else { error = Error; message = arg1; } return { error: error, message: message, printValue: j$.fnNameFor(error) + (typeof message === 'undefined' ? '' : ': ' + matchersUtil.pp(message)) }; } function isErrorConstructor(value) { return typeof value === 'function' && (value === Error || j$.isError_(value.prototype)); } }; getJasmineRequireObj().toBeResolved = function(j$) { /** * Expect a promise to be resolved. * @function * @async * @name async-matchers#toBeResolved * @since 3.1.0 * @example * await expectAsync(aPromise).toBeResolved(); * @example * return expectAsync(aPromise).toBeResolved(); */ return function toBeResolved() { return { compare: function(actual) { if (!j$.isPromiseLike(actual)) { throw new Error('Expected toBeResolved to be called on a promise.'); } return actual.then( function() { return {pass: true}; }, function() { return {pass: false}; } ); } }; }; }; getJasmineRequireObj().toBeResolvedTo = function(j$) { /** * Expect a promise to be resolved to a value equal to the expected, using deep equality comparison. * @function * @async * @name async-matchers#toBeResolvedTo * @since 3.1.0 * @param {Object} expected - Value that the promise is expected to resolve to * @example * await expectAsync(aPromise).toBeResolvedTo({prop: 'value'}); * @example * return expectAsync(aPromise).toBeResolvedTo({prop: 'value'}); */ return function toBeResolvedTo(matchersUtil) { return { compare: function(actualPromise, expectedValue) { if (!j$.isPromiseLike(actualPromise)) { throw new Error('Expected toBeResolvedTo to be called on a promise.'); } function prefix(passed) { return 'Expected a promise ' + (passed ? 'not ' : '') + 'to be resolved to ' + matchersUtil.pp(expectedValue); } return actualPromise.then( function(actualValue) { if (matchersUtil.equals(actualValue, expectedValue)) { return { pass: true, message: prefix(true) + '.' }; } else { return { pass: false, message: prefix(false) + ' but it was resolved to ' + matchersUtil.pp(actualValue) + '.' }; } }, function() { return { pass: false, message: prefix(false) + ' but it was rejected.' }; } ); } }; }; }; getJasmineRequireObj().DiffBuilder = function (j$) { return function DiffBuilder(config) { var prettyPrinter = (config || {}).prettyPrinter || j$.makePrettyPrinter(), mismatches = new j$.MismatchTree(), path = new j$.ObjectPath(), actualRoot = undefined, expectedRoot = undefined; return { setRoots: function (actual, expected) { actualRoot = actual; expectedRoot = expected; }, recordMismatch: function (formatter) { mismatches.add(path, formatter); }, getMessage: function () { var messages = []; mismatches.traverse(function (path, isLeaf, formatter) { var actualCustom, expectedCustom, useCustom, derefResult = dereferencePath(path, actualRoot, expectedRoot, prettyPrinter), actual = derefResult.actual, expected = derefResult.expected; if (formatter) { messages.push(formatter(actual, expected, path, prettyPrinter)); return true; } actualCustom = prettyPrinter.customFormat_(actual); expectedCustom = prettyPrinter.customFormat_(expected); useCustom = !(j$.util.isUndefined(actualCustom) && j$.util.isUndefined(expectedCustom)); if (useCustom) { messages.push(wrapPrettyPrinted(actualCustom, expectedCustom, path)); return false; // don't recurse further } if (isLeaf) { messages.push(defaultFormatter(actual, expected, path, prettyPrinter)); } return true; }); return messages.join('\n'); }, withPath: function (pathComponent, block) { var oldPath = path; path = path.add(pathComponent); block(); path = oldPath; } }; function defaultFormatter(actual, expected, path, prettyPrinter) { return wrapPrettyPrinted(prettyPrinter(actual), prettyPrinter(expected), path); } function wrapPrettyPrinted(actual, expected, path) { return 'Expected ' + path + (path.depth() ? ' = ' : '') + actual + ' to equal ' + expected + '.'; } }; function dereferencePath(objectPath, actual, expected, pp) { function handleAsymmetricExpected() { if (j$.isAsymmetricEqualityTester_(expected) && j$.isFunction_(expected.valuesForDiff_)) { var asymmetricResult = expected.valuesForDiff_(actual, pp); expected = asymmetricResult.self; actual = asymmetricResult.other; } } var i; handleAsymmetricExpected(); for (i = 0; i < objectPath.components.length; i++) { actual = actual[objectPath.components[i]]; expected = expected[objectPath.components[i]]; handleAsymmetricExpected(); } return {actual: actual, expected: expected}; } }; getJasmineRequireObj().MatchersUtil = function(j$) { // TODO: convert all uses of j$.pp to use the injected pp /** * _Note:_ Do not construct this directly. Jasmine will construct one and * pass it to matchers and asymmetric equality testers. * @name MatchersUtil * @classdesc Utilities for use in implementing matchers * @constructor */ function MatchersUtil(options) { options = options || {}; this.customTesters_ = options.customTesters || []; /** * Formats a value for use in matcher failure messages and similar contexts, * taking into account the current set of custom value formatters. * @function * @name MatchersUtil#pp * @since 3.6.0 * @param {*} value The value to pretty-print * @return {string} The pretty-printed value */ this.pp = options.pp || function() {}; }; /** * Determines whether `haystack` contains `needle`, using the same comparison * logic as {@link MatchersUtil#equals}. * @function * @name MatchersUtil#contains * @since 2.0.0 * @param {*} haystack The collection to search * @param {*} needle The value to search for * @param [customTesters] An array of custom equality testers * @returns {boolean} True if `needle` was found in `haystack` */ MatchersUtil.prototype.contains = function(haystack, needle, customTesters) { if (j$.isSet(haystack)) { return haystack.has(needle); } if ((Object.prototype.toString.apply(haystack) === '[object Array]') || (!!haystack && !haystack.indexOf)) { for (var i = 0; i < haystack.length; i++) { if (this.equals(haystack[i], needle, customTesters)) { return true; } } return false; } return !!haystack && haystack.indexOf(needle) >= 0; }; MatchersUtil.prototype.buildFailureMessage = function() { var self = this; var args = Array.prototype.slice.call(arguments, 0), matcherName = args[0], isNot = args[1], actual = args[2], expected = args.slice(3), englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); }); var message = 'Expected ' + self.pp(actual) + (isNot ? ' not ' : ' ') + englishyPredicate; if (expected.length > 0) { for (var i = 0; i < expected.length; i++) { if (i > 0) { message += ','; } message += ' ' + self.pp(expected[i]); } } return message + '.'; }; MatchersUtil.prototype.asymmetricDiff_ = function(a, b, aStack, bStack, customTesters, diffBuilder) { if (j$.isFunction_(b.valuesForDiff_)) { var values = b.valuesForDiff_(a, this.pp); this.eq_(values.other, values.self, aStack, bStack, customTesters, diffBuilder); } else { diffBuilder.recordMismatch(); } }; MatchersUtil.prototype.asymmetricMatch_ = function(a, b, aStack, bStack, customTesters, diffBuilder) { var asymmetricA = j$.isAsymmetricEqualityTester_(a), asymmetricB = j$.isAsymmetricEqualityTester_(b), shim, result; if (asymmetricA === asymmetricB) { return undefined; } shim = j$.asymmetricEqualityTesterArgCompatShim(this, customTesters); if (asymmetricA) { result = a.asymmetricMatch(b, shim); if (!result) { diffBuilder.recordMismatch(); } return result; } if (asymmetricB) { result = b.asymmetricMatch(a, shim); if (!result) { this.asymmetricDiff_(a, b, aStack, bStack, customTesters, diffBuilder); } return result; } }; /** * Determines whether two values are deeply equal to each other. * @function * @name MatchersUtil#equals * @since 2.0.0 * @param {*} a The first value to compare * @param {*} b The second value to compare * @param [customTesters] An array of custom equality testers * @returns {boolean} True if the values are equal */ MatchersUtil.prototype.equals = function(a, b, customTestersOrDiffBuilder, diffBuilderOrNothing) { var customTesters, diffBuilder; if (isDiffBuilder(customTestersOrDiffBuilder)) { diffBuilder = customTestersOrDiffBuilder; } else { customTesters = customTestersOrDiffBuilder; diffBuilder = diffBuilderOrNothing; } customTesters = customTesters || this.customTesters_; diffBuilder = diffBuilder || j$.NullDiffBuilder(); diffBuilder.setRoots(a, b); return this.eq_(a, b, [], [], customTesters, diffBuilder); }; // Equality function lovingly adapted from isEqual in // [Underscore](http://underscorejs.org) MatchersUtil.prototype.eq_ = function(a, b, aStack, bStack, customTesters, diffBuilder) { var result = true, self = this, i; var asymmetricResult = this.asymmetricMatch_(a, b, aStack, bStack, customTesters, diffBuilder); if (!j$.util.isUndefined(asymmetricResult)) { return asymmetricResult; } for (i = 0; i < customTesters.length; i++) { var customTesterResult = customTesters[i](a, b); if (!j$.util.isUndefined(customTesterResult)) { if (!customTesterResult) { diffBuilder.recordMismatch(); } return customTesterResult; } } if (a instanceof Error && b instanceof Error) { result = a.message == b.message; if (!result) { diffBuilder.recordMismatch(); } return result; } // Identical objects are equal. `0 === -0`, but they aren't identical. // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal). if (a === b) { result = a !== 0 || 1 / a == 1 / b; if (!result) { diffBuilder.recordMismatch(); } return result; } // A strict comparison is necessary because `null == undefined`. if (a === null || b === null) { result = a === b; if (!result) { diffBuilder.recordMismatch(); } return result; } var className = Object.prototype.toString.call(a); if (className != Object.prototype.toString.call(b)) { diffBuilder.recordMismatch(); return false; } switch (className) { // Strings, numbers, dates, and booleans are compared by value. case '[object String]': // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is // equivalent to `new String("5")`. result = a == String(b); if (!result) { diffBuilder.recordMismatch(); } return result; case '[object Number]': // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for // other numeric values. result = a != +a ? b != +b : (a === 0 && b === 0 ? 1 / a == 1 / b : a == +b); if (!result) { diffBuilder.recordMismatch(); } return result; case '[object Date]': case '[object Boolean]': // Coerce dates and booleans to numeric primitive values. Dates are compared by their // millisecond representations. Note that invalid dates with millisecond representations // of `NaN` are not equivalent. result = +a == +b; if (!result) { diffBuilder.recordMismatch(); } return result; // RegExps are compared by their source patterns and flags. case '[object RegExp]': return a.source == b.source && a.global == b.global && a.multiline == b.multiline && a.ignoreCase == b.ignoreCase; } if (typeof a != 'object' || typeof b != 'object') { diffBuilder.recordMismatch(); return false; } var aIsDomNode = j$.isDomNode(a); var bIsDomNode = j$.isDomNode(b); if (aIsDomNode && bIsDomNode) { // At first try to use DOM3 method isEqualNode result = a.isEqualNode(b); if (!result) { diffBuilder.recordMismatch(); } return result; } if (aIsDomNode || bIsDomNode) { diffBuilder.recordMismatch(); return false; } var aIsPromise = j$.isPromise(a); var bIsPromise = j$.isPromise(b); if (aIsPromise && bIsPromise) { return a === b; } // Assume equality for cyclic structures. The algorithm for detecting cyclic // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. var length = aStack.length; while (length--) { // Linear search. Performance is inversely proportional to the number of // unique nested structures. if (aStack[length] == a) { return bStack[length] == b; } } // Add the first object to the stack of traversed objects. aStack.push(a); bStack.push(b); var size = 0; // Recursively compare objects and arrays. // Compare array lengths to determine if a deep comparison is necessary. if (className == '[object Array]') { var aLength = a.length; var bLength = b.length; diffBuilder.withPath('length', function() { if (aLength !== bLength) { diffBuilder.recordMismatch(); result = false; } }); for (i = 0; i < aLength || i < bLength; i++) { diffBuilder.withPath(i, function() { if (i >= bLength) { diffBuilder.recordMismatch(actualArrayIsLongerFormatter.bind(null, self.pp)); result = false; } else { result = self.eq_(i < aLength ? a[i] : void 0, i < bLength ? b[i] : void 0, aStack, bStack, customTesters, diffBuilder) && result; } }); } if (!result) { return false; } } else if (j$.isMap(a) && j$.isMap(b)) { if (a.size != b.size) { diffBuilder.recordMismatch(); return false; } var keysA = []; var keysB = []; a.forEach( function( valueA, keyA ) { keysA.push( keyA ); }); b.forEach( function( valueB, keyB ) { keysB.push( keyB ); }); // For both sets of keys, check they map to equal values in both maps. // Keep track of corresponding keys (in insertion order) in order to handle asymmetric obj keys. var mapKeys = [keysA, keysB]; var cmpKeys = [keysB, keysA]; var mapIter, mapKey, mapValueA, mapValueB; var cmpIter, cmpKey; for (i = 0; result && i < mapKeys.length; i++) { mapIter = mapKeys[i]; cmpIter = cmpKeys[i]; for (var j = 0; result && j < mapIter.length; j++) { mapKey = mapIter[j]; cmpKey = cmpIter[j]; mapValueA = a.get(mapKey); // Only use the cmpKey when one of the keys is asymmetric and the corresponding key matches, // otherwise explicitly look up the mapKey in the other Map since we want keys with unique // obj identity (that are otherwise equal) to not match. if (j$.isAsymmetricEqualityTester_(mapKey) || j$.isAsymmetricEqualityTester_(cmpKey) && this.eq_(mapKey, cmpKey, aStack, bStack, customTesters, j$.NullDiffBuilder())) { mapValueB = b.get(cmpKey); } else { mapValueB = b.get(mapKey); } result = this.eq_(mapValueA, mapValueB, aStack, bStack, customTesters, j$.NullDiffBuilder()); } } if (!result) { diffBuilder.recordMismatch(); return false; } } else if (j$.isSet(a) && j$.isSet(b)) { if (a.size != b.size) { diffBuilder.recordMismatch(); return false; } var valuesA = []; a.forEach( function( valueA ) { valuesA.push( valueA ); }); var valuesB = []; b.forEach( function( valueB ) { valuesB.push( valueB ); }); // For both sets, check they are all contained in the other set var setPairs = [[valuesA, valuesB], [valuesB, valuesA]]; var stackPairs = [[aStack, bStack], [bStack, aStack]]; var baseValues, baseValue, baseStack; var otherValues, otherValue, otherStack; var found; var prevStackSize; for (i = 0; result && i < setPairs.length; i++) { baseValues = setPairs[i][0]; otherValues = setPairs[i][1]; baseStack = stackPairs[i][0]; otherStack = stackPairs[i][1]; // For each value in the base set... for (var k = 0; result && k < baseValues.length; k++) { baseValue = baseValues[k]; found = false; // ... test that it is present in the other set for (var l = 0; !found && l < otherValues.length; l++) { otherValue = otherValues[l]; prevStackSize = baseStack.length; // compare by value equality found = this.eq_(baseValue, otherValue, baseStack, otherStack, customTesters, j$.NullDiffBuilder()); if (!found && prevStackSize !== baseStack.length) { baseStack.splice(prevStackSize); otherStack.splice(prevStackSize); } } result = result && found; } } if (!result) { diffBuilder.recordMismatch(); return false; } } else { // Objects with different constructors are not equivalent, but `Object`s // or `Array`s from different frames are. var aCtor = a.constructor, bCtor = b.constructor; if (aCtor !== bCtor && isFunction(aCtor) && isFunction(bCtor) && a instanceof aCtor && b instanceof bCtor && !(aCtor instanceof aCtor && bCtor instanceof bCtor)) { diffBuilder.recordMismatch(constructorsAreDifferentFormatter.bind(null, this.pp)); return false; } } // Deep compare objects. var aKeys = keys(a, className == '[object Array]'), key; size = aKeys.length; // Ensure that both objects contain the same number of properties before comparing deep equality. if (keys(b, className == '[object Array]').length !== size) { diffBuilder.recordMismatch(objectKeysAreDifferentFormatter.bind(null, this.pp)); return false; } for (i = 0; i < size; i++) { key = aKeys[i]; // Deep compare each member if (!j$.util.has(b, key)) { diffBuilder.recordMismatch(objectKeysAreDifferentFormatter.bind(null, this.pp)); result = false; continue; } diffBuilder.withPath(key, function() { if(!self.eq_(a[key], b[key], aStack, bStack, customTesters, diffBuilder)) { result = false; } }); } if (!result) { return false; } // Remove the first object from the stack of traversed objects. aStack.pop(); bStack.pop(); return result; }; function keys(obj, isArray) { var allKeys = Object.keys ? Object.keys(obj) : (function(o) { var keys = []; for (var key in o) { if (j$.util.has(o, key)) { keys.push(key); } } return keys; })(obj); if (!isArray) { return allKeys; } if (allKeys.length === 0) { return allKeys; } var extraKeys = []; for (var i = 0; i < allKeys.length; i++) { if (!/^[0-9]+$/.test(allKeys[i])) { extraKeys.push(allKeys[i]); } } return extraKeys; } function isFunction(obj) { return typeof obj === 'function'; } function objectKeysAreDifferentFormatter(pp, actual, expected, path) { var missingProperties = j$.util.objectDifference(expected, actual), extraProperties = j$.util.objectDifference(actual, expected), missingPropertiesMessage = formatKeyValuePairs(pp, missingProperties), extraPropertiesMessage = formatKeyValuePairs(pp, extraProperties), messages = []; if (!path.depth()) { path = 'object'; } if (missingPropertiesMessage.length) { messages.push('Expected ' + path + ' to have properties' + missingPropertiesMessage); } if (extraPropertiesMessage.length) { messages.push('Expected ' + path + ' not to have properties' + extraPropertiesMessage); } return messages.join('\n'); } function constructorsAreDifferentFormatter(pp, actual, expected, path) { if (!path.depth()) { path = 'object'; } return 'Expected ' + path + ' to be a kind of ' + j$.fnNameFor(expected.constructor) + ', but was ' + pp(actual) + '.'; } function actualArrayIsLongerFormatter(pp, actual, expected, path) { return 'Unexpected ' + path + (path.depth() ? ' = ' : '') + pp(actual) + ' in array.'; } function formatKeyValuePairs(pp, obj) { var formatted = ''; for (var key in obj) { formatted += '\n ' + key + ': ' + pp(obj[key]); } return formatted; } function isDiffBuilder(obj) { return obj && typeof obj.recordMismatch === 'function'; } return MatchersUtil; }; getJasmineRequireObj().MismatchTree = function (j$) { /* To be able to apply custom object formatters at all possible levels of an object graph, DiffBuilder needs to be able to know not just where the mismatch occurred but also all ancestors of the mismatched value in both the expected and actual object graphs. MismatchTree maintains that context and provides it via the traverse method. */ function MismatchTree(path) { this.path = path || new j$.ObjectPath([]); this.formatter = undefined; this.children = []; this.isMismatch = false; } MismatchTree.prototype.add = function (path, formatter) { var key, child; if (path.depth() === 0) { this.formatter = formatter; this.isMismatch = true; } else { key = path.components[0]; path = path.shift(); child = this.child(key); if (!child) { child = new MismatchTree(this.path.add(key)); this.children.push(child); } child.add(path, formatter); } }; MismatchTree.prototype.traverse = function (visit) { var i, hasChildren = this.children.length > 0; if (this.isMismatch || hasChildren) { if (visit(this.path, !hasChildren, this.formatter)) { for (i = 0; i < this.children.length; i++) { this.children[i].traverse(visit); } } } }; MismatchTree.prototype.child = function(key) { var i, pathEls; for (i = 0; i < this.children.length; i++) { pathEls = this.children[i].path.components; if (pathEls[pathEls.length - 1] === key) { return this.children[i]; } } }; return MismatchTree; }; getJasmineRequireObj().nothing = function() { /** * {@link expect} nothing explicitly. * @function * @name matchers#nothing * @since 2.8.0 * @example * expect().nothing(); */ function nothing() { return { compare: function() { return { pass: true }; } }; } return nothing; }; getJasmineRequireObj().NullDiffBuilder = function(j$) { return function() { return { withPath: function(_, block) { block(); }, setRoots: function() {}, recordMismatch: function() {} }; }; }; getJasmineRequireObj().ObjectPath = function(j$) { function ObjectPath(components) { this.components = components || []; } ObjectPath.prototype.toString = function() { if (this.components.length) { return '$' + map(this.components, formatPropertyAccess).join(''); } else { return ''; } }; ObjectPath.prototype.add = function(component) { return new ObjectPath(this.components.concat([component])); }; ObjectPath.prototype.shift = function() { return new ObjectPath(this.components.slice(1)); }; ObjectPath.prototype.depth = function() { return this.components.length; }; function formatPropertyAccess(prop) { if (typeof prop === 'number') { return '[' + prop + ']'; } if (isValidIdentifier(prop)) { return '.' + prop; } return '[\'' + prop + '\']'; } function map(array, fn) { var results = []; for (var i = 0; i < array.length; i++) { results.push(fn(array[i])); } return results; } function isValidIdentifier(string) { return /^[A-Za-z\$_][A-Za-z0-9\$_]*$/.test(string); } return ObjectPath; }; getJasmineRequireObj().requireAsyncMatchers = function(jRequire, j$) { var availableMatchers = [ 'toBePending', 'toBeResolved', 'toBeRejected', 'toBeResolvedTo', 'toBeRejectedWith', 'toBeRejectedWithError' ], matchers = {}; for (var i = 0; i < availableMatchers.length; i++) { var name = availableMatchers[i]; matchers[name] = jRequire[name](j$); } return matchers; }; getJasmineRequireObj().toBe = function(j$) { /** * {@link expect} the actual value to be `===` to the expected value. * @function * @name matchers#toBe * @since 1.3.0 * @param {Object} expected - The expected value to compare against. * @example * expect(thing).toBe(realThing); */ function toBe(matchersUtil) { var tip = ' Tip: To check for deep equality, use .toEqual() instead of .toBe().'; return { compare: function(actual, expected) { var result = { pass: actual === expected }; if (typeof expected === 'object') { result.message = matchersUtil.buildFailureMessage('toBe', result.pass, actual, expected) + tip; } return result; } }; } return toBe; }; getJasmineRequireObj().toBeCloseTo = function() { /** * {@link expect} the actual value to be within a specified precision of the expected value. * @function * @name matchers#toBeCloseTo * @since 1.3.0 * @param {Object} expected - The expected value to compare against. * @param {Number} [precision=2] - The number of decimal points to check. * @example * expect(number).toBeCloseTo(42.2, 3); */ function toBeCloseTo() { return { compare: function(actual, expected, precision) { if (precision !== 0) { precision = precision || 2; } if (expected === null || actual === null) { throw new Error('Cannot use toBeCloseTo with null. Arguments evaluated to: ' + 'expect(' + actual + ').toBeCloseTo(' + expected + ').' ); } var pow = Math.pow(10, precision + 1); var delta = Math.abs(expected - actual); var maxDelta = Math.pow(10, -precision) / 2; return { pass: Math.round(delta * pow) <= maxDelta * pow }; } }; } return toBeCloseTo; }; getJasmineRequireObj().toBeDefined = function() { /** * {@link expect} the actual value to be defined. (Not `undefined`) * @function * @name matchers#toBeDefined * @since 1.3.0 * @example * expect(result).toBeDefined(); */ function toBeDefined() { return { compare: function(actual) { return { pass: (void 0 !== actual) }; } }; } return toBeDefined; }; getJasmineRequireObj().toBeFalse = function() { /** * {@link expect} the actual value to be `false`. * @function * @name matchers#toBeFalse * @since 3.5.0 * @example * expect(result).toBeFalse(); */ function toBeFalse() { return { compare: function(actual) { return { pass: actual === false }; } }; } return toBeFalse; }; getJasmineRequireObj().toBeFalsy = function() { /** * {@link expect} the actual value to be falsy * @function * @name matchers#toBeFalsy * @since 2.0.0 * @example * expect(result).toBeFalsy(); */ function toBeFalsy() { return { compare: function(actual) { return { pass: !actual }; } }; } return toBeFalsy; }; getJasmineRequireObj().toBeGreaterThan = function() { /** * {@link expect} the actual value to be greater than the expected value. * @function * @name matchers#toBeGreaterThan * @since 2.0.0 * @param {Number} expected - The value to compare against. * @example * expect(result).toBeGreaterThan(3); */ function toBeGreaterThan() { return { compare: function(actual, expected) { return { pass: actual > expected }; } }; } return toBeGreaterThan; }; getJasmineRequireObj().toBeGreaterThanOrEqual = function() { /** * {@link expect} the actual value to be greater than or equal to the expected value. * @function * @name matchers#toBeGreaterThanOrEqual * @since 2.0.0 * @param {Number} expected - The expected value to compare against. * @example * expect(result).toBeGreaterThanOrEqual(25); */ function toBeGreaterThanOrEqual() { return { compare: function(actual, expected) { return { pass: actual >= expected }; } }; } return toBeGreaterThanOrEqual; }; getJasmineRequireObj().toBeInstanceOf = function(j$) { var usageError = j$.formatErrorMsg('', 'expect(value).toBeInstanceOf()'); /** * {@link expect} the actual to be an instance of the expected class * @function * @name matchers#toBeInstanceOf * @since 3.5.0 * @param {Object} expected - The class or constructor function to check for * @example * expect('foo').toBeInstanceOf(String); * expect(3).toBeInstanceOf(Number); * expect(new Error()).toBeInstanceOf(Error); */ function toBeInstanceOf(matchersUtil) { return { compare: function(actual, expected) { var actualType = actual && actual.constructor ? j$.fnNameFor(actual.constructor) : matchersUtil.pp(actual), expectedType = expected ? j$.fnNameFor(expected) : matchersUtil.pp(expected), expectedMatcher, pass; try { expectedMatcher = new j$.Any(expected); pass = expectedMatcher.asymmetricMatch(actual); } catch (error) { throw new Error(usageError('Expected value is not a constructor function')); } if (pass) { return { pass: true, message: 'Expected instance of ' + actualType + ' not to be an instance of ' + expectedType }; } else { return { pass: false, message: 'Expected instance of ' + actualType + ' to be an instance of ' + expectedType }; } } }; } return toBeInstanceOf; }; getJasmineRequireObj().toBeLessThan = function() { /** * {@link expect} the actual value to be less than the expected value. * @function * @name matchers#toBeLessThan * @since 2.0.0 * @param {Number} expected - The expected value to compare against. * @example * expect(result).toBeLessThan(0); */ function toBeLessThan() { return { compare: function(actual, expected) { return { pass: actual < expected }; } }; } return toBeLessThan; }; getJasmineRequireObj().toBeLessThanOrEqual = function() { /** * {@link expect} the actual value to be less than or equal to the expected value. * @function * @name matchers#toBeLessThanOrEqual * @since 2.0.0 * @param {Number} expected - The expected value to compare against. * @example * expect(result).toBeLessThanOrEqual(123); */ function toBeLessThanOrEqual() { return { compare: function(actual, expected) { return { pass: actual <= expected }; } }; } return toBeLessThanOrEqual; }; getJasmineRequireObj().toBeNaN = function(j$) { /** * {@link expect} the actual value to be `NaN` (Not a Number). * @function * @name matchers#toBeNaN * @since 1.3.0 * @example * expect(thing).toBeNaN(); */ function toBeNaN(matchersUtil) { return { compare: function(actual) { var result = { pass: (actual !== actual) }; if (result.pass) { result.message = 'Expected actual not to be NaN.'; } else { result.message = function() { return 'Expected ' + matchersUtil.pp(actual) + ' to be NaN.'; }; } return result; } }; } return toBeNaN; }; getJasmineRequireObj().toBeNegativeInfinity = function(j$) { /** * {@link expect} the actual value to be `-Infinity` (-infinity). * @function * @name matchers#toBeNegativeInfinity * @since 2.6.0 * @example * expect(thing).toBeNegativeInfinity(); */ function toBeNegativeInfinity(matchersUtil) { return { compare: function(actual) { var result = { pass: (actual === Number.NEGATIVE_INFINITY) }; if (result.pass) { result.message = 'Expected actual not to be -Infinity.'; } else { result.message = function() { return 'Expected ' + matchersUtil.pp(actual) + ' to be -Infinity.'; }; } return result; } }; } return toBeNegativeInfinity; }; getJasmineRequireObj().toBeNull = function() { /** * {@link expect} the actual value to be `null`. * @function * @name matchers#toBeNull * @since 1.3.0 * @example * expect(result).toBeNull(); */ function toBeNull() { return { compare: function(actual) { return { pass: actual === null }; } }; } return toBeNull; }; getJasmineRequireObj().toBePositiveInfinity = function(j$) { /** * {@link expect} the actual value to be `Infinity` (infinity). * @function * @name matchers#toBePositiveInfinity * @since 2.6.0 * @example * expect(thing).toBePositiveInfinity(); */ function toBePositiveInfinity(matchersUtil) { return { compare: function(actual) { var result = { pass: (actual === Number.POSITIVE_INFINITY) }; if (result.pass) { result.message = 'Expected actual not to be Infinity.'; } else { result.message = function() { return 'Expected ' + matchersUtil.pp(actual) + ' to be Infinity.'; }; } return result; } }; } return toBePositiveInfinity; }; getJasmineRequireObj().toBeTrue = function() { /** * {@link expect} the actual value to be `true`. * @function * @name matchers#toBeTrue * @since 3.5.0 * @example * expect(result).toBeTrue(); */ function toBeTrue() { return { compare: function(actual) { return { pass: actual === true }; } }; } return toBeTrue; }; getJasmineRequireObj().toBeTruthy = function() { /** * {@link expect} the actual value to be truthy. * @function * @name matchers#toBeTruthy * @since 2.0.0 * @example * expect(thing).toBeTruthy(); */ function toBeTruthy() { return { compare: function(actual) { return { pass: !!actual }; } }; } return toBeTruthy; }; getJasmineRequireObj().toBeUndefined = function() { /** * {@link expect} the actual value to be `undefined`. * @function * @name matchers#toBeUndefined * @since 1.3.0 * @example * expect(result).toBeUndefined(): */ function toBeUndefined() { return { compare: function(actual) { return { pass: void 0 === actual }; } }; } return toBeUndefined; }; getJasmineRequireObj().toContain = function() { /** * {@link expect} the actual value to contain a specific value. * @function * @name matchers#toContain * @since 2.0.0 * @param {Object} expected - The value to look for. * @example * expect(array).toContain(anElement); * expect(string).toContain(substring); */ function toContain(matchersUtil) { return { compare: function(actual, expected) { return { pass: matchersUtil.contains(actual, expected) }; } }; } return toContain; }; getJasmineRequireObj().toEqual = function(j$) { /** * {@link expect} the actual value to be equal to the expected, using deep equality comparison. * @function * @name matchers#toEqual * @since 1.3.0 * @param {Object} expected - Expected value * @example * expect(bigObject).toEqual({"foo": ['bar', 'baz']}); */ function toEqual(matchersUtil) { return { compare: function(actual, expected) { var result = { pass: false }, diffBuilder = j$.DiffBuilder({prettyPrinter: matchersUtil.pp}); result.pass = matchersUtil.equals(actual, expected, diffBuilder); // TODO: only set error message if test fails result.message = diffBuilder.getMessage(); return result; } }; } return toEqual; }; getJasmineRequireObj().toHaveBeenCalled = function(j$) { var getErrorMsg = j$.formatErrorMsg('', 'expect().toHaveBeenCalled()'); /** * {@link expect} the actual (a {@link Spy}) to have been called. * @function * @name matchers#toHaveBeenCalled * @since 1.3.0 * @example * expect(mySpy).toHaveBeenCalled(); * expect(mySpy).not.toHaveBeenCalled(); */ function toHaveBeenCalled(matchersUtil) { return { compare: function(actual) { var result = {}; if (!j$.isSpy(actual)) { throw new Error(getErrorMsg('Expected a spy, but got ' + matchersUtil.pp(actual) + '.')); } if (arguments.length > 1) { throw new Error(getErrorMsg('Does not take arguments, use toHaveBeenCalledWith')); } result.pass = actual.calls.any(); result.message = result.pass ? 'Expected spy ' + actual.and.identity + ' not to have been called.' : 'Expected spy ' + actual.and.identity + ' to have been called.'; return result; } }; } return toHaveBeenCalled; }; getJasmineRequireObj().toHaveBeenCalledBefore = function(j$) { var getErrorMsg = j$.formatErrorMsg('', 'expect().toHaveBeenCalledBefore()'); /** * {@link expect} the actual value (a {@link Spy}) to have been called before another {@link Spy}. * @function * @name matchers#toHaveBeenCalledBefore * @since 2.6.0 * @param {Spy} expected - {@link Spy} that should have been called after the `actual` {@link Spy}. * @example * expect(mySpy).toHaveBeenCalledBefore(otherSpy); */ function toHaveBeenCalledBefore(matchersUtil) { return { compare: function(firstSpy, latterSpy) { if (!j$.isSpy(firstSpy)) { throw new Error(getErrorMsg('Expected a spy, but got ' + matchersUtil.pp(firstSpy) + '.')); } if (!j$.isSpy(latterSpy)) { throw new Error(getErrorMsg('Expected a spy, but got ' + matchersUtil.pp(latterSpy) + '.')); } var result = { pass: false }; if (!firstSpy.calls.count()) { result.message = 'Expected spy ' + firstSpy.and.identity + ' to have been called.'; return result; } if (!latterSpy.calls.count()) { result.message = 'Expected spy ' + latterSpy.and.identity + ' to have been called.'; return result; } var latest1stSpyCall = firstSpy.calls.mostRecent().invocationOrder; var first2ndSpyCall = latterSpy.calls.first().invocationOrder; result.pass = latest1stSpyCall < first2ndSpyCall; if (result.pass) { result.message = 'Expected spy ' + firstSpy.and.identity + ' to not have been called before spy ' + latterSpy.and.identity + ', but it was'; } else { var first1stSpyCall = firstSpy.calls.first().invocationOrder; var latest2ndSpyCall = latterSpy.calls.mostRecent().invocationOrder; if(first1stSpyCall < first2ndSpyCall) { result.message = 'Expected latest call to spy ' + firstSpy.and.identity + ' to have been called before first call to spy ' + latterSpy.and.identity + ' (no interleaved calls)'; } else if (latest2ndSpyCall > latest1stSpyCall) { result.message = 'Expected first call to spy ' + latterSpy.and.identity + ' to have been called after latest call to spy ' + firstSpy.and.identity + ' (no interleaved calls)'; } else { result.message = 'Expected spy ' + firstSpy.and.identity + ' to have been called before spy ' + latterSpy.and.identity; } } return result; } }; } return toHaveBeenCalledBefore; }; getJasmineRequireObj().toHaveBeenCalledOnceWith = function (j$) { var getErrorMsg = j$.formatErrorMsg('', 'expect().toHaveBeenCalledOnceWith(...arguments)'); /** * {@link expect} the actual (a {@link Spy}) to have been called exactly once, and exactly with the particular arguments. * @function * @name matchers#toHaveBeenCalledOnceWith * @since 3.6.0 * @param {...Object} - The arguments to look for * @example * expect(mySpy).toHaveBeenCalledOnceWith('foo', 'bar', 2); */ function toHaveBeenCalledOnceWith(util) { return { compare: function () { var args = Array.prototype.slice.call(arguments, 0), actual = args[0], expectedArgs = args.slice(1); if (!j$.isSpy(actual)) { throw new Error(getErrorMsg('Expected a spy, but got ' + j$.pp(actual) + '.')); } var prettyPrintedCalls = actual.calls.allArgs().map(function (argsForCall) { return ' ' + j$.pp(argsForCall); }); if (actual.calls.count() === 1 && util.contains(actual.calls.allArgs(), expectedArgs)) { return { pass: true, message: 'Expected spy ' + actual.and.identity + ' to have been called 0 times, multiple times, or once, but with arguments different from:\n' + ' ' + j$.pp(expectedArgs) + '\n' + 'But the actual call was:\n' + prettyPrintedCalls.join(',\n') + '.\n\n' }; } function getDiffs() { return actual.calls.allArgs().map(function (argsForCall, callIx) { var diffBuilder = new j$.DiffBuilder(); util.equals(argsForCall, expectedArgs, diffBuilder); return diffBuilder.getMessage(); }); } function butString() { switch (actual.calls.count()) { case 0: return 'But it was never called.\n\n'; case 1: return 'But the actual call was:\n' + prettyPrintedCalls.join(',\n') + '.\n' + getDiffs().join('\n') + '\n\n'; default: return 'But the actual calls were:\n' + prettyPrintedCalls.join(',\n') + '.\n\n'; } } return { pass: false, message: 'Expected spy ' + actual.and.identity + ' to have been called only once, and with given args:\n' + ' ' + j$.pp(expectedArgs) + '\n' + butString() }; } }; } return toHaveBeenCalledOnceWith; }; getJasmineRequireObj().toHaveBeenCalledTimes = function(j$) { var getErrorMsg = j$.formatErrorMsg('', 'expect().toHaveBeenCalledTimes()'); /** * {@link expect} the actual (a {@link Spy}) to have been called the specified number of times. * @function * @name matchers#toHaveBeenCalledTimes * @since 2.4.0 * @param {Number} expected - The number of invocations to look for. * @example * expect(mySpy).toHaveBeenCalledTimes(3); */ function toHaveBeenCalledTimes(matchersUtil) { return { compare: function(actual, expected) { if (!j$.isSpy(actual)) { throw new Error(getErrorMsg('Expected a spy, but got ' + matchersUtil.pp(actual) + '.')); } var args = Array.prototype.slice.call(arguments, 0), result = { pass: false }; if (!j$.isNumber_(expected)) { throw new Error(getErrorMsg('The expected times failed is a required argument and must be a number.')); } actual = args[0]; var calls = actual.calls.count(); var timesMessage = expected === 1 ? 'once' : expected + ' times'; result.pass = calls === expected; result.message = result.pass ? 'Expected spy ' + actual.and.identity + ' not to have been called ' + timesMessage + '. It was called ' + calls + ' times.' : 'Expected spy ' + actual.and.identity + ' to have been called ' + timesMessage + '. It was called ' + calls + ' times.'; return result; } }; } return toHaveBeenCalledTimes; }; getJasmineRequireObj().toHaveBeenCalledWith = function(j$) { var getErrorMsg = j$.formatErrorMsg('', 'expect().toHaveBeenCalledWith(...arguments)'); /** * {@link expect} the actual (a {@link Spy}) to have been called with particular arguments at least once. * @function * @name matchers#toHaveBeenCalledWith * @since 1.3.0 * @param {...Object} - The arguments to look for * @example * expect(mySpy).toHaveBeenCalledWith('foo', 'bar', 2); */ function toHaveBeenCalledWith(matchersUtil) { return { compare: function() { var args = Array.prototype.slice.call(arguments, 0), actual = args[0], expectedArgs = args.slice(1), result = { pass: false }; if (!j$.isSpy(actual)) { throw new Error(getErrorMsg('Expected a spy, but got ' + matchersUtil.pp(actual) + '.')); } if (!actual.calls.any()) { result.message = function() { return 'Expected spy ' + actual.and.identity + ' to have been called with:\n' + ' ' + matchersUtil.pp(expectedArgs) + '\nbut it was never called.'; }; return result; } if (matchersUtil.contains(actual.calls.allArgs(), expectedArgs)) { result.pass = true; result.message = function() { return 'Expected spy ' + actual.and.identity + ' not to have been called with:\n' + ' ' + matchersUtil.pp(expectedArgs) + '\nbut it was.'; }; } else { result.message = function() { var prettyPrintedCalls = actual.calls.allArgs().map(function(argsForCall) { return ' ' + matchersUtil.pp(argsForCall); }); var diffs = actual.calls.allArgs().map(function(argsForCall, callIx) { var diffBuilder = new j$.DiffBuilder(); matchersUtil.equals(argsForCall, expectedArgs, diffBuilder); return 'Call ' + callIx + ':\n' + diffBuilder.getMessage().replace(/^/mg, ' '); }); return 'Expected spy ' + actual.and.identity + ' to have been called with:\n' + ' ' + matchersUtil.pp(expectedArgs) + '\n' + '' + 'but actual calls were:\n' + prettyPrintedCalls.join(',\n') + '.\n\n' + diffs.join('\n'); }; } return result; } }; } return toHaveBeenCalledWith; }; getJasmineRequireObj().toHaveClass = function(j$) { /** * {@link expect} the actual value to be a DOM element that has the expected class * @function * @name matchers#toHaveClass * @since 3.0.0 * @param {Object} expected - The class name to test for * @example * var el = document.createElement('div'); * el.className = 'foo bar baz'; * expect(el).toHaveClass('bar'); */ function toHaveClass(matchersUtil) { return { compare: function(actual, expected) { if (!isElement(actual)) { throw new Error(matchersUtil.pp(actual) + ' is not a DOM element'); } return { pass: actual.classList.contains(expected) }; } }; } function isElement(maybeEl) { return maybeEl && maybeEl.classList && j$.isFunction_(maybeEl.classList.contains); } return toHaveClass; }; getJasmineRequireObj().toHaveSize = function(j$) { /** * {@link expect} the actual size to be equal to the expected, using array-like length or object keys size. * @function * @name matchers#toHaveSize * @since 3.6.0 * @param {Object} expected - Expected size * @example * array = [1,2]; * expect(array).toHaveSize(2); */ function toHaveSize() { return { compare: function(actual, expected) { var result = { pass: false }; if (j$.isA_('WeakSet', actual) || j$.isWeakMap(actual) || j$.isDataView(actual)) { throw new Error('Cannot get size of ' + actual + '.'); } if (j$.isSet(actual) || j$.isMap(actual)) { result.pass = actual.size === expected; } else if (isLength(actual.length)) { result.pass = actual.length === expected; } else { result.pass = Object.keys(actual).length === expected; } return result; } }; } var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991; // eslint-disable-line compat/compat function isLength(value) { return (typeof value == 'number') && value > -1 && value % 1 === 0 && value <= MAX_SAFE_INTEGER; } return toHaveSize; }; getJasmineRequireObj().toMatch = function(j$) { var getErrorMsg = j$.formatErrorMsg('', 'expect().toMatch( || )'); /** * {@link expect} the actual value to match a regular expression * @function * @name matchers#toMatch * @since 1.3.0 * @param {RegExp|String} expected - Value to look for in the string. * @example * expect("my string").toMatch(/string$/); * expect("other string").toMatch("her"); */ function toMatch() { return { compare: function(actual, expected) { if (!j$.isString_(expected) && !j$.isA_('RegExp', expected)) { throw new Error(getErrorMsg('Expected is not a String or a RegExp')); } var regexp = new RegExp(expected); return { pass: regexp.test(actual) }; } }; } return toMatch; }; getJasmineRequireObj().toThrow = function(j$) { var getErrorMsg = j$.formatErrorMsg('', 'expect(function() {}).toThrow()'); /** * {@link expect} a function to `throw` something. * @function * @name matchers#toThrow * @since 2.0.0 * @param {Object} [expected] - Value that should be thrown. If not provided, simply the fact that something was thrown will be checked. * @example * expect(function() { return 'things'; }).toThrow('foo'); * expect(function() { return 'stuff'; }).toThrow(); */ function toThrow(matchersUtil) { return { compare: function(actual, expected) { var result = { pass: false }, threw = false, thrown; if (typeof actual != 'function') { throw new Error(getErrorMsg('Actual is not a Function')); } try { actual(); } catch (e) { threw = true; thrown = e; } if (!threw) { result.message = 'Expected function to throw an exception.'; return result; } if (arguments.length == 1) { result.pass = true; result.message = function() { return 'Expected function not to throw, but it threw ' + matchersUtil.pp(thrown) + '.'; }; return result; } if (matchersUtil.equals(thrown, expected)) { result.pass = true; result.message = function() { return 'Expected function not to throw ' + matchersUtil.pp(expected) + '.'; }; } else { result.message = function() { return 'Expected function to throw ' + matchersUtil.pp(expected) + ', but it threw ' + matchersUtil.pp(thrown) + '.'; }; } return result; } }; } return toThrow; }; getJasmineRequireObj().toThrowError = function(j$) { var getErrorMsg = j$.formatErrorMsg('', 'expect(function() {}).toThrowError(, )'); /** * {@link expect} a function to `throw` an `Error`. * @function * @name matchers#toThrowError * @since 2.0.0 * @param {Error} [expected] - `Error` constructor the object that was thrown needs to be an instance of. If not provided, `Error` will be used. * @param {RegExp|String} [message] - The message that should be set on the thrown `Error` * @example * expect(function() { return 'things'; }).toThrowError(MyCustomError, 'message'); * expect(function() { return 'things'; }).toThrowError(MyCustomError, /bar/); * expect(function() { return 'stuff'; }).toThrowError(MyCustomError); * expect(function() { return 'other'; }).toThrowError(/foo/); * expect(function() { return 'other'; }).toThrowError(); */ function toThrowError(matchersUtil) { return { compare: function(actual) { var errorMatcher = getMatcher.apply(null, arguments), thrown; if (typeof actual != 'function') { throw new Error(getErrorMsg('Actual is not a Function')); } try { actual(); return fail('Expected function to throw an Error.'); } catch (e) { thrown = e; } if (!j$.isError_(thrown)) { return fail(function() { return 'Expected function to throw an Error, but it threw ' + matchersUtil.pp(thrown) + '.'; }); } return errorMatcher.match(thrown); } }; function getMatcher() { var expected, errorType; if (arguments[2]) { errorType = arguments[1]; expected = arguments[2]; if (!isAnErrorType(errorType)) { throw new Error(getErrorMsg('Expected error type is not an Error.')); } return exactMatcher(expected, errorType); } else if (arguments[1]) { expected = arguments[1]; if (isAnErrorType(arguments[1])) { return exactMatcher(null, arguments[1]); } else { return exactMatcher(arguments[1], null); } } else { return anyMatcher(); } } function anyMatcher() { return { match: function(error) { return pass('Expected function not to throw an Error, but it threw ' + j$.fnNameFor(error) + '.'); } }; } function exactMatcher(expected, errorType) { if (expected && !isStringOrRegExp(expected)) { if (errorType) { throw new Error(getErrorMsg('Expected error message is not a string or RegExp.')); } else { throw new Error(getErrorMsg('Expected is not an Error, string, or RegExp.')); } } function messageMatch(message) { if (typeof expected == 'string') { return expected == message; } else { return expected.test(message); } } var errorTypeDescription = errorType ? j$.fnNameFor(errorType) : 'an exception'; function thrownDescription(thrown) { var thrownName = errorType ? j$.fnNameFor(thrown.constructor) : 'an exception', thrownMessage = ''; if (expected) { thrownMessage = ' with message ' + matchersUtil.pp(thrown.message); } return thrownName + thrownMessage; } function messageDescription() { if (expected === null) { return ''; } else if (expected instanceof RegExp) { return ' with a message matching ' + matchersUtil.pp(expected); } else { return ' with message ' + matchersUtil.pp(expected); } } function matches(error) { return (errorType === null || error instanceof errorType) && (expected === null || messageMatch(error.message)); } return { match: function(thrown) { if (matches(thrown)) { return pass(function() { return 'Expected function not to throw ' + errorTypeDescription + messageDescription() + '.'; }); } else { return fail(function() { return 'Expected function to throw ' + errorTypeDescription + messageDescription() + ', but it threw ' + thrownDescription(thrown) + '.'; }); } } }; } function isStringOrRegExp(potential) { return potential instanceof RegExp || (typeof potential == 'string'); } function isAnErrorType(type) { if (typeof type !== 'function') { return false; } var Surrogate = function() {}; Surrogate.prototype = type.prototype; return j$.isError_(new Surrogate()); } } function pass(message) { return { pass: true, message: message }; } function fail(message) { return { pass: false, message: message }; } return toThrowError; }; getJasmineRequireObj().toThrowMatching = function(j$) { var usageError = j$.formatErrorMsg('', 'expect(function() {}).toThrowMatching()'); /** * {@link expect} a function to `throw` something matching a predicate. * @function * @name matchers#toThrowMatching * @since 3.0.0 * @param {Function} predicate - A function that takes the thrown exception as its parameter and returns true if it matches. * @example * expect(function() { throw new Error('nope'); }).toThrowMatching(function(thrown) { return thrown.message === 'nope'; }); */ function toThrowMatching(matchersUtil) { return { compare: function(actual, predicate) { var thrown; if (typeof actual !== 'function') { throw new Error(usageError('Actual is not a Function')); } if (typeof predicate !== 'function') { throw new Error(usageError('Predicate is not a Function')); } try { actual(); return fail('Expected function to throw an exception.'); } catch (e) { thrown = e; } if (predicate(thrown)) { return pass('Expected function not to throw an exception matching a predicate.'); } else { return fail(function() { return 'Expected function to throw an exception matching a predicate, ' + 'but it threw ' + thrownDescription(thrown) + '.'; }); } } }; function thrownDescription(thrown) { if (thrown && thrown.constructor) { return j$.fnNameFor(thrown.constructor) + ' with message ' + matchersUtil.pp(thrown.message); } else { return matchersUtil.pp(thrown); } } } function pass(message) { return { pass: true, message: message }; } function fail(message) { return { pass: false, message: message }; } return toThrowMatching; }; getJasmineRequireObj().MockDate = function() { function MockDate(global) { var self = this; var currentTime = 0; if (!global || !global.Date) { self.install = function() {}; self.tick = function() {}; self.uninstall = function() {}; return self; } var GlobalDate = global.Date; self.install = function(mockDate) { if (mockDate instanceof GlobalDate) { currentTime = mockDate.getTime(); } else { currentTime = new GlobalDate().getTime(); } global.Date = FakeDate; }; self.tick = function(millis) { millis = millis || 0; currentTime = currentTime + millis; }; self.uninstall = function() { currentTime = 0; global.Date = GlobalDate; }; createDateProperties(); return self; function FakeDate() { switch (arguments.length) { case 0: return new GlobalDate(currentTime); case 1: return new GlobalDate(arguments[0]); case 2: return new GlobalDate(arguments[0], arguments[1]); case 3: return new GlobalDate(arguments[0], arguments[1], arguments[2]); case 4: return new GlobalDate( arguments[0], arguments[1], arguments[2], arguments[3] ); case 5: return new GlobalDate( arguments[0], arguments[1], arguments[2], arguments[3], arguments[4] ); case 6: return new GlobalDate( arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5] ); default: return new GlobalDate( arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6] ); } } function createDateProperties() { FakeDate.prototype = GlobalDate.prototype; FakeDate.now = function() { if (GlobalDate.now) { return currentTime; } else { throw new Error('Browser does not support Date.now()'); } }; FakeDate.toSource = GlobalDate.toSource; FakeDate.toString = GlobalDate.toString; FakeDate.parse = GlobalDate.parse; FakeDate.UTC = GlobalDate.UTC; } } return MockDate; }; getJasmineRequireObj().makePrettyPrinter = function(j$) { function SinglePrettyPrintRun(customObjectFormatters, pp) { this.customObjectFormatters_ = customObjectFormatters; this.ppNestLevel_ = 0; this.seen = []; this.length = 0; this.stringParts = []; this.pp_ = pp; } function hasCustomToString(value) { // value.toString !== Object.prototype.toString if value has no custom toString but is from another context (e.g. // iframe, web worker) try { return ( j$.isFunction_(value.toString) && value.toString !== Object.prototype.toString && value.toString() !== Object.prototype.toString.call(value) ); } catch (e) { // The custom toString() threw. return true; } } SinglePrettyPrintRun.prototype.format = function(value) { this.ppNestLevel_++; try { var customFormatResult = this.applyCustomFormatters_(value); if (customFormatResult) { this.emitScalar(customFormatResult); } else if (j$.util.isUndefined(value)) { this.emitScalar('undefined'); } else if (value === null) { this.emitScalar('null'); } else if (value === 0 && 1 / value === -Infinity) { this.emitScalar('-0'); } else if (value === j$.getGlobal()) { this.emitScalar(''); } else if (value.jasmineToString) { this.emitScalar(value.jasmineToString(this.pp_)); } else if (typeof value === 'string') { this.emitString(value); } else if (j$.isSpy(value)) { this.emitScalar('spy on ' + value.and.identity); } else if (j$.isSpy(value.toString)) { this.emitScalar('spy on ' + value.toString.and.identity); } else if (value instanceof RegExp) { this.emitScalar(value.toString()); } else if (typeof value === 'function') { this.emitScalar('Function'); } else if (j$.isDomNode(value)) { if (value.tagName) { this.emitDomElement(value); } else { this.emitScalar('HTMLNode'); } } else if (value instanceof Date) { this.emitScalar('Date(' + value + ')'); } else if (j$.isSet(value)) { this.emitSet(value); } else if (j$.isMap(value)) { this.emitMap(value); } else if (j$.isTypedArray_(value)) { this.emitTypedArray(value); } else if ( value.toString && typeof value === 'object' && !j$.isArray_(value) && hasCustomToString(value) ) { try { this.emitScalar(value.toString()); } catch (e) { this.emitScalar('has-invalid-toString-method'); } } else if (j$.util.arrayContains(this.seen, value)) { this.emitScalar( '' ); } else if (j$.isArray_(value) || j$.isA_('Object', value)) { this.seen.push(value); if (j$.isArray_(value)) { this.emitArray(value); } else { this.emitObject(value); } this.seen.pop(); } else { this.emitScalar(value.toString()); } } catch (e) { if (this.ppNestLevel_ > 1 || !(e instanceof MaxCharsReachedError)) { throw e; } } finally { this.ppNestLevel_--; } }; SinglePrettyPrintRun.prototype.applyCustomFormatters_ = function(value) { return customFormat(value, this.customObjectFormatters_); }; SinglePrettyPrintRun.prototype.iterateObject = function(obj, fn) { var objKeys = keys(obj, j$.isArray_(obj)); var isGetter = function isGetter(prop) {}; if (obj.__lookupGetter__) { isGetter = function isGetter(prop) { var getter = obj.__lookupGetter__(prop); return !j$.util.isUndefined(getter) && getter !== null; }; } var length = Math.min(objKeys.length, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); for (var i = 0; i < length; i++) { var property = objKeys[i]; fn(property, isGetter(property)); } return objKeys.length > length; }; SinglePrettyPrintRun.prototype.emitScalar = function(value) { this.append(value); }; SinglePrettyPrintRun.prototype.emitString = function(value) { this.append("'" + value + "'"); }; SinglePrettyPrintRun.prototype.emitArray = function(array) { if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { this.append('Array'); return; } var length = Math.min(array.length, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); this.append('[ '); for (var i = 0; i < length; i++) { if (i > 0) { this.append(', '); } this.format(array[i]); } if (array.length > length) { this.append(', ...'); } var self = this; var first = array.length === 0; var truncated = this.iterateObject(array, function(property, isGetter) { if (first) { first = false; } else { self.append(', '); } self.formatProperty(array, property, isGetter); }); if (truncated) { this.append(', ...'); } this.append(' ]'); }; SinglePrettyPrintRun.prototype.emitSet = function(set) { if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { this.append('Set'); return; } this.append('Set( '); var size = Math.min(set.size, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); var i = 0; set.forEach(function(value, key) { if (i >= size) { return; } if (i > 0) { this.append(', '); } this.format(value); i++; }, this); if (set.size > size) { this.append(', ...'); } this.append(' )'); }; SinglePrettyPrintRun.prototype.emitMap = function(map) { if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { this.append('Map'); return; } this.append('Map( '); var size = Math.min(map.size, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); var i = 0; map.forEach(function(value, key) { if (i >= size) { return; } if (i > 0) { this.append(', '); } this.format([key, value]); i++; }, this); if (map.size > size) { this.append(', ...'); } this.append(' )'); }; SinglePrettyPrintRun.prototype.emitObject = function(obj) { var ctor = obj.constructor, constructorName; constructorName = typeof ctor === 'function' && obj instanceof ctor ? j$.fnNameFor(obj.constructor) : 'null'; this.append(constructorName); if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { return; } var self = this; this.append('({ '); var first = true; var truncated = this.iterateObject(obj, function(property, isGetter) { if (first) { first = false; } else { self.append(', '); } self.formatProperty(obj, property, isGetter); }); if (truncated) { this.append(', ...'); } this.append(' })'); }; SinglePrettyPrintRun.prototype.emitTypedArray = function(arr) { var constructorName = j$.fnNameFor(arr.constructor), limitedArray = Array.prototype.slice.call( arr, 0, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH ), itemsString = Array.prototype.join.call(limitedArray, ', '); if (limitedArray.length !== arr.length) { itemsString += ', ...'; } this.append(constructorName + ' [ ' + itemsString + ' ]'); }; SinglePrettyPrintRun.prototype.emitDomElement = function(el) { var tagName = el.tagName.toLowerCase(), attrs = el.attributes, i, len = attrs.length, out = '<' + tagName, attr; for (i = 0; i < len; i++) { attr = attrs[i]; out += ' ' + attr.name; if (attr.value !== '') { out += '="' + attr.value + '"'; } } out += '>'; if (el.childElementCount !== 0 || el.textContent !== '') { out += '...'; } this.append(out); }; SinglePrettyPrintRun.prototype.formatProperty = function( obj, property, isGetter ) { this.append(property); this.append(': '); if (isGetter) { this.append(''); } else { this.format(obj[property]); } }; SinglePrettyPrintRun.prototype.append = function(value) { // This check protects us from the rare case where an object has overriden // `toString()` with an invalid implementation (returning a non-string). if (typeof value !== 'string') { value = Object.prototype.toString.call(value); } var result = truncate(value, j$.MAX_PRETTY_PRINT_CHARS - this.length); this.length += result.value.length; this.stringParts.push(result.value); if (result.truncated) { throw new MaxCharsReachedError(); } }; function truncate(s, maxlen) { if (s.length <= maxlen) { return { value: s, truncated: false }; } s = s.substring(0, maxlen - 4) + ' ...'; return { value: s, truncated: true }; } function MaxCharsReachedError() { this.message = 'Exceeded ' + j$.MAX_PRETTY_PRINT_CHARS + ' characters while pretty-printing a value'; } MaxCharsReachedError.prototype = new Error(); function keys(obj, isArray) { var allKeys = Object.keys ? Object.keys(obj) : (function(o) { var keys = []; for (var key in o) { if (j$.util.has(o, key)) { keys.push(key); } } return keys; })(obj); if (!isArray) { return allKeys; } if (allKeys.length === 0) { return allKeys; } var extraKeys = []; for (var i = 0; i < allKeys.length; i++) { if (!/^[0-9]+$/.test(allKeys[i])) { extraKeys.push(allKeys[i]); } } return extraKeys; } function customFormat(value, customObjectFormatters) { var i, result; for (i = 0; i < customObjectFormatters.length; i++) { result = customObjectFormatters[i](value); if (result !== undefined) { return result; } } } return function(customObjectFormatters) { customObjectFormatters = customObjectFormatters || []; var pp = function(value) { var prettyPrinter = new SinglePrettyPrintRun(customObjectFormatters, pp); prettyPrinter.format(value); return prettyPrinter.stringParts.join(''); }; pp.customFormat_ = function(value) { return customFormat(value, customObjectFormatters); }; return pp; }; }; getJasmineRequireObj().QueueRunner = function(j$) { function StopExecutionError() {} StopExecutionError.prototype = new Error(); j$.StopExecutionError = StopExecutionError; function once(fn) { var called = false; return function(arg) { if (!called) { called = true; // Direct call using single parameter, because cleanup/next does not need more fn(arg); } return null; }; } function emptyFn() {} function QueueRunner(attrs) { var queueableFns = attrs.queueableFns || []; this.queueableFns = queueableFns.concat(attrs.cleanupFns || []); this.firstCleanupIx = queueableFns.length; this.onComplete = attrs.onComplete || emptyFn; this.clearStack = attrs.clearStack || function(fn) { fn(); }; this.onException = attrs.onException || emptyFn; this.userContext = attrs.userContext || new j$.UserContext(); this.timeout = attrs.timeout || { setTimeout: setTimeout, clearTimeout: clearTimeout }; this.fail = attrs.fail || emptyFn; this.globalErrors = attrs.globalErrors || { pushListener: emptyFn, popListener: emptyFn }; this.completeOnFirstError = !!attrs.completeOnFirstError; this.errored = false; if (typeof this.onComplete !== 'function') { throw new Error('invalid onComplete ' + JSON.stringify(this.onComplete)); } this.deprecated = attrs.deprecated; } QueueRunner.prototype.execute = function() { var self = this; this.handleFinalError = function(message, source, lineno, colno, error) { // Older browsers would send the error as the first parameter. HTML5 // specifies the the five parameters above. The error instance should // be preffered, otherwise the call stack would get lost. self.onException(error || message); }; this.globalErrors.pushListener(this.handleFinalError); this.run(0); }; QueueRunner.prototype.skipToCleanup = function(lastRanIndex) { if (lastRanIndex < this.firstCleanupIx) { this.run(this.firstCleanupIx); } else { this.run(lastRanIndex + 1); } }; QueueRunner.prototype.clearTimeout = function(timeoutId) { Function.prototype.apply.apply(this.timeout.clearTimeout, [ j$.getGlobal(), [timeoutId] ]); }; QueueRunner.prototype.setTimeout = function(fn, timeout) { return Function.prototype.apply.apply(this.timeout.setTimeout, [ j$.getGlobal(), [fn, timeout] ]); }; QueueRunner.prototype.attempt = function attempt(iterativeIndex) { var self = this, completedSynchronously = true, handleError = function handleError(error) { onException(error); next(error); }, cleanup = once(function cleanup() { if (timeoutId !== void 0) { self.clearTimeout(timeoutId); } self.globalErrors.popListener(handleError); }), next = once(function next(err) { cleanup(); if (j$.isError_(err)) { if (!(err instanceof StopExecutionError) && !err.jasmineMessage) { self.fail(err); } self.errored = errored = true; } function runNext() { if (self.completeOnFirstError && errored) { self.skipToCleanup(iterativeIndex); } else { self.run(iterativeIndex + 1); } } if (completedSynchronously) { self.setTimeout(runNext); } else { runNext(); } }), errored = false, queueableFn = self.queueableFns[iterativeIndex], timeoutId; next.fail = function nextFail() { self.fail.apply(null, arguments); self.errored = errored = true; next(); }; self.globalErrors.pushListener(handleError); if (queueableFn.timeout !== undefined) { var timeoutInterval = queueableFn.timeout || j$.DEFAULT_TIMEOUT_INTERVAL; timeoutId = self.setTimeout(function() { var error = new Error( 'Timeout - Async function did not complete within ' + timeoutInterval + 'ms ' + (queueableFn.timeout ? '(custom timeout)' : '(set by jasmine.DEFAULT_TIMEOUT_INTERVAL)') ); onException(error); next(); }, timeoutInterval); } try { if (queueableFn.fn.length === 0) { var maybeThenable = queueableFn.fn.call(self.userContext); if (maybeThenable && j$.isFunction_(maybeThenable.then)) { maybeThenable.then(next, onPromiseRejection); completedSynchronously = false; return { completedSynchronously: false }; } } else { queueableFn.fn.call(self.userContext, next); completedSynchronously = false; return { completedSynchronously: false }; } } catch (e) { onException(e); self.errored = errored = true; } cleanup(); return { completedSynchronously: true, errored: errored }; function onException(e) { self.onException(e); self.errored = errored = true; } function onPromiseRejection(e) { onException(e); next(); } }; QueueRunner.prototype.run = function(recursiveIndex) { var length = this.queueableFns.length, self = this, iterativeIndex; for ( iterativeIndex = recursiveIndex; iterativeIndex < length; iterativeIndex++ ) { var result = this.attempt(iterativeIndex); if (!result.completedSynchronously) { return; } self.errored = self.errored || result.errored; if (this.completeOnFirstError && result.errored) { this.skipToCleanup(iterativeIndex); return; } } this.clearStack(function() { self.globalErrors.popListener(self.handleFinalError); self.onComplete(self.errored && new StopExecutionError()); }); }; return QueueRunner; }; getJasmineRequireObj().ReportDispatcher = function(j$) { function ReportDispatcher(methods, queueRunnerFactory) { var dispatchedMethods = methods || []; for (var i = 0; i < dispatchedMethods.length; i++) { var method = dispatchedMethods[i]; this[method] = (function(m) { return function() { dispatch(m, arguments); }; })(method); } var reporters = []; var fallbackReporter = null; this.addReporter = function(reporter) { reporters.push(reporter); }; this.provideFallbackReporter = function(reporter) { fallbackReporter = reporter; }; this.clearReporters = function() { reporters = []; }; return this; function dispatch(method, args) { if (reporters.length === 0 && fallbackReporter !== null) { reporters.push(fallbackReporter); } var onComplete = args[args.length - 1]; args = j$.util.argsToArray(args).splice(0, args.length - 1); var fns = []; for (var i = 0; i < reporters.length; i++) { var reporter = reporters[i]; addFn(fns, reporter, method, args); } queueRunnerFactory({ queueableFns: fns, onComplete: onComplete, isReporter: true }); } function addFn(fns, reporter, method, args) { var fn = reporter[method]; if (!fn) { return; } var thisArgs = j$.util.cloneArgs(args); if (fn.length <= 1) { fns.push({ fn: function() { return fn.apply(reporter, thisArgs); } }); } else { fns.push({ fn: function(done) { return fn.apply(reporter, thisArgs.concat([done])); } }); } } } return ReportDispatcher; }; getJasmineRequireObj().interface = function(jasmine, env) { var jasmineInterface = { /** * Callback passed to parts of the Jasmine base interface. * * By default Jasmine assumes this function completes synchronously. * If you have code that you need to test asynchronously, you can declare that you receive a `done` callback, return a Promise, or use the `async` keyword if it is supported in your environment. * @callback implementationCallback * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. */ /** * Create a group of specs (often called a suite). * * Calls to `describe` can be nested within other calls to compose your suite as a tree. * @name describe * @since 1.3.0 * @function * @global * @param {String} description Textual description of the group * @param {Function} specDefinitions Function for Jasmine to invoke that will define inner suites and specs */ describe: function(description, specDefinitions) { return env.describe(description, specDefinitions); }, /** * A temporarily disabled [`describe`]{@link describe} * * Specs within an `xdescribe` will be marked pending and not executed * @name xdescribe * @since 1.3.0 * @function * @global * @param {String} description Textual description of the group * @param {Function} specDefinitions Function for Jasmine to invoke that will define inner suites and specs */ xdescribe: function(description, specDefinitions) { return env.xdescribe(description, specDefinitions); }, /** * A focused [`describe`]{@link describe} * * If suites or specs are focused, only those that are focused will be executed * @see fit * @name fdescribe * @since 2.1.0 * @function * @global * @param {String} description Textual description of the group * @param {Function} specDefinitions Function for Jasmine to invoke that will define inner suites and specs */ fdescribe: function(description, specDefinitions) { return env.fdescribe(description, specDefinitions); }, /** * Define a single spec. A spec should contain one or more {@link expect|expectations} that test the state of the code. * * A spec whose expectations all succeed will be passing and a spec with any failures will fail. * The name `it` is a pronoun for the test target, not an abbreviation of anything. It makes the * spec more readable by connecting the function name `it` and the argument `description` as a * complete sentence. * @name it * @since 1.3.0 * @function * @global * @param {String} description Textual description of what this spec is checking * @param {implementationCallback} [testFunction] Function that contains the code of your test. If not provided the test will be `pending`. * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async spec. * @see async */ it: function() { return env.it.apply(env, arguments); }, /** * A temporarily disabled [`it`]{@link it} * * The spec will report as `pending` and will not be executed. * @name xit * @since 1.3.0 * @function * @global * @param {String} description Textual description of what this spec is checking. * @param {implementationCallback} [testFunction] Function that contains the code of your test. Will not be executed. */ xit: function() { return env.xit.apply(env, arguments); }, /** * A focused [`it`]{@link it} * * If suites or specs are focused, only those that are focused will be executed. * @name fit * @since 2.1.0 * @function * @global * @param {String} description Textual description of what this spec is checking. * @param {implementationCallback} testFunction Function that contains the code of your test. * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async spec. * @see async */ fit: function() { return env.fit.apply(env, arguments); }, /** * Run some shared setup before each of the specs in the {@link describe} in which it is called. * @name beforeEach * @since 1.3.0 * @function * @global * @param {implementationCallback} [function] Function that contains the code to setup your specs. * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async beforeEach. * @see async */ beforeEach: function() { return env.beforeEach.apply(env, arguments); }, /** * Run some shared teardown after each of the specs in the {@link describe} in which it is called. * @name afterEach * @since 1.3.0 * @function * @global * @param {implementationCallback} [function] Function that contains the code to teardown your specs. * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async afterEach. * @see async */ afterEach: function() { return env.afterEach.apply(env, arguments); }, /** * Run some shared setup once before all of the specs in the {@link describe} are run. * * _Note:_ Be careful, sharing the setup from a beforeAll makes it easy to accidentally leak state between your specs so that they erroneously pass or fail. * @name beforeAll * @since 2.1.0 * @function * @global * @param {implementationCallback} [function] Function that contains the code to setup your specs. * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async beforeAll. * @see async */ beforeAll: function() { return env.beforeAll.apply(env, arguments); }, /** * Run some shared teardown once after all of the specs in the {@link describe} are run. * * _Note:_ Be careful, sharing the teardown from a afterAll makes it easy to accidentally leak state between your specs so that they erroneously pass or fail. * @name afterAll * @since 2.1.0 * @function * @global * @param {implementationCallback} [function] Function that contains the code to teardown your specs. * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async afterAll. * @see async */ afterAll: function() { return env.afterAll.apply(env, arguments); }, /** * Sets a user-defined property that will be provided to reporters as part of the properties field of {@link SpecResult} * @name setSpecProperty * @since 3.6.0 * @function * @param {String} key The name of the property * @param {*} value The value of the property */ setSpecProperty: function(key, value) { return env.setSpecProperty(key, value); }, /** * Sets a user-defined property that will be provided to reporters as part of the properties field of {@link SuiteResult} * @name setSuiteProperty * @since 3.6.0 * @function * @param {String} key The name of the property * @param {*} value The value of the property */ setSuiteProperty: function(key, value) { return env.setSuiteProperty(key, value); }, /** * Create an expectation for a spec. * @name expect * @since 1.3.0 * @function * @global * @param {Object} actual - Actual computed value to test expectations against. * @return {matchers} */ expect: function(actual) { return env.expect(actual); }, /** * Create an asynchronous expectation for a spec. Note that the matchers * that are provided by an asynchronous expectation all return promises * which must be either returned from the spec or waited for using `await` * in order for Jasmine to associate them with the correct spec. * @name expectAsync * @since 3.3.0 * @function * @global * @param {Object} actual - Actual computed value to test expectations against. * @return {async-matchers} * @example * await expectAsync(somePromise).toBeResolved(); * @example * return expectAsync(somePromise).toBeResolved(); */ expectAsync: function(actual) { return env.expectAsync(actual); }, /** * Mark a spec as pending, expectation results will be ignored. * @name pending * @since 2.0.0 * @function * @global * @param {String} [message] - Reason the spec is pending. */ pending: function() { return env.pending.apply(env, arguments); }, /** * Explicitly mark a spec as failed. * @name fail * @since 2.1.0 * @function * @global * @param {String|Error} [error] - Reason for the failure. */ fail: function() { return env.fail.apply(env, arguments); }, /** * Install a spy onto an existing object. * @name spyOn * @since 1.3.0 * @function * @global * @param {Object} obj - The object upon which to install the {@link Spy}. * @param {String} methodName - The name of the method to replace with a {@link Spy}. * @returns {Spy} */ spyOn: function(obj, methodName) { return env.spyOn(obj, methodName); }, /** * Install a spy on a property installed with `Object.defineProperty` onto an existing object. * @name spyOnProperty * @since 2.6.0 * @function * @global * @param {Object} obj - The object upon which to install the {@link Spy} * @param {String} propertyName - The name of the property to replace with a {@link Spy}. * @param {String} [accessType=get] - The access type (get|set) of the property to {@link Spy} on. * @returns {Spy} */ spyOnProperty: function(obj, methodName, accessType) { return env.spyOnProperty(obj, methodName, accessType); }, /** * Installs spies on all writable and configurable properties of an object. * @name spyOnAllFunctions * @since 3.2.1 * @function * @global * @param {Object} obj - The object upon which to install the {@link Spy}s * @returns {Object} the spied object */ spyOnAllFunctions: function(obj) { return env.spyOnAllFunctions(obj); }, jsApiReporter: new jasmine.JsApiReporter({ timer: new jasmine.Timer() }), /** * @namespace jasmine */ jasmine: jasmine }; /** * Add a custom equality tester for the current scope of specs. * * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}. * @name jasmine.addCustomEqualityTester * @since 2.0.0 * @function * @param {Function} tester - A function which takes two arguments to compare and returns a `true` or `false` comparison result if it knows how to compare them, and `undefined` otherwise. * @see custom_equality */ jasmine.addCustomEqualityTester = function(tester) { env.addCustomEqualityTester(tester); }; /** * Add custom matchers for the current scope of specs. * * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}. * @name jasmine.addMatchers * @since 2.0.0 * @function * @param {Object} matchers - Keys from this object will be the new matcher names. * @see custom_matcher */ jasmine.addMatchers = function(matchers) { return env.addMatchers(matchers); }; /** * Add custom async matchers for the current scope of specs. * * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}. * @name jasmine.addAsyncMatchers * @since 3.5.0 * @function * @param {Object} matchers - Keys from this object will be the new async matcher names. * @see custom_matcher */ jasmine.addAsyncMatchers = function(matchers) { return env.addAsyncMatchers(matchers); }; /** * Add a custom object formatter for the current scope of specs. * * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}. * @name jasmine.addCustomObjectFormatter * @since 3.6.0 * @function * @param {Function} formatter - A function which takes a value to format and returns a string if it knows how to format it, and `undefined` otherwise. * @see custom_object_formatters */ jasmine.addCustomObjectFormatter = function(formatter) { return env.addCustomObjectFormatter(formatter); }; /** * Get the currently booted mock {Clock} for this Jasmine environment. * @name jasmine.clock * @since 2.0.0 * @function * @returns {Clock} */ jasmine.clock = function() { return env.clock; }; /** * Create a bare {@link Spy} object. This won't be installed anywhere and will not have any implementation behind it. * @name jasmine.createSpy * @since 1.3.0 * @function * @param {String} [name] - Name to give the spy. This will be displayed in failure messages. * @param {Function} [originalFn] - Function to act as the real implementation. * @return {Spy} */ jasmine.createSpy = function(name, originalFn) { return env.createSpy(name, originalFn); }; /** * Create an object with multiple {@link Spy}s as its members. * @name jasmine.createSpyObj * @since 1.3.0 * @function * @param {String} [baseName] - Base name for the spies in the object. * @param {String[]|Object} methodNames - Array of method names to create spies for, or Object whose keys will be method names and values the {@link Spy#and#returnValue|returnValue}. * @param {String[]|Object} [propertyNames] - Array of property names to create spies for, or Object whose keys will be propertynames and values the {@link Spy#and#returnValue|returnValue}. * @return {Object} */ jasmine.createSpyObj = function(baseName, methodNames, propertyNames) { return env.createSpyObj(baseName, methodNames, propertyNames); }; /** * Add a custom spy strategy for the current scope of specs. * * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}. * @name jasmine.addSpyStrategy * @since 3.5.0 * @function * @param {String} name - The name of the strategy (i.e. what you call from `and`) * @param {Function} factory - Factory function that returns the plan to be executed. */ jasmine.addSpyStrategy = function(name, factory) { return env.addSpyStrategy(name, factory); }; /** * Set the default spy strategy for the current scope of specs. * * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}. * @name jasmine.setDefaultSpyStrategy * @function * @param {Function} defaultStrategyFn - a function that assigns a strategy * @example * beforeEach(function() { * jasmine.setDefaultSpyStrategy(and => and.returnValue(true)); * }); */ jasmine.setDefaultSpyStrategy = function(defaultStrategyFn) { return env.setDefaultSpyStrategy(defaultStrategyFn); }; return jasmineInterface; }; getJasmineRequireObj().Spy = function(j$) { var nextOrder = (function() { var order = 0; return function() { return order++; }; })(); var matchersUtil = new j$.MatchersUtil({ customTesters: [], pp: j$.makePrettyPrinter() }); /** * _Note:_ Do not construct this directly, use {@link spyOn}, {@link spyOnProperty}, {@link jasmine.createSpy}, or {@link jasmine.createSpyObj} * @constructor * @name Spy */ function Spy( name, originalFn, customStrategies, defaultStrategyFn, getPromise ) { var numArgs = typeof originalFn === 'function' ? originalFn.length : 0, wrapper = makeFunc(numArgs, function(context, args, invokeNew) { return spy(context, args, invokeNew); }), strategyDispatcher = new SpyStrategyDispatcher({ name: name, fn: originalFn, getSpy: function() { return wrapper; }, customStrategies: customStrategies, getPromise: getPromise }), callTracker = new j$.CallTracker(), spy = function(context, args, invokeNew) { /** * @name Spy.callData * @property {object} object - `this` context for the invocation. * @property {number} invocationOrder - Order of the invocation. * @property {Array} args - The arguments passed for this invocation. */ var callData = { object: context, invocationOrder: nextOrder(), args: Array.prototype.slice.apply(args) }; callTracker.track(callData); var returnValue = strategyDispatcher.exec(context, args, invokeNew); callData.returnValue = returnValue; return returnValue; }; function makeFunc(length, fn) { switch (length) { case 1: return function wrap1(a) { return fn(this, arguments, this instanceof wrap1); }; case 2: return function wrap2(a, b) { return fn(this, arguments, this instanceof wrap2); }; case 3: return function wrap3(a, b, c) { return fn(this, arguments, this instanceof wrap3); }; case 4: return function wrap4(a, b, c, d) { return fn(this, arguments, this instanceof wrap4); }; case 5: return function wrap5(a, b, c, d, e) { return fn(this, arguments, this instanceof wrap5); }; case 6: return function wrap6(a, b, c, d, e, f) { return fn(this, arguments, this instanceof wrap6); }; case 7: return function wrap7(a, b, c, d, e, f, g) { return fn(this, arguments, this instanceof wrap7); }; case 8: return function wrap8(a, b, c, d, e, f, g, h) { return fn(this, arguments, this instanceof wrap8); }; case 9: return function wrap9(a, b, c, d, e, f, g, h, i) { return fn(this, arguments, this instanceof wrap9); }; default: return function wrap() { return fn(this, arguments, this instanceof wrap); }; } } for (var prop in originalFn) { if (prop === 'and' || prop === 'calls') { throw new Error( "Jasmine spies would overwrite the 'and' and 'calls' properties on the object being spied upon" ); } wrapper[prop] = originalFn[prop]; } /** * @member {SpyStrategy} - Accesses the default strategy for the spy. This strategy will be used * whenever the spy is called with arguments that don't match any strategy * created with {@link Spy#withArgs}. * @name Spy#and * @since 2.0.0 * @example * spyOn(someObj, 'func').and.returnValue(42); */ wrapper.and = strategyDispatcher.and; /** * Specifies a strategy to be used for calls to the spy that have the * specified arguments. * @name Spy#withArgs * @since 3.0.0 * @function * @param {...*} args - The arguments to match * @type {SpyStrategy} * @example * spyOn(someObj, 'func').withArgs(1, 2, 3).and.returnValue(42); * someObj.func(1, 2, 3); // returns 42 */ wrapper.withArgs = function() { return strategyDispatcher.withArgs.apply(strategyDispatcher, arguments); }; wrapper.calls = callTracker; if (defaultStrategyFn) { defaultStrategyFn(wrapper.and); } return wrapper; } function SpyStrategyDispatcher(strategyArgs) { var baseStrategy = new j$.SpyStrategy(strategyArgs); var argsStrategies = new StrategyDict(function() { return new j$.SpyStrategy(strategyArgs); }); this.and = baseStrategy; this.exec = function(spy, args, invokeNew) { var strategy = argsStrategies.get(args); if (!strategy) { if (argsStrategies.any() && !baseStrategy.isConfigured()) { throw new Error( "Spy '" + strategyArgs.name + "' received a call with arguments " + j$.pp(Array.prototype.slice.call(args)) + ' but all configured strategies specify other arguments.' ); } else { strategy = baseStrategy; } } return strategy.exec(spy, args, invokeNew); }; this.withArgs = function() { return { and: argsStrategies.getOrCreate(arguments) }; }; } function StrategyDict(strategyFactory) { this.strategies = []; this.strategyFactory = strategyFactory; } StrategyDict.prototype.any = function() { return this.strategies.length > 0; }; StrategyDict.prototype.getOrCreate = function(args) { var strategy = this.get(args); if (!strategy) { strategy = this.strategyFactory(); this.strategies.push({ args: args, strategy: strategy }); } return strategy; }; StrategyDict.prototype.get = function(args) { var i; for (i = 0; i < this.strategies.length; i++) { if (matchersUtil.equals(args, this.strategies[i].args)) { return this.strategies[i].strategy; } } }; return Spy; }; getJasmineRequireObj().SpyFactory = function(j$) { function SpyFactory(getCustomStrategies, getDefaultStrategyFn, getPromise) { var self = this; this.createSpy = function(name, originalFn) { return j$.Spy( name, originalFn, getCustomStrategies(), getDefaultStrategyFn(), getPromise ); }; this.createSpyObj = function(baseName, methodNames, propertyNames) { var baseNameIsCollection = j$.isObject_(baseName) || j$.isArray_(baseName); if (baseNameIsCollection) { propertyNames = methodNames; methodNames = baseName; baseName = 'unknown'; } var obj = {}; var spy, descriptor; var methods = normalizeKeyValues(methodNames); for (var i = 0; i < methods.length; i++) { spy = obj[methods[i][0]] = self.createSpy( baseName + '.' + methods[i][0] ); if (methods[i].length > 1) { spy.and.returnValue(methods[i][1]); } } var properties = normalizeKeyValues(propertyNames); for (var i = 0; i < properties.length; i++) { descriptor = { get: self.createSpy(baseName + '.' + properties[i][0] + '.get'), set: self.createSpy(baseName + '.' + properties[i][0] + '.set') }; if (properties[i].length > 1) { descriptor.get.and.returnValue(properties[i][1]); descriptor.set.and.returnValue(properties[i][1]); } Object.defineProperty(obj, properties[i][0], descriptor); } if (methods.length === 0 && properties.length === 0) { throw 'createSpyObj requires a non-empty array or object of method names to create spies for'; } return obj; }; } function normalizeKeyValues(object) { var result = []; if (j$.isArray_(object)) { for (var i = 0; i < object.length; i++) { result.push([object[i]]); } } else if (j$.isObject_(object)) { for (var key in object) { if (object.hasOwnProperty(key)) { result.push([key, object[key]]); } } } return result; } return SpyFactory; }; getJasmineRequireObj().SpyRegistry = function(j$) { var spyOnMsg = j$.formatErrorMsg('', 'spyOn(, )'); var spyOnPropertyMsg = j$.formatErrorMsg( '', 'spyOnProperty(, , [accessType])' ); function SpyRegistry(options) { options = options || {}; var global = options.global || j$.getGlobal(); var createSpy = options.createSpy; var currentSpies = options.currentSpies || function() { return []; }; this.allowRespy = function(allow) { this.respy = allow; }; this.spyOn = function(obj, methodName) { var getErrorMsg = spyOnMsg; if (j$.util.isUndefined(obj) || obj === null) { throw new Error( getErrorMsg( 'could not find an object to spy upon for ' + methodName + '()' ) ); } if (j$.util.isUndefined(methodName) || methodName === null) { throw new Error(getErrorMsg('No method name supplied')); } if (j$.util.isUndefined(obj[methodName])) { throw new Error(getErrorMsg(methodName + '() method does not exist')); } if (obj[methodName] && j$.isSpy(obj[methodName])) { if (this.respy) { return obj[methodName]; } else { throw new Error( getErrorMsg(methodName + ' has already been spied upon') ); } } var descriptor = Object.getOwnPropertyDescriptor(obj, methodName); if (descriptor && !(descriptor.writable || descriptor.set)) { throw new Error( getErrorMsg(methodName + ' is not declared writable or has no setter') ); } var originalMethod = obj[methodName], spiedMethod = createSpy(methodName, originalMethod), restoreStrategy; if ( Object.prototype.hasOwnProperty.call(obj, methodName) || (obj === global && methodName === 'onerror') ) { restoreStrategy = function() { obj[methodName] = originalMethod; }; } else { restoreStrategy = function() { if (!delete obj[methodName]) { obj[methodName] = originalMethod; } }; } currentSpies().push({ restoreObjectToOriginalState: restoreStrategy }); obj[methodName] = spiedMethod; return spiedMethod; }; this.spyOnProperty = function(obj, propertyName, accessType) { var getErrorMsg = spyOnPropertyMsg; accessType = accessType || 'get'; if (j$.util.isUndefined(obj)) { throw new Error( getErrorMsg( 'spyOn could not find an object to spy upon for ' + propertyName + '' ) ); } if (j$.util.isUndefined(propertyName)) { throw new Error(getErrorMsg('No property name supplied')); } var descriptor = j$.util.getPropertyDescriptor(obj, propertyName); if (!descriptor) { throw new Error(getErrorMsg(propertyName + ' property does not exist')); } if (!descriptor.configurable) { throw new Error( getErrorMsg(propertyName + ' is not declared configurable') ); } if (!descriptor[accessType]) { throw new Error( getErrorMsg( 'Property ' + propertyName + ' does not have access type ' + accessType ) ); } if (j$.isSpy(descriptor[accessType])) { if (this.respy) { return descriptor[accessType]; } else { throw new Error( getErrorMsg( propertyName + '#' + accessType + ' has already been spied upon' ) ); } } var originalDescriptor = j$.util.clone(descriptor), spy = createSpy(propertyName, descriptor[accessType]), restoreStrategy; if (Object.prototype.hasOwnProperty.call(obj, propertyName)) { restoreStrategy = function() { Object.defineProperty(obj, propertyName, originalDescriptor); }; } else { restoreStrategy = function() { delete obj[propertyName]; }; } currentSpies().push({ restoreObjectToOriginalState: restoreStrategy }); descriptor[accessType] = spy; Object.defineProperty(obj, propertyName, descriptor); return spy; }; this.spyOnAllFunctions = function(obj) { if (j$.util.isUndefined(obj)) { throw new Error( 'spyOnAllFunctions could not find an object to spy upon' ); } var pointer = obj, props = [], prop, descriptor; while (pointer) { for (prop in pointer) { if ( Object.prototype.hasOwnProperty.call(pointer, prop) && pointer[prop] instanceof Function ) { descriptor = Object.getOwnPropertyDescriptor(pointer, prop); if ( (descriptor.writable || descriptor.set) && descriptor.configurable ) { props.push(prop); } } } pointer = Object.getPrototypeOf(pointer); } for (var i = 0; i < props.length; i++) { this.spyOn(obj, props[i]); } return obj; }; this.clearSpies = function() { var spies = currentSpies(); for (var i = spies.length - 1; i >= 0; i--) { var spyEntry = spies[i]; spyEntry.restoreObjectToOriginalState(); } }; } return SpyRegistry; }; getJasmineRequireObj().SpyStrategy = function(j$) { /** * @interface SpyStrategy */ function SpyStrategy(options) { options = options || {}; var self = this; /** * Get the identifying information for the spy. * @name SpyStrategy#identity * @since 3.0.0 * @member * @type {String} */ this.identity = options.name || 'unknown'; this.originalFn = options.fn || function() {}; this.getSpy = options.getSpy || function() {}; this.plan = this._defaultPlan = function() {}; var k, cs = options.customStrategies || {}; for (k in cs) { if (j$.util.has(cs, k) && !this[k]) { this[k] = createCustomPlan(cs[k]); } } var getPromise = typeof options.getPromise === 'function' ? options.getPromise : function() {}; var requirePromise = function(name) { var Promise = getPromise(); if (!Promise) { throw new Error( name + ' requires global Promise, or `Promise` configured with `jasmine.getEnv().configure()`' ); } return Promise; }; /** * Tell the spy to return a promise resolving to the specified value when invoked. * @name SpyStrategy#resolveTo * @since 3.5.0 * @function * @param {*} value The value to return. */ this.resolveTo = function(value) { var Promise = requirePromise('resolveTo'); self.plan = function() { return Promise.resolve(value); }; return self.getSpy(); }; /** * Tell the spy to return a promise rejecting with the specified value when invoked. * @name SpyStrategy#rejectWith * @since 3.5.0 * @function * @param {*} value The value to return. */ this.rejectWith = function(value) { var Promise = requirePromise('rejectWith'); self.plan = function() { return Promise.reject(value); }; return self.getSpy(); }; } function createCustomPlan(factory) { return function() { var plan = factory.apply(null, arguments); if (!j$.isFunction_(plan)) { throw new Error('Spy strategy must return a function'); } this.plan = plan; return this.getSpy(); }; } /** * Execute the current spy strategy. * @name SpyStrategy#exec * @since 2.0.0 * @function */ SpyStrategy.prototype.exec = function(context, args, invokeNew) { var contextArgs = [context].concat( args ? Array.prototype.slice.call(args) : [] ); var target = this.plan.bind.apply(this.plan, contextArgs); return invokeNew ? new target() : target(); }; /** * Tell the spy to call through to the real implementation when invoked. * @name SpyStrategy#callThrough * @since 2.0.0 * @function */ SpyStrategy.prototype.callThrough = function() { this.plan = this.originalFn; return this.getSpy(); }; /** * Tell the spy to return the value when invoked. * @name SpyStrategy#returnValue * @since 2.0.0 * @function * @param {*} value The value to return. */ SpyStrategy.prototype.returnValue = function(value) { this.plan = function() { return value; }; return this.getSpy(); }; /** * Tell the spy to return one of the specified values (sequentially) each time the spy is invoked. * @name SpyStrategy#returnValues * @since 2.1.0 * @function * @param {...*} values - Values to be returned on subsequent calls to the spy. */ SpyStrategy.prototype.returnValues = function() { var values = Array.prototype.slice.call(arguments); this.plan = function() { return values.shift(); }; return this.getSpy(); }; /** * Tell the spy to throw an error when invoked. * @name SpyStrategy#throwError * @since 2.0.0 * @function * @param {Error|Object|String} something Thing to throw */ SpyStrategy.prototype.throwError = function(something) { var error = j$.isString_(something) ? new Error(something) : something; this.plan = function() { throw error; }; return this.getSpy(); }; /** * Tell the spy to call a fake implementation when invoked. * @name SpyStrategy#callFake * @since 2.0.0 * @function * @param {Function} fn The function to invoke with the passed parameters. */ SpyStrategy.prototype.callFake = function(fn) { if (!(j$.isFunction_(fn) || j$.isAsyncFunction_(fn))) { throw new Error( 'Argument passed to callFake should be a function, got ' + fn ); } this.plan = fn; return this.getSpy(); }; /** * Tell the spy to do nothing when invoked. This is the default. * @name SpyStrategy#stub * @since 2.0.0 * @function */ SpyStrategy.prototype.stub = function(fn) { this.plan = function() {}; return this.getSpy(); }; SpyStrategy.prototype.isConfigured = function() { return this.plan !== this._defaultPlan; }; return SpyStrategy; }; getJasmineRequireObj().StackTrace = function(j$) { function StackTrace(error) { var lines = error.stack.split('\n').filter(function(line) { return line !== ''; }); var extractResult = extractMessage(error.message, lines); if (extractResult) { this.message = extractResult.message; lines = extractResult.remainder; } var parseResult = tryParseFrames(lines); this.frames = parseResult.frames; this.style = parseResult.style; } var framePatterns = [ // PhantomJS on Linux, Node, Chrome, IE, Edge // e.g. " at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)" // Note that the "function name" can include a surprisingly large set of // characters, including angle brackets and square brackets. { re: /^\s*at ([^\)]+) \(([^\)]+)\)$/, fnIx: 1, fileLineColIx: 2, style: 'v8' }, // NodeJS alternate form, often mixed in with the Chrome style // e.g. " at /some/path:4320:20 { re: /\s*at (.+)$/, fileLineColIx: 1, style: 'v8' }, // PhantomJS on OS X, Safari, Firefox // e.g. "run@http://localhost:8888/__jasmine__/jasmine.js:4320:27" // or "http://localhost:8888/__jasmine__/jasmine.js:4320:27" { re: /^(([^@\s]+)@)?([^\s]+)$/, fnIx: 2, fileLineColIx: 3, style: 'webkit' } ]; // regexes should capture the function name (if any) as group 1 // and the file, line, and column as group 2. function tryParseFrames(lines) { var style = null; var frames = lines.map(function(line) { var convertedLine = first(framePatterns, function(pattern) { var overallMatch = line.match(pattern.re), fileLineColMatch; if (!overallMatch) { return null; } fileLineColMatch = overallMatch[pattern.fileLineColIx].match( /^(.*):(\d+):\d+$/ ); if (!fileLineColMatch) { return null; } style = style || pattern.style; return { raw: line, file: fileLineColMatch[1], line: parseInt(fileLineColMatch[2], 10), func: overallMatch[pattern.fnIx] }; }); return convertedLine || { raw: line }; }); return { style: style, frames: frames }; } function first(items, fn) { var i, result; for (i = 0; i < items.length; i++) { result = fn(items[i]); if (result) { return result; } } } function extractMessage(message, stackLines) { var len = messagePrefixLength(message, stackLines); if (len > 0) { return { message: stackLines.slice(0, len).join('\n'), remainder: stackLines.slice(len) }; } } function messagePrefixLength(message, stackLines) { if (!stackLines[0].match(/^\w*Error/)) { return 0; } var messageLines = message.split('\n'); var i; for (i = 1; i < messageLines.length; i++) { if (messageLines[i] !== stackLines[i]) { return 0; } } return messageLines.length; } return StackTrace; }; getJasmineRequireObj().Suite = function(j$) { function Suite(attrs) { this.env = attrs.env; this.id = attrs.id; this.parentSuite = attrs.parentSuite; this.description = attrs.description; this.expectationFactory = attrs.expectationFactory; this.asyncExpectationFactory = attrs.asyncExpectationFactory; this.expectationResultFactory = attrs.expectationResultFactory; this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure; this.beforeFns = []; this.afterFns = []; this.beforeAllFns = []; this.afterAllFns = []; this.timer = attrs.timer || new j$.Timer(); this.children = []; /** * @typedef SuiteResult * @property {Int} id - The unique id of this suite. * @property {String} description - The description text passed to the {@link describe} that made this suite. * @property {String} fullName - The full description including all ancestors of this suite. * @property {Expectation[]} failedExpectations - The list of expectations that failed in an {@link afterAll} for this suite. * @property {Expectation[]} deprecationWarnings - The list of deprecation warnings that occurred on this suite. * @property {String} status - Once the suite has completed, this string represents the pass/fail status of this suite. * @property {number} duration - The time in ms for Suite execution, including any before/afterAll, before/afterEach. * @property {Object} properties - User-supplied properties, if any, that were set using {@link Env#setSuiteProperty} */ this.result = { id: this.id, description: this.description, fullName: this.getFullName(), failedExpectations: [], deprecationWarnings: [], duration: null, properties: null }; } Suite.prototype.setSuiteProperty = function(key, value) { this.result.properties = this.result.properties || {}; this.result.properties[key] = value; }; Suite.prototype.expect = function(actual) { return this.expectationFactory(actual, this); }; Suite.prototype.expectAsync = function(actual) { return this.asyncExpectationFactory(actual, this); }; Suite.prototype.getFullName = function() { var fullName = []; for ( var parentSuite = this; parentSuite; parentSuite = parentSuite.parentSuite ) { if (parentSuite.parentSuite) { fullName.unshift(parentSuite.description); } } return fullName.join(' '); }; Suite.prototype.pend = function() { this.markedPending = true; }; Suite.prototype.beforeEach = function(fn) { this.beforeFns.unshift(fn); }; Suite.prototype.beforeAll = function(fn) { this.beforeAllFns.push(fn); }; Suite.prototype.afterEach = function(fn) { this.afterFns.unshift(fn); }; Suite.prototype.afterAll = function(fn) { this.afterAllFns.unshift(fn); }; Suite.prototype.startTimer = function() { this.timer.start(); }; Suite.prototype.endTimer = function() { this.result.duration = this.timer.elapsed(); }; function removeFns(queueableFns) { for (var i = 0; i < queueableFns.length; i++) { queueableFns[i].fn = null; } } Suite.prototype.cleanupBeforeAfter = function() { removeFns(this.beforeAllFns); removeFns(this.afterAllFns); removeFns(this.beforeFns); removeFns(this.afterFns); }; Suite.prototype.addChild = function(child) { this.children.push(child); }; Suite.prototype.status = function() { if (this.markedPending) { return 'pending'; } if (this.result.failedExpectations.length > 0) { return 'failed'; } else { return 'passed'; } }; Suite.prototype.canBeReentered = function() { return this.beforeAllFns.length === 0 && this.afterAllFns.length === 0; }; Suite.prototype.getResult = function() { this.result.status = this.status(); return this.result; }; Suite.prototype.sharedUserContext = function() { if (!this.sharedContext) { this.sharedContext = this.parentSuite ? this.parentSuite.clonedSharedUserContext() : new j$.UserContext(); } return this.sharedContext; }; Suite.prototype.clonedSharedUserContext = function() { return j$.UserContext.fromExisting(this.sharedUserContext()); }; Suite.prototype.onException = function() { if (arguments[0] instanceof j$.errors.ExpectationFailed) { return; } var data = { matcherName: '', passed: false, expected: '', actual: '', error: arguments[0] }; var failedExpectation = this.expectationResultFactory(data); if (!this.parentSuite) { failedExpectation.globalErrorType = 'afterAll'; } this.result.failedExpectations.push(failedExpectation); }; Suite.prototype.addExpectationResult = function() { if (isFailure(arguments)) { var data = arguments[1]; this.result.failedExpectations.push(this.expectationResultFactory(data)); if (this.throwOnExpectationFailure) { throw new j$.errors.ExpectationFailed(); } } }; Suite.prototype.addDeprecationWarning = function(deprecation) { if (typeof deprecation === 'string') { deprecation = { message: deprecation }; } this.result.deprecationWarnings.push( this.expectationResultFactory(deprecation) ); }; function isFailure(args) { return !args[0]; } return Suite; }; if (typeof window == void 0 && typeof exports == 'object') { /* globals exports */ exports.Suite = jasmineRequire.Suite; } getJasmineRequireObj().Timer = function() { var defaultNow = (function(Date) { return function() { return new Date().getTime(); }; })(Date); function Timer(options) { options = options || {}; var now = options.now || defaultNow, startTime; this.start = function() { startTime = now(); }; this.elapsed = function() { return now() - startTime; }; } return Timer; }; getJasmineRequireObj().TreeProcessor = function() { function TreeProcessor(attrs) { var tree = attrs.tree, runnableIds = attrs.runnableIds, queueRunnerFactory = attrs.queueRunnerFactory, nodeStart = attrs.nodeStart || function() {}, nodeComplete = attrs.nodeComplete || function() {}, failSpecWithNoExpectations = !!attrs.failSpecWithNoExpectations, orderChildren = attrs.orderChildren || function(node) { return node.children; }, excludeNode = attrs.excludeNode || function(node) { return false; }, stats = { valid: true }, processed = false, defaultMin = Infinity, defaultMax = 1 - Infinity; this.processTree = function() { processNode(tree, true); processed = true; return stats; }; this.execute = function(done) { if (!processed) { this.processTree(); } if (!stats.valid) { throw 'invalid order'; } var childFns = wrapChildren(tree, 0); queueRunnerFactory({ queueableFns: childFns, userContext: tree.sharedUserContext(), onException: function() { tree.onException.apply(tree, arguments); }, onComplete: done }); }; function runnableIndex(id) { for (var i = 0; i < runnableIds.length; i++) { if (runnableIds[i] === id) { return i; } } } function processNode(node, parentExcluded) { var executableIndex = runnableIndex(node.id); if (executableIndex !== undefined) { parentExcluded = false; } if (!node.children) { var excluded = parentExcluded || excludeNode(node); stats[node.id] = { excluded: excluded, willExecute: !excluded && !node.markedPending, segments: [ { index: 0, owner: node, nodes: [node], min: startingMin(executableIndex), max: startingMax(executableIndex) } ] }; } else { var hasExecutableChild = false; var orderedChildren = orderChildren(node); for (var i = 0; i < orderedChildren.length; i++) { var child = orderedChildren[i]; processNode(child, parentExcluded); if (!stats.valid) { return; } var childStats = stats[child.id]; hasExecutableChild = hasExecutableChild || childStats.willExecute; } stats[node.id] = { excluded: parentExcluded, willExecute: hasExecutableChild }; segmentChildren(node, orderedChildren, stats[node.id], executableIndex); if (!node.canBeReentered() && stats[node.id].segments.length > 1) { stats = { valid: false }; } } } function startingMin(executableIndex) { return executableIndex === undefined ? defaultMin : executableIndex; } function startingMax(executableIndex) { return executableIndex === undefined ? defaultMax : executableIndex; } function segmentChildren( node, orderedChildren, nodeStats, executableIndex ) { var currentSegment = { index: 0, owner: node, nodes: [], min: startingMin(executableIndex), max: startingMax(executableIndex) }, result = [currentSegment], lastMax = defaultMax, orderedChildSegments = orderChildSegments(orderedChildren); function isSegmentBoundary(minIndex) { return ( lastMax !== defaultMax && minIndex !== defaultMin && lastMax < minIndex - 1 ); } for (var i = 0; i < orderedChildSegments.length; i++) { var childSegment = orderedChildSegments[i], maxIndex = childSegment.max, minIndex = childSegment.min; if (isSegmentBoundary(minIndex)) { currentSegment = { index: result.length, owner: node, nodes: [], min: defaultMin, max: defaultMax }; result.push(currentSegment); } currentSegment.nodes.push(childSegment); currentSegment.min = Math.min(currentSegment.min, minIndex); currentSegment.max = Math.max(currentSegment.max, maxIndex); lastMax = maxIndex; } nodeStats.segments = result; } function orderChildSegments(children) { var specifiedOrder = [], unspecifiedOrder = []; for (var i = 0; i < children.length; i++) { var child = children[i], segments = stats[child.id].segments; for (var j = 0; j < segments.length; j++) { var seg = segments[j]; if (seg.min === defaultMin) { unspecifiedOrder.push(seg); } else { specifiedOrder.push(seg); } } } specifiedOrder.sort(function(a, b) { return a.min - b.min; }); return specifiedOrder.concat(unspecifiedOrder); } function executeNode(node, segmentNumber) { if (node.children) { return { fn: function(done) { var onStart = { fn: function(next) { nodeStart(node, next); } }; queueRunnerFactory({ onComplete: function() { var args = Array.prototype.slice.call(arguments, [0]); node.cleanupBeforeAfter(); nodeComplete(node, node.getResult(), function() { done.apply(undefined, args); }); }, queueableFns: [onStart].concat(wrapChildren(node, segmentNumber)), userContext: node.sharedUserContext(), onException: function() { node.onException.apply(node, arguments); } }); } }; } else { return { fn: function(done) { node.execute( done, stats[node.id].excluded, failSpecWithNoExpectations ); } }; } } function wrapChildren(node, segmentNumber) { var result = [], segmentChildren = stats[node.id].segments[segmentNumber].nodes; for (var i = 0; i < segmentChildren.length; i++) { result.push( executeNode(segmentChildren[i].owner, segmentChildren[i].index) ); } if (!stats[node.id].willExecute) { return result; } return node.beforeAllFns.concat(result).concat(node.afterAllFns); } } return TreeProcessor; }; getJasmineRequireObj().UserContext = function(j$) { function UserContext() {} UserContext.fromExisting = function(oldContext) { var context = new UserContext(); for (var prop in oldContext) { if (oldContext.hasOwnProperty(prop)) { context[prop] = oldContext[prop]; } } return context; }; return UserContext; }; getJasmineRequireObj().version = function() { return '3.6.0'; };cjs-128.0/installed-tests/js/testLang.js0000664000175000017500000000577414771557763017144 0ustar fabiofabio/* eslint-disable no-restricted-properties */ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2008 litl, LLC // tests for imports.lang module // except for Lang.Class and Lang.Interface, which are tested in testLegacyClass const Lang = imports.lang; describe('Lang module', function () { it('counts properties with Lang.countProperties()', function () { var foo = {'a': 10, 'b': 11}; expect(Lang.countProperties(foo)).toEqual(2); }); it('copies properties from one object to another with Lang.copyProperties()', function () { var foo = {'a': 10, 'b': 11}; var bar = {}; Lang.copyProperties(foo, bar); expect(bar).toEqual(foo); }); it('copies properties without an underscore with Lang.copyPublicProperties()', function () { var foo = {'a': 10, 'b': 11, '_c': 12}; var bar = {}; Lang.copyPublicProperties(foo, bar); expect(bar).toEqual({'a': 10, 'b': 11}); }); it('copies property getters and setters', function () { var foo = { 'a': 10, 'b': 11, get c() { return this.a; }, set c(n) { this.a = n; }, }; var bar = {}; Lang.copyProperties(foo, bar); expect(bar.__lookupGetter__('c')).not.toBeNull(); expect(bar.__lookupSetter__('c')).not.toBeNull(); // this should return the value of 'a' expect(bar.c).toEqual(10); // this should set 'a' value bar.c = 13; expect(bar.a).toEqual(13); }); describe('bind()', function () { let o; beforeEach(function () { o = { callback() { return true; }, }; spyOn(o, 'callback').and.callThrough(); }); it('calls the bound function with the supplied this-object', function () { let callback = Lang.bind(o, o.callback); callback(); expect(o.callback.calls.mostRecent()).toEqual(jasmine.objectContaining({ object: o, args: [], returnValue: true, })); }); it('throws an error when no function supplied', function () { expect(() => Lang.bind(o, undefined)).toThrow(); }); it('throws an error when this-object undefined', function () { expect(() => Lang.bind(undefined, function () {})).toThrow(); }); it('supplies extra arguments to the function', function () { let callback = Lang.bind(o, o.callback, 42, 1138); callback(); expect(o.callback).toHaveBeenCalledWith(42, 1138); }); it('appends the extra arguments to any arguments passed', function () { let callback = Lang.bind(o, o.callback, 42, 1138); callback(1, 2, 3); expect(o.callback).toHaveBeenCalledWith(1, 2, 3, 42, 1138); }); }); }); cjs-128.0/installed-tests/js/testNamespace.js0000664000175000017500000000044414771557763020144 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2012 Red Hat, Inc. const Regress = imports.gi.Regress; describe('GI repository namespace', function () { it('supplies a name', function () { expect(Regress.__name__).toEqual('Regress'); }); }); cjs-128.0/installed-tests/js/testGettext.js0000664000175000017500000000124114771557763017670 0ustar fabiofabio// -*- mode: js; indent-tabs-mode: nil -*- // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2015 Endless Mobile, Inc. const Gettext = imports.gettext; describe('Gettext module', function () { // We don't actually want to mess with the locale, so just use setlocale's // query mode. We also don't want to make this test locale-dependent, so // just assert that it returns a string with at least length 1 (the shortest // locale is "C".) it('setlocale returns a locale', function () { let locale = Gettext.setlocale(Gettext.LocaleCategory.ALL, null); expect(locale.length).not.toBeLessThan(1); }); }); cjs-128.0/installed-tests/js/testEncoding.js0000664000175000017500000003630414771557763020002 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2021 Evan Welsh // Some test inputs are derived from https://github.com/denoland/deno/blob/923214c53725651792f6d55c5401bf6b475622ea/op_crates/web/08_text_encoding.js // Data originally from https://encoding.spec.whatwg.org/encodings.json import GLib from 'gi://GLib'; import Gio from 'gi://Gio'; import {arrayLikeWithExactContents} from './matchers.js'; /** * Loads a JSON file from a URI and parses it. * * @param {string} src the URI to load from * @returns {any} */ function loadJSONFromResource(src) { const file = Gio.File.new_for_uri(src); const [, bytes] = file.load_contents(null); const decoder = new TextDecoder(); const jsonRaw = decoder.decode(bytes); const json = JSON.parse(jsonRaw); return json; } /** * Encoded form of '𝓽𝓮𝔁𝓽' * * @returns {number[]} */ function encodedMultibyteCharArray() { return [ 0xf0, 0x9d, 0x93, 0xbd, 0xf0, 0x9d, 0x93, 0xae, 0xf0, 0x9d, 0x94, 0x81, 0xf0, 0x9d, 0x93, 0xbd, ]; } describe('Text Encoding', function () { it('toString() uses spec-compliant tags', function () { const encoder = new TextEncoder(); expect(encoder.toString()).toBe('[object TextEncoder]'); const decoder = new TextDecoder(); expect(decoder.toString()).toBe('[object TextDecoder]'); }); describe('TextEncoder', function () { describe('encode()', function () { it('can encode UTF8 (multi-byte chars)', function () { const input = '𝓽𝓮𝔁𝓽'; const encoder = new TextEncoder(); const encoded = encoder.encode(input); expect(encoded).toEqual( arrayLikeWithExactContents([...encodedMultibyteCharArray()]) ); }); }); describe('encodeInto()', function () { it('can encode UTF8 (Latin chars) into a Uint8Array', function () { const input = 'text'; const encoder = new TextEncoder(); const bytes = new Uint8Array(5); const result = encoder.encodeInto(input, bytes); expect(result.read).toBe(4); expect(result.written).toBe(4); expect(bytes).toEqual( arrayLikeWithExactContents([0x74, 0x65, 0x78, 0x74, 0x00]) ); }); it('can fully encode UTF8 (multi-byte chars) into a Uint8Array', function () { const input = '𝓽𝓮𝔁𝓽'; const encoder = new TextEncoder(); const bytes = new Uint8Array(17); const result = encoder.encodeInto(input, bytes); expect(result.read).toBe(8); expect(result.written).toBe(16); expect(bytes).toEqual( arrayLikeWithExactContents([ ...encodedMultibyteCharArray(), 0x00, ]) ); }); it('can partially encode UTF8 into an under-allocated Uint8Array', function () { const input = '𝓽𝓮𝔁𝓽'; const encoder = new TextEncoder(); const bytes = new Uint8Array(5); const result = encoder.encodeInto(input, bytes); expect(result.read).toBe(2); expect(result.written).toBe(4); expect(bytes).toEqual( arrayLikeWithExactContents([ ...encodedMultibyteCharArray().slice(0, 4), 0x00, ]) ); }); }); }); describe('TextDecoder', function () { describe('decode()', function () { it('fatal is false by default', function () { const decoder = new TextDecoder(); expect(decoder.fatal).toBeFalse(); }); it('ignoreBOM is false by default', function () { const decoder = new TextDecoder(); expect(decoder.ignoreBOM).toBeFalse(); }); it('fatal is true when passed', function () { const decoder = new TextDecoder(undefined, {fatal: true}); expect(decoder.fatal).toBeTrue(); }); it('ignoreBOM is true when passed', function () { const decoder = new TextDecoder(undefined, {ignoreBOM: true}); expect(decoder.ignoreBOM).toBeTrue(); }); it('fatal is coerced to a boolean value', function () { const decoder = new TextDecoder(undefined, {fatal: 1}); expect(decoder.fatal).toBeTrue(); }); it('ignoreBOM is coerced to a boolean value', function () { const decoder = new TextDecoder(undefined, {ignoreBOM: ''}); expect(decoder.ignoreBOM).toBeFalse(); }); it('throws on empty input', function () { const decoder = new TextDecoder(); const input = ''; expect(() => decoder.decode(input)).toThrowError( 'Provided input cannot be converted to ArrayBufferView or ArrayBuffer' ); }); it('throws on null input', function () { const decoder = new TextDecoder(); const input = null; expect(() => decoder.decode(input)).toThrowError( 'Provided input cannot be converted to ArrayBufferView or ArrayBuffer' ); }); it('throws on invalid encoding label', function () { expect(() => new TextDecoder('bad')).toThrowError( "Invalid encoding label: 'bad'" ); }); it('decodes undefined as an empty string', function () { const decoder = new TextDecoder(); const input = undefined; expect(decoder.decode(input)).toBe(''); }); it('decodes UTF-8 byte array (Uint8Array)', function () { const decoder = new TextDecoder(); const input = new Uint8Array([...encodedMultibyteCharArray()]); expect(decoder.decode(input)).toBe('𝓽𝓮𝔁𝓽'); }); it('decodes GLib.Bytes', function () { const decoder = new TextDecoder(); const input = new GLib.Bytes(encodedMultibyteCharArray()); expect(decoder.decode(input)).toBe('𝓽𝓮𝔁𝓽'); }); it('ignores byte order marker (BOM)', function () { const decoder = new TextDecoder('utf-8', {ignoreBOM: true}); const input = new Uint8Array([ 0xef, 0xbb, 0xbf, ...encodedMultibyteCharArray(), ]); expect(decoder.decode(input)).toBe('𝓽𝓮𝔁𝓽'); }); it('handles invalid byte order marker (BOM)', function () { const decoder = new TextDecoder('utf-8', {ignoreBOM: true}); const input = new Uint8Array([ 0xef, 0xbb, 0x89, ...encodedMultibyteCharArray(), ]); expect(decoder.decode(input)).toBe('ﻉ𝓽𝓮𝔁𝓽'); }); }); describe('UTF-8 Encoding Converter', function () { it('can decode (not fatal)', function () { const decoder = new TextDecoder(); const decoded = decoder.decode(new Uint8Array([120, 193, 120])); expect(decoded).toEqual('x�x'); }); it('can decode (fatal)', function () { const decoder = new TextDecoder(undefined, { fatal: true, }); expect(() => { decoder.decode(new Uint8Array([120, 193, 120])); }).toThrowError( TypeError, /malformed UTF-8 character sequence/ ); }); }); describe('Multi-byte Encoding Converter (iconv)', function () { it('can decode Big-5', function () { const decoder = new TextDecoder('big5'); const bytes = [ 164, 164, 177, 192, 183, 124, 177, 181, 168, 252, 184, 103, 192, 217, 179, 161, 188, 208, 183, 199, 192, 203, 197, 231, 167, 189, 169, 101, 176, 85, ]; const decoded = decoder.decode(new Uint8Array(bytes)); expect(decoded).toEqual('中推會接受經濟部標準檢驗局委託'); }); it('can decode Big-5 with incorrect input bytes', function () { const decoder = new TextDecoder('big5'); const bytes = [ 164, 164, 177, 192, 183, 124, // Invalid byte... 0xa1, ]; const decoded = decoder.decode(new Uint8Array(bytes)); expect(decoded).toEqual('中推會�'); }); it('can decode Big-5 with long incorrect input bytes', function () { const decoder = new TextDecoder('big5'); const bytes = [164, 164, 177, 192, 183, 124]; const baseLength = 1000; const longBytes = new Array(baseLength) .fill(bytes, 0, baseLength) .flat(); // Append invalid byte sequence... longBytes.push(0xa3); const decoded = decoder.decode(new Uint8Array(longBytes)); const baseResult = '中推會'; const longResult = [ ...new Array(baseLength).fill(baseResult, 0, baseLength), '�', ].join(''); expect(decoded).toEqual(longResult); }); it('can decode Big-5 HKSCS with supplemental characters', function () { // The characters below roughly mean 'hard' or 'solid' and // 'rooster' respectively. They were chosen for their Unicode // and HKSCS positioning, not meaning. // Big5-HKSCS bytes for the supplemental character 𠕇 const supplementalBytes = [250, 64]; // Big5-HKSCS bytes for the non-supplemental characters 公雞 const nonSupplementalBytes = [164, 189, 194, 251]; const decoder = new TextDecoder('big5-hkscs'); // We currently allocate 12 additional bytes of padding // and a minimum of 256... // This should produce 400 non-supplemental bytes (50 * 2 * 4) // and 16 supplemental bytes (4 * 4) const repeatedNonSupplementalBytes = new Array(50).fill(nonSupplementalBytes).flat(); const bytes = [ ...repeatedNonSupplementalBytes, ...supplementalBytes, ...repeatedNonSupplementalBytes, ...supplementalBytes, ...repeatedNonSupplementalBytes, ...supplementalBytes, ...repeatedNonSupplementalBytes, ...supplementalBytes, ]; const expectedNonSupplemental = new Array(50).fill('公雞'); const expected = [ ...expectedNonSupplemental, '𠕇', ...expectedNonSupplemental, '𠕇', ...expectedNonSupplemental, '𠕇', ...expectedNonSupplemental, '𠕇', ].join(''); // Calculate the number of bytes the UTF-16 characters should // occupy. const expectedU16Bytes = [...expected].reduce((prev, next) => { const utf16code = next.codePointAt(0); // Test whether this unit is supplemental const additionalBytes = utf16code > 0xFFFF ? 2 : 0; return prev + 2 + additionalBytes; }, 0); // We set a minimum buffer allocation of 256 bytes, // this ensures that this test exceeds that. expect(expectedU16Bytes / 2).toBeGreaterThan(256); // The length of the input bytes should always be less // than the expected output because UTF-16 uses 4 bytes // to represent some characters HKSCS needs only 2 for. expect(bytes.length).toBeLessThan(expectedU16Bytes); // 4 supplemental characters, each with two additional bytes. expect(bytes.length + 4 * 2).toBe(expectedU16Bytes); const decoded = decoder.decode(new Uint8Array(bytes)); expect(decoded).toBe(expected); }); }); describe('Single Byte Encoding Converter', function () { it('can decode legacy single byte encoding (not fatal)', function () { const decoder = new TextDecoder('iso-8859-6'); const decoded = decoder.decode(new Uint8Array([161, 200, 200])); expect(decoded).toEqual('�بب'); }); it('can decode legacy single byte encoding (fatal)', function () { const decoder = new TextDecoder('iso-8859-6', { fatal: true, }); expect(() => { decoder.decode(new Uint8Array([161, 200, 200])); }).toThrowError(TypeError); }); it('can decode ASCII', function () { const input = new Uint8Array([0x89, 0x95, 0x9f, 0xbf]); const decoder = new TextDecoder('ascii'); expect(decoder.decode(input)).toBe('‰•Ÿ¿'); }); // Straight from https://encoding.spec.whatwg.org/encodings.json const encodingsTable = loadJSONFromResource( 'resource:///org/cjs/jsunit/modules/encodings.json' ); const singleByteEncodings = encodingsTable.filter(group => { return group.heading === 'Legacy single-byte encodings'; })[0].encodings; const buffer = new ArrayBuffer(255); const view = new Uint8Array(buffer); for (let i = 0, l = view.byteLength; i < l; i++) view[i] = i; for (let i = 0, l = singleByteEncodings.length; i < l; i++) { const encoding = singleByteEncodings[i]; it(`${encoding.name} can be decoded.`, function () { for (const label of encoding.labels) { const decoder = new TextDecoder(label); expect(() => decoder.decode(view)).not.toThrow(); expect(decoder.encoding).toBe( encoding.name.toLowerCase() ); } }); } }); }); }); cjs-128.0/installed-tests/js/testGLib.js0000664000175000017500000003470314771557763017072 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2011 Giovanni Campagna // SPDX-FileCopyrightText: 2019, 2023 Philip Chimento const GLib = imports.gi.GLib; describe('GVariant constructor', function () { it('constructs a string variant', function () { let strVariant = new GLib.Variant('s', 'mystring'); expect(strVariant.get_string()[0]).toEqual('mystring'); expect(strVariant.deepUnpack()).toEqual('mystring'); }); it('constructs a string variant (backwards compatible API)', function () { let strVariant = new GLib.Variant('s', 'mystring'); let strVariantOld = GLib.Variant.new('s', 'mystring'); expect(strVariant.equal(strVariantOld)).toBeTruthy(); }); it('constructs a struct variant', function () { let structVariant = new GLib.Variant('(sogvau)', [ 'a string', '/a/object/path', 'asig', // nature new GLib.Variant('s', 'variant'), [7, 3], ]); expect(structVariant.n_children()).toEqual(5); let unpacked = structVariant.deepUnpack(); expect(unpacked[0]).toEqual('a string'); expect(unpacked[1]).toEqual('/a/object/path'); expect(unpacked[2]).toEqual('asig'); expect(unpacked[3] instanceof GLib.Variant).toBeTruthy(); expect(unpacked[3].deepUnpack()).toEqual('variant'); expect(Array.isArray(unpacked[4])).toBeTruthy(); expect(unpacked[4].length).toEqual(2); }); it('constructs a maybe variant', function () { let maybeVariant = new GLib.Variant('ms', null); expect(maybeVariant.deepUnpack()).toBeNull(); maybeVariant = new GLib.Variant('ms', 'string'); expect(maybeVariant.deepUnpack()).toEqual('string'); }); it('constructs a byte array variant', function () { const byteArray = new TextEncoder().encode('pizza'); const byteArrayVariant = new GLib.Variant('ay', byteArray); expect(new TextDecoder().decode(byteArrayVariant.deepUnpack())) .toEqual('pizza'); }); it('constructs a byte array variant from a string', function () { const byteArrayVariant = new GLib.Variant('ay', 'pizza'); expect(new TextDecoder().decode(byteArrayVariant.deepUnpack())) .toEqual('pizza\0'); }); it('0-terminates a byte array variant constructed from a string', function () { const byteArrayVariant = new GLib.Variant('ay', 'pizza'); const a = byteArrayVariant.deepUnpack(); [112, 105, 122, 122, 97, 0].forEach((val, ix) => expect(a[ix]).toEqual(val)); }); it('does not 0-terminate a byte array variant constructed from a Uint8Array', function () { const byteArray = new TextEncoder().encode('pizza'); const byteArrayVariant = new GLib.Variant('ay', byteArray); const a = byteArrayVariant.deepUnpack(); [112, 105, 122, 122, 97].forEach((val, ix) => expect(a[ix]).toEqual(val)); }); }); describe('GVariant unpack', function () { let v; beforeEach(function () { v = new GLib.Variant('a{sv}', {foo: new GLib.Variant('s', 'bar')}); }); it('preserves type information if the unpacked object contains variants', function () { expect(v.deepUnpack().foo instanceof GLib.Variant).toBeTruthy(); expect(v.deep_unpack().foo instanceof GLib.Variant).toBeTruthy(); }); it('recursive leaves no variants in the unpacked object', function () { expect(v.recursiveUnpack().foo instanceof GLib.Variant).toBeFalsy(); expect(v.recursiveUnpack().foo).toEqual('bar'); }); }); describe('GVariant strv', function () { let v; beforeEach(function () { v = new GLib.Variant('as', ['a', 'b', 'c', 'foo']); }); it('unpacked matches constructed', function () { expect(v.deepUnpack()).toEqual(['a', 'b', 'c', 'foo']); }); it('getter matches constructed', function () { expect(v.get_strv()).toEqual(['a', 'b', 'c', 'foo']); }); it('getter (dup) matches constructed', function () { expect(v.dup_strv()).toEqual(['a', 'b', 'c', 'foo']); }); }); describe('GVariantDict lookup', function () { let variantDict; beforeEach(function () { variantDict = new GLib.VariantDict(null); variantDict.insert_value('foo', GLib.Variant.new_string('bar')); }); it('returns the unpacked variant', function () { expect(variantDict.lookup('foo')).toEqual('bar'); expect(variantDict.lookup('foo', null)).toEqual('bar'); expect(variantDict.lookup('foo', 's')).toEqual('bar'); expect(variantDict.lookup('foo', new GLib.VariantType('s'))).toEqual('bar'); }); it("returns null if the key isn't present", function () { expect(variantDict.lookup('bar')).toBeNull(); expect(variantDict.lookup('bar', null)).toBeNull(); expect(variantDict.lookup('bar', 's')).toBeNull(); expect(variantDict.lookup('bar', new GLib.VariantType('s'))).toBeNull(); }); }); describe('GLib spawn processes', function () { it('sync with null envp', function () { const [ret, stdout, stderr, exit_status] = GLib.spawn_sync( null, ['true'], null, GLib.SpawnFlags.SEARCH_PATH, null); expect(ret).toBe(true); expect(stdout).toEqual(new Uint8Array()); expect(stderr).toEqual(new Uint8Array()); expect(exit_status).toBe(0); }).pend('https://gitlab.gnome.org/GNOME/glib/-/merge_requests/3523'); }); describe('GLib string function overrides', function () { let numExpectedWarnings; function expectWarnings(count) { numExpectedWarnings = count; for (let c = 0; c < count; c++) { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, '*not introspectable*'); } } function assertWarnings(testName) { for (let c = 0; c < numExpectedWarnings; c++) { GLib.test_assert_expected_messages_internal('Cjs', 'testGLib.js', 0, `test GLib.${testName}`); } numExpectedWarnings = 0; } beforeEach(function () { numExpectedWarnings = 0; }); // TODO: Add Regress.func_not_nullable_untyped_gpointer_in and move to testRegress.js it('GLib.str_hash errors when marshalling null to a not-nullable parameter', function () { // This tests that we don't marshal null to a not-nullable untyped gpointer. expect(() => GLib.str_hash(null)).toThrowError( /Argument [a-z]+ may not be null/ ); }); it('GLib.stpcpy', function () { expect(() => GLib.stpcpy('dest', 'src')).toThrowError(/not introspectable/); }); it('GLib.strstr_len', function () { expectWarnings(4); expect(GLib.strstr_len('haystack', -1, 'needle')).toBeNull(); expect(GLib.strstr_len('haystacks', -1, 'stack')).toEqual('stacks'); expect(GLib.strstr_len('haystacks', 4, 'stack')).toBeNull(); expect(GLib.strstr_len('haystack', 4, 'ays')).toEqual('aystack'); assertWarnings('strstr_len'); }); it('GLib.strrstr', function () { expectWarnings(2); expect(GLib.strrstr('haystack', 'needle')).toBeNull(); expect(GLib.strrstr('hackstacks', 'ack')).toEqual('acks'); assertWarnings('strrstr'); }); it('GLib.strrstr_len', function () { expectWarnings(3); expect(GLib.strrstr_len('haystack', -1, 'needle')).toBeNull(); expect(GLib.strrstr_len('hackstacks', -1, 'ack')).toEqual('acks'); expect(GLib.strrstr_len('hackstacks', 4, 'ack')).toEqual('ackstacks'); assertWarnings('strrstr_len'); }); it('GLib.strup', function () { expectWarnings(1); expect(GLib.strup('string')).toEqual('STRING'); assertWarnings('strup'); }); it('GLib.strdown', function () { expectWarnings(1); expect(GLib.strdown('STRING')).toEqual('string'); assertWarnings('strdown'); }); it('GLib.strreverse', function () { expectWarnings(1); expect(GLib.strreverse('abcdef')).toEqual('fedcba'); assertWarnings('strreverse'); }); it('GLib.ascii_dtostr', function () { expectWarnings(2); expect(GLib.ascii_dtostr('', GLib.ASCII_DTOSTR_BUF_SIZE, Math.PI)) .toEqual('3.141592653589793'); expect(GLib.ascii_dtostr('', 4, Math.PI)).toEqual('3.14'); assertWarnings('ascii_dtostr'); }); it('GLib.ascii_formatd', function () { expect(() => GLib.ascii_formatd('', 8, '%e', Math.PI)).toThrowError(/not introspectable/); }); it('GLib.strchug', function () { expectWarnings(2); expect(GLib.strchug('text')).toEqual('text'); expect(GLib.strchug(' text')).toEqual('text'); assertWarnings('strchug'); }); it('GLib.strchomp', function () { expectWarnings(2); expect(GLib.strchomp('text')).toEqual('text'); expect(GLib.strchomp('text ')).toEqual('text'); assertWarnings('strchomp'); }); it('GLib.strstrip', function () { expectWarnings(4); expect(GLib.strstrip('text')).toEqual('text'); expect(GLib.strstrip(' text')).toEqual('text'); expect(GLib.strstrip('text ')).toEqual('text'); expect(GLib.strstrip(' text ')).toEqual('text'); assertWarnings('strstrip'); }); it('GLib.strdelimit', function () { expectWarnings(4); expect(GLib.strdelimit('1a2b3c4', 'abc', '_'.charCodeAt())).toEqual('1_2_3_4'); expect(GLib.strdelimit('1-2_3<4', null, '|'.charCodeAt())).toEqual('1|2|3|4'); expect(GLib.strdelimit('1a2b3c4', 'abc', '_')).toEqual('1_2_3_4'); expect(GLib.strdelimit('1-2_3<4', null, '|')).toEqual('1|2|3|4'); assertWarnings('strdelimit'); }); it('GLib.strcanon', function () { expectWarnings(2); expect(GLib.strcanon('1a2b3c4', 'abc', '?'.charCodeAt())).toEqual('?a?b?c?'); expect(GLib.strcanon('1a2b3c4', 'abc', '?')).toEqual('?a?b?c?'); assertWarnings('strcanon'); }); it('GLib.base64_encode', function () { const ascii = 'hello\0world'; const base64 = 'aGVsbG8Ad29ybGQ='; expect(GLib.base64_encode(ascii)).toBe(base64); const encoded = new TextEncoder().encode(ascii); expect(GLib.base64_encode(encoded)).toBe(base64); }); }); describe('GLib.MatchInfo', function () { let shouldBePatchedProtoype; beforeAll(function () { shouldBePatchedProtoype = GLib.MatchInfo.prototype; }); let regex; beforeEach(function () { regex = new GLib.Regex('h(?el)lo', 0, 0); }); it('cannot be constructed', function () { expect(() => new GLib.MatchInfo()).toThrow(); expect(() => new shouldBePatchedProtoype.constructor()).toThrow(); }); it('is returned from GLib.Regex.match', function () { const [, match] = regex.match('foo', 0); expect(match).toBeInstanceOf(GLib.MatchInfo); expect(match.toString()).toContain('CjsPrivate.MatchInfo'); }); it('stores the string that was matched', function () { const [, match] = regex.match('foo', 0); expect(match.get_string()).toEqual('foo'); }); it('truncates the string when it has zeroes as g_match_info_get_string() would', function () { const [, match] = regex.match_full('ab\0cd', 0, 0); expect(match.get_string()).toEqual('ab'); }); it('is returned from GLib.Regex.match_all', function () { const [, match] = regex.match_all('foo', 0); expect(match).toBeInstanceOf(GLib.MatchInfo); expect(match.toString()).toContain('CjsPrivate.MatchInfo'); }); it('is returned from GLib.Regex.match_all_full', function () { const [, match] = regex.match_all_full('foo', 0, 0); expect(match).toBeInstanceOf(GLib.MatchInfo); expect(match.toString()).toContain('CjsPrivate.MatchInfo'); }); it('is returned from GLib.Regex.match_full', function () { const [, match] = regex.match_full('foo', 0, 0); expect(match).toBeInstanceOf(GLib.MatchInfo); expect(match.toString()).toContain('CjsPrivate.MatchInfo'); }); describe('method', function () { let match; beforeEach(function () { [, match] = regex.match('hello hello world', 0); }); it('expand_references', function () { expect(match.expand_references('\\0-\\1')).toBe('hello-el'); expect(shouldBePatchedProtoype.expand_references.call(match, '\\0-\\1')).toBe('hello-el'); }); it('fetch', function () { expect(match.fetch(0)).toBe('hello'); expect(shouldBePatchedProtoype.fetch.call(match, 0)).toBe('hello'); }); it('fetch_all', function () { expect(match.fetch_all()).toEqual(['hello', 'el']); expect(shouldBePatchedProtoype.fetch_all.call(match)).toEqual(['hello', 'el']); }); it('fetch_named', function () { expect(match.fetch_named('foo')).toBe('el'); expect(shouldBePatchedProtoype.fetch_named.call(match, 'foo')).toBe('el'); }); it('fetch_named_pos', function () { expect(match.fetch_named_pos('foo')).toEqual([true, 1, 3]); expect(shouldBePatchedProtoype.fetch_named_pos.call(match, 'foo')).toEqual([true, 1, 3]); }); it('fetch_pos', function () { expect(match.fetch_pos(1)).toEqual([true, 1, 3]); expect(shouldBePatchedProtoype.fetch_pos.call(match, 1)).toEqual([true, 1, 3]); }); it('get_match_count', function () { expect(match.get_match_count()).toBe(2); expect(shouldBePatchedProtoype.get_match_count.call(match)).toBe(2); }); it('get_string', function () { expect(match.get_string()).toBe('hello hello world'); expect(shouldBePatchedProtoype.get_string.call(match)).toBe('hello hello world'); }); it('is_partial_match', function () { expect(match.is_partial_match()).toBeFalse(); expect(shouldBePatchedProtoype.is_partial_match.call(match)).toBeFalse(); }); it('matches', function () { expect(match.matches()).toBeTrue(); expect(shouldBePatchedProtoype.matches.call(match)).toBeTrue(); }); it('next', function () { expect(match.next()).toBeTrue(); expect(shouldBePatchedProtoype.next.call(match)).toBeFalse(); }); }); }); cjs-128.0/installed-tests/js/testPromise.js0000664000175000017500000000616614771557763017675 0ustar fabiofabio// -*- mode: js; indent-tabs-mode: nil -*- // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileContributor: Authored by: Marco Trevisan // SPDX-FileCopyrightText: 2022 Canonical, Ltd. const {GLib} = imports.gi; class SubPromise extends Promise { constructor(executor) { super((resolve, reject) => { executor(resolve, reject); }); } } const PromiseResult = { RESOLVED: 0, FAILED: 1, }; describe('Promise', function () { let loop; beforeEach(function () { loop = GLib.MainLoop.new(null, false); }); function executePromise(promise) { const promiseResult = {}; promise.then(value => { promiseResult.result = PromiseResult.RESOLVED; promiseResult.value = value; }).catch(e => { promiseResult.result = PromiseResult.FAILED; promiseResult.error = e; }); while (promiseResult.result === undefined) loop.get_context().iteration(true); return promiseResult; } it('waits for all promises before handling unhandled, when handled', function () { let error; let resolved; const promise = async () => { await new SubPromise(resolve => resolve('success')); const rejecting = new SubPromise((_resolve, reject) => reject('got error')); try { await rejecting; } catch (e) { error = e; } finally { resolved = true; } expect(resolved).toBeTrue(); expect(error).toBe('got error'); return 'parent-returned'; }; expect(executePromise(promise())).toEqual({ result: PromiseResult.RESOLVED, value: 'parent-returned', }); }); it('waits for all promises before handling unhandled, when unhandled', function () { const thenHandler = jasmine.createSpy('thenHandler'); const promise = async () => { await new Promise(resolve => resolve('success')); await new Promise((_resolve, reject) => reject(new Error('got error'))); return 'parent-returned'; }; promise().then(thenHandler).catch(); expect(thenHandler).not.toHaveBeenCalled(); GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => loop.quit()); GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'Unhandled promise rejection.*'); loop.run(); GLib.test_assert_expected_messages_internal('Cjs', 'testPromise.js', 0, 'warnsIfRejected'); }); it('do not lead to high-priority IDLE starvation', function () { const promise = new Promise(resolve => { const id = GLib.idle_add(GLib.PRIORITY_HIGH, () => { resolve(); return GLib.SOURCE_REMOVE; }); GLib.Source.set_name_by_id(id, `Test Idle source ${id}`); }); expect(executePromise(promise)).toEqual({ result: PromiseResult.RESOLVED, value: undefined, }); }); }); cjs-128.0/installed-tests/js/testPackage.js0000664000175000017500000000605114771557763017603 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2017 Red Hat, Inc. 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); }); }); cjs-128.0/installed-tests/js/testImporter.js0000664000175000017500000002354214771557763020055 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2008 litl, LLC describe('GI importer', function () { it('can import GI modules', function () { var GLib = imports.gi.GLib; expect(GLib.MAJOR_VERSION).toEqual(2); }); describe('on failure', function () { // For these tests, we provide special overrides files to sabotage the // import, at the path resource:///org/cjs/jsunit/modules/badOverrides. let oldSearchPath; beforeAll(function () { oldSearchPath = imports.overrides.searchPath.slice(); imports.overrides.searchPath = ['resource:///org/cjs/jsunit/modules/badOverrides']; }); afterAll(function () { imports.overrides.searchPath = oldSearchPath; }); it("throws an exception when the overrides file can't be imported", function () { expect(() => imports.gi.WarnLib).toThrowError(SyntaxError); }); it('throws an exception when the overrides import throws one', function () { expect(() => imports.gi.GIMarshallingTests).toThrow('💩'); }); it('throws an exception when the overrides _init throws one', function () { expect(() => imports.gi.Regress).toThrow('💩'); }); it('throws an exception when the overrides _init is a primitive', function () { expect(() => imports.gi.Gio).toThrowError(/_init/); }); }); }); // Jasmine v3 often uses duck-typing (checking for a property to determine a type) to pretty print objects. // Unfortunately, checking for jasmineToString and other properties causes our importer objects to throw when resolving. // Luckily, we can override the default behavior with a custom formatter. function formatImporter(obj) { if (typeof obj === 'object' && obj.toString && (obj.toString()?.startsWith('[object GjsModule') || obj.toString()?.startsWith('[GjsFileImporter '))) return obj.toString(); return undefined; } describe('Importer', function () { let oldSearchPath; let foobar, subA, subB, subFoobar; beforeAll(function () { oldSearchPath = imports.searchPath.slice(); imports.searchPath = ['resource:///org/cjs/jsunit/modules']; foobar = imports.foobar; subA = imports.subA; subB = imports.subA.subB; subFoobar = subB.foobar; }); afterAll(function () { imports.searchPath = oldSearchPath; }); beforeEach(function () { jasmine.addCustomObjectFormatter(formatImporter); }); it('is on the global object (backwards compatibility)', function () { expect(imports instanceof globalThis.GjsFileImporter).toBeTruthy(); }); it('is abstract', function () { expect(() => new globalThis.GjsFileImporter()).toThrow(); }); it('exists', function () { expect(imports).toBeDefined(); }); it('has a toString representation', function () { expect(imports.toString()).toEqual('[GjsFileImporter root]'); expect(subA.toString()).toEqual('[GjsFileImporter subA]'); }); it('throws an import error when trying to import a nonexistent module', function () { expect(() => imports.nonexistentModuleName) .toThrow(jasmine.objectContaining({name: 'ImportError'})); }); it('throws an error when evaluating the module file throws an error', function () { expect(() => imports.alwaysThrows).toThrow(); // Try again to make sure that we properly discarded the module object expect(() => imports.alwaysThrows).toThrow(); }); it('can import a module', function () { expect(foobar).toBeDefined(); expect(foobar.foo).toEqual('This is foo'); expect(foobar.bar).toEqual('This is bar'); }); it('can import a module with a toString property', function () { expect(foobar.testToString('foo')).toEqual('foo'); }); it('makes deleting the import a no-op', function () { expect(delete imports.foobar).toBeFalsy(); expect(imports.foobar).toBe(foobar); }); it('gives the same object when importing a second time', function () { foobar.somethingElse = 'Should remain'; const foobar2 = imports.foobar; expect(foobar2.somethingElse).toEqual('Should remain'); }); it('can import a submodule', function () { expect(subB).toBeDefined(); expect(subFoobar).toBeDefined(); expect(subFoobar.foo).toEqual('This is foo'); expect(subFoobar.bar).toEqual('This is bar'); }); it('imports modules with a toString representation', function () { expect(Object.prototype.toString.call(foobar)) .toEqual('[object GjsModule foobar]'); expect(subFoobar.toString()) .toEqual('[object GjsModule subA.subB.foobar]'); }); it('does not share the same object for a module on a different path', function () { foobar.somethingElse = 'Should remain'; expect(subFoobar.somethingElse).not.toBeDefined(); }); it('gives the same object when importing a submodule a second time', function () { subFoobar.someProp = 'Should be here'; const subFoobar2 = imports.subA.subB.foobar; expect(subFoobar2.someProp).toEqual('Should be here'); }); it('has no meta properties on the toplevel importer', function () { expect(imports.__moduleName__).toBeNull(); expect(imports.__parentModule__).toBeNull(); }); it('sets the names of imported modules', function () { expect(subA.__moduleName__).toEqual('subA'); expect(subB.__moduleName__).toEqual('subB'); }); it('gives a module the importer object as parent module', function () { expect(subA.__parentModule__).toBe(imports); }); it('gives a submodule the module as parent module', function () { expect(subB.__parentModule__).toBe(subA); }); // We want to check that the copy of the 'a' module imported directly // is the same as the copy that 'b' imports, and that we don't have two // copies because of the A imports B imports A loop. it('does not make a separate copy of a module imported in two places', function () { let A = imports.mutualImport.a; A.incrementCount(); expect(A.getCount()).toEqual(1); expect(A.getCountViaB()).toEqual(1); }); it('evaluates an __init__.js file in an imported directory', function () { expect(subB.testImporterFunction()).toEqual('__init__ function tested'); }); it('throws on an __init__.js file with a syntax error', function () { expect(() => imports.subBadInit.SOMETHING).toThrowError(SyntaxError); }); it('throws when an __init__.js throws an error', function () { expect(() => imports.subErrorInit.SOMETHING).toThrowError('a bad init!'); }); it('accesses a class defined in an __init__.js file', function () { let o = new subB.ImporterClass(); expect(o).not.toBeNull(); expect(o.testMethod()).toEqual('__init__ class tested'); }); it('can import a file encoded in UTF-8', function () { const ModUnicode = imports.modunicode; expect(ModUnicode.uval).toEqual('const \u2665 utf8'); }); describe("properties defined in the module's lexical scope", function () { let LexicalScope; beforeAll(function () { globalThis.expectMe = true; LexicalScope = imports.lexicalScope; }); it('will log a compatibility warning when accessed', function () { const GLib = imports.gi.GLib; GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, "Some code accessed the property 'b' on the module " + "'lexicalScope'.*"); GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, "Some code accessed the property 'c' on the module " + "'lexicalScope'.*"); void LexicalScope.b; void LexicalScope.c; // g_test_assert_expected_messages() is a macro, not introspectable GLib.test_assert_expected_messages_internal('Cjs', 'testImporter.js', 179, ''); }); it('can be accessed', function () { expect(LexicalScope.a).toEqual(1); expect(LexicalScope.b).toEqual(2); expect(LexicalScope.c).toEqual(3); expect(LexicalScope.d).toEqual(4); }); it('does not leak module properties into the global scope', function () { expect(globalThis.d).not.toBeDefined(); }); }); describe('enumerating modules', function () { let keys; beforeEach(function () { keys = []; for (let key in imports) keys.push(key); }); it('gets all of them', function () { expect(keys).toContain('foobar', 'subA', 'mutualImport', 'modunicode'); }); it('includes modules that throw on import', function () { expect(keys).toContain('alwaysThrows'); }); it('does not include meta properties', function () { expect(keys).not.toContain('__parentModule__'); expect(keys).not.toContain('__moduleName__'); expect(keys).not.toContain('searchPath'); }); }); it("doesn't crash when resolving a non-string property", function () { expect(imports[0]).not.toBeDefined(); expect(imports.foobar[0]).not.toBeDefined(); }); it('scripts support relative dynamic imports', async function () { const {say} = await import('./modules/say.js'); expect(typeof say).toBe('function'); expect(say('hello')).toBe('<( hello )'); }); it('imported scripts support relative dynamic imports', async function () { const response = await imports.dynamic.test(); expect(response).toBe('<( I did it! )'); }); }); cjs-128.0/installed-tests/js/testGlobal.js0000664000175000017500000000220014771557763017440 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2022 Evan Welsh describe('globalThis', () => { function itIsDefined(value, message) { it(`${message ? `${message} ` : ''}is defined`, function () { expect(value).toBeDefined(); }); } it('is equal to window', function () { expect(globalThis.window).toBe(globalThis); expect(window.globalThis).toBe(globalThis); }); describe('WeakRef', () => { itIsDefined(globalThis.WeakRef); }); describe('console', () => { itIsDefined(globalThis.console); }); describe('TextEncoder', () => { itIsDefined(globalThis.TextEncoder); }); describe('TextDecoder', () => { itIsDefined(globalThis.TextDecoder); }); describe('ARGV', () => { itIsDefined(globalThis.ARGV); }); describe('print function', () => { itIsDefined(globalThis.log, 'log'); itIsDefined(globalThis.print, 'print'); itIsDefined(globalThis.printerr, 'printerr'); itIsDefined(globalThis.logError, 'logError'); }); }); cjs-128.0/installed-tests/js/testSignals.js0000664000175000017500000001700214771557763017646 0ustar fabiofabio/* eslint-disable no-restricted-properties */ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2008 litl, LLC const GLib = imports.gi.GLib; const Lang = imports.lang; const Signals = imports.signals; const Foo = new Lang.Class({ Name: 'Foo', Implements: [Signals.WithSignals], _init() {}, }); describe('Legacy object with signals', function () { testSignals(Foo); }); class FooWithoutSignals {} Signals.addSignalMethods(FooWithoutSignals.prototype); describe('Object with signals added', function () { testSignals(FooWithoutSignals); }); function testSignals(klass) { let foo, bar; beforeEach(function () { foo = new klass(); bar = jasmine.createSpy('bar'); }); it('emit works with no connections', function () { expect(() => foo.emit('random-event')).not.toThrow(); }); ['connect', 'connectAfter'].forEach(connectMethod => { describe(`using ${connectMethod}`, function () { it('calls a signal handler when a signal is emitted', function () { foo[connectMethod]('bar', bar); foo.emit('bar', 'This is a', 'This is b'); expect(bar).toHaveBeenCalledWith(foo, 'This is a', 'This is b'); }); it('calls remaining handlers after one is disconnected', function () { const id1 = foo[connectMethod]('bar', bar); const bar2 = jasmine.createSpy('bar2'); const id2 = foo[connectMethod]('bar', bar2); foo.emit('bar'); expect(bar).toHaveBeenCalledTimes(1); expect(bar2).toHaveBeenCalledTimes(1); foo.disconnect(id1); foo.emit('bar'); expect(bar).toHaveBeenCalledTimes(1); expect(bar2).toHaveBeenCalledTimes(2); foo.disconnect(id2); }); it('does not call a signal handler after the signal is disconnected', function () { let id = foo[connectMethod]('bar', bar); foo.emit('bar', 'This is a', 'This is b'); bar.calls.reset(); foo.disconnect(id); // this emission should do nothing foo.emit('bar', 'Another a', 'Another b'); expect(bar).not.toHaveBeenCalled(); }); it('can disconnect a signal handler during signal emission', function () { var toRemove = []; let firstId = foo[connectMethod]('bar', function (theFoo) { theFoo.disconnect(toRemove[0]); theFoo.disconnect(toRemove[1]); }); toRemove.push(foo[connectMethod]('bar', bar)); toRemove.push(foo[connectMethod]('bar', bar)); // emit signal; what should happen is that the second two handlers are // disconnected before they get invoked foo.emit('bar'); expect(bar).not.toHaveBeenCalled(); // clean up the last handler foo.disconnect(firstId); expect(() => foo.disconnect(firstId)).toThrowError( `No signal connection ${firstId} found`); // poke in private implementation to verify no handlers left expect(Object.keys(foo._signalConnections).length).toEqual(0); }); it('distinguishes multiple signals', function () { let bonk = jasmine.createSpy('bonk'); foo[connectMethod]('bar', bar); foo[connectMethod]('bonk', bonk); foo[connectMethod]('bar', bar); foo.emit('bar'); expect(bar).toHaveBeenCalledTimes(2); expect(bonk).not.toHaveBeenCalled(); foo.emit('bonk'); expect(bar).toHaveBeenCalledTimes(2); expect(bonk).toHaveBeenCalledTimes(1); foo.emit('bar'); expect(bar).toHaveBeenCalledTimes(4); expect(bonk).toHaveBeenCalledTimes(1); foo.disconnectAll(); bar.calls.reset(); bonk.calls.reset(); // these post-disconnect emissions should do nothing foo.emit('bar'); foo.emit('bonk'); expect(bar).not.toHaveBeenCalled(); expect(bonk).not.toHaveBeenCalled(); }); it('determines if a signal is connected on a JS object', function () { let id = foo[connectMethod]('bar', bar); expect(foo.signalHandlerIsConnected(id)).toEqual(true); foo.disconnect(id); expect(foo.signalHandlerIsConnected(id)).toEqual(false); }); it('does not call a subsequent connected callbacks if stopped by earlier', function () { const afterBar = jasmine.createSpy('bar'); const afterAfterBar = jasmine.createSpy('barBar'); foo[connectMethod]('bar', bar.and.returnValue(true)); foo[connectMethod]('bar', afterBar); foo[connectMethod]('bar', afterAfterBar); foo.emit('bar', 'This is a', 123); expect(bar).toHaveBeenCalledWith(foo, 'This is a', 123); expect(afterBar).not.toHaveBeenCalled(); expect(afterAfterBar).not.toHaveBeenCalled(); }); describe('with exception in signal handler', function () { let bar2; beforeEach(function () { bar.and.throwError('Exception we are throwing on purpose'); bar2 = jasmine.createSpy('bar'); foo[connectMethod]('bar', bar); foo[connectMethod]('bar', bar2); GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: Exception in callback for signal: *'); foo.emit('bar'); }); it('does not affect other callbacks', function () { expect(bar).toHaveBeenCalledTimes(1); expect(bar2).toHaveBeenCalledTimes(1); }); it('does not disconnect the callback', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'JS ERROR: Exception in callback for signal: *'); foo.emit('bar'); expect(bar).toHaveBeenCalledTimes(2); expect(bar2).toHaveBeenCalledTimes(2); }); }); }); }); it('using connectAfter calls a signal handler later than when using connect when a signal is emitted', function () { const afterBar = jasmine.createSpy('bar'); foo.connectAfter('bar', (...args) => { expect(bar).toHaveBeenCalledWith(foo, 'This is a', 'This is b'); afterBar(...args); }); foo.connect('bar', bar); foo.emit('bar', 'This is a', 'This is b'); expect(afterBar).toHaveBeenCalledWith(foo, 'This is a', 'This is b'); }); it('does not call a connected after handler when stopped by connect', function () { const afterBar = jasmine.createSpy('bar'); foo.connectAfter('bar', afterBar); foo.connect('bar', bar.and.returnValue(true)); foo.emit('bar', 'This is a', 'This is b'); expect(bar).toHaveBeenCalledWith(foo, 'This is a', 'This is b'); expect(afterBar).not.toHaveBeenCalled(); }); } cjs-128.0/installed-tests/js/matchers.js0000664000175000017500000000406514771557763017161 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2021 Evan Welsh /** * A jasmine asymmetric matcher which expects an array-like object * to contain the given element array in the same order with the * same length. Useful for testing typed arrays. * * @template T * @param {T[]} elements an array of elements to compare with * @returns */ export function arrayLikeWithExactContents(elements) { return { /** * @param {ArrayLike} compareTo an array-like object to compare to * @returns {boolean} */ asymmetricMatch(compareTo) { return ( compareTo.length === elements.length && elements.every((e, i) => e === compareTo[i]) ); }, /** * @returns {string} */ jasmineToString() { return `)`; }, }; } /** * A jasmine asymmetric matcher which compares a given string to an * array-like object of bytes. The compared bytes are decoded using * TextDecoder and then compared using jasmine.stringMatching. * * @param {string | RegExp} text the text or regular expression to compare decoded bytes to * @param {string} [encoding] the encoding of elements * @returns */ export function decodedStringMatching(text, encoding = 'utf-8') { const matcher = jasmine.stringMatching(text); return { /** * @param {ArrayLike} compareTo an array of bytes to decode and compare to * @returns {boolean} */ asymmetricMatch(compareTo) { const decoder = new TextDecoder(encoding); const decoded = decoder.decode(new Uint8Array(Array.from(compareTo))); return matcher.asymmetricMatch(decoded, []); }, /** * @returns {string} */ jasmineToString() { return ``; }, }; } cjs-128.0/installed-tests/js/testGObjectDestructionAccess.js0000664000175000017500000007055414771557763023144 0ustar fabiofabio// -*- mode: js; indent-tabs-mode: nil -*- // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2017 Canonical, Ltd. imports.gi.versions.Gtk = '3.0'; const {GLib, Gio, CjsTestTools, GObject, Gtk} = imports.gi; const {system: System} = imports; describe('Access to destroyed GObject', function () { let destroyedWindow; beforeAll(function () { Gtk.init(null); }); beforeEach(function () { destroyedWindow = new Gtk.Window({type: Gtk.WindowType.TOPLEVEL}); destroyedWindow.set_title('To be destroyed'); destroyedWindow.destroy(); }); it('Get property', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'Object Gtk.Window (0x* disposed *'); expect(destroyedWindow.title).toBe('To be destroyed'); GLib.test_assert_expected_messages_internal('Cjs', 'testGObjectDestructionAccess.js', 0, 'testExceptionInDestroyedObjectPropertyGet'); }); it('Set property', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'Object Gtk.Window (0x* disposed *'); GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'Object Gtk.Window (0x* disposed *'); destroyedWindow.title = 'I am dead'; expect(destroyedWindow.title).toBe('I am dead'); GLib.test_assert_expected_messages_internal('Cjs', 'testGObjectDestructionAccess.js', 0, 'testExceptionInDestroyedObjectPropertySet'); }); it('Add expando property', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'Object Gtk.Window (0x* disposed *'); destroyedWindow.expandoProperty = 'Hello!'; GLib.test_assert_expected_messages_internal('Cjs', 'testGObjectDestructionAccess.js', 0, 'testExceptionInDestroyedObjectExpandoPropertySet'); }); it('Access to unset expando property', function () { expect(destroyedWindow.expandoProperty).toBeUndefined(); }); it('Access previously set expando property', function () { destroyedWindow = new Gtk.Window({type: Gtk.WindowType.TOPLEVEL}); destroyedWindow.expandoProperty = 'Hello!'; destroyedWindow.destroy(); expect(destroyedWindow.expandoProperty).toBe('Hello!'); }); it('Access to getter method', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'Object Gtk.Window (0x* disposed *'); expect(destroyedWindow.get_title()).toBe('To be destroyed'); GLib.test_assert_expected_messages_internal('Cjs', 'testGObjectDestructionAccess.js', 0, 'testExceptionInDestroyedObjectMethodGet'); }); it('Access to setter method', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'Object Gtk.Window (0x* disposed *'); GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'Object Gtk.Window (0x* disposed *'); destroyedWindow.set_title('I am dead'); expect(destroyedWindow.get_title()).toBe('I am dead'); GLib.test_assert_expected_messages_internal('Cjs', 'testGObjectDestructionAccess.js', 0, 'testExceptionInDestroyedObjectMethodSet'); }); it('Proto function connect', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'Object Gtk.Window (0x* disposed *'); expect(destroyedWindow.connect('foo-signal', () => {})).toBe(0); GLib.test_assert_expected_messages_internal('Cjs', 'testGObjectDestructionAccess.js', 0, 'testExceptionInDestroyedObjectConnect'); }); it('Proto function connect_after', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'Object Gtk.Window (0x* disposed *'); expect(destroyedWindow.connect_after('foo-signal', () => {})).toBe(0); GLib.test_assert_expected_messages_internal('Cjs', 'testGObjectDestructionAccess.js', 0, 'testExceptionInDestroyedObjectConnectAfter'); }); it('Proto function emit', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'Object Gtk.Window (0x* disposed *'); expect(destroyedWindow.emit('keys-changed')).toBeUndefined(); GLib.test_assert_expected_messages_internal('Cjs', 'testGObjectDestructionAccess.js', 0, 'testExceptionInDestroyedObjectEmit'); }); it('Proto function signals_disconnect', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'Object Gtk.Window (0x* disposed *'); expect(GObject.signal_handlers_disconnect_by_func(destroyedWindow, () => {})).toBe(0); GLib.test_assert_expected_messages_internal('Cjs', 'testGObjectDestructionAccess.js', 0, 'testExceptionInDestroyedObjectSignalsDisconnect'); }); it('Proto function signals_block', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'Object Gtk.Window (0x* disposed *'); expect(GObject.signal_handlers_block_by_func(destroyedWindow, () => {})).toBe(0); GLib.test_assert_expected_messages_internal('Cjs', 'testGObjectDestructionAccess.js', 0, 'testExceptionInDestroyedObjectSignalsBlock'); }); it('Proto function signals_unblock', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'Object Gtk.Window (0x* disposed *'); expect(GObject.signal_handlers_unblock_by_func(destroyedWindow, () => {})).toBe(0); GLib.test_assert_expected_messages_internal('Cjs', 'testGObjectDestructionAccess.js', 0, 'testExceptionInDestroyedObjectSignalsUnblock'); }); it('Proto function toString', function () { expect(destroyedWindow.toString()).toMatch( /\[object \(DISPOSED\) instance wrapper GIName:Gtk.Window jsobj@0x[a-f0-9]+ native@0x[a-f0-9]+\]/); }); it('Proto function toString before/after', function () { var validWindow = new Gtk.Window({type: Gtk.WindowType.TOPLEVEL}); expect(validWindow.toString()).toMatch( /\[object instance wrapper GIName:Gtk.Window jsobj@0x[a-f0-9]+ native@0x[a-f0-9]+\]/); validWindow.destroy(); expect(validWindow.toString()).toMatch( /\[object \(DISPOSED\) instance wrapper GIName:Gtk.Window jsobj@0x[a-f0-9]+ native@0x[a-f0-9]+\]/); }); }); describe('Access to finalized GObject', function () { let destroyedWindow; beforeAll(function () { Gtk.init(null); }); beforeEach(function () { destroyedWindow = new Gtk.Window({type: Gtk.WindowType.TOPLEVEL}); destroyedWindow.set_title('To be destroyed'); destroyedWindow.destroy(); GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'Object Gtk.Window (0x* disposed *'); CjsTestTools.unref(destroyedWindow); GLib.test_assert_expected_messages_internal('Cjs', 'testGObjectDestructionAccess.js', 0, 'testExceptionInDestroyedObjectPropertyGet'); }); afterEach(function () { destroyedWindow = null; GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, '*Object 0x* has been finalized *'); System.gc(); GLib.test_assert_expected_messages_internal('Cjs', 'testGObjectDestructionAccess.js', 0, 'generates a warn on object garbage collection'); }); it('Get property', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'Object Gtk.Window (0x* finalized *'); expect(destroyedWindow.title).toBeUndefined(); GLib.test_assert_expected_messages_internal('Cjs', 'testGObjectDestructionAccess.js', 0, 'testExceptionInDestroyedObjectPropertyGet'); }); it('Set property', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'Object Gtk.Window (0x* finalized *'); GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'Object Gtk.Window (0x* finalized *'); destroyedWindow.title = 'I am dead'; expect(destroyedWindow.title).toBeUndefined(); GLib.test_assert_expected_messages_internal('Cjs', 'testGObjectDestructionAccess.js', 0, 'testExceptionInDestroyedObjectPropertySet'); }); it('Add expando property', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'Object Gtk.Window (0x* finalized *'); destroyedWindow.expandoProperty = 'Hello!'; GLib.test_assert_expected_messages_internal('Cjs', 'testGObjectDestructionAccess.js', 0, 'testExceptionInDestroyedObjectExpandoPropertySet'); }); it('Access to unset expando property', function () { expect(destroyedWindow.expandoProperty).toBeUndefined(); }); it('Access previously set expando property', function () { destroyedWindow = new Gtk.Window({type: Gtk.WindowType.TOPLEVEL}); destroyedWindow.expandoProperty = 'Hello!'; destroyedWindow.destroy(); expect(destroyedWindow.expandoProperty).toBe('Hello!'); }); it('Access to getter method', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'Object Gtk.Window (0x* finalized *'); GLib.test_expect_message('Gtk', GLib.LogLevelFlags.LEVEL_CRITICAL, '*GTK_IS_WINDOW*'); expect(destroyedWindow.get_title()).toBeNull(); GLib.test_assert_expected_messages_internal('Cjs', 'testGObjectDestructionAccess.js', 0, 'testExceptionInDestroyedObjectMethodGet'); }); it('Access to setter method', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'Object Gtk.Window (0x* finalized *'); GLib.test_expect_message('Gtk', GLib.LogLevelFlags.LEVEL_CRITICAL, '*GTK_IS_WINDOW*'); destroyedWindow.set_title('I am dead'); GLib.test_assert_expected_messages_internal('Cjs', 'testGObjectDestructionAccess.js', 0, 'testExceptionInDestroyedObjectMethodSet'); }); it('Proto function connect', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'Object Gtk.Window (0x* finalized *'); expect(destroyedWindow.connect('foo-signal', () => { })).toBe(0); GLib.test_assert_expected_messages_internal('Cjs', 'testGObjectDestructionAccess.js', 0, 'testExceptionInDestroyedObjectConnect'); }); it('Proto function connect_after', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'Object Gtk.Window (0x* finalized *'); expect(destroyedWindow.connect_after('foo-signal', () => { })).toBe(0); GLib.test_assert_expected_messages_internal('Cjs', 'testGObjectDestructionAccess.js', 0, 'testExceptionInDestroyedObjectConnectAfter'); }); it('Proto function emit', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'Object Gtk.Window (0x* finalized *'); expect(destroyedWindow.emit('keys-changed')).toBeUndefined(); GLib.test_assert_expected_messages_internal('Cjs', 'testGObjectDestructionAccess.js', 0, 'testExceptionInDestroyedObjectEmit'); }); it('Proto function signals_disconnect', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'Object Gtk.Window (0x* finalized *'); expect(GObject.signal_handlers_disconnect_by_func(destroyedWindow, () => { })).toBe(0); GLib.test_assert_expected_messages_internal('Cjs', 'testGObjectDestructionAccess.js', 0, 'testExceptionInDestroyedObjectSignalsDisconnect'); }); it('Proto function signals_block', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'Object Gtk.Window (0x* finalized *'); expect(GObject.signal_handlers_block_by_func(destroyedWindow, () => { })).toBe(0); GLib.test_assert_expected_messages_internal('Cjs', 'testGObjectDestructionAccess.js', 0, 'testExceptionInDestroyedObjectSignalsBlock'); }); it('Proto function signals_unblock', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'Object Gtk.Window (0x* finalized *'); expect(GObject.signal_handlers_unblock_by_func(destroyedWindow, () => { })).toBe(0); GLib.test_assert_expected_messages_internal('Cjs', 'testGObjectDestructionAccess.js', 0, 'testExceptionInDestroyedObjectSignalsUnblock'); }); it('Proto function toString', function () { expect(destroyedWindow.toString()).toMatch( /\[object \(FINALIZED\) instance wrapper GIName:Gtk.Window jsobj@0x[a-f0-9]+ native@0x[a-f0-9]+\]/); }); }); describe('Disposed or finalized GObject', function () { beforeAll(function () { CjsTestTools.init(); }); afterEach(function () { CjsTestTools.reset(); }); [true, false].forEach(gc => { it(`is marked as disposed when it is a manually disposed property ${gc ? '' : 'not '}garbage collected`, function () { const emblem = new Gio.EmblemedIcon({ gicon: new Gio.ThemedIcon({name: 'alarm'}), }); let {gicon} = emblem; gicon.run_dispose(); gicon = null; System.gc(); Array(10).fill().forEach(() => { // We need to repeat the test to ensure that we disassociate // wrappers from disposed objects on destruction. gicon = emblem.gicon; expect(gicon.toString()).toMatch( /\[object \(DISPOSED\) instance wrapper .* jsobj@0x[a-f0-9]+ native@0x[a-f0-9]+\]/); gicon = null; if (gc) System.gc(); }); }); }); it('calls dispose vfunc on explicit disposal only', function () { const callSpy = jasmine.createSpy('vfunc_dispose'); const DisposeFile = GObject.registerClass(class DisposeFile extends Gio.ThemedIcon { vfunc_dispose(...args) { expect(this.names).toEqual(['dummy']); callSpy(...args); } }); let file = new DisposeFile({name: 'dummy'}); file.run_dispose(); expect(callSpy).toHaveBeenCalledOnceWith(); file.run_dispose(); expect(callSpy).toHaveBeenCalledTimes(2); file = null; GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, '*during garbage collection*offending callback was dispose()*'); System.gc(); GLib.test_assert_expected_messages_internal('Cjs', 'testGObjectDestructionAccess.js', 0, 'calls dispose vfunc on explicit disposal only'); expect(callSpy).toHaveBeenCalledTimes(2); }); it('generates a warn on object garbage collection', function () { CjsTestTools.unref(Gio.File.new_for_path('/')); GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, '*Object 0x* has been finalized *'); System.gc(); GLib.test_assert_expected_messages_internal('Cjs', 'testGObjectDestructionAccess.js', 0, 'generates a warn on object garbage collection'); }); it('generates a warn on object garbage collection if has expando property', function () { let file = Gio.File.new_for_path('/'); file.toggleReferenced = true; CjsTestTools.unref(file); expect(file.toString()).toMatch( /\[object \(FINALIZED\) instance wrapper GType:GLocalFile jsobj@0x[a-f0-9]+ native@0x[a-f0-9]+\]/); file = null; GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, '*Object 0x* has been finalized *'); System.gc(); GLib.test_assert_expected_messages_internal('Cjs', 'testGObjectDestructionAccess.js', 0, 'generates a warn on object garbage collection if has expando property'); }); it('generates a warn if already disposed at garbage collection', function () { const loop = new GLib.MainLoop(null, false); let file = Gio.File.new_for_path('/'); CjsTestTools.delayed_unref(file, 1); // Will happen after dispose file.run_dispose(); let done = false; GLib.timeout_add(GLib.PRIORITY_DEFAULT, 50, () => (done = true)); while (!done) loop.get_context().iteration(true); file = null; GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, '*Object 0x* has been finalized *'); System.gc(); GLib.test_assert_expected_messages_internal('Cjs', 'testGObjectDestructionAccess.js', 0, 'generates a warn if already disposed at garbage collection'); }); [true, false].forEach(gc => { it(`created from other function is marked as disposed and ${gc ? '' : 'not '}garbage collected`, function () { let file = Gio.File.new_for_path('/'); CjsTestTools.save_object(file); file.run_dispose(); file = null; System.gc(); Array(10).fill().forEach(() => { // We need to repeat the test to ensure that we disassociate // wrappers from disposed objects on destruction. expect(CjsTestTools.peek_saved()).toMatch( /\[object \(DISPOSED\) instance wrapper GType:GLocalFile jsobj@0x[a-f0-9]+ native@0x[a-f0-9]+\]/); if (gc) System.gc(); }); }); }); it('returned from function is marked as disposed', function () { expect(CjsTestTools.get_disposed(Gio.File.new_for_path('/'))).toMatch( /\[object \(DISPOSED\) instance wrapper GType:GLocalFile jsobj@0x[a-f0-9]+ native@0x[a-f0-9]+\]/); }); it('returned from function is marked as disposed and then as finalized', function () { let file = Gio.File.new_for_path('/'); CjsTestTools.save_object(file); CjsTestTools.delayed_unref(file, 30); file.run_dispose(); let disposedFile = CjsTestTools.get_saved(); expect(disposedFile).toEqual(file); expect(disposedFile).toMatch( /\[object \(DISPOSED\) instance wrapper GType:GLocalFile jsobj@0x[a-f0-9]+ native@0x[a-f0-9]+\]/); file = null; System.gc(); const loop = new GLib.MainLoop(null, false); GLib.timeout_add(GLib.PRIORITY_DEFAULT, 50, () => loop.quit()); loop.run(); expect(disposedFile).toMatch( /\[object \(FINALIZED\) instance wrapper GType:GLocalFile jsobj@0x[a-f0-9]+ native@0x[a-f0-9]+\]/); GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, '*Object 0x* has been finalized *'); disposedFile = null; System.gc(); GLib.test_assert_expected_messages_internal('Cjs', 'testGObjectDestructionAccess.js', 0, 'returned from function is marked as disposed and then as finalized'); }); it('ignores toggling queued unref toggles', function () { let file = Gio.File.new_for_path('/'); file.expandMeWithToggleRef = true; CjsTestTools.ref(file); CjsTestTools.unref_other_thread(file); file.run_dispose(); }); it('ignores toggling queued toggles', function () { let file = Gio.File.new_for_path('/'); file.expandMeWithToggleRef = true; CjsTestTools.ref_other_thread(file); CjsTestTools.unref_other_thread(file); file.run_dispose(); }); it('can be disposed from other thread', function () { let file = Gio.File.new_for_path('/'); file.expandMeWithToggleRef = true; CjsTestTools.ref(file); CjsTestTools.unref_other_thread(file); CjsTestTools.run_dispose_other_thread(file); }); it('can be garbage collected once disposed from other thread', function () { let file = Gio.File.new_for_path('/'); file.expandMeWithToggleRef = true; CjsTestTools.run_dispose_other_thread(file); file = null; System.gc(); }); }); describe('GObject with toggle references', function () { beforeAll(function () { CjsTestTools.init(); }); afterEach(function () { CjsTestTools.reset(); }); it('can be re-reffed from other thread delayed', function () { let file = Gio.File.new_for_path('/'); file.expandMeWithToggleRef = true; const objectAddress = System.addressOfGObject(file); CjsTestTools.save_object_unreffed(file); CjsTestTools.delayed_ref_other_thread(file, 10); file = null; System.gc(); const loop = new GLib.MainLoop(null, false); GLib.timeout_add(GLib.PRIORITY_DEFAULT, 50, () => loop.quit()); loop.run(); // We need to cleanup the extra ref we added before now. // However, depending on whether the thread ref happens the object // may be already finalized, and in such case we need to throw try { file = CjsTestTools.steal_saved(); if (file) { expect(System.addressOfGObject(file)).toBe(objectAddress); expect(file instanceof Gio.File).toBeTruthy(); CjsTestTools.unref(file); } } catch (e) { expect(() => { throw e; }).toThrowError(/.*Unhandled GType.*/); } }); it('can be re-reffed and unreffed again from other thread', function () { let file = Gio.File.new_for_path('/'); const objectAddress = System.addressOfGObject(file); file.expandMeWithToggleRef = true; CjsTestTools.save_object(file); CjsTestTools.ref(file); CjsTestTools.delayed_unref_other_thread(file, 10); file = null; System.gc(); const loop = new GLib.MainLoop(null, false); GLib.timeout_add(GLib.PRIORITY_DEFAULT, 50, () => loop.quit()); loop.run(); file = CjsTestTools.get_saved(); expect(System.addressOfGObject(file)).toBe(objectAddress); expect(file instanceof Gio.File).toBeTruthy(); }); it('can be re-reffed and unreffed again from other thread with delay', function () { let file = Gio.File.new_for_path('/'); file.expandMeWithToggleRef = true; CjsTestTools.delayed_ref_unref_other_thread(file, 10); file = null; System.gc(); const loop = new GLib.MainLoop(null, false); GLib.timeout_add(GLib.PRIORITY_DEFAULT, 50, () => loop.quit()); loop.run(); }); it('can be toggled up by getting a GWeakRef', function () { let file = Gio.File.new_for_path('/'); file.expandMeWithToggleRef = true; CjsTestTools.save_weak(file); CjsTestTools.get_weak(); }); it('can be toggled up by getting a GWeakRef from another thread', function () { let file = Gio.File.new_for_path('/'); file.expandMeWithToggleRef = true; CjsTestTools.save_weak(file); CjsTestTools.get_weak_other_thread(); }); it('can be toggled up by getting a GWeakRef from another thread and re-reffed in main thread', function () { let file = Gio.File.new_for_path('/'); file.expandMeWithToggleRef = true; CjsTestTools.save_weak(file); CjsTestTools.get_weak_other_thread(); // Ok, let's play more dirty now... CjsTestTools.ref(file); // toggle up CjsTestTools.unref(file); // toggle down CjsTestTools.ref(file); CjsTestTools.ref(file); CjsTestTools.unref(file); CjsTestTools.unref(file); }); it('can be toggled up by getting a GWeakRef from another and re-reffed from various threads', function () { let file = Gio.File.new_for_path('/'); file.expandMeWithToggleRef = true; CjsTestTools.save_weak(file); CjsTestTools.get_weak_other_thread(); CjsTestTools.ref_other_thread(file); CjsTestTools.unref_other_thread(file); CjsTestTools.ref(file); CjsTestTools.unref(file); CjsTestTools.ref_other_thread(file); CjsTestTools.unref(file); CjsTestTools.ref(file); CjsTestTools.unref_other_thread(file); }); it('can be toggled up-down from various threads when the wrapper is gone', function () { let file = Gio.File.new_for_path('/'); file.expandMeWithToggleRef = true; // We also check that late thread events won't affect the destroyed wrapper const threads = []; threads.push(CjsTestTools.delayed_ref_unref_other_thread(file, 0)); threads.push(CjsTestTools.delayed_ref_unref_other_thread(file, 100000)); threads.push(CjsTestTools.delayed_ref_unref_other_thread(file, 200000)); threads.push(CjsTestTools.delayed_ref_unref_other_thread(file, 300000)); CjsTestTools.save_object(file); CjsTestTools.save_weak(file); file = null; System.gc(); threads.forEach(th => th.join()); CjsTestTools.clear_saved(); System.gc(); expect(CjsTestTools.get_weak()).toBeNull(); }); it('can be toggled up-down from various threads when disposed and the wrapper is gone', function () { let file = Gio.File.new_for_path('/'); file.expandMeWithToggleRef = true; // We also check that late thread events won't affect the destroyed wrapper const threads = []; threads.push(CjsTestTools.delayed_ref_unref_other_thread(file, 0)); threads.push(CjsTestTools.delayed_ref_unref_other_thread(file, 100000)); threads.push(CjsTestTools.delayed_ref_unref_other_thread(file, 200000)); threads.push(CjsTestTools.delayed_ref_unref_other_thread(file, 300000)); CjsTestTools.save_object(file); CjsTestTools.save_weak(file); file.run_dispose(); file = null; System.gc(); threads.forEach(th => th.join()); CjsTestTools.clear_saved(); expect(CjsTestTools.get_weak()).toBeNull(); }); it('can be finalized while queued in toggle queue', function () { let file = Gio.File.new_for_path('/'); file.expandMeWithToggleRef = true; CjsTestTools.ref(file); CjsTestTools.unref_other_thread(file); CjsTestTools.unref_other_thread(file); GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, '*Object 0x* has been finalized *'); file = null; System.gc(); GLib.test_assert_expected_messages_internal('Cjs', 'testGObjectDestructionAccess.js', 0, 'can be finalized while queued in toggle queue'); }); xit('can be toggled up-down from various threads while getting a GWeakRef from main', function () { let file = Gio.File.new_for_path('/'); file.expandMeWithToggleRef = true; CjsTestTools.save_weak(file); const ids = []; let threads = []; ids.push(GLib.idle_add(GLib.PRIORITY_DEFAULT, () => { threads = threads.slice(-50); try { threads.push(CjsTestTools.delayed_ref_unref_other_thread(file, 1)); } catch (e) { // If creating the thread failed we're almost going out of memory // so let's first wait for the ones allocated to complete. threads.forEach(th => th.join()); threads = []; } return GLib.SOURCE_CONTINUE; })); const loop = new GLib.MainLoop(null, false); ids.push(GLib.idle_add(GLib.PRIORITY_DEFAULT, () => { expect(CjsTestTools.get_weak()).toEqual(file); return GLib.SOURCE_CONTINUE; })); // We must not timeout due to deadlock #404 and finally not crash per #297 GLib.timeout_add(GLib.PRIORITY_DEFAULT, 3000, () => loop.quit()); loop.run(); ids.forEach(id => GLib.source_remove(id)); // We also check that late thread events won't affect the destroyed wrapper CjsTestTools.save_object(file); file = null; System.gc(); threads.forEach(th => th.join()); expect(CjsTestTools.get_saved_ref_count()).toBeGreaterThan(0); CjsTestTools.clear_saved(); System.gc(); expect(CjsTestTools.get_weak()).toBeNull(); }).pend('Flaky, see https://gitlab.gnome.org/GNOME/gjs/-/issues/NNN'); }); cjs-128.0/installed-tests/js/testSystem.js0000664000175000017500000000563114771557763017537 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2013 Pavel Vasin // SPDX-FileCopyrightText: 2013 Giovanni Campagna // SPDX-FileCopyrightText: 2017 Claudio André // SPDX-FileCopyrightText: 2019 Philip Chimento // SPDX-FileCopyrightText: 2019 Canonical, Ltd. const System = imports.system; const {Gio, GObject} = imports.gi; describe('System.addressOf()', function () { it('gives different results for different objects', function () { let a = {some: 'object'}; let b = {different: 'object'}; expect(System.addressOf(a)).not.toEqual(System.addressOf(b)); }); }); describe('System.refcount()', function () { it('gives the correct number', function () { let o = new GObject.Object({}); expect(System.refcount(o)).toEqual(1); }); }); describe('System.addressOfGObject()', function () { it('gives different results for different objects', function () { let a = new GObject.Object({}); let b = new GObject.Object({}); expect(System.addressOfGObject(a)).toEqual(System.addressOfGObject(a)); expect(System.addressOfGObject(a)).not.toEqual(System.addressOfGObject(b)); }); it('throws for non GObject objects', function () { expect(() => System.addressOfGObject({})) .toThrowError(/Object 0x[a-f0-9]+ is not a GObject/); }); }); describe('System.gc()', function () { it('does not crash the application', function () { expect(System.gc).not.toThrow(); }); }); describe('System.dumpHeap()', function () { it('throws but does not crash when given a nonexistent path', function () { expect(() => System.dumpHeap('/does/not/exist')).toThrow(); }); }); describe('System.dumpMemoryInfo()', function () { it('', function () { expect(() => System.dumpMemoryInfo('memory.md')).not.toThrow(); expect(() => Gio.File.new_for_path('memory.md').delete(null)).not.toThrow(); }); it('throws but does not crash when given a nonexistent path', function () { expect(() => System.dumpMemoryInfo('/does/not/exist')).toThrowError(/\/does\/not\/exist/); }); }); describe('System.programPath', function () { it('is null when executed from minijasmine', function () { expect(System.programPath).toBe(null); }); }); describe('System.programArgs', function () { it('System.programArgs is an array', function () { expect(Array.isArray(System.programArgs)).toBeTruthy(); }); it('modifications persist', function () { System.programArgs.push('--foo'); expect(System.programArgs.pop()).toBe('--foo'); }); it('System.programArgs is equal to ARGV', function () { expect(System.programArgs).toEqual(ARGV); ARGV.push('--foo'); expect(System.programArgs.pop()).toBe('--foo'); }); }); cjs-128.0/installed-tests/js/testGObjectInterface.js0000664000175000017500000004133014771557763021405 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2015 Endless Mobile, Inc. const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const AGObjectInterface = GObject.registerClass({ GTypeName: 'ArbitraryGTypeName', Requires: [GObject.Object], Properties: { 'interface-prop': GObject.ParamSpec.string('interface-prop', 'Interface property', 'Must be overridden in implementation', GObject.ParamFlags.READABLE, 'foobar'), }, Signals: { 'interface-signal': {}, }, }, class AGObjectInterface extends GObject.Interface { requiredG() { throw new GObject.NotImplementedError(); } optionalG() { return 'AGObjectInterface.optionalG()'; } }); const InterfaceRequiringGObjectInterface = GObject.registerClass({ Requires: [AGObjectInterface], }, class InterfaceRequiringGObjectInterface extends GObject.Interface { optionalG() { return `InterfaceRequiringGObjectInterface.optionalG()\n${ AGObjectInterface.optionalG(this)}`; } }); const GObjectImplementingGObjectInterface = GObject.registerClass({ Implements: [AGObjectInterface], Properties: { 'interface-prop': GObject.ParamSpec.override('interface-prop', AGObjectInterface), 'class-prop': GObject.ParamSpec.string('class-prop', 'Class property', 'A property that is not on the interface', GObject.ParamFlags.READABLE, 'meh'), }, Signals: { 'class-signal': {}, }, }, class GObjectImplementingGObjectInterface extends GObject.Object { get interface_prop() { return 'foobar'; } get class_prop() { return 'meh'; } requiredG() {} optionalG() { return AGObjectInterface.optionalG(this); } }); const MinimalImplementationOfAGObjectInterface = GObject.registerClass({ Implements: [AGObjectInterface], Properties: { 'interface-prop': GObject.ParamSpec.override('interface-prop', AGObjectInterface), }, }, class MinimalImplementationOfAGObjectInterface extends GObject.Object { requiredG() {} }); const ImplementationOfTwoInterfaces = GObject.registerClass({ Implements: [AGObjectInterface, InterfaceRequiringGObjectInterface], Properties: { 'interface-prop': GObject.ParamSpec.override('interface-prop', AGObjectInterface), }, }, class ImplementationOfTwoInterfaces extends GObject.Object { requiredG() {} optionalG() { return InterfaceRequiringGObjectInterface.optionalG(this); } }); const ImplementationOfIntrospectedInterface = GObject.registerClass({ Implements: [Gio.Action], Properties: { 'enabled': GObject.ParamSpec.override('enabled', Gio.Action), 'name': GObject.ParamSpec.override('name', Gio.Action), 'state': GObject.ParamSpec.override('state', Gio.Action), 'state-type': GObject.ParamSpec.override('state-type', Gio.Action), 'parameter-type': GObject.ParamSpec.override('parameter-type', Gio.Action), }, }, class ImplementationOfIntrospectedInterface extends GObject.Object { get name() { return 'inaction'; } }); describe('GObject interface', function () { it('cannot be instantiated', function () { expect(() => new AGObjectInterface()).toThrow(); }); it('has a name', function () { expect(AGObjectInterface.name).toEqual('AGObjectInterface'); }); it('reports its type name', function () { expect(AGObjectInterface.$gtype.name).toEqual('ArbitraryGTypeName'); }); it('can be implemented by a GObject class', function () { let obj; expect(() => { obj = new GObjectImplementingGObjectInterface(); }).not.toThrow(); expect(obj instanceof AGObjectInterface).toBeTruthy(); }); it('is implemented by a GObject class with the correct class object', function () { let obj = new GObjectImplementingGObjectInterface(); expect(obj.constructor).toBe(GObjectImplementingGObjectInterface); expect(obj.constructor.name) .toEqual('GObjectImplementingGObjectInterface'); }); it('can have its required function implemented', function () { expect(() => { let obj = new GObjectImplementingGObjectInterface(); obj.requiredG(); }).not.toThrow(); }); it('must have its required function implemented', function () { const BadObject = GObject.registerClass({ Implements: [AGObjectInterface], Properties: { 'interface-prop': GObject.ParamSpec.override('interface-prop', AGObjectInterface), }, }, class BadObject extends GObject.Object {}); expect(() => new BadObject().requiredG()) .toThrowError(GObject.NotImplementedError); }); it("doesn't have to have its optional function implemented", function () { let obj; expect(() => { obj = new MinimalImplementationOfAGObjectInterface(); }).not.toThrow(); expect(obj instanceof AGObjectInterface).toBeTruthy(); }); it('can have its optional function deferred to by the implementation', function () { let obj = new MinimalImplementationOfAGObjectInterface(); expect(obj.optionalG()).toEqual('AGObjectInterface.optionalG()'); }); it('can have its function chained up to', function () { let obj = new GObjectImplementingGObjectInterface(); expect(obj.optionalG()).toEqual('AGObjectInterface.optionalG()'); }); it('can require another interface', function () { let obj; expect(() => { obj = new ImplementationOfTwoInterfaces(); }).not.toThrow(); expect(obj instanceof AGObjectInterface).toBeTruthy(); expect(obj instanceof InterfaceRequiringGObjectInterface).toBeTruthy(); }); it('can chain up to another interface', function () { let obj = new ImplementationOfTwoInterfaces(); expect(obj.optionalG()) .toEqual('InterfaceRequiringGObjectInterface.optionalG()\nAGObjectInterface.optionalG()'); }); it("defers to the last interface's optional function", function () { const MinimalImplementationOfTwoInterfaces = GObject.registerClass({ Implements: [AGObjectInterface, InterfaceRequiringGObjectInterface], Properties: { 'interface-prop': GObject.ParamSpec.override('interface-prop', AGObjectInterface), }, }, class MinimalImplementationOfTwoInterfaces extends GObject.Object { requiredG() {} }); let obj = new MinimalImplementationOfTwoInterfaces(); expect(obj.optionalG()) .toEqual('InterfaceRequiringGObjectInterface.optionalG()\nAGObjectInterface.optionalG()'); }); it('must be implemented by a class that implements all required interfaces', function () { expect(() => GObject.registerClass({ Implements: [InterfaceRequiringGObjectInterface], }, class BadObject { required() {} })).toThrow(); }); it('must be implemented by a class that implements required interfaces in correct order', function () { expect(() => GObject.registerClass({ Implements: [InterfaceRequiringGObjectInterface, AGObjectInterface], }, class BadObject { required() {} })).toThrow(); }); it('can require an interface from C', function () { const InitableInterface = GObject.registerClass({ Requires: [GObject.Object, Gio.Initable], }, class InitableInterface extends GObject.Interface {}); expect(() => GObject.registerClass({ Implements: [InitableInterface], }, class BadObject {})).toThrow(); }); it('can connect class signals on the implementing class', function (done) { function quitLoop() { expect(classSignalSpy).toHaveBeenCalled(); done(); } let obj = new GObjectImplementingGObjectInterface(); let classSignalSpy = jasmine.createSpy('classSignalSpy') .and.callFake(quitLoop); obj.connect('class-signal', classSignalSpy); GLib.idle_add(GLib.PRIORITY_DEFAULT, () => { obj.emit('class-signal'); return GLib.SOURCE_REMOVE; }); }); it('can connect interface signals on the implementing class', function (done) { function quitLoop() { expect(interfaceSignalSpy).toHaveBeenCalled(); done(); } let obj = new GObjectImplementingGObjectInterface(); let interfaceSignalSpy = jasmine.createSpy('interfaceSignalSpy') .and.callFake(quitLoop); obj.connect('interface-signal', interfaceSignalSpy); GLib.idle_add(GLib.PRIORITY_DEFAULT, () => { obj.emit('interface-signal'); return GLib.SOURCE_REMOVE; }); }); it('can define properties on the implementing class', function () { let obj = new GObjectImplementingGObjectInterface(); expect(obj.interface_prop).toEqual('foobar'); expect(obj.class_prop).toEqual('meh'); }); it('must have its properties overridden', function () { // Failing to override an interface property doesn't raise an error but // instead logs a critical warning. GLib.test_expect_message('GLib-GObject', GLib.LogLevelFlags.LEVEL_CRITICAL, "Object class * doesn't implement property 'interface-prop' from " + "interface 'ArbitraryGTypeName'"); GObject.registerClass({ Implements: [AGObjectInterface], }, class MyNaughtyObject extends GObject.Object { requiredG() {} }); // g_test_assert_expected_messages() is a macro, not introspectable GLib.test_assert_expected_messages_internal('Cjs', 'testGObjectInterface.js', 253, 'testGObjectMustOverrideInterfaceProperties'); }); it('can have introspected properties overriden', function () { let obj = new ImplementationOfIntrospectedInterface(); expect(obj.name).toEqual('inaction'); }); it('can be implemented by a class as well as its parent class', function () { const SubObject = GObject.registerClass( class SubObject extends GObjectImplementingGObjectInterface {}); let obj = new SubObject(); expect(obj instanceof AGObjectInterface).toBeTruthy(); expect(obj.interface_prop).toEqual('foobar'); // override not needed }); it('can be reimplemented by a subclass of a class that already implements it', function () { const SubImplementer = GObject.registerClass({ Implements: [AGObjectInterface], }, class SubImplementer extends GObjectImplementingGObjectInterface {}); let obj = new SubImplementer(); expect(obj instanceof AGObjectInterface).toBeTruthy(); expect(obj.interface_prop).toEqual('foobar'); // override not needed }); it('has a toString() defintion', function () { expect(new GObjectImplementingGObjectInterface().toString()).toMatch( /\[object instance wrapper GType:Gjs_GObjectImplementingGObjectInterface jsobj@0x[a-f0-9]+ native@0x[a-f0-9]+\]/); }); it('has instance definition', function () { const obj = new GObjectImplementingGObjectInterface(); const obj2 = new ImplementationOfTwoInterfaces(); const file = Gio.File.new_for_path('/'); expect(obj).toBeInstanceOf(AGObjectInterface); expect(obj).not.toBeInstanceOf(InterfaceRequiringGObjectInterface); expect(obj2).toBeInstanceOf(AGObjectInterface); expect(obj2).toBeInstanceOf(InterfaceRequiringGObjectInterface); expect(new GObject.Object()).not.toBeInstanceOf(AGObjectInterface); expect(file).toBeInstanceOf(Gio.File); expect(file).toBeInstanceOf(GObject.Object); }); it('has instance definition for non-object type', function () { expect(null).not.toBeInstanceOf(AGObjectInterface); expect(true).not.toBeInstanceOf(AGObjectInterface); expect(undefined).not.toBeInstanceOf(AGObjectInterface); expect(123456).not.toBeInstanceOf(AGObjectInterface); expect(54321n).not.toBeInstanceOf(AGObjectInterface); expect('no way!').not.toBeInstanceOf(AGObjectInterface); expect(new Date()).not.toBeInstanceOf(AGObjectInterface); }); it('has instance definition for non-object type for native interface', function () { expect(null).not.toBeInstanceOf(Gio.File); expect(true).not.toBeInstanceOf(Gio.File); expect(undefined).not.toBeInstanceOf(Gio.File); expect(12345).not.toBeInstanceOf(Gio.File); expect(54321n).not.toBeInstanceOf(Gio.File); expect('no way!').not.toBeInstanceOf(Gio.File); expect(new Date()).not.toBeInstanceOf(Gio.File); }); describe('prototype', function () { let file, originalDup; beforeAll(function () { file = Gio.File.new_for_path('/'); originalDup = Gio.File.prototype.dup; }); it('toString is enumerable and defined', function () { expect(Object.getOwnPropertyNames(Gio.File.prototype)).toContain('toString'); expect(Gio.File.prototype.toString).toBeDefined(); }); it('method properties are enumerated', function () { const expectedMethods = [ 'copy_attributes', 'copy_async', 'create_async', 'create_readwrite_async', 'delete_async', 'enumerate_children', ]; const methods = Object.getOwnPropertyNames(Gio.File.prototype); expect(methods).toEqual(jasmine.arrayContaining(expectedMethods)); }); it('method properties are defined', function () { const methods = Object.getOwnPropertyNames(Gio.File.prototype); for (const method of methods) { expect(Gio.File.prototype[method]).toBeDefined(); expect(Gio.File.prototype[method]).toBeInstanceOf(Function); } }); it('overrides are inherited by implementing classes', function () { spyOn(Gio.File.prototype, 'dup'); expect(file).toBeInstanceOf(Gio.File); expect(file).toBeInstanceOf(Gio._LocalFilePrototype.constructor); file.dup(); expect(Gio.File.prototype.dup).toHaveBeenCalledOnceWith(); Gio.File.prototype.dup = originalDup; expect(file.dup).toBe(originalDup); }); it('overrides cannot be changed by instances of child classes', function () { spyOn(Gio.File.prototype, 'dup'); expect(file).toBeInstanceOf(Gio.File); expect(file).toBeInstanceOf(Gio._LocalFilePrototype.constructor); file.dup = 5; expect(Gio.File.prototype.dup).not.toBe(5); expect(Gio._LocalFilePrototype.dup).not.toBe(5); file.dup = originalDup; expect(file.dup).toBe(originalDup); }); it('unknown properties are inherited by implementing classes', function () { Gio.File.prototype._originalDup = originalDup; expect(file._originalDup).toBe(originalDup); Gio.File.prototype._originalDup = 5; expect(file._originalDup).toBe(5); delete Gio.File.prototype._originalDup; expect(file._originalDup).not.toBeDefined(); }); it('original property can be shadowed by class prototype property', function () { spyOn(Gio._LocalFilePrototype, 'dup').and.returnValue(5); expect(file.dup()).toBe(5); expect(Gio._LocalFilePrototype.dup).toHaveBeenCalled(); }); it('overridden property can be shadowed by class prototype property', function () { spyOn(Gio._LocalFilePrototype, 'dup'); spyOn(Gio.File.prototype, 'dup'); file.dup(); expect(Gio._LocalFilePrototype.dup).toHaveBeenCalled(); expect(Gio.File.prototype.dup).not.toHaveBeenCalled(); }); it('shadowed property can be restored', function () { Gio._LocalFilePrototype.dup = 5; expect(file.dup).toBe(5); delete Gio._LocalFilePrototype.dup; expect(file.dup).toBeInstanceOf(Function); }); }); }); describe('Specific class and interface checks', function () { it('Gio.AsyncInitable must implement vfunc_async_init', function () { expect(() => GObject.registerClass({ Implements: [Gio.Initable, Gio.AsyncInitable], }, class BadAsyncInitable extends GObject.Object { vfunc_init() {} })).toThrow(); }); }); cjs-128.0/installed-tests/js/jsunit.gresources.xml0000664000175000017500000000344414771557763021233 0ustar fabiofabio complex3.ui complex4.ui jasmine.js minijasmine-executor.js minijasmine.js modules/alwaysThrows.js modules/badOverrides/GIMarshallingTests.js modules/badOverrides/Gio.js modules/badOverrides/Regress.js modules/badOverrides/WarnLib.js modules/badOverrides2/GIMarshallingTests.js modules/badOverrides2/Gio.js modules/badOverrides2/Regress.js modules/badOverrides2/WarnLib.js modules/data.txt modules/dynamic.js modules/encodings.json modules/exports.js modules/foobar.js modules/greet.js modules/importmeta.js modules/lexicalScope.js modules/modunicode.js modules/mutualImport/a.js modules/mutualImport/b.js modules/overrides/GIMarshallingTests.js modules/say.js modules/sideEffect.js modules/sideEffect2.js modules/sideEffect3.js modules/sideEffect4.js modules/subA/subB/__init__.js modules/subA/subB/baz.js modules/subA/subB/foobar.js modules/subBadInit/__init__.js modules/subErrorInit/__init__.js cjs-128.0/installed-tests/js/testLegacyByteArray.js0000664000175000017500000001707614771557763021310 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2010 litl, LLC // SPDX-FileCopyrightText: 2017 Philip Chimento const ByteArray = imports.byteArray; const {GIMarshallingTests, CjsTestTools, GLib} = imports.gi; describe('Uint8Array with legacy ByteArray functions', function () { it('can be created from a string', function () { let a = ByteArray.fromString('abcd'); expect(a.length).toEqual(4); [97, 98, 99, 100].forEach((val, ix) => expect(a[ix]).toEqual(val)); }); it('can be encoded from a string', function () { // Pick a string likely to be stored internally as Latin1 let a = ByteArray.fromString('äbcd', 'LATIN1'); expect(a.length).toEqual(4); [228, 98, 99, 100].forEach((val, ix) => expect(a[ix]).toEqual(val)); // Try again with a string not likely to be Latin1 internally a = ByteArray.fromString('⅜', 'UTF-8'); expect(a.length).toEqual(3); [0xe2, 0x85, 0x9c].forEach((val, ix) => expect(a[ix]).toEqual(val)); }); it('encodes as UTF-8 by default', function () { let a = ByteArray.fromString('⅜'); expect(a.length).toEqual(3); [0xe2, 0x85, 0x9c].forEach((val, ix) => expect(a[ix]).toEqual(val)); }); it('can be converted to a string of ASCII characters', function () { let a = new Uint8Array(4); a[0] = 97; a[1] = 98; a[2] = 99; a[3] = 100; let s = ByteArray.toString(a); expect(s.length).toEqual(4); expect(s).toEqual('abcd'); }); it('can be converted to a string of UTF-8 characters even if it ends with a 0', function () { const a = Uint8Array.of(97, 98, 99, 100, 0); const s = ByteArray.toString(a); expect(s.length).toEqual(4); expect(s).toEqual('abcd'); }); it('can be converted to a string of encoded characters even with a 0 byte', function () { const a = Uint8Array.of(97, 98, 99, 100, 0); const s = ByteArray.toString(a, 'LATIN1'); expect(s.length).toEqual(4); expect(s).toEqual('abcd'); }); it('stops converting to a string at an embedded 0 byte', function () { const a = Uint8Array.of(97, 98, 0, 99, 100); const s = ByteArray.toString(a); expect(s.length).toEqual(2); expect(s).toEqual('ab'); }); it('deals gracefully with a 0-length array', function () { const a = new Uint8Array(0); expect(ByteArray.toString(a)).toEqual(''); expect(ByteArray.toGBytes(a).get_size()).toEqual(0); }); it('deals gracefully with a 0-length GLib.Bytes', function () { const noBytes = ByteArray.toGBytes(new Uint8Array(0)); expect(ByteArray.fromGBytes(noBytes).length).toEqual(0); }); it('deals gracefully with a non-aligned GBytes', function () { const unalignedBytes = CjsTestTools.new_unaligned_bytes(48); const arr = ByteArray.fromGBytes(unalignedBytes); expect(arr.length).toEqual(48); expect(Array.prototype.slice.call(arr, 0, 4)).toEqual([1, 2, 3, 4]); }); it('deals gracefully with a GBytes in static storage', function () { const staticBytes = CjsTestTools.new_static_bytes(); const arr = ByteArray.fromGBytes(staticBytes); arr[2] = 42; expect(Array.from(arr)).toEqual([104, 101, 42, 108, 111, 0]); }); it('deals gracefully with a 0-length string', function () { expect(ByteArray.fromString('').length).toEqual(0); expect(ByteArray.fromString('', 'LATIN1').length).toEqual(0); }); it('deals gracefully with a non Uint8Array', function () { const a = [97, 98, 99, 100, 0]; expect(() => ByteArray.toString(a)).toThrow(); expect(() => ByteArray.toGBytes(a)).toThrow(); }); describe('legacy toString() behavior', function () { beforeEach(function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_WARNING, 'Some code called array.toString()*'); }); it('is preserved when created from a string', function () { let a = ByteArray.fromString('⅜'); expect(a.toString()).toEqual('⅜'); }); it('is preserved when marshalled from GI', function () { let a = GIMarshallingTests.bytearray_full_return(); expect(a.toString()).toEqual(''); }); afterEach(function () { GLib.test_assert_expected_messages_internal('Cjs', 'testByteArray.js', 0, 'testToStringCompatibility'); }); }); }); describe('Legacy byte array object', function () { it('has length 0 for empty array', function () { let a = new ByteArray.ByteArray(); expect(a.length).toEqual(0); }); describe('initially sized to 10', function () { let a; beforeEach(function () { a = new ByteArray.ByteArray(10); }); it('has length 10', function () { expect(a.length).toEqual(10); }); it('is initialized to zeroes', function () { for (let i = 0; i < a.length; ++i) expect(a[i]).toEqual(0); }); }); it('assigns values correctly', function () { let a = new ByteArray.ByteArray(256); for (let i = 0; i < a.length; ++i) a[i] = 255 - i; for (let i = 0; i < a.length; ++i) expect(a[i]).toEqual(255 - i); }); describe('assignment past end', function () { let a; beforeEach(function () { a = new ByteArray.ByteArray(); a[2] = 5; }); it('implicitly lengthens the array', function () { expect(a.length).toEqual(3); expect(a[2]).toEqual(5); }); it('implicitly creates zero bytes', function () { expect(a[0]).toEqual(0); expect(a[1]).toEqual(0); }); }); it('changes the length when assigning to length property', function () { let a = new ByteArray.ByteArray(20); expect(a.length).toEqual(20); a.length = 5; expect(a.length).toEqual(5); }); describe('conversions', function () { let a; beforeEach(function () { a = new ByteArray.ByteArray(); a[0] = 255; }); it('gives a byte 5 when assigning 5', function () { a[0] = 5; expect(a[0]).toEqual(5); }); it('gives a byte 0 when assigning null', function () { a[0] = null; expect(a[0]).toEqual(0); }); it('gives a byte 0 when assigning undefined', function () { a[0] = undefined; expect(a[0]).toEqual(0); }); it('rounds off when assigning a double', function () { a[0] = 3.14; expect(a[0]).toEqual(3); }); }); it('can be created from an array', function () { let a = ByteArray.fromArray([1, 2, 3, 4]); expect(a.length).toEqual(4); [1, 2, 3, 4].forEach((val, ix) => expect(a[ix]).toEqual(val)); }); it('can be converted to a string of ASCII characters', function () { let a = new ByteArray.ByteArray(4); a[0] = 97; a[1] = 98; a[2] = 99; a[3] = 100; let s = a.toString(); expect(s.length).toEqual(4); expect(s).toEqual('abcd'); }); it('can be passed in with transfer none', function () { const refByteArray = ByteArray.fromArray([0, 49, 0xFF, 51]); expect(() => GIMarshallingTests.bytearray_none_in(refByteArray)).not.toThrow(); }); }); cjs-128.0/installed-tests/js/testFundamental.js0000664000175000017500000001357314771557763020515 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2013 Lionel Landwerlin // SPDX-FileCopyrightText: 2021 Marco Trevisan const {GObject, Regress} = imports.gi; const TestObj = GObject.registerClass({ Signals: { 'test-fundamental-value-funcs': {param_types: [Regress.TestFundamentalObject.$gtype]}, 'test-fundamental-value-funcs-subtype': {param_types: [Regress.TestFundamentalSubObject.$gtype]}, 'test-fundamental-no-funcs': { param_types: Regress.TestFundamentalObjectNoGetSetFunc ? [Regress.TestFundamentalObjectNoGetSetFunc.$gtype] : [], }, 'test-fundamental-no-funcs-subtype': { param_types: Regress.TestFundamentalSubObjectNoGetSetFunc ? [Regress.TestFundamentalSubObjectNoGetSetFunc.$gtype] : [], }, }, }, class TestObj extends GObject.Object {}); describe('Fundamental type support', function () { it('can marshal a subtype of a custom fundamental type into a supertype GValue', function () { const fund = new Regress.TestFundamentalSubObject('plop'); expect(() => GObject.strdup_value_contents(fund)).not.toThrow(); const obj = new TestObj(); const signalSpy = jasmine.createSpy('signalSpy'); obj.connect('test-fundamental-value-funcs', signalSpy); obj.emit('test-fundamental-value-funcs', fund); expect(signalSpy).toHaveBeenCalledWith(obj, fund); }); it('can marshal a subtype of a custom fundamental type into a GValue', function () { const fund = new Regress.TestFundamentalSubObject('plop'); const obj = new TestObj(); const signalSpy = jasmine.createSpy('signalSpy'); obj.connect('test-fundamental-value-funcs-subtype', signalSpy); obj.emit('test-fundamental-value-funcs-subtype', fund); expect(signalSpy).toHaveBeenCalledWith(obj, fund); }); it('can marshal a custom fundamental type into a GValue if contains a pointer and does not provide setter and getters', function () { const fund = new Regress.TestFundamentalObjectNoGetSetFunc('foo'); expect(() => GObject.strdup_value_contents(fund)).not.toThrow(); const obj = new TestObj(); const signalSpy = jasmine.createSpy('signalSpy'); obj.connect('test-fundamental-no-funcs', signalSpy); obj.connect('test-fundamental-no-funcs', (_o, f) => expect(f.get_data()).toBe('foo')); obj.emit('test-fundamental-no-funcs', fund); expect(signalSpy).toHaveBeenCalledWith(obj, fund); }); it('can marshal a subtype of a custom fundamental type into a GValue if contains a pointer and does not provide setter and getters', function () { const fund = new Regress.TestFundamentalSubObjectNoGetSetFunc('foo'); const obj = new TestObj(); const signalSpy = jasmine.createSpy('signalSpy'); obj.connect('test-fundamental-no-funcs-subtype', signalSpy); obj.connect('test-fundamental-no-funcs-subtype', (_o, f) => expect(f.get_data()).toBe('foo')); obj.emit('test-fundamental-no-funcs-subtype', fund); expect(signalSpy).toHaveBeenCalledWith(obj, fund); }); it('cannot marshal a custom fundamental type into a GValue of different gtype', function () { const fund = new Regress.TestFundamentalObjectNoGetSetFunc('foo'); const obj = new TestObj(); const signalSpy = jasmine.createSpy('signalSpy'); obj.connect('test-fundamental-value-funcs', signalSpy); expect(() => obj.emit('test-fundamental-value-funcs', fund)).toThrowError( / RegressTestFundamentalObjectNoGetSetFunc .* conversion to a GValue.* RegressTestFundamentalObject/); }); it('can marshal a custom fundamental type into a GValue of super gtype', function () { const fund = new Regress.TestFundamentalSubObjectNoGetSetFunc('foo'); const obj = new TestObj(); const signalSpy = jasmine.createSpy('signalSpy'); obj.connect('test-fundamental-no-funcs', signalSpy); obj.connect('test-fundamental-no-funcs', (_o, f) => expect(f.get_data()).toBe('foo')); obj.emit('test-fundamental-no-funcs', fund); expect(signalSpy).toHaveBeenCalledWith(obj, fund); }); it('cannot marshal a custom fundamental type into a GValue of sub gtype', function () { const fund = new Regress.TestFundamentalObjectNoGetSetFunc('foo'); const obj = new TestObj(); const signalSpy = jasmine.createSpy('signalSpy'); obj.connect('test-fundamental-no-funcs-subtype', signalSpy); expect(() => obj.emit('test-fundamental-no-funcs-subtype', fund)).toThrowError( / RegressTestFundamentalObjectNoGetSetFunc .* conversion to a GValue.* RegressTestFundamentalSubObjectNoGetSetFunc/); }); it('can marshal a custom fundamental type into a transformable type', function () { Regress.TestFundamentalObjectNoGetSetFunc.make_compatible_with_fundamental_sub_object(); const fund = new Regress.TestFundamentalObjectNoGetSetFunc('foo'); const obj = new TestObj(); const signalSpy = jasmine.createSpy('signalSpy'); obj.connect('test-fundamental-value-funcs-subtype', signalSpy); obj.connect('test-fundamental-value-funcs-subtype', (_o, f) => expect(f instanceof Regress.TestFundamentalSubObject).toBeTrue()); obj.emit('test-fundamental-value-funcs-subtype', fund); expect(signalSpy).toHaveBeenCalled(); }); it('can marshal to a null value', function () { const v = new GObject.Value(); expect(v.init(Regress.TestFundamentalObject.$gtype)).toBeNull(); }); it('can marshal to a null value if has no getter function', function () { const v = new GObject.Value(); expect(v.init(Regress.TestFundamentalObjectNoGetSetFunc.$gtype)).toBeNull(); }); }); cjs-128.0/installed-tests/js/testGtk3.js0000664000175000017500000003052414771557763017062 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2013 Giovanni Campagna imports.gi.versions.Gtk = '3.0'; 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 ` `; } const MyComplexGtkSubclass = GObject.registerClass({ Template: new TextEncoder().encode(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; } testChildrenExist() { 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/cjs/jsunit/complex3.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({type: Gtk.WindowType.TOPLEVEL}); content = new ClassName(); content.label_child.emit('grab-focus'); content.label_child2.emit('grab-focus'); 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(null); }); 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('static inheritance works', function () { expect(MyComplexGtkSubclass.get_css_name()).toEqual('complex-subclass'); }); it('avoid crashing when GTK vfuncs are called in garbage collection', function () { GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, '*during garbage collection*offending callback was destroy()*'); const BadLabel = GObject.registerClass(class BadLabel extends Gtk.Label { vfunc_destroy() {} }); new BadLabel(); System.gc(); GLib.test_assert_expected_messages_internal('Cjs', 'testGtk3.js', 0, 'Gtk overrides avoid crashing and print a stack trace'); }); it('GTK vfuncs are not called if the object is disposed', function () { const spy = jasmine.createSpy('vfunc_destroy'); const NotSoGoodLabel = GObject.registerClass(class NotSoGoodLabel extends Gtk.Label { vfunc_destroy() { spy(); } }); let label = new NotSoGoodLabel(); label.destroy(); expect(spy).toHaveBeenCalledTimes(1); GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, '*during garbage collection*offending callback was destroy()*'); label = null; System.gc(); GLib.test_assert_expected_messages_internal('Cjs', 'testGtk3.js', 0, 'GTK vfuncs are not called if the object is disposed'); }); it('destroy signal is emitted while disposing objects', function () { const label = new Gtk.Label({label: 'Hello'}); const handleDispose = jasmine.createSpy('handleDispose').and.callFake(() => { expect(label.label).toBe('Hello'); }); label.connect('destroy', handleDispose); label.destroy(); expect(handleDispose).toHaveBeenCalledWith(label); GLib.test_expect_message('Cjs', GLib.LogLevelFlags.LEVEL_CRITICAL, 'Object Gtk.Label (0x* disposed *'); expect(label.label).toBe('Hello'); GLib.test_assert_expected_messages_internal('Cjs', 'testGtk3.js', 0, 'GTK destroy signal is emitted while disposing objects'); }); it('destroy signal is not emitted when objects are garbage collected', function () { let label = new Gtk.Label({label: 'Hello'}); const handleDispose = jasmine.createSpy('handleDispose').and.callFake(() => { expect(label.label).toBe('Hello'); }); label.connect('destroy', handleDispose); label = null; System.gc(); System.gc(); expect(handleDispose).not.toHaveBeenCalled(); }); it('accepts string in place of GdkAtom', function () { expect(() => Gtk.Clipboard.get(1)).toThrow(); expect(() => Gtk.Clipboard.get(true)).toThrow(); expect(() => Gtk.Clipboard.get(() => undefined)).toThrow(); const clipboard = Gtk.Clipboard.get('CLIPBOARD'); const primary = Gtk.Clipboard.get('PRIMARY'); const anotherClipboard = Gtk.Clipboard.get('CLIPBOARD'); expect(clipboard).toBeTruthy(); expect(primary).toBeTruthy(); expect(clipboard).not.toBe(primary); expect(clipboard).toBe(anotherClipboard); }); it('accepts null in place of GdkAtom as GDK_NONE', function () { const clipboard = Gtk.Clipboard.get('NONE'); const clipboard2 = Gtk.Clipboard.get(null); expect(clipboard2).toBe(clipboard); }); it('uses the correct GType for null child properties', function () { let s = new Gtk.Stack(); let p = new Gtk.Box(); s.add_named(p, 'foo'); expect(s.get_child_by_name('foo')).toBe(p); s.child_set_property(p, 'name', null); expect(s.get_child_by_name('foo')).toBeNull(); }); it('can create a Gtk.TreeIter with accessible stamp field', function () { const iter = new Gtk.TreeIter(); iter.stamp = 42; expect(iter.stamp).toEqual(42); }); it('can get style properties using GObject.Value', function () { let win = new Gtk.ScrolledWindow(); let value = new GObject.Value(); value.init(GObject.TYPE_BOOLEAN); win.style_get_property('scrollbars-within-bevel', value); expect(value.get_boolean()).toBeDefined(); value.unset(); value.init(GObject.TYPE_INT); let preVal = Math.max(512521, Math.random() * Number.MAX_SAFE_INTEGER); value.set_int(preVal); win.style_get_property('scrollbar-spacing', value); expect(value.get_int()).not.toEqual(preVal); win = new Gtk.Window(); value.unset(); value.init(GObject.TYPE_STRING); value.set_string('EMPTY'); win.style_get_property('decoration-button-layout', value); expect(value.get_string()).not.toEqual('EMPTY'); }); it('can pass a parent object to a child at construction', function () { const frame = new Gtk.Frame(); let frameChild = null; frame.connect('add', (_widget, child) => { frameChild = child; }); const widget = new Gtk.Label({parent: frame}); expect(widget).toBe(frameChild); expect(widget instanceof Gtk.Label).toBeTruthy(); expect(frameChild instanceof Gtk.Label).toBeTruthy(); expect(frameChild.visible).toBe(false); expect(() => widget.show()).not.toThrow(); expect(frameChild.visible).toBe(true); }); function asyncIdle() { return new Promise(resolve => { GLib.idle_add(GLib.PRIORITY_DEFAULT, () => { resolve(); return GLib.SOURCE_REMOVE; }); }); } it('does not leak instance when connecting template signal', async function () { const LeakTestWidget = GObject.registerClass({ Template: new TextEncoder().encode(` `), }, class LeakTestWidget extends Gtk.Button { buttonClicked() {} }); const weakRef = new WeakRef(new LeakTestWidget()); await asyncIdle(); // It takes two GC cycles to free the widget, because of the tardy sweep // problem (https://gitlab.gnome.org/GNOME/gjs/-/issues/217) System.gc(); System.gc(); expect(weakRef.deref()).toBeUndefined(); }); }); cjs-128.0/installed-tests/js/testCairoModule.js0000664000175000017500000000153414771557763020454 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2021 Philip Chimento import Cairo from 'cairo'; import giCairo from 'gi://cairo'; describe('Cairo imported as ES module', function () { it('cairo default import', function () { // one from cairoNative, one from cairo JS. expect(typeof Cairo.Context).toBe('function'); expect(typeof Cairo.Format).toBe('object'); }); // cairo doesn't have named exports }); describe('Cairo imported via GI', function () { it('has the same functionality as imports.cairo', function () { const surface = new giCairo.ImageSurface(Cairo.Format.ARGB32, 1, 1); void new giCairo.Context(surface); }); it('has boxed types from the GIR file', function () { void new giCairo.RectangleInt(); }); }); cjs-128.0/installed-tests/debugger/0000775000175000017500000000000014771557763016160 5ustar fabiofabiocjs-128.0/installed-tests/debugger/step.debugger.js0000664000175000017500000000034714771557763021260 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2018 Philip Chimento function a() { b(); print('A line in a'); } function b() { print('A line in b'); } a(); cjs-128.0/installed-tests/debugger/delete.debugger.output0000664000175000017500000000126314771557763022471 0ustar fabiofabioGJS debugger. Type "help" for help db> # SPDX-License-Identifier: MIT OR LGPL-2.0-or-later db> # SPDX-FileCopyrightText: 2018 Philip Chimento db> b 4 Breakpoint 1 at delete.debugger.js:4:1 db> b 5 Breakpoint 2 at delete.debugger.js:5:1 db> b 6 Breakpoint 3 at delete.debugger.js:6:1 db> b 7 Breakpoint 4 at delete.debugger.js:7:1 db> # Check that breakpoint 4 still remains after deleting 1-3 db> delete 1 Breakpoint 1 at delete.debugger.js:4:1 deleted db> del 2 Breakpoint 2 at delete.debugger.js:5:1 deleted db> d 3 Breakpoint 3 at delete.debugger.js:6:1 deleted db> c 1 2 3 4 Breakpoint 4, toplevel at delete.debugger.js:7:1 db> c 5 Program exited with code 0 cjs-128.0/installed-tests/debugger/frame.debugger.output0000664000175000017500000000057314771557763022324 0ustar fabiofabioGJS debugger. Type "help" for help db> # SPDX-License-Identifier: MIT OR LGPL-2.0-or-later db> # SPDX-FileCopyrightText: 2018 Philip Chimento db> c Debugger statement, b() at frame.debugger.js:8:5 db> frame 2 #2 toplevel at frame.debugger.js:11:1 11 a(); db> f 1 #1 a() at frame.debugger.js:4:5 4 b(); db> c Program exited with code 0 cjs-128.0/installed-tests/debugger/detach.debugger.output0000664000175000017500000000032314771557763022453 0ustar fabiofabioGJS debugger. Type "help" for help db> # SPDX-License-Identifier: MIT OR LGPL-2.0-or-later db> # SPDX-FileCopyrightText: 2018 Philip Chimento db> detach hi Program exited with code 0 cjs-128.0/installed-tests/debugger/set.debugger.output0000664000175000017500000000145514771557763022025 0ustar fabiofabioGJS debugger. Type "help" for help db> # SPDX-License-Identifier: MIT OR LGPL-2.0-or-later db> # SPDX-FileCopyrightText: 2018 Philip Chimento db> # Currently the only option is "pretty" for pretty-printing. Set doesn't yet db> # allow setting variables in the program. db> c Debugger statement, toplevel at set.debugger.js:4:1 db> p a $1 = [object Object] {} db> set pretty 0 db> p a $2 = [object Object] db> set pretty 1 db> p a $3 = [object Object] {} db> set pretty off db> p a $4 = [object Object] db> set pretty on db> p a $5 = [object Object] {} db> set pretty false db> p a $6 = [object Object] db> set pretty true db> p a $7 = [object Object] {} db> set pretty no db> p a $8 = [object Object] db> set pretty yes db> p a $9 = [object Object] {} db> q Program exited with code 0 cjs-128.0/installed-tests/debugger/set.debugger0000664000175000017500000000062314771557763020462 0ustar fabiofabio# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2018 Philip Chimento # Currently the only option is "pretty" for pretty-printing. Set doesn't yet # allow setting variables in the program. c p a set pretty 0 p a set pretty 1 p a set pretty off p a set pretty on p a set pretty false p a set pretty true p a set pretty no p a set pretty yes p a q cjs-128.0/installed-tests/debugger/throw.debugger0000664000175000017500000000034214771557763021030 0ustar fabiofabio# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2018 Philip Chimento set ignoreCaughtExceptions false c f 1 throw {} f 0 p 3.14 throw 'foobar' + $1 fin throw foo throw cjs-128.0/installed-tests/debugger/breakpoint.debugger.js0000664000175000017500000000033214771557763022435 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2018 Philip Chimento print('1'); print('2'); function foo() { print('Function foo'); } print('3'); foo(); cjs-128.0/installed-tests/debugger/continue.debugger.js0000664000175000017500000000022514771557763022124 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2018 Philip Chimento debugger; debugger; cjs-128.0/installed-tests/debugger/lastvalues.debugger.output0000664000175000017500000000067214771557763023415 0ustar fabiofabioGJS debugger. Type "help" for help db> # SPDX-License-Identifier: MIT OR LGPL-2.0-or-later db> # SPDX-FileCopyrightText: 2020 Philip Chimento db> c Debugger statement, toplevel at lastvalues.debugger.js:6:1 db> p a $1 = undefined db> p b $2 = null db> p c $3 = 42 db> p $1 $4 = undefined db> p $2 $5 = null db> p $3 $6 = 42 db> p $$ $7 = 42 db> p $6*3 $8 = 126 db> p $$*3 $9 = 378 db> c Program exited with code 0 cjs-128.0/installed-tests/debugger/throw-ignored.debugger0000664000175000017500000000017614771557763022462 0ustar fabiofabio# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2021 Florian Müllner c q cjs-128.0/installed-tests/debugger/until.debugger.js0000664000175000017500000000032614771557763021435 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2018 Philip Chimento print('1'); print('2'); print('3'); (function () { print('4'); })(); print('5'); cjs-128.0/installed-tests/debugger/quit.debugger.js0000664000175000017500000000021614771557763021262 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2018 Philip Chimento print('hi'); cjs-128.0/installed-tests/debugger/return.debugger0000664000175000017500000000033314771557763021204 0ustar fabiofabio# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2018 Philip Chimento b 4 b 8 b 12 c f 1 return f 0 return ret 5 ret foo p 2 ret `${4 * 10 + $1} is the answer` c cjs-128.0/installed-tests/debugger/keys.debugger.js0000664000175000017500000000035714771557763021261 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2018 Philip Chimento const a = { foo: 1, bar: null, tres: undefined, [Symbol('s')]: 'string', }; debugger; void a; cjs-128.0/installed-tests/debugger/list.debugger0000664000175000017500000000031614771557763020641 0ustar fabiofabio# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2021 Mantoh Nasah Kuma set colors false list list 4 list 11 list 12 list 0 list divide break 4 c list q cjs-128.0/installed-tests/debugger/next.debugger.output0000664000175000017500000000120014771557763022174 0ustar fabiofabioGJS debugger. Type "help" for help db> # SPDX-License-Identifier: MIT OR LGPL-2.0-or-later db> # SPDX-FileCopyrightText: 2018 Philip Chimento db> c Debugger statement, a() at next.debugger.js:4:5 db> next a() at next.debugger.js:4:5 db> n a() at next.debugger.js:5:5 A line in b db> n a() at next.debugger.js:6:5 A line in a db> n a() at next.debugger.js:7:1 No value returned. db> n a() at next.debugger.js:7:1 toplevel at next.debugger.js:13:1 db> n toplevel at next.debugger.js:13:1 db> n toplevel at next.debugger.js:14:1 No value returned. db> n toplevel at next.debugger.js:14:1 Program exited with code 0 cjs-128.0/installed-tests/debugger/frame.debugger0000664000175000017500000000021714771557763020760 0ustar fabiofabio# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2018 Philip Chimento c frame 2 f 1 c cjs-128.0/installed-tests/debugger/throw-ignored.debugger.js0000664000175000017500000000043214771557763023070 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2021 Florian Müllner let count = 0; function a() { throw new Error(`Exception nº ${++count}`); } try { a(); } catch (e) { print(`Caught exception: ${e}`); } a(); cjs-128.0/installed-tests/debugger/backtrace.debugger0000664000175000017500000000041414771557763021604 0ustar fabiofabio# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2018 Philip Chimento backtrace c bt c backtrace full bt full where c # test printing locals when exception is thrown before initialization of a value c bt full q cjs-128.0/installed-tests/debugger/until.debugger.output0000664000175000017500000000071214771557763022360 0ustar fabiofabioGJS debugger. Type "help" for help db> # SPDX-License-Identifier: MIT OR LGPL-2.0-or-later db> # SPDX-FileCopyrightText: 2018 Philip Chimento db> until 5 toplevel at until.debugger.js:3:1 1 2 db> upto 7 toplevel at until.debugger.js:5:1 3 entered frame: () at until.debugger.js:7:5 db> u 9 () at until.debugger.js:7:5 4 No value returned. toplevel at until.debugger.js:9:1 db> c 5 Program exited with code 0 cjs-128.0/installed-tests/debugger/down-up.debugger.js0000664000175000017500000000036714771557763021700 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2018 Philip Chimento function a() { b(); } function b() { c(); } function c() { d(); } function d() { debugger; } a(); cjs-128.0/installed-tests/debugger/list.debugger.output0000664000175000017500000000327414771557763022206 0ustar fabiofabioGJS debugger. Type "help" for help db> # SPDX-License-Identifier: MIT OR LGPL-2.0-or-later db> # SPDX-FileCopyrightText: 2021 Mantoh Nasah Kuma db> set colors false db> list 6 else if (a === undefined || b === undefined) 7 return undefined; 8 else 9 return a / b; 10 } *11 divide(); db> list 4 1 // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later 2 // SPDX-FileCopyrightText: 2021 Mantoh Nasah Kuma 3 function divide(a, b) { *4 if (b === 0) 5 return undefined; 6 else if (a === undefined || b === undefined) 7 return undefined; 8 else 9 return a / b; db> list 11 6 else if (a === undefined || b === undefined) 7 return undefined; 8 else 9 return a / b; 10 } *11 divide(); db> list 12 7 return undefined; 8 else 9 return a / b; 10 } 11 divide(); db> list 0 1 // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later 2 // SPDX-FileCopyrightText: 2021 Mantoh Nasah Kuma 3 function divide(a, b) { 4 if (b === 0) 5 return undefined; db> list divide Unknown option db> break 4 Breakpoint 1 at list.debugger.js:4:9 db> c Breakpoint 1, divide() at list.debugger.js:4:9 db> list 1 // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later 2 // SPDX-FileCopyrightText: 2021 Mantoh Nasah Kuma 3 function divide(a, b) { *4 if (b === 0) 5 return undefined; 6 else if (a === undefined || b === undefined) 7 return undefined; 8 else 9 return a / b; db> q Program exited with code 0 cjs-128.0/installed-tests/debugger/lastvalues.debugger.js0000664000175000017500000000031614771557763022464 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2020 Philip Chimento const a = undefined; const b = null; const c = 42; debugger; void (a, b, c); cjs-128.0/installed-tests/debugger/until.debugger0000664000175000017500000000022414771557763021017 0ustar fabiofabio# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2018 Philip Chimento until 5 upto 7 u 9 c cjs-128.0/installed-tests/debugger/list.debugger.js0000664000175000017500000000047014771557763021255 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2021 Mantoh Nasah Kuma function divide(a, b) { if (b === 0) return undefined; else if (a === undefined || b === undefined) return undefined; else return a / b; } divide(); cjs-128.0/installed-tests/debugger/continue.debugger.output0000664000175000017500000000052314771557763023051 0ustar fabiofabioGJS debugger. Type "help" for help db> # SPDX-License-Identifier: MIT OR LGPL-2.0-or-later db> # SPDX-FileCopyrightText: 2018 Philip Chimento db> continue Debugger statement, toplevel at continue.debugger.js:3:1 db> cont Debugger statement, toplevel at continue.debugger.js:4:1 db> c Program exited with code 0 cjs-128.0/installed-tests/debugger/delete.debugger0000664000175000017500000000034114771557763021126 0ustar fabiofabio# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2018 Philip Chimento b 4 b 5 b 6 b 7 # Check that breakpoint 4 still remains after deleting 1-3 delete 1 del 2 d 3 c c cjs-128.0/installed-tests/debugger/next.debugger0000664000175000017500000000022414771557763020642 0ustar fabiofabio# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2018 Philip Chimento c next n n n n n n n cjs-128.0/installed-tests/debugger/frame.debugger.js0000664000175000017500000000030114771557763021365 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2018 Philip Chimento function a() { b(); } function b() { debugger; } a(); cjs-128.0/installed-tests/debugger/print.debugger0000664000175000017500000000040014771557763021014 0ustar fabiofabio# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2018 Philip Chimento c # Simple types print a p b p c p d p e p f p g # Objects print h print/b h print/p h p i p/b i p j p k p/b k p l p m p n p o c cjs-128.0/installed-tests/debugger/throw.debugger.js0000664000175000017500000000035414771557763021446 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2018 Philip Chimento function a() { debugger; return 5; } try { a(); } catch (e) { print(`Exception: ${e}`); } cjs-128.0/installed-tests/debugger/.eslintrc.yml0000664000175000017500000000023714771557763020606 0ustar fabiofabio--- # SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2018 Philip Chimento rules: no-debugger: 'off' cjs-128.0/installed-tests/debugger/step.debugger0000664000175000017500000000022714771557763020642 0ustar fabiofabio# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2018 Philip Chimento s s s s s s s s s s s s cjs-128.0/installed-tests/debugger/backtrace.debugger.output0000664000175000017500000000307214771557763023146 0ustar fabiofabioGJS debugger. Type "help" for help db> # SPDX-License-Identifier: MIT OR LGPL-2.0-or-later db> # SPDX-FileCopyrightText: 2018 Philip Chimento db> backtrace #0 toplevel at backtrace.debugger.js:3:1 db> c Debugger statement, toplevel at backtrace.debugger.js:3:1 db> bt #0 toplevel at backtrace.debugger.js:3:1 db> c Debugger statement, ([object Array], 0, [object Array]) at backtrace.debugger.js:5:5 db> backtrace full #0 ([object Array], 0, [object Array]) at backtrace.debugger.js:5:5 arguments = [object Arguments] array = [object Array] #1 toplevel at backtrace.debugger.js:4:37 db> bt full #0 ([object Array], 0, [object Array]) at backtrace.debugger.js:5:5 arguments = [object Arguments] array = [object Array] #1 toplevel at backtrace.debugger.js:4:37 db> where #0 ([object Array], 0, [object Array]) at backtrace.debugger.js:5:5 #1 toplevel at backtrace.debugger.js:4:37 db> c Debugger statement, (1, 0, [object Array]) at backtrace.debugger.js:7:9 db> # test printing locals when exception is thrown before initialization of a value db> c 1 Unwinding due to exception. (Type 'c' to continue unwinding.) #0 mistake([object Array]) at backtrace.debugger.js:14:34 14 let {uninitialized_} = array.shift(); Exception value is: $1 = [object TypeError] TypeError: array.shift() is undefined db> bt full #0 mistake([object Array]) at backtrace.debugger.js:14:34 uninitialized_ = #1 toplevel at backtrace.debugger.js:16:8 db> q Program exited with code 0 cjs-128.0/installed-tests/debugger/lastvalues.debugger0000664000175000017500000000026114771557763022050 0ustar fabiofabio# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2020 Philip Chimento c p a p b p c p $1 p $2 p $3 p $$ p $6*3 p $$*3 c cjs-128.0/installed-tests/debugger/print.debugger.js0000664000175000017500000000125714771557763021442 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2018 Philip Chimento const {GObject} = imports.gi; const a = undefined; const b = null; const c = 42; const d = 'some string'; const e = false; const f = true; const g = Symbol('foobar'); const h = [1, 'money', 2, 'show', {three: 'to', 'get ready': 'go cat go'}]; const i = {some: 'plain object', that: 'has keys'}; const j = new Set([5, 6, 7]); const k = class J {}; const l = new GObject.Object(); const m = new Error('message'); const n = {a: 1}; const o = {some: 'plain object', [Symbol('that')]: 'has symbols'}; debugger; void (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o); cjs-128.0/installed-tests/debugger/throw-ignored.debugger.output0000664000175000017500000000072414771557763024020 0ustar fabiofabioGJS debugger. Type "help" for help db> # SPDX-License-Identifier: MIT OR LGPL-2.0-or-later db> # SPDX-FileCopyrightText: 2021 Florian Müllner db> c Caught exception: Error: Exception nº 1 Unwinding due to exception. (Type 'c' to continue unwinding.) #0 a() at throw-ignored.debugger.js:7:11 7 throw new Error(`Exception nº ${++count}`); Exception value is: $1 = [object Error] Error: Exception nº 2 db> q Program exited with code 0 cjs-128.0/installed-tests/debugger/continue.debugger0000664000175000017500000000021714771557763021512 0ustar fabiofabio# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2018 Philip Chimento continue cont c cjs-128.0/installed-tests/debugger/return.debugger.output0000664000175000017500000000156014771557763022546 0ustar fabiofabioGJS debugger. Type "help" for help db> # SPDX-License-Identifier: MIT OR LGPL-2.0-or-later db> # SPDX-FileCopyrightText: 2018 Philip Chimento db> b 4 Breakpoint 1 at return.debugger.js:4:5 db> b 8 Breakpoint 2 at return.debugger.js:8:5 db> b 12 Breakpoint 3 at return.debugger.js:12:5 db> c Breakpoint 1, func1() at return.debugger.js:4:5 db> f 1 #1 toplevel at return.debugger.js:15:7 15 print(func1()); db> return To return, you must select the newest frame (use 'frame 0') db> f 0 #0 func1() at return.debugger.js:4:5 4 return 1; db> return undefined Breakpoint 2, func2() at return.debugger.js:8:5 db> ret 5 5 Breakpoint 3, func3() at return.debugger.js:12:5 db> ret foo Exception caught while evaluating foo: [object ReferenceError] db> p 2 $1 = 2 db> ret `${4 * 10 + $1} is the answer` 42 is the answer Program exited with code 0 cjs-128.0/installed-tests/debugger/down-up.debugger.output0000664000175000017500000000151414771557763022617 0ustar fabiofabioGJS debugger. Type "help" for help db> # SPDX-License-Identifier: MIT OR LGPL-2.0-or-later db> # SPDX-FileCopyrightText: 2018 Philip Chimento db> c Debugger statement, d() at down-up.debugger.js:16:5 db> down Youngest frame selected; you cannot go down. db> up #1 c() at down-up.debugger.js:12:5 12 d(); db> up #2 b() at down-up.debugger.js:8:5 8 c(); db> up #3 a() at down-up.debugger.js:4:5 4 b(); db> up #4 toplevel at down-up.debugger.js:19:1 19 a(); db> up Initial frame selected; you cannot go up. db> down #3 a() at down-up.debugger.js:4:5 4 b(); db> dn #2 b() at down-up.debugger.js:8:5 8 c(); db> dn #1 c() at down-up.debugger.js:12:5 12 d(); db> dn #0 d() at down-up.debugger.js:16:5 16 debugger; db> c Program exited with code 0 cjs-128.0/installed-tests/debugger/finish.debugger.output0000664000175000017500000000116114771557763022504 0ustar fabiofabioGJS debugger. Type "help" for help db> # SPDX-License-Identifier: MIT OR LGPL-2.0-or-later db> # SPDX-FileCopyrightText: 2018 Philip Chimento db> c Print me Debugger statement, foo() at finish.debugger.js:5:5 db> finish Run till exit from foo() at finish.debugger.js:5:5 Print me also No value returned. toplevel at finish.debugger.js:16:1 db> c Print me Debugger statement, bar() at finish.debugger.js:11:5 db> fin Run till exit from bar() at finish.debugger.js:11:5 Print me also Value returned is: $1 = 5 toplevel at finish.debugger.js:17:1 db> c Print me at the end Program exited with code 0 cjs-128.0/installed-tests/debugger/next.debugger.js0000664000175000017500000000036514771557763021263 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2018 Philip Chimento function a() { debugger; b(); print('A line in a'); } function b() { print('A line in b'); } a(); cjs-128.0/installed-tests/debugger/backtrace.debugger.js0000664000175000017500000000062414771557763022222 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2018 Philip Chimento debugger; [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]].every(array => { debugger; array.every(num => { debugger; print(num); return false; }); return false; }); function mistake(array) { let {uninitialized_} = array.shift(); } mistake([]); cjs-128.0/installed-tests/debugger/breakpoint.debugger0000664000175000017500000000024014771557763022020 0ustar fabiofabio# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2018 Philip Chimento breakpoint 4 break 6 b 8 c c c c cjs-128.0/installed-tests/debugger/breakpoint.debugger.output0000664000175000017500000000106214771557763023362 0ustar fabiofabioGJS debugger. Type "help" for help db> # SPDX-License-Identifier: MIT OR LGPL-2.0-or-later db> # SPDX-FileCopyrightText: 2018 Philip Chimento db> breakpoint 4 Breakpoint 1 at breakpoint.debugger.js:4:1 db> break 6 Breakpoint 2 at breakpoint.debugger.js:6:5 db> b 8 Breakpoint 3 at breakpoint.debugger.js:8:1 db> c 1 Breakpoint 1, toplevel at breakpoint.debugger.js:4:1 db> c 2 Breakpoint 3, toplevel at breakpoint.debugger.js:8:1 db> c 3 Breakpoint 2, foo() at breakpoint.debugger.js:6:5 db> c Function foo Program exited with code 0 cjs-128.0/installed-tests/debugger/delete.debugger.js0000664000175000017500000000027514771557763021547 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2018 Philip Chimento print('1'); print('2'); print('3'); print('4'); print('5'); cjs-128.0/installed-tests/debugger/keys.debugger.output0000664000175000017500000000111414771557763022175 0ustar fabiofabioGJS debugger. Type "help" for help db> # SPDX-License-Identifier: MIT OR LGPL-2.0-or-later db> # SPDX-FileCopyrightText: 2018 Philip Chimento db> c Debugger statement, toplevel at keys.debugger.js:9:1 db> keys a "foo", "bar", "tres", Symbol("s") db> k a "foo", "bar", "tres", Symbol("s") db> keys a.foo a.foo is 1, not an object db> keys {} No own properties db> keys bar Exception caught while evaluating bar: [object ReferenceError] db> keys ['a', 'b', 'c'] "0", "1", "2", "length" db> keys Missing argument. See 'help keys' db> c Program exited with code 0 cjs-128.0/installed-tests/debugger/print.debugger.output0000664000175000017500000000232114771557763022357 0ustar fabiofabioGJS debugger. Type "help" for help db> # SPDX-License-Identifier: MIT OR LGPL-2.0-or-later db> # SPDX-FileCopyrightText: 2018 Philip Chimento db> c Debugger statement, toplevel at print.debugger.js:19:1 db> # Simple types db> print a $1 = undefined db> p b $2 = null db> p c $3 = 42 db> p d $4 = "some string" db> p e $5 = false db> p f $6 = true db> p g $7 = Symbol("foobar") db> # Objects db> print h $8 = [object Array] [1, "money", 2, "show", { three: "to", get ready: "go cat go" }] db> print/b h $9 = [object Array] [object Array] db> print/p h $10 = [object Array] [1, "money", 2, "show", { three: "to", get ready: "go cat go" }] db> p i $11 = [object Object] { some: "plain object", that: "has keys" } db> p/b i $12 = [object Object] [object Object] db> p j $13 = [object Set] {} db> p k $14 = [object Function] [ Function: J ] db> p/b k $15 = [object Function] [object Function] db> p l $16 = [object GObject_Object] [object instance wrapper GIName:GObject.Object jsobj@0xADDR native@0xADDR] db> p m $17 = [object Error] Error: message db> p n $18 = [object Object] { a: 1 } db> p o $19 = [object Object] { some: "plain object", [Symbol("that")]: "has symbols" } db> c Program exited with code 0 cjs-128.0/installed-tests/debugger/return.debugger.js0000664000175000017500000000043514771557763021622 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2018 Philip Chimento function func1() { return 1; } function func2() { return 2; } function func3() { return 3; } print(func1()); print(func2()); print(func3()); cjs-128.0/installed-tests/debugger/throw.debugger.output0000664000175000017500000000207314771557763022372 0ustar fabiofabioGJS debugger. Type "help" for help db> # SPDX-License-Identifier: MIT OR LGPL-2.0-or-later db> # SPDX-FileCopyrightText: 2018 Philip Chimento db> set ignoreCaughtExceptions false db> c Debugger statement, a() at throw.debugger.js:4:5 db> f 1 #1 toplevel at throw.debugger.js:9:5 9 a(); db> throw {} To throw, you must select the newest frame (use 'frame 0') db> f 0 #0 a() at throw.debugger.js:4:5 4 debugger; db> p 3.14 $1 = 3.14 db> throw 'foobar' + $1 Unwinding due to exception. (Type 'c' to continue unwinding.) #0 a() at throw.debugger.js:4:5 4 debugger; Exception value is: $2 = "foobar3.14" db> fin Run till exit from a() at throw.debugger.js:4:5 Frame terminated by exception: $3 = "foobar3.14" (To rethrow it, type 'throw'.) Unwinding due to exception. (Type 'c' to continue unwinding.) #0 toplevel at throw.debugger.js:9:5 9 a(); Exception value is: $4 = "foobar3.14" db> throw foo Exception caught while evaluating foo: [object ReferenceError] db> throw Exception: foobar3.14 Program exited with code 0 cjs-128.0/installed-tests/debugger/set.debugger.js0000664000175000017500000000024114771557763021071 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2018 Philip Chimento const a = {}; debugger; void a; cjs-128.0/installed-tests/debugger/quit.debugger.output0000664000175000017500000000031314771557763022204 0ustar fabiofabioGJS debugger. Type "help" for help db> # SPDX-License-Identifier: MIT OR LGPL-2.0-or-later db> # SPDX-FileCopyrightText: 2018 Philip Chimento db> q Program exited with code 0 cjs-128.0/installed-tests/debugger/detach.debugger0000664000175000017500000000020614771557763021114 0ustar fabiofabio# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2018 Philip Chimento detach cjs-128.0/installed-tests/debugger/detach.debugger.js0000664000175000017500000000021614771557763021530 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2018 Philip Chimento print('hi'); cjs-128.0/installed-tests/debugger/finish.debugger.js0000664000175000017500000000054514771557763021565 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2018 Philip Chimento function foo() { print('Print me'); debugger; print('Print me also'); } function bar() { print('Print me'); debugger; print('Print me also'); return 5; } foo(); bar(); print('Print me at the end'); cjs-128.0/installed-tests/debugger/step.debugger.output0000664000175000017500000000153414771557763022203 0ustar fabiofabioGJS debugger. Type "help" for help db> # SPDX-License-Identifier: MIT OR LGPL-2.0-or-later db> # SPDX-FileCopyrightText: 2018 Philip Chimento db> s toplevel at step.debugger.js:12:1 entered frame: a() at step.debugger.js:4:5 db> s a() at step.debugger.js:4:5 entered frame: b() at step.debugger.js:9:5 db> s b() at step.debugger.js:9:5 A line in b db> s b() at step.debugger.js:10:1 No value returned. db> s b() at step.debugger.js:10:1 a() at step.debugger.js:4:5 db> s a() at step.debugger.js:4:5 db> s a() at step.debugger.js:5:5 A line in a db> s a() at step.debugger.js:6:1 No value returned. db> s a() at step.debugger.js:6:1 toplevel at step.debugger.js:12:1 db> s toplevel at step.debugger.js:12:1 db> s toplevel at step.debugger.js:13:1 No value returned. db> s toplevel at step.debugger.js:13:1 Program exited with code 0 cjs-128.0/installed-tests/debugger/quit.debugger0000664000175000017500000000020114771557763020641 0ustar fabiofabio# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2018 Philip Chimento q cjs-128.0/installed-tests/debugger/keys.debugger0000664000175000017500000000030414771557763020636 0ustar fabiofabio# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2018 Philip Chimento c keys a k a keys a.foo keys {} keys bar keys ['a', 'b', 'c'] keys c cjs-128.0/installed-tests/debugger/down-up.debugger0000664000175000017500000000024514771557763021260 0ustar fabiofabio# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2018 Philip Chimento c down up up up up up down dn dn dn c cjs-128.0/installed-tests/debugger/finish.debugger0000664000175000017500000000022014771557763021140 0ustar fabiofabio# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2018 Philip Chimento c finish c fin c cjs-128.0/installed-tests/scripts/0000775000175000017500000000000014771557763016063 5ustar fabiofabiocjs-128.0/installed-tests/scripts/testWarnings.sh0000664000175000017500000000172214771557763021111 0ustar fabiofabio#!/bin/sh # SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2017 Philip Chimento if test "$GJS_USE_UNINSTALLED_FILES" = "1"; then gjs="$TOP_BUILDDIR/cjs-console" else gjs="cjs-console" fi total=0 report () { exit_code=$? total=$((total + 1)) if test $exit_code -eq 0; then echo "ok $total - $1" else echo "not ok $total - $1" fi } $gjs -c 'imports.signals.addSignalMethods({connect: "foo"})' 2>&1 | \ grep -q 'addSignalMethods is replacing existing .* connect method' report "overwriting method with Signals.addSignalMethods() should warn" $gjs -c 'imports.gi.GLib.get_home_dir("foobar")' 2>&1 | \ grep -q 'Too many arguments to .*: expected 0, got 1' report "passing too many arguments to a GI function should warn" $gjs -c '**' 2>&1 | \ grep -q 'SyntaxError.*@ :1:1' report "file and line number are logged for syntax errors" echo "1..$total" cjs-128.0/installed-tests/scripts/testCommandLine.sh0000664000175000017500000003114514771557763021511 0ustar fabiofabio#!/bin/sh # SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2016 Endless Mobile, Inc. # SPDX-FileCopyrightText: 2016 Philip Chimento if test "$GJS_USE_UNINSTALLED_FILES" = "1"; then gjs="$TOP_BUILDDIR/cjs-console" else gjs="cjs-console" fi # Avoid interference in the profiler tests from stray environment variable unset GJS_ENABLE_PROFILER # Avoid interference in the warning tests from G_DEBUG=fatal-warnings/criticals OLD_G_DEBUG="$G_DEBUG" # This JS script should exit immediately with code 42. If that is not working, # then it will exit after 3 seconds as a fallback, with code 0. cat <exit.js const GLib = imports.gi.GLib; let loop = GLib.MainLoop.new(null, false); GLib.idle_add(GLib.PRIORITY_LOW, () => imports.system.exit(42)); GLib.timeout_add_seconds(GLib.PRIORITY_HIGH, 3, () => loop.quit()); loop.run(); EOF # this JS script fails if either 1) --help is not passed to it, or 2) the string # "sentinel" is not in its search path cat <help.js const System = imports.system; if (imports.searchPath.indexOf('sentinel') == -1) System.exit(1); if (ARGV.indexOf('--help') == -1) System.exit(1); System.exit(0); EOF # this JS script should print one string (jobs are run before the interpreter # finishes) and should not print the other (jobs should not be run after the # interpreter is instructed to quit) cat <promise.js const System = imports.system; Promise.resolve().then(() => { print('Should be printed'); System.exit(42); }); Promise.resolve().then(() => print('Should not be printed')); EOF # this JS script should not cause an unhandled promise rejection cat <awaitcatch.js async function foo() { throw new Error('foo'); } async function bar() { try { await foo(); } catch (e) {} } bar(); EOF # this JS script should fail to import a second version of the same namespace cat <doublegi.js import 'gi://Gio?version=2.0'; import 'gi://Gio?version=75.94'; EOF # this JS script is used to test ARGV handling cat <argv.js const System = imports.system; if (System.programPath.endsWith('/argv.js')) System.exit(0); else System.exit(1); EOF # this JS script is used to test correct exiting from signal callbacks cat <signalexit.js import GLib from 'gi://GLib'; import GObject from 'gi://GObject'; import { exit } from 'system'; const Button = GObject.registerClass({ Signals: { 'clicked': {}, }, }, class Button extends GObject.Object { go() { this.emit('clicked'); } }); const button = new Button(); button.connect('clicked', () => exit(15)); let n = 1; GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 2, () => { print(\`click \${n++}\`); button.go(); return GLib.SOURCE_CONTINUE; }); const loop = new GLib.MainLoop(null, false); loop.run(); EOF # this is similar to exit.js but should exit with an unhandled promise rejection cat <promiseexit.js const {GLib} = imports.gi; const System = imports.system; const loop = GLib.MainLoop.new(null, false); Promise.reject(); GLib.idle_add(GLib.PRIORITY_LOW, () => System.exit(42)); GLib.timeout_add_seconds(GLib.PRIORITY_HIGH, 3, () => loop.quit()); loop.run(); EOF total=0 report () { exit_code=$? total=$((total + 1)) if test $exit_code -eq 0; then echo "ok $total - $1" else echo "not ok $total - $1" fi } report_xfail () { exit_code=$? total=$((total + 1)) if test $exit_code -eq 23; then echo "not ok $total - $1 (leaked memory)" elif test $exit_code -ne 0; then echo "ok $total - $1 (exit code $exit_code)" else echo "not ok $total - $1" fi } skip () { total=$((total + 1)) echo "ok $total - $1 # SKIP $2" } $gjs --invalid-option >/dev/null 2>/dev/null report_xfail "Invalid option should exit with failure" $gjs --invalid-option 2>&1 | grep -q invalid-option report "Invalid option should print a relevant message" # Test that System.exit() works in cjs-console $gjs -c 'imports.system.exit(0)' report "System.exit(0) should exit successfully" $gjs -c 'imports.system.exit(42)' test $? -eq 42 report "System.exit(42) should exit with the correct exit code" # Test the System.programPath works in cjs-console $gjs argv.js report "System.programPath should end in '/argv.js' when gjs argv.js is run" # FIXME: should check -eq 42 specifically, but in debug mode we will be # hitting an assertion. For this reason, skip when running under valgrind # since nothing will be freed. Also suppress LSan for the same reason. echo "# VALGRIND = $VALGRIND" if test -z $VALGRIND; then ASAN_OPTIONS=detect_leaks=0 $gjs exit.js test $? -ne 0 report "System.exit() should still exit across an FFI boundary" # https://gitlab.gnome.org/GNOME/gjs/-/issues/417 output="$(ASAN_OPTIONS=detect_leaks=0 $gjs promiseexit.js 2>&1)" test $? -ne 0 && printf '%s' "$output" | grep -q "Unhandled promise rejection" report "Unhandled promise rejections should still be printed when exiting" else skip "System.exit() should still exit across an FFI boundary" "running under valgrind" skip "Unhandled promise rejections should still be printed when exiting" "running under valgrind" fi # ensure the encoding of argv is being properly handled $gjs -c 'imports.system.exit((ARGV[0] !== "Valentín") ? 1 : 0)' "Valentín" report "Basic unicode encoding (accents, etc) should be functioning properly for ARGV and imports." $gjs -c 'imports.system.exit((ARGV[0] !== "☭") ? 1 : 0)' "☭" report "Unicode encoding for symbols should be functioning properly for ARGV and imports." # gjs --help prints GJS help $gjs --help >/dev/null report "--help should succeed" test -n "$($gjs --help)" report "--help should print something" # print GJS help even if it's not the first argument $gjs -I . --help >/dev/null report "should succeed when --help is not first arg" test -n "$($gjs -I . --help)" report "should print something when --help is not first arg" # --help before a script file name prints GJS help $gjs --help help.js >/dev/null report "--help should succeed before a script file" test -n "$($gjs --help help.js)" report "--help should print something before a script file" # --help before a -c argument prints GJS help script='imports.system.exit(1)' $gjs --help -c "$script" >/dev/null report "--help should succeed before -c" test -n "$($gjs --help -c "$script")" report "--help should print something before -c" # --help after a script file name is passed to the script $gjs -I sentinel help.js --help report "--help after script file should be passed to script" test -z "$($gjs -I sentinel help.js --help)" report "--help after script file should not print anything" # --help after a -c argument is passed to the script script='if(ARGV[0] !== "--help") imports.system.exit(1)' $gjs -c "$script" --help report "--help after -c should be passed to script" test -z "$($gjs -c "$script" --help)" report "--help after -c should not print anything" # -I after a program is not consumed by GJS # Temporary behaviour: still consume the argument, but give a warning # "$gjs" help.js --help -I sentinel # report_xfail "-I after script file should not be added to search path" # fi G_DEBUG=$(echo "$G_DEBUG" | sed -e 's/fatal-warnings,\{0,1\}//') $gjs help.js --help -I sentinel 2>&1 | grep -q 'Cjs-WARNING.*--include-path' report "-I after script should succeed but give a warning" $gjs -c 'imports.system.exit(0)' --coverage-prefix=foo --coverage-output=foo 2>&1 | grep -q 'Cjs-WARNING.*--coverage-prefix' report "--coverage-prefix after script should succeed but give a warning" $gjs -c 'imports.system.exit(0)' --coverage-prefix=foo --coverage-output=foo 2>&1 | grep -q 'Cjs-WARNING.*--coverage-output' report "--coverage-output after script should succeed but give a warning" rm -f foo/coverage.lcov G_DEBUG="$OLD_G_DEBUG" for version_arg in --version --jsversion; do # --version and --jsversion work $gjs $version_arg >/dev/null report "$version_arg should work" test -n "$($gjs $version_arg)" report "$version_arg should print something" # --version and --jsversion after a script go to the script script="if(ARGV[0] !== '$version_arg') imports.system.exit(1)" $gjs -c "$script" $version_arg report "$version_arg after -c should be passed to script" test -z "$($gjs -c "$script" $version_arg)" report "$version_arg after -c should not print anything" done # --profile rm -f gjs-*.syscap foo.syscap $gjs -c 'imports.system.exit(0)' && ! stat gjs-*.syscap > /dev/null 2>&1 report "no profiling data should be dumped without --profile" # Skip some tests if built without profiler support if $gjs --profile -c 1 2>&1 | grep -q 'Cjs-Message.*Profiler is disabled'; then reason="profiler is disabled" skip "--profile should dump profiling data to the default file name" "$reason" skip "--profile with argument should dump profiling data to the named file" "$reason" skip "GJS_ENABLE_PROFILER=1 should enable the profiler" "$reason" else rm -f gjs-*.syscap $gjs --profile -c 'imports.system.exit(0)' && stat gjs-*.syscap > /dev/null 2>&1 report "--profile should dump profiling data to the default file name" rm -f gjs-*.syscap $gjs --profile=foo.syscap -c 'imports.system.exit(0)' && test -f foo.syscap report "--profile with argument should dump profiling data to the named file" rm -f foo.syscap && rm -f gjs-*.syscap GJS_ENABLE_PROFILER=1 $gjs -c 'imports.system.exit(0)' && stat gjs-*.syscap > /dev/null 2>&1 report "GJS_ENABLE_PROFILER=1 should enable the profiler" rm -f gjs-*.syscap fi # interpreter handles queued promise jobs correctly output=$($gjs promise.js) test $? -eq 42 report "interpreter should exit with the correct exit code from a queued promise job" test -n "$output" -a -z "${output##*Should be printed*}" report "interpreter should run queued promise jobs before finishing" test -n "${output##*Should not be printed*}" report "interpreter should stop running jobs when one calls System.exit()" $gjs -c "Promise.resolve().then(() => { throw new Error(); });" 2>&1 | grep -q 'Cjs-WARNING.*Unhandled promise rejection.*[sS]tack trace' report "unhandled promise rejection should be reported" test -z "$($gjs awaitcatch.js)" report "catching an await expression should not cause unhandled rejection" # https://gitlab.gnome.org/GNOME/gjs/issues/18 G_DEBUG=$(echo "$G_DEBUG" | sed -e 's/fatal-warnings,\{0,1\}//') $gjs -c "(async () => await true)(); void foobar;" 2>&1 | grep -q 'ReferenceError: foobar is not defined' report "main program exceptions are not swallowed by queued promise jobs" G_DEBUG="$OLD_G_DEBUG" # https://gitlab.gnome.org/GNOME/gjs/issues/26 $gjs -c 'new imports.gi.Gio.Subprocess({argv: ["true"]}).init(null);' report "object unref from other thread after shutdown should not race" # https://gitlab.gnome.org/GNOME/gjs/issues/212 if test -n "$ENABLE_GTK"; then G_DEBUG=$(echo "$G_DEBUG" | sed -e 's/fatal-warnings,\{0,1\}//' -e 's/fatal-criticals,\{0,1\}//') $gjs -c 'imports.gi.versions.Gtk = "3.0"; const Gtk = imports.gi.Gtk; const GObject = imports.gi.GObject; Gtk.init(null); let BadWidget = GObject.registerClass(class BadWidget extends Gtk.Widget { vfunc_destroy() {}; }); let w = new BadWidget ();' report "avoid crashing when GTK vfuncs are called on context destroy" G_DEBUG="$OLD_G_DEBUG" else skip "avoid crashing when GTK vfuncs are called on context destroy" "GTK disabled" fi # https://gitlab.gnome.org/GNOME/gjs/-/issues/322 $gjs --coverage-prefix=$(pwd) --coverage-output=$(pwd) awaitcatch.js grep -q TN: coverage.lcov report "coverage prefix is treated as an absolute path" rm -f coverage.lcov $gjs -m doublegi.js 2>&1 | grep -q 'already loaded' report "avoid statically importing two versions of the same module" # https://gitlab.gnome.org/GNOME/gjs/-/issues/19 echo "# VALGRIND = $VALGRIND" if test -z $VALGRIND; then output=$(env LSAN_OPTIONS=detect_leaks=0 ASAN_OPTIONS=detect_leaks=0 \ $gjs -m signalexit.js) test $? -eq 15 report "exit with correct code from a signal callback" test -n "$output" -a -z "${output##*click 1*}" report "avoid asserting when System.exit is called from a signal callback" test -n "${output##*click 2*}" report "exit after first System.exit call in a signal callback" else skip "exit with correct code from a signal callback" "running under valgrind" skip "avoid asserting when System.exit is called from a signal callback" "running under valgrind" skip "exit after first System.exit call in a signal callback" "running under valgrind" fi rm -f exit.js help.js promise.js awaitcatch.js doublegi.js argv.js \ signalexit.js promiseexit.js echo "1..$total" cjs-128.0/installed-tests/scripts/testExamples.sh0000664000175000017500000000211614771557763021075 0ustar fabiofabio#!/bin/bash # SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2018 Claudio André DIR="$( cd "$( dirname "${0}" )" && pwd )" source "${DIR}"/common.sh # Run the examples $gjs -m examples/gio-cat.js meson.build report "run the gio-cat.js example" if [[ -n "${ENABLE_GTK}" ]]; then export graphical_gjs="xvfb-run -a dbus-run-session -- $gjs" eval timeout 5s $graphical_gjs -m examples/calc.js report_timeout "run the calc.js example" eval timeout 5s $graphical_gjs -m examples/gtk3.js report_timeout "run the gtk3.js example" eval timeout 5s $graphical_gjs -m examples/gtk-application.js report_timeout "run the gtk-application.js example" eval timeout 5s $graphical_gjs -m examples/gettext.js report_timeout "run the gettext.js example" else skip "run the calc.js example" "running without GTK" skip "run the gtk3.js example" "running without GTK" skip "run the gtk-application.js example" "running without GTK" skip "run the gettext.js example" "running without GTK" fi echo "1..$total" cjs-128.0/installed-tests/scripts/common.sh0000664000175000017500000000201614771557763017706 0ustar fabiofabio#!/bin/sh # SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2016 Philip Chimento if test "$GJS_USE_UNINSTALLED_FILES" = "1"; then gjs="$TOP_BUILDDIR/cjs-console" else gjs="cjs-console" fi # Avoid interference in the profiler tests from stray environment variable unset GJS_ENABLE_PROFILER total=0 report () { exit_code=$? total=$((total + 1)) if test $exit_code -eq 0; then echo "ok $total - $1" else echo "not ok $total - $1 [EXIT CODE: $exit_code]" fi } report_timeout () { exit_code=$? total=$((total + 1)) if test $exit_code -eq 0 -o $exit_code -eq 124; then echo "ok $total - $1" else echo "not ok $total - $1 [EXIT CODE: $exit_code]" fi } report_xfail () { exit_code=$? total=$((total + 1)) if test $exit_code -ne 0; then echo "ok $total - $1" else echo "not ok $total - $1" fi } skip () { total=$((total + 1)) echo "ok $total - $1 # SKIP $2" } cjs-128.0/installed-tests/scripts/testCommandLineModules.sh0000664000175000017500000000440414771557763023040 0ustar fabiofabio#!/bin/sh # SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2016 Endless Mobile, Inc. # SPDX-FileCopyrightText: 2016 Philip Chimento if test "$GJS_USE_UNINSTALLED_FILES" = "1"; then gjs="$TOP_BUILDDIR/cjs-console" else gjs="cjs-console" fi total=0 report () { exit_code=$? total=$((total + 1)) if test $exit_code -eq 0; then echo "ok $total - $1" else echo "not ok $total - $1" fi } # Avoid interference in the profiler tests from stray environment variable unset GJS_ENABLE_PROFILER # Avoid interference in the warning tests from G_DEBUG=fatal-warnings/criticals OLD_G_DEBUG="$G_DEBUG" cat <doubledynamicImportee.js export function noop() {} EOF # this JS script should succeed without an error on the second import cat <doubledynamic.js let done = false; import("./doubledynamicImportee.js") .then(ddi => { ddi.noop(); }) .finally(() => { if (done) imports.mainloop.quit(); done = true; }); import("./doubledynamicImportee.js") .then(ddi => { ddi.noop(); }) .finally(() => { if (done) imports.mainloop.quit(); done = true; }); imports.mainloop.run(); EOF $gjs doubledynamic.js report "ensure dynamic imports load even if the same import resolves elsewhere first" cat <dynamicImplicitMainloopImportee.js export const EXIT_CODE = 21; EOF cat <dynamicImplicitMainloop.js import("./dynamicImplicitMainloopImportee.js") .then(({ EXIT_CODE }) => { imports.system.exit(EXIT_CODE); }); EOF $gjs dynamicImplicitMainloop.js test $? -eq 21 report "ensure dynamic imports resolve without an explicit mainloop" cat <dynamicTopLevelAwaitImportee.js export const EXIT_CODE = 32; EOF cat <dynamicTopLevelAwait.js const {EXIT_CODE} = await import("./dynamicTopLevelAwaitImportee.js") const system = await import('system'); system.exit(EXIT_CODE); EOF $gjs -m dynamicTopLevelAwait.js test $? -eq 32 report "ensure top level await can import modules" rm -f doubledynamic.js doubledynamicImportee.js \ dynamicImplicitMainloop.js dynamicImplicitMainloopImportee.js \ dynamicTopLevelAwait.js dynamicTopLevelAwaitImportee.js echo "1..$total" cjs-128.0/installed-tests/minijasmine-module.test.in0000664000175000017500000000036114771557763021470 0ustar fabiofabio# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2020 Philip Chimento [Test] Type=session Exec=@installed_tests_execdir@/minijasmine @installed_tests_execdir@/js/@name@ -m Output=TAP cjs-128.0/installed-tests/minijasmine.cpp0000664000175000017500000000643114771557763017407 0ustar fabiofabio/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2016 Philip Chimento #include // for setlocale, LC_ALL #include #include // for exit #include #include #include #include #include #include [[noreturn]] static void bail_out(GjsContext* gjs_context, const char* msg) { g_object_unref(gjs_context); g_print("Bail out! %s\n", msg); exit(1); } [[noreturn]] static void bail_out(GjsContext* gjs_context, GError* error) { g_print("Bail out! %s\n", error->message); g_object_unref(gjs_context); g_error_free(error); exit(1); } int main(int argc, char **argv) { if (argc < 2) g_error("Need a test file"); g_setenv("GJS_DEBUG_OUTPUT", "stderr", false); setlocale(LC_ALL, ""); if (g_getenv("GJS_USE_UNINSTALLED_FILES") != NULL) { g_irepository_prepend_search_path(g_getenv("TOP_BUILDDIR")); } else { g_irepository_prepend_search_path(INSTTESTDIR); g_irepository_prepend_library_path(INSTTESTDIR); } const char *coverage_prefix = g_getenv("GJS_UNIT_COVERAGE_PREFIX"); const char *coverage_output_path = g_getenv("GJS_UNIT_COVERAGE_OUTPUT"); const char *search_path[] = { "resource:///org/cjs/jsunit", NULL }; if (coverage_prefix) gjs_coverage_enable(); GjsContext *cx = gjs_context_new_with_search_path((char **)search_path); GjsCoverage *coverage = NULL; if (coverage_prefix) { const char *coverage_prefixes[2] = { coverage_prefix, NULL }; if (!coverage_output_path) { bail_out(cx, "GJS_UNIT_COVERAGE_OUTPUT is required when using GJS_UNIT_COVERAGE_PREFIX"); } GFile *output = g_file_new_for_commandline_arg(coverage_output_path); coverage = gjs_coverage_new(coverage_prefixes, cx, output); g_object_unref(output); } GError *error = NULL; bool success; uint8_t code; uint8_t u8_exitcode_ignored; int exitcode_ignored; if (!gjs_context_eval_module_file( cx, "resource:///org/cjs/jsunit/minijasmine.js", &u8_exitcode_ignored, &error)) bail_out(cx, error); bool eval_as_module = argc >= 3 && strcmp(argv[2], "-m") == 0; if (eval_as_module) { success = gjs_context_eval_module_file(cx, argv[1], &u8_exitcode_ignored, &error); } else { success = gjs_context_eval_file(cx, argv[1], &exitcode_ignored, &error); } if (!success) bail_out(cx, error); success = gjs_context_eval_module_file( cx, "resource:///org/cjs/jsunit/minijasmine-executor.js", &code, &error); if (!success) bail_out(cx, error); if (coverage) { gjs_coverage_write_statistics(coverage); g_clear_object(&coverage); } gjs_memory_report("before destroying context", false); g_object_unref(cx); gjs_memory_report("after destroying context", true); /* For TAP, should actually be return 0; as a nonzero return code would * indicate an error in the test harness. But that would be quite silly * when running the tests outside of the TAP driver. */ return code; } cjs-128.0/installed-tests/debugger.test.in0000664000175000017500000000037114771557763017467 0ustar fabiofabio# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2018 Philip Chimento [Test] Type=session Exec=@installed_tests_execdir@/debugger-test.sh @installed_tests_execdir@/debugger/@name@ Output=TAP cjs-128.0/installed-tests/extra/0000775000175000017500000000000014771557763015517 5ustar fabiofabiocjs-128.0/installed-tests/extra/lsan.supp0000664000175000017500000000121414771557763017363 0ustar fabiofabio# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2017 Endless Mobile, Inc. # SpiderMonkey leaks a mutex for each GC helper thread. leak:js::HelperThread::threadLoop # https://bugs.freedesktop.org/show_bug.cgi?id=105466 leak:libfontconfig.so.1 # https://bugzilla.mozilla.org/show_bug.cgi?id=1478679 leak:js::coverage::LCovSource::writeScript leak:js/src/util/Text.cpp # GIO Module instances are created once and they're expected to be "leaked" leak:g_io_module_new # Gtk test may leak because of a Gdk/X11 issue: # https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/6037 leak:gdk_x11_selection_input_stream_new_async cjs-128.0/installed-tests/extra/tsan.supp0000664000175000017500000000072014771557763017374 0ustar fabiofabio# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2021 Canonical Ltd. # When calling a closure we expect the async function to initialize # memory that is later managed by the worker thread, if something is not # synchronized, this is not a gjs issue so we can ignore it. # Also those are mostly false positive as it can be tested by removing # this line and testing with glib compiled with -Db_sanitize=thread race:gjs_closure_invoke cjs-128.0/installed-tests/extra/gjs.supp0000664000175000017500000000501014771557763017207 0ustar fabiofabio# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2008 litl, LLC # Valgrind suppressions file for GJS # This is intended to be used in addition to GLib's glib.supp file. # SpiderMonkey leaks { mozjs-thread-stack-init Memcheck:Leak match-leak-kinds: possible fun:calloc ... fun:pthread_create@@GLIBC_2.2.5 fun:_ZN7mozilla9TimeStamp20ComputeProcessUptimeEv fun:_ZN7mozilla9TimeStamp15ProcessCreationEPb fun:_ZN2JS6detail25InitWithFailureDiagnosticEb fun:_Z7JS_Initv } # Various things that I don't believe are related to GJS { gtk-style-context Memcheck:Leak match-leak-kinds: possible fun:malloc fun:g_malloc ... fun:gtk_css_node_declaration_make_writable* ... fun:gtk_style_constructed } # https://bugs.freedesktop.org/show_bug.cgi?id=105466 { freedesktop-bug-105466 Memcheck:Leak match-leak-kinds: definite fun:malloc ... fun:FcConfigSubstituteWithPat fun:_cairo_ft_resolve_pattern fun:_cairo_ft_font_face_get_implementation fun:cairo_scaled_font_create fun:_cairo_gstate_ensure_scaled_font ... fun:_cairo_default_context_get_scaled_font fun:cairo_show_text } # Data that Cairo keeps around for the process lifetime # This could be freed by calling cairo_debug_reset_static_data(), but it's # not a good idea to call that function in production, because certain versions # of Cairo have bugs that cause it to fail assertions and crash. { cairo-static-data Memcheck:Leak match-leak-kinds: definite fun:malloc ... fun:FcPatternDuplicate fun:_cairo_ft_font_face_create_for_pattern ... fun:_cairo_gstate_ensure_scaled_font ... fun:_cairo_default_context_get_scaled_font ... fun:cairo_show_text } # https://gitlab.gnome.org/GNOME/gobject-introspection/issues/265 { gobject-introspection-default-repository Memcheck:Leak match-leak-kinds: definite fun:realloc ... fun:build_typelib_key fun:register_internal } # Workaround for https://github.com/mesonbuild/meson/issues/4427 # When fixed, valgrind should already not trace bash { bash-workaround Memcheck:Leak match-leak-kinds: definite fun:malloc fun:xmalloc fun:set_default_locale fun:main } # https://gitlab.gnome.org/GNOME/glib/-/issues/1911 { g-type-register-static Memcheck:Leak match-leak-kinds:possible fun:malloc ... fun:g_type_register_static } { g-type-register-static-calloc Memcheck:Leak match-leak-kinds:possible fun:calloc ... fun:g_type_register_static } cjs-128.0/meson.build0000664000175000017500000007146714771557763013436 0ustar fabiofabio# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2019 Philip Chimento # SPDX-FileCopyrightText: 2019 Chun-wei Fan project( 'cjs', 'cpp', 'c', version : '128.0', license : ['MIT', 'LGPL2+'], meson_version : '>= 0.62.0', default_options : [ 'cpp_std=c++17', 'cpp_rtti=false', 'cpp_eh=none', 'c_std=c99', 'warning_level=2', 'b_pch=true' ] ) # cpp_rtti: SpiderMonkey can be compiled with or without runtime type # information, and the default is without. We must match that option because we # need to derive from SpiderMonkey classes. api_version = '1.0' api_name = '@0@-@1@'.format(meson.project_name(), api_version) gnome = import('gnome') pkg = import('pkgconfig') top_include = include_directories('.') prefix = get_option('prefix') bindir = get_option('bindir') libdir = get_option('libdir') datadir = get_option('datadir') libexecdir = get_option('libexecdir') cjsjsdir = datadir / api_name pkglibdir = libdir / meson.project_name() installed_tests_execdir = libexecdir / 'installed-tests' / meson.project_name() installed_tests_metadir = datadir / 'installed-tests' / meson.project_name() ### Check for conflicting build options ######################################## if get_option('systemtap') and not get_option('dtrace') error('-Ddtrace=true is required for -Dsystemtap=true') endif release_build = get_option('buildtype').startswith('release') if release_build and get_option('verbose_logs') error('-Dverbose_logs=true is not allowed with --buildtype=release') endif ### Check for compiler args #################################################### cxx = meson.get_compiler('cpp') cc = meson.get_compiler('c') if cc.get_id() == 'msvc' add_project_arguments(cxx.get_supported_arguments([ '-utf-8', # Use UTF-8 mode '/Zc:externConstexpr', # Required for 'extern constexpr' on MSVC '/Zc:preprocessor', # Required to consume the mozjs-128 headers on MSVC # Ignore spurious compiler warnings for things that GLib and SpiderMonkey # header files commonly do '-FImsvc_recommended_pragmas.h', '-EHsc', '-D_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS', # Don't worry about the C++17 deprecations '-D__PRETTY_FUNCTION__=__FUNCSIG__', '-wd4099', '-wd4251', '-wd4291', '-wd4800', '-wd5030', ]), language: ['cpp', 'c']) else # Ignore spurious compiler warnings for things that GLib and SpiderMonkey # header files commonly do add_project_arguments(cxx.get_supported_arguments([ '-fno-strict-aliasing', '-Wno-variadic-macros', # GLib uses these in header files '-Wno-missing-field-initializers', # SpiderMonkey JSClass, among others '-Wno-dangling-pointer', # Root list in JS::Rooted with GCC 12 ]), language: 'cpp') add_project_arguments(cc.get_supported_arguments([ '-Wno-typedef-redefinition', # GLib does this in header files ]), language: 'c') endif if cc.get_argument_syntax() == 'msvc' add_project_arguments(cxx.get_supported_arguments([ '-Dssize_t=gssize', # Windows SDK/MSVC headers do not come with ssize_t '-DNOMINMAX', # We don't want 'min' or 'max' to interfere '-DSSIZE_MAX=G_MAXSSIZE', # Windows SDK/MSVC headers do not come with SSIZE_MAX ]), language: ['cpp', 'c']) else if get_option('bsymbolic_functions') if not cxx.has_link_argument('-Bsymbolic-functions') error('''-Bsymbolic-functions not supported, configure with -Dbsymbolic_functions=false''') endif add_project_link_arguments('-Bsymbolic-functions', language: ['cpp', 'c']) if cc.has_argument('-fno-semantic-interposition') add_project_arguments('-fno-semantic-interposition', language: 'c') endif if cxx.has_argument('-fno-semantic-interposition') add_project_arguments('-fno-semantic-interposition', language: 'cpp') endif endif endif # -fno-rtti is not compatible with the vptr sanitizer (part of ubsan) if not get_option('cpp_rtti') and get_option('b_sanitize') != 'none' and \ cxx.has_argument('-fno-sanitize=vptr') add_project_arguments('-fno-sanitize=vptr', language: 'cpp') endif if get_option('verbose_logs') add_project_arguments([ '-DGJS_VERBOSE_ENABLE_PROPS=1', '-DGJS_VERBOSE_ENABLE_MARSHAL=1', '-DGJS_VERBOSE_ENABLE_LIFECYCLE=1', '-DGJS_VERBOSE_ENABLE_GI_USAGE=1', '-DGJS_VERBOSE_ENABLE_GCLOSURE=1', '-DGJS_VERBOSE_ENABLE_GSIGNAL=1', ], language: 'cpp') endif if release_build add_project_arguments('-DG_DISABLE_CAST_CHECKS', language: ['c', 'cpp']) endif ### Check for required libraries ############################################### null_dep = dependency('', required : false) # Note: Notify GNOME release team when adding or updating dependencies glib_required_version = '>= 2.66.0' glib = dependency('glib-2.0', version: glib_required_version, fallback: ['glib', 'libglib_dep']) gthread = dependency('gthread-2.0', version: glib_required_version, fallback: ['glib', 'libgthread_dep']) gobject = dependency('gobject-2.0', version: glib_required_version, fallback: ['glib', 'libgobject_dep']) gio = dependency('gio-2.0', version: glib_required_version, fallback: ['glib', 'libgio_dep']) ffi = dependency('libffi', fallback: ['libffi', 'ffi_dep']) gi = dependency('gobject-introspection-1.0', version: '>= 1.66.0', fallback: ['gobject-introspection', 'girepo_dep']) cairo = dependency('cairo', fallback: ['cairo', 'libcairo_dep']) cairo_gobject = dependency('cairo-gobject', fallback: ['cairo', 'libcairogobject_dep']) cairo_xlib = dependency('cairo-xlib', required: false) spidermonkey = dependency('mozjs-128') sysprof_capture = dependency('sysprof-capture-4', required: get_option('profiler'), include_type: 'system', fallback: ['sysprof', 'libsysprof_capture_dep'], default_options: [ 'agent=false', 'examples=false', 'gtk=false', 'tests=false', 'tools=false', 'libsysprof=false', 'sysprofd=none', 'help=false', ]) readline = cxx.find_library('readline', required: get_option('readline')) # On some systems we need to link readline to a termcap compatible library readline_code = ''' #include #include int main(void) { readline("foo"); return 0; }''' readline_deps = [readline] if readline.found() and not cxx.links(readline_code, dependencies: readline) extra_readline_libs = ['ncursesw', 'ncurses', 'curses', 'termcap'] found = false foreach lib : extra_readline_libs termcap = cxx.find_library(lib, required: false) if cxx.links(readline_code, dependencies: [readline, termcap]) found = true readline_deps += termcap break endif endforeach if not found error('''Couldn't figure out how to link readline library. Configure with -Dreadline=disabled to skip the readline features.''') endif endif if cxx.links(''' #include int main(void) { std::atomic_int64_t value = ATOMIC_VAR_INIT(0); return value.load(); } ''', name: '64-bit atomics built-in') libatomic = null_dep else libatomic = cc.find_library('atomic', required: false) endif build_profiler = sysprof_capture.found() profiler_deps = [sysprof_capture] if build_profiler and not cxx.has_function('timer_settime') extra_timer_libs = ['rt', 'posix4'] found = false foreach lib : extra_timer_libs timer_lib = cxx.find_library(lib, required: false) if cxx.has_function('timer_settime', dependencies: timer_lib) found = true profiler_deps += timer_lib break endif endforeach if not found or not cxx.has_header_symbol('signal.h', 'SIGEV_THREAD_ID') if get_option('profiler').enabled() error('''The profiler is currently only supported on Linux. The standard library must support timer_settime() and SIGEV_THREAD_ID. Configure with -Dprofiler=auto or -Dprofiler=disabled to skip it on other platforms.''') endif build_profiler = false endif endif build_readline = readline.found() ### Check for library features ################################################# # Check if SpiderMonkey was compiled with --enable-debug. If this is the case, # you must compile all your sources with -DDEBUG=1 # See https://bugzilla.mozilla.org/show_bug.cgi?id=1261161 debug_arg = [] nondebug_spidermonkey = cxx.compiles(''' #include #ifdef JS_DEBUG #error debug yes, if we did not already error out due to DEBUG not being defined #endif ''', dependencies: spidermonkey, name: 'SpiderMonkey is a non-debug build') if not nondebug_spidermonkey debug_arg = ['-DDEBUG'] # for compile tests endif if release_build and not nondebug_spidermonkey error('''You are trying to make a release build with a debug-enabled copy of SpiderMonkey. This is probably not what you want, since it will have bad performance and is not binary-compatible with release builds of SpiderMonkey. Try configuring SpiderMonkey with --disable-debug.''') endif # Check if a minimal SpiderMonkey program compiles, links, and runs. If not, # it's most likely the case that SpiderMonkey was configured incorrectly, for # example by building mozglue as a shared library. minimal_program = cxx.run(''' #include int main(void) { if (!JS_Init()) return 1; JS_ShutDown(); return 0; } ''', args: debug_arg, dependencies: spidermonkey, name: 'SpiderMonkey sanity check') recommended_configuration = ''' Check the recommended configuration: https://github.com/spidermonkey-embedders/spidermonkey-embedding-examples/blob/esr91/docs/Building%20SpiderMonkey.md''' if not minimal_program.compiled() error('''A minimal SpiderMonkey program could not be compiled or linked. Most likely you should build it with a different configuration.''' + recommended_configuration) elif meson.is_cross_build() warning('''This is a cross build. A check that a minimal SpiderMonkey program executes will not be performed. Before shipping GJS, you should check that it does not crash on startup, since building SpiderMonkey with the wrong configuration may cause that.''' + recommended_configuration) elif minimal_program.returncode() != 0 error('''A minimal SpiderMonkey program failed to execute. Most likely you should build it with a different configuration.''' + recommended_configuration) endif have_printf_alternative_int = cc.compiles(''' #include int main(void) { printf("%Id", (int)0); return 0; } ''', args: ['-Werror', '-Wformat'], name: 'printf() supports %I alternative int syntax') ### Check for external programs ################################################ dtrace = find_program('dtrace', required: get_option('dtrace')) dbus_run_session = find_program('dbus-run-session', required: not get_option('skip_dbus_tests')) glib_compile_schemas = find_program('glib-compile-schemas') ### Generate config.h ########################################################## header_conf = configuration_data() versions = meson.project_version().split('.') major_version = versions[0].to_int() header_conf.set_quoted('VERSION', meson.project_version()) header_conf.set('GJS_VERSION', major_version, description: 'The GJS version as an integer') header_conf.set_quoted('PACKAGE_STRING', '@0@ @1@'.format(meson.project_name(), meson.project_version())) header_conf.set('ENABLE_PROFILER', build_profiler, description: 'Build the profiler') # COMPAT: SpiderMonkey headers in some places use DEBUG instead of JS_DEBUG # https://bugzilla.mozilla.org/show_bug.cgi?id=1261161 */ header_conf.set('DEBUG', not nondebug_spidermonkey, description: 'SpiderMonkey was compiled with --enable-debug') header_conf.set('HAVE_DTRACE', get_option('dtrace'), description: 'Using dtrace probes') header_conf.set('HAVE_PRINTF_ALTERNATIVE_INT', have_printf_alternative_int, description: 'printf() accepts "%Id" for alternative integer output') if build_readline header_conf.set('HAVE_READLINE_READLINE_H', cxx.check_header('readline/readline.h', prefix: '#include ', required: readline.found())) endif header_conf.set('USE_UNITY_BUILD', get_option('unity')) header_conf.set('HAVE_SYS_SYSCALL_H', cxx.check_header('sys/syscall.h')) header_conf.set('HAVE_UNISTD_H', cxx.check_header('unistd.h')) header_conf.set('HAVE_SIGNAL_H', cxx.check_header('signal.h', required: build_profiler)) # enable GNU extensions on systems that have them header_conf.set('_GNU_SOURCE', 1) configure_file(output: 'config.h', configuration: header_conf) ### Build dtrace probes ######################################################## if get_option('dtrace') probes_header_gen = generator(dtrace, output: '@BASENAME@.h', arguments: ['-C', '-h', '-s', '@INPUT@', '-o', '@OUTPUT@']) probes_objfile_gen = generator(dtrace, output: '@BASENAME@.o', arguments: ['-G', '-s', '@INPUT@', '-o', '@OUTPUT@']) probes_header = probes_header_gen.process('gi/cjs_gi_probes.d') probes_objfile = probes_objfile_gen.process('gi/cjs_gi_probes.d') else probes_header = [] probes_objfile = [] endif tapset_subst = configuration_data({'EXPANDED_LIBDIR': libdir}) tapset = configure_file(input: 'cjs/cjs.stp.in', output: 'cjs.stp', configuration: tapset_subst) if get_option('systemtap') install_data(tapset, install_dir: datadir / 'systemtap' / 'tapset') endif ### Build library ############################################################## directory_defines = [ '-DGJS_JS_DIR="@0@"'.format(prefix / cjsjsdir), '-DPKGLIBDIR="@0@"'.format(prefix / pkglibdir), ] cjs_public_headers = [ 'cjs/context.h', 'cjs/coverage.h', 'cjs/error-types.h', 'cjs/gjs.h', 'cjs/macros.h', 'cjs/mem.h', 'cjs/profiler.h', ] # For historical reasons, some files live in gi/ # Some headers in the following list were formerly public libcjs_sources = [ 'gi/arg.cpp', 'gi/arg.h', 'gi/arg-inl.h', 'gi/arg-cache.cpp', 'gi/arg-cache.h', 'gi/boxed.cpp', 'gi/boxed.h', 'gi/closure.cpp', 'gi/closure.h', 'gi/cwrapper.cpp', 'gi/cwrapper.h', 'gi/enumeration.cpp', 'gi/enumeration.h', 'gi/foreign.cpp', 'gi/foreign.h', 'gi/fundamental.cpp', 'gi/fundamental.h', 'gi/function.cpp', 'gi/function.h', 'gi/gerror.cpp', 'gi/gerror.h', 'gi/cjs_gi_trace.h', 'gi/gobject.cpp', 'gi/gobject.h', 'gi/gtype.cpp', 'gi/gtype.h', 'gi/interface.cpp', 'gi/interface.h', 'gi/ns.cpp', 'gi/ns.h', 'gi/object.cpp', 'gi/object.h', 'gi/param.cpp', 'gi/param.h', 'gi/private.cpp', 'gi/private.h', 'gi/repo.cpp', 'gi/repo.h', 'gi/toggle.cpp', 'gi/toggle.h', 'gi/union.cpp', 'gi/union.h', 'gi/utils-inl.h', 'gi/value.cpp', 'gi/value.h', 'gi/wrapperutils.cpp', 'gi/wrapperutils.h', 'cjs/atoms.cpp', 'cjs/atoms.h', 'cjs/byteArray.cpp', 'cjs/byteArray.h', 'cjs/context.cpp', 'cjs/context-private.h', 'cjs/coverage.cpp', 'cjs/debugger.cpp', 'cjs/deprecation.cpp', 'cjs/deprecation.h', 'cjs/engine.cpp', 'cjs/engine.h', 'cjs/error-types.cpp', 'cjs/global.cpp', 'cjs/global.h', 'cjs/importer.cpp', 'cjs/importer.h', 'cjs/internal.cpp', 'cjs/internal.h', 'cjs/mainloop.cpp', 'cjs/mainloop.h', 'cjs/mem.cpp', 'cjs/mem-private.h', 'cjs/module.cpp', 'cjs/module.h', 'cjs/native.cpp', 'cjs/native.h', 'cjs/objectbox.cpp', 'cjs/objectbox.h', 'cjs/profiler.cpp', 'cjs/profiler-private.h', 'cjs/text-encoding.cpp', 'cjs/text-encoding.h', 'cjs/promise.cpp', 'cjs/promise.h', 'cjs/stack.cpp', 'modules/console.cpp', 'modules/console.h', 'modules/print.cpp', 'modules/print.h', 'modules/system.cpp', 'modules/system.h', 'modules/cairo-private.h', 'modules/cairo-module.h', 'modules/cairo-region.cpp', 'modules/cairo-context.cpp', 'modules/cairo-path.cpp', 'modules/cairo-surface.cpp', 'modules/cairo-image-surface.cpp', 'modules/cairo-ps-surface.cpp', 'modules/cairo-pdf-surface.cpp', 'modules/cairo-svg-surface.cpp', 'modules/cairo-pattern.cpp', 'modules/cairo-gradient.cpp', 'modules/cairo-linear-gradient.cpp', 'modules/cairo-radial-gradient.cpp', 'modules/cairo-surface-pattern.cpp', 'modules/cairo-solid-pattern.cpp', 'modules/cairo.cpp', ] # CjsPrivate introspection sources libcjs_private_sources = [ 'libgjs-private/gjs-gdbus-wrapper.c', 'libgjs-private/gjs-gdbus-wrapper.h', 'libgjs-private/gjs-match-info.c', 'libgjs-private/gjs-match-info.h', 'libgjs-private/gjs-util.c', 'libgjs-private/gjs-util.h', ] libcjs_jsapi_sources = [ 'cjs/jsapi-class.h', 'cjs/jsapi-dynamic-class.cpp', 'cjs/jsapi-util-args.h', 'cjs/jsapi-util-error.cpp', 'cjs/jsapi-util-root.h', 'cjs/jsapi-util-string.cpp', 'cjs/jsapi-util.cpp', 'cjs/jsapi-util.h', 'util/console.cpp', 'util/console.h', 'util/log.cpp', 'util/log.h', 'util/misc.cpp', 'util/misc.h', ] module_resource_srcs = gnome.compile_resources('js-resources', 'js.gresource.xml', c_name: 'js_resources') module_resource_lib = static_library('js-resources', module_resource_srcs, dependencies: gio, override_options: ['unity=off']) libcjs_dependencies = [glib, gobject, gthread, gio, gi, ffi, cairo, cairo_gobject, spidermonkey, readline, libatomic] pkg_dependencies = [glib, gobject, gthread, gio, gi, ffi, cairo, cairo_gobject, spidermonkey] if cairo_xlib.found() libcjs_dependencies += cairo_xlib pkg_dependencies += cairo_xlib endif if build_readline libcjs_dependencies += readline_deps endif libcjs_cpp_args = ['-DGJS_COMPILATION'] + directory_defines # Check G-I and/or Meson on this one. libcjs_cpp_args += ['-DG_LOG_DOMAIN="Cjs"'] if host_machine.system() == 'windows' # We need these defines to build properly for all Windows builds libcjs_cpp_args += ['-DWIN32', '-DXP_WIN', '-DWIN32_LEAN_AND_MEAN'] endif # This dependency should provide everything that is needed to compile cjs except # the sources themselves, is used to compile both the static libraries and the # tests base_build_dep = declare_dependency( compile_args: libcjs_cpp_args, dependencies: libcjs_dependencies) internal_build_dep = declare_dependency( compile_args: (release_build ? ['-DG_DISABLE_ASSERT'] : []), dependencies: [ base_build_dep, build_profiler ? profiler_deps : [], ]) libcjs_jsapi = static_library(meson.project_name() + '-jsapi', libcjs_jsapi_sources, probes_header, probes_objfile, cpp_pch: 'cjs/cjs_pch.hh', dependencies: internal_build_dep, install: false) # We need to create an internal static library to be able to link with the tests # that may use internal APIs. This is also used to generate the actual shared # library so that we compile its sources just once. libcjs_internal = static_library('cjs-internal', libcjs_sources, probes_header, probes_objfile, cpp_pch: 'cjs/cjs_pch.hh', dependencies: internal_build_dep, link_with: libcjs_jsapi) link_args = [] symbol_map = 'libcjs.map' symbol_list = 'libcjs.symbols' link_args += cxx.get_supported_link_arguments([ '-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(), symbol_map), '-Wl,-exported_symbols_list,@0@/@1@'.format(meson.current_source_dir(), symbol_list), # macOS linker ]) libcjs = shared_library(meson.project_name(), sources: libcjs_private_sources, link_args: link_args, link_depends: [symbol_map, symbol_list], link_whole: [libcjs_internal, module_resource_lib], dependencies: base_build_dep, version: '0.0.0', soversion: '0', gnu_symbol_visibility: 'hidden', install: true) install_headers(cjs_public_headers, subdir: api_name / 'cjs') # Allow using libcjs as a subproject libcjs_dep = declare_dependency(link_with: [libcjs, libcjs_jsapi], dependencies: base_build_dep, include_directories: top_include) ### Build CjsPrivate introspection library ##################################### cjs_private_gir = gnome.generate_gir(libcjs, includes: ['GObject-2.0', 'Gio-2.0'], sources: libcjs_private_sources, namespace: 'CjsPrivate', nsversion: '1.0', identifier_prefix: 'Gjs', symbol_prefix: 'gjs_', fatal_warnings: get_option('werror'), install: true, install_gir: false, install_dir_typelib: pkglibdir / 'girepository-1.0') cjs_private_typelib = cjs_private_gir[1] ### Build cjs-console interpreter ############################################## cjs_console_srcs = ['cjs/console.cpp'] cjs_console = executable('cjs-console', cjs_console_srcs, dependencies: libcjs_dep, install: true) meson.add_install_script('build/symlink-cjs.py', bindir) ### Install data files ######################################################### install_data('installed-tests/extra/gjs.supp', install_dir: cjsjsdir / 'valgrind') install_data('installed-tests/extra/lsan.supp', install_dir: cjsjsdir / 'lsan') if get_option('installed_tests') schemadir = datadir / 'glib-2.0' / 'schemas' install_data('installed-tests/js/org.cinnamon.CjsTest.gschema.xml', install_dir: schemadir) meson.add_install_script(glib_compile_schemas, prefix / schemadir, skip_if_destdir: true) endif ### Generate pkg-config file ################################################### pkg.generate(libcjs, name: api_name, description: 'JS bindings for GObjects', requires: [glib, gobject, gio], requires_private: pkg_dependencies, subdirs: api_name, variables: [ 'exec_prefix=${prefix}', 'datarootdir=${datadir}', 'cjs_console=${bindir}/cjs-console', ]) ### Test environment ########################################################### tests_environment = environment() gi_tests_builddir = meson.project_build_root() / 'subprojects' / 'gobject-introspection-tests' js_tests_builddir = meson.current_build_dir() / 'installed-tests' / 'js' libcjs_test_tools_builddir = js_tests_builddir / 'libgjstesttools' # GJS_PATH is empty here since we want to force the use of our own # resources. G_FILENAME_ENCODING ensures filenames are not UTF-8 tests_environment.set('TOP_BUILDDIR', meson.project_build_root()) tests_environment.set('GJS_USE_UNINSTALLED_FILES', '1') tests_environment.set('GJS_PATH', '') tests_environment.set('GJS_DEBUG_OUTPUT', 'stderr') tests_environment.prepend('GI_TYPELIB_PATH', meson.current_build_dir(), gi_tests_builddir, js_tests_builddir, libcjs_test_tools_builddir) tests_environment.prepend('LD_LIBRARY_PATH', meson.current_build_dir(), gi_tests_builddir, js_tests_builddir, libcjs_test_tools_builddir) tests_environment.prepend('DYLD_LIBRARY_PATH', meson.current_build_dir(), gi_tests_builddir, js_tests_builddir, libcjs_test_tools_builddir) tests_environment.set('G_FILENAME_ENCODING', 'latin1') # Workaround for https://github.com/google/sanitizers/issues/1322 tests_environment.set('ASAN_OPTIONS', 'intercept_tls_get_addr=0') tests_environment.set('LSAN_OPTIONS', 'fast_unwind_on_malloc=0,exitcode=23,suppressions=@0@'.format( meson.current_source_dir() / 'installed-tests' / 'extra' / 'lsan.supp')) tests_environment.set('TSAN_OPTIONS', 'history_size=5,force_seq_cst_atomics=1,suppressions=@0@'.format( meson.current_source_dir() / 'installed-tests' / 'extra' / 'tsan.supp')) tests_environment.set('G_SLICE', 'always-malloc') tests_environment.set('NO_AT_BRIDGE', '1') tests_environment.set('GSETTINGS_SCHEMA_DIR', js_tests_builddir) tests_environment.set('GSETTINGS_BACKEND', 'memory') tests_environment.set('G_DEBUG', 'fatal-warnings,fatal-criticals') tests_locale = 'N/A' if cxx.get_argument_syntax() != 'msvc' result = run_command('build/choose-tests-locale.sh', check: false) if result.returncode() == 0 tests_locale = result.stdout().strip() tests_environment.set('LC_ALL', tests_locale) endif endif if not get_option('skip_gtk_tests') tests_environment.set('ENABLE_GTK', 'yes') endif if get_option('b_coverage') tests_environment.set('GJS_UNIT_COVERAGE_OUTPUT', 'lcov') tests_environment.set('GJS_UNIT_COVERAGE_PREFIX', 'resource:///org/cinnamon/cjs') endif ### Tests and test setups ###################################################### # External code should not error out even when building with -Werror gi_tests = subproject('gobject-introspection-tests', default_options: ['werror=false', 'cairo=true', 'install_dir=@0@'.format(installed_tests_execdir)]) subdir('installed-tests') # Note: The test program in test/ needs to be ported # to Windows before we can build it on Windows. if host_machine.system() != 'windows' subdir('test') endif valgrind_environment = environment() valgrind_environment.set('G_SLICE', 'always-malloc,debug-blocks') valgrind_environment.set('G_DEBUG', 'fatal-warnings,fatal-criticals,gc-friendly') valgrind_environment.set('VALGRIND', 'valgrind') glib_prefix = glib.get_variable(pkgconfig: 'prefix', default_value: '/usr') glib_suppresssions = (glib_prefix / 'share' / 'glib-2.0' / 'valgrind' / 'glib.supp') cjs_suppressions = (meson.current_source_dir() / 'installed-tests' / 'extra' / 'cjs.supp') valgrind_args = [ '--suppressions=@0@'.format(glib_suppresssions), '--suppressions=@0@'.format(cjs_suppressions), '--leak-check=full', '--num-callers=15', '--trace-children=yes', '--trace-children-skip=*basename,*cat,*diff,*echo,*grep,*rm,*sed,*stat,*true', '--error-exitcode=1' ] add_test_setup('quiet', env: ['GJS_DEBUG_TOPICS='], is_default: true) add_test_setup('verbose') add_test_setup('valgrind', timeout_multiplier: 40, env: valgrind_environment, exe_wrapper: ['valgrind'] + valgrind_args) zeal2_environment = environment() zeal2_environment.set('JS_GC_ZEAL', '2,10') add_test_setup('extra_gc', timeout_multiplier: 40, env: zeal2_environment) zeal4_environment = environment() zeal4_environment.set('JS_GC_ZEAL', '4') add_test_setup('pre_verify', timeout_multiplier: 40, env: zeal4_environment) zeal11_environment = environment() zeal11_environment.set('JS_GC_ZEAL', '11') add_test_setup('post_verify', timeout_multiplier: 2, env: zeal11_environment) ### Warn about conditions that may affect runtime ############################## if tests_locale == 'C' or tests_locale == 'N/A' warning('''Your libc does not have the C.UTF-8 locale and no other suitable UTF-8 fallback locale could be found. You can still build GJS, but some tests will fail.''') endif if get_option('buildtype').startswith('debug') and nondebug_spidermonkey warning('''Your copy of SpiderMonkey is not debug-enabled, but you are building a debug or debugoptimized build. This will make development more difficult. Consider reconfiguring SpiderMonkey with --enable-debug.''') endif if get_option('skip_gtk_tests') warning('Not using GTK, not all tests will be run.') endif if get_option('skip_dbus_tests') warning('Not using DBus, not all tests will be run.') endif ### Summarize options ########################################################## summary({ 'prefix': prefix, 'bindir': prefix / bindir, 'libdir': prefix / libdir, 'datadir': prefix / datadir, 'libexecdir': prefix / libexecdir, }, section: 'Directories') locations = [] foreach dep: [ffi, glib, gi, spidermonkey, readline, sysprof_capture] if dep.type_name() == 'pkgconfig' locations += 'in @0@'.format(dep.get_variable(pkgconfig: 'prefix')) else locations += dep.type_name() endif endforeach summary({ 'libffi': '@0@ (@1@)'.format(ffi.version(), locations[0]), 'GLib': '@0@ (@1@)'.format(glib.version(), locations[1]), 'GObject introspection': '@0@ (@1@)'.format(gi.version(), locations[2]), 'SpiderMonkey': '@0@ (@1@, @2@ build)'.format(spidermonkey.version(), locations[3], nondebug_spidermonkey ? 'release' : 'debug'), }, section: 'Dependencies') if build_readline summary('Readline', '(@0@)'.format(locations[4]), section: 'Dependencies') endif if build_profiler summary('Sysprof', '@0@ (@1@)'.format(sysprof_capture.version(), locations[5]), section: 'Dependencies') endif summary({ 'Build type': get_option('buildtype'), 'Installed tests': get_option('installed_tests'), '-Bsymbolic-functions': get_option('bsymbolic_functions'), 'Skip DBus tests': get_option('skip_dbus_tests'), 'Skip GTK tests': get_option('skip_gtk_tests'), 'Extra debug logs': get_option('verbose_logs'), 'Precompiled headers': get_option('b_pch'), }, section: 'Build options', bool_yn: true) summary({ 'Use readline for input': build_readline, 'Profiler (Linux only)': build_profiler, 'Dtrace debugging': get_option('dtrace'), 'Systemtap debugging': get_option('systemtap'), }, section: 'Optional features', bool_yn: true) ### Development environment #################################################### meson.add_devenv({'GJS_USE_UNINSTALLED_FILES': '1'}) ### Maintainer scripts ######################################################### run_target('maintainer-upload-release', command: ['build/maintainer-upload-release.sh', meson.project_name(), meson.project_version()]) cjs-128.0/gjs.doap0000664000175000017500000000266614771557763012717 0ustar fabiofabio gjs gjs GNOME JavaScript bindings GNOME JavaScript bindings C++ Philip Chimento ptomato pchimento cjs-128.0/.gitignore0000664000175000017500000000057314771557763013252 0ustar fabiofabio# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2020 Philip Chimento /tools/node_modules # artifact from ci-templates: /container-build-report.xml debian/tmp debian/libcjs0 debian/libcjs-dbg debian/libcjs-dev debian/cjs debian/.debhelper debian/*.substvars debian/*.debhelper.log debian/debhelper-build-stamp debian/filescjs-128.0/CPPLINT.cfg0000664000175000017500000000124614771557763013052 0ustar fabiofabio# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2018 Philip Chimento # This is the toplevel CPPLINT.cfg file set noparent # We give a limit to clang-format of 80, but we allow 100 here for cases where # it really is more readable to have a longer line linelength=100 # Exceptions to Google style # - build/include_order: We have a special order for include files, see "Header # inclusion order" in CPP_Style_Guide.md. # - build/c++11: This rule bans certain C++ standard library features, which # have their own alternatives in the Chromium codebase, doesn't apply to us. filter=-build/include_order,-build/c++11 cjs-128.0/.clangd0000664000175000017500000000034614771557763012511 0ustar fabiofabio# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2024 Philip Chimento Diagnostics: ClangTidy: Remove: bugprone-sizeof-expression # Interferes with g_clear_pointer() cjs-128.0/.eslintrc.yml0000664000175000017500000001542514771557763013710 0ustar fabiofabio--- # SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2018 Claudio André env: es2021: true extends: 'eslint:recommended' plugins: - jsdoc rules: array-bracket-newline: - error - consistent array-bracket-spacing: - error - never array-callback-return: error arrow-parens: - error - as-needed arrow-spacing: error block-scoped-var: error block-spacing: error brace-style: error # Waiting for this to have matured a bit in eslint # camelcase: # - error # - properties: never # allow: [^vfunc_, ^on_, _instance_init] comma-dangle: - error - arrays: always-multiline objects: always-multiline imports: always-multiline functions: never comma-spacing: - error - before: false after: true comma-style: - error - last computed-property-spacing: error curly: - error - multi-or-nest - consistent dot-location: - error - property eol-last: error eqeqeq: error func-call-spacing: error func-name-matching: error func-style: - error - declaration - allowArrowFunctions: true indent: - error - 4 - ignoredNodes: # Allow not indenting the body of GObject.registerClass, since in the # future it's intended to be a decorator - 'CallExpression[callee.object.name=GObject][callee.property.name=registerClass] > ClassExpression:first-child' # Allow dedenting chained member expressions MemberExpression: 'off' jsdoc/check-alignment: error jsdoc/check-param-names: error jsdoc/check-tag-names: error jsdoc/check-types: error jsdoc/implements-on-classes: error jsdoc/require-jsdoc: error jsdoc/require-param: error jsdoc/require-param-description: error jsdoc/require-param-name: error jsdoc/require-param-type: error jsdoc/tag-lines: - error - always - count: 0 startLines: 1 key-spacing: - error - beforeColon: false afterColon: true keyword-spacing: - error - before: true after: true linebreak-style: - error - unix lines-between-class-members: - error - always - exceptAfterSingleLine: true max-nested-callbacks: error max-statements-per-line: error new-parens: error no-array-constructor: error no-await-in-loop: error no-caller: error no-constant-condition: - error - checkLoops: false no-div-regex: error no-empty: - error - allowEmptyCatch: true no-extra-bind: error no-extra-parens: - error - all - conditionalAssign: false nestedBinaryExpressions: false returnAssign: false no-implicit-coercion: - error - allow: - '!!' no-invalid-this: error no-iterator: error no-label-var: error no-lonely-if: error no-loop-func: error no-nested-ternary: error no-new-object: error no-new-wrappers: error no-octal-escape: error no-proto: error no-prototype-builtins: 'off' no-restricted-globals: [error, window] no-restricted-properties: - error - object: imports property: format message: Use template strings - object: pkg property: initFormat message: Use template strings - object: Lang property: copyProperties message: Use Object.assign() - object: Lang property: bind message: Use arrow notation or Function.prototype.bind() - object: Lang property: Class message: Use ES6 classes no-restricted-syntax: - error - selector: >- MethodDefinition[key.name="_init"] > FunctionExpression[params.length=1] > BlockStatement[body.length=1] CallExpression[arguments.length=1][callee.object.type="Super"][callee.property.name="_init"] > Identifier:first-child message: _init() that only calls super._init() is unnecessary - selector: >- MethodDefinition[key.name="_init"] > FunctionExpression[params.length=0] > BlockStatement[body.length=1] CallExpression[arguments.length=0][callee.object.type="Super"][callee.property.name="_init"] message: _init() that only calls super._init() is unnecessary - selector: BinaryExpression[operator="instanceof"][right.name="Array"] message: Use Array.isArray() no-return-assign: error no-return-await: error no-self-compare: error no-shadow: error no-shadow-restricted-names: error no-spaced-func: error no-tabs: error no-template-curly-in-string: error no-throw-literal: error no-trailing-spaces: error no-undef-init: error no-unneeded-ternary: error no-unused-expressions: error no-unused-vars: - error # Vars use a suffix _ instead of a prefix because of file-scope private vars - varsIgnorePattern: (^unused|_$) argsIgnorePattern: ^(unused|_) no-useless-call: error no-useless-computed-key: error no-useless-concat: error no-useless-constructor: error no-useless-rename: error no-useless-return: error no-whitespace-before-property: error no-with: error nonblock-statement-body-position: - error - below object-curly-newline: - error - consistent: true multiline: true object-curly-spacing: error object-shorthand: error operator-assignment: error operator-linebreak: error padded-blocks: - error - never # These may be a bit controversial, we can try them out and enable them later # prefer-const: error # prefer-destructuring: error prefer-numeric-literals: error prefer-promise-reject-errors: error prefer-rest-params: error prefer-spread: error prefer-template: error quotes: - error - single - avoidEscape: true require-await: error rest-spread-spacing: error semi: - error - always semi-spacing: - error - before: false after: true semi-style: error space-before-blocks: error space-before-function-paren: - error - named: never # for `function ()` and `async () =>`, preserve space around keywords anonymous: always asyncArrow: always space-in-parens: error space-infix-ops: - error - int32Hint: false space-unary-ops: error spaced-comment: error switch-colon-spacing: error symbol-description: error template-curly-spacing: error template-tag-spacing: error unicode-bom: error wrap-iife: - error - inside yield-star-spacing: error yoda: error settings: jsdoc: mode: typescript globals: ARGV: readonly Debugger: readonly GIRepositoryGType: readonly globalThis: readonly imports: readonly Intl: readonly log: readonly logError: readonly print: readonly printerr: readonly window: readonly TextEncoder: readonly TextDecoder: readonly console: readonly setTimeout: readonly setInterval: readonly clearTimeout: readonly clearInterval: readonly parserOptions: ecmaVersion: 2022 cjs-128.0/COPYING0000664000175000017500000000021214771557763012303 0ustar fabiofabioThis project is dual-licensed as MIT and LGPLv2+. See the header of each file and .reuse/dep5 for files' individual license information. cjs-128.0/.editorconfig0000664000175000017500000000053114771557763013731 0ustar fabiofabio# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2021 Sonny Piers # EditorConfig is awesome: https://EditorConfig.org root = true [*] indent_style = space indent_size = 4 charset = utf-8 trim_trailing_whitespace = true end_of_line = lf insert_final_newline = true [*.js] quote_type = single cjs-128.0/util/0000775000175000017500000000000014771557763012232 5ustar fabiofabiocjs-128.0/util/console.cpp0000664000175000017500000000270214771557763014401 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2021 Evan Welsh #include #include #ifdef HAVE_UNISTD_H # include #elif defined(_WIN32) # include #endif #include #include "util/console.h" /** * ANSI escape code sequences to manipulate terminals. * * See * https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_(Control_Sequence_Introducer)_sequences */ namespace ANSICode { /** * ANSI escape code sequence to clear the terminal screen. * * Combination of 0x1B (Escape) and the sequence nJ where n=2, * n=2 clears the entire display instead of only after the cursor. */ constexpr const char CLEAR_SCREEN[] = "\x1b[2J"; } // namespace ANSICode #ifdef HAVE_UNISTD_H const int stdin_fd = STDIN_FILENO; const int stdout_fd = STDOUT_FILENO; const int stderr_fd = STDERR_FILENO; #elif defined(_WIN32) const int stdin_fd = _fileno(stdin); const int stdout_fd = _fileno(stdout); const int stderr_fd = _fileno(stderr); #else const int stdin_fd = 0; const int stdout_fd = 1; const int stderr_fd = 2; #endif bool gjs_console_is_tty(int fd) { #ifdef HAVE_UNISTD_H return isatty(fd); #elif defined(_WIN32) return _isatty(fd); #else return false; #endif } bool gjs_console_clear() { if (stdout_fd < 0 || !g_log_writer_supports_color(stdout_fd)) return false; return fputs(ANSICode::CLEAR_SCREEN, stdout) > 0 && fflush(stdout) > 0; } cjs-128.0/util/misc.h0000664000175000017500000000471714771557763013347 0ustar fabiofabio/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2008 litl, LLC #ifndef UTIL_MISC_H_ #define UTIL_MISC_H_ #include #include #include // for FILE, stdout #include // for memcpy #include // for g_malloc #ifdef G_DISABLE_ASSERT # define GJS_USED_ASSERT [[maybe_unused]] #else # define GJS_USED_ASSERT #endif bool gjs_environment_variable_is_set (const char *env_variable_name); char** gjs_g_strv_concat(char*** strv_array, int len); /* * _gjs_memdup2: * @mem: (nullable): the memory to copy. * @byte_size: the number of bytes to copy. * * Allocates @byte_size bytes of memory, and copies @byte_size bytes into it * from @mem. If @mem is null or @byte_size is 0 it returns null. * * This replaces g_memdup(), which was prone to integer overflows when * converting the argument from a gsize to a guint. * * This static inline version is a backport of the new public g_memdup2() API * from GLib 2.68. * See https://gitlab.gnome.org/GNOME/glib/-/merge_requests/1927. * It should be replaced when GLib 2.68 becomes the stable branch. * * Returns: (nullable): a pointer to the newly-allocated copy of the memory, * or null if @mem is null. */ static inline void* _gjs_memdup2(const void* mem, size_t byte_size) { if (!mem || byte_size == 0) return nullptr; void* new_mem = g_malloc(byte_size); memcpy(new_mem, mem, byte_size); return new_mem; } /* * LogFile: * RAII class encapsulating access to a FILE* pointer that must be closed, * unless it is an already-open fallback file such as stdout or stderr. */ class LogFile { FILE* m_fp; const char* m_errmsg; bool m_should_close : 1; LogFile(const LogFile&) = delete; LogFile& operator=(const LogFile&) = delete; public: explicit LogFile(const char* filename, FILE* fallback_fp = stdout) : m_errmsg(nullptr), m_should_close(false) { if (filename) { m_fp = fopen(filename, "a"); if (!m_fp) m_errmsg = strerror(errno); else m_should_close = true; } else { m_fp = fallback_fp; } } ~LogFile() { if (m_should_close) fclose(m_fp); } FILE* fp() { return m_fp; } bool has_error() { return !!m_errmsg; } const char* errmsg() { return m_errmsg; } }; #endif // UTIL_MISC_H_ cjs-128.0/util/console.h0000664000175000017500000000117514771557763014051 0ustar fabiofabio/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * SPDX-License-Identifier: MIT OR LGPL-2.0-or-later * SPDX-FileCopyrightText: 2021 Evan Welsh */ #ifndef UTIL_CONSOLE_H_ #define UTIL_CONSOLE_H_ #include /* This file has to be valid C, because it's used in libgjs-private */ #include /* IWYU pragma: keep */ #include #include "cjs/macros.h" G_BEGIN_DECLS extern const int stdout_fd; extern const int stdin_fd; extern const int stderr_fd; GJS_USE bool gjs_console_is_tty(int fd); bool gjs_console_clear(void); G_END_DECLS #endif // UTIL_CONSOLE_H_ cjs-128.0/util/log.h0000664000175000017500000001145114771557763013166 0ustar fabiofabio/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2008 litl, LLC #ifndef UTIL_LOG_H_ #define UTIL_LOG_H_ #include /* The idea of this is to be able to have one big log file for the entire * environment, and grep out what you care about. So each module or app * should have its own entry in the enum. Be sure to add new enum entries * to the switch in log.c */ typedef enum { GJS_DEBUG_GI_USAGE, GJS_DEBUG_MEMORY, GJS_DEBUG_CONTEXT, GJS_DEBUG_IMPORTER, GJS_DEBUG_NATIVE, GJS_DEBUG_CAIRO, GJS_DEBUG_KEEP_ALIVE, GJS_DEBUG_MAINLOOP, GJS_DEBUG_GREPO, GJS_DEBUG_GNAMESPACE, GJS_DEBUG_GOBJECT, GJS_DEBUG_GFUNCTION, GJS_DEBUG_GCLOSURE, GJS_DEBUG_GBOXED, GJS_DEBUG_GENUM, GJS_DEBUG_GPARAM, GJS_DEBUG_GERROR, GJS_DEBUG_GFUNDAMENTAL, GJS_DEBUG_GINTERFACE, GJS_DEBUG_GTYPE, GJS_DEBUG_LAST, } GjsDebugTopic; /* These defines are because we have some pretty expensive and * extremely verbose debug output in certain areas, that's useful * sometimes, but just too much to compile in by default. The areas * tend to be broader and less focused than the ones represented by * GjsDebugTopic. * * Don't use these special "disabled by default" log macros to print * anything that's an abnormal or error situation. * * Don't use them for one-time events, either. They are for routine * stuff that happens over and over and would deluge the logs, so * should be off by default. */ /* Whether to be verbose about JavaScript property access and resolution */ #ifndef GJS_VERBOSE_ENABLE_PROPS #define GJS_VERBOSE_ENABLE_PROPS 0 #endif /* Whether to be verbose about JavaScript function arg and closure marshaling */ #ifndef GJS_VERBOSE_ENABLE_MARSHAL #define GJS_VERBOSE_ENABLE_MARSHAL 0 #endif /* Whether to be verbose about constructing, destroying, and gc-rooting * various kinds of JavaScript thingy */ #ifndef GJS_VERBOSE_ENABLE_LIFECYCLE #define GJS_VERBOSE_ENABLE_LIFECYCLE 0 #endif /* Whether to log all gobject-introspection types and methods we use */ #ifndef GJS_VERBOSE_ENABLE_GI_USAGE #define GJS_VERBOSE_ENABLE_GI_USAGE 0 #endif /* Whether to log all callback GClosure debugging (finalizing, invalidating etc) */ #ifndef GJS_VERBOSE_ENABLE_GCLOSURE #define GJS_VERBOSE_ENABLE_GCLOSURE 0 #endif /* Whether to log all GObject signal debugging */ #ifndef GJS_VERBOSE_ENABLE_GSIGNAL #define GJS_VERBOSE_ENABLE_GSIGNAL 0 #endif #if GJS_VERBOSE_ENABLE_PROPS # define GJS_USED_VERBOSE_PROPS # define gjs_debug_jsprop(topic, ...) \ do { \ gjs_debug(topic, __VA_ARGS__); \ } while (0) #else # define GJS_USED_VERBOSE_PROPS [[maybe_unused]] # define gjs_debug_jsprop(topic, ...) ((void)0) #endif #if GJS_VERBOSE_ENABLE_MARSHAL # define GJS_USED_VERBOSE_MARSHAL # define gjs_debug_marshal(topic, ...) \ do { \ gjs_debug(topic, __VA_ARGS__); \ } while (0) #else # define GJS_USED_VERBOSE_MARSHAL [[maybe_unused]] # define gjs_debug_marshal(topic, ...) ((void)0) #endif #if GJS_VERBOSE_ENABLE_LIFECYCLE # define GJS_USED_VERBOSE_LIFECYCLE # define gjs_debug_lifecycle(topic, ...) \ do { \ gjs_debug(topic, __VA_ARGS__); \ } while (0) #else # define GJS_USED_VERBOSE_LIFECYCLE [[maybe_unused]] # define gjs_debug_lifecycle(topic, ...) ((void)0) #endif #if GJS_VERBOSE_ENABLE_GI_USAGE # define GJS_USED_VERBOSE_GI_USAGE # define gjs_debug_gi_usage(...) \ do { \ gjs_debug(GJS_DEBUG_GI_USAGE, __VA_ARGS__); \ } while (0) #else # define GJS_USED_VERBOSE_GI_USAGE [[maybe_unused]] # define gjs_debug_gi_usage(...) ((void)0) #endif #if GJS_VERBOSE_ENABLE_GCLOSURE # define GJS_USED_VERBOSE_GCLOSURE # define gjs_debug_closure(...) \ do { \ gjs_debug(GJS_DEBUG_GCLOSURE, __VA_ARGS__); \ } while (0) #else # define GJS_USED_VERBOSE_GCLOSURE [[maybe_unused]] # define gjs_debug_closure(...) ((void)0) #endif #if GJS_VERBOSE_ENABLE_GSIGNAL # define GJS_USED_VERBOSE_GSIGNAL # define gjs_debug_gsignal(...) \ do { \ gjs_debug(GJS_DEBUG_GOBJECT, __VA_ARGS__); \ } while (0) #else # define GJS_USED_VERBOSE_GSIGNAL [[maybe_unused]] # define gjs_debug_gsignal(...) ((void)0) #endif void gjs_log_init(); void gjs_log_cleanup(); [[gnu::format(printf, 2, 3)]] void gjs_debug(GjsDebugTopic topic, const char* format, ...); #endif // UTIL_LOG_H_ cjs-128.0/util/log.cpp0000664000175000017500000001523614771557763013526 0ustar fabiofabio/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2008 litl, LLC #include #include #include #include // for FILE, fprintf, fflush, fopen, fputs, fseek #include // for strchr, strcmp #ifdef _WIN32 # include # include # ifndef F_OK # define F_OK 0 # endif #else # include // for getpid #endif #include #include // for atomic_bool #include // for unique_ptr #include // for string #include #include "cjs/jsapi-util.h" #include "util/log.h" #include "util/misc.h" static std::atomic_bool s_initialized = ATOMIC_VAR_INIT(false); static bool s_debug_log_enabled = false; static bool s_print_thread = false; static std::unique_ptr s_log_file; static GjsAutoPointer s_timer; static std::array s_enabled_topics; static const char* topic_to_prefix(GjsDebugTopic topic) { switch (topic) { case GJS_DEBUG_GI_USAGE: return "JS GI USE"; case GJS_DEBUG_MEMORY: return "JS MEMORY"; case GJS_DEBUG_CONTEXT: return "JS CTX"; case GJS_DEBUG_IMPORTER: return "JS IMPORT"; case GJS_DEBUG_NATIVE: return "JS NATIVE"; case GJS_DEBUG_CAIRO: return "JS CAIRO"; case GJS_DEBUG_KEEP_ALIVE: return "JS KP ALV"; case GJS_DEBUG_MAINLOOP: return "JS MAINLOOP"; case GJS_DEBUG_GREPO: return "JS G REPO"; case GJS_DEBUG_GNAMESPACE: return "JS G NS"; case GJS_DEBUG_GOBJECT: return "JS G OBJ"; case GJS_DEBUG_GFUNCTION: return "JS G FUNC"; case GJS_DEBUG_GFUNDAMENTAL: return "JS G FNDMTL"; case GJS_DEBUG_GCLOSURE: return "JS G CLSR"; case GJS_DEBUG_GBOXED: return "JS G BXD"; case GJS_DEBUG_GENUM: return "JS G ENUM"; case GJS_DEBUG_GPARAM: return "JS G PRM"; case GJS_DEBUG_GERROR: return "JS G ERR"; case GJS_DEBUG_GINTERFACE: return "JS G IFACE"; case GJS_DEBUG_GTYPE: return "JS GTYPE"; default: return "???"; } } static GjsDebugTopic prefix_to_topic(const char* prefix) { for (unsigned i = 0; i < GJS_DEBUG_LAST; i++) { auto topic = static_cast(i); if (g_str_equal(topic_to_prefix(topic), prefix)) return topic; } return GJS_DEBUG_LAST; } void gjs_log_init() { bool expected = false; if (!s_initialized.compare_exchange_strong(expected, true)) return; if (gjs_environment_variable_is_set("GJS_DEBUG_TIMESTAMP")) s_timer = g_timer_new(); s_print_thread = gjs_environment_variable_is_set("GJS_DEBUG_THREAD"); const char* debug_output = g_getenv("GJS_DEBUG_OUTPUT"); if (debug_output && g_str_equal(debug_output, "stderr")) { s_debug_log_enabled = true; } else if (debug_output) { std::string log_file; char* c; /* Allow debug-%u.log for per-pid logfiles as otherwise log * messages from multiple processes can overwrite each other. * * (printf below should be safe as we check '%u' is the only format * string) */ c = strchr(const_cast(debug_output), '%'); if (c && c[1] == 'u' && !strchr(c + 1, '%')) { GjsAutoChar file_name; #if defined(__clang__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wformat-nonliteral\"") #endif file_name = g_strdup_printf(debug_output, getpid()); #if defined(__clang__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) _Pragma("GCC diagnostic pop") #endif log_file = file_name.get(); } else { log_file = debug_output; } /* avoid truncating in case we're using shared logfile */ s_log_file = std::make_unique(log_file.c_str()); if (s_log_file->has_error()) { fprintf(stderr, "Failed to open log file `%s': %s\n", log_file.c_str(), g_strerror(errno)); } s_debug_log_enabled = true; } if (!s_log_file) s_log_file = std::make_unique(nullptr, stderr); if (s_debug_log_enabled) { auto* topics = g_getenv("GJS_DEBUG_TOPICS"); s_enabled_topics.fill(topics == nullptr); if (topics) { GjsAutoStrv prefixes(g_strsplit(topics, ";", -1)); for (unsigned i = 0; prefixes[i] != NULL; i++) { GjsDebugTopic topic = prefix_to_topic(prefixes[i]); if (topic != GJS_DEBUG_LAST) s_enabled_topics[topic] = true; } } } } void gjs_log_cleanup() { bool expected = true; if (!s_initialized.compare_exchange_strong(expected, false)) return; s_timer = nullptr; s_enabled_topics.fill(false); } #define PREFIX_LENGTH 12 static void write_to_stream(FILE *logfp, const char *prefix, const char *s) { /* seek to end to avoid truncating in case we're using shared logfile */ (void)fseek(logfp, 0, SEEK_END); fprintf(logfp, "%*s: %s", PREFIX_LENGTH, prefix, s); if (!g_str_has_suffix(s, "\n")) fputs("\n", logfp); fflush(logfp); } void gjs_debug(GjsDebugTopic topic, const char *format, ...) { va_list args; char *s; if (!s_debug_log_enabled || !s_enabled_topics[topic]) return; va_start (args, format); s = g_strdup_vprintf (format, args); va_end (args); if (s_timer) { static gdouble previous = 0.0; gdouble total = g_timer_elapsed(s_timer, NULL) * 1000.0; gdouble since = total - previous; const char *ts_suffix; char *s2; if (since > 50.0) { ts_suffix = "!! "; } else if (since > 100.0) { ts_suffix = "!!! "; } else if (since > 200.0) { ts_suffix = "!!!!"; } else { ts_suffix = " "; } s2 = g_strdup_printf("%g %s%s", total, ts_suffix, s); g_free(s); s = s2; previous = total; } if (s_print_thread) { char *s2 = g_strdup_printf("(thread %p) %s", g_thread_self(), s); g_free(s); s = s2; } write_to_stream(s_log_file->fp(), topic_to_prefix(topic), s); g_free(s); } cjs-128.0/util/misc.cpp0000664000175000017500000000233614771557763013675 0ustar fabiofabio/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2008 litl, LLC #include #include #include "util/misc.h" bool gjs_environment_variable_is_set(const char *env_variable_name) { const char *s; s = g_getenv(env_variable_name); if (!s) return false; if (*s == '\0') return false; return true; } /** gjs_g_strv_concat: * * Concate an array of string arrays to one string array. The strings in each * array is copied to the resulting array. * * @strv_array: array of 0-terminated arrays of strings. Null elements are * allowed. * @len: number of arrays in @strv_array * * Returns: (transfer full): a newly allocated 0-terminated array of strings. */ char** gjs_g_strv_concat(char*** strv_array, int len) { GPtrArray* array = g_ptr_array_sized_new(16); for (int i = 0; i < len; i++) { char** strv = strv_array[i]; if (!strv) continue; for (int j = 0; strv[j]; ++j) g_ptr_array_add(array, g_strdup(strv[j])); } g_ptr_array_add(array, nullptr); return reinterpret_cast(g_ptr_array_free(array, false)); } cjs-128.0/.github/0000775000175000017500000000000014771557763012615 5ustar fabiofabiocjs-128.0/.github/workflows/0000775000175000017500000000000014771557763014652 5ustar fabiofabiocjs-128.0/.github/workflows/build.yml0000664000175000017500000000107114771557763016473 0ustar fabiofabioname: Build on: push: branches: - master pull_request: branches: - master workflow_dispatch: inputs: debug_enabled: type: boolean description: 'Start an SSH server on failure.' required: false default: false jobs: build: uses: linuxmint/github-actions/.github/workflows/do-builds.yml@master with: commit_id: master ############################## Comma separated list - like 'linuxmint/xapp, linuxmint/cinnamon-desktop' dependencies: ############################## cjs-128.0/.clang-format0000664000175000017500000000164114771557763013632 0ustar fabiofabio--- # SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2018 Philip Chimento # Global Options Go Here IndentWidth: 4 ColumnLimit: 80 --- Language: Cpp BasedOnStyle: Google AccessModifierOffset: -3 # to match cpplint AllowShortIfStatementsOnASingleLine: false AllowShortLoopsOnASingleLine: false CommentPragmas: '^ NOLINT' DerivePointerAlignment: false ForEachMacros: [] IncludeBlocks: Preserve IndentPPDirectives: AfterHash IndentWidth: 4 MacroBlockBegin: "^JSNATIVE_TEST_FUNC_BEGIN$" MacroBlockEnd: "^JSNATIVE_TEST_FUNC_END$" PointerAlignment: Left # Google style allows both, but clang-format doesn't SpacesBeforeTrailingComments: 2 --- # We should use eslint --fix instead, but we need to find a way to get that to # operate on diffs like clang-format does. Language: JavaScript DisableFormat: true SortIncludes: false # https://bugs.llvm.org/show_bug.cgi?id=27042 ... cjs-128.0/test/0000775000175000017500000000000014771557763012234 5ustar fabiofabiocjs-128.0/test/gjs-test-rooting.cpp0000664000175000017500000002230214771557763016156 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2017 Endless Mobile, Inc. #include #include // for size_t #include #include #include // for JS_GC, JS_SetGCCallback, JSGCStatus #include #include #include #include // for JS_NewObject #include "cjs/context-private.h" #include "cjs/jsapi-util-root.h" #include "test/gjs-test-utils.h" class JSTracer; // COMPAT: https://gitlab.gnome.org/GNOME/glib/-/merge_requests/1553 #ifdef __clang_analyzer__ void g_assertion_message(const char*, const char*, int, const char*, const char*) __attribute__((analyzer_noreturn)); #endif static GMutex gc_lock; static GCond gc_finished; static int gc_counter; // TestObj reserved slots static const size_t POINTER = 0; #define PARENT(fx) ((GjsUnitTestFixture *)fx) struct GjsRootingFixture { GjsUnitTestFixture parent; bool finalized; bool notify_called; GjsMaybeOwned* obj; // only used in callback test cases }; static void test_obj_finalize(JS::GCContext*, JSObject* obj) { bool* finalized_p = JS::GetMaybePtrFromReservedSlot(obj, POINTER); g_assert_false(*finalized_p); *finalized_p = true; } static const JSClassOps test_obj_class_ops = { nullptr, // addProperty nullptr, // deleteProperty nullptr, // enumerate nullptr, // newEnumerate nullptr, // resolve nullptr, // mayResolve test_obj_finalize}; static JSClass test_obj_class = { "TestObj", JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_FOREGROUND_FINALIZE, &test_obj_class_ops}; static JSObject * test_obj_new(GjsRootingFixture *fx) { JSObject *retval = JS_NewObject(PARENT(fx)->cx, &test_obj_class); JS::SetReservedSlot(retval, POINTER, JS::PrivateValue(&fx->finalized)); return retval; } static void on_gc(JSContext*, JSGCStatus status, JS::GCReason, void*) { if (status != JSGC_END) return; g_mutex_lock(&gc_lock); g_atomic_int_inc(&gc_counter); g_cond_broadcast(&gc_finished); g_mutex_unlock(&gc_lock); } static void setup(GjsRootingFixture *fx, gconstpointer unused) { gjs_unit_test_fixture_setup(PARENT(fx), unused); JS_SetGCCallback(PARENT(fx)->cx, on_gc, fx); } static void teardown(GjsRootingFixture *fx, gconstpointer unused) { gjs_unit_test_fixture_teardown(PARENT(fx), unused); } static void wait_for_gc(GjsRootingFixture *fx) { int count = g_atomic_int_get(&gc_counter); JS_GC(PARENT(fx)->cx); g_mutex_lock(&gc_lock); while (count == g_atomic_int_get(&gc_counter)) { g_cond_wait(&gc_finished, &gc_lock); } g_mutex_unlock(&gc_lock); } static void test_maybe_owned_rooted_flag_set_when_rooted(GjsRootingFixture* fx, const void*) { auto* obj = new GjsMaybeOwned(); obj->root(PARENT(fx)->cx, JS_NewPlainObject(PARENT(fx)->cx)); g_assert_true(obj->rooted()); delete obj; } static void test_maybe_owned_rooted_flag_not_set_when_not_rooted( GjsRootingFixture* fx, const void*) { auto* obj = new GjsMaybeOwned(); *obj = JS_NewPlainObject(PARENT(fx)->cx); g_assert_false(obj->rooted()); delete obj; } static void test_maybe_owned_rooted_keeps_alive_across_gc(GjsRootingFixture* fx, const void*) { auto* obj = new GjsMaybeOwned(); obj->root(PARENT(fx)->cx, test_obj_new(fx)); wait_for_gc(fx); g_assert_false(fx->finalized); delete obj; wait_for_gc(fx); g_assert_true(fx->finalized); } static void test_maybe_owned_rooted_is_collected_after_reset( GjsRootingFixture* fx, const void*) { auto* obj = new GjsMaybeOwned(); obj->root(PARENT(fx)->cx, test_obj_new(fx)); obj->reset(); wait_for_gc(fx); g_assert_true(fx->finalized); delete obj; } static void update_weak_pointer(JSTracer* trc, JS::Compartment*, void* data) { auto* obj = static_cast(data); if (*obj) obj->update_after_gc(trc); } static void test_maybe_owned_weak_pointer_is_collected_by_gc( GjsRootingFixture* fx, const void*) { auto* obj = new GjsMaybeOwned(); *obj = test_obj_new(fx); JS_AddWeakPointerCompartmentCallback(PARENT(fx)->cx, &update_weak_pointer, obj); wait_for_gc(fx); g_assert_true(fx->finalized); JS_RemoveWeakPointerCompartmentCallback(PARENT(fx)->cx, &update_weak_pointer); delete obj; } static void test_maybe_owned_heap_rooted_keeps_alive_across_gc( GjsRootingFixture* fx, const void*) { auto* obj = new GjsMaybeOwned(); obj->root(PARENT(fx)->cx, test_obj_new(fx)); wait_for_gc(fx); g_assert_false(fx->finalized); delete obj; wait_for_gc(fx); g_assert_true(fx->finalized); } static void test_maybe_owned_switching_mode_keeps_same_value( GjsRootingFixture* fx, const void*) { JSObject *test_obj = test_obj_new(fx); auto* obj = new GjsMaybeOwned(); *obj = test_obj; g_assert_true(*obj == test_obj); obj->switch_to_rooted(PARENT(fx)->cx); g_assert_true(obj->rooted()); g_assert_true(*obj == test_obj); obj->switch_to_unrooted(PARENT(fx)->cx); g_assert_false(obj->rooted()); g_assert_true(*obj == test_obj); delete obj; } static void test_maybe_owned_switch_to_rooted_prevents_collection( GjsRootingFixture* fx, const void*) { auto* obj = new GjsMaybeOwned(); *obj = test_obj_new(fx); obj->switch_to_rooted(PARENT(fx)->cx); wait_for_gc(fx); g_assert_false(fx->finalized); delete obj; } static void test_maybe_owned_switch_to_unrooted_allows_collection( GjsRootingFixture* fx, const void*) { auto* obj = new GjsMaybeOwned(); obj->root(PARENT(fx)->cx, test_obj_new(fx)); obj->switch_to_unrooted(PARENT(fx)->cx); JS_AddWeakPointerCompartmentCallback(PARENT(fx)->cx, &update_weak_pointer, obj); wait_for_gc(fx); g_assert_true(fx->finalized); JS_RemoveWeakPointerCompartmentCallback(PARENT(fx)->cx, &update_weak_pointer); delete obj; } static void context_destroyed(JSContext*, void* data) { auto fx = static_cast(data); g_assert_false(fx->notify_called); g_assert_false(fx->finalized); fx->notify_called = true; fx->obj->reset(); } static void test_maybe_owned_notify_callback_called_on_context_destroy( GjsRootingFixture* fx, const void*) { auto* gjs = GjsContextPrivate::from_cx(PARENT(fx)->cx); fx->obj = new GjsMaybeOwned(); fx->obj->root(PARENT(fx)->cx, test_obj_new(fx)); gjs->register_notifier(context_destroyed, fx); gjs_unit_test_destroy_context(PARENT(fx)); g_assert_true(fx->notify_called); delete fx->obj; } static void test_maybe_owned_object_destroyed_after_notify( GjsRootingFixture* fx, const void*) { auto* gjs = GjsContextPrivate::from_cx(PARENT(fx)->cx); fx->obj = new GjsMaybeOwned(); fx->obj->root(PARENT(fx)->cx, test_obj_new(fx)); gjs->register_notifier(context_destroyed, fx); gjs_unit_test_destroy_context(PARENT(fx)); g_assert_true(fx->finalized); delete fx->obj; } void gjs_test_add_tests_for_rooting(void) { #define ADD_ROOTING_TEST(path, f) \ g_test_add("/rooting/" path, GjsRootingFixture, nullptr, setup, f, \ teardown); ADD_ROOTING_TEST("maybe-owned/rooted-flag-set-when-rooted", test_maybe_owned_rooted_flag_set_when_rooted); ADD_ROOTING_TEST("maybe-owned/rooted-flag-not-set-when-not-rooted", test_maybe_owned_rooted_flag_not_set_when_not_rooted); ADD_ROOTING_TEST("maybe-owned/rooted-keeps-alive-across-gc", test_maybe_owned_rooted_keeps_alive_across_gc); ADD_ROOTING_TEST("maybe-owned/rooted-is-collected-after-reset", test_maybe_owned_rooted_is_collected_after_reset); ADD_ROOTING_TEST("maybe-owned/weak-pointer-is-collected-by-gc", test_maybe_owned_weak_pointer_is_collected_by_gc); ADD_ROOTING_TEST("maybe-owned/heap-rooted-keeps-alive-across-gc", test_maybe_owned_heap_rooted_keeps_alive_across_gc); ADD_ROOTING_TEST("maybe-owned/switching-mode-keeps-same-value", test_maybe_owned_switching_mode_keeps_same_value); ADD_ROOTING_TEST("maybe-owned/switch-to-rooted-prevents-collection", test_maybe_owned_switch_to_rooted_prevents_collection); ADD_ROOTING_TEST("maybe-owned/switch-to-unrooted-allows-collection", test_maybe_owned_switch_to_unrooted_allows_collection); #undef ADD_ROOTING_TEST #define ADD_CONTEXT_DESTROY_TEST(path, f) \ g_test_add("/rooting/" path, GjsRootingFixture, nullptr, setup, f, nullptr); ADD_CONTEXT_DESTROY_TEST("maybe-owned/notify-callback-called-on-context-destroy", test_maybe_owned_notify_callback_called_on_context_destroy); ADD_CONTEXT_DESTROY_TEST("maybe-owned/object-destroyed-after-notify", test_maybe_owned_object_destroyed_after_notify); #undef ADD_CONTEXT_DESTROY_TEST } cjs-128.0/test/meson.build0000664000175000017500000000344614771557763014405 0ustar fabiofabio# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2019 Philip Chimento ### Unit tests ################################################################# mock_js_resources_files = gnome.compile_resources('mock-js-resources', 'mock-js-resources.gresource.xml', c_name: 'mock_js_resources', source_dir: '..') libcjs_tests_common = static_library('gjs-tests-common', sources: [ 'gjs-test-utils.cpp', 'gjs-test-utils.h', 'gjs-test-common.cpp', 'gjs-test-common.h', ], cpp_args: libcjs_cpp_args, include_directories: top_include, dependencies: libcjs_dependencies, ) gjs_tests_sources = [ 'gjs-tests.cpp', 'gjs-test-coverage.cpp', 'gjs-test-no-introspection-object.cpp', 'gjs-test-no-introspection-object.h', ] gjs_tests = executable('gjs-tests', gjs_tests_sources, mock_js_resources_files, include_directories: top_include, dependencies: libcjs_dep, link_with: libcjs_tests_common) test('API tests', gjs_tests, args: ['--tap', '--keep-going', '--verbose'], depends: cjs_private_typelib, env: tests_environment, protocol: 'tap', suite: 'C', timeout: 60, priority: 10) gjs_tests_internal = executable('gjs-tests-internal', sources: [ 'gjs-tests-internal.cpp', 'gjs-test-call-args.cpp', 'gjs-test-rooting.cpp', 'gjs-test-jsapi-utils.cpp', 'gjs-test-toggle-queue.cpp', module_resource_srcs, ], include_directories: top_include, cpp_args: libcjs_cpp_args, dependencies: [libcjs_dependencies, libgjstesttools_dep], link_with: [libcjs_tests_common, libcjs_internal]) test('Internal API tests', gjs_tests_internal, args: ['--tap', '--keep-going', '--verbose'], env: tests_environment, protocol: 'tap', suite: 'C', priority: 10) cjs-128.0/test/check-headers.sh0000664000175000017500000001070114771557763015255 0ustar fabiofabio#!/usr/bin/env bash # SPDX-License-Identifier: LGPL-2.0-or-later # SPDX-FileCopyrightText: 2024 Philip Chimento # Drafted with assistance from ChatGPT. # https://chat.openai.com/share/0cd77782-13b5-4775-80d0-c77c7749fb9d if [ -n "$SELFTEST" ]; then unset SELFTEST set -x self="$(realpath "$0")" test_paths=() trap 'rm -rf -- "${test_paths[@]}"' EXIT test_env() { local code_path code_path=$(mktemp -d -t "check-pch-XXXXXX") test_paths+=("$code_path") cd "$code_path" || exit mkdir gi gjs libgjs-private modules test util } expect_success() { "$self" || exit 1 } expect_failure() { "$self" && exit 1 || true } # config.h is included test_env echo "#include " > gjs/program.c expect_success # config.h must be in angle brackets test_env echo '#include "config.h"' > gjs/program.c expect_failure # public headers are skipped test_env echo "#include " > gjs/macros.h expect_success # config.h must be included test_env echo "#include " > gjs/program.c expect_failure # config.h is included first test_env echo '#include ' > gjs/program.c echo '#include ' >> gjs/program.c expect_success # config.h must be included first test_env echo '#include ' > gjs/program.c echo '#include ' >> gjs/program.c expect_failure # other non-include things can come before the include test_env cat > gjs/program.h < EOF expect_success # spaces are taken into account test_env cat > gjs/program.c < #endif #include EOF expect_failure # header blocks in right order test_env cat > gjs/program.c < #include #include #include #include #include "program.h" EOF expect_success # header blocks in wrong order test_env cat > gjs/program.c < #include #include #include #include "program.h" #include EOF expect_failure exit 0 fi failed=0 function report_out_of_order { file="$1" shift include="$1" shift descr="$1" shift headers=("$@") if [[ ${#headers[@]} -ne 0 ]]; then echo "Error: $file: include $include before $descr header ${headers[0]}" failed=1 return 1 fi return 0 } function check_config_header { file="$1" included_files=($(sed -nE 's/^#[[:space:]]*include[[:space:]]*([<"][^>"]+[>"]).*/\1/p' "$file")) if [[ "${included_files[0]}" != "" ]]; then echo "Error: $file: include as the first #include directive" failed=1 fi c_headers=() cpp_headers=() gnome_headers=() moz_headers=() gjs_headers=() for include in "${included_files[@]:1}"; do if [[ "$include" =~ \".*\.h\" ]]; then gjs_headers+=("$include") continue fi report_out_of_order "$file" "$include" "GJS" "${gjs_headers[@]}" || continue if [[ "$include" =~ \<(js|mozilla).*\.h\> ]]; then moz_headers+=("$include") continue fi report_out_of_order "$file" "$include" "Mozilla" "${moz_headers[@]}" || continue if [[ "$include" =~ \<(ffi|sysprof.*|cairo.*|g.*)\.h\> ]]; then gnome_headers+=("$include") continue fi report_out_of_order "$file" "$include" "GNOME platform" "${gnome_headers[@]}" || continue if [[ "$include" =~ \<.*\.h\> ]]; then report_out_of_order "$file" "$include" "C++ standard library" "${cpp_headers[@]}" || continue c_headers+=("$include") elif [[ "$include" =~ \<.*\> ]]; then cpp_headers+=("$include") else echo "Error: Need to fix your regex to handle $include." failed=1 fi done } files=$(find gi gjs libgjs-private modules test util \ -name '*.c' -o -name '*.cpp' -o -name '*.h') for file in $files; do if [[ "$file" == "gjs/gjs.h" || "$file" == "gjs/macros.h" ]]; then continue; fi if grep -ql "^GJS_EXPORT" "$file"; then continue; fi check_config_header "$file" done if [[ $failed -ne 0 ]]; then echo "Errors found." exit 1 else echo "OK." fi cjs-128.0/test/test-bus.conf0000664000175000017500000000404414771557763014653 0ustar fabiofabio session unix:tmpdir=/tmp 1000000000 1000000000 1000000000 120000 240000 100000 10000 100000 10000 50000 50000 50000 300000 cjs-128.0/test/gjs-test-no-introspection-object.cpp0000664000175000017500000000467314771557763021266 0ustar fabiofabio/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2020 Endless Mobile Inc. #include #include "test/gjs-test-no-introspection-object.h" struct _GjsTestNoIntrospectionObject { GObject parent_instance; int a_int; }; G_DEFINE_TYPE(GjsTestNoIntrospectionObject, gjstest_no_introspection_object, G_TYPE_OBJECT) static GjsTestNoIntrospectionObject* last_object = nullptr; static void gjstest_no_introspection_object_init( GjsTestNoIntrospectionObject* self) { self->a_int = 0; last_object = self; } static void gjstest_no_introspection_object_set_property(GObject* object, unsigned prop_id, const GValue* value, GParamSpec* pspec) { GjsTestNoIntrospectionObject* self = GJSTEST_NO_INTROSPECTION_OBJECT(object); switch (prop_id) { case 1: self->a_int = g_value_get_int(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } } static void gjstest_no_introspection_object_get_property(GObject* object, unsigned prop_id, GValue* value, GParamSpec* pspec) { GjsTestNoIntrospectionObject* self = GJSTEST_NO_INTROSPECTION_OBJECT(object); switch (prop_id) { case 1: g_value_set_int(value, self->a_int); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } } static void gjstest_no_introspection_object_class_init( GjsTestNoIntrospectionObjectClass* klass) { GObjectClass* object_class = G_OBJECT_CLASS(klass); object_class->set_property = gjstest_no_introspection_object_set_property; object_class->get_property = gjstest_no_introspection_object_get_property; g_object_class_install_property( object_class, 1, g_param_spec_int("a-int", "An integer", "An integer property", 0, 100000000, 0, G_PARAM_READWRITE)); } GjsTestNoIntrospectionObject* gjstest_no_introspection_object_peek() { return last_object; } cjs-128.0/test/gjs-test-no-introspection-object.h0000664000175000017500000000130214771557763020715 0ustar fabiofabio/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2020 Endless Mobile Inc. #ifndef TEST_GJS_TEST_NO_INTROSPECTION_OBJECT_H_ #define TEST_GJS_TEST_NO_INTROSPECTION_OBJECT_H_ #include #include #define GJSTEST_TYPE_NO_INTROSPECTION_OBJECT \ gjstest_no_introspection_object_get_type() G_DECLARE_FINAL_TYPE(GjsTestNoIntrospectionObject, gjstest_no_introspection_object, GJSTEST, NO_INTROSPECTION_OBJECT, GObject) GjsTestNoIntrospectionObject* gjstest_no_introspection_object_peek(); #endif // TEST_GJS_TEST_NO_INTROSPECTION_OBJECT_H_ cjs-128.0/test/gjs-test-call-args.cpp0000664000175000017500000004510414771557763016347 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2016 Endless Mobile, Inc. #include #include #include // for strlen, strstr #include #include #include #include #include // for CurrentGlobalOrNull #include #include #include #include #include #include #include // for UniqueChars #include #include "cjs/jsapi-util-args.h" #include "cjs/jsapi-util.h" #include "test/gjs-test-common.h" #include "test/gjs-test-utils.h" namespace mozilla { union Utf8Unit; } // COMPAT: https://gitlab.gnome.org/GNOME/glib/-/merge_requests/1553 #ifdef __clang_analyzer__ void g_assertion_message(const char*, const char*, int, const char*, const char*) __attribute__((analyzer_noreturn)); #endif #define assert_match(str, pattern) \ G_STMT_START { \ const char *__s1 = (str), *__s2 = (pattern); \ if (!g_pattern_match_simple(__s2, __s1)) { \ g_assertion_message_cmpstr( \ G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ "assertion failed (\"" #str "\" matches \"" #pattern "\")", \ __s1, "~=", __s2); \ } \ } \ G_STMT_END typedef enum _test_enum { ZERO, ONE, TWO, THREE } test_enum_t; typedef enum _test_signed_enum { MINUS_THREE = -3, MINUS_TWO, MINUS_ONE } test_signed_enum_t; #define JSNATIVE_TEST_FUNC_BEGIN(name) \ static bool \ name(JSContext *cx, \ unsigned argc, \ JS::Value *vp) \ { \ JS::CallArgs args = JS::CallArgsFromVp(argc, vp); \ bool retval; #define JSNATIVE_TEST_FUNC_END \ if (retval) \ args.rval().setUndefined(); \ return retval; \ } JSNATIVE_TEST_FUNC_BEGIN(no_args) retval = gjs_parse_call_args(cx, "noArgs", args, ""); JSNATIVE_TEST_FUNC_END JSNATIVE_TEST_FUNC_BEGIN(no_args_ignore_trailing) retval = gjs_parse_call_args(cx, "noArgsIgnoreTrailing", args, "!"); JSNATIVE_TEST_FUNC_END #define JSNATIVE_NO_ASSERT_TYPE_TEST_FUNC(type, fmt) \ JSNATIVE_TEST_FUNC_BEGIN(type##_arg_no_assert) \ type val; \ retval = gjs_parse_call_args(cx, #type "ArgNoAssert", args, fmt, \ "val", &val); \ JSNATIVE_TEST_FUNC_END JSNATIVE_NO_ASSERT_TYPE_TEST_FUNC(bool, "b"); JSNATIVE_NO_ASSERT_TYPE_TEST_FUNC(int, "i"); #undef JSNATIVE_NO_ASSERT_TYPE_TEST_FUNC JSNATIVE_TEST_FUNC_BEGIN(object_arg_no_assert) JS::RootedObject val(cx); retval = gjs_parse_call_args(cx, "objectArgNoAssert", args, "o", "val", &val); JSNATIVE_TEST_FUNC_END JSNATIVE_TEST_FUNC_BEGIN(optional_int_args_no_assert) int val1, val2; retval = gjs_parse_call_args(cx, "optionalIntArgsNoAssert", args, "i|i", "val1", &val1, "val2", &val2); JSNATIVE_TEST_FUNC_END JSNATIVE_TEST_FUNC_BEGIN(args_ignore_trailing) int val; retval = gjs_parse_call_args(cx, "argsIgnoreTrailing", args, "!i", "val", &val); JSNATIVE_TEST_FUNC_END JSNATIVE_TEST_FUNC_BEGIN(one_of_each_type) bool boolval; JS::UniqueChars strval; GjsAutoChar fileval; JS::RootedString jsstrval(cx); int intval; unsigned uintval; int64_t int64val; double dblval; JS::RootedObject objval(cx); retval = gjs_parse_call_args( cx, "oneOfEachType", args, "bsFSiutfo", "bool", &boolval, "str", &strval, "file", &fileval, "jsstr", &jsstrval, "int", &intval, "uint", &uintval, "int64", &int64val, "dbl", &dblval, "obj", &objval); g_assert_cmpint(boolval, ==, true); g_assert_cmpstr(strval.get(), ==, "foo"); g_assert_cmpstr(fileval, ==, "foo"); bool match; bool ok = JS_StringEqualsLiteral(cx, jsstrval, "foo", &match); g_assert_true(ok); g_assert_true(match); g_assert_cmpint(intval, ==, 1); g_assert_cmpint(uintval, ==, 1); g_assert_cmpint(int64val, ==, 1); g_assert_cmpfloat(dblval, ==, 1.0); g_assert_true(objval); JSNATIVE_TEST_FUNC_END JSNATIVE_TEST_FUNC_BEGIN(optional_args_all) bool val1, val2, val3; retval = gjs_parse_call_args(cx, "optionalArgsAll", args, "b|bb", "val1", &val1, "val2", &val2, "val3", &val3); g_assert_cmpint(val1, ==, true); g_assert_cmpint(val2, ==, true); g_assert_cmpint(val3, ==, true); JSNATIVE_TEST_FUNC_END JSNATIVE_TEST_FUNC_BEGIN(optional_args_only_required) bool val1 = false, val2 = false, val3 = false; retval = gjs_parse_call_args(cx, "optionalArgsOnlyRequired", args, "b|bb", "val1", &val1, "val2", &val2, "val3", &val3); g_assert_cmpint(val1, ==, true); g_assert_cmpint(val2, ==, false); g_assert_cmpint(val3, ==, false); JSNATIVE_TEST_FUNC_END JSNATIVE_TEST_FUNC_BEGIN(only_optional_args) int val1, val2; retval = gjs_parse_call_args(cx, "onlyOptionalArgs", args, "|ii", "val1", &val1, "val2", &val2); JSNATIVE_TEST_FUNC_END JSNATIVE_TEST_FUNC_BEGIN(unsigned_enum_arg) test_enum_t val; retval = gjs_parse_call_args(cx, "unsignedEnumArg", args, "i", "enum_param", &val); g_assert_cmpint(val, ==, ONE); JSNATIVE_TEST_FUNC_END JSNATIVE_TEST_FUNC_BEGIN(signed_enum_arg) test_signed_enum_t val; retval = gjs_parse_call_args(cx, "signedEnumArg", args, "i", "enum_param", &val); g_assert_cmpint(val, ==, MINUS_ONE); JSNATIVE_TEST_FUNC_END JSNATIVE_TEST_FUNC_BEGIN(one_of_each_nullable_type) JS::UniqueChars strval; GjsAutoChar fileval; JS::RootedString jsstrval(cx); JS::RootedObject objval(cx); retval = gjs_parse_call_args(cx, "oneOfEachNullableType", args, "?s?F?S?o", "strval", &strval, "fileval", &fileval, "jsstrval", &jsstrval, "objval", &objval); g_assert_null(strval); g_assert_null(fileval); g_assert_false(jsstrval); g_assert_false(objval); JSNATIVE_TEST_FUNC_END JSNATIVE_TEST_FUNC_BEGIN(unwind_free_test) int intval; unsigned uval; JS::RootedString jsstrval(cx); JS::RootedObject objval(cx); retval = gjs_parse_call_args(cx, "unwindFreeTest", args, "oSiu", "objval", &objval, "jsstrval", &jsstrval, "intval", &intval, "error", &uval); g_assert_false(objval); g_assert_false(jsstrval); JSNATIVE_TEST_FUNC_END #define JSNATIVE_BAD_NULLABLE_TEST_FUNC(type, fmt) \ JSNATIVE_TEST_FUNC_BEGIN(type##_invalid_nullable) \ type val; \ retval = gjs_parse_call_args(cx, #type "InvalidNullable", \ args, "?" fmt, \ "val", &val); \ JSNATIVE_TEST_FUNC_END JSNATIVE_BAD_NULLABLE_TEST_FUNC(bool, "b"); JSNATIVE_BAD_NULLABLE_TEST_FUNC(int, "i"); JSNATIVE_BAD_NULLABLE_TEST_FUNC(unsigned, "u"); JSNATIVE_BAD_NULLABLE_TEST_FUNC(int64_t, "t"); JSNATIVE_BAD_NULLABLE_TEST_FUNC(double, "f"); #undef JSNATIVE_BAD_NULLABLE_TEST_FUNC #define JSNATIVE_BAD_TYPE_TEST_FUNC(type, ch) \ JSNATIVE_TEST_FUNC_BEGIN(type##_invalid_type) \ type val; \ retval = gjs_parse_call_args(cx, #type "InvalidType", args, ch, \ "val", &val); \ JSNATIVE_TEST_FUNC_END JSNATIVE_BAD_TYPE_TEST_FUNC(bool, "i"); JSNATIVE_BAD_TYPE_TEST_FUNC(int, "u"); JSNATIVE_BAD_TYPE_TEST_FUNC(unsigned, "t"); JSNATIVE_BAD_TYPE_TEST_FUNC(int64_t, "f"); JSNATIVE_BAD_TYPE_TEST_FUNC(double, "b"); JSNATIVE_BAD_TYPE_TEST_FUNC(GjsAutoChar, "i"); #undef JSNATIVE_BAD_TYPE_TEST_FUNC JSNATIVE_TEST_FUNC_BEGIN(UniqueChars_invalid_type) JS::UniqueChars value; retval = gjs_parse_call_args(cx, "UniqueCharsInvalidType", args, "i", "value", &value); JSNATIVE_TEST_FUNC_END JSNATIVE_TEST_FUNC_BEGIN(JSString_invalid_type) JS::RootedString val(cx); retval = gjs_parse_call_args(cx, "JSStringInvalidType", args, "i", "val", &val); JSNATIVE_TEST_FUNC_END JSNATIVE_TEST_FUNC_BEGIN(object_invalid_type) JS::RootedObject val(cx); retval = gjs_parse_call_args(cx, "objectInvalidType", args, "i", "val", &val); JSNATIVE_TEST_FUNC_END static JSFunctionSpec native_test_funcs[] = { JS_FN("noArgs", no_args, 0, 0), JS_FN("noArgsIgnoreTrailing", no_args_ignore_trailing, 0, 0), JS_FN("boolArgNoAssert", bool_arg_no_assert, 0, 0), JS_FN("intArgNoAssert", int_arg_no_assert, 0, 0), JS_FN("objectArgNoAssert", object_arg_no_assert, 0, 0), JS_FN("optionalIntArgsNoAssert", optional_int_args_no_assert, 0, 0), JS_FN("argsIgnoreTrailing", args_ignore_trailing, 0, 0), JS_FN("oneOfEachType", one_of_each_type, 0, 0), JS_FN("optionalArgsAll", optional_args_all, 0, 0), JS_FN("optionalArgsOnlyRequired", optional_args_only_required, 0, 0), JS_FN("onlyOptionalArgs", only_optional_args, 0, 0), JS_FN("unsignedEnumArg", unsigned_enum_arg, 0, 0), JS_FN("signedEnumArg", signed_enum_arg, 0, 0), JS_FN("oneOfEachNullableType", one_of_each_nullable_type, 0, 0), JS_FN("unwindFreeTest", unwind_free_test, 0, 0), JS_FN("boolInvalidNullable", bool_invalid_nullable, 0, 0), JS_FN("intInvalidNullable", int_invalid_nullable, 0, 0), JS_FN("unsignedInvalidNullable", unsigned_invalid_nullable, 0, 0), JS_FN("int64_tInvalidNullable", int64_t_invalid_nullable, 0, 0), JS_FN("doubleInvalidNullable", double_invalid_nullable, 0, 0), JS_FN("boolInvalidType", bool_invalid_type, 0, 0), JS_FN("intInvalidType", int_invalid_type, 0, 0), JS_FN("unsignedInvalidType", unsigned_invalid_type, 0, 0), JS_FN("int64_tInvalidType", int64_t_invalid_type, 0, 0), JS_FN("doubleInvalidType", double_invalid_type, 0, 0), JS_FN("GjsAutoCharInvalidType", GjsAutoChar_invalid_type, 0, 0), JS_FN("UniqueCharsInvalidType", UniqueChars_invalid_type, 0, 0), JS_FN("JSStringInvalidType", JSString_invalid_type, 0, 0), JS_FN("objectInvalidType", object_invalid_type, 0, 0), JS_FS_END}; static void setup(GjsUnitTestFixture *fx, gconstpointer unused) { gjs_unit_test_fixture_setup(fx, unused); JS::RootedObject global{fx->cx, JS::CurrentGlobalOrNull(fx->cx)}; bool success = JS_DefineFunctions(fx->cx, global, native_test_funcs); g_assert_true(success); } static void run_code(GjsUnitTestFixture *fx, gconstpointer code) { const char *script = (const char *) code; JS::SourceText source; bool ok = source.init(fx->cx, script, strlen(script), JS::SourceOwnership::Borrowed); g_assert_true(ok); JS::CompileOptions options(fx->cx); options.setFileAndLine("unit test", 1); JS::RootedValue ignored(fx->cx); ok = JS::Evaluate(fx->cx, options, source, &ignored); g_assert_null(gjs_test_get_exception_message(fx->cx)); g_assert_true(ok); } static void run_code_expect_exception(GjsUnitTestFixture *fx, gconstpointer code) { const char *script = (const char *) code; JS::SourceText source; bool ok = source.init(fx->cx, script, strlen(script), JS::SourceOwnership::Borrowed); g_assert_true(ok); JS::CompileOptions options(fx->cx); options.setFileAndLine("unit test", 1); JS::RootedValue ignored(fx->cx); ok = JS::Evaluate(fx->cx, options, source, &ignored); g_assert_false(ok); GjsAutoChar message = gjs_test_get_exception_message(fx->cx); g_assert_nonnull(message); /* Cheap way to shove an expected exception message into the data argument */ const char *expected_msg = strstr((const char *) code, "//"); if (expected_msg != NULL) { expected_msg += 2; assert_match(message, expected_msg); } } void gjs_test_add_tests_for_parse_call_args(void) { #define ADD_CALL_ARGS_TEST_BASE(path, code, f) \ g_test_add("/callargs/" path, GjsUnitTestFixture, code, setup, f, \ gjs_unit_test_fixture_teardown) #define ADD_CALL_ARGS_TEST(path, code) \ ADD_CALL_ARGS_TEST_BASE(path, code, run_code) #define ADD_CALL_ARGS_TEST_XFAIL(path, code) \ ADD_CALL_ARGS_TEST_BASE(path, code, run_code_expect_exception) ADD_CALL_ARGS_TEST("no-args-works", "noArgs()"); ADD_CALL_ARGS_TEST_XFAIL("no-args-fails-on-extra-args", "noArgs(1, 2, 3)//*Expected 0 arguments, got 3"); ADD_CALL_ARGS_TEST("no-args-ignores-trailing", "noArgsIgnoreTrailing(1, 2, 3)"); ADD_CALL_ARGS_TEST_XFAIL("too-many-args-fails", "intArgNoAssert(1, 2)" "//*Expected 1 arguments, got 2"); ADD_CALL_ARGS_TEST_XFAIL("too-many-args-fails-when-more-than-optional", "optionalIntArgsNoAssert(1, 2, 3)" "//*Expected minimum 1 arguments (and 1 optional), got 3"); ADD_CALL_ARGS_TEST_XFAIL("too-few-args-fails", "intArgNoAssert()//*At least 1 argument required, " "but only 0 passed"); ADD_CALL_ARGS_TEST_XFAIL("too-few-args-fails-with-optional", "optionalIntArgsNoAssert()//*At least 1 argument " "required, but only 0 passed"); ADD_CALL_ARGS_TEST("args-ignores-trailing", "argsIgnoreTrailing(1, 2, 3)"); ADD_CALL_ARGS_TEST( "one-of-each-type-works", "oneOfEachType(true, 'foo', 'foo', 'foo', 1, 1, 1, 1, {})"); ADD_CALL_ARGS_TEST("optional-args-work-when-passing-all-args", "optionalArgsAll(true, true, true)"); ADD_CALL_ARGS_TEST("optional-args-work-when-passing-only-required-args", "optionalArgsOnlyRequired(true)"); ADD_CALL_ARGS_TEST("enum-types-work", "unsignedEnumArg(1)"); ADD_CALL_ARGS_TEST("signed-enum-types-work", "signedEnumArg(-1)"); ADD_CALL_ARGS_TEST("one-of-each-nullable-type-works", "oneOfEachNullableType(null, null, null, null)"); ADD_CALL_ARGS_TEST("passing-no-arguments-when-all-optional", "onlyOptionalArgs()"); ADD_CALL_ARGS_TEST("passing-some-arguments-when-all-optional", "onlyOptionalArgs(1)"); ADD_CALL_ARGS_TEST("passing-all-arguments-when-all-optional", "onlyOptionalArgs(1, 1)"); ADD_CALL_ARGS_TEST_XFAIL("allocated-args-are-freed-on-error", "unwindFreeTest({}, 'foo', 1, -1)" "//*Value * is out of range"); ADD_CALL_ARGS_TEST_XFAIL("nullable-bool-is-invalid", "boolInvalidNullable(true)" "//*Invalid format string combination ?b"); ADD_CALL_ARGS_TEST_XFAIL("nullable-int-is-invalid", "intInvalidNullable(1)" "//*Invalid format string combination ?i"); ADD_CALL_ARGS_TEST_XFAIL("nullable-unsigned-is-invalid", "unsignedInvalidNullable(1)" "//*Invalid format string combination ?u"); ADD_CALL_ARGS_TEST_XFAIL("nullable-int64-is-invalid", "int64_tInvalidNullable(1)" "//*Invalid format string combination ?t"); ADD_CALL_ARGS_TEST_XFAIL("nullable-double-is-invalid", "doubleInvalidNullable(1)" "//*Invalid format string combination ?f"); ADD_CALL_ARGS_TEST_XFAIL("invalid-bool-type", "boolInvalidType(1)" "//*Wrong type for i, got bool?"); ADD_CALL_ARGS_TEST_XFAIL("invalid-int-type", "intInvalidType(1)" "//*Wrong type for u, got int32_t?"); ADD_CALL_ARGS_TEST_XFAIL("invalid-unsigned-type", "unsignedInvalidType(1)" "//*Wrong type for t, got uint32_t?"); ADD_CALL_ARGS_TEST_XFAIL("invalid-int64-type", "int64_tInvalidType(1)" "//*Wrong type for f, got int64_t?"); ADD_CALL_ARGS_TEST_XFAIL("invalid-double-type", "doubleInvalidType(false)" "//*Wrong type for b, got double?"); ADD_CALL_ARGS_TEST_XFAIL("invalid-autochar-type", "GjsAutoCharInvalidType(1)" "//*Wrong type for i, got GjsAutoChar?"); ADD_CALL_ARGS_TEST_XFAIL("invalid-autojschar-type", "UniqueCharsInvalidType(1)" "//*Wrong type for i, got JS::UniqueChars?"); ADD_CALL_ARGS_TEST_XFAIL( "invalid-jsstring-type", "JSStringInvalidType(1)" "//*Wrong type for i, got JS::MutableHandleString"); ADD_CALL_ARGS_TEST_XFAIL("invalid-object-type", "objectInvalidType(1)" "//*Wrong type for i, got JS::MutableHandleObject"); ADD_CALL_ARGS_TEST_XFAIL("invalid-boolean", "boolArgNoAssert({})//*Not a boolean"); ADD_CALL_ARGS_TEST_XFAIL("invalid-object", "objectArgNoAssert(3)//*Not an object"); #undef ADD_CALL_ARGS_TEST_XFAIL #undef ADD_CALL_ARGS_TEST #undef ADD_CALL_ARGS_TEST_BASE } cjs-128.0/test/gjs-test-utils.cpp0000664000175000017500000000234414771557763015641 0ustar fabiofabio/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2008 litl, LLC // SPDX-FileCopyrightText: 2016 Endless Mobile, Inc. #include #include #include #include // for JS_GetContextPrivate #include #include #include "cjs/context-private.h" #include "cjs/context.h" #include "cjs/jsapi-util.h" #include "test/gjs-test-common.h" #include "test/gjs-test-utils.h" void gjs_unit_test_fixture_setup(GjsUnitTestFixture* fx, const void*) { fx->gjs_context = gjs_context_new(); fx->cx = (JSContext *) gjs_context_get_native_context(fx->gjs_context); auto* gjs = static_cast(JS_GetContextPrivate(fx->cx)); fx->realm = JS::EnterRealm(fx->cx, gjs->global()); } void gjs_unit_test_destroy_context(GjsUnitTestFixture *fx) { GjsAutoChar message = gjs_test_get_exception_message(fx->cx); if (message) g_printerr("**\n%s\n", message.get()); JS::LeaveRealm(fx->cx, fx->realm); g_object_unref(fx->gjs_context); } void gjs_unit_test_fixture_teardown(GjsUnitTestFixture* fx, const void*) { gjs_unit_test_destroy_context(fx); } cjs-128.0/test/gjs-test-toggle-queue.cpp0000664000175000017500000006433214771557763017111 0ustar fabiofabio/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2021 Canonical, Ltd. // SPDX-FileContributor: Marco Trevisan #include #include #include #include #include #include #include // for tie #include // for pair #include #include #include #include // for JS_GC #include #include "gi/object.h" #include "gi/toggle.h" #include "cjs/context.h" #include "cjs/jsapi-util.h" #include "installed-tests/js/libgjstesttools/gjs-test-tools.h" #include "test/gjs-test-utils.h" namespace Gjs { namespace Test { static GMutex s_gc_lock; static GCond s_gc_finished; static std::atomic_int s_gc_counter; static std::deque> s_toggle_history; struct ObjectInstance : ::ObjectInstance { using ::ObjectInstance::ensure_uses_toggle_ref; using ::ObjectInstance::new_for_gobject; using ::ObjectInstance::wrapper_is_rooted; }; struct ToggleQueue { static decltype(::ToggleQueue::get_default()) get_default() { return ::ToggleQueue::get_default(); } static void reset_queue() { auto tq = get_default(); tq->m_shutdown = false; g_clear_handle_id(&tq->m_idle_id, g_source_remove); tq->q.clear(); } static decltype(::ToggleQueue::q) queue() { return get_default()->q; } static ::ToggleQueue::Handler handler() { return get_default()->m_toggle_handler; } }; namespace TQ { static void on_gc(JSContext*, JSGCStatus status, JS::GCReason, void*) { if (status != JSGC_END) return; g_mutex_lock(&s_gc_lock); s_gc_counter.fetch_add(1); g_cond_broadcast(&s_gc_finished); g_mutex_unlock(&s_gc_lock); } static void setup(GjsUnitTestFixture* fx, const void*) { g_irepository_prepend_search_path(g_getenv("TOP_BUILDDIR")); gjs_test_tools_init(); gjs_unit_test_fixture_setup(fx, nullptr); JS_SetGCCallback(fx->cx, on_gc, fx); GjsAutoError error; int code; const char* gi_initializer = "imports.gi;"; g_assert_true(gjs_context_eval(fx->gjs_context, gi_initializer, -1, "", &code, &error)); g_assert_no_error(error); } static void wait_for_gc(GjsUnitTestFixture* fx) { int count = s_gc_counter.load(); JS_GC(fx->cx); g_mutex_lock(&s_gc_lock); while (count == s_gc_counter.load()) { g_cond_wait(&s_gc_finished, &s_gc_lock); } g_mutex_unlock(&s_gc_lock); } static void teardown(GjsUnitTestFixture* fx, const void*) { for (auto pair : s_toggle_history) ToggleQueue::get_default()->cancel(pair.first); s_toggle_history.clear(); gjs_unit_test_fixture_teardown(fx, nullptr); g_assert_true(ToggleQueue::queue().empty()); ToggleQueue::reset_queue(); gjs_test_tools_reset(); } } // namespace TQ static ::ObjectInstance* new_test_gobject(GjsUnitTestFixture* fx) { GjsAutoUnref gobject( G_OBJECT(g_object_new(G_TYPE_OBJECT, nullptr))); auto* object = ObjectInstance::new_for_gobject(fx->cx, gobject); static_cast(object)->ensure_uses_toggle_ref(fx->cx); return object; } static void wait_for(int interval) { GjsAutoPointer loop( g_main_loop_new(nullptr, false)); g_timeout_add_full( G_PRIORITY_LOW, interval, [](void* data) { g_main_loop_quit(static_cast(data)); return G_SOURCE_REMOVE; }, loop, nullptr); g_main_loop_run(loop); } static void toggles_handler(::ObjectInstance* object, ::ToggleQueue::Direction direction) { s_toggle_history.emplace_back(object, direction); } static void test_toggle_queue_unlock_empty(GjsUnitTestFixture*, const void*) { assert_equal(ToggleQueue::get_default()->cancel(nullptr), false, false); } static void test_toggle_queue_unlock_same_thread(GjsUnitTestFixture*, const void*) { auto tq = ToggleQueue::get_default(); assert_equal(tq->cancel(nullptr), false, false); assert_equal(ToggleQueue::get_default()->cancel(nullptr), false, false); } static void test_toggle_blocks_other_thread(GjsUnitTestFixture*, const void*) { struct LockedQueue { decltype(ToggleQueue::get_default()) tq = ToggleQueue::get_default(); }; auto locked_queue = std::make_unique(); assert_equal(locked_queue->tq->cancel(nullptr), false, false); std::atomic_bool other_thread_running(false); std::atomic_bool accessed_from_other_thread(false); auto th = std::thread([&accessed_from_other_thread, &other_thread_running] { other_thread_running.store(true); auto locked_queue = std::make_unique(); accessed_from_other_thread.store(true); assert_equal(ToggleQueue::get_default()->cancel(nullptr), false, false); other_thread_running = false; }); while (!other_thread_running.load()) g_assert_false(accessed_from_other_thread.load()); std::this_thread::sleep_for(std::chrono::milliseconds(100)); g_assert_true(other_thread_running); g_assert_false(accessed_from_other_thread); auto other_queue = std::make_unique(); assert_equal(other_queue->tq->cancel(nullptr), false, false); other_queue.reset(); std::this_thread::sleep_for(std::chrono::milliseconds(100)); g_assert_true(other_thread_running); g_assert_false(accessed_from_other_thread); // Ok, now other thread may get the lock... locked_queue.reset(); while (!accessed_from_other_thread.load()) { } g_assert_true(accessed_from_other_thread); // Can enter again from main thread! th.join(); g_assert_false(other_thread_running); assert_equal(ToggleQueue::get_default()->cancel(nullptr), false, false); } static void test_toggle_queue_empty(GjsUnitTestFixture*, const void*) { auto tq = ToggleQueue::get_default(); tq->handle_all_toggles(toggles_handler); g_assert_true(s_toggle_history.empty()); } static void test_toggle_queue_empty_cancel(GjsUnitTestFixture*, const void*) { auto tq = ToggleQueue::get_default(); bool toggle_down_queued, toggle_up_queued; std::tie(toggle_down_queued, toggle_up_queued) = tq->cancel(nullptr); g_assert_false(toggle_down_queued); g_assert_false(toggle_up_queued); } static void test_toggle_queue_enqueue_one(GjsUnitTestFixture* fx, const void*) { auto* instance = new_test_gobject(fx); auto tq = ToggleQueue::get_default(); tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler); tq->handle_all_toggles(toggles_handler); assert_equal(s_toggle_history.size(), 1LU); assert_equal(s_toggle_history.front(), instance, ::ToggleQueue::Direction::UP); } static void test_toggle_queue_enqueue_one_cancel(GjsUnitTestFixture* fx, const void*) { auto* instance = new_test_gobject(fx); auto tq = ToggleQueue::get_default(); tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler); bool toggle_down_queued, toggle_up_queued; std::tie(toggle_down_queued, toggle_up_queued) = tq->cancel(instance); g_assert_false(toggle_down_queued); g_assert_true(toggle_up_queued); tq->handle_all_toggles(toggles_handler); g_assert_true(s_toggle_history.empty()); } static void test_toggle_queue_enqueue_many_equal(GjsUnitTestFixture* fx, const void*) { auto* instance = new_test_gobject(fx); auto tq = ToggleQueue::get_default(); tq->enqueue(instance, ::ToggleQueue::Direction::DOWN, toggles_handler); tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler); tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler); tq->enqueue(instance, ::ToggleQueue::Direction::DOWN, toggles_handler); tq->handle_all_toggles(toggles_handler); assert_equal(s_toggle_history.size(), 0LU); bool toggle_down_queued, toggle_up_queued; std::tie(toggle_down_queued, toggle_up_queued) = tq->cancel(instance); g_assert_false(toggle_down_queued); g_assert_false(toggle_up_queued); } static void test_toggle_queue_enqueue_many_equal_cancel(GjsUnitTestFixture* fx, const void*) { auto* instance = new_test_gobject(fx); auto tq = ToggleQueue::get_default(); tq->enqueue(instance, ::ToggleQueue::Direction::DOWN, toggles_handler); tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler); tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler); tq->enqueue(instance, ::ToggleQueue::Direction::DOWN, toggles_handler); bool toggle_down_queued, toggle_up_queued; std::tie(toggle_down_queued, toggle_up_queued) = tq->cancel(instance); g_assert_false(toggle_down_queued); g_assert_false(toggle_up_queued); } static void test_toggle_queue_enqueue_more_up(GjsUnitTestFixture* fx, const void*) { auto* instance = new_test_gobject(fx); auto tq = ToggleQueue::get_default(); tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler); tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler); tq->enqueue(instance, ::ToggleQueue::Direction::DOWN, toggles_handler); tq->enqueue(instance, ::ToggleQueue::Direction::DOWN, toggles_handler); tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler); tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler); tq->handle_all_toggles(toggles_handler); assert_equal(s_toggle_history.size(), 2LU); assert_equal(s_toggle_history.at(0), instance, ::ToggleQueue::Direction::UP); assert_equal(s_toggle_history.at(1), instance, ::ToggleQueue::Direction::UP); bool toggle_down_queued, toggle_up_queued; std::tie(toggle_down_queued, toggle_up_queued) = tq->cancel(instance); g_assert_false(toggle_down_queued); g_assert_false(toggle_up_queued); } static void test_toggle_queue_enqueue_only_up(GjsUnitTestFixture* fx, const void*) { auto* instance = new_test_gobject(fx); auto tq = ToggleQueue::get_default(); tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler); tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler); tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler); tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler); tq->handle_all_toggles(toggles_handler); assert_equal(s_toggle_history.size(), 4LU); assert_equal(s_toggle_history.at(0), instance, ::ToggleQueue::Direction::UP); assert_equal(s_toggle_history.at(1), instance, ::ToggleQueue::Direction::UP); assert_equal(s_toggle_history.at(2), instance, ::ToggleQueue::Direction::UP); assert_equal(s_toggle_history.at(3), instance, ::ToggleQueue::Direction::UP); bool toggle_down_queued, toggle_up_queued; std::tie(toggle_down_queued, toggle_up_queued) = tq->cancel(instance); g_assert_false(toggle_down_queued); g_assert_false(toggle_up_queued); } static void test_toggle_queue_handle_more_up(GjsUnitTestFixture* fx, const void*) { auto* instance = new_test_gobject(fx); auto tq = ToggleQueue::get_default(); tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler); tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler); tq->enqueue(instance, ::ToggleQueue::Direction::DOWN, toggles_handler); tq->enqueue(instance, ::ToggleQueue::Direction::DOWN, toggles_handler); tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler); tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler); wait_for(50); assert_equal(s_toggle_history.size(), 2LU); assert_equal(s_toggle_history.at(0), instance, ::ToggleQueue::Direction::UP); assert_equal(s_toggle_history.at(1), instance, ::ToggleQueue::Direction::UP); bool toggle_down_queued, toggle_up_queued; std::tie(toggle_down_queued, toggle_up_queued) = tq->cancel(instance); g_assert_false(toggle_down_queued); g_assert_false(toggle_up_queued); } static void test_toggle_queue_handle_only_up(GjsUnitTestFixture* fx, const void*) { auto* instance = new_test_gobject(fx); auto tq = ToggleQueue::get_default(); tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler); tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler); tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler); tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler); wait_for(50); assert_equal(s_toggle_history.size(), 4LU); assert_equal(s_toggle_history.at(0), instance, ::ToggleQueue::Direction::UP); assert_equal(s_toggle_history.at(1), instance, ::ToggleQueue::Direction::UP); assert_equal(s_toggle_history.at(2), instance, ::ToggleQueue::Direction::UP); assert_equal(s_toggle_history.at(3), instance, ::ToggleQueue::Direction::UP); bool toggle_down_queued, toggle_up_queued; std::tie(toggle_down_queued, toggle_up_queued) = tq->cancel(instance); g_assert_false(toggle_down_queued); g_assert_false(toggle_up_queued); } static void test_toggle_queue_enqueue_only_up_cancel(GjsUnitTestFixture* fx, const void*) { auto* instance = new_test_gobject(fx); auto tq = ToggleQueue::get_default(); tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler); tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler); tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler); tq->enqueue(instance, ::ToggleQueue::Direction::UP, toggles_handler); bool toggle_down_queued, toggle_up_queued; std::tie(toggle_down_queued, toggle_up_queued) = tq->cancel(instance); g_assert_false(toggle_down_queued); g_assert_true(toggle_up_queued); tq->handle_all_toggles(toggles_handler); g_assert_true(s_toggle_history.empty()); } static void test_toggle_queue_object_from_main_thread(GjsUnitTestFixture* fx, const void*) { auto* instance = new_test_gobject(fx); auto tq = ToggleQueue::get_default(); GjsAutoUnref reffed(instance->ptr(), GjsAutoTakeOwnership()); bool toggle_down_queued, toggle_up_queued; std::tie(toggle_down_queued, toggle_up_queued) = tq->cancel(instance); g_assert_false(toggle_down_queued); g_assert_false(toggle_up_queued); tq->handle_all_toggles(toggles_handler); g_assert_true(s_toggle_history.empty()); } static void test_toggle_queue_object_from_main_thread_already_enqueued( GjsUnitTestFixture* fx, const void*) { auto* instance = new_test_gobject(fx); GjsAutoUnref reffed; GjsAutoError error; reffed = instance->ptr(); gjs_test_tools_ref_other_thread(reffed, &error); g_assert_no_error(error); assert_equal(ToggleQueue::queue().size(), 1LU); assert_equal(ToggleQueue::queue().at(0).object, instance); assert_equal(ToggleQueue::queue().at(0).direction, ::ToggleQueue::Direction::UP); auto tq = ToggleQueue::get_default(); bool toggle_down_queued, toggle_up_queued; std::tie(toggle_down_queued, toggle_up_queued) = tq->cancel(instance); g_assert_false(toggle_down_queued); g_assert_true(toggle_up_queued); tq->handle_all_toggles(toggles_handler); g_assert_true(s_toggle_history.empty()); } static void test_toggle_queue_object_from_main_thread_unref_already_enqueued( GjsUnitTestFixture* fx, const void*) { auto* instance = new_test_gobject(fx); GjsAutoUnref reffed; GjsAutoError error; reffed = instance->ptr(); gjs_test_tools_ref_other_thread(reffed, &error); g_assert_no_error(error); assert_equal(ToggleQueue::queue().size(), 1LU); assert_equal(ToggleQueue::queue().at(0).direction, ::ToggleQueue::Direction::UP); reffed.reset(); g_assert_true(ToggleQueue::queue().empty()); auto tq = ToggleQueue::get_default(); bool toggle_down_queued, toggle_up_queued; std::tie(toggle_down_queued, toggle_up_queued) = tq->cancel(instance); g_assert_false(toggle_down_queued); g_assert_false(toggle_up_queued); tq->handle_all_toggles(toggles_handler); g_assert_true(s_toggle_history.empty()); } static void test_toggle_queue_object_from_other_thread_ref_unref( GjsUnitTestFixture* fx, const void*) { auto* instance = new_test_gobject(fx); GjsAutoError error; gjs_test_tools_ref_other_thread(instance->ptr(), &error); g_assert_no_error(error); assert_equal(ToggleQueue::queue().size(), 1LU); assert_equal(ToggleQueue::queue().at(0).direction, ::ToggleQueue::Direction::UP); gjs_test_tools_unref_other_thread(instance->ptr(), &error); g_assert_no_error(error); g_assert_true(ToggleQueue::queue().empty()); auto tq = ToggleQueue::get_default(); bool toggle_down_queued, toggle_up_queued; std::tie(toggle_down_queued, toggle_up_queued) = tq->cancel(instance); g_assert_false(toggle_down_queued); g_assert_false(toggle_up_queued); tq->handle_all_toggles(toggles_handler); g_assert_true(s_toggle_history.empty()); } static void test_toggle_queue_object_handle_up(GjsUnitTestFixture* fx, const void*) { auto* instance = new_test_gobject(fx); auto* instance_test = reinterpret_cast(instance); GjsAutoError error; gjs_test_tools_ref_other_thread(instance->ptr(), &error); g_assert_no_error(error); GjsAutoUnref reffed(instance->ptr()); assert_equal(ToggleQueue::queue().size(), 1LU); assert_equal(ToggleQueue::queue().at(0).direction, ::ToggleQueue::Direction::UP); wait_for(50); g_assert_true(instance_test->wrapper_is_rooted()); ToggleQueue::get_default()->handle_all_toggles(toggles_handler); g_assert_true(s_toggle_history.empty()); } static void test_toggle_queue_object_handle_up_down(GjsUnitTestFixture* fx, const void*) { auto* instance = new_test_gobject(fx); auto* instance_test = reinterpret_cast(instance); GjsAutoError error; gjs_test_tools_ref_other_thread(instance->ptr(), &error); g_assert_no_error(error); assert_equal(ToggleQueue::queue().size(), 1LU); assert_equal(ToggleQueue::queue().at(0).direction, ::ToggleQueue::Direction::UP); gjs_test_tools_unref_other_thread(instance->ptr(), &error); g_assert_no_error(error); g_assert_true(ToggleQueue::queue().empty()); wait_for(50); g_assert_false(instance_test->wrapper_is_rooted()); ToggleQueue::get_default()->handle_all_toggles(toggles_handler); g_assert_true(s_toggle_history.empty()); } static void test_toggle_queue_object_handle_up_down_delayed( GjsUnitTestFixture* fx, const void*) { auto* instance = new_test_gobject(fx); auto* instance_test = reinterpret_cast(instance); GjsAutoError error; gjs_test_tools_ref_other_thread(instance->ptr(), &error); g_assert_no_error(error); assert_equal(ToggleQueue::queue().size(), 1LU); assert_equal(ToggleQueue::queue().at(0).direction, ::ToggleQueue::Direction::UP); wait_for(50); g_assert_true(instance_test->wrapper_is_rooted()); ToggleQueue::get_default()->handle_all_toggles(toggles_handler); g_assert_true(s_toggle_history.empty()); gjs_test_tools_unref_other_thread(instance->ptr(), &error); g_assert_no_error(error); assert_equal(ToggleQueue::queue().size(), 1LU); assert_equal(ToggleQueue::queue().at(0).direction, ::ToggleQueue::Direction::DOWN); wait_for(50); g_assert_false(instance_test->wrapper_is_rooted()); ToggleQueue::get_default()->handle_all_toggles(toggles_handler); g_assert_true(s_toggle_history.empty()); } static void test_toggle_queue_object_handle_up_down_on_gc( GjsUnitTestFixture* fx, const void*) { auto* instance = new_test_gobject(fx); GjsAutoError error; gjs_test_tools_ref_other_thread(instance->ptr(), &error); g_assert_no_error(error); assert_equal(ToggleQueue::queue().size(), 1LU); assert_equal(ToggleQueue::queue().at(0).direction, ::ToggleQueue::Direction::UP); gjs_test_tools_unref_other_thread(instance->ptr(), &error); g_assert_no_error(error); g_assert_true(ToggleQueue::queue().empty()); GWeakRef weak_ref; g_weak_ref_init(&weak_ref, instance->ptr()); TQ::wait_for_gc(fx); g_assert_null(g_weak_ref_get(&weak_ref)); ToggleQueue::get_default()->handle_all_toggles(toggles_handler); g_assert_true(s_toggle_history.empty()); } static void test_toggle_queue_object_handle_many_up(GjsUnitTestFixture* fx, const void*) { auto* instance = new_test_gobject(fx); auto* instance_test = reinterpret_cast(instance); GjsAutoError error; gjs_test_tools_ref_other_thread(instance->ptr(), &error); g_assert_no_error(error); GjsAutoUnref reffed(instance->ptr()); // Simulating the case where late threads are causing this... ToggleQueue::get_default()->enqueue(instance, ::ToggleQueue::Direction::UP, ToggleQueue().handler()); assert_equal(ToggleQueue::queue().size(), 2LU); assert_equal(ToggleQueue::queue().at(0).direction, ::ToggleQueue::Direction::UP); assert_equal(ToggleQueue::queue().at(1).direction, ::ToggleQueue::Direction::UP); wait_for(50); g_assert_true(instance_test->wrapper_is_rooted()); ToggleQueue::get_default()->handle_all_toggles(toggles_handler); g_assert_true(s_toggle_history.empty()); } static void test_toggle_queue_object_handle_many_up_and_down( GjsUnitTestFixture* fx, const void*) { auto* instance = new_test_gobject(fx); auto* instance_test = reinterpret_cast(instance); // This is something similar to what is happening on #297 GjsAutoError error; gjs_test_tools_ref_other_thread(instance->ptr(), &error); g_assert_no_error(error); ToggleQueue::get_default()->enqueue(instance, ::ToggleQueue::Direction::UP, ToggleQueue().handler()); gjs_test_tools_unref_other_thread(instance->ptr(), &error); g_assert_no_error(error); ToggleQueue::get_default()->enqueue( instance, ::ToggleQueue::Direction::DOWN, ToggleQueue().handler()); g_assert_true(ToggleQueue::queue().empty()); wait_for(50); g_assert_false(instance_test->wrapper_is_rooted()); g_assert_true(ToggleQueue::queue().empty()); GWeakRef weak_ref; g_assert_true(G_IS_OBJECT(instance->ptr())); g_weak_ref_init(&weak_ref, instance->ptr()); TQ::wait_for_gc(fx); g_assert_null(g_weak_ref_get(&weak_ref)); g_assert_true(ToggleQueue::queue().empty()); } void add_tests_for_toggle_queue() { #define ADD_TOGGLE_QUEUE_TEST(path, f) \ g_test_add("/toggle-queue/" path, GjsUnitTestFixture, nullptr, TQ::setup, \ f, TQ::teardown); ADD_TOGGLE_QUEUE_TEST("spin-lock/unlock-empty", test_toggle_queue_unlock_empty); ADD_TOGGLE_QUEUE_TEST("spin-lock/unlock-same-thread", test_toggle_queue_unlock_same_thread); ADD_TOGGLE_QUEUE_TEST("spin-lock/blocks-other-thread", test_toggle_blocks_other_thread); ADD_TOGGLE_QUEUE_TEST("empty", test_toggle_queue_empty); ADD_TOGGLE_QUEUE_TEST("empty_cancel", test_toggle_queue_empty_cancel); ADD_TOGGLE_QUEUE_TEST("enqueue_one", test_toggle_queue_enqueue_one); ADD_TOGGLE_QUEUE_TEST("enqueue_one_cancel", test_toggle_queue_enqueue_one_cancel); ADD_TOGGLE_QUEUE_TEST("enqueue_many_equal", test_toggle_queue_enqueue_many_equal); ADD_TOGGLE_QUEUE_TEST("enqueue_many_equal_cancel", test_toggle_queue_enqueue_many_equal_cancel); ADD_TOGGLE_QUEUE_TEST("enqueue_more_up", test_toggle_queue_enqueue_more_up); ADD_TOGGLE_QUEUE_TEST("enqueue_only_up", test_toggle_queue_enqueue_only_up); ADD_TOGGLE_QUEUE_TEST("enqueue_only_up_cancel", test_toggle_queue_enqueue_only_up_cancel); ADD_TOGGLE_QUEUE_TEST("handle_more_up", test_toggle_queue_handle_more_up); ADD_TOGGLE_QUEUE_TEST("handle_only_up", test_toggle_queue_handle_only_up); ADD_TOGGLE_QUEUE_TEST("object/not-enqueued_main_thread", test_toggle_queue_object_from_main_thread); ADD_TOGGLE_QUEUE_TEST( "object/already_enqueued_main_thread", test_toggle_queue_object_from_main_thread_already_enqueued); ADD_TOGGLE_QUEUE_TEST( "object/already_enqueued_unref_main_thread", test_toggle_queue_object_from_main_thread_unref_already_enqueued); ADD_TOGGLE_QUEUE_TEST("object/ref_unref_other_thread", test_toggle_queue_object_from_other_thread_ref_unref); ADD_TOGGLE_QUEUE_TEST("object/handle_up", test_toggle_queue_object_handle_up); ADD_TOGGLE_QUEUE_TEST("object/handle_up_down", test_toggle_queue_object_handle_up_down); ADD_TOGGLE_QUEUE_TEST("object/handle_up_down_delayed", test_toggle_queue_object_handle_up_down_delayed); ADD_TOGGLE_QUEUE_TEST("object/handle_up_down_on_gc", test_toggle_queue_object_handle_up_down_on_gc); ADD_TOGGLE_QUEUE_TEST("object/handle_many_up", test_toggle_queue_object_handle_many_up); ADD_TOGGLE_QUEUE_TEST("object/handle_many_up_and_down", test_toggle_queue_object_handle_many_up_and_down); #undef ADD_TOGGLE_QUEUE_TEST } } // namespace Test } // namespace Gjs cjs-128.0/test/gjs-tests.cpp0000664000175000017500000012767714771557763014707 0ustar fabiofabio/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2008 litl, LLC #include #include #include // for size_t, strlen #include #include #include // for u16string, u32string #include #include #include #include #include // for g_unlink #include #include #include #include #include #include #include #include #include #include #include #include #include // for UniqueChars #include #include // for JS_GetClassObject #include // for JSProto_Number #include // for MakeStringSpan #include "gi/arg-inl.h" #include "gi/js-value-inl.h" #include "cjs/context.h" #include "cjs/error-types.h" #include "cjs/jsapi-util.h" #include "cjs/profiler.h" #include "test/gjs-test-no-introspection-object.h" #include "test/gjs-test-utils.h" #include "util/misc.h" namespace mozilla { union Utf8Unit; } // COMPAT: https://gitlab.gnome.org/GNOME/glib/-/merge_requests/1553 #ifdef __clang_analyzer__ void g_assertion_message(const char*, const char*, int, const char*, const char*) __attribute__((analyzer_noreturn)); #endif #define VALID_UTF8_STRING "\303\211\303\226 foobar \343\203\237" namespace Gjs { namespace Test { static unsigned cpp_random_seed = 0; using Gjs::Test::assert_equal; template struct is_char_helper : public std::false_type {}; template <> struct is_char_helper : public std::true_type {}; template <> struct is_char_helper : public std::true_type {}; template <> struct is_char_helper : public std::true_type {}; template <> struct is_char_helper : public std::true_type {}; template struct is_char : public is_char_helper>::type {}; template inline constexpr bool is_char_v = is_char::value; template T get_random_number() { std::mt19937_64 gen(cpp_random_seed); if constexpr (std::is_same_v) { return g_random_boolean(); } else if constexpr (is_char_v) { return std::char_traits::to_char_type( get_random_number::int_type>()); } else if constexpr (std::is_integral_v) { T lowest_value = std::numeric_limits::lowest(); if constexpr (std::is_unsigned_v) lowest_value = 1; return std::uniform_int_distribution(lowest_value)(gen); } else if constexpr (std::is_arithmetic_v) { T lowest_value = std::numeric_limits::epsilon(); return std::uniform_real_distribution(lowest_value)(gen); } else if constexpr (std::is_pointer_v) { return reinterpret_cast(get_random_number()); } // COMPAT: Work around cppcheck bug https://trac.cppcheck.net/ticket/10731 g_assert_not_reached(); } static void gjstest_test_func_gjs_context_construct_destroy(void) { GjsContext *context; /* Construct twice just to possibly a case where global state from * the first leaks. */ context = gjs_context_new (); g_object_unref (context); context = gjs_context_new (); g_object_unref (context); } static void gjstest_test_func_gjs_context_construct_eval(void) { GjsContext *context; int estatus; GjsAutoError error; context = gjs_context_new (); if (!gjs_context_eval (context, "1+1", -1, "", &estatus, &error)) g_error ("%s", error->message); g_object_unref (context); } static void gjstest_test_func_gjs_context_eval_dynamic_import() { GjsAutoUnref gjs = gjs_context_new(); GjsAutoError error; int status; bool ok = gjs_context_eval(gjs, R"js( import('system') .catch(logError) .finally(() => imports.mainloop.quit()); imports.mainloop.run(); )js", -1, "
", &status, &error); g_assert_true(ok); g_assert_no_error(error); } static void gjstest_test_func_gjs_context_eval_dynamic_import_relative() { GjsAutoUnref gjs = gjs_context_new(); GjsAutoError error; int status; bool ok = g_file_set_contents("num.js", "export default 77;", -1, &error); g_assert_true(ok); g_assert_no_error(error); ok = gjs_context_eval(gjs, R"js( let num; import('./num.js') .then(module => (num = module.default)) .catch(logError) .finally(() => imports.mainloop.quit()); imports.mainloop.run(); num; )js", -1, "
", &status, &error); g_assert_true(ok); g_assert_no_error(error); g_assert_cmpint(status, ==, 77); g_unlink("num.js"); } static void gjstest_test_func_gjs_context_eval_dynamic_import_bad() { GjsAutoUnref gjs = gjs_context_new(); GjsAutoError error; int status; g_test_expect_message("Cjs", G_LOG_LEVEL_WARNING, "*Unknown module: 'badmodule'*"); bool ok = gjs_context_eval(gjs, R"js( let isBad = false; import('badmodule') .catch(err => { logError(err); isBad = true; }) .finally(() => imports.mainloop.quit()); imports.mainloop.run(); if (isBad) imports.system.exit(10); )js", -1, "
", &status, &error); g_assert_false(ok); g_assert_cmpuint(status, ==, 10); g_test_assert_expected_messages(); } static void gjstest_test_func_gjs_context_eval_non_zero_terminated(void) { GjsAutoUnref gjs = gjs_context_new(); GjsAutoError error; int status; // This string is invalid JS if it is treated as zero-terminated bool ok = gjs_context_eval(gjs, "77!", 2, "", &status, &error); g_assert_true(ok); g_assert_no_error(error); g_assert_cmpint(status, ==, 77); } static void gjstest_test_func_gjs_context_exit(void) { GjsContext *context = gjs_context_new(); GjsAutoError error; int status; bool ok = gjs_context_eval(context, "imports.system.exit(0);", -1, "", &status, &error); g_assert_false(ok); g_assert_error(error, GJS_ERROR, GJS_ERROR_SYSTEM_EXIT); g_assert_cmpuint(status, ==, 0); error.reset(); ok = gjs_context_eval(context, "imports.system.exit(42);", -1, "", &status, &error); g_assert_false(ok); g_assert_error(error, GJS_ERROR, GJS_ERROR_SYSTEM_EXIT); g_assert_cmpuint(status, ==, 42); g_object_unref(context); } static void gjstest_test_func_gjs_context_eval_module_file() { GjsAutoUnref gjs = gjs_context_new(); uint8_t exit_status; GjsAutoError error; bool ok = gjs_context_eval_module_file( gjs, "resource:///org/cinnamon/cjs/mock/test/modules/default.js", &exit_status, &error); g_assert_true(ok); g_assert_no_error(error); // for modules, last executed statement is _not_ the exit code g_assert_cmpuint(exit_status, ==, 0); } static void gjstest_test_func_gjs_context_eval_module_file_throw() { GjsAutoUnref gjs = gjs_context_new(); uint8_t exit_status; GjsAutoError error; g_test_expect_message("Cjs", G_LOG_LEVEL_CRITICAL, "*bad module*"); bool ok = gjs_context_eval_module_file( gjs, "resource:///org/cinnamon/cjs/mock/test/modules/throws.js", &exit_status, &error); g_assert_false(ok); g_assert_error(error, GJS_ERROR, GJS_ERROR_FAILED); g_assert_cmpuint(exit_status, ==, 1); g_test_assert_expected_messages(); } static void gjstest_test_func_gjs_context_eval_module_file_exit() { GjsAutoUnref gjs = gjs_context_new(); GjsAutoError error; uint8_t exit_status; bool ok = gjs_context_eval_module_file( gjs, "resource:///org/cinnamon/cjs/mock/test/modules/exit0.js", &exit_status, &error); g_assert_false(ok); g_assert_error(error, GJS_ERROR, GJS_ERROR_SYSTEM_EXIT); g_assert_cmpuint(exit_status, ==, 0); error.reset(); ok = gjs_context_eval_module_file( gjs, "resource:///org/cinnamon/cjs/mock/test/modules/exit.js", &exit_status, &error); g_assert_false(ok); g_assert_error(error, GJS_ERROR, GJS_ERROR_SYSTEM_EXIT); g_assert_cmpuint(exit_status, ==, 42); } static void gjstest_test_func_gjs_context_eval_module_file_fail_instantiate() { GjsAutoUnref gjs = gjs_context_new(); GjsAutoError error; uint8_t exit_status; g_test_expect_message("Cjs", G_LOG_LEVEL_WARNING, "*foo*"); // evaluating this module without registering 'foo' first should make it // fail ModuleLink bool ok = gjs_context_eval_module_file( gjs, "resource:///org/cinnamon/cjs/mock/test/modules/import.js", &exit_status, &error); g_assert_false(ok); g_assert_error(error, GJS_ERROR, GJS_ERROR_FAILED); g_assert_cmpuint(exit_status, ==, 1); g_test_assert_expected_messages(); } static void gjstest_test_func_gjs_context_eval_module_file_exit_code_omitted_warning() { GjsAutoUnref gjs = gjs_context_new(); GjsAutoError error; g_test_expect_message("Cjs", G_LOG_LEVEL_WARNING, "*foo*"); bool ok = gjs_context_eval_module_file( gjs, "resource:///org/cinnamon/cjs/mock/test/modules/import.js", nullptr, &error); g_assert_false(ok); g_assert_error(error, GJS_ERROR, GJS_ERROR_FAILED); g_test_assert_expected_messages(); } static void gjstest_test_func_gjs_context_eval_module_file_exit_code_omitted_no_warning() { GjsAutoUnref gjs = gjs_context_new(); GjsAutoError error; bool ok = gjs_context_eval_module_file( gjs, "resource:///org/cinnamon/cjs/mock/test/modules/default.js", nullptr, &error); g_assert_true(ok); g_assert_no_error(error); } static void gjstest_test_func_gjs_context_eval_file_exit_code_omitted_throw() { GjsAutoUnref gjs = gjs_context_new(); GjsAutoError error; g_test_expect_message("Cjs", G_LOG_LEVEL_CRITICAL, "*bad module*"); bool ok = gjs_context_eval_file( gjs, "resource:///org/cinnamon/cjs/mock/test/modules/throws.js", nullptr, &error); g_assert_false(ok); g_assert_error(error, GJS_ERROR, GJS_ERROR_FAILED); g_test_assert_expected_messages(); } static void gjstest_test_func_gjs_context_eval_file_exit_code_omitted_no_throw() { GjsAutoUnref gjs = gjs_context_new(); GjsAutoError error; bool ok = gjs_context_eval_file( gjs, "resource:///org/cinnamon/cjs/mock/test/modules/nothrows.js", nullptr, &error); g_assert_true(ok); g_assert_no_error(error); } static void gjstest_test_func_gjs_context_register_module_eval_module() { GjsAutoUnref gjs = gjs_context_new(); GjsAutoError error; bool ok = gjs_context_register_module( gjs, "foo", "resource:///org/cinnamon/cjs/mock/test/modules/default.js", &error); g_assert_true(ok); g_assert_no_error(error); uint8_t exit_status; ok = gjs_context_eval_module(gjs, "foo", &exit_status, &error); g_assert_true(ok); g_assert_no_error(error); g_assert_cmpuint(exit_status, ==, 0); } static void gjstest_test_func_gjs_context_register_module_eval_module_file() { GjsAutoUnref gjs = gjs_context_new(); GjsAutoError error; bool ok = gjs_context_register_module( gjs, "foo", "resource:///org/cinnamon/cjs/mock/test/modules/default.js", &error); g_assert_true(ok); g_assert_no_error(error); uint8_t exit_status; ok = gjs_context_eval_module_file( gjs, "resource:///org/cinnamon/cjs/mock/test/modules/import.js", &exit_status, &error); g_assert_true(ok); g_assert_no_error(error); g_assert_cmpuint(exit_status, ==, 0); } static void gjstest_test_func_gjs_context_register_module_eval_jsapi( GjsUnitTestFixture* fx, const void*) { GjsAutoError error; bool ok = gjs_context_register_module( fx->gjs_context, "foo", "resource:///org/cinnamon/cjs/mock/test/modules/default.js", &error); g_assert_true(ok); g_assert_no_error(error); JS::CompileOptions options{fx->cx}; options.setFileAndLine("import.js", 1); static const char* code = R"js( let error; const loop = new imports.gi.GLib.MainLoop(null, false); import('foo') .then(module => { if (module.default !== 77) throw new Error('wrong number'); }) .catch(e => (error = e)) .finally(() => loop.quit()); loop.run(); if (error) throw error; )js"; JS::SourceText source; ok = source.init(fx->cx, code, strlen(code), JS::SourceOwnership::Borrowed); g_assert_true(ok); JS::RootedValue unused{fx->cx}; ok = JS::Evaluate(fx->cx, options, source, &unused); gjs_log_exception(fx->cx); // will fail test if exception pending g_assert_true(ok); } static void gjstest_test_func_gjs_context_register_module_eval_jsapi_rel( GjsUnitTestFixture* fx, const void*) { JS::CompileOptions options{fx->cx}; options.setFileAndLine("import.js", 1); static const char* code = R"js( let error; const loop = new imports.gi.GLib.MainLoop(null, false); import('./foo.js') .catch(e => (error = e)) .finally(() => loop.quit()); loop.run(); if (error) throw error; )js"; JS::SourceText source; bool ok = source.init(fx->cx, code, strlen(code), JS::SourceOwnership::Borrowed); g_assert_true(ok); JS::RootedValue unused{fx->cx}; ok = JS::Evaluate(fx->cx, options, source, &unused); g_assert_false(ok); g_test_expect_message("Cjs", G_LOG_LEVEL_WARNING, "JS ERROR: ImportError*relative*"); gjs_log_exception(fx->cx); g_test_assert_expected_messages(); } static void gjstest_test_func_gjs_context_register_module_non_existent() { GjsAutoUnref gjs = gjs_context_new(); GjsAutoError error; bool ok = gjs_context_register_module(gjs, "foo", "nonexist.js", &error); g_assert_false(ok); g_assert_error(error, GJS_ERROR, GJS_ERROR_FAILED); } static void gjstest_test_func_gjs_context_eval_module_unregistered() { GjsAutoUnref gjs = gjs_context_new(); GjsAutoError error; uint8_t exit_status; bool ok = gjs_context_eval_module(gjs, "foo", &exit_status, &error); g_assert_false(ok); g_assert_error(error, GJS_ERROR, GJS_ERROR_FAILED); g_assert_cmpuint(exit_status, ==, 1); } static void gjstest_test_func_gjs_context_eval_module_exit_code_omitted_throw() { GjsAutoUnref gjs = gjs_context_new(); GjsAutoError error; bool ok = gjs_context_eval_module(gjs, "foo", nullptr, &error); g_assert_false(ok); g_assert_error(error, GJS_ERROR, GJS_ERROR_FAILED); } static void gjstest_test_func_gjs_context_eval_module_exit_code_omitted_no_throw() { GjsAutoUnref gjs = gjs_context_new(); GjsAutoError error; bool ok = gjs_context_register_module( gjs, "lies", "resource:///org/cinnamon/cjs/mock/test/modules/nothrows.js", &error); g_assert_true(ok); g_assert_no_error(error); ok = gjs_context_eval_module(gjs, "lies", NULL, &error); g_assert_true(ok); g_assert_no_error(error); } static void gjstest_test_func_gjs_context_module_eval_jsapi_throws( GjsUnitTestFixture* fx, const void*) { GjsAutoError error; bool ok = gjs_context_register_module( fx->gjs_context, "foo", "resource:///org/cinnamon/cjs/mock/test/modules/throws.js", &error); g_assert_true(ok); g_assert_no_error(error); JS::CompileOptions options{fx->cx}; options.setFileAndLine("import.js", 1); static const char* code = R"js( let error; const loop = new imports.gi.GLib.MainLoop(null, false); import('foo') .catch(e => (error = e)) .finally(() => loop.quit()); loop.run(); error; )js"; JS::SourceText source; ok = source.init(fx->cx, code, strlen(code), JS::SourceOwnership::Borrowed); g_assert_true(ok); JS::RootedValue thrown{fx->cx}; ok = JS::Evaluate(fx->cx, options, source, &thrown); gjs_log_exception(fx->cx); // will fail test if exception pending g_assert_true(ok); g_assert_true(thrown.isObject()); JS::RootedObject thrown_obj{fx->cx, &thrown.toObject()}; JS::RootedValue message{fx->cx}; ok = JS_GetProperty(fx->cx, thrown_obj, "message", &message); g_assert_true(ok); g_assert_true(message.isString()); bool match = false; ok = JS_StringEqualsAscii(fx->cx, message.toString(), "bad module", &match); g_assert_true(ok); g_assert_true(match); } static void gjstest_test_func_gjs_context_run_in_realm() { GjsAutoUnref gjs = gjs_context_new(); auto* cx = static_cast(gjs_context_get_native_context(gjs)); g_assert_null(JS::GetCurrentRealmOrNull(cx)); struct RunInRealmData { int sentinel; bool has_run; } data{42, false}; gjs_context_run_in_realm( gjs, [](GjsContext* gjs, void* ptr) { g_assert_true(GJS_IS_CONTEXT(gjs)); auto* data = static_cast(ptr); g_assert_cmpint(data->sentinel, ==, 42); auto* cx = static_cast(gjs_context_get_native_context(gjs)); g_assert_nonnull(JS::GetCurrentRealmOrNull(cx)); data->has_run = true; }, &data); g_assert_null(JS::GetCurrentRealmOrNull(cx)); g_assert_true(data.has_run); } #define JS_CLASS "\ const GObject = imports.gi.GObject; \ const FooBar = GObject.registerClass(class FooBar extends GObject.Object {}); \ " static void gjstest_test_func_gjs_gobject_js_defined_type(void) { GjsContext *context = gjs_context_new(); GjsAutoError error; int status; bool ok = gjs_context_eval(context, JS_CLASS, -1, "", &status, &error); g_assert_no_error(error); g_assert_true(ok); GType foo_type = g_type_from_name("Gjs_FooBar"); g_assert_cmpuint(foo_type, !=, G_TYPE_INVALID); gpointer foo = g_object_new(foo_type, NULL); g_assert_true(G_IS_OBJECT(foo)); g_object_unref(foo); g_object_unref(context); } static void gjstest_test_func_gjs_gobject_without_introspection(void) { GjsAutoUnref context = gjs_context_new(); GjsAutoError error; int status; /* Ensure class */ g_type_class_ref(GJSTEST_TYPE_NO_INTROSPECTION_OBJECT); #define TESTJS \ "const {GObject} = imports.gi;" \ "var obj = GObject.Object.newv(" \ " GObject.type_from_name('GjsTestNoIntrospectionObject'), []);" \ "obj.a_int = 1234;" bool ok = gjs_context_eval(context, TESTJS, -1, "", &status, &error); g_assert_true(ok); g_assert_no_error(error); GjsTestNoIntrospectionObject* obj = gjstest_no_introspection_object_peek(); g_assert_nonnull(obj); int val = 0; g_object_get(obj, "a-int", &val, NULL); g_assert_cmpint(val, ==, 1234); #undef TESTJS } static void gjstest_test_func_gjs_context_eval_exit_code_omitted_throw() { GjsAutoUnref context = gjs_context_new(); GjsAutoError error; g_test_expect_message("Cjs", G_LOG_LEVEL_CRITICAL, "*wrong code*"); const char bad_js[] = "throw new Error('wrong code');"; bool ok = gjs_context_eval(context, bad_js, -1, "", nullptr, &error); g_assert_false(ok); g_assert_error(error, GJS_ERROR, GJS_ERROR_FAILED); g_test_assert_expected_messages(); } static void gjstest_test_func_gjs_context_eval_exit_code_omitted_no_throw() { GjsAutoUnref context = gjs_context_new(); GjsAutoError error; const char good_js[] = "let num = 77;"; bool ok = gjs_context_eval(context, good_js, -1, "", nullptr, &error); g_assert_true(ok); g_assert_no_error(error); } static void gjstest_test_func_gjs_jsapi_util_string_js_string_utf8( GjsUnitTestFixture* fx, const void*) { JS::RootedValue js_string(fx->cx); g_assert_true(gjs_string_from_utf8(fx->cx, VALID_UTF8_STRING, &js_string)); g_assert_true(js_string.isString()); JS::UniqueChars utf8_result = gjs_string_to_utf8(fx->cx, js_string); g_assert_nonnull(utf8_result); g_assert_cmpstr(VALID_UTF8_STRING, ==, utf8_result.get()); } static void gjstest_test_func_gjs_jsapi_util_error_throw(GjsUnitTestFixture* fx, const void*) { JS::RootedValue exc(fx->cx), value(fx->cx); /* Test that we can throw */ gjs_throw(fx->cx, "This is an exception %d", 42); g_assert_true(JS_IsExceptionPending(fx->cx)); JS_GetPendingException(fx->cx, &exc); g_assert_false(exc.isUndefined()); JS::RootedObject exc_obj(fx->cx, &exc.toObject()); JS_GetProperty(fx->cx, exc_obj, "message", &value); g_assert_true(value.isString()); JS::UniqueChars s = gjs_string_to_utf8(fx->cx, value); g_assert_nonnull(s); g_assert_cmpstr(s.get(), ==, "This is an exception 42"); /* keep this around before we clear it */ JS::RootedValue previous(fx->cx, exc); JS_ClearPendingException(fx->cx); g_assert_false(JS_IsExceptionPending(fx->cx)); /* Check that we don't overwrite a pending exception */ JS_SetPendingException(fx->cx, previous); g_assert_true(JS_IsExceptionPending(fx->cx)); gjs_throw(fx->cx, "Second different exception %s", "foo"); g_assert_true(JS_IsExceptionPending(fx->cx)); exc = JS::UndefinedValue(); JS_GetPendingException(fx->cx, &exc); g_assert_false(exc.isUndefined()); g_assert_true(&exc.toObject() == &previous.toObject()); } static void test_jsapi_util_error_throw_cause(GjsUnitTestFixture* fx, const void*) { g_test_expect_message("Cjs", G_LOG_LEVEL_WARNING, "JS ERROR: Error: Exception 1\n" "Caused by: Error: Exception 2"); gjs_throw(fx->cx, "Exception 1"); gjs_throw(fx->cx, "Exception 2"); gjs_log_exception(fx->cx); g_test_expect_message("Cjs", G_LOG_LEVEL_WARNING, "JS ERROR: Error: Exception 1\n" "Caused by: Error: Exception 2\n" "Caused by: Error: Exception 3"); gjs_throw(fx->cx, "Exception 1"); gjs_throw(fx->cx, "Exception 2"); gjs_throw(fx->cx, "Exception 3"); gjs_log_exception(fx->cx); g_test_expect_message("Cjs", G_LOG_LEVEL_WARNING, "JS ERROR: 42"); JS::RootedValue non_object(fx->cx, JS::Int32Value(42)); JS_SetPendingException(fx->cx, non_object); gjs_throw(fx->cx, "This exception will be dropped"); gjs_log_exception(fx->cx); g_test_assert_expected_messages(); } static void test_jsapi_util_string_utf8_nchars_to_js(GjsUnitTestFixture* fx, const void*) { JS::RootedValue v_out(fx->cx); bool ok = gjs_string_from_utf8_n(fx->cx, VALID_UTF8_STRING, strlen(VALID_UTF8_STRING), &v_out); g_assert_true(ok); g_assert_true(v_out.isString()); } static void test_jsapi_util_string_char16_data(GjsUnitTestFixture* fx, const void*) { char16_t *chars; size_t len; JS::ConstUTF8CharsZ jschars(VALID_UTF8_STRING, strlen(VALID_UTF8_STRING)); JS::RootedString str(fx->cx, JS_NewStringCopyUTF8Z(fx->cx, jschars)); g_assert_true(gjs_string_get_char16_data(fx->cx, str, &chars, &len)); std::u16string result(chars, len); g_assert_true(result == u"\xc9\xd6 foobar \u30df"); g_free(chars); /* Try with a string that is likely to be stored as Latin-1 */ str = JS_NewStringCopyZ(fx->cx, "abcd"); bool ok = gjs_string_get_char16_data(fx->cx, str, &chars, &len); g_assert_true(ok); result.assign(chars, len); g_assert_true(result == u"abcd"); g_free(chars); } static void test_jsapi_util_string_to_ucs4(GjsUnitTestFixture* fx, const void*) { gunichar *chars; size_t len; JS::ConstUTF8CharsZ jschars(VALID_UTF8_STRING, strlen(VALID_UTF8_STRING)); JS::RootedString str(fx->cx, JS_NewStringCopyUTF8Z(fx->cx, jschars)); g_assert_true(gjs_string_to_ucs4(fx->cx, str, &chars, &len)); std::u32string result(chars, chars + len); g_assert_true(result == U"\xc9\xd6 foobar \u30df"); g_free(chars); /* Try with a string that is likely to be stored as Latin-1 */ str = JS_NewStringCopyZ(fx->cx, "abcd"); bool ok = gjs_string_to_ucs4(fx->cx, str, &chars, &len); g_assert_true(ok); result.assign(chars, chars + len); g_assert_true(result == U"abcd"); g_free(chars); } static void test_gjs_debug_id_string_no_quotes(GjsUnitTestFixture* fx, const void*) { jsid id = gjs_intern_string_to_id(fx->cx, "prop_key"); std::string debug_output = gjs_debug_id(id); g_assert_cmpstr(debug_output.c_str(), ==, "prop_key"); } static void test_gjs_debug_string_quotes(GjsUnitTestFixture* fx, const void*) { JS::ConstUTF8CharsZ chars("a string", strlen("a string")); JSString* str = JS_NewStringCopyUTF8Z(fx->cx, chars); std::string debug_output = gjs_debug_string(str); g_assert_cmpstr(debug_output.c_str(), ==, "\"a string\""); } static void test_gjs_debug_value_bigint(GjsUnitTestFixture* fx, const void*) { JS::BigInt* bi = JS::NumberToBigInt(fx->cx, 42); std::string debug_output = gjs_debug_bigint(bi); g_assert_cmpstr(debug_output.c_str(), ==, "42n (modulo 2^64)"); bi = JS::NumberToBigInt(fx->cx, -42); debug_output = gjs_debug_bigint(bi); g_assert_cmpstr(debug_output.c_str(), ==, "-42n (modulo 2^64)"); } static void test_gjs_debug_value_bigint_uint64(GjsUnitTestFixture* fx, const void*) { // gjs_debug_value(BigIntValue) prints whatever fits into int64_t, because // more complicated operations might be fallible JS::BigInt* bi = JS::NumberToBigInt(fx->cx, G_MAXUINT64); std::string debug_output = gjs_debug_bigint(bi); g_assert_cmpstr(debug_output.c_str(), ==, "18446744073709551615n (modulo 2^64)"); } static void test_gjs_debug_value_bigint_huge(GjsUnitTestFixture* fx, const void*) { JS::BigInt* bi = JS::SimpleStringToBigInt( fx->cx, mozilla::MakeStringSpan("10000000000000001"), 16); std::string debug_output = gjs_debug_bigint(bi); g_assert_cmpstr(debug_output.c_str(), ==, "1n (modulo 2^64)"); bi = JS::SimpleStringToBigInt( fx->cx, mozilla::MakeStringSpan("-10000000000000001"), 16); debug_output = gjs_debug_bigint(bi); g_assert_cmpstr(debug_output.c_str(), ==, "-1n (modulo 2^64)"); } static void test_gjs_debug_value_string_quotes(GjsUnitTestFixture* fx, const void*) { JS::RootedValue v(fx->cx); bool ok = gjs_string_from_utf8(fx->cx, "a string", &v); g_assert_true(ok); std::string debug_output = gjs_debug_value(v); g_assert_cmpstr(debug_output.c_str(), ==, "\"a string\""); } static void gjstest_test_func_util_misc_strv_concat_null(void) { char **ret; ret = gjs_g_strv_concat(NULL, 0); g_assert_nonnull(ret); g_assert_null(ret[0]); g_strfreev(ret); } static void gjstest_test_func_util_misc_strv_concat_pointers(void) { char *strv0[2] = {(char*)"foo", NULL}; char *strv1[1] = {NULL}; char **strv2 = NULL; char *strv3[2] = {(char*)"bar", NULL}; char **stuff[4]; char **ret; stuff[0] = strv0; stuff[1] = strv1; stuff[2] = strv2; stuff[3] = strv3; ret = gjs_g_strv_concat(stuff, 4); g_assert_nonnull(ret); g_assert_cmpstr(ret[0], ==, strv0[0]); /* same string */ g_assert_true(ret[0] != strv0[0]); // different pointer g_assert_cmpstr(ret[1], ==, strv3[0]); g_assert_true(ret[1] != strv3[0]); g_assert_null(ret[2]); g_strfreev(ret); } static void gjstest_test_profiler_start_stop(void) { GjsAutoUnref context = GJS_CONTEXT( g_object_new(GJS_TYPE_CONTEXT, "profiler-enabled", TRUE, nullptr)); GjsProfiler *profiler = gjs_context_get_profiler(context); gjs_profiler_set_filename(profiler, "dont-conflict-with-other-test.syscap"); gjs_profiler_start(profiler); for (size_t ix = 0; ix < 100; ix++) { GjsAutoError error; int estatus; #define TESTJS "[1,5,7,1,2,3,67,8].sort()" if (!gjs_context_eval(context, TESTJS, -1, "", &estatus, &error)) g_printerr("ERROR: %s", error->message); #undef TESTJS } gjs_profiler_stop(profiler); if (g_unlink("dont-conflict-with-other-test.syscap") != 0) g_message("Temp profiler file not deleted"); } static void gjstest_test_safe_integer_max(GjsUnitTestFixture* fx, const void*) { JS::RootedObject number_class_object(fx->cx); JS::RootedValue safe_value(fx->cx); g_assert_true( JS_GetClassObject(fx->cx, JSProto_Number, &number_class_object)); g_assert_true(JS_GetProperty(fx->cx, number_class_object, "MAX_SAFE_INTEGER", &safe_value)); g_assert_cmpint(safe_value.toNumber(), ==, Gjs::max_safe_big_number()); } static void gjstest_test_safe_integer_min(GjsUnitTestFixture* fx, const void*) { JS::RootedObject number_class_object(fx->cx); JS::RootedValue safe_value(fx->cx); g_assert_true( JS_GetClassObject(fx->cx, JSProto_Number, &number_class_object)); g_assert_true(JS_GetProperty(fx->cx, number_class_object, "MIN_SAFE_INTEGER", &safe_value)); g_assert_cmpint(safe_value.toNumber(), ==, Gjs::min_safe_big_number()); } static void gjstest_test_args_set_get_unset() { GIArgument arg = {0}; gjs_arg_set(&arg, true); g_assert_true(arg.v_boolean); gjs_arg_set(&arg, false); g_assert_false(arg.v_boolean); gjs_arg_set(&arg, true); g_assert_true(arg.v_boolean); gjs_arg_unset(&arg); g_assert_false(arg.v_boolean); int8_t random_int8 = get_random_number(); gjs_arg_set(&arg, random_int8); assert_equal(arg.v_int8, random_int8); assert_equal(gjs_arg_get(&arg), random_int8); uint8_t random_uint8 = get_random_number(); gjs_arg_set(&arg, random_uint8); assert_equal(arg.v_uint8, random_uint8); assert_equal(gjs_arg_get(&arg), random_uint8); int16_t random_int16 = get_random_number(); gjs_arg_set(&arg, random_int16); assert_equal(arg.v_int16, random_int16); assert_equal(gjs_arg_get(&arg), random_int16); uint16_t random_uint16 = get_random_number(); gjs_arg_set(&arg, random_uint16); assert_equal(arg.v_uint16, random_uint16); assert_equal(gjs_arg_get(&arg), random_uint16); int32_t random_int32 = get_random_number(); gjs_arg_set(&arg, random_int32); assert_equal(arg.v_int32, random_int32); assert_equal(gjs_arg_get(&arg), random_int32); uint32_t random_uint32 = get_random_number(); gjs_arg_set(&arg, random_uint32); assert_equal(arg.v_uint32, random_uint32); assert_equal(gjs_arg_get(&arg), random_uint32); int64_t random_int64 = get_random_number(); gjs_arg_set(&arg, random_int64); assert_equal(arg.v_int64, random_int64); assert_equal(gjs_arg_get(&arg), random_int64); uint64_t random_uint64 = get_random_number(); gjs_arg_set(&arg, random_uint64); assert_equal(arg.v_uint64, random_uint64); assert_equal(gjs_arg_get(&arg), random_uint64); char32_t random_char32 = get_random_number(); gjs_arg_set(&arg, random_char32); assert_equal(static_cast(arg.v_uint32), random_char32); assert_equal(gjs_arg_get(&arg), random_char32); float random_float = get_random_number(); gjs_arg_set(&arg, random_float); assert_equal(arg.v_float, random_float); assert_equal(gjs_arg_get(&arg), random_float); double random_double = get_random_number(); gjs_arg_set(&arg, random_double); assert_equal(arg.v_double, random_double); assert_equal(gjs_arg_get(&arg), random_double); void* random_ptr = get_random_number(); gjs_arg_set(&arg, random_ptr); assert_equal(arg.v_pointer, random_ptr); assert_equal(gjs_arg_get(&arg), random_ptr); GjsAutoChar cstr = g_strdup("Gjs argument string"); gjs_arg_set(&arg, cstr.get()); assert_equal(arg.v_string, const_cast("Gjs argument string")); assert_equal(static_cast(arg.v_string), static_cast(cstr.get())); gjs_arg_set(&arg, TRUE); g_assert_true(arg.v_boolean); g_assert_true((gjs_arg_get(&arg))); gjs_arg_set(&arg, FALSE); g_assert_false(arg.v_boolean); g_assert_false((gjs_arg_get(&arg))); gjs_arg_set(&arg, TRUE); g_assert_true(arg.v_boolean); gjs_arg_unset(&arg); g_assert_false(arg.v_boolean); GType random_gtype = get_random_number(); gjs_arg_set(&arg, random_gtype); if constexpr (std::is_same_v) assert_equal(static_cast(arg.v_size), random_gtype); else if constexpr (std::is_same_v) assert_equal(static_cast(arg.v_ulong), random_gtype); assert_equal(gjs_arg_get(&arg), random_gtype); int random_signed_iface = get_random_number(); gjs_arg_set(&arg, random_signed_iface); assert_equal(arg.v_int, random_signed_iface); assert_equal(gjs_arg_get(&arg), random_signed_iface); unsigned random_unsigned_iface = get_random_number(); gjs_arg_set(&arg, random_unsigned_iface); assert_equal(arg.v_uint, random_unsigned_iface); assert_equal(gjs_arg_get(&arg), random_unsigned_iface); } static void gjstest_test_args_rounded_values() { GIArgument arg = {0}; gjs_arg_set(&arg, std::numeric_limits::max()); g_test_expect_message( G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "*cannot be safely stored in a JS Number and may be rounded"); assert_equal(gjs_arg_get_maybe_rounded(&arg), static_cast(gjs_arg_get(&arg))); g_test_assert_expected_messages(); gjs_arg_set(&arg, std::numeric_limits::min()); g_test_expect_message( G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "*cannot be safely stored in a JS Number and may be rounded"); assert_equal(gjs_arg_get_maybe_rounded(&arg), static_cast(gjs_arg_get(&arg))); g_test_assert_expected_messages(); gjs_arg_set(&arg, std::numeric_limits::max()); g_test_expect_message( G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "*cannot be safely stored in a JS Number and may be rounded"); assert_equal(gjs_arg_get_maybe_rounded(&arg), static_cast(gjs_arg_get(&arg))); g_test_assert_expected_messages(); gjs_arg_set(&arg, std::numeric_limits::min()); assert_equal(gjs_arg_get_maybe_rounded(&arg), 0.0); } static void gjstest_test_func_gjs_context_argv_array() { GjsAutoUnref gjs = gjs_context_new(); GjsAutoError error; int status; const char* argv[1] = {"test"}; bool ok = gjs_context_define_string_array(gjs, "ARGV", 1, argv, &error); g_assert_no_error(error); g_assert_true(ok); ok = gjs_context_eval(gjs, R"js( imports.system.exit(ARGV[0] === "test" ? 0 : 1) )js", -1, "
", &status, &error); g_assert_cmpint(status, ==, 0); g_assert_error(error, GJS_ERROR, GJS_ERROR_SYSTEM_EXIT); g_assert_false(ok); } } // namespace Test } // namespace Gjs int main(int argc, char **argv) { using namespace Gjs::Test; // NOLINT(build/namespaces) /* Avoid interference in the tests from stray environment variable */ g_unsetenv("GJS_ENABLE_PROFILER"); g_unsetenv("GJS_TRACE_FD"); for (int i = 0; i < argc; i++) { const char* seed = nullptr; if (g_str_has_prefix(argv[i], "--cpp-seed=") && strlen(argv[i]) > 11) seed = argv[i] + 11; else if (i < argc - 1 && g_str_equal(argv[i], "--cpp-seed")) seed = argv[i + 1]; if (seed) cpp_random_seed = std::stoi(seed); } g_test_init(&argc, &argv, nullptr); if (!cpp_random_seed) cpp_random_seed = g_test_rand_int(); g_message("Using C++ random seed %u\n", cpp_random_seed); g_test_add_func("/cjs/context/construct/destroy", gjstest_test_func_gjs_context_construct_destroy); g_test_add_func("/cjs/context/construct/eval", gjstest_test_func_gjs_context_construct_eval); g_test_add_func("/cjs/context/argv", gjstest_test_func_gjs_context_argv_array); g_test_add_func("/cjs/context/eval/dynamic-import", gjstest_test_func_gjs_context_eval_dynamic_import); g_test_add_func("/cjs/context/eval/dynamic-import/relative", gjstest_test_func_gjs_context_eval_dynamic_import_relative); g_test_add_func("/cjs/context/eval/dynamic-import/bad", gjstest_test_func_gjs_context_eval_dynamic_import_bad); g_test_add_func("/cjs/context/eval/non-zero-terminated", gjstest_test_func_gjs_context_eval_non_zero_terminated); g_test_add_func("/cjs/context/exit", gjstest_test_func_gjs_context_exit); g_test_add_func("/cjs/context/eval-module-file", gjstest_test_func_gjs_context_eval_module_file); g_test_add_func("/cjs/context/eval-module-file/throw", gjstest_test_func_gjs_context_eval_module_file_throw); g_test_add_func("/cjs/context/eval-module-file/exit", gjstest_test_func_gjs_context_eval_module_file_exit); g_test_add_func( "/cjs/context/eval-module-file/fail-instantiate", gjstest_test_func_gjs_context_eval_module_file_fail_instantiate); g_test_add_func("/cjs/context/register-module/eval-module", gjstest_test_func_gjs_context_register_module_eval_module); g_test_add_func( "/cjs/context/register-module/eval-module-file", gjstest_test_func_gjs_context_register_module_eval_module_file); g_test_add("/cjs/context/register-module/eval-jsapi", GjsUnitTestFixture, nullptr, gjs_unit_test_fixture_setup, gjstest_test_func_gjs_context_register_module_eval_jsapi, gjs_unit_test_fixture_teardown); g_test_add("/cjs/context/register-module/eval-jsapi-relative", GjsUnitTestFixture, nullptr, gjs_unit_test_fixture_setup, gjstest_test_func_gjs_context_register_module_eval_jsapi_rel, gjs_unit_test_fixture_teardown); g_test_add_func("/cjs/context/register-module/non-existent", gjstest_test_func_gjs_context_register_module_non_existent); g_test_add_func("/cjs/context/eval-module/unregistered", gjstest_test_func_gjs_context_eval_module_unregistered); g_test_add_func("/cjs/gobject/js_defined_type", gjstest_test_func_gjs_gobject_js_defined_type); g_test_add_func("/cjs/gobject/without_introspection", gjstest_test_func_gjs_gobject_without_introspection); g_test_add_func("/cjs/profiler/start_stop", gjstest_test_profiler_start_stop); g_test_add_func("/util/misc/strv/concat/null", gjstest_test_func_util_misc_strv_concat_null); g_test_add_func("/util/misc/strv/concat/pointers", gjstest_test_func_util_misc_strv_concat_pointers); g_test_add_func("/gi/args/set-get-unset", gjstest_test_args_set_get_unset); g_test_add_func("/gi/args/rounded_values", gjstest_test_args_rounded_values); g_test_add_func( "/cjs/context/eval-module-file/exit-code-omitted-warning", gjstest_test_func_gjs_context_eval_module_file_exit_code_omitted_warning); g_test_add_func( "/cjs/context/eval-module-file/exit-code-omitted-no-warning", gjstest_test_func_gjs_context_eval_module_file_exit_code_omitted_no_warning); g_test_add_func("/cjs/context/eval-file/exit-code-omitted-no-throw", gjstest_test_func_gjs_context_eval_file_exit_code_omitted_no_throw); g_test_add_func("/cjs/context/eval-file/exit-code-omitted-throw", gjstest_test_func_gjs_context_eval_file_exit_code_omitted_throw); g_test_add_func("/cjs/context/eval/exit-code-omitted-throw", gjstest_test_func_gjs_context_eval_exit_code_omitted_throw); g_test_add_func("/cjs/context/eval/exit-code-omitted-no-throw", gjstest_test_func_gjs_context_eval_exit_code_omitted_no_throw); g_test_add_func("/cjs/context/eval-module/exit-code-omitted-throw", gjstest_test_func_gjs_context_eval_module_exit_code_omitted_throw); g_test_add_func( "/cjs/context/eval-module/exit-code-omitted-no-throw", gjstest_test_func_gjs_context_eval_module_exit_code_omitted_no_throw); g_test_add("/cjs/context/eval-module/jsapi-throw", GjsUnitTestFixture, nullptr, gjs_unit_test_fixture_setup, gjstest_test_func_gjs_context_module_eval_jsapi_throws, gjs_unit_test_fixture_teardown); g_test_add_func("/cjs/context/run-in-realm", gjstest_test_func_gjs_context_run_in_realm); #define ADD_JSAPI_UTIL_TEST(path, func) \ g_test_add("/cjs/jsapi/util/" path, GjsUnitTestFixture, NULL, \ gjs_unit_test_fixture_setup, func, \ gjs_unit_test_fixture_teardown) ADD_JSAPI_UTIL_TEST("error/throw", gjstest_test_func_gjs_jsapi_util_error_throw); ADD_JSAPI_UTIL_TEST("error/throw-cause", test_jsapi_util_error_throw_cause); ADD_JSAPI_UTIL_TEST("string/js/string/utf8", gjstest_test_func_gjs_jsapi_util_string_js_string_utf8); ADD_JSAPI_UTIL_TEST("string/utf8-nchars-to-js", test_jsapi_util_string_utf8_nchars_to_js); ADD_JSAPI_UTIL_TEST("string/char16_data", test_jsapi_util_string_char16_data); ADD_JSAPI_UTIL_TEST("string/to_ucs4", test_jsapi_util_string_to_ucs4); ADD_JSAPI_UTIL_TEST("gi/args/safe-integer/max", gjstest_test_safe_integer_max); ADD_JSAPI_UTIL_TEST("gi/args/safe-integer/min", gjstest_test_safe_integer_min); // Debug functions ADD_JSAPI_UTIL_TEST("debug_id/string/no-quotes", test_gjs_debug_id_string_no_quotes); ADD_JSAPI_UTIL_TEST("debug_string/quotes", test_gjs_debug_string_quotes); ADD_JSAPI_UTIL_TEST("debug_value/bigint", test_gjs_debug_value_bigint); ADD_JSAPI_UTIL_TEST("debug_value/bigint/uint64", test_gjs_debug_value_bigint_uint64); ADD_JSAPI_UTIL_TEST("debug_value/bigint/huge", test_gjs_debug_value_bigint_huge); ADD_JSAPI_UTIL_TEST("debug_value/string/quotes", test_gjs_debug_value_string_quotes); #undef ADD_JSAPI_UTIL_TEST gjs_test_add_tests_for_coverage (); g_test_run(); return 0; } cjs-128.0/test/mock-js-resources.gresource.xml0000664000175000017500000000104514771557763020326 0ustar fabiofabio test/gjs-test-coverage/loadedJSFromResource.js test/modules/default.js test/modules/exit.js test/modules/exit0.js test/modules/import.js test/modules/throws.js test/modules/nothrows.js cjs-128.0/test/gjs-test-utils.h0000664000175000017500000000534514771557763015312 0ustar fabiofabio/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2013 Endless Mobile, Inc. // SPDX-FileContributor: Authored By: Sam Spilsbury #ifndef TEST_GJS_TEST_UTILS_H_ #define TEST_GJS_TEST_UTILS_H_ #include #include // for uintptr_t #include // for pair #include // for numeric_limits #include #include // for is_same #include // IWYU pragma: keep #include // for g_assert_... #include #include "cjs/context.h" struct GjsUnitTestFixture { GjsContext *gjs_context; JSContext *cx; JS::Realm* realm; }; void gjs_unit_test_fixture_setup(GjsUnitTestFixture* fx, const void* unused); void gjs_unit_test_destroy_context(GjsUnitTestFixture *fx); void gjs_unit_test_fixture_teardown(GjsUnitTestFixture* fx, const void* unused); void gjs_test_add_tests_for_coverage (); void gjs_test_add_tests_for_parse_call_args(void); void gjs_test_add_tests_for_rooting(void); void gjs_test_add_tests_for_jsapi_utils(); namespace Gjs { namespace Test { void add_tests_for_toggle_queue(); template constexpr bool comparable_types() { if constexpr (std::is_same()) { return true; } else if constexpr (std::is_arithmetic_v == std::is_arithmetic_v) { return std::is_signed_v == std::is_signed_v; } else if constexpr (std::is_enum_v == std::is_enum_v) { return std::is_signed_v == std::is_signed_v; } else { return false; } } template constexpr void assert_equal(T a, U b) { static_assert(comparable_types()); if constexpr (std::is_integral_v || std::is_enum_v) { if constexpr (std::is_unsigned_v) g_assert_cmpuint(a, ==, b); else g_assert_cmpint(a, ==, b); } else if constexpr (std::is_arithmetic_v) { g_assert_cmpfloat_with_epsilon(a, b, std::numeric_limits::epsilon()); } else if constexpr (std::is_same_v) { g_assert_cmpstr(a, ==, b); } else if constexpr (std::is_same_v) { assert_equal(a.c_str(), b.c_str()); } else if constexpr (std::is_pointer_v) { assert_equal(reinterpret_cast(a), reinterpret_cast(b)); } else { g_assert_true(a == b); } } template constexpr void assert_equal(std::pair const& pair, T first, U second) { assert_equal(pair.first, first); assert_equal(pair.second, second); } } // namespace Test } // namespace Gjs #endif // TEST_GJS_TEST_UTILS_H_ cjs-128.0/test/gjs-test-common.h0000664000175000017500000000055314771557763015436 0ustar fabiofabio/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2018 Philip Chimento #ifndef TEST_GJS_TEST_COMMON_H_ #define TEST_GJS_TEST_COMMON_H_ #include struct JSContext; char* gjs_test_get_exception_message(JSContext* cx); #endif // TEST_GJS_TEST_COMMON_H_ cjs-128.0/test/modules/0000775000175000017500000000000014771557763013704 5ustar fabiofabiocjs-128.0/test/modules/default.js0000664000175000017500000000031414771557763015664 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2021 Philip Chimento export default 77; // eslint-disable-next-line no-unused-expressions 77; cjs-128.0/test/modules/exit0.js0000664000175000017500000000025714771557763015277 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2021 Philip Chimento import System from 'system'; System.exit(0); cjs-128.0/test/modules/exit.js0000664000175000017500000000026014771557763015211 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2021 Philip Chimento import System from 'system'; System.exit(42); cjs-128.0/test/modules/throws.js0000664000175000017500000000024114771557763015565 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2021 Philip Chimento throw new Error('bad module'); cjs-128.0/test/modules/.eslintrc.yml0000664000175000017500000000024714771557763016333 0ustar fabiofabio--- # SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2021 Philip Chimento parserOptions: sourceType: module cjs-128.0/test/modules/import.js0000664000175000017500000000031614771557763015554 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2021 Philip Chimento import num from 'foo'; if (num !== 77) throw new Error('wrong number'); cjs-128.0/test/modules/nothrows.js0000664000175000017500000000021014771557763016116 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2022 Nasah Kuma let num_ = 77; cjs-128.0/test/gjs-tests-internal.cpp0000664000175000017500000000134514771557763016500 0ustar fabiofabio/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2021 Canonical, Ltd. // SPDX-FileContributor: Marco Trevisan #include #include #include "test/gjs-test-utils.h" int main(int argc, char** argv) { /* Avoid interference in the tests from stray environment variable */ g_unsetenv("GJS_ENABLE_PROFILER"); g_unsetenv("GJS_TRACE_FD"); g_test_init(&argc, &argv, nullptr); gjs_test_add_tests_for_rooting(); gjs_test_add_tests_for_parse_call_args(); gjs_test_add_tests_for_jsapi_utils(); Gjs::Test::add_tests_for_toggle_queue(); g_test_run(); return 0; } cjs-128.0/test/gjs-test-coverage.cpp0000664000175000017500000014432214771557763016277 0ustar fabiofabio/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2014 Endless Mobile, Inc. // SPDX-FileContributor: Authored By: Sam Spilsbury #include #include // for errno #include // for sscanf, size_t #include // for strtol, atoi, mkdtemp #include // for strlen, strstr, strcmp, strncmp, strcspn #include #include #include #include "cjs/context.h" #include "cjs/coverage.h" #include "cjs/jsapi-util.h" // COMPAT: https://gitlab.gnome.org/GNOME/glib/-/merge_requests/1553 #ifdef __clang_analyzer__ void g_assertion_message(const char*, const char*, int, const char*, const char*) __attribute__((analyzer_noreturn)); #endif typedef struct _GjsCoverageFixture { GjsContext *context; GjsCoverage *coverage; GFile *tmp_output_dir; GFile *tmp_js_script; GFile *lcov_output_dir; GFile *lcov_output; } GjsCoverageFixture; static void replace_file(GFile *file, const char *contents) { GjsAutoError error; g_file_replace_contents(file, contents, strlen(contents), NULL /* etag */, FALSE /* make backup */, G_FILE_CREATE_NONE, NULL /* etag out */, NULL /* cancellable */, &error); g_assert_no_error(error); } static void recursive_delete_dir(GFile *dir) { GFileEnumerator *files = g_file_enumerate_children(dir, G_FILE_ATTRIBUTE_STANDARD_TYPE, G_FILE_QUERY_INFO_NONE, NULL, NULL); while (TRUE) { GFile *file; GFileInfo *info; if (!g_file_enumerator_iterate(files, &info, &file, NULL, NULL) || !file || !info) break; if (g_file_info_get_file_type(info) == G_FILE_TYPE_DIRECTORY) { recursive_delete_dir(file); continue; } g_file_delete(file, NULL, NULL); } g_file_delete(dir, NULL, NULL); g_object_unref(files); } static void gjs_coverage_fixture_set_up(void* fixture_data, const void*) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; const char* js_script = "var f = function () { return 1; }\n"; char *tmp_output_dir_name = g_strdup("/tmp/gjs_coverage_tmp.XXXXXX"); tmp_output_dir_name = mkdtemp(tmp_output_dir_name); if (!tmp_output_dir_name) g_error("Failed to create temporary directory for test files: %s\n", strerror(errno)); fixture->tmp_output_dir = g_file_new_for_path(tmp_output_dir_name); fixture->tmp_js_script = g_file_get_child(fixture->tmp_output_dir, "gjs_coverage_script.js"); fixture->lcov_output_dir = g_file_get_child(fixture->tmp_output_dir, "gjs_coverage_test_coverage"); fixture->lcov_output = g_file_get_child(fixture->lcov_output_dir, "coverage.lcov"); g_file_make_directory_with_parents(fixture->lcov_output_dir, NULL, NULL); char *tmp_js_script_filename = g_file_get_path(fixture->tmp_js_script); /* Allocate a strv that we can pass over to gjs_coverage_new */ char *coverage_paths[] = { tmp_js_script_filename, NULL }; char *search_paths[] = { tmp_output_dir_name, NULL }; gjs_coverage_enable(); fixture->context = gjs_context_new_with_search_path((char **) search_paths); fixture->coverage = gjs_coverage_new(coverage_paths, fixture->context, fixture->lcov_output_dir); replace_file(fixture->tmp_js_script, js_script); g_free(tmp_output_dir_name); g_free(tmp_js_script_filename); } static void gjs_coverage_fixture_tear_down(void* fixture_data, const void*) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; recursive_delete_dir(fixture->tmp_output_dir); g_object_unref(fixture->tmp_js_script); g_object_unref(fixture->tmp_output_dir); g_object_unref(fixture->lcov_output_dir); g_object_unref(fixture->lcov_output); g_object_unref(fixture->coverage); g_object_unref(fixture->context); } static const char * line_starting_with(const char *data, const char *needle) { const gsize needle_length = strlen(needle); const char *iter = data; while (iter) { if (strncmp(iter, needle, needle_length) == 0) return iter; iter = strstr(iter, "\n"); if (iter) iter += 1; } return NULL; } static char * write_statistics_and_get_coverage_data(GjsCoverage *coverage, GFile *lcov_output) { gjs_coverage_write_statistics(coverage); char *coverage_data_contents; g_file_load_contents(lcov_output, NULL /* cancellable */, &coverage_data_contents, nullptr, /* length out */ NULL /* etag */, NULL /* error */); return coverage_data_contents; } static char * get_script_identifier(GFile *script) { char *filename = g_file_get_path(script); if (!filename) filename = g_file_get_uri(script); return filename; } static bool eval_script(GjsContext *cx, GFile *script) { char *filename = get_script_identifier(script); bool retval = gjs_context_eval_file(cx, filename, NULL, NULL); g_free(filename); return retval; } static char * eval_script_and_get_coverage_data(GjsContext *context, GjsCoverage *coverage, GFile *script, GFile *lcov_output) { eval_script(context, script); return write_statistics_and_get_coverage_data(coverage, lcov_output); } static void assert_coverage_data_contains_value_for_key(const char *data, const char *key, const char *value) { const char *sf_line = line_starting_with(data, key); g_assert_nonnull(sf_line); GjsAutoChar actual = g_strndup(&sf_line[strlen(key)], strlen(value)); g_assert_cmpstr(value, ==, actual); } using CoverageDataMatchFunc = void (*)(const char *value, const void *user_data); static void assert_coverage_data_matches_value_for_key(const char *data, const char *key, CoverageDataMatchFunc match, const void *user_data) { const char *line = line_starting_with(data, key); g_assert_nonnull(line); (*match)(line, user_data); } static void assert_coverage_data_matches_values_for_key(const char *data, const char *key, size_t n, CoverageDataMatchFunc match, const void *user_data, size_t data_size) { const char *line = line_starting_with (data, key); /* Keep matching. If we fail to match one of them then * bail out */ char *data_iterator = (char *) user_data; while (line && n > 0) { (*match)(line, data_iterator); line = line_starting_with(line + 1, key); --n; data_iterator += data_size; } /* If n is zero then we've found all available matches */ g_assert_cmpuint(n, ==, 0); } static void test_covered_file_is_duplicated_into_output_if_resource( void* fixture_data, const void*) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; const char *mock_resource_filename = "resource:///org/cinnamon/cjs/mock/test/gjs-test-coverage/loadedJSFromResource.js"; const char *coverage_scripts[] = { mock_resource_filename, NULL }; g_object_unref(fixture->context); g_object_unref(fixture->coverage); char *js_script_dirname = g_file_get_path(fixture->tmp_output_dir); char *search_paths[] = { js_script_dirname, NULL }; fixture->context = gjs_context_new_with_search_path(search_paths); fixture->coverage = gjs_coverage_new(coverage_scripts, fixture->context, fixture->lcov_output_dir); bool ok = gjs_context_eval_file(fixture->context, mock_resource_filename, nullptr, nullptr); g_assert_true(ok); gjs_coverage_write_statistics(fixture->coverage); GFile *expected_temporary_js_script = g_file_resolve_relative_path(fixture->lcov_output_dir, "org/cinnamon/cjs/mock/test/gjs-test-coverage/loadedJSFromResource.js"); g_assert_true(g_file_query_exists(expected_temporary_js_script, NULL)); g_object_unref(expected_temporary_js_script); g_free(js_script_dirname); } static GFile * get_output_file_for_script_on_disk(GFile *script, GFile *output_dir) { char *base = g_file_get_basename(script); GFile *output = g_file_get_child(output_dir, base); g_free(base); return output; } static char * get_output_path_for_script_on_disk(GFile *script, GFile *output_dir) { GFile *output = get_output_file_for_script_on_disk(script, output_dir); char *output_path = g_file_get_path(output); g_object_unref(output); return output_path; } static void test_covered_file_is_duplicated_into_output_if_path( void* fixture_data, const void*) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; eval_script(fixture->context, fixture->tmp_js_script); gjs_coverage_write_statistics(fixture->coverage); GFile *expected_temporary_js_script = get_output_file_for_script_on_disk(fixture->tmp_js_script, fixture->lcov_output_dir); g_assert_true(g_file_query_exists(expected_temporary_js_script, NULL)); g_object_unref(expected_temporary_js_script); } static void test_previous_contents_preserved(void* fixture_data, const void*) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; const char *existing_contents = "existing_contents\n"; replace_file(fixture->lcov_output, existing_contents); char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->context, fixture->coverage, fixture->tmp_js_script, fixture->lcov_output); g_assert_nonnull(strstr(coverage_data_contents, existing_contents)); g_free(coverage_data_contents); } static void test_new_contents_written(void* fixture_data, const void*) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; const char *existing_contents = "existing_contents\n"; replace_file(fixture->lcov_output, existing_contents); char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->context, fixture->coverage, fixture->tmp_js_script, fixture->lcov_output); /* We have new content in the coverage data */ g_assert_cmpstr(existing_contents, !=, coverage_data_contents); g_free(coverage_data_contents); } static void test_expected_source_file_name_written_to_coverage_data( void* fixture_data, const void*) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->context, fixture->coverage, fixture->tmp_js_script, fixture->lcov_output); char *expected_source_filename = get_output_path_for_script_on_disk(fixture->tmp_js_script, fixture->lcov_output_dir); assert_coverage_data_contains_value_for_key(coverage_data_contents, "SF:", expected_source_filename); g_free(expected_source_filename); g_free(coverage_data_contents); } static void test_expected_entry_not_written_for_nonexistent_file( void* fixture_data, const void*) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; const char *coverage_paths[] = { "doesnotexist", NULL }; g_object_unref(fixture->coverage); fixture->coverage = gjs_coverage_new(coverage_paths, fixture->context, fixture->lcov_output_dir); GFile *doesnotexist = g_file_new_for_path("doesnotexist"); char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->context, fixture->coverage, doesnotexist, fixture->lcov_output); const char *sf_line = line_starting_with(coverage_data_contents, "SF:"); g_assert_null(sf_line); g_free(coverage_data_contents); g_object_unref(doesnotexist); } typedef enum _BranchTaken { NOT_EXECUTED, NOT_TAKEN, TAKEN } BranchTaken; typedef struct _BranchLineData { int expected_branch_line; int expected_id; BranchTaken taken; } BranchLineData; static void branch_at_line_should_be_taken(const char *line, const void *user_data) { auto branch_data = static_cast(user_data); int line_no, branch_id, block_no, hit_count_num, nmatches; char hit_count[20]; /* can hold maxint64 (19 digits) + nul terminator */ /* Advance past "BRDA:" */ line += 5; nmatches = sscanf(line, "%i,%i,%i,%19s", &line_no, &block_no, &branch_id, hit_count); g_assert_cmpint(nmatches, ==, 4); /* Determine the branch hit count. It will be either: * > -1 if the line containing the branch was never executed, or * > N times the branch was taken. * * The value of -1 is represented by a single "-" character, so * we should detect this case and set the value based on that */ if (strlen(hit_count) == 1 && *hit_count == '-') hit_count_num = -1; else hit_count_num = atoi(hit_count); g_assert_cmpint(line_no, ==, branch_data->expected_branch_line); g_assert_cmpint(branch_id, ==, branch_data->expected_id); switch (branch_data->taken) { case NOT_EXECUTED: g_assert_cmpint(hit_count_num, ==, -1); break; case NOT_TAKEN: g_assert_cmpint(hit_count_num, ==, 0); break; case TAKEN: g_assert_cmpint(hit_count_num, >, 0); break; default: g_assert_true(false && "Invalid branch state"); }; } static void test_single_branch_coverage_written_to_coverage_data( void* fixture_data, const void*) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; const char *script_with_basic_branch = "let x = 0;\n" "if (x > 0)\n" " x++;\n" "else\n" " x++;\n"; replace_file(fixture->tmp_js_script, script_with_basic_branch); char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->context, fixture->coverage, fixture->tmp_js_script, fixture->lcov_output); const BranchLineData expected_branches[] = {{2, 0, TAKEN}, {2, 1, NOT_TAKEN}}; const gsize expected_branches_len = G_N_ELEMENTS(expected_branches); /* There are two possible branches here, the second should be taken * and the first should not have been */ assert_coverage_data_matches_values_for_key(coverage_data_contents, "BRDA:", expected_branches_len, branch_at_line_should_be_taken, expected_branches, sizeof(BranchLineData)); assert_coverage_data_contains_value_for_key(coverage_data_contents, "BRF:", "2"); assert_coverage_data_contains_value_for_key(coverage_data_contents, "BRH:", "1"); g_free(coverage_data_contents); } static void test_multiple_branch_coverage_written_to_coverage_data( void* fixture_data, const void*) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; const char *script_with_case_statements_branch = "let y;\n" "for (let x = 0; x < 3; x++) {\n" " switch (x) {\n" " case 0:\n" " y = x + 1;\n" " break;\n" " case 1:\n" " y = x + 1;\n" " break;\n" " case 2:\n" " y = x + 1;\n" " break;\n" " }\n" "}\n"; replace_file(fixture->tmp_js_script, script_with_case_statements_branch); char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->context, fixture->coverage, fixture->tmp_js_script, fixture->lcov_output); const BranchLineData expected_branches[] = { {2, 0, TAKEN}, {2, 1, TAKEN}, {3, 0, TAKEN}, {3, 1, TAKEN}, {3, 2, TAKEN}, {3, 3, NOT_TAKEN}, }; const gsize expected_branches_len = G_N_ELEMENTS(expected_branches); /* There are two possible branches here, the second should be taken * and the first should not have been */ assert_coverage_data_matches_values_for_key(coverage_data_contents, "BRDA:", expected_branches_len, branch_at_line_should_be_taken, expected_branches, sizeof(BranchLineData)); g_free(coverage_data_contents); } static void test_branches_for_multiple_case_statements_fallthrough( void* fixture_data, const void*) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; const char *script_with_case_statements_branch = "let y;\n" "for (let x = 0; x < 3; x++) {\n" " switch (x) {\n" " case 0:\n" " case 1:\n" " y = x + 1;\n" " break;\n" " case 2:\n" " y = x + 1;\n" " break;\n" " case 3:\n" " y = x +1;\n" " break;\n" " }\n" "}\n"; replace_file(fixture->tmp_js_script, script_with_case_statements_branch); char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->context, fixture->coverage, fixture->tmp_js_script, fixture->lcov_output); const BranchLineData expected_branches[] = { {2, 0, TAKEN}, {2, 1, TAKEN}, {3, 0, TAKEN}, {3, 1, TAKEN}, {3, 2, NOT_TAKEN}, {3, 3, NOT_TAKEN}, }; const gsize expected_branches_len = G_N_ELEMENTS(expected_branches); /* There are two possible branches here, the second should be taken * and the first should not have been */ assert_coverage_data_matches_values_for_key(coverage_data_contents, "BRDA:", expected_branches_len, branch_at_line_should_be_taken, expected_branches, sizeof(BranchLineData)); g_free(coverage_data_contents); } static void any_line_matches_not_executed_branch(const char *data) { const char *line = line_starting_with(data, "BRDA:"); while (line) { int line_no, branch_id, block_no; char hit_count; /* Advance past "BRDA:" */ line += 5; int nmatches = sscanf(line, "%i,%i,%i,%c", &line_no, &block_no, &branch_id, &hit_count); g_assert_cmpint(nmatches, ==, 4); if (line_no == 3 && branch_id == 0 && hit_count == '-') return; line = line_starting_with(line + 1, "BRDA:"); } g_assert_true(false && "BRDA line with line 3 not found"); } static void test_branch_not_hit_written_to_coverage_data(void* fixture_data, const void*) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; const char *script_with_never_executed_branch = "let x = 0;\n" "if (x > 0) {\n" " if (x > 0)\n" " x++;\n" "} else {\n" " x++;\n" "}\n"; replace_file(fixture->tmp_js_script, script_with_never_executed_branch); char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->context, fixture->coverage, fixture->tmp_js_script, fixture->lcov_output); any_line_matches_not_executed_branch(coverage_data_contents); g_free(coverage_data_contents); } static void has_function_name(const char *line, const void *user_data) { const char *expected_function_name = *(static_cast(user_data)); /* Advance past "FN:" */ line += 3; /* Advance past the first comma */ while (*(line - 1) != ',') ++line; GjsAutoChar actual = g_strndup(line, strlen(expected_function_name)); g_assert_cmpstr(actual, ==, expected_function_name); } static void test_function_names_written_to_coverage_data(void* fixture_data, const void*) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; const char *script_with_named_and_unnamed_functions = "function f(){}\n" "let b = function(){}\n"; replace_file(fixture->tmp_js_script, script_with_named_and_unnamed_functions); char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->context, fixture->coverage, fixture->tmp_js_script, fixture->lcov_output); const char* expected_function_names[] = { "top-level", "f", "b", }; const gsize expected_function_names_len = G_N_ELEMENTS(expected_function_names); /* Just expect that we've got an FN matching out expected function names */ assert_coverage_data_matches_values_for_key(coverage_data_contents, "FN:", expected_function_names_len, has_function_name, expected_function_names, sizeof(const char *)); g_free(coverage_data_contents); } static void has_function_line(const char *line, const void *user_data) { const char *expected_function_line = *(static_cast(user_data)); /* Advance past "FN:" */ line += 3; GjsAutoChar actual = g_strndup(line, strlen(expected_function_line)); g_assert_cmpstr(actual, ==, expected_function_line); } static void test_function_lines_written_to_coverage_data(void* fixture_data, const void*) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; const char *script_with_functions = "function f(){}\n" "\n" "function g(){}\n"; replace_file(fixture->tmp_js_script, script_with_functions); char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->context, fixture->coverage, fixture->tmp_js_script, fixture->lcov_output); const char* const expected_function_lines[] = { "1", "1", "3", }; const gsize expected_function_lines_len = G_N_ELEMENTS(expected_function_lines); assert_coverage_data_matches_values_for_key(coverage_data_contents, "FN:", expected_function_lines_len, has_function_line, expected_function_lines, sizeof(const char *)); g_free(coverage_data_contents); } typedef struct _FunctionHitCountData { const char *function; unsigned int hit_count_minimum; } FunctionHitCountData; static void hit_count_is_more_than_for_function(const char *line, const void *user_data) { auto data = static_cast(user_data); char *detected_function = NULL; unsigned int hit_count; size_t max_buf_size; int nmatches; /* Advance past "FNDA:" */ line += 5; max_buf_size = strcspn(line, "\n"); detected_function = g_new(char, max_buf_size + 1); GjsAutoChar format_string = g_strdup_printf("%%5u,%%%zus", max_buf_size); // clang-format off #if defined(__clang__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wformat-nonliteral\"") #endif nmatches = sscanf(line, format_string, &hit_count, detected_function); #if defined(__clang__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) _Pragma("GCC diagnostic pop") #endif // clang-format on g_assert_cmpint(nmatches, ==, 2); g_assert_cmpstr(data->function, ==, detected_function); g_assert_cmpuint(hit_count, >=, data->hit_count_minimum); g_free(detected_function); } /* For functions with whitespace between their definition and * first executable line, its possible that the JS engine might * enter their frame a little later in the script than where their * definition starts. We need to handle that case */ static void test_function_hit_counts_for_big_functions_written_to_coverage_data( void* fixture_data, const void*) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; const char *script_with_executed_functions = "function f(){\n" "\n" "\n" "var x = 1;\n" "}\n" "let b = function(){}\n" "f();\n" "b();\n"; replace_file(fixture->tmp_js_script, script_with_executed_functions); char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->context, fixture->coverage, fixture->tmp_js_script, fixture->lcov_output); const FunctionHitCountData expected_hit_counts[] = { {"top-level", 1}, {"f", 1}, {"b", 1}, }; const gsize expected_hit_count_len = G_N_ELEMENTS(expected_hit_counts); /* There are two possible branches here, the second should be taken * and the first should not have been */ assert_coverage_data_matches_values_for_key(coverage_data_contents, "FNDA:", expected_hit_count_len, hit_count_is_more_than_for_function, expected_hit_counts, sizeof(FunctionHitCountData)); g_free(coverage_data_contents); } /* For functions which start executing at a function declaration * we also need to make sure that we roll back to the real function, */ static void test_function_hit_counts_for_little_functions_written_to_coverage_data( void* fixture_data, const void*) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; const char *script_with_executed_functions = "function f(){\n" "var x = function(){};\n" "}\n" "let b = function(){}\n" "f();\n" "b();\n"; replace_file(fixture->tmp_js_script, script_with_executed_functions); char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->context, fixture->coverage, fixture->tmp_js_script, fixture->lcov_output); const FunctionHitCountData expected_hit_counts[] = { {"top-level", 1}, {"f", 1}, {"b", 1}, }; const gsize expected_hit_count_len = G_N_ELEMENTS(expected_hit_counts); /* There are two possible branches here, the second should be taken * and the first should not have been */ assert_coverage_data_matches_values_for_key(coverage_data_contents, "FNDA:", expected_hit_count_len, hit_count_is_more_than_for_function, expected_hit_counts, sizeof(FunctionHitCountData)); g_free(coverage_data_contents); } static void test_function_hit_counts_written_to_coverage_data( void* fixture_data, const void*) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; const char *script_with_executed_functions = "function f(){}\n" "let b = function(){}\n" "f();\n" "b();\n"; replace_file(fixture->tmp_js_script, script_with_executed_functions); char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->context, fixture->coverage, fixture->tmp_js_script, fixture->lcov_output); const FunctionHitCountData expected_hit_counts[] = { {"top-level", 1}, {"f", 1}, {"b", 1}, }; const gsize expected_hit_count_len = G_N_ELEMENTS(expected_hit_counts); /* There are two possible branches here, the second should be taken * and the first should not have been */ assert_coverage_data_matches_values_for_key(coverage_data_contents, "FNDA:", expected_hit_count_len, hit_count_is_more_than_for_function, expected_hit_counts, sizeof(FunctionHitCountData)); g_free(coverage_data_contents); } static void test_total_function_coverage_written_to_coverage_data( void* fixture_data, const void*) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; const char *script_with_some_executed_functions = "function f(){}\n" "let b = function(){}\n" "f();\n"; replace_file(fixture->tmp_js_script, script_with_some_executed_functions); char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->context, fixture->coverage, fixture->tmp_js_script, fixture->lcov_output); /* More than one assert per test is bad, but we are testing interlinked concepts */ assert_coverage_data_contains_value_for_key(coverage_data_contents, "FNF:", "3"); assert_coverage_data_contains_value_for_key(coverage_data_contents, "FNH:", "2"); g_free(coverage_data_contents); } typedef struct _LineCountIsMoreThanData { unsigned int expected_lineno; unsigned int expected_to_be_more_than; } LineCountIsMoreThanData; static void line_hit_count_is_more_than(const char *line, const void *user_data) { auto data = static_cast(user_data); const char *coverage_line = &line[3]; char *comma_ptr = NULL; unsigned int lineno = strtol(coverage_line, &comma_ptr, 10); g_assert_cmpint(comma_ptr[0], ==, ','); char *end_ptr = NULL; unsigned int value = strtol(&comma_ptr[1], &end_ptr, 10); g_assert_true(end_ptr[0] == '\0' || end_ptr[0] == '\n'); g_assert_cmpuint(lineno, ==, data->expected_lineno); g_assert_cmpuint(value, >, data->expected_to_be_more_than); } static void test_single_line_hit_written_to_coverage_data(void* fixture_data, const void*) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->context, fixture->coverage, fixture->tmp_js_script, fixture->lcov_output); const LineCountIsMoreThanData data = {1, 0}; assert_coverage_data_matches_value_for_key(coverage_data_contents, "DA:", line_hit_count_is_more_than, &data); g_free(coverage_data_contents); } static void test_hits_on_multiline_if_cond(void* fixture_data, const void*) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; const char *script_with_multine_if_cond = "let a = 1;\n" "let b = 1;\n" "if (a &&\n" " b) {\n" "}\n"; replace_file(fixture->tmp_js_script, script_with_multine_if_cond); char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->context, fixture->coverage, fixture->tmp_js_script, fixture->lcov_output); /* Hits on all lines, including both lines with a condition (3 and 4) */ const LineCountIsMoreThanData data[] = {{1, 0}, {2, 0}, {3, 0}, {4, 0}}; assert_coverage_data_matches_value_for_key(coverage_data_contents, "DA:", line_hit_count_is_more_than, data); g_free(coverage_data_contents); } static void test_full_line_tally_written_to_coverage_data(void* fixture_data, const void*) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->context, fixture->coverage, fixture->tmp_js_script, fixture->lcov_output); /* More than one assert per test is bad, but we are testing interlinked concepts */ assert_coverage_data_contains_value_for_key(coverage_data_contents, "LF:", "1"); assert_coverage_data_contains_value_for_key(coverage_data_contents, "LH:", "1"); g_free(coverage_data_contents); } static void test_no_hits_to_coverage_data_for_unexecuted(void* fixture_data, const void*) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; char *coverage_data_contents = write_statistics_and_get_coverage_data(fixture->coverage, fixture->lcov_output); /* No files were executed, so the coverage data is empty. */ g_assert_cmpstr(coverage_data_contents, ==, "\n"); g_free(coverage_data_contents); } static void test_end_of_record_section_written_to_coverage_data( void* fixture_data, const void*) { GjsCoverageFixture *fixture = (GjsCoverageFixture *) fixture_data; char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->context, fixture->coverage, fixture->tmp_js_script, fixture->lcov_output); g_assert_nonnull(strstr(coverage_data_contents, "end_of_record")); g_free(coverage_data_contents); } typedef struct _GjsCoverageMultipleSourcesFixture { GjsCoverageFixture base_fixture; GFile *second_js_source_file; } GjsCoverageMultpleSourcesFixutre; static void gjs_coverage_multiple_source_files_to_single_output_fixture_set_up(gpointer fixture_data, gconstpointer user_data) { gjs_coverage_fixture_set_up(fixture_data, user_data); GjsCoverageMultpleSourcesFixutre *fixture = (GjsCoverageMultpleSourcesFixutre *) fixture_data; fixture->second_js_source_file = g_file_get_child(fixture->base_fixture.tmp_output_dir, "gjs_coverage_second_source_file.js"); /* Because GjsCoverage searches the coverage paths at object-creation time, * we need to destroy the previously constructed one and construct it again */ char *first_js_script_path = g_file_get_path(fixture->base_fixture.tmp_js_script); char *second_js_script_path = g_file_get_path(fixture->second_js_source_file); char *coverage_paths[] = { first_js_script_path, second_js_script_path, NULL }; g_object_unref(fixture->base_fixture.context); g_object_unref(fixture->base_fixture.coverage); char *output_path = g_file_get_path(fixture->base_fixture.tmp_output_dir); char *search_paths[] = { output_path, NULL }; fixture->base_fixture.context = gjs_context_new_with_search_path(search_paths); fixture->base_fixture.coverage = gjs_coverage_new(coverage_paths, fixture->base_fixture.context, fixture->base_fixture.lcov_output_dir); g_free(output_path); g_free(first_js_script_path); g_free(second_js_script_path); char *base_name = g_file_get_basename(fixture->base_fixture.tmp_js_script); char *base_name_without_extension = g_strndup(base_name, strlen(base_name) - 3); char* mock_script = g_strconcat("const FirstScript = imports.", base_name_without_extension, ";\n", "let a = FirstScript.f;\n" "\n", NULL); replace_file(fixture->second_js_source_file, mock_script); g_free(mock_script); g_free(base_name_without_extension); g_free(base_name); } static void gjs_coverage_multiple_source_files_to_single_output_fixture_tear_down(gpointer fixture_data, gconstpointer user_data) { GjsCoverageMultpleSourcesFixutre *fixture = (GjsCoverageMultpleSourcesFixutre *) fixture_data; g_object_unref(fixture->second_js_source_file); gjs_coverage_fixture_tear_down(fixture_data, user_data); } static void test_multiple_source_file_records_written_to_coverage_data( void* fixture_data, const void*) { GjsCoverageMultpleSourcesFixutre *fixture = (GjsCoverageMultpleSourcesFixutre *) fixture_data; char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->base_fixture.context, fixture->base_fixture.coverage, fixture->second_js_source_file, fixture->base_fixture.lcov_output); const char *first_sf_record = line_starting_with(coverage_data_contents, "SF:"); g_assert_nonnull(first_sf_record); const char *second_sf_record = line_starting_with(first_sf_record + 1, "SF:"); g_assert_nonnull(second_sf_record); g_free(coverage_data_contents); } typedef struct _ExpectedSourceFileCoverageData { const char *source_file_path; LineCountIsMoreThanData *more_than; unsigned int n_more_than_matchers; const char expected_lines_hit_character; const char expected_lines_found_character; } ExpectedSourceFileCoverageData; static void assert_coverage_data_for_source_file(ExpectedSourceFileCoverageData *expected, const size_t expected_size, const char *section_start) { gsize i; for (i = 0; i < expected_size; ++i) { if (strncmp(§ion_start[3], expected[i].source_file_path, strlen (expected[i].source_file_path)) == 0) { assert_coverage_data_matches_values_for_key(section_start, "DA:", expected[i].n_more_than_matchers, line_hit_count_is_more_than, expected[i].more_than, sizeof (LineCountIsMoreThanData)); const char *total_hits_record = line_starting_with(section_start, "LH:"); g_assert_cmpint(total_hits_record[3], ==, expected[i].expected_lines_hit_character); const char *total_found_record = line_starting_with(section_start, "LF:"); g_assert_cmpint(total_found_record[3], ==, expected[i].expected_lines_found_character); return; } } g_assert_true(false && "Expected source file path to be found in section"); } static void test_correct_line_coverage_data_written_for_both_source_file_sections( void* fixture_data, const void*) { GjsCoverageMultpleSourcesFixutre *fixture = (GjsCoverageMultpleSourcesFixutre *) fixture_data; char *coverage_data_contents = eval_script_and_get_coverage_data(fixture->base_fixture.context, fixture->base_fixture.coverage, fixture->second_js_source_file, fixture->base_fixture.lcov_output); LineCountIsMoreThanData first_script_matcher = {1, 0}; LineCountIsMoreThanData second_script_matchers[] = {{1, 0}, {2, 0}}; char *first_script_output_path = get_output_path_for_script_on_disk(fixture->base_fixture.tmp_js_script, fixture->base_fixture.lcov_output_dir); char *second_script_output_path = get_output_path_for_script_on_disk(fixture->second_js_source_file, fixture->base_fixture.lcov_output_dir); ExpectedSourceFileCoverageData expected[] = { { first_script_output_path, &first_script_matcher, 1, '1', '1' }, { second_script_output_path, second_script_matchers, 2, '2', '2' } }; const gsize expected_len = G_N_ELEMENTS(expected); const char *first_sf_record = line_starting_with(coverage_data_contents, "SF:"); assert_coverage_data_for_source_file(expected, expected_len, first_sf_record); const char *second_sf_record = line_starting_with(first_sf_record + 3, "SF:"); assert_coverage_data_for_source_file(expected, expected_len, second_sf_record); g_free(first_script_output_path); g_free(second_script_output_path); g_free(coverage_data_contents); } typedef struct _FixturedTest { gsize fixture_size; GTestFixtureFunc set_up; GTestFixtureFunc tear_down; } FixturedTest; static void add_test_for_fixture(const char *name, FixturedTest *fixture, GTestFixtureFunc test_func, gconstpointer user_data) { g_test_add_vtable(name, fixture->fixture_size, user_data, fixture->set_up, test_func, fixture->tear_down); } void gjs_test_add_tests_for_coverage() { FixturedTest coverage_fixture = { sizeof(GjsCoverageFixture), gjs_coverage_fixture_set_up, gjs_coverage_fixture_tear_down }; add_test_for_fixture("/cjs/coverage/file_duplicated_into_output_path", &coverage_fixture, test_covered_file_is_duplicated_into_output_if_path, NULL); add_test_for_fixture("/cjs/coverage/file_duplicated_full_resource_path", &coverage_fixture, test_covered_file_is_duplicated_into_output_if_resource, NULL); add_test_for_fixture("/cjs/coverage/contents_preserved_accumulate_mode", &coverage_fixture, test_previous_contents_preserved, NULL); add_test_for_fixture("/cjs/coverage/new_contents_appended_accumulate_mode", &coverage_fixture, test_new_contents_written, NULL); add_test_for_fixture("/cjs/coverage/expected_source_file_name_written_to_coverage_data", &coverage_fixture, test_expected_source_file_name_written_to_coverage_data, NULL); add_test_for_fixture("/cjs/coverage/entry_not_written_for_nonexistent_file", &coverage_fixture, test_expected_entry_not_written_for_nonexistent_file, NULL); add_test_for_fixture("/cjs/coverage/single_branch_coverage_written_to_coverage_data", &coverage_fixture, test_single_branch_coverage_written_to_coverage_data, NULL); add_test_for_fixture("/cjs/coverage/multiple_branch_coverage_written_to_coverage_data", &coverage_fixture, test_multiple_branch_coverage_written_to_coverage_data, NULL); add_test_for_fixture("/cjs/coverage/branches_for_multiple_case_statements_fallthrough", &coverage_fixture, test_branches_for_multiple_case_statements_fallthrough, NULL); add_test_for_fixture("/cjs/coverage/not_hit_branch_point_written_to_coverage_data", &coverage_fixture, test_branch_not_hit_written_to_coverage_data, NULL); add_test_for_fixture("/cjs/coverage/function_names_written_to_coverage_data", &coverage_fixture, test_function_names_written_to_coverage_data, NULL); add_test_for_fixture("/cjs/coverage/function_lines_written_to_coverage_data", &coverage_fixture, test_function_lines_written_to_coverage_data, NULL); add_test_for_fixture("/cjs/coverage/function_hit_counts_written_to_coverage_data", &coverage_fixture, test_function_hit_counts_written_to_coverage_data, NULL); add_test_for_fixture("/cjs/coverage/big_function_hit_counts_written_to_coverage_data", &coverage_fixture, test_function_hit_counts_for_big_functions_written_to_coverage_data, NULL); add_test_for_fixture("/cjs/coverage/little_function_hit_counts_written_to_coverage_data", &coverage_fixture, test_function_hit_counts_for_little_functions_written_to_coverage_data, NULL); add_test_for_fixture("/cjs/coverage/total_function_coverage_written_to_coverage_data", &coverage_fixture, test_total_function_coverage_written_to_coverage_data, NULL); add_test_for_fixture("/cjs/coverage/single_line_hit_written_to_coverage_data", &coverage_fixture, test_single_line_hit_written_to_coverage_data, NULL); add_test_for_fixture("/cjs/coverage/hits_on_multiline_if_cond", &coverage_fixture, test_hits_on_multiline_if_cond, NULL); add_test_for_fixture("/cjs/coverage/full_line_tally_written_to_coverage_data", &coverage_fixture, test_full_line_tally_written_to_coverage_data, NULL); add_test_for_fixture("/cjs/coverage/no_hits_for_unexecuted_file", &coverage_fixture, test_no_hits_to_coverage_data_for_unexecuted, NULL); add_test_for_fixture("/cjs/coverage/end_of_record_section_written_to_coverage_data", &coverage_fixture, test_end_of_record_section_written_to_coverage_data, NULL); FixturedTest coverage_for_multiple_files_to_single_output_fixture = { sizeof(GjsCoverageMultpleSourcesFixutre), gjs_coverage_multiple_source_files_to_single_output_fixture_set_up, gjs_coverage_multiple_source_files_to_single_output_fixture_tear_down }; add_test_for_fixture("/cjs/coverage/multiple_source_file_records_written_to_coverage_data", &coverage_for_multiple_files_to_single_output_fixture, test_multiple_source_file_records_written_to_coverage_data, NULL); add_test_for_fixture("/cjs/coverage/correct_line_coverage_data_written_for_both_sections", &coverage_for_multiple_files_to_single_output_fixture, test_correct_line_coverage_data_written_for_both_source_file_sections, NULL); } cjs-128.0/test/check-pch.sh0000664000175000017500000001245514771557763014424 0ustar fabiofabio#!/usr/bin/env bash # SPDX-License-Identifier: LGPL-2.0-or-later # SPDX-FileCopyrightText: 2021 Marco Trevisan set -e if [ -n "$SELFTEST" ]; then unset SELFTEST set -x self="$(realpath "$0")" test_paths=() trap 'rm -rf -- "${test_paths[@]}"' EXIT test_env() { local code_path="$(mktemp -t -d "check-pch-XXXXXX")" test_paths+=("$code_path") cd "$code_path" mkdir gjs gi echo "#include " >> gjs/gjs_pch.hh } expect_success() { "$self" || exit 1 } expect_failure() { "$self" && exit 1 || true } test_env echo "#include " >> gi/code.c expect_success test_env echo "#include " >> gi/code.c echo "#include " >> gi/code.c expect_failure test_env echo "#include " >> gi/code.c echo "#include " >> gi/code1.cpp echo "#include " >> gi/code1.c expect_failure test_env echo "#include " >> gi/code.c echo "#include // check-pch: ignore" >> gi/other-code.c expect_success test_env echo "#include " >> gi/code.c echo "#include // NOcheck-pch: ignore" >> gi/code.c echo "#include // check-pch: ignoreNO" >> gi/code.c echo "#include // check-pch: ignore, yes" >> gi/other-code.c expect_failure test_env echo "#include " >> gjs/gjs_pch.hh echo "#include " >> gi/code.c expect_failure test_env echo "#include // check-pch: ignore, yes" >> gjs/gjs_pch.hh echo "#include " >> gi/code.c expect_success test_env echo "#include " >> gi/ignored-file.hh echo "#include " >> gi/code.c expect_success test_env echo '# include ' >> gi/code.c echo '# include "local/header.h"' >> gi/code.c expect_success test_env echo "#include " >> gi/code.c echo '#include "local/header.h"' >> gjs/gjs_pch.hh expect_failure test_env echo "# include " >> gi/code.c echo "# include " >> gi/code.c echo " # include " >> gi/other-file.c echo "# include " >> gjs/gjs_pch.hh expect_success test_env echo "# include " >> gi/code.c echo "# include /*comment*/" >> gi/invalid-file.c expect_failure test_env echo "# include " >> gi/code.c echo " # include " >> gi/other-file.c expect_failure test_env echo "#include " >> gi/code.c echo "//#include " >> gi/invalid-file.c echo "// #include " >> gi/invalid-file.c echo "//#include " >> gjs/gjs_pch.hh expect_success test_env echo "#include " >> gi/code.c echo "/*comment*/#include /*comment*/" >> gi/invalid-file.c # This is not supported: expect_failure test_env echo "#include " >> gi/code.c echo "# /*FIXME */ include /*Why should you do it?*/ " >> gi/invalid-file.c # This is not supported: expect_failure exit 0 fi PCH_FILES=(gjs/gjs_pch.hh) IGNORE_COMMENT="check-pch: ignore" CODE_PATHS=(gjs gi) INCLUDED_FILES=( \*.c \*.cpp \*.h ) grep_include_lines() { grep -h '^\s*#\s*include\s*[<"][^>"]\+[>"]' "$@" | uniq } grep_header_file() { local header_file="${1//./\\.}" shift grep -qs "^\s*#\s*include\s*[<\"]${header_file}[>\"]" "$@" } # List all the included headers mapfile -t includes < <(grep_include_lines \ -r \ $(printf -- "--include %s\n" "${INCLUDED_FILES[@]}") \ "${CODE_PATHS[@]}" \ | grep -vw "$IGNORE_COMMENT") missing=() for h in "${includes[@]}"; do if [[ "$h" =~ \#[[:space:]]*include[[:space:]]*\<([^\>]+)\> ]]; then header_file="${BASH_REMATCH[1]}" if ! grep_header_file "$header_file" "${PCH_FILES[@]}"; then echo "Header <$header_file> not added to PCH file" missing+=("$header_file") fi fi done if [ "${#missing[@]}" -gt 0 ]; then echo echo "Headers not added to the PCH file found, please add to ${PCH_FILES[*]}" echo "Otherwise you can ignore them with a leading comment such as" echo " #include <${missing[0]}> // $IGNORE_COMMENT" exit 1 fi # And now, the other way around... mapfile -t pch_includes < <(grep_include_lines \ "${PCH_FILES[@]}" \ | grep -vw "$IGNORE_COMMENT") unneeded=() for h in "${pch_includes[@]}"; do if [[ "$h" =~ \#[[:space:]]*include[[:space:]]*[\<\"]([^\>\"]+)[\>\"] ]]; then header_file="${BASH_REMATCH[1]}" if ! grep_header_file "$header_file" -r \ $(printf -- "--include %s\n" "${INCLUDED_FILES[@]}") \ "${CODE_PATHS[@]}"; then echo "Header <$header_file> included in one PCH is not used in code" unneeded+=("$header_file") fi fi done if [ "${#unneeded[@]}" -gt 0 ]; then echo echo "Unneeded headers added to the PCH file found, remove from ${PCH_FILES[*]}" echo "Otherwise you can ignore them with a leading comment such as" echo " #include <${unneeded[0]}> // $IGNORE_COMMENT" exit 1 fi cjs-128.0/test/gjs-test-coverage/0000775000175000017500000000000014771557763015565 5ustar fabiofabiocjs-128.0/test/gjs-test-coverage/loadedJSFromResource.js0000664000175000017500000000031414771557763022142 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2014 Endless Mobile, Inc. /* eslint-disable jsdoc/require-jsdoc */ /* exported mockFunction */ function mockFunction() {} cjs-128.0/test/test-ci.sh0000664000175000017500000001525714771557763014152 0ustar fabiofabio#!/bin/sh -e # SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2017 Claudio André do_Set_Env () { #Save cache on $pwd (required by artifacts) mkdir -p "$(pwd)"/.cache XDG_CACHE_HOME="$(pwd)"/.cache export XDG_CACHE_HOME #SpiderMonkey and libgjs export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib64:/usr/local/lib: export GI_TYPELIB_PATH=$GI_TYPELIB_PATH:/usr/lib64/girepository-1.0 #Macros export ACLOCAL_PATH=$ACLOCAL_PATH:/usr/local/share/aclocal export SHELL=/bin/bash PATH=$PATH:~/.local/bin export DISPLAY="${DISPLAY:-:0}" } do_Get_Upstream_Base () { echo '-----------------------------------------' echo 'Finding common ancestor' if git show-branch ci-upstream-base 2> /dev/null; then echo "Already found" return fi # We need to add a new remote for the upstream target branch, since this # script could be running in a personal fork of the repository which has out # of date branches. # # Limit the fetch to a certain date horizon to limit the amount of data we # get. If the branch was forked from the main branch before this horizon, it # should probably be rebased. git remote add upstream https://gitlab.gnome.org/GNOME/gjs.git || \ git remote set-url upstream https://gitlab.gnome.org/GNOME/gjs.git # $CI_MERGE_REQUEST_TARGET_BRANCH_NAME is only defined if we’re running in a # merge request pipeline; fall back to $CI_DEFAULT_BRANCH otherwise. base_branch="${CI_MERGE_REQUEST_TARGET_BRANCH_NAME:-${CI_DEFAULT_BRANCH}}" if ! git fetch --shallow-since="28 days ago" --no-tags upstream "$base_branch"; then echo "Main branch doesn't have history in the past 28 days, fetching " echo "the last 30 commits." git fetch --depth=30 --no-tags upstream "$base_branch" fi git branch -f ci-upstream-base-branch FETCH_HEAD # Work out the newest common ancestor between the detached HEAD that this CI # job has checked out, and the upstream target branch (which will typically # be `upstream/main` or `upstream/gnome-nn`). newest_common_ancestor_sha=$(git merge-base ci-upstream-base-branch HEAD) if test -z "$newest_common_ancestor_sha"; then echo "Couldn’t find common ancestor with the upstream main branch. This" echo "typically happens if you branched a long time ago. Please update" echo "your clone, rebase, and re-push your branch." echo "Base revisions:" git log --oneline -10 ci-upstream-base-branch echo "Branch revisions:" git log --oneline -10 HEAD exit 1 fi echo "Merge base:" git show --no-patch "$newest_common_ancestor_sha" git branch -f ci-upstream-base "$newest_common_ancestor_sha" echo '-----------------------------------------' } do_Compare_With_Upstream_Base () { echo '-----------------------------------------' echo 'Comparing the code with upstream merge base' sort < /cwd/base-report.txt > /cwd/base-report-sorted.txt sort < /cwd/head-report.txt > /cwd/head-report-sorted.txt NEW_WARNINGS=$(comm -13 /cwd/base-report-sorted.txt /cwd/head-report-sorted.txt | wc -l) REMOVED_WARNINGS=$(comm -23 /cwd/base-report-sorted.txt /cwd/head-report-sorted.txt | wc -l) if test "$NEW_WARNINGS" -ne 0; then echo '-----------------------------------------' echo "### $NEW_WARNINGS new warning(s) found by $1 ###" echo '-----------------------------------------' diff -U0 /cwd/base-report.txt /cwd/head-report.txt || true echo '-----------------------------------------' exit 1 else echo "$REMOVED_WARNINGS warning(s) were fixed." echo "=> $1 Ok" fi } do_Create_Artifacts_Folder () { # Create the artifacts folders save_dir="$(pwd)" mkdir -p "$save_dir"/analysis; touch "$save_dir"/analysis/doing-"$1" } do_Check_Script_Errors () { local total=0 total=$(cat scripts.log | grep 'not ok ' | wc -l) if test "$total" -gt 0; then echo '-----------------------------------------' echo "### Found $total errors on scripts.log ###" echo '-----------------------------------------' exit 1 fi } # ----------- Run the Tests ----------- if test -n "$TEST"; then extra_opts="($TEST)" fi . test/extra/do_environment.sh # Show some environment info do_Print_Labels 'ENVIRONMENT' echo "Running on: $BASE $OS" echo "Job: $CI_JOB_NAME" echo "Configure options: ${CONFIG_OPTS-$BUILD_OPTS}" echo "Doing: $1 $extra_opts" do_Create_Artifacts_Folder "$1" # Ignore extra git security checks as we don't care in CI. git config --global --add safe.directory "${PWD}" if test "$1" = "SETUP"; then do_Show_Info do_Print_Labels 'Show GJS git information' git log --pretty=format:"%h %cd %s" -1 elif test "$1" = "BUILD"; then do_Set_Env DEFAULT_CONFIG_OPTS="-Dreadline=enabled -Dprofiler=enabled -Ddtrace=false \ -Dsystemtap=false -Dverbose_logs=false --werror" meson setup _build $DEFAULT_CONFIG_OPTS $CONFIG_OPTS ninja -C _build if test "$TEST" != "skip"; then xvfb-run -a meson test -C _build $TEST_OPTS fi elif test "$1" = "SH_CHECKS"; then # It doesn't (re)build, just run the 'Tests' do_Print_Labels 'Shell Scripts Check' do_Set_Env export LC_ALL=C.UTF-8 export LANG=C.UTF-8 export LANGUAGE=C.UTF-8 export NO_AT_BRIDGE=1 sudo ninja -C _build install installed-tests/scripts/testExamples.sh > scripts.log do_Check_Script_Errors elif test "$1" = "CPPLINT"; then do_Print_Labels 'C/C++ Linter report ' cpplint --quiet $(find . -name \*.cpp -or -name \*.h | sort) 2>&1 >/dev/null | \ tee "$save_dir"/analysis/head-report.txt | \ sed -E -e 's/:[0-9]+:/:LINE:/' -e 's/ +/ /g' \ > /cwd/head-report.txt cat "$save_dir"/analysis/head-report.txt echo do_Get_Upstream_Base if test $(git rev-parse HEAD) = $(git rev-parse ci-upstream-base); then echo '-----------------------------------------' echo 'Running against upstream' echo '=> cpplint: Nothing to do' do_Done exit 0 fi git checkout ci-upstream-base cpplint --quiet $(find . -name \*.cpp -or -name \*.h | sort) 2>&1 >/dev/null | \ tee "$save_dir"/analysis/base-report.txt | \ sed -E -e 's/:[0-9]+:/:LINE:/' -e 's/ +/ /g' \ > /cwd/base-report.txt echo # Compare the report with merge base and fail if new warnings are found do_Compare_With_Upstream_Base "cpplint" elif test "$1" = "UPSTREAM_BASE"; then do_Get_Upstream_Base exit 0 fi # Releases stuff and finishes do_Done cjs-128.0/test/gjs-test-jsapi-utils.cpp0000664000175000017500000005475614771557763016763 0ustar fabiofabio/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * SPDX-License-Identifier: MIT OR LGPL-2.0-or-later * * Copyright (c) 2020 Marco Trevisan */ #include #include // for NULL #include // for move, swap #include #include #include "cjs/jsapi-util.h" struct _GjsTestObject { GObject parent_instance; int stuff; }; G_DECLARE_FINAL_TYPE(GjsTestObject, gjs_test_object, GJS_TEST, OBJECT, GObject) G_DEFINE_TYPE(GjsTestObject, gjs_test_object, G_TYPE_OBJECT) struct Fixture { GjsTestObject* ptr; }; static void gjs_test_object_init(GjsTestObject*) {} void gjs_test_object_class_init(GjsTestObjectClass*) {} static GjsTestObject* gjs_test_object_new() { return GJS_TEST_OBJECT(g_object_new(gjs_test_object_get_type(), NULL)); } static void setup(Fixture* fx, const void*) { fx->ptr = gjs_test_object_new(); g_assert_nonnull(fx->ptr); g_object_add_weak_pointer(G_OBJECT(fx->ptr), reinterpret_cast(&fx->ptr)); } static void teardown(Fixture* fx, const void*) { // Weak pointer will have reset the pointer to null if the last reference // was dropped g_assert_null(fx->ptr); } using GjsAutoTestObject = GjsAutoPointer; static void test_gjs_autopointer_size() { g_assert_cmpuint(sizeof(GjsAutoTestObject), ==, sizeof(GjsTestObject*)); } static void test_gjs_autopointer_ctor_empty() { GjsAutoTestObject autoptr; g_assert_null(autoptr.get()); g_assert_null(autoptr); } static void test_gjs_autopointer_ctor_basic(Fixture* fx, const void*) { GjsAutoTestObject autoptr(fx->ptr); g_assert_true(autoptr == fx->ptr); g_assert_true(autoptr.get() == fx->ptr); } static void test_gjs_autopointer_ctor_take_ownership(Fixture* fx, const void*) { GjsAutoTestObject autoptr(fx->ptr, GjsAutoTakeOwnership()); g_assert_true(autoptr == fx->ptr); g_assert_true(autoptr.get() == fx->ptr); g_object_unref(fx->ptr); } static void test_gjs_autopointer_ctor_assign(Fixture* fx, const void*) { GjsAutoTestObject autoptr = fx->ptr; g_assert_true(autoptr == fx->ptr); g_assert_true(autoptr.get() == fx->ptr); } static void test_gjs_autopointer_ctor_assign_other(Fixture* fx, const void*) { GjsAutoTestObject autoptr1 = fx->ptr; GjsAutoTestObject autoptr2 = autoptr1; g_assert_true(autoptr1 == fx->ptr); g_assert_true(autoptr1.get() == fx->ptr); g_assert_true(autoptr2 == fx->ptr); g_assert_true(autoptr2.get() == fx->ptr); } static void test_gjs_autopointer_dtor(Fixture* fx, const void*) { g_object_ref(fx->ptr); { GjsAutoTestObject autoptr(fx->ptr); g_assert_true(autoptr == fx->ptr); g_assert_true(autoptr.get() == fx->ptr); } g_assert_nonnull(fx->ptr); g_object_unref(fx->ptr); } static void test_gjs_autopointer_dtor_cpp() { bool deleted = false; auto dtor_callback = [&deleted] { deleted = true; }; struct TestStruct { explicit TestStruct(decltype(dtor_callback) cb) : _delete_cb(cb) {} ~TestStruct() { _delete_cb(); } decltype(dtor_callback) _delete_cb; }; g_assert_false(deleted); { auto* ptr = new TestStruct(dtor_callback); GjsAutoCppPointer autoptr(ptr); g_assert_true(ptr == autoptr); } g_assert_true(deleted); } static void test_gjs_autopointer_dtor_cpp_array() { unsigned deleted = 0; auto dtor_callback = [&deleted] { deleted++; }; struct TestStruct { TestStruct(decltype(dtor_callback) cb) // NOLINT(runtime/explicit) : _delete_cb(cb) {} ~TestStruct() { _delete_cb(); } int val = 5; decltype(dtor_callback) _delete_cb; }; g_assert_cmpint(deleted, ==, 0); { // using GjsAutoCppPointer1 = GjsAutoPointer>; TestStruct* ptrs = new TestStruct[3]{dtor_callback, dtor_callback, dtor_callback}; GjsAutoCppPointer autoptr(ptrs); g_assert_cmpint(autoptr[0].val, ==, 5); g_assert_cmpint(autoptr[1].val, ==, 5); g_assert_cmpint(autoptr[2].val, ==, 5); autoptr[1].val = 4; TestStruct const& const_struct_const_1 = autoptr[1]; g_assert_cmpint(const_struct_const_1.val, ==, 4); // const_struct_const_1.val = 3; // This will would not compile TestStruct& test_struct_1 = autoptr[1]; test_struct_1.val = 3; g_assert_cmpint(test_struct_1.val, ==, 3); int* int_ptrs = new int[3]{5, 6, 7}; GjsAutoCppPointer int_autoptr(int_ptrs); g_assert_cmpint(int_autoptr[0], ==, 5); g_assert_cmpint(int_autoptr[1], ==, 6); g_assert_cmpint(int_autoptr[2], ==, 7); } g_assert_cmpuint(deleted, ==, 3); } static void test_gjs_autopointer_dtor_take_ownership(Fixture* fx, const void*) { { GjsAutoTestObject autoptr(fx->ptr, GjsAutoTakeOwnership()); g_assert_true(autoptr == fx->ptr); g_assert_true(autoptr.get() == fx->ptr); } g_assert_nonnull(fx->ptr); g_object_unref(fx->ptr); } static void test_gjs_autopointer_dtor_default_free() { GjsAutoPointer autoptr(g_strdup("Please, FREE ME!")); g_assert_cmpstr(autoptr, ==, "Please, FREE ME!"); } static void test_gjs_autopointer_dtor_no_free_pointer() { const char* str = "DO NOT FREE ME"; GjsAutoPointer autoptr(const_cast(str)); g_assert_cmpstr(autoptr, ==, "DO NOT FREE ME"); } static void gobject_free(GObject* p) { g_object_unref(p); } static GObject* gobject_copy(GObject* p) { return static_cast(g_object_ref(p)); } static void test_gjs_autopointer_cast_free_func_type() { // No assertions; this test fails to compile if the casts are wrong using TypedAutoPointer = GjsAutoPointer; TypedAutoPointer autoptr{gjs_test_object_new()}; TypedAutoPointer copy{autoptr.copy()}; } static void test_gjs_autopointer_assign_operator() { GjsAutoTestObject autoptr; auto* ptr = gjs_test_object_new(); autoptr = ptr; g_assert_true(autoptr == ptr); g_assert_true(autoptr.get() == ptr); } static void test_gjs_autopointer_assign_operator_other_ptr() { auto* ptr1 = gjs_test_object_new(); auto* ptr2 = gjs_test_object_new(); g_object_add_weak_pointer(G_OBJECT(ptr1), reinterpret_cast(&ptr1)); GjsAutoTestObject autoptr(ptr1); g_object_ref(ptr1); autoptr = ptr2; g_assert_true(autoptr == ptr2); g_assert_nonnull(ptr1); g_object_unref(ptr1); g_assert_null(ptr1); } static void test_gjs_autopointer_assign_operator_self_ptr(Fixture* fx, const void*) { GjsAutoTestObject autoptr(fx->ptr); g_object_ref(fx->ptr); autoptr = fx->ptr; g_assert_true(autoptr == fx->ptr); } static void test_gjs_autopointer_assign_operator_object(Fixture* fx, const void*) { GjsAutoTestObject autoptr1; GjsAutoTestObject autoptr2; autoptr1 = fx->ptr; autoptr2 = autoptr1; g_assert_true(autoptr1 == autoptr2); g_assert_true(autoptr2.get() == fx->ptr); } static void test_gjs_autopointer_assign_operator_other_object() { auto* ptr1 = gjs_test_object_new(); auto* ptr2 = gjs_test_object_new(); g_object_add_weak_pointer(G_OBJECT(ptr1), reinterpret_cast(&ptr1)); g_object_add_weak_pointer(G_OBJECT(ptr2), reinterpret_cast(&ptr2)); { GjsAutoTestObject autoptr1(ptr1); GjsAutoTestObject autoptr2(ptr2); g_object_ref(ptr1); autoptr1 = autoptr2; g_assert_true(autoptr1 == ptr2); g_assert_true(autoptr2 == ptr2); g_assert_nonnull(ptr1); g_object_unref(ptr1); } g_assert_null(ptr1); g_assert_null(ptr2); } static void test_gjs_autopointer_assign_operator_self_object(Fixture* fx, const void*) { GjsAutoTestObject autoptr(fx->ptr); autoptr = *&autoptr; g_assert_true(autoptr == fx->ptr); } static void test_gjs_autopointer_assign_operator_copy_and_swap(Fixture* fx, const void*) { GjsAutoTestObject autoptr(fx->ptr); auto* ptr = fx->ptr; auto test_copy_fun = [ptr](GjsAutoTestObject data) { g_assert_true(data == ptr); }; test_copy_fun(autoptr); g_assert_true(autoptr == fx->ptr); } static void test_gjs_autopointer_operator_move(Fixture* fx, const void*) { GjsAutoTestObject autoptr(fx->ptr); void* ptr = fx->ptr; auto test_move_fun = [ptr](GjsAutoTestObject&& data) { g_assert_true(ptr == data); }; // Accessing a value after moving out of it is bad in general, but here it // is done on purpose, to test that the autoptr's move constructor empties // the old autoptr. test_move_fun(std::move(autoptr)); g_assert_nonnull(autoptr); // cppcheck-suppress accessMoved // cppcheck-suppress accessMoved GjsAutoTestObject autoptr2 = std::move(autoptr); g_assert_true(autoptr2 == fx->ptr); g_assert_null(autoptr); // cppcheck-suppress accessMoved } static void test_gjs_autopointer_operator_swap(Fixture* fx, const void*) { GjsAutoTestObject autoptr1(fx->ptr); GjsAutoTestObject autoptr2; std::swap(autoptr1, autoptr2); g_assert_null(autoptr1); g_assert_true(autoptr2 == fx->ptr); } static void test_gjs_autopointer_assign_operator_arrow(Fixture* fx, const void*) { GjsAutoTestObject autoptr(fx->ptr); int value = g_random_int(); autoptr->stuff = value; g_assert_cmpint(autoptr->stuff, ==, value); } static void test_gjs_autopointer_assign_operator_deference(Fixture* fx, const void*) { GjsAutoTestObject autoptr(fx->ptr); fx->ptr->stuff = g_random_int(); GjsTestObject tobj = *autoptr; g_assert_cmpint(fx->ptr->stuff, ==, tobj.stuff); } static void test_gjs_autopointer_assign_operator_bool(Fixture* fx, const void*) { auto bool_to_gboolean = [](bool v) -> gboolean { return !!v; }; g_assert_false(bool_to_gboolean(GjsAutoTestObject())); g_assert_true(bool_to_gboolean(GjsAutoTestObject(gjs_test_object_new()))); GjsAutoTestObject autoptr(fx->ptr); autoptr.reset(); g_assert_false(bool_to_gboolean(autoptr)); } static void test_gjs_autopointer_assign_operator_array() { auto* ptrs = g_new0(GjsTestObject, 5); GjsAutoPointer autopointers(ptrs); for (int i = 0; i < 5; i++) { autopointers[i].stuff = i; g_assert_cmpint(ptrs[i].stuff, ==, i); g_assert_cmpint(autopointers[i].stuff, ==, i); } } static void test_gjs_autopointer_get(Fixture* fx, const void*) { GjsAutoTestObject autoptr(fx->ptr); g_assert_true(fx->ptr == autoptr.get()); } static void test_gjs_autopointer_out(Fixture* fx, const void*) { GjsAutoTestObject autoptr(fx->ptr); g_assert_true(fx->ptr == *(autoptr.out())); } static void test_gjs_autopointer_release(Fixture* fx, const void*) { GjsAutoTestObject autoptr(fx->ptr); g_assert_nonnull(autoptr); auto* released = autoptr.release(); g_assert_true(released == fx->ptr); g_assert_null(autoptr); g_object_unref(fx->ptr); } static void test_gjs_autopointer_reset_nullptr(Fixture* fx, const void*) { GjsAutoTestObject empty; empty.reset(); g_assert_null(empty); GjsAutoTestObject autoptr(fx->ptr); g_assert_nonnull(autoptr); g_object_ref(fx->ptr); autoptr.reset(); g_assert_null(autoptr); g_assert_nonnull(fx->ptr); g_object_unref(fx->ptr); } static void test_gjs_autopointer_reset_self_ptr(Fixture* fx, const void*) { GjsAutoTestObject autoptr(fx->ptr); g_assert_true(autoptr == fx->ptr); g_object_ref(fx->ptr); autoptr.reset(fx->ptr); g_assert_true(autoptr == fx->ptr); g_assert_nonnull(fx->ptr); } static void test_gjs_autopointer_reset_other_ptr() { auto* ptr1 = gjs_test_object_new(); auto* ptr2 = gjs_test_object_new(); g_object_add_weak_pointer(G_OBJECT(ptr1), reinterpret_cast(&ptr1)); g_object_add_weak_pointer(G_OBJECT(ptr2), reinterpret_cast(&ptr2)); { GjsAutoTestObject autoptr(ptr1); g_assert_true(autoptr == ptr1); g_object_ref(ptr1); autoptr.reset(ptr2); g_assert_true(autoptr == ptr2); g_assert_nonnull(ptr1); g_assert_nonnull(ptr2); g_object_unref(ptr1); } g_assert_null(ptr1); g_assert_null(ptr2); } static void test_gjs_autopointer_swap_other_ptr(Fixture* fx, const void*) { GjsAutoTestObject autoptr1(fx->ptr); GjsAutoTestObject autoptr2; autoptr1.swap(autoptr2); g_assert_null(autoptr1); g_assert_true(autoptr2 == fx->ptr); g_assert_nonnull(fx->ptr); } static void test_gjs_autopointer_swap_self_ptr(Fixture* fx, const void*) { GjsAutoTestObject autoptr(fx->ptr); autoptr.swap(autoptr); g_assert_true(autoptr == fx->ptr); g_assert_nonnull(fx->ptr); } static void test_gjs_autopointer_swap_empty(Fixture* fx, const void*) { GjsAutoTestObject autoptr1(fx->ptr); GjsAutoTestObject autoptr2; autoptr1.swap(autoptr2); g_assert_null(autoptr1); g_assert_true(autoptr2 == fx->ptr); g_assert_nonnull(fx->ptr); } static void test_gjs_autopointer_copy(Fixture* fx, const void*) { GjsAutoTestObject autoptr(fx->ptr); g_assert_true(fx->ptr == autoptr.copy()); g_object_unref(fx->ptr); } static void test_gjs_autopointer_as() { GjsAutoTestObject autoptr(gjs_test_object_new()); g_assert_cmpuint(autoptr.as()->ref_count, ==, 1); } static void test_gjs_autochar_init() { char* str = g_strdup("FoooBar"); GjsAutoChar autoptr = str; g_assert_cmpstr(autoptr, ==, "FoooBar"); g_assert_cmpuint(autoptr[4], ==, 'B'); g_assert_true(autoptr == str); } static void test_gjs_autochar_init_take_ownership() { const char* str = "FoooBarConst"; GjsAutoChar autoptr(str, GjsAutoTakeOwnership()); g_assert_cmpstr(autoptr, ==, str); g_assert_cmpuint(autoptr[4], ==, 'B'); g_assert_true(autoptr != str); } static void test_gjs_autochar_copy() { GjsAutoChar autoptr = g_strdup("FoooBar"); char* copy = autoptr.copy(); g_assert_cmpstr(autoptr, ==, copy); g_assert_true(autoptr != copy); g_free(copy); } static void test_gjs_autostrv_init() { const char* strv[] = {"FOO", "Bar", "BAZ", nullptr}; GjsAutoStrv autoptr = g_strdupv(const_cast(strv)); g_assert_true(g_strv_equal(strv, autoptr)); for (int i = g_strv_length(const_cast(strv)); i >= 0; i--) g_assert_cmpstr(autoptr[i], ==, strv[i]); } static void test_gjs_autostrv_init_take_ownership() { const char* strv[] = {"FOO", "Bar", "BAZ", nullptr}; GjsAutoStrv autoptr(const_cast(strv), GjsAutoTakeOwnership()); for (int i = g_strv_length(const_cast(strv)); i >= 0; i--) g_assert_cmpstr(autoptr[i], ==, strv[i]); g_assert_false(autoptr == strv); } static void test_gjs_autostrv_copy() { const char* strv[] = {"FOO", "Bar", "BAZ", nullptr}; GjsAutoStrv autoptr = g_strdupv(const_cast(strv)); char** copy = autoptr.copy(); for (int i = g_strv_length(const_cast(strv)); i >= 0; i--) g_assert_cmpstr(copy[i], ==, strv[i]); g_assert_false(autoptr == copy); g_strfreev(copy); } static void test_gjs_autotypeclass_init() { GjsAutoTypeClass autoclass(gjs_test_object_get_type()); g_assert_nonnull(autoclass); g_assert_cmpint(autoclass->g_type_class.g_type, ==, gjs_test_object_get_type()); } static void test_gjs_error_init() { GjsAutoError error = g_error_new_literal(G_FILE_ERROR, G_FILE_ERROR_EXIST, "Message"); g_assert_nonnull(error); g_assert_cmpint(error->domain, ==, G_FILE_ERROR); g_assert_cmpint(error->code, ==, G_FILE_ERROR_EXIST); g_assert_cmpstr(error->message, ==, "Message"); error = g_error_new_literal(G_FILE_ERROR, G_FILE_ERROR_FAILED, "Other"); g_assert_error(error, G_FILE_ERROR, G_FILE_ERROR_FAILED); g_assert_cmpstr(error->message, ==, "Other"); } static void test_gjs_error_out() { GjsAutoError error( g_error_new_literal(G_FILE_ERROR, G_FILE_ERROR_EXIST, "Message")); g_clear_error(&error); g_assert_null(error); } #define ADD_AUTOPTRTEST(path, func) \ g_test_add(path, Fixture, nullptr, setup, func, teardown); void gjs_test_add_tests_for_jsapi_utils(void) { g_test_add_func("/cjs/jsapi-utils/gjs-autopointer/size", test_gjs_autopointer_size); g_test_add_func("/cjs/jsapi-utils/gjs-autopointer/constructor/empty", test_gjs_autopointer_ctor_empty); ADD_AUTOPTRTEST("/cjs/jsapi-utils/gjs-autopointer/constructor/basic", test_gjs_autopointer_ctor_basic); ADD_AUTOPTRTEST( "/cjs/jsapi-utils/gjs-autopointer/constructor/take_ownership", test_gjs_autopointer_ctor_take_ownership); ADD_AUTOPTRTEST("/cjs/jsapi-utils/gjs-autopointer/constructor/assignment", test_gjs_autopointer_ctor_assign); ADD_AUTOPTRTEST( "/cjs/jsapi-utils/gjs-autopointer/constructor/assignment/other", test_gjs_autopointer_ctor_assign_other); ADD_AUTOPTRTEST("/cjs/jsapi-utils/gjs-autopointer/destructor", test_gjs_autopointer_dtor); ADD_AUTOPTRTEST( "/cjs/jsapi-utils/gjs-autopointer/destructor/take_ownership", test_gjs_autopointer_dtor_take_ownership); g_test_add_func("/cjs/jsapi-utils/gjs-autopointer/destructor/default_free", test_gjs_autopointer_dtor_default_free); g_test_add_func( "/cjs/jsapi-utils/gjs-autopointer/destructor/no_free_pointer", test_gjs_autopointer_dtor_no_free_pointer); g_test_add_func("/cjs/jsapi-utils/gjs-autopointer/free_and_ref_funcs", test_gjs_autopointer_cast_free_func_type); g_test_add_func("/cjs/jsapi-utils/gjs-autopointer/destructor/c++", test_gjs_autopointer_dtor_cpp); g_test_add_func("/cjs/jsapi-utils/gjs-autopointer/destructor/c++-array", test_gjs_autopointer_dtor_cpp_array); g_test_add_func("/cjs/jsapi-utils/gjs-autopointer/operator/assign", test_gjs_autopointer_assign_operator); g_test_add_func( "/cjs/jsapi-utils/gjs-autopointer/operator/assign/other_ptr", test_gjs_autopointer_assign_operator_other_ptr); ADD_AUTOPTRTEST("/cjs/jsapi-utils/gjs-autopointer/operator/assign/self_ptr", test_gjs_autopointer_assign_operator_self_ptr); ADD_AUTOPTRTEST("/cjs/jsapi-utils/gjs-autopointer/operator/assign/object", test_gjs_autopointer_assign_operator_object); g_test_add_func( "/cjs/jsapi-utils/gjs-autopointer/operator/assign/other_object", test_gjs_autopointer_assign_operator_other_object); ADD_AUTOPTRTEST( "/cjs/jsapi-utils/gjs-autopointer/operator/assign/self_object", test_gjs_autopointer_assign_operator_self_object); ADD_AUTOPTRTEST( "/cjs/jsapi-utils/gjs-autopointer/operator/assign/copy_and_swap", test_gjs_autopointer_assign_operator_copy_and_swap); ADD_AUTOPTRTEST("/cjs/jsapi-utils/gjs-autopointer/operator/move", test_gjs_autopointer_operator_move); ADD_AUTOPTRTEST("/cjs/jsapi-utils/gjs-autopointer/operator/swap", test_gjs_autopointer_operator_swap); ADD_AUTOPTRTEST("/cjs/jsapi-utils/gjs-autopointer/operator/arrow", test_gjs_autopointer_assign_operator_arrow); ADD_AUTOPTRTEST("/cjs/jsapi-utils/gjs-autopointer/operator/deference", test_gjs_autopointer_assign_operator_deference); ADD_AUTOPTRTEST("/cjs/jsapi-utils/gjs-autopointer/operator/bool", test_gjs_autopointer_assign_operator_bool); g_test_add_func("/cjs/jsapi-utils/gjs-autopointer/operator/array", test_gjs_autopointer_assign_operator_array); ADD_AUTOPTRTEST("/cjs/jsapi-utils/gjs-autopointer/method/get", test_gjs_autopointer_get); ADD_AUTOPTRTEST("/cjs/jsapi-utils/gjs-autopointer/method/out", test_gjs_autopointer_out); ADD_AUTOPTRTEST("/cjs/jsapi-utils/gjs-autopointer/method/release", test_gjs_autopointer_release); ADD_AUTOPTRTEST("/cjs/jsapi-utils/gjs-autopointer/method/reset/nullptr", test_gjs_autopointer_reset_nullptr); g_test_add_func("/cjs/jsapi-utils/gjs-autopointer/method/reset/other_ptr", test_gjs_autopointer_reset_other_ptr); ADD_AUTOPTRTEST("/cjs/jsapi-utils/gjs-autopointer/method/reset/self_ptr", test_gjs_autopointer_reset_self_ptr); ADD_AUTOPTRTEST("/cjs/jsapi-utils/gjs-autopointer/method/swap/other_ptr", test_gjs_autopointer_swap_other_ptr); ADD_AUTOPTRTEST("/cjs/jsapi-utils/gjs-autopointer/method/swap/self_ptr", test_gjs_autopointer_swap_self_ptr); ADD_AUTOPTRTEST("/cjs/jsapi-utils/gjs-autopointer/method/swap/empty", test_gjs_autopointer_swap_empty); ADD_AUTOPTRTEST("/cjs/jsapi-utils/gjs-autopointer/method/copy", test_gjs_autopointer_copy); g_test_add_func("/cjs/jsapi-utils/gjs-autopointer/method/as", test_gjs_autopointer_as); // Other implementations g_test_add_func("/cjs/jsapi-utils/gjs-autochar/init", test_gjs_autochar_init); g_test_add_func("/cjs/jsapi-utils/gjs-autochar/init/take_ownership", test_gjs_autochar_init_take_ownership); g_test_add_func("/cjs/jsapi-utils/gjs-autochar/copy", test_gjs_autochar_copy); g_test_add_func("/cjs/jsapi-utils/gjs-autostrv/init", test_gjs_autostrv_init); g_test_add_func("/cjs/jsapi-utils/gjs-autostrv/init/take_ownership", test_gjs_autostrv_init_take_ownership); g_test_add_func("/cjs/jsapi-utils/gjs-autostrv/copy", test_gjs_autostrv_copy); g_test_add_func("/cjs/jsapi-utils/gjs-autotypeclass/init", test_gjs_autotypeclass_init); g_test_add_func("/cjs/jsapi-utils/gjs-autoerror/init", test_gjs_error_init); g_test_add_func("/cjs/jsapi-utils/gjs-autoerror/as-out-value", test_gjs_error_out); } cjs-128.0/test/gjs-test-common.cpp0000664000175000017500000000222614771557763015770 0ustar fabiofabio/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2018 Philip Chimento #include #include #include #include #include #include #include #include #include "test/gjs-test-common.h" // COMPAT: https://gitlab.gnome.org/GNOME/glib/-/merge_requests/1553 #ifdef __clang_analyzer__ void g_assertion_message(const char*, const char*, int, const char*, const char*) __attribute__((analyzer_noreturn)); #endif char* gjs_test_get_exception_message(JSContext* cx) { if (!JS_IsExceptionPending(cx)) return nullptr; JS::RootedValue v_exc(cx); g_assert_true(JS_GetPendingException(cx, &v_exc)); g_assert_true(v_exc.isObject()); JS::RootedObject exc(cx, &v_exc.toObject()); JSErrorReport* report = JS_ErrorFromException(cx, exc); g_assert_nonnull(report); char* retval = g_strdup(report->message().c_str()); g_assert_nonnull(retval); JS_ClearPendingException(cx); return retval; } cjs-128.0/test/extra/0000775000175000017500000000000014771557763013357 5ustar fabiofabiocjs-128.0/test/extra/Dockerfile.debug0000664000175000017500000000674114771557763016446 0ustar fabiofabio# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2020 Philip Chimento # === Build stage === FROM registry.fedoraproject.org/fedora:40 AS build ARG MOZJS_BRANCH=mozjs115 ARG MOZJS_BUILDDEPS=${MOZJS_BRANCH} ARG BUILD_OPTS= ENV SHELL=/bin/bash # mozjs115 cannot be built with python3.11 # cbindgen should be included in builddep(mozjs128) RUN dnf -y install 'dnf-command(builddep)' \ autoconf213 \ cbindgen \ clang \ clang-devel \ cmake \ git \ llvm \ llvm-devel \ make \ ninja-build \ python3.10 \ python3-packaging \ rust \ which \ xz RUN dnf -y builddep ${MOZJS_BUILDDEPS} WORKDIR /root RUN mkdir -p include-what-you-use/_build ADD https://include-what-you-use.org/downloads/include-what-you-use-0.22.src.tar.gz /root/include-what-you-use/ WORKDIR /root/include-what-you-use RUN tar xzf include-what-you-use-0.22.src.tar.gz --strip-components=1 WORKDIR /root/include-what-you-use/_build RUN cmake -G Ninja -DCMAKE_INSTALL_PREFIX=/usr .. RUN ninja RUN DESTDIR=/root/iwyu-install ninja install WORKDIR /root RUN git clone --no-tags --depth 1 https://github.com/ptomato/mozjs.git -b ${MOZJS_BRANCH} RUN mkdir -p mozjs/_build WORKDIR /root/mozjs/_build ENV PYTHON3=/usr/bin/python3.10 RUN ../js/src/configure --prefix=/usr --libdir=/usr/lib64 --disable-jemalloc \ --with-system-zlib --with-intl-api --enable-debug \ ${BUILD_OPTS} RUN make -j$(nproc) RUN DESTDIR=/root/mozjs-install make install RUN rm -f /root/mozjs-install/usr/lib64/libjs_static.ajs # === Actual Docker image === FROM registry.fedoraproject.org/fedora:40 ARG LOCALES=tr_TR ENV SHELL=/bin/bash # List is comprised of base dependencies for CI scripts, gjs, and debug packages # needed for informative stack traces, e.g. in Valgrind. # # Do everything in one RUN command so that the dnf cache is not cached in the # final Docker image. RUN dnf -y install --enablerepo=fedora-debuginfo,updates-debuginfo \ binutils \ cairo-debuginfo \ cairo-debugsource \ cairo-gobject-devel \ clang \ compiler-rt \ dbus-daemon \ dbus-x11 \ diffutils \ fontconfig-debuginfo \ fontconfig-debugsource \ gcc-c++ \ git \ glib2-debuginfo \ glib2-debugsource \ glib2-devel \ glibc-debuginfo \ glibc-gconv-extra \ glibc-locale-source \ gnome-desktop-testing \ gobject-introspection-debuginfo \ gobject-introspection-debugsource \ gobject-introspection-devel \ gtk3-debuginfo \ gtk3-debugsource \ gtk3-devel \ gtk4-debuginfo \ gtk4-debugsource \ gtk4-devel \ lcov \ libasan \ libubsan \ libtsan \ meson \ ninja-build \ pkgconf \ readline-devel \ sysprof-devel \ systemtap-sdt-devel \ valgrind \ which \ Xvfb \ xz \ && \ dnf clean all && rm -rf /var/cache/dnf COPY --from=build /root/mozjs-install/usr /usr COPY --from=build /root/iwyu-install/usr /usr RUN ln -s /usr/bin/iwyu_tool.py /usr/bin/iwyu_tool # Enable sudo for wheel users RUN sed -i -e 's/# %wheel/%wheel/' -e '0,/%wheel/{s/%wheel/# %wheel/}' \ /etc/sudoers ENV HOST_USER_ID 5555 RUN useradd -u $HOST_USER_ID -G wheel -ms /bin/bash user # Enable locales needed for specific tests RUN for locale in ${LOCALES}; do \ localedef --verbose --force -i "$locale" "$locale" || true; \ localedef --verbose --force -i "$locale" -f UTF-8 "$locale".UTF-8 || true; \ done USER user WORKDIR /home/user ENV LANG C.UTF-8 cjs-128.0/test/extra/do_environment.sh0000664000175000017500000000237314771557763016746 0ustar fabiofabio#!/bin/sh -e # SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2017 Claudio André do_Print_Labels () { if test -n "$1"; then label_len=${#1} span=$(((54 - $label_len) / 2)) echo echo "= ======================================================== =" printf "%s %${span}s %s %${span}s %s\n" "=" "" "$1" "" "=" echo "= ======================================================== =" else echo "= ========================= Done ========================= =" echo fi } do_Done () { # Done. De-initializes whatever is needed do_Print_Labels 'FINISHED' } do_Show_Info () { local compiler="${CC:-gcc}" echo '-----------------------------------------' echo 'Build system information' echo -n "Processors: "; grep -c ^processor /proc/cpuinfo grep ^MemTotal /proc/meminfo id; uname -a printenv echo '-----------------------------------------' cat /etc/*-release echo '-----------------------------------------' echo 'Compiler version' $compiler --version echo '-----------------------------------------' $compiler -dM -E -x c /dev/null echo '-----------------------------------------' } cjs-128.0/test/extra/Dockerfile0000664000175000017500000000563014771557763015355 0ustar fabiofabio# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2020 Philip Chimento # === Build Spidermonkey stage === FROM registry.fedoraproject.org/fedora:40 AS mozjs-build ARG MOZJS_BRANCH=mozjs115 ARG MOZJS_BUILDDEPS=${MOZJS_BRANCH} ARG BUILD_OPTS= ENV SHELL=/bin/bash # mozjs115 cannot be built with python3.11 # cbindgen should be included in builddep(mozjs128) RUN dnf -y install 'dnf-command(builddep)' \ autoconf213 \ cbindgen \ clang \ git \ llvm \ llvm-devel \ make \ python3.10 \ python-packaging \ rust \ which \ xz RUN dnf -y builddep ${MOZJS_BUILDDEPS} WORKDIR /root RUN git clone --no-tags --depth 1 https://github.com/ptomato/mozjs.git -b ${MOZJS_BRANCH} RUN mkdir -p mozjs/_build WORKDIR /root/mozjs/_build ENV PYTHON3=/usr/bin/python3.10 RUN ../js/src/configure --prefix=/usr --libdir=/usr/lib64 --disable-jemalloc \ --with-system-zlib --with-intl-api ${BUILD_OPTS} RUN make -j$(nproc) RUN DESTDIR=/root/mozjs-install make install RUN rm -f /root/mozjs-install/usr/lib64/libjs_static.ajs # === Actual Docker image === FROM registry.fedoraproject.org/fedora:40 ARG LOCALES=tr_TR ENV SHELL=/bin/bash # List is comprised of base dependencies for CI scripts, gjs, and debug packages # needed for informative stack traces, e.g. in Valgrind. # # Do everything in one RUN command so that the dnf cache is not cached in the # final Docker image. RUN dnf -y install --enablerepo=fedora-debuginfo,updates-debuginfo \ binutils \ cairo-debuginfo \ cairo-debugsource \ cairo-gobject-devel \ clang \ compiler-rt \ dbus-daemon \ dbus-x11 \ diffutils \ fontconfig-debuginfo \ fontconfig-debugsource \ gcc-c++ \ git \ glib2-debuginfo \ glib2-debugsource \ glib2-devel \ glibc-debuginfo \ glibc-gconv-extra \ glibc-locale-source \ gnome-desktop-testing \ gobject-introspection-debuginfo \ gobject-introspection-debugsource \ gobject-introspection-devel \ gtk3-debuginfo \ gtk3-debugsource \ gtk3-devel \ gtk4-debuginfo \ gtk4-debugsource \ gtk4-devel \ lcov \ libasan \ libubsan \ libtsan \ meson \ ninja-build \ pkgconf \ readline-devel \ systemtap-sdt-devel \ valgrind \ which \ Xvfb \ xz \ && \ dnf clean all && rm -rf /var/cache/dnf COPY --from=mozjs-build /root/mozjs-install/usr /usr # Enable sudo for wheel users RUN sed -i -e 's/# %wheel/%wheel/' -e '0,/%wheel/{s/%wheel/# %wheel/}' \ /etc/sudoers ENV HOST_USER_ID 5555 RUN useradd -u $HOST_USER_ID -G wheel -ms /bin/bash user # Enable locales needed for specific tests RUN for locale in ${LOCALES}; do \ localedef --verbose --force -i "$locale" "$locale" || true; \ localedef --verbose --force -i "$locale" -f UTF-8 "$locale".UTF-8 || true; \ done USER user WORKDIR /home/user ENV LANG C.UTF-8 cjs-128.0/modules/0000775000175000017500000000000014771557763012725 5ustar fabiofabiocjs-128.0/modules/console.cpp0000664000175000017500000002450114771557763015075 0ustar fabiofabio/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* vim: set ts=8 sw=4 et tw=78: */ // SPDX-License-Identifier: MPL-1.1 OR GPL-2.0-or-later OR LGPL-2.1-or-later // SPDX-FileCopyrightText: 1998 Netscape Communications Corporation #include // for HAVE_READLINE_READLINE_H #ifdef HAVE_SIGNAL_H # include # include # ifdef _WIN32 # define sigjmp_buf jmp_buf # define siglongjmp(e, v) longjmp (e, v) # define sigsetjmp(v, m) setjmp (v) # endif #endif #ifdef HAVE_READLINE_READLINE_H # include // include before readline/readline.h # include # include #endif #include #include #include // for g_fprintf #include #include #include // for JS_EncodeStringToUTF8 #include #include #include #include #include // for CurrentGlobalOrNull #include #include #include #include #include // for UniqueChars #include #include #include #include // for JS_NewPlainObject #include "cjs/atoms.h" #include "cjs/context-private.h" #include "cjs/global.h" #include "cjs/jsapi-util.h" #include "cjs/macros.h" #include "modules/console.h" namespace mozilla { union Utf8Unit; } static void gjs_console_warning_reporter(JSContext*, JSErrorReport* report) { JS::PrintError(stderr, report, /* reportWarnings = */ true); } /* Based on js::shell::AutoReportException from SpiderMonkey. */ class AutoReportException { JSContext *m_cx; public: explicit AutoReportException(JSContext *cx) : m_cx(cx) {} ~AutoReportException() { if (!JS_IsExceptionPending(m_cx)) return; /* Get exception object before printing and clearing exception. */ JS::ExceptionStack exnStack(m_cx); JS::ErrorReportBuilder report(m_cx); if (!JS::StealPendingExceptionStack(m_cx, &exnStack) || !report.init(m_cx, exnStack, JS::ErrorReportBuilder::NoSideEffects)) { g_printerr("(Unable to print exception)\n"); JS_ClearPendingException(m_cx); return; } g_assert(!report.report()->isWarning()); JS::PrintError(stderr, report, /* reportWarnings = */ false); if (exnStack.stack()) { JS::UniqueChars stack_str{ format_saved_frame(m_cx, exnStack.stack(), 2)}; if (!stack_str) { g_printerr("(Unable to print stack trace)\n"); } else { GjsAutoChar encoded_stack_str{g_filename_from_utf8( stack_str.get(), -1, nullptr, nullptr, nullptr)}; if (!encoded_stack_str) g_printerr("(Unable to print stack trace)\n"); else g_printerr("%s", stack_str.get()); } } JS_ClearPendingException(m_cx); } }; // Adapted from https://stackoverflow.com/a/17035073/172999 class AutoCatchCtrlC { #ifdef HAVE_SIGNAL_H void (*m_prev_handler)(int); static void handler(int signal) { if (signal == SIGINT) siglongjmp(jump_buffer, 1); } public: static sigjmp_buf jump_buffer; AutoCatchCtrlC() { m_prev_handler = signal(SIGINT, &AutoCatchCtrlC::handler); } ~AutoCatchCtrlC() { if (m_prev_handler != SIG_ERR) signal(SIGINT, m_prev_handler); } void raise_default() { if (m_prev_handler != SIG_ERR) signal(SIGINT, m_prev_handler); raise(SIGINT); } #endif // HAVE_SIGNAL_H }; #ifdef HAVE_SIGNAL_H sigjmp_buf AutoCatchCtrlC::jump_buffer; #endif // HAVE_SIGNAL_H [[nodiscard]] static bool gjs_console_readline(char** bufp, const char* prompt) { #ifdef HAVE_READLINE_READLINE_H char *line; line = readline(prompt); if (!line) return false; if (line[0] != '\0') add_history(line); *bufp = line; #else // !HAVE_READLINE_READLINE_H char line[256]; fprintf(stdout, "%s", prompt); fflush(stdout); if (!fgets(line, sizeof line, stdin)) return false; *bufp = g_strdup(line); #endif // !HAVE_READLINE_READLINE_H return true; } std::string print_string_value(JSContext* cx, JS::HandleValue v_string) { if (!v_string.isString()) return "[unexpected result from printing value]"; JS::RootedString printed_string(cx, v_string.toString()); JS::AutoSaveExceptionState exc_state(cx); JS::UniqueChars chars(JS_EncodeStringToUTF8(cx, printed_string)); exc_state.restore(); if (!chars) return "[error printing value]"; return chars.get(); } /* Return value of false indicates an uncatchable exception, rather than any * exception. (This is because the exception should be auto-printed around the * invocation of this function.) */ [[nodiscard]] static bool gjs_console_eval_and_print(JSContext* cx, JS::HandleObject global, const std::string& bytes, int lineno) { JS::SourceText source; if (!source.init(cx, bytes.c_str(), bytes.size(), JS::SourceOwnership::Borrowed)) return false; JS::CompileOptions options(cx); options.setFileAndLine("typein", lineno); JS::RootedValue result(cx); if (!JS::Evaluate(cx, options, source, &result)) { if (!JS_IsExceptionPending(cx)) return false; } GjsContextPrivate* gjs = GjsContextPrivate::from_cx(cx); gjs->schedule_gc_if_needed(); JS::AutoSaveExceptionState exc_state(cx); JS::RootedValue v_printed_string(cx); JS::RootedValue v_pretty_print( cx, gjs_get_global_slot(global, GjsGlobalSlot::PRETTY_PRINT_FUNC)); bool ok = JS::Call(cx, global, v_pretty_print, JS::HandleValueArray(result), &v_printed_string); if (!ok) gjs_log_exception(cx); exc_state.restore(); if (ok) { g_fprintf(stdout, "%s\n", print_string_value(cx, v_printed_string).c_str()); } else { g_fprintf(stdout, "[error printing value]\n"); } return true; } GJS_JSAPI_RETURN_CONVENTION static bool gjs_console_interact(JSContext *context, unsigned argc, JS::Value *vp) { JS::CallArgs argv = JS::CallArgsFromVp(argc, vp); volatile bool eof, exit_warning; // accessed after setjmp() JS::RootedObject global{context, JS::CurrentGlobalOrNull(context)}; char* temp_buf; volatile int lineno; // accessed after setjmp() volatile int startline; // accessed after setjmp() #ifndef HAVE_READLINE_READLINE_H int rl_end = 0; // nonzero if using readline and any text is typed in #endif JS::SetWarningReporter(context, gjs_console_warning_reporter); AutoCatchCtrlC ctrl_c; // Separate initialization from declaration because of possible overwriting // when siglongjmp() jumps into this function eof = exit_warning = false; temp_buf = nullptr; lineno = 1; do { /* * Accumulate lines until we get a 'compilable unit' - one that either * generates an error (before running out of source) or that compiles * cleanly. This should be whenever we get a complete statement that * coincides with the end of a line. */ startline = lineno; std::string buffer; do { #ifdef HAVE_SIGNAL_H // sigsetjmp() returns 0 if control flow encounters it normally, and // nonzero if it's been jumped to. In the latter case, use a while // loop so that we call sigsetjmp() a second time to reinit the jump // buffer. while (sigsetjmp(AutoCatchCtrlC::jump_buffer, 1) != 0) { g_fprintf(stdout, "\n"); if (buffer.empty() && rl_end == 0) { if (!exit_warning) { g_fprintf(stdout, "(To exit, press Ctrl+C again or Ctrl+D)\n"); exit_warning = true; } else { ctrl_c.raise_default(); } } else { exit_warning = false; } buffer.clear(); startline = lineno = 1; } #endif // HAVE_SIGNAL_H if (!gjs_console_readline( &temp_buf, startline == lineno ? "gjs> " : ".... ")) { eof = true; break; } buffer += temp_buf; buffer += "\n"; g_free(temp_buf); lineno++; } while (!JS_Utf8BufferIsCompilableUnit(context, global, buffer.c_str(), buffer.size())); bool ok; { AutoReportException are(context); ok = gjs_console_eval_and_print(context, global, buffer, startline); } exit_warning = false; GjsContextPrivate* gjs = GjsContextPrivate::from_cx(context); ok = gjs->run_jobs_fallible() && ok; if (!ok) { /* If this was an uncatchable exception, throw another uncatchable * exception on up to the surrounding JS::Evaluate() in main(). This * happens when you run cjs-console and type imports.system.exit(0); * at the prompt. If we don't throw another uncatchable exception * here, then it's swallowed and main() won't exit. */ return false; } } while (!eof); g_fprintf(stdout, "\n"); argv.rval().setUndefined(); return true; } bool gjs_define_console_stuff(JSContext *context, JS::MutableHandleObject module) { module.set(JS_NewPlainObject(context)); const GjsAtoms& atoms = GjsContextPrivate::atoms(context); return JS_DefineFunctionById(context, module, atoms.interact(), gjs_console_interact, 1, GJS_MODULE_PROP_FLAGS); } cjs-128.0/modules/print.cpp0000664000175000017500000001432214771557763014567 0ustar fabiofabio/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2008 litl, LLC // SPDX-FileCopyrightText: 2009 Red Hat, Inc. #include #include #include #include #include // for JS_EncodeStringToUTF8 #include #include #include // for JS_DefineFunctions #include // for JS_FN, JSFunctionSpec, JS_FS_END #include #include #include // for UniqueChars #include #include // for JS_NewPlainObject #include "cjs/global.h" #include "cjs/jsapi-util.h" #include "cjs/macros.h" #include "modules/print.h" GJS_JSAPI_RETURN_CONVENTION static bool gjs_log(JSContext* cx, unsigned argc, JS::Value* vp) { JS::CallArgs argv = JS::CallArgsFromVp(argc, vp); if (argc != 1) { gjs_throw(cx, "Must pass a single argument to log()"); return false; } /* JS::ToString might throw, in which case we will only log that the value * could not be converted to string */ JS::AutoSaveExceptionState exc_state(cx); JS::RootedString jstr(cx, JS::ToString(cx, argv[0])); exc_state.restore(); if (!jstr) { g_message("JS LOG: "); return true; } JS::UniqueChars s(JS_EncodeStringToUTF8(cx, jstr)); if (!s) return false; g_message("JS LOG: %s", s.get()); argv.rval().setUndefined(); return true; } GJS_JSAPI_RETURN_CONVENTION static bool gjs_log_error(JSContext* cx, unsigned argc, JS::Value* vp) { JS::CallArgs argv = JS::CallArgsFromVp(argc, vp); if ((argc != 1 && argc != 2) || !argv[0].isObject()) { gjs_throw( cx, "Must pass an exception and optionally a message to logError()"); return false; } JS::RootedString jstr(cx); if (argc == 2) { /* JS::ToString might throw, in which case we will only log that the * value could not be converted to string */ JS::AutoSaveExceptionState exc_state(cx); jstr = JS::ToString(cx, argv[1]); exc_state.restore(); } gjs_log_exception_full(cx, argv[0], jstr, G_LOG_LEVEL_WARNING); argv.rval().setUndefined(); return true; } GJS_JSAPI_RETURN_CONVENTION static bool gjs_print_parse_args(JSContext* cx, const JS::CallArgs& argv, std::string* buffer) { g_assert(buffer && "forgot out parameter"); buffer->clear(); for (unsigned n = 0; n < argv.length(); ++n) { /* JS::ToString might throw, in which case we will only log that the * value could not be converted to string */ JS::AutoSaveExceptionState exc_state(cx); JS::RootedString jstr(cx, JS::ToString(cx, argv[n])); exc_state.restore(); if (jstr) { JS::UniqueChars s(JS_EncodeStringToUTF8(cx, jstr)); if (!s) return false; *buffer += s.get(); if (n < (argv.length() - 1)) *buffer += ' '; } else { *buffer = ""; return true; } } return true; } GJS_JSAPI_RETURN_CONVENTION static bool gjs_print(JSContext* context, unsigned argc, JS::Value* vp) { JS::CallArgs argv = JS::CallArgsFromVp(argc, vp); std::string buffer; if (!gjs_print_parse_args(context, argv, &buffer)) return false; g_print("%s\n", buffer.c_str()); argv.rval().setUndefined(); return true; } GJS_JSAPI_RETURN_CONVENTION static bool gjs_printerr(JSContext* context, unsigned argc, JS::Value* vp) { JS::CallArgs argv = JS::CallArgsFromVp(argc, vp); std::string buffer; if (!gjs_print_parse_args(context, argv, &buffer)) return false; g_printerr("%s\n", buffer.c_str()); argv.rval().setUndefined(); return true; } // The pretty-print functionality is best written in JS, but needs to be used // from C++ code. This stores the prettyPrint() function in a slot on the global // object so that it can be used internally by the Console module. // This function is not available to user code. GJS_JSAPI_RETURN_CONVENTION static bool set_pretty_print_function(JSContext*, unsigned argc, JS::Value* vp) { JS::CallArgs args = JS::CallArgsFromVp(argc, vp); // can only be called internally, so OK to assert correct arguments g_assert(args.length() == 2 && "setPrettyPrintFunction takes 2 arguments"); JS::Value v_global = args[0]; JS::Value v_func = args[1]; g_assert(v_global.isObject() && "first argument must be an object"); g_assert(v_func.isObject() && "second argument must be an object"); gjs_set_global_slot(&v_global.toObject(), GjsGlobalSlot::PRETTY_PRINT_FUNC, v_func); args.rval().setUndefined(); return true; } GJS_JSAPI_RETURN_CONVENTION static bool get_pretty_print_function(JSContext*, unsigned argc, JS::Value* vp) { JS::CallArgs args = JS::CallArgsFromVp(argc, vp); g_assert(args.length() == 1 && "getPrettyPrintFunction takes 1 arguments"); JS::Value v_global = args[0]; g_assert(v_global.isObject() && "argument must be an object"); JS::Value pretty_print = gjs_get_global_slot( &v_global.toObject(), GjsGlobalSlot::PRETTY_PRINT_FUNC); args.rval().set(pretty_print); return true; } // clang-format off static constexpr JSFunctionSpec funcs[] = { JS_FN("log", gjs_log, 1, GJS_MODULE_PROP_FLAGS), JS_FN("logError", gjs_log_error, 2, GJS_MODULE_PROP_FLAGS), JS_FN("print", gjs_print, 0, GJS_MODULE_PROP_FLAGS), JS_FN("printerr", gjs_printerr, 0, GJS_MODULE_PROP_FLAGS), JS_FN("setPrettyPrintFunction", set_pretty_print_function, 1, GJS_MODULE_PROP_FLAGS), JS_FN("getPrettyPrintFunction", get_pretty_print_function, 1, GJS_MODULE_PROP_FLAGS), JS_FS_END}; // clang-format on bool gjs_define_print_stuff(JSContext* context, JS::MutableHandleObject module) { module.set(JS_NewPlainObject(context)); if (!module) return false; return JS_DefineFunctions(context, module, funcs); } cjs-128.0/modules/cairo-radial-gradient.cpp0000664000175000017500000000334614771557763017561 0ustar fabiofabio/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2010 litl, LLC. #include #include #include // for JSPROP_READONLY #include #include #include #include // for JS_NewObjectWithGivenProto #include // for JSProtoKey #include "cjs/jsapi-util-args.h" #include "modules/cairo-private.h" namespace JS { class CallArgs; } JSObject* CairoRadialGradient::new_proto(JSContext* cx, JSProtoKey) { JS::RootedObject parent_proto(cx, CairoGradient::prototype(cx)); return JS_NewObjectWithGivenProto(cx, nullptr, parent_proto); } cairo_pattern_t* CairoRadialGradient::constructor_impl( JSContext* context, const JS::CallArgs& argv) { double cx0, cy0, radius0, cx1, cy1, radius1; cairo_pattern_t* pattern; if (!gjs_parse_call_args(context, "RadialGradient", argv, "ffffff", "cx0", &cx0, "cy0", &cy0, "radius0", &radius0, "cx1", &cx1, "cy1", &cy1, "radius1", &radius1)) return nullptr; pattern = cairo_pattern_create_radial(cx0, cy0, radius0, cx1, cy1, radius1); if (!gjs_cairo_check_status(context, cairo_pattern_status(pattern), "pattern")) return nullptr; return pattern; } const JSPropertySpec CairoRadialGradient::proto_props[] = { JS_STRING_SYM_PS(toStringTag, "RadialGradient", JSPROP_READONLY), JS_PS_END}; const JSFunctionSpec CairoRadialGradient::proto_funcs[] = { // getRadialCircles JS_FS_END}; cjs-128.0/modules/cairo-linear-gradient.cpp0000664000175000017500000000311314771557763017567 0ustar fabiofabio/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2010 litl, LLC. #include #include #include // for JSPROP_READONLY #include #include #include #include // for JS_NewObjectWithGivenProto #include // for JSProtoKey #include "cjs/jsapi-util-args.h" #include "modules/cairo-private.h" namespace JS { class CallArgs; } JSObject* CairoLinearGradient::new_proto(JSContext* cx, JSProtoKey) { JS::RootedObject parent_proto(cx, CairoGradient::prototype(cx)); return JS_NewObjectWithGivenProto(cx, nullptr, parent_proto); } cairo_pattern_t* CairoLinearGradient::constructor_impl( JSContext* context, const JS::CallArgs& argv) { double x0, y0, x1, y1; cairo_pattern_t* pattern; if (!gjs_parse_call_args(context, "LinearGradient", argv, "ffff", "x0", &x0, "y0", &y0, "x1", &x1, "y1", &y1)) return nullptr; pattern = cairo_pattern_create_linear(x0, y0, x1, y1); if (!gjs_cairo_check_status(context, cairo_pattern_status(pattern), "pattern")) return nullptr; return pattern; } const JSPropertySpec CairoLinearGradient::proto_props[] = { JS_STRING_SYM_PS(toStringTag, "LinearGradient", JSPROP_READONLY), JS_PS_END}; const JSFunctionSpec CairoLinearGradient::proto_funcs[] = { // getLinearPoints JS_FS_END}; cjs-128.0/modules/cairo-solid-pattern.cpp0000664000175000017500000000563414771557763017321 0ustar fabiofabio/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2010 litl, LLC. #include #include #include #include // for JSPROP_READONLY #include #include #include #include // for JS_NewObjectWithGivenProto #include // for JSProtoKey #include "cjs/jsapi-util-args.h" #include "cjs/macros.h" #include "modules/cairo-private.h" JSObject* CairoSolidPattern::new_proto(JSContext* cx, JSProtoKey) { JS::RootedObject parent_proto(cx, CairoPattern::prototype(cx)); return JS_NewObjectWithGivenProto(cx, nullptr, parent_proto); } // clang-format off const JSPropertySpec CairoSolidPattern::proto_props[] = { JS_STRING_SYM_PS(toStringTag, "SolidPattern", JSPROP_READONLY), JS_PS_END}; // clang-format on GJS_JSAPI_RETURN_CONVENTION static bool createRGB_func(JSContext *context, unsigned argc, JS::Value *vp) { JS::CallArgs argv = JS::CallArgsFromVp (argc, vp); double red, green, blue; cairo_pattern_t *pattern; if (!gjs_parse_call_args(context, "createRGB", argv, "fff", "red", &red, "green", &green, "blue", &blue)) return false; pattern = cairo_pattern_create_rgb(red, green, blue); if (!gjs_cairo_check_status(context, cairo_pattern_status(pattern), "pattern")) return false; JSObject* pattern_wrapper = CairoSolidPattern::from_c_ptr(context, pattern); if (!pattern_wrapper) return false; cairo_pattern_destroy(pattern); argv.rval().setObjectOrNull(pattern_wrapper); return true; } GJS_JSAPI_RETURN_CONVENTION static bool createRGBA_func(JSContext *context, unsigned argc, JS::Value *vp) { JS::CallArgs argv = JS::CallArgsFromVp (argc, vp); double red, green, blue, alpha; cairo_pattern_t *pattern; if (!gjs_parse_call_args(context, "createRGBA", argv, "ffff", "red", &red, "green", &green, "blue", &blue, "alpha", &alpha)) return false; pattern = cairo_pattern_create_rgba(red, green, blue, alpha); if (!gjs_cairo_check_status(context, cairo_pattern_status(pattern), "pattern")) return false; JSObject* pattern_wrapper = CairoSolidPattern::from_c_ptr(context, pattern); if (!pattern_wrapper) return false; cairo_pattern_destroy(pattern); argv.rval().setObjectOrNull(pattern_wrapper); return true; } // clang-format off const JSFunctionSpec CairoSolidPattern::static_funcs[] = { JS_FN("createRGB", createRGB_func, 0, 0), JS_FN("createRGBA", createRGBA_func, 0, 0), JS_FS_END}; // clang-format on cjs-128.0/modules/console.h0000664000175000017500000000073414771557763014544 0ustar fabiofabio/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2008 litl, LLC #ifndef MODULES_CONSOLE_H_ #define MODULES_CONSOLE_H_ #include #include #include "cjs/macros.h" GJS_JSAPI_RETURN_CONVENTION bool gjs_define_console_stuff(JSContext *context, JS::MutableHandleObject module); #endif // MODULES_CONSOLE_H_ cjs-128.0/modules/cairo-context.cpp0000664000175000017500000011607514771557763016222 0ustar fabiofabio/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2010 litl, LLC. #include #include // for size_t #include #include #include #include #include // for JS::NewArrayObject #include #include #include #include // for JSPROP_READONLY #include #include #include #include // for UniqueChars #include #include // for JS_NewPlainObject #include "gi/arg-inl.h" #include "gi/arg.h" #include "gi/foreign.h" #include "cjs/enum-utils.h" #include "cjs/jsapi-util-args.h" #include "cjs/jsapi-util.h" #include "cjs/macros.h" #include "modules/cairo-private.h" #define _GJS_CAIRO_CONTEXT_GET_PRIV_CR_CHECKED(cx, argc, vp, argv, obj) \ GJS_GET_THIS(cx, argc, vp, argv, obj); \ cairo_t* cr; \ if (!CairoContext::for_js_typecheck(cx, obj, &cr, &argv)) \ return false; \ if (!cr) \ return true; #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(mname) \ GJS_JSAPI_RETURN_CONVENTION \ static bool mname##_func(JSContext* context, unsigned argc, \ JS::Value* vp) { \ _GJS_CAIRO_CONTEXT_GET_PRIV_CR_CHECKED(context, argc, vp, argv, obj) #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END \ return gjs_cairo_check_status(context, cairo_status(cr), "context"); \ } #define _GJS_CAIRO_CONTEXT_CHECK_NO_ARGS(m) \ if (argc > 0) { \ gjs_throw(context, "Context." #m "() takes no arguments"); \ return false; \ } #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(method, cfunc) \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \ cfunc(cr); \ argv.rval().setUndefined(); \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC0I(method, cfunc) \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \ int ret; \ _GJS_CAIRO_CONTEXT_CHECK_NO_ARGS(method) \ ret = (int)cfunc(cr); \ argv.rval().setInt32(ret); \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC0B(method, cfunc) \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \ cairo_bool_t ret; \ _GJS_CAIRO_CONTEXT_CHECK_NO_ARGS(method) \ ret = cfunc(cr); \ argv.rval().setBoolean(ret); \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC2FFAFF(method, cfunc, n1, n2) \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \ double arg1, arg2; \ if (!gjs_parse_call_args(context, #method, argv, "ff", #n1, &arg1, #n2, \ &arg2)) \ return false; \ cfunc(cr, &arg1, &arg2); \ if (cairo_status(cr) == CAIRO_STATUS_SUCCESS) { \ JS::RootedObject array(context, JS::NewArrayObject(context, 2)); \ if (!array) \ return false; \ JS::RootedValue r{context, \ JS::NumberValue(JS::CanonicalizeNaN(arg1))}; \ if (!JS_SetElement(context, array, 0, r)) \ return false; \ r.setNumber(JS::CanonicalizeNaN(arg2)); \ if (!JS_SetElement(context, array, 1, r)) \ return false; \ argv.rval().setObject(*array); \ } \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC0AFF(method, cfunc) \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \ double arg1, arg2; \ _GJS_CAIRO_CONTEXT_CHECK_NO_ARGS(method) \ cfunc(cr, &arg1, &arg2); \ if (cairo_status(cr) == CAIRO_STATUS_SUCCESS) { \ JS::RootedObject array(context, JS::NewArrayObject(context, 2)); \ if (!array) \ return false; \ JS::RootedValue r{context, \ JS::NumberValue(JS::CanonicalizeNaN(arg1))}; \ if (!JS_SetElement(context, array, 0, r)) \ return false; \ r.setNumber(JS::CanonicalizeNaN(arg2)); \ if (!JS_SetElement(context, array, 1, r)) \ return false; \ argv.rval().setObject(*array); \ } \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC0AFFFF(method, cfunc) \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \ double arg1, arg2, arg3, arg4; \ _GJS_CAIRO_CONTEXT_CHECK_NO_ARGS(method) \ cfunc(cr, &arg1, &arg2, &arg3, &arg4); \ { \ JS::RootedObject array(context, JS::NewArrayObject(context, 4)); \ if (!array) \ return false; \ JS::RootedValue r{context, \ JS::NumberValue(JS::CanonicalizeNaN(arg1))}; \ if (!JS_SetElement(context, array, 0, r)) \ return false; \ r.setNumber(JS::CanonicalizeNaN(arg2)); \ if (!JS_SetElement(context, array, 1, r)) \ return false; \ r.setNumber(JS::CanonicalizeNaN(arg3)); \ if (!JS_SetElement(context, array, 2, r)) \ return false; \ r.setNumber(JS::CanonicalizeNaN(arg4)); \ if (!JS_SetElement(context, array, 3, r)) \ return false; \ argv.rval().setObject(*array); \ } \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC0F(method, cfunc) \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \ double ret; \ _GJS_CAIRO_CONTEXT_CHECK_NO_ARGS(method) \ ret = cfunc(cr); \ argv.rval().setNumber(JS::CanonicalizeNaN(ret)); \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC1(method, cfunc, fmt, t1, n1) \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \ t1 arg1; \ if (!gjs_parse_call_args(context, #method, argv, fmt, \ #n1, &arg1)) \ return false; \ cfunc(cr, arg1); \ argv.rval().setUndefined(); \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC2(method, cfunc, fmt, t1, n1, t2, n2) \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \ t1 arg1; \ t2 arg2; \ if (!gjs_parse_call_args(context, #method, argv, fmt, \ #n1, &arg1, #n2, &arg2)) \ return false; \ cfunc(cr, arg1, arg2); \ argv.rval().setUndefined(); \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC2B(method, cfunc, fmt, t1, n1, t2, n2) \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \ t1 arg1; \ t2 arg2; \ cairo_bool_t ret; \ if (!gjs_parse_call_args(context, #method, argv, fmt, \ #n1, &arg1, #n2, &arg2)) \ return false; \ ret = cfunc(cr, arg1, arg2); \ argv.rval().setBoolean(ret); \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC3(method, cfunc, fmt, t1, n1, t2, n2, t3, n3) \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \ t1 arg1; \ t2 arg2; \ t3 arg3; \ if (!gjs_parse_call_args(context, #method, argv, fmt, \ #n1, &arg1, #n2, &arg2, #n3, &arg3)) \ return false; \ cfunc(cr, arg1, arg2, arg3); \ argv.rval().setUndefined(); \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC4(method, cfunc, fmt, t1, n1, t2, n2, t3, n3, t4, n4) \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \ t1 arg1; \ t2 arg2; \ t3 arg3; \ t4 arg4; \ if (!gjs_parse_call_args(context, #method, argv, fmt, \ #n1, &arg1, #n2, &arg2, \ #n3, &arg3, #n4, &arg4)) \ return false; \ cfunc(cr, arg1, arg2, arg3, arg4); \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC5(method, cfunc, fmt, t1, n1, t2, n2, t3, n3, t4, n4, t5, n5) \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \ t1 arg1; \ t2 arg2; \ t3 arg3; \ t4 arg4; \ t5 arg5; \ if (!gjs_parse_call_args(context, #method, argv, fmt, \ #n1, &arg1, #n2, &arg2, #n3, &arg3, \ #n4, &arg4, #n5, &arg5)) \ return false; \ cfunc(cr, arg1, arg2, arg3, arg4, arg5); \ argv.rval().setUndefined(); \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END #define _GJS_CAIRO_CONTEXT_DEFINE_FUNC6(method, cfunc, fmt, t1, n1, t2, n2, t3, n3, t4, n4, t5, n5, t6, n6) \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \ t1 arg1; \ t2 arg2; \ t3 arg3; \ t4 arg4; \ t5 arg5; \ t6 arg6; \ if (!gjs_parse_call_args(context, #method, argv, fmt, \ #n1, &arg1, #n2, &arg2, #n3, &arg3, \ #n4, &arg4, #n5, &arg5, #n6, &arg6)) \ return false; \ cfunc(cr, arg1, arg2, arg3, arg4, arg5, arg6); \ argv.rval().setUndefined(); \ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END GJS_JSAPI_RETURN_CONVENTION cairo_t* CairoContext::constructor_impl(JSContext* context, const JS::CallArgs& argv) { cairo_t *cr; JS::RootedObject surface_wrapper(context); if (!gjs_parse_call_args(context, "Context", argv, "o", "surface", &surface_wrapper)) return nullptr; cairo_surface_t* surface = CairoSurface::for_js(context, surface_wrapper); if (!surface) return nullptr; cr = cairo_create(surface); if (!gjs_cairo_check_status(context, cairo_status(cr), "context")) return nullptr; return cr; } void CairoContext::finalize_impl(JS::GCContext*, cairo_t* cr) { if (!cr) return; cairo_destroy(cr); } /* Properties */ // clang-format off const JSPropertySpec CairoContext::proto_props[] = { JS_STRING_SYM_PS(toStringTag, "Context", JSPROP_READONLY), JS_PS_END}; // clang-format on /* Methods */ _GJS_CAIRO_CONTEXT_DEFINE_FUNC5(arc, cairo_arc, "fffff", double, xc, double, yc, double, radius, double, angle1, double, angle2) _GJS_CAIRO_CONTEXT_DEFINE_FUNC5(arcNegative, cairo_arc_negative, "fffff", double, xc, double, yc, double, radius, double, angle1, double, angle2) _GJS_CAIRO_CONTEXT_DEFINE_FUNC6(curveTo, cairo_curve_to, "ffffff", double, x1, double, y1, double, x2, double, y2, double, x3, double, y3) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(clip, cairo_clip) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(clipPreserve, cairo_clip_preserve) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0AFFFF(clipExtents, cairo_clip_extents) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(closePath, cairo_close_path) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(copyPage, cairo_copy_page) _GJS_CAIRO_CONTEXT_DEFINE_FUNC2FFAFF(deviceToUser, cairo_device_to_user, "x", "y") _GJS_CAIRO_CONTEXT_DEFINE_FUNC2FFAFF(deviceToUserDistance, cairo_device_to_user_distance, "x", "y") _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(fill, cairo_fill) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(fillPreserve, cairo_fill_preserve) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0AFFFF(fillExtents, cairo_fill_extents) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0I(getAntialias, cairo_get_antialias) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0AFF(getCurrentPoint, cairo_get_current_point) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0I(getDashCount, cairo_get_dash_count) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0I(getFillRule, cairo_get_fill_rule) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0I(getLineCap, cairo_get_line_cap) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0I(getLineJoin, cairo_get_line_join) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0F(getLineWidth, cairo_get_line_width) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0F(getMiterLimit, cairo_get_miter_limit) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0I(getOperator, cairo_get_operator) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0F(getTolerance, cairo_get_tolerance) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0B(hasCurrentPoint, cairo_has_current_point) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(identityMatrix, cairo_identity_matrix) _GJS_CAIRO_CONTEXT_DEFINE_FUNC2B(inFill, cairo_in_fill, "ff", double, x, double, y) _GJS_CAIRO_CONTEXT_DEFINE_FUNC2B(inStroke, cairo_in_stroke, "ff", double, x, double, y) _GJS_CAIRO_CONTEXT_DEFINE_FUNC2(lineTo, cairo_line_to, "ff", double, x, double, y) _GJS_CAIRO_CONTEXT_DEFINE_FUNC2(moveTo, cairo_move_to, "ff", double, x, double, y) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(newPath, cairo_new_path) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(newSubPath, cairo_new_sub_path) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(paint, cairo_paint) _GJS_CAIRO_CONTEXT_DEFINE_FUNC1(paintWithAlpha, cairo_paint_with_alpha, "f", double, alpha) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0AFFFF(pathExtents, cairo_path_extents) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(pushGroup, cairo_push_group) _GJS_CAIRO_CONTEXT_DEFINE_FUNC1(pushGroupWithContent, cairo_push_group_with_content, "i", cairo_content_t, content) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(popGroupToSource, cairo_pop_group_to_source) _GJS_CAIRO_CONTEXT_DEFINE_FUNC4(rectangle, cairo_rectangle, "ffff", double, x, double, y, double, width, double, height) _GJS_CAIRO_CONTEXT_DEFINE_FUNC6(relCurveTo, cairo_rel_curve_to, "ffffff", double, dx1, double, dy1, double, dx2, double, dy2, double, dx3, double, dy3) _GJS_CAIRO_CONTEXT_DEFINE_FUNC2(relLineTo, cairo_rel_line_to, "ff", double, dx, double, dy) _GJS_CAIRO_CONTEXT_DEFINE_FUNC2(relMoveTo, cairo_rel_move_to, "ff", double, dx, double, dy) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(resetClip, cairo_reset_clip) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(restore, cairo_restore) _GJS_CAIRO_CONTEXT_DEFINE_FUNC1(rotate, cairo_rotate, "f", double, angle) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(save, cairo_save) _GJS_CAIRO_CONTEXT_DEFINE_FUNC2(scale, cairo_scale, "ff", double, sx, double, sy) _GJS_CAIRO_CONTEXT_DEFINE_FUNC1(setAntialias, cairo_set_antialias, "i", cairo_antialias_t, antialias) _GJS_CAIRO_CONTEXT_DEFINE_FUNC1(setFillRule, cairo_set_fill_rule, "i", cairo_fill_rule_t, fill_rule) _GJS_CAIRO_CONTEXT_DEFINE_FUNC1(setFontSize, cairo_set_font_size, "f", double, size) _GJS_CAIRO_CONTEXT_DEFINE_FUNC1(setLineCap, cairo_set_line_cap, "i", cairo_line_cap_t, line_cap) _GJS_CAIRO_CONTEXT_DEFINE_FUNC1(setLineJoin, cairo_set_line_join, "i", cairo_line_join_t, line_join) _GJS_CAIRO_CONTEXT_DEFINE_FUNC1(setLineWidth, cairo_set_line_width, "f", double, width) _GJS_CAIRO_CONTEXT_DEFINE_FUNC1(setMiterLimit, cairo_set_miter_limit, "f", double, limit) _GJS_CAIRO_CONTEXT_DEFINE_FUNC1(setOperator, cairo_set_operator, "i", cairo_operator_t, op) _GJS_CAIRO_CONTEXT_DEFINE_FUNC1(setTolerance, cairo_set_tolerance, "f", double, tolerance) _GJS_CAIRO_CONTEXT_DEFINE_FUNC3(setSourceRGB, cairo_set_source_rgb, "fff", double, red, double, green, double, blue) _GJS_CAIRO_CONTEXT_DEFINE_FUNC4(setSourceRGBA, cairo_set_source_rgba, "ffff", double, red, double, green, double, blue, double, alpha) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(showPage, cairo_show_page) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(stroke, cairo_stroke) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0(strokePreserve, cairo_stroke_preserve) _GJS_CAIRO_CONTEXT_DEFINE_FUNC0AFFFF(strokeExtents, cairo_stroke_extents) _GJS_CAIRO_CONTEXT_DEFINE_FUNC2(translate, cairo_translate, "ff", double, tx, double, ty) _GJS_CAIRO_CONTEXT_DEFINE_FUNC2FFAFF(userToDevice, cairo_user_to_device, "x", "y") _GJS_CAIRO_CONTEXT_DEFINE_FUNC2FFAFF(userToDeviceDistance, cairo_user_to_device_distance, "x", "y") bool CairoContext::dispose(JSContext* context, unsigned argc, JS::Value* vp) { _GJS_CAIRO_CONTEXT_GET_PRIV_CR_CHECKED(context, argc, vp, rec, obj); cairo_destroy(cr); CairoContext::unset_private(obj); rec.rval().setUndefined(); return true; } GJS_JSAPI_RETURN_CONVENTION static bool appendPath_func(JSContext *context, unsigned argc, JS::Value *vp) { _GJS_CAIRO_CONTEXT_GET_PRIV_CR_CHECKED(context, argc, vp, argv, obj); JS::RootedObject path_wrapper(context); if (!gjs_parse_call_args(context, "path", argv, "o", "path", &path_wrapper)) return false; cairo_path_t* path; if (!CairoPath::for_js_typecheck(context, path_wrapper, &path, &argv)) return false; cairo_append_path(cr, path); argv.rval().setUndefined(); return true; } GJS_JSAPI_RETURN_CONVENTION static bool copyPath_func(JSContext *context, unsigned argc, JS::Value *vp) { _GJS_CAIRO_CONTEXT_GET_PRIV_CR_CHECKED(context, argc, vp, argv, obj); cairo_path_t *path; if (!gjs_parse_call_args(context, "", argv, "")) return false; path = cairo_copy_path(cr); JSObject* retval = CairoPath::take_c_ptr(context, path); if (!retval) return false; argv.rval().setObject(*retval); return true; } GJS_JSAPI_RETURN_CONVENTION static bool copyPathFlat_func(JSContext *context, unsigned argc, JS::Value *vp) { _GJS_CAIRO_CONTEXT_GET_PRIV_CR_CHECKED(context, argc, vp, argv, obj); cairo_path_t *path; if (!gjs_parse_call_args(context, "", argv, "")) return false; path = cairo_copy_path_flat(cr); JSObject* retval = CairoPath::take_c_ptr(context, path); if (!retval) return false; argv.rval().setObject(*retval); return true; } GJS_JSAPI_RETURN_CONVENTION static bool mask_func(JSContext *context, unsigned argc, JS::Value *vp) { _GJS_CAIRO_CONTEXT_GET_PRIV_CR_CHECKED(context, argc, vp, argv, obj); JS::RootedObject pattern_wrapper(context); if (!gjs_parse_call_args(context, "mask", argv, "o", "pattern", &pattern_wrapper)) return false; cairo_pattern_t* pattern = CairoPattern::for_js(context, pattern_wrapper); if (!pattern) return false; cairo_mask(cr, pattern); if (!gjs_cairo_check_status(context, cairo_status(cr), "context")) return false; argv.rval().setUndefined(); return true; } GJS_JSAPI_RETURN_CONVENTION static bool maskSurface_func(JSContext *context, unsigned argc, JS::Value *vp) { _GJS_CAIRO_CONTEXT_GET_PRIV_CR_CHECKED(context, argc, vp, argv, obj); JS::RootedObject surface_wrapper(context); double x, y; if (!gjs_parse_call_args(context, "maskSurface", argv, "off", "surface", &surface_wrapper, "x", &x, "y", &y)) return false; cairo_surface_t* surface = CairoSurface::for_js(context, surface_wrapper); if (!surface) return false; cairo_mask_surface(cr, surface, x, y); if (!gjs_cairo_check_status(context, cairo_status(cr), "context")) return false; argv.rval().setUndefined(); return true; } GJS_JSAPI_RETURN_CONVENTION static bool setDash_func(JSContext *context, unsigned argc, JS::Value *vp) { _GJS_CAIRO_CONTEXT_GET_PRIV_CR_CHECKED(context, argc, vp, argv, obj); guint i; JS::RootedObject dashes(context); double offset; guint len; bool is_array; if (!gjs_parse_call_args(context, "setDash", argv, "of", "dashes", &dashes, "offset", &offset)) return false; if (!JS::IsArrayObject(context, dashes, &is_array)) return false; if (!is_array) { gjs_throw(context, "dashes must be an array"); return false; } if (!JS::GetArrayLength(context, dashes, &len)) { gjs_throw(context, "Can't get length of dashes"); return false; } std::unique_ptr dashes_c = std::make_unique(len); size_t dashes_c_size = 0; JS::RootedValue elem(context); for (i = 0; i < len; ++i) { double b; elem.setUndefined(); if (!JS_GetElement(context, dashes, i, &elem)) { return false; } if (elem.isUndefined()) continue; if (!JS::ToNumber(context, elem, &b)) return false; if (b <= 0) { gjs_throw(context, "Dash value must be positive"); return false; } dashes_c[dashes_c_size++] = b; } cairo_set_dash(cr, dashes_c.get(), dashes_c_size, offset); argv.rval().setUndefined(); return true; } GJS_JSAPI_RETURN_CONVENTION static bool setSource_func(JSContext *context, unsigned argc, JS::Value *vp) { _GJS_CAIRO_CONTEXT_GET_PRIV_CR_CHECKED(context, argc, vp, argv, obj); JS::RootedObject pattern_wrapper(context); if (!gjs_parse_call_args(context, "setSource", argv, "o", "pattern", &pattern_wrapper)) return false; cairo_pattern_t* pattern = CairoPattern::for_js(context, pattern_wrapper); if (!pattern) return false; cairo_set_source(cr, pattern); if (!gjs_cairo_check_status(context, cairo_status(cr), "context")) return false; argv.rval().setUndefined(); return true; } GJS_JSAPI_RETURN_CONVENTION static bool setSourceSurface_func(JSContext *context, unsigned argc, JS::Value *vp) { _GJS_CAIRO_CONTEXT_GET_PRIV_CR_CHECKED(context, argc, vp, argv, obj); JS::RootedObject surface_wrapper(context); double x, y; if (!gjs_parse_call_args(context, "setSourceSurface", argv, "off", "surface", &surface_wrapper, "x", &x, "y", &y)) return false; cairo_surface_t* surface = CairoSurface::for_js(context, surface_wrapper); if (!surface) return false; cairo_set_source_surface(cr, surface, x, y); if (!gjs_cairo_check_status(context, cairo_status(cr), "context")) return false; argv.rval().setUndefined(); return true; } GJS_JSAPI_RETURN_CONVENTION static bool showText_func(JSContext *context, unsigned argc, JS::Value *vp) { _GJS_CAIRO_CONTEXT_GET_PRIV_CR_CHECKED(context, argc, vp, argv, obj); JS::UniqueChars utf8; if (!gjs_parse_call_args(context, "showText", argv, "s", "utf8", &utf8)) return false; cairo_show_text(cr, utf8.get()); if (!gjs_cairo_check_status(context, cairo_status(cr), "context")) return false; argv.rval().setUndefined(); return true; } GJS_JSAPI_RETURN_CONVENTION static bool selectFontFace_func(JSContext *context, unsigned argc, JS::Value *vp) { _GJS_CAIRO_CONTEXT_GET_PRIV_CR_CHECKED(context, argc, vp, argv, obj); JS::UniqueChars family; cairo_font_slant_t slant; cairo_font_weight_t weight; if (!gjs_parse_call_args(context, "selectFontFace", argv, "sii", "family", &family, "slang", &slant, "weight", &weight)) return false; cairo_select_font_face(cr, family.get(), slant, weight); if (!gjs_cairo_check_status(context, cairo_status(cr), "context")) return false; argv.rval().setUndefined(); return true; } GJS_JSAPI_RETURN_CONVENTION static bool popGroup_func(JSContext *context, unsigned argc, JS::Value *vp) { _GJS_CAIRO_CONTEXT_GET_PRIV_CR_CHECKED(context, argc, vp, rec, obj); cairo_pattern_t *pattern; JSObject *pattern_wrapper; if (argc > 0) { gjs_throw(context, "Context.popGroup() takes no arguments"); return false; } pattern = cairo_pop_group(cr); if (!gjs_cairo_check_status(context, cairo_status(cr), "context")) return false; pattern_wrapper = gjs_cairo_pattern_from_pattern(context, pattern); cairo_pattern_destroy(pattern); if (!pattern_wrapper) { gjs_throw(context, "failed to create pattern"); return false; } rec.rval().setObject(*pattern_wrapper); return true; } GJS_JSAPI_RETURN_CONVENTION static bool getSource_func(JSContext *context, unsigned argc, JS::Value *vp) { _GJS_CAIRO_CONTEXT_GET_PRIV_CR_CHECKED(context, argc, vp, rec, obj); cairo_pattern_t *pattern; JSObject *pattern_wrapper; if (argc > 0) { gjs_throw(context, "Context.getSource() takes no arguments"); return false; } pattern = cairo_get_source(cr); if (!gjs_cairo_check_status(context, cairo_status(cr), "context")) return false; /* pattern belongs to the context, so keep the reference */ pattern_wrapper = gjs_cairo_pattern_from_pattern(context, pattern); if (!pattern_wrapper) { gjs_throw(context, "failed to create pattern"); return false; } rec.rval().setObject(*pattern_wrapper); return true; } GJS_JSAPI_RETURN_CONVENTION static bool getTarget_func(JSContext *context, unsigned argc, JS::Value *vp) { _GJS_CAIRO_CONTEXT_GET_PRIV_CR_CHECKED(context, argc, vp, rec, obj); cairo_surface_t *surface; if (argc > 0) { gjs_throw(context, "Context.getTarget() takes no arguments"); return false; } surface = cairo_get_target(cr); if (!gjs_cairo_check_status(context, cairo_status(cr), "context")) return false; /* surface belongs to the context, so keep the reference */ JSObject* surface_wrapper = CairoSurface::from_c_ptr(context, surface); if (!surface_wrapper) { /* exception already set */ return false; } rec.rval().setObject(*surface_wrapper); return true; } GJS_JSAPI_RETURN_CONVENTION static bool getGroupTarget_func(JSContext *context, unsigned argc, JS::Value *vp) { _GJS_CAIRO_CONTEXT_GET_PRIV_CR_CHECKED(context, argc, vp, rec, obj); cairo_surface_t *surface; if (argc > 0) { gjs_throw(context, "Context.getGroupTarget() takes no arguments"); return false; } surface = cairo_get_group_target(cr); if (!gjs_cairo_check_status(context, cairo_status(cr), "context")) return false; /* surface belongs to the context, so keep the reference */ JSObject* surface_wrapper = CairoSurface::from_c_ptr(context, surface); if (!surface_wrapper) { /* exception already set */ return false; } rec.rval().setObject(*surface_wrapper); return true; } GJS_JSAPI_RETURN_CONVENTION static bool textExtents_func(JSContext* cx, unsigned argc, JS::Value* vp) { _GJS_CAIRO_CONTEXT_GET_PRIV_CR_CHECKED(cx, argc, vp, args, this_obj); JS::UniqueChars utf8; if (!gjs_parse_call_args(cx, "textExtents", args, "s", "utf8", &utf8)) return false; cairo_text_extents_t extents; cairo_text_extents(cr, utf8.get(), &extents); if (!gjs_cairo_check_status(cx, cairo_status(cr), "context")) return false; JS::RootedObject extents_obj(cx, JS_NewPlainObject(cx)); if (!extents_obj) return false; JSPropertySpec properties[] = { JS_DOUBLE_PS("xBearing", extents.x_bearing, JSPROP_ENUMERATE), JS_DOUBLE_PS("yBearing", extents.y_bearing, JSPROP_ENUMERATE), JS_DOUBLE_PS("width", extents.width, JSPROP_ENUMERATE), JS_DOUBLE_PS("height", extents.height, JSPROP_ENUMERATE), JS_DOUBLE_PS("xAdvance", extents.x_advance, JSPROP_ENUMERATE), JS_DOUBLE_PS("yAdvance", extents.y_advance, JSPROP_ENUMERATE), JS_PS_END}; if (!JS_DefineProperties(cx, extents_obj, properties)) return false; args.rval().setObject(*extents_obj); return true; } // clang-format off const JSFunctionSpec CairoContext::proto_funcs[] = { JS_FN("$dispose", &CairoContext::dispose, 0, 0), JS_FN("appendPath", appendPath_func, 0, 0), JS_FN("arc", arc_func, 0, 0), JS_FN("arcNegative", arcNegative_func, 0, 0), JS_FN("clip", clip_func, 0, 0), JS_FN("clipExtents", clipExtents_func, 0, 0), JS_FN("clipPreserve", clipPreserve_func, 0, 0), JS_FN("closePath", closePath_func, 0, 0), JS_FN("copyPage", copyPage_func, 0, 0), JS_FN("copyPath", copyPath_func, 0, 0), JS_FN("copyPathFlat", copyPathFlat_func, 0, 0), JS_FN("curveTo", curveTo_func, 0, 0), JS_FN("deviceToUser", deviceToUser_func, 0, 0), JS_FN("deviceToUserDistance", deviceToUserDistance_func, 0, 0), JS_FN("fill", fill_func, 0, 0), JS_FN("fillPreserve", fillPreserve_func, 0, 0), JS_FN("fillExtents", fillExtents_func, 0, 0), // fontExtents JS_FN("getAntialias", getAntialias_func, 0, 0), JS_FN("getCurrentPoint", getCurrentPoint_func, 0, 0), // getDash JS_FN("getDashCount", getDashCount_func, 0, 0), JS_FN("getFillRule", getFillRule_func, 0, 0), // getFontFace // getFontMatrix // getFontOptions JS_FN("getGroupTarget", getGroupTarget_func, 0, 0), JS_FN("getLineCap", getLineCap_func, 0, 0), JS_FN("getLineJoin", getLineJoin_func, 0, 0), JS_FN("getLineWidth", getLineWidth_func, 0, 0), // getMatrix JS_FN("getMiterLimit", getMiterLimit_func, 0, 0), JS_FN("getOperator", getOperator_func, 0, 0), // getScaledFont JS_FN("getSource", getSource_func, 0, 0), JS_FN("getTarget", getTarget_func, 0, 0), JS_FN("getTolerance", getTolerance_func, 0, 0), // glyphPath // glyphExtents JS_FN("hasCurrentPoint", hasCurrentPoint_func, 0, 0), JS_FN("identityMatrix", identityMatrix_func, 0, 0), JS_FN("inFill", inFill_func, 0, 0), JS_FN("inStroke", inStroke_func, 0, 0), JS_FN("lineTo", lineTo_func, 0, 0), JS_FN("mask", mask_func, 0, 0), JS_FN("maskSurface", maskSurface_func, 0, 0), JS_FN("moveTo", moveTo_func, 0, 0), JS_FN("newPath", newPath_func, 0, 0), JS_FN("newSubPath", newSubPath_func, 0, 0), JS_FN("paint", paint_func, 0, 0), JS_FN("paintWithAlpha", paintWithAlpha_func, 0, 0), JS_FN("pathExtents", pathExtents_func, 0, 0), JS_FN("popGroup", popGroup_func, 0, 0), JS_FN("popGroupToSource", popGroupToSource_func, 0, 0), JS_FN("pushGroup", pushGroup_func, 0, 0), JS_FN("pushGroupWithContent", pushGroupWithContent_func, 0, 0), JS_FN("rectangle", rectangle_func, 0, 0), JS_FN("relCurveTo", relCurveTo_func, 0, 0), JS_FN("relLineTo", relLineTo_func, 0, 0), JS_FN("relMoveTo", relMoveTo_func, 0, 0), JS_FN("resetClip", resetClip_func, 0, 0), JS_FN("restore", restore_func, 0, 0), JS_FN("rotate", rotate_func, 0, 0), JS_FN("save", save_func, 0, 0), JS_FN("scale", scale_func, 0, 0), JS_FN("selectFontFace", selectFontFace_func, 0, 0), JS_FN("setAntialias", setAntialias_func, 0, 0), JS_FN("setDash", setDash_func, 0, 0), // setFontFace // setFontMatrix // setFontOptions JS_FN("setFontSize", setFontSize_func, 0, 0), JS_FN("setFillRule", setFillRule_func, 0, 0), JS_FN("setLineCap", setLineCap_func, 0, 0), JS_FN("setLineJoin", setLineJoin_func, 0, 0), JS_FN("setLineWidth", setLineWidth_func, 0, 0), // setMatrix JS_FN("setMiterLimit", setMiterLimit_func, 0, 0), JS_FN("setOperator", setOperator_func, 0, 0), // setScaledFont JS_FN("setSource", setSource_func, 0, 0), JS_FN("setSourceRGB", setSourceRGB_func, 0, 0), JS_FN("setSourceRGBA", setSourceRGBA_func, 0, 0), JS_FN("setSourceSurface", setSourceSurface_func, 0, 0), JS_FN("setTolerance", setTolerance_func, 0, 0), // showGlyphs JS_FN("showPage", showPage_func, 0, 0), JS_FN("showText", showText_func, 0, 0), // showTextGlyphs JS_FN("stroke", stroke_func, 0, 0), JS_FN("strokeExtents", strokeExtents_func, 0, 0), JS_FN("strokePreserve", strokePreserve_func, 0, 0), // textPath JS_FN("textExtents", textExtents_func, 1, 0), // transform JS_FN("translate", translate_func, 0, 0), JS_FN("userToDevice", userToDevice_func, 0, 0), JS_FN("userToDeviceDistance", userToDeviceDistance_func, 0, 0), JS_FS_END}; // clang-format on [[nodiscard]] static bool context_to_gi_argument( JSContext* context, JS::Value value, const char* arg_name, GjsArgumentType argument_type, GITransfer transfer, GjsArgumentFlags flags, GIArgument* arg) { if (value.isNull()) { if (!(flags & GjsArgumentFlags::MAY_BE_NULL)) { GjsAutoChar display_name = gjs_argument_display_name(arg_name, argument_type); gjs_throw(context, "%s may not be null", display_name.get()); return false; } gjs_arg_unset(arg); return true; } JS::RootedObject obj(context, &value.toObject()); cairo_t* cr = CairoContext::for_js(context, obj); if (!cr) return false; if (transfer == GI_TRANSFER_EVERYTHING) cairo_reference(cr); gjs_arg_set(arg, cr); return true; } GJS_JSAPI_RETURN_CONVENTION static bool context_from_gi_argument(JSContext* context, JS::MutableHandleValue value_p, GIArgument* arg) { JSObject* obj = CairoContext::from_c_ptr( context, static_cast(arg->v_pointer)); if (!obj) { gjs_throw(context, "Could not create Cairo context"); return false; } value_p.setObject(*obj); return true; } static bool context_release_argument(JSContext*, GITransfer transfer, GIArgument* arg) { if (transfer != GI_TRANSFER_NOTHING) cairo_destroy(gjs_arg_get(arg)); return true; } void gjs_cairo_context_init(void) { static GjsForeignInfo foreign_info = {context_to_gi_argument, context_from_gi_argument, context_release_argument}; gjs_struct_foreign_register("cairo", "Context", &foreign_info); } cjs-128.0/modules/print.h0000664000175000017500000000070214771557763014231 0ustar fabiofabio/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2020 Evan Welsh #ifndef MODULES_PRINT_H_ #define MODULES_PRINT_H_ #include #include #include "cjs/macros.h" GJS_JSAPI_RETURN_CONVENTION bool gjs_define_print_stuff(JSContext* context, JS::MutableHandleObject module); #endif // MODULES_PRINT_H_ cjs-128.0/modules/core/0000775000175000017500000000000014771557763013655 5ustar fabiofabiocjs-128.0/modules/core/_cairo.js0000664000175000017500000000357614771557763015462 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2010 litl, LLC. /* exported Antialias, Content, Extend, FillRule, Filter, FontSlant, FontWeight, Format, LineCap, LineJoin, Operator, PatternType, SurfaceType */ var Antialias = { DEFAULT: 0, NONE: 1, GRAY: 2, SUBPIXEL: 3, }; var Content = { COLOR: 0x1000, ALPHA: 0x2000, COLOR_ALPHA: 0x3000, }; var Extend = { NONE: 0, REPEAT: 1, REFLECT: 2, PAD: 3, }; var FillRule = { WINDING: 0, EVEN_ODD: 1, }; var Filter = { FAST: 0, GOOD: 1, BEST: 2, NEAREST: 3, BILINEAR: 4, GAUSSIAN: 5, }; var FontSlant = { NORMAL: 0, ITALIC: 1, OBLIQUE: 2, }; var FontWeight = { NORMAL: 0, BOLD: 1, }; var Format = { ARGB32: 0, RGB24: 1, A8: 2, A1: 3, RGB16_565: 4, }; var LineCap = { BUTT: 0, ROUND: 1, SQUARE: 2, /** @deprecated Historical typo of {@link LineCap.Square}, kept for compatibility reasons */ SQUASH: 2, }; var LineJoin = { MITER: 0, ROUND: 1, BEVEL: 2, }; var Operator = { CLEAR: 0, SOURCE: 1, OVER: 2, IN: 3, OUT: 4, ATOP: 5, DEST: 6, DEST_OVER: 7, DEST_IN: 8, DEST_OUT: 9, DEST_ATOP: 10, XOR: 11, ADD: 12, SATURATE: 13, MULTIPLY: 14, SCREEN: 15, OVERLAY: 16, DARKEN: 17, LIGHTEN: 18, COLOR_DODGE: 19, COLOR_BURN: 20, HARD_LIGHT: 21, SOFT_LIGHT: 22, DIFFERENCE: 23, EXCLUSION: 24, HSL_HUE: 25, HSL_SATURATION: 26, HSL_COLOR: 27, HSL_LUMINOSITY: 28, }; var PatternType = { SOLID: 0, SURFACE: 1, LINEAR: 2, RADIAL: 3, }; var SurfaceType = { IMAGE: 0, PDF: 1, PS: 2, XLIB: 3, XCB: 4, GLITZ: 5, QUARTZ: 6, WIN32: 7, BEOS: 8, DIRECTFB: 9, SVG: 10, OS2: 11, WIN32_PRINTING: 12, QUARTZ_IMAGE: 13, }; cjs-128.0/modules/core/_format.js0000664000175000017500000000470214771557763015645 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2012 Red Hat, Inc. // SPDX-FileCopyrightText: 2012 Giovanni Campagna /* exported vprintf */ const CjsPrivate = imports.gi.CjsPrivate; function vprintf(string, args) { let i = 0; let usePos = false; return string.replace(/%(?:([1-9][0-9]*)\$)?(I+)?([0-9]+)?(?:\.([0-9]+))?(.)/g, function (str, posGroup, flagsGroup, widthGroup, precisionGroup, genericGroup) { if (precisionGroup !== '' && precisionGroup !== undefined && genericGroup !== 'f') throw new Error("Precision can only be specified for 'f'"); let hasAlternativeIntFlag = flagsGroup && flagsGroup.indexOf('I') !== -1; if (hasAlternativeIntFlag && genericGroup !== 'd') throw new Error("Alternative output digits can only be specified for 'd'"); let pos = parseInt(posGroup, 10) || 0; if (!usePos && i === 0) usePos = pos > 0; if (usePos && pos === 0 || !usePos && pos > 0) throw new Error('Numbered and unnumbered conversion specifications cannot be mixed'); let fillChar = widthGroup && widthGroup[0] === '0' ? '0' : ' '; let width = parseInt(widthGroup, 10) || 0; function fillWidth(s, c, w) { let fill = c.repeat(w); return fill.substr(s.length) + s; } function getArg() { return usePos ? args[pos - 1] : args[i++]; } let s = ''; switch (genericGroup) { case '%': return '%'; case 's': s = String(getArg()); break; case 'd': { let intV = parseInt(getArg()); if (hasAlternativeIntFlag) s = CjsPrivate.format_int_alternative_output(intV); else s = intV.toString(); break; } case 'x': s = parseInt(getArg()).toString(16); break; case 'f': if (precisionGroup === '' || precisionGroup === undefined) s = parseFloat(getArg()).toString(); else s = parseFloat(getArg()).toFixed(parseInt(precisionGroup)); break; default: throw new Error(`Unsupported conversion character %${genericGroup}`); } return fillWidth(s, fillChar, width); }); } cjs-128.0/modules/core/_common.js0000664000175000017500000001074114771557763015645 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2020 Philip Chimento /* exported _checkAccessors, _createBuilderConnectFunc, _createClosure, _registerType */ // This is a helper module in which to put code that is common between the // legacy GObject.Class system and the new GObject.registerClass system. var _registerType = Symbol('GObject register type hook'); function _generateAccessors(pspec, propdesc, GObject) { const {name, flags} = pspec; const readable = flags & GObject.ParamFlags.READABLE; const writable = flags & GObject.ParamFlags.WRITABLE; if (!propdesc) { propdesc = { configurable: true, enumerable: true, }; } if (readable && writable) { if (!propdesc.get && !propdesc.set) { const privateName = Symbol(`__autogeneratedAccessor__${name}`); const defaultValue = pspec.get_default_value(); propdesc.get = function () { if (!(privateName in this)) this[privateName] = defaultValue; return this[privateName]; }; propdesc.set = function (value) { if (value !== this[privateName]) { this[privateName] = value; this.notify(name); } }; } else if (!propdesc.get) { propdesc.get = function () { throw new Error(`setter defined without getter for property ${name}`); }; } else if (!propdesc.set) { propdesc.set = function () { throw new Error(`getter defined without setter for property ${name}`); }; } } else if (readable && !propdesc.get) { propdesc.get = function () { throw new Error(`missing getter for read-only property ${name}`); }; } else if (writable && !propdesc.set) { propdesc.set = function () { throw new Error(`missing setter for write-only property ${name}`); }; } return propdesc; } function _checkAccessors(proto, pspec, GObject) { const {name, flags} = pspec; if (flags & GObject.ParamFlags.CONSTRUCT_ONLY) return; const underscoreName = name.replace(/-/g, '_'); const camelName = name.replace(/-([a-z])/g, match => match[1].toUpperCase()); let propdesc = Object.getOwnPropertyDescriptor(proto, name); let dashPropdesc = propdesc, underscorePropdesc, camelPropdesc; const nameIsCompound = name.includes('-'); if (nameIsCompound) { underscorePropdesc = Object.getOwnPropertyDescriptor(proto, underscoreName); camelPropdesc = Object.getOwnPropertyDescriptor(proto, camelName); if (!propdesc) propdesc = underscorePropdesc; if (!propdesc) propdesc = camelPropdesc; } const readable = flags & GObject.ParamFlags.READABLE; const writable = flags & GObject.ParamFlags.WRITABLE; if (!propdesc || (readable && !propdesc.get) || (writable && !propdesc.set)) propdesc = _generateAccessors(pspec, propdesc, GObject); if (!dashPropdesc) Object.defineProperty(proto, name, propdesc); if (nameIsCompound) { if (!underscorePropdesc) Object.defineProperty(proto, underscoreName, propdesc); if (!camelPropdesc) Object.defineProperty(proto, camelName, propdesc); } } function _createClosure(builder, thisArg, handlerName, swapped, connectObject) { connectObject ??= thisArg; if (swapped) { throw new Error('Unsupported template signal flag "swapped"'); } else if (typeof thisArg[handlerName] === 'undefined') { throw new Error(`A handler called ${handlerName} was not ` + `defined on ${thisArg}`); } return thisArg[handlerName].bind(connectObject); } function _createBuilderConnectFunc(klass) { const {GObject} = imports.gi; return function (builder, obj, signalName, handlerName, connectObj, flags) { const objects = builder.get_objects(); const thisObj = objects.find(o => o instanceof klass); const swapped = flags & GObject.ConnectFlags.SWAPPED; const closure = _createClosure(builder, thisObj, handlerName, swapped, connectObj); if (flags & GObject.ConnectFlags.AFTER) obj.connect_after(signalName, closure); else obj.connect(signalName, closure); }; } cjs-128.0/modules/core/.eslintrc.yml0000664000175000017500000000023614771557763016302 0ustar fabiofabio--- # SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2020 Evan Welsh rules: jsdoc/require-jsdoc: 'off' cjs-128.0/modules/core/_gettext.js0000664000175000017500000000424614771557763016044 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2009 Red Hat, Inc. /* exported bindtextdomain, dcgettext, dgettext, dngettext, domain, dpgettext, gettext, LocaleCategory, ngettext, pgettext, setlocale, textdomain */ /** * This module provides a convenience layer for the "gettext" family of functions, * relying on GLib for the actual implementation. * * Usage: * * const Gettext = imports.gettext; * * Gettext.textdomain("myapp"); * Gettext.bindtextdomain("myapp", "/usr/share/locale"); * * let translated = Gettext.gettext("Hello world!"); */ const GLib = imports.gi.GLib; const CjsPrivate = imports.gi.CjsPrivate; var LocaleCategory = CjsPrivate.LocaleCategory; function setlocale(category, locale) { return CjsPrivate.setlocale(category, locale); } function textdomain(dom) { return CjsPrivate.textdomain(dom); } function bindtextdomain(dom, location) { return CjsPrivate.bindtextdomain(dom, location); } function gettext(msgid) { return GLib.dgettext(null, msgid); } function dgettext(dom, msgid) { return GLib.dgettext(dom, msgid); } function dcgettext(dom, msgid, category) { return GLib.dcgettext(dom, msgid, category); } function ngettext(msgid1, msgid2, n) { return GLib.dngettext(null, msgid1, msgid2, n); } function dngettext(dom, msgid1, msgid2, n) { return GLib.dngettext(dom, msgid1, msgid2, n); } // FIXME: missing dcngettext ? function pgettext(context, msgid) { return GLib.dpgettext2(null, context, msgid); } function dpgettext(dom, context, msgid) { return GLib.dpgettext2(dom, context, msgid); } /** * Create an object with bindings for gettext, ngettext, * and pgettext bound to a particular translation domain. * * @param {string} domainName Translation domain string * @returns {object} an object with gettext bindings */ function domain(domainName) { return { gettext(msgid) { return GLib.dgettext(domainName, msgid); }, ngettext(msgid1, msgid2, n) { return GLib.dngettext(domainName, msgid1, msgid2, n); }, pgettext(context, msgid) { return GLib.dpgettext2(domainName, context, msgid); }, }; } cjs-128.0/modules/core/_signals.js0000664000175000017500000001226114771557763016014 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2008 litl, LLC // SPDX-FileCopyrightText: 2022 Canonical Ltd. // SPDX-FileContributor: Marco Trevisan /* exported addSignalMethods */ // A couple principals of this simple signal system: // 1) should look just like our GObject signal binding // 2) memory and safety matter more than speed of connect/disconnect/emit // 3) the expectation is that a given object will have a very small number of // connections, but they may be to different signal names function _connectFull(name, callback, after) { // be paranoid about callback arg since we'd start to throw from emit() // if it was messed up if (typeof callback !== 'function') throw new Error('When connecting signal must give a callback that is a function'); // we instantiate the "signal machinery" only on-demand if anything // gets connected. if (this._signalConnections === undefined) { this._signalConnections = Object.create(null); this._signalConnectionsByName = Object.create(null); this._nextConnectionId = 1; } const id = this._nextConnectionId; this._nextConnectionId += 1; this._signalConnections[id] = { name, callback, after, }; const connectionsByName = this._signalConnectionsByName[name] ?? []; if (!connectionsByName.length) this._signalConnectionsByName[name] = connectionsByName; connectionsByName.push(id); return id; } function _connect(name, callback) { return _connectFull.call(this, name, callback, false); } function _connectAfter(name, callback) { return _connectFull.call(this, name, callback, true); } function _disconnect(id) { const connection = this._signalConnections?.[id]; if (!connection) throw new Error(`No signal connection ${id} found`); if (connection.disconnected) throw new Error(`Signal handler id ${id} already disconnected`); connection.disconnected = true; delete this._signalConnections[id]; const ids = this._signalConnectionsByName[connection.name]; if (!ids) return; const indexOfId = ids.indexOf(id); if (indexOfId !== -1) ids.splice(indexOfId, 1); if (ids.length === 0) delete this._signalConnectionsByName[connection.name]; } function _signalHandlerIsConnected(id) { const connection = this._signalConnections?.[id]; return !!connection && !connection.disconnected; } function _disconnectAll() { Object.values(this._signalConnections ?? {}).forEach(c => (c.disconnected = true)); delete this._signalConnections; delete this._signalConnectionsByName; } function _emit(name, ...args) { const connections = this._signalConnectionsByName?.[name]; // may not be any signal handlers at all, if not then return if (!connections) return; // To deal with re-entrancy (removal/addition while // emitting), we copy out a list of what was connected // at emission start; and just before invoking each // handler we check its disconnected flag. const handlers = connections.map(id => this._signalConnections[id]); // create arg array which is emitter + everything passed in except // signal name. Would be more convenient not to pass emitter to // the callback, but trying to be 100% consistent with GObject // which does pass it in. Also if we pass in the emitter here, // people don't create closures with the emitter in them, // which would be a cycle. const argArray = [this, ...args]; const afterHandlers = []; const beforeHandlers = handlers.filter(c => { if (!c.after) return true; afterHandlers.push(c); return false; }); if (!_callHandlers(beforeHandlers, argArray)) _callHandlers(afterHandlers, argArray); } function _callHandlers(handlers, argArray) { for (const handler of handlers) { if (handler.disconnected) continue; try { // since we pass "null" for this, the global object will be used. const ret = handler.callback.apply(null, argArray); // if the callback returns true, we don't call the next // signal handlers if (ret === true) return true; } catch (e) { // just log any exceptions so that callbacks can't disrupt // signal emission logError(e, `Exception in callback for signal: ${handler.name}`); } } return false; } function _addSignalMethod(proto, functionName, func) { if (proto[functionName] && proto[functionName] !== func) log(`WARNING: addSignalMethods is replacing existing ${proto} ${functionName} method`); proto[functionName] = func; } function addSignalMethods(proto) { _addSignalMethod(proto, 'connect', _connect); _addSignalMethod(proto, 'connectAfter', _connectAfter); _addSignalMethod(proto, 'disconnect', _disconnect); _addSignalMethod(proto, 'emit', _emit); _addSignalMethod(proto, 'signalHandlerIsConnected', _signalHandlerIsConnected); // this one is not in GObject, but useful _addSignalMethod(proto, 'disconnectAll', _disconnectAll); } cjs-128.0/modules/core/overrides/0000775000175000017500000000000014771557763015657 5ustar fabiofabiocjs-128.0/modules/core/overrides/Gtk.js0000664000175000017500000001200114771557763016734 0ustar fabiofabio// application/javascript;version=1.8 // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2013 Giovanni Campagna const Legacy = imports._legacy; const {Gio, CjsPrivate, GLib, GObject} = imports.gi; const {_createBuilderConnectFunc, _createClosure, _registerType} = imports._common; const Gi = imports._gi; let Gtk; let BuilderScope; function _init() { Gtk = this; Gtk.children = GObject.__gtkChildren__; Gtk.cssName = GObject.__gtkCssName__; Gtk.internalChildren = GObject.__gtkInternalChildren__; Gtk.template = GObject.__gtkTemplate__; let {GtkWidgetClass} = Legacy.defineGtkLegacyObjects(GObject, Gtk); Gtk.Widget.prototype.__metaclass__ = GtkWidgetClass; if (Gtk.Container && Gtk.Container.prototype.child_set_property) { Gtk.Container.prototype.child_set_property = function (child, property, value) { CjsPrivate.gtk_container_child_set_property(this, child, property, value); }; } if (Gtk.CustomSorter) { Gtk.CustomSorter.new = CjsPrivate.gtk_custom_sorter_new; Gtk.CustomSorter.prototype.set_sort_func = function (sortFunc) { CjsPrivate.gtk_custom_sorter_set_sort_func(this, sortFunc); }; } Gtk.Widget.prototype._init = function (params) { const klass = this.constructor; const wrapper = GObject.Object.prototype._init.call(this, params) ?? this; if (klass[Gtk.template]) { let children = klass[Gtk.children] ?? []; for (let child of children) { wrapper[child.replace(/-/g, '_')] = wrapper.get_template_child(klass, child); } let internalChildren = klass[Gtk.internalChildren] ?? []; for (let child of internalChildren) { wrapper[`_${child.replace(/-/g, '_')}`] = wrapper.get_template_child(klass, child); } } return wrapper; }; Gtk.Widget._classInit = function (klass) { return GObject.Object._classInit(klass); }; Object.defineProperty(Gtk.Widget, _registerType, { value: _registerWidgetType, writable: false, configurable: false, enumerable: false, }); if (Gtk.Widget.prototype.get_first_child) { Gtk.Widget.prototype[Symbol.iterator] = function* () { for (let c = this.get_first_child(); c; c = c.get_next_sibling()) yield c; }; } if (Gtk.BuilderScope) { BuilderScope = GObject.registerClass({ Implements: [Gtk.BuilderScope], }, class extends GObject.Object { vfunc_create_closure(builder, handlerName, flags, connectObject) { const swapped = flags & Gtk.BuilderClosureFlags.SWAPPED; const thisArg = builder.get_current_object(); return Gi.associateClosure( connectObject ?? thisArg, _createClosure(builder, thisArg, handlerName, swapped, connectObject) ); } }); } } function _registerWidgetType(klass) { const template = klass[Gtk.template]; const cssName = klass[Gtk.cssName]; const children = klass[Gtk.children]; const internalChildren = klass[Gtk.internalChildren]; if (template) { klass.prototype._instance_init = function () { this.init_template(); }; } GObject.Object[_registerType](klass); if (cssName) Gtk.Widget.set_css_name.call(klass, cssName); if (template) { if (typeof template === 'string') { try { const uri = GLib.Uri.parse(template, GLib.UriFlags.NONE); const scheme = uri.get_scheme(); if (scheme === 'resource') { Gtk.Widget.set_template_from_resource.call(klass, uri.get_path()); } else if (scheme === 'file') { const file = Gio.File.new_for_uri(template); const [, contents] = file.load_contents(null); Gtk.Widget.set_template.call(klass, contents); } else { throw new TypeError(`Invalid template URI: ${template}`); } } catch (err) { if (!(err instanceof GLib.UriError)) throw err; const contents = new TextEncoder().encode(template); Gtk.Widget.set_template.call(klass, contents); } } else { Gtk.Widget.set_template.call(klass, template); } if (BuilderScope) Gtk.Widget.set_template_scope.call(klass, new BuilderScope()); else Gtk.Widget.set_connect_func.call(klass, _createBuilderConnectFunc(klass)); } if (children) { children.forEach(child => Gtk.Widget.bind_template_child_full.call(klass, child, false, 0)); } if (internalChildren) { internalChildren.forEach(child => Gtk.Widget.bind_template_child_full.call(klass, child, true, 0)); } } cjs-128.0/modules/core/overrides/.eslintrc.yml0000664000175000017500000000031114771557763020276 0ustar fabiofabio--- # SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2018 Philip Chimento rules: no-unused-vars: - error - varsIgnorePattern: ^_init$ cjs-128.0/modules/core/overrides/GLib.js0000664000175000017500000005162314771557763017041 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2011 Giovanni Campagna // SPDX-FileCopyrightText: 2023 Philip Chimento const {setMainLoopHook} = imports._promiseNative; let GLib; const SIMPLE_TYPES = ['b', 'y', 'n', 'q', 'i', 'u', 'x', 't', 'h', 'd', 's', 'o', 'g']; function _readSingleType(signature, forceSimple) { let char = signature.shift(); let isSimple = false; if (!SIMPLE_TYPES.includes(char)) { if (forceSimple) throw new TypeError('Invalid GVariant signature (a simple type was expected)'); } else { isSimple = true; } if (char === 'm' || char === 'a') return [char].concat(_readSingleType(signature, false)); if (char === '{') { let key = _readSingleType(signature, true); let val = _readSingleType(signature, false); let close = signature.shift(); if (close !== '}') throw new TypeError('Invalid GVariant signature for type DICT_ENTRY (expected "}"'); return [char].concat(key, val, close); } if (char === '(') { let res = [char]; while (true) { if (signature.length === 0) throw new TypeError('Invalid GVariant signature for type TUPLE (expected ")")'); let next = signature[0]; if (next === ')') { signature.shift(); return res.concat(next); } let el = _readSingleType(signature); res = res.concat(el); } } // Valid types are simple types, arrays, maybes, tuples, dictionary entries and variants if (!isSimple && char !== 'v') throw new TypeError(`Invalid GVariant signature (${char} is not a valid type)`); return [char]; } function _packVariant(signature, value) { if (signature.length === 0) throw new TypeError('GVariant signature cannot be empty'); let char = signature.shift(); switch (char) { case 'b': return GLib.Variant.new_boolean(value); case 'y': return GLib.Variant.new_byte(value); case 'n': return GLib.Variant.new_int16(value); case 'q': return GLib.Variant.new_uint16(value); case 'i': return GLib.Variant.new_int32(value); case 'u': return GLib.Variant.new_uint32(value); case 'x': return GLib.Variant.new_int64(value); case 't': return GLib.Variant.new_uint64(value); case 'h': return GLib.Variant.new_handle(value); case 'd': return GLib.Variant.new_double(value); case 's': return GLib.Variant.new_string(value); case 'o': return GLib.Variant.new_object_path(value); case 'g': return GLib.Variant.new_signature(value); case 'v': return GLib.Variant.new_variant(value); case 'm': if (value !== null) { return GLib.Variant.new_maybe(null, _packVariant(signature, value)); } else { return GLib.Variant.new_maybe(new GLib.VariantType( _readSingleType(signature, false).join('')), null); } case 'a': { let arrayType = _readSingleType(signature, false); if (arrayType[0] === 's') { // special case for array of strings return GLib.Variant.new_strv(value); } if (arrayType[0] === 'y') { // special case for array of bytes if (typeof value === 'string') value = Uint8Array.of(...new TextEncoder().encode(value), 0); const bytes = new GLib.Bytes(value); return GLib.Variant.new_from_bytes(new GLib.VariantType('ay'), bytes, true); } let arrayValue = []; if (arrayType[0] === '{') { // special case for dictionaries for (let key in value) { let copy = [].concat(arrayType); let child = _packVariant(copy, [key, value[key]]); arrayValue.push(child); } } else { for (let i = 0; i < value.length; i++) { let copy = [].concat(arrayType); let child = _packVariant(copy, value[i]); arrayValue.push(child); } } return GLib.Variant.new_array(new GLib.VariantType(arrayType.join('')), arrayValue); } case '(': { let children = []; for (let i = 0; i < value.length; i++) { let next = signature[0]; if (next === ')') break; children.push(_packVariant(signature, value[i])); } if (signature[0] !== ')') throw new TypeError('Invalid GVariant signature for type TUPLE (expected ")")'); signature.shift(); return GLib.Variant.new_tuple(children); } case '{': { let key = _packVariant(signature, value[0]); let child = _packVariant(signature, value[1]); if (signature[0] !== '}') throw new TypeError('Invalid GVariant signature for type DICT_ENTRY (expected "}")'); signature.shift(); return GLib.Variant.new_dict_entry(key, child); } default: throw new TypeError(`Invalid GVariant signature (unexpected character ${char})`); } } function _unpackVariant(variant, deep, recursive = false) { switch (String.fromCharCode(variant.classify())) { case 'b': return variant.get_boolean(); case 'y': return variant.get_byte(); case 'n': return variant.get_int16(); case 'q': return variant.get_uint16(); case 'i': return variant.get_int32(); case 'u': return variant.get_uint32(); case 'x': return variant.get_int64(); case 't': return variant.get_uint64(); case 'h': return variant.get_handle(); case 'd': return variant.get_double(); case 'o': case 'g': case 's': // g_variant_get_string has length as out argument return variant.get_string()[0]; case 'v': { const ret = variant.get_variant(); if (deep && recursive && ret instanceof GLib.Variant) return _unpackVariant(ret, deep, recursive); return ret; } case 'm': { let val = variant.get_maybe(); if (deep && val) return _unpackVariant(val, deep, recursive); else return val; } case 'a': if (variant.is_of_type(new GLib.VariantType('a{?*}'))) { // special case containers let ret = { }; let nElements = variant.n_children(); for (let i = 0; i < nElements; i++) { // always unpack the dictionary entry, and always unpack // the key (or it cannot be added as a key) let val = _unpackVariant(variant.get_child_value(i), deep, recursive); let key; if (!deep) key = _unpackVariant(val[0], true); else key = val[0]; ret[key] = val[1]; } return ret; } if (variant.is_of_type(new GLib.VariantType('ay'))) { // special case byte arrays return variant.get_data_as_bytes().toArray(); } // fall through case '(': case '{': { let ret = []; let nElements = variant.n_children(); for (let i = 0; i < nElements; i++) { let val = variant.get_child_value(i); if (deep) ret.push(_unpackVariant(val, deep, recursive)); else ret.push(val); } return ret; } } throw new Error('Assertion failure: this code should not be reached'); } function _notIntrospectableError(funcName, replacement) { return new Error(`${funcName} is not introspectable. Use ${replacement} instead.`); } function _warnNotIntrospectable(funcName, replacement) { logError(_notIntrospectableError(funcName, replacement)); } function _escapeCharacterSetChars(char) { if ('-^]\\'.includes(char)) return `\\${char}`; return char; } function _init() { // this is imports.gi.GLib GLib = this; GLib.MainLoop.prototype.runAsync = function (...args) { return new Promise((resolve, reject) => { setMainLoopHook(() => { try { resolve(this.run(...args)); } catch (error) { reject(error); } }); }); }; // For convenience in property min or max values, since GLib.MAXINT64 and // friends will log a warning when used this.MAXINT64_BIGINT = 0x7fff_ffff_ffff_ffffn; this.MININT64_BIGINT = -this.MAXINT64_BIGINT - 1n; this.MAXUINT64_BIGINT = 0xffff_ffff_ffff_ffffn; // small HACK: we add a matches() method to standard Errors so that // you can do "if (e.matches(Ns.FooError, Ns.FooError.SOME_CODE))" // without checking instanceof Error.prototype.matches = function () { return false; }; // Guard against domains that aren't valid quarks and would lead // to a crash const quarkToString = this.quark_to_string; const realNewLiteral = this.Error.new_literal; this.Error.new_literal = function (domain, code, message) { if (quarkToString(domain) === null) throw new TypeError(`Error.new_literal: ${domain} is not a valid domain`); return realNewLiteral(domain, code, message); }; this.Variant._new_internal = function (sig, value) { let signature = Array.prototype.slice.call(sig); let variant = _packVariant(signature, value); if (signature.length !== 0) throw new TypeError('Invalid GVariant signature (more than one single complete type)'); return variant; }; // Deprecate version of new GLib.Variant() this.Variant.new = function (sig, value) { return new GLib.Variant(sig, value); }; this.Variant.prototype.unpack = function () { return _unpackVariant(this, false); }; this.Variant.prototype.deepUnpack = function () { return _unpackVariant(this, true); }; // backwards compatibility alias this.Variant.prototype.deep_unpack = this.Variant.prototype.deepUnpack; // Note: discards type information, if the variant contains any 'v' types this.Variant.prototype.recursiveUnpack = function () { return _unpackVariant(this, true, true); }; this.Variant.prototype.toString = function () { return `[object variant of type "${this.get_type_string()}"]`; }; this.Bytes.prototype.toArray = function () { return imports._byteArrayNative.fromGBytes(this); }; this.log_structured = /** * @param {string} logDomain Log domain. * @param {GLib.LogLevelFlags} logLevel Log level, either from GLib.LogLevelFlags, or a user-defined level. * @param {Record} fields Key-value pairs of structured data to add to the log entry. * @returns {void} */ function log_structured(logDomain, logLevel, fields) { /** @type {Record} */ let variantFields = {}; for (let key in fields) { const field = fields[key]; if (field instanceof Uint8Array) { variantFields[key] = new GLib.Variant('ay', field); } else if (typeof field === 'string') { variantFields[key] = new GLib.Variant('s', field); } else if (field instanceof GLib.Variant) { // GLib.log_variant converts all Variants that are // not 'ay' or 's' type to strings by printing // them. // // https://gitlab.gnome.org/GNOME/glib/-/blob/a380bfdf93cb3bfd3cd4caedc0127c4e5717545b/glib/gmessages.c#L1894 variantFields[key] = field; } else { throw new TypeError(`Unsupported value ${field}, log_structured supports GLib.Variant, Uint8Array, and string values.`); } } GLib.log_variant(logDomain, logLevel, new GLib.Variant('a{sv}', variantFields)); }; // CjsPrivate depends on GLib so we cannot import it // before GLib is fully resolved. this.log_set_writer_func_variant = function (...args) { const {log_set_writer_func} = imports.gi.CjsPrivate; log_set_writer_func(...args); }; this.log_set_writer_default = function (...args) { const {log_set_writer_default} = imports.gi.CjsPrivate; log_set_writer_default(...args); }; this.log_set_writer_func = function (writer_func) { const {log_set_writer_func} = imports.gi.CjsPrivate; if (typeof writer_func !== 'function') { log_set_writer_func(writer_func); } else { log_set_writer_func(function (logLevel, stringFields) { const stringFieldsObj = {...stringFields.recursiveUnpack()}; return writer_func(logLevel, stringFieldsObj); }); } }; this.VariantDict.prototype.lookup = function (key, variantType = null, deep = false) { if (typeof variantType === 'string') variantType = new GLib.VariantType(variantType); const variant = this.lookup_value(key, variantType); if (variant === null) return null; return _unpackVariant(variant, deep); }; // Prevent user code from calling GLib string manipulation functions that // return the same string that was passed in. These can't be annotated // properly, and will mostly crash. // Here we provide approximate implementations of the functions so that if // they had happened to work in the past, they will continue working, but // log a stack trace and a suggestion of what to use instead. // Exceptions are thrown instead for GLib.stpcpy() of which the return value // is useless anyway and GLib.ascii_formatd() which is too complicated to // implement here. this.stpcpy = function () { throw _notIntrospectableError('GLib.stpcpy()', 'the + operator'); }; this.strstr_len = function (haystack, len, needle) { _warnNotIntrospectable('GLib.strstr_len()', 'String.indexOf()'); let searchString = haystack; if (len !== -1) searchString = searchString.slice(0, len); const index = searchString.indexOf(needle); if (index === -1) return null; return haystack.slice(index); }; this.strrstr = function (haystack, needle) { _warnNotIntrospectable('GLib.strrstr()', 'String.lastIndexOf()'); const index = haystack.lastIndexOf(needle); if (index === -1) return null; return haystack.slice(index); }; this.strrstr_len = function (haystack, len, needle) { _warnNotIntrospectable('GLib.strrstr_len()', 'String.lastIndexOf()'); let searchString = haystack; if (len !== -1) searchString = searchString.slice(0, len); const index = searchString.lastIndexOf(needle); if (index === -1) return null; return haystack.slice(index); }; this.strup = function (string) { _warnNotIntrospectable('GLib.strup()', 'String.toUpperCase() or GLib.ascii_strup()'); return string.toUpperCase(); }; this.strdown = function (string) { _warnNotIntrospectable('GLib.strdown()', 'String.toLowerCase() or GLib.ascii_strdown()'); return string.toLowerCase(); }; this.strreverse = function (string) { _warnNotIntrospectable('GLib.strreverse()', 'Array.reverse() and String.join()'); return [...string].reverse().join(''); }; this.ascii_dtostr = function (unused, len, number) { _warnNotIntrospectable('GLib.ascii_dtostr()', 'JS string conversion'); return `${number}`.slice(0, len); }; this.ascii_formatd = function () { throw _notIntrospectableError('GLib.ascii_formatd()', 'Number.toExponential() and string interpolation'); }; this.strchug = function (string) { _warnNotIntrospectable('GLib.strchug()', 'String.trimStart()'); return string.trimStart(); }; this.strchomp = function (string) { _warnNotIntrospectable('GLib.strchomp()', 'String.trimEnd()'); return string.trimEnd(); }; // g_strstrip() is a macro and therefore doesn't even appear in the GIR // file, but we may as well include it here since it's trivial this.strstrip = function (string) { _warnNotIntrospectable('GLib.strstrip()', 'String.trim()'); return string.trim(); }; this.strdelimit = function (string, delimiters, newDelimiter) { _warnNotIntrospectable('GLib.strdelimit()', 'String.replace()'); if (delimiters === null) delimiters = GLib.STR_DELIMITERS; if (typeof newDelimiter === 'number') newDelimiter = String.fromCharCode(newDelimiter); const delimiterChars = delimiters.split(''); const escapedDelimiterChars = delimiterChars.map(_escapeCharacterSetChars); const delimiterRegex = new RegExp(`[${escapedDelimiterChars.join('')}]`, 'g'); return string.replace(delimiterRegex, newDelimiter); }; this.strcanon = function (string, validChars, substitutor) { _warnNotIntrospectable('GLib.strcanon()', 'String.replace()'); if (typeof substitutor === 'number') substitutor = String.fromCharCode(substitutor); const validArray = validChars.split(''); const escapedValidArray = validArray.map(_escapeCharacterSetChars); const invalidRegex = new RegExp(`[^${escapedValidArray.join('')}]`, 'g'); return string.replace(invalidRegex, substitutor); }; // Prevent user code from calling GThread functions which always crash this.Thread.new = function () { throw _notIntrospectableError('GLib.Thread.new()', 'GIO asynchronous methods or Promise()'); }; this.Thread.try_new = function () { throw _notIntrospectableError('GLib.Thread.try_new()', 'GIO asynchronous methods or Promise()'); }; this.Thread.exit = function () { throw new Error('\'GLib.Thread.exit()\' may not be called in GJS'); }; this.Thread.prototype.ref = function () { throw new Error('\'GLib.Thread.ref()\' may not be called in GJS'); }; this.Thread.prototype.unref = function () { throw new Error('\'GLib.Thread.unref()\' may not be called in GJS'); }; // Override GLib.MatchInfo with a type that keeps the UTF-8 encoded search // string alive. const oldMatchInfo = this.MatchInfo; let matchInfoPatched = false; function patchMatchInfo(GLibModule) { if (matchInfoPatched) return; const {MatchInfo} = imports.gi.CjsPrivate; const originalMatchInfoMethods = new Set(Object.keys(oldMatchInfo.prototype)); const overriddenMatchInfoMethods = new Set(Object.keys(MatchInfo.prototype)); const symmetricDifference = new Set(originalMatchInfoMethods); for (const method of overriddenMatchInfoMethods) { if (symmetricDifference.has(method)) symmetricDifference.delete(method); else symmetricDifference.add(method); } if (symmetricDifference.size !== 0) throw new Error(`Methods of GMatchInfo and GjsMatchInfo don't match: ${[...symmetricDifference]}`); GLibModule.MatchInfo = MatchInfo; matchInfoPatched = true; } // We can't monkeypatch GLib.MatchInfo directly at override time, because // importing CjsPrivate requires GLib. So this monkeypatches GLib.MatchInfo // with a Proxy that overwrites itself with the real CjsPrivate.MatchInfo // as soon as you try to do anything with it. const allProxyOperations = ['apply', 'construct', 'defineProperty', 'deleteProperty', 'get', 'getOwnPropertyDescriptor', 'getPrototypeOf', 'has', 'isExtensible', 'ownKeys', 'preventExtensions', 'set', 'setPrototypeOf']; function delegateToMatchInfo(op) { return function (target, ...params) { patchMatchInfo(GLib); return Reflect[op](GLib.MatchInfo, ...params); }; } this.MatchInfo = new Proxy(function () {}, Object.fromEntries(allProxyOperations.map(op => [op, delegateToMatchInfo(op)]))); this.Regex.prototype.match = function (...args) { patchMatchInfo(GLib); return imports.gi.CjsPrivate.regex_match(this, ...args); }; this.Regex.prototype.match_full = function (...args) { patchMatchInfo(GLib); return imports.gi.CjsPrivate.regex_match_full(this, ...args); }; this.Regex.prototype.match_all = function (...args) { patchMatchInfo(GLib); return imports.gi.CjsPrivate.regex_match_all(this, ...args); }; this.Regex.prototype.match_all_full = function (...args) { patchMatchInfo(GLib); return imports.gi.CjsPrivate.regex_match_all_full(this, ...args); }; } cjs-128.0/modules/core/overrides/Gio.js0000664000175000017500000007503114771557763016741 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2011 Giovanni Campagna var GLib = imports.gi.GLib; var CjsPrivate = imports.gi.CjsPrivate; var Signals = imports.signals; var Gio; // Ensures that a Gio.UnixFDList being passed into or out of a DBus method with // a parameter type that includes 'h' somewhere, actually has entries in it for // each of the indices being passed as an 'h' parameter. function _validateFDVariant(variant, fdList) { switch (String.fromCharCode(variant.classify())) { case 'b': case 'y': case 'n': case 'q': case 'i': case 'u': case 'x': case 't': case 'd': case 'o': case 'g': case 's': return; case 'h': { const val = variant.get_handle(); const numFds = fdList.get_length(); if (val >= numFds) { throw new Error(`handle ${val} is out of range of Gio.UnixFDList ` + `containing ${numFds} FDs`); } return; } case 'v': _validateFDVariant(variant.get_variant(), fdList); return; case 'm': { let val = variant.get_maybe(); if (val) _validateFDVariant(val, fdList); return; } case 'a': case '(': case '{': { let nElements = variant.n_children(); for (let ix = 0; ix < nElements; ix++) _validateFDVariant(variant.get_child_value(ix), fdList); return; } } throw new Error('Assertion failure: this code should not be reached'); } function _proxyInvoker(methodName, sync, inSignature, argArray) { var replyFunc; var flags = 0; var cancellable = null; let fdList = null; /* Convert argArray to a *real* array */ argArray = Array.prototype.slice.call(argArray); /* The default replyFunc only logs the responses */ replyFunc = _logReply; var signatureLength = inSignature.length; var minNumberArgs = signatureLength; var maxNumberArgs = signatureLength + 4; if (argArray.length < minNumberArgs) { throw new Error(`Not enough arguments passed for method: ${ methodName}. Expected ${minNumberArgs}, got ${argArray.length}`); } else if (argArray.length > maxNumberArgs) { throw new Error(`Too many arguments passed for method ${methodName}. ` + `Maximum is ${maxNumberArgs} including one callback, ` + 'Gio.Cancellable, Gio.UnixFDList, and/or flags'); } while (argArray.length > signatureLength) { var argNum = argArray.length - 1; var arg = argArray.pop(); if (typeof arg === 'function' && !sync) { replyFunc = arg; } else if (typeof arg === 'number') { flags = arg; } else if (arg instanceof Gio.Cancellable) { cancellable = arg; } else if (arg instanceof Gio.UnixFDList) { fdList = arg; } else { throw new Error(`Argument ${argNum} of method ${methodName} is ` + `${typeof arg}. It should be a callback, flags, ` + 'Gio.UnixFDList, or a Gio.Cancellable'); } } const inTypeString = `(${inSignature.join('')})`; const inVariant = new GLib.Variant(inTypeString, argArray); if (inTypeString.includes('h')) { if (!fdList) { throw new Error(`Method ${methodName} with input type containing ` + '\'h\' must have a Gio.UnixFDList as an argument'); } _validateFDVariant(inVariant, fdList); } var asyncCallback = (proxy, result) => { try { const [outVariant, outFdList] = proxy.call_with_unix_fd_list_finish(result); replyFunc(outVariant.deepUnpack(), null, outFdList); } catch (e) { replyFunc([], e, null); } }; if (sync) { const [outVariant, outFdList] = this.call_with_unix_fd_list_sync( methodName, inVariant, flags, -1, fdList, cancellable); if (fdList) return [outVariant.deepUnpack(), outFdList]; return outVariant.deepUnpack(); } return this.call_with_unix_fd_list(methodName, inVariant, flags, -1, fdList, cancellable, asyncCallback); } function _logReply(result, exc) { if (exc !== null) log(`Ignored exception from dbus method: ${exc}`); } function _makeProxyMethod(method, sync) { var i; var name = method.name; var inArgs = method.in_args; var inSignature = []; for (i = 0; i < inArgs.length; i++) inSignature.push(inArgs[i].signature); return function (...args) { return _proxyInvoker.call(this, name, sync, inSignature, args); }; } function _convertToNativeSignal(proxy, senderName, signalName, parameters) { Signals._emit.call(proxy, signalName, senderName, parameters.deepUnpack()); } function _propertyGetter(name) { let value = this.get_cached_property(name); return value ? value.deepUnpack() : null; } function _propertySetter(name, signature, value) { let variant = new GLib.Variant(signature, value); this.set_cached_property(name, variant); this.call('org.freedesktop.DBus.Properties.Set', new GLib.Variant('(ssv)', [this.g_interface_name, name, variant]), Gio.DBusCallFlags.NONE, -1, null, (proxy, result) => { try { this.call_finish(result); } catch (e) { log(`Could not set property ${name} on remote object ${ this.g_object_path}: ${e.message}`); } }); } function _addDBusConvenience() { let info = this.g_interface_info; if (!info) return; if (info.signals.length > 0) this.connect('g-signal', _convertToNativeSignal); let i, methods = info.methods; for (i = 0; i < methods.length; i++) { var method = methods[i]; let remoteMethod = _makeProxyMethod(methods[i], false); this[`${method.name}Remote`] = remoteMethod; this[`${method.name}Sync`] = _makeProxyMethod(methods[i], true); this[`${method.name}Async`] = function (...args) { return new Promise((resolve, reject) => { args.push((result, error, fdList) => { if (error) reject(error); else if (fdList) resolve([result, fdList]); else resolve(result); }); remoteMethod.call(this, ...args); }); }; } let properties = info.properties; for (i = 0; i < properties.length; i++) { let name = properties[i].name; let signature = properties[i].signature; let flags = properties[i].flags; let getter = () => { throw new Error(`Property ${name} is not readable`); }; let setter = () => { throw new Error(`Property ${name} is not writable`); }; if (flags & Gio.DBusPropertyInfoFlags.READABLE) getter = _propertyGetter.bind(this, name); if (flags & Gio.DBusPropertyInfoFlags.WRITABLE) setter = _propertySetter.bind(this, name, signature); Object.defineProperty(this, name, { get: getter, set: setter, configurable: false, enumerable: true, }); } } function _makeProxyWrapper(interfaceXml) { var info = _newInterfaceInfo(interfaceXml); var iname = info.name; function wrapper(bus, name, object, asyncCallback, cancellable, flags = Gio.DBusProxyFlags.NONE) { var obj = new Gio.DBusProxy({ g_connection: bus, g_interface_name: iname, g_interface_info: info, g_name: name, g_flags: flags, g_object_path: object, }); if (!cancellable) cancellable = null; if (asyncCallback) { obj.init_async(GLib.PRIORITY_DEFAULT, cancellable).then( () => asyncCallback(obj, null)).catch(e => asyncCallback(null, e)); } else { obj.init(cancellable); } return obj; } wrapper.newAsync = function newAsync(bus, name, object, cancellable, flags = Gio.DBusProxyFlags.NONE) { const obj = new Gio.DBusProxy({ g_connection: bus, g_interface_name: info.name, g_interface_info: info, g_name: name, g_flags: flags, g_object_path: object, }); return new Promise((resolve, reject) => obj.init_async(GLib.PRIORITY_DEFAULT, cancellable ?? null).then( () => resolve(obj)).catch(reject)); }; return wrapper; } function _newNodeInfo(constructor, value) { if (typeof value === 'string') return constructor(value); throw TypeError(`Invalid type ${Object.prototype.toString.call(value)}`); } function _newInterfaceInfo(value) { var nodeInfo = Gio.DBusNodeInfo.new_for_xml(value); return nodeInfo.interfaces[0]; } function _injectToMethod(klass, method, addition) { var previous = klass[method]; klass[method] = function (...args) { addition.apply(this, args); return previous.apply(this, args); }; } function _injectToStaticMethod(klass, method, addition) { var previous = klass[method]; klass[method] = function (...parameters) { let obj = previous.apply(this, parameters); addition.apply(obj, parameters); return obj; }; } function _wrapFunction(klass, method, addition) { var previous = klass[method]; klass[method] = function (...args) { args.unshift(previous); return addition.apply(this, args); }; } function _makeOutSignature(args) { var ret = '('; for (var i = 0; i < args.length; i++) ret += args[i].signature; return `${ret})`; } function _handleMethodCall(info, impl, methodName, parameters, invocation) { // prefer a sync version if available if (this[methodName]) { let retval; try { const fdList = invocation.get_message().get_unix_fd_list(); retval = this[methodName](...parameters.deepUnpack(), fdList); } catch (e) { if (e instanceof GLib.Error) { invocation.return_gerror(e); } else { let name = e.name; if (!name.includes('.')) { // likely to be a normal JS error name = `org.gnome.gjs.JSError.${name}`; } logError(e, `Exception in method call: ${methodName}`); invocation.return_dbus_error(name, e.message); } return; } if (retval === undefined) { // undefined (no return value) is the empty tuple retval = new GLib.Variant('()', []); } try { let outFdList = null; if (!(retval instanceof GLib.Variant)) { // attempt packing according to out signature let methodInfo = info.lookup_method(methodName); let outArgs = methodInfo.out_args; let outSignature = _makeOutSignature(outArgs); if (outSignature.includes('h') && retval[retval.length - 1] instanceof Gio.UnixFDList) { outFdList = retval.pop(); } else if (outArgs.length === 1) { // if one arg, we don't require the handler wrapping it // into an Array retval = [retval]; } retval = new GLib.Variant(outSignature, retval); } invocation.return_value_with_unix_fd_list(retval, outFdList); } catch (e) { // if we don't do this, the other side will never see a reply invocation.return_dbus_error('org.gnome.gjs.JSError.ValueError', 'Service implementation returned an incorrect value type'); } } else if (this[`${methodName}Async`]) { const fdList = invocation.get_message().get_unix_fd_list(); this[`${methodName}Async`](parameters.deepUnpack(), invocation, fdList); } else { log(`Missing handler for DBus method ${methodName}`); invocation.return_gerror(new Gio.DBusError({ code: Gio.DBusError.UNKNOWN_METHOD, message: `Method ${methodName} is not implemented`, })); } } function _handlePropertyGet(info, impl, propertyName) { let propInfo = info.lookup_property(propertyName); let jsval = this[propertyName]; if (jsval?.get_type_string?.() === propInfo.signature) return jsval; else if (jsval !== undefined) return new GLib.Variant(propInfo.signature, jsval); else return null; } function _handlePropertySet(info, impl, propertyName, newValue) { this[propertyName] = newValue.deepUnpack(); } function _wrapJSObject(interfaceInfo, jsObj) { var info; if (interfaceInfo instanceof Gio.DBusInterfaceInfo) info = interfaceInfo; else info = Gio.DBusInterfaceInfo.new_for_xml(interfaceInfo); info.cache_build(); var impl = new CjsPrivate.DBusImplementation({g_interface_info: info}); impl.connect('handle-method-call', function (self, methodName, parameters, invocation) { return _handleMethodCall.call(jsObj, info, self, methodName, parameters, invocation); }); impl.connect('handle-property-get', function (self, propertyName) { return _handlePropertyGet.call(jsObj, info, self, propertyName); }); impl.connect('handle-property-set', function (self, propertyName, value) { return _handlePropertySet.call(jsObj, info, self, propertyName, value); }); return impl; } function* _listModelIterator() { let _index = 0; const _len = this.get_n_items(); while (_index < _len) yield this.get_item(_index++); } function _promisify(proto, asyncFunc, finishFunc = undefined) { if (proto[asyncFunc] === undefined) throw new Error(`${proto} has no method named ${asyncFunc}`); if (finishFunc === undefined) { if (asyncFunc.endsWith('_begin') || asyncFunc.endsWith('_async')) finishFunc = `${asyncFunc.slice(0, -5)}finish`; else finishFunc = `${asyncFunc}_finish`; } if (proto[finishFunc] === undefined) throw new Error(`${proto} has no method named ${finishFunc}`); const originalFuncName = `_original_${asyncFunc}`; if (proto[originalFuncName] !== undefined) return; proto[originalFuncName] = proto[asyncFunc]; proto[asyncFunc] = function (...args) { if (args.length === this[originalFuncName].length) return this[originalFuncName](...args); return new Promise((resolve, reject) => { let {stack: callStack} = new Error(); this[originalFuncName](...args, function (source, res) { try { const result = source !== null && source[finishFunc] !== undefined ? source[finishFunc](res) : proto[finishFunc](res); if (Array.isArray(result) && result.length > 1 && result[0] === true) result.shift(); resolve(result); } catch (error) { callStack = callStack.split('\n').filter(line => line.indexOf('_promisify/') === -1).join('\n'); if (error.stack) error.stack += `### Promise created here: ###\n${callStack}`; else error.stack = callStack; reject(error); } }); }); }; } function _notIntrospectableError(funcName, replacement) { return new Error(`${funcName} is not introspectable. Use ${replacement} instead.`); } function _warnNotIntrospectable(funcName, replacement) { logError(_notIntrospectableError(funcName, replacement)); } function _init() { Gio = this; Gio.Application.prototype.runAsync = GLib.MainLoop.prototype.runAsync; Gio.DBus = { // Namespace some functions get: Gio.bus_get, get_finish: Gio.bus_get_finish, get_sync: Gio.bus_get_sync, own_name: Gio.bus_own_name, own_name_on_connection: Gio.bus_own_name_on_connection, unown_name: Gio.bus_unown_name, watch_name: Gio.bus_watch_name, watch_name_on_connection: Gio.bus_watch_name_on_connection, unwatch_name: Gio.bus_unwatch_name, }; Object.defineProperties(Gio.DBus, { 'session': { get() { return Gio.bus_get_sync(Gio.BusType.SESSION, null); }, enumerable: false, }, 'system': { get() { return Gio.bus_get_sync(Gio.BusType.SYSTEM, null); }, enumerable: false, }, }); Gio.DBusConnection.prototype.watch_name = function (name, flags, appeared, vanished) { return Gio.bus_watch_name_on_connection(this, name, flags, appeared, vanished); }; Gio.DBusConnection.prototype.unwatch_name = function (id) { return Gio.bus_unwatch_name(id); }; Gio.DBusConnection.prototype.own_name = function (name, flags, acquired, lost) { return Gio.bus_own_name_on_connection(this, name, flags, acquired, lost); }; Gio.DBusConnection.prototype.unown_name = function (id) { return Gio.bus_unown_name(id); }; _injectToMethod(Gio.DBusProxy.prototype, 'init', _addDBusConvenience); _promisify(Gio.DBusProxy.prototype, 'init_async'); _injectToMethod(Gio.DBusProxy.prototype, 'init_async', _addDBusConvenience); _injectToStaticMethod(Gio.DBusProxy, 'new_sync', _addDBusConvenience); _injectToStaticMethod(Gio.DBusProxy, 'new_finish', _addDBusConvenience); _injectToStaticMethod(Gio.DBusProxy, 'new_for_bus_sync', _addDBusConvenience); _injectToStaticMethod(Gio.DBusProxy, 'new_for_bus_finish', _addDBusConvenience); Gio.DBusProxy.prototype.connectSignal = Signals._connect; Gio.DBusProxy.prototype.disconnectSignal = Signals._disconnect; Gio.DBusProxy.makeProxyWrapper = _makeProxyWrapper; // Some helpers _wrapFunction(Gio.DBusNodeInfo, 'new_for_xml', _newNodeInfo); Gio.DBusInterfaceInfo.new_for_xml = _newInterfaceInfo; Gio.DBusExportedObject = CjsPrivate.DBusImplementation; Gio.DBusExportedObject.wrapJSObject = _wrapJSObject; // ListStore Gio.ListStore.prototype[Symbol.iterator] = _listModelIterator; Gio.ListStore.prototype.insert_sorted = function (item, compareFunc) { return CjsPrivate.list_store_insert_sorted(this, item, compareFunc); }; Gio.ListStore.prototype.sort = function (compareFunc) { return CjsPrivate.list_store_sort(this, compareFunc); }; // Promisify Gio._promisify = _promisify; // Temporary Gio.File.prototype fix Gio._LocalFilePrototype = Gio.File.new_for_path('/').constructor.prototype; Gio.File.prototype.replace_contents_async = function replace_contents_async(contents, etag, make_backup, flags, cancellable, callback) { return this.replace_contents_bytes_async(contents, etag, make_backup, flags, cancellable, callback); }; // Best-effort attempt to replace set_attribute(), which is not // introspectable due to the pointer argument Gio.File.prototype.set_attribute = function set_attribute(attribute, type, value, flags, cancellable) { _warnNotIntrospectable('Gio.File.prototype.set_attribute', 'set_attribute_{type}'); switch (type) { case Gio.FileAttributeType.STRING: return this.set_attribute_string(attribute, value, flags, cancellable); case Gio.FileAttributeType.BYTE_STRING: return this.set_attribute_byte_string(attribute, value, flags, cancellable); case Gio.FileAttributeType.UINT32: return this.set_attribute_uint32(attribute, value, flags, cancellable); case Gio.FileAttributeType.INT32: return this.set_attribute_int32(attribute, value, flags, cancellable); case Gio.FileAttributeType.UINT64: return this.set_attribute_uint64(attribute, value, flags, cancellable); case Gio.FileAttributeType.INT64: return this.set_attribute_int64(attribute, value, flags, cancellable); case Gio.FileAttributeType.INVALID: case Gio.FileAttributeType.BOOLEAN: case Gio.FileAttributeType.OBJECT: case Gio.FileAttributeType.STRINGV: throw _notIntrospectableError('This attribute type', 'Gio.FileInfo'); } }; Gio.FileInfo.prototype.set_attribute = function set_attribute(attribute, type, value) { _warnNotIntrospectable('Gio.FileInfo.prototype.set_attribute', 'set_attribute_{type}'); switch (type) { case Gio.FileAttributeType.INVALID: return this.remove_attribute(attribute); case Gio.FileAttributeType.STRING: return this.set_attribute_string(attribute, value); case Gio.FileAttributeType.BYTE_STRING: return this.set_attribute_byte_string(attribute, value); case Gio.FileAttributeType.BOOLEAN: return this.set_attribute_boolean(attribute, value); case Gio.FileAttributeType.UINT32: return this.set_attribute_uint32(attribute, value); case Gio.FileAttributeType.INT32: return this.set_attribute_int32(attribute, value); case Gio.FileAttributeType.UINT64: return this.set_attribute_uint64(attribute, value); case Gio.FileAttributeType.INT64: return this.set_attribute_int64(attribute, value); case Gio.FileAttributeType.OBJECT: return this.set_attribute_object(attribute, value); case Gio.FileAttributeType.STRINGV: return this.set_attribute_stringv(attribute, value); } }; Gio.InputStream.prototype.createSyncIterator = function* createSyncIterator(count) { while (true) { const bytes = this.read_bytes(count, null); if (bytes.get_size() === 0) return; yield bytes; } }; Gio.InputStream.prototype.createAsyncIterator = async function* createAsyncIterator( count, ioPriority = GLib.PRIORITY_DEFAULT) { const self = this; function next() { return new Promise((resolve, reject) => { self.read_bytes_async(count, ioPriority, null, (_self, res) => { try { const bytes = self.read_bytes_finish(res); resolve(bytes); } catch (err) { reject(err); } }); }); } while (true) { // eslint-disable-next-line no-await-in-loop const bytes = await next(count); if (bytes.get_size() === 0) return; yield bytes; } }; Gio.FileEnumerator.prototype[Symbol.iterator] = function* FileEnumeratorIterator() { while (true) { try { const info = this.next_file(null); if (info === null) break; yield info; } catch (err) { this.close(null); throw err; } } this.close(null); }; Gio.FileEnumerator.prototype[Symbol.asyncIterator] = async function* AsyncFileEnumeratorIterator() { const self = this; function next() { return new Promise((resolve, reject) => { self.next_files_async(1, GLib.PRIORITY_DEFAULT, null, (_self, res) => { try { const files = self.next_files_finish(res); resolve(files.length === 0 ? null : files[0]); } catch (err) { reject(err); } }); }); } function close() { return new Promise((resolve, reject) => { self.close_async(GLib.PRIORITY_DEFAULT, null, (_self, res) => { try { resolve(self.close_finish(res)); } catch (err) { reject(err); } }); }); } while (true) { try { // eslint-disable-next-line no-await-in-loop const info = await next(); if (info === null) break; yield info; } catch (err) { // eslint-disable-next-line no-await-in-loop await close(); throw err; } } return close(); }; // Override Gio.Settings and Gio.SettingsSchema - the C API asserts if // trying to access a nonexistent schema or key, which is not handy for // shell-extension writers Gio.SettingsSchema.prototype._realGetKey = Gio.SettingsSchema.prototype.get_key; Gio.SettingsSchema.prototype.get_key = function (key) { if (!this.has_key(key)) throw new Error(`GSettings key ${key} not found in schema ${this.get_id()}`); return this._realGetKey(key); }; Gio.Settings.prototype._realMethods = Object.assign({}, Gio.Settings.prototype); function createCheckedMethod(method, checkMethod = '_checkKey') { return function (id, ...args) { this[checkMethod](id); return this._realMethods[method].call(this, id, ...args); }; } Object.assign(Gio.Settings.prototype, { _realInit: Gio.Settings.prototype._init, // add manually, not enumerable _init(props = {}) { // 'schema' is a deprecated alias for schema_id const schemaIdProp = ['schema', 'schema-id', 'schema_id', 'schemaId'].find(prop => prop in props); const settingsSchemaProp = ['settings-schema', 'settings_schema', 'settingsSchema'].find(prop => prop in props); if (!schemaIdProp && !settingsSchemaProp) { throw new Error('One of property \'schema-id\' or ' + '\'settings-schema\' are required for Gio.Settings'); } if (settingsSchemaProp && !(props[settingsSchemaProp] instanceof Gio.SettingsSchema)) throw new Error(`Value of property '${settingsSchemaProp}' is not of type Gio.SettingsSchema`); const source = Gio.SettingsSchemaSource.get_default(); const settingsSchema = settingsSchemaProp ? props[settingsSchemaProp] : source.lookup(props[schemaIdProp], true); if (!settingsSchema) throw new Error(`GSettings schema ${props[schemaIdProp]} not found`); const settingsSchemaPath = settingsSchema.get_path(); if (props['path'] === undefined && !settingsSchemaPath) { throw new Error('Attempting to create schema ' + `'${settingsSchema.get_id()}' without a path`); } if (props['path'] !== undefined && settingsSchemaPath && props['path'] !== settingsSchemaPath) { throw new Error(`GSettings created for path '${props['path']}'` + `, but schema specifies '${settingsSchemaPath}'`); } return this._realInit(props); }, _checkKey(key) { // Avoid using has_key(); checking a JS array is faster than calling // through G-I. if (!this._keys) this._keys = this.settings_schema.list_keys(); if (!this._keys.includes(key)) throw new Error(`GSettings key ${key} not found in schema ${this.schema_id}`); }, _checkChild(name) { if (!this._children) this._children = this.list_children(); if (!this._children.includes(name)) throw new Error(`Child ${name} not found in GSettings schema ${this.schema_id}`); }, get_boolean: createCheckedMethod('get_boolean'), set_boolean: createCheckedMethod('set_boolean'), get_double: createCheckedMethod('get_double'), set_double: createCheckedMethod('set_double'), get_enum: createCheckedMethod('get_enum'), set_enum: createCheckedMethod('set_enum'), get_flags: createCheckedMethod('get_flags'), set_flags: createCheckedMethod('set_flags'), get_int: createCheckedMethod('get_int'), set_int: createCheckedMethod('set_int'), get_int64: createCheckedMethod('get_int64'), set_int64: createCheckedMethod('set_int64'), get_string: createCheckedMethod('get_string'), set_string: createCheckedMethod('set_string'), get_strv: createCheckedMethod('get_strv'), set_strv: createCheckedMethod('set_strv'), get_uint: createCheckedMethod('get_uint'), set_uint: createCheckedMethod('set_uint'), get_uint64: createCheckedMethod('get_uint64'), set_uint64: createCheckedMethod('set_uint64'), get_value: createCheckedMethod('get_value'), set_value: createCheckedMethod('set_value'), bind: createCheckedMethod('bind'), bind_writable: createCheckedMethod('bind_writable'), create_action: createCheckedMethod('create_action'), get_default_value: createCheckedMethod('get_default_value'), get_user_value: createCheckedMethod('get_user_value'), is_writable: createCheckedMethod('is_writable'), reset: createCheckedMethod('reset'), get_child: createCheckedMethod('get_child', '_checkChild'), }); // ActionMap // add_action_entries is not introspectable // https://gitlab.gnome.org/GNOME/gjs/-/issues/407 Gio.ActionMap.prototype.add_action_entries = function add_action_entries(entries) { for (const {name, activate, parameter_type, state, change_state} of entries) { if (typeof parameter_type === 'string' && !GLib.variant_type_string_is_valid(parameter_type)) throw new Error(`parameter_type "${parameter_type}" is not a valid VariantType`); const action = new Gio.SimpleAction({ name, parameter_type: typeof parameter_type === 'string' ? new GLib.VariantType(parameter_type) : null, state: typeof state === 'string' ? GLib.Variant.parse(null, state, null, null) : null, }); if (typeof activate === 'function') action.connect('activate', activate.bind(action)); if (typeof change_state === 'function') action.connect('change-state', change_state.bind(action)); this.add_action(action); } }; } cjs-128.0/modules/core/overrides/cairo.js0000664000175000017500000000051114771557763017307 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2018 Philip Chimento // This override adds the builtin Cairo bindings to imports.gi.cairo. // (It's confusing to have two incompatible ways to import Cairo.) function _init() { Object.assign(this, imports.cairo); } cjs-128.0/modules/core/overrides/GObject.js0000664000175000017500000010563014771557763017537 0ustar fabiofabio/* exported _init, interfaces, properties, registerClass, requires, signals */ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2011 Jasper St. Pierre // SPDX-FileCopyrightText: 2017 Philip Chimento , const Gi = imports._gi; const {CjsPrivate, GLib} = imports.gi; const {_checkAccessors, _registerType} = imports._common; const Legacy = imports._legacy; let GObject; var GTypeName = Symbol('GType name'); var GTypeFlags = Symbol('GType flags'); var interfaces = Symbol('GObject interfaces'); var properties = Symbol('GObject properties'); var requires = Symbol('GObject interface requires'); var signals = Symbol('GObject signals'); // These four will be aliased to GTK var _gtkChildren = Symbol('GTK widget template children'); var _gtkCssName = Symbol('GTK widget CSS name'); var _gtkInternalChildren = Symbol('GTK widget template internal children'); var _gtkTemplate = Symbol('GTK widget template'); function registerClass(...args) { let klass = args[0]; if (args.length === 2) { // The two-argument form is the convenient syntax without ESnext // decorators and class data properties. The first argument is an // object with meta info such as properties and signals. The second // argument is the class expression for the class itself. // // var MyClass = GObject.registerClass({ // Properties: { ... }, // Signals: { ... }, // }, class MyClass extends GObject.Object { // _init() { ... } // }); // // When decorators and class data properties become part of the JS // standard, this function can be used directly as a decorator. let metaInfo = args[0]; klass = args[1]; if ('GTypeName' in metaInfo) klass[GTypeName] = metaInfo.GTypeName; if ('GTypeFlags' in metaInfo) klass[GTypeFlags] = metaInfo.GTypeFlags; if ('Implements' in metaInfo) klass[interfaces] = metaInfo.Implements; if ('Properties' in metaInfo) klass[properties] = metaInfo.Properties; if ('Signals' in metaInfo) klass[signals] = metaInfo.Signals; if ('Requires' in metaInfo) klass[requires] = metaInfo.Requires; if ('CssName' in metaInfo) klass[_gtkCssName] = metaInfo.CssName; if ('Template' in metaInfo) klass[_gtkTemplate] = metaInfo.Template; if ('Children' in metaInfo) klass[_gtkChildren] = metaInfo.Children; if ('InternalChildren' in metaInfo) klass[_gtkInternalChildren] = metaInfo.InternalChildren; } if (!(klass.prototype instanceof GObject.Object) && !(klass.prototype instanceof GObject.Interface)) { throw new TypeError('GObject.registerClass() used with invalid base ' + `class (is ${Object.getPrototypeOf(klass).name})`); } if ('_classInit' in klass) { klass = klass._classInit(klass); } else { // Lang.Class compatibility. klass = _resolveLegacyClassFunction(klass, '_classInit')(klass); } return klass; } function _resolveLegacyClassFunction(klass, func) { // Find the "least derived" class with a _classInit static function; there // definitely is one, since this class must inherit from GObject let initclass = klass; while (typeof initclass[func] === 'undefined') initclass = Object.getPrototypeOf(initclass.prototype).constructor; return initclass[func]; } function _defineGType(klass, giPrototype, registeredType) { const config = { enumerable: false, configurable: false, }; /** * class Example { * // The JS object for this class' ObjectPrototype * static [Gi.gobject_prototype_symbol] = ... * static get $gtype () { * return ...; * } * } * * // Equal to the same property on the constructor * Example.prototype[Gi.gobject_prototype_symbol] = ... */ Object.defineProperty(klass, '$gtype', { ...config, get() { return registeredType; }, }); Object.defineProperty(klass.prototype, Gi.gobject_prototype_symbol, { ...config, writable: false, value: giPrototype, }); } // Some common functions between GObject.Class and GObject.Interface function _createSignals(gtype, sigs) { for (let signalName in sigs) { let obj = sigs[signalName]; let flags = obj.flags !== undefined ? obj.flags : GObject.SignalFlags.RUN_FIRST; let accumulator = obj.accumulator !== undefined ? obj.accumulator : GObject.AccumulatorType.NONE; let rtype = obj.return_type !== undefined ? obj.return_type : GObject.TYPE_NONE; let paramtypes = obj.param_types !== undefined ? obj.param_types : []; try { obj.signal_id = Gi.signal_new(gtype, signalName, flags, accumulator, rtype, paramtypes); } catch (e) { throw new TypeError(`Invalid signal ${signalName}: ${e.message}`); } } } function _getCallerBasename() { const stackLines = new Error().stack.trim().split('\n'); const lineRegex = new RegExp(/@(.+:\/\/)?(.*\/)?(.+)\.js:\d+(:[\d]+)?$/); let thisFile = null; let thisDir = null; for (let line of stackLines) { let match = line.match(lineRegex); if (match) { let scriptDir = match[2]; let scriptBasename = match[3]; if (!thisFile) { thisDir = scriptDir; thisFile = scriptBasename; continue; } if (scriptDir === thisDir && scriptBasename === thisFile) continue; if (scriptDir && scriptDir.startsWith('/org/cinnamon/cjs/')) continue; let basename = scriptBasename; if (scriptDir) { scriptDir = scriptDir.replace(/^\/|\/$/g, ''); basename = `${scriptDir.split('/').reverse()[0]}_${basename}`; } return basename; } } return null; } function _createGTypeName(klass) { const sanitizeGType = s => s.replace(/[^a-z0-9+_-]/gi, '_'); if (Object.hasOwn(klass, GTypeName)) { let sanitized = sanitizeGType(klass[GTypeName]); if (sanitized !== klass[GTypeName]) { logError(new RangeError(`Provided GType name '${klass[GTypeName]}' ` + `is not valid; automatically sanitized to '${sanitized}'`)); } return sanitized; } let gtypeClassName = klass.name; if (GObject.gtypeNameBasedOnJSPath) { let callerBasename = _getCallerBasename(); if (callerBasename) gtypeClassName = `${callerBasename}_${gtypeClassName}`; } if (gtypeClassName === '') gtypeClassName = `anonymous_${GLib.uuid_string_random()}`; return sanitizeGType(`Gjs_${gtypeClassName}`); } function _propertiesAsArray(klass) { let propertiesArray = []; if (Object.hasOwn(klass, properties)) { for (let prop in klass[properties]) propertiesArray.push(klass[properties][prop]); } return propertiesArray; } function _copyInterfacePrototypeDescriptors(targetPrototype, sourceInterface) { Object.entries(Object.getOwnPropertyDescriptors(sourceInterface)) .filter(([key, descriptor]) => // Don't attempt to copy the constructor or toString implementations !['constructor', 'toString'].includes(key) && // Ignore properties starting with __ (typeof key !== 'string' || !key.startsWith('__')) && // Don't override an implementation on the target !Object.hasOwn(targetPrototype, key) && descriptor && // Only copy if the descriptor has a getter, is a function, or is enumerable. (typeof descriptor.value === 'function' || descriptor.get || descriptor.enumerable)) .forEach(([key, descriptor]) => { Object.defineProperty(targetPrototype, key, descriptor); }); } function _interfacePresent(required, klass) { if (!klass[interfaces]) return false; if (klass[interfaces].includes(required)) return true; // implemented here // Might be implemented on a parent class return _interfacePresent(required, Object.getPrototypeOf(klass)); } function _checkInterface(iface, proto) { // Checks for specific interfaces // Default vfunc_async_init() will run vfunc_init() in a thread and crash. // Change error message when https://gitlab.gnome.org/GNOME/gjs/issues/72 // has been solved. if (iface.$gtype.name === 'GAsyncInitable' && !Object.getOwnPropertyNames(proto).includes('vfunc_init_async')) throw new Error("It's not currently possible to implement Gio.AsyncInitable."); // Check that proto implements all of this interface's required interfaces. // "proto" refers to the object's prototype (which implements the interface) // whereas "iface.prototype" is the interface's prototype (which may still // contain unimplemented methods.) if (typeof iface[requires] === 'undefined') return; let unfulfilledReqs = iface[requires].filter(required => { // Either the interface is not present or it is not listed before the // interface that requires it or the class does not inherit it. This is // so that required interfaces don't copy over properties from other // interfaces that require them. let ifaces = proto.constructor[interfaces]; return (!_interfacePresent(required, proto.constructor) || ifaces.indexOf(required) > ifaces.indexOf(iface)) && !(proto instanceof required); }).map(required => // required.name will be present on JS classes, but on introspected // GObjects it will be the C name. The alternative is just so that // we print something if there is garbage in Requires. required.name || required); if (unfulfilledReqs.length > 0) { throw new Error('The following interfaces must be implemented before ' + `${iface.name}: ${unfulfilledReqs.join(', ')}`); } } function _registerGObjectType(klass) { const gtypename = _createGTypeName(klass); const gflags = Object.hasOwn(klass, GTypeFlags) ? klass[GTypeFlags] : 0; const gobjectInterfaces = Object.hasOwn(klass, interfaces) ? klass[interfaces] : []; const propertiesArray = _propertiesAsArray(klass); const parent = Object.getPrototypeOf(klass); const gobjectSignals = Object.hasOwn(klass, signals) ? klass[signals] : []; // Default to the GObject-specific prototype, fallback on the JS prototype // for GI native classes. const parentPrototype = parent.prototype[Gi.gobject_prototype_symbol] ?? parent.prototype; const [giPrototype, registeredType] = Gi.register_type_with_class(klass, parentPrototype, gtypename, gflags, gobjectInterfaces, propertiesArray); _defineGType(klass, giPrototype, registeredType); _createSignals(klass.$gtype, gobjectSignals); // Reverse the interface array to give the last required interface // precedence over the first. const requiredInterfaces = [...gobjectInterfaces].reverse(); requiredInterfaces.forEach(iface => _copyInterfacePrototypeDescriptors(klass, iface)); requiredInterfaces.forEach(iface => _copyInterfacePrototypeDescriptors(klass.prototype, iface.prototype)); Object.getOwnPropertyNames(klass.prototype) .filter(name => name.startsWith('vfunc_') || name.startsWith('on_')) .forEach(name => { let descr = Object.getOwnPropertyDescriptor(klass.prototype, name); if (typeof descr.value !== 'function') return; let func = klass.prototype[name]; if (name.startsWith('vfunc_')) { giPrototype[Gi.hook_up_vfunc_symbol](name.slice(6), func); } else if (name.startsWith('on_')) { let id = GObject.signal_lookup(name.slice(3).replace('_', '-'), klass.$gtype); if (id !== 0) { GObject.signal_override_class_closure(id, klass.$gtype, function (...argArray) { let emitter = argArray.shift(); return func.apply(emitter, argArray); }); } } }); gobjectInterfaces.forEach(iface => _checkInterface(iface, klass.prototype)); // Lang.Class parent classes don't support static inheritance if (!('implements' in klass)) klass.implements = GObject.Object.implements; } function _interfaceInstanceOf(instance) { if (instance && typeof instance === 'object' && GObject.Interface.prototype.isPrototypeOf(this.prototype)) return GObject.type_is_a(instance, this); return false; } function _registerInterfaceType(klass) { const gtypename = _createGTypeName(klass); const gobjectInterfaces = Object.hasOwn(klass, requires) ? klass[requires] : []; const props = _propertiesAsArray(klass); const gobjectSignals = Object.hasOwn(klass, signals) ? klass[signals] : []; const [giPrototype, registeredType] = Gi.register_interface_with_class( klass, gtypename, gobjectInterfaces, props); _defineGType(klass, giPrototype, registeredType); _createSignals(klass.$gtype, gobjectSignals); Object.defineProperty(klass, Symbol.hasInstance, { value: _interfaceInstanceOf, }); } function _checkProperties(klass) { if (!Object.hasOwn(klass, properties)) return; for (let pspec of Object.values(klass[properties])) _checkAccessors(klass.prototype, pspec, GObject); } function _init() { GObject = this; function _makeDummyClass(obj, name, upperName, gtypeName, actual) { let gtype = GObject.type_from_name(gtypeName); obj[`TYPE_${upperName}`] = gtype; obj[name] = function (v) { return actual(v); }; obj[name].$gtype = gtype; } GObject.gtypeNameBasedOnJSPath = false; _makeDummyClass(GObject, 'VoidType', 'NONE', 'void', function () {}); _makeDummyClass(GObject, 'Char', 'CHAR', 'gchar', Number); _makeDummyClass(GObject, 'UChar', 'UCHAR', 'guchar', Number); _makeDummyClass(GObject, 'Unichar', 'UNICHAR', 'gint', String); GObject.TYPE_BOOLEAN = GObject.type_from_name('gboolean'); GObject.Boolean = Boolean; Boolean.$gtype = GObject.TYPE_BOOLEAN; _makeDummyClass(GObject, 'Int', 'INT', 'gint', Number); _makeDummyClass(GObject, 'UInt', 'UINT', 'guint', Number); _makeDummyClass(GObject, 'Long', 'LONG', 'glong', Number); _makeDummyClass(GObject, 'ULong', 'ULONG', 'gulong', Number); _makeDummyClass(GObject, 'Int64', 'INT64', 'gint64', Number); _makeDummyClass(GObject, 'UInt64', 'UINT64', 'guint64', Number); GObject.TYPE_ENUM = GObject.type_from_name('GEnum'); GObject.TYPE_FLAGS = GObject.type_from_name('GFlags'); _makeDummyClass(GObject, 'Float', 'FLOAT', 'gfloat', Number); GObject.TYPE_DOUBLE = GObject.type_from_name('gdouble'); GObject.Double = Number; Number.$gtype = GObject.TYPE_DOUBLE; GObject.TYPE_STRING = GObject.type_from_name('gchararray'); GObject.String = String; String.$gtype = GObject.TYPE_STRING; GObject.TYPE_JSOBJECT = GObject.type_from_name('JSObject'); GObject.JSObject = Object; Object.$gtype = GObject.TYPE_JSOBJECT; GObject.TYPE_POINTER = GObject.type_from_name('gpointer'); GObject.TYPE_BOXED = GObject.type_from_name('GBoxed'); GObject.TYPE_PARAM = GObject.type_from_name('GParam'); GObject.TYPE_INTERFACE = GObject.type_from_name('GInterface'); GObject.TYPE_OBJECT = GObject.type_from_name('GObject'); GObject.TYPE_VARIANT = GObject.type_from_name('GVariant'); _makeDummyClass(GObject, 'Type', 'GTYPE', 'GType', GObject.type_from_name); GObject.ParamSpec.char = function (name, nick, blurb, flags, minimum, maximum, defaultValue) { return GObject.param_spec_char(name, nick, blurb, minimum, maximum, defaultValue, flags); }; GObject.ParamSpec.uchar = function (name, nick, blurb, flags, minimum, maximum, defaultValue) { return GObject.param_spec_uchar(name, nick, blurb, minimum, maximum, defaultValue, flags); }; GObject.ParamSpec.int = function (name, nick, blurb, flags, minimum, maximum, defaultValue) { return GObject.param_spec_int(name, nick, blurb, minimum, maximum, defaultValue, flags); }; GObject.ParamSpec.uint = function (name, nick, blurb, flags, minimum, maximum, defaultValue) { return GObject.param_spec_uint(name, nick, blurb, minimum, maximum, defaultValue, flags); }; GObject.ParamSpec.long = function (name, nick, blurb, flags, minimum, maximum, defaultValue) { return GObject.param_spec_long(name, nick, blurb, minimum, maximum, defaultValue, flags); }; GObject.ParamSpec.ulong = function (name, nick, blurb, flags, minimum, maximum, defaultValue) { return GObject.param_spec_ulong(name, nick, blurb, minimum, maximum, defaultValue, flags); }; GObject.ParamSpec.int64 = function (name, nick, blurb, flags, minimum, maximum, defaultValue) { return GObject.param_spec_int64(name, nick, blurb, minimum, maximum, defaultValue, flags); }; GObject.ParamSpec.uint64 = function (name, nick, blurb, flags, minimum, maximum, defaultValue) { return GObject.param_spec_uint64(name, nick, blurb, minimum, maximum, defaultValue, flags); }; GObject.ParamSpec.float = function (name, nick, blurb, flags, minimum, maximum, defaultValue) { return GObject.param_spec_float(name, nick, blurb, minimum, maximum, defaultValue, flags); }; GObject.ParamSpec.boolean = function (name, nick, blurb, flags, defaultValue) { return GObject.param_spec_boolean(name, nick, blurb, defaultValue, flags); }; GObject.ParamSpec.flags = function (name, nick, blurb, flags, flagsType, defaultValue) { return GObject.param_spec_flags(name, nick, blurb, flagsType, defaultValue, flags); }; GObject.ParamSpec.enum = function (name, nick, blurb, flags, enumType, defaultValue) { return GObject.param_spec_enum(name, nick, blurb, enumType, defaultValue, flags); }; GObject.ParamSpec.double = function (name, nick, blurb, flags, minimum, maximum, defaultValue) { return GObject.param_spec_double(name, nick, blurb, minimum, maximum, defaultValue, flags); }; GObject.ParamSpec.string = function (name, nick, blurb, flags, defaultValue) { return GObject.param_spec_string(name, nick, blurb, defaultValue, flags); }; GObject.ParamSpec.boxed = function (name, nick, blurb, flags, boxedType) { return GObject.param_spec_boxed(name, nick, blurb, boxedType, flags); }; GObject.ParamSpec.object = function (name, nick, blurb, flags, objectType) { return GObject.param_spec_object(name, nick, blurb, objectType, flags); }; GObject.ParamSpec.jsobject = function (name, nick, blurb, flags) { return GObject.param_spec_boxed(name, nick, blurb, Object.$gtype, flags); }; GObject.ParamSpec.param = function (name, nick, blurb, flags, paramType) { return GObject.param_spec_param(name, nick, blurb, paramType, flags); }; GObject.ParamSpec.override = Gi.override_property; Object.defineProperties(GObject.ParamSpec.prototype, { 'name': { configurable: false, enumerable: false, get() { return this.get_name(); }, }, '_nick': { configurable: false, enumerable: false, get() { return this.get_nick(); }, }, 'nick': { configurable: false, enumerable: false, get() { return this.get_nick(); }, }, '_blurb': { configurable: false, enumerable: false, get() { return this.get_blurb(); }, }, 'blurb': { configurable: false, enumerable: false, get() { return this.get_blurb(); }, }, 'default_value': { configurable: false, enumerable: false, get() { return this.get_default_value(); }, }, 'flags': { configurable: false, enumerable: false, get() { return CjsPrivate.param_spec_get_flags(this); }, }, 'value_type': { configurable: false, enumerable: false, get() { return CjsPrivate.param_spec_get_value_type(this); }, }, 'owner_type': { configurable: false, enumerable: false, get() { return CjsPrivate.param_spec_get_owner_type(this); }, }, }); let {GObjectMeta, GObjectInterface} = Legacy.defineGObjectLegacyObjects(GObject); GObject.Class = GObjectMeta; GObject.Interface = GObjectInterface; GObject.Object.prototype.__metaclass__ = GObject.Class; // For compatibility with Lang.Class... we need a _construct // or the Lang.Class constructor will fail. GObject.Object.prototype._construct = function (...args) { this._init(...args); return this; }; GObject.registerClass = registerClass; GObject.Object.new = function (gtype, props = {}) { const constructor = Gi.lookupConstructor(gtype); if (!constructor) throw new Error(`Constructor for gtype ${gtype} not found`); return new constructor(props); }; GObject.Object.new_with_properties = function (gtype, names, values) { if (!Array.isArray(names) || !Array.isArray(values)) throw new Error('new_with_properties takes two arrays (names, values)'); if (names.length !== values.length) throw new Error('Arrays passed to new_with_properties must be the same length'); const props = Object.fromEntries(names.map((name, ix) => [name, values[ix]])); return GObject.Object.new(gtype, props); }; GObject.Object._classInit = function (klass) { _checkProperties(klass); if (_registerType in klass) klass[_registerType](klass); else _resolveLegacyClassFunction(klass, _registerType)(klass); return klass; }; // For backwards compatibility only. Use instanceof instead. GObject.Object.implements = function (iface) { if (iface.$gtype) return GObject.type_is_a(this, iface.$gtype); return false; }; Object.defineProperty(GObject.Object, _registerType, { value: _registerGObjectType, writable: false, configurable: false, enumerable: false, }); Object.defineProperty(GObject.Interface, _registerType, { value: _registerInterfaceType, writable: false, configurable: false, enumerable: false, }); GObject.Interface._classInit = function (klass) { if (_registerType in klass) klass[_registerType](klass); else _resolveLegacyClassFunction(klass, _registerType)(klass); Object.getOwnPropertyNames(klass.prototype) .filter(key => key !== 'constructor') .concat(Object.getOwnPropertySymbols(klass.prototype)) .forEach(key => { let descr = Object.getOwnPropertyDescriptor(klass.prototype, key); // Create wrappers on the interface object so that generics work (e.g. // SomeInterface.some_function(this, blah) instead of // SomeInterface.prototype.some_function.call(this, blah) if (typeof descr.value === 'function') { let interfaceProto = klass.prototype; // capture in closure klass[key] = function (thisObj, ...args) { return interfaceProto[key].call(thisObj, ...args); }; } Object.defineProperty(klass.prototype, key, descr); }); return klass; }; /** * Use this to signify a function that must be overridden in an * implementation of the interface. */ GObject.NotImplementedError = class NotImplementedError extends Error { get name() { return 'NotImplementedError'; } }; // These will be copied in the Gtk overrides // Use __X__ syntax to indicate these variables should not be used publicly. GObject.__gtkCssName__ = _gtkCssName; GObject.__gtkTemplate__ = _gtkTemplate; GObject.__gtkChildren__ = _gtkChildren; GObject.__gtkInternalChildren__ = _gtkInternalChildren; // Expose GObject static properties for ES6 classes GObject.GTypeName = GTypeName; GObject.requires = requires; GObject.interfaces = interfaces; GObject.properties = properties; GObject.signals = signals; // Replacement for non-introspectable g_object_set() GObject.Object.prototype.set = function (params) { Object.assign(this, params); }; GObject.Object.prototype.bind_property_full = function (...args) { return CjsPrivate.g_object_bind_property_full(this, ...args); }; if (GObject.BindingGroup !== undefined) { GObject.BindingGroup.prototype.bind_full = function (...args) { return CjsPrivate.g_binding_group_bind_full(this, ...args); }; } // fake enum for signal accumulators, keep in sync with gi/object.c GObject.AccumulatorType = { NONE: 0, FIRST_WINS: 1, TRUE_HANDLED: 2, }; GObject.Object.prototype.disconnect = function (id) { return GObject.signal_handler_disconnect(this, id); }; GObject.Object.prototype.block_signal_handler = function (id) { return GObject.signal_handler_block(this, id); }; GObject.Object.prototype.unblock_signal_handler = function (id) { return GObject.signal_handler_unblock(this, id); }; GObject.Object.prototype.stop_emission_by_name = function (detailedName) { return GObject.signal_stop_emission_by_name(this, detailedName); }; // A simple workaround if you have a class with .connect, .disconnect or .emit // methods (such as Gio.Socket.connect or NMClient.Device.disconnect) // The original g_signal_* functions are not introspectable anyway, because // we need our own handling of signal argument marshalling GObject.signal_connect = function (object, name, handler) { return GObject.Object.prototype.connect.call(object, name, handler); }; GObject.signal_connect_after = function (object, name, handler) { return GObject.Object.prototype.connect_after.call(object, name, handler); }; GObject.signal_emit_by_name = function (object, ...nameAndArgs) { return GObject.Object.prototype.emit.apply(object, nameAndArgs); }; // Replacements for signal_handler_find() and similar functions, which can't // work normally since we connect private closures GObject._real_signal_handler_find = GObject.signal_handler_find; GObject._real_signal_handlers_block_matched = GObject.signal_handlers_block_matched; GObject._real_signal_handlers_unblock_matched = GObject.signal_handlers_unblock_matched; GObject._real_signal_handlers_disconnect_matched = GObject.signal_handlers_disconnect_matched; /** * Finds the first signal handler that matches certain selection criteria. * The criteria are passed as properties of a match object. * The match object has to be non-empty for successful matches. * If no handler was found, a falsy value is returned. * * @function * @param {GObject.Object} instance - the instance owning the signal handler * to be found. * @param {object} match - a properties object indicating whether to match * by signal ID, detail, or callback function. * @param {string} [match.signalId] - signal the handler has to be connected * to. * @param {string} [match.detail] - signal detail the handler has to be * connected to. * @param {Function} [match.func] - the callback function the handler will * invoke. * @returns {number | bigint | object | null} A valid non-0 signal handler ID for * a successful match. */ GObject.signal_handler_find = function (instance, match) { // For backwards compatibility if (arguments.length === 7) // eslint-disable-next-line prefer-rest-params return GObject._real_signal_handler_find(...arguments); return instance[Gi.signal_find_symbol](match); }; /** * Blocks all handlers on an instance that match certain selection criteria. * The criteria are passed as properties of a match object. * The match object has to have at least `func` for successful matches. * If no handlers were found, 0 is returned, the number of blocked handlers * otherwise. * * @function * @param {GObject.Object} instance - the instance owning the signal handler * to be found. * @param {object} match - a properties object indicating whether to match * by signal ID, detail, or callback function. * @param {string} [match.signalId] - signal the handler has to be connected * to. * @param {string} [match.detail] - signal detail the handler has to be * connected to. * @param {Function} match.func - the callback function the handler will * invoke. * @returns {number} The number of handlers that matched. */ GObject.signal_handlers_block_matched = function (instance, match) { // For backwards compatibility if (arguments.length === 7) // eslint-disable-next-line prefer-rest-params return GObject._real_signal_handlers_block_matched(...arguments); return instance[Gi.signals_block_symbol](match); }; /** * Unblocks all handlers on an instance that match certain selection * criteria. * The criteria are passed as properties of a match object. * The match object has to have at least `func` for successful matches. * If no handlers were found, 0 is returned, the number of unblocked * handlers otherwise. * The match criteria should not apply to any handlers that are not * currently blocked. * * @function * @param {GObject.Object} instance - the instance owning the signal handler * to be found. * @param {object} match - a properties object indicating whether to match * by signal ID, detail, or callback function. * @param {string} [match.signalId] - signal the handler has to be connected * to. * @param {string} [match.detail] - signal detail the handler has to be * connected to. * @param {Function} match.func - the callback function the handler will * invoke. * @returns {number} The number of handlers that matched. */ GObject.signal_handlers_unblock_matched = function (instance, match) { // For backwards compatibility if (arguments.length === 7) // eslint-disable-next-line prefer-rest-params return GObject._real_signal_handlers_unblock_matched(...arguments); return instance[Gi.signals_unblock_symbol](match); }; /** * Disconnects all handlers on an instance that match certain selection * criteria. * The criteria are passed as properties of a match object. * The match object has to have at least `func` for successful matches. * If no handlers were found, 0 is returned, the number of disconnected * handlers otherwise. * * @function * @param {GObject.Object} instance - the instance owning the signal handler * to be found. * @param {object} match - a properties object indicating whether to match * by signal ID, detail, or callback function. * @param {string} [match.signalId] - signal the handler has to be connected * to. * @param {string} [match.detail] - signal detail the handler has to be * connected to. * @param {Function} match.func - the callback function the handler will * invoke. * @returns {number} The number of handlers that matched. */ GObject.signal_handlers_disconnect_matched = function (instance, match) { // For backwards compatibility if (arguments.length === 7) // eslint-disable-next-line prefer-rest-params return GObject._real_signal_handlers_disconnect_matched(...arguments); return instance[Gi.signals_disconnect_symbol](match); }; // Also match the macros used in C APIs, even though they're not introspected /** * Blocks all handlers on an instance that match `func`. * * @function * @param {GObject.Object} instance - the instance to block handlers from. * @param {Function} func - the callback function the handler will invoke. * @returns {number} The number of handlers that matched. */ GObject.signal_handlers_block_by_func = function (instance, func) { return instance[Gi.signals_block_symbol]({func}); }; /** * Unblocks all handlers on an instance that match `func`. * * @function * @param {GObject.Object} instance - the instance to unblock handlers from. * @param {Function} func - the callback function the handler will invoke. * @returns {number} The number of handlers that matched. */ GObject.signal_handlers_unblock_by_func = function (instance, func) { return instance[Gi.signals_unblock_symbol]({func}); }; /** * Disconnects all handlers on an instance that match `func`. * * @function * @param {GObject.Object} instance - the instance to remove handlers from. * @param {Function} func - the callback function the handler will invoke. * @returns {number} The number of handlers that matched. */ GObject.signal_handlers_disconnect_by_func = function (instance, func) { return instance[Gi.signals_disconnect_symbol]({func}); }; GObject.signal_handlers_disconnect_by_data = function () { throw new Error('GObject.signal_handlers_disconnect_by_data() is not \ introspectable. Use GObject.signal_handlers_disconnect_by_func() instead.'); }; function unsupportedDataMethod() { throw new Error('Data access methods are unsupported. Use normal JS properties instead.'); } GObject.Object.prototype.get_data = unsupportedDataMethod; GObject.Object.prototype.get_qdata = unsupportedDataMethod; GObject.Object.prototype.set_data = unsupportedDataMethod; GObject.Object.prototype.steal_data = unsupportedDataMethod; GObject.Object.prototype.steal_qdata = unsupportedDataMethod; function unsupportedRefcountingMethod() { throw new Error("Don't modify an object's reference count in JS."); } GObject.Object.prototype.force_floating = unsupportedRefcountingMethod; GObject.Object.prototype.ref = unsupportedRefcountingMethod; GObject.Object.prototype.ref_sink = unsupportedRefcountingMethod; GObject.Object.prototype.unref = unsupportedRefcountingMethod; } cjs-128.0/modules/cairo-region.cpp0000664000175000017500000002344614771557763016020 0ustar fabiofabio/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2014 Red Hat, Inc. #include #include #include #include #include #include #include // for JSPROP_READONLY #include #include #include #include #include // for JS_NewPlainObject #include "gi/arg-inl.h" #include "gi/arg.h" #include "gi/foreign.h" #include "cjs/atoms.h" #include "cjs/context-private.h" #include "cjs/enum-utils.h" #include "cjs/jsapi-util-args.h" #include "cjs/jsapi-util.h" #include "cjs/macros.h" #include "modules/cairo-private.h" GJS_JSAPI_RETURN_CONVENTION static bool fill_rectangle(JSContext *context, JS::HandleObject obj, cairo_rectangle_int_t *rect); #define PRELUDE \ GJS_GET_THIS(context, argc, vp, argv, obj); \ cairo_region_t* this_region; \ if (!CairoRegion::for_js_typecheck(context, obj, &this_region, &argv)) \ return false; #define RETURN_STATUS \ return gjs_cairo_check_status(context, cairo_region_status(this_region), "region"); #define REGION_DEFINE_REGION_FUNC(method) \ GJS_JSAPI_RETURN_CONVENTION \ static bool method##_func(JSContext* context, unsigned argc, \ JS::Value* vp) { \ PRELUDE; \ JS::RootedObject other_obj(context); \ if (!gjs_parse_call_args(context, #method, argv, "o", "other_region", \ &other_obj)) \ return false; \ \ cairo_region_t* other_region = \ CairoRegion::for_js(context, other_obj); \ \ cairo_region_##method(this_region, other_region); \ argv.rval().setUndefined(); \ RETURN_STATUS; \ } #define REGION_DEFINE_RECT_FUNC(method) \ GJS_JSAPI_RETURN_CONVENTION \ static bool method##_rectangle_func(JSContext* context, unsigned argc, \ JS::Value* vp) { \ PRELUDE; \ JS::RootedObject rect_obj(context); \ if (!gjs_parse_call_args(context, #method, argv, "o", "rect", \ &rect_obj)) \ return false; \ \ cairo_rectangle_int_t rect; \ if (!fill_rectangle(context, rect_obj, &rect)) \ return false; \ \ cairo_region_##method##_rectangle(this_region, &rect); \ argv.rval().setUndefined(); \ RETURN_STATUS; \ } REGION_DEFINE_REGION_FUNC(union) REGION_DEFINE_REGION_FUNC(subtract) REGION_DEFINE_REGION_FUNC(intersect) REGION_DEFINE_REGION_FUNC(xor) REGION_DEFINE_RECT_FUNC(union) REGION_DEFINE_RECT_FUNC(subtract) REGION_DEFINE_RECT_FUNC(intersect) REGION_DEFINE_RECT_FUNC(xor) GJS_JSAPI_RETURN_CONVENTION static bool fill_rectangle(JSContext *context, JS::HandleObject obj, cairo_rectangle_int_t *rect) { const GjsAtoms& atoms = GjsContextPrivate::atoms(context); JS::RootedValue val(context); if (!JS_GetPropertyById(context, obj, atoms.x(), &val)) return false; if (!JS::ToInt32(context, val, &rect->x)) return false; if (!JS_GetPropertyById(context, obj, atoms.y(), &val)) return false; if (!JS::ToInt32(context, val, &rect->y)) return false; if (!JS_GetPropertyById(context, obj, atoms.width(), &val)) return false; if (!JS::ToInt32(context, val, &rect->width)) return false; if (!JS_GetPropertyById(context, obj, atoms.height(), &val)) return false; if (!JS::ToInt32(context, val, &rect->height)) return false; return true; } GJS_JSAPI_RETURN_CONVENTION static JSObject * make_rectangle(JSContext *context, cairo_rectangle_int_t *rect) { const GjsAtoms& atoms = GjsContextPrivate::atoms(context); JS::RootedObject rect_obj(context, JS_NewPlainObject(context)); if (!rect_obj) return nullptr; JS::RootedValue val(context); val = JS::Int32Value(rect->x); if (!JS_SetPropertyById(context, rect_obj, atoms.x(), val)) return nullptr; val = JS::Int32Value(rect->y); if (!JS_SetPropertyById(context, rect_obj, atoms.y(), val)) return nullptr; val = JS::Int32Value(rect->width); if (!JS_SetPropertyById(context, rect_obj, atoms.width(), val)) return nullptr; val = JS::Int32Value(rect->height); if (!JS_SetPropertyById(context, rect_obj, atoms.height(), val)) return nullptr; return rect_obj; } GJS_JSAPI_RETURN_CONVENTION static bool num_rectangles_func(JSContext *context, unsigned argc, JS::Value *vp) { PRELUDE; int n_rects; if (!gjs_parse_call_args(context, "num_rectangles", argv, "")) return false; n_rects = cairo_region_num_rectangles(this_region); argv.rval().setInt32(n_rects); RETURN_STATUS; } GJS_JSAPI_RETURN_CONVENTION static bool get_rectangle_func(JSContext *context, unsigned argc, JS::Value *vp) { PRELUDE; int i; JSObject *rect_obj; cairo_rectangle_int_t rect; if (!gjs_parse_call_args(context, "get_rectangle", argv, "i", "rect", &i)) return false; cairo_region_get_rectangle(this_region, i, &rect); rect_obj = make_rectangle(context, &rect); argv.rval().setObjectOrNull(rect_obj); RETURN_STATUS; } // clang-format off const JSPropertySpec CairoRegion::proto_props[] = { JS_STRING_SYM_PS(toStringTag, "Region", JSPROP_READONLY), JS_PS_END}; // clang-format on const JSFunctionSpec CairoRegion::proto_funcs[] = { JS_FN("union", union_func, 0, 0), JS_FN("subtract", subtract_func, 0, 0), JS_FN("intersect", intersect_func, 0, 0), JS_FN("xor", xor_func, 0, 0), JS_FN("unionRectangle", union_rectangle_func, 0, 0), JS_FN("subtractRectangle", subtract_rectangle_func, 0, 0), JS_FN("intersectRectangle", intersect_rectangle_func, 0, 0), JS_FN("xorRectangle", xor_rectangle_func, 0, 0), JS_FN("numRectangles", num_rectangles_func, 0, 0), JS_FN("getRectangle", get_rectangle_func, 0, 0), JS_FS_END}; cairo_region_t* CairoRegion::constructor_impl(JSContext* context, const JS::CallArgs& argv) { if (!gjs_parse_call_args(context, "Region", argv, "")) return nullptr; return cairo_region_create(); } void CairoRegion::finalize_impl(JS::GCContext*, cairo_region_t* region) { if (!region) return; cairo_region_destroy(region); } [[nodiscard]] static bool region_to_gi_argument( JSContext* context, JS::Value value, const char* arg_name, GjsArgumentType argument_type, GITransfer transfer, GjsArgumentFlags flags, GIArgument* arg) { if (value.isNull()) { if (!(flags & GjsArgumentFlags::MAY_BE_NULL)) { GjsAutoChar display_name = gjs_argument_display_name(arg_name, argument_type); gjs_throw(context, "%s may not be null", display_name.get()); return false; } gjs_arg_unset(arg); return true; } JS::RootedObject obj(context, &value.toObject()); cairo_region_t *region; if (!CairoRegion::for_js_typecheck(context, obj, ®ion)) return false; if (transfer == GI_TRANSFER_EVERYTHING) cairo_region_destroy(region); gjs_arg_set(arg, region); return true; } GJS_JSAPI_RETURN_CONVENTION static bool region_from_gi_argument(JSContext* context, JS::MutableHandleValue value_p, GIArgument* arg) { JSObject* obj = CairoRegion::from_c_ptr(context, gjs_arg_get(arg)); if (!obj) return false; value_p.setObject(*obj); return true; } static bool region_release_argument(JSContext*, GITransfer transfer, GIArgument* arg) { if (transfer != GI_TRANSFER_NOTHING) cairo_region_destroy(gjs_arg_get(arg)); return true; } void gjs_cairo_region_init(void) { static GjsForeignInfo foreign_info = {region_to_gi_argument, region_from_gi_argument, region_release_argument}; gjs_struct_foreign_register("cairo", "Region", &foreign_info); } cjs-128.0/modules/cairo-image-surface.cpp0000664000175000017500000001267214771557763017244 0ustar fabiofabio/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2010 litl, LLC. #include #include #include #include // for JSPROP_READONLY #include #include #include #include // for JS_NewObjectWithGivenProto #include // for JSProtoKey #include "cjs/jsapi-util-args.h" #include "cjs/jsapi-util.h" #include "cjs/macros.h" #include "modules/cairo-private.h" JSObject* CairoImageSurface::new_proto(JSContext* cx, JSProtoKey) { JS::RootedObject parent_proto(cx, CairoSurface::prototype(cx)); return JS_NewObjectWithGivenProto(cx, nullptr, parent_proto); } cairo_surface_t* CairoImageSurface::constructor_impl(JSContext* context, const JS::CallArgs& argv) { int format, width, height; cairo_surface_t *surface; // create_for_data optional parameter if (!gjs_parse_call_args(context, "ImageSurface", argv, "iii", "format", &format, "width", &width, "height", &height)) return nullptr; surface = cairo_image_surface_create((cairo_format_t) format, width, height); if (!gjs_cairo_check_status(context, cairo_surface_status(surface), "surface")) return nullptr; return surface; } // clang-format off const JSPropertySpec CairoImageSurface::proto_props[] = { JS_STRING_SYM_PS(toStringTag, "ImageSurface", JSPROP_READONLY), JS_PS_END}; // clang-format on GJS_JSAPI_RETURN_CONVENTION static bool createFromPNG_func(JSContext *context, unsigned argc, JS::Value *vp) { JS::CallArgs argv = JS::CallArgsFromVp (argc, vp); GjsAutoChar filename; cairo_surface_t *surface; if (!gjs_parse_call_args(context, "createFromPNG", argv, "F", "filename", &filename)) return false; surface = cairo_image_surface_create_from_png(filename); if (!gjs_cairo_check_status(context, cairo_surface_status(surface), "surface")) return false; JSObject* surface_wrapper = CairoImageSurface::from_c_ptr(context, surface); if (!surface_wrapper) return false; cairo_surface_destroy(surface); argv.rval().setObject(*surface_wrapper); return true; } GJS_JSAPI_RETURN_CONVENTION static bool getFormat_func(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_THIS(context, argc, vp, rec, obj); cairo_format_t format; if (argc > 1) { gjs_throw(context, "ImageSurface.getFormat() takes no arguments"); return false; } cairo_surface_t* surface = CairoSurface::for_js(context, obj); if (!surface) return false; format = cairo_image_surface_get_format(surface); if (!gjs_cairo_check_status(context, cairo_surface_status(surface), "surface")) return false; rec.rval().setInt32(format); return true; } GJS_JSAPI_RETURN_CONVENTION static bool getWidth_func(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_THIS(context, argc, vp, rec, obj); int width; if (argc > 1) { gjs_throw(context, "ImageSurface.getWidth() takes no arguments"); return false; } cairo_surface_t* surface = CairoSurface::for_js(context, obj); if (!surface) return false; width = cairo_image_surface_get_width(surface); if (!gjs_cairo_check_status(context, cairo_surface_status(surface), "surface")) return false; rec.rval().setInt32(width); return true; } GJS_JSAPI_RETURN_CONVENTION static bool getHeight_func(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_THIS(context, argc, vp, rec, obj); int height; if (argc > 1) { gjs_throw(context, "ImageSurface.getHeight() takes no arguments"); return false; } cairo_surface_t* surface = CairoSurface::for_js(context, obj); if (!surface) return false; height = cairo_image_surface_get_height(surface); if (!gjs_cairo_check_status(context, cairo_surface_status(surface), "surface")) return false; rec.rval().setInt32(height); return true; } GJS_JSAPI_RETURN_CONVENTION static bool getStride_func(JSContext *context, unsigned argc, JS::Value *vp) { GJS_GET_THIS(context, argc, vp, rec, obj); int stride; if (argc > 1) { gjs_throw(context, "ImageSurface.getStride() takes no arguments"); return false; } cairo_surface_t* surface = CairoSurface::for_js(context, obj); if (!surface) return false; stride = cairo_image_surface_get_stride(surface); if (!gjs_cairo_check_status(context, cairo_surface_status(surface), "surface")) return false; rec.rval().setInt32(stride); return true; } const JSFunctionSpec CairoImageSurface::proto_funcs[] = { JS_FN("createFromPNG", createFromPNG_func, 0, 0), // getData JS_FN("getFormat", getFormat_func, 0, 0), JS_FN("getWidth", getWidth_func, 0, 0), JS_FN("getHeight", getHeight_func, 0, 0), JS_FN("getStride", getStride_func, 0, 0), JS_FS_END}; const JSFunctionSpec CairoImageSurface::static_funcs[] = { JS_FN("createFromPNG", createFromPNG_func, 1, GJS_MODULE_PROP_FLAGS), JS_FS_END}; cjs-128.0/modules/cairo-svg-surface.cpp0000664000175000017500000000430314771557763016751 0ustar fabiofabio/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2010 litl, LLC. // SPDX-FileCopyrightText: 2020 Philip Chimento #include #include // for CAIRO_HAS_SVG_SURFACE #include #if CAIRO_HAS_SVG_SURFACE # include #endif #include #if CAIRO_HAS_SVG_SURFACE # include // for JSPROP_READONLY # include # include # include // for JS_NewObjectWithGivenProto # include // for JSProtoKey #endif #include "cjs/jsapi-util.h" #if CAIRO_HAS_SVG_SURFACE # include "cjs/jsapi-util-args.h" # include "modules/cairo-private.h" namespace JS { class CallArgs; } JSObject* CairoSVGSurface::new_proto(JSContext* cx, JSProtoKey) { JS::RootedObject parent_proto(cx, CairoSurface::prototype(cx)); return JS_NewObjectWithGivenProto(cx, nullptr, parent_proto); } cairo_surface_t* CairoSVGSurface::constructor_impl(JSContext* context, const JS::CallArgs& argv) { GjsAutoChar filename; double width, height; cairo_surface_t *surface; if (!gjs_parse_call_args(context, "SVGSurface", argv, "Fff", "filename", &filename, "width", &width, "height", &height)) return nullptr; surface = cairo_svg_surface_create(filename, width, height); if (!gjs_cairo_check_status(context, cairo_surface_status(surface), "surface")) return nullptr; return surface; } // clang-format off const JSPropertySpec CairoSVGSurface::proto_props[] = { JS_STRING_SYM_PS(toStringTag, "SVGSurface", JSPROP_READONLY), JS_PS_END}; // clang-format on #else JSObject* CairoSVGSurface::from_c_ptr(JSContext* context, cairo_surface_t* surface) { gjs_throw(context, "could not create SVG surface, recompile cairo and gjs with " "SVG support."); return nullptr; } #endif /* CAIRO_HAS_SVG_SURFACE */ cjs-128.0/modules/cairo-path.cpp0000664000175000017500000000261714771557763015466 0ustar fabiofabio/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2010 Red Hat, Inc. // SPDX-FileCopyrightText: 2020 Philip Chimento #include #include #include // for JSPROP_READONLY #include #include #include #include // for JS_NewObjectWithGivenProto #include "modules/cairo-private.h" // clang-format off const JSPropertySpec CairoPath::proto_props[] = { JS_STRING_SYM_PS(toStringTag, "Path", JSPROP_READONLY), JS_PS_END}; // clang-format on /* * CairoPath::take_c_ptr(): * Same as CWrapper::from_c_ptr(), but always takes ownership of the pointer * rather than copying it. It's not possible to copy a cairo_path_t*. */ JSObject* CairoPath::take_c_ptr(JSContext* cx, cairo_path_t* ptr) { JS::RootedObject proto(cx, CairoPath::prototype(cx)); if (!proto) return nullptr; JS::RootedObject wrapper( cx, JS_NewObjectWithGivenProto(cx, &CairoPath::klass, proto)); if (!wrapper) return nullptr; CairoPath::init_private(wrapper, ptr); debug_lifecycle(ptr, wrapper, "take_c_ptr"); return wrapper; } void CairoPath::finalize_impl(JS::GCContext*, cairo_path_t* path) { if (!path) return; cairo_path_destroy(path); } cjs-128.0/modules/esm/0000775000175000017500000000000014771557763013511 5ustar fabiofabiocjs-128.0/modules/esm/console.js0000664000175000017500000004544214771557763015522 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2021 Evan Welsh const DEFAULT_LOG_DOMAIN = 'Cjs-Console'; // A line-by-line implementation of https://console.spec.whatwg.org/. // 2.2.1. Formatting specifiers // https://console.spec.whatwg.org/#formatting-specifiers // // %s - string // %d or %i - integer // %f - float // %o - "optimal" object formatting // %O - "generic" object formatting // %c - "CSS" formatting (unimplemented by GJS) /** * A simple regex to capture formatting specifiers */ const specifierTest = /%(d|i|s|f|o|O|c)/; /** * @param {string} str a string to check for format specifiers like %s or %i * @returns {boolean} */ function hasFormatSpecifiers(str) { return specifierTest.test(str); } /** * @param {any} item an item to format */ function formatGenerically(item) { return JSON.stringify(item, null, 4); } /** * @param {any} item an item to format * @returns {string} */ function formatOptimally(item) { const GLib = imports.gi.GLib; // Handle optimal error formatting. if (item instanceof Error || item instanceof GLib.Error) { return `${item.toString()}${item.stack ? '\n' : ''}${item.stack ?.split('\n') // Pad each stacktrace line. .map(line => line.padStart(2, ' ')) .join('\n')}`; } // TODO: Enhance 'optimal' formatting. // There is a current work on a better object formatter for GJS in // https://gitlab.gnome.org/GNOME/gjs/-/merge_requests/587 if (typeof item === 'object' && item !== null) { if (item.constructor?.name !== 'Object') return `${item.constructor?.name} ${JSON.stringify(item, null, 4)}`; else if (item[Symbol.toStringTag] === 'GIRepositoryNamespace') return `[${item[Symbol.toStringTag]} ${item.__name__}]`; } return JSON.stringify(item, null, 4); } /** * Implementation of the WHATWG Console object. */ class Console { #groupIndentation = ''; #countLabels = {}; #timeLabels = {}; #logDomain = DEFAULT_LOG_DOMAIN; get [Symbol.toStringTag]() { return 'Console'; } // 1.1 Logging functions // https://console.spec.whatwg.org/#logging /** * Logs a critical message if the condition is not truthy. * {@see console.error()} for additional information. * * @param {boolean} condition a boolean condition which, if false, causes * the log to print * @param {...any} data formatting substitutions, if applicable * @returns {void} */ assert(condition, ...data) { if (condition) return; const message = 'Assertion failed'; if (data.length === 0) data.push(message); if (typeof data[0] !== 'string') { data.unshift(message); } else { const first = data.shift(); data.unshift(`${message}: ${first}`); } this.#logger('assert', data); } /** * Resets grouping and clears the terminal on systems supporting ANSI * terminal control sequences. * * In file-based stdout or systems which do not support clearing, * console.clear() has no visual effect. * * @returns {void} */ clear() { this.#groupIndentation = ''; imports.gi.CjsPrivate.clear_terminal(); } /** * Logs a message with severity equal to {@see GLib.LogLevelFlags.DEBUG}. * * @param {...any} data formatting substitutions, if applicable */ debug(...data) { this.#logger('debug', data); } /** * Logs a message with severity equal to {@see GLib.LogLevelFlags.CRITICAL}. * Does not use {@see GLib.LogLevelFlags.ERROR} to avoid asserting and * forcibly shutting down the application. * * @param {...any} data formatting substitutions, if applicable */ error(...data) { this.#logger('error', data); } /** * Logs a message with severity equal to {@see GLib.LogLevelFlags.INFO}. * * @param {...any} data formatting substitutions, if applicable */ info(...data) { this.#logger('info', data); } /** * Logs a message with severity equal to {@see GLib.LogLevelFlags.MESSAGE}. * * @param {...any} data formatting substitutions, if applicable */ log(...data) { this.#logger('log', data); } // 1.1.7 table(tabularData, properties) table(tabularData, _properties) { this.log(tabularData); } /** * @param {...any} data formatting substitutions, if applicable * @returns {void} */ trace(...data) { if (data.length === 0) data = ['Trace']; this.#logger('trace', data); } /** * Logs a message with severity equal to {@see GLib.LogLevelFlags.WARNING}. * * @param {...any} data formatting substitutions, if applicable * @returns {void} */ warn(...data) { this.#logger('warn', data); } /** * @param {object} item an item to format generically * @param {never} [options] any additional options for the formatter. Unused * in our implementation. */ dir(item, options) { const object = formatGenerically(item); this.#printer('dir', [object], options); } /** * @param {...any} data formatting substitutions, if applicable * @returns {void} */ dirxml(...data) { this.log(...data); } // 1.2 Counting functions // https://console.spec.whatwg.org/#counting /** * Logs how many times console.count(label) has been called with a given * label. * {@see console.countReset()} for resetting a count. * * @param {string} label unique identifier for this action * @returns {void} */ count(label) { this.#countLabels[label] ??= 0; const count = ++this.#countLabels[label]; const concat = `${label}: ${count}`; this.#logger('count', [concat]); } /** * @param {string} label the unique label to reset the count for * @returns {void} */ countReset(label) { const count = this.#countLabels[label]; if (typeof count !== 'number') this.#printer('reportWarning', [`No count found for label: '${label}'.`]); else this.#countLabels[label] = 0; } // 1.3 Grouping functions // https://console.spec.whatwg.org/#grouping /** * @param {...any} data formatting substitutions, if applicable * @returns {void} */ group(...data) { this.#logger('group', data); this.#groupIndentation += ' '; } /** * Alias for console.group() * * @param {...any} data formatting substitutions, if applicable * @returns {void} */ groupCollapsed(...data) { // We can't 'collapse' output in a terminal, so we alias to // group() this.group(...data); } /** * @returns {void} */ groupEnd() { this.#groupIndentation = this.#groupIndentation.slice(0, -2); } // 1.4 Timing functions // https://console.spec.whatwg.org/#timing /** * @param {string} label unique identifier for this action, pass to * console.timeEnd() to complete * @returns {void} */ time(label) { this.#timeLabels[label] = imports.gi.GLib.get_monotonic_time(); } /** * Logs the time since the last call to console.time(label) where label is * the same. * * @param {string} label unique identifier for this action, pass to * console.timeEnd() to complete * @param {...any} data string substitutions, if applicable * @returns {void} */ timeLog(label, ...data) { const startTime = this.#timeLabels[label]; if (typeof startTime !== 'number') { this.#printer('reportWarning', [ `No time log found for label: '${label}'.`, ]); } else { const durationMs = (imports.gi.GLib.get_monotonic_time() - startTime) / 1000; const concat = `${label}: ${durationMs.toFixed(3)} ms`; data.unshift(concat); this.#printer('timeLog', data); } } /** * Logs the time since the last call to console.time(label) and completes * the action. * Call console.time(label) again to re-measure. * * @param {string} label unique identifier for this action * @returns {void} */ timeEnd(label) { const startTime = this.#timeLabels[label]; if (typeof startTime !== 'number') { this.#printer('reportWarning', [ `No time log found for label: '${label}'.`, ]); } else { delete this.#timeLabels[label]; const durationMs = (imports.gi.GLib.get_monotonic_time() - startTime) / 1000; const concat = `${label}: ${durationMs.toFixed(3)} ms`; this.#printer('timeEnd', [concat]); } } // Non-standard functions which are de-facto standards. // Similar to Node, we define these as no-ops for now. /** * @deprecated Not implemented in GJS * * @param {string} _label unique identifier for this action, pass to * console.profileEnd to complete * @returns {void} */ profile(_label) {} /** * @deprecated Not implemented in GJS * * @param {string} _label unique identifier for this action * @returns {void} */ profileEnd(_label) {} /** * @deprecated Not implemented in GJS * * @param {string} _label unique identifier for this action * @returns {void} */ timeStamp(_label) {} // GJS-specific extensions for integrating with GLib structured logging /** * @param {string} logDomain the GLib log domain this Console should print * with. Defaults to 'Gjs-Console'. * @returns {void} */ setLogDomain(logDomain) { this.#logDomain = String(logDomain); } /** * @returns {string} */ get logDomain() { return this.#logDomain; } // 2. Supporting abstract operations // https://console.spec.whatwg.org/#supporting-ops /** * 2.1. Logger * https://console.spec.whatwg.org/#logger * * Conditionally applies formatting based on the inputted arguments, * and prints at the provided severity (logLevel) * * @param {string} logLevel the severity (log level) the args should be * emitted with * @param {unknown[]} args the arguments to pass to the printer * @returns {void} */ #logger(logLevel, args) { if (args.length === 0) return; const [first, ...rest] = args; if (rest.length === 0) { this.#printer(logLevel, [first]); return undefined; } // If first does not contain any format specifiers, don't call Formatter if (typeof first !== 'string' || !hasFormatSpecifiers(first)) { this.#printer(logLevel, args); return undefined; } // Otherwise, perform print the result of Formatter. this.#printer(logLevel, this.#formatter([first, ...rest])); return undefined; } /** * 2.2. Formatter * https://console.spec.whatwg.org/#formatter * * @param {[string, ...any[]]} args an array of format strings followed by * their arguments */ #formatter(args) { // The initial formatting string is the first arg let target = args[0]; if (args.length === 1) return target; const current = args[1]; // Find the index of the first format specifier. const specifierIndex = specifierTest.exec(target).index; const specifier = target.slice(specifierIndex, specifierIndex + 2); let converted = null; switch (specifier) { case '%s': converted = String(current); break; case '%d': case '%i': if (typeof current === 'symbol') converted = Number.NaN; else converted = parseInt(current, 10); break; case '%f': if (typeof current === 'symbol') converted = Number.NaN; else converted = parseFloat(current); break; case '%o': converted = formatOptimally(current); break; case '%O': converted = formatGenerically(current); break; case '%c': converted = ''; break; } // If any of the previous steps set converted, replace the specifier in // target with the converted value. if (converted !== null) { target = target.slice(0, specifierIndex) + converted + target.slice(specifierIndex + 2); } /** * Create the next format input... * * @type {[string, ...any[]]} */ const result = [target, ...args.slice(2)]; if (!hasFormatSpecifiers(target)) return result; if (result.length === 1) return result; return this.#formatter(result); } /** * @typedef {object} PrinterOptions * @param {Array.} [stackTrace] an error stacktrace to append * @param {Record} [fields] fields to include in the structured * logging call */ /** * 2.3. Printer * https://console.spec.whatwg.org/#printer * * This implementation of Printer maps WHATWG log severity to * {@see GLib.LogLevelFlags} and outputs using GLib structured logging. * * @param {string} logLevel the log level (log tag) the args should be * emitted with * @param {unknown[]} args the arguments to print, either a format string * with replacement args or multiple strings * @param {PrinterOptions} [options] additional options for the * printer * @returns {void} */ #printer(logLevel, args, options) { const GLib = imports.gi.GLib; let severity; switch (logLevel) { case 'log': case 'dir': case 'dirxml': case 'trace': case 'group': case 'groupCollapsed': case 'timeLog': case 'timeEnd': severity = GLib.LogLevelFlags.LEVEL_MESSAGE; break; case 'debug': severity = GLib.LogLevelFlags.LEVEL_DEBUG; break; case 'count': case 'info': severity = GLib.LogLevelFlags.LEVEL_INFO; break; case 'warn': case 'countReset': case 'reportWarning': severity = GLib.LogLevelFlags.LEVEL_WARNING; break; case 'error': case 'assert': severity = GLib.LogLevelFlags.LEVEL_CRITICAL; break; default: severity = GLib.LogLevelFlags.LEVEL_MESSAGE; } const output = args .map(a => { if (a === null) return 'null'; else if (typeof a === 'object') return formatOptimally(a); else if (typeof a === 'function') return a.toString(); else if (typeof a === 'undefined') return 'undefined'; else if (typeof a === 'bigint') return `${a}n`; else return String(a); }) .join(' '); let formattedOutput = this.#groupIndentation + output; const extraFields = {}; let stackTrace = options?.stackTrace; if (!stackTrace && (logLevel === 'trace' || severity <= GLib.LogLevelFlags.LEVEL_WARNING)) { stackTrace = new Error().stack; const currentFile = stackTrace.match(/^[^@]*@(.*):\d+:\d+$/m)?.at(1); const index = stackTrace.lastIndexOf(currentFile) + currentFile.length; stackTrace = stackTrace.substring(index).split('\n'); // Remove the remainder of the first line stackTrace.shift(); } if (logLevel === 'trace') { if (stackTrace?.length) { formattedOutput += `\n${stackTrace.map(s => `${this.#groupIndentation}${s}`).join('\n')}`; } else { formattedOutput += `\n${this.#groupIndentation}No trace available`; } } if (stackTrace?.length) { const [stackLine] = stackTrace; const match = stackLine.match(/^([^@]*)@(.*):(\d+):\d+$/); if (match) { const [_, func, file, line] = match; if (func) extraFields.CODE_FUNC = func; if (file) extraFields.CODE_FILE = file; if (line) extraFields.CODE_LINE = line; } } GLib.log_structured(this.#logDomain, severity, { MESSAGE: formattedOutput, ...extraFields, ...options?.fields ?? {}, }); } } const console = new Console(); /** * @param {string} domain set the GLib log domain for the global console object. */ function setConsoleLogDomain(domain) { console.setLogDomain(domain); } /** * @returns {string} */ function getConsoleLogDomain() { return console.logDomain; } /** * For historical web-compatibility reasons, the namespace object for * console must have {} as its [[Prototype]]. * * @type {Omit} */ const globalConsole = Object.create({}); const propertyNames = /** @type {['constructor', ...Array]} */ // eslint-disable-next-line no-extra-parens (Object.getOwnPropertyNames(Console.prototype)); const propertyDescriptors = Object.getOwnPropertyDescriptors(Console.prototype); for (const key of propertyNames) { if (key === 'constructor') continue; // This non-standard function shouldn't be included. if (key === 'setLogDomain') continue; const descriptor = propertyDescriptors[key]; if (typeof descriptor.value !== 'function') continue; Object.defineProperty(globalConsole, key, { ...descriptor, value: descriptor.value.bind(console), }); } Object.defineProperties(globalConsole, { [Symbol.toStringTag]: { configurable: false, enumerable: true, get() { return 'console'; }, }, }); Object.freeze(globalConsole); Object.defineProperty(globalThis, 'console', { configurable: false, enumerable: true, writable: false, value: globalConsole, }); export { getConsoleLogDomain, setConsoleLogDomain, DEFAULT_LOG_DOMAIN }; export default { getConsoleLogDomain, setConsoleLogDomain, DEFAULT_LOG_DOMAIN, }; cjs-128.0/modules/esm/gettext.js0000664000175000017500000000103714771557763015534 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2020 Evan Welsh export let { LocaleCategory, setlocale, textdomain, bindtextdomain, gettext, dgettext, dcgettext, ngettext, dngettext, pgettext, dpgettext, domain, } = imports._gettext; export default { LocaleCategory, setlocale, textdomain, bindtextdomain, gettext, dgettext, dcgettext, ngettext, dngettext, pgettext, dpgettext, domain, }; cjs-128.0/modules/esm/.eslintrc.yml0000664000175000017500000000032314771557763016133 0ustar fabiofabio--- # SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2020 Evan Welsh extends: '../../.eslintrc.yml' parserOptions: sourceType: 'module' ecmaVersion: 2022 cjs-128.0/modules/esm/_encoding/0000775000175000017500000000000014771557763015436 5ustar fabiofabiocjs-128.0/modules/esm/_encoding/encoding.js0000664000175000017500000001137514771557763017571 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2021 Evan Welsh import {getEncodingFromLabel} from './encodingMap.js'; class TextDecoder { /** * @type {string} */ encoding; /** * @type {boolean} */ ignoreBOM; /** * @type {boolean} */ fatal; /** * @private * @type {string} */ _internalEncoding; get [Symbol.toStringTag]() { return 'TextDecoder'; } /** * @param {string} encoding The encoding to decode into * @param {object} [options] Decoding options * @param {boolean=} options.fatal Whether to throw or substitute when invalid characters are encountered * @param {boolean=} options.ignoreBOM Whether to ignore the byte order for UTF-8 arrays */ constructor(encoding = 'utf-8', options = {}) { const {fatal = false, ignoreBOM = false} = options; const encodingDefinition = getEncodingFromLabel(`${encoding}`); if (!encodingDefinition) throw new RangeError(`Invalid encoding label: '${encoding}'`); if (encodingDefinition.label === 'replacement') { throw new RangeError( `Unsupported replacement encoding: '${encoding}'` ); } Object.defineProperty(this, '_internalEncoding', { value: encodingDefinition.internalLabel, enumerable: false, writable: false, configurable: false, }); Object.defineProperty(this, 'encoding', { value: encodingDefinition.label, enumerable: true, writable: false, configurable: false, }); Object.defineProperty(this, 'ignoreBOM', { value: Boolean(ignoreBOM), enumerable: true, writable: false, configurable: false, }); Object.defineProperty(this, 'fatal', { value: Boolean(fatal), enumerable: true, writable: false, configurable: false, }); } /** * @param {unknown} bytes a typed array of bytes to decode * @param {object} [options] Decoding options * @param {boolean=} options.stream Unsupported option. Whether to stream the decoded bytes. * @returns */ decode(bytes, options = {}) { const {stream = false} = options; if (stream) { throw new Error( 'TextDecoder does not implement the \'stream\' option.' ); } /** @type {Uint8Array} */ let input; if (bytes instanceof ArrayBuffer) { input = new Uint8Array(bytes); } else if (bytes instanceof Uint8Array) { input = bytes; } else if (ArrayBuffer.isView(bytes)) { let {buffer, byteLength, byteOffset} = bytes; input = new Uint8Array(buffer, byteOffset, byteLength); } else if (bytes === undefined) { input = new Uint8Array(0); } else if (bytes instanceof import.meta.importSync('gi').GLib.Bytes) { input = bytes.toArray(); } else { throw new Error( 'Provided input cannot be converted to ArrayBufferView or ArrayBuffer' ); } if ( this.ignoreBOM && input.length > 2 && input[0] === 0xef && input[1] === 0xbb && input[2] === 0xbf ) { if (this.encoding !== 'utf-8') throw new Error('Cannot ignore BOM for non-UTF8 encoding.'); let {buffer, byteLength, byteOffset} = input; input = new Uint8Array(buffer, byteOffset + 3, byteLength - 3); } const Encoding = import.meta.importSync('_encodingNative'); return Encoding.decode(input, this._internalEncoding, this.fatal); } } class TextEncoder { get [Symbol.toStringTag]() { return 'TextEncoder'; } get encoding() { return 'utf-8'; } encode(input = '') { const Encoding = import.meta.importSync('_encodingNative'); // The TextEncoder specification only allows for UTF-8 encoding. return Encoding.encode(`${input}`, 'utf-8'); } encodeInto(input = '', output = new Uint8Array()) { const Encoding = import.meta.importSync('_encodingNative'); // The TextEncoder specification only allows for UTF-8 encoding. return Encoding.encodeInto(`${input}`, output); } } Object.defineProperty(globalThis, 'TextEncoder', { configurable: false, enumerable: true, writable: false, value: TextEncoder, }); Object.defineProperty(globalThis, 'TextDecoder', { configurable: false, enumerable: true, writable: false, value: TextDecoder, }); cjs-128.0/modules/esm/_encoding/util.js0000664000175000017500000000174214771557763016755 0ustar fabiofabio// SPDX-License-Identifier: MIT // SPDX-FileCopyrightText: Node.js contributors. All rights reserved. // Modified from https://github.com/nodejs/node/blob/78680c1cbc8b0c435963bc512e826b2a6227c315/lib/internal/encoding.js /** * Trims ASCII whitespace from a string. * `String.prototype.trim` removes non-ASCII whitespace. * * @param {string} label the label to trim * @returns {string} */ export const trimAsciiWhitespace = label => { let s = 0; let e = label.length; while ( s < e && (label[s] === '\u0009' || label[s] === '\u000a' || label[s] === '\u000c' || label[s] === '\u000d' || label[s] === '\u0020') ) s++; while ( e > s && (label[e - 1] === '\u0009' || label[e - 1] === '\u000a' || label[e - 1] === '\u000c' || label[e - 1] === '\u000d' || label[e - 1] === '\u0020') ) e--; return label.slice(s, e); }; cjs-128.0/modules/esm/_encoding/encodingMap.js0000664000175000017500000001676414771557763020236 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2021 Evan Welsh import {trimAsciiWhitespace} from './util.js'; // Data derived from https://encoding.spec.whatwg.org/encodings.json const encodingMap = { 'utf-8': [ 'unicode-1-1-utf-8', 'unicode11utf8', 'unicode20utf8', 'utf-8', 'utf8', 'x-unicode20utf8', ], ibm866: ['866', 'cp866', 'csibm866', 'ibm866'], 'iso-8859-2': [ 'csisolatin2', 'iso-8859-2', 'iso-ir-101', 'iso8859-2', 'iso88592', 'iso_8859-2', 'iso_8859-2:1987', 'l2', 'latin2', ], 'iso-8859-3': [ 'csisolatin3', 'iso-8859-3', 'iso-ir-109', 'iso8859-3', 'iso88593', 'iso_8859-3', 'iso_8859-3:1988', 'l3', 'latin3', ], 'iso-8859-4': [ 'csisolatin4', 'iso-8859-4', 'iso-ir-110', 'iso8859-4', 'iso88594', 'iso_8859-4', 'iso_8859-4:1988', 'l4', 'latin4', ], 'iso-8859-5': [ 'csisolatincyrillic', 'cyrillic', 'iso-8859-5', 'iso-ir-144', 'iso8859-5', 'iso88595', 'iso_8859-5', 'iso_8859-5:1988', ], 'iso-8859-6': [ 'arabic', 'asmo-708', 'csiso88596e', 'csiso88596i', 'csisolatinarabic', 'ecma-114', 'iso-8859-6', 'iso-8859-6-e', 'iso-8859-6-i', 'iso-ir-127', 'iso8859-6', 'iso88596', 'iso_8859-6', 'iso_8859-6:1987', ], 'iso-8859-7': [ 'csisolatingreek', 'ecma-118', 'elot_928', 'greek', 'greek8', 'iso-8859-7', 'iso-ir-126', 'iso8859-7', 'iso88597', 'iso_8859-7', 'iso_8859-7:1987', 'sun_eu_greek', ], 'iso-8859-8': [ 'csiso88598e', 'csisolatinhebrew', 'hebrew', 'iso-8859-8', 'iso-8859-8-e', 'iso-ir-138', 'iso8859-8', 'iso88598', 'iso_8859-8', 'iso_8859-8:1988', 'visual', ], 'iso-8859-8-i': ['csiso88598i', 'iso-8859-8-i', 'logical'], 'iso-8859-10': [ 'csisolatin6', 'iso-8859-10', 'iso-ir-157', 'iso8859-10', 'iso885910', 'l6', 'latin6', ], 'iso-8859-13': ['iso-8859-13', 'iso8859-13', 'iso885913'], 'iso-8859-14': ['iso-8859-14', 'iso8859-14', 'iso885914'], 'iso-8859-15': [ 'csisolatin9', 'iso-8859-15', 'iso8859-15', 'iso885915', 'iso_8859-15', 'l9', ], 'iso-8859-16': ['iso-8859-16'], 'koi8-r': ['cskoi8r', 'koi', 'koi8', 'koi8-r', 'koi8_r'], 'koi8-u': ['koi8-ru', 'koi8-u'], macintosh: ['csmacintosh', 'mac', 'macintosh', 'x-mac-roman'], 'windows-874': [ 'dos-874', 'iso-8859-11', 'iso8859-11', 'iso885911', 'tis-620', 'windows-874', ], 'windows-1250': ['cp1250', 'windows-1250', 'x-cp1250'], 'windows-1251': ['cp1251', 'windows-1251', 'x-cp1251'], 'windows-1252': [ 'ansi_x3.4-1968', 'ascii', 'cp1252', 'cp819', 'csisolatin1', 'ibm819', 'iso-8859-1', 'iso-ir-100', 'iso8859-1', 'iso88591', 'iso_8859-1', 'iso_8859-1:1987', 'l1', 'latin1', 'us-ascii', 'windows-1252', 'x-cp1252', ], 'windows-1253': ['cp1253', 'windows-1253', 'x-cp1253'], 'windows-1254': [ 'cp1254', 'csisolatin5', 'iso-8859-9', 'iso-ir-148', 'iso8859-9', 'iso88599', 'iso_8859-9', 'iso_8859-9:1989', 'l5', 'latin5', 'windows-1254', 'x-cp1254', ], 'windows-1255': ['cp1255', 'windows-1255', 'x-cp1255'], 'windows-1256': ['cp1256', 'windows-1256', 'x-cp1256'], 'windows-1257': ['cp1257', 'windows-1257', 'x-cp1257'], 'windows-1258': ['cp1258', 'windows-1258', 'x-cp1258'], 'x-mac-cyrillic': ['x-mac-cyrillic', 'x-mac-ukrainian'], gbk: [ 'chinese', 'csgb2312', 'csiso58gb231280', 'gb2312', 'gb_2312', 'gb_2312-80', 'gbk', 'iso-ir-58', 'x-gbk', ], gb18030: ['gb18030'], big5: [ 'big5', // Unlike the standard WHATWG encoder // the Hong Kong Supplementary Character Set // is not bundled in big5 by iconv // "big5-hkscs", 'cn-big5', 'csbig5', 'x-x-big5', ], 'euc-jp': ['cseucpkdfmtjapanese', 'euc-jp', 'x-euc-jp'], 'iso-2022-jp': ['csiso2022jp', 'iso-2022-jp'], shift_jis: [ 'csshiftjis', 'ms932', 'ms_kanji', 'shift-jis', 'shift_jis', 'sjis', 'windows-31j', 'x-sjis', ], 'euc-kr': [ 'cseuckr', 'csksc56011987', 'euc-kr', 'iso-ir-149', 'korean', 'ks_c_5601-1987', 'ks_c_5601-1989', 'ksc5601', 'ksc_5601', 'windows-949', ], 'utf-16be': ['unicodefffe', 'utf-16be'], 'utf-16le': [ 'csunicode', 'iso-10646-ucs-2', 'ucs-2', 'unicode', 'unicodefeff', 'utf-16', 'utf-16le', ], }; /** * Construct a map from each potential label to the canonical label * for an encoding. */ const encodings = new Map( Object.entries(encodingMap).flatMap(([encoding, labels]) => { return labels.map(label => [label, encoding]); }) ); // Maps WHATWG specified labels to the appropriate iconv // encoding label if iconv does not support the WHATWG label. // // Mapping here preserves the WHATWG as the label on the // TextDecoder so this change is transparent to API users. const internalEncodings = new Map([ // iso-8859-8-i is functionally equivalent to iso-8859-8 // as we are not encoding or decoding control characters. ['iso-8859-8-i', 'iso-8859-8'], // iconv follows a different naming convention for this // encoding ['x-mac-cyrillic', 'MacCyrillic'], // Support HKSCS as a standalone encoding, iconv doesn't // bundle it with Big5 like WHATWG does... ['big5-hkscs', 'big5-hkscs'], ]); /** * @typedef Encoding * @property {string} internalLabel * @property {string} label */ /** * @param {string} label the encoding label * @returns {Encoding | null} */ export function getEncodingFromLabel(label) { const formattedLabel = trimAsciiWhitespace(label.toLowerCase()); let canonicalLabel = encodings.get(formattedLabel); // Lookup an internal mapping using the canonical name, if found, or // the formatted label otherwise. // // x-mac-ukrainian > x-mac-cyrillic > MacCyrillic // (canonical label) (internal label) // // big5-hkscs > undefined > big5-hkscs // (canonical label) (internal label) // let internalLabel = internalEncodings.get( canonicalLabel ?? formattedLabel ); // If both the canonical label and the internal encoding // are not found, this encoding is unsupported. if (!canonicalLabel && !internalLabel) return null; if (internalLabel) { return { label: canonicalLabel ?? formattedLabel, internalLabel, }; } return { label: canonicalLabel, internalLabel: canonicalLabel, }; } cjs-128.0/modules/esm/_bootstrap/0000775000175000017500000000000014771557763015665 5ustar fabiofabiocjs-128.0/modules/esm/_bootstrap/default.js0000664000175000017500000000050014771557763017642 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2021 Evan Welsh // Bootstrap file which supports ESM imports. // Bootstrap the Encoding API import '_encoding/encoding'; // Bootstrap the Console API import 'console'; // Bootstrap the Timers API import '_timers'; cjs-128.0/modules/esm/system.js0000664000175000017500000000120514771557763015371 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2020 Evan Welsh const system = import.meta.importSync('system'); export let { addressOf, addressOfGObject, breakpoint, clearDateCaches, dumpHeap, dumpMemoryInfo, exit, gc, programArgs, programInvocationName, programPath, refcount, version, } = system; export default { addressOf, addressOfGObject, breakpoint, clearDateCaches, dumpHeap, dumpMemoryInfo, exit, gc, programArgs, programInvocationName, programPath, refcount, version, }; cjs-128.0/modules/esm/_timers.js0000664000175000017500000001015014771557763015506 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2021 Evan Welsh /* exported setTimeout, setInterval, clearTimeout, clearInterval */ /* eslint no-implicit-coercion: ["error", {"allow": ["+"]}] */ // Note: implicit coercion with + is used to perform the ToNumber algorithm from // the timers specification /** * @param {number} delay a number value (in milliseconds) */ function validateDelay(delay) { // |0 always returns a signed 32-bit integer. return Math.max(0, +delay | 0); } /** @type {Map} */ const timeouts = new Map(); /** * @param {GLib.Source} source the source to add to our map */ function addSource(source) { const id = source.attach(null); timeouts.set(source, id); } /** * @param {GLib.Source} source the source object to remove from our map */ function releaseSource(source) { timeouts.delete(source); } /** * @param {unknown} thisArg 'this' argument * @returns {asserts thisArg is (null | undefined | typeof globalThis)} */ function checkThis(thisArg) { if (thisArg !== null && thisArg !== undefined && thisArg !== globalThis) throw new TypeError('Illegal invocation'); } /** * @param {number} timeout a timeout in milliseconds * @param {(...args) => any} handler a callback * @returns {GLib.Source} */ function createTimeoutSource(timeout, handler) { const source = imports.gi.GLib.timeout_source_new(timeout); source.set_priority(imports.gi.GLib.PRIORITY_DEFAULT); imports.gi.GObject.source_set_closure(source, handler); return source; } /** * @this {typeof globalThis} * @param {(...args) => any} callback a callback function * @param {number} delay the duration in milliseconds to wait before running callback * @param {...any} args arguments to pass to callback */ function setTimeout(callback, delay = 0, ...args) { checkThis(this); delay = validateDelay(delay); const boundCallback = callback.bind(globalThis, ...args); const source = createTimeoutSource(delay, () => { if (!timeouts.has(source)) return imports.gi.GLib.SOURCE_REMOVE; boundCallback(); releaseSource(source); import.meta.importSync('_promiseNative').drainMicrotaskQueue(); return imports.gi.GLib.SOURCE_REMOVE; }); addSource(source); return source; } /** * @this {typeof globalThis} * @param {(...args) => any} callback a callback function * @param {number} delay the duration in milliseconds to wait between calling callback * @param {...any} args arguments to pass to callback */ function setInterval(callback, delay = 0, ...args) { checkThis(this); delay = validateDelay(delay); const boundCallback = callback.bind(globalThis, ...args); const source = createTimeoutSource(delay, () => { if (!timeouts.has(source)) return imports.gi.GLib.SOURCE_REMOVE; boundCallback(); import.meta.importSync('_promiseNative').drainMicrotaskQueue(); return imports.gi.GLib.SOURCE_CONTINUE; }); addSource(source); return source; } /** * @param {GLib.Source} source the timeout to clear */ function _clearTimer(source) { if (!timeouts.has(source)) return; if (source) { source.destroy(); releaseSource(source); } } /** * @param {GLib.Source} timeout the timeout to clear */ function clearTimeout(timeout = null) { _clearTimer(timeout); } /** * @param {Glib.Source} timeout the timeout to clear */ function clearInterval(timeout = null) { _clearTimer(timeout); } Object.defineProperty(globalThis, 'setTimeout', { configurable: false, enumerable: true, writable: true, value: setTimeout, }); Object.defineProperty(globalThis, 'setInterval', { configurable: false, enumerable: true, writable: true, value: setInterval, }); Object.defineProperty(globalThis, 'clearTimeout', { configurable: false, enumerable: true, writable: true, value: clearTimeout, }); Object.defineProperty(globalThis, 'clearInterval', { configurable: false, enumerable: true, writable: true, value: clearInterval, }); cjs-128.0/modules/esm/gi.js0000664000175000017500000000207214771557763014447 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2020 Evan Welsh const gi = import.meta.importSync('gi'); const Gi = { require(namespace, version = undefined) { if (namespace === 'versions') throw new Error('Cannot import namespace "versions", use the version parameter of Gi.require to specify versions.'); let oldVersion = gi.versions[namespace]; if (version !== undefined) gi.versions[namespace] = version; try { const module = gi[namespace]; if (version !== undefined && version !== module.__version__) { throw new Error(`Version ${module.__version__} of GI module ${ namespace} already loaded, cannot load version ${version}`); } return module; } catch (error) { // Roll back change to versions object if import failed gi.versions[namespace] = oldVersion; throw error; } }, }; Object.freeze(Gi); export default Gi; cjs-128.0/modules/esm/cairo.js0000664000175000017500000000036614771557763015151 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2020 Evan Welsh const cairo = import.meta.importSync('cairoNative'); export default Object.assign( {}, imports._cairo, cairo ); cjs-128.0/modules/script/0000775000175000017500000000000014771557763014231 5ustar fabiofabiocjs-128.0/modules/script/format.js0000664000175000017500000000171214771557763016060 0ustar fabiofabio// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2012 Red Hat, Inc. // SPDX-FileCopyrightText: 2012 Giovanni Campagna /* exported format, printf, vprintf */ var {vprintf} = imports._format; function printf(fmt, ...args) { print(vprintf(fmt, args)); } /* * This function is intended to extend the String object and provide a * String.format API for string formatting. * It has to be set up using String.prototype.format = Format.format; * Usage: * "somestring %s %d".format('hello', 5); * It supports %s, %d, %x and %f. * For %f it also supports precisions like "%.2f".format(1.526). * All specifiers can be prefixed with a minimum field width, e.g. * "%5s".format("foo"). * Unless the width is prefixed with '0', the formatted string will be padded * with spaces. */ function format(...args) { return vprintf(this, args); } cjs-128.0/modules/script/byteArray.js0000664000175000017500000000455314771557763016540 0ustar fabiofabio/* exported ByteArray, fromArray, fromGBytes, fromString, toGBytes, toString */ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2017 Philip Chimento // Allow toString to be declared. /* eslint no-redeclare: ["error", { "builtinGlobals": false }] */ var {fromGBytes, fromString, toString} = imports._byteArrayNative; const {GLib} = imports.gi; // For backwards compatibility /** * @param {Iterable} array an iterable to convert into a ByteArray * wrapper * @returns {ByteArray} */ function fromArray(array) { return new ByteArray(Uint8Array.from(array)); } /** * @param {Uint8Array} array the Uint8Array to convert to GLib.Bytes * @returns {GLib.Bytes} */ function toGBytes(array) { if (!(array instanceof Uint8Array)) throw new Error('Argument to ByteArray.toGBytes() must be a Uint8Array'); return new GLib.Bytes(array); } var ByteArray = class ByteArray { constructor(arg = 0) { if (arg instanceof Uint8Array) this._array = arg; else this._array = new Uint8Array(arg); return new Proxy(this, ByteArray); } static get(target, prop, receiver) { if (!Number.isNaN(Number.parseInt(prop))) return Reflect.get(target._array, prop); return Reflect.get(target, prop, receiver); } static set(target, prop, val, receiver) { let ix = Number.parseInt(prop); if (!Number.isNaN(ix)) { if (ix >= target._array.length) { let newArray = new Uint8Array(ix + 1); newArray.set(target._array); target._array = newArray; } return Reflect.set(target._array, prop, val); } return Reflect.set(target, prop, val, receiver); } get length() { return this._array.length; } set length(newLength) { if (newLength === this._array.length) return; if (newLength < this._array.length) { this._array = new Uint8Array(this._array.buffer, 0, newLength); return; } let newArray = new Uint8Array(newLength); newArray.set(this._array); this._array = newArray; } toString(encoding = 'UTF-8') { return toString(this._array, encoding); } toGBytes() { return toGBytes(this._array); } }; cjs-128.0/modules/script/package.js0000664000175000017500000002505214771557763016166 0ustar fabiofabio// SPDX-FileCopyrightText: 2012 Giovanni Campagna // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later /* exported checkSymbol, datadir, init, initFormat, initGettext, initSubmodule, libdir, localedir, moduledir, name, pkgdatadir, pkglibdir, prefix, require, requireSymbol, run, start, version */ /** * This module provides a set of convenience APIs for building packaged * applications. */ imports.gi.versions.GIRepository = '2.0'; 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; // public var name; var version; var prefix; var datadir; var libdir; var pkgdatadir; var pkglibdir; var moduledir; var localedir; // private let _pkgname; let _base; let _submoduledir; function _findEffectiveEntryPointName() { let entryPoint = System.programInvocationName; while (GLib.file_test(entryPoint, GLib.FileTest.IS_SYMLINK)) entryPoint = GLib.file_read_link(entryPoint); return GLib.path_get_basename(entryPoint); } function _runningFromSource() { let binary = Gio.File.new_for_path(System.programInvocationName); let sourceBinary = Gio.File.new_for_path(`./src/${name}`); return binary.equal(sourceBinary); } function _runningFromMesonSource() { return GLib.getenv('MESON_BUILD_ROOT') && GLib.getenv('MESON_SOURCE_ROOT'); } function _makeNamePath(n) { return `/${n.replace(/\./g, '/')}`; } /** * Initialize directories and global variables. Must be called * before any of other API in Package is used. * `params` must be an object with at least the following keys: * - name: the package name ($(PACKAGE_NAME) in autotools, * eg. org.foo.Bar) * - version: the package version * - prefix: the installation prefix * * init() will take care to check if the program is running from * the source directory or not, by looking for a 'src' directory. * * At the end, the global variable 'pkg' will contain the * Package module (imports.package). Additionally, the following * module variables will be available: * - name: the base name of the entry point (eg. org.foo.Bar.App) * - version: same as in @params * - prefix: the installation prefix (as passed in @params) * - datadir, libdir: the final datadir and libdir when installed; * usually, these would be prefix + '/share' and * and prefix + '/lib' (or '/lib64') * - pkgdatadir: the directory to look for private data files, such as * images, stylesheets and UI definitions; * this will be datadir + name when installed and * './data' when running from the source tree * - pkglibdir: the directory to look for private typelibs and C * libraries; * this will be libdir + name when installed and * './lib' when running from the source tree * - moduledir: the directory to look for JS modules; * this will be pkglibdir when installed and * './src' when running from the source tree * - localedir: the directory containing gettext translation files; * this will be datadir + '/locale' when installed * and './po' in the source tree * * All paths are absolute and will not end with '/'. * * As a side effect, init() calls GLib.set_prgname(). * * @param {object} params package parameters */ function init(params) { globalThis.pkg = imports.package; _pkgname = params.name; name = _findEffectiveEntryPointName(); version = params.version; // Must call it first, because it can only be called // once, and other library calls might have it as a // side effect GLib.set_prgname(name); prefix = params.prefix; libdir = params.libdir; datadir = GLib.build_filenamev([prefix, 'share']); let libpath, girpath; if (_runningFromMesonSource()) { log('Running from Meson, using local files'); let bld = GLib.getenv('MESON_BUILD_ROOT'); let src = GLib.getenv('MESON_SOURCE_ROOT'); pkglibdir = libpath = girpath = GLib.build_filenamev([bld, 'lib']); pkgdatadir = GLib.build_filenamev([bld, 'data']); localedir = GLib.build_filenamev([bld, 'po']); _submoduledir = GLib.build_filenamev([bld, 'subprojects']); GLib.setenv('GSETTINGS_SCHEMA_DIR', pkgdatadir, true); try { let resource = Gio.Resource.load(GLib.build_filenamev([bld, 'src', `${name}.src.gresource`])); resource._register(); moduledir = `resource://${_makeNamePath(name)}/js`; } catch (e) { moduledir = GLib.build_filenamev([src, 'src']); } } else if (_runningFromSource()) { log('Running from source tree, using local files'); // Running from source directory _base = GLib.get_current_dir(); _submoduledir = _base; pkglibdir = GLib.build_filenamev([_base, 'lib']); libpath = GLib.build_filenamev([pkglibdir, '.libs']); girpath = pkglibdir; pkgdatadir = GLib.build_filenamev([_base, 'data']); localedir = GLib.build_filenamev([_base, 'po']); moduledir = GLib.build_filenamev([_base, 'src']); GLib.setenv('GSETTINGS_SCHEMA_DIR', pkgdatadir, true); } else { _base = prefix; pkglibdir = GLib.build_filenamev([libdir, _pkgname]); libpath = pkglibdir; girpath = GLib.build_filenamev([pkglibdir, 'girepository-1.0']); pkgdatadir = GLib.build_filenamev([datadir, _pkgname]); localedir = GLib.build_filenamev([datadir, 'locale']); try { let resource = Gio.Resource.load(GLib.build_filenamev([pkgdatadir, `${name}.src.gresource`])); resource._register(); moduledir = `resource://${_makeNamePath(name)}/js`; } catch (e) { moduledir = pkgdatadir; } } imports.searchPath.unshift(moduledir); GIRepository.Repository.prepend_search_path(girpath); GIRepository.Repository.prepend_library_path(libpath); try { let resource = Gio.Resource.load(GLib.build_filenamev([pkgdatadir, `${name}.data.gresource`])); resource._register(); } catch (e) { } } /** * This is a convenience function if your package has a * single entry point. * You must define a main(ARGV) function inside a main.js * module in moduledir. * * @param {object} params see init() */ function start(params) { init(params); run(imports.main); } /** * This is the function to use if you want to have multiple * entry points in one package. * You must define a main(ARGV) function inside the passed * in module, and then the launcher would be * * imports.package.init(...); * imports.package.run(imports.entrypoint); * * @param {object} module the module to run * @returns {number|undefined} the exit code of the module's main() function */ function run(module) { return module.main([System.programInvocationName].concat(ARGV)); } /** * Mark a dependency on a specific version of one or more * external GI typelibs. * `libs` must be an object whose keys are a typelib name, * and values are the respective version. The empty string * indicates any version. * * @param {object} libs the external dependencies to import */ function require(libs) { for (let l in libs) requireSymbol(l, libs[l]); } /** * As checkSymbol(), but exit with an error if the * dependency cannot be satisfied. * * @param {string} lib an external dependency to import * @param {string} [ver] version of the dependency * @param {string} [symbol] symbol to check for */ function requireSymbol(lib, ver, symbol) { if (!checkSymbol(lib, ver, symbol)) { if (symbol) printerr(`Unsatisfied dependency: No ${symbol} in ${lib}`); else printerr(`Unsatisfied dependency: ${lib}`); System.exit(1); } } /** * 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') * * @param {string} lib an external dependency to import * @param {string} [ver] version of the dependency * @param {string} [symbol] symbol to check for * @returns {boolean} true if `lib` can be imported and provides `symbol`, false * otherwise */ function checkSymbol(lib, ver, symbol) { let Lib = null; if (ver) imports.gi.versions[lib] = ver; try { Lib = imports.gi[lib]; } catch (e) { return false; } if (!symbol) return true; // Done let [klass, sym] = symbol.split('.'); if (klass === 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); let gettext = imports.gettext; globalThis._ = gettext.gettext; globalThis.C_ = gettext.pgettext; globalThis.N_ = function (x) { return x; }; } function initFormat() { // eslint-disable-next-line no-restricted-properties let format = imports.format; String.prototype.format = format.format; } function initSubmodule(moduleName) { if (_runningFromMesonSource() || _runningFromSource()) { // Running from source tree, add './moduleName' to search paths let submoduledir = GLib.build_filenamev([_submoduledir, moduleName]); let libpath; if (_runningFromMesonSource()) libpath = submoduledir; else libpath = GLib.build_filenamev([submoduledir, '.libs']); GIRepository.Repository.prepend_search_path(submoduledir); GIRepository.Repository.prepend_library_path(libpath); } else { // Running installed, submodule is in $(pkglibdir), nothing to do } } cjs-128.0/modules/script/gettext.js0000664000175000017500000000071614771557763016257 0ustar fabiofabio// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2019 Evan Welsh /* exported LocaleCategory, bindtextdomain, dcgettext, dgettext, dngettext, domain, dpgettext, gettext, ngettext, pgettext, setlocale, textdomain */ var { LocaleCategory, bindtextdomain, dcgettext, dgettext, dngettext, domain, dpgettext, gettext, ngettext, pgettext, setlocale, textdomain, } = imports._gettext; cjs-128.0/modules/script/mainloop.js0000664000175000017500000000433514771557763016412 0ustar fabiofabio/* -*- mode: js; indent-tabs-mode: nil; -*- */ // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later // SPDX-FileCopyrightText: 2012 Giovanni Campagna /* exported idle_add, idle_source, quit, run, source_remove, timeout_add, timeout_add_seconds, timeout_seconds_source, timeout_source */ // A layer of convenience and backwards-compatibility over GLib MainLoop facilities const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; var _mainLoops = {}; function run(name) { if (!_mainLoops[name]) _mainLoops[name] = GLib.MainLoop.new(null, false); _mainLoops[name].run(); } function quit(name) { if (!_mainLoops[name]) throw new Error('No main loop with this id'); let loop = _mainLoops[name]; _mainLoops[name] = null; if (!loop.is_running()) throw new Error('Main loop was stopped already'); loop.quit(); } // eslint-disable-next-line camelcase function idle_source(handler, priority) { let s = GLib.idle_source_new(); GObject.source_set_closure(s, handler); if (priority !== undefined) s.set_priority(priority); return s; } // eslint-disable-next-line camelcase function idle_add(handler, priority) { return idle_source(handler, priority).attach(null); } // eslint-disable-next-line camelcase function timeout_source(timeout, handler, priority) { let s = GLib.timeout_source_new(timeout); GObject.source_set_closure(s, handler); if (priority !== undefined) s.set_priority(priority); return s; } // eslint-disable-next-line camelcase function timeout_seconds_source(timeout, handler, priority) { let s = GLib.timeout_source_new_seconds(timeout); GObject.source_set_closure(s, handler); if (priority !== undefined) s.set_priority(priority); return s; } // eslint-disable-next-line camelcase function timeout_add(timeout, handler, priority) { return timeout_source(timeout, handler, priority).attach(null); } // eslint-disable-next-line camelcase function timeout_add_seconds(timeout, handler, priority) { return timeout_seconds_source(timeout, handler, priority).attach(null); } // eslint-disable-next-line camelcase function source_remove(id) { return GLib.source_remove(id); } cjs-128.0/modules/script/.eslintrc.yml0000664000175000017500000000023614771557763016656 0ustar fabiofabio--- # SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2020 Evan Welsh rules: jsdoc/require-jsdoc: 'off' cjs-128.0/modules/script/_bootstrap/0000775000175000017500000000000014771557763016405 5ustar fabiofabiocjs-128.0/modules/script/_bootstrap/debugger.js0000664000175000017500000007051414771557763020536 0ustar fabiofabio/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */ /* global debuggee, quit, loadNative, readline, uneval */ // SPDX-License-Identifier: MPL-2.0 // SPDX-FileCopyrightText: 2011 Mozilla Foundation and contributors /* * This is a simple command-line debugger for GJS programs. It is based on * jorendb, which is a toy debugger for shell-js programs included in the * SpiderMonkey source. * * To run it: gjs -d path/to/file.js * Execution will stop at debugger statements, and you'll get a prompt before * the first frame is executed. */ const {print, logError} = loadNative('_print'); // Debugger state. var focusedFrame = null; var topFrame = null; var debuggeeValues = {}; var nextDebuggeeValueIndex = 1; var lastExc = null; var options = {pretty: true, colors: true, ignoreCaughtExceptions: true}; var breakpoints = [undefined]; // Breakpoint numbers start at 1 var skipUnwindHandler = false; // Cleanup functions to run when we next re-enter the repl. var replCleanups = []; // Convert a debuggee value v to a string. function dvToString(v) { if (typeof v === 'undefined') return 'undefined'; // uneval(undefined) === '(void 0)', confusing if (typeof v === 'object' && v !== null) return `[object ${v.class}]`; const s = uneval(v); if (s.length > 400) return `${s.substr(0, 400)}...<${s.length - 400} more bytes>...`; return s; } function debuggeeValueToString(dv, style = {pretty: options.pretty}) { // Special sentinel values returned by Debugger.Environment.getVariable() if (typeof dv === 'object' && dv !== null) { if (dv.missingArguments) return ['', undefined]; if (dv.optimizedOut) return ['', undefined]; if (dv.uninitialized) return ['', undefined]; if (!(dv instanceof Debugger.Object)) return ['', JSON.stringify(dv, null, 4)]; } const dvrepr = dvToString(dv); if (!style.pretty || (typeof dv !== 'object') || (dv === null)) return [dvrepr, undefined]; const exec = debuggeeGlobalWrapper.executeInGlobalWithBindings.bind(debuggeeGlobalWrapper); if (['TypeError', 'Error', 'GIRespositoryNamespace', 'GObject_Object'].includes(dv.class)) { const errval = exec('v.toString()', {v: dv}); return [dvrepr, errval['return']]; } if (style.brief) return [dvrepr, dvrepr]; const str = exec('imports._print.getPrettyPrintFunction(globalThis)(v)', {v: dv}); if ('throw' in str) { if (style.noerror) return [dvrepr, undefined]; const substyle = {...style, noerror: true}; return [dvrepr, debuggeeValueToString(str.throw, substyle)]; } return [dvrepr, str['return']]; } function showDebuggeeValue(dv, style = {pretty: options.pretty}) { const i = nextDebuggeeValueIndex++; debuggeeValues[`$${i}`] = dv; debuggeeValues['$$'] = dv; const [brief, full] = debuggeeValueToString(dv, style); print(`$${i} = ${brief}`); if (full !== undefined) print(full); } Object.defineProperty(Debugger.Frame.prototype, 'num', { configurable: true, enumerable: false, get() { let i = 0; let f; for (f = topFrame; f && f !== this; f = f.older) i++; return f === null ? undefined : i; }, }); Debugger.Frame.prototype.describeFrame = function () { if (this.type === 'call') { return `${this.callee.name || ''}(${ this.arguments.map(dvToString).join(', ')})`; } else if (this.type === 'global') { return 'toplevel'; } else { return `${this.type} code`; } }; Debugger.Frame.prototype.describePosition = function () { if (this.script) return this.script.describeOffset(this.offset); return null; }; Debugger.Frame.prototype.describeFull = function () { const fr = this.describeFrame(); const pos = this.describePosition(); if (pos) return `${fr} at ${pos}`; return fr; }; Object.defineProperty(Debugger.Frame.prototype, 'line', { configurable: true, enumerable: false, get() { if (this.script) return this.script.getOffsetLocation(this.offset).lineNumber; else return null; }, }); Debugger.Script.prototype.describeOffset = function describeOffset(offset) { const {lineNumber, columnNumber} = this.getOffsetLocation(offset); const url = this.url || ''; return `${url}:${lineNumber}:${columnNumber}`; }; function showFrame(f, n, option = {btCommand: false, fullOption: false}) { if (f === undefined || f === null) { f = focusedFrame; if (f === null) { print('No stack.'); return; } } if (n === undefined) { n = f.num; if (n === undefined) throw new Error('Internal error: frame not on stack'); } print(`#${n.toString().padEnd(4)} ${f.describeFull()}`); if (option.btCommand) { if (option.fullOption) { const variables = f.environment.names(); for (let i = 0; i < variables.length; i++) { if (variables.length === 0) print('No locals.'); const value = f.environment.getVariable(variables[i]); const [brief] = debuggeeValueToString(value, {brief: false, pretty: false}); print(`${variables[i]} = ${brief}`); } } } else { let lineNumber = f.line; print(` ${lineNumber}\t${f.script.source.text.split('\n')[lineNumber - 1]}`); } } function saveExcursion(fn) { const tf = topFrame, ff = focusedFrame; try { return fn(); } finally { topFrame = tf; focusedFrame = ff; } } // Evaluate @expr in the current frame, logging and suppressing any exceptions function evalInFrame(expr) { if (!focusedFrame) { print('No stack'); return; } skipUnwindHandler = true; let cv; try { cv = saveExcursion( () => focusedFrame.evalWithBindings(`(${expr})`, debuggeeValues)); } finally { skipUnwindHandler = false; } if (cv === null) { print(`Debuggee died while evaluating ${expr}`); return; } const {throw: exc, return: dv} = cv; if (exc) { print(`Exception caught while evaluating ${expr}: ${dvToString(exc)}`); return; } return {value: dv}; } // Accept debugger commands starting with '#' so that scripting the debugger // can be annotated function commentCommand(comment) { void comment; } // Evaluate an expression in the Debugger global - used for debugging the // debugger function evalCommand(expr) { eval(expr); } function quitCommand() { dbg.removeAllDebuggees(); quit(0); } quitCommand.summary = 'Quit the debugger'; quitCommand.helpText = `USAGE quit`; function backtraceCommand(option) { if (topFrame === null) print('No stack.'); if (option === '') { for (let i = 0, f = topFrame; f; i++, f = f.older) showFrame(f, i, {btCommand: true, fullOption: false}); } else if (option === 'full') { for (let i = 0, f = topFrame; f; i++, f = f.older) showFrame(f, i, {btCommand: true, fullOption: true}); } else { print('Invalid option'); } } backtraceCommand.summary = 'Print backtrace of all stack frames and details of all local variables if the full option is added'; backtraceCommand.helpText = `USAGE bt