SynEdit: Test for Beautifier

git-svn-id: trunk@22378 -
This commit is contained in:
martin 2009-11-02 01:36:49 +00:00
parent 65aa0e0875
commit 0c7bfacc34
2 changed files with 313 additions and 10 deletions

View File

@ -29,6 +29,7 @@ type
private
FBaseTestName: String;
FBaseTestNames: Array of String;
FFixedBaseTestNames: Integer;
FForm : TForm;
FSynEdit : TTestSynEdit;
procedure SetBaseTestName(const AValue: String);
@ -63,6 +64,8 @@ type
procedure PopPushBaseName(Add: String);
procedure PopBaseName;
property BaseTestName: String read FBaseTestName write SetBaseTestName;
procedure IncFixedBaseTestNames;
procedure DecFixedBaseTestNames;
property SynEdit: TTestSynEdit read FSynEdit;
property Form: TForm read FForm;
protected
@ -138,6 +141,7 @@ begin
FForm := TForm.Create(nil);
ReCreateEdit;
FForm.Show;
FFixedBaseTestNames := 0;
end;
procedure TTestBase.TearDown;
@ -167,6 +171,15 @@ procedure TTestBase.TestIsText(Name, Text: String; FullText: Boolean = False);
var
i, j, x, y: Integer;
s: String;
function MyDbg(t: String): String;
begin
Result := '';
while(pos(LineEnding, t) > 0) do begin
Result := Result + '"' + copy(t, 1, pos(LineEnding, t)-1) + '" Len='+IntTostr(pos(LineEnding, t)-1) + DbgStr(copy(t, 1, pos(LineEnding, t)-1)) + LineEnding;
system.Delete(t, 1, pos(LineEnding, t)-1+length(LineEnding));
end;
Result := Result + '"' + t + '" Len='+IntTostr(length(t)) + DbgStr(t);
end;
begin
if FullText then
s := SynEdit.TestFullText
@ -186,6 +199,8 @@ begin
inc(i);
end;
Debugln(['IsText - Failed at x/y=(',x,', ',y,') Expected: ',LineEnding, MyDbg(Text), LineEnding,
'Got: ',LineEnding, MyDbg(s), LineEnding ]);
TestFail(Name, Format('IsText - Failed at x/y=(%d, %d)%sExpected: "%s"...%sGot: "%s"%s%s ',
[x, y, LineEnding,
DbgStr(copy(Text,j, i-j+5)), LineEnding,
@ -231,7 +246,7 @@ end;
procedure TTestBase.SetBaseTestName(const AValue: String);
begin
FBaseTestNames := nil;
SetLength(FBaseTestNames, FFixedBaseTestNames);
PushBaseName(AValue);
end;
@ -441,5 +456,15 @@ begin
FBaseTestName := LinesToText(FBaseTestNames, ' ');
end;
procedure TTestBase.IncFixedBaseTestNames;
begin
Inc(FFixedBaseTestNames);
end;
procedure TTestBase.DecFixedBaseTestNames;
begin
Dec(FFixedBaseTestNames);
end;
end.

View File

@ -11,13 +11,36 @@ uses
type
{ TTestSynSelection }
TCallBackData = record
LinePos: Integer;
Indent: Integer; // Indent in spaces (Logical = Physical)
RelativeToLinePos: Integer; // Intend specifies +/- offset from intend on RTLine
// 0: for absolute indent
IndentChars: String ; // use the following string to indent; maybe empty, single char, or string
IndentCharsFromLinePos: Integer; // Line for tab/space mix; set to -1 if unknown
end;
{ TTestSynBeautifier }
TTestSynBeautifier = class(TTestBase)
protected
procedure TestRedoUndo(Name: String; Text: Array of String; X, Y: Integer; Data: Array of const);
procedure TestRedoUndo(Name: String; Text: Array of String; X, Y: Integer;
Data: Array of const;
SelX: Integer = -1; SelY: Integer = -1);
protected
FGetIndentCallBackResult: Boolean;
FCallBackData: Array of TCallBackData;
FGotLogCaret: TPoint;
FGotFirstLinePos, FGotLastLinePos: Integer;
FGotReason: TSynEditorCommand;
function GetIndentCallBack( Sender: TObject; // the beautifier
Editor: TObject; // the synedit
LogCaret, OldLogCaret: TPoint;
FirstLinePos, LastLinePos: Integer;
Reason: TSynEditorCommand; // what caused the evnt
SetIndentProc: TSynBeautifierSetIndentProc
): boolean;
published
(* Test includes:
- no indent if switched off
@ -33,11 +56,19 @@ type
*)
procedure DefaultIndent;
(*
- no unindent, if selection (none persistent) (TODO)
- no un-indent if switched off
- un-indent after VK_BACK (if at end of leading whitespace)
- no un-indent after VK_BACK (if not at end of leading whitespace)
- act on empty line (only leading whitespace; no chars follow) (with/without trim space)
- unindent after empty line (find indent from lines above)
- handle tabs, in leading whitespace (TODO)
- no unindent, if selection (none persistent)
Todo: decide on none-overwritable block
- TODO: fix group undo
*)
//procedure DefaultUnIndent;
procedure DefaultUnIndent;
procedure IndentCallBack;
end;
@ -45,8 +76,9 @@ implementation
{ TTestSynBeautifier }
var SkipGroupUndo: Boolean;
procedure TTestSynBeautifier.TestRedoUndo(Name: String; Text: Array of String;
X, Y: Integer; Data: array of const);
X, Y: Integer; Data: array of const; SelX: Integer = -1; SelY: Integer = -1);
function data2txt(n: Integer): String;
begin
@ -65,7 +97,10 @@ var
begin
PushBaseName(Name + '(no group-undo)');
SetLines(Text);
SetCaret(X, Y);
if (SelX >= 0) then
SetCaretAndSel(SelX, SelY, X, Y)
else
SetCaret(X, Y);
SynEdit.Options := SynEdit.Options - [eoGroupUndo];
for i := 0 to length(data) div 4 - 1 do begin
st := [];
@ -97,6 +132,11 @@ begin
TestIsFullText('2nd Undo #'+IntToStr(i+1), data2txt(i*4+3));
end;
if SkipGroupUndo then begin
PopBaseName;
exit;
end;
PopPushBaseName(Name + '(group-undo)');
SetLines(Text);
SetCaret(X, Y);
@ -161,6 +201,7 @@ procedure TTestSynBeautifier.DefaultIndent;
end;
begin
SkipGroupUndo := False;
ReCreateEdit;
SynEdit.TabWidth := 4;
SynEdit.TrimSpaceType := settMoveCaret;
@ -308,10 +349,10 @@ begin
]);
TestRedoUndo('CTRL-N empty line', TestText, 1,3, [ VK_N, 1, 3, ExpText([ -4, ' ']) ] ); // TODO: should it?
//TestRedoUndo('CTRL-N pos1 unindeted',TestText2, 1,2, [ VK_N, 1, 2, ExpText2([ -2, '']) ] ); // TODO: must not
TestRedoUndo('CTRL-N pos1 unindeted',TestText2, 1,2, [ VK_N, 1, 2, ExpText2([ -2, '']) ] ); // TODO: must not
TestRedoUndo('CTRL-N after space', TestText, 5,2, [ VK_N, 5, 2, ExpText([ -2, ' ']) ] ); // NOT trimmed
//TestRedoUndo('CTRL-N before space', TestText, 1,2, [ VK_N, 1, 2, ExpText([ -2, '']) ] ); // TODO: indents too much
TestRedoUndo('CTRL-N before space', TestText, 1,2, [ VK_N, 1, 2, ExpText([ -2, '']) ] ); // TODO: indents too much
TestRedoUndo('CTRL-N mid space', TestText, 4,2, [ VK_N, 4, 2, ExpText([ -2, ' ']) ] ); // NOT trimmed
@ -434,10 +475,247 @@ begin
]);
{%endregion}
{%region CTRL-N}
PopPushBaseName('SpaceOnly');
// VK_N will be ctrl-n (special detection in TestRedoUndo
TSynBeautifier(SynEdit.Beautifier).IndentType := sbitSpace;
TestRedoUndo('CTRL-N EOL', TestText, 5,1, [ VK_N, 5, 1, ExpText([ -2, '']) ] ); // trimmed
TestRedoUndo('CTRL-N pos1 unindeted',TestText2, 1,2, [ VK_N, 1, 2, ExpText2([ -2, '']) ] ); // TODO: must not
TestRedoUndo('CTRL-N after space', TestText, 5,2, [ VK_N, 5, 2, ExpText([ -2, ' ']) ] );
TestRedoUndo('CTRL-N before space', TestText, 1,2, [ VK_N, 1, 2, ExpText([ -2, '']) ] ); // TODO: indents too much
TestRedoUndo('CTRL-N mid space', TestText, 4,2, [ VK_N, 4, 2, ExpText([ -2, ' ']) ] );
{%endregion}
{%endregion}
end;
procedure TTestSynBeautifier.DefaultUnIndent;
function TestText: TStringArray;
begin
SetLength(Result, 11);
Result[0] := ' a;';
Result[1] := ' b';
Result[2] := ' c';
Result[3] := '';
Result[4] := ' d';
Result[5] := '';
Result[6] := ' x';
Result[7] := '';
Result[8] := ' y';
Result[9] := ' z';
Result[10]:= '';
end;
function ExpText(rpl: array of const): String;
begin
Result := LinesToText(LinesReplace(TestText, rpl))
end;
begin
SkipGroupUndo := True; // Todo
ReCreateEdit;
SynEdit.TabWidth := 4;
SynEdit.TrimSpaceType := settMoveCaret;
BaseTestName := 'UnIndent pff';
SynEdit.Options := SynEdit.Options - [eoTrimTrailingSpaces, eoScrollPastEol, eoAutoIndent];
SynEdit.Options2 := SynEdit.Options2 - [eoPersistentBlock];
TestRedoUndo('simple', TestText, 5,2, [ VK_BACK, 4,2, ExpText([ 2, ' b']) ] );
TestRedoUndo('not at indent-end', TestText, 4,2, [ VK_BACK, 3,2, ExpText([ 2, ' b']) ] );
BaseTestName := 'UnIndent';
SynEdit.Options := SynEdit.Options + [eoAutoIndent] - [eoTrimTrailingSpaces, eoScrollPastEol];
TestRedoUndo('simple', TestText, 5,2, [ VK_BACK, 3,2, ExpText([ 2, ' b']) ] );
TestRedoUndo('simple twice', TestText, 5,2, [
VK_BACK, 3,2, ExpText([ 2, ' b']),
VK_BACK, 1,2, ExpText([ 2, 'b'])
]);
TestRedoUndo('not at indent-end', TestText, 4,2, [ VK_BACK, 3,2, ExpText([ 2, ' b']) ] );
TestRedoUndo('2 level', TestText, 7,3, [
VK_BACK, 5,3, ExpText([ 3, ' c']),
VK_BACK, 3,3, ExpText([ 3, ' c']),
VK_BACK, 1,3, ExpText([ 3, 'c'])
]);
TestRedoUndo('2 lvl, not indent-end', TestText, 6,3, [ VK_BACK, 5,3, ExpText([ 3, ' c']) ] );
TestRedoUndo('2 lvl, not indent-end 2', TestText, 5,3, [ VK_BACK, 4,3, ExpText([ 3, ' c']) ] );
TestRedoUndo('below empty line', TestText, 7,5, [ VK_BACK, 5,5, ExpText([ 5, ' d']) ] );
TestRedoUndo('below empty, not I-End', TestText, 6,5, [ VK_BACK, 5,5, ExpText([ 5, ' d']) ] );
TestRedoUndo('below empty, not I-End 2',TestText, 5,5, [ VK_BACK, 4,5, ExpText([ 5, ' d']) ] );
TestRedoUndo('below empty line, many', TestText, 7,5, [
VK_BACK, 5,5, ExpText([ 5, ' d']),
VK_BACK, 3,5, ExpText([ 5, ' d']),
VK_BACK, 1,5, ExpText([ 5, 'd'])
]);
TestRedoUndo('unindent single space', TestText, 6,10, [ VK_BACK, 5,10, ExpText([ 10, ' z']) ] );
TestRedoUndo('empty line in many', TestText, 6,10, [
VK_BACK, 5,10, ExpText([ 10, ' z']),
VK_BACK, 3,10, ExpText([ 10, ' z']),
VK_BACK, 1,10, ExpText([ 10, 'z'])
]);
SynEdit.Options := SynEdit.Options - [eoTrimTrailingSpaces];
TestRedoUndo('only indent, no text', TestText, 8,3, [
VK_BACK, 7,3, ExpText([ 3, ' ']),
VK_BACK, 5,3, ExpText([ 3, ' ']),
VK_BACK, 3,3, ExpText([ 3, ' ']),
VK_BACK, 1,3, ExpText([ 3, ''])
]);
SynEdit.Options := SynEdit.Options + [eoTrimTrailingSpaces];
TestRedoUndo('only indent, no text (trim)', TestText, 8,3, [
VK_BACK, 7,3, ExpText([ 3, ' ']),
VK_BACK, 5,3, ExpText([ 3, ' ']),
VK_BACK, 3,3, ExpText([ 3, ' ']),
VK_BACK, 1,3, ExpText([ 3, ''])
]);
SynEdit.Options := SynEdit.Options - [eoTrimTrailingSpaces];
TestRedoUndo('no unindent (selection)', TestText, 5,2, [ VK_BACK, 4,2, ExpText([ 2, ' b']) ], 4,2);
TestRedoUndo('no unindent (selection)', TestText, 5,2, [ VK_BACK, 1,2, ExpText([ 2, 'b']) ], 1,2);
SynEdit.Options2 := SynEdit.Options2 + [eoPersistentBlock];
TestRedoUndo('unindent (persist selection)', TestText, 5,2, [ VK_BACK, 3,2, ExpText([ 2, ' b']) ], 4,2);
TestRedoUndo('unindent (persist selection)', TestText, 5,2, [ VK_BACK, 3,2, ExpText([ 2, ' b']) ], 1,2);
SynEdit.Options2 := SynEdit.Options2 - [eoPersistentBlock];
end;
function TTestSynBeautifier.GetIndentCallBack(Sender: TObject; Editor: TObject;
LogCaret, OldLogCaret: TPoint; FirstLinePos, LastLinePos: Integer; Reason: TSynEditorCommand;
SetIndentProc: TSynBeautifierSetIndentProc): boolean;
var
i: integer;
begin
FGotLogCaret := LogCaret;
FGotFirstLinePos := FirstLinePos;
FGotLastLinePos := LastLinePos;
FGotReason := LastLinePos;
for i := 0 to high(FCallBackData) do
SetIndentProc( FCallBackData[i].LinePos,
FCallBackData[i].Indent,
FCallBackData[i].RelativeToLinePos,
FCallBackData[i].IndentChars,
FCallBackData[i].IndentCharsFromLinePos
);
Result := FGetIndentCallBackResult;
end;
procedure TTestSynBeautifier.IndentCallBack;
function TestText: TStringArray;
begin
SetLength(Result, 11);
Result[0] := ' a;';
Result[1] := #9+'b';
Result[2] := ' c';
Result[3] := '';
Result[4] := #32#9+' d';
Result[5] := '';
Result[6] := ' x';
Result[7] := '';
Result[8] := ' y';
Result[9] := ' z';
Result[10]:= '';
end;
function ExpText(rpl: array of const): String;
begin
Result := LinesToText(LinesReplace(TestText, rpl))
end;
procedure SetCB(Res: Boolean; Action: array of const);
var i: integer;
begin
FGetIndentCallBackResult := Res;
FGotLogCaret := Point(-3, -33);
FGotFirstLinePos := -99;
FGotLastLinePos := -99;
FGotReason := 27997;
SetLength(FCallBackData, length(Action) div 5);
for i := 0 to length(Action) div 5 - 1 do begin
FCallBackData[i].LinePos := Action[i*5 + 0].VInteger;
FCallBackData[i].Indent := Action[i*5 + 1].VInteger;
FCallBackData[i].RelativeToLinePos := Action[i*5 + 2].VInteger;
case Action[i*5 + 3].VType of
vtString: FCallBackData[i].IndentChars := AnsiString(Action[i*5 + 3].VString);
vtAnsiString: FCallBackData[i].IndentChars := AnsiString(Action[i*5 + 3].VAnsiString);
vtChar: FCallBackData[i].IndentChars := Action[i*5 + 3].VChar;
end;
FCallBackData[i].IndentCharsFromLinePos := Action[i*5 + 4].VInteger;
end;
end;
begin
ReCreateEdit;
try
SynEdit.Beautifier.OnGetDesiredIndent := @GetIndentCallBack;
SynEdit.TabWidth := 4;
SynEdit.TrimSpaceType := settMoveCaret;
BaseTestName := 'Callback';
SynEdit.Options := SynEdit.Options + [eoAutoIndent] - [eoTrimTrailingSpaces, eoScrollPastEol];
TSynBeautifier(SynEdit.Beautifier).IndentType := sbitSpace; // sbitCopySpaceTab;
SetCB(False, []);
TestRedoUndo('cb result = false', TestText, 3,2, [ VK_RETURN, 5,3, ExpText([ -3, ' ']) ] );
SetCB(True, []);
TestRedoUndo('cb result = true ', TestText, 3,2, [ VK_RETURN, 1,3, ExpText([ -3, '']) ] );
// LinPos, Indend, RelativeLinePos=0, Char(s), indentFromLine=-1,
SetCB(True, [2, 2, 0, '', -1,
3, 3, 0, '', -1
]);
TestRedoUndo('cb absolute', TestText, 3,2, [ VK_RETURN, 1,3, ExpText([ 2, ' b', ' ']) ] ); // caret is NOT adjusted
SetCB(True, [2, 3, 0, '', -1,
3, 3, 0, '', 2,
4, 2, 0, '', -1
]);
TestRedoUndo('cb absolute 2', TestText, 3,2, [ VK_RETURN, 1,3, ExpText([ 2, 2, ' b', ' ', ' c']) ] );
SetCB(True, [2, 7, 0, '', 2,
3, 6, 0, '', 2
]);
TestRedoUndo('cb absolute, keep-cur', TestText, 3,2, [ VK_RETURN, 1,3, ExpText([ 2, #9+' b', #9+' ']) ] );
SetCB(True, [2, -2, 2, '', -1,
3, 3, 2, '', -1
]);
TestRedoUndo('cb relative ', TestText, 3,2, [ VK_RETURN, 1,3, ExpText([ 2, ' b', ' ']) ] );
SetCB(True, [2, 3, 2, '', -1,
3, -1, 2, '', -1
]);
TestRedoUndo('cb relative 2', TestText, 3,2, [ VK_RETURN, 1,3, ExpText([ 2, ' b', ' ']) ] );
SetCB(True, [2, 3, 2, '', -1,
3, 1, 3, '', -1
]);
TestRedoUndo('cb relative 3', TestText, 3,2, [ VK_RETURN, 1,3, ExpText([ 2, ' b', ' ']) ] );
SetCB(True, [2, 3, 2, '', 2,
3, 0, 2, '', 2
]);
TestRedoUndo('cb relative, keep', TestText, 3,2, [ VK_RETURN, 1,3, ExpText([ 2, #9+' b', #9+' ']) ] );
SetCB(True, [1, 9, 0, #9, -1]);
TestRedoUndo('cb abs; char=tab', TestText, 5,1, [ VK_RETURN, 1,2, ExpText([ 1, #9#9+' a;', '']) ] ); // caret is NOT adjusted
finally
SynEdit.Beautifier.OnGetDesiredIndent := nil;
end;
end;
initialization
RegisterTest(TTestSynBeautifier);