IdTestTcpClient.pas 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. unit IdTestTcpClient;
  2. //todo add test to TIdCustomTCPServer to ensure OnDisconnect is called
  3. //when connection is closed, also when exception happens, eg graceful close
  4. {
  5. observations:
  6. IdDefTimeout = 0;
  7. IdTimeoutDefault = -1;
  8. IdTimeoutInfinite = -2;
  9. IdTimeoutDefault when passed as a default param means 'use Self.ReadTimeout' ?
  10. From help file:
  11. IdDefTimeout
  12. Default timeout value when establishing socket-based connections.
  13. IdDefTimeout is an Integer constant that represents the value to
  14. use as the default timeout value for socket-based connection operations.
  15. used in
  16. TIdIOHandlerStack.ReadTimeout,
  17. TIdFTP.TransferTimeout (but actually sets FDataChannel.IOHandler.ReadTimeout)
  18. property TIdIOHandlerStack.ReadTimeout: Integer;
  19. Indicates the milliseconds to wait for a readable IOHandler connection.
  20. If ReadTimeout contains the value 0 (zero), the value in IdTimeoutInfinite
  21. is used as the timeout interval.
  22. TIdIOHandlerStack
  23. published
  24. property ReadTimeout default IdDefTimeout;
  25. but actually initialised to:
  26. FReadTimeOut := IdTimeoutDefault;
  27. It can seem more intuitive that timeout=0 means immediate timeout?
  28. or assume it means 'no timeout' eg infinite
  29. currently have 2 options for infinite, none for immediate
  30. how to test that a client is actually using infinite timeout?
  31. }
  32. interface
  33. uses
  34. IdTest,
  35. IdStack,
  36. IdComponent,
  37. IdGlobal,
  38. IdExceptionCore,
  39. IdTcpClient,
  40. IdObjs,
  41. IdSys,
  42. IdThreadSafe,
  43. IdTcpServer,
  44. IdContext;
  45. type
  46. TIdTestTcpClient = class(TIdTest)
  47. private
  48. FList:TIdThreadSafeStringList;
  49. FServerShouldEcho: Boolean;
  50. procedure DoServerExecute(AContext: TIdContext);
  51. procedure CallbackConnect(Sender:TObject);
  52. procedure CallbackDisconnect(Sender:TObject);
  53. procedure CallbackStatus(ASender: TObject; const AStatus: TIdStatus; const AStatusText: string);
  54. published
  55. procedure TestTimeouts;
  56. procedure TestConnectErrors;
  57. procedure TestEvent;
  58. end;
  59. implementation
  60. procedure TIdTestTcpClient.CallbackConnect(Sender: TObject);
  61. var
  62. astr:string;
  63. begin
  64. aStr:='OnConnect: Sender='+sender.Classname;
  65. FList.Add(astr);
  66. end;
  67. procedure TIdTestTcpClient.CallbackDisconnect(Sender: TObject);
  68. var
  69. astr:string;
  70. begin
  71. aStr:='OnDisconnect: Sender='+Sender.ClassName;
  72. FList.Add(astr);
  73. end;
  74. procedure TIdTestTcpClient.CallbackStatus(ASender: TObject;
  75. const AStatus: TIdStatus; const AStatusText: string);
  76. var
  77. astr:string;
  78. begin
  79. aStr:='OnStatus: Sender='+ASender.ClassName+' Text='+AStatusText;
  80. FList.Add(aStr);
  81. end;
  82. procedure TIdTestTcpClient.DoServerExecute(AContext: TIdContext);
  83. var
  84. aStr:string;
  85. begin
  86. aStr:=AContext.Connection.IOHandler.Readln;
  87. if FServerShouldEcho then
  88. begin
  89. AContext.Connection.IOHandler.WriteLn(aStr);
  90. end;
  91. if aStr='normal' then
  92. begin
  93. AContext.Connection.IOHandler.WriteLn('reply');
  94. end
  95. else if aStr='dropme' then
  96. begin
  97. AContext.Connection.Disconnect;
  98. end;
  99. end;
  100. procedure TIdTestTcpClient.TestConnectErrors;
  101. //checks that exceptions are raised as expected
  102. var
  103. aClient:TIdTCPClient;
  104. aExpected:Boolean;
  105. begin
  106. aClient:=TIdTCPClient.Create(nil);
  107. try
  108. //no host given
  109. try
  110. aExpected:=False;
  111. aClient.Port:=80;
  112. aClient.Host:='';
  113. aClient.Connect;
  114. except
  115. on e:Exception do
  116. begin
  117. aExpected:=e is EIdHostRequired;
  118. end;
  119. end;
  120. Assert(aExpected);
  121. //no port given
  122. try
  123. aExpected:=False;
  124. aClient.Port:=0;
  125. aClient.Host:='127.0.0.1';
  126. aClient.Connect;
  127. except
  128. on e:Exception do
  129. begin
  130. aExpected:=e is EIdPortRequired;
  131. end;
  132. end;
  133. Assert(aExpected);
  134. //hoping that this port is unused (its the max port number)
  135. try
  136. aExpected:=False;
  137. aClient.Port:=65535;
  138. aClient.Host:='127.0.0.1';
  139. //odd. specifying timeout gives EIdNotASocket
  140. //aClient.ConnectTimeout:=500;
  141. aClient.Connect;
  142. except
  143. on e:Exception do
  144. begin
  145. aExpected:=e is EIdSocketError;
  146. end;
  147. end;
  148. Assert(aExpected);
  149. finally
  150. Sys.FreeAndNil(aClient);
  151. end;
  152. end;
  153. function ListToCommaText(const aSafe:TIdThreadSafeStringList):string;
  154. begin
  155. with aSafe.Lock do
  156. try
  157. Result:=CommaText;
  158. finally
  159. aSafe.Unlock;
  160. end;
  161. end;
  162. procedure ListToDebug(const aList:TIdStringList);
  163. var
  164. i:Integer;
  165. begin
  166. for i:=0 to aList.Count-1 do
  167. begin
  168. DebugOutput(aList[i]);
  169. end;
  170. end;
  171. procedure SafeListToDebug(const aSafe:TIdThreadSafeStringList);
  172. var
  173. aList:TIdStringList;
  174. begin
  175. aList:=aSafe.Lock;
  176. try
  177. ListToDebug(aList);
  178. finally
  179. aSafe.Unlock;
  180. end;
  181. end;
  182. procedure TIdTestTcpClient.TestEvent;
  183. var
  184. aClient:TIdTCPClient;
  185. aServer:TIdTCPServer;
  186. aStr:string;
  187. //TIdIOHandlerStack.CheckForDisconnect( commented disconnect
  188. begin
  189. FList:=TIdThreadSafeStringList.Create;
  190. aClient:=TIdTCPClient.Create;
  191. aServer:=TIdTCPServer.Create;
  192. try
  193. aServer.OnExecute:=Self.DoServerExecute;
  194. aServer.DefaultPort:=12121;
  195. aServer.Active:=True;
  196. aClient.OnStatus:=Self.CallbackStatus;
  197. aClient.OnDisconnected:=Self.CallbackDisconnect;
  198. aClient.OnConnected:=Self.CallbackConnect;
  199. aClient.Host:='127.0.0.1';
  200. aClient.Port:=12121;
  201. //scenario #1
  202. DebugOutput('Client Disconnects:');
  203. FList.Clear;
  204. aClient.Connect;
  205. aClient.IOHandler.WriteLn('normal');
  206. aStr:=aClient.IOHandler.Readln;
  207. aClient.Disconnect;
  208. SafeListToDebug(FList);
  209. //scenario #2
  210. DebugOutput('Server Disconnects:');
  211. FList.Clear;
  212. try
  213. aClient.Connect;
  214. aClient.IOHandler.WriteLn('dropme');
  215. aStr:=aClient.IOHandler.Readln;
  216. except
  217. //ignore the graceful disconnect exception
  218. end;
  219. SafeListToDebug(FList);
  220. //aStr:=ListToCommaText(FList);
  221. //Assert(aStr='',aStr);
  222. finally
  223. sys.FreeAndNil(aClient);
  224. sys.FreeAndNil(aServer);
  225. sys.FreeAndNil(FList);
  226. end;
  227. end;
  228. procedure TIdTestTcpClient.TestTimeouts;
  229. const
  230. cServerPort = 20200;
  231. var
  232. FServer: TIdTcpServer;
  233. FClient: TIdTcpClient;
  234. LResult: string;
  235. //LStart: Integer;
  236. //LEnd: Integer;
  237. begin
  238. FServerShouldEcho := False;
  239. FServer := TIdTcpServer.Create;
  240. try
  241. FServer.DefaultPort := cServerPort;
  242. FServer.OnExecute := DoServerExecute;
  243. FServer.Active := true;
  244. FClient := TIdTcpClient.Create;
  245. try
  246. FClient.Connect('127.0.0.1', cServerPort);
  247. FClient.ReadTimeout := 500;
  248. LResult:=FClient.IOHandler.Readln;
  249. Assert(LResult = '');
  250. Assert(FClient.IOHandler.ReadLnTimedOut);
  251. FServerShouldEcho := True;
  252. FClient.IOHandler.WriteLn('Hello, World!');
  253. LResult := FClient.IOHandler.ReadLn;
  254. Assert(LResult = 'Hello, World!', LResult);
  255. {
  256. Make some test regarding the readtimeout property and the atimeout param
  257. of some methods.
  258. So the methods needing tests include:
  259. WriteChar
  260. ReadChar
  261. WriteString
  262. ReadString
  263. WriteBytes
  264. ReadBytes
  265. WriteInteger
  266. ReadInteger
  267. Try for some of them the timeout stuff. the ReadTimeout property is the default.
  268. It defaults to -1 (IdTimeoutInfinite). When ReadTimeout is set, that's the
  269. default for all read* methods. when a specific read method has a ATimeout property,
  270. try setting that one to see if it overrides the ReadTimeout property.
  271. the length of timeout should also be measured.
  272. eg set timeout to 3000. read. check that the read method blocked for between 2 and 4 seconds.
  273. this ensures that timeouts aren't occurring instantly.
  274. needs a portable timing mechanism? gettickcount on windows?
  275. }
  276. finally
  277. Sys.FreeAndNil(FClient);
  278. end;
  279. finally
  280. Sys.FreeAndNil(FServer);
  281. end;
  282. end;
  283. initialization
  284. TIdTest.RegisterTest(TIdTestTcpClient);
  285. end.