Родитель
5eb8a1e733
Коммит
91f116109e
|
@ -42,6 +42,13 @@ export type TraceCategory =
|
|||
| 'launch'
|
||||
| 'breakpoints';
|
||||
|
||||
export enum Step {
|
||||
Over,
|
||||
In,
|
||||
Out,
|
||||
Run
|
||||
}
|
||||
|
||||
export interface LaunchRequestArguments
|
||||
extends DebugProtocol.LaunchRequestArguments {
|
||||
logFile: string;
|
||||
|
@ -174,31 +181,81 @@ export class ApexReplayDebug extends LoggingDebugSession {
|
|||
public continueRequest(
|
||||
response: DebugProtocol.ContinueResponse,
|
||||
args: DebugProtocol.ContinueArguments
|
||||
): void {
|
||||
this.executeStep(response, Step.Run);
|
||||
}
|
||||
|
||||
public nextRequest(
|
||||
response: DebugProtocol.NextResponse,
|
||||
args: DebugProtocol.NextArguments
|
||||
): void {
|
||||
this.executeStep(response, Step.Over);
|
||||
}
|
||||
|
||||
public stepInRequest(
|
||||
response: DebugProtocol.StepInResponse,
|
||||
args: DebugProtocol.StepInArguments
|
||||
): void {
|
||||
this.executeStep(response, Step.In);
|
||||
}
|
||||
|
||||
public stepOutRequest(
|
||||
response: DebugProtocol.StepOutResponse,
|
||||
args: DebugProtocol.StepOutArguments
|
||||
): void {
|
||||
this.executeStep(response, Step.Out);
|
||||
}
|
||||
|
||||
protected executeStep(
|
||||
response: DebugProtocol.Response,
|
||||
stepType: Step
|
||||
): void {
|
||||
response.success = true;
|
||||
this.sendResponse(response);
|
||||
const prevNumOfFrames = this.logContext.getNumOfFrames();
|
||||
while (this.logContext.hasLogLines()) {
|
||||
this.logContext.updateFrames(this.getHandlerForDebugConsole());
|
||||
const topFrame = this.logContext.getTopFrame();
|
||||
if (topFrame && topFrame.source) {
|
||||
const topFrameUri = this.convertClientPathToDebugger(
|
||||
topFrame.source.path
|
||||
const curNumOfFrames = this.logContext.getNumOfFrames();
|
||||
if (
|
||||
(stepType === Step.Over &&
|
||||
curNumOfFrames !== 0 &&
|
||||
curNumOfFrames <= prevNumOfFrames) ||
|
||||
(stepType === Step.In && curNumOfFrames >= prevNumOfFrames) ||
|
||||
(stepType === Step.Out &&
|
||||
curNumOfFrames !== 0 &&
|
||||
curNumOfFrames < prevNumOfFrames)
|
||||
) {
|
||||
return this.sendEvent(
|
||||
new StoppedEvent('step', ApexReplayDebug.THREAD_ID)
|
||||
);
|
||||
const topFrameLine = this.convertClientLineToDebugger(topFrame.line);
|
||||
if (
|
||||
this.breakpoints.has(topFrameUri) &&
|
||||
this.breakpoints.get(topFrameUri)!.indexOf(topFrameLine) !== -1
|
||||
) {
|
||||
this.sendEvent(
|
||||
new StoppedEvent('breakpoint', ApexReplayDebug.THREAD_ID)
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (this.shouldStopForBreakpoint()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.sendEvent(new TerminatedEvent());
|
||||
}
|
||||
|
||||
protected shouldStopForBreakpoint(): boolean {
|
||||
const topFrame = this.logContext.getTopFrame();
|
||||
if (topFrame && topFrame.source) {
|
||||
const topFrameUri = this.convertClientPathToDebugger(
|
||||
topFrame.source.path
|
||||
);
|
||||
const topFrameLine = this.convertClientLineToDebugger(topFrame.line);
|
||||
if (
|
||||
this.breakpoints.has(topFrameUri) &&
|
||||
this.breakpoints.get(topFrameUri)!.indexOf(topFrameLine) !== -1
|
||||
) {
|
||||
this.sendEvent(
|
||||
new StoppedEvent('breakpoint', ApexReplayDebug.THREAD_ID)
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public setBreakPointsRequest(
|
||||
response: DebugProtocol.SetBreakpointsResponse,
|
||||
args: DebugProtocol.SetBreakpointsArguments
|
||||
|
|
|
@ -18,4 +18,4 @@ export const EVENT_METHOD_EXIT = 'METHOD_EXIT';
|
|||
export const EVENT_STATEMENT_EXECUTE = 'STATEMENT_EXECUTE';
|
||||
export const EXEC_ANON_SIGNATURE = 'execute_anonymous_apex';
|
||||
export const GET_LINE_BREAKPOINT_INFO_EVENT = 'getLineBreakpointInfo';
|
||||
export const LINE_BREAKPOINT_INFO_REQUEST = 'lineBreakpointInfo';
|
||||
export const LINE_BREAKPOINT_INFO_REQUEST = 'lineBreakpointInfo';
|
||||
|
|
|
@ -81,6 +81,10 @@ export class LogContext {
|
|||
return this.stackFrameInfos;
|
||||
}
|
||||
|
||||
public getNumOfFrames(): number {
|
||||
return this.stackFrameInfos.length;
|
||||
}
|
||||
|
||||
public getTopFrame(): StackFrame | undefined {
|
||||
if (this.stackFrameInfos.length > 0) {
|
||||
return this.stackFrameInfos[this.stackFrameInfos.length - 1];
|
||||
|
|
|
@ -54,6 +54,10 @@ export class MockApexReplayDebug extends ApexReplayDebug {
|
|||
public getBreakpoints(): Map<string, number[]> {
|
||||
return this.breakpoints;
|
||||
}
|
||||
|
||||
public shouldStopForBreakpoint(): boolean {
|
||||
return super.shouldStopForBreakpoint();
|
||||
}
|
||||
}
|
||||
|
||||
// tslint:disable:no-unused-expression
|
||||
|
@ -165,9 +169,7 @@ describe('Replay debugger adapter - unit', () => {
|
|||
0
|
||||
).args[0];
|
||||
expect(actualResponse.success).to.be.false;
|
||||
expect(actualResponse.message).to.be.equal(
|
||||
nls.localize('no_log_file_text')
|
||||
);
|
||||
expect(actualResponse.message).to.equal(nls.localize('no_log_file_text'));
|
||||
});
|
||||
|
||||
it('Should send response', () => {
|
||||
|
@ -180,7 +182,7 @@ describe('Replay debugger adapter - unit', () => {
|
|||
expect(hasLogLinesStub.calledOnce).to.be.true;
|
||||
expect(printToDebugConsoleStub.calledOnce).to.be.true;
|
||||
const consoleMessage = printToDebugConsoleStub.getCall(0).args[0];
|
||||
expect(consoleMessage).to.be.equal(
|
||||
expect(consoleMessage).to.equal(
|
||||
nls.localize('session_started_text', logFileName)
|
||||
);
|
||||
expect(sendResponseSpy.calledOnce).to.be.true;
|
||||
|
@ -280,9 +282,7 @@ describe('Replay debugger adapter - unit', () => {
|
|||
|
||||
expect(printToDebugConsoleStub.calledOnce).to.be.true;
|
||||
const consoleMessage = printToDebugConsoleStub.getCall(0).args[0];
|
||||
expect(consoleMessage).to.be.equal(
|
||||
nls.localize('session_terminated_text')
|
||||
);
|
||||
expect(consoleMessage).to.equal(nls.localize('session_terminated_text'));
|
||||
expect(sendResponseSpy.calledOnce).to.be.true;
|
||||
const actualResponse: DebugProtocol.DisconnectResponse = sendResponseSpy.getCall(
|
||||
0
|
||||
|
@ -401,7 +401,7 @@ describe('Replay debugger adapter - unit', () => {
|
|||
let sendEventSpy: sinon.SinonSpy;
|
||||
let hasLogLinesStub: sinon.SinonStub;
|
||||
let updateFramesStub: sinon.SinonStub;
|
||||
let getTopFrameStub: sinon.SinonStub;
|
||||
let shouldStopForBreakpointStub: sinon.SinonStub;
|
||||
let response: DebugProtocol.ContinueResponse;
|
||||
let args: DebugProtocol.ContinueArguments;
|
||||
const launchRequestArgs: LaunchRequestArguments = {
|
||||
|
@ -429,8 +429,8 @@ describe('Replay debugger adapter - unit', () => {
|
|||
if (updateFramesStub) {
|
||||
updateFramesStub.restore();
|
||||
}
|
||||
if (getTopFrameStub) {
|
||||
getTopFrameStub.restore();
|
||||
if (shouldStopForBreakpointStub) {
|
||||
shouldStopForBreakpointStub.restore();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -458,13 +458,89 @@ describe('Replay debugger adapter - unit', () => {
|
|||
.onSecondCall()
|
||||
.returns(false);
|
||||
updateFramesStub = sinon.stub(LogContext.prototype, 'updateFrames');
|
||||
getTopFrameStub = sinon
|
||||
.stub(LogContext.prototype, 'getTopFrame')
|
||||
.returns({ line: 2, source: { path: '/path/foo.cls' } } as StackFrame);
|
||||
adapter.getBreakpoints().set('file:///path/foo.cls', [2]);
|
||||
shouldStopForBreakpointStub = sinon
|
||||
.stub(MockApexReplayDebug.prototype, 'shouldStopForBreakpoint')
|
||||
.returns(true);
|
||||
|
||||
adapter.continueRequest(response, args);
|
||||
|
||||
expect(sendResponseSpy.calledOnce).to.be.true;
|
||||
const actualResponse: DebugProtocol.StackTraceResponse = sendResponseSpy.getCall(
|
||||
0
|
||||
).args[0];
|
||||
expect(actualResponse.success).to.be.true;
|
||||
expect(sendEventSpy.called).to.be.false;
|
||||
});
|
||||
|
||||
it('Should not hit breakpoint', () => {
|
||||
hasLogLinesStub = sinon
|
||||
.stub(LogContext.prototype, 'hasLogLines')
|
||||
.onFirstCall()
|
||||
.returns(true)
|
||||
.onSecondCall()
|
||||
.returns(false);
|
||||
updateFramesStub = sinon.stub(LogContext.prototype, 'updateFrames');
|
||||
shouldStopForBreakpointStub = sinon
|
||||
.stub(MockApexReplayDebug.prototype, 'shouldStopForBreakpoint')
|
||||
.returns(false);
|
||||
|
||||
adapter.continueRequest(response, args);
|
||||
|
||||
expect(sendResponseSpy.calledOnce).to.be.true;
|
||||
const actualResponse: DebugProtocol.StackTraceResponse = sendResponseSpy.getCall(
|
||||
0
|
||||
).args[0];
|
||||
expect(actualResponse.success).to.be.true;
|
||||
expect(sendEventSpy.calledOnce).to.be.true;
|
||||
const event = sendEventSpy.getCall(0).args[0];
|
||||
expect(event).to.be.instanceof(TerminatedEvent);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Stepping', () => {
|
||||
let sendResponseSpy: sinon.SinonSpy;
|
||||
let sendEventSpy: sinon.SinonSpy;
|
||||
let hasLogLinesStub: sinon.SinonStub;
|
||||
let updateFramesStub: sinon.SinonStub;
|
||||
let getNumOfFramesStub: sinon.SinonStub;
|
||||
|
||||
beforeEach(() => {
|
||||
sendResponseSpy = sinon.spy(ApexReplayDebug.prototype, 'sendResponse');
|
||||
sendEventSpy = sinon.spy(ApexReplayDebug.prototype, 'sendEvent');
|
||||
updateFramesStub = sinon.stub(LogContext.prototype, 'updateFrames');
|
||||
hasLogLinesStub = sinon
|
||||
.stub(LogContext.prototype, 'hasLogLines')
|
||||
.onFirstCall()
|
||||
.returns(true)
|
||||
.onSecondCall()
|
||||
.returns(false);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
sendResponseSpy.restore();
|
||||
sendEventSpy.restore();
|
||||
hasLogLinesStub.restore();
|
||||
updateFramesStub.restore();
|
||||
getNumOfFramesStub.restore();
|
||||
});
|
||||
|
||||
it('Should send step over', () => {
|
||||
getNumOfFramesStub = sinon
|
||||
.stub(LogContext.prototype, 'getNumOfFrames')
|
||||
.onFirstCall()
|
||||
.returns(2)
|
||||
.onSecondCall()
|
||||
.returns(2);
|
||||
|
||||
adapter.nextRequest(
|
||||
Object.assign(adapter.getDefaultResponse(), {
|
||||
body: {}
|
||||
}),
|
||||
{
|
||||
threadId: ApexReplayDebug.THREAD_ID
|
||||
}
|
||||
);
|
||||
|
||||
expect(sendResponseSpy.calledOnce).to.be.true;
|
||||
const actualResponse: DebugProtocol.StackTraceResponse = sendResponseSpy.getCall(
|
||||
0
|
||||
|
@ -473,12 +549,71 @@ describe('Replay debugger adapter - unit', () => {
|
|||
expect(sendEventSpy.calledOnce).to.be.true;
|
||||
const event = sendEventSpy.getCall(0).args[0];
|
||||
expect(event).to.be.instanceof(StoppedEvent);
|
||||
expect((event as StoppedEvent).body.reason).to.equal('step');
|
||||
});
|
||||
|
||||
it('Should send step in', () => {
|
||||
getNumOfFramesStub = sinon
|
||||
.stub(LogContext.prototype, 'getNumOfFrames')
|
||||
.onFirstCall()
|
||||
.returns(2)
|
||||
.onSecondCall()
|
||||
.returns(3);
|
||||
|
||||
adapter.stepInRequest(
|
||||
Object.assign(adapter.getDefaultResponse(), {
|
||||
body: {}
|
||||
}),
|
||||
{
|
||||
threadId: ApexReplayDebug.THREAD_ID
|
||||
}
|
||||
);
|
||||
|
||||
expect(sendResponseSpy.calledOnce).to.be.true;
|
||||
const actualResponse: DebugProtocol.StackTraceResponse = sendResponseSpy.getCall(
|
||||
0
|
||||
).args[0];
|
||||
expect(actualResponse.success).to.be.true;
|
||||
expect(sendEventSpy.calledOnce).to.be.true;
|
||||
const event = sendEventSpy.getCall(0).args[0];
|
||||
expect(event).to.be.instanceof(StoppedEvent);
|
||||
expect((event as StoppedEvent).body.reason).to.equal('step');
|
||||
});
|
||||
|
||||
it('Should send step out', () => {
|
||||
getNumOfFramesStub = sinon
|
||||
.stub(LogContext.prototype, 'getNumOfFrames')
|
||||
.onFirstCall()
|
||||
.returns(2)
|
||||
.onSecondCall()
|
||||
.returns(1);
|
||||
|
||||
adapter.stepOutRequest(
|
||||
Object.assign(adapter.getDefaultResponse(), {
|
||||
body: {}
|
||||
}),
|
||||
{
|
||||
threadId: ApexReplayDebug.THREAD_ID
|
||||
}
|
||||
);
|
||||
|
||||
expect(sendResponseSpy.calledOnce).to.be.true;
|
||||
const actualResponse: DebugProtocol.StackTraceResponse = sendResponseSpy.getCall(
|
||||
0
|
||||
).args[0];
|
||||
expect(actualResponse.success).to.be.true;
|
||||
expect(sendEventSpy.calledOnce).to.be.true;
|
||||
const event = sendEventSpy.getCall(0).args[0];
|
||||
expect(event).to.be.instanceof(StoppedEvent);
|
||||
expect((event as StoppedEvent).body.reason).to.equal('step');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Breakpoints', () => {
|
||||
let sendResponseSpy: sinon.SinonSpy;
|
||||
let sendEventSpy: sinon.SinonSpy;
|
||||
let canSetLineBreakpointStub: sinon.SinonStub;
|
||||
let getTopFrameStub: sinon.SinonStub;
|
||||
let response: DebugProtocol.SetBreakpointsResponse;
|
||||
let args: DebugProtocol.SetBreakpointsArguments;
|
||||
const launchRequestArgs: LaunchRequestArguments = {
|
||||
|
@ -496,13 +631,44 @@ describe('Replay debugger adapter - unit', () => {
|
|||
source: {}
|
||||
};
|
||||
sendResponseSpy = sinon.spy(ApexReplayDebug.prototype, 'sendResponse');
|
||||
sendEventSpy = sinon.spy(ApexReplayDebug.prototype, 'sendEvent');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
sendResponseSpy.restore();
|
||||
sendEventSpy.restore();
|
||||
if (canSetLineBreakpointStub) {
|
||||
canSetLineBreakpointStub.restore();
|
||||
}
|
||||
if (getTopFrameStub) {
|
||||
getTopFrameStub.restore();
|
||||
}
|
||||
});
|
||||
|
||||
it('Should stop for breakpoint', () => {
|
||||
getTopFrameStub = sinon
|
||||
.stub(LogContext.prototype, 'getTopFrame')
|
||||
.returns({ line: 2, source: { path: '/path/foo.cls' } } as StackFrame);
|
||||
adapter.getBreakpoints().set('file:///path/foo.cls', [2]);
|
||||
|
||||
const isStopped = adapter.shouldStopForBreakpoint();
|
||||
|
||||
expect(isStopped).to.be.true;
|
||||
expect(sendEventSpy.called).to.be.true;
|
||||
const event = sendEventSpy.getCall(0).args[0];
|
||||
expect(event).to.be.instanceof(StoppedEvent);
|
||||
});
|
||||
|
||||
it('Should not stop for breakpoint', () => {
|
||||
getTopFrameStub = sinon
|
||||
.stub(LogContext.prototype, 'getTopFrame')
|
||||
.returns({ line: 2, source: { path: '/path/foo.cls' } } as StackFrame);
|
||||
adapter.getBreakpoints().set('file:///path/bar.cls', [2]);
|
||||
|
||||
const isStopped = adapter.shouldStopForBreakpoint();
|
||||
|
||||
expect(isStopped).to.be.false;
|
||||
expect(sendEventSpy.called).to.be.false;
|
||||
});
|
||||
|
||||
it('Should not return breakpoints when path argument is invalid', () => {
|
||||
|
|
|
@ -96,6 +96,7 @@ describe('LogContext', () => {
|
|||
|
||||
it('Should start with empty array of stackframes', () => {
|
||||
expect(context.getFrames()).to.be.empty;
|
||||
expect(context.getNumOfFrames()).to.equal(0);
|
||||
expect(context.getTopFrame()).to.be.undefined;
|
||||
});
|
||||
|
||||
|
@ -177,8 +178,8 @@ describe('LogContext', () => {
|
|||
context.setState(new LogEntryState());
|
||||
context.parseLogEvent(`${EVENT_EXECUTE_ANONYMOUS}: foo`);
|
||||
|
||||
expect(context.getExecAnonScriptMapping().size).to.be.equal(1);
|
||||
expect(context.getExecAnonScriptMapping().get(1)).to.be.equal(0);
|
||||
expect(context.getExecAnonScriptMapping().size).to.equal(1);
|
||||
expect(context.getExecAnonScriptMapping().get(1)).to.equal(0);
|
||||
});
|
||||
|
||||
it('Should detect FrameEntry with CODE_UNIT_STARTED', () => {
|
||||
|
@ -255,7 +256,7 @@ describe('LogContext', () => {
|
|||
});
|
||||
|
||||
it('Should return debug log path for execute anonymous signature', () => {
|
||||
expect(context.getUriFromSignature(EXEC_ANON_SIGNATURE)).to.be.equal(
|
||||
expect(context.getUriFromSignature(EXEC_ANON_SIGNATURE)).to.equal(
|
||||
encodeURI('file://' + context.getLogFilePath())
|
||||
);
|
||||
});
|
||||
|
@ -263,7 +264,7 @@ describe('LogContext', () => {
|
|||
it('Should return URI for inner class', () => {
|
||||
expect(
|
||||
context.getUriFromSignature('namespace.Foo.Bar(Integer)')
|
||||
).to.be.equal('/path/foo.cls');
|
||||
).to.equal('/path/foo.cls');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -27,7 +27,7 @@ describe('Log context utilities', () => {
|
|||
});
|
||||
|
||||
it('Should strip brackets', () => {
|
||||
expect(util.stripBrackets('[20]')).to.be.equal('20');
|
||||
expect(util.stripBrackets('[20]')).to.equal('20');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -43,7 +43,7 @@ describe('Frame entry event', () => {
|
|||
expect(state.handle(context)).to.be.false;
|
||||
|
||||
const frames = context.getFrames();
|
||||
expect(frames.length).to.be.equal(2);
|
||||
expect(context.getNumOfFrames()).to.equal(2);
|
||||
expect(frames[1]).to.deep.equal({
|
||||
id: 1,
|
||||
line: 0,
|
||||
|
|
|
@ -41,7 +41,7 @@ describe('LogEntry event', () => {
|
|||
|
||||
expect(isStopped).to.be.true;
|
||||
const stackFrames = context.getFrames();
|
||||
expect(stackFrames.length).to.equal(1);
|
||||
expect(context.getNumOfFrames()).to.equal(1);
|
||||
const stackFrame = stackFrames[0];
|
||||
expect(stackFrame.id).to.equal(0);
|
||||
expect(stackFrame.name).to.equal('');
|
||||
|
|
|
@ -39,7 +39,7 @@ describe('Statement execute event', () => {
|
|||
const state = new StatementExecuteState(['2']);
|
||||
|
||||
expect(state.handle(context)).to.be.true;
|
||||
expect(context.getFrames()[0].line).to.be.equal(2);
|
||||
expect(context.getFrames()[0].line).to.equal(2);
|
||||
});
|
||||
|
||||
it('Should update execute anonymous specific frame', () => {
|
||||
|
@ -48,6 +48,6 @@ describe('Statement execute event', () => {
|
|||
const state = new StatementExecuteState(['2']);
|
||||
|
||||
expect(state.handle(context)).to.be.true;
|
||||
expect(context.getFrames()[0].line).to.be.equal(5);
|
||||
expect(context.getFrames()[0].line).to.equal(5);
|
||||
});
|
||||
});
|
||||
|
|
Загрузка…
Ссылка в новой задаче