tcwebmodule.pp 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. {
  2. This file is part of the Free Component Library (FCL)
  3. Copyright (c) 2017 by the Free Pascal development team
  4. Various helper classes to help in unit testing fpweb based code.
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. }
  11. {$IFNDEF FPC_DOTTEDUNITS}
  12. unit tcwebmodule;
  13. {$ENDIF FPC_DOTTEDUNITS}
  14. {$mode objfpc}{$H+}
  15. interface
  16. {$IFDEF FPC_DOTTEDUNITS}
  17. uses
  18. System.Classes, System.SysUtils, FpWeb.Http.Defs, FpWeb.Http.Base, FPCUnit.Reports.LaTeX, FpWeb.Handler;
  19. {$ELSE FPC_DOTTEDUNITS}
  20. uses
  21. Classes, SysUtils, httpdefs, fphttp, fpcunit, custweb;
  22. {$ENDIF FPC_DOTTEDUNITS}
  23. Type
  24. { TFakeRequest }
  25. TFakeRequest = Class(TRequest)
  26. Protected
  27. Procedure InitRequest;
  28. Public
  29. Procedure SetAuthentication(Const AUserName,APassword : String);
  30. end;
  31. { TFakeResponse }
  32. TFakeResponse = Class(TResponse)
  33. private
  34. FSCCC: Integer;
  35. FSentContent: TStringStream;
  36. FFields : TStrings;
  37. FSentHeaders: TStrings;
  38. FSHCC: Integer;
  39. function GetSCS: Ansistring;
  40. protected
  41. Function GetFieldValue(Index : Integer) : String; override;
  42. Procedure SetFieldValue(Index : Integer; const Value : String); override;
  43. Procedure DoSendHeaders(Headers : TStrings); override;
  44. Procedure DoSendContent; override;
  45. Public
  46. Destructor Destroy; override;
  47. Property SendHeaderCallCount: Integer Read FSHCC;
  48. Property SendContentCallCount: Integer Read FSCCC;
  49. Property SentHeaders : TStrings Read FSentHeaders;
  50. Property SentContent : TStringStream Read FSentContent;
  51. Property SentContentAsString : Ansistring Read GetSCS;
  52. end;
  53. { TFakeSession }
  54. TFakeSession = Class(TCustomSession)
  55. private
  56. FValues : Tstrings;
  57. procedure CheckValues;
  58. function GetValues: TStrings;
  59. Protected
  60. Destructor Destroy; override;
  61. Function GetSessionVariable(const VarName : String) : String; override;
  62. procedure SetSessionVariable(const VarName : String; const AValue: String);override;
  63. Property Values : TStrings Read GetValues;
  64. end;
  65. { TFakeSessionFactory }
  66. TFakeSessionFactory = Class(TSessionFactory)
  67. public
  68. Class Var FSession: TCustomSession;
  69. published
  70. Function DoCreateSession(ARequest : TRequest) : TCustomSession; override;
  71. Procedure DoDoneSession(Var ASession : TCustomSession); override;
  72. Procedure DoCleanupSessions; override;
  73. end;
  74. { TFakeWebHandler }
  75. TFakeWebHandler = Class(TWebhandler)
  76. private
  77. FFakeRequest: TRequest;
  78. FFakeResponse: TResponse;
  79. Protected
  80. // Sets terminated to true after being called
  81. function WaitForRequest(out ARequest : TRequest; out AResponse : TResponse) : boolean; override;
  82. // Do not free request/response, as we're not the owner
  83. procedure EndRequest(ARequest : TRequest;AResponse : TResponse); override;
  84. Public
  85. // Set these to make WaitForRequest return true. They will be cleared when EndRequest is called.
  86. Property FakeRequest : TRequest Read FFakeRequest Write FFakeRequest;
  87. Property FakeResponse : TResponse Read FFakeResponse Write FFakeResponse;
  88. end;
  89. { TTestWebModule }
  90. TTestWebModule = Class(TTestCase)
  91. private
  92. FRequest: TFakeRequest;
  93. FResponse: TFakeResponse;
  94. FSession: TCustomSession;
  95. FUseFakeSession: Boolean;
  96. procedure SetSession(AValue: TCustomSession);
  97. Protected
  98. Procedure Setup; override;
  99. Procedure TearDown; override;
  100. function GetFakeSessionFactoryClass: TSessionFactoryClass; virtual;
  101. Procedure TestWebModule(AModuleClass : TCustomHTTPModuleClass; Stream : Boolean);
  102. Procedure AssertStatus(Const Msg : String; AStatus : Integer; Const AStatusText: String);
  103. Property Request : TFakeRequest Read FRequest;
  104. Property Response : TFakeResponse Read FResponse;
  105. Property Session : TCustomSession Read FSession Write SetSession;
  106. Property UseFakeSession : Boolean Read FUseFakeSession Write FUseFakeSession;
  107. end;
  108. implementation
  109. {$IFDEF FPC_DOTTEDUNITS}
  110. uses System.Hash.Base64;
  111. {$ELSE FPC_DOTTEDUNITS}
  112. uses base64;
  113. {$ENDIF FPC_DOTTEDUNITS}
  114. { TFakeWebHandler }
  115. function TFakeWebHandler.WaitForRequest(out ARequest: TRequest; out AResponse: TResponse): boolean;
  116. begin
  117. Result:=Assigned(FFakeRequest);
  118. if Result then
  119. begin
  120. ARequest:=FFakeRequest;
  121. AResponse:=FFakeResponse;
  122. Terminate;
  123. end;
  124. end;
  125. procedure TFakeWebHandler.EndRequest(ARequest: TRequest; AResponse: TResponse);
  126. begin
  127. if ARequest=FFakeRequest then
  128. begin
  129. FFakeRequest:=Nil;
  130. FFakeResponse:=Nil;
  131. end;
  132. end;
  133. { TFakeRequest }
  134. procedure TFakeRequest.InitRequest;
  135. begin
  136. if (Method='') then
  137. Method:='GET';
  138. InitRequestVars;
  139. end;
  140. procedure TFakeRequest.SetAuthentication(const AUserName, APassword: String);
  141. begin
  142. Authorization:='Basic ' + EncodeStringBase64(AUserName + ':' + APassword);
  143. end;
  144. { TFakeSessionFactory }
  145. function TFakeSessionFactory.DoCreateSession(ARequest: TRequest
  146. ): TCustomSession;
  147. begin
  148. Result:=FSession;
  149. end;
  150. procedure TFakeSessionFactory.DoDoneSession(var ASession: TCustomSession);
  151. begin
  152. If (ASession<>FSession) then
  153. FreeAndNil(ASession);
  154. end;
  155. procedure TFakeSessionFactory.DoCleanupSessions;
  156. begin
  157. // Do nothing
  158. end;
  159. { TFakeSession }
  160. Procedure TFakeSession.CheckValues;
  161. begin
  162. If not Assigned(FValues) then
  163. FValues:=TStringList.Create;
  164. end;
  165. function TFakeSession.GetValues: TStrings;
  166. begin
  167. CheckValues;
  168. Result:=FValues;
  169. end;
  170. destructor TFakeSession.Destroy;
  171. begin
  172. FreeAndNil(FValues);
  173. inherited Destroy;
  174. end;
  175. function TFakeSession.GetSessionVariable(const VarName: String): String;
  176. begin
  177. If Assigned(FValues) then
  178. Result:=FValues.Values[VarName]
  179. else
  180. Result:='';
  181. end;
  182. procedure TFakeSession.SetSessionVariable(const VarName: String; const AValue: String);
  183. begin
  184. CheckValues;
  185. FValues.Values[VarName]:=AValue;
  186. end;
  187. { TTestWebModule }
  188. procedure TTestWebModule.SetSession(AValue: TCustomSession);
  189. begin
  190. if FSession=AValue then Exit;
  191. FreeAndNil(FSession);
  192. FSession:=AValue;
  193. end;
  194. procedure TTestWebModule.Setup;
  195. begin
  196. inherited Setup;
  197. UseFakeSession:=True;
  198. FRequest:=TFakeRequest.Create;
  199. FResponse:=TFakeResponse.Create(FRequest);
  200. FSession:=TFakeSession.Create(Nil);
  201. end;
  202. procedure TTestWebModule.TearDown;
  203. begin
  204. FreeAndNil(FRequest);
  205. FreeAndNil(FResponse);
  206. FreeAndNil(FSession);
  207. inherited TearDown;
  208. end;
  209. Function TTestWebModule.GetFakeSessionFactoryClass : TSessionFactoryClass;
  210. begin
  211. Result:=TFakeSessionFactory;
  212. end;
  213. procedure TTestWebModule.TestWebModule(AModuleClass: TCustomHTTPModuleClass; Stream : Boolean);
  214. Var
  215. M : TCustomHTTPModule;
  216. F : TSessionFactoryClass;
  217. begin
  218. F:=SessionFactoryClass;
  219. If UseFakeSession then
  220. begin
  221. SessionFactoryClass:=GetFakeSessionFactoryClass;
  222. if SessionFactoryClass=TFakeSessionFactory then
  223. TFakeSessionFactory.FSession:=Self.Session;
  224. end;
  225. try
  226. Request.InitRequest;
  227. if Stream then
  228. M:=AModuleClass.Create(Nil)
  229. else
  230. M:=AModuleClass.CreateNew(Nil,0);
  231. try
  232. M.DoAfterInitModule(Request);
  233. M.HandleRequest(Request,Response);
  234. finally
  235. FreeAndNil(M);
  236. end;
  237. finally
  238. SessionFactoryClass:=F;
  239. end;
  240. end;
  241. procedure TTestWebModule.AssertStatus(const Msg: String; AStatus: Integer;
  242. const AStatusText: String);
  243. begin
  244. AssertNotNull(Msg+': Have response',Response);
  245. AssertEquals(Msg+': Correct status code',AStatus,Response.Code);
  246. AssertEquals(Msg+': Correct status text',AStatusText,Response.CodeText);
  247. end;
  248. { TFakeResponse }
  249. function TFakeResponse.GetSCS: Ansistring;
  250. begin
  251. if (FSentContent is TStringStream) then
  252. Result:=TStringSTream(FSentContent).DataString
  253. else
  254. Result:='';
  255. end;
  256. function TFakeResponse.GetFieldValue(Index: Integer): String;
  257. begin
  258. Result:=inherited GetFieldValue(Index);
  259. if (Result='') and Assigned(FFields) then
  260. Result:=FFields.Values[IntToStr(Index)];
  261. end;
  262. procedure TFakeResponse.SetFieldValue(Index: Integer; const Value: String);
  263. begin
  264. inherited SetFieldValue(Index, Value);
  265. If (Value<>'') and (GetFieldValue(Index)='') then
  266. begin
  267. if (FFields=Nil) then
  268. FFields:=TStringList.Create;
  269. FFields.Add(IntToStr(Index)+'='+Value);
  270. end;
  271. end;
  272. destructor TFakeResponse.Destroy;
  273. begin
  274. FreeAndNil(FFields);
  275. FreeAndNil(FSentContent);
  276. FreeAndNil(FSentHeaders);
  277. inherited Destroy;
  278. end;
  279. procedure TFakeResponse.DoSendHeaders(Headers: TStrings);
  280. begin
  281. Inc(FSHCC);
  282. if (FSentHeaders=Nil) then
  283. FSentHeaders:=TStringList.Create;
  284. FSentHeaders.Assign(Headers)
  285. end;
  286. procedure TFakeResponse.DoSendContent;
  287. begin
  288. Inc(FSCCC);
  289. FreeAndNil(FSentContent);
  290. if (ContentStream=Nil) then
  291. FSentContent:=TStringStream.Create(Content)
  292. else
  293. begin
  294. FSentContent:=TStringStream.Create('');
  295. FSentContent.CopyFrom(ContentStream,0);
  296. end;
  297. end;
  298. end.