testu.pp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640
  1. {$mode objfpc}
  2. {$modeswitch advancedrecords}
  3. {$h+}
  4. unit testu;
  5. Interface
  6. uses
  7. classes, sysutils, tresults;
  8. { ---------------------------------------------------------------------
  9. utility functions, shared by several programs of the test suite
  10. ---------------------------------------------------------------------}
  11. type
  12. TCharSet = set of char;
  13. TVerboseLevel=(V_Abort,V_Error,V_Warning,V_Normal,V_Debug,V_SQL);
  14. TConfig = record
  15. NeedOptions,
  16. DelOptions,
  17. NeedCPU,
  18. SkipCPU,
  19. SkipEmu,
  20. NeedTarget,
  21. SkipTarget,
  22. MinVersion,
  23. MaxVersion,
  24. KnownRunNote,
  25. KnownCompileNote,
  26. RecompileOpt: string;
  27. ResultCode : longint;
  28. KnownRunError : longint;
  29. KnownCompileError : longint;
  30. NeedRecompile : boolean;
  31. NeedLibrary : boolean;
  32. NeededAfter : boolean;
  33. IsInteractive : boolean;
  34. IsKnownRunError,
  35. IsKnownCompileError : boolean;
  36. NoRun : boolean;
  37. UsesGraph : boolean;
  38. ShouldFail : boolean;
  39. Timeout : longint;
  40. Category : string;
  41. Note : string;
  42. Files : string;
  43. ConfigFileSrc : string;
  44. ConfigFileDst : string;
  45. WpoParas : string;
  46. WpoPasses : longint;
  47. DelFiles : string;
  48. ExpectMsgs : array of longint;
  49. end;
  50. // Test run data
  51. TTestRunData = Record
  52. logfile: string;
  53. longlogfile : string;
  54. os: string;
  55. cpu: string;
  56. category: string;
  57. version: string;
  58. submitter: string;
  59. machine: string;
  60. config : string;
  61. description : string;
  62. Date : TDateTime;
  63. CompilerDate,
  64. CompilerFullVersion,
  65. CompilerRevision,
  66. TestsRevision,
  67. RTLRevision,
  68. PackagesRevision : String;
  69. CPUID : Integer;
  70. OSID : Integer;
  71. VersionID : Integer;
  72. CategoryID : Integer;
  73. RunID : Int64;
  74. //ConfigID : Integer;
  75. PlatformID : Integer;
  76. StatusCount : Array[TTestStatus] of Integer;
  77. end;
  78. { TTestResultData }
  79. TTestResultData = record
  80. PlatformID : Integer;
  81. TestID : Integer;
  82. ID : Int64;
  83. RunID : Int64;
  84. TestResult : TTestStatus;
  85. Log : String;
  86. Date : TDateTime;
  87. function ResultDiffers(aResult : TTestResultData; CompareLog : Boolean = False) : Boolean;
  88. end;
  89. Const
  90. DoVerbose : boolean = false;
  91. DoSQL : boolean = false;
  92. MaxLogSize : LongInt = 50000;
  93. procedure TrimB(var s:string);
  94. procedure TrimE(var s:string);
  95. function upper(const s : string) : string;
  96. procedure Verbose(lvl:TVerboseLevel;const s:string);
  97. function GetConfig(const logprefix,fn:string;var r:TConfig):boolean;
  98. Function GetFileContents (FN : String) : String;
  99. function GetUnitTestConfig(const logprefix,fn,SrcDir: string; var r : TConfig) : Boolean;
  100. const
  101. { Constants used in IsAbsolute function }
  102. TargetHasDosStyleDirectories : boolean = false;
  103. TargetAmigaLike : boolean = false;
  104. TargetIsMacOS : boolean = false;
  105. TargetIsUnix : boolean = false;
  106. { File path helper functions }
  107. function SplitPath(const s:string):string;
  108. function SplitBasePath(const s:string): string;
  109. Function SplitFileName(const s:string):string;
  110. Function SplitFileBase(const s:string):string;
  111. Function SplitFileExt(const s:string):string;
  112. Function FileExists (Const F : String) : Boolean;
  113. Function PathExists (Const F : String) : Boolean;
  114. Function IsAbsolute (Const F : String) : boolean;
  115. function GetToken(var s: string; Delims: TCharSet = [' ']):string;
  116. Implementation
  117. function posr(c : Char; const s : AnsiString) : integer;
  118. var
  119. i : integer;
  120. begin
  121. i := length(s);
  122. while (i>0) and (s[i] <> c) do dec(i);
  123. Result := i;
  124. end;
  125. function GetToken(var s: string; Delims: TCharSet = [' ']):string;
  126. var
  127. i : longint;
  128. p: PChar;
  129. begin
  130. p:=PChar(s);
  131. i:=0;
  132. while (p^ <> #0) and not (p^ in Delims) do begin
  133. Inc(p);
  134. Inc(i);
  135. end;
  136. GetToken:=Copy(s,1,i);
  137. Delete(s,1,i+1);
  138. end;
  139. function SplitPath(const s:string):string;
  140. var
  141. i : longint;
  142. begin
  143. i:=Length(s);
  144. while (i>0) and not(s[i] in ['/','\'{$IFDEF MACOS},':'{$ENDIF}]) do
  145. dec(i);
  146. SplitPath:=Copy(s,1,i);
  147. end;
  148. function SplitBasePath(const s:string): string;
  149. var
  150. i : longint;
  151. begin
  152. i:=1;
  153. while (i<length(s)) and not(s[i] in ['/','\'{$IFDEF MACOS},':'{$ENDIF}]) do
  154. inc(i);
  155. if s[i] in ['/','\'{$IFDEF MACOS},':'{$ENDIF}] then
  156. dec(i);
  157. SplitBasePath:=Copy(s,1,i);
  158. end;
  159. Function SplitFileName(const s:string):string;
  160. begin
  161. Result:=ExtractFileName(S);
  162. end;
  163. Function SplitFileBase(const s:string):string;
  164. begin
  165. Result:=ChangeFileExt(ExtractFileName(S),'');
  166. end;
  167. Function SplitFileExt(const s:string):string;
  168. begin
  169. Result:=ExtractFileExt(S);
  170. end;
  171. Function FileExists (Const F : String) : Boolean;
  172. begin
  173. Result:=SysUtils.FileExists(F);
  174. end;
  175. Function PathExists (Const F : String) : Boolean;
  176. {
  177. Returns True if the file exists, False if not.
  178. }
  179. begin
  180. Result:=DirectoryExists(F);
  181. end;
  182. { extracted from rtl/macos/macutils.inc }
  183. function IsMacFullPath (const path: string): Boolean;
  184. begin
  185. if Pos(':', path) = 0 then {its partial}
  186. IsMacFullPath := false
  187. else if path[1] = ':' then
  188. IsMacFullPath := false
  189. else
  190. IsMacFullPath := true
  191. end;
  192. Function IsAbsolute (Const F : String) : boolean;
  193. {
  194. Returns True if the name F is a absolute file name
  195. }
  196. begin
  197. IsAbsolute:=false;
  198. if TargetHasDosStyleDirectories then
  199. begin
  200. if (F[1]='/') or (F[1]='\') then
  201. IsAbsolute:=true;
  202. if (Length(F)>2) and (F[2]=':') and ((F[3]='\') or (F[3]='/')) then
  203. IsAbsolute:=true;
  204. end
  205. else if TargetAmigaLike then
  206. begin
  207. if (length(F)>0) and (Pos(':',F) <> 0) then
  208. IsAbsolute:=true;
  209. end
  210. else if TargetIsMacOS then
  211. begin
  212. IsAbsolute:=IsMacFullPath(F);
  213. end
  214. { generic case }
  215. else if (F[1]='/') then
  216. IsAbsolute:=true;
  217. end;
  218. procedure Verbose(lvl:TVerboseLevel;const s:string);
  219. begin
  220. case lvl of
  221. V_Normal :
  222. writeln(s);
  223. V_Debug :
  224. if DoVerbose then
  225. writeln('Debug: ',s);
  226. V_SQL :
  227. if DoSQL then
  228. writeln('SQL: ',s);
  229. V_Warning :
  230. writeln('Warning: ',s);
  231. V_Error :
  232. begin
  233. writeln('Error: ',s);
  234. halt(1);
  235. end;
  236. V_Abort :
  237. begin
  238. writeln('Abort: ',s);
  239. halt(0);
  240. end;
  241. end;
  242. Flush(output);
  243. end;
  244. procedure TrimB(var s:string);
  245. begin
  246. while (s<>'') and (s[1] in [' ',#9]) do
  247. delete(s,1,1);
  248. end;
  249. procedure TrimE(var s:string);
  250. begin
  251. while (s<>'') and (s[length(s)] in [' ',#9]) do
  252. delete(s,length(s),1);
  253. end;
  254. function upper(const s : string) : string;
  255. var
  256. i,l : longint;
  257. begin
  258. Result:='';
  259. L:=Length(S);
  260. SetLength(Result,l);
  261. for i:=1 to l do
  262. if s[i] in ['a'..'z'] then
  263. Result[i]:=char(byte(s[i])-32)
  264. else
  265. Result[i]:=s[i];
  266. end;
  267. function GetConfig(const logprefix,fn:string;var r:TConfig):boolean;
  268. var
  269. t : text;
  270. part,code : integer;
  271. l : longint;
  272. p : sizeint;
  273. s,res: string;
  274. function GetEntry(const entry:string):boolean;
  275. var
  276. i : longint;
  277. begin
  278. Getentry:=false;
  279. Res:='';
  280. if Upper(Copy(s,1,length(entry)))=Upper(entry) then
  281. begin
  282. Delete(s,1,length(entry));
  283. TrimB(s);
  284. if (s<>'') then
  285. begin
  286. if (s[1]='=') then
  287. begin
  288. delete(s,1,1);
  289. i:=pos('}',s);
  290. if i=0 then
  291. i:=255
  292. else
  293. dec(i);
  294. res:=Copy(s,1,i);
  295. TrimB(res);
  296. TrimE(res);
  297. end;
  298. Verbose(V_Debug,'Config: '+Entry+' = "'+Res+'"');
  299. GetEntry:=true;
  300. end;
  301. end;
  302. end;
  303. begin
  304. FillChar(r,sizeof(r),0);
  305. GetConfig:=false;
  306. Verbose(V_Debug,'Reading '+fn);
  307. assign(t,fn);
  308. {$I-}
  309. reset(t);
  310. {$I+}
  311. if ioresult<>0 then
  312. begin
  313. Verbose(V_Error,'Can''t open '+fn);
  314. exit;
  315. end;
  316. r.Note:='';
  317. while not eof(t) do
  318. begin
  319. readln(t,s);
  320. if Copy(s,1,3)=#$EF#$BB#$BF then
  321. delete(s,1,3);
  322. TrimB(s);
  323. if s<>'' then
  324. begin
  325. if s[1]='{' then
  326. begin
  327. delete(s,1,1);
  328. TrimB(s);
  329. if (s<>'') and (s[1]='%') then
  330. begin
  331. delete(s,1,1);
  332. if GetEntry('OPT') then
  333. r.NeedOptions:=res
  334. else
  335. if GetEntry('DELOPT') then
  336. r.DelOptions:=res
  337. else
  338. if GetEntry('TARGET') then
  339. r.NeedTarget:=res
  340. else
  341. if GetEntry('SKIPTARGET') then
  342. r.SkipTarget:=res
  343. else
  344. if GetEntry('CPU') then
  345. r.NeedCPU:=res
  346. else
  347. if GetEntry('SKIPCPU') then
  348. r.SkipCPU:=res
  349. else
  350. if GetEntry('SKIPEMU') then
  351. r.SkipEmu:=res
  352. else
  353. if GetEntry('VERSION') then
  354. r.MinVersion:=res
  355. else
  356. if GetEntry('MAXVERSION') then
  357. r.MaxVersion:=res
  358. else
  359. if GetEntry('RESULT') then
  360. Val(res,r.ResultCode,code)
  361. else
  362. if GetEntry('GRAPH') then
  363. r.UsesGraph:=true
  364. else
  365. if GetEntry('FAIL') then
  366. r.ShouldFail:=true
  367. else
  368. if GetEntry('RECOMPILE') then
  369. begin
  370. r.NeedRecompile:=true;
  371. r.RecompileOpt:=res;
  372. end
  373. else
  374. if GetEntry('NORUN') then
  375. r.NoRun:=true
  376. else
  377. if GetEntry('NEEDLIBRARY') then
  378. r.NeedLibrary:=true
  379. else
  380. if GetEntry('NEEDEDAFTER') then
  381. r.NeededAfter:=true
  382. else
  383. if GetEntry('KNOWNRUNERROR') then
  384. begin
  385. r.IsKnownRunError:=true;
  386. if res<>'' then
  387. begin
  388. val(res,l,code);
  389. if code>1 then
  390. begin
  391. part:=code;
  392. val(copy(res,1,code-1),l,code);
  393. delete(res,1,part);
  394. end;
  395. if code=0 then
  396. r.KnownRunError:=l;
  397. if res<>'' then
  398. r.KnownRunNote:=res;
  399. end;
  400. end
  401. else
  402. if GetEntry('KNOWNCOMPILEERROR') then
  403. begin
  404. r.IsKnownCompileError:=true;
  405. if res<>'' then
  406. begin
  407. val(res,l,code);
  408. if code>1 then
  409. begin
  410. part:=code;
  411. val(copy(res,1,code-1),l,code);
  412. delete(res,1,part);
  413. end;
  414. if code=0 then
  415. r.KnownCompileError:=l;
  416. if res<>'' then
  417. r.KnownCompileNote:=res;
  418. end;
  419. end
  420. else
  421. if GetEntry('INTERACTIVE') then
  422. r.IsInteractive:=true
  423. else
  424. if GetEntry('NOTE') then
  425. begin
  426. R.Note:='Note: '+res;
  427. Verbose(V_Normal,r.Note);
  428. end
  429. else
  430. if GetEntry('TIMEOUT') then
  431. Val(res,r.Timeout,code)
  432. else
  433. if GetEntry('FILES') then
  434. r.Files:=res
  435. else
  436. if GetEntry('CONFIGFILE') then
  437. begin
  438. l:=Pos(' ',res);
  439. if l>0 then
  440. begin
  441. r.ConfigFileSrc:=Copy(res,1,l-1);
  442. r.ConfigFileDst:=Copy(res,l+1,Length(res)-l+1);
  443. if r.ConfigFileSrc='' then
  444. Verbose(V_Error,'Config file source is empty');
  445. if r.ConfigFileDst='' then
  446. Verbose(V_Error,'Config file destination is empty');
  447. end
  448. else
  449. begin
  450. r.ConfigFileSrc:=res;
  451. r.ConfigFileDst:=res;
  452. end;
  453. end
  454. else
  455. if GetEntry('WPOPARAS') then
  456. r.wpoparas:=res
  457. else
  458. if GetEntry('WPOPASSES') then
  459. val(res,r.wpopasses,code)
  460. else
  461. if GetEntry('DELFILES') then
  462. r.DelFiles:=res
  463. else
  464. if GetEntry('EXPECTMSGS') then
  465. begin
  466. p:=Pos(',',res);
  467. while p>0 do
  468. begin
  469. Val(Copy(res,1,p-1),l,code);
  470. if code<>0 then
  471. Verbose(V_Error,'Invalid value in EXPECTMSGS list: '+Copy(res,1,p-1));
  472. Insert(l,r.ExpectMsgs,Length(r.ExpectMsgs));
  473. Delete(res,1,p);
  474. p:=Pos(',',res);
  475. end;
  476. Val(res,l,code);
  477. if code<>0 then
  478. Verbose(V_Error,'Invalid value in EXPECTMSGS list: '+res);
  479. Insert(l,r.ExpectMsgs,Length(r.ExpectMsgs));
  480. end
  481. else
  482. Verbose(V_Error,'Unknown entry: '+s);
  483. end;
  484. end
  485. else
  486. break;
  487. end;
  488. end;
  489. close(t);
  490. GetConfig:=true;
  491. end;
  492. Function GetFileContents (FN : String) : String;
  493. Var
  494. F : Text;
  495. S : String;
  496. begin
  497. Result:='';
  498. Assign(F,FN);
  499. {$I-}
  500. Reset(F);
  501. If IOResult<>0 then
  502. Exit;
  503. {$I+}
  504. While Not(EOF(F)) do
  505. begin
  506. ReadLn(F,S);
  507. if length(Result)<MaxLogSize then
  508. Result:=Result+S+LineEnding;
  509. end;
  510. Close(F);
  511. end;
  512. function GetUnitTestConfig(const logprefix,fn,SrcDir : string; var r : TConfig) : Boolean;
  513. var
  514. Path : string;
  515. lClassName : string;
  516. lMethodName : string;
  517. slashpos : integer;
  518. FileName : string;
  519. s,line : string;
  520. Src : TStrings;
  521. begin
  522. Result := False;
  523. FillChar(r,sizeof(r),0);
  524. if pos('.',fn) > 0 then exit; // This is normally not a unit-test
  525. slashpos := posr('/',fn);
  526. if slashpos < 1 then exit;
  527. lMethodName := copy(fn,slashpos+1,length(fn));
  528. Path := copy(fn,1,slashpos-1);
  529. slashpos := posr('/',Path);
  530. if slashpos > 0 then
  531. begin
  532. lClassName := copy(Path,slashpos+1,length(Path));
  533. Path := copy(Path,1,slashpos-1);
  534. end
  535. else
  536. begin
  537. lClassName := Path;
  538. path := '.';
  539. end;
  540. if upper(lClassName[1])<>'T' then exit;
  541. FileName := SrcDir+Path+DirectorySeparator+copy(lowercase(lClassName),2,length(lClassName));
  542. if FileExists(FileName+'.pas') then
  543. FileName := FileName + '.pas'
  544. else if FileExists(FileName+'.pp') then
  545. FileName := FileName + '.pp'
  546. else
  547. exit;
  548. Src:=TStringList.Create;
  549. try
  550. Verbose(V_Debug,logprefix+'Reading: '+FileName);
  551. Src.LoadFromFile(FileName);
  552. for Line in Src do
  553. if Line<>'' then
  554. begin
  555. s:=Line;
  556. TrimB(s);
  557. if SameText(copy(s,1,9),'PROCEDURE') then
  558. begin
  559. if pos(';',s)>11 then
  560. begin
  561. s := copy(s,11,pos(';',s)-11);
  562. TrimB(s);
  563. if SameText(s,lClassName+'.'+lMethodName) then
  564. begin
  565. Result := True;
  566. r.Note:= 'unittest';
  567. end;
  568. end;
  569. end;
  570. end;
  571. finally
  572. Src.Free
  573. end;
  574. end;
  575. { TTestResultData }
  576. function TTestResultData.ResultDiffers(aResult: TTestResultData; CompareLog: Boolean): Boolean;
  577. begin
  578. Result:=(PlatformID<>aResult.PlatFormID);
  579. Result:=Result or (TestID<>aResult.TestID);
  580. Result:=Result or (TestResult<>aResult.TestResult);
  581. if CompareLog and Not Result then
  582. Result:=Log<>aResult.Log;
  583. end;
  584. end.