mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-05-18 02:02:37 +02:00
301 lines
9.7 KiB
ObjectPascal
301 lines
9.7 KiB
ObjectPascal
unit TestStepping;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, math, TestDbgControl, TestDbgTestSuites,
|
|
TTestWatchUtilities, TestCommonSources, TestDbgConfig, TestOutputLogger,
|
|
DbgIntfDebuggerBase, DbgIntfBaseTypes, LazLoggerBase, Forms;
|
|
|
|
type
|
|
|
|
{ TTestStepping }
|
|
|
|
TTestStepping = class(TDBGTestCase)
|
|
protected
|
|
Src: TCommonSource;
|
|
Dbg: TDebuggerIntf;
|
|
procedure TestLocation(ATestName, ABrkName: String; ABreakHitCount: Integer = 1); // only line-number
|
|
function IsAtLocation(ABrkName: String): Boolean; // only line-number
|
|
published
|
|
(* Step over to work with various events happening during the step
|
|
- creation/exit of threads
|
|
- ignored breakpoints (same thread / other thread)
|
|
- TODO: ignored exception: caught inside the step
|
|
- TODO: ignored exception: caught caught outside the step (step to except/finally)
|
|
*)
|
|
procedure TestStepOver;
|
|
procedure TestStepOverInstr;
|
|
end;
|
|
|
|
implementation
|
|
|
|
var
|
|
ControlTest, ControlTestStepOver, ControlTestStepOverInstr: Pointer;
|
|
|
|
procedure TTestStepping.TestLocation(ATestName, ABrkName: String;
|
|
ABreakHitCount: Integer);
|
|
var
|
|
lc: TDBGLocationRec;
|
|
begin
|
|
AssertDebuggerState(dsPause);
|
|
lc := Debugger.LazDebugger.GetLocation;
|
|
TestEquals(ATestName+' '+ABrkName+' Loc', Src.BreakPoints[ABrkName], lc.SrcLine);
|
|
if ABreakHitCount >= 0 then
|
|
TestEquals(ATestName+' '+ABrkName+' HitCnt', Debugger.BreakPointByName(ABrkName).HitCount, ABreakHitCount);
|
|
end;
|
|
|
|
function TTestStepping.IsAtLocation(ABrkName: String): Boolean;
|
|
var
|
|
lc: TDBGLocationRec;
|
|
begin
|
|
lc := Debugger.LazDebugger.GetLocation;
|
|
Result := Src.BreakPoints[ABrkName] = lc.SrcLine;
|
|
end;
|
|
|
|
procedure TTestStepping.TestStepOver;
|
|
var
|
|
ExeName: String;
|
|
MainBrk, BrkDis, BrkHitCnt: TDBGBreakPoint;
|
|
ThreadIdMain: Integer;
|
|
begin
|
|
if SkipTest then exit;
|
|
if not TestControlCanTest(ControlTestStepOver) then exit;
|
|
Src := GetCommonSourceFor(AppDir + 'StepOverPrg.pas');
|
|
TestCompile(Src, ExeName);
|
|
|
|
TestTrue('Start debugger', Debugger.StartDebugger(AppDir, ExeName));
|
|
dbg := Debugger.LazDebugger;
|
|
|
|
try
|
|
MainBrk := Debugger.SetBreakPoint(Src, 'BrkStart');
|
|
Debugger.SetBreakPoint(Src, 'BrkThreadCreateInStep');
|
|
Debugger.SetBreakPoint(Src, 'BrkInterfereByThread');
|
|
Debugger.SetBreakPoint(Src, 'BrkNested');
|
|
|
|
BrkDis := Debugger.SetBreakPoint(Src, 'BrkDisabled');
|
|
BrkHitCnt := Debugger.SetBreakPoint(Src, 'BrkHitCnt');
|
|
BrkDis.Enabled := False;
|
|
BrkHitCnt.BreakHitCount := 999;
|
|
AssertDebuggerNotInErrorState;
|
|
|
|
Debugger.RunToNextPause(dcRun);
|
|
AssertDebuggerState(dsPause);
|
|
TestLocation('Init', 'BrkStart');
|
|
ThreadIdMain := dbg.Threads.CurrentThreads.CurrentThreadId;
|
|
|
|
// Step over a line
|
|
Debugger.RunToNextPause(dcStepOver);
|
|
AssertDebuggerState(dsPause);
|
|
TestLocation('At AfterStep', 'AfterStep', -1);
|
|
|
|
// Step over a longer line
|
|
Debugger.RunToNextPause(dcStepOver);
|
|
AssertDebuggerState(dsPause);
|
|
TestLocation('At AfterStepLongLine', 'AfterStepLongLine', -1);
|
|
|
|
// Step over a subroutine call
|
|
Debugger.RunToNextPause(dcStepOver);
|
|
AssertDebuggerState(dsPause);
|
|
TestLocation('At AfterStepProc', 'AfterStepProc', -1);
|
|
|
|
// Step over a several subroutine calls
|
|
Debugger.RunToNextPause(dcStepOver);
|
|
AssertDebuggerState(dsPause);
|
|
TestLocation('At AfterStepProcLong', 'AfterStepProcLong', -1);
|
|
|
|
// Step over a subroutine call, with sleep
|
|
Debugger.RunToNextPause(dcStepOver);
|
|
AssertDebuggerState(dsPause);
|
|
TestLocation('At AfterStepSleepProc', 'AfterStepSleepProc', -1);
|
|
|
|
// Step over a call to sleep
|
|
Debugger.RunToNextPause(dcStepOver);
|
|
AssertDebuggerState(dsPause);
|
|
TestLocation('At AfterStepSleep', 'AfterStepSleep', -1);
|
|
|
|
// Step over a subroutine call, with a disabled breakpoint
|
|
Debugger.RunToNextPause(dcStepOver);
|
|
AssertDebuggerState(dsPause);
|
|
TestLocation('At AfterStepBrkDis', 'AfterStepBrkDis', -1);
|
|
|
|
// Step over a subroutine call, with a breakpoint that continues
|
|
Debugger.RunToNextPause(dcStepOver);
|
|
AssertDebuggerState(dsPause);
|
|
TestLocation('At AfterStepBrkHitCnt', 'AfterStepBrkHitCnt', -1);
|
|
|
|
TestEquals('No Hit for disabled break', 0, BrkDis.HitCount);
|
|
TestEquals('No Hit for skipped break', 1, BrkHitCnt.HitCount);
|
|
|
|
BrkDis.Enabled := True;
|
|
// Step over a subroutine call, BUT STOP at the breakpoint within it
|
|
Debugger.RunToNextPause(dcStepOver);
|
|
AssertDebuggerState(dsPause);
|
|
TestLocation('At BrkDisabled', 'BrkDisabled', -1);
|
|
// And do another step
|
|
Debugger.RunToNextPause(dcStepOver);
|
|
AssertDebuggerState(dsPause);
|
|
|
|
TestEquals('No Hit for disabled break', 1, BrkDis.HitCount);
|
|
TestEquals('No Hit for skipped break', 1, BrkHitCnt.HitCount);
|
|
|
|
|
|
// Step over a RECURSIVE subroutine call
|
|
Debugger.RunToNextPause(dcRun);
|
|
AssertDebuggerState(dsPause);
|
|
TestLocation('At BrkNested', 'BrkNested', -1);
|
|
|
|
Debugger.RunToNextPause(dcStepOver);
|
|
AssertDebuggerState(dsPause);
|
|
Debugger.RunToNextPause(dcStepOver);
|
|
AssertDebuggerState(dsPause);
|
|
TestLocation('At AfterNested', 'AfterNested', -1);
|
|
|
|
|
|
(* The debugger will encounter a thread create event, during the stepping
|
|
This will mean the main-loop's FCurrentThread is the new thread
|
|
*)
|
|
Debugger.RunToNextPause(dcRun);
|
|
AssertDebuggerState(dsPause);
|
|
TestLocation('At BrkThreadCreateInStep', 'BrkThreadCreateInStep', -1);
|
|
|
|
Debugger.RunToNextPause(dcStepOver);
|
|
AssertDebuggerState(dsPause);
|
|
TestLocation('At AfterThreadCreateInStep', 'AfterThreadCreateInStep', -1);
|
|
TestEquals('ThreadId AfterThreadCreateInStep', ThreadIdMain, dbg.Threads.CurrentThreads.CurrentThreadId);
|
|
|
|
(* The debugger will step over a call.
|
|
Other threads will hit the FHiddenBreakpoint
|
|
*)
|
|
Debugger.RunToNextPause(dcRun);
|
|
AssertDebuggerState(dsPause);
|
|
TestLocation('At BrkInterfereByThread', 'BrkInterfereByThread', -1);
|
|
|
|
Debugger.RunToNextPause(dcStepOver);
|
|
Debugger.RunToNextPause(dcStepOver);
|
|
AssertDebuggerState(dsPause);
|
|
TestLocation('At AfterInterfereByThread', 'AfterInterfereByThread', -1);
|
|
TestEquals('ThreadId AfterInterfereByThread', ThreadIdMain, dbg.Threads.CurrentThreads.CurrentThreadId);
|
|
|
|
|
|
dbg.Stop;
|
|
finally
|
|
Debugger.ClearDebuggerMonitors;
|
|
Debugger.FreeDebugger;
|
|
|
|
AssertTestErrors;
|
|
end;
|
|
end;
|
|
|
|
procedure TTestStepping.TestStepOverInstr;
|
|
procedure StepInstrToNextLine(AName: String; MaxSteps: integer = 50);
|
|
var
|
|
lc: TDBGLocationRec;
|
|
begin
|
|
lc := Debugger.LazDebugger.GetLocation;
|
|
repeat
|
|
Debugger.RunToNextPause(dcStepOverInstr);
|
|
AssertDebuggerState(dsPause, 'step instr '+AName);
|
|
dec(MaxSteps);
|
|
until (lc.SrcLine <> Debugger.LazDebugger.GetLocation.SrcLine) or (MaxSteps <= 0);
|
|
end;
|
|
|
|
var
|
|
ExeName: String;
|
|
MainBrk, BrkDis, BrkHitCnt: TDBGBreakPoint;
|
|
ThreadIdMain: Integer;
|
|
begin
|
|
if SkipTest then exit;
|
|
if not TestControlCanTest(ControlTestStepOverInstr) then exit;
|
|
Src := GetCommonSourceFor(AppDir + 'StepOverPrg.pas');
|
|
TestCompile(Src, ExeName);
|
|
|
|
TestTrue('Start debugger', Debugger.StartDebugger(AppDir, ExeName));
|
|
dbg := Debugger.LazDebugger;
|
|
|
|
try
|
|
MainBrk := Debugger.SetBreakPoint(Src, 'BrkStart');
|
|
Debugger.SetBreakPoint(Src, 'BrkThreadCreateInStep');
|
|
Debugger.SetBreakPoint(Src, 'BrkInterfereByThread');
|
|
Debugger.SetBreakPoint(Src, 'BrkNested');
|
|
|
|
BrkDis := Debugger.SetBreakPoint(Src, 'BrkDisabled');
|
|
BrkHitCnt := Debugger.SetBreakPoint(Src, 'BrkHitCnt');
|
|
BrkDis.Enabled := False;
|
|
BrkHitCnt.BreakHitCount := 999;
|
|
AssertDebuggerNotInErrorState;
|
|
|
|
Debugger.RunToNextPause(dcRun);
|
|
AssertDebuggerState(dsPause);
|
|
TestLocation('Init', 'BrkStart');
|
|
ThreadIdMain := dbg.Threads.CurrentThreads.CurrentThreadId;
|
|
|
|
StepInstrToNextLine('Go to AfterStep');
|
|
AssertDebuggerState(dsPause);
|
|
TestLocation('At AfterStep', 'AfterStep', -1);
|
|
|
|
StepInstrToNextLine('Go to AfterStepLongLine');
|
|
AssertDebuggerState(dsPause);
|
|
TestLocation('At AfterStepLongLine', 'AfterStepLongLine', -1);
|
|
|
|
StepInstrToNextLine('Go to AfterStepProc');
|
|
AssertDebuggerState(dsPause);
|
|
TestLocation('At AfterStepProc', 'AfterStepProc', -1);
|
|
|
|
StepInstrToNextLine('Go to AfterStepProcLong');
|
|
AssertDebuggerState(dsPause);
|
|
TestLocation('At AfterStepProcLong', 'AfterStepProcLong', -1);
|
|
|
|
StepInstrToNextLine('Go to AfterStepSleepProc');
|
|
AssertDebuggerState(dsPause);
|
|
TestLocation('At AfterStepSleepProc', 'AfterStepSleepProc', -1);
|
|
|
|
StepInstrToNextLine('Go to AfterStepSleep');
|
|
AssertDebuggerState(dsPause);
|
|
TestLocation('At AfterStepSleep', 'AfterStepSleep', -1);
|
|
|
|
StepInstrToNextLine('Go to AfterStepBrkDis');
|
|
AssertDebuggerState(dsPause);
|
|
TestLocation('At AfterStepBrkDis', 'AfterStepBrkDis', -1);
|
|
|
|
StepInstrToNextLine('Go to AfterStepBrkHitCnt');
|
|
AssertDebuggerState(dsPause);
|
|
TestLocation('At AfterStepBrkHitCnt', 'AfterStepBrkHitCnt', -1);
|
|
|
|
TestEquals('No Hit for disabled break', 0, BrkDis.HitCount);
|
|
TestEquals('No Hit for skipped break', 1, BrkHitCnt.HitCount);
|
|
|
|
|
|
Debugger.RunToNextPause(dcRun);
|
|
AssertDebuggerState(dsPause);
|
|
TestLocation('At BrkNested', 'BrkNested', -1);
|
|
|
|
StepInstrToNextLine('Go to AfterNested 1');
|
|
AssertDebuggerState(dsPause);
|
|
StepInstrToNextLine('Go to AfterNested 2');
|
|
AssertDebuggerState(dsPause);
|
|
TestLocation('At AfterNested', 'AfterNested', -1);
|
|
|
|
|
|
|
|
dbg.Stop;
|
|
finally
|
|
Debugger.ClearDebuggerMonitors;
|
|
Debugger.FreeDebugger;
|
|
|
|
AssertTestErrors;
|
|
end;
|
|
end;
|
|
|
|
|
|
initialization
|
|
|
|
RegisterDbgTest(TTestStepping);
|
|
ControlTest := TestControlRegisterTest('TTestStepping');
|
|
ControlTestStepOver := TestControlRegisterTest('TTestStepOver', ControlTest);
|
|
ControlTestStepOverInstr := TestControlRegisterTest('TTestStepOverInstr', ControlTest);
|
|
end.
|
|
|