7
7
using System . Threading ;
8
8
using System . Threading . Tasks ;
9
9
using Microsoft . AspNetCore . SignalR ;
10
+ using Microsoft . AspNetCore . Testing ;
10
11
using Microsoft . Extensions . Logging ;
11
12
using Microsoft . Extensions . Options ;
12
13
using Microsoft . JSInterop ;
@@ -26,7 +27,7 @@ public async Task CreateRemoteJSDataStreamAsync_CreatesStream()
26
27
var jsStreamReference = Mock . Of < IJSStreamReference > ( ) ;
27
28
28
29
// Act
29
- var remoteJSDataStream = await RemoteJSDataStream . CreateRemoteJSDataStreamAsync ( _jsRuntime , jsStreamReference , totalLength : 100 , maximumIncomingBytes : 10_000 , jsInteropDefaultCallTimeout : TimeSpan . FromMinutes ( 1 ) , pauseIncomingBytesThreshold : 50 , resumeIncomingBytesThreshold : 25 , cancellationToken : CancellationToken . None ) ;
30
+ var remoteJSDataStream = await RemoteJSDataStream . CreateRemoteJSDataStreamAsync ( _jsRuntime , jsStreamReference , totalLength : 100 , maximumIncomingBytes : 10_000 , jsInteropDefaultCallTimeout : TimeSpan . FromMinutes ( 1 ) , pauseIncomingBytesThreshold : 50 , resumeIncomingBytesThreshold : 25 , cancellationToken : CancellationToken . None ) . DefaultTimeout ( ) ;
30
31
31
32
// Assert
32
33
Assert . NotNull ( remoteJSDataStream ) ;
@@ -40,7 +41,7 @@ public async Task ReceiveData_DoesNotFindStream()
40
41
var unrecognizedGuid = 10 ;
41
42
42
43
// Act
43
- var success = await RemoteJSDataStream . ReceiveData ( _jsRuntime , streamId : unrecognizedGuid , chunkId : 0 , chunk , error : null ) ;
44
+ var success = await RemoteJSDataStream . ReceiveData ( _jsRuntime , streamId : unrecognizedGuid , chunkId : 0 , chunk , error : null ) . DefaultTimeout ( ) ;
44
45
45
46
// Assert
46
47
Assert . False ( success ) ;
@@ -60,17 +61,17 @@ public async Task ReceiveData_SuccessReadsBackStream()
60
61
var sendDataTask = Task . Run ( async ( ) =>
61
62
{
62
63
// Act 1
63
- var success = await RemoteJSDataStream . ReceiveData ( jsRuntime , streamId , chunkId : 0 , chunk , error : null ) ;
64
+ var success = await RemoteJSDataStream . ReceiveData ( jsRuntime , streamId , chunkId : 0 , chunk , error : null ) . DefaultTimeout ( ) ;
64
65
return success ;
65
66
} ) ;
66
67
67
68
// Act & Assert 2
68
69
using var memoryStream = new MemoryStream ( ) ;
69
- await remoteJSDataStream . CopyToAsync ( memoryStream ) ;
70
+ await remoteJSDataStream . CopyToAsync ( memoryStream ) . DefaultTimeout ( ) ;
70
71
Assert . Equal ( chunk , memoryStream . ToArray ( ) ) ;
71
72
72
73
// Act & Assert 3
73
- var sendDataCompleted = await sendDataTask ;
74
+ var sendDataCompleted = await sendDataTask . DefaultTimeout ( ) ;
74
75
Assert . True ( sendDataCompleted ) ;
75
76
}
76
77
@@ -88,17 +89,17 @@ public async Task ReceiveData_SuccessReadsBackPipeReader()
88
89
var sendDataTask = Task . Run ( async ( ) =>
89
90
{
90
91
// Act 1
91
- var success = await RemoteJSDataStream . ReceiveData ( jsRuntime , streamId , chunkId : 0 , chunk , error : null ) ;
92
+ var success = await RemoteJSDataStream . ReceiveData ( jsRuntime , streamId , chunkId : 0 , chunk , error : null ) . DefaultTimeout ( ) ;
92
93
return success ;
93
94
} ) ;
94
95
95
96
// Act & Assert 2
96
97
using var memoryStream = new MemoryStream ( ) ;
97
- await remoteJSDataStream . PipeReader . CopyToAsync ( memoryStream ) ;
98
+ await remoteJSDataStream . PipeReader . CopyToAsync ( memoryStream ) . DefaultTimeout ( ) ;
98
99
Assert . Equal ( chunk , memoryStream . ToArray ( ) ) ;
99
100
100
101
// Act & Assert 3
101
- var sendDataCompleted = await sendDataTask ;
102
+ var sendDataCompleted = await sendDataTask . DefaultTimeout ( ) ;
102
103
Assert . True ( sendDataCompleted ) ;
103
104
}
104
105
@@ -111,12 +112,12 @@ public async Task ReceiveData_WithError()
111
112
var streamId = GetStreamId ( remoteJSDataStream , jsRuntime ) ;
112
113
113
114
// Act & Assert 1
114
- var success = await RemoteJSDataStream . ReceiveData ( jsRuntime , streamId , chunkId : 0 , chunk : null , error : "some error" ) ;
115
+ var success = await RemoteJSDataStream . ReceiveData ( jsRuntime , streamId , chunkId : 0 , chunk : null , error : "some error" ) . DefaultTimeout ( ) ;
115
116
Assert . False ( success ) ;
116
117
117
118
// Act & Assert 2
118
119
using var mem = new MemoryStream ( ) ;
119
- var ex = await Assert . ThrowsAsync < InvalidOperationException > ( async ( ) => await remoteJSDataStream . CopyToAsync ( mem ) ) ;
120
+ var ex = await Assert . ThrowsAsync < InvalidOperationException > ( async ( ) => await remoteJSDataStream . CopyToAsync ( mem ) . DefaultTimeout ( ) ) ;
120
121
Assert . Equal ( "An error occurred while reading the remote stream: some error" , ex . Message ) ;
121
122
}
122
123
@@ -130,12 +131,12 @@ public async Task ReceiveData_WithZeroLengthChunk()
130
131
var chunk = Array . Empty < byte > ( ) ;
131
132
132
133
// Act & Assert 1
133
- var ex = await Assert . ThrowsAsync < EndOfStreamException > ( async ( ) => await RemoteJSDataStream . ReceiveData ( jsRuntime , streamId , chunkId : 0 , chunk , error : null ) ) ;
134
+ var ex = await Assert . ThrowsAsync < EndOfStreamException > ( async ( ) => await RemoteJSDataStream . ReceiveData ( jsRuntime , streamId , chunkId : 0 , chunk , error : null ) . DefaultTimeout ( ) ) ;
134
135
Assert . Equal ( "The incoming data chunk cannot be empty." , ex . Message ) ;
135
136
136
137
// Act & Assert 2
137
138
using var mem = new MemoryStream ( ) ;
138
- ex = await Assert . ThrowsAsync < EndOfStreamException > ( async ( ) => await remoteJSDataStream . CopyToAsync ( mem ) ) ;
139
+ ex = await Assert . ThrowsAsync < EndOfStreamException > ( async ( ) => await remoteJSDataStream . CopyToAsync ( mem ) . DefaultTimeout ( ) ) ;
139
140
Assert . Equal ( "The incoming data chunk cannot be empty." , ex . Message ) ;
140
141
}
141
142
@@ -150,12 +151,12 @@ public async Task ReceiveData_ProvidedWithMoreBytesThanRemaining()
150
151
var chunk = new byte [ 110 ] ; // 100 byte totalLength for stream
151
152
152
153
// Act & Assert 1
153
- var ex = await Assert . ThrowsAsync < EndOfStreamException > ( async ( ) => await RemoteJSDataStream . ReceiveData ( jsRuntime , streamId , chunkId : 0 , chunk , error : null ) ) ;
154
+ var ex = await Assert . ThrowsAsync < EndOfStreamException > ( async ( ) => await RemoteJSDataStream . ReceiveData ( jsRuntime , streamId , chunkId : 0 , chunk , error : null ) . DefaultTimeout ( ) ) ;
154
155
Assert . Equal ( "The incoming data stream declared a length 100, but 110 bytes were sent." , ex . Message ) ;
155
156
156
157
// Act & Assert 2
157
158
using var mem = new MemoryStream ( ) ;
158
- ex = await Assert . ThrowsAsync < EndOfStreamException > ( async ( ) => await remoteJSDataStream . CopyToAsync ( mem ) ) ;
159
+ ex = await Assert . ThrowsAsync < EndOfStreamException > ( async ( ) => await remoteJSDataStream . CopyToAsync ( mem ) . DefaultTimeout ( ) ) ;
159
160
Assert . Equal ( "The incoming data stream declared a length 100, but 110 bytes were sent." , ex . Message ) ;
160
161
}
161
162
@@ -174,26 +175,25 @@ public async Task ReceiveData_ProvidedWithOutOfOrderChunk_SimulatesSignalRDiscon
174
175
{
175
176
await RemoteJSDataStream . ReceiveData ( jsRuntime , streamId , chunkId : i , chunk , error : null ) ;
176
177
}
177
- var ex = await Assert . ThrowsAsync < EndOfStreamException > ( async ( ) => await RemoteJSDataStream . ReceiveData ( jsRuntime , streamId , chunkId : 7 , chunk , error : null ) ) ;
178
+ var ex = await Assert . ThrowsAsync < EndOfStreamException > ( async ( ) => await RemoteJSDataStream . ReceiveData ( jsRuntime , streamId , chunkId : 7 , chunk , error : null ) . DefaultTimeout ( ) ) ;
178
179
Assert . Equal ( "Out of sequence chunk received, expected 5, but received 7." , ex . Message ) ;
179
180
180
181
// Act & Assert 2
181
182
using var mem = new MemoryStream ( ) ;
182
- ex = await Assert . ThrowsAsync < EndOfStreamException > ( async ( ) => await remoteJSDataStream . CopyToAsync ( mem ) ) ;
183
+ ex = await Assert . ThrowsAsync < EndOfStreamException > ( async ( ) => await remoteJSDataStream . CopyToAsync ( mem ) . DefaultTimeout ( ) ) ;
183
184
Assert . Equal ( "Out of sequence chunk received, expected 5, but received 7." , ex . Message ) ;
184
185
}
185
186
186
187
[ Fact ]
187
188
public async Task ReceiveData_NoDataProvidedBeforeTimeout_StreamDisposed ( )
188
189
{
189
190
// Arrange
191
+ var unhandledExceptionRaisedTask = new TaskCompletionSource < bool > ( ) ;
190
192
var jsRuntime = new TestRemoteJSRuntime ( Options . Create ( new CircuitOptions ( ) ) , Options . Create ( new HubOptions ( ) ) , Mock . Of < ILogger < RemoteJSRuntime > > ( ) ) ;
191
- var timeoutExceptionRaisedSemaphore = new SemaphoreSlim ( initialCount : 0 , maxCount : 1 ) ;
192
193
jsRuntime . UnhandledException += ( _ , ex ) =>
193
194
{
194
- Assert . Equal ( "Did not receive any data in the alloted time." , ex . Message ) ;
195
- Assert . IsType < TimeoutException > ( ex ) ;
196
- timeoutExceptionRaisedSemaphore . Release ( ) ;
195
+ Assert . Equal ( "Did not receive any data in the allotted time." , ex . Message ) ;
196
+ unhandledExceptionRaisedTask . SetResult ( ex is TimeoutException ) ;
197
197
} ;
198
198
199
199
var jsStreamReference = Mock . Of < IJSStreamReference > ( ) ;
@@ -202,41 +202,40 @@ public async Task ReceiveData_NoDataProvidedBeforeTimeout_StreamDisposed()
202
202
jsStreamReference ,
203
203
totalLength : 15 ,
204
204
maximumIncomingBytes : 10_000 ,
205
- jsInteropDefaultCallTimeout : TimeSpan . FromSeconds ( 10 ) , // Note we're using a 10 second timeout for this test
205
+ jsInteropDefaultCallTimeout : TimeSpan . FromSeconds ( 1 ) ,
206
206
pauseIncomingBytesThreshold : 50 ,
207
207
resumeIncomingBytesThreshold : 25 ,
208
208
cancellationToken : CancellationToken . None ) ;
209
209
var streamId = GetStreamId ( remoteJSDataStream , jsRuntime ) ;
210
210
var chunk = new byte [ ] { 3 , 5 , 7 } ;
211
211
212
212
// Act & Assert 1
213
- // Trigger timeout and ensure unhandled exception raised to crush circuit
214
- remoteJSDataStream . InvalidateLastDataReceivedTimeForTimeout ( ) ;
215
- await timeoutExceptionRaisedSemaphore . WaitAsync ( ) ;
213
+ // Ensure unhandled exception raised to crush circuit
214
+ var unhandledExceptionResult = await unhandledExceptionRaisedTask . Task . DefaultTimeout ( ) ;
215
+ Assert . True ( unhandledExceptionResult ) ;
216
216
217
217
// Act & Assert 2
218
218
// Confirm exception also raised on pipe reader
219
219
using var mem = new MemoryStream ( ) ;
220
- var ex = await Assert . ThrowsAsync < TimeoutException > ( async ( ) => await remoteJSDataStream . CopyToAsync ( mem ) ) ;
220
+ var ex = await Assert . ThrowsAsync < TimeoutException > ( async ( ) => await remoteJSDataStream . CopyToAsync ( mem ) . DefaultTimeout ( ) ) ;
221
221
Assert . Equal ( "Did not receive any data in the allotted time." , ex . Message ) ;
222
222
223
223
// Act & Assert 3
224
224
// Ensures stream is disposed after the timeout and any additional chunks aren't accepted
225
- var success = await RemoteJSDataStream . ReceiveData ( jsRuntime , streamId , chunkId : 0 , chunk , error : null ) ;
225
+ var success = await RemoteJSDataStream . ReceiveData ( jsRuntime , streamId , chunkId : 0 , chunk , error : null ) . DefaultTimeout ( ) ;
226
226
Assert . False ( success ) ;
227
227
}
228
228
229
229
[ Fact ]
230
230
public async Task ReceiveData_ReceivesDataThenTimesout_StreamDisposed ( )
231
231
{
232
232
// Arrange
233
+ var unhandledExceptionRaisedTask = new TaskCompletionSource < bool > ( ) ;
233
234
var jsRuntime = new TestRemoteJSRuntime ( Options . Create ( new CircuitOptions ( ) ) , Options . Create ( new HubOptions ( ) ) , Mock . Of < ILogger < RemoteJSRuntime > > ( ) ) ;
234
- var timeoutExceptionRaisedSemaphore = new SemaphoreSlim ( initialCount : 0 , maxCount : 1 ) ;
235
235
jsRuntime . UnhandledException += ( _ , ex ) =>
236
236
{
237
237
Assert . Equal ( "Did not receive any data in the allotted time." , ex . Message ) ;
238
- Assert . IsType < TimeoutException > ( ex ) ;
239
- timeoutExceptionRaisedSemaphore . Release ( ) ;
238
+ unhandledExceptionRaisedTask . SetResult ( ex is TimeoutException ) ;
240
239
} ;
241
240
242
241
var jsStreamReference = Mock . Of < IJSStreamReference > ( ) ;
@@ -245,35 +244,35 @@ public async Task ReceiveData_ReceivesDataThenTimesout_StreamDisposed()
245
244
jsStreamReference ,
246
245
totalLength : 15 ,
247
246
maximumIncomingBytes : 10_000 ,
248
- jsInteropDefaultCallTimeout : TimeSpan . FromSeconds ( 30 ) , // Note we're using a 30 second timeout for this test
247
+ jsInteropDefaultCallTimeout : TimeSpan . FromSeconds ( 3 ) ,
249
248
pauseIncomingBytesThreshold : 50 ,
250
249
resumeIncomingBytesThreshold : 25 ,
251
250
cancellationToken : CancellationToken . None ) ;
252
251
var streamId = GetStreamId ( remoteJSDataStream , jsRuntime ) ;
253
252
var chunk = new byte [ ] { 3 , 5 , 7 } ;
254
253
255
254
// Act & Assert 1
256
- var success = await RemoteJSDataStream . ReceiveData ( jsRuntime , streamId , chunkId : 0 , chunk , error : null ) ;
255
+ var success = await RemoteJSDataStream . ReceiveData ( jsRuntime , streamId , chunkId : 0 , chunk , error : null ) . DefaultTimeout ( ) ;
257
256
Assert . True ( success ) ;
258
257
259
258
// Act & Assert 2
260
- success = await RemoteJSDataStream . ReceiveData ( jsRuntime , streamId , chunkId : 1 , chunk , error : null ) ;
259
+ success = await RemoteJSDataStream . ReceiveData ( jsRuntime , streamId , chunkId : 1 , chunk , error : null ) . DefaultTimeout ( ) ;
261
260
Assert . True ( success ) ;
262
261
263
262
// Act & Assert 3
264
- // Trigger timeout and ensure unhandled exception raised to crush circuit
265
- remoteJSDataStream . InvalidateLastDataReceivedTimeForTimeout ( ) ;
266
- await timeoutExceptionRaisedSemaphore . WaitAsync ( ) ;
263
+ // Ensure unhandled exception raised to crush circuit
264
+ var unhandledExceptionResult = await unhandledExceptionRaisedTask . Task . DefaultTimeout ( ) ;
265
+ Assert . True ( unhandledExceptionResult ) ;
267
266
268
267
// Act & Assert 4
269
268
// Confirm exception also raised on pipe reader
270
269
using var mem = new MemoryStream ( ) ;
271
- var ex = await Assert . ThrowsAsync < TimeoutException > ( async ( ) => await remoteJSDataStream . CopyToAsync ( mem ) ) ;
272
- Assert . Equal ( "Did not receive any data in the alloted time." , ex . Message ) ;
270
+ var ex = await Assert . ThrowsAsync < TimeoutException > ( async ( ) => await remoteJSDataStream . CopyToAsync ( mem ) . DefaultTimeout ( ) ) ;
271
+ Assert . Equal ( "Did not receive any data in the allotted time." , ex . Message ) ;
273
272
274
273
// Act & Assert 5
275
274
// Ensures stream is disposed after the timeout and any additional chunks aren't accepted
276
- success = await RemoteJSDataStream . ReceiveData ( jsRuntime , streamId , chunkId : 2 , chunk , error : null ) ;
275
+ success = await RemoteJSDataStream . ReceiveData ( jsRuntime , streamId , chunkId : 2 , chunk , error : null ) . DefaultTimeout ( ) ;
277
276
Assert . False ( success ) ;
278
277
}
279
278
0 commit comments