dbtests.pp 12 KB

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