tsutils.pp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506
  1. { ---------------------------------------------------------------------
  2. utility functions, shared by several programs of the test suite
  3. ---------------------------------------------------------------------}
  4. {$mode objfpc}
  5. {$modeswitch advancedrecords}
  6. {$h+}
  7. unit tsutils;
  8. Interface
  9. uses
  10. classes, sysutils, tstypes;
  11. Type
  12. TOnVerboseEvent = procedure(lvl:TVerboseLevel; const aMsg : String) of object;
  13. var
  14. OnVerbose : TOnVerboseEvent = Nil;
  15. IsCGI : boolean = false;
  16. DoVerbose : boolean = false;
  17. DoSQL : boolean = false;
  18. MaxLogSize : LongInt = 50000;
  19. procedure TrimB(var s:string);
  20. procedure TrimE(var s:string);
  21. function upper(const s : string) : string;
  22. procedure Verbose(lvl:TVerboseLevel; const s:string);
  23. function GetConfig(const logprefix,fn:string;out aConfig:TConfig):boolean;
  24. function GetUnitTestConfig(const logprefix,fn,SrcDir: string; out aConfig : TConfig) : Boolean;
  25. Function GetFileContents (FN : String) : String;
  26. const
  27. { Constants used in IsAbsolute function }
  28. TargetHasDosStyleDirectories : boolean = false;
  29. TargetAmigaLike : boolean = false;
  30. TargetIsMacOS : boolean = false;
  31. TargetIsUnix : boolean = false;
  32. { File path helper functions }
  33. function SplitPath(const s:string):string;
  34. function SplitBasePath(const s:string): string;
  35. Function SplitFileName(const s:string):string;
  36. Function SplitFileBase(const s:string):string;
  37. Function SplitFileExt(const s:string):string;
  38. Function FileExists (Const F : String) : Boolean;
  39. Function PathExists (Const F : String) : Boolean;
  40. Function IsAbsolute (Const F : String) : boolean;
  41. function GetToken(var s: string; Delims: TCharSet = [' ']):string;
  42. Implementation
  43. function posr(c : Char; const s : AnsiString) : integer;
  44. var
  45. i : integer;
  46. begin
  47. i := length(s);
  48. while (i>0) and (s[i] <> c) do dec(i);
  49. Result := i;
  50. end;
  51. function GetToken(var s: string; Delims: TCharSet = [' ']):string;
  52. var
  53. i : longint;
  54. p: PChar;
  55. begin
  56. p:=PChar(s);
  57. i:=0;
  58. while (p^ <> #0) and not (p^ in Delims) do begin
  59. Inc(p);
  60. Inc(i);
  61. end;
  62. GetToken:=Copy(s,1,i);
  63. Delete(s,1,i+1);
  64. end;
  65. function SplitPath(const s:string):string;
  66. var
  67. i : longint;
  68. begin
  69. i:=Length(s);
  70. while (i>0) and not(s[i] in ['/','\'{$IFDEF MACOS},':'{$ENDIF}]) do
  71. dec(i);
  72. SplitPath:=Copy(s,1,i);
  73. end;
  74. function SplitBasePath(const s:string): string;
  75. var
  76. i : longint;
  77. begin
  78. i:=1;
  79. while (i<length(s)) and not(s[i] in ['/','\'{$IFDEF MACOS},':'{$ENDIF}]) do
  80. inc(i);
  81. if s[i] in ['/','\'{$IFDEF MACOS},':'{$ENDIF}] then
  82. dec(i);
  83. SplitBasePath:=Copy(s,1,i);
  84. end;
  85. Function SplitFileName(const s:string):string;
  86. begin
  87. Result:=ExtractFileName(S);
  88. end;
  89. Function SplitFileBase(const s:string):string;
  90. begin
  91. Result:=ChangeFileExt(ExtractFileName(S),'');
  92. end;
  93. Function SplitFileExt(const s:string):string;
  94. begin
  95. Result:=ExtractFileExt(S);
  96. end;
  97. Function FileExists (Const F : String) : Boolean;
  98. begin
  99. Result:=SysUtils.FileExists(F);
  100. end;
  101. Function PathExists (Const F : String) : Boolean;
  102. {
  103. Returns True if the file exists, False if not.
  104. }
  105. begin
  106. Result:=DirectoryExists(F);
  107. end;
  108. { extracted from rtl/macos/macutils.inc }
  109. function IsMacFullPath (const path: string): Boolean;
  110. begin
  111. if Pos(':', path) = 0 then {its partial}
  112. IsMacFullPath := false
  113. else if path[1] = ':' then
  114. IsMacFullPath := false
  115. else
  116. IsMacFullPath := true
  117. end;
  118. Function IsAbsolute (Const F : String) : boolean;
  119. {
  120. Returns True if the name F is a absolute file name
  121. }
  122. begin
  123. IsAbsolute:=false;
  124. if TargetHasDosStyleDirectories then
  125. begin
  126. if (F[1]='/') or (F[1]='\') then
  127. IsAbsolute:=true;
  128. if (Length(F)>2) and (F[2]=':') and ((F[3]='\') or (F[3]='/')) then
  129. IsAbsolute:=true;
  130. end
  131. else if TargetAmigaLike then
  132. begin
  133. if (length(F)>0) and (Pos(':',F) <> 0) then
  134. IsAbsolute:=true;
  135. end
  136. else if TargetIsMacOS then
  137. begin
  138. IsAbsolute:=IsMacFullPath(F);
  139. end
  140. { generic case }
  141. else if (F[1]='/') then
  142. IsAbsolute:=true;
  143. end;
  144. procedure Verbose(lvl:TVerboseLevel;const s:string);
  145. const
  146. lPrefixes : Array[TVerboseLevel] of string = ('Abort','Error','Warning','Info','Debug','SQL');
  147. var
  148. lOutput : String;
  149. Procedure DoOutput;
  150. begin
  151. if not IsCGI then
  152. begin
  153. Writeln(output,lOutput);
  154. Flush(output);
  155. end
  156. else
  157. begin
  158. Writeln(stderr,lOutput);
  159. Flush(stderr);
  160. end;
  161. if Assigned(OnVerbose) then
  162. OnVerbose(lvl,lOutput);
  163. end;
  164. begin
  165. lOutput:=lPrefixes[lvl]+': '+S;
  166. case lvl of
  167. V_Normal :
  168. DoOutput;
  169. V_Debug :
  170. if DoVerbose then
  171. DoOutput;
  172. V_SQL :
  173. if DoSQL then
  174. DoOutput;
  175. V_Warning :
  176. DoOutput;
  177. V_Error :
  178. begin
  179. DoOutput;
  180. if not IsCGI then
  181. halt(1);
  182. end;
  183. V_Abort :
  184. begin
  185. DoOutput;
  186. if not IsCGI then
  187. halt(0);
  188. end;
  189. end;
  190. end;
  191. procedure TrimB(var s:string);
  192. begin
  193. S:=TrimLeft(S);
  194. end;
  195. procedure TrimE(var s:string);
  196. begin
  197. S:=TrimRight(S);
  198. end;
  199. function upper(const s : string) : string;
  200. var
  201. i,l : longint;
  202. begin
  203. Result:='';
  204. L:=Length(S);
  205. SetLength(Result,l);
  206. for i:=1 to l do
  207. if s[i] in ['a'..'z'] then
  208. Result[i]:=char(byte(s[i])-32)
  209. else
  210. Result[i]:=s[i];
  211. end;
  212. function GetConfig(const logprefix,fn:string;out aConfig:TConfig):boolean;
  213. Procedure ExtractCodeAndNote(s : String; out aCode : Integer; out aNote : String);
  214. var
  215. i : Integer;
  216. begin
  217. aCode:=0;
  218. aNote:='';
  219. if S='' then
  220. exit;
  221. I:=1;
  222. While (i<=Length(s)) and (S[I] in ['0'..'9']) do
  223. Inc(i);
  224. if I>1 then
  225. aCode:=StrToIntDef(Copy(S,1,i-1),0);
  226. aNote:=Copy(S,I,Length(S)-I+1);
  227. end;
  228. function GetEntry(S : String; Out entry, Res :string):boolean;
  229. var
  230. i : longint;
  231. begin
  232. Result:=False;
  233. Entry:='';
  234. Res:='';
  235. S:=TrimLeft(s);
  236. if (s='') or (S[1]<>'{') then exit(False);
  237. Delete(S,1,1);
  238. S:=TrimLeft(s);
  239. if (s='') or (S[1]<>'%') then exit(False);
  240. Delete(S,1,1);
  241. S:=TrimLeft(s);
  242. i:=Pos('}',S);
  243. if I=0 then exit(False);
  244. S:=Copy(S,1,I-1);
  245. i:=Pos('=',S);
  246. if I=0 then
  247. Entry:=Trim(S)
  248. else
  249. begin
  250. Entry:=Trim(Copy(S,1,I-1));
  251. Res:=Trim(Copy(S,I+1,Length(S)-I));
  252. end;
  253. Result:=True;
  254. Verbose(V_Debug,'Config: '+Entry+' = "'+Res+'"');
  255. end;
  256. Procedure AnalyseEntry(aEntry,aValue : string);
  257. var
  258. l,p,code : Integer;
  259. begin
  260. case UpperCase(aEntry) of
  261. 'OPT': aConfig.NeedOptions:=aValue;
  262. 'DELOPT': aConfig.DelOptions:=aValue;
  263. 'TARGET': aConfig.NeedTarget:=aValue;
  264. 'SKIPTARGET': aConfig.SkipTarget:=aValue;
  265. 'CPU': aConfig.NeedCPU:=aValue;
  266. 'SKIPCPU': aConfig.SkipCPU:=aValue;
  267. 'SKIPEMU': aConfig.SkipEmu:=aValue;
  268. 'VERSION': aConfig.MinVersion:=aValue;
  269. 'MAXVERSION': aConfig.MaxVersion:=aValue;
  270. 'RESULT' : aConfig.ResultCode:=StrToIntDef(aValue,0);
  271. 'GRAPH' : aConfig.UsesGraph:=true;
  272. 'FAIL' : aConfig.ShouldFail:=true;
  273. 'NORUN': aConfig.NoRun:=true;
  274. 'NEEDLIBRARY': aConfig.NeedLibrary:=true;
  275. 'NEEDEDAFTER': aConfig.NeededAfter:=true;
  276. 'TIMEOUT': aConfig.Timeout:=StrToIntDef(aValue,0);
  277. 'FILES': aConfig.Files:=aValue;
  278. 'WPOPARAS': aConfig.wpoparas:=aValue;
  279. 'WPOPASSES': aConfig.wpopasses:=StrToIntDef(aValue,0);
  280. 'DELFILES': aConfig.DelFiles:=aValue;
  281. 'INTERACTIVE': aConfig.IsInteractive:=true;
  282. 'RECOMPILE':
  283. begin
  284. aConfig.NeedRecompile:=true;
  285. aConfig.RecompileOpt:=aValue;
  286. end;
  287. 'KNOWNRUNERROR':
  288. begin
  289. aConfig.IsKnownRunError:=true;
  290. ExtractCodeAndNote(aValue,aConfig.KnownRunError,aConfig.KnownRunNote);
  291. end;
  292. 'KNOWNCOMPILEERROR':
  293. begin
  294. aConfig.IsKnownCompileError:=true;
  295. ExtractCodeAndNote(aValue,aConfig.KnownCompileError,aConfig.KnownCompileNote);
  296. end;
  297. 'NOTE':
  298. begin
  299. aConfig.Note:=aValue;
  300. Verbose(V_Normal,LogPrefix+aConfig.Note);
  301. end;
  302. 'CONFIGFILE':
  303. begin
  304. l:=Pos(' ',aValue);
  305. if l>0 then
  306. begin
  307. aConfig.ConfigFileSrc:=Trim(Copy(aValue,1,l-1));
  308. aConfig.ConfigFileDst:=Trim(Copy(aValue,l+1,Length(aValue)-l+1));
  309. if aConfig.ConfigFileSrc='' then
  310. Verbose(V_Error,LogPrefix+' File '+fn+' Config file source is empty');
  311. if aConfig.ConfigFileDst='' then
  312. Verbose(V_Error,LogPrefix+' File '+fn+' Config file destination is empty');
  313. end
  314. else
  315. begin
  316. aConfig.ConfigFileSrc:=aValue;
  317. aConfig.ConfigFileDst:=aValue;
  318. end;
  319. end;
  320. 'EXPECTMSGS':
  321. begin
  322. p:=Pos(',',aValue);
  323. while p>0 do
  324. begin
  325. val(Copy(aValue,1,p-1),l,code);
  326. if code<>0 then
  327. Verbose(V_Error,LogPrefix+' File '+fn+' Invalid value in EXPECTMSGS list: '+Copy(aValue,1,p-1));
  328. Insert(l,aConfig.ExpectMsgs,Length(aConfig.ExpectMsgs));
  329. Delete(aValue,1,p);
  330. p:=Pos(',',aValue);
  331. end;
  332. Val(aValue,l,code);
  333. if code<>0 then
  334. Verbose(V_Error,LogPrefix+' File '+fn+' Invalid value in EXPECTMSGS list: '+aValue);
  335. Insert(l,aConfig.ExpectMsgs,Length(aConfig.ExpectMsgs));
  336. end;
  337. else
  338. Verbose(V_Error,LogPrefix+' File '+fn+' Unknown entry: '+aEntry+' with value: '+aValue);
  339. end;
  340. end;
  341. var
  342. l : TStringList;
  343. lErr : longint;
  344. s,aEntry,aValue: string;
  345. begin
  346. Result:=False;
  347. aConfig:=Default(TConfig);
  348. GetConfig:=false;
  349. Verbose(V_Debug,LogPrefix+'Reading '+fn);
  350. lErr:=0;
  351. L:=TStringList.Create;
  352. try
  353. try
  354. L.LoadFromFile(FN);
  355. except
  356. on E : Exception do
  357. begin
  358. Verbose(V_WARNING,'Error '+E.ClassName+' loading '+fn+': '+E.Message);
  359. exit;
  360. end;
  361. end;
  362. For S in L do
  363. begin
  364. if GetEntry(S,aEntry,aValue) then
  365. AnalyseEntry(aEntry,aValue)
  366. else
  367. Inc(lErr);
  368. if lErr>2 then
  369. Break;
  370. end;
  371. finally
  372. L.Free;
  373. end;
  374. Result:=true;
  375. end;
  376. Function GetFileContents (FN : String) : String;
  377. begin
  378. Result:=Sysutils.GetFileAsString(FN);
  379. end;
  380. function GetUnitTestConfig(const logprefix,fn,SrcDir : string; out aConfig : TConfig) : Boolean;
  381. var
  382. Path : string;
  383. lClassName : string;
  384. lMethodName : string;
  385. slashpos : integer;
  386. FileName : string;
  387. s,line : string;
  388. Src : TStrings;
  389. begin
  390. Result := False;
  391. aConfig:=Default(TConfig);
  392. if pos('.',fn) > 0 then exit; // This is normally not a unit-test
  393. slashpos := posr('/',fn);
  394. if slashpos < 1 then exit;
  395. lMethodName := copy(fn,slashpos+1,length(fn));
  396. Path := copy(fn,1,slashpos-1);
  397. slashpos := posr('/',Path);
  398. if slashpos > 0 then
  399. begin
  400. lClassName := copy(Path,slashpos+1,length(Path));
  401. Path := copy(Path,1,slashpos-1);
  402. end
  403. else
  404. begin
  405. lClassName := Path;
  406. path := '.';
  407. end;
  408. if upper(lClassName[1])<>'T' then exit;
  409. FileName := SrcDir+Path+DirectorySeparator+copy(lowercase(lClassName),2,length(lClassName));
  410. if FileExists(FileName+'.pas') then
  411. FileName := FileName + '.pas'
  412. else if FileExists(FileName+'.pp') then
  413. FileName := FileName + '.pp'
  414. else
  415. exit;
  416. Src:=TStringList.Create;
  417. try
  418. Verbose(V_Debug,logprefix+'Reading: '+FileName);
  419. Src.LoadFromFile(FileName);
  420. for Line in Src do
  421. if Line<>'' then
  422. begin
  423. s:=Line;
  424. TrimB(s);
  425. if SameText(copy(s,1,9),'PROCEDURE') then
  426. begin
  427. if pos(';',s)>11 then
  428. begin
  429. s := copy(s,11,pos(';',s)-11);
  430. TrimB(s);
  431. if SameText(s,lClassName+'.'+lMethodName) then
  432. begin
  433. Result := True;
  434. aConfig.Note:= 'unittest';
  435. end;
  436. end;
  437. end;
  438. end;
  439. finally
  440. Src.Free
  441. end;
  442. end;
  443. end.