dbtests.pp 14 KB

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