diff --git a/.gitattributes b/.gitattributes index bdfc717ce2..5e734f00d2 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1024,6 +1024,7 @@ components/codetools/tests/runtestscodetools.lpi svneol=native#text/plain components/codetools/tests/runtestscodetools.lpr svneol=native#text/plain components/codetools/tests/testbasiccodetools.pas svneol=native#text/plain components/codetools/tests/testcfgscript.pas svneol=native#text/plain +components/codetools/tests/testcodecompletion.pas svneol=native#text/plain components/codetools/tests/testcompleteblock.pas svneol=native#text/plain components/codetools/tests/testcth2pas.pas svneol=native#text/pascal components/codetools/tests/testctrangescan.pas svneol=native#text/plain diff --git a/components/codetools/tests/runtestscodetools.lpi b/components/codetools/tests/runtestscodetools.lpi index 8aac6d762e..819b5345f2 100644 --- a/components/codetools/tests/runtestscodetools.lpi +++ b/components/codetools/tests/runtestscodetools.lpi @@ -37,7 +37,7 @@ - + @@ -89,6 +89,11 @@ + + + + + diff --git a/components/codetools/tests/runtestscodetools.lpr b/components/codetools/tests/runtestscodetools.lpr index 27d39ecdf2..43d41c5143 100644 --- a/components/codetools/tests/runtestscodetools.lpr +++ b/components/codetools/tests/runtestscodetools.lpr @@ -38,7 +38,7 @@ uses {$ENDIF} TestBasicCodetools, TestCTRangeScan, TestPascalParser, TestMethodJumpTool, TestStdCodetools, - TestFindDeclaration, TestCompleteBlock, TestRefactoring, fdt_arrays; + TestFindDeclaration, TestCompleteBlock, TestRefactoring, fdt_arrays, testcodecompletion; const ConfigFilename = 'codetools.config'; diff --git a/components/codetools/tests/testcodecompletion.pas b/components/codetools/tests/testcodecompletion.pas new file mode 100644 index 0000000000..794765443f --- /dev/null +++ b/components/codetools/tests/testcodecompletion.pas @@ -0,0 +1,212 @@ +unit TestCodeCompletion; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils, CodeToolManager, CodeCache, + LazLogger, LazFileUtils, fpcunit, testregistry, + TestFinddeclaration; + +type + + { TTestCodeCompletion } + + TTestCodeCompletion = class(TTestCase) + private + procedure CheckDiff(Msg, Expected, Actual: string); + procedure WriteSource(aFilename: string; Line, Col: integer); + procedure Test(Title: string; Src: array of string; Line, Col: integer; + Expected: array of string); + published + procedure TestIntfProcUpdateArgName; + end; + +implementation + +{ TTestCodeCompletion } + +procedure TTestCodeCompletion.CheckDiff(Msg, Expected, Actual: string); +// search diff, ignore changes in spaces +const + SpaceChars = [#9,#10,#13,' ']; +var + ExpectedP, ActualP: PChar; + + function FindLineEnd(p: PChar): PChar; + begin + Result:=p; + while not (Result^ in [#0,#10,#13]) do inc(Result); + end; + + function FindLineStart(p, MinP: PChar): PChar; + begin + while (p>MinP) and not (p[-1] in [#10,#13]) do dec(p); + Result:=p; + end; + + procedure DiffFound; + var + ActLineStartP, ActLineEndP, p, StartPos: PChar; + ExpLine, ActLine: String; + i: Integer; + begin + writeln('Diff found "',Msg,'". Lines:'); + // write correct lines + p:=PChar(Expected); + repeat + StartPos:=p; + while not (p^ in [#0,#10,#13]) do inc(p); + ExpLine:=copy(Expected,StartPos-PChar(Expected)+1,p-StartPos); + if p^ in [#10,#13] then begin + if (p[1] in [#10,#13]) and (p^<>p[1]) then + inc(p,2) + else + inc(p); + end; + if (p<=ExpectedP) and (p^<>#0) then begin + writeln('= ',ExpLine); + end else begin + // diff line + // write actual line + ActLineStartP:=FindLineStart(ActualP,PChar(Actual)); + ActLineEndP:=FindLineEnd(ActualP); + ActLine:=copy(Actual,ActLineStartP-PChar(Actual)+1,ActLineEndP-ActLineStartP); + writeln('- ',ActLine); + // write expected line + writeln('+ ',ExpLine); + // write empty line with pointer ^ + for i:=1 to 2+ExpectedP-StartPos do write(' '); + writeln('^'); + AssertEquals(Msg,ExpLine,ActLine); + break; + end; + until p^=#0; + + writeln('DiffFound Actual:-----------------------'); + writeln(Actual); + writeln('DiffFound Expected:---------------------'); + writeln(Expected); + writeln('DiffFound ------------------------------'); + Fail('diff found, but lines are the same, internal error'); + end; + +var + IsSpaceNeeded: Boolean; + LastChar: Char; +begin + if Expected='' then Expected:=' '; + if Actual='' then Actual:=' '; + ExpectedP:=PChar(Expected); + ActualP:=PChar(Actual); + repeat + //writeln('TTestCodeCompletion.CheckDiff Exp="',ExpectedP^,'" Act="',ActualP^,'"'); + case ExpectedP^ of + #0: + begin + // check that rest of Actual has only spaces + while ActualP^ in SpaceChars do inc(ActualP); + if ActualP^<>#0 then + DiffFound; + exit; + end; + ' ',#9,#10,#13: + begin + // skip space in Expected + IsSpaceNeeded:=false; + if ExpectedP>PChar(Expected) then + LastChar:=ExpectedP[-1] + else + LastChar:=#0; + while ExpectedP^ in SpaceChars do inc(ExpectedP); + if (LastChar in ['a'..'z','A'..'Z','0'..'9','_','$']) + and (ExpectedP^ in ['a'..'z','A'..'Z','0'..'9','_','$']) then + IsSpaceNeeded:=true; + if IsSpaceNeeded and (not (ActualP^ in SpaceChars)) then + DiffFound; + while ActualP^ in SpaceChars do inc(ActualP); + end; + else + while ActualP^ in SpaceChars do inc(ActualP); + if ExpectedP^<>ActualP^ then + DiffFound; + inc(ExpectedP); + inc(ActualP); + end; + until false; +end; + +procedure TTestCodeCompletion.WriteSource(aFilename: string; Line, Col: integer + ); +var + Code: TCodeBuffer; + s: String; + i: Integer; +begin + writeln('Testcode:-File="',aFilename,'"----------------------------------:'); + + Code:=CodeToolBoss.FindFile(aFilename); + if Code=nil then + Fail('file was not loaded/created: "'+aFilename+'"'); + for i:=1 to Code.LineCount do begin + s:=Code.GetLine(i-1,true); + if i=Line then begin + write('*'); + s:=LeftStr(s,Col-1)+'|'+copy(s,Col,length(s)); + end; + if (s='') or not (s[length(s)] in [#10,#13]) then + s+=LineEnding; + write(Format('%:4d: ',[i]),s); + end; +end; + +procedure TTestCodeCompletion.Test(Title: string; Src: array of string; Line, + Col: integer; Expected: array of string); +var + i, NewX, NewY, NewTopLine, BlockTopLine, BlockBottomLine: Integer; + s: String; + NewCode, Code: TCodeBuffer; +begin + Code:=CodeToolBoss.CreateFile('test1.pas'); + s:=''; + for i:=Low(Src) to High(Src) do + s+=Src[i]+LineEnding; + Code.Source:=s; + + if not CodeToolBoss.CompleteCode(Code,Col,Line,Line,NewCode,NewX,NewY,NewTopLine, + BlockTopLine,BlockBottomLine,false) then + begin + WriteSource(Code.Filename,Line,Col); + Fail(Title+'call CompleteCode failed: '+CodeToolBoss.ErrorDbgMsg); + end; + s:=''; + for i:=Low(Expected) to High(Expected) do + s+=Expected[i]+LineEnding; + CheckDiff(Title,s,Code.Source); +end; + +procedure TTestCodeCompletion.TestIntfProcUpdateArgName; +begin + Test('TestIntfProcUpdateArgName', + ['unit test1;' + ,'interface' + ,'procedure DoIt(a: longint);' + ,'implementation' + ,'procedure DoIt(b: longint);' + ,'begin end;' + ,'end.'], + 3,1, + ['unit test1;' + ,'interface' + ,'procedure DoIt(a: longint);' + ,'implementation' + ,'procedure DoIt(a: longint);' + ,'begin end;' + ,'end.']); +end; + +initialization + RegisterTests([TTestCodeCompletion]); +end. +