Skip to content

Commit c826262

Browse files
author
Tony Crisci
committed
add integration tests
Integration tests test the client and the server interfaces together on a real bus. Run integration tests with `npm run integration`. This commit also includes fixes for bugs that were found while writing the tests.
1 parent 3cc2654 commit c826262

15 files changed

Lines changed: 567 additions & 23 deletions

.babelrc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"plugins": [
3+
[ "@babel/plugin-proposal-decorators", { "decoratorsBeforeExport": true, "legacy": false } ],
4+
"@babel/plugin-proposal-class-properties"
5+
]
6+
}

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ logs
1212
results
1313

1414
node_modules
15-
npm-debug.log
15+
npm-debug.log
16+
package-lock.json

.tern-project

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"plugins": {
3+
"node": {}
4+
}
5+
}

index.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,8 @@ module.exports.createServer = server.createServer;
156156

157157
// new stuff
158158
const variant = require('./lib/service/variant');
159-
const interface = require('./lib/service/interface');
160-
module.exports.interface = interface;
159+
const iface = require('./lib/service/interface');
160+
module.exports.interface = iface;
161161
module.exports.Variant = variant.Variant;
162+
// TODO move me off the interface
163+
module.exports.MethodError = iface.MethodError;

lib/bus.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ module.exports = function bus(conn, opts) {
231231
that.names = that.names || {};
232232
that.names[name] = new Name(that);
233233
let obj = that.names[name].getObject(path);
234+
// TODO error interface already added at this path
234235
obj.addInterface(iface);
235236
resolve(result);
236237
}

lib/client/proxy-object.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ const parseSignature = require('../signature');
33
const ProxyInterface = require('./proxy-interface');
44
const variant = require('../service/variant');
55
const Variant = variant.Variant;
6+
const { MethodError } = require('../service/interface');
67
const {
78
assertInterfaceNameValid,
89
assertObjectPathValid,
@@ -130,10 +131,15 @@ class ProxyObject {
130131
body: body
131132
};
132133

133-
return new Promise(resolve => {
134-
this.bus.invoke(msg, (err, ...busResult) => {
134+
return new Promise((resolve, reject) => {
135+
this.bus.invoke(msg, function(err, ...busResult) {
135136
if (err) {
136-
throw new Error(err);
137+
if (this.message && this.message.hasOwnProperty('errorName')) {
138+
reject(new MethodError(this.message.errorName, err[0]));
139+
} else {
140+
reject(err);
141+
}
142+
return;
137143
}
138144
// TODO refactor this into a method
139145
let result = [];

lib/service/handlers.js

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ function handleGetProperty(bus, msg, name, path) {
5151
}
5252

5353
let body = variant.jsToMarshalFmt(property.signature, propertyValue);
54+
5455
bus.sendReply(msg, 'v', [body]);
5556
}
5657

@@ -100,7 +101,7 @@ function handleSetProperty(bus, msg, name, path) {
100101
let property = properties[prop];
101102

102103
if (!(property.access === ACCESS_WRITE || property.access === ACCESS_READWRITE)) {
103-
bus.sendError(msg, INVALID_ARGS, `Property ${prop} does not have write access`);
104+
bus.sendError(msg, INVALID_ARGS, `Property does not have write access: '${prop}'`);
104105
}
105106

106107
let valueSignature = variant.collapseSignature(value[0][0])
@@ -115,14 +116,15 @@ function handleSetProperty(bus, msg, name, path) {
115116

116117
function handleStdIfaces(bus, msg, name) {
117118
let {
118-
interface,
119119
member,
120120
path,
121121
signature
122122
} = msg;
123123

124-
if (!isInterfaceNameValid(interface)) {
125-
bus.sendError(msg, INVALID_ARGS, `Invalid interface name: '${interface}'`);
124+
let ifaceName = msg.interface;
125+
126+
if (!isInterfaceNameValid(ifaceName)) {
127+
bus.sendError(msg, INVALID_ARGS, `Invalid interface name: '${ifaceName}'`);
126128
return true;
127129
}
128130

@@ -136,12 +138,12 @@ function handleStdIfaces(bus, msg, name) {
136138
return true;
137139
}
138140

139-
if (interface === 'org.freedesktop.DBus.Introspectable' &&
141+
if (ifaceName === 'org.freedesktop.DBus.Introspectable' &&
140142
member === 'Introspect' &&
141143
!signature) {
142144
handleIntrospect(bus, msg, name, path);
143145
return true;
144-
} else if (interface === 'org.freedesktop.DBus.Properties') {
146+
} else if (ifaceName === 'org.freedesktop.DBus.Properties') {
145147
if (member === 'Get' && signature === 'ss') {
146148
handleGetProperty(bus, msg, name, path);
147149
return true;
@@ -161,11 +163,12 @@ module.exports = function(msg, bus) {
161163
let {
162164
path,
163165
member,
164-
interface,
165166
destination,
166167
signature
167168
} = msg;
168169

170+
let ifaceName = msg.interface;
171+
169172
signature = signature || '';
170173

171174
if (Object.keys(bus.names) === 0) {
@@ -194,7 +197,7 @@ module.exports = function(msg, bus) {
194197
}
195198

196199
let obj = name.getObject(path);
197-
let iface = obj.interfaces[interface];
200+
let iface = obj.interfaces[ifaceName];
198201

199202
if (!iface) {
200203
return false;

lib/service/variant.js

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,13 @@ function parse(variant) {
8888
}
8989

9090
function jsToMarshalFmt(signature, value) {
91-
// TODO better error handling because this is called on user functions and
92-
// they may have mismatched signature and value
91+
if (value === undefined) {
92+
throw new Error(`expected value for signature: ${signature}`);
93+
}
94+
if (signature === undefined) {
95+
throw new Error(`expected signature for value: ${value}`);
96+
}
97+
9398
let signatureStr = null;
9499
if (typeof signature === 'string') {
95100
signatureStr = signature;
@@ -99,17 +104,23 @@ function jsToMarshalFmt(signature, value) {
99104
}
100105

101106
if (signature.child.length === 0) {
102-
if (value.constructor === Variant) {
103-
return [value.signature, value.value];
107+
if (signature.type === 'v') {
108+
if (value.constructor !== Variant) {
109+
throw new Error(`expected a Variant for value (got ${typeof value})`);
110+
}
111+
return [ signature.type, jsToMarshalFmt(value.signature, value.value) ];
104112
} else {
105-
return [signature.type, value];
113+
return [ signature.type, value ];
106114
}
107115
}
108116

109117
if (signature.type === 'a') {
110118
let result = [];
111119
if (signature.child[0].type === '{') {
112120
// this is an array of dictionary elements
121+
if (value.constructor !== Object) {
122+
throw new Error(`expecting an object for signature '${signatureStr}' (got ${typeof value})`);
123+
}
113124
for (let k of Object.keys(value)) {
114125
let v = value[k];
115126
if (v.constructor === Variant) {
@@ -119,6 +130,9 @@ function jsToMarshalFmt(signature, value) {
119130
}
120131
}
121132
} else {
133+
if (!Array.isArray(value)) {
134+
throw new Error(`expecting an array for signature '${signatureStr}' (got ${typeof value})`);
135+
}
122136
for (let v of value) {
123137
if (v.constructor === Variant) {
124138
result.push(jsToMarshalFmt(v.signature, v.value));
@@ -127,18 +141,29 @@ function jsToMarshalFmt(signature, value) {
127141
}
128142
}
129143
}
130-
return [collapseSignature(signature), result];
144+
return [ signatureStr, result ];
131145
} else if (signature.type === '(') {
146+
if (!Array.isArray(value)) {
147+
throw new Error(`expecting an array for signature '${signatureStr}' (got ${typeof value})`);
148+
}
149+
if (value.length !== signature.child.length) {
150+
throw new Error(`expecting struct to have ${signature.child.length} members (got ${value.length} members)`);
151+
}
132152
let result = [];
133153
for (let i = 0; i < value.length; ++i) {
134154
let v = value[i];
135-
if (v.constructor === Variant) {
155+
if (signature.child[i] === 'v') {
156+
if (v.constructor !== Variant) {
157+
throw new Error(`expected a Variant for struct member ${i+1} (got ${v})`);
158+
}
136159
result.push(jsToMarshalFmt(v.signature, v.value));
137160
} else {
138161
result.push(jsToMarshalFmt(signature.child[i], v)[1]);;
139162
}
140163
}
141-
return [signatureStr, result];
164+
return [ signatureStr, result ];
165+
} else {
166+
throw new Error(`got unknown complex type: ${signature.type}`);
142167
}
143168
}
144169

package.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,22 @@
4545
"abstract-socket": "^2.0.0"
4646
},
4747
"devDependencies": {
48+
"@babel/core": "^7.1.5",
49+
"@babel/plugin-proposal-class-properties": "^7.1.0",
50+
"@babel/plugin-proposal-decorators": "^7.1.2",
51+
"babel-core": "^7.0.0-bridge.0",
52+
"babel-jest": "^23.6.0",
4853
"commander": "^2.19.0",
4954
"eslint": "^5.0.0",
5055
"eslint-config-prettier": "^3.0.0",
5156
"eslint-plugin-markdown": "^1.0.0-beta.6",
5257
"eslint-plugin-prettier": "^3.0.0",
5358
"husky": "^1.0.0",
59+
"jest": "^23.6.0",
5460
"lint-staged": "^8.0.0",
5561
"mocha": "*",
56-
"prettier": "^1.7.4"
62+
"prettier": "^1.7.4",
63+
"regenerator-runtime": "^0.12.1"
5764
},
5865
"scripts": {
5966
"lint": "npm run lint:docs && npm run lint:code",
@@ -64,6 +71,7 @@
6471
"prettier": "prettier --write index.js '{bin,lib,examples,test}/**/*.js'",
6572
"prettier:docs": "prettier-markdown README.md",
6673
"eslint-check": "eslint --print-config .eslintrc | eslint-config-prettier-check",
74+
"integration": "dbus-run-session -- jest ./test/integration",
6775
"prepublish": "npm prune"
6876
},
6977
"lint-staged": {
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Test some of the standard dbus interfaces to make sure the client works
2+
// correctly
3+
4+
let dbus = require('../../');
5+
let bus = dbus.sessionBus();
6+
7+
afterAll(() => {
8+
bus.connection.stream.end();
9+
});
10+
11+
test('lists names on the bus', async () => {
12+
let object = await bus.getProxyObject('org.freedesktop.DBus', '/org/freedesktop/DBus');
13+
let iface = object.getInterface('org.freedesktop.DBus');
14+
expect(iface).toBeDefined();
15+
let names = await iface.ListNames();
16+
expect(names.length).toBeGreaterThan(0);
17+
expect(names).toEqual(expect.arrayContaining(['org.freedesktop.DBus']));
18+
});
19+
20+
test('get stats', async () => {
21+
let object = await bus.getProxyObject('org.freedesktop.DBus', '/org/freedesktop/DBus');
22+
let iface = object.getInterface('org.freedesktop.DBus.Debug.Stats');
23+
let stats = await iface.GetStats();
24+
expect(stats).toBeInstanceOf(Object);
25+
expect(stats).toHaveProperty('BusNames');
26+
let busNames = stats['BusNames'];
27+
expect(busNames).toBeInstanceOf(dbus.Variant);
28+
expect(busNames.signature).toBe('u');
29+
expect(busNames.value).toBeGreaterThan(0);
30+
});

0 commit comments

Comments
 (0)