1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347
|
unit TestLFMTrees;
{$i runtestscodetools.inc}
interface
uses
Classes, SysUtils, CodeToolManager, CodeCache, LFMTrees,
LazLogger, fpcunit, testregistry, TestGlobals;
type
{ TCustomTestLFMTrees }
TCustomTestLFMTrees = class(TTestCase)
private
FControlsCode: TCodeBuffer;
FLFMCode: TCodeBuffer;
FUnitCode: TCodeBuffer;
FSources: TFPList; // list of TCodeBuffer
function GetSourceCount: integer;
function GetSources(Index: integer): TCodeBuffer;
protected
procedure SetUp; override;
procedure TearDown; override;
function AddControls(const aFilename: string = 'controls.pas'): TCodeBuffer;
function AddFormUnit(const Fields: array of string;
const aFormClass: string = 'TForm';
const aFilename: string = 'unit1.pas'): TCodeBuffer;
function AddSource(aFilename, aSource: string): TCodeBuffer;
public
constructor Create; override;
destructor Destroy; override;
procedure CheckLFM;
procedure CheckLFMParseError(ErrorType: TLFMErrorType; const CursorPos: TCodeXYPosition; ErrorMsg: string);
procedure WriteSource(const CursorPos: TCodeXYPosition);
property SourceCount: integer read GetSourceCount;
property Sources[Index: integer]: TCodeBuffer read GetSources;
property ControlsCode: TCodeBuffer read FControlsCode;
property UnitCode: TCodeBuffer read FUnitCode;
property LFMCode: TCodeBuffer read FLFMCode;
end;
{ TTestLFMTrees }
TTestLFMTrees = class(TCustomTestLFMTrees)
published
procedure LFMEmptyForm;
procedure LFMChildComponent;
procedure LFMUnitname;
procedure LFM_RootUnitnameWrong;
procedure LFM_ChildUnitnameWrong;
end;
implementation
{ TCustomTestLFMTrees }
function TCustomTestLFMTrees.GetSourceCount: integer;
begin
Result:=FSources.Count;
end;
function TCustomTestLFMTrees.GetSources(Index: integer): TCodeBuffer;
begin
Result:=TCodeBuffer(FSources[Index]);
end;
procedure TCustomTestLFMTrees.SetUp;
begin
inherited SetUp;
end;
procedure TCustomTestLFMTrees.TearDown;
var
i: Integer;
Buf: TCodeBuffer;
begin
for i:=0 to FSources.Count-1 do begin
Buf:=Sources[i];
Buf.IsDeleted:=true;
Buf.Source:='';
end;
FControlsCode:=nil;
FUnitCode:=nil;
FLFMCode:=nil;
inherited TearDown;
end;
function TCustomTestLFMTrees.AddControls(const aFilename: string): TCodeBuffer;
begin
FControlsCode:=AddSource(aFilename,LinesToStr([
'unit Controls;',
'{$mode objfpc}{$H+}',
'interface',
'uses Classes;',
'type',
' TCaption = type string;',
' TAction = class(TComponent)',
' published',
' property OnExecute: TNotifyEvent;',
' end;',
'',
' TControl = class(TComponent)',
' published',
' property Caption: TCaption;',
' property Left: integer;',
' property Top: integer;',
//' property Width: integer;',
//' property Height: integer;',
' property OnClick: TNotifyEvent;',
' end;',
'',
' TButton = class(TControl)',
' published',
' property Default: Boolean;',
' end;',
'',
' TFormStyle = (fsNormal, fsMDIChild, fsMDIForm, fsStayOnTop, fsSplash, fsSystemStayOnTop);',
' TForm = class(TControl)',
' published',
' property FormStyle: TFormStyle;',
' end;',
'end.',
'implementation',
'end.'
]));
Result:=FControlsCode;
end;
function TCustomTestLFMTrees.AddFormUnit(const Fields: array of string;
const aFormClass: string; const aFilename: string): TCodeBuffer;
var
Src: String;
i: Integer;
begin
Src:='';
for i:=low(Fields) to high(Fields) do begin
Src:=Src+' '+Fields[i]+';'+sLineBreak;
end;
FUnitCode:=AddSource(aFilename,LinesToStr([
'unit Unit1;',
'{$mode objfpc}{$H+}',
'interface',
'uses Controls;',
'type',
' '+aFormClass+'1 = class('+aFormClass+')',
Src+' end;',
'implementation',
'end.'
]));
Result:=FUnitCode;
end;
function TCustomTestLFMTrees.AddSource(aFilename, aSource: string): TCodeBuffer;
begin
Result:=CodeToolBoss.CreateFile(aFilename);
FSources.Add(Result);
Result.Source:=aSource;
end;
constructor TCustomTestLFMTrees.Create;
begin
inherited Create;
FSources:=TFPList.Create;
end;
destructor TCustomTestLFMTrees.Destroy;
begin
FreeAndNil(FSources);
inherited Destroy;
end;
procedure TCustomTestLFMTrees.CheckLFM;
var
LFMTree: TLFMTree;
LFMErr: TLFMError;
begin
LFMTree:=nil;
try
if CodeToolBoss.CheckLFM(UnitCode,LFMCode,LFMTree,true,true,true) then
exit;
WriteSource(CodeXYPosition(CodeToolBoss.ErrorColumn,CodeToolBoss.ErrorLine,CodeToolBoss.ErrorCode));
if LFMTree<>nil then begin
LFMErr:=LFMTree.FirstError;
while LFMErr<>nil do begin
writeln('LFM Error: (',LFMErr.Caret.Y,',',LFMErr.Caret.X,') ',LFMErr.ErrorMessage);
LFMErr:=LFMErr.NextError;
end;
end;
Fail('CheckLFM error "'+CodeToolBoss.ErrorMessage+'"');
finally
LFMTree.Free;
end;
end;
procedure TCustomTestLFMTrees.CheckLFMParseError(ErrorType: TLFMErrorType;
const CursorPos: TCodeXYPosition; ErrorMsg: string);
var
LFMTree: TLFMTree;
LFMErr: TLFMError;
begin
LFMTree:=nil;
try
if CodeToolBoss.CheckLFM(UnitCode,LFMCode,LFMTree,true,true,true) then begin
WriteSource(CursorPos);
Fail('TCustomTestLFMTrees.CheckLFMParseError Missing '+LFMErrorTypeNames[ErrorType]+': '+CursorPos.Code.Filename+'('+IntToStr(CursorPos.Y)+','+IntToStr(CursorPos.X)+'): '+ErrorMsg);
end;
if LFMTree=nil then begin
WriteSource(CursorPos);
Fail('missing LFMTree');
end;
LFMErr:=LFMTree.FirstError;
while LFMErr<>nil do begin
//writeln('LFM Error: (',LFMErr.Caret.Y,',',LFMErr.Caret.X,') ',LFMErr.ErrorMessage);
if (CursorPos.Code=LFMErr.Source)
and (CursorPos.X=LFMErr.Caret.X)
and (CursorPos.Y=LFMErr.Caret.Y)
and (ErrorType=LFMErr.ErrorType)
and (LFMErr.ErrorMessage=ErrorMsg) then
begin
// error found
exit;
end;
LFMErr:=LFMErr.NextError;
end;
writeln('LFM Error Candidates:');
LFMErr:=LFMTree.FirstError;
while LFMErr<>nil do begin
writeln('LFM-Error: ',LFMErr.ErrorType,': (',LFMErr.Caret.Y,',',LFMErr.Caret.X,') ',LFMErr.ErrorMessage);
LFMErr:=LFMErr.NextError;
end;
Fail('TCustomTestLFMTrees.CheckLFMParseError Missing '+LFMErrorTypeNames[ErrorType]+': '+CursorPos.Code.Filename+'('+IntToStr(CursorPos.Y)+','+IntToStr(CursorPos.X)+'): '+ErrorMsg);
finally
LFMTree.Free;
end;
end;
procedure TCustomTestLFMTrees.WriteSource(const CursorPos: TCodeXYPosition);
procedure MyWriteSources(AtCursorPos: boolean);
var
i, LineNo: Integer;
Line: String;
CurCode: TCodeBuffer;
begin
for i:=0 to SourceCount-1 do begin
CurCode:=Sources[i];
if AtCursorPos then begin
if (CurCode<>CursorPos.Code) then continue;
end else begin
if (CurCode=CursorPos.Code) then continue;
end;
writeln('WriteSources ',i,'/',SourceCount,' ',CurCode.Filename);
for LineNo:=1 to CurCode.LineCount do begin
Line:=CurCode.GetLine(LineNo-1,false);
if (CurCode=CursorPos.Code) and (LineNo=CursorPos.Y) then begin
write('*');
Line:=LeftStr(Line,CursorPos.X-1)+'|'+copy(Line,CursorPos.X,length(Line));
end;
writeln(Format('%:4d: ',[LineNo]),Line);
end;
end;
end;
begin
writeln('TCustomTestLFMTrees.WriteSource CursorPos=',dbgs(CursorPos));
// write good sources
MyWriteSources(false);
// write error source
MyWriteSources(true);
end;
{ TTestLFMTrees }
procedure TTestLFMTrees.LFMEmptyForm;
begin
AddControls;
AddFormUnit([]);
FLFMCode:=AddSource('unit1.lfm',LinesToStr([
'object Form1: TForm1',
'end'
]));
CheckLFM;
end;
procedure TTestLFMTrees.LFMChildComponent;
begin
AddControls;
AddFormUnit(['Button1: TButton']);
FLFMCode:=AddSource('unit1.lfm',LinesToStr([
'object Form1: TForm1',
' Left = 300',
' object Button1: TButton',
' Caption = ''ClickMe''',
' Default = True',
' end',
'end'
]));
CheckLFM;
end;
procedure TTestLFMTrees.LFMUnitname;
begin
AddControls;
AddFormUnit(['Button1: Controls.TButton']);
FLFMCode:=AddSource('unit1.lfm',LinesToStr([
'object Form1: unit1/TForm1',
' object Button1: Controls/TButton',
' end',
'end'
]));
CheckLFM;
end;
procedure TTestLFMTrees.LFM_RootUnitnameWrong;
begin
AddControls;
AddFormUnit(['Button1: TButton']);
FLFMCode:=AddSource('unit1.lfm',LinesToStr([
'object Form1: Fool/TForm1',
' object Button1: Controls/TButton',
' end',
'end'
]));
CheckLFMParseError(lfmeMissingRoot,CodeXYPosition(15,1,FLFMCode),'unitname Fool mismatch');
end;
procedure TTestLFMTrees.LFM_ChildUnitnameWrong;
begin
AddControls;
AddFormUnit(['Button1: TButton']);
FLFMCode:=AddSource('unit1.lfm',LinesToStr([
'object Form1: unit1/TForm1',
' object Button1: Fool/TButton',
' end',
'end'
]));
CheckLFMParseError(lfmeObjectIncompatible,CodeXYPosition(19,2,FLFMCode),'Controls expected, but Fool found. See unit1.pas(7,5)');
end;
initialization
RegisterTest(TTestLFMTrees);
end.
|