ConnectionCloseTest.cs 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. using Microsoft.VisualStudio.TestTools.UnitTesting;
  2. using System;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Net.Sockets;
  6. using System.Reflection;
  7. using System.Threading;
  8. using System.Threading.Tasks;
  9. namespace S7.Net.UnitTest
  10. {
  11. /// <summary>
  12. /// Test stream which only gives 1 byte per read.
  13. /// </summary>
  14. class TestStreamConnectionClose : Stream
  15. {
  16. private readonly CancellationTokenSource _cancellationTokenSource;
  17. public TestStreamConnectionClose(CancellationTokenSource cancellationTokenSource)
  18. {
  19. _cancellationTokenSource = cancellationTokenSource;
  20. }
  21. public override bool CanRead => false;
  22. public override bool CanSeek => throw new NotImplementedException();
  23. public override bool CanWrite => true;
  24. public override long Length => throw new NotImplementedException();
  25. public override long Position { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
  26. public override void Flush()
  27. {
  28. throw new NotImplementedException();
  29. }
  30. public override int Read(byte[] buffer, int offset, int count)
  31. {
  32. throw new NotImplementedException();
  33. }
  34. public override long Seek(long offset, SeekOrigin origin)
  35. {
  36. throw new NotImplementedException();
  37. }
  38. public override void SetLength(long value)
  39. {
  40. throw new NotImplementedException();
  41. }
  42. public override void Write(byte[] buffer, int offset, int count)
  43. {
  44. _cancellationTokenSource.Cancel();
  45. }
  46. }
  47. /// <summary>
  48. /// These tests are intended to test <see cref="StreamExtensions"/> functions and other stream-related special cases.
  49. /// </summary>
  50. [TestClass]
  51. public class ConnectionCloseTest
  52. {
  53. const short TestServerPort = 31122;
  54. const string TestServerIp = "127.0.0.1";
  55. [TestMethod]
  56. public async Task Test_CancellationDuringTransmission()
  57. {
  58. var plc = new Plc(CpuType.S7300, TestServerIp, TestServerPort, 0, 2);
  59. // Set up a shared cancellation source so we can let the stream
  60. // initiate cancel after some data has been written to it.
  61. var cancellationSource = new CancellationTokenSource();
  62. var cancellationToken = cancellationSource.Token;
  63. var stream = new TestStreamConnectionClose(cancellationSource);
  64. var requestData = new byte[100]; // empty data, it does not matter what is in there
  65. // Set up access to private method and field
  66. var dynMethod = plc.GetType().GetMethod("NoLockRequestTpduAsync",
  67. BindingFlags.NonPublic | BindingFlags.Instance);
  68. if (dynMethod == null)
  69. {
  70. throw new NullReferenceException("Could not find method 'NoLockRequestTpduAsync' on Plc object.");
  71. }
  72. var tcpClientField = plc.GetType().GetField("tcpClient", BindingFlags.NonPublic | BindingFlags.Instance);
  73. if (tcpClientField == null)
  74. {
  75. throw new NullReferenceException("Could not find field 'tcpClient' on Plc object.");
  76. }
  77. // Set a value to tcpClient field so we can later ensure that it has been closed.
  78. tcpClientField.SetValue(plc, new TcpClient());
  79. var tcpClientValue = tcpClientField.GetValue(plc);
  80. Assert.IsNotNull(tcpClientValue);
  81. try
  82. {
  83. var result = (Task<COTP.TPDU>) dynMethod.Invoke(plc, new object[] { stream, requestData, cancellationToken });
  84. await result;
  85. }
  86. catch (OperationCanceledException)
  87. {
  88. Console.WriteLine("Task was cancelled as expected.");
  89. // Ensure that the plc connection was closed since the task was cancelled
  90. // after data has been sent through the network. We expect that the tcpClient
  91. // object was set to NULL
  92. var tcpClientValueAfter = tcpClientField.GetValue(plc);
  93. Assert.IsNull(tcpClientValueAfter);
  94. return;
  95. }
  96. catch (Exception e)
  97. {
  98. Assert.Fail($"Wrong exception type received. Expected {typeof(OperationCanceledException)}, received {e.GetType()}.");
  99. }
  100. // Ensure test fails if cancellation did not occur.
  101. Assert.Fail("Task was not cancelled as expected.");
  102. }
  103. [TestMethod]
  104. public async Task Test_CancellationBeforeTransmission()
  105. {
  106. var plc = new Plc(CpuType.S7300, TestServerIp, TestServerPort, 0, 2);
  107. // Set up a cancellation source
  108. var cancellationSource = new CancellationTokenSource();
  109. var cancellationToken = cancellationSource.Token;
  110. var stream = new TestStreamConnectionClose(cancellationSource);
  111. var requestData = new byte[100]; // empty data, it does not matter what is in there
  112. // Set up access to private method and field
  113. var dynMethod = plc.GetType().GetMethod("NoLockRequestTpduAsync",
  114. BindingFlags.NonPublic | BindingFlags.Instance);
  115. if (dynMethod == null)
  116. {
  117. throw new NullReferenceException("Could not find method 'NoLockRequestTpduAsync' on Plc object.");
  118. }
  119. var tcpClientField = plc.GetType().GetField("tcpClient", BindingFlags.NonPublic | BindingFlags.Instance);
  120. if (tcpClientField == null)
  121. {
  122. throw new NullReferenceException("Could not find field 'tcpClient' on Plc object.");
  123. }
  124. // Set a value to tcpClient field so we can later ensure that it has been closed.
  125. tcpClientField.SetValue(plc, new TcpClient());
  126. var tcpClientValue = tcpClientField.GetValue(plc);
  127. Assert.IsNotNull(tcpClientValue);
  128. try
  129. {
  130. // cancel the task before we start transmitting data
  131. cancellationSource.Cancel();
  132. var result = (Task<COTP.TPDU>)dynMethod.Invoke(plc, new object[] { stream, requestData, cancellationToken });
  133. await result;
  134. }
  135. catch (OperationCanceledException)
  136. {
  137. Console.WriteLine("Task was cancelled as expected.");
  138. // Ensure that the plc connection was not closed, since we cancelled the task before
  139. // sending data through the network. We expect that the tcpClient
  140. // object was NOT set to NULL
  141. var tcpClientValueAfter = tcpClientField.GetValue(plc);
  142. Assert.IsNotNull(tcpClientValueAfter);
  143. return;
  144. }
  145. catch (Exception e)
  146. {
  147. Assert.Fail($"Wrong exception type received. Expected {typeof(OperationCanceledException)}, received {e.GetType()}.");
  148. }
  149. // Ensure test fails if cancellation did not occur.
  150. Assert.Fail("Task was not cancelled as expected.");
  151. }
  152. }
  153. }