service.spec.ts 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. // Copyright (c) Jupyter Development Team.
  2. // Distributed under the terms of the Modified BSD License.
  3. import { init } from './utils';
  4. init();
  5. import { KernelSpec, KernelSpecManager, Session } from '@jupyterlab/services';
  6. import {
  7. createSession,
  8. JupyterServer,
  9. signalToPromise
  10. } from '@jupyterlab/testutils';
  11. import { JSONExt, UUID } from '@lumino/coreutils';
  12. import { Debugger } from '../src/debugger';
  13. import { IDebugger } from '../src/tokens';
  14. import { handleRequest, KERNELSPECS } from './utils';
  15. /**
  16. * A Test class to mock a KernelSpecManager
  17. */
  18. class TestKernelSpecManager extends KernelSpecManager {
  19. intercept: KernelSpec.ISpecModels | null = null;
  20. /**
  21. * Request the kernel specs
  22. */
  23. protected async requestSpecs(): Promise<void> {
  24. if (this.intercept) {
  25. handleRequest(this, 200, this.intercept);
  26. }
  27. return super.requestSpecs();
  28. }
  29. }
  30. const server = new JupyterServer();
  31. beforeAll(async () => {
  32. jest.setTimeout(20000);
  33. await server.start();
  34. });
  35. afterAll(async () => {
  36. await server.shutdown();
  37. });
  38. describe('Debugging support', () => {
  39. const specs = JSONExt.deepCopy(KERNELSPECS) as KernelSpec.ISpecModels;
  40. let specsManager: TestKernelSpecManager;
  41. let service: Debugger.Service;
  42. let config: IDebugger.IConfig;
  43. beforeAll(async () => {
  44. specsManager = new TestKernelSpecManager({ standby: 'never' });
  45. specsManager.intercept = specs;
  46. await specsManager.refreshSpecs();
  47. config = new Debugger.Config();
  48. service = new Debugger.Service({ specsManager, config });
  49. });
  50. afterAll(async () => {
  51. service.dispose();
  52. specsManager.dispose();
  53. });
  54. describe('#isAvailable', () => {
  55. it('should return true for kernels that have support for debugging', async () => {
  56. const enabled = await service.isAvailable({
  57. kernel: { name: 'xpython' }
  58. } as any);
  59. expect(enabled).toBe(true);
  60. });
  61. it.skip('should return false for kernels that do not have support for debugging', async () => {
  62. // The kernel spec are mocked in KERNELSPECS
  63. const enabled = await service.isAvailable({
  64. kernel: { name: 'python3' }
  65. } as any);
  66. expect(enabled).toBe(false);
  67. });
  68. });
  69. });
  70. describe('DebuggerService', () => {
  71. const specsManager = new KernelSpecManager();
  72. let connection: Session.ISessionConnection;
  73. let config: IDebugger.IConfig;
  74. let session: IDebugger.ISession;
  75. let service: IDebugger;
  76. beforeEach(async () => {
  77. connection = await createSession({
  78. name: '',
  79. type: 'test',
  80. path: UUID.uuid4()
  81. });
  82. await connection.changeKernel({ name: 'xpython' });
  83. session = new Debugger.Session({ connection });
  84. config = new Debugger.Config();
  85. service = new Debugger.Service({ specsManager, config });
  86. });
  87. afterEach(async () => {
  88. await connection.shutdown();
  89. connection.dispose();
  90. session.dispose();
  91. (service as Debugger.Service).dispose();
  92. });
  93. describe('#constructor()', () => {
  94. it('should create a new instance', () => {
  95. expect(service).toBeInstanceOf(Debugger.Service);
  96. });
  97. });
  98. describe('#start()', () => {
  99. it('should start the service if the session is set', async () => {
  100. service.session = session;
  101. await service.start();
  102. expect(service.isStarted).toEqual(true);
  103. });
  104. });
  105. describe('#stop()', () => {
  106. it('should stop the service if the session is set', async () => {
  107. service.session = session;
  108. await service.start();
  109. await service.stop();
  110. expect(service.isStarted).toEqual(false);
  111. });
  112. });
  113. describe('#session', () => {
  114. it('should emit the sessionChanged signal when setting the session', () => {
  115. const sessionChangedEvents: (IDebugger.ISession | null)[] = [];
  116. service.sessionChanged.connect((_, newSession) => {
  117. sessionChangedEvents.push(newSession);
  118. });
  119. service.session = session;
  120. expect(sessionChangedEvents.length).toEqual(1);
  121. expect(sessionChangedEvents[0]).toEqual(session);
  122. });
  123. });
  124. describe('protocol', () => {
  125. const code = [
  126. 'i = 0',
  127. 'i += 1',
  128. 'i += 1',
  129. 'j = i**2',
  130. 'j += 1',
  131. 'print(i, j)'
  132. ].join('\n');
  133. let breakpoints: IDebugger.IBreakpoint[];
  134. let sourceId: string;
  135. beforeEach(async () => {
  136. service.session = session;
  137. await service.restoreState(true);
  138. const breakpointLines: number[] = [3, 5];
  139. sourceId = service.getCodeId(code);
  140. breakpoints = breakpointLines.map(
  141. (line: number): IDebugger.IBreakpoint => {
  142. return {
  143. line,
  144. verified: true,
  145. source: {
  146. path: sourceId
  147. }
  148. };
  149. }
  150. );
  151. await service.updateBreakpoints(code, breakpoints);
  152. });
  153. describe('#updateBreakpoints', () => {
  154. it('should update the breakpoints', async () => {
  155. const { model } = service;
  156. model.breakpoints.restoreBreakpoints(
  157. new Map<string, IDebugger.IBreakpoint[]>()
  158. );
  159. await service.updateBreakpoints(code, breakpoints);
  160. const bpList = model.breakpoints.getBreakpoints(sourceId);
  161. expect(bpList.length).toEqual(breakpoints.length);
  162. expect(bpList[0].line).toEqual(breakpoints[0].line);
  163. expect(bpList[1].line).toEqual(breakpoints[1].line);
  164. expect(bpList[0].source).toEqual(breakpoints[0].source);
  165. expect(bpList[1].source).toEqual(breakpoints[1].source);
  166. });
  167. });
  168. describe('#restoreState', () => {
  169. it('should restore the breakpoints', async () => {
  170. const { model } = service;
  171. model.breakpoints.restoreBreakpoints(
  172. new Map<string, IDebugger.IBreakpoint[]>()
  173. );
  174. const bpList1 = model.breakpoints.getBreakpoints(sourceId);
  175. expect(bpList1.length).toEqual(0);
  176. await service.restoreState(true);
  177. const bpList2 = model.breakpoints.getBreakpoints(sourceId);
  178. expect(bpList2).toEqual(breakpoints);
  179. });
  180. });
  181. describe('#restart', () => {
  182. it('should restart the debugger and send the breakpoints again', async () => {
  183. await service.restart();
  184. const { model } = service;
  185. model.breakpoints.restoreBreakpoints(
  186. new Map<string, IDebugger.IBreakpoint[]>()
  187. );
  188. await service.restoreState(true);
  189. const bpList = model.breakpoints.getBreakpoints(sourceId);
  190. expect(bpList).toEqual(breakpoints);
  191. });
  192. });
  193. describe('#hasStoppedThreads', () => {
  194. it('should return false if the model is null', () => {
  195. const hasStoppedThreads = service.hasStoppedThreads();
  196. expect(hasStoppedThreads).toBe(false);
  197. });
  198. it('should return true when the execution has stopped', async () => {
  199. const { model } = service;
  200. const variablesChanged = signalToPromise(model.variables.changed);
  201. // trigger a manual execute request
  202. connection!.kernel!.requestExecute({ code });
  203. // wait for the first stopped event and variables changed
  204. await variablesChanged;
  205. const hasStoppedThreads = service.hasStoppedThreads();
  206. expect(hasStoppedThreads).toBe(true);
  207. await service.restart();
  208. });
  209. });
  210. });
  211. });