dbtests.pp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  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. function GetTestPreviousRunHistoryID(TestRunID : Integer) : Integer;
  25. function GetTestNextRunHistoryID(TestRunID : Integer) : Integer;
  26. function AddTestHistoryEntry(TestRunID,TestPreviousID : Integer) : boolean;
  27. { ---------------------------------------------------------------------
  28. Low-level DB access.
  29. ---------------------------------------------------------------------}
  30. Type
  31. TQueryResult = PMYSQL_RES;
  32. Function ConnectToDatabase(DatabaseName,Host,User,Password,Port : String) : Boolean;
  33. Procedure DisconnectDatabase;
  34. Function InsertQuery(const Query : string) : Integer;
  35. Function RunQuery (Qry : String; Var res : TQueryResult) : Boolean ;
  36. Procedure FreeQueryResult (Res : TQueryResult);
  37. Function GetResultField (Res : TQueryResult; Id : Integer) : String;
  38. Function IDQuery(Qry : String) : Integer;
  39. Function StringQuery(Qry : String) : String;
  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 : PMYSQL;
  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_SQL,'Connection params : '+DatabaseName+' '+Host+' '+User+' '+Port);
  60. if Port<>'' then
  61. begin
  62. Val(Port,PortNb,Error);
  63. if Error<>0 then
  64. PortNb:=0;
  65. end
  66. else
  67. PortNB:=0;
  68. Connection:=mysql_init(Nil);
  69. Result:=mysql_real_connect(Connection,PChar(Host),PChar(User),PChar(Password),Nil,PortNb,Nil,CLIENT_MULTI_RESULTS)<>Nil;
  70. If Not Result then
  71. begin
  72. S:=Strpas(mysql_error(connection));
  73. Verbose(V_ERROR,'Failed to connect to database : '+S);
  74. end
  75. else
  76. begin
  77. Result:=Mysql_select_db(Connection,Pchar(DatabaseName))>=0;
  78. If Not result then
  79. begin
  80. S:=StrPas(mysql_error(connection));
  81. DisconnectDatabase;
  82. Verbose(V_Error,'Failed to select database : '+S);
  83. end;
  84. end;
  85. end;
  86. Procedure DisconnectDatabase;
  87. begin
  88. mysql_close(Connection);
  89. end;
  90. Function RunQuery (Qry : String; Var res : TQueryResult) : Boolean ;
  91. begin
  92. Verbose(V_DEBUG,'Running query:'+Qry);
  93. Result:=mysql_query(Connection,PChar(qry))=0;
  94. If Not Result then
  95. Verbose(V_WARNING,'Query : '+Qry+'Failed : '+Strpas(mysql_error(connection)))
  96. else
  97. Res:=Mysql_store_result(connection);
  98. end;
  99. { No warning if it fails }
  100. Function RunSilentQuery (Qry : String; Var res : TQueryResult) : Boolean ;
  101. begin
  102. Verbose(V_SQL,'Executing query:'+Qry);
  103. Result:=False;
  104. try
  105. With CreateQuery(Qry) do
  106. try
  107. ExecSQL;
  108. Result:=True;
  109. (Transaction as TSQLTransaction).Commit;
  110. finally
  111. Free;
  112. end;
  113. except
  114. On E : exception do
  115. begin
  116. Connection.Transaction.RollBack;
  117. if not Silent then
  118. Verbose(V_WARNING,'Query : '+Qry+'Failed : '+E.Message);
  119. end;
  120. end;
  121. end;
  122. begin
  123. Result:=False;
  124. Verbose(V_SQL,'Running query:'+Qry);
  125. Res:=CreateQuery(Qry);
  126. try
  127. Res.Open;
  128. Result:=True;
  129. except
  130. On E : exception do
  131. begin
  132. FreeAndNil(Res);
  133. Try
  134. Connection.Transaction.RollBack;
  135. except
  136. end;
  137. if not Silent then
  138. Verbose(V_WARNING,'Query : '+Qry+'Failed : '+E.Message);
  139. end;
  140. end;
  141. end;
  142. Var
  143. Row : PPchar;
  144. begin
  145. if Res=Nil then
  146. Result:=''
  147. else
  148. begin
  149. Row:=mysql_fetch_row(Res);
  150. If (Row=Nil) or (Row[ID]=Nil) then
  151. Result:=''
  152. else
  153. Result:=strpas(Row[ID]);
  154. end;
  155. Verbose(V_SQL,'Field value '+Result);
  156. end;
  157. Procedure FreeQueryResult (Res : TQueryResult);
  158. begin
  159. mysql_free_result(Res);
  160. end;
  161. Function IDQuery(Qry : String) : Integer;
  162. Var
  163. Res : TQueryResult;
  164. begin
  165. Result:=-1;
  166. If RunQuery(Qry,Res) then
  167. begin
  168. Result:=StrToIntDef(GetResultField(Res,0),-1);
  169. FreeQueryResult(Res);
  170. end;
  171. end;
  172. Function StringQuery(Qry : String) : String;
  173. Var
  174. Res : TQueryResult;
  175. begin
  176. Result:='';
  177. If RunQuery(Qry,Res) then
  178. begin
  179. Result:=GetResultField(Res,0);
  180. FreeQueryResult(Res);
  181. end;
  182. end;
  183. Function EscapeSQL( S : String) : String;
  184. begin
  185. // Result:=StringReplace(S,'\','\\',[rfReplaceAll]);
  186. Result:=StringReplace(S,'''','''''',[rfReplaceAll]);
  187. Verbose(V_SQL,'EscapeSQL : "'+S+'" -> "'+Result+'"');
  188. end;
  189. Function SQLDate(D : TDateTime) : String;
  190. begin
  191. Result:=FormatDateTime('YYYY/MM/DD hh:nn:ss',D);
  192. end;
  193. { ---------------------------------------------------------------------
  194. High-level access
  195. ---------------------------------------------------------------------}
  196. Function GetTestID(Name : string) : Integer;
  197. Const
  198. SFromName = 'SELECT T_ID FROM TESTS WHERE (T_NAME="%s")';
  199. begin
  200. Result:=IDQuery(Format(SFromName,[Name]));
  201. end;
  202. Function GetOSID(Name : String) : Integer;
  203. Const
  204. SFromName = 'SELECT TO_ID FROM TESTOS WHERE (TO_NAME="%s")';
  205. begin
  206. Result:=IDQuery(Format(SFromName,[Name]));
  207. end;
  208. Function GetVersionID(Name : String) : Integer;
  209. Const
  210. SFromName = 'SELECT TV_ID FROM TESTVERSION WHERE (TV_VERSION="%s")';
  211. begin
  212. Result:=IDQuery(Format(SFromName,[Name]));
  213. end;
  214. Function GetCPUID(Name : String) : Integer;
  215. Const
  216. SFromName = 'SELECT TC_ID FROM TESTCPU WHERE (TC_NAME="%s")';
  217. begin
  218. Result:=IDQuery(Format(SFromName,[Name]));
  219. end;
  220. Function GetCategoryID(Name : String) : Integer;
  221. Const
  222. SFromName = 'SELECT TCAT_ID FROM TESTCATEGORY WHERE (TCAT_NAME="%s")';
  223. begin
  224. Result:=IDQuery(Format(SFromName,[Name]));
  225. end;
  226. Function GetRunID(OSID, CPUID, VERSIONID : Integer; Date : TDateTime) : Integer;
  227. Const
  228. SFromIDS = 'SELECT TU_ID FROM TESTRUN WHERE '+
  229. ' (TU_OS_FK=%d) '+
  230. ' AND (TU_CPU_FK=%d) '+
  231. ' AND (TU_VERSION_FK=%d) '+
  232. ' AND (TU_DATE="%s")';
  233. begin
  234. Result:=IDQuery(Format(SFromIDS,[OSID,CPUID,VERSIONID,SQLDate(Date)]));
  235. end;
  236. Function InsertQuery(const Query : string) : Integer;
  237. Var
  238. Res : TQueryResult;
  239. begin
  240. If RunQuery(Query,Res) then
  241. begin
  242. Result:=mysql_insert_id(connection);
  243. FreeQueryResult(Res);
  244. end
  245. else
  246. Result:=-1;
  247. end;
  248. Function AddRun(OSID, CPUID, VERSIONID, CATEGORYID : Integer; Date : TDateTime) : Integer;
  249. Const
  250. SInsertRun = 'INSERT INTO TESTRUN '+
  251. '(TU_OS_FK,TU_CPU_FK,TU_VERSION_FK,TU_CATEGORY_FK,TU_DATE)'+
  252. ' VALUES '+
  253. '(%d,%d,%d,%d,"%s")';
  254. var
  255. Qry : string;
  256. begin
  257. qry:=Format(SInsertRun,[OSID,CPUID,VERSIONID,CATEGORYID,SQLDate(Date)]);
  258. Result:=InsertQuery(Qry);
  259. end;
  260. function posr(c : Char; const s : AnsiString) : integer;
  261. var
  262. i : integer;
  263. begin
  264. i := length(s);
  265. while (i>0) and (s[i] <> c) do dec(i);
  266. Result := i;
  267. end;
  268. function GetUnitTestConfig(const fn : string; var r : TConfig) : Boolean;
  269. var
  270. Path : string;
  271. ClassName : string;
  272. MethodName : string;
  273. slashpos : integer;
  274. FileName : string;
  275. s : string;
  276. t : text;
  277. begin
  278. Result := False;
  279. FillChar(r,sizeof(r),0);
  280. if pos('.',fn) > 0 then exit; // This is normally not a unit-test
  281. slashpos := posr('/',fn);
  282. if slashpos < 1 then exit;
  283. MethodName := copy(fn,slashpos+1,length(fn));
  284. Path := copy(fn,1,slashpos-1);
  285. slashpos := posr('/',Path);
  286. if slashpos > 0 then
  287. begin
  288. ClassName := copy(Path,slashpos+1,length(Path));
  289. Path := copy(Path,1,slashpos-1);
  290. end
  291. else
  292. begin
  293. ClassName := Path;
  294. path := '.';
  295. end;
  296. if upper(ClassName[1])<>'T' then exit;
  297. FileName := TestSrcDir+RelSrcDir+Path+DirectorySeparator+copy(lowercase(ClassName),2,length(classname));
  298. if FileExists(FileName+'.pas') then
  299. FileName := FileName + '.pas'
  300. else if FileExists(FileName+'.pp') then
  301. FileName := FileName + '.pp'
  302. else exit;
  303. Verbose(V_Debug,'Reading: '+FileName);
  304. assign(t,FileName);
  305. {$I-}
  306. reset(t);
  307. {$I+}
  308. if ioresult<>0 then
  309. begin
  310. Verbose(V_Error,'Can''t open '+FileName);
  311. exit;
  312. end;
  313. while not eof(t) do
  314. begin
  315. readln(t,s);
  316. if s<>'' then
  317. begin
  318. TrimB(s);
  319. if SameText(copy(s,1,9),'PROCEDURE') then
  320. begin
  321. if pos(';',s)>11 then
  322. begin
  323. s := copy(s,11,pos(';',s)-11);
  324. TrimB(s);
  325. if SameText(s,ClassName+'.'+MethodName) then
  326. begin
  327. Result := True;
  328. r.Note:= 'unittest';
  329. end;
  330. end;
  331. end;
  332. end;
  333. end;
  334. close(t);
  335. end;
  336. Function AddTest(Name : String; AddSource : Boolean) : Integer;
  337. Const
  338. SInsertTest = 'INSERT INTO TESTS (T_NAME,T_ADDDATE)'+
  339. ' VALUES ("%s",NOW())';
  340. Var
  341. Info : TConfig;
  342. Res : TQueryResult;
  343. begin
  344. Result:=-1;
  345. If (FileExists(TestSrcDir+RelSrcDir+Name) and
  346. GetConfig(TestSrcDir+RelSrcDir+Name,Info)) or
  347. GetUnitTestConfig(Name,Info) then
  348. begin
  349. If RunQuery(Format(SInsertTest,[Name]),Res) then
  350. begin
  351. FreeQueryResult(Res);
  352. Result:=GetTestID(Name);
  353. If Result=-1 then
  354. Verbose(V_WARNING,'Could not find newly added test!')
  355. else
  356. If AddSource then
  357. UpdateTest(Result,Info,GetFileContents(Name))
  358. else
  359. UpdateTest(Result,Info,'');
  360. end
  361. end
  362. else
  363. Verbose(V_ERROR,'Could not find test "'+Name+'" or info about this test.');
  364. end;
  365. Const
  366. B : Array[Boolean] of String = ('-','+');
  367. Function UpdateTest(ID : Integer; Info : TConfig; Source : String) : Boolean;
  368. Const
  369. SUpdateTest = 'Update TESTS SET '+
  370. ' T_CPU="%s", T_OS="%s", T_VERSION="%s",'+
  371. ' T_GRAPH="%s", T_INTERACTIVE="%s", T_RESULT=%d,'+
  372. ' T_FAIL="%s", T_RECOMPILE="%s", T_NORUN="%s",'+
  373. ' T_NEEDLIBRARY="%s", T_KNOWNRUNERROR=%d,'+
  374. ' T_KNOWN="%s", T_NOTE="%s", T_OPTS = "%s"'+
  375. ' %s '+
  376. 'WHERE'+
  377. ' T_ID=%d';
  378. Var
  379. Qry : String;
  380. Res : TQueryResult;
  381. begin
  382. If Source<>'' then
  383. begin
  384. Source:=EscapeSQL(Source);
  385. Source:=', T_SOURCE="'+Source+'"';
  386. end;
  387. With Info do
  388. Qry:=Format(SUpdateTest,[EscapeSQL(NeedCPU),'',EscapeSQL(MinVersion),
  389. B[usesGraph],B[IsInteractive],ResultCode,
  390. B[ShouldFail],B[NeedRecompile],B[NoRun],
  391. B[NeedLibrary],KnownRunError,
  392. B[IsKnownCompileError],EscapeSQL(Note),EscapeSQL(NeedOptions),
  393. Source,
  394. ID
  395. ]);
  396. Result:=RunQuery(Qry,res);
  397. FreeQueryResult(Res);
  398. end;
  399. Function AddTestResult(TestID,RunID,TestRes : Integer;
  400. OK, Skipped : Boolean;
  401. Log : String;var is_new : boolean) : Integer;
  402. Const
  403. SInsertRes='Insert into TESTRESULTS '+
  404. '(TR_TEST_FK,TR_TESTRUN_FK,TR_OK,TR_SKIP,TR_RESULT) '+
  405. ' VALUES '+
  406. '(%d,%d,"%s","%s",%d) ';
  407. SSelectId='SELECT TR_ID FROM TESTRESULTS WHERE (TR_TEST_FK=%d) '+
  408. ' AND (TR_TESTRUN_FK=%d)';
  409. SInsertLog='Update TESTRESULTS SET TR_LOG="%s"'+
  410. ',TR_OK="%s",TR_SKIP="%s",TR_RESULT=%d WHERE (TR_ID=%d)';
  411. Var
  412. Qry : String;
  413. Res : TQueryResult;
  414. updateValues : boolean;
  415. begin
  416. updateValues:=false;
  417. Result:=-1;
  418. Qry:=Format(SInsertRes,
  419. [TestID,RunID,B[OK],B[Skipped],TestRes,EscapeSQL(Log)]);
  420. If RunSilentQuery(Qry,Res) then
  421. Result:=mysql_insert_id(connection)
  422. else
  423. begin
  424. Qry:=format(SSelectId,[TestId,RunId]);
  425. Result:=IDQuery(Qry);
  426. if Result<>-1 then
  427. updateValues:=true;
  428. end;
  429. if (Result<>-1) and ((Log<>'') or updateValues) then
  430. begin
  431. Qry:=format(SInsertLog,[EscapeSQL(Log),B[OK],B[Skipped],TestRes,Result]);
  432. if not RunQuery(Qry,Res) then
  433. begin
  434. Verbose(V_Warning,'Insert Log failed');
  435. end;
  436. FreeQueryResult(Res);
  437. end;
  438. { If test already existed, return false for is_new to avoid double counting }
  439. is_new:=not updateValues;
  440. end;
  441. Function RequireTestID(Name : String): Integer;
  442. begin
  443. Result:=GetTestID(Name);
  444. If Result=-1 then
  445. Result:=AddTest(Name,FileExists(Name));
  446. If Result=-1 then
  447. Verbose(V_WARNING,'Could not find or create entry for test '+Name);
  448. end;
  449. Function CleanTestRun(ID : Integer) : Boolean;
  450. Const
  451. SDeleteRun = 'DELETE FROM TESTRESULTS WHERE TR_TESTRUN_FK=%d';
  452. Var
  453. Res : TQueryResult;
  454. begin
  455. Result:=RunQuery(Format(SDeleteRun,[ID]),Res);
  456. FreeQueryResult(Res);
  457. end;
  458. function GetTestPreviousRunHistoryID(TestRunID : Integer) : Integer;
  459. begin
  460. GetTestPreviousRunHistoryID:=IDQuery(
  461. format('SELECT TH_PREVIOUS_FK FROM TESTRUNHISTORY WHERE TH_ID_FK=%d',[TestRunID]));
  462. end;
  463. function GetTestNextRunHistoryID(TestRunID : Integer) : Integer;
  464. begin
  465. GetTestNextRunHistoryID:=IDQuery(
  466. format('SELECT TH_ID_FK FROM TESTRUNHISTORY WHERE TH_PREVIOUS_FK=%d',[TestRunID]));
  467. end;
  468. function AddTestHistoryEntry(TestRunID,TestPreviousID : Integer) : boolean;
  469. var
  470. qry : string;
  471. res : TQueryResult;
  472. begin
  473. qry:=format('INSERT INTO TESTRUNHISTORY (TH_ID_FK,TH_PREVIOUS_FK) '+
  474. ' VALUES (%d,%d)',[TestRunID,TestPreviousID]);
  475. If RunQuery(qry,res) then
  476. begin
  477. FreeQueryResult(res);
  478. AddTestHistoryEntry:=true;
  479. end
  480. else
  481. AddTestHistoryEntry:=false;
  482. end;
  483. begin
  484. initialisemysql;
  485. end.