@neurodevs/ndx-native 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.nvmrc +1 -0
- package/.vscode/launch.json +58 -0
- package/.vscode/settings.json +67 -0
- package/.vscode/tasks.json +130 -0
- package/LICENSE +21 -0
- package/README.md +2 -0
- package/build/.spruce/settings.json +11 -0
- package/build/__tests__/AbstractPackageTest.d.ts +7 -0
- package/build/__tests__/AbstractPackageTest.js +14 -0
- package/build/__tests__/AbstractPackageTest.js.map +1 -0
- package/build/__tests__/impl/LabrecorderAdapter.test.d.ts +30 -0
- package/build/__tests__/impl/LabrecorderAdapter.test.js +164 -0
- package/build/__tests__/impl/LabrecorderAdapter.test.js.map +1 -0
- package/build/__tests__/impl/LiblslAdapter.test.d.ts +84 -0
- package/build/__tests__/impl/LiblslAdapter.test.js +750 -0
- package/build/__tests__/impl/LiblslAdapter.test.js.map +1 -0
- package/build/__tests__/impl/LibxdfAdapter.test.d.ts +45 -0
- package/build/__tests__/impl/LibxdfAdapter.test.js +243 -0
- package/build/__tests__/impl/LibxdfAdapter.test.js.map +1 -0
- package/build/consts.d.ts +11 -0
- package/build/consts.js +21 -0
- package/build/consts.js.map +1 -0
- package/build/impl/LabrecorderAdapter.d.ts +30 -0
- package/build/impl/LabrecorderAdapter.js +69 -0
- package/build/impl/LabrecorderAdapter.js.map +1 -0
- package/build/impl/LiblslAdapter.d.ts +172 -0
- package/build/impl/LiblslAdapter.js +360 -0
- package/build/impl/LiblslAdapter.js.map +1 -0
- package/build/impl/LibxdfAdapter.d.ts +92 -0
- package/build/impl/LibxdfAdapter.js +130 -0
- package/build/impl/LibxdfAdapter.js.map +1 -0
- package/build/index.d.ts +14 -0
- package/build/index.js +18 -0
- package/build/index.js.map +1 -0
- package/build/lib/handleError.d.ts +8 -0
- package/build/lib/handleError.js +25 -0
- package/build/lib/handleError.js.map +1 -0
- package/build/testDoubles/Labrecorder/FakeLabrecorder.d.ts +15 -0
- package/build/testDoubles/Labrecorder/FakeLabrecorder.js +26 -0
- package/build/testDoubles/Labrecorder/FakeLabrecorder.js.map +1 -0
- package/build/testDoubles/Liblsl/FakeLiblsl.d.ts +57 -0
- package/build/testDoubles/Liblsl/FakeLiblsl.js +140 -0
- package/build/testDoubles/Liblsl/FakeLiblsl.js.map +1 -0
- package/build/testDoubles/Libxdf/FakeLibxdf.d.ts +13 -0
- package/build/testDoubles/Libxdf/FakeLibxdf.js +25 -0
- package/build/testDoubles/Libxdf/FakeLibxdf.js.map +1 -0
- package/build/testDoubles/Libxdf/SpyLibxdf.d.ts +6 -0
- package/build/testDoubles/Libxdf/SpyLibxdf.js +10 -0
- package/build/testDoubles/Libxdf/SpyLibxdf.js.map +1 -0
- package/eslint.config.mjs +3 -0
- package/package.json +84 -0
- package/src/.spruce/settings.json +11 -0
- package/src/__tests__/AbstractPackageTest.ts +16 -0
- package/src/__tests__/impl/LabrecorderAdapter.test.ts +197 -0
- package/src/__tests__/impl/LiblslAdapter.test.ts +907 -0
- package/src/__tests__/impl/LibxdfAdapter.test.ts +290 -0
- package/src/consts.ts +21 -0
- package/src/impl/LabrecorderAdapter.ts +100 -0
- package/src/impl/LiblslAdapter.ts +616 -0
- package/src/impl/LibxdfAdapter.ts +227 -0
- package/src/index.ts +26 -0
- package/src/lib/handleError.ts +24 -0
- package/src/testDoubles/Labrecorder/FakeLabrecorder.ts +37 -0
- package/src/testDoubles/Liblsl/FakeLiblsl.ts +204 -0
- package/src/testDoubles/Libxdf/FakeLibxdf.ts +33 -0
- package/src/testDoubles/Libxdf/SpyLibxdf.ts +13 -0
- package/tsconfig.json +28 -0
|
@@ -0,0 +1,750 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { randomInt } from 'crypto';
|
|
8
|
+
import { test, assert } from '@neurodevs/node-tdd';
|
|
9
|
+
import { createPointer, DataType, unwrapPointer, } from 'ffi-rs';
|
|
10
|
+
import LiblslAdapter from '../../impl/LiblslAdapter.js';
|
|
11
|
+
import FakeLiblsl from '../../testDoubles/Liblsl/FakeLiblsl.js';
|
|
12
|
+
import AbstractPackageTest from '../AbstractPackageTest.js';
|
|
13
|
+
export default class LiblslAdapterTest extends AbstractPackageTest {
|
|
14
|
+
static instance;
|
|
15
|
+
static libraryPath;
|
|
16
|
+
static libraryOptions;
|
|
17
|
+
static fakeBindings;
|
|
18
|
+
static shouldThrowWhenCreatingBindings;
|
|
19
|
+
static fakeInfoHandle;
|
|
20
|
+
static fakeOutletHandle;
|
|
21
|
+
static fakeInletHandle = {};
|
|
22
|
+
static fakeDescHandle;
|
|
23
|
+
static fakeChildHandle;
|
|
24
|
+
static fakeChildNamedChannel;
|
|
25
|
+
static createStreamInfoParams;
|
|
26
|
+
static destroyStreamInfoParams;
|
|
27
|
+
static appendChildParams = [];
|
|
28
|
+
static appendChildValueParams;
|
|
29
|
+
static appendChildHitCount;
|
|
30
|
+
static getDescriptionParams;
|
|
31
|
+
static getChannelCountParams;
|
|
32
|
+
static createOutletParams;
|
|
33
|
+
static pushSampleFloatTimestampParams;
|
|
34
|
+
static pushSampleStringTimestampParams;
|
|
35
|
+
static destroyOutletParams;
|
|
36
|
+
static createInletParams;
|
|
37
|
+
static flushInletParams;
|
|
38
|
+
static destroyInletParams;
|
|
39
|
+
static localClockParams;
|
|
40
|
+
static ffiRsOpenOptions;
|
|
41
|
+
static ffiRsDefineOptions;
|
|
42
|
+
static ffiRsLoadOptions;
|
|
43
|
+
static async beforeEach() {
|
|
44
|
+
await super.beforeEach();
|
|
45
|
+
delete this.libraryPath;
|
|
46
|
+
delete this.libraryOptions;
|
|
47
|
+
delete this.createStreamInfoParams;
|
|
48
|
+
delete this.createOutletParams;
|
|
49
|
+
delete this.destroyOutletParams;
|
|
50
|
+
delete this.createInletParams;
|
|
51
|
+
delete this.destroyInletParams;
|
|
52
|
+
delete this.pushSampleFloatTimestampParams;
|
|
53
|
+
delete this.getDescriptionParams;
|
|
54
|
+
delete this.getChannelCountParams;
|
|
55
|
+
this.appendChildParams = [];
|
|
56
|
+
this.appendChildValueParams = [];
|
|
57
|
+
process.env.LIBLSL_PATH = this.generateId();
|
|
58
|
+
this.fakeInfoHandle = {};
|
|
59
|
+
this.fakeDescHandle = {};
|
|
60
|
+
this.fakeOutletHandle = {};
|
|
61
|
+
this.fakeChildHandle = {};
|
|
62
|
+
this.fakeChildNamedChannel = {};
|
|
63
|
+
this.fakeBindings = this.FakeBindings();
|
|
64
|
+
this.shouldThrowWhenCreatingBindings = false;
|
|
65
|
+
this.appendChildHitCount = 0;
|
|
66
|
+
delete this.ffiRsOpenOptions;
|
|
67
|
+
LiblslAdapter.open = (options) => {
|
|
68
|
+
this.ffiRsOpenOptions = options;
|
|
69
|
+
};
|
|
70
|
+
LiblslAdapter.define = (options) => {
|
|
71
|
+
this.ffiRsDefineOptions = options;
|
|
72
|
+
if (this.shouldThrowWhenCreatingBindings) {
|
|
73
|
+
throw new Error('Failed to create bindings!');
|
|
74
|
+
}
|
|
75
|
+
return this.fakeBindings;
|
|
76
|
+
};
|
|
77
|
+
LiblslAdapter.load = (options) => {
|
|
78
|
+
this.ffiRsLoadOptions = options;
|
|
79
|
+
return this.fakeNumResolveResults;
|
|
80
|
+
};
|
|
81
|
+
LiblslAdapter.resetInstance();
|
|
82
|
+
this.instance = LiblslAdapter.getInstance();
|
|
83
|
+
}
|
|
84
|
+
static async createsInstance() {
|
|
85
|
+
assert.isTruthy(this.instance, 'Failed to create instance!');
|
|
86
|
+
}
|
|
87
|
+
static async throwsWhenBindingsFailToLoad() {
|
|
88
|
+
this.shouldThrowWhenCreatingBindings = true;
|
|
89
|
+
LiblslAdapter.resetInstance();
|
|
90
|
+
const err = assert.doesThrow(() => LiblslAdapter.getInstance());
|
|
91
|
+
assert.isTrue(err.message.includes(this.generateFailedMessage()), 'Did not receive the expected error!');
|
|
92
|
+
}
|
|
93
|
+
static async callsOpenOnFfiRs() {
|
|
94
|
+
assert.isEqualDeep(this.ffiRsOpenOptions, {
|
|
95
|
+
library: 'lsl',
|
|
96
|
+
path: process.env.LIBLSL_PATH,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
static async worksAsASingleton() {
|
|
100
|
+
const liblsl = LiblslAdapter.getInstance();
|
|
101
|
+
//@ts-ignore
|
|
102
|
+
assert.isInstanceOf(liblsl, LiblslAdapter);
|
|
103
|
+
}
|
|
104
|
+
static async singletonIsTheSame() {
|
|
105
|
+
assert.isEqual(LiblslAdapter.getInstance(), LiblslAdapter.getInstance());
|
|
106
|
+
}
|
|
107
|
+
static canSetInstance() {
|
|
108
|
+
const fake = new FakeLiblsl();
|
|
109
|
+
LiblslAdapter.setInstance(fake);
|
|
110
|
+
assert.isEqual(LiblslAdapter.getInstance(), fake);
|
|
111
|
+
}
|
|
112
|
+
static async createsExpectedBindingsWithFfiRs() {
|
|
113
|
+
assert.isEqualDeep(this.ffiRsDefineOptions, {
|
|
114
|
+
lsl_create_streaminfo: {
|
|
115
|
+
library: 'lsl',
|
|
116
|
+
retType: DataType.External,
|
|
117
|
+
paramsType: [
|
|
118
|
+
DataType.String,
|
|
119
|
+
DataType.String,
|
|
120
|
+
DataType.I32,
|
|
121
|
+
DataType.Double,
|
|
122
|
+
DataType.I32,
|
|
123
|
+
DataType.String,
|
|
124
|
+
],
|
|
125
|
+
},
|
|
126
|
+
lsl_destroy_streaminfo: {
|
|
127
|
+
library: 'lsl',
|
|
128
|
+
retType: DataType.Void,
|
|
129
|
+
paramsType: [DataType.External],
|
|
130
|
+
},
|
|
131
|
+
lsl_create_outlet: {
|
|
132
|
+
library: 'lsl',
|
|
133
|
+
retType: DataType.External,
|
|
134
|
+
paramsType: [DataType.External, DataType.I32, DataType.I32],
|
|
135
|
+
},
|
|
136
|
+
lsl_push_sample_ft: {
|
|
137
|
+
library: 'lsl',
|
|
138
|
+
retType: DataType.I32,
|
|
139
|
+
paramsType: [
|
|
140
|
+
DataType.External,
|
|
141
|
+
DataType.FloatArray,
|
|
142
|
+
DataType.Double,
|
|
143
|
+
],
|
|
144
|
+
},
|
|
145
|
+
lsl_push_sample_strt: {
|
|
146
|
+
library: 'lsl',
|
|
147
|
+
retType: DataType.I32,
|
|
148
|
+
paramsType: [
|
|
149
|
+
DataType.External,
|
|
150
|
+
DataType.StringArray,
|
|
151
|
+
DataType.Double,
|
|
152
|
+
],
|
|
153
|
+
},
|
|
154
|
+
lsl_destroy_outlet: {
|
|
155
|
+
library: 'lsl',
|
|
156
|
+
retType: DataType.Void,
|
|
157
|
+
paramsType: [DataType.External],
|
|
158
|
+
},
|
|
159
|
+
lsl_create_inlet: {
|
|
160
|
+
library: 'lsl',
|
|
161
|
+
retType: DataType.External,
|
|
162
|
+
paramsType: [
|
|
163
|
+
DataType.External,
|
|
164
|
+
DataType.I32,
|
|
165
|
+
DataType.I32,
|
|
166
|
+
DataType.I32,
|
|
167
|
+
],
|
|
168
|
+
},
|
|
169
|
+
lsl_inlet_flush: {
|
|
170
|
+
library: 'lsl',
|
|
171
|
+
retType: DataType.I32,
|
|
172
|
+
paramsType: [DataType.External],
|
|
173
|
+
},
|
|
174
|
+
lsl_destroy_inlet: {
|
|
175
|
+
library: 'lsl',
|
|
176
|
+
retType: DataType.Void,
|
|
177
|
+
paramsType: [DataType.External],
|
|
178
|
+
},
|
|
179
|
+
lsl_get_desc: {
|
|
180
|
+
library: 'lsl',
|
|
181
|
+
retType: DataType.External,
|
|
182
|
+
paramsType: [DataType.External],
|
|
183
|
+
},
|
|
184
|
+
lsl_get_channel_count: {
|
|
185
|
+
library: 'lsl',
|
|
186
|
+
retType: DataType.I32,
|
|
187
|
+
paramsType: [DataType.External],
|
|
188
|
+
},
|
|
189
|
+
lsl_append_child: {
|
|
190
|
+
library: 'lsl',
|
|
191
|
+
retType: DataType.External,
|
|
192
|
+
paramsType: [DataType.External, DataType.String],
|
|
193
|
+
},
|
|
194
|
+
lsl_append_child_value: {
|
|
195
|
+
library: 'lsl',
|
|
196
|
+
retType: DataType.External,
|
|
197
|
+
paramsType: [
|
|
198
|
+
DataType.External,
|
|
199
|
+
DataType.String,
|
|
200
|
+
DataType.String,
|
|
201
|
+
],
|
|
202
|
+
},
|
|
203
|
+
lsl_local_clock: {
|
|
204
|
+
library: 'lsl',
|
|
205
|
+
retType: DataType.Double,
|
|
206
|
+
paramsType: [],
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
static async createsStreamInfoWithRequiredParams() {
|
|
211
|
+
const options = this.generateRandomCreateStreamInfoOptions();
|
|
212
|
+
const actual = this.instance.createStreamInfo(options);
|
|
213
|
+
assert.isEqual(actual, this.fakeInfoHandle);
|
|
214
|
+
assert.isEqualDeep(this.createStreamInfoParams, Object.values(options));
|
|
215
|
+
}
|
|
216
|
+
static async destroyStreamInfoPasses() {
|
|
217
|
+
const infoHandle = this.createRandomInfoHandle();
|
|
218
|
+
this.instance.destroyStreamInfo({ infoHandle });
|
|
219
|
+
assert.isEqualDeep(this.destroyStreamInfoParams, [infoHandle], 'Did not call destroyStreamInfo with expected params!');
|
|
220
|
+
}
|
|
221
|
+
static async resolvesStreamInfoByProp() {
|
|
222
|
+
await this.resolveByProp();
|
|
223
|
+
assert.isEqualDeep(this.ffiRsLoadOptions, {
|
|
224
|
+
library: 'lsl',
|
|
225
|
+
funcName: 'lsl_resolve_byprop',
|
|
226
|
+
retType: DataType.I32,
|
|
227
|
+
paramsType: [
|
|
228
|
+
DataType.External,
|
|
229
|
+
DataType.I32,
|
|
230
|
+
DataType.String,
|
|
231
|
+
DataType.String,
|
|
232
|
+
DataType.I32,
|
|
233
|
+
DataType.Double,
|
|
234
|
+
],
|
|
235
|
+
paramsValue: [
|
|
236
|
+
this.resultsBufferPtr,
|
|
237
|
+
this.maxResults,
|
|
238
|
+
this.prop,
|
|
239
|
+
this.value,
|
|
240
|
+
this.minResults,
|
|
241
|
+
this.timeoutMs / 1000,
|
|
242
|
+
],
|
|
243
|
+
runInNewThread: true,
|
|
244
|
+
}, 'Did not call resolveByProp with expected params!');
|
|
245
|
+
}
|
|
246
|
+
static async resolveByPropAcceptsOptionalArgs() {
|
|
247
|
+
const minResults = randomInt(0, 10);
|
|
248
|
+
const timeoutMs = Math.random() * 1000;
|
|
249
|
+
await this.resolveByProp({
|
|
250
|
+
minResults,
|
|
251
|
+
timeoutMs,
|
|
252
|
+
});
|
|
253
|
+
assert.isEqualDeep({
|
|
254
|
+
minResults: this.ffiRsLoadOptions?.['paramsValue']?.[4],
|
|
255
|
+
timeoutMs: this.ffiRsLoadOptions?.['paramsValue']?.[5],
|
|
256
|
+
}, { minResults, timeoutMs: timeoutMs / 1000 }, 'Did not call resolveByProp with expected params!');
|
|
257
|
+
}
|
|
258
|
+
static async returnsHandlesForResolvedStreamInfos() {
|
|
259
|
+
LiblslAdapter.alloc = (size) => {
|
|
260
|
+
const buffer = Buffer.alloc(size);
|
|
261
|
+
for (let i = 0; i < this.fakeNumResolveResults; i++) {
|
|
262
|
+
const offset = i * this.bytesPerPointer;
|
|
263
|
+
buffer.writeBigUInt64LE(BigInt(i + 1), offset);
|
|
264
|
+
}
|
|
265
|
+
return buffer;
|
|
266
|
+
};
|
|
267
|
+
const expectedHandles = [];
|
|
268
|
+
for (let i = 0; i < this.fakeNumResolveResults; i++) {
|
|
269
|
+
expectedHandles.push(BigInt(i + 1));
|
|
270
|
+
}
|
|
271
|
+
const actualHandles = await this.resolveByProp();
|
|
272
|
+
const actual = actualHandles.map((_h, i) => (i + 1).toString());
|
|
273
|
+
const expected = expectedHandles.map((h) => h.toString());
|
|
274
|
+
assert.isEqualDeep(actual, expected, 'Did not receive expected resolved stream info handles!');
|
|
275
|
+
}
|
|
276
|
+
static async createsOutletWithRequiredParams() {
|
|
277
|
+
const { options, outletHandle } = this.createRandomOutlet();
|
|
278
|
+
const expected = {
|
|
279
|
+
...options,
|
|
280
|
+
maxBufferedMs: options.maxBufferedMs / 1000,
|
|
281
|
+
};
|
|
282
|
+
assert.isEqualDeep(this.createOutletParams, Object.values(expected));
|
|
283
|
+
assert.isEqual(outletHandle, this.fakeOutletHandle);
|
|
284
|
+
}
|
|
285
|
+
static async pushesFloatSample() {
|
|
286
|
+
const expected = [1.0, 2.0, 3.0];
|
|
287
|
+
const timestamp = randomInt(100);
|
|
288
|
+
const options = {
|
|
289
|
+
outletHandle: this.fakeOutletHandle,
|
|
290
|
+
sample: expected,
|
|
291
|
+
timestamp,
|
|
292
|
+
};
|
|
293
|
+
this.instance.pushSampleFloatTimestamp(options);
|
|
294
|
+
assert.isEqualDeep(this.pushSampleFloatTimestampParams, [
|
|
295
|
+
this.fakeOutletHandle,
|
|
296
|
+
expected,
|
|
297
|
+
timestamp,
|
|
298
|
+
]);
|
|
299
|
+
}
|
|
300
|
+
static async pushesStringSample() {
|
|
301
|
+
const expected = [this.generateId()];
|
|
302
|
+
const timestamp = randomInt(100);
|
|
303
|
+
const options = {
|
|
304
|
+
outletHandle: this.fakeOutletHandle,
|
|
305
|
+
sample: expected,
|
|
306
|
+
timestamp,
|
|
307
|
+
};
|
|
308
|
+
this.instance.pushSampleStringTimestamp(options);
|
|
309
|
+
assert.isEqual(this.pushSampleStringTimestampParams?.[0], this.fakeOutletHandle);
|
|
310
|
+
assert.isEqualDeep(this.pushSampleStringTimestampParams?.[1], expected);
|
|
311
|
+
assert.isEqual(this.pushSampleStringTimestampParams?.[2], timestamp);
|
|
312
|
+
}
|
|
313
|
+
static async addingSingleChannelGetsDescription() {
|
|
314
|
+
const infoHandle = this.createRandomInfoHandle();
|
|
315
|
+
const channel = this.generateRandomChannelValues();
|
|
316
|
+
this.instance.appendChannelsToStreamInfo({
|
|
317
|
+
infoHandle,
|
|
318
|
+
channels: [channel],
|
|
319
|
+
});
|
|
320
|
+
assert.isEqualDeep(this.getDescriptionParams?.[0], [infoHandle]);
|
|
321
|
+
assert.isEqual(this.appendChildParams?.[0][0], this.fakeDescHandle);
|
|
322
|
+
assert.isEqual(this.appendChildParams?.[0][1], 'channels');
|
|
323
|
+
assert.isEqual(this.appendChildParams?.[1][0], this.fakeChildHandle);
|
|
324
|
+
assert.isEqual(this.appendChildParams?.[1][1], 'channel');
|
|
325
|
+
assert.isLength(this.appendChildValueParams, 3);
|
|
326
|
+
for (let i = 0; i < 3; i++) {
|
|
327
|
+
const param = this.appendChildValueParams[i];
|
|
328
|
+
assert.isEqual(param[0], this.fakeChildNamedChannel);
|
|
329
|
+
}
|
|
330
|
+
assert.isEqual(this.appendChildValueParams[0][1], 'label');
|
|
331
|
+
assert.isEqual(this.appendChildValueParams[1][1], 'unit');
|
|
332
|
+
assert.isEqual(this.appendChildValueParams[2][1], 'type');
|
|
333
|
+
assert.isEqual(this.appendChildValueParams[0][2], channel.label);
|
|
334
|
+
assert.isEqual(this.appendChildValueParams[1][2], channel.units);
|
|
335
|
+
assert.isEqual(this.appendChildValueParams[2][2], channel.type);
|
|
336
|
+
}
|
|
337
|
+
static async addingMultpleChannelsAddsChildrenToChannelsChild() {
|
|
338
|
+
const infoHandle = this.createRandomInfoHandle();
|
|
339
|
+
const channel1 = this.generateRandomChannelValues();
|
|
340
|
+
const channel2 = this.generateRandomChannelValues();
|
|
341
|
+
this.instance.appendChannelsToStreamInfo({
|
|
342
|
+
infoHandle,
|
|
343
|
+
channels: [channel1, channel2],
|
|
344
|
+
});
|
|
345
|
+
assert.isEqual(this.appendChildParams?.[2][0], this.fakeChildHandle);
|
|
346
|
+
assert.isEqual(this.appendChildParams?.[2][1], 'channel');
|
|
347
|
+
assert.isEqual(this.appendChildValueParams[3][2], channel2.label);
|
|
348
|
+
assert.isEqual(this.appendChildValueParams[4][2], channel2.units);
|
|
349
|
+
assert.isEqual(this.appendChildValueParams[5][2], channel2.type);
|
|
350
|
+
}
|
|
351
|
+
static async canDestroyOutlet() {
|
|
352
|
+
const { outletHandle } = this.createRandomOutlet();
|
|
353
|
+
const options = { outletHandle };
|
|
354
|
+
this.instance.destroyOutlet(options);
|
|
355
|
+
assert.isEqualDeep(this.destroyOutletParams, Object.values(options));
|
|
356
|
+
}
|
|
357
|
+
static async callingLocalClockTwiceReturnsDifferentTimestamps() {
|
|
358
|
+
const t1 = this.instance.localClock();
|
|
359
|
+
await this.wait(10);
|
|
360
|
+
const t2 = this.instance.localClock();
|
|
361
|
+
assert.isNotEqual(t1, t2);
|
|
362
|
+
}
|
|
363
|
+
static async localClockBindingReceivesEmptyArray() {
|
|
364
|
+
delete this.localClockParams;
|
|
365
|
+
this.instance.localClock();
|
|
366
|
+
assert.isEqualDeep(this.localClockParams, []);
|
|
367
|
+
}
|
|
368
|
+
static async defaultsToMacOsPath() {
|
|
369
|
+
delete process.env.LIBLSL_PATH;
|
|
370
|
+
LiblslAdapter.resetInstance();
|
|
371
|
+
this.instance = LiblslAdapter.getInstance();
|
|
372
|
+
assert.isEqual(this.ffiRsOpenOptions?.path, '/opt/homebrew/Cellar/lsl/1.16.2/lib/liblsl.1.16.2.dylib');
|
|
373
|
+
}
|
|
374
|
+
static async createInletWithRequiredParams() {
|
|
375
|
+
const { options, inletHandle } = this.createRandomInlet();
|
|
376
|
+
const { maxBufferedMs } = options;
|
|
377
|
+
const expected = {
|
|
378
|
+
...options,
|
|
379
|
+
maxBufferedMs: maxBufferedMs / 1000,
|
|
380
|
+
};
|
|
381
|
+
const maxChunkSize = 0;
|
|
382
|
+
const shouldRecover = 1;
|
|
383
|
+
assert.isEqualDeep(this.createInletParams, [...Object.values(expected), maxChunkSize, shouldRecover], 'Did not call createInlet with expected params!');
|
|
384
|
+
assert.isEqual(inletHandle, this.fakeInletHandle, 'Did not receive expected inlet!');
|
|
385
|
+
}
|
|
386
|
+
static async getsChannelCountFromInfoHandle() {
|
|
387
|
+
const infoHandle = this.createRandomInfoHandle();
|
|
388
|
+
const channelCount = this.instance.getChannelCount({ infoHandle });
|
|
389
|
+
assert.isEqual(channelCount, this.channelCount);
|
|
390
|
+
}
|
|
391
|
+
static async openStreamCallsBinding() {
|
|
392
|
+
const { inletHandle } = this.createRandomInlet();
|
|
393
|
+
const timeoutMs = randomInt(1000);
|
|
394
|
+
const errorCodePtr = unwrapPointer(createPointer({
|
|
395
|
+
paramsType: [DataType.U8Array],
|
|
396
|
+
paramsValue: [new Int32Array(1)],
|
|
397
|
+
}))[0];
|
|
398
|
+
await this.instance.openStream({
|
|
399
|
+
inletHandle,
|
|
400
|
+
timeoutMs,
|
|
401
|
+
errorCodePtr,
|
|
402
|
+
});
|
|
403
|
+
assert.isEqualDeep(this.ffiRsLoadOptions, {
|
|
404
|
+
library: 'lsl',
|
|
405
|
+
funcName: 'lsl_open_stream',
|
|
406
|
+
retType: DataType.Void,
|
|
407
|
+
paramsType: [
|
|
408
|
+
DataType.External,
|
|
409
|
+
DataType.Double,
|
|
410
|
+
DataType.External,
|
|
411
|
+
],
|
|
412
|
+
paramsValue: [inletHandle, timeoutMs / 1000, errorCodePtr],
|
|
413
|
+
runInNewThread: true,
|
|
414
|
+
}, 'Did not call openStream with expected options!');
|
|
415
|
+
}
|
|
416
|
+
static async closeStreamCallsBinding() {
|
|
417
|
+
const { inletHandle } = this.createRandomInlet();
|
|
418
|
+
this.instance.closeStream({
|
|
419
|
+
inletHandle,
|
|
420
|
+
});
|
|
421
|
+
assert.isEqualDeep(this.ffiRsLoadOptions, {
|
|
422
|
+
library: 'lsl',
|
|
423
|
+
funcName: 'lsl_close_stream',
|
|
424
|
+
retType: DataType.Void,
|
|
425
|
+
paramsType: [DataType.External],
|
|
426
|
+
paramsValue: [inletHandle],
|
|
427
|
+
}, 'Did not call closeStream with expected options!');
|
|
428
|
+
}
|
|
429
|
+
static async pullSampleCallsBinding() {
|
|
430
|
+
const { inletHandle } = this.createRandomInlet();
|
|
431
|
+
const sampleBuffer = Buffer.alloc(4 * this.channelCount);
|
|
432
|
+
const sampleBufferElements = this.channelCount;
|
|
433
|
+
const timeoutMs = 0.0;
|
|
434
|
+
const sampleBufferPtr = unwrapPointer(createPointer({
|
|
435
|
+
paramsType: [DataType.U8Array],
|
|
436
|
+
paramsValue: [sampleBuffer],
|
|
437
|
+
}))[0];
|
|
438
|
+
const errorCodePtr = unwrapPointer(createPointer({
|
|
439
|
+
paramsType: [DataType.U8Array],
|
|
440
|
+
paramsValue: [new Int32Array(1)],
|
|
441
|
+
}))[0];
|
|
442
|
+
this.instance.pullSample({
|
|
443
|
+
inletHandle,
|
|
444
|
+
sampleBufferPtr,
|
|
445
|
+
sampleBufferElements,
|
|
446
|
+
timeoutMs,
|
|
447
|
+
errorCodePtr,
|
|
448
|
+
});
|
|
449
|
+
assert.isEqualDeep(this.ffiRsLoadOptions, {
|
|
450
|
+
library: 'lsl',
|
|
451
|
+
funcName: 'lsl_pull_sample_f',
|
|
452
|
+
retType: DataType.Double,
|
|
453
|
+
paramsType: [
|
|
454
|
+
DataType.External,
|
|
455
|
+
DataType.External,
|
|
456
|
+
DataType.I32,
|
|
457
|
+
DataType.Double,
|
|
458
|
+
DataType.External,
|
|
459
|
+
],
|
|
460
|
+
paramsValue: [
|
|
461
|
+
inletHandle,
|
|
462
|
+
sampleBufferPtr,
|
|
463
|
+
sampleBufferElements,
|
|
464
|
+
timeoutMs,
|
|
465
|
+
errorCodePtr,
|
|
466
|
+
],
|
|
467
|
+
}, 'Did not call pullSample with expected options!');
|
|
468
|
+
}
|
|
469
|
+
static async pullChunkCallsBinding() {
|
|
470
|
+
const { inletHandle } = this.createRandomInlet();
|
|
471
|
+
const sampleBuffer = Buffer.alloc(4 * this.chunkSize * this.channelCount);
|
|
472
|
+
const timestampBuffer = Buffer.alloc(8 * this.chunkSize);
|
|
473
|
+
const sampleBufferElements = this.chunkSize * this.channelCount;
|
|
474
|
+
const timestampBufferElements = this.chunkSize;
|
|
475
|
+
const timeoutMs = 0.0;
|
|
476
|
+
const sampleBufferPtr = unwrapPointer(createPointer({
|
|
477
|
+
paramsType: [DataType.U8Array],
|
|
478
|
+
paramsValue: [sampleBuffer],
|
|
479
|
+
}))[0];
|
|
480
|
+
const timestampBufferPtr = unwrapPointer(createPointer({
|
|
481
|
+
paramsType: [DataType.U8Array],
|
|
482
|
+
paramsValue: [timestampBuffer],
|
|
483
|
+
}))[0];
|
|
484
|
+
const errorCodePtr = unwrapPointer(createPointer({
|
|
485
|
+
paramsType: [DataType.U8Array],
|
|
486
|
+
paramsValue: [new Int32Array(1)],
|
|
487
|
+
}))[0];
|
|
488
|
+
this.instance.pullChunk({
|
|
489
|
+
inletHandle,
|
|
490
|
+
sampleBufferPtr,
|
|
491
|
+
timestampBufferPtr,
|
|
492
|
+
sampleBufferElements,
|
|
493
|
+
timestampBufferElements,
|
|
494
|
+
timeoutMs,
|
|
495
|
+
errorCodePtr,
|
|
496
|
+
});
|
|
497
|
+
assert.isEqualDeep(this.ffiRsLoadOptions, {
|
|
498
|
+
library: 'lsl',
|
|
499
|
+
funcName: 'lsl_pull_chunk_f',
|
|
500
|
+
retType: DataType.Double,
|
|
501
|
+
paramsType: [
|
|
502
|
+
DataType.External,
|
|
503
|
+
DataType.External,
|
|
504
|
+
DataType.External,
|
|
505
|
+
DataType.I32,
|
|
506
|
+
DataType.I32,
|
|
507
|
+
DataType.Double,
|
|
508
|
+
DataType.External,
|
|
509
|
+
],
|
|
510
|
+
paramsValue: [
|
|
511
|
+
inletHandle,
|
|
512
|
+
sampleBufferPtr,
|
|
513
|
+
timestampBufferPtr,
|
|
514
|
+
sampleBufferElements,
|
|
515
|
+
timestampBufferElements,
|
|
516
|
+
timeoutMs,
|
|
517
|
+
errorCodePtr,
|
|
518
|
+
],
|
|
519
|
+
}, 'Did not call pullChunk with expected options!');
|
|
520
|
+
}
|
|
521
|
+
static async flushInletCallsBinding() {
|
|
522
|
+
const { inletHandle } = this.createRandomInlet();
|
|
523
|
+
this.instance.flushInlet({ inletHandle });
|
|
524
|
+
assert.isEqualDeep(this.flushInletParams, [inletHandle], 'Should have called flushInlet with expected params!');
|
|
525
|
+
}
|
|
526
|
+
static async destroyInletCallsBinding() {
|
|
527
|
+
const { inletHandle } = this.createRandomInlet();
|
|
528
|
+
this.instance.destroyInlet({ inletHandle });
|
|
529
|
+
assert.isEqualDeep(this.destroyInletParams, [inletHandle], 'Should have called destroyInlet with expected params!');
|
|
530
|
+
}
|
|
531
|
+
static createRandomInfoHandle() {
|
|
532
|
+
return this.instance.createStreamInfo(this.generateRandomCreateStreamInfoOptions());
|
|
533
|
+
}
|
|
534
|
+
static async resolveByProp(options) {
|
|
535
|
+
return this.instance.resolveByProp({
|
|
536
|
+
prop: this.prop,
|
|
537
|
+
value: this.value,
|
|
538
|
+
minResults: this.minResults,
|
|
539
|
+
timeoutMs: this.timeoutMs,
|
|
540
|
+
...options,
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
static createRandomOutlet() {
|
|
544
|
+
const options = this.createRandomOutletOptions();
|
|
545
|
+
const outletHandle = this.instance.createOutlet(options);
|
|
546
|
+
return { options, outletHandle };
|
|
547
|
+
}
|
|
548
|
+
static createRandomOutletOptions() {
|
|
549
|
+
const infoHandle = this.createRandomInfoHandle();
|
|
550
|
+
return {
|
|
551
|
+
infoHandle,
|
|
552
|
+
chunkSize: randomInt(10),
|
|
553
|
+
maxBufferedMs: randomInt(10),
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
static createRandomInlet() {
|
|
557
|
+
const options = this.createRandomInletOptions();
|
|
558
|
+
const inletHandle = this.instance.createInlet(options);
|
|
559
|
+
return { options, inletHandle };
|
|
560
|
+
}
|
|
561
|
+
static createRandomInletOptions() {
|
|
562
|
+
const infoHandle = this.createRandomInfoHandle();
|
|
563
|
+
return {
|
|
564
|
+
infoHandle,
|
|
565
|
+
maxBufferedMs: randomInt(10),
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
static generateRandomChannelValues() {
|
|
569
|
+
return {
|
|
570
|
+
label: this.generateId(),
|
|
571
|
+
type: this.generateId(),
|
|
572
|
+
units: this.generateId(),
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
static generateRandomCreateStreamInfoOptions() {
|
|
576
|
+
return {
|
|
577
|
+
name: this.generateId(),
|
|
578
|
+
type: this.generateId(),
|
|
579
|
+
channelCount: randomInt(1, 10),
|
|
580
|
+
sampleRateHz: randomInt(100),
|
|
581
|
+
channelFormat: randomInt(7),
|
|
582
|
+
sourceId: this.generateId(),
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
// resolveByProp
|
|
586
|
+
static maxResults = 1024;
|
|
587
|
+
static bytesPerPointer = 8;
|
|
588
|
+
static minResults = 1;
|
|
589
|
+
static timeoutMs = 1000;
|
|
590
|
+
static prop = this.generateId();
|
|
591
|
+
static value = this.generateId();
|
|
592
|
+
static fakeNumResolveResults = 2;
|
|
593
|
+
static resultsBuffer = Buffer.alloc(this.maxResults * this.bytesPerPointer);
|
|
594
|
+
static resultsBufferRef = createPointer({
|
|
595
|
+
paramsType: [DataType.U8Array],
|
|
596
|
+
paramsValue: [this.resultsBuffer],
|
|
597
|
+
});
|
|
598
|
+
static resultsBufferPtr = unwrapPointer(this.resultsBufferRef)[0];
|
|
599
|
+
static generateFailedMessage() {
|
|
600
|
+
return `Loading the liblsl dylib failed! I tried to load it from ${process.env.LIBLSL_PATH}.`;
|
|
601
|
+
}
|
|
602
|
+
static FakeBindings() {
|
|
603
|
+
return {
|
|
604
|
+
lsl_create_streaminfo: (params) => {
|
|
605
|
+
this.createStreamInfoParams = params;
|
|
606
|
+
return this.fakeInfoHandle;
|
|
607
|
+
},
|
|
608
|
+
lsl_destroy_streaminfo: (params) => {
|
|
609
|
+
this.destroyStreamInfoParams = params;
|
|
610
|
+
},
|
|
611
|
+
lsl_create_outlet: (params) => {
|
|
612
|
+
this.createOutletParams = params;
|
|
613
|
+
return this.fakeOutletHandle;
|
|
614
|
+
},
|
|
615
|
+
lsl_push_sample_ft: (params) => {
|
|
616
|
+
this.pushSampleFloatTimestampParams = params;
|
|
617
|
+
return 0;
|
|
618
|
+
},
|
|
619
|
+
lsl_push_sample_strt: (params) => {
|
|
620
|
+
this.pushSampleStringTimestampParams = params;
|
|
621
|
+
return 0;
|
|
622
|
+
},
|
|
623
|
+
lsl_destroy_outlet: (params) => {
|
|
624
|
+
this.destroyOutletParams = params;
|
|
625
|
+
},
|
|
626
|
+
lsl_create_inlet: (params) => {
|
|
627
|
+
this.createInletParams = params;
|
|
628
|
+
return this.fakeInletHandle;
|
|
629
|
+
},
|
|
630
|
+
lsl_inlet_flush: (params) => {
|
|
631
|
+
this.flushInletParams = params;
|
|
632
|
+
return 0;
|
|
633
|
+
},
|
|
634
|
+
lsl_destroy_inlet: (params) => {
|
|
635
|
+
this.destroyInletParams = params;
|
|
636
|
+
},
|
|
637
|
+
lsl_local_clock: (params) => {
|
|
638
|
+
this.localClockParams = params;
|
|
639
|
+
return new Date().getTime();
|
|
640
|
+
},
|
|
641
|
+
lsl_get_channel_count: (info) => {
|
|
642
|
+
this.getChannelCountParams = [info];
|
|
643
|
+
return this.channelCount;
|
|
644
|
+
},
|
|
645
|
+
lsl_get_desc: (info) => {
|
|
646
|
+
this.getDescriptionParams = [info];
|
|
647
|
+
return this.fakeDescHandle;
|
|
648
|
+
},
|
|
649
|
+
lsl_append_child: (params) => {
|
|
650
|
+
this.appendChildParams.push(params);
|
|
651
|
+
if (this.appendChildHitCount === 0) {
|
|
652
|
+
this.appendChildHitCount++;
|
|
653
|
+
return this.fakeChildHandle;
|
|
654
|
+
}
|
|
655
|
+
return this.fakeChildNamedChannel;
|
|
656
|
+
},
|
|
657
|
+
lsl_append_child_value: (params) => {
|
|
658
|
+
this.appendChildValueParams.push(params);
|
|
659
|
+
},
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
__decorate([
|
|
664
|
+
test()
|
|
665
|
+
], LiblslAdapterTest, "createsInstance", null);
|
|
666
|
+
__decorate([
|
|
667
|
+
test()
|
|
668
|
+
], LiblslAdapterTest, "throwsWhenBindingsFailToLoad", null);
|
|
669
|
+
__decorate([
|
|
670
|
+
test()
|
|
671
|
+
], LiblslAdapterTest, "callsOpenOnFfiRs", null);
|
|
672
|
+
__decorate([
|
|
673
|
+
test()
|
|
674
|
+
], LiblslAdapterTest, "worksAsASingleton", null);
|
|
675
|
+
__decorate([
|
|
676
|
+
test()
|
|
677
|
+
], LiblslAdapterTest, "singletonIsTheSame", null);
|
|
678
|
+
__decorate([
|
|
679
|
+
test()
|
|
680
|
+
], LiblslAdapterTest, "canSetInstance", null);
|
|
681
|
+
__decorate([
|
|
682
|
+
test()
|
|
683
|
+
], LiblslAdapterTest, "createsExpectedBindingsWithFfiRs", null);
|
|
684
|
+
__decorate([
|
|
685
|
+
test()
|
|
686
|
+
], LiblslAdapterTest, "createsStreamInfoWithRequiredParams", null);
|
|
687
|
+
__decorate([
|
|
688
|
+
test()
|
|
689
|
+
], LiblslAdapterTest, "destroyStreamInfoPasses", null);
|
|
690
|
+
__decorate([
|
|
691
|
+
test()
|
|
692
|
+
], LiblslAdapterTest, "resolvesStreamInfoByProp", null);
|
|
693
|
+
__decorate([
|
|
694
|
+
test()
|
|
695
|
+
], LiblslAdapterTest, "resolveByPropAcceptsOptionalArgs", null);
|
|
696
|
+
__decorate([
|
|
697
|
+
test()
|
|
698
|
+
], LiblslAdapterTest, "returnsHandlesForResolvedStreamInfos", null);
|
|
699
|
+
__decorate([
|
|
700
|
+
test()
|
|
701
|
+
], LiblslAdapterTest, "createsOutletWithRequiredParams", null);
|
|
702
|
+
__decorate([
|
|
703
|
+
test()
|
|
704
|
+
], LiblslAdapterTest, "pushesFloatSample", null);
|
|
705
|
+
__decorate([
|
|
706
|
+
test()
|
|
707
|
+
], LiblslAdapterTest, "pushesStringSample", null);
|
|
708
|
+
__decorate([
|
|
709
|
+
test()
|
|
710
|
+
], LiblslAdapterTest, "addingSingleChannelGetsDescription", null);
|
|
711
|
+
__decorate([
|
|
712
|
+
test()
|
|
713
|
+
], LiblslAdapterTest, "addingMultpleChannelsAddsChildrenToChannelsChild", null);
|
|
714
|
+
__decorate([
|
|
715
|
+
test()
|
|
716
|
+
], LiblslAdapterTest, "canDestroyOutlet", null);
|
|
717
|
+
__decorate([
|
|
718
|
+
test()
|
|
719
|
+
], LiblslAdapterTest, "callingLocalClockTwiceReturnsDifferentTimestamps", null);
|
|
720
|
+
__decorate([
|
|
721
|
+
test()
|
|
722
|
+
], LiblslAdapterTest, "localClockBindingReceivesEmptyArray", null);
|
|
723
|
+
__decorate([
|
|
724
|
+
test()
|
|
725
|
+
], LiblslAdapterTest, "defaultsToMacOsPath", null);
|
|
726
|
+
__decorate([
|
|
727
|
+
test()
|
|
728
|
+
], LiblslAdapterTest, "createInletWithRequiredParams", null);
|
|
729
|
+
__decorate([
|
|
730
|
+
test()
|
|
731
|
+
], LiblslAdapterTest, "getsChannelCountFromInfoHandle", null);
|
|
732
|
+
__decorate([
|
|
733
|
+
test()
|
|
734
|
+
], LiblslAdapterTest, "openStreamCallsBinding", null);
|
|
735
|
+
__decorate([
|
|
736
|
+
test()
|
|
737
|
+
], LiblslAdapterTest, "closeStreamCallsBinding", null);
|
|
738
|
+
__decorate([
|
|
739
|
+
test()
|
|
740
|
+
], LiblslAdapterTest, "pullSampleCallsBinding", null);
|
|
741
|
+
__decorate([
|
|
742
|
+
test()
|
|
743
|
+
], LiblslAdapterTest, "pullChunkCallsBinding", null);
|
|
744
|
+
__decorate([
|
|
745
|
+
test()
|
|
746
|
+
], LiblslAdapterTest, "flushInletCallsBinding", null);
|
|
747
|
+
__decorate([
|
|
748
|
+
test()
|
|
749
|
+
], LiblslAdapterTest, "destroyInletCallsBinding", null);
|
|
750
|
+
//# sourceMappingURL=LiblslAdapter.test.js.map
|