testu.pp 15 KB

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