dbtests.pp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  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) : 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. Function GetResultField (Res : TQueryResult; Id : Integer) : String;
  102. Var
  103. Row : TMYSQL_ROW;
  104. begin
  105. if Res=Nil then
  106. Result:=''
  107. else
  108. begin
  109. Row:=mysql_fetch_row(Res);
  110. If (Row=Nil) or (Row[ID]=Nil) then
  111. Result:=''
  112. else
  113. Result:=strpas(Row[ID]);
  114. end;
  115. Verbose(V_DEBUG,'Field value '+Result);
  116. end;
  117. Procedure FreeQueryResult (Res : TQueryResult);
  118. begin
  119. mysql_free_result(Res);
  120. end;
  121. Function IDQuery(Qry : String) : Integer;
  122. Var
  123. Res : TQueryResult;
  124. begin
  125. Result:=-1;
  126. If RunQuery(Qry,Res) then
  127. begin
  128. Result:=StrToIntDef(GetResultField(Res,0),-1);
  129. FreeQueryResult(Res);
  130. end;
  131. end;
  132. Function EscapeSQL( S : String) : String;
  133. begin
  134. Result:=StringReplace(S,'"','\"',[rfReplaceAll]);
  135. Verbose(V_DEBUG,'EscapeSQL : "'+S+'" -> "'+Result+'"');
  136. end;
  137. Function SQLDate(D : TDateTime) : String;
  138. begin
  139. Result:=FormatDateTime('YYYY/MM/DD hh:nn:ss',D);
  140. end;
  141. { ---------------------------------------------------------------------
  142. High-level access
  143. ---------------------------------------------------------------------}
  144. Function GetTestID(Name : string) : Integer;
  145. Const
  146. SFromName = 'SELECT T_ID FROM TESTS WHERE (T_NAME="%s")';
  147. begin
  148. Result:=IDQuery(Format(SFromName,[Name]));
  149. end;
  150. Function GetOSID(Name : String) : Integer;
  151. Const
  152. SFromName = 'SELECT TO_ID FROM TESTOS WHERE (TO_NAME="%s")';
  153. begin
  154. Result:=IDQuery(Format(SFromName,[Name]));
  155. end;
  156. Function GetVersionID(Name : String) : Integer;
  157. Const
  158. SFromName = 'SELECT TV_ID FROM TESTVERSION WHERE (TV_VERSION="%s")';
  159. begin
  160. Result:=IDQuery(Format(SFromName,[Name]));
  161. end;
  162. Function GetCPUID(Name : String) : Integer;
  163. Const
  164. SFromName = 'SELECT TC_ID FROM TESTCPU WHERE (TC_NAME="%s")';
  165. begin
  166. Result:=IDQuery(Format(SFromName,[Name]));
  167. end;
  168. Function GetCategoryID(Name : String) : Integer;
  169. Const
  170. SFromName = 'SELECT TCAT_ID FROM TESTCATEGORY WHERE (TCAT_NAME="%s")';
  171. begin
  172. Result:=IDQuery(Format(SFromName,[Name]));
  173. end;
  174. Function GetRunID(OSID, CPUID, VERSIONID : Integer; Date : TDateTime) : Integer;
  175. Const
  176. SFromIDS = 'SELECT TU_ID FROM TESTRUN WHERE '+
  177. ' (TU_OS_FK=%d) '+
  178. ' AND (TU_CPU_FK=%d) '+
  179. ' AND (TU_VERSION_FK=%d) '+
  180. ' AND (TU_DATE="%s")';
  181. begin
  182. Result:=IDQuery(Format(SFromIDS,[OSID,CPUID,VERSIONID,SQLDate(Date)]));
  183. end;
  184. Function AddRun(OSID, CPUID, VERSIONID, CATEGORYID : Integer; Date : TDateTime) : Integer;
  185. Const
  186. SInsertRun = 'INSERT INTO TESTRUN '+
  187. '(TU_OS_FK,TU_CPU_FK,TU_VERSION_FK,TU_CATEGORY_FK,TU_DATE)'+
  188. ' VALUES '+
  189. '(%d,%d,%d,%d,"%s")';
  190. Var
  191. Res : TQueryResult;
  192. begin
  193. If RunQuery(Format(SInsertRun,[OSID,CPUID,VERSIONID,CATEGORYID,SQLDate(Date)]),Res) then
  194. Result:=mysql_insert_id(@connection)
  195. else
  196. Result:=-1;
  197. end;
  198. function posr(c : Char; const s : AnsiString) : integer;
  199. var
  200. i : integer;
  201. begin
  202. i := length(s);
  203. while (i>0) and (s[i] <> c) do dec(i);
  204. Result := i;
  205. end;
  206. function GetUnitTestConfig(const fn : string; var r : TConfig) : Boolean;
  207. var
  208. Path : string;
  209. ClassName : string;
  210. MethodName : string;
  211. slashpos : integer;
  212. FileName : string;
  213. s : string;
  214. t : text;
  215. begin
  216. Result := False;
  217. FillChar(r,sizeof(r),0);
  218. if pos('.',fn) > 0 then exit; // This is normally not a unit-test
  219. slashpos := posr('/',fn);
  220. if slashpos < 1 then exit;
  221. MethodName := copy(fn,slashpos+1,length(fn));
  222. Path := copy(fn,1,slashpos-1);
  223. slashpos := posr('/',Path);
  224. if slashpos > 0 then
  225. begin
  226. ClassName := copy(Path,slashpos+1,length(Path));
  227. Path := copy(Path,1,slashpos-1);
  228. end
  229. else
  230. begin
  231. ClassName := Path;
  232. path := '.';
  233. end;
  234. if upper(ClassName[1])<>'T' then exit;
  235. FileName := TestSrcDir+RelSrcDir+Path+DirectorySeparator+copy(lowercase(ClassName),2,length(classname));
  236. if FileExists(FileName+'.pas') then
  237. FileName := FileName + '.pas'
  238. else if FileExists(FileName+'.pp') then
  239. FileName := FileName + '.pp'
  240. else exit;
  241. Verbose(V_Debug,'Reading '+FileName);
  242. assign(t,FileName);
  243. {$I-}
  244. reset(t);
  245. {$I+}
  246. if ioresult<>0 then
  247. begin
  248. Verbose(V_Error,'Can''t open '+FileName);
  249. exit;
  250. end;
  251. while not eof(t) do
  252. begin
  253. readln(t,s);
  254. if s<>'' then
  255. begin
  256. TrimB(s);
  257. if SameText(copy(s,1,9),'PROCEDURE') then
  258. begin
  259. if pos(';',s)>11 then
  260. begin
  261. s := copy(s,11,pos(';',s)-11);
  262. TrimB(s);
  263. if SameText(s,ClassName+'.'+MethodName) then
  264. begin
  265. Result := True;
  266. r.Note:= 'unittest';
  267. end;
  268. end;
  269. end;
  270. end;
  271. end;
  272. close(t);
  273. end;
  274. Function AddTest(Name : String; AddSource : Boolean) : Integer;
  275. Const
  276. SInsertTest = 'INSERT INTO TESTS (T_NAME,T_ADDDATE)'+
  277. ' VALUES ("%s",NOW())';
  278. Var
  279. Info : TConfig;
  280. Res : TQueryResult;
  281. begin
  282. Result:=-1;
  283. If (FileExists(TestSrcDir+RelSrcDir+Name) and
  284. GetConfig(TestSrcDir+RelSrcDir+Name,Info)) or
  285. GetUnitTestConfig(Name,Info) then
  286. begin
  287. If RunQuery(Format(SInsertTest,[Name]),Res) then
  288. begin
  289. Result:=GetTestID(Name);
  290. If Result=-1 then
  291. Verbose(V_WARNING,'Could not find newly added test!')
  292. else
  293. If AddSource then
  294. UpdateTest(Result,Info,GetFileContents(Name))
  295. else
  296. UpdateTest(Result,Info,'');
  297. end
  298. end
  299. else
  300. Verbose(V_ERROR,'Could not find test "'+Name+'" or info about this test.');
  301. end;
  302. Const
  303. B : Array[Boolean] of String = ('-','+');
  304. Function UpdateTest(ID : Integer; Info : TConfig; Source : String) : Boolean;
  305. Const
  306. SUpdateTest = 'Update TESTS SET '+
  307. ' T_CPU="%s", T_OS="%s", T_VERSION="%s",'+
  308. ' T_GRAPH="%s", T_INTERACTIVE="%s", T_RESULT=%d,'+
  309. ' T_FAIL="%s", T_RECOMPILE="%s", T_NORUN="%s",'+
  310. ' T_NEEDLIBRARY="%s", T_KNOWNRUNERROR=%d,'+
  311. ' T_KNOWN="%s", T_NOTE="%s", T_OPTS = "%s"'+
  312. ' %s '+
  313. 'WHERE'+
  314. ' T_ID=%d';
  315. Var
  316. Qry : String;
  317. Res : TQueryResult;
  318. begin
  319. If Source<>'' then
  320. begin
  321. Source:=EscapeSQL(Source);
  322. Source:=', T_SOURCE="'+Source+'"';
  323. end;
  324. With Info do
  325. Qry:=Format(SUpdateTest,[EscapeSQL(NeedCPU),'',EscapeSQL(MinVersion),
  326. B[usesGraph],B[IsInteractive],ResultCode,
  327. B[ShouldFail],B[NeedRecompile],B[NoRun],
  328. B[NeedLibrary],KnownRunError,
  329. B[IsKnownCompileError],EscapeSQL(Note),EscapeSQL(NeedOptions),
  330. Source,
  331. ID
  332. ]);
  333. Result:=RunQuery(Qry,res)
  334. end;
  335. Function AddTestResult(TestID,RunID,TestRes : Integer;
  336. OK, Skipped : Boolean;
  337. Log : String) : Integer;
  338. Const
  339. SInsertRes='Insert into TESTRESULTS '+
  340. '(TR_TEST_FK,TR_TESTRUN_FK,TR_OK,TR_SKIP,TR_RESULT,TR_LOG) '+
  341. ' VALUES '+
  342. '(%d,%d,"%s","%s",%d,"%s") ';
  343. Var
  344. Qry : String;
  345. Res : TQueryResult;
  346. begin
  347. Result:=-1;
  348. Qry:=Format(SInsertRes,
  349. [TestID,RunID,B[OK],B[Skipped],TestRes,EscapeSQL(Log)]);
  350. If RunQuery(Qry,Res) then
  351. Result:=mysql_insert_id(@connection);
  352. end;
  353. Function RequireTestID(Name : String): Integer;
  354. begin
  355. Result:=GetTestID(Name);
  356. If Result=-1 then
  357. Result:=AddTest(Name,FileExists(Name));
  358. If Result=-1 then
  359. Verbose(V_WARNING,'Could not find or create entry for test '+Name);
  360. end;
  361. Function CleanTestRun(ID : Integer) : Boolean;
  362. Const
  363. SDeleteRun = 'DELETE FROM TESTRESULTS WHERE TR_TESTRUN_FK=%d';
  364. Var
  365. Res : TQueryResult;
  366. begin
  367. Result:=RunQuery(Format(SDeleteRun,[ID]),Res);
  368. end;
  369. end.