123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367 |
- // Copyright (c) Jupyter Development Team.
- // Distributed under the terms of the Modified BSD License.
- import { init } from './utils';
- init();
- import { Session } from '@jupyterlab/services';
- import {
- createSession,
- JupyterServer,
- signalToPromises
- } from '@jupyterlab/testutils';
- import { find } from '@lumino/algorithm';
- import { PromiseDelegate, UUID } from '@lumino/coreutils';
- import { DebugProtocol } from '@vscode/debugprotocol';
- import { Debugger } from '../src/debugger';
- import { IDebugger } from '../src/tokens';
- const server = new JupyterServer();
- beforeAll(async () => {
- jest.setTimeout(20000);
- await server.start();
- });
- afterAll(async () => {
- await server.shutdown();
- });
- describe('Debugger.Session', () => {
- let connection: Session.ISessionConnection;
- beforeEach(async () => {
- const path = UUID.uuid4();
- connection = await createSession({
- name: '',
- type: 'test',
- path
- });
- await connection.changeKernel({ name: 'xpython' });
- });
- afterEach(async () => {
- await connection.shutdown();
- });
- describe('#isDisposed', () => {
- it('should return whether the object is disposed', () => {
- const debugSession = new Debugger.Session({
- connection
- });
- expect(debugSession.isDisposed).toEqual(false);
- debugSession.dispose();
- expect(debugSession.isDisposed).toEqual(true);
- });
- });
- describe('#eventMessage', () => {
- it('should be emitted when sending debug messages', async () => {
- const debugSession = new Debugger.Session({
- connection
- });
- let events: string[] = [];
- debugSession.eventMessage.connect((sender, event) => {
- events.push(event.event);
- });
- await debugSession.start();
- await debugSession.stop();
- expect(events).toEqual(
- expect.arrayContaining(['output', 'initialized', 'process'])
- );
- });
- });
- describe('#sendRequest success', () => {
- it('should send debug messages to the kernel', async () => {
- const debugSession = new Debugger.Session({
- connection
- });
- await debugSession.start();
- const code = 'i=0\ni+=1\ni+=1';
- const reply = await debugSession.sendRequest('dumpCell', {
- code
- });
- await debugSession.stop();
- expect(reply.body.sourcePath).toContain('.py');
- });
- });
- describe('#sendRequest failure', () => {
- it('should handle replies with success false', async () => {
- const debugSession = new Debugger.Session({
- connection
- });
- await debugSession.start();
- const reply = await debugSession.sendRequest('evaluate', {
- expression: 'a'
- });
- await debugSession.stop();
- const { success, message } = reply;
- expect(success).toBe(false);
- expect(message).toBeTruthy();
- });
- });
- });
- describe('protocol', () => {
- const code = [
- 'i = 0',
- 'i += 1',
- 'i += 1',
- 'j = i**2',
- 'j += 1',
- 'print(i, j)',
- 'for i in range(10):',
- ' j = i ** 2',
- ' print(j)',
- 'print(j)'
- ].join('\n');
- const breakpoints: DebugProtocol.SourceBreakpoint[] = [
- { line: 3 },
- { line: 5 }
- ];
- let connection: Session.ISessionConnection;
- let debugSession: Debugger.Session;
- let threadId = 1;
- beforeEach(async () => {
- const path = UUID.uuid4();
- connection = await createSession({
- name: '',
- type: 'test',
- path
- });
- await connection.changeKernel({ name: 'xpython' });
- debugSession = new Debugger.Session({
- connection
- });
- await debugSession.start();
- const stoppedFuture = new PromiseDelegate<void>();
- debugSession.eventMessage.connect(
- (sender: Debugger.Session, event: IDebugger.ISession.Event) => {
- switch (event.event) {
- case 'thread': {
- const msg = event as DebugProtocol.ThreadEvent;
- threadId = msg.body.threadId;
- break;
- }
- case 'stopped':
- const msg = event as DebugProtocol.StoppedEvent;
- threadId = msg.body.threadId!;
- stoppedFuture.resolve();
- break;
- default:
- break;
- }
- }
- );
- const reply = await debugSession.sendRequest('dumpCell', {
- code
- });
- await debugSession.sendRequest('setBreakpoints', {
- breakpoints,
- source: { path: reply.body.sourcePath },
- sourceModified: false
- });
- await debugSession.sendRequest('configurationDone', {});
- // trigger an execute_request
- connection!.kernel!.requestExecute({ code });
- // wait for the first stopped event
- await stoppedFuture.promise;
- });
- afterEach(async () => {
- await debugSession.stop();
- debugSession.dispose();
- await connection.shutdown();
- connection.dispose();
- });
- describe('#debugInfo', () => {
- it('should return the state of the current debug session', async () => {
- const reply = await debugSession.sendRequest('debugInfo', {});
- expect(reply.body.isStarted).toBe(true);
- const breakpoints = reply.body.breakpoints;
- // breakpoints are in the same file
- expect(breakpoints.length).toEqual(1);
- const breakpointsInfo = breakpoints[0];
- const breakpointLines = breakpointsInfo.breakpoints.map(bp => {
- return bp.line;
- });
- expect(breakpointLines).toEqual([3, 5]);
- });
- });
- describe('#stackTrace', () => {
- it('should return the correct stackframes', async () => {
- const reply = await debugSession.sendRequest('stackTrace', {
- threadId
- });
- expect(reply.success).toBe(true);
- const stackFrames = reply.body.stackFrames;
- expect(stackFrames.length).toEqual(1);
- const frame = stackFrames[0];
- // first breakpoint
- expect(frame.line).toEqual(3);
- });
- });
- describe('#scopes', () => {
- it('should return the correct scopes', async () => {
- const stackFramesReply = await debugSession.sendRequest('stackTrace', {
- threadId
- });
- const frameId = stackFramesReply.body.stackFrames[0].id;
- const scopesReply = await debugSession.sendRequest('scopes', {
- frameId
- });
- const scopes = scopesReply.body.scopes;
- expect(scopes.length).toBeGreaterThanOrEqual(1);
- const locals = scopes.find(scope => scope.name === 'Locals');
- expect(locals).toBeTruthy();
- });
- });
- const getVariables = async (
- start?: number,
- count?: number
- ): Promise<DebugProtocol.Variable[]> => {
- const stackFramesReply = await debugSession.sendRequest('stackTrace', {
- threadId
- });
- const frameId = stackFramesReply.body.stackFrames[0].id;
- const scopesReply = await debugSession.sendRequest('scopes', {
- frameId
- });
- const scopes = scopesReply.body.scopes;
- const variablesReference = scopes[0].variablesReference;
- const variablesReply = await debugSession.sendRequest('variables', {
- variablesReference,
- start,
- count
- });
- return variablesReply.body.variables;
- };
- describe('#variables', () => {
- it('should return the variables and their values', async () => {
- const variables = await getVariables();
- expect(variables.length).toBeGreaterThan(0);
- const i = find(variables, variable => variable.name === 'i');
- expect(i).toBeDefined();
- expect(i!.type).toEqual('int');
- expect(i!.value).toEqual('1');
- });
- });
- describe('#variablesPagination', () => {
- it.skip('should return the amount of variables requested', async () => {
- await debugSession.sendRequest('continue', { threadId });
- const variables = await getVariables(1, 1);
- const integers = variables.filter(variable => variable.type === 'int');
- expect(integers).toBeDefined();
- expect(integers.length).toEqual(1);
- });
- });
- describe('#continue', () => {
- it('should proceed to the next breakpoint', async () => {
- const [first, second] = signalToPromises(debugSession.eventMessage, 2);
- await debugSession.sendRequest('continue', { threadId });
- // wait for debug events
- const [, continued] = await first;
- expect(continued.event).toEqual('continued');
- const [, stopped] = await second;
- expect(stopped.event).toEqual('stopped');
- const variables = await getVariables();
- const i = find(variables, variable => variable.name === 'i');
- expect(i).toBeDefined();
- expect(i!.type).toEqual('int');
- expect(i!.value).toEqual('2');
- const j = find(variables, variable => variable.name === 'j');
- expect(j).toBeDefined();
- expect(j!.type).toEqual('int');
- expect(j!.value).toEqual('4');
- });
- });
- describe('#source', () => {
- it('should retrieve the source of the dumped code cell', async () => {
- const stackFramesReply = await debugSession.sendRequest('stackTrace', {
- threadId
- });
- const frame = stackFramesReply.body.stackFrames[0];
- const source = frame.source;
- const reply = await debugSession.sendRequest('source', {
- source: { path: source!.path! },
- sourceReference: source!.sourceReference!
- });
- const sourceCode = reply.body.content;
- expect(sourceCode).toEqual(code);
- });
- });
- describe('#evaluate', () => {
- it('should evaluate the code sent to the kernel', async () => {
- const stackFramesReply = await debugSession.sendRequest('stackTrace', {
- threadId
- });
- const frameId = stackFramesReply.body.stackFrames[0].id;
- const reply = await debugSession.sendRequest('evaluate', {
- frameId,
- context: 'repl',
- expression: 'k = 123',
- format: {}
- });
- expect(reply.success).toBe(true);
- const variables = await getVariables();
- const k = find(variables, variable => variable.name === 'k');
- expect(k).toBeDefined();
- expect(k!.type).toEqual('int');
- expect(k!.value).toEqual('123');
- });
- });
- describe('#setBreakpoints with condition', () => {
- it('should be hit when the condition is true', async () => {
- const reply = await debugSession.sendRequest('dumpCell', {
- code
- });
- await debugSession.sendRequest('setBreakpoints', {
- breakpoints: [{ line: 9, condition: 'i == 5' }],
- source: { path: reply.body.sourcePath },
- sourceModified: false
- });
- await debugSession.sendRequest('configurationDone', {});
- // advance to the conditional breakpoint
- await debugSession.sendRequest('continue', { threadId });
- const variables = await getVariables();
- const j = find(variables, variable => variable.name === 'j');
- expect(j).toBeDefined();
- expect(j!.value).toEqual('25');
- });
- });
- });
|