service.spec.ts 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  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 { Session, KernelSpecManager, KernelSpec } from '@jupyterlab/services';
  6. import {
  7. createSession,
  8. signalToPromise,
  9. JupyterServer
  10. } from '@jupyterlab/testutils';
  11. import { UUID, JSONExt } from '@lumino/coreutils';
  12. import { Debugger } from '../src/debugger';
  13. import { IDebugger } from '../src/tokens';
  14. import { KERNELSPECS, handleRequest } 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. let xpython: Session.ISessionConnection;
  44. let ipykernel: Session.ISessionConnection;
  45. beforeAll(async () => {
  46. xpython = await createSession({
  47. name: '',
  48. type: 'test',
  49. path: UUID.uuid4()
  50. });
  51. await xpython.changeKernel({ name: 'xpython' });
  52. ipykernel = await createSession({
  53. name: '',
  54. type: 'test',
  55. path: UUID.uuid4()
  56. });
  57. await ipykernel.changeKernel({ name: 'python3' });
  58. specsManager = new TestKernelSpecManager({ standby: 'never' });
  59. specsManager.intercept = specs;
  60. await specsManager.refreshSpecs();
  61. config = new Debugger.Config();
  62. service = new Debugger.Service({ specsManager, config });
  63. });
  64. afterAll(async () => {
  65. await Promise.all([xpython.shutdown(), ipykernel.shutdown()]);
  66. service.dispose();
  67. specsManager.dispose();
  68. });
  69. describe('#isAvailable', () => {
  70. it('should return true for kernels that have support for debugging', async () => {
  71. const enabled = await service.isAvailable(xpython);
  72. expect(enabled).toBe(true);
  73. });
  74. it('should return false for kernels that do not have support for debugging', async () => {
  75. const enabled = await service.isAvailable(ipykernel);
  76. expect(enabled).toBe(false);
  77. });
  78. });
  79. });
  80. describe('DebuggerService', () => {
  81. const specsManager = new KernelSpecManager();
  82. let connection: Session.ISessionConnection;
  83. let config: IDebugger.IConfig;
  84. let session: IDebugger.ISession;
  85. let service: IDebugger;
  86. beforeEach(async () => {
  87. connection = await createSession({
  88. name: '',
  89. type: 'test',
  90. path: UUID.uuid4()
  91. });
  92. await connection.changeKernel({ name: 'xpython' });
  93. session = new Debugger.Session({ connection });
  94. config = new Debugger.Config();
  95. service = new Debugger.Service({ specsManager, config });
  96. });
  97. afterEach(async () => {
  98. await connection.shutdown();
  99. connection.dispose();
  100. session.dispose();
  101. (service as Debugger.Service).dispose();
  102. });
  103. describe('#constructor()', () => {
  104. it('should create a new instance', () => {
  105. expect(service).toBeInstanceOf(Debugger.Service);
  106. });
  107. });
  108. describe('#start()', () => {
  109. it('should start the service if the session is set', async () => {
  110. service.session = session;
  111. await service.start();
  112. expect(service.isStarted).toEqual(true);
  113. });
  114. });
  115. describe('#stop()', () => {
  116. it('should stop the service if the session is set', async () => {
  117. service.session = session;
  118. await service.start();
  119. await service.stop();
  120. expect(service.isStarted).toEqual(false);
  121. });
  122. });
  123. describe('#session', () => {
  124. it('should emit the sessionChanged signal when setting the session', () => {
  125. const sessionChangedEvents: (IDebugger.ISession | null)[] = [];
  126. service.sessionChanged.connect((_, newSession) => {
  127. sessionChangedEvents.push(newSession);
  128. });
  129. service.session = session;
  130. expect(sessionChangedEvents.length).toEqual(1);
  131. expect(sessionChangedEvents[0]).toEqual(session);
  132. });
  133. });
  134. describe('protocol', () => {
  135. const code = [
  136. 'i = 0',
  137. 'i += 1',
  138. 'i += 1',
  139. 'j = i**2',
  140. 'j += 1',
  141. 'print(i, j)'
  142. ].join('\n');
  143. let breakpoints: IDebugger.IBreakpoint[];
  144. let sourceBreakpoints: IDebugger.IBreakpoint[];
  145. let sourceId: string;
  146. beforeEach(async () => {
  147. service.session = session;
  148. await service.restoreState(true);
  149. const breakpointLines: number[] = [3, 5];
  150. sourceId = service.getCodeId(code);
  151. breakpoints = breakpointLines.map((l: number, index: number) => {
  152. return {
  153. id: index,
  154. line: l,
  155. verified: true,
  156. source: {
  157. path: sourceId
  158. }
  159. };
  160. });
  161. // create the list of source breakpoints expected from a restore
  162. sourceBreakpoints = breakpoints.map(breakpoint => {
  163. return {
  164. line: breakpoint.line,
  165. verified: breakpoint.verified
  166. };
  167. });
  168. await service.updateBreakpoints(code, breakpoints);
  169. });
  170. describe('#updateBreakpoints', () => {
  171. it('should update the breakpoints', () => {
  172. const { model } = service;
  173. const bpList = model.breakpoints.getBreakpoints(sourceId);
  174. expect(bpList).toEqual(breakpoints);
  175. });
  176. });
  177. describe('#restoreState', () => {
  178. it('should restore the breakpoints', async () => {
  179. const { model } = service;
  180. model.breakpoints.restoreBreakpoints(
  181. new Map<string, IDebugger.IBreakpoint[]>()
  182. );
  183. const bpList1 = model.breakpoints.getBreakpoints(sourceId);
  184. expect(bpList1.length).toEqual(0);
  185. await service.restoreState(true);
  186. const bpList2 = model.breakpoints.getBreakpoints(sourceId);
  187. expect(bpList2).toEqual(sourceBreakpoints);
  188. });
  189. });
  190. describe('#restart', () => {
  191. it('should restart the debugger and send the breakpoints again', async () => {
  192. await service.restart();
  193. const { model } = service;
  194. model.breakpoints.restoreBreakpoints(
  195. new Map<string, IDebugger.IBreakpoint[]>()
  196. );
  197. await service.restoreState(true);
  198. const bpList = model.breakpoints.getBreakpoints(sourceId);
  199. breakpoints[0].id = 2;
  200. breakpoints[1].id = 3;
  201. expect(bpList).toEqual(sourceBreakpoints);
  202. });
  203. });
  204. describe('#hasStoppedThreads', () => {
  205. it('should return false if the model is null', () => {
  206. const hasStoppedThreads = service.hasStoppedThreads();
  207. expect(hasStoppedThreads).toBe(false);
  208. });
  209. it('should return true when the execution has stopped', async () => {
  210. const { model } = service;
  211. const variablesChanged = signalToPromise(model.variables.changed);
  212. // trigger a manual execute request
  213. connection!.kernel!.requestExecute({ code });
  214. // wait for the first stopped event and variables changed
  215. await variablesChanged;
  216. const hasStoppedThreads = service.hasStoppedThreads();
  217. expect(hasStoppedThreads).toBe(true);
  218. await service.restart();
  219. });
  220. });
  221. });
  222. });