@neurodevs/node-lsl 3.1.0 → 3.3.0
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/build/Liblsl.d.ts +6 -0
- package/build/Liblsl.js +5 -0
- package/build/Liblsl.js.map +1 -1
- package/build/LslOutlet.d.ts +4 -2
- package/build/LslOutlet.js +10 -7
- package/build/LslOutlet.js.map +1 -1
- package/build/TimeMarkerOutlet.d.ts +4 -0
- package/build/TimeMarkerOutlet.js +26 -0
- package/build/TimeMarkerOutlet.js.map +1 -0
- package/build/__tests__/behavioral/LslOutlet.test.d.ts +2 -2
- package/build/__tests__/behavioral/LslOutlet.test.js +17 -51
- package/build/__tests__/behavioral/LslOutlet.test.js.map +1 -1
- package/build/__tests__/behavioral/TimeMarkerOutlet.test.d.ts +8 -0
- package/build/__tests__/behavioral/TimeMarkerOutlet.test.js +85 -0
- package/build/__tests__/behavioral/TimeMarkerOutlet.test.js.map +1 -0
- package/build/__tests__/implementation/Liblsl.test.d.ts +3 -0
- package/build/__tests__/implementation/Liblsl.test.js +28 -8
- package/build/__tests__/implementation/Liblsl.test.js.map +1 -1
- package/build/__tests__/support/SpyLiblsl.d.ts +16 -0
- package/build/__tests__/support/SpyLiblsl.js +31 -0
- package/build/__tests__/support/SpyLiblsl.js.map +1 -0
- package/build/__tests__/support/consts.d.ts +2 -0
- package/build/__tests__/support/consts.js +14 -0
- package/build/__tests__/support/consts.js.map +1 -0
- package/build/__tests__/support/generateRandomOutletOptions.d.ts +2 -0
- package/build/__tests__/support/generateRandomOutletOptions.js +21 -0
- package/build/__tests__/support/generateRandomOutletOptions.js.map +1 -0
- package/build/errors/SpruceError.js.map +1 -1
- package/build/index.d.ts +1 -0
- package/build/index.js +3 -1
- package/build/index.js.map +1 -1
- package/package.json +1 -1
- package/src/Liblsl.ts +13 -0
- package/src/LslOutlet.ts +13 -9
- package/src/TimeMarkerOutlet.ts +19 -0
- package/src/__tests__/behavioral/LslOutlet.test.ts +22 -72
- package/src/__tests__/behavioral/TimeMarkerOutlet.test.ts +54 -0
- package/src/__tests__/implementation/Liblsl.test.ts +32 -8
- package/src/__tests__/support/SpyLiblsl.ts +46 -0
- package/src/__tests__/support/consts.ts +12 -0
- package/src/__tests__/support/generateRandomOutletOptions.ts +19 -0
- package/src/index.ts +2 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { CreateOutletOptions, CreateStreamInfoOptions, Liblsl, BoundOutlet, BoundStreamInfo, AppendChannelsToStreamInfoOptions, PushSampleOptions } from '../../Liblsl';
|
|
2
|
+
export declare class SpyLiblsl implements Liblsl {
|
|
3
|
+
lastCreateStreamInfoOptions?: CreateStreamInfoOptions;
|
|
4
|
+
lastAppendChannelsToStreamInfoOptions?: AppendChannelsToStreamInfoOptions;
|
|
5
|
+
lastCreateOutletOptions?: CreateOutletOptions;
|
|
6
|
+
lastPushSampleOptions?: PushSampleOptions;
|
|
7
|
+
outlet: BoundOutlet;
|
|
8
|
+
streamInfo: BoundStreamInfo;
|
|
9
|
+
createStreamInfoHitCount: number;
|
|
10
|
+
destroyOutletHitCount: number;
|
|
11
|
+
createStreamInfo(options: CreateStreamInfoOptions): BoundStreamInfo;
|
|
12
|
+
appendChannelsToStreamInfo(options: AppendChannelsToStreamInfoOptions): void;
|
|
13
|
+
createOutlet(options: CreateOutletOptions): BoundOutlet;
|
|
14
|
+
destroyOutlet(): void;
|
|
15
|
+
pushSample(options: PushSampleOptions): void;
|
|
16
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SpyLiblsl = void 0;
|
|
4
|
+
class SpyLiblsl {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.outlet = {};
|
|
7
|
+
this.streamInfo = {};
|
|
8
|
+
this.createStreamInfoHitCount = 0;
|
|
9
|
+
this.destroyOutletHitCount = 0;
|
|
10
|
+
}
|
|
11
|
+
createStreamInfo(options) {
|
|
12
|
+
this.createStreamInfoHitCount++;
|
|
13
|
+
this.lastCreateStreamInfoOptions = options;
|
|
14
|
+
return this.streamInfo;
|
|
15
|
+
}
|
|
16
|
+
appendChannelsToStreamInfo(options) {
|
|
17
|
+
this.lastAppendChannelsToStreamInfoOptions = options;
|
|
18
|
+
}
|
|
19
|
+
createOutlet(options) {
|
|
20
|
+
this.lastCreateOutletOptions = options;
|
|
21
|
+
return this.outlet;
|
|
22
|
+
}
|
|
23
|
+
destroyOutlet() {
|
|
24
|
+
this.destroyOutletHitCount++;
|
|
25
|
+
}
|
|
26
|
+
pushSample(options) {
|
|
27
|
+
this.lastPushSampleOptions = options;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
exports.SpyLiblsl = SpyLiblsl;
|
|
31
|
+
//# sourceMappingURL=SpyLiblsl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SpyLiblsl.js","sourceRoot":"","sources":["../../../src/__tests__/support/SpyLiblsl.ts"],"names":[],"mappings":";;;AAUA,MAAa,SAAS;IAAtB;QAMQ,WAAM,GAAgB,EAAiB,CAAA;QACvC,eAAU,GAAoB,EAAqB,CAAA;QACnD,6BAAwB,GAAG,CAAC,CAAA;QAC5B,0BAAqB,GAAG,CAAC,CAAA;IA0BjC,CAAC;IAxBO,gBAAgB,CAAC,OAAgC;QACvD,IAAI,CAAC,wBAAwB,EAAE,CAAA;QAC/B,IAAI,CAAC,2BAA2B,GAAG,OAAO,CAAA;QAC1C,OAAO,IAAI,CAAC,UAAU,CAAA;IACvB,CAAC;IAEM,0BAA0B,CAChC,OAA0C;QAE1C,IAAI,CAAC,qCAAqC,GAAG,OAAO,CAAA;IACrD,CAAC;IAEM,YAAY,CAAC,OAA4B;QAC/C,IAAI,CAAC,uBAAuB,GAAG,OAAO,CAAA;QACtC,OAAO,IAAI,CAAC,MAAM,CAAA;IACnB,CAAC;IAEM,aAAa;QACnB,IAAI,CAAC,qBAAqB,EAAE,CAAA;IAC7B,CAAC;IAEM,UAAU,CAAC,OAA0B;QAC3C,IAAI,CAAC,qBAAqB,GAAG,OAAO,CAAA;IACrC,CAAC;CACD;AAnCD,8BAmCC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TEST_CHANNEL_FORMATS = void 0;
|
|
4
|
+
exports.TEST_CHANNEL_FORMATS = [
|
|
5
|
+
'undefined',
|
|
6
|
+
'float32',
|
|
7
|
+
'double64',
|
|
8
|
+
'string',
|
|
9
|
+
'int32',
|
|
10
|
+
'int16',
|
|
11
|
+
'int8',
|
|
12
|
+
'int64',
|
|
13
|
+
];
|
|
14
|
+
//# sourceMappingURL=consts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consts.js","sourceRoot":"","sources":["../../../src/__tests__/support/consts.ts"],"names":[],"mappings":";;;AAAa,QAAA,oBAAoB,GAAG;IACnC,WAAW;IACX,SAAS;IACT,UAAU;IACV,QAAQ;IACR,OAAO;IACP,OAAO;IACP,MAAM;IACN,OAAO;CACP,CAAA"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const crypto_1 = require("crypto");
|
|
4
|
+
const test_utils_1 = require("@sprucelabs/test-utils");
|
|
5
|
+
const consts_1 = require("./consts");
|
|
6
|
+
function generateRandomOutletOptions(randomChannelIdx) {
|
|
7
|
+
return {
|
|
8
|
+
name: (0, test_utils_1.generateId)(),
|
|
9
|
+
type: (0, test_utils_1.generateId)(),
|
|
10
|
+
channelNames: new Array((0, crypto_1.randomInt)(1, 10)).fill((0, test_utils_1.generateId)()),
|
|
11
|
+
sampleRate: Math.random() * 10,
|
|
12
|
+
channelFormat: consts_1.TEST_CHANNEL_FORMATS[randomChannelIdx],
|
|
13
|
+
sourceId: (0, test_utils_1.generateId)(),
|
|
14
|
+
manufacturer: (0, test_utils_1.generateId)(),
|
|
15
|
+
unit: (0, test_utils_1.generateId)(),
|
|
16
|
+
chunkSize: (0, crypto_1.randomInt)(0, 10),
|
|
17
|
+
maxBuffered: (0, crypto_1.randomInt)(0, 10),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
exports.default = generateRandomOutletOptions;
|
|
21
|
+
//# sourceMappingURL=generateRandomOutletOptions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generateRandomOutletOptions.js","sourceRoot":"","sources":["../../../src/__tests__/support/generateRandomOutletOptions.ts"],"names":[],"mappings":";;AAAA,mCAAkC;AAClC,uDAAmD;AAEnD,qCAA+C;AAE/C,SAAwB,2BAA2B,CAAC,gBAAwB;IAC3E,OAAO;QACN,IAAI,EAAE,IAAA,uBAAU,GAAE;QAClB,IAAI,EAAE,IAAA,uBAAU,GAAE;QAClB,YAAY,EAAE,IAAI,KAAK,CAAC,IAAA,kBAAS,EAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAA,uBAAU,GAAE,CAAC;QAC5D,UAAU,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE;QAC9B,aAAa,EAAE,6BAAoB,CAAC,gBAAgB,CAAC;QACrD,QAAQ,EAAE,IAAA,uBAAU,GAAE;QACtB,YAAY,EAAE,IAAA,uBAAU,GAAE;QAC1B,IAAI,EAAE,IAAA,uBAAU,GAAE;QAClB,SAAS,EAAE,IAAA,kBAAS,EAAC,CAAC,EAAE,EAAE,CAAC;QAC3B,WAAW,EAAE,IAAA,kBAAS,EAAC,CAAC,EAAE,EAAE,CAAC;KACT,CAAA;AACtB,CAAC;AAbD,8CAaC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SpruceError.js","sourceRoot":"","sources":["../../src/errors/SpruceError.ts"],"names":[],"mappings":";;;;;AAAA,8DAA+C;AAG/C,MAAqB,WAAY,SAAQ,eAA6B;IACrE,kDAAkD;IAC3C,eAAe;QACrB,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;QACxB,IAAI,OAAO,CAAA;QACX,QAAQ,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,IAAI,EAAE;
|
|
1
|
+
{"version":3,"file":"SpruceError.js","sourceRoot":"","sources":["../../src/errors/SpruceError.ts"],"names":[],"mappings":";;;;;AAAA,8DAA+C;AAG/C,MAAqB,WAAY,SAAQ,eAA6B;IACrE,kDAAkD;IAC3C,eAAe;QACrB,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;QACxB,IAAI,OAAO,CAAA;QACX,QAAQ,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,IAAI,EAAE,CAAC;YACvB,KAAK,uBAAuB;gBAC3B,OAAO,GAAG,wEAAwE,OAAO,CAAC,UAAU,GAAG,CAAA;gBACvG,MAAK;YACN;gBACC,OAAO,GAAG,KAAK,CAAC,eAAe,EAAE,CAAA;QACnC,CAAC;QAED,MAAM,WAAW,GAAG,OAAO,CAAC,eAAe;YAC1C,CAAC,CAAC,OAAO,CAAC,eAAe;YACzB,CAAC,CAAC,OAAO,CAAA;QAEV,OAAO,WAAW,CAAA;IACnB,CAAC;CACD;AAnBD,8BAmBC"}
|
package/build/index.d.ts
CHANGED
package/build/index.js
CHANGED
|
@@ -17,11 +17,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
17
17
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
18
18
|
};
|
|
19
19
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
-
exports.LslOutletImpl = exports.LiblslImpl = void 0;
|
|
20
|
+
exports.TimeMarkerOutlet = exports.LslOutletImpl = exports.LiblslImpl = void 0;
|
|
21
21
|
var Liblsl_1 = require("./Liblsl");
|
|
22
22
|
Object.defineProperty(exports, "LiblslImpl", { enumerable: true, get: function () { return __importDefault(Liblsl_1).default; } });
|
|
23
23
|
__exportStar(require("./Liblsl"), exports);
|
|
24
24
|
var LslOutlet_1 = require("./LslOutlet");
|
|
25
25
|
Object.defineProperty(exports, "LslOutletImpl", { enumerable: true, get: function () { return __importDefault(LslOutlet_1).default; } });
|
|
26
26
|
__exportStar(require("./LslOutlet"), exports);
|
|
27
|
+
var TimeMarkerOutlet_1 = require("./TimeMarkerOutlet");
|
|
28
|
+
Object.defineProperty(exports, "TimeMarkerOutlet", { enumerable: true, get: function () { return __importDefault(TimeMarkerOutlet_1).default; } });
|
|
27
29
|
//# sourceMappingURL=index.js.map
|
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA,mCAAgD;AAAvC,qHAAA,OAAO,OAAc;AAC9B,2CAAwB;AAExB,yCAAsD;AAA7C,2HAAA,OAAO,OAAiB;AACjC,8CAA2B"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA,mCAAgD;AAAvC,qHAAA,OAAO,OAAc;AAC9B,2CAAwB;AAExB,yCAAsD;AAA7C,2HAAA,OAAO,OAAiB;AACjC,8CAA2B;AAE3B,uDAAgE;AAAvD,qIAAA,OAAO,OAAoB"}
|
package/package.json
CHANGED
package/src/Liblsl.ts
CHANGED
|
@@ -48,6 +48,7 @@ export default class LiblslImpl implements Liblsl {
|
|
|
48
48
|
['string', 'string', 'int', 'double', 'int', 'string'],
|
|
49
49
|
],
|
|
50
50
|
lsl_create_outlet: [outletType, [streamInfo, 'int', 'int']],
|
|
51
|
+
lsl_destroy_outlet: ['void', [outletType]],
|
|
51
52
|
lsl_local_clock: ['double', []],
|
|
52
53
|
lsl_push_sample_ft: ['void', [outletType, FloatArray, 'double']],
|
|
53
54
|
lsl_get_desc: [xmlPtr, [streamInfo]],
|
|
@@ -104,6 +105,11 @@ export default class LiblslImpl implements Liblsl {
|
|
|
104
105
|
return this.bindings.lsl_create_outlet(info, chunkSize, maxBuffered)
|
|
105
106
|
}
|
|
106
107
|
|
|
108
|
+
public destroyOutlet(options: DestroyOutletOptions): void {
|
|
109
|
+
const { outlet } = assertOptions(options, ['outlet'])
|
|
110
|
+
this.bindings.lsl_destroy_outlet(outlet)
|
|
111
|
+
}
|
|
112
|
+
|
|
107
113
|
public pushSample(options: PushSampleOptions): void {
|
|
108
114
|
const { outlet, sample } = assertOptions(options, ['outlet', 'sample'])
|
|
109
115
|
|
|
@@ -116,6 +122,7 @@ export interface Liblsl {
|
|
|
116
122
|
createStreamInfo(options: CreateStreamInfoOptions): BoundStreamInfo
|
|
117
123
|
appendChannelsToStreamInfo(options: AppendChannelsToStreamInfoOptions): void
|
|
118
124
|
createOutlet(options: CreateOutletOptions): BoundOutlet
|
|
125
|
+
destroyOutlet(options: DestroyOutletOptions): void
|
|
119
126
|
pushSample(options: PushSampleOptions): void
|
|
120
127
|
}
|
|
121
128
|
|
|
@@ -141,6 +148,10 @@ export interface CreateOutletOptions {
|
|
|
141
148
|
maxBuffered: number
|
|
142
149
|
}
|
|
143
150
|
|
|
151
|
+
export interface DestroyOutletOptions {
|
|
152
|
+
outlet: BoundOutlet
|
|
153
|
+
}
|
|
154
|
+
|
|
144
155
|
export interface PushSampleOptions {
|
|
145
156
|
outlet: BoundOutlet
|
|
146
157
|
sample: LslSample
|
|
@@ -169,6 +180,8 @@ export interface LiblslBindings {
|
|
|
169
180
|
maxBuffered: number
|
|
170
181
|
): BoundOutlet
|
|
171
182
|
|
|
183
|
+
lsl_destroy_outlet(outlet: BoundOutlet): void
|
|
184
|
+
|
|
172
185
|
lsl_push_sample_ft(
|
|
173
186
|
outlet: BoundOutlet,
|
|
174
187
|
sample: LslSample,
|
package/src/LslOutlet.ts
CHANGED
|
@@ -63,6 +63,10 @@ export default class LslOutletImpl implements LslOutlet {
|
|
|
63
63
|
return new (this.Class ?? this)(options)
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
public destroy() {
|
|
67
|
+
this.lsl.destroyOutlet({ outlet: this.outlet })
|
|
68
|
+
}
|
|
69
|
+
|
|
66
70
|
public pushSample(sample: LslSample) {
|
|
67
71
|
this.lsl.pushSample({ outlet: this.outlet, sample })
|
|
68
72
|
}
|
|
@@ -93,7 +97,6 @@ export default class LslOutletImpl implements LslOutlet {
|
|
|
93
97
|
|
|
94
98
|
private assertValidChannelFormat(channelFormat: ChannelFormat) {
|
|
95
99
|
const validFormats = CHANNEL_FORMATS
|
|
96
|
-
|
|
97
100
|
if (validFormats.indexOf(channelFormat) === -1) {
|
|
98
101
|
throw new SchemaError({
|
|
99
102
|
code: 'INVALID_PARAMETERS',
|
|
@@ -104,11 +107,11 @@ export default class LslOutletImpl implements LslOutlet {
|
|
|
104
107
|
}
|
|
105
108
|
|
|
106
109
|
private assertValidSampleRate(sampleRate: number) {
|
|
107
|
-
if (!this.
|
|
110
|
+
if (!this.isGreaterThanZero(sampleRate)) {
|
|
108
111
|
throw new SchemaError({
|
|
109
112
|
code: 'INVALID_PARAMETERS',
|
|
110
113
|
parameters: ['sampleRate'],
|
|
111
|
-
friendlyMessage: 'Sample rate must be a positive number.',
|
|
114
|
+
friendlyMessage: 'Sample rate must be a positive number or zero.',
|
|
112
115
|
})
|
|
113
116
|
}
|
|
114
117
|
}
|
|
@@ -123,18 +126,18 @@ export default class LslOutletImpl implements LslOutlet {
|
|
|
123
126
|
}
|
|
124
127
|
}
|
|
125
128
|
|
|
126
|
-
private
|
|
127
|
-
return value
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
private isPositiveIntegerOrZero(value: number) {
|
|
131
|
-
return Number.isInteger(value) && value >= 0
|
|
129
|
+
private isGreaterThanZero(value: number) {
|
|
130
|
+
return value >= 0
|
|
132
131
|
}
|
|
133
132
|
|
|
134
133
|
private isPositiveInteger(value: number) {
|
|
135
134
|
return Number.isInteger(value) && value > 0
|
|
136
135
|
}
|
|
137
136
|
|
|
137
|
+
private isPositiveIntegerOrZero(value: number) {
|
|
138
|
+
return Number.isInteger(value) && value >= 0
|
|
139
|
+
}
|
|
140
|
+
|
|
138
141
|
private get lsl() {
|
|
139
142
|
return LiblslImpl.getInstance()
|
|
140
143
|
}
|
|
@@ -154,6 +157,7 @@ const CHANNEL_FORMATS = [
|
|
|
154
157
|
export type ChannelFormat = (typeof CHANNEL_FORMATS)[number]
|
|
155
158
|
|
|
156
159
|
export interface LslOutlet {
|
|
160
|
+
destroy(): void
|
|
157
161
|
pushSample(sample: LslSample): void
|
|
158
162
|
}
|
|
159
163
|
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import LslOutletImpl, { LslOutletOptions } from './LslOutlet'
|
|
2
|
+
|
|
3
|
+
export default class TimeMarkerOutlet extends LslOutletImpl {
|
|
4
|
+
public static Outlet(options?: Partial<LslOutletOptions>) {
|
|
5
|
+
const defaultOptions = {
|
|
6
|
+
name: 'Time markers',
|
|
7
|
+
type: 'Markers',
|
|
8
|
+
channelNames: ['Markers'],
|
|
9
|
+
sampleRate: 0,
|
|
10
|
+
channelFormat: 'string',
|
|
11
|
+
sourceId: 'time-markers',
|
|
12
|
+
manufacturer: 'N/A',
|
|
13
|
+
unit: 'N/A',
|
|
14
|
+
chunkSize: 0,
|
|
15
|
+
maxBuffered: 0,
|
|
16
|
+
} as LslOutletOptions
|
|
17
|
+
return new (this.Class ?? this)({ ...defaultOptions, ...options })
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -5,51 +5,24 @@ import AbstractSpruceTest, {
|
|
|
5
5
|
errorAssert,
|
|
6
6
|
generateId,
|
|
7
7
|
} from '@sprucelabs/test-utils'
|
|
8
|
-
import LiblslImpl, {
|
|
9
|
-
CreateOutletOptions,
|
|
10
|
-
CreateStreamInfoOptions,
|
|
11
|
-
Liblsl,
|
|
12
|
-
BoundOutlet,
|
|
13
|
-
BoundStreamInfo,
|
|
14
|
-
LslSample,
|
|
15
|
-
AppendChannelsToStreamInfoOptions,
|
|
16
|
-
PushSampleOptions,
|
|
17
|
-
} from '../../Liblsl'
|
|
8
|
+
import LiblslImpl, { LslSample } from '../../Liblsl'
|
|
18
9
|
import LslOutlet, { ChannelFormat, LslOutletOptions } from '../../LslOutlet'
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
'float32',
|
|
23
|
-
'double64',
|
|
24
|
-
'string',
|
|
25
|
-
'int32',
|
|
26
|
-
'int16',
|
|
27
|
-
'int8',
|
|
28
|
-
'int64',
|
|
29
|
-
] as const
|
|
10
|
+
import { TEST_CHANNEL_FORMATS } from '../support/consts'
|
|
11
|
+
import generateRandomOutletOptions from '../support/generateRandomOutletOptions'
|
|
12
|
+
import { SpyLiblsl } from '../support/SpyLiblsl'
|
|
30
13
|
|
|
31
14
|
export default class LslOutletTest extends AbstractSpruceTest {
|
|
32
15
|
private static spyLiblsl: SpyLiblsl
|
|
33
16
|
private static randomOutletOptions: LslOutletOptions
|
|
34
|
-
private static
|
|
17
|
+
private static channelFormatIdx: number
|
|
35
18
|
|
|
36
19
|
protected static async beforeEach() {
|
|
37
20
|
await super.beforeEach()
|
|
38
21
|
delete LslOutlet.Class
|
|
39
|
-
this.
|
|
40
|
-
this.randomOutletOptions =
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
channelNames: new Array(randomInt(1, 10)).fill(generateId()),
|
|
44
|
-
sampleRate: Math.random() * 10,
|
|
45
|
-
channelFormat: CHANNEL_FORMATS[this.channelIdx],
|
|
46
|
-
sourceId: generateId(),
|
|
47
|
-
manufacturer: generateId(),
|
|
48
|
-
unit: generateId(),
|
|
49
|
-
chunkSize: randomInt(0, 10),
|
|
50
|
-
maxBuffered: randomInt(0, 10),
|
|
51
|
-
}
|
|
52
|
-
|
|
22
|
+
this.channelFormatIdx = randomInt(8)
|
|
23
|
+
this.randomOutletOptions = generateRandomOutletOptions(
|
|
24
|
+
this.channelFormatIdx
|
|
25
|
+
)
|
|
53
26
|
this.spyLiblsl = new SpyLiblsl()
|
|
54
27
|
LiblslImpl.setInstance(this.spyLiblsl)
|
|
55
28
|
}
|
|
@@ -81,7 +54,7 @@ export default class LslOutletTest extends AbstractSpruceTest {
|
|
|
81
54
|
|
|
82
55
|
@test()
|
|
83
56
|
protected static async throwsWithInvalidSampleRate() {
|
|
84
|
-
this.
|
|
57
|
+
this.Outlet({ sampleRate: 0 })
|
|
85
58
|
this.assertThrowsInvalidSampleRate(-1)
|
|
86
59
|
this.assertThrowsInvalidSampleRate(-1.5)
|
|
87
60
|
}
|
|
@@ -112,8 +85,7 @@ export default class LslOutletTest extends AbstractSpruceTest {
|
|
|
112
85
|
|
|
113
86
|
@test()
|
|
114
87
|
protected static async supportsAllKnownChannelFormats() {
|
|
115
|
-
const
|
|
116
|
-
for (const format of valid) {
|
|
88
|
+
for (const format of TEST_CHANNEL_FORMATS) {
|
|
117
89
|
this.Outlet({ channelFormat: format as ChannelFormat })
|
|
118
90
|
}
|
|
119
91
|
}
|
|
@@ -122,6 +94,7 @@ export default class LslOutletTest extends AbstractSpruceTest {
|
|
|
122
94
|
@test('pushing [1] sample sends to LSL', [1])
|
|
123
95
|
protected static async canPushSampleToLsl(sample: LslSample) {
|
|
124
96
|
this.pushSample(sample)
|
|
97
|
+
|
|
125
98
|
assert.isEqual(
|
|
126
99
|
this.spyLiblsl.lastPushSampleOptions?.outlet,
|
|
127
100
|
this.spyLiblsl.outlet
|
|
@@ -150,7 +123,7 @@ export default class LslOutletTest extends AbstractSpruceTest {
|
|
|
150
123
|
|
|
151
124
|
assert.isEqualDeep(this.spyLiblsl.lastCreateStreamInfoOptions, {
|
|
152
125
|
...options,
|
|
153
|
-
channelFormat: this.
|
|
126
|
+
channelFormat: this.channelFormatIdx,
|
|
154
127
|
})
|
|
155
128
|
}
|
|
156
129
|
|
|
@@ -158,6 +131,7 @@ export default class LslOutletTest extends AbstractSpruceTest {
|
|
|
158
131
|
protected static async canOverrideClassInstantiatedInFactory() {
|
|
159
132
|
LslOutlet.Class = CheckingOutlet
|
|
160
133
|
const instance = this.Outlet()
|
|
134
|
+
|
|
161
135
|
assert.isInstanceOf(instance, CheckingOutlet)
|
|
162
136
|
}
|
|
163
137
|
|
|
@@ -179,6 +153,14 @@ export default class LslOutletTest extends AbstractSpruceTest {
|
|
|
179
153
|
})
|
|
180
154
|
}
|
|
181
155
|
|
|
156
|
+
@test()
|
|
157
|
+
protected static async canDestroyOutlet() {
|
|
158
|
+
const outlet = this.Outlet()
|
|
159
|
+
outlet.destroy()
|
|
160
|
+
|
|
161
|
+
assert.isEqual(this.spyLiblsl.destroyOutletHitCount, 1)
|
|
162
|
+
}
|
|
163
|
+
|
|
182
164
|
@test()
|
|
183
165
|
protected static async pushSampleShouldNotCreateMultipleOutlets() {
|
|
184
166
|
const outlet = this.Outlet()
|
|
@@ -231,38 +213,6 @@ export default class LslOutletTest extends AbstractSpruceTest {
|
|
|
231
213
|
}
|
|
232
214
|
}
|
|
233
215
|
|
|
234
|
-
class SpyLiblsl implements Liblsl {
|
|
235
|
-
public lastCreateStreamInfoOptions?: CreateStreamInfoOptions
|
|
236
|
-
public lastAppendChannelsToStreamInfoOptions?: AppendChannelsToStreamInfoOptions
|
|
237
|
-
public lastCreateOutletOptions?: CreateOutletOptions
|
|
238
|
-
public lastPushSampleOptions?: PushSampleOptions
|
|
239
|
-
|
|
240
|
-
public outlet: BoundOutlet = {} as BoundOutlet
|
|
241
|
-
public streamInfo: BoundStreamInfo = {} as BoundStreamInfo
|
|
242
|
-
public createStreamInfoHitCount = 0
|
|
243
|
-
|
|
244
|
-
public createStreamInfo(options: CreateStreamInfoOptions): BoundStreamInfo {
|
|
245
|
-
this.createStreamInfoHitCount++
|
|
246
|
-
this.lastCreateStreamInfoOptions = options
|
|
247
|
-
return this.streamInfo
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
public appendChannelsToStreamInfo(
|
|
251
|
-
options: AppendChannelsToStreamInfoOptions
|
|
252
|
-
): void {
|
|
253
|
-
this.lastAppendChannelsToStreamInfoOptions = options
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
public createOutlet(options: CreateOutletOptions): BoundOutlet {
|
|
257
|
-
this.lastCreateOutletOptions = options
|
|
258
|
-
return this.outlet
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
public pushSample(options: PushSampleOptions): void {
|
|
262
|
-
this.lastPushSampleOptions = options
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
216
|
class CheckingOutlet extends LslOutlet {
|
|
267
217
|
public constructor(options: LslOutletOptions) {
|
|
268
218
|
super(options)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { randomInt } from 'crypto'
|
|
2
|
+
import AbstractSpruceTest, { assert, test } from '@sprucelabs/test-utils'
|
|
3
|
+
import LiblslImpl from '../../Liblsl'
|
|
4
|
+
import { LslOutletOptions } from '../../LslOutlet'
|
|
5
|
+
import TimeMarkerOutlet from '../../TimeMarkerOutlet'
|
|
6
|
+
import generateRandomOutletOptions from '../support/generateRandomOutletOptions'
|
|
7
|
+
import { SpyLiblsl } from '../support/SpyLiblsl'
|
|
8
|
+
|
|
9
|
+
export default class TimeMarkerOutletTest extends AbstractSpruceTest {
|
|
10
|
+
private static spyLiblsl: SpyLiblsl
|
|
11
|
+
|
|
12
|
+
protected static async beforeEach() {
|
|
13
|
+
await super.beforeEach()
|
|
14
|
+
TimeMarkerOutlet.Class = SpyTimeMarkerOutlet
|
|
15
|
+
this.spyLiblsl = new SpyLiblsl()
|
|
16
|
+
LiblslImpl.setInstance(this.spyLiblsl)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@test()
|
|
20
|
+
protected static async loadsWithTimeMarkerSpecificOptions() {
|
|
21
|
+
const outlet = this.Outlet()
|
|
22
|
+
assert.isEqualDeep(outlet.options, {
|
|
23
|
+
name: 'Time markers',
|
|
24
|
+
type: 'Markers',
|
|
25
|
+
channelNames: ['Markers'],
|
|
26
|
+
sampleRate: 0,
|
|
27
|
+
channelFormat: 'string',
|
|
28
|
+
sourceId: 'time-markers',
|
|
29
|
+
manufacturer: 'N/A',
|
|
30
|
+
unit: 'N/A',
|
|
31
|
+
chunkSize: 0,
|
|
32
|
+
maxBuffered: 0,
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@test()
|
|
37
|
+
protected static async canOverrideDefaultOptions() {
|
|
38
|
+
const options = generateRandomOutletOptions(randomInt(8))
|
|
39
|
+
const outlet = this.Outlet(options)
|
|
40
|
+
assert.isEqualDeep(outlet.options, options)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
private static Outlet(options?: Partial<LslOutletOptions>) {
|
|
44
|
+
return TimeMarkerOutlet.Outlet(options) as SpyTimeMarkerOutlet
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
class SpyTimeMarkerOutlet extends TimeMarkerOutlet {
|
|
49
|
+
public options: LslOutletOptions
|
|
50
|
+
public constructor(options: LslOutletOptions) {
|
|
51
|
+
super(options)
|
|
52
|
+
this.options = options
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -13,6 +13,7 @@ import LiblslImpl, {
|
|
|
13
13
|
CreateStreamInfoOptions,
|
|
14
14
|
AppendChannelsToStreamInfoOptions,
|
|
15
15
|
CreateOutletOptions,
|
|
16
|
+
DestroyOutletOptions,
|
|
16
17
|
PushSampleOptions,
|
|
17
18
|
LslChannel,
|
|
18
19
|
BoundOutlet,
|
|
@@ -33,6 +34,7 @@ export default class LiblslTest extends AbstractSpruceTest {
|
|
|
33
34
|
private static createStreamInfoParams?: any[]
|
|
34
35
|
private static appendChildParams: any[] = []
|
|
35
36
|
private static createOutletParams?: any[]
|
|
37
|
+
private static destroyOutletParams?: any[]
|
|
36
38
|
private static pushSampleParams?: any[]
|
|
37
39
|
private static appendChildValueParams: any[]
|
|
38
40
|
private static localClock: number
|
|
@@ -48,6 +50,7 @@ export default class LiblslTest extends AbstractSpruceTest {
|
|
|
48
50
|
delete this.libraryOptions
|
|
49
51
|
delete this.createStreamInfoParams
|
|
50
52
|
delete this.createOutletParams
|
|
53
|
+
delete this.destroyOutletParams
|
|
51
54
|
delete this.pushSampleParams
|
|
52
55
|
delete this.getDescriptionParams
|
|
53
56
|
this.appendChildParams = []
|
|
@@ -75,6 +78,10 @@ export default class LiblslTest extends AbstractSpruceTest {
|
|
|
75
78
|
this.createOutletParams = params
|
|
76
79
|
return this.fakeOutlet
|
|
77
80
|
},
|
|
81
|
+
lsl_destroy_outlet: (...params: any[]) => {
|
|
82
|
+
this.destroyOutletParams = params
|
|
83
|
+
return
|
|
84
|
+
},
|
|
78
85
|
lsl_push_sample_ft: (...params: any[]) => {
|
|
79
86
|
this.pushSampleParams = params
|
|
80
87
|
return 0
|
|
@@ -203,6 +210,7 @@ export default class LiblslTest extends AbstractSpruceTest {
|
|
|
203
210
|
['string', 'string', 'int', 'double', 'int', 'string'],
|
|
204
211
|
],
|
|
205
212
|
lsl_create_outlet: [outletType, [streamInfo, 'int', 'int']],
|
|
213
|
+
lsl_destroy_outlet: ['void', [outletType]],
|
|
206
214
|
lsl_local_clock: ['double', []],
|
|
207
215
|
lsl_push_sample_ft: ['void', [outletType, FloatArray, 'double']],
|
|
208
216
|
lsl_get_desc: [xmlPtr, [streamInfo]],
|
|
@@ -226,15 +234,9 @@ export default class LiblslTest extends AbstractSpruceTest {
|
|
|
226
234
|
|
|
227
235
|
@test()
|
|
228
236
|
protected static async canCreateOutletWithRequiredParams() {
|
|
229
|
-
const
|
|
230
|
-
const options = {
|
|
231
|
-
info,
|
|
232
|
-
chunkSize: randomInt(10),
|
|
233
|
-
maxBuffered: randomInt(10),
|
|
234
|
-
}
|
|
235
|
-
const actual = this.lsl.createOutlet(options)
|
|
237
|
+
const { options, outlet } = this.createRandomOutlet()
|
|
236
238
|
assert.isEqualDeep(this.createOutletParams, Object.values(options))
|
|
237
|
-
assert.isEqual(
|
|
239
|
+
assert.isEqual(outlet, this.fakeOutlet)
|
|
238
240
|
}
|
|
239
241
|
|
|
240
242
|
@test()
|
|
@@ -302,12 +304,32 @@ export default class LiblslTest extends AbstractSpruceTest {
|
|
|
302
304
|
assert.isEqual(this.appendChildValueParams[5][2], channel2.type)
|
|
303
305
|
}
|
|
304
306
|
|
|
307
|
+
@test()
|
|
308
|
+
protected static async canDestroyOutlet() {
|
|
309
|
+
const outlet = this.createRandomOutlet()
|
|
310
|
+
const options = { outlet }
|
|
311
|
+
this.lsl.destroyOutlet(options)
|
|
312
|
+
|
|
313
|
+
assert.isEqualDeep(this.destroyOutletParams, Object.values(options))
|
|
314
|
+
}
|
|
315
|
+
|
|
305
316
|
private static createRandomStreamInfo() {
|
|
306
317
|
return this.lsl.createStreamInfo(
|
|
307
318
|
this.generateRandomCreateStreamInfoOptions()
|
|
308
319
|
)
|
|
309
320
|
}
|
|
310
321
|
|
|
322
|
+
private static createRandomOutlet() {
|
|
323
|
+
const info = this.createRandomStreamInfo()
|
|
324
|
+
const options = {
|
|
325
|
+
info,
|
|
326
|
+
chunkSize: randomInt(10),
|
|
327
|
+
maxBuffered: randomInt(10),
|
|
328
|
+
}
|
|
329
|
+
const outlet = this.lsl.createOutlet(options)
|
|
330
|
+
return { options, outlet }
|
|
331
|
+
}
|
|
332
|
+
|
|
311
333
|
private static generateRandomChannelValues(): LslChannel {
|
|
312
334
|
return {
|
|
313
335
|
label: generateId(),
|
|
@@ -341,6 +363,8 @@ class FakeLiblsl implements Liblsl {
|
|
|
341
363
|
return {} as BoundOutlet
|
|
342
364
|
}
|
|
343
365
|
|
|
366
|
+
public destroyOutlet(_options: DestroyOutletOptions): void {}
|
|
367
|
+
|
|
344
368
|
public pushSample(_options: PushSampleOptions): void {}
|
|
345
369
|
}
|
|
346
370
|
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CreateOutletOptions,
|
|
3
|
+
CreateStreamInfoOptions,
|
|
4
|
+
Liblsl,
|
|
5
|
+
BoundOutlet,
|
|
6
|
+
BoundStreamInfo,
|
|
7
|
+
AppendChannelsToStreamInfoOptions,
|
|
8
|
+
PushSampleOptions,
|
|
9
|
+
} from '../../Liblsl'
|
|
10
|
+
|
|
11
|
+
export class SpyLiblsl implements Liblsl {
|
|
12
|
+
public lastCreateStreamInfoOptions?: CreateStreamInfoOptions
|
|
13
|
+
public lastAppendChannelsToStreamInfoOptions?: AppendChannelsToStreamInfoOptions
|
|
14
|
+
public lastCreateOutletOptions?: CreateOutletOptions
|
|
15
|
+
public lastPushSampleOptions?: PushSampleOptions
|
|
16
|
+
|
|
17
|
+
public outlet: BoundOutlet = {} as BoundOutlet
|
|
18
|
+
public streamInfo: BoundStreamInfo = {} as BoundStreamInfo
|
|
19
|
+
public createStreamInfoHitCount = 0
|
|
20
|
+
public destroyOutletHitCount = 0
|
|
21
|
+
|
|
22
|
+
public createStreamInfo(options: CreateStreamInfoOptions): BoundStreamInfo {
|
|
23
|
+
this.createStreamInfoHitCount++
|
|
24
|
+
this.lastCreateStreamInfoOptions = options
|
|
25
|
+
return this.streamInfo
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public appendChannelsToStreamInfo(
|
|
29
|
+
options: AppendChannelsToStreamInfoOptions
|
|
30
|
+
): void {
|
|
31
|
+
this.lastAppendChannelsToStreamInfoOptions = options
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public createOutlet(options: CreateOutletOptions): BoundOutlet {
|
|
35
|
+
this.lastCreateOutletOptions = options
|
|
36
|
+
return this.outlet
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public destroyOutlet() {
|
|
40
|
+
this.destroyOutletHitCount++
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public pushSample(options: PushSampleOptions): void {
|
|
44
|
+
this.lastPushSampleOptions = options
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { randomInt } from 'crypto'
|
|
2
|
+
import { generateId } from '@sprucelabs/test-utils'
|
|
3
|
+
import { LslOutletOptions } from '../../LslOutlet'
|
|
4
|
+
import { TEST_CHANNEL_FORMATS } from './consts'
|
|
5
|
+
|
|
6
|
+
export default function generateRandomOutletOptions(randomChannelIdx: number) {
|
|
7
|
+
return {
|
|
8
|
+
name: generateId(),
|
|
9
|
+
type: generateId(),
|
|
10
|
+
channelNames: new Array(randomInt(1, 10)).fill(generateId()),
|
|
11
|
+
sampleRate: Math.random() * 10,
|
|
12
|
+
channelFormat: TEST_CHANNEL_FORMATS[randomChannelIdx],
|
|
13
|
+
sourceId: generateId(),
|
|
14
|
+
manufacturer: generateId(),
|
|
15
|
+
unit: generateId(),
|
|
16
|
+
chunkSize: randomInt(0, 10),
|
|
17
|
+
maxBuffered: randomInt(0, 10),
|
|
18
|
+
} as LslOutletOptions
|
|
19
|
+
}
|