dbtests.pp 13 KB

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