tests: Port InjectionManager test to jasmine

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/3164>
This commit is contained in:
Florian Müllner 2024-01-20 08:00:39 +01:00 committed by Marge Bot
parent 9315d42dd6
commit d62146bc19
2 changed files with 149 additions and 92 deletions

View File

@ -21,6 +21,7 @@ unit_testenv.append('GI_TYPELIB_PATH', st_typelib_path, separator: ':')
unit_tests = [ unit_tests = [
'highlighter', 'highlighter',
'injectionManager',
] ]
foreach test : unit_tests foreach test : unit_tests
@ -39,7 +40,6 @@ foreach test : unit_tests
endforeach endforeach
legacy_tests = [ legacy_tests = [
'injectionManager',
'insertSorted', 'insertSorted',
'jsParse', 'jsParse',
'markup', 'markup',

View File

@ -1,16 +1,16 @@
const JsUnit = imports.jsUnit;
import GObject from 'gi://GObject'; import GObject from 'gi://GObject';
import 'resource:///org/gnome/shell/ui/environment.js'; import 'resource:///org/gnome/shell/ui/environment.js';
import {InjectionManager} from 'resource:///org/gnome/shell/extensions/extension.js'; import {InjectionManager} from 'resource:///org/gnome/shell/extensions/extension.js';
const FIXED_NUMBER = 42;
class Object1 { class Object1 {
count = 0; count = 0;
getNumber() { getNumber() {
return 42; return FIXED_NUMBER;
} }
getCount() { getCount() {
@ -29,109 +29,166 @@ class GObject1 extends GObject.Object {
static { static {
GObject.registerClass(this); GObject.registerClass(this);
} }
plonk() {
this.set_property('plonked', true);
}
} }
/** describe('InjectionManager', () => {
* @param {object} object to modify const INJECTIONS = {
*/ 'getNumber': originalMethod => {
function addInjections(object) {
// extend original method
injectionManager.overrideMethod(
object, 'getNumber', originalMethod => {
return function () { return function () {
// eslint-disable-next-line no-invalid-this // eslint-disable-next-line no-invalid-this
const num = originalMethod.call(this); const num = originalMethod.call(this);
return 2 * num; return 2 * num;
}; };
}); },
'getCount': () => {
// override original method
injectionManager.overrideMethod(
object, 'getCount', () => {
return function () { return function () {
return 42; return FIXED_NUMBER;
}; };
}); },
'getOtherNumber': () => {
// inject new method
injectionManager.overrideMethod(
object, 'getOtherNumber', () => {
return function () { return function () {
return 42; return FIXED_NUMBER;
}; };
}); },
} };
let injectionManager;
const injectionManager = new InjectionManager(); function injectAll(obj) {
let obj; for (const func in INJECTIONS) {
injectionManager.overrideMethod(obj,
func, INJECTIONS[func]);
}
}
// Prototype injections beforeEach(() => {
addInjections(Object1.prototype); injectionManager = new InjectionManager();
obj = new Object1();
// new obj is modified
JsUnit.assertEquals(obj.getNumber(), 84);
JsUnit.assertEquals(obj.getCount(), 42);
JsUnit.assertEquals(obj.count, 0);
JsUnit.assertEquals(obj.getOtherNumber(), 42);
injectionManager.clear();
obj = new Object1();
// new obj is unmodified
JsUnit.assertEquals(obj.getNumber(), 42);
JsUnit.assertEquals(obj.getCount(), obj.count);
JsUnit.assert(obj.count > 0);
JsUnit.assertRaises(() => obj.getOtherNumber());
// instance injections
addInjections(obj);
// obj is now modified
JsUnit.assertEquals(obj.getNumber(), 84);
JsUnit.assertEquals(obj.getCount(), 42);
JsUnit.assertEquals(obj.count, 1);
JsUnit.assertEquals(obj.getOtherNumber(), 42);
injectionManager.restoreMethod(obj, 'getNumber');
JsUnit.assertEquals(obj.getNumber(), 42);
injectionManager.clear();
// obj is unmodified again
JsUnit.assertEquals(obj.getNumber(), 42);
JsUnit.assertEquals(obj.getCount(), obj.count);
JsUnit.assert(obj.count > 0);
JsUnit.assertRaises(() => obj.getOtherNumber());
// GObject injections
const gobj = new GObject1();
let vfuncCalled;
injectionManager.overrideMethod(
GObject1.prototype, 'vfunc_set_property', originalMethod => {
return function (...args) {
// eslint-disable-next-line no-invalid-this
originalMethod.apply(this, args);
vfuncCalled = true;
};
}); });
// gobj is now modified afterEach(() => {
vfuncCalled = false; injectionManager.clear();
gobj.plonk(); });
JsUnit.assertTrue(vfuncCalled);
injectionManager.clear(); it('can extend prototype methods', () => {
injectionManager.overrideMethod(Object1.prototype,
'getNumber', INJECTIONS['getNumber']);
// gobj is unmodified again const obj = new Object1();
vfuncCalled = false; expect(obj.getNumber()).toEqual(2 * FIXED_NUMBER);
gobj.plonk(); });
JsUnit.assertFalse(vfuncCalled);
it('can replace prototype methods', () => {
injectionManager.overrideMethod(Object1.prototype,
'getCount', INJECTIONS['getCount']);
const obj = new Object1();
expect(obj.getCount()).toEqual(42);
expect(obj.count).toEqual(0);
});
it('can inject methods into prototypes', () => {
injectionManager.overrideMethod(Object1.prototype,
'getOtherNumber', INJECTIONS['getOtherNumber']);
const obj = new Object1();
expect(obj.getOtherNumber()).toEqual(42);
});
it('can undo prototype injections', () => {
injectAll(Object1.prototype);
injectionManager.clear();
const obj = new Object1();
expect(obj.getNumber()).toEqual(FIXED_NUMBER);
expect(obj.getCount()).toEqual(obj.count);
expect(obj.count).toBeGreaterThan(0);
expect(() => obj.getOtherNumber()).toThrow();
});
it('can extend instance methods', () => {
const obj = new Object1();
injectionManager.overrideMethod(obj,
'getNumber', INJECTIONS['getNumber']);
expect(obj.getNumber()).toEqual(2 * FIXED_NUMBER);
});
it('can replace instance methods', () => {
const obj = new Object1();
injectionManager.overrideMethod(obj,
'getCount', INJECTIONS['getCount']);
expect(obj.getCount()).toEqual(42);
expect(obj.count).toEqual(0);
});
it('can inject methods into instances', () => {
const obj = new Object1();
injectionManager.overrideMethod(obj,
'getOtherNumber', INJECTIONS['getOtherNumber']);
expect(obj.getOtherNumber()).toEqual(42);
});
it('can undo instance injections', () => {
const obj = new Object1();
injectAll(obj);
injectionManager.clear();
expect(obj.getNumber()).toEqual(FIXED_NUMBER);
expect(obj.getCount()).toEqual(obj.count);
expect(obj.count).toBeGreaterThan(0);
expect(() => obj.getOtherNumber()).toThrow();
});
it('can restore an original method', () => {
const obj = new Object1();
injectAll(obj);
expect(obj.getNumber()).toEqual(2 * FIXED_NUMBER);
injectionManager.restoreMethod(obj, 'getNumber');
expect(obj.getNumber()).toEqual(FIXED_NUMBER);
});
it('can extend a GObject vfunc', () => {
const gobj = new GObject1();
let vfuncCalled;
injectionManager.overrideMethod(
GObject1.prototype, 'vfunc_set_property', originalMethod => {
return function (...args) {
// eslint-disable-next-line no-invalid-this
originalMethod.apply(this, args);
vfuncCalled = true;
};
});
vfuncCalled = false;
gobj.set_property('plonked', true);
expect(vfuncCalled).toEqual(true);
});
it('can restore a GObject vfunc', () => {
const gobj = new GObject1();
let vfuncCalled;
injectionManager.overrideMethod(
GObject1.prototype, 'vfunc_set_property', originalMethod => {
return function (...args) {
// eslint-disable-next-line no-invalid-this
originalMethod.apply(this, args);
vfuncCalled = true;
};
});
injectionManager.clear();
vfuncCalled = false;
gobj.set_property('plonked', true);
expect(vfuncCalled).toEqual(false);
});
});