check_encapsulation_inactivity_tests.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. /*******************************************************************************
  2. * Copyright (c) 2025, Martin Melik Merkumians
  3. * All rights reserved.
  4. *
  5. * Martin Melik Merkumians < Initial creation >
  6. ******************************************************************************/
  7. #include <CppUTest/TestHarness.h>
  8. #include <CppUTestExt/MockSupport.h>
  9. #include <stdint.h>
  10. #include <string.h>
  11. extern "C" {
  12. #include "core/typedefs.h"
  13. #include "enet_encap/encap.h"
  14. #include "ports/socket_timer.h"
  15. }
  16. /* Forward declarations of external variables */
  17. extern SocketTimer g_timestamps[OPENER_NUMBER_OF_SUPPORTED_SESSIONS];
  18. extern MilliSeconds g_actual_time;
  19. /* Function being tested */
  20. extern void CheckEncapsulationInactivity(int socket_handle);
  21. /* External structure for network options */
  22. struct TcpIpNetworkOptions {
  23. uint32_t encapsulation_inactivity_timeout;
  24. };
  25. extern TcpIpNetworkOptions g_tcpip;
  26. TEST_GROUP(CheckEncapsulationInactivity){ void setup(){ mock().clear();
  27. /* Initialize socket timers */
  28. SocketTimerArrayInitialize(g_timestamps, OPENER_NUMBER_OF_SUPPORTED_SESSIONS);
  29. /* Reset global time */
  30. g_actual_time = 0;
  31. }
  32. void teardown() {
  33. mock().clear();
  34. }
  35. }
  36. ;
  37. /**
  38. * Test 1: Encapsulation inactivity timeout disabled (timeout = 0)
  39. * Expected: No session/connection should be closed
  40. */
  41. TEST(CheckEncapsulationInactivity, TimeoutDisabledDoesNothing) {
  42. g_tcpip.encapsulation_inactivity_timeout = 0;
  43. int test_socket = 5;
  44. /* Set up a socket timer */
  45. SocketTimerSetSocket(&g_timestamps[0], test_socket);
  46. SocketTimerSetLastUpdate(&g_timestamps[0], 100);
  47. g_actual_time = 500;
  48. /* Call the function - should do nothing since timeout is disabled */
  49. CheckEncapsulationInactivity(test_socket);
  50. }
  51. /**
  52. * Test 2: Encapsulation inactivity timeout enabled, but time delta below
  53. * threshold Expected: No session/connection should be closed
  54. */
  55. TEST(CheckEncapsulationInactivity, InactivityBelowThresholdDoesNothing) {
  56. g_tcpip.encapsulation_inactivity_timeout = 10; /* 10 seconds */
  57. int test_socket = 5;
  58. /* Set up a socket timer with recent update */
  59. SocketTimerSetSocket(&g_timestamps[0], test_socket);
  60. SocketTimerSetLastUpdate(&g_timestamps[0], 1000);
  61. g_actual_time = 1500; /* Only 500ms elapsed, threshold is 10000ms */
  62. CheckEncapsulationInactivity(test_socket);
  63. }
  64. /**
  65. * Test 3: Encapsulation inactivity timeout exceeded
  66. * Expected: CloseClass3ConnectionBasedOnSession, CloseTcpSocket, and
  67. * RemoveSession should all be called in sequence
  68. */
  69. TEST(CheckEncapsulationInactivity,
  70. InactivityThresholdExceededClosesConnection) {
  71. g_tcpip.encapsulation_inactivity_timeout = 5; /* 5 seconds = 5000ms */
  72. int test_socket = 3;
  73. CipSessionHandle expected_session = 2;
  74. /* Set up a socket timer with old update time */
  75. SocketTimerSetSocket(&g_timestamps[0], test_socket);
  76. SocketTimerSetLastUpdate(&g_timestamps[0], 1000);
  77. g_actual_time = 7000; /* 6000ms elapsed, exceeds 5000ms threshold */
  78. /* Set up mock expectations in order */
  79. mock()
  80. .expectOneCall("GetSessionFromSocket")
  81. .withIntParameter("socket_handle", test_socket)
  82. .andReturnValue((int)expected_session);
  83. mock()
  84. .expectOneCall("CloseClass3ConnectionBasedOnSession")
  85. .withIntParameter("encapsulation_session_handle", (int)expected_session);
  86. mock()
  87. .expectOneCall("CloseTcpSocket")
  88. .withIntParameter("socket_handle", test_socket);
  89. mock().expectOneCall("RemoveSession").withIntParameter("socket", test_socket);
  90. CheckEncapsulationInactivity(test_socket);
  91. }
  92. /**
  93. * Test 4: Socket with no timer found
  94. * Expected: No cleanup should be performed if socket timer is NULL
  95. */
  96. TEST(CheckEncapsulationInactivity, InvalidSocketTimerDoesNothing) {
  97. g_tcpip.encapsulation_inactivity_timeout = 5;
  98. int invalid_socket = 999; /* Socket that doesn't have a timer */
  99. g_actual_time = 10000;
  100. CheckEncapsulationInactivity(invalid_socket);
  101. }
  102. /**
  103. * Test 5: Exact timeout boundary condition
  104. * Expected: Connection should be closed when delta equals timeout threshold
  105. */
  106. TEST(CheckEncapsulationInactivity, ExactTimeoutBoundaryClosesConnection) {
  107. g_tcpip.encapsulation_inactivity_timeout = 3; /* 3 seconds = 3000ms */
  108. int test_socket = 7;
  109. CipSessionHandle expected_session = 4;
  110. SocketTimerSetSocket(&g_timestamps[0], test_socket);
  111. SocketTimerSetLastUpdate(&g_timestamps[0], 2000);
  112. g_actual_time = 5000; /* Exactly 3000ms elapsed */
  113. /* Set up mock expectations */
  114. mock()
  115. .expectOneCall("GetSessionFromSocket")
  116. .withIntParameter("socket_handle", test_socket)
  117. .andReturnValue((int)expected_session);
  118. mock()
  119. .expectOneCall("CloseClass3ConnectionBasedOnSession")
  120. .withIntParameter("encapsulation_session_handle", (int)expected_session);
  121. mock()
  122. .expectOneCall("CloseTcpSocket")
  123. .withIntParameter("socket_handle", test_socket);
  124. mock().expectOneCall("RemoveSession").withIntParameter("socket", test_socket);
  125. CheckEncapsulationInactivity(test_socket);
  126. }
  127. /**
  128. * Test 6: Just below timeout boundary
  129. * Expected: Connection should NOT be closed when delta is one less than
  130. * threshold
  131. */
  132. TEST(CheckEncapsulationInactivity, JustBelowTimeoutBoundaryDoesNotClose) {
  133. g_tcpip.encapsulation_inactivity_timeout = 3; /* 3 seconds = 3000ms */
  134. int test_socket = 8;
  135. SocketTimerSetSocket(&g_timestamps[0], test_socket);
  136. SocketTimerSetLastUpdate(&g_timestamps[0], 2000);
  137. g_actual_time = 4999; /* 2999ms elapsed, just below 3000ms threshold */
  138. CheckEncapsulationInactivity(test_socket);
  139. }
  140. /**
  141. * Test 7: Multiple socket timers - verify independent checking
  142. * Expected: Each socket should be evaluated independently
  143. */
  144. TEST(CheckEncapsulationInactivity, MultipleSocketsIndependentEvaluation) {
  145. g_tcpip.encapsulation_inactivity_timeout = 2; /* 2 seconds = 2000ms */
  146. /* Set up first socket timer (inactive) */
  147. SocketTimerSetSocket(&g_timestamps[0], 10);
  148. SocketTimerSetLastUpdate(&g_timestamps[0], 100);
  149. /* Set up second socket timer (active) */
  150. SocketTimerSetSocket(&g_timestamps[1], 20);
  151. SocketTimerSetLastUpdate(&g_timestamps[1], 2500);
  152. g_actual_time = 3000;
  153. /* First socket inactive check: 3000 - 100 = 2900ms > 2000ms */
  154. mock()
  155. .expectOneCall("GetSessionFromSocket")
  156. .withIntParameter("socket_handle", 10)
  157. .andReturnValue(1);
  158. mock()
  159. .expectOneCall("CloseClass3ConnectionBasedOnSession")
  160. .withIntParameter("encapsulation_session_handle", 1);
  161. mock().expectOneCall("CloseTcpSocket").withIntParameter("socket_handle", 10);
  162. mock().expectOneCall("RemoveSession").withIntParameter("socket", 10);
  163. CheckEncapsulationInactivity(10);
  164. /* Clear mocks for next check */
  165. mock().clear();
  166. /* Second socket is still active: 3000 - 2500 = 500ms < 2000ms */
  167. CheckEncapsulationInactivity(20);
  168. }
  169. /**
  170. * Test 8: Zero timeout disables feature
  171. * Expected: Feature disabled when timeout is 0
  172. */
  173. TEST(CheckEncapsulationInactivity, ZeroTimeoutDisablesFeature) {
  174. g_tcpip.encapsulation_inactivity_timeout = 0;
  175. int test_socket = 11;
  176. SocketTimerSetSocket(&g_timestamps[0], test_socket);
  177. SocketTimerSetLastUpdate(&g_timestamps[0], 0);
  178. g_actual_time = 100000;
  179. CheckEncapsulationInactivity(test_socket);
  180. }
  181. /**
  182. * Test 9: Very large timeout (1 hour)
  183. * Expected: Connection stays open even with significant elapsed time
  184. */
  185. TEST(CheckEncapsulationInactivity, LargeTimeoutValue) {
  186. g_tcpip.encapsulation_inactivity_timeout = 3600; /* 1 hour */
  187. int test_socket = 12;
  188. SocketTimerSetSocket(&g_timestamps[0], test_socket);
  189. SocketTimerSetLastUpdate(&g_timestamps[0], 1000);
  190. g_actual_time = 1800000; /* 30 minutes - still below 1 hour threshold */
  191. CheckEncapsulationInactivity(test_socket);
  192. }
  193. /**
  194. * Test 10: One second timeout (minimum practical value)
  195. * Expected: Timeout closes connection after 1 second of inactivity
  196. */
  197. TEST(CheckEncapsulationInactivity, MinimumOneSecondTimeout) {
  198. g_tcpip.encapsulation_inactivity_timeout = 1; /* 1 second = 1000ms */
  199. int test_socket = 13;
  200. CipSessionHandle expected_session = 6;
  201. SocketTimerSetSocket(&g_timestamps[0], test_socket);
  202. SocketTimerSetLastUpdate(&g_timestamps[0], 500);
  203. g_actual_time = 2000; /* 1500ms elapsed */
  204. mock()
  205. .expectOneCall("GetSessionFromSocket")
  206. .withIntParameter("socket_handle", test_socket)
  207. .andReturnValue((int)expected_session);
  208. mock()
  209. .expectOneCall("CloseClass3ConnectionBasedOnSession")
  210. .withIntParameter("encapsulation_session_handle", (int)expected_session);
  211. mock()
  212. .expectOneCall("CloseTcpSocket")
  213. .withIntParameter("socket_handle", test_socket);
  214. mock().expectOneCall("RemoveSession").withIntParameter("socket", test_socket);
  215. CheckEncapsulationInactivity(test_socket);
  216. }
  217. /**
  218. * Test 11: Socket at array boundary
  219. * Expected: Works correctly at edge of timer array
  220. */
  221. TEST(CheckEncapsulationInactivity, SocketAtArrayBoundary) {
  222. g_tcpip.encapsulation_inactivity_timeout = 2;
  223. int last_index = OPENER_NUMBER_OF_SUPPORTED_SESSIONS - 1;
  224. int test_socket = 100 + last_index;
  225. SocketTimerSetSocket(&g_timestamps[last_index], test_socket);
  226. SocketTimerSetLastUpdate(&g_timestamps[last_index], 0);
  227. g_actual_time = 3000; /* 3000ms > 2000ms threshold */
  228. mock()
  229. .expectOneCall("GetSessionFromSocket")
  230. .withIntParameter("socket_handle", test_socket)
  231. .andReturnValue(7);
  232. mock()
  233. .expectOneCall("CloseClass3ConnectionBasedOnSession")
  234. .withIntParameter("encapsulation_session_handle", 7);
  235. mock()
  236. .expectOneCall("CloseTcpSocket")
  237. .withIntParameter("socket_handle", test_socket);
  238. mock().expectOneCall("RemoveSession").withIntParameter("socket", test_socket);
  239. CheckEncapsulationInactivity(test_socket);
  240. }
  241. /**
  242. * Test 12: Off-by-one boundary test
  243. * Expected: Connection closed when diff > threshold, not closed when diff <
  244. * threshold
  245. */
  246. TEST(CheckEncapsulationInactivity, OffByOneBoundaryTest) {
  247. g_tcpip.encapsulation_inactivity_timeout = 5; /* 5000ms */
  248. int test_socket = 15;
  249. SocketTimerSetSocket(&g_timestamps[0], test_socket);
  250. SocketTimerSetLastUpdate(&g_timestamps[0], 0);
  251. /* Test just below: 4999ms should not close */
  252. g_actual_time = 4999;
  253. CheckEncapsulationInactivity(test_socket);
  254. /* Advance time - now 5001ms should close */
  255. g_actual_time = 5001;
  256. mock()
  257. .expectOneCall("GetSessionFromSocket")
  258. .withIntParameter("socket_handle", test_socket)
  259. .andReturnValue(9);
  260. mock()
  261. .expectOneCall("CloseClass3ConnectionBasedOnSession")
  262. .withIntParameter("encapsulation_session_handle", 9);
  263. mock()
  264. .expectOneCall("CloseTcpSocket")
  265. .withIntParameter("socket_handle", test_socket);
  266. mock().expectOneCall("RemoveSession").withIntParameter("socket", test_socket);
  267. CheckEncapsulationInactivity(test_socket);
  268. }