@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.
Files changed (42) hide show
  1. package/build/Liblsl.d.ts +6 -0
  2. package/build/Liblsl.js +5 -0
  3. package/build/Liblsl.js.map +1 -1
  4. package/build/LslOutlet.d.ts +4 -2
  5. package/build/LslOutlet.js +10 -7
  6. package/build/LslOutlet.js.map +1 -1
  7. package/build/TimeMarkerOutlet.d.ts +4 -0
  8. package/build/TimeMarkerOutlet.js +26 -0
  9. package/build/TimeMarkerOutlet.js.map +1 -0
  10. package/build/__tests__/behavioral/LslOutlet.test.d.ts +2 -2
  11. package/build/__tests__/behavioral/LslOutlet.test.js +17 -51
  12. package/build/__tests__/behavioral/LslOutlet.test.js.map +1 -1
  13. package/build/__tests__/behavioral/TimeMarkerOutlet.test.d.ts +8 -0
  14. package/build/__tests__/behavioral/TimeMarkerOutlet.test.js +85 -0
  15. package/build/__tests__/behavioral/TimeMarkerOutlet.test.js.map +1 -0
  16. package/build/__tests__/implementation/Liblsl.test.d.ts +3 -0
  17. package/build/__tests__/implementation/Liblsl.test.js +28 -8
  18. package/build/__tests__/implementation/Liblsl.test.js.map +1 -1
  19. package/build/__tests__/support/SpyLiblsl.d.ts +16 -0
  20. package/build/__tests__/support/SpyLiblsl.js +31 -0
  21. package/build/__tests__/support/SpyLiblsl.js.map +1 -0
  22. package/build/__tests__/support/consts.d.ts +2 -0
  23. package/build/__tests__/support/consts.js +14 -0
  24. package/build/__tests__/support/consts.js.map +1 -0
  25. package/build/__tests__/support/generateRandomOutletOptions.d.ts +2 -0
  26. package/build/__tests__/support/generateRandomOutletOptions.js +21 -0
  27. package/build/__tests__/support/generateRandomOutletOptions.js.map +1 -0
  28. package/build/errors/SpruceError.js.map +1 -1
  29. package/build/index.d.ts +1 -0
  30. package/build/index.js +3 -1
  31. package/build/index.js.map +1 -1
  32. package/package.json +1 -1
  33. package/src/Liblsl.ts +13 -0
  34. package/src/LslOutlet.ts +13 -9
  35. package/src/TimeMarkerOutlet.ts +19 -0
  36. package/src/__tests__/behavioral/LslOutlet.test.ts +22 -72
  37. package/src/__tests__/behavioral/TimeMarkerOutlet.test.ts +54 -0
  38. package/src/__tests__/implementation/Liblsl.test.ts +32 -8
  39. package/src/__tests__/support/SpyLiblsl.ts +46 -0
  40. package/src/__tests__/support/consts.ts +12 -0
  41. package/src/__tests__/support/generateRandomOutletOptions.ts +19 -0
  42. 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,2 @@
1
+ export declare const TEST_CHANNEL_FORMATS: string[];
2
+ export type TestChannelFormat = (typeof TEST_CHANNEL_FORMATS)[number];
@@ -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,2 @@
1
+ import { LslOutletOptions } from '../../LslOutlet';
2
+ export default function generateRandomOutletOptions(randomChannelIdx: number): LslOutletOptions;
@@ -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;YACtB,KAAK,uBAAuB;gBAC3B,OAAO,GAAG,wEAAwE,OAAO,CAAC,UAAU,GAAG,CAAA;gBACvG,MAAK;YACN;gBACC,OAAO,GAAG,KAAK,CAAC,eAAe,EAAE,CAAA;SAClC;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"}
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
@@ -2,3 +2,4 @@ export { default as LiblslImpl } from './Liblsl';
2
2
  export * from './Liblsl';
3
3
  export { default as LslOutletImpl } from './LslOutlet';
4
4
  export * from './LslOutlet';
5
+ export { default as TimeMarkerOutlet } from './TimeMarkerOutlet';
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
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neurodevs/node-lsl",
3
- "version": "3.1.0",
3
+ "version": "3.3.0",
4
4
  "main": "build/index.js",
5
5
  "repository": "https://github.com/neurodevs/node-lsl.git",
6
6
  "scripts": {
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.isPositiveNumber(sampleRate)) {
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 isPositiveNumber(value: number) {
127
- return value > 0
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
- export const CHANNEL_FORMATS = [
21
- 'undefined',
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 channelIdx: number
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.channelIdx = randomInt(7)
40
- this.randomOutletOptions = {
41
- name: generateId(),
42
- type: generateId(),
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.assertThrowsInvalidSampleRate(0)
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 valid = CHANNEL_FORMATS
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.channelIdx,
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 info = this.createRandomStreamInfo()
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(actual, this.fakeOutlet)
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,12 @@
1
+ export const TEST_CHANNEL_FORMATS = [
2
+ 'undefined',
3
+ 'float32',
4
+ 'double64',
5
+ 'string',
6
+ 'int32',
7
+ 'int16',
8
+ 'int8',
9
+ 'int64',
10
+ ]
11
+
12
+ export type TestChannelFormat = (typeof TEST_CHANNEL_FORMATS)[number]
@@ -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
+ }