utcprocess.pp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  1. unit utcprocess;
  2. {$mode objfpc}{$H+}
  3. interface
  4. uses
  5. Classes, SysUtils, fpcunit, testutils, testregistry, pipes, process;
  6. type
  7. { TTestProcess }
  8. TTestProcess= class(TTestCase)
  9. private
  10. FProc: TProcess;
  11. FProc2: TProcess;
  12. FProc3: TProcess;
  13. procedure AssertFileContent(const aFileName, aContent: String);
  14. procedure AssertFileContent(const aFileName: String; aContent: array of string);
  15. procedure AssertGenOutLines(const S: String; aCount: integer);
  16. procedure AssertGenOutLinesFile(const aFileName : string; aCount : Integer);
  17. procedure CreateInputLinesFile(const aFileName : string; aCount : Integer);
  18. function GetHelper(const aHelper: string): String;
  19. function GetTestFile(const aName: string): String;
  20. function ReadProcessOutput(aProc: TProcess; ReadStdErr : Boolean = False): string;
  21. procedure WaitForFile(const aFileName: String);
  22. protected
  23. procedure CheckHelper(const aHelper : string);
  24. procedure SetUp; override;
  25. procedure TearDown; override;
  26. property Proc : TProcess read FProc;
  27. property Proc2 : TProcess read FProc2;
  28. property Proc3 : TProcess read FProc3;
  29. published
  30. procedure TestHookUp;
  31. procedure TestSimple;
  32. procedure TestSimpleParam;
  33. Procedure TestExitStatus;
  34. Procedure TestWaitFor;
  35. Procedure TestOptionWaitOnExit;
  36. Procedure TestTerminate;
  37. Procedure TestPipes;
  38. Procedure TestWritePipes;
  39. Procedure TestStdErr;
  40. Procedure TestStdErrToOutput;
  41. Procedure TestInputFile;
  42. Procedure TestOutputFile;
  43. Procedure TestStdErrFile;
  44. Procedure TestStdErrToStdOut;
  45. Procedure TestInputNull;
  46. Procedure TestOutputFileExistingAppend;
  47. Procedure TestOutputFileExistingTruncate;
  48. Procedure TestOutputFileExistingAtStart;
  49. Procedure TestPipeOut;
  50. Procedure TestPipeOutToFile;
  51. Procedure TestPipeInOutToFile;
  52. Procedure TestPipeRestart;
  53. end;
  54. implementation
  55. uses dateutils;
  56. const
  57. dotouch = 'tdotouch';
  58. docat = 'tdocat';
  59. doexit = 'tdoexit';
  60. genout = 't_genout';
  61. fntouch = 'touch.txt';
  62. fntestoutput = 'output.txt';
  63. fntestinput = 'input.txt';
  64. var
  65. TestDir : String;
  66. TmpDir : String;
  67. procedure TTestProcess.AssertFileContent(const aFileName,aContent : String);
  68. begin
  69. AssertFileContent(aFileName,[aContent]);
  70. end;
  71. procedure TTestProcess.AssertFileContent(const aFileName : String; aContent : Array of string);
  72. var
  73. L : TStrings;
  74. I : integer;
  75. begin
  76. L:=TStringList.Create;
  77. try
  78. L.LoadFromFile(aFileName);
  79. AssertEquals('Line count',Length(aContent),L.Count);
  80. for I:=0 to L.Count-1 do
  81. AssertEquals('Line '+Inttostr(i)+'content',aContent[I],L[i]);
  82. finally
  83. L.Free;
  84. end;
  85. end;
  86. Procedure TTestProcess.WaitForFile(const aFileName : String);
  87. var
  88. aCount : Integer;
  89. FN : String;
  90. Exists : boolean;
  91. begin
  92. FN:=aFileName;
  93. aCount:=0;
  94. Repeat
  95. Sleep(20);
  96. Inc(aCount);
  97. Exists:=FileExists(FN);
  98. Until (aCount>=50) or Exists;
  99. AssertTrue('File did not appear: '+FN,Exists);
  100. Sleep(20);
  101. end;
  102. procedure TTestProcess.TestHookUp;
  103. procedure AssertNoFile(const FN :string);
  104. begin
  105. AssertFalse('File '+FN+' does not exist',FileExists(FN));
  106. end;
  107. begin
  108. AssertNotNull('Have process 1',Proc);
  109. AssertNotNull('Have process 2',Proc2);
  110. AssertNotNull('Have process 3',Proc3);
  111. AssertNoFile(fntouch);
  112. AssertNoFile(GetTestFile(fnTouch));
  113. AssertNoFile(GetTestFile(fntestoutput));
  114. end;
  115. procedure TTestProcess.TestSimple;
  116. begin
  117. Proc.Executable:=GetHelper(dotouch);
  118. Proc.Execute;
  119. AssertNull('no input stream',Proc.Input);
  120. AssertNull('no output stream',Proc.Output);
  121. AssertNull('no error stream',Proc.Stderr);
  122. WaitForFile(fntouch);
  123. AssertFileContent(fntouch,fntouch);
  124. end;
  125. procedure TTestProcess.TestSimpleParam;
  126. var
  127. FN : String;
  128. begin
  129. FN:=GetTestFile(fntouch);
  130. Proc.Executable:=GetHelper(dotouch);
  131. Proc.Parameters.Add(FN);
  132. Proc.Execute;
  133. WaitForFile(FN);
  134. AssertFileContent(FN,FN);
  135. end;
  136. procedure TTestProcess.TestExitStatus;
  137. // Test that halt(23) results in 23...
  138. begin
  139. Proc.Executable:=GetHelper(doexit);
  140. Proc.Parameters.Add('23');
  141. Proc.Execute;
  142. Proc.WaitOnExit;
  143. AssertEquals('Exit code',23,Proc.ExitStatus);
  144. end;
  145. procedure TTestProcess.TestWaitFor;
  146. var
  147. N : TDateTime;
  148. ms : Int64;
  149. begin
  150. Proc.Executable:=GetHelper(doexit);
  151. Proc.Parameters.Add('0');
  152. Proc.Parameters.Add('1000');
  153. N:=Now;
  154. Proc.Execute;
  155. Proc.WaitOnExit;
  156. ms:=MilliSecondsBetween(Now,N);
  157. AssertEquals('Exit code',0,Proc.ExitStatus);
  158. AssertTrue('Wait time',ms>900);
  159. end;
  160. procedure TTestProcess.TestOptionWaitOnExit;
  161. var
  162. N : TDateTime;
  163. ms : Int64;
  164. begin
  165. Proc.Executable:=GetHelper(doexit);
  166. Proc.Parameters.Add('0');
  167. Proc.Parameters.Add('1000');
  168. N:=Now;
  169. Proc.Options:=Proc.Options+[poWaitOnExit];
  170. Proc.Execute;
  171. ms:=MilliSecondsBetween(Now,N);
  172. AssertEquals('Exit code',0,Proc.ExitStatus);
  173. AssertTrue('Wait time',ms>900);
  174. end;
  175. procedure TTestProcess.TestTerminate;
  176. var
  177. N : TDateTime;
  178. ms : Int64;
  179. begin
  180. Proc.Executable:=GetHelper(doexit);
  181. Proc.Parameters.Add('0');
  182. Proc.Parameters.Add('2000');
  183. N:=Now;
  184. Proc.Execute;
  185. Sleep(500);
  186. Proc.Terminate(23);
  187. ms:=MilliSecondsBetween(Now,N);
  188. AssertTrue('Process exits at once',ms<1000);
  189. {$IFDEF UNIX}
  190. // Also check Kill if term will not work
  191. AssertTrue('Exit status',(15=Proc.ExitStatus) or (9=Proc.ExitStatus));
  192. {$ENDIF}
  193. {$IFDEF WINDOWS}
  194. // Check exit status provided to terminate.
  195. AssertTrue('Exit status',(23=Proc.ExitCode));
  196. {$ENDIF}
  197. end;
  198. procedure TTestProcess.AssertGenOutLines(const S : String; aCount : integer);
  199. var
  200. L : TStrings;
  201. I : Integer;
  202. begin
  203. sleep(100);
  204. // Writeln('Testing >>',S,'<<');
  205. L:=TStringList.Create;
  206. try
  207. L.Text:=S;
  208. AssertEquals('Count',aCount,L.Count);
  209. For I:=1 to aCount do
  210. AssertEquals('Content Line '+IntToStr(I),'Line '+IntToStr(I),L[I-1]);
  211. finally
  212. L.Free;
  213. end;
  214. end;
  215. procedure TTestProcess.AssertGenOutLinesFile(const aFileName: string; aCount: Integer);
  216. var
  217. L : TStrings;
  218. I : Integer;
  219. begin
  220. sleep(100);
  221. // Writeln('Testing file >>',aFileName,'<<');
  222. L:=TStringList.Create;
  223. try
  224. L.LoadFromFile(aFileName);
  225. AssertEquals('Count',aCount,L.Count);
  226. For I:=1 to aCount do
  227. AssertEquals('Content Line '+IntToStr(I),'Line '+IntToStr(I),L[I-1]);
  228. finally
  229. L.Free;
  230. end;
  231. end;
  232. procedure TTestProcess.CreateInputLinesFile(const aFileName: string; aCount: Integer);
  233. var
  234. L : TStrings;
  235. I : Integer;
  236. begin
  237. // Writeln('Creating Test file >>',aFileName,'<<');
  238. L:=TStringList.Create;
  239. try
  240. For I:=1 to aCount do
  241. L.Add('Line '+IntToStr(I));
  242. L.SaveToFile(aFileName);
  243. finally
  244. L.Free;
  245. end;
  246. end;
  247. function TTestProcess.ReadProcessOutput(aProc: TProcess; ReadStdErr: Boolean): string;
  248. var
  249. aRead,aLen: Integer;
  250. S : String;
  251. St : TInputPipeStream;
  252. begin
  253. aRead:=0;
  254. aLen:=0;
  255. S:='';
  256. Sleep(100);
  257. if ReadStdErr then
  258. st:=aProc.StdErr
  259. else
  260. st:=aProc.Output;
  261. AssertNotNull('Have stream to read output from',St);
  262. AssertTrue('Read input',aProc.ReadInputStream(St,aRead,aLen,S,100));
  263. SetLength(S,aRead);
  264. // Writeln('>>>',S,'<<<');
  265. Result:=S;
  266. end;
  267. procedure TTestProcess.TestPipes;
  268. var
  269. S : String;
  270. begin
  271. Proc.Executable:=GetHelper(genout);
  272. Proc.Options:=[poUsePipes];
  273. Proc.Execute;
  274. AssertNotNull('input stream',Proc.Input);
  275. AssertNotNull('output stream',Proc.Output);
  276. AssertNotNull('error stream',Proc.Stderr);
  277. S:=ReadProcessOutput(Proc);
  278. AssertGenOutLines(S,3);
  279. end;
  280. procedure TTestProcess.TestWritePipes;
  281. var
  282. Sin,Sout : String;
  283. begin
  284. Proc.Executable:=GetHelper(docat);
  285. Proc.Options:=[poUsePipes];
  286. Proc.Execute;
  287. // Note: this test will only work for small amounts of data, less than pipe buffer size.
  288. Sin:='this is some text'+sLineBreak+'And some more text'+sLineBreak;
  289. Proc.Input.Write(Sin[1],Length(Sin));
  290. Proc.CloseInput;
  291. SOut:=ReadProcessOutput(Proc);
  292. AssertEquals('Out equals in',SIn,Sout);
  293. end;
  294. procedure TTestProcess.TestStdErr;
  295. var
  296. S : String;
  297. begin
  298. Proc.Executable:=GetHelper(genout);
  299. Proc.Parameters.Add('-3');
  300. Proc.Options:=[poUsePipes];
  301. Proc.Execute;
  302. S:=ReadProcessOutput(Proc,true);
  303. AssertGenOutLines(S,3);
  304. end;
  305. procedure TTestProcess.TestStdErrToOutput;
  306. var
  307. S : String;
  308. begin
  309. Proc.Executable:=GetHelper(genout);
  310. Proc.Parameters.Add('-3');
  311. Proc.Options:=[poUsePipes,poStderrToOutPut];
  312. Proc.Execute;
  313. S:=ReadProcessOutput(Proc);
  314. AssertGenOutLines(S,3);
  315. end;
  316. procedure TTestProcess.TestInputFile;
  317. var
  318. S : String;
  319. begin
  320. CreateInputLinesFile(GetTestFile(fntestinput),3);
  321. Proc.Executable:=GetHelper(docat);
  322. Proc.InputDescriptor.FileName:=GetTestFile(fntestinput);
  323. AssertTrue('Descriptor IOType', Proc.InputDescriptor.IOType=iotFile);
  324. Proc.OutputDescriptor.IOType:=iotPipe;
  325. Proc.Execute;
  326. AssertNull('input stream',Proc.Input);
  327. AssertNotNull('output stream',Proc.Output);
  328. AssertNull('error stream',Proc.Stderr);
  329. S:=ReadProcessOutput(Proc);
  330. AssertGenOutLines(S,3);
  331. end;
  332. procedure TTestProcess.TestOutputFile;
  333. begin
  334. Proc.Executable:=GetHelper(genout);
  335. Proc.OutputDescriptor.FileName:=GetTestFile(fntestoutput);
  336. Proc.Execute;
  337. AssertGenOutLinesFile(GetTestFile(fntestoutput),3);
  338. end;
  339. procedure TTestProcess.TestStdErrFile;
  340. begin
  341. Proc.Executable:=GetHelper(genout);
  342. Proc.Parameters.Add('-3');
  343. Proc.ErrorDescriptor.FileName:=GetTestFile(fntestoutput);
  344. Proc.Execute;
  345. AssertGenOutLinesFile(GetTestFile(fntestoutput),3);
  346. end;
  347. procedure TTestProcess.TestStdErrToStdOut;
  348. begin
  349. Proc.Executable:=GetHelper(genout);
  350. Proc.Options:=Proc.Options+[poStderrToOutPut];
  351. Proc.Parameters.Add('-3');
  352. Proc.OutputDescriptor.FileName:=GetTestFile(fntestoutput);
  353. Proc.Execute;
  354. AssertGenOutLinesFile(GetTestFile(fntestoutput),3);
  355. end;
  356. procedure TTestProcess.TestInputNull;
  357. var
  358. B : TBytes;
  359. begin
  360. Proc.Executable:=GetHelper(docat);
  361. Proc.InputDescriptor.IOType:=iotNull;
  362. Proc.OutputDescriptor.FileName:=GetTestFile(fntestoutput);
  363. Proc.Execute;
  364. Sleep(100);
  365. B:=Sysutils.GetFileContents(GetTestFile(fntestoutput));
  366. AssertEquals('Empty file',0,Length(B));
  367. end;
  368. procedure TTestProcess.TestOutputFileExistingAppend;
  369. // Check that we actually append
  370. begin
  371. CreateInputLinesFile(GetTestFile(fntestoutput),3);
  372. Proc.Executable:=GetHelper(genout);
  373. Proc.Parameters.add('3');
  374. Proc.Parameters.add('3');
  375. Proc.OutputDescriptor.FileName:=GetTestFile(fntestoutput);
  376. Proc.OutputDescriptor.FileWriteMode:=fwmAppend;
  377. Proc.Execute;
  378. AssertGenOutLinesFile(GetTestFile(fntestoutput),6);
  379. end;
  380. procedure TTestProcess.TestOutputFileExistingTruncate;
  381. // Check that we actually rewrite
  382. begin
  383. CreateInputLinesFile(GetTestFile(fntestoutput),6);
  384. AssertGenOutLinesFile(GetTestFile(fntestoutput),6);
  385. Proc.Executable:=GetHelper(genout);
  386. Proc.OutputDescriptor.FileName:=GetTestFile(fntestoutput);
  387. Proc.OutputDescriptor.FileWriteMode:=fwmTruncate;
  388. Proc.Execute;
  389. AssertGenOutLinesFile(GetTestFile(fntestoutput),3);
  390. end;
  391. procedure TTestProcess.TestOutputFileExistingAtStart;
  392. // Check that we actually write at start of file...
  393. // Write file with 6 lines (1-6), overwrite files with first 3 lines 7-9
  394. // Result has 7 - 8 - 9 - 4 - 5 -6
  395. var
  396. L : TStrings;
  397. I : Integer;
  398. begin
  399. CreateInputLinesFile(GetTestFile(fntestoutput),6);
  400. Proc.Executable:=GetHelper(genout);
  401. Proc.Parameters.add('3');
  402. Proc.Parameters.add('6'); // Offset 6, so first output line is 7
  403. Proc.OutputDescriptor.FileName:=GetTestFile(fntestoutput);
  404. Proc.OutputDescriptor.FileWriteMode:=fwmAtStart;
  405. Proc.Execute;
  406. sleep(100);
  407. // Writeln('Testing file >>',aFileName,'<<');
  408. L:=TStringList.Create;
  409. try
  410. L.LoadFromFile(GetTestFile(fntestoutput));
  411. AssertEquals('Count',6,L.Count);
  412. For I:=1 to 3 do
  413. AssertEquals('Content Line '+IntToStr(I),'Line '+IntToStr(I+6),L[I-1]);
  414. For I:=4 to 6 do
  415. AssertEquals('Content Line '+IntToStr(I),'Line '+IntToStr(I),L[I-1]);
  416. finally
  417. L.Free;
  418. end;
  419. end;
  420. procedure TTestProcess.TestPipeOut;
  421. { Simulate
  422. genout | docat
  423. we read output of docat.
  424. }
  425. var
  426. S : String;
  427. begin
  428. Proc.Executable:=GetHelper(genout);
  429. Proc2.Executable:=GetHelper(docat);
  430. Proc2.OutputDescriptor.IOType:=iotPipe;
  431. Proc.OutputDescriptor.Process:=Proc2;
  432. AssertTrue('Proc2 input is pipe',Proc2.InputDescriptor.IOType=iotPipe);
  433. Proc2.Execute;
  434. Proc.execute;
  435. S:=ReadProcessOutput(Proc2);
  436. AssertGenOutLines(S,3);
  437. end;
  438. procedure TTestProcess.TestPipeOutToFile;
  439. { Simulate
  440. genout | docat > file
  441. we read output from file
  442. }
  443. var
  444. S : String;
  445. begin
  446. Proc.Executable:=GetHelper(genout);
  447. Proc2.Executable:=GetHelper(docat);
  448. Proc2.OutputDescriptor.FileName:=GetTestFile(fntestoutput);
  449. Proc.OutputDescriptor.Process:=Proc2;
  450. AssertTrue('Proc2 input is pipe',Proc2.InputDescriptor.IOType=iotPipe);
  451. Proc2.Execute;
  452. Proc.execute;
  453. AssertGenOutLinesFile(GetTestFile(fntestoutput),3);
  454. end;
  455. procedure TTestProcess.TestPipeInOutToFile;
  456. { Simulate
  457. docat <input | docat > file
  458. we read output from file
  459. }
  460. var
  461. S : String;
  462. begin
  463. CreateInputLinesFile(GetTestFile(fntestinput),3);
  464. Proc.Executable:=GetHelper(docat);
  465. Proc.InputDescriptor.FileName:=GetTestFile(fntestinput);
  466. Proc2.Executable:=GetHelper(docat);
  467. Proc2.OutputDescriptor.FileName:=GetTestFile(fntestoutput);
  468. Proc.OutputDescriptor.Process:=Proc2;
  469. AssertTrue('Proc2 input is pipe',Proc2.InputDescriptor.IOType=iotPipe);
  470. Proc2.Execute;
  471. Proc.execute;
  472. AssertGenOutLinesFile(GetTestFile(fntestoutput),3);
  473. end;
  474. procedure TTestProcess.TestPipeRestart;
  475. begin
  476. end;
  477. function TTestProcess.GetTestFile(const aName: string) : String;
  478. begin
  479. if TmpDir='' then
  480. TmpDir:=GetTempDir(False);
  481. Result:=IncludeTrailingPathDelimiter(TmpDir)+aName;
  482. end;
  483. function TTestProcess.GetHelper(const aHelper: string) : String;
  484. begin
  485. if TestDir='' then
  486. TestDir:=ExtractFilePath(ParamStr(0));
  487. Result:=IncludeTrailingPathDelimiter(TestDir)+aHelper;
  488. {$IFDEF WINDOWS}
  489. Result:=Result+'.exe';
  490. {$ENDIF}
  491. end;
  492. procedure TTestProcess.CheckHelper(const aHelper: string);
  493. var
  494. F : String;
  495. begin
  496. F:=GetHelper(aHelper);
  497. AssertTrue('No helper '+F+' please compile '+aHelper+'.pp',FileExists(F));
  498. end;
  499. procedure TTestProcess.SetUp;
  500. begin
  501. FProc:=TProcess.Create(Nil);
  502. FProc2:=TProcess.Create(Nil);
  503. FProc3:=TProcess.Create(Nil);
  504. // CheckHelper(dols);
  505. CheckHelper(genout);
  506. CheckHelper(docat);
  507. CheckHelper(dotouch);
  508. CheckHelper(doexit);
  509. DeleteFile(fntouch);
  510. DeleteFile(GetTestFile(fntouch));
  511. DeleteFile(GetTestFile(fntestoutput));
  512. end;
  513. procedure TTestProcess.TearDown;
  514. begin
  515. FreeAndNil(FProc);
  516. end;
  517. initialization
  518. RegisterTest(TTestProcess);
  519. end.