service.spec.ts 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. import { Session } from '@jupyterlab/services';
  2. import {
  3. createSession,
  4. signalToPromise,
  5. JupyterServer
  6. } from '@jupyterlab/testutils';
  7. import { UUID } from '@lumino/coreutils';
  8. import { DebuggerModel } from '../src/model';
  9. import { DebuggerService } from '../src/service';
  10. import { DebugSession } from '../src/session';
  11. import { IDebugger } from '../src/tokens';
  12. const server = new JupyterServer();
  13. beforeAll(async () => {
  14. await server.start();
  15. });
  16. afterAll(async () => {
  17. await server.shutdown();
  18. });
  19. describe('Debugging support', () => {
  20. const service = new DebuggerService();
  21. let xpython: Session.ISessionConnection;
  22. let ipykernel: Session.ISessionConnection;
  23. beforeAll(async () => {
  24. xpython = await createSession({
  25. name: '',
  26. type: 'test',
  27. path: UUID.uuid4()
  28. });
  29. await xpython.changeKernel({ name: 'xpython' });
  30. ipykernel = await createSession({
  31. name: '',
  32. type: 'test',
  33. path: UUID.uuid4()
  34. });
  35. await ipykernel.changeKernel({ name: 'python3' });
  36. });
  37. afterAll(async () => {
  38. await Promise.all([xpython.shutdown(), ipykernel.shutdown()]);
  39. });
  40. describe('#isAvailable', () => {
  41. it('should return true for kernels that have support for debugging', async () => {
  42. const enabled = await service.isAvailable(xpython);
  43. expect(enabled).toBe(true);
  44. });
  45. it('should return false for kernels that do not have support for debugging', async () => {
  46. const enabled = await service.isAvailable(ipykernel);
  47. expect(enabled).toBe(false);
  48. });
  49. });
  50. });
  51. describe('DebuggerService', () => {
  52. let connection: Session.ISessionConnection;
  53. let model: DebuggerModel;
  54. let session: IDebugger.ISession;
  55. let service: IDebugger;
  56. beforeEach(async () => {
  57. connection = await createSession({
  58. name: '',
  59. type: 'test',
  60. path: UUID.uuid4()
  61. });
  62. await connection.changeKernel({ name: 'xpython' });
  63. session = new DebugSession({ connection });
  64. model = new DebuggerModel();
  65. service = new DebuggerService();
  66. });
  67. afterEach(async () => {
  68. await connection.shutdown();
  69. connection.dispose();
  70. session.dispose();
  71. (service as DebuggerService).dispose();
  72. });
  73. describe('#constructor()', () => {
  74. it('should create a new instance', () => {
  75. expect(service).toBeInstanceOf(DebuggerService);
  76. });
  77. });
  78. describe('#start()', () => {
  79. it('should start the service if the session is set', async () => {
  80. service.session = session;
  81. await service.start();
  82. expect(service.isStarted).toEqual(true);
  83. });
  84. it('should throw an error if the session is not set', async () => {
  85. await expect(service.start()).rejects.toThrow(
  86. "Cannot read property 'start' of null"
  87. );
  88. });
  89. });
  90. describe('#stop()', () => {
  91. it('should stop the service if the session is set', async () => {
  92. service.session = session;
  93. await service.start();
  94. await service.stop();
  95. expect(service.isStarted).toEqual(false);
  96. });
  97. });
  98. describe('#session', () => {
  99. it('should emit the sessionChanged signal when setting the session', () => {
  100. const sessionChangedEvents: IDebugger.ISession[] = [];
  101. service.sessionChanged.connect((_, newSession) => {
  102. sessionChangedEvents.push(newSession);
  103. });
  104. service.session = session;
  105. expect(sessionChangedEvents.length).toEqual(1);
  106. expect(sessionChangedEvents[0]).toEqual(session);
  107. });
  108. });
  109. describe('#model', () => {
  110. it('should emit the modelChanged signal when setting the model', () => {
  111. const modelChangedEvents: DebuggerModel[] = [];
  112. service.modelChanged.connect((_, newModel) => {
  113. modelChangedEvents.push(newModel as DebuggerModel);
  114. });
  115. service.model = model;
  116. expect(modelChangedEvents.length).toEqual(1);
  117. expect(modelChangedEvents[0]).toEqual(model);
  118. });
  119. });
  120. describe('protocol', () => {
  121. const code = [
  122. 'i = 0',
  123. 'i += 1',
  124. 'i += 1',
  125. 'j = i**2',
  126. 'j += 1',
  127. 'print(i, j)'
  128. ].join('\n');
  129. let breakpoints: IDebugger.IBreakpoint[];
  130. let sourceId: string;
  131. beforeEach(async () => {
  132. service.session = session;
  133. service.model = model;
  134. await service.restoreState(true);
  135. const breakpointLines: number[] = [3, 5];
  136. sourceId = service.getCodeId(code);
  137. breakpoints = breakpointLines.map((l: number, index: number) => {
  138. return {
  139. id: index,
  140. line: l,
  141. active: true,
  142. verified: true,
  143. source: {
  144. path: sourceId
  145. }
  146. };
  147. });
  148. await service.updateBreakpoints(code, breakpoints);
  149. });
  150. describe('#updateBreakpoints', () => {
  151. it('should update the breakpoints', () => {
  152. const bpList = model.breakpoints.getBreakpoints(sourceId);
  153. expect(bpList).toEqual(breakpoints);
  154. });
  155. });
  156. describe('#restoreState', () => {
  157. it('should restore the breakpoints', async () => {
  158. model.breakpoints.restoreBreakpoints(
  159. new Map<string, IDebugger.IBreakpoint[]>()
  160. );
  161. const bpList1 = model.breakpoints.getBreakpoints(sourceId);
  162. expect(bpList1.length).toEqual(0);
  163. await service.restoreState(true);
  164. const bpList2 = model.breakpoints.getBreakpoints(sourceId);
  165. expect(bpList2).toEqual(breakpoints);
  166. });
  167. });
  168. describe('#restart', () => {
  169. it('should restart the debugger and send the breakpoints again', async () => {
  170. await service.restart();
  171. model.breakpoints.restoreBreakpoints(
  172. new Map<string, IDebugger.IBreakpoint[]>()
  173. );
  174. await service.restoreState(true);
  175. const bpList = model.breakpoints.getBreakpoints(sourceId);
  176. breakpoints[0].id = 2;
  177. breakpoints[1].id = 3;
  178. expect(bpList).toEqual(breakpoints);
  179. });
  180. });
  181. describe('#hasStoppedThreads', () => {
  182. it('should return false if the model is null', () => {
  183. service.model = null;
  184. const hasStoppedThreads = service.hasStoppedThreads();
  185. expect(hasStoppedThreads).toBe(false);
  186. });
  187. it('should return true when the execution has stopped', async () => {
  188. const variablesChanged = signalToPromise(model.variables.changed);
  189. // trigger a manual execute request
  190. connection.kernel.requestExecute({ code });
  191. // wait for the first stopped event and variables changed
  192. await variablesChanged;
  193. const hasStoppedThreads = service.hasStoppedThreads();
  194. expect(hasStoppedThreads).toBe(true);
  195. await service.restart();
  196. });
  197. });
  198. });
  199. });