tcunitsearch.pas 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938
  1. {
  2. This file is part of the Free Component Library (FCL)
  3. Copyright (c) 2018 by Michael Van Canneyt
  4. Unit tests for Pascal-to-Javascript converter class.
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************
  11. Examples:
  12. ./testpas2js --suite=TestCLI_UnitSearch.
  13. ./testpas2js --suite=TestUS_Program
  14. ./testpas2js --suite=TestUS_UsesEmptyFileFail
  15. }
  16. unit TCUnitSearch;
  17. {$mode objfpc}{$H+}
  18. interface
  19. uses
  20. Classes, SysUtils, contnrs,
  21. fpcunit, testregistry,
  22. PScanner, PasTree,
  23. {$IFDEF CheckPasTreeRefCount}PasResolveEval,{$ENDIF}
  24. Pas2jsFileUtils, Pas2jsCompiler, Pas2JSPCUCompiler, Pas2jsFileCache, Pas2jsLogger,
  25. tcmodules;
  26. type
  27. { TTestCompiler }
  28. TTestCompiler = class(TPas2jsPCUCompiler)
  29. private
  30. FExitCode: longint;
  31. protected
  32. function GetExitCode: Longint; override;
  33. procedure SetExitCode(Value: Longint); override;
  34. end;
  35. { TCLIFile }
  36. TCLIFile = class
  37. public
  38. Filename: string;
  39. Source: string;
  40. Age: TPas2jsFileAgeTime;
  41. Attr: TPas2jsFileAttr;
  42. constructor Create(const aFilename, Src: string; aAge: TPas2jsFileAgeTime;
  43. aAttr: TPas2jsFileAttr);
  44. end;
  45. { TCLILogMsg }
  46. TCLILogMsg = class
  47. public
  48. Msg: string;
  49. MsgTxt: string;
  50. MsgType: TMessageType;
  51. MsgNumber: integer;
  52. MsgFile: string;
  53. MsgLine: integer;
  54. MsgCol: integer;
  55. end;
  56. { TCustomTestCLI }
  57. TCustomTestCLI = class(TTestCase)
  58. private
  59. FCurDate: TDateTime;
  60. FErrorCol: integer;
  61. FErrorFile: string;
  62. FErrorLine: integer;
  63. FErrorMsg: string;
  64. FErrorNumber: integer;
  65. FWorkDir: string;
  66. FCompilerExe: string;
  67. FCompiler: TTestCompiler;
  68. FDefaultFileAge: longint;
  69. FFiles: TObjectList; // list of TCLIFile
  70. FLogMsgs: TObjectList; // list ot TCLILogMsg
  71. FParams: TStringList;
  72. {$IFDEF EnablePasTreeGlobalRefCount}
  73. FElementRefCountAtSetup: int64;
  74. {$ENDIF}
  75. function GetExitCode: integer;
  76. function GetFiles(Index: integer): TCLIFile;
  77. function GetLogMsgs(Index: integer): TCLILogMsg;
  78. procedure SetExitCode(const AValue: integer);
  79. procedure SetWorkDir(const AValue: string);
  80. protected
  81. procedure SetUp; override;
  82. procedure TearDown; override;
  83. procedure DoLog(Sender: TObject; const Msg: String);
  84. Function OnReadDirectory(Dir: TPas2jsCachedDirectory): boolean; virtual;
  85. Function OnReadFile(aFilename: string; var aSource: string): boolean; virtual;
  86. procedure OnWriteFile(aFilename: string; Source: string); virtual;
  87. procedure WriteSources;
  88. public
  89. constructor Create; override;
  90. destructor Destroy; override;
  91. procedure Compile(const Args: array of string; ExpectedExitCode: longint = 0);
  92. property Compiler: TTestCompiler read FCompiler;
  93. property CompilerExe: string read FCompilerExe write FCompilerExe;
  94. property Params: TStringList read FParams;
  95. property Files[Index: integer]: TCLIFile read GetFiles; // files an directories
  96. function FileCount: integer;
  97. function FindFile(Filename: string): TCLIFile; // files and directories
  98. function ExpandFilename(const Filename: string): string;
  99. function AddFile(Filename, Source: string): TCLIFile;
  100. function AddFile(Filename: string; const SourceLines: array of string): TCLIFile;
  101. function AddUnit(Filename: string; const Intf, Impl: array of string): TCLIFile;
  102. function AddDir(Filename: string): TCLIFile;
  103. procedure AssertFileExists(Filename: string);
  104. property WorkDir: string read FWorkDir write SetWorkDir;
  105. property DefaultFileAge: longint read FDefaultFileAge write FDefaultFileAge;
  106. property ExitCode: integer read GetExitCode write SetExitCode;
  107. property LogMsgs[Index: integer]: TCLILogMsg read GetLogMsgs;
  108. function GetLogCount: integer;
  109. property ErrorMsg: string read FErrorMsg write FErrorMsg;
  110. property ErrorFile: string read FErrorFile write FErrorFile;
  111. property ErrorLine: integer read FErrorLine write FErrorLine;
  112. property ErrorCol: integer read FErrorCol write FErrorCol;
  113. property ErrorNumber: integer read FErrorNumber write FErrorNumber;
  114. property CurDate: TDateTime read FCurDate write FCurDate;
  115. end;
  116. { TTestCLI_UnitSearch }
  117. TTestCLI_UnitSearch = class(TCustomTestCLI)
  118. published
  119. procedure TestUS_CreateRelativePath;
  120. procedure TestUS_Program;
  121. procedure TestUS_UsesEmptyFileFail;
  122. procedure TestUS_Program_o;
  123. procedure TestUS_Program_FU;
  124. procedure TestUS_Program_FU_o;
  125. procedure TestUS_Program_FE_o;
  126. procedure TestUS_IncludeSameDir;
  127. // uses 'in' modifier
  128. procedure TestUS_UsesInFile;
  129. procedure TestUS_UsesInFile_Duplicate;
  130. procedure TestUS_UsesInFile_IndirectDuplicate;
  131. procedure TestUS_UsesInFile_WorkNotEqProgDir;
  132. procedure TestUS_UsesInFileTwice;
  133. procedure TestUS_UseUnitTwiceFail;
  134. procedure TestUS_UseUnitTwiceViaNameSpace;
  135. // namespace
  136. Procedure TestDefaultNameSpaceLast;
  137. Procedure TestDefaultNameSpaceAfterNameSpace;
  138. Procedure TestNoNameSpaceBeforeDefaultNameSpace;
  139. Procedure TestNoNameSpaceAndDefaultNameSpace;
  140. end;
  141. function LinesToStr(const Lines: array of string): string;
  142. implementation
  143. function LinesToStr(const Lines: array of string): string;
  144. var
  145. i: Integer;
  146. begin
  147. Result:='';
  148. for i:=low(Lines) to high(Lines) do
  149. Result:=Result+Lines[i]+LineEnding;
  150. end;
  151. { TCLIFile }
  152. constructor TCLIFile.Create(const aFilename, Src: string;
  153. aAge: TPas2jsFileAgeTime; aAttr: TPas2jsFileAttr);
  154. begin
  155. Filename:=aFilename;
  156. Source:=Src;
  157. Age:=aAge;
  158. Attr:=aAttr;
  159. end;
  160. { TTestCompiler }
  161. function TTestCompiler.GetExitCode: Longint;
  162. begin
  163. Result:=FExitCode;
  164. end;
  165. procedure TTestCompiler.SetExitCode(Value: Longint);
  166. begin
  167. FExitCode:=Value;
  168. end;
  169. { TCustomTestCLI }
  170. function TCustomTestCLI.GetFiles(Index: integer): TCLIFile;
  171. begin
  172. Result:=TCLIFile(FFiles[Index]);
  173. end;
  174. function TCustomTestCLI.GetExitCode: integer;
  175. begin
  176. Result:=Compiler.ExitCode;
  177. end;
  178. function TCustomTestCLI.GetLogMsgs(Index: integer): TCLILogMsg;
  179. begin
  180. Result:=TCLILogMsg(FLogMsgs[Index]);
  181. end;
  182. procedure TCustomTestCLI.SetExitCode(const AValue: integer);
  183. begin
  184. Compiler.ExitCode:=AValue;
  185. end;
  186. procedure TCustomTestCLI.SetWorkDir(const AValue: string);
  187. var
  188. NewValue: String;
  189. begin
  190. NewValue:=IncludeTrailingPathDelimiter(ExpandFileNamePJ(ResolveDots(AValue)));
  191. if FWorkDir=NewValue then Exit;
  192. FWorkDir:=NewValue;
  193. end;
  194. procedure TCustomTestCLI.SetUp;
  195. begin
  196. {$IFDEF EnablePasTreeGlobalRefCount}
  197. FElementRefCountAtSetup:=TPasElement.GlobalRefCount;
  198. {$ENDIF}
  199. inherited SetUp;
  200. FDefaultFileAge:=DateTimeToFileDate(Now);
  201. WorkDir:=ExtractFilePath(ParamStr(0));
  202. {$IFDEF Windows}
  203. CompilerExe:='P:\bin\pas2js.exe';
  204. {$ELSE}
  205. CompilerExe:='/usr/bin/pas2js';
  206. {$ENDIF}
  207. FCompiler:=TTestCompiler.Create;
  208. //FCompiler.ConfigSupport:=TPas2JSFileConfigSupport.Create(FCompiler);
  209. Compiler.Log.OnLog:=@DoLog;
  210. Compiler.FileCache.OnReadDirectory:=@OnReadDirectory;
  211. Compiler.FileCache.OnReadFile:=@OnReadFile;
  212. Compiler.FileCache.OnWriteFile:=@OnWriteFile;
  213. end;
  214. procedure TCustomTestCLI.TearDown;
  215. {$IFDEF CheckPasTreeRefCount}
  216. var
  217. El: TPasElement;
  218. i: integer;
  219. {$ENDIF}
  220. begin
  221. FreeAndNil(FCompiler);
  222. FParams.Clear;
  223. FFiles.Clear;
  224. FLogMsgs.Clear;
  225. FErrorMsg:='';
  226. FErrorFile:='';
  227. FErrorLine:=0;
  228. FErrorCol:=0;
  229. FErrorNumber:=0;
  230. inherited TearDown;
  231. {$IFDEF EnablePasTreeGlobalRefCount}
  232. if FElementRefCountAtSetup<>TPasElement.GlobalRefCount then
  233. begin
  234. writeln('TCustomTestCLI.TearDown GlobalRefCount Was='+IntToStr(FElementRefCountAtSetup)+' Now='+IntToStr(TPasElement.GlobalRefCount));
  235. {$IFDEF CheckPasTreeRefCount}
  236. El:=TPasElement.FirstRefEl;
  237. while El<>nil do
  238. begin
  239. writeln(' ',GetObjName(El),' RefIds.Count=',El.RefIds.Count,':');
  240. for i:=0 to El.RefIds.Count-1 do
  241. writeln(' ',El.RefIds[i]);
  242. El:=El.NextRefEl;
  243. end;
  244. {$ENDIF}
  245. Halt;
  246. Fail('TCustomTestCLI.TearDown Was='+IntToStr(FElementRefCountAtSetup)+' Now='+IntToStr(TPasElement.GlobalRefCount));
  247. end;
  248. {$ENDIF}
  249. end;
  250. procedure TCustomTestCLI.DoLog(Sender: TObject; const Msg: String);
  251. var
  252. LogMsg: TCLILogMsg;
  253. begin
  254. {$IF defined(VerbosePasResolver) or defined(VerbosePCUFiler)}
  255. writeln('TCustomTestCLI.DoLog ',Msg,' File=',Compiler.Log.LastMsgFile,' Line=',Compiler.Log.LastMsgLine);
  256. {$ENDIF}
  257. LogMsg:=TCLILogMsg.Create;
  258. LogMsg.Msg:=Msg;
  259. LogMsg.MsgTxt:=Compiler.Log.LastMsgTxt;
  260. LogMsg.MsgType:=Compiler.Log.LastMsgType;
  261. LogMsg.MsgFile:=Compiler.Log.LastMsgFile;
  262. LogMsg.MsgLine:=Compiler.Log.LastMsgLine;
  263. LogMsg.MsgCol:=Compiler.Log.LastMsgCol;
  264. LogMsg.MsgNumber:=Compiler.Log.LastMsgNumber;
  265. FLogMsgs.Add(LogMsg);
  266. if (LogMsg.MsgType<=mtError) then
  267. begin
  268. if (ErrorFile='')
  269. or ((ErrorLine<1) and (LogMsg.MsgLine>0)) then
  270. begin
  271. ErrorMsg:=LogMsg.MsgTxt;
  272. ErrorFile:=LogMsg.MsgFile;
  273. ErrorLine:=LogMsg.MsgLine;
  274. ErrorCol:=LogMsg.MsgCol;
  275. end;
  276. end;
  277. end;
  278. function TCustomTestCLI.OnReadDirectory(Dir: TPas2jsCachedDirectory): boolean;
  279. var
  280. i: Integer;
  281. aFile: TCLIFile;
  282. Path: String;
  283. begin
  284. Path:=Dir.Path;
  285. //writeln('TCustomTestCLI.ReadDirectory START ',Path,' ',Dir.Count);
  286. Dir.Add('.',DefaultFileAge,faDirectory,4096);
  287. Dir.Add('..',DefaultFileAge,faDirectory,4096);
  288. for i:=0 to FileCount-1 do
  289. begin
  290. aFile:=Files[i];
  291. if CompareFilenames(ExtractFilePath(aFile.Filename),Path)<>0 then continue;
  292. //writeln('TCustomTestCLI.ReadDirectory ',aFile.Filename);
  293. Dir.Add(ExtractFileName(aFile.Filename),aFile.Age,aFile.Attr,length(aFile.Source));
  294. end;
  295. //writeln('TCustomTestCLI.ReadDirectory END ',Path,' ',Dir.Count);
  296. Result:=true;
  297. end;
  298. function TCustomTestCLI.OnReadFile(aFilename: string; var aSource: string
  299. ): boolean;
  300. var
  301. aFile: TCLIFile;
  302. begin
  303. aFile:=FindFile(aFilename);
  304. //writeln('TCustomTestCLI.ReadFile ',aFilename,' Found=',aFile<>nil);
  305. if aFile=nil then exit(false);
  306. if (faDirectory and aFile.Attr)>0 then
  307. begin
  308. {$IF defined(VerbosePasResolver) or defined(VerbosePCUFiler)}
  309. writeln('[20180224000557] TCustomTestCLI.OnReadFile ',aFilename);
  310. {$ENDIF}
  311. EPas2jsFileCache.Create('TCustomTestCLI.OnReadFile: reading directory '+aFilename);
  312. end;
  313. aSource:=aFile.Source;
  314. //writeln('TCustomTestCLI.OnReadFile ',aFile.Filename,' "',LeftStr(aFile.Source,50),'"');
  315. Result:=true;
  316. end;
  317. procedure TCustomTestCLI.OnWriteFile(aFilename: string; Source: string);
  318. var
  319. aFile: TCLIFile;
  320. {$IF defined(VerboseUnitQueue) or defined(VerbosePCUFiler)}
  321. //i: Integer;
  322. {$ENDIF}
  323. begin
  324. aFile:=FindFile(aFilename);
  325. {$IF defined(VerboseUnitQueue) or defined(VerbosePCUFiler)}
  326. writeln('TCustomTestCLI.WriteFile ',aFilename,' Found=',aFile<>nil,' SrcLen=',length(Source));
  327. {$ENDIF}
  328. if aFile<>nil then
  329. begin
  330. if (faDirectory and aFile.Attr)>0 then
  331. begin
  332. {$IF defined(VerboseUnitQueue) or defined(VerbosePCUFiler)}
  333. writeln('[20180223175616] TCustomTestCLI.OnWriteFile ',aFilename);
  334. {$ENDIF}
  335. raise EPas2jsFileCache.Create('unable to write file to directory "'+aFilename+'"');
  336. end;
  337. end else
  338. begin
  339. {$IF defined(VerboseUnitQueue) or defined(VerbosePCUFiler)}
  340. //writeln('TCustomTestCLI.OnWriteFile FFiles: ',FFiles.Count);
  341. //for i:=0 to FFiles.Count-1 do
  342. //begin
  343. // aFile:=TCLIFile(FFiles[i]);
  344. // writeln(' ',i,': Filename=',aFile.Filename,' ',CompareFilenames(aFile.Filename,aFilename),' Dir=',(aFile.Attr and faDirectory)>0,' Len=',length(aFile.Source));
  345. //end;
  346. {$ENDIF}
  347. aFile:=TCLIFile.Create(aFilename,'',0,0);
  348. FFiles.Add(aFile);
  349. end;
  350. aFile.Source:=Source;
  351. aFile.Attr:=faNormal;
  352. aFile.Age:=DateTimeToFileDate(CurDate);
  353. writeln('TCustomTestCLI.OnWriteFile ',aFile.Filename,' Found=',FindFile(aFilename)<>nil,' "',LeftStr(aFile.Source,50),'" ');
  354. //writeln('TCustomTestCLI.OnWriteFile ',aFile.Source);
  355. end;
  356. procedure TCustomTestCLI.WriteSources;
  357. var
  358. i, j, aRow, aCol: Integer;
  359. aFile: TCLIFile;
  360. SrcLines: TStringList;
  361. Line, aFilename: String;
  362. IsSrc: Boolean;
  363. begin
  364. writeln('TCustomTestCLI.WriteSources START');
  365. aFilename:=ErrorFile;
  366. aRow:=ErrorLine;
  367. aCol:=ErrorCol;
  368. SrcLines:=TStringList.Create;
  369. try
  370. for i:=0 to FileCount-1 do
  371. begin
  372. aFile:=Files[i];
  373. if (faDirectory and aFile.Attr)>0 then continue;
  374. writeln('Testcode:-File="',aFile.Filename,'"----------------------------------:');
  375. SrcLines.Text:=aFile.Source;
  376. IsSrc:=ExtractFilename(aFile.Filename)=ExtractFileName(aFilename);
  377. for j:=1 to SrcLines.Count do
  378. begin
  379. Line:=SrcLines[j-1];
  380. if IsSrc and (j=aRow) then
  381. begin
  382. write('*');
  383. Line:=LeftStr(Line,aCol-1)+'|'+copy(Line,aCol,length(Line));
  384. end;
  385. writeln(Format('%:4d: ',[j]),Line);
  386. end;
  387. end;
  388. finally
  389. SrcLines.Free;
  390. end;
  391. end;
  392. constructor TCustomTestCLI.Create;
  393. begin
  394. inherited Create;
  395. FFiles:=TObjectList.Create(true);
  396. FLogMsgs:=TObjectList.Create(true);
  397. FParams:=TStringList.Create;
  398. CurDate:=Now;
  399. end;
  400. destructor TCustomTestCLI.Destroy;
  401. begin
  402. FreeAndNil(FFiles);
  403. FreeAndNil(FLogMsgs);
  404. FreeAndNil(FParams);
  405. inherited Destroy;
  406. end;
  407. procedure TCustomTestCLI.Compile(const Args: array of string;
  408. ExpectedExitCode: longint);
  409. var
  410. i: Integer;
  411. begin
  412. AssertEquals('Initial System.ExitCode',0,system.ExitCode);
  413. for i:=low(Args) to High(Args) do
  414. Params.Add(Args[i]);
  415. try
  416. try
  417. //writeln('TCustomTestCLI.Compile WorkDir=',WorkDir);
  418. Compiler.Run(CompilerExe,WorkDir,Params,false);
  419. except
  420. on E: ECompilerTerminate do
  421. begin
  422. {$IF defined(VerbosePasResolver) or defined(VerbosePCUFiler)}
  423. writeln('TCustomTestCLI.Compile ',E.ClassName,':',E.Message);
  424. {$ENDIF}
  425. end;
  426. on E: Exception do
  427. begin
  428. {$IF defined(VerbosePasResolver) or defined(VerbosePCUFiler)}
  429. writeln('TCustomTestCLI.Compile ',E.ClassName,':',E.Message);
  430. {$ENDIF}
  431. Fail('TCustomTestCLI.Compile '+E.ClassName+':'+E.Message);
  432. end;
  433. end;
  434. finally
  435. Compiler.Log.CloseOutputFile;
  436. end;
  437. if ExpectedExitCode<>ExitCode then
  438. begin
  439. WriteSources;
  440. AssertEquals('ExitCode',ExpectedExitCode,ExitCode);
  441. end;
  442. end;
  443. function TCustomTestCLI.FileCount: integer;
  444. begin
  445. Result:=FFiles.Count;
  446. end;
  447. function TCustomTestCLI.FindFile(Filename: string): TCLIFile;
  448. var
  449. i: Integer;
  450. begin
  451. Filename:=ExpandFilename(Filename);
  452. for i:=0 to FileCount-1 do
  453. if CompareFilenames(Files[i].Filename,Filename)=0 then
  454. exit(Files[i]);
  455. Result:=nil;
  456. end;
  457. function TCustomTestCLI.ExpandFilename(const Filename: string): string;
  458. begin
  459. Result:=SetDirSeparators(Filename);
  460. if not FilenameIsAbsolute(Result) then
  461. Result:=WorkDir+Result;
  462. Result:=ResolveDots(Result);
  463. end;
  464. function TCustomTestCLI.AddFile(Filename, Source: string): TCLIFile;
  465. begin
  466. Filename:=ExpandFilename(Filename);
  467. {$IFDEF VerbosePCUFiler}
  468. writeln('TCustomTestCLI.AddFile ',Filename);
  469. {$ENDIF}
  470. Result:=FindFile(Filename);
  471. if Result<>nil then
  472. raise Exception.Create('[20180224001050] TCustomTestCLI.AddFile already exists: '+Filename);
  473. Result:=TCLIFile.Create(Filename,Source,DefaultFileAge,faNormal);
  474. FFiles.Add(Result);
  475. AddDir(ExtractFilePath(Filename));
  476. end;
  477. function TCustomTestCLI.AddFile(Filename: string;
  478. const SourceLines: array of string): TCLIFile;
  479. begin
  480. Result:=AddFile(Filename,LinesToStr(SourceLines));
  481. end;
  482. function TCustomTestCLI.AddUnit(Filename: string; const Intf,
  483. Impl: array of string): TCLIFile;
  484. var
  485. Name: String;
  486. begin
  487. Name:=ExtractFilenameOnly(Filename);
  488. Result:=AddFile(Filename,
  489. 'unit '+Name+';'+LineEnding
  490. +'interface'+LineEnding
  491. +LinesToStr(Intf)
  492. +'implementation'+LineEnding
  493. +LinesToStr(Impl)
  494. +'end.'+LineEnding);
  495. end;
  496. function TCustomTestCLI.AddDir(Filename: string): TCLIFile;
  497. var
  498. p: Integer;
  499. Dir: String;
  500. aFile: TCLIFile;
  501. begin
  502. Result:=nil;
  503. Filename:=IncludeTrailingPathDelimiter(ExpandFilename(Filename));
  504. p:=length(Filename);
  505. while p>1 do
  506. begin
  507. if Filename[p]=PathDelim then
  508. begin
  509. Dir:=LeftStr(Filename,p-1);
  510. aFile:=FindFile(Dir);
  511. if Result=nil then
  512. Result:=aFile;
  513. if aFile=nil then
  514. begin
  515. {$IFDEF VerbosePCUFiler}
  516. writeln('TCustomTestCLI.AddDir add Dir=',Dir);
  517. {$ENDIF}
  518. FFiles.Add(TCLIFile.Create(Dir,'',DefaultFileAge,faDirectory));
  519. end
  520. else if (aFile.Attr and faDirectory)=0 then
  521. begin
  522. {$IFDEF VerbosePCUFiler}
  523. writeln('[20180224001036] TCustomTestCLI.AddDir file exists: Dir=',Dir);
  524. {$ENDIF}
  525. raise EPas2jsFileCache.Create('[20180224001036] TCustomTestCLI.AddDir Dir='+Dir);
  526. end;
  527. dec(p);
  528. end else
  529. dec(p);
  530. end;
  531. end;
  532. procedure TCustomTestCLI.AssertFileExists(Filename: string);
  533. var
  534. aFile: TCLIFile;
  535. begin
  536. aFile:=FindFile(Filename);
  537. AssertNotNull('File not found: '+Filename,aFile);
  538. end;
  539. function TCustomTestCLI.GetLogCount: integer;
  540. begin
  541. Result:=FLogMsgs.Count;
  542. end;
  543. { TTestCLI_UnitSearch }
  544. procedure TTestCLI_UnitSearch.TestUS_CreateRelativePath;
  545. procedure DoTest(Filename, BaseDirectory, Expected: string;
  546. UsePointDirectory: boolean = false);
  547. var
  548. Actual: String;
  549. begin
  550. ForcePathDelims(Filename);
  551. ForcePathDelims(BaseDirectory);
  552. ForcePathDelims(Expected);
  553. if not TryCreateRelativePath(Filename,BaseDirectory,UsePointDirectory,true,Actual) then
  554. Actual:=Filename;
  555. AssertEquals('TryCreateRelativePath(File='+Filename+',Base='+BaseDirectory+')',
  556. Expected,Actual);
  557. end;
  558. begin
  559. DoTest('/a','/a','');
  560. DoTest('/a','/a','.',true);
  561. DoTest('/a','/a/','');
  562. DoTest('/a/b','/a/b','');
  563. DoTest('/a/b','/a/b/','');
  564. DoTest('/a','/a/','');
  565. DoTest('/a','','/a');
  566. DoTest('/a/b','/a','b');
  567. DoTest('/a/b','/a/','b');
  568. DoTest('/a/b','/a//','b');
  569. DoTest('/a','/a/b','..');
  570. DoTest('/a','/a/b/','..');
  571. DoTest('/a','/a/b//','..');
  572. DoTest('/a/','/a/b','..');
  573. DoTest('/a','/a/b/c','../..');
  574. DoTest('/a','/a/b//c','../..');
  575. DoTest('/a','/a//b/c','../..');
  576. DoTest('/a','/a//b/c/','../..');
  577. DoTest('/a','/b','/a');
  578. DoTest('~/bin','/','~/bin');
  579. DoTest('$(HOME)/bin','/','$(HOME)/bin');
  580. {$IFDEF MSWindows}
  581. DoTest('D:\a\b\c.pas','D:\a\d\','..\b\c.pas');
  582. {$ENDIF}
  583. end;
  584. procedure TTestCLI_UnitSearch.TestUS_Program;
  585. begin
  586. AddUnit('system.pp',[''],['']);
  587. AddFile('test1.pas',[
  588. 'begin',
  589. 'end.']);
  590. Compile(['test1.pas','-va']);
  591. AssertNotNull('test1.js not found',FindFile('test1.js'));
  592. end;
  593. procedure TTestCLI_UnitSearch.TestUS_UsesEmptyFileFail;
  594. begin
  595. AddFile('system.pp','');
  596. AddFile('test1.pas',[
  597. 'begin',
  598. 'end.']);
  599. Compile(['test1.pas'],ExitCodeSyntaxError);
  600. AssertEquals('ErrorMsg','Expected "unit"',ErrorMsg);
  601. end;
  602. procedure TTestCLI_UnitSearch.TestUS_Program_o;
  603. begin
  604. AddUnit('system.pp',[''],['']);
  605. AddFile('test1.pas',[
  606. 'begin',
  607. 'end.']);
  608. Compile(['test1.pas','-obla.js']);
  609. AssertNotNull('bla.js not found',FindFile('bla.js'));
  610. end;
  611. procedure TTestCLI_UnitSearch.TestUS_Program_FU;
  612. begin
  613. AddUnit('system.pp',[''],['']);
  614. AddFile('test1.pas',[
  615. 'begin',
  616. 'end.']);
  617. AddDir('lib');
  618. Compile(['test1.pas','-FUlib']);
  619. AssertNotNull('lib/test1.js not found',FindFile('lib/test1.js'));
  620. end;
  621. procedure TTestCLI_UnitSearch.TestUS_Program_FU_o;
  622. begin
  623. AddUnit('system.pp',[''],['']);
  624. AddFile('test1.pas',[
  625. 'begin',
  626. 'end.']);
  627. AddDir('lib');
  628. Compile(['test1.pas','-FUlib','-ofoo.js']);
  629. AssertNotNull('lib/system.js not found',FindFile('lib/system.js'));
  630. AssertNotNull('foo.js not found',FindFile('foo.js'));
  631. end;
  632. procedure TTestCLI_UnitSearch.TestUS_Program_FE_o;
  633. begin
  634. AddUnit('system.pp',[''],['']);
  635. AddFile('test1.pas',[
  636. 'begin',
  637. 'end.']);
  638. AddDir('lib');
  639. Compile(['test1.pas','-FElib','-ofoo.js']);
  640. AssertNotNull('lib/system.js not found',FindFile('lib/system.js'));
  641. AssertNotNull('foo.js not found',FindFile('foo.js'));
  642. end;
  643. procedure TTestCLI_UnitSearch.TestUS_IncludeSameDir;
  644. begin
  645. AddUnit('system.pp',[''],['']);
  646. AddFile('sub/defines.inc',[
  647. '{$Define foo}',
  648. '']);
  649. AddUnit('sub/unit1.pas',
  650. ['{$I defines.inc}',
  651. '{$ifdef foo}',
  652. 'var a: longint;',
  653. '{$endif}'],
  654. ['']);
  655. AddFile('test1.pas',[
  656. 'uses unit1;',
  657. 'begin',
  658. ' a:=3;',
  659. 'end.']);
  660. AddDir('lib');
  661. Compile(['test1.pas','-Fusub','-FElib','-ofoo.js']);
  662. end;
  663. procedure TTestCLI_UnitSearch.TestUS_UsesInFile;
  664. begin
  665. AddUnit('system.pp',[''],['']);
  666. AddUnit('unit1.pas',
  667. ['uses bird in ''unit2.pas'';',
  668. 'var a: longint;'],
  669. ['']);
  670. AddUnit('unit2.pas',
  671. ['var b: longint;'],
  672. ['']);
  673. AddFile('test1.pas',[
  674. 'uses foo in ''unit1.pas'', bar in ''unit2.pas'';',
  675. 'begin',
  676. ' bar.b:=foo.a;',
  677. ' a:=b;',
  678. 'end.']);
  679. Compile(['test1.pas','-Jc']);
  680. end;
  681. procedure TTestCLI_UnitSearch.TestUS_UsesInFile_Duplicate;
  682. begin
  683. // check if using two different units with same name
  684. AddUnit('system.pp',[''],['']);
  685. AddUnit('unit1.pas',
  686. ['var a: longint;'],
  687. ['']);
  688. AddUnit('sub/unit1.pas',
  689. ['var b: longint;'],
  690. ['']);
  691. AddFile('test1.pas',[
  692. 'uses foo in ''unit1.pas'', bar in ''sub/unit1.pas'';',
  693. 'begin',
  694. ' bar.b:=foo.a;',
  695. ' a:=b;',
  696. 'end.']);
  697. Compile(['test1.pas','-Jc'],ExitCodeSyntaxError);
  698. AssertEquals('ErrorMsg','Duplicate file found: "'+WorkDir+'sub/unit1.pas" and "'+WorkDir+'unit1.pas"',ErrorMsg);
  699. end;
  700. procedure TTestCLI_UnitSearch.TestUS_UsesInFile_IndirectDuplicate;
  701. begin
  702. // check if using two different units with same name
  703. AddUnit('system.pp',[''],['']);
  704. AddUnit('unit1.pas',
  705. ['var a: longint;'],
  706. ['']);
  707. AddUnit('sub/unit1.pas',
  708. ['var b: longint;'],
  709. ['']);
  710. AddUnit('unit2.pas',
  711. ['uses unit1 in ''unit1.pas'';'],
  712. ['']);
  713. AddFile('test1.pas',[
  714. 'uses unit2, foo in ''sub/unit1.pas'';',
  715. 'begin',
  716. 'end.']);
  717. Compile(['test1.pas','-Jc'],ExitCodeSyntaxError);
  718. AssertEquals('ErrorMsg','Duplicate file found: "'+WorkDir+'unit1.pas" and "'+WorkDir+'sub/unit1.pas"',ErrorMsg);
  719. end;
  720. procedure TTestCLI_UnitSearch.TestUS_UsesInFile_WorkNotEqProgDir;
  721. begin
  722. AddUnit('system.pp',[''],['']);
  723. AddUnit('sub/unit2.pas',
  724. ['var a: longint;'],
  725. ['']);
  726. AddUnit('sub/unit1.pas',
  727. ['uses unit2;'],
  728. ['']);
  729. AddFile('sub/test1.pas',[
  730. 'uses foo in ''unit1.pas'';',
  731. 'begin',
  732. 'end.']);
  733. Compile(['sub/test1.pas','-Jc']);
  734. end;
  735. procedure TTestCLI_UnitSearch.TestUS_UsesInFileTwice;
  736. begin
  737. AddUnit('system.pp',[''],['']);
  738. AddUnit('unit1.pas',
  739. ['var a: longint;'],
  740. ['']);
  741. AddFile('test1.pas',[
  742. 'uses foo in ''unit1.pas'', bar in ''unit1.pas'';',
  743. 'begin',
  744. ' bar.a:=foo.a;',
  745. ' a:=a;',
  746. 'end.']);
  747. Compile(['test1.pas','-Jc']);
  748. end;
  749. procedure TTestCLI_UnitSearch.TestUS_UseUnitTwiceFail;
  750. begin
  751. AddUnit('system.pp',[''],['']);
  752. AddUnit('sub.unit1.pas',
  753. ['var a: longint;'],
  754. ['']);
  755. AddFile('test1.pas',[
  756. 'uses sub.Unit1, sub.unit1;',
  757. 'begin',
  758. ' a:=a;',
  759. 'end.']);
  760. Compile(['test1.pas','-FNsub','-Jc'],ExitCodeSyntaxError);
  761. AssertEquals('ErrorMsg','Duplicate identifier "sub.unit1"',ErrorMsg);
  762. end;
  763. procedure TTestCLI_UnitSearch.TestUS_UseUnitTwiceViaNameSpace;
  764. begin
  765. AddUnit('system.pp',[''],['']);
  766. AddUnit('sub.unit1.pas',
  767. ['var a: longint;'],
  768. ['']);
  769. AddFile('test1.pas',[
  770. 'uses unit1, sub.unit1;',
  771. 'begin',
  772. ' unit1.a:=sub.unit1.a;',
  773. ' a:=a;',
  774. 'end.']);
  775. Compile(['test1.pas','-FNsub','-Jc']);
  776. end;
  777. procedure TTestCLI_UnitSearch.TestDefaultNameSpaceLast;
  778. begin
  779. AddUnit('system.pp',[''],['']);
  780. AddUnit('Unit2.pas',
  781. ['var i: longint;'],
  782. ['']);
  783. AddUnit('NS1.Unit2.pas',
  784. ['var j: longint;'],
  785. ['']);
  786. AddFile('test1.pas',[
  787. 'uses unIt2;',
  788. 'var',
  789. ' k: longint;',
  790. 'begin',
  791. ' k:=i;',
  792. 'end.']);
  793. Compile(['test1.pas','','-Jc']);
  794. end;
  795. procedure TTestCLI_UnitSearch.TestDefaultNameSpaceAfterNameSpace;
  796. begin
  797. AddUnit('system.pp',[''],['']);
  798. AddUnit('prg.Unit2.pas',
  799. ['var j: longint;'],
  800. ['']);
  801. AddUnit('sub.Unit2.pas',
  802. ['var i: longint;'],
  803. ['']);
  804. AddFile('prg.test1.pas',[
  805. 'uses unIt2;',
  806. 'var',
  807. ' k: longint;',
  808. 'begin',
  809. ' k:=i;',
  810. 'end.']);
  811. Compile(['prg.test1.pas','-FNsub','-Jc']);
  812. end;
  813. procedure TTestCLI_UnitSearch.TestNoNameSpaceBeforeDefaultNameSpace;
  814. begin
  815. AddUnit('system.pp',[''],['']);
  816. AddUnit('prg.Unit2.pas',
  817. ['var j: longint;'],
  818. ['']);
  819. AddUnit('Unit2.pas',
  820. ['var i: longint;'],
  821. ['']);
  822. AddFile('prg.test1.pas',[
  823. 'uses unIt2;',
  824. 'var',
  825. ' k: longint;',
  826. 'begin',
  827. ' k:=i;',
  828. 'end.']);
  829. Compile(['prg.test1.pas','','-Jc']);
  830. end;
  831. procedure TTestCLI_UnitSearch.TestNoNameSpaceAndDefaultNameSpace;
  832. begin
  833. AddUnit('system.pp',[''],['']);
  834. AddUnit('UnitA.pas',
  835. ['type TBool = boolean;'],
  836. ['']);
  837. AddUnit('ThirdParty.UnitB.pas',
  838. ['uses UnitA;',
  839. 'type TAlias = TBool;'],
  840. ['']);
  841. AddUnit('MyProject.UnitA.pas',
  842. [
  843. 'uses ThirdParty.UnitB;',
  844. 'var a: TAlias;'],
  845. ['']);
  846. AddFile('MyProject.Main.pas',[
  847. 'uses MyProject.UnitA;',
  848. 'var',
  849. ' b: boolean;',
  850. 'begin',
  851. ' b:=a;',
  852. 'end.']);
  853. Compile(['MyProject.Main.pas','','-Jc']);
  854. end;
  855. Initialization
  856. RegisterTests([TTestCLI_UnitSearch]);
  857. end.