Skip to content
This repository has been archived by the owner on Oct 29, 2024. It is now read-only.

Commit

Permalink
Add high level client
Browse files Browse the repository at this point in the history
High level client automatically subscribes to NameOwnerChanged signal
once proxy object is retrieved.
  • Loading branch information
lesha1201 committed Dec 1, 2021
1 parent 60a4de9 commit 45e7ab2
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 4 deletions.
27 changes: 25 additions & 2 deletions lib/bus.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ const {
const ProxyObject = require('./client/proxy-object');

const xmlHeader = '<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">\n';
const nameOwnerMatchRule =
"type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',path='/org/freedesktop/DBus',member='NameOwnerChanged'";

/**
* @class
Expand Down Expand Up @@ -67,6 +69,7 @@ class MessageBus extends EventEmitter {
this._nameOwners = {};
this._methodHandlers = [];
this._serviceObjects = {};
this._isHighLevelClientInitialized = false;

// An object with match rule keys and refcount values. Used only by
// the internal high-level function `_addMatch` for refcounting.
Expand Down Expand Up @@ -188,9 +191,14 @@ class MessageBus extends EventEmitter {
* @param [xml] {string} - xml introspection data.
* @returns {Promise} - a Promise that resolves with the `ProxyObject`.
*/
getProxyObject (name, path, xml) {
async getProxyObject (name, path, xml) {
const obj = new ProxyObject(this, name, path);
return obj._init(xml);

const objInitPromise = obj._init(xml);

await this._initHighLevelClient();

return objInitPromise;
}

/**
Expand Down Expand Up @@ -404,6 +412,21 @@ class MessageBus extends EventEmitter {
}
}

async _initHighLevelClient () {
if (this._isHighLevelClientInitialized) {
return;
}

try {
await this._addMatch(nameOwnerMatchRule);
} catch (error) {
this.emit("error", error);
return;
}

this._isHighLevelClientInitialized = true;
}

_introspect (path) {
assertObjectPathValid(path);
const xml = {
Expand Down
2 changes: 1 addition & 1 deletion lib/client/proxy-interface.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ class ProxyInterface extends EventEmitter {
}

_signalMatchRuleString (eventName) {
return `type='signal',sender=${this.$object.name},interface='${this.$name}',path='${this.$object.path}',member='${eventName}'`;
return `type='signal',sender='${this.$object.name}',interface='${this.$name}',path='${this.$object.path}',member='${eventName}'`;
}

_getEventListener (signal) {
Expand Down
51 changes: 51 additions & 0 deletions test/integration/signals.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Test that signals emit correctly

const dbus = require('../../');
const { waitForMessage } = require('../util');

const Variant = dbus.Variant;

const {
Expand Down Expand Up @@ -122,6 +124,16 @@ class SignalsInterface extends Interface {
const testIface = new SignalsInterface(TEST_IFACE);
const testIface2 = new SignalsInterface(TEST_IFACE);

async function createTestService(name) {
const testBus = dbus.sessionBus();
const testIface = new SignalsInterface(TEST_IFACE);

await testBus.requestName(name);
testBus.export(TEST_PATH, testIface);

return [testBus, testIface];
}

beforeAll(async () => {
await Promise.all([
bus.requestName(TEST_NAME),
Expand Down Expand Up @@ -262,3 +274,42 @@ test('bug #86: signals dont get lost when no previous method calls have been mad

expect(cb).toHaveBeenCalledTimes(3);
});

test('client continues receive signals from restarted DBus service', async () => {
const clientBus = dbus.sessionBus();

const testServiceName = 'local.test.signals';
let [testBus] = await createTestService(testServiceName);

const object = await clientBus.getProxyObject(testServiceName, TEST_PATH);
const test = object.getInterface(TEST_IFACE);
const cb = jest.fn();

expect(clientBus._nameOwners[testServiceName]).toEqual(testBus.name);

test.on('HelloWorld', cb);
test.on('SignalMultiple', cb);
test.on('SignalComplicated', cb);

await test.EmitSignals();

expect(cb).toHaveBeenCalledTimes(3);

await testBus.releaseName(testServiceName);
testBus.disconnect();

await waitForMessage(clientBus, { member: 'NameOwnerChanged' });
expect(clientBus._nameOwners[testServiceName]).toEqual('');

[testBus] = await createTestService(testServiceName);

await waitForMessage(clientBus, { member: 'NameOwnerChanged' });
expect(clientBus._nameOwners[testServiceName]).toEqual(testBus.name);

await test.EmitSignals();

expect(cb).toHaveBeenCalledTimes(6);

clientBus.disconnect();
testBus.disconnect();
});
20 changes: 19 additions & 1 deletion test/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,24 @@ async function ping (bus) {
}));
}

/**
* Waits for a message that passes a filter on a provided bus.
*/
function waitForMessage(bus, messageFilter) {
return new Promise((resolve) => {
bus.on('message', (message) => {
const isMessageValid = Object.entries(messageFilter).every(
([key, value]) => message[key] === value
);

if (isMessageValid) {
resolve();
}
});
});
}

module.exports = {
ping: ping
ping,
waitForMessage,
};

0 comments on commit 45e7ab2

Please sign in to comment.