dbtests.pp 13 KB

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