PageRenderTime 27ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 1ms

/fpcbuild-2.6.0/fpcsrc/tests/utils/dbtests.pp

#
Pascal | 489 lines | 379 code | 86 blank | 24 comment | 21 complexity | 03f102b8b55cb7255c443e56fd791486 MD5 | raw file
Possible License(s): LGPL-3.0, BSD-3-Clause, GPL-2.0, LGPL-2.0, LGPL-2.1
  1. {$mode objfpc}
  2. {$H+}
  3. unit dbtests;
  4. Interface
  5. Uses
  6. {$ifndef ver1_0}
  7. mysql4,
  8. {$else}
  9. mysql,
  10. {$endif}
  11. testu;
  12. { ---------------------------------------------------------------------
  13. High-level access
  14. ---------------------------------------------------------------------}
  15. Function GetTestID(Name : string) : Integer;
  16. Function GetOSID(Name : String) : Integer;
  17. Function GetCPUID(Name : String) : Integer;
  18. Function GetCategoryID(Name : String) : Integer;
  19. Function GetVersionID(Name : String) : Integer;
  20. Function GetRunID(OSID, CPUID, VERSIONID : Integer; Date : TDateTime) : Integer;
  21. Function AddRun(OSID, CPUID, VERSIONID, CATEGORYID : Integer; Date : TDateTime) : Integer;
  22. Function AddTest(Name : String; AddSource : Boolean) : Integer;
  23. Function UpdateTest(ID : Integer; Info : TConfig; Source : String) : Boolean;
  24. Function AddTestResult(TestID,RunID,TestRes : Integer;
  25. OK, Skipped : Boolean;
  26. Log : String;var is_new : boolean) : Integer;
  27. Function RequireTestID(Name : String): Integer;
  28. Function CleanTestRun(ID : Integer) : Boolean;
  29. { ---------------------------------------------------------------------
  30. Low-level DB access.
  31. ---------------------------------------------------------------------}
  32. Type
  33. TQueryResult = PMYSQL_RES;
  34. Function ConnectToDatabase(DatabaseName,Host,User,Password,Port : String) : Boolean;
  35. Procedure DisconnectDatabase;
  36. Function RunQuery (Qry : String; Var res : TQueryResult) : Boolean ;
  37. Procedure FreeQueryResult (Res : TQueryResult);
  38. Function GetResultField (Res : TQueryResult; Id : Integer) : String;
  39. Function IDQuery(Qry : String) : Integer;
  40. Function EscapeSQL( S : String) : String;
  41. Function SQLDate(D : TDateTime) : String;
  42. var
  43. RelSrcDir,
  44. TestSrcDir : string;
  45. Implementation
  46. Uses
  47. SysUtils;
  48. { ---------------------------------------------------------------------
  49. Low-level DB access.
  50. ---------------------------------------------------------------------}
  51. Var
  52. Connection : TMYSQL;
  53. Function ConnectToDatabase(DatabaseName,Host,User,Password,Port : String) : Boolean;
  54. Var
  55. S : String;
  56. PortNb : longint;
  57. Error : word;
  58. begin
  59. Verbose(V_DEBUG,'Connection params : '+DatabaseName+' '+Host+' '+User+' '+Password+' '+Port);
  60. if Port<>'' then
  61. begin
  62. Val(Port,PortNb,Error);
  63. if Error<>0 then
  64. PortNb:=0;
  65. end;
  66. {$ifdef ver1_0}
  67. Result:=mysql_connect(@Connection,PChar(Host),PChar(User),PChar(Password))<>Nil;
  68. {$else}
  69. mysql_init(@Connection);
  70. Result:=mysql_real_connect(@Connection,PChar(Host),PChar(User),PChar(Password),Nil,PortNb,Nil,0)<>Nil;
  71. {$endif}
  72. If Not Result then
  73. begin
  74. S:=Strpas(mysql_error(@connection));
  75. Verbose(V_ERROR,'Failed to connect to database : '+S);
  76. end
  77. else
  78. begin
  79. Result:=Mysql_select_db(@Connection,Pchar(DatabaseName))>=0;
  80. If Not result then
  81. begin
  82. S:=StrPas(mysql_error(@connection));
  83. DisconnectDatabase;
  84. Verbose(V_Error,'Failed to select database : '+S);
  85. end;
  86. end;
  87. end;
  88. Procedure DisconnectDatabase;
  89. begin
  90. mysql_close(@Connection);
  91. end;
  92. Function RunQuery (Qry : String; Var res : TQueryResult) : Boolean ;
  93. begin
  94. Verbose(V_DEBUG,'Running query:'+Qry);
  95. Result:=mysql_query(@Connection,PChar(qry))=0;
  96. If Not Result then
  97. Verbose(V_WARNING,'Query : '+Qry+'Failed : '+Strpas(mysql_error(@connection)))
  98. else
  99. Res:=Mysql_store_result(@connection);
  100. end;
  101. { No warning if it fails }
  102. Function RunSilentQuery (Qry : String; Var res : TQueryResult) : Boolean ;
  103. begin
  104. Verbose(V_DEBUG,'Running silent query:'+Qry);
  105. Result:=mysql_query(@Connection,PChar(qry))=0;
  106. If Not Result then
  107. Verbose(V_DEBUG,'Silent query : '+Qry+'Failed : '+Strpas(mysql_error(@connection)))
  108. else
  109. Res:=Mysql_store_result(@connection);
  110. end;
  111. Function GetResultField (Res : TQueryResult; Id : Integer) : String;
  112. Var
  113. Row : TMYSQL_ROW;
  114. begin
  115. if Res=Nil then
  116. Result:=''
  117. else
  118. begin
  119. Row:=mysql_fetch_row(Res);
  120. If (Row=Nil) or (Row[ID]=Nil) then
  121. Result:=''
  122. else
  123. Result:=strpas(Row[ID]);
  124. end;
  125. Verbose(V_DEBUG,'Field value '+Result);
  126. end;
  127. Procedure FreeQueryResult (Res : TQueryResult);
  128. begin
  129. mysql_free_result(Res);
  130. end;
  131. Function IDQuery(Qry : String) : Integer;
  132. Var
  133. Res : TQueryResult;
  134. begin
  135. Result:=-1;
  136. If RunQuery(Qry,Res) then
  137. begin
  138. Result:=StrToIntDef(GetResultField(Res,0),-1);
  139. FreeQueryResult(Res);
  140. end;
  141. end;
  142. Function EscapeSQL( S : String) : String;
  143. begin
  144. Result:=StringReplace(S,'\','\\',[rfReplaceAll]);
  145. Result:=StringReplace(Result,'"','\"',[rfReplaceAll]);
  146. Verbose(V_DEBUG,'EscapeSQL : "'+S+'" -> "'+Result+'"');
  147. end;
  148. Function SQLDate(D : TDateTime) : String;
  149. begin
  150. Result:=FormatDateTime('YYYY/MM/DD hh:nn:ss',D);
  151. end;
  152. { ---------------------------------------------------------------------
  153. High-level access
  154. ---------------------------------------------------------------------}
  155. Function GetTestID(Name : string) : Integer;
  156. Const
  157. SFromName = 'SELECT T_ID FROM TESTS WHERE (T_NAME="%s")';
  158. begin
  159. Result:=IDQuery(Format(SFromName,[Name]));
  160. end;
  161. Function GetOSID(Name : String) : Integer;
  162. Const
  163. SFromName = 'SELECT TO_ID FROM TESTOS WHERE (TO_NAME="%s")';
  164. begin
  165. Result:=IDQuery(Format(SFromName,[Name]));
  166. end;
  167. Function GetVersionID(Name : String) : Integer;
  168. Const
  169. SFromName = 'SELECT TV_ID FROM TESTVERSION WHERE (TV_VERSION="%s")';
  170. begin
  171. Result:=IDQuery(Format(SFromName,[Name]));
  172. end;
  173. Function GetCPUID(Name : String) : Integer;
  174. Const
  175. SFromName = 'SELECT TC_ID FROM TESTCPU WHERE (TC_NAME="%s")';
  176. begin
  177. Result:=IDQuery(Format(SFromName,[Name]));
  178. end;
  179. Function GetCategoryID(Name : String) : Integer;
  180. Const
  181. SFromName = 'SELECT TCAT_ID FROM TESTCATEGORY WHERE (TCAT_NAME="%s")';
  182. begin
  183. Result:=IDQuery(Format(SFromName,[Name]));
  184. end;
  185. Function GetRunID(OSID, CPUID, VERSIONID : Integer; Date : TDateTime) : Integer;
  186. Const
  187. SFromIDS = 'SELECT TU_ID FROM TESTRUN WHERE '+
  188. ' (TU_OS_FK=%d) '+
  189. ' AND (TU_CPU_FK=%d) '+
  190. ' AND (TU_VERSION_FK=%d) '+
  191. ' AND (TU_DATE="%s")';
  192. begin
  193. Result:=IDQuery(Format(SFromIDS,[OSID,CPUID,VERSIONID,SQLDate(Date)]));
  194. end;
  195. Function AddRun(OSID, CPUID, VERSIONID, CATEGORYID : Integer; Date : TDateTime) : Integer;
  196. Const
  197. SInsertRun = 'INSERT INTO TESTRUN '+
  198. '(TU_OS_FK,TU_CPU_FK,TU_VERSION_FK,TU_CATEGORY_FK,TU_DATE)'+
  199. ' VALUES '+
  200. '(%d,%d,%d,%d,"%s")';
  201. Var
  202. Res : TQueryResult;
  203. begin
  204. If RunQuery(Format(SInsertRun,[OSID,CPUID,VERSIONID,CATEGORYID,SQLDate(Date)]),Res) then
  205. Result:=mysql_insert_id(@connection)
  206. else
  207. Result:=-1;
  208. end;
  209. function posr(c : Char; const s : AnsiString) : integer;
  210. var
  211. i : integer;
  212. begin
  213. i := length(s);
  214. while (i>0) and (s[i] <> c) do dec(i);
  215. Result := i;
  216. end;
  217. function GetUnitTestConfig(const fn : string; var r : TConfig) : Boolean;
  218. var
  219. Path : string;
  220. ClassName : string;
  221. MethodName : string;
  222. slashpos : integer;
  223. FileName : string;
  224. s : string;
  225. t : text;
  226. begin
  227. Result := False;
  228. FillChar(r,sizeof(r),0);
  229. if pos('.',fn) > 0 then exit; // This is normally not a unit-test
  230. slashpos := posr('/',fn);
  231. if slashpos < 1 then exit;
  232. MethodName := copy(fn,slashpos+1,length(fn));
  233. Path := copy(fn,1,slashpos-1);
  234. slashpos := posr('/',Path);
  235. if slashpos > 0 then
  236. begin
  237. ClassName := copy(Path,slashpos+1,length(Path));
  238. Path := copy(Path,1,slashpos-1);
  239. end
  240. else
  241. begin
  242. ClassName := Path;
  243. path := '.';
  244. end;
  245. if upper(ClassName[1])<>'T' then exit;
  246. FileName := TestSrcDir+RelSrcDir+Path+DirectorySeparator+copy(lowercase(ClassName),2,length(classname));
  247. if FileExists(FileName+'.pas') then
  248. FileName := FileName + '.pas'
  249. else if FileExists(FileName+'.pp') then
  250. FileName := FileName + '.pp'
  251. else exit;
  252. Verbose(V_Debug,'Reading '+FileName);
  253. assign(t,FileName);
  254. {$I-}
  255. reset(t);
  256. {$I+}
  257. if ioresult<>0 then
  258. begin
  259. Verbose(V_Error,'Can''t open '+FileName);
  260. exit;
  261. end;
  262. while not eof(t) do
  263. begin
  264. readln(t,s);
  265. if s<>'' then
  266. begin
  267. TrimB(s);
  268. if SameText(copy(s,1,9),'PROCEDURE') then
  269. begin
  270. if pos(';',s)>11 then
  271. begin
  272. s := copy(s,11,pos(';',s)-11);
  273. TrimB(s);
  274. if SameText(s,ClassName+'.'+MethodName) then
  275. begin
  276. Result := True;
  277. r.Note:= 'unittest';
  278. end;
  279. end;
  280. end;
  281. end;
  282. end;
  283. close(t);
  284. end;
  285. Function AddTest(Name : String; AddSource : Boolean) : Integer;
  286. Const
  287. SInsertTest = 'INSERT INTO TESTS (T_NAME,T_ADDDATE)'+
  288. ' VALUES ("%s",NOW())';
  289. Var
  290. Info : TConfig;
  291. Res : TQueryResult;
  292. begin
  293. Result:=-1;
  294. If (FileExists(TestSrcDir+RelSrcDir+Name) and
  295. GetConfig(TestSrcDir+RelSrcDir+Name,Info)) or
  296. GetUnitTestConfig(Name,Info) then
  297. begin
  298. If RunQuery(Format(SInsertTest,[Name]),Res) then
  299. begin
  300. Result:=GetTestID(Name);
  301. If Result=-1 then
  302. Verbose(V_WARNING,'Could not find newly added test!')
  303. else
  304. If AddSource then
  305. UpdateTest(Result,Info,GetFileContents(Name))
  306. else
  307. UpdateTest(Result,Info,'');
  308. end
  309. end
  310. else
  311. Verbose(V_ERROR,'Could not find test "'+Name+'" or info about this test.');
  312. end;
  313. Const
  314. B : Array[Boolean] of String = ('-','+');
  315. Function UpdateTest(ID : Integer; Info : TConfig; Source : String) : Boolean;
  316. Const
  317. SUpdateTest = 'Update TESTS SET '+
  318. ' T_CPU="%s", T_OS="%s", T_VERSION="%s",'+
  319. ' T_GRAPH="%s", T_INTERACTIVE="%s", T_RESULT=%d,'+
  320. ' T_FAIL="%s", T_RECOMPILE="%s", T_NORUN="%s",'+
  321. ' T_NEEDLIBRARY="%s", T_KNOWNRUNERROR=%d,'+
  322. ' T_KNOWN="%s", T_NOTE="%s", T_OPTS = "%s"'+
  323. ' %s '+
  324. 'WHERE'+
  325. ' T_ID=%d';
  326. Var
  327. Qry : String;
  328. Res : TQueryResult;
  329. begin
  330. If Source<>'' then
  331. begin
  332. Source:=EscapeSQL(Source);
  333. Source:=', T_SOURCE="'+Source+'"';
  334. end;
  335. With Info do
  336. Qry:=Format(SUpdateTest,[EscapeSQL(NeedCPU),'',EscapeSQL(MinVersion),
  337. B[usesGraph],B[IsInteractive],ResultCode,
  338. B[ShouldFail],B[NeedRecompile],B[NoRun],
  339. B[NeedLibrary],KnownRunError,
  340. B[IsKnownCompileError],EscapeSQL(Note),EscapeSQL(NeedOptions),
  341. Source,
  342. ID
  343. ]);
  344. Result:=RunQuery(Qry,res)
  345. end;
  346. Function AddTestResult(TestID,RunID,TestRes : Integer;
  347. OK, Skipped : Boolean;
  348. Log : String;var is_new : boolean) : Integer;
  349. Const
  350. SInsertRes='Insert into TESTRESULTS '+
  351. '(TR_TEST_FK,TR_TESTRUN_FK,TR_OK,TR_SKIP,TR_RESULT) '+
  352. ' VALUES '+
  353. '(%d,%d,"%s","%s",%d) ';
  354. SSelectId='SELECT TR_ID FROM TESTRESULTS WHERE (TR_TEST_FK=%d) '+
  355. ' AND (TR_TESTRUN_FK=%d)';
  356. SInsertLog='Update TESTRESULTS SET TR_LOG="%s"'+
  357. ',TR_OK="%s",TR_SKIP="%s",TR_RESULT=%d WHERE (TR_ID=%d)';
  358. Var
  359. Qry : String;
  360. Res : TQueryResult;
  361. updateValues : boolean;
  362. begin
  363. updateValues:=false;
  364. Result:=-1;
  365. Qry:=Format(SInsertRes,
  366. [TestID,RunID,B[OK],B[Skipped],TestRes,EscapeSQL(Log)]);
  367. If RunSilentQuery(Qry,Res) then
  368. Result:=mysql_insert_id(@connection)
  369. else
  370. begin
  371. Qry:=format(SSelectId,[TestId,RunId]);
  372. Result:=IDQuery(Qry);
  373. if Result<>-1 then
  374. updateValues:=true;
  375. end;
  376. if (Result<>-1) and ((Log<>'') or updateValues) then
  377. begin
  378. Qry:=format(SInsertLog,[EscapeSQL(Log),B[OK],B[Skipped],TestRes,Result]);
  379. if not RunQuery(Qry,Res) then
  380. begin
  381. Verbose(V_Warning,'Insert Log failed');
  382. end;
  383. end;
  384. { If test already existed, return false for is_new to avoid double counting }
  385. is_new:=not updateValues;
  386. end;
  387. Function RequireTestID(Name : String): Integer;
  388. begin
  389. Result:=GetTestID(Name);
  390. If Result=-1 then
  391. Result:=AddTest(Name,FileExists(Name));
  392. If Result=-1 then
  393. Verbose(V_WARNING,'Could not find or create entry for test '+Name);
  394. end;
  395. Function CleanTestRun(ID : Integer) : Boolean;
  396. Const
  397. SDeleteRun = 'DELETE FROM TESTRESULTS WHERE TR_TESTRUN_FK=%d';
  398. Var
  399. Res : TQueryResult;
  400. begin
  401. Result:=RunQuery(Format(SDeleteRun,[ID]),Res);
  402. end;
  403. end.