@neurodevs/node-lsl 17.1.1 → 18.0.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 (178) hide show
  1. package/.claude/settings.local.json +10 -0
  2. package/.regressionproof.json +7 -0
  3. package/.vscode/launch.json +3 -2
  4. package/README.md +9 -35
  5. package/build/.spruce/settings.json +5 -1
  6. package/build/__tests__/AbstractPackageTest.d.ts +4 -4
  7. package/build/__tests__/AbstractPackageTest.js +9 -11
  8. package/build/__tests__/AbstractPackageTest.js.map +1 -1
  9. package/build/__tests__/impl/LslBackpressureMonitor.test.d.ts +7 -0
  10. package/build/__tests__/impl/LslBackpressureMonitor.test.js +26 -0
  11. package/build/__tests__/impl/LslBackpressureMonitor.test.js.map +1 -0
  12. package/build/__tests__/impl/LslEventMarkerEmitter.test.d.ts +34 -0
  13. package/build/__tests__/impl/LslEventMarkerEmitter.test.js +243 -0
  14. package/build/__tests__/impl/LslEventMarkerEmitter.test.js.map +1 -0
  15. package/build/__tests__/impl/LslStreamInfo.test.d.ts +0 -3
  16. package/build/__tests__/impl/LslStreamInfo.test.js +2 -27
  17. package/build/__tests__/impl/LslStreamInfo.test.js.map +1 -1
  18. package/build/__tests__/impl/LslStreamInlet.test.d.ts +20 -8
  19. package/build/__tests__/impl/LslStreamInlet.test.js +224 -69
  20. package/build/__tests__/impl/LslStreamInlet.test.js.map +1 -1
  21. package/build/__tests__/impl/LslStreamOutlet.test.d.ts +3 -7
  22. package/build/__tests__/impl/LslStreamOutlet.test.js +36 -46
  23. package/build/__tests__/impl/LslStreamOutlet.test.js.map +1 -1
  24. package/build/__tests__/impl/LslWebSocketBridge.test.js +16 -17
  25. package/build/__tests__/impl/LslWebSocketBridge.test.js.map +1 -1
  26. package/build/assertions.d.ts +1 -1
  27. package/build/assertions.js.map +1 -1
  28. package/build/impl/LslBackpressureMonitor.d.ts +8 -0
  29. package/build/impl/LslBackpressureMonitor.js +8 -0
  30. package/build/impl/LslBackpressureMonitor.js.map +1 -0
  31. package/build/impl/LslEventMarkerEmitter.d.ts +35 -0
  32. package/build/impl/LslEventMarkerEmitter.js +86 -0
  33. package/build/impl/LslEventMarkerEmitter.js.map +1 -0
  34. package/build/impl/LslStreamInfo.d.ts +4 -8
  35. package/build/impl/LslStreamInfo.js +8 -21
  36. package/build/impl/LslStreamInfo.js.map +1 -1
  37. package/build/impl/LslStreamInlet.d.ts +31 -50
  38. package/build/impl/LslStreamInlet.js +79 -149
  39. package/build/impl/LslStreamInlet.js.map +1 -1
  40. package/build/impl/LslStreamOutlet.d.ts +15 -18
  41. package/build/impl/LslStreamOutlet.js +39 -47
  42. package/build/impl/LslStreamOutlet.js.map +1 -1
  43. package/build/impl/LslWebSocketBridge.d.ts +4 -5
  44. package/build/impl/LslWebSocketBridge.js +8 -8
  45. package/build/impl/LslWebSocketBridge.js.map +1 -1
  46. package/build/impl/workers/inlet/LslStreamInlet.worker.d.ts +22 -0
  47. package/build/impl/workers/inlet/LslStreamInlet.worker.js +42 -0
  48. package/build/impl/workers/inlet/LslStreamInlet.worker.js.map +1 -0
  49. package/build/impl/workers/inlet/StreamInletWorker.d.ts +73 -0
  50. package/build/impl/workers/inlet/StreamInletWorker.js +253 -0
  51. package/build/impl/workers/inlet/StreamInletWorker.js.map +1 -0
  52. package/build/impl/workers/outlet/LslStreamOutlet.worker.d.ts +32 -0
  53. package/build/impl/workers/outlet/LslStreamOutlet.worker.js +82 -0
  54. package/build/impl/workers/outlet/LslStreamOutlet.worker.js.map +1 -0
  55. package/build/index.d.ts +12 -14
  56. package/build/index.js +14 -16
  57. package/build/index.js.map +1 -1
  58. package/build/scripts/finalizeHandles.d.ts +2 -0
  59. package/build/scripts/finalizeHandles.js +48 -0
  60. package/build/scripts/finalizeHandles.js.map +1 -0
  61. package/build/scripts/runLslEventMarkerEmitter.js +11 -0
  62. package/build/scripts/runLslEventMarkerEmitter.js.map +1 -0
  63. package/build/scripts/runOutletToInlet.js +7 -6
  64. package/build/scripts/runOutletToInlet.js.map +1 -1
  65. package/build/scripts/testDestroyOutlet.d.ts +1 -0
  66. package/build/scripts/testDestroyOutlet.js +4 -0
  67. package/build/scripts/testDestroyOutlet.js.map +1 -0
  68. package/build/testDoubles/BackpressureMonitor/FakeBackpressureMonitor.d.ts +6 -0
  69. package/build/testDoubles/BackpressureMonitor/FakeBackpressureMonitor.js +10 -0
  70. package/build/testDoubles/BackpressureMonitor/FakeBackpressureMonitor.js.map +1 -0
  71. package/build/testDoubles/EventMarkerEmitter/FakeEventMarkerEmitter.d.ts +17 -0
  72. package/build/testDoubles/EventMarkerEmitter/FakeEventMarkerEmitter.js +30 -0
  73. package/build/testDoubles/EventMarkerEmitter/FakeEventMarkerEmitter.js.map +1 -0
  74. package/build/testDoubles/EventMarkerEmitter/SpyEventMarkerEmitter.d.ts +10 -0
  75. package/build/testDoubles/EventMarkerEmitter/SpyEventMarkerEmitter.js +23 -0
  76. package/build/testDoubles/EventMarkerEmitter/SpyEventMarkerEmitter.js.map +1 -0
  77. package/build/testDoubles/EventMarkerEmitter/ThrowingEventMarkerEmitter.d.ts +9 -0
  78. package/build/testDoubles/EventMarkerEmitter/ThrowingEventMarkerEmitter.js +19 -0
  79. package/build/testDoubles/EventMarkerEmitter/ThrowingEventMarkerEmitter.js.map +1 -0
  80. package/build/testDoubles/StreamInfo/FakeStreamInfo.d.ts +3 -3
  81. package/build/testDoubles/StreamInfo/FakeStreamInfo.js +3 -3
  82. package/build/testDoubles/StreamInfo/FakeStreamInfo.js.map +1 -1
  83. package/build/testDoubles/StreamInlet/FakeStreamInlet.d.ts +8 -10
  84. package/build/testDoubles/StreamInlet/FakeStreamInlet.js +6 -7
  85. package/build/testDoubles/StreamInlet/FakeStreamInlet.js.map +1 -1
  86. package/build/testDoubles/StreamInlet/SpyStreamInlet.d.ts +2 -5
  87. package/build/testDoubles/StreamInlet/SpyStreamInlet.js +2 -8
  88. package/build/testDoubles/StreamInlet/SpyStreamInlet.js.map +1 -1
  89. package/build/testDoubles/StreamOutlet/FakeStreamOutlet.d.ts +7 -9
  90. package/build/testDoubles/StreamOutlet/FakeStreamOutlet.js +5 -8
  91. package/build/testDoubles/StreamOutlet/FakeStreamOutlet.js.map +1 -1
  92. package/build/testDoubles/WebSocketBridge/FakeWebSocketBridge.d.ts +1 -1
  93. package/build/testDoubles/WebSocketBridge/FakeWebSocketBridge.js +1 -1
  94. package/build/testDoubles/WebSocketBridge/FakeWebSocketBridge.js.map +1 -1
  95. package/build/testDoubles/WorkerThreads/FakeWorker.d.ts +27 -0
  96. package/build/testDoubles/WorkerThreads/FakeWorker.js +67 -0
  97. package/build/testDoubles/WorkerThreads/FakeWorker.js.map +1 -0
  98. package/build/validations.js +0 -2
  99. package/build/validations.js.map +1 -1
  100. package/package.json +23 -7
  101. package/src/.spruce/settings.json +5 -1
  102. package/src/__tests__/AbstractPackageTest.ts +12 -14
  103. package/src/__tests__/impl/LslBackpressureMonitor.test.ts +25 -0
  104. package/src/__tests__/impl/LslEventMarkerEmitter.test.ts +322 -0
  105. package/src/__tests__/impl/LslStreamInfo.test.ts +3 -40
  106. package/src/__tests__/impl/LslStreamInlet.test.ts +344 -97
  107. package/src/__tests__/impl/LslStreamOutlet.test.ts +61 -69
  108. package/src/__tests__/impl/LslWebSocketBridge.test.ts +18 -18
  109. package/src/assertions.ts +2 -1
  110. package/src/impl/LslBackpressureMonitor.ts +13 -0
  111. package/src/impl/LslEventMarkerEmitter.ts +133 -0
  112. package/src/impl/LslStreamInfo.ts +17 -29
  113. package/src/impl/LslStreamInlet.ts +102 -215
  114. package/src/impl/LslStreamOutlet.ts +54 -78
  115. package/src/impl/LslWebSocketBridge.ts +12 -12
  116. package/src/impl/workers/inlet/LslStreamInlet.worker.ts +74 -0
  117. package/src/impl/workers/inlet/StreamInletWorker.ts +348 -0
  118. package/src/impl/workers/outlet/LslStreamOutlet.worker.ts +144 -0
  119. package/src/index.ts +14 -17
  120. package/src/scripts/finalizeHandles.ts +63 -0
  121. package/src/scripts/runLslEventMarkerEmitter.ts +15 -0
  122. package/src/scripts/runOutletToInlet.ts +10 -8
  123. package/src/scripts/testDestroyOutlet.ts +5 -0
  124. package/src/testDoubles/BackpressureMonitor/FakeBackpressureMonitor.ts +13 -0
  125. package/src/testDoubles/EventMarkerEmitter/FakeEventMarkerEmitter.ts +46 -0
  126. package/src/testDoubles/EventMarkerEmitter/SpyEventMarkerEmitter.ts +31 -0
  127. package/src/testDoubles/EventMarkerEmitter/ThrowingEventMarkerEmitter.ts +29 -0
  128. package/src/testDoubles/StreamInfo/FakeStreamInfo.ts +4 -4
  129. package/src/testDoubles/StreamInlet/FakeStreamInlet.ts +11 -17
  130. package/src/testDoubles/StreamInlet/SpyStreamInlet.ts +3 -16
  131. package/src/testDoubles/StreamOutlet/FakeStreamOutlet.ts +12 -15
  132. package/src/testDoubles/WebSocketBridge/FakeWebSocketBridge.ts +1 -1
  133. package/src/testDoubles/WorkerThreads/FakeWorker.ts +99 -0
  134. package/src/validations.ts +0 -2
  135. package/build/__tests__/impl/LiblslAdapter.test.d.ts +0 -79
  136. package/build/__tests__/impl/LiblslAdapter.test.js +0 -688
  137. package/build/__tests__/impl/LiblslAdapter.test.js.map +0 -1
  138. package/build/__tests__/impl/LslEventMarkerOutlet.test.d.ts +0 -21
  139. package/build/__tests__/impl/LslEventMarkerOutlet.test.js +0 -160
  140. package/build/__tests__/impl/LslEventMarkerOutlet.test.js.map +0 -1
  141. package/build/handleError.d.ts +0 -8
  142. package/build/handleError.js +0 -25
  143. package/build/handleError.js.map +0 -1
  144. package/build/impl/LiblslAdapter.d.ts +0 -154
  145. package/build/impl/LiblslAdapter.js +0 -317
  146. package/build/impl/LiblslAdapter.js.map +0 -1
  147. package/build/impl/LslEventMarkerOutlet.d.ts +0 -28
  148. package/build/impl/LslEventMarkerOutlet.js +0 -63
  149. package/build/impl/LslEventMarkerOutlet.js.map +0 -1
  150. package/build/scripts/runLslEventMarkerOutlet.js +0 -18
  151. package/build/scripts/runLslEventMarkerOutlet.js.map +0 -1
  152. package/build/testDoubles/EventMarkerOutlet/FakeEventMarkerOutlet.d.ts +0 -13
  153. package/build/testDoubles/EventMarkerOutlet/FakeEventMarkerOutlet.js +0 -26
  154. package/build/testDoubles/EventMarkerOutlet/FakeEventMarkerOutlet.js.map +0 -1
  155. package/build/testDoubles/EventMarkerOutlet/MockEventMarkerOutlet.d.ts +0 -17
  156. package/build/testDoubles/EventMarkerOutlet/MockEventMarkerOutlet.js +0 -46
  157. package/build/testDoubles/EventMarkerOutlet/MockEventMarkerOutlet.js.map +0 -1
  158. package/build/testDoubles/EventMarkerOutlet/SpyEventMarkerOutlet.d.ts +0 -9
  159. package/build/testDoubles/EventMarkerOutlet/SpyEventMarkerOutlet.js +0 -19
  160. package/build/testDoubles/EventMarkerOutlet/SpyEventMarkerOutlet.js.map +0 -1
  161. package/build/testDoubles/EventMarkerOutlet/ThrowingEventMarkerOutlet.d.ts +0 -9
  162. package/build/testDoubles/EventMarkerOutlet/ThrowingEventMarkerOutlet.js +0 -19
  163. package/build/testDoubles/EventMarkerOutlet/ThrowingEventMarkerOutlet.js.map +0 -1
  164. package/build/testDoubles/Liblsl/FakeLiblsl.d.ts +0 -50
  165. package/build/testDoubles/Liblsl/FakeLiblsl.js +0 -126
  166. package/build/testDoubles/Liblsl/FakeLiblsl.js.map +0 -1
  167. package/src/__tests__/impl/LiblslAdapter.test.ts +0 -817
  168. package/src/__tests__/impl/LslEventMarkerOutlet.test.ts +0 -195
  169. package/src/handleError.ts +0 -24
  170. package/src/impl/LiblslAdapter.ts +0 -543
  171. package/src/impl/LslEventMarkerOutlet.ts +0 -97
  172. package/src/scripts/runLslEventMarkerOutlet.ts +0 -23
  173. package/src/testDoubles/EventMarkerOutlet/FakeEventMarkerOutlet.ts +0 -36
  174. package/src/testDoubles/EventMarkerOutlet/MockEventMarkerOutlet.ts +0 -75
  175. package/src/testDoubles/EventMarkerOutlet/SpyEventMarkerOutlet.ts +0 -24
  176. package/src/testDoubles/EventMarkerOutlet/ThrowingEventMarkerOutlet.ts +0 -28
  177. package/src/testDoubles/Liblsl/FakeLiblsl.ts +0 -182
  178. /package/build/scripts/{runLslEventMarkerOutlet.d.ts → runLslEventMarkerEmitter.d.ts} +0 -0
@@ -196,7 +196,7 @@ export default class LslStreamInfoTest extends AbstractPackageTest {
196
196
  assert.isEqualDeep(
197
197
  this.fakeLiblsl.lastAppendChannelsToStreamInfoOptions,
198
198
  {
199
- info: this.instance.boundInfo,
199
+ infoHandle: this.instance.infoHandle,
200
200
  channels: this.channelNames.map((label: string) => ({
201
201
  label,
202
202
  units: this.units,
@@ -212,49 +212,12 @@ export default class LslStreamInfoTest extends AbstractPackageTest {
212
212
  this.instance.destroy()
213
213
 
214
214
  assert.isEqualDeep(
215
- this.fakeLiblsl.lastDestroyStreamInfoOptions?.info,
216
- this.instance.boundInfo,
215
+ this.fakeLiblsl.lastDestroyStreamInfoOptions?.infoHandle,
216
+ this.instance.infoHandle,
217
217
  'Should have called destroyStreamInfo!'
218
218
  )
219
219
  }
220
220
 
221
- @test()
222
- protected static async doesNotCreateNewStreamInfoIfPassedBoundInfo() {
223
- this.LslStreamInfo({
224
- boundInfo: this.instance.boundInfo,
225
- })
226
-
227
- assert.isEqual(
228
- this.fakeLiblsl.createStreamInfoHitCount,
229
- 1,
230
- 'Should not have created new stream info!'
231
- )
232
- }
233
-
234
- @test()
235
- protected static async setsBoundInfoWhenPassed() {
236
- const instance = this.LslStreamInfo({
237
- boundInfo: this.instance.boundInfo,
238
- })
239
-
240
- assert.isEqualDeep(
241
- instance.boundInfo,
242
- this.instance.boundInfo,
243
- 'Should have set bound stream info!'
244
- )
245
- }
246
-
247
- @test()
248
- protected static async cachesInfoAttributesByBoundInfo() {
249
- const cached = LslStreamInfo.From(this.instance.boundInfo)
250
-
251
- assert.isEqualDeep(
252
- cached,
253
- this.instance,
254
- 'Should have set bound stream info!'
255
- )
256
- }
257
-
258
221
  private static readonly defaultOptions = {
259
222
  name: this.name,
260
223
  type: this.type,
@@ -1,17 +1,61 @@
1
- import { test, assert } from '@neurodevs/node-tdd'
1
+ import { Worker } from 'node:worker_threads'
2
2
 
3
- import { StreamInletOptions } from '../../impl/LslStreamInlet.js'
3
+ import { FakeLiblsl } from '@neurodevs/ndx-native'
4
+ import { test, assert } from '@neurodevs/node-tdd'
5
+ import {
6
+ createPointer,
7
+ DataType,
8
+ FieldType,
9
+ JsExternal,
10
+ PointerType,
11
+ unwrapPointer,
12
+ } from 'ffi-rs'
13
+
14
+ import LslStreamInlet, {
15
+ StreamInletOptions,
16
+ } from '../../impl/LslStreamInlet.js'
17
+ import StreamInletWorker from '../../impl/workers/inlet/StreamInletWorker.js'
4
18
  import { SpyStreamInlet } from '../../testDoubles/StreamInlet/SpyStreamInlet.js'
19
+ import FakeWorker from '../../testDoubles/WorkerThreads/FakeWorker.js'
5
20
  import AbstractPackageTest from '../AbstractPackageTest.js'
6
21
 
7
22
  export default class LslStreamInletTest extends AbstractPackageTest {
8
23
  private static instance: SpyStreamInlet
9
24
 
10
25
  private static callsToOnData: {
11
- samples: Float32Array
12
- timestamps: Float64Array
26
+ samples: number[]
27
+ timestamps: number[]
13
28
  }[]
14
29
 
30
+ private static readonly sampleBufferPtr = unwrapPointer(
31
+ createPointer({
32
+ paramsType: [DataType.U8Array],
33
+ paramsValue: [new Float32Array(this.channelCount * this.chunkSize)],
34
+ })
35
+ )[0]
36
+
37
+ private static readonly timestampBufferPtr = unwrapPointer(
38
+ createPointer({
39
+ paramsType: [DataType.U8Array],
40
+ paramsValue: [new Float64Array(this.chunkSize)],
41
+ })
42
+ )[0]
43
+
44
+ private static readonly errorCodePtr = unwrapPointer(
45
+ createPointer({
46
+ paramsType: [DataType.U8Array],
47
+ paramsValue: [new Int32Array(1)],
48
+ })
49
+ )[0]
50
+
51
+ protected static async beforeAll() {
52
+ assert.isEqual(
53
+ LslStreamInlet.waitAfterOpenStreamMs,
54
+ 100,
55
+ 'Default waitAfterOpenStreamMs should be 100!'
56
+ )
57
+ }
58
+
15
59
  protected static async beforeEach() {
16
60
  await super.beforeEach()
17
61
 
@@ -21,6 +65,16 @@ export default class LslStreamInletTest extends AbstractPackageTest {
21
65
 
22
66
  this.callsToOnData = []
23
67
 
68
+ LslStreamInlet.waitAfterOpenStreamMs = 0
69
+
70
+ LslStreamInlet.Worker = FakeWorker as unknown as typeof Worker
71
+ FakeWorker.resetTestDoubles()
72
+
73
+ StreamInletWorker.lsl = this.fakeLiblsl
74
+ StreamInletWorker.freePointer = () => {}
75
+
76
+ FakeWorker.fakeOnData = this.onData
77
+
24
78
  this.instance = await this.LslStreamInlet()
25
79
  }
26
80
 
@@ -31,46 +85,82 @@ export default class LslStreamInletTest extends AbstractPackageTest {
31
85
 
32
86
  @test()
33
87
  protected static async callsBindingsToCreateStreamInlet() {
34
- const fakeInfo = this.instance.getStreamInfo()
88
+ await this.startPulling()
35
89
 
36
- assert.isTruthy(fakeInfo, 'Should have created stream info!')
90
+ assert.isTruthy(this.inletHandle, 'Should have created stream info!')
37
91
 
38
92
  assert.isEqualDeep(
39
93
  this.fakeLiblsl.lastCreateInletOptions,
40
94
  {
41
- info: fakeInfo.boundInfo,
95
+ infoHandle: this.inletHandle,
42
96
  maxBufferedMs: this.maxBufferedMs,
43
97
  },
44
98
  'Should have called createInlet!'
45
99
  )
100
+
101
+ this.stopPulling()
46
102
  }
47
103
 
48
104
  @test()
49
- protected static async flushQueueCallsLslBinding() {
50
- this.instance.flushQueue()
105
+ protected static async flushInletCallsLslBinding() {
106
+ this.instance.flushInlet()
51
107
 
52
108
  assert.isEqualDeep(
53
109
  this.fakeLiblsl.lastFlushInletOptions,
54
- { inlet: this.boundInlet },
110
+ { inletHandle: this.inletHandle },
55
111
  'Should have called flushInlet!'
56
112
  )
57
113
  }
58
114
 
59
115
  @test()
60
116
  protected static async destroyCallsLslBinding() {
61
- this.instance.destroy()
117
+ await this.startPullingThenDestroy()
62
118
 
63
119
  assert.isEqualDeep(
64
120
  this.fakeLiblsl.lastDestroyInletOptions,
65
- { inlet: this.boundInlet },
121
+ { inletHandle: this.inletHandle },
66
122
  'Did not destroy inlet!'
67
123
  )
68
124
  }
69
125
 
126
+ @test()
127
+ protected static async destroyFreesAllPointers() {
128
+ interface FreePointerParams {
129
+ paramsType: FieldType[]
130
+ paramsValue: JsExternal[]
131
+ pointerType: PointerType
132
+ }
133
+
134
+ let calls: FreePointerParams[] = []
135
+
136
+ StreamInletWorker.freePointer = (params: FreePointerParams) => {
137
+ calls.push(params)
138
+ }
139
+
140
+ await this.startPullingThenDestroy()
141
+
142
+ const { paramsType, pointerType } = calls[0]
143
+
144
+ assert.isEqualDeep(
145
+ { paramsType, pointerType },
146
+ {
147
+ paramsType: [
148
+ DataType.U8Array,
149
+ DataType.U8Array,
150
+ DataType.U8Array,
151
+ DataType.U8Array,
152
+ ],
153
+ pointerType: PointerType.CPointer,
154
+ },
155
+
156
+ 'Destroy did not free all pointers!'
157
+ )
158
+ }
159
+
70
160
  @test()
71
161
  protected static async destroyCallsStopIfIsRunning() {
72
- this.instance.startPulling()
73
- this.instance.destroy()
162
+ await this.startPulling()
163
+ this.destroy()
74
164
 
75
165
  assert.isFalse(
76
166
  this.instance.isRunning,
@@ -98,14 +188,135 @@ export default class LslStreamInletTest extends AbstractPackageTest {
98
188
 
99
189
  @test()
100
190
  protected static async startPullingSetsIsRunningToTrue() {
101
- this.startPulling()
102
- await this.wait(10)
191
+ await this.startPulling()
103
192
 
104
193
  assert.isTrue(this.isRunning, 'isRunning should be true!')
105
194
 
106
195
  this.stopPulling()
107
196
  }
108
197
 
198
+ @test()
199
+ protected static async doesNotStartPullingTwiceIfAlreadyRunning() {
200
+ await this.startPulling()
201
+ await this.startPulling()
202
+
203
+ assert.isEqual(
204
+ FakeWorker.callsToPostMessage.filter(
205
+ (msg) => msg.type === 'startPulling'
206
+ ).length,
207
+ 1,
208
+ 'startPulling did not return early!'
209
+ )
210
+
211
+ this.stopPulling()
212
+ }
213
+
214
+ @test()
215
+ protected static async startPullingOpensInletStream() {
216
+ await this.startPulling()
217
+
218
+ const aboutOneYearInMs = 32000000 * 1000
219
+
220
+ assert.isEqualDeep(
221
+ this.fakeLiblsl.lastOpenStreamOptions,
222
+ {
223
+ inletHandle: this.inletHandle,
224
+ timeoutMs: aboutOneYearInMs,
225
+ errorCodePtr: this.errorCodePtr,
226
+ },
227
+ 'Did not open inlet stream!'
228
+ )
229
+
230
+ this.stopPulling()
231
+ }
232
+
233
+ @test()
234
+ protected static async opensInletStreamWithPassedTimeout() {
235
+ const openStreamTimeoutMs = 1000 * Math.random()
236
+
237
+ const instance = await this.LslStreamInlet({
238
+ openStreamTimeoutMs,
239
+ })
240
+
241
+ await this.startPulling(instance)
242
+
243
+ assert.isEqualDeep(
244
+ this.fakeLiblsl.lastOpenStreamOptions,
245
+ {
246
+ inletHandle: this.inletHandle,
247
+ timeoutMs: openStreamTimeoutMs,
248
+ errorCodePtr: this.errorCodePtr,
249
+ },
250
+ 'Did not open inlet stream with passed timeout!'
251
+ )
252
+
253
+ instance.stopPulling()
254
+ }
255
+
256
+ @test()
257
+ protected static async waitsAfterOpeningStreamWithPassedOption() {
258
+ const waitAfterOpenStreamMs = 10
259
+
260
+ const instance = await this.LslStreamInlet({
261
+ waitAfterOpenStreamMs,
262
+ })
263
+
264
+ let t0 = Date.now()
265
+ await this.startPulling(instance)
266
+ let elapsed = Date.now() - t0
267
+
268
+ instance.stopPulling()
269
+
270
+ assert.isAbove(
271
+ elapsed,
272
+ waitAfterOpenStreamMs * 0.8,
273
+ 'Did not wait after opening stream!'
274
+ )
275
+ }
276
+
277
+ @test()
278
+ protected static async stopPullingClosesInletStream() {
279
+ await this.startPulling()
280
+ this.stopPulling()
281
+
282
+ assert.isEqualDeep(
283
+ this.fakeLiblsl.lastCloseStreamOptions,
284
+ {
285
+ inletHandle: this.inletHandle,
286
+ },
287
+ 'Did not close inlet stream!'
288
+ )
289
+ }
290
+
291
+ @test()
292
+ protected static async stopPullingFlushesInletByDefault() {
293
+ await this.startPulling()
294
+ this.stopPulling()
295
+
296
+ assert.isEqualDeep(
297
+ this.fakeLiblsl.lastFlushInletOptions,
298
+ {
299
+ inletHandle: this.inletHandle,
300
+ },
301
+ 'Did not flush inlet!'
302
+ )
303
+ }
304
+
305
+ @test()
306
+ protected static async stopPullingDoesNotFlushInletIfPassedOption() {
307
+ const instance = await this.LslStreamInlet({
308
+ flushInletOnStop: false,
309
+ })
310
+
311
+ await this.startPulling(instance)
312
+ instance.stopPulling()
313
+
314
+ assert.isUndefined(
315
+ this.fakeLiblsl.lastFlushInletOptions,
316
+ 'Should not have flushed inlet!'
317
+ )
318
+ }
319
+
109
320
  @test()
110
321
  protected static async stopPullingSetsIsRunningToFalse() {
111
322
  await this.startThenStop()
@@ -137,16 +348,16 @@ export default class LslStreamInletTest extends AbstractPackageTest {
137
348
 
138
349
  @test()
139
350
  protected static async callsPullSampleIfChunkSizeIsOne() {
140
- const inlet = await this.runChunkSizeOne()
351
+ await this.runChunkSizeOne()
141
352
 
142
353
  assert.isEqualDeep(
143
354
  this.fakeLiblsl.lastPullSampleOptions,
144
355
  {
145
- inlet: this.boundInlet,
146
- dataBufferPtr: inlet['dataBufferPtr'],
147
- dataBufferElements: this.channelCount,
148
- timeout: 0,
149
- errcodePtr: inlet['errorCodeBufferPtr'],
356
+ inletHandle: this.inletHandle,
357
+ sampleBufferPtr: this.sampleBufferPtr,
358
+ sampleBufferElements: this.channelCount,
359
+ timeoutMs: 0,
360
+ errorCodePtr: this.errorCodePtr,
150
361
  },
151
362
  'Should have called pullSample!'
152
363
  )
@@ -159,46 +370,114 @@ export default class LslStreamInletTest extends AbstractPackageTest {
159
370
  assert.isEqualDeep(
160
371
  this.fakeLiblsl.lastPullChunkOptions,
161
372
  {
162
- inlet: this.boundInlet,
163
- dataBufferPtr: this.instance['dataBufferPtr'],
164
- timestampBufferPtr: this.instance['timestampBufferPtr'],
165
- dataBufferElements: this.chunkSize * this.channelCount,
373
+ inletHandle: this.inletHandle,
374
+ sampleBufferPtr: this.sampleBufferPtr,
375
+ timestampBufferPtr: this.timestampBufferPtr,
376
+ sampleBufferElements: this.chunkSize * this.channelCount,
166
377
  timestampBufferElements: this.chunkSize,
167
- timeout: 0,
168
- errcodePtr: this.instance['errorCodeBufferPtr'],
378
+ timeoutMs: 0,
379
+ errorCodePtr: this.errorCodePtr,
169
380
  },
170
381
  'Should have called pullChunk!'
171
382
  )
172
383
  }
173
384
 
385
+ @test()
386
+ protected static async pushSampleHandlesErrorCode() {
387
+ let passedErrorCode: number | undefined
388
+
389
+ StreamInletWorker.handleError = (errorCode: number) => {
390
+ passedErrorCode = errorCode
391
+ }
392
+
393
+ const fakeErrorCode = [-4, -3, -2, -1, 0][Math.floor(Math.random() * 5)]
394
+ const worker = this.getFakeWorker()
395
+
396
+ worker['inletWorker']['pullErrorBuffer'].writeInt32LE(fakeErrorCode)
397
+
398
+ await this.startThenStop()
399
+
400
+ assert.isEqualDeep(
401
+ passedErrorCode,
402
+ FakeLiblsl.fakeErrorCode,
403
+ 'Did not pass the expected error code to handleError!'
404
+ )
405
+ }
406
+
407
+ @test()
408
+ protected static async waitsBetweenPullsIfPassedOption() {
409
+ const waitBetweenPullsMs = 10
410
+
411
+ const instance = await this.LslStreamInlet({
412
+ waitBetweenPullsMs,
413
+ })
414
+
415
+ let pulls = 0
416
+
417
+ const inletWorker = this.getFakeWorker(instance)['inletWorker']
418
+ const original = inletWorker['pullDataOnce'].bind(inletWorker)
419
+
420
+ inletWorker['pullDataOnce'] = async () => {
421
+ pulls++
422
+ await original()
423
+ }
424
+
425
+ const t0 = Date.now()
426
+ const promise = this.startPulling(instance)
427
+
428
+ while (pulls < 2) {
429
+ await this.wait(1)
430
+ }
431
+
432
+ const elapsed = Date.now() - t0
433
+
434
+ assert.isAbove(
435
+ elapsed,
436
+ waitBetweenPullsMs * 0.8,
437
+ 'Did not wait between pulls'
438
+ )
439
+
440
+ instance.stopPulling()
441
+ await promise
442
+ }
443
+
444
+ @test()
445
+ protected static async waitsOneMillisecondBetweenPullsIfNotPassed() {
446
+ assert.isEqual(
447
+ this.instance['waitBetweenPullsMs'],
448
+ 1,
449
+ 'Default waitBetweenPullsMs should be 1!'
450
+ )
451
+ }
452
+
174
453
  @test()
175
454
  protected static async passesTimeoutMsOptionToPullSample() {
176
- const timeoutMs = 1000 * Math.random()
455
+ const pullTimeoutMs = 1000 * Math.random()
177
456
 
178
457
  await this.runInletWithOptions({
179
458
  chunkSize: 1,
180
- timeoutMs,
459
+ pullTimeoutMs,
181
460
  })
182
461
 
183
462
  assert.isEqual(
184
- this.fakeLiblsl.lastPullSampleOptions?.timeout,
185
- timeoutMs / 1000,
186
- 'Did not pass timeoutMs to pullSample!'
463
+ this.fakeLiblsl.lastPullSampleOptions?.timeoutMs,
464
+ pullTimeoutMs,
465
+ 'Did not pass pullTimeoutMs to pullSample!'
187
466
  )
188
467
  }
189
468
 
190
469
  @test()
191
470
  protected static async passesTimeoutMsOptionToPullChunk() {
192
- const timeoutMs = 1000 * Math.random()
471
+ const pullTimeoutMs = 1000 * Math.random()
193
472
 
194
473
  await this.runInletWithOptions({
195
474
  chunkSize: this.chunkSize,
196
- timeoutMs,
475
+ pullTimeoutMs,
197
476
  })
198
477
 
199
478
  assert.isEqual(
200
- this.fakeLiblsl.lastPullChunkOptions?.timeout,
201
- timeoutMs / 1000,
479
+ this.fakeLiblsl.lastPullChunkOptions?.timeoutMs,
480
+ pullTimeoutMs,
202
481
  'Did not pass timeoutMs to pullChunk!'
203
482
  )
204
483
  }
@@ -217,57 +496,9 @@ export default class LslStreamInletTest extends AbstractPackageTest {
217
496
  )
218
497
  }
219
498
 
220
- @test()
221
- protected static async throwsWithUnknownErrorCode() {
222
- await this.assertThrowsWithErrorCode(
223
- -999,
224
- `An unknown liblsl error has occurred!`
225
- )
226
- }
227
-
228
- @test()
229
- protected static async throwsWithErrorCodeNegativeOne() {
230
- await this.assertThrowsWithErrorCode(
231
- -1,
232
- `The liblsl operation failed due to a timeout!`
233
- )
234
- }
235
-
236
- @test()
237
- protected static async throwsWithErrorCodeNegativeTwo() {
238
- await this.assertThrowsWithErrorCode(
239
- -2,
240
- `The liblsl stream has been lost!`
241
- )
242
- }
243
-
244
- @test()
245
- protected static async throwsWithErrorCodeNegativeThree() {
246
- await this.assertThrowsWithErrorCode(
247
- -3,
248
- 'A liblsl argument was incorrectly specified!'
249
- )
250
- }
251
-
252
- @test()
253
- protected static async throwsWithErrorCodeNegativeFour() {
254
- await this.assertThrowsWithErrorCode(
255
- -4,
256
- 'An internal liblsl error has occurred!'
257
- )
258
- }
259
-
260
- private static async assertThrowsWithErrorCode(
261
- errorCode: number,
262
- message: string
263
- ) {
264
- await this.startThenStop()
265
-
266
- this.instance['errorCodeBuffer'].writeInt32LE(errorCode)
267
-
268
- assert.doesThrow(() => {
269
- this.instance['handleErrorCodeIfPresent']()
270
- }, message)
499
+ private static async startPullingThenDestroy() {
500
+ await this.startPulling()
501
+ this.destroy()
271
502
  }
272
503
 
273
504
  private static async runChunkSizeOne() {
@@ -286,31 +517,47 @@ export default class LslStreamInletTest extends AbstractPackageTest {
286
517
 
287
518
  private static async startThenStop(inlet?: SpyStreamInlet) {
288
519
  const instance = inlet || this.instance
289
- instance.startPulling()
520
+ await this.startPulling(instance)
290
521
  await this.wait(10)
291
522
  instance.stopPulling()
292
523
  }
293
524
 
294
- private static startPulling() {
295
- this.instance.startPulling()
525
+ private static async startPulling(instance?: SpyStreamInlet) {
526
+ const inst = instance || this.instance
527
+ const startPullingPromise = inst.startPulling()
528
+
529
+ while (!this.getFakeWorker(inst).createInletPromise) {
530
+ await this.wait(1)
531
+ }
532
+
533
+ await this.getFakeWorker(inst).createInletPromise
534
+ inst['workerReady'] = true
535
+
536
+ await startPullingPromise
296
537
  }
297
538
 
298
539
  private static stopPulling() {
299
540
  this.instance.stopPulling()
300
541
  }
301
542
 
543
+ private static destroy() {
544
+ this.instance.destroy()
545
+ }
546
+
302
547
  private static get isRunning() {
303
548
  return this.instance.isRunning
304
549
  }
305
550
 
306
- private static get boundInlet() {
307
- return this.instance.getBoundInlet()
551
+ private static get inletHandle() {
552
+ return this.getFakeWorker()['inletWorker']['inletHandle']
553
+ }
554
+
555
+ private static getFakeWorker(instance?: SpyStreamInlet) {
556
+ const inst = instance || this.instance
557
+ return inst['worker'] as unknown as FakeWorker
308
558
  }
309
559
 
310
- private static onData = (
311
- samples: Float32Array,
312
- timestamps: Float64Array
313
- ) => {
560
+ private static onData = (samples: number[], timestamps: number[]) => {
314
561
  this.callsToOnData.push({ samples, timestamps })
315
562
  }
316
563