123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605 |
- unit utcprocess;
- {$mode objfpc}{$H+}
- interface
- uses
- Classes, SysUtils, fpcunit, testutils, testregistry, pipes, process;
- type
- { TTestProcess }
- TTestProcess= class(TTestCase)
- private
- FProc: TProcess;
- FProc2: TProcess;
- FProc3: TProcess;
- procedure AssertFileContent(const aFileName, aContent: String);
- procedure AssertFileContent(const aFileName: String; aContent: array of string);
- procedure AssertGenOutLines(const S: String; aCount: integer);
- procedure AssertGenOutLinesFile(const aFileName : string; aCount : Integer);
- procedure CreateInputLinesFile(const aFileName : string; aCount : Integer);
- function GetHelper(const aHelper: string): String;
- function GetTestFile(const aName: string): String;
- function ReadProcessOutput(aProc: TProcess; ReadStdErr : Boolean = False): string;
- procedure WaitForFile(const aFileName: String);
- protected
- procedure CheckHelper(const aHelper : string);
- procedure SetUp; override;
- procedure TearDown; override;
- property Proc : TProcess read FProc;
- property Proc2 : TProcess read FProc2;
- property Proc3 : TProcess read FProc3;
- published
- procedure TestHookUp;
- procedure TestSimple;
- procedure TestSimpleParam;
- Procedure TestExitStatus;
- Procedure TestWaitFor;
- Procedure TestOptionWaitOnExit;
- Procedure TestTerminate;
- Procedure TestPipes;
- Procedure TestWritePipes;
- Procedure TestStdErr;
- Procedure TestStdErrToOutput;
- Procedure TestInputFile;
- Procedure TestOutputFile;
- Procedure TestStdErrFile;
- Procedure TestStdErrToStdOut;
- Procedure TestInputNull;
- Procedure TestOutputFileExistingAppend;
- Procedure TestOutputFileExistingTruncate;
- Procedure TestOutputFileExistingAtStart;
- Procedure TestPipeOut;
- Procedure TestPipeOutToFile;
- Procedure TestPipeInOutToFile;
- Procedure TestPipeRestart;
- end;
- implementation
- uses dateutils;
- const
- dotouch = 'tdotouch';
- docat = 'tdocat';
- doexit = 'tdoexit';
- genout = 't_genout';
- fntouch = 'touch.txt';
- fntestoutput = 'output.txt';
- fntestinput = 'input.txt';
- var
- TestDir : String;
- TmpDir : String;
- procedure TTestProcess.AssertFileContent(const aFileName,aContent : String);
- begin
- AssertFileContent(aFileName,[aContent]);
- end;
- procedure TTestProcess.AssertFileContent(const aFileName : String; aContent : Array of string);
- var
- L : TStrings;
- I : integer;
- begin
- L:=TStringList.Create;
- try
- L.LoadFromFile(aFileName);
- AssertEquals('Line count',Length(aContent),L.Count);
- for I:=0 to L.Count-1 do
- AssertEquals('Line '+Inttostr(i)+'content',aContent[I],L[i]);
- finally
- L.Free;
- end;
- end;
- Procedure TTestProcess.WaitForFile(const aFileName : String);
- var
- aCount : Integer;
- FN : String;
- Exists : boolean;
- begin
- FN:=aFileName;
- aCount:=0;
- Repeat
- Sleep(20);
- Inc(aCount);
- Exists:=FileExists(FN);
- Until (aCount>=50) or Exists;
- AssertTrue('File did not appear: '+FN,Exists);
- Sleep(20);
- end;
- procedure TTestProcess.TestHookUp;
- procedure AssertNoFile(const FN :string);
- begin
- AssertFalse('File '+FN+' does not exist',FileExists(FN));
- end;
- begin
- AssertNotNull('Have process 1',Proc);
- AssertNotNull('Have process 2',Proc2);
- AssertNotNull('Have process 3',Proc3);
- AssertNoFile(fntouch);
- AssertNoFile(GetTestFile(fnTouch));
- AssertNoFile(GetTestFile(fntestoutput));
- end;
- procedure TTestProcess.TestSimple;
- begin
- Proc.Executable:=GetHelper(dotouch);
- Proc.Execute;
- AssertNull('no input stream',Proc.Input);
- AssertNull('no output stream',Proc.Output);
- AssertNull('no error stream',Proc.Stderr);
- WaitForFile(fntouch);
- AssertFileContent(fntouch,fntouch);
- end;
- procedure TTestProcess.TestSimpleParam;
- var
- FN : String;
- begin
- FN:=GetTestFile(fntouch);
- Proc.Executable:=GetHelper(dotouch);
- Proc.Parameters.Add(FN);
- Proc.Execute;
- WaitForFile(FN);
- AssertFileContent(FN,FN);
- end;
- procedure TTestProcess.TestExitStatus;
- // Test that halt(23) results in 23...
- begin
- Proc.Executable:=GetHelper(doexit);
- Proc.Parameters.Add('23');
- Proc.Execute;
- Proc.WaitOnExit;
- AssertEquals('Exit code',23,Proc.ExitStatus);
- end;
- procedure TTestProcess.TestWaitFor;
- var
- N : TDateTime;
- ms : Int64;
- begin
- Proc.Executable:=GetHelper(doexit);
- Proc.Parameters.Add('0');
- Proc.Parameters.Add('1000');
- N:=Now;
- Proc.Execute;
- Proc.WaitOnExit;
- ms:=MilliSecondsBetween(Now,N);
- AssertEquals('Exit code',0,Proc.ExitStatus);
- AssertTrue('Wait time',ms>900);
- end;
- procedure TTestProcess.TestOptionWaitOnExit;
- var
- N : TDateTime;
- ms : Int64;
- begin
- Proc.Executable:=GetHelper(doexit);
- Proc.Parameters.Add('0');
- Proc.Parameters.Add('1000');
- N:=Now;
- Proc.Options:=Proc.Options+[poWaitOnExit];
- Proc.Execute;
- ms:=MilliSecondsBetween(Now,N);
- AssertEquals('Exit code',0,Proc.ExitStatus);
- AssertTrue('Wait time',ms>900);
- end;
- procedure TTestProcess.TestTerminate;
- var
- N : TDateTime;
- ms : Int64;
- begin
- Proc.Executable:=GetHelper(doexit);
- Proc.Parameters.Add('0');
- Proc.Parameters.Add('2000');
- N:=Now;
- Proc.Execute;
- Sleep(500);
- Proc.Terminate(23);
- ms:=MilliSecondsBetween(Now,N);
- AssertTrue('Process exits at once',ms<1000);
- {$IFDEF UNIX}
- // Also check Kill if term will not work
- AssertTrue('Exit status',(15=Proc.ExitStatus) or (9=Proc.ExitStatus));
- {$ENDIF}
- {$IFDEF WINDOWS}
- // Check exit status provided to terminate.
- AssertTrue('Exit status',(23=Proc.ExitCode));
- {$ENDIF}
- end;
- procedure TTestProcess.AssertGenOutLines(const S : String; aCount : integer);
- var
- L : TStrings;
- I : Integer;
- begin
- sleep(100);
- // Writeln('Testing >>',S,'<<');
- L:=TStringList.Create;
- try
- L.Text:=S;
- AssertEquals('Count',aCount,L.Count);
- For I:=1 to aCount do
- AssertEquals('Content Line '+IntToStr(I),'Line '+IntToStr(I),L[I-1]);
- finally
- L.Free;
- end;
- end;
- procedure TTestProcess.AssertGenOutLinesFile(const aFileName: string; aCount: Integer);
- var
- L : TStrings;
- I : Integer;
- begin
- sleep(100);
- // Writeln('Testing file >>',aFileName,'<<');
- L:=TStringList.Create;
- try
- L.LoadFromFile(aFileName);
- AssertEquals('Count',aCount,L.Count);
- For I:=1 to aCount do
- AssertEquals('Content Line '+IntToStr(I),'Line '+IntToStr(I),L[I-1]);
- finally
- L.Free;
- end;
- end;
- procedure TTestProcess.CreateInputLinesFile(const aFileName: string; aCount: Integer);
- var
- L : TStrings;
- I : Integer;
- begin
- // Writeln('Creating Test file >>',aFileName,'<<');
- L:=TStringList.Create;
- try
- For I:=1 to aCount do
- L.Add('Line '+IntToStr(I));
- L.SaveToFile(aFileName);
- finally
- L.Free;
- end;
- end;
- function TTestProcess.ReadProcessOutput(aProc: TProcess; ReadStdErr: Boolean): string;
- var
- aRead,aLen: Integer;
- S : String;
- St : TInputPipeStream;
- begin
- aRead:=0;
- aLen:=0;
- S:='';
- Sleep(100);
- if ReadStdErr then
- st:=aProc.StdErr
- else
- st:=aProc.Output;
- AssertNotNull('Have stream to read output from',St);
- AssertTrue('Read input',aProc.ReadInputStream(St,aRead,aLen,S,100));
- SetLength(S,aRead);
- // Writeln('>>>',S,'<<<');
- Result:=S;
- end;
- procedure TTestProcess.TestPipes;
- var
- S : String;
- begin
- Proc.Executable:=GetHelper(genout);
- Proc.Options:=[poUsePipes];
- Proc.Execute;
- AssertNotNull('input stream',Proc.Input);
- AssertNotNull('output stream',Proc.Output);
- AssertNotNull('error stream',Proc.Stderr);
- S:=ReadProcessOutput(Proc);
- AssertGenOutLines(S,3);
- end;
- procedure TTestProcess.TestWritePipes;
- var
- Sin,Sout : String;
- begin
- Proc.Executable:=GetHelper(docat);
- Proc.Options:=[poUsePipes];
- Proc.Execute;
- // Note: this test will only work for small amounts of data, less than pipe buffer size.
- Sin:='this is some text'+sLineBreak+'And some more text'+sLineBreak;
- Proc.Input.Write(Sin[1],Length(Sin));
- Proc.CloseInput;
- SOut:=ReadProcessOutput(Proc);
- AssertEquals('Out equals in',SIn,Sout);
- end;
- procedure TTestProcess.TestStdErr;
- var
- S : String;
- begin
- Proc.Executable:=GetHelper(genout);
- Proc.Parameters.Add('-3');
- Proc.Options:=[poUsePipes];
- Proc.Execute;
- S:=ReadProcessOutput(Proc,true);
- AssertGenOutLines(S,3);
- end;
- procedure TTestProcess.TestStdErrToOutput;
- var
- S : String;
- begin
- Proc.Executable:=GetHelper(genout);
- Proc.Parameters.Add('-3');
- Proc.Options:=[poUsePipes,poStderrToOutPut];
- Proc.Execute;
- S:=ReadProcessOutput(Proc);
- AssertGenOutLines(S,3);
- end;
- procedure TTestProcess.TestInputFile;
- var
- S : String;
- begin
- CreateInputLinesFile(GetTestFile(fntestinput),3);
- Proc.Executable:=GetHelper(docat);
- Proc.InputDescriptor.FileName:=GetTestFile(fntestinput);
- AssertTrue('Descriptor IOType', Proc.InputDescriptor.IOType=iotFile);
- Proc.OutputDescriptor.IOType:=iotPipe;
- Proc.Execute;
- AssertNull('input stream',Proc.Input);
- AssertNotNull('output stream',Proc.Output);
- AssertNull('error stream',Proc.Stderr);
- S:=ReadProcessOutput(Proc);
- AssertGenOutLines(S,3);
- end;
- procedure TTestProcess.TestOutputFile;
- begin
- Proc.Executable:=GetHelper(genout);
- Proc.OutputDescriptor.FileName:=GetTestFile(fntestoutput);
- Proc.Execute;
- AssertGenOutLinesFile(GetTestFile(fntestoutput),3);
- end;
- procedure TTestProcess.TestStdErrFile;
- begin
- Proc.Executable:=GetHelper(genout);
- Proc.Parameters.Add('-3');
- Proc.ErrorDescriptor.FileName:=GetTestFile(fntestoutput);
- Proc.Execute;
- AssertGenOutLinesFile(GetTestFile(fntestoutput),3);
- end;
- procedure TTestProcess.TestStdErrToStdOut;
- begin
- Proc.Executable:=GetHelper(genout);
- Proc.Options:=Proc.Options+[poStderrToOutPut];
- Proc.Parameters.Add('-3');
- Proc.OutputDescriptor.FileName:=GetTestFile(fntestoutput);
- Proc.Execute;
- AssertGenOutLinesFile(GetTestFile(fntestoutput),3);
- end;
- procedure TTestProcess.TestInputNull;
- var
- B : TBytes;
- begin
- Proc.Executable:=GetHelper(docat);
- Proc.InputDescriptor.IOType:=iotNull;
- Proc.OutputDescriptor.FileName:=GetTestFile(fntestoutput);
- Proc.Execute;
- Sleep(100);
- B:=Sysutils.GetFileContents(GetTestFile(fntestoutput));
- AssertEquals('Empty file',0,Length(B));
- end;
- procedure TTestProcess.TestOutputFileExistingAppend;
- // Check that we actually append
- begin
- CreateInputLinesFile(GetTestFile(fntestoutput),3);
- Proc.Executable:=GetHelper(genout);
- Proc.Parameters.add('3');
- Proc.Parameters.add('3');
- Proc.OutputDescriptor.FileName:=GetTestFile(fntestoutput);
- Proc.OutputDescriptor.FileWriteMode:=fwmAppend;
- Proc.Execute;
- AssertGenOutLinesFile(GetTestFile(fntestoutput),6);
- end;
- procedure TTestProcess.TestOutputFileExistingTruncate;
- // Check that we actually rewrite
- begin
- CreateInputLinesFile(GetTestFile(fntestoutput),6);
- AssertGenOutLinesFile(GetTestFile(fntestoutput),6);
- Proc.Executable:=GetHelper(genout);
- Proc.OutputDescriptor.FileName:=GetTestFile(fntestoutput);
- Proc.OutputDescriptor.FileWriteMode:=fwmTruncate;
- Proc.Execute;
- AssertGenOutLinesFile(GetTestFile(fntestoutput),3);
- end;
- procedure TTestProcess.TestOutputFileExistingAtStart;
- // Check that we actually write at start of file...
- // Write file with 6 lines (1-6), overwrite files with first 3 lines 7-9
- // Result has 7 - 8 - 9 - 4 - 5 -6
- var
- L : TStrings;
- I : Integer;
- begin
- CreateInputLinesFile(GetTestFile(fntestoutput),6);
- Proc.Executable:=GetHelper(genout);
- Proc.Parameters.add('3');
- Proc.Parameters.add('6'); // Offset 6, so first output line is 7
- Proc.OutputDescriptor.FileName:=GetTestFile(fntestoutput);
- Proc.OutputDescriptor.FileWriteMode:=fwmAtStart;
- Proc.Execute;
- sleep(100);
- // Writeln('Testing file >>',aFileName,'<<');
- L:=TStringList.Create;
- try
- L.LoadFromFile(GetTestFile(fntestoutput));
- AssertEquals('Count',6,L.Count);
- For I:=1 to 3 do
- AssertEquals('Content Line '+IntToStr(I),'Line '+IntToStr(I+6),L[I-1]);
- For I:=4 to 6 do
- AssertEquals('Content Line '+IntToStr(I),'Line '+IntToStr(I),L[I-1]);
- finally
- L.Free;
- end;
- end;
- procedure TTestProcess.TestPipeOut;
- { Simulate
- genout | docat
- we read output of docat.
- }
- var
- S : String;
- begin
- Proc.Executable:=GetHelper(genout);
- Proc2.Executable:=GetHelper(docat);
- Proc2.OutputDescriptor.IOType:=iotPipe;
- Proc.OutputDescriptor.Process:=Proc2;
- AssertTrue('Proc2 input is pipe',Proc2.InputDescriptor.IOType=iotPipe);
- Proc2.Execute;
- Proc.execute;
- S:=ReadProcessOutput(Proc2);
- AssertGenOutLines(S,3);
- end;
- procedure TTestProcess.TestPipeOutToFile;
- { Simulate
- genout | docat > file
- we read output from file
- }
- var
- S : String;
- begin
- Proc.Executable:=GetHelper(genout);
- Proc2.Executable:=GetHelper(docat);
- Proc2.OutputDescriptor.FileName:=GetTestFile(fntestoutput);
- Proc.OutputDescriptor.Process:=Proc2;
- AssertTrue('Proc2 input is pipe',Proc2.InputDescriptor.IOType=iotPipe);
- Proc2.Execute;
- Proc.execute;
- AssertGenOutLinesFile(GetTestFile(fntestoutput),3);
- end;
- procedure TTestProcess.TestPipeInOutToFile;
- { Simulate
- docat <input | docat > file
- we read output from file
- }
- var
- S : String;
- begin
- CreateInputLinesFile(GetTestFile(fntestinput),3);
- Proc.Executable:=GetHelper(docat);
- Proc.InputDescriptor.FileName:=GetTestFile(fntestinput);
- Proc2.Executable:=GetHelper(docat);
- Proc2.OutputDescriptor.FileName:=GetTestFile(fntestoutput);
- Proc.OutputDescriptor.Process:=Proc2;
- AssertTrue('Proc2 input is pipe',Proc2.InputDescriptor.IOType=iotPipe);
- Proc2.Execute;
- Proc.execute;
- AssertGenOutLinesFile(GetTestFile(fntestoutput),3);
- end;
- procedure TTestProcess.TestPipeRestart;
- begin
- end;
- function TTestProcess.GetTestFile(const aName: string) : String;
- begin
- if TmpDir='' then
- TmpDir:=GetTempDir(False);
- Result:=IncludeTrailingPathDelimiter(TmpDir)+aName;
- end;
- function TTestProcess.GetHelper(const aHelper: string) : String;
- begin
- if TestDir='' then
- TestDir:=ExtractFilePath(ParamStr(0));
- Result:=IncludeTrailingPathDelimiter(TestDir)+aHelper;
- {$IFDEF WINDOWS}
- Result:=Result+'.exe';
- {$ENDIF}
- end;
- procedure TTestProcess.CheckHelper(const aHelper: string);
- var
- F : String;
- begin
- F:=GetHelper(aHelper);
- AssertTrue('No helper '+F+' please compile '+aHelper+'.pp',FileExists(F));
- end;
- procedure TTestProcess.SetUp;
- begin
- FProc:=TProcess.Create(Nil);
- FProc2:=TProcess.Create(Nil);
- FProc3:=TProcess.Create(Nil);
- // CheckHelper(dols);
- CheckHelper(genout);
- CheckHelper(docat);
- CheckHelper(dotouch);
- CheckHelper(doexit);
- DeleteFile(fntouch);
- DeleteFile(GetTestFile(fntouch));
- DeleteFile(GetTestFile(fntestoutput));
- end;
- procedure TTestProcess.TearDown;
- begin
- FreeAndNil(FProc);
- end;
- initialization
- RegisterTest(TTestProcess);
- end.
|