1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using System.Diagnostics;
6 using System.IO.PortsTests;
7 using System.Threading;
8 using System.Threading.Tasks;
9 using Legacy.Support;
10 using Xunit;
11 using Xunit.NetCore.Extensions;
12 
13 namespace System.IO.Ports.Tests
14 {
15     public class SerialStream_WriteTimeout_Property : PortsTest
16     {
17         // The default number of bytes to write with when testing timeout with Write(byte[], int, int)
18         private static readonly int s_DEFAULT_WRITE_BYTE_ARRAY_SIZE = TCSupport.MinimumBlockingByteCount;
19 
20         // The large number of bytes to write with when testing timeout with Write(byte[], int, int)
21         // This needs to be large enough for Write timeout
22         private const int DEFAULT_WRITE_BYTE_LARGE_ARRAY_SIZE = 1024 * 100;
23 
24         // The BaudRate to use to make Write timeout when writing DEFAULT_WRITE_BYTE_LARGE_ARRAY_SIZE bytes
25         private const int LARGEWRITE_BAUDRATE = 1200;
26 
27         // The timeout to use to make Write timeout when writing DEFAULT_WRITE_BYTE_LARGE_ARRAY_SIZE
28         private const int LARGEWRITE_TIMEOUT = 750;
29 
30         // The default byte to call with WriteByte
31         private const byte DEFAULT_WRITE_BYTE = 33;
32 
33         // The amount of time to wait when expecting an long timeout
34         private const int DEFAULT_WAIT_LONG_TIMEOUT = 250;
35 
36         // The maximum acceptable time allowed when a write method should timeout immediately
37         private const int MAX_ACCEPTABLE_ZERO_TIMEOUT = 100;
38 
39         // The maximum acceptable time allowed when a write method should timeout immediately when it is called for the first time
40         private const int MAX_ACCEPTABLE_WARMUP_ZERO_TIMEOUT = 1000;
41 
42         // The maximum acceptable percentage difference allowed when a write method is called for the first time
43         private const double MAX_ACCEPTABLE_WARMUP_PERCENTAGE_DIFFERENCE = .5;
44 
45         // The maximum acceptable percentage difference allowed
46         private const double MAX_ACCEPTABLE_PERCENTAGE_DIFFERENCE = .15;
47 
48         private const int SUCCESSIVE_WriteTimeout_SOMEDATA = 950;
49 
50         private const int NUM_TRYS = 5;
51 
WriteMethodDelegate(Stream stream)52         private delegate void WriteMethodDelegate(Stream stream);
53 
54         #region Test Cases
55 
56         [ConditionalFact(nameof(HasOneSerialPort))]
WriteTimeout_DefaultValue()57         public void WriteTimeout_DefaultValue()
58         {
59             using (SerialPort com = new SerialPort(TCSupport.LocalMachineSerialInfo.FirstAvailablePortName))
60             {
61                 com.Open();
62                 Stream stream = com.BaseStream;
63 
64                 Debug.WriteLine("Verifying the default value of WriteTimeout");
65 
66                 Assert.True(stream.WriteTimeout == -1,
67                     string.Format(
68                         "Err_1707azhpbn Verifying the default value of WriteTimeout Expected={0} Actual={1} FAILED", -1,
69                         stream.WriteTimeout));
70             }
71         }
72 
73         [ConditionalFact(nameof(HasOneSerialPort))]
WriteTimeout_AfterClose()74         public void WriteTimeout_AfterClose()
75         {
76             Debug.WriteLine("Verifying setting WriteTimeout after the SerialPort was closed");
77 
78             VerifyException(2048, null, typeof(ObjectDisposedException));
79         }
80 
81         [ConditionalFact(nameof(HasOneSerialPort))]
WriteTimeout_Int32MinValue()82         public void WriteTimeout_Int32MinValue()
83         {
84             Debug.WriteLine("Verifying Int32.MinValue WriteTimeout");
85 
86             VerifyException(int.MinValue, typeof(ArgumentOutOfRangeException));
87         }
88 
89         [ConditionalFact(nameof(HasOneSerialPort))]
WriteTimeout_NEG2()90         public void WriteTimeout_NEG2()
91         {
92             Debug.WriteLine("Verifying -2 WriteTimeout");
93 
94             VerifyException(-2, typeof(ArgumentOutOfRangeException));
95         }
96 
97         [ConditionalFact(nameof(HasOneSerialPort))]
WriteTimeout_ZERO()98         public void WriteTimeout_ZERO()
99         {
100             Debug.WriteLine("Verifying 0 WriteTimeout");
101 
102             VerifyException(0, typeof(ArgumentOutOfRangeException));
103         }
104 
105         [ConditionalFact(nameof(HasOneSerialPort), nameof(HasHardwareFlowControl))]
WriteTimeout_Default_Write_byte_int_int()106         public void WriteTimeout_Default_Write_byte_int_int()
107         {
108             Debug.WriteLine("Verifying default WriteTimeout with Write(byte[] buffer, int offset, int count)");
109 
110             VerifyDefaultTimeout(Write_byte_int_int);
111         }
112 
113         [ConditionalFact(nameof(HasOneSerialPort), nameof(HasSingleByteTransmitBlocking))]
WriteTimeout_Default_WriteByte()114         public void WriteTimeout_Default_WriteByte()
115         {
116             Debug.WriteLine("Verifying default WriteTimeout with WriteByte()");
117 
118             VerifyDefaultTimeout(WriteByte);
119         }
120 
121         [ConditionalFact(nameof(HasOneSerialPort), nameof(HasHardwareFlowControl))]
WriteTimeout_Infinite_Write_byte_int_int()122         public void WriteTimeout_Infinite_Write_byte_int_int()
123         {
124             Debug.WriteLine("Verifying infinite WriteTimeout with Write(byte[] buffer, int offset, int count)");
125 
126             VerifyLongTimeout(Write_byte_int_int, -1);
127         }
128 
129         [ConditionalFact(nameof(HasOneSerialPort), nameof(HasSingleByteTransmitBlocking))]
WriteTimeout_Infinite_WriteByte()130         public void WriteTimeout_Infinite_WriteByte()
131         {
132             Debug.WriteLine("Verifying infinite WriteTimeout with WriteByte()");
133 
134             VerifyLongTimeout(WriteByte, -1);
135         }
136 
137         [ConditionalFact(nameof(HasOneSerialPort), nameof(HasHardwareFlowControl))]
WriteTimeout_Int32MaxValue_Write_byte_int_int()138         public void WriteTimeout_Int32MaxValue_Write_byte_int_int()
139         {
140             Debug.WriteLine("Verifying Int32.MaxValue WriteTimeout with Write(byte[] buffer, int offset, int count)");
141 
142             VerifyLongTimeout(Write_byte_int_int, int.MaxValue - 1);
143         }
144 
145         [ConditionalFact(nameof(HasOneSerialPort), nameof(HasSingleByteTransmitBlocking))]
WriteTimeout_Int32MaxValue_WriteByte()146         public void WriteTimeout_Int32MaxValue_WriteByte()
147         {
148             Debug.WriteLine("Verifying Int32.MaxValue WriteTimeout with WriteByte()");
149 
150             VerifyLongTimeout(WriteByte, int.MaxValue - 1);
151         }
152 
153         [Trait(XunitConstants.Category, XunitConstants.IgnoreForCI)]  // Timing-sensitive
154         [ConditionalFact(nameof(HasOneSerialPort), nameof(HasHardwareFlowControl))]
WriteTimeout_750_Write_byte_int_int()155         public void WriteTimeout_750_Write_byte_int_int()
156         {
157             Debug.WriteLine("Verifying 750 WriteTimeout with Write(byte[] buffer, int offset, int count)");
158 
159             VerifyTimeout(Write_byte_int_int, 750);
160         }
161 
162         [Trait(XunitConstants.Category, XunitConstants.IgnoreForCI)]  // Timing-sensitive
163         [ConditionalFact(nameof(HasOneSerialPort), nameof(HasSingleByteTransmitBlocking))]
WriteTimeout_750_WriteByte()164         public void WriteTimeout_750_WriteByte()
165         {
166             Debug.WriteLine("Verifying 750 WriteTimeout with WriteByte()");
167 
168             VerifyTimeout(WriteByte, 750);
169         }
170 
171         [Trait(XunitConstants.Category, XunitConstants.IgnoreForCI)]  // Timing-sensitive
172         [ConditionalFact(nameof(HasOneSerialPort), nameof(HasHardwareFlowControl))]
SuccessiveWriteTimeoutNoData_Write_byte_int_int()173         public void SuccessiveWriteTimeoutNoData_Write_byte_int_int()
174         {
175             using (SerialPort com = new SerialPort(TCSupport.LocalMachineSerialInfo.FirstAvailablePortName))
176             {
177                 com.Open();
178                 com.Handshake = Handshake.RequestToSend;
179                 Stream stream = com.BaseStream;
180                 stream.WriteTimeout = 850;
181 
182                 Debug.WriteLine("Verifying WriteTimeout={0} with successive call to Write(byte[], int, int) and no data", stream.WriteTimeout);
183 
184                 Assert.Throws<TimeoutException>(() => stream.Write(new byte[s_DEFAULT_WRITE_BYTE_ARRAY_SIZE], 0, s_DEFAULT_WRITE_BYTE_ARRAY_SIZE));
185 
186                 VerifyTimeout(Write_byte_int_int, stream);
187             }
188         }
189 
190         [ConditionalFact(nameof(HasNullModem), nameof(HasHardwareFlowControl))]
SuccessiveWriteTimeoutSomeData_Write_byte_int_int()191         public void SuccessiveWriteTimeoutSomeData_Write_byte_int_int()
192         {
193             using (var com1 = new SerialPort(TCSupport.LocalMachineSerialInfo.FirstAvailablePortName))
194             {
195                 var asyncEnableRts = new AsyncEnableRts();
196                 var t = new Task(asyncEnableRts.EnableRTS);
197 
198                 com1.Open();
199                 com1.Handshake = Handshake.RequestToSend;
200                 Stream stream = com1.BaseStream;
201                 stream.WriteTimeout = SUCCESSIVE_WriteTimeout_SOMEDATA;
202 
203                 Debug.WriteLine(
204                     "Verifying WriteTimeout={0} with successive call to Write(byte[], int, int) and some data being received in the first call",
205                     stream.WriteTimeout);
206 
207                 // Call EnableRTS asynchronously this will enable RTS in the middle of the following write call allowing it to succeed
208                 // before the timeout is reached
209                 t.Start();
210                 TCSupport.WaitForTaskToStart(t);
211                 try
212                 {
213                     stream.Write(new byte[s_DEFAULT_WRITE_BYTE_ARRAY_SIZE], 0, s_DEFAULT_WRITE_BYTE_ARRAY_SIZE);
214                 }
215                 catch (TimeoutException)
216                 {
217                 }
218 
219                 asyncEnableRts.Stop();
220 
221                 TCSupport.WaitForTaskCompletion(t);
222 
223                 // Make sure there is no bytes in the buffer so the next call to write will timeout
224                 com1.DiscardInBuffer();
225 
226                 VerifyTimeout(Write_byte_int_int, stream);
227             }
228         }
229 
230         [Trait(XunitConstants.Category, XunitConstants.IgnoreForCI)]  // Timing-sensitive
231         [ConditionalFact(nameof(HasOneSerialPort), nameof(HasSingleByteTransmitBlocking))]
SuccessiveWriteTimeoutNoData_WriteByte()232         public void SuccessiveWriteTimeoutNoData_WriteByte()
233         {
234             using (SerialPort com = new SerialPort(TCSupport.LocalMachineSerialInfo.FirstAvailablePortName))
235             {
236                 com.Open();
237                 com.Handshake = Handshake.RequestToSend;
238                 Stream stream = com.BaseStream;
239                 stream.WriteTimeout = 850;
240 
241                 Debug.WriteLine("Verifying WriteTimeout={0} with successive call to WriteByte() and no data",
242                     stream.WriteTimeout);
243 
244                 Assert.Throws<TimeoutException>(() => stream.WriteByte(DEFAULT_WRITE_BYTE));
245 
246                 VerifyTimeout(WriteByte, stream);
247             }
248         }
249 
250         [ConditionalFact(nameof(HasNullModem), nameof(HasHardwareFlowControl))]
SuccessiveWriteTimeoutSomeData_WriteByte()251         public void SuccessiveWriteTimeoutSomeData_WriteByte()
252         {
253             using (var com1 = new SerialPort(TCSupport.LocalMachineSerialInfo.FirstAvailablePortName))
254             {
255                 var asyncEnableRts = new AsyncEnableRts();
256                 var t = new Task(asyncEnableRts.EnableRTS);
257 
258                 com1.Open();
259                 com1.Handshake = Handshake.RequestToSend;
260                 Stream stream = com1.BaseStream;
261                 stream.WriteTimeout = SUCCESSIVE_WriteTimeout_SOMEDATA;
262 
263                 Debug.WriteLine(
264                     "Verifying WriteTimeout={0} with successive call to WriteByte() and some data being received in the first call",
265                     stream.WriteTimeout);
266 
267                 // Call EnableRTS asynchronously this will enable RTS in the middle of the following write call allowing it to succeed
268                 // before the timeout is reached
269                 t.Start();
270                 TCSupport.WaitForTaskToStart(t);
271                 try
272                 {
273                     stream.WriteByte(DEFAULT_WRITE_BYTE);
274                 }
275                 catch (TimeoutException)
276                 {
277                 }
278 
279                 asyncEnableRts.Stop();
280 
281                 TCSupport.WaitForTaskCompletion(t);
282 
283                 // Make sure there is no bytes in the buffer so the next call to write will timeout
284                 com1.DiscardInBuffer();
285 
286                 VerifyTimeout(WriteByte, stream);
287             }
288         }
289 
290         [ConditionalFact(nameof(HasOneSerialPort), nameof(HasHardwareFlowControl))]
WriteTimeout_0_Write_byte_int_int()291         public void WriteTimeout_0_Write_byte_int_int()
292         {
293             Debug.WriteLine("Verifying 0 WriteTimeout with Write(byte[] buffer, int offset, int count)");
294             Verify0Timeout(Write_byte_int_int);
295         }
296 
297         [ConditionalFact(nameof(HasOneSerialPort), nameof(HasHardwareFlowControl))]
WriteTimeout_0_WriteByte()298         public void WriteTimeout_0_WriteByte()
299         {
300             Debug.WriteLine("Verifying 0 WriteTimeout with WriteByte()");
301             Verify0Timeout(WriteByte);
302         }
303 
304         [ConditionalFact(nameof(HasOneSerialPort), nameof(HasHardwareFlowControl))]
WriteTimeout_LargeWrite()305         public void WriteTimeout_LargeWrite()
306         {
307             using (SerialPort com = new SerialPort(TCSupport.LocalMachineSerialInfo.FirstAvailablePortName))
308             {
309                 com.Open();
310                 com.BaudRate = LARGEWRITE_BAUDRATE;
311                 com.Handshake = Handshake.RequestToSend;
312                 Stream stream = com.BaseStream;
313                 stream.WriteTimeout = LARGEWRITE_TIMEOUT;
314 
315                 Debug.WriteLine("Verifying {0} WriteTimeout with Write(byte[] , int, int) and writing {1} bytes", LARGEWRITE_TIMEOUT, DEFAULT_WRITE_BYTE_LARGE_ARRAY_SIZE);
316 
317                 VerifyTimeout(Write_byte_int_int_Large, stream);
318             }
319         }
320 
321         private class AsyncEnableRts
322         {
323             private bool _stop;
324 
EnableRTS()325             public void EnableRTS()
326             {
327                 lock (this)
328                 {
329                     using (var com2 = new SerialPort(TCSupport.LocalMachineSerialInfo.SecondAvailablePortName))
330                     {
331                         int sleepPeriod = SUCCESSIVE_WriteTimeout_SOMEDATA / 2;
332 
333                         // Sleep some random period with of a maximum duration of half the largest possible timeout value for a write method on COM1
334                         Thread.Sleep(sleepPeriod);
335 
336                         com2.Open();
337                         com2.RtsEnable = true;
338 
339                         while (!_stop)
340                             Monitor.Wait(this);
341 
342                         com2.RtsEnable = false;
343                     }
344                 }
345             }
346 
Stop()347             public void Stop()
348             {
349                 lock (this)
350                 {
351                     _stop = true;
352                     Monitor.Pulse(this);
353                 }
354             }
355         }
356         #endregion
357 
358         #region Verification for Test Cases
359 
VerifyDefaultTimeout(WriteMethodDelegate writeMethod)360         private void VerifyDefaultTimeout(WriteMethodDelegate writeMethod)
361         {
362             using (SerialPort com1 = TCSupport.InitFirstSerialPort())
363             {
364                 Debug.WriteLine("Serial port being used : " + com1.PortName);
365 
366 
367                 com1.Open();
368                 com1.Handshake = Handshake.RequestToSend;
369                 com1.BaseStream.ReadTimeout = 1;
370 
371                 Assert.Equal(-1, com1.BaseStream.WriteTimeout);
372 
373                 VerifyLongTimeout(writeMethod, com1);
374             }
375         }
376 
VerifyLongTimeout(WriteMethodDelegate writeMethod, int writeTimeout)377         private void VerifyLongTimeout(WriteMethodDelegate writeMethod, int writeTimeout)
378         {
379             using (SerialPort com1 = TCSupport.InitFirstSerialPort())
380             {
381                 com1.Open();
382                 com1.Handshake = Handshake.RequestToSend;
383                 com1.BaseStream.ReadTimeout = 1;
384                 com1.BaseStream.WriteTimeout = 1;
385 
386                 com1.BaseStream.WriteTimeout = writeTimeout;
387 
388                 Assert.Equal(writeTimeout, com1.BaseStream.WriteTimeout);
389 
390                 VerifyLongTimeout(writeMethod, com1);
391             }
392         }
393 
VerifyLongTimeout(WriteMethodDelegate writeMethod, SerialPort com1)394         private void VerifyLongTimeout(WriteMethodDelegate writeMethod, SerialPort com1)
395         {
396             var writeThread = new WriteDelegateThread(com1.BaseStream, writeMethod);
397             var t = new Task(writeThread.CallWrite);
398 
399             t.Start();
400             Thread.Sleep(DEFAULT_WAIT_LONG_TIMEOUT);
401             Assert.False(t.IsCompleted,
402                 string.Format("Err_17071ahpa!!! {0} terminated with a long timeout of {1}ms", writeMethod.Method.Name, com1.BaseStream.WriteTimeout));
403             com1.Handshake = Handshake.None;
404             TCSupport.WaitForTaskCompletion(t);
405         }
406 
VerifyTimeout(WriteMethodDelegate writeMethod, int WriteTimeout)407         private void VerifyTimeout(WriteMethodDelegate writeMethod, int WriteTimeout)
408         {
409             using (var com1 = new SerialPort(TCSupport.LocalMachineSerialInfo.FirstAvailablePortName))
410 
411             {
412                 com1.Open();
413                 com1.Handshake = Handshake.RequestToSend;
414                 com1.BaseStream.ReadTimeout = 1;
415                 com1.BaseStream.WriteTimeout = 1;
416 
417                 com1.BaseStream.WriteTimeout = WriteTimeout;
418 
419                 Assert.Equal(WriteTimeout, com1.BaseStream.WriteTimeout);
420 
421                 VerifyTimeout(writeMethod, com1.BaseStream);
422             }
423         }
424 
VerifyTimeout(WriteMethodDelegate writeMethod, Stream stream)425         private void VerifyTimeout(WriteMethodDelegate writeMethod, Stream stream)
426         {
427             var timer = new Stopwatch();
428             int expectedTime = stream.WriteTimeout;
429             int actualTime;
430             double percentageDifference;
431 
432 
433             // Warmup the write method. When called for the first time the write method seems to take much longer then subsequent calls
434             timer.Start();
435             try
436             {
437                 writeMethod(stream);
438             }
439             catch (TimeoutException) { }
440             timer.Stop();
441             actualTime = (int)timer.ElapsedMilliseconds;
442             percentageDifference = Math.Abs((expectedTime - actualTime) / (double)expectedTime);
443 
444             // Verify that the percentage difference between the expected and actual timeout is less then maxPercentageDifference
445             Assert.True(percentageDifference <= MAX_ACCEPTABLE_WARMUP_PERCENTAGE_DIFFERENCE,
446                 string.Format("Err_88558amuph!!!: The write method timedout in {0} expected {1} percentage difference: {2} when called for the first time",
447                     actualTime, expectedTime, percentageDifference));
448 
449             actualTime = 0;
450             timer.Reset();
451 
452             // Perform the actual test verifying that the write method times out in approximately WriteTimeout milliseconds
453             Thread.CurrentThread.Priority = ThreadPriority.Highest;
454 
455             for (var i = 0; i < NUM_TRYS; i++)
456             {
457                 timer.Start();
458                 try { writeMethod(stream); }
459                 catch (TimeoutException) { }
460                 timer.Stop();
461 
462                 actualTime += (int)timer.ElapsedMilliseconds;
463                 timer.Reset();
464             }
465 
466             Thread.CurrentThread.Priority = ThreadPriority.Normal;
467             actualTime /= NUM_TRYS;
468             percentageDifference = Math.Abs((expectedTime - actualTime) / (double)expectedTime);
469 
470             // Verify that the percentage difference between the expected and actual timeout is less then maxPercentageDifference
471             Assert.True(percentageDifference <= MAX_ACCEPTABLE_PERCENTAGE_DIFFERENCE,
472                 string.Format("Err_56485ahpbz!!!: The write method timedout in {0} expected {1} percentage difference: {2}", actualTime, expectedTime, percentageDifference));
473         }
474 
Verify0Timeout(WriteMethodDelegate writeMethod)475         private void Verify0Timeout(WriteMethodDelegate writeMethod)
476         {
477             using (var com1 = new SerialPort(TCSupport.LocalMachineSerialInfo.FirstAvailablePortName))
478 
479             {
480                 com1.Open();
481                 com1.Handshake = Handshake.RequestToSend;
482                 com1.BaseStream.ReadTimeout = 1;
483                 com1.BaseStream.WriteTimeout = 1;
484 
485                 com1.BaseStream.WriteTimeout = 1;
486 
487                 Assert.Equal(1, com1.BaseStream.WriteTimeout);
488 
489                 Verify0Timeout(writeMethod, com1.BaseStream);
490             }
491         }
492 
Verify0Timeout(WriteMethodDelegate writeMethod, Stream stream)493         private void Verify0Timeout(WriteMethodDelegate writeMethod, Stream stream)
494         {
495             var timer = new Stopwatch();
496             int actualTime;
497 
498             // Warmup the write method. When called for the first time the write method seems to take much longer then subsequent calls
499             timer.Start();
500             try
501             {
502                 writeMethod(stream);
503             }
504             catch (TimeoutException) { }
505             timer.Stop();
506             actualTime = (int)timer.ElapsedMilliseconds;
507 
508             // Verify that the time the method took to timeout is less then the maximum acceptable time
509             Assert.True(actualTime <= MAX_ACCEPTABLE_WARMUP_ZERO_TIMEOUT,
510                 string.Format("Err_277a0ahpsb!!!: With a timeout of 0 the write method timedout in {0} expected something less then {1} when called for the first time",
511                     actualTime, MAX_ACCEPTABLE_WARMUP_ZERO_TIMEOUT));
512 
513             actualTime = 0;
514             timer.Reset();
515 
516             // Perform the actual test verifying that the write method times out in approximately WriteTimeout milliseconds
517             Thread.CurrentThread.Priority = ThreadPriority.Highest;
518 
519             for (var i = 0; i < NUM_TRYS; i++)
520             {
521                 timer.Start();
522                 try { writeMethod(stream); }
523                 catch (TimeoutException) { }
524                 timer.Stop();
525 
526                 actualTime += (int)timer.ElapsedMilliseconds;
527                 timer.Reset();
528             }
529 
530             Thread.CurrentThread.Priority = ThreadPriority.Normal;
531             actualTime /= NUM_TRYS;
532 
533             // Verify that the time the method took to timeout is less then the maximum acceptable time
534             Assert.True(actualTime <= MAX_ACCEPTABLE_ZERO_TIMEOUT,
535                 string.Format("Err_112389ahbp!!!: With a timeout of 0 the write method timedout in {0} expected something less then {1}",
536                     actualTime, MAX_ACCEPTABLE_ZERO_TIMEOUT));
537         }
538 
VerifyException(int readTimeout, Type expectedException)539         private void VerifyException(int readTimeout, Type expectedException)
540         {
541             VerifyException(readTimeout, expectedException, expectedException);
542         }
543 
VerifyException(int readTimeout, Type expectedExceptionAfterOpen, Type expectedExceptionAfterClose)544         private void VerifyException(int readTimeout, Type expectedExceptionAfterOpen, Type expectedExceptionAfterClose)
545         {
546             using (SerialPort com = new SerialPort(TCSupport.LocalMachineSerialInfo.FirstAvailablePortName))
547             {
548                 com.Open();
549                 Stream stream = com.BaseStream;
550 
551                 VerifyException(stream, readTimeout, expectedExceptionAfterOpen);
552 
553                 com.Close();
554 
555                 VerifyException(stream, readTimeout, expectedExceptionAfterClose);
556             }
557         }
558 
VerifyException(Stream stream, int WriteTimeout, Type expectedException)559         private void VerifyException(Stream stream, int WriteTimeout, Type expectedException)
560         {
561             int origWriteTimeout = stream.WriteTimeout;
562 
563             if (expectedException == null)
564             {
565                 stream.WriteTimeout = WriteTimeout;
566                 Assert.Equal(WriteTimeout, stream.WriteTimeout);
567             }
568             else
569             {
570                 Assert.Throws(expectedException, () => stream.WriteTimeout = WriteTimeout);
571                 Assert.Equal(origWriteTimeout, stream.WriteTimeout);
572             }
573         }
574 
Write_byte_int_int(Stream stream)575         private void Write_byte_int_int(Stream stream)
576         {
577             stream.Write(new byte[s_DEFAULT_WRITE_BYTE_ARRAY_SIZE], 0, s_DEFAULT_WRITE_BYTE_ARRAY_SIZE);
578         }
579 
Write_byte_int_int_Large(Stream stream)580         private void Write_byte_int_int_Large(Stream stream)
581         {
582             stream.Write(new byte[DEFAULT_WRITE_BYTE_LARGE_ARRAY_SIZE], 0, DEFAULT_WRITE_BYTE_LARGE_ARRAY_SIZE);
583         }
584 
WriteByte(Stream stream)585         private void WriteByte(Stream stream)
586         {
587             stream.WriteByte(DEFAULT_WRITE_BYTE);
588         }
589 
590         private class WriteDelegateThread
591         {
WriteDelegateThread(Stream stream, WriteMethodDelegate writeMethod)592             public WriteDelegateThread(Stream stream, WriteMethodDelegate writeMethod)
593             {
594                 _stream = stream;
595                 _writeMethod = writeMethod;
596             }
597 
CallWrite()598             public void CallWrite()
599             {
600                 _writeMethod(_stream);
601             }
602 
603             private readonly WriteMethodDelegate _writeMethod;
604             private readonly Stream _stream;
605         }
606 
607         #endregion
608     }
609 }
610