ikernel.spec.ts 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516
  1. // Copyright (c) Jupyter Development Team.
  2. import 'jest';
  3. import { PageConfig } from '@jupyterlab/coreutils';
  4. import { UUID } from '@lumino/coreutils';
  5. import { PromiseDelegate } from '@lumino/coreutils';
  6. import {
  7. Kernel,
  8. KernelMessage,
  9. KernelSpec,
  10. KernelSpecAPI,
  11. KernelManager,
  12. KernelAPI
  13. } from '../../src';
  14. import {
  15. expectFailure,
  16. testEmission,
  17. JupyterServer,
  18. flakyIt as it
  19. } from '@jupyterlab/testutils';
  20. import { KernelTester, handleRequest } from '../utils';
  21. const server = new JupyterServer();
  22. beforeAll(async () => {
  23. await server.start();
  24. });
  25. afterAll(async () => {
  26. await server.shutdown();
  27. });
  28. describe('Kernel.IKernel', () => {
  29. let defaultKernel: Kernel.IKernelConnection;
  30. let specs: KernelSpec.ISpecModels;
  31. let kernelManager: KernelManager;
  32. beforeAll(async () => {
  33. jest.setTimeout(20000);
  34. kernelManager = new KernelManager();
  35. specs = await KernelSpecAPI.getSpecs();
  36. });
  37. beforeEach(async () => {
  38. defaultKernel = await kernelManager.startNew();
  39. await defaultKernel.info;
  40. });
  41. afterEach(async () => {
  42. await defaultKernel.shutdown();
  43. defaultKernel.dispose();
  44. });
  45. afterAll(async () => {
  46. const models = await KernelAPI.listRunning();
  47. await Promise.all(models.map(m => KernelAPI.shutdownKernel(m.id)));
  48. });
  49. describe('#disposed', () => {
  50. it('should be emitted when the kernel is disposed', async () => {
  51. await defaultKernel.info;
  52. let called = false;
  53. defaultKernel.disposed.connect((sender, args) => {
  54. expect(sender).toBe(defaultKernel);
  55. expect(args).toBeUndefined();
  56. called = true;
  57. });
  58. defaultKernel.dispose();
  59. expect(called).toBe(true);
  60. });
  61. it('should be emitted when the kernel is shut down', async () => {
  62. await defaultKernel.info;
  63. let called = false;
  64. defaultKernel.disposed.connect((sender, args) => {
  65. expect(sender).toBe(defaultKernel);
  66. expect(args).toBeUndefined();
  67. called = true;
  68. });
  69. await defaultKernel.shutdown();
  70. expect(called).toBe(true);
  71. });
  72. });
  73. describe('#statusChanged', () => {
  74. it('should be a signal following the Kernel status', async () => {
  75. let called = false;
  76. defaultKernel.statusChanged.connect(() => {
  77. if (defaultKernel.status === 'busy') {
  78. called = true;
  79. }
  80. });
  81. await defaultKernel.requestExecute({ code: 'a=1' }, true).done;
  82. expect(called).toBe(true);
  83. });
  84. });
  85. describe('#iopubMessage', () => {
  86. it('should be emitted for an iopub message', async () => {
  87. let called = false;
  88. defaultKernel.iopubMessage.connect((k, msg) => {
  89. called = true;
  90. });
  91. await defaultKernel.requestExecute({ code: 'a=1' }, true).done;
  92. expect(called).toBe(true);
  93. });
  94. it('should be emitted regardless of the sender', async () => {
  95. const tester = new KernelTester();
  96. const kernel = await tester.start();
  97. const msgId = UUID.uuid4();
  98. const emission = testEmission(kernel.iopubMessage, {
  99. find: (k, msg) => msg.header.msg_id === msgId
  100. });
  101. const msg = KernelMessage.createMessage({
  102. msgType: 'status',
  103. channel: 'iopub',
  104. session: tester.serverSessionId,
  105. msgId,
  106. content: {
  107. execution_state: 'idle'
  108. }
  109. });
  110. tester.send(msg);
  111. await emission;
  112. await tester.shutdown();
  113. tester.dispose();
  114. });
  115. });
  116. describe('#unhandledMessage', () => {
  117. let tester: KernelTester;
  118. beforeEach(() => {
  119. tester = new KernelTester();
  120. });
  121. afterEach(async () => {
  122. await tester.shutdown();
  123. tester.dispose();
  124. });
  125. it('should be emitted for an unhandled message', async () => {
  126. const kernel = await tester.start();
  127. const msgId = UUID.uuid4();
  128. const emission = testEmission(kernel.unhandledMessage, {
  129. find: (k, msg) => msg.header.msg_id === msgId
  130. });
  131. const msg = KernelMessage.createMessage({
  132. msgType: 'kernel_info_request',
  133. channel: 'shell',
  134. session: tester.serverSessionId,
  135. msgId,
  136. content: {}
  137. });
  138. msg.parent_header = { session: kernel.clientId };
  139. tester.send(msg);
  140. await emission;
  141. });
  142. it('should not be emitted for an iopub signal', async () => {
  143. const kernel = await tester.start();
  144. // We'll send two messages, first an iopub message, then a shell message.
  145. // The unhandledMessage signal should only emit once for the shell message.
  146. const msgId = UUID.uuid4();
  147. const emission = testEmission(kernel.unhandledMessage, {
  148. test: (k, msg) => {
  149. expect(msg.header.msg_id).toBe(msgId);
  150. }
  151. });
  152. // Send an iopub message.
  153. tester.sendStatus(UUID.uuid4(), 'idle');
  154. // Send a shell message.
  155. const msg = KernelMessage.createMessage({
  156. msgType: 'kernel_info_request',
  157. channel: 'shell',
  158. session: tester.serverSessionId,
  159. msgId,
  160. content: {}
  161. });
  162. msg.parent_header = { session: kernel.clientId };
  163. tester.send(msg);
  164. await emission;
  165. });
  166. it('should not be emitted for a different client session', async () => {
  167. const kernel = await tester.start();
  168. // We'll send two messages, first a message with a different session, then
  169. // one with the current client session. The unhandledMessage signal should
  170. // only emit once for the current session message.
  171. const msgId = 'message from right session';
  172. const emission = testEmission(kernel.unhandledMessage, {
  173. test: (k, msg) => {
  174. expect((msg.parent_header as KernelMessage.IHeader).session).toBe(
  175. kernel.clientId
  176. );
  177. expect(msg.header.msg_id).toBe(msgId);
  178. }
  179. });
  180. // Send a shell message with the wrong client (parent) session.
  181. const msg1 = KernelMessage.createMessage({
  182. msgType: 'kernel_info_request',
  183. channel: 'shell',
  184. session: tester.serverSessionId,
  185. msgId: 'message from wrong session',
  186. content: {}
  187. });
  188. msg1.parent_header = { session: 'wrong session' };
  189. tester.send(msg1);
  190. // Send a shell message with the right client (parent) session.
  191. const msg2 = KernelMessage.createMessage({
  192. msgType: 'kernel_info_request',
  193. channel: 'shell',
  194. session: tester.serverSessionId,
  195. msgId: msgId,
  196. content: {}
  197. });
  198. msg2.parent_header = { session: kernel.clientId };
  199. tester.send(msg2);
  200. await emission;
  201. });
  202. });
  203. describe('#anyMessage', () => {
  204. let tester: KernelTester;
  205. beforeEach(() => {
  206. tester = new KernelTester();
  207. });
  208. afterEach(async () => {
  209. await tester.shutdown();
  210. tester.dispose();
  211. });
  212. it('should be emitted for an unhandled message', async () => {
  213. const kernel = await tester.start();
  214. const msgId = UUID.uuid4();
  215. const emission = testEmission(kernel.anyMessage, {
  216. test: (k, args) => {
  217. expect(args.msg.header.msg_id).toBe(msgId);
  218. expect(args.msg.header.msg_type).toBe('kernel_info_request');
  219. expect(args.direction).toBe('recv');
  220. }
  221. });
  222. const msg = KernelMessage.createMessage({
  223. msgType: 'kernel_info_request',
  224. channel: 'shell',
  225. session: tester.serverSessionId,
  226. msgId,
  227. content: {}
  228. });
  229. msg.parent_header = { session: kernel.clientId };
  230. tester.send(msg);
  231. await emission;
  232. });
  233. it('should be emitted for an iopub message', async () => {
  234. const kernel = await tester.start();
  235. const msgId = 'idle status';
  236. const emission = testEmission(kernel.anyMessage, {
  237. test: (k, args) => {
  238. expect((args.msg.header as any).msg_id).toBe(msgId);
  239. expect(args.direction).toBe('recv');
  240. }
  241. });
  242. tester.sendStatus(msgId, 'idle');
  243. await emission;
  244. });
  245. it('should be emitted for an stdin message', async () => {
  246. const kernel = await tester.start();
  247. const emission = testEmission(kernel.anyMessage, {
  248. test: (k, { msg, direction }) => {
  249. if (!KernelMessage.isInputReplyMsg(msg)) {
  250. throw new Error('Unexpected message');
  251. }
  252. if (msg.content.status !== 'ok') {
  253. throw new Error('Message has been changed');
  254. }
  255. expect(msg.content.value).toBe('foo');
  256. expect(direction).toBe('send');
  257. }
  258. });
  259. kernel.sendInputReply({ status: 'ok', value: 'foo' });
  260. await emission;
  261. });
  262. });
  263. describe('#id', () => {
  264. it('should be a string', () => {
  265. expect(typeof defaultKernel.id).toBe('string');
  266. });
  267. });
  268. describe('#name', () => {
  269. it('should be a string', () => {
  270. expect(typeof defaultKernel.name).toBe('string');
  271. });
  272. });
  273. describe('#username', () => {
  274. it('should be a string', () => {
  275. expect(typeof defaultKernel.username).toBe('string');
  276. });
  277. });
  278. describe('#serverSettings', () => {
  279. it('should be the server settings', () => {
  280. expect(defaultKernel.serverSettings.baseUrl).toBe(
  281. PageConfig.getBaseUrl()
  282. );
  283. });
  284. });
  285. describe('#clientId', () => {
  286. it('should be a string', () => {
  287. expect(typeof defaultKernel.clientId).toBe('string');
  288. });
  289. });
  290. describe('#status', () => {
  291. beforeEach(async () => {
  292. await defaultKernel.info;
  293. });
  294. it('should get an idle status', async () => {
  295. const emission = testEmission(defaultKernel.statusChanged, {
  296. find: () => defaultKernel.status === 'idle'
  297. });
  298. await defaultKernel.requestExecute({ code: 'a=1' }).done;
  299. await emission;
  300. });
  301. it('should get a restarting status', async () => {
  302. const emission = testEmission(defaultKernel.statusChanged, {
  303. find: () => defaultKernel.status === 'restarting'
  304. });
  305. await defaultKernel.restart();
  306. await emission;
  307. });
  308. it('should get a busy status', async () => {
  309. const emission = testEmission(defaultKernel.statusChanged, {
  310. find: () => defaultKernel.status === 'busy'
  311. });
  312. await defaultKernel.requestExecute({ code: 'a=1' }, true).done;
  313. await emission;
  314. });
  315. it('should get an unknown status while disconnected', async () => {
  316. const tester = new KernelTester();
  317. const kernel = await tester.start();
  318. const emission = testEmission(kernel.statusChanged, {
  319. find: () => kernel.status === 'unknown'
  320. });
  321. await tester.close();
  322. await emission;
  323. tester.dispose();
  324. });
  325. it('should get a dead status', async () => {
  326. const tester = new KernelTester();
  327. const kernel = await tester.start();
  328. const dead = testEmission(kernel.statusChanged, {
  329. find: () => kernel.status === 'dead'
  330. });
  331. tester.sendStatus(UUID.uuid4(), 'dead');
  332. await dead;
  333. tester.dispose();
  334. });
  335. });
  336. describe('#info', () => {
  337. it('should get the kernel info', async () => {
  338. const name = (await defaultKernel.info).language_info.name;
  339. const defaultSpecs = specs.kernelspecs[specs.default]!;
  340. expect(name).toBe(defaultSpecs.language);
  341. });
  342. });
  343. describe('#spec', () => {
  344. it('should resolve with the spec', async () => {
  345. const spec = await defaultKernel.spec;
  346. expect(spec!.name).toBe(specs.default);
  347. });
  348. });
  349. describe('#isDisposed', () => {
  350. it('should be true after we dispose of the kernel', async () => {
  351. const kernel = defaultKernel.clone();
  352. expect(kernel.isDisposed).toBe(false);
  353. kernel.dispose();
  354. expect(kernel.isDisposed).toBe(true);
  355. });
  356. it('should be safe to call multiple times', async () => {
  357. const kernel = defaultKernel.clone();
  358. expect(kernel.isDisposed).toBe(false);
  359. expect(kernel.isDisposed).toBe(false);
  360. kernel.dispose();
  361. expect(kernel.isDisposed).toBe(true);
  362. expect(kernel.isDisposed).toBe(true);
  363. });
  364. });
  365. describe('#dispose()', () => {
  366. it('should dispose of the resources held by the kernel', async () => {
  367. const kernel = defaultKernel.clone();
  368. const future = kernel.requestExecute({ code: 'foo' });
  369. expect(future.isDisposed).toBe(false);
  370. kernel.dispose();
  371. expect(future.isDisposed).toBe(true);
  372. });
  373. it('should be safe to call twice', async () => {
  374. const kernel = defaultKernel.clone();
  375. const future = kernel.requestExecute({ code: 'foo' });
  376. expect(future.isDisposed).toBe(false);
  377. kernel.dispose();
  378. expect(future.isDisposed).toBe(true);
  379. expect(kernel.isDisposed).toBe(true);
  380. kernel.dispose();
  381. expect(future.isDisposed).toBe(true);
  382. expect(kernel.isDisposed).toBe(true);
  383. });
  384. });
  385. describe('#sendShellMessage()', () => {
  386. let tester: KernelTester;
  387. let kernel: Kernel.IKernelConnection;
  388. beforeEach(async () => {
  389. tester = new KernelTester();
  390. kernel = await tester.start();
  391. });
  392. afterEach(async () => {
  393. await tester.shutdown();
  394. tester.dispose();
  395. });
  396. it('should send a message to the kernel', async () => {
  397. const done = new PromiseDelegate<void>();
  398. const msgId = UUID.uuid4();
  399. tester.onMessage(msg => {
  400. try {
  401. expect(msg.header.msg_id).toBe(msgId);
  402. } catch (e) {
  403. done.reject(e);
  404. throw e;
  405. }
  406. done.resolve();
  407. });
  408. const msg = KernelMessage.createMessage({
  409. msgType: 'comm_info_request',
  410. channel: 'shell',
  411. username: kernel.username,
  412. session: kernel.clientId,
  413. msgId,
  414. content: {}
  415. });
  416. kernel.sendShellMessage(msg, true);
  417. await done.promise;
  418. });
  419. it('should send a binary message', async () => {
  420. const done = new PromiseDelegate<void>();
  421. const msgId = UUID.uuid4();
  422. tester.onMessage(msg => {
  423. try {
  424. const decoder = new TextDecoder('utf8');
  425. const item = msg.buffers![0] as DataView;
  426. expect(decoder.decode(item)).toBe('hello');
  427. } catch (e) {
  428. done.reject(e);
  429. throw e;
  430. }
  431. done.resolve();
  432. });
  433. const encoder = new TextEncoder();
  434. const data = encoder.encode('hello');
  435. const msg = KernelMessage.createMessage({
  436. msgType: 'comm_info_request',
  437. channel: 'shell',
  438. username: kernel.username,
  439. session: kernel.clientId,
  440. msgId,
  441. content: {},
  442. buffers: [data, data.buffer]
  443. });
  444. kernel.sendShellMessage(msg, true);
  445. await done.promise;
  446. });
  447. it('should fail if the kernel is dead', async () => {
  448. // Create a promise that resolves when the kernel's status changes to dead
  449. const dead = testEmission(kernel.statusChanged, {
  450. find: () => kernel.status === 'dead'
  451. });
  452. tester.sendStatus(UUID.uuid4(), 'dead');
  453. await dead;
  454. expect(kernel.status).toBe('dead');
  455. const msg = KernelMessage.createMessage({
  456. msgType: 'kernel_info_request',
  457. channel: 'shell',
  458. username: kernel.username,
  459. session: kernel.clientId,
  460. content: {}
  461. });
  462. expect(() => {
  463. kernel.sendShellMessage(msg, true);
  464. }).toThrowError(/Kernel is dead/);
  465. });
  466. it('should handle out of order messages', async () => {
  467. // This test that a future.done promise resolves when a status idle and
  468. // reply come through, even if the status comes first.
  469. const msg = KernelMessage.createMessage({
  470. msgType: 'kernel_info_request',
  471. channel: 'shell',
  472. username: kernel.username,
  473. session: kernel.clientId,
  474. content: {}
  475. });
  476. const future = kernel.sendShellMessage(msg, true);
  477. tester.onMessage(msg => {
  478. // trigger onDone
  479. tester.send(
  480. KernelMessage.createMessage({
  481. msgType: 'status',
  482. channel: 'iopub',
  483. username: kernel.username,
  484. session: kernel.clientId,
  485. content: { execution_state: 'idle' },
  486. parentHeader: msg.header
  487. })
  488. );
  489. future.onIOPub = () => {
  490. tester.send(
  491. KernelMessage.createMessage({
  492. msgType: 'comm_open',
  493. channel: 'shell',
  494. username: kernel.username,
  495. session: kernel.clientId,
  496. content: {
  497. comm_id: 'abcd',
  498. target_name: 'target',
  499. data: {}
  500. },
  501. parentHeader: msg.header
  502. })
  503. );
  504. };
  505. });
  506. await future.done;
  507. });
  508. });
  509. describe('#interrupt()', () => {
  510. it('should interrupt and resolve with a valid server response', async () => {
  511. const kernel = await kernelManager.startNew();
  512. await kernel.interrupt();
  513. await kernel.shutdown();
  514. });
  515. it('should throw an error for an invalid response', async () => {
  516. handleRequest(defaultKernel, 200, {
  517. id: defaultKernel.id,
  518. name: defaultKernel.name
  519. });
  520. const interrupt = defaultKernel.interrupt();
  521. await expectFailure(interrupt, 'Invalid response: 200 OK');
  522. });
  523. it('should throw an error for an error response', async () => {
  524. handleRequest(defaultKernel, 500, {});
  525. const interrupt = defaultKernel.interrupt();
  526. await expectFailure(interrupt, '');
  527. });
  528. it('should fail if the kernel is dead', async () => {
  529. const tester = new KernelTester();
  530. const kernel = await tester.start();
  531. // Create a promise that resolves when the kernel's status changes to dead
  532. const dead = testEmission(kernel.statusChanged, {
  533. find: () => kernel.status === 'dead'
  534. });
  535. tester.sendStatus(UUID.uuid4(), 'dead');
  536. await dead;
  537. await expectFailure(kernel.interrupt(), 'Kernel is dead');
  538. tester.dispose();
  539. });
  540. });
  541. describe('#restart()', () => {
  542. beforeEach(async () => {
  543. await defaultKernel.info;
  544. });
  545. it('should restart and resolve with a valid server response', async () => {
  546. await defaultKernel.restart();
  547. });
  548. it('should fail if the kernel does not restart', async () => {
  549. handleRequest(defaultKernel, 500, {});
  550. const restart = defaultKernel.restart();
  551. await expectFailure(restart, '');
  552. });
  553. it('should throw an error for an invalid response', async () => {
  554. const { id, name } = defaultKernel;
  555. handleRequest(defaultKernel, 205, { id, name });
  556. await expectFailure(
  557. defaultKernel.restart(),
  558. 'Invalid response: 205 Reset Content'
  559. );
  560. });
  561. it('should throw an error for an error response', async () => {
  562. handleRequest(defaultKernel, 500, {});
  563. const restart = defaultKernel.restart();
  564. await expectFailure(restart);
  565. });
  566. it('should throw an error for an invalid id', async () => {
  567. handleRequest(defaultKernel, 200, {});
  568. const restart = defaultKernel.restart();
  569. await expectFailure(restart);
  570. });
  571. it('should dispose of existing comm and future objects', async () => {
  572. const comm = defaultKernel.createComm('test');
  573. const future = defaultKernel.requestExecute({ code: 'foo' });
  574. await defaultKernel.restart();
  575. await defaultKernel.info;
  576. expect(future.isDisposed).toBe(true);
  577. expect(comm.isDisposed).toBe(true);
  578. });
  579. });
  580. describe('#reconnect()', () => {
  581. it('should create a new websocket and resolve the returned promise', async () => {
  582. const oldWS = (defaultKernel as any)._ws;
  583. await defaultKernel.reconnect();
  584. expect((defaultKernel as any)._ws).not.toBe(oldWS);
  585. });
  586. it('should emit `"connecting"`, then `"connected"` status', async () => {
  587. const emission = testEmission(defaultKernel.connectionStatusChanged, {
  588. find: () => defaultKernel.connectionStatus === 'connecting',
  589. test: async () => {
  590. await testEmission(defaultKernel.connectionStatusChanged, {
  591. find: () => defaultKernel.connectionStatus === 'connected'
  592. });
  593. }
  594. });
  595. await defaultKernel.reconnect();
  596. await emission;
  597. });
  598. it('return promise should reject if the kernel is disposed or disconnected', async () => {
  599. const connection = defaultKernel.reconnect();
  600. defaultKernel.dispose();
  601. try {
  602. await connection;
  603. // If the connection did not reject, so test fails.
  604. throw new Error('Reconnection promise did not reject');
  605. } catch (e) {
  606. /* Connection promise reject - test passes */
  607. }
  608. });
  609. });
  610. describe('#shutdown()', () => {
  611. it('should shut down and resolve with a valid server response', async () => {
  612. const kernel = await kernelManager.startNew();
  613. await kernel.shutdown();
  614. });
  615. it('should throw an error for an invalid response', async () => {
  616. handleRequest(defaultKernel, 200, {
  617. id: UUID.uuid4(),
  618. name: 'foo'
  619. });
  620. const shutdown = defaultKernel.shutdown();
  621. await expectFailure(shutdown, 'Invalid response: 200 OK');
  622. });
  623. it('should handle a 404 error', async () => {
  624. const kernel = await kernelManager.startNew();
  625. handleRequest(kernel, 404, {});
  626. await kernel.shutdown();
  627. });
  628. it('should throw an error for an error response', async () => {
  629. handleRequest(defaultKernel, 500, {});
  630. const shutdown = defaultKernel.shutdown();
  631. await expectFailure(shutdown, '');
  632. });
  633. it('should still pass if the kernel is dead', async () => {
  634. const tester = new KernelTester();
  635. const kernel = await tester.start();
  636. // Create a promise that resolves when the kernel's status changes to dead
  637. const dead = testEmission(kernel.statusChanged, {
  638. find: () => kernel.status === 'dead'
  639. });
  640. tester.sendStatus(UUID.uuid4(), 'dead');
  641. await dead;
  642. await kernel.shutdown();
  643. tester.dispose();
  644. });
  645. });
  646. describe('#requestKernelInfo()', () => {
  647. it('should resolve the promise', async () => {
  648. const msg = (await defaultKernel.requestKernelInfo())!;
  649. if (msg.content.status !== 'ok') {
  650. throw new Error('Message error');
  651. }
  652. const name = msg.content.language_info.name;
  653. expect(name).toBeTruthy();
  654. });
  655. });
  656. describe('#requestComplete()', () => {
  657. it('should resolve the promise', async () => {
  658. const options: KernelMessage.ICompleteRequestMsg['content'] = {
  659. code: 'hello',
  660. cursor_pos: 4
  661. };
  662. await defaultKernel.requestComplete(options);
  663. });
  664. it('should reject the promise if the kernel is dead', async () => {
  665. const options: KernelMessage.ICompleteRequestMsg['content'] = {
  666. code: 'hello',
  667. cursor_pos: 4
  668. };
  669. const tester = new KernelTester();
  670. const kernel = await tester.start();
  671. // Create a promise that resolves when the kernel's status changes to dead
  672. const dead = testEmission(kernel.statusChanged, {
  673. find: () => kernel.status === 'dead'
  674. });
  675. tester.sendStatus(UUID.uuid4(), 'dead');
  676. await dead;
  677. await expectFailure(kernel.requestComplete(options), 'Kernel is dead');
  678. tester.dispose();
  679. });
  680. });
  681. describe('#requestInspect()', () => {
  682. it('should resolve the promise', async () => {
  683. const options: KernelMessage.IInspectRequestMsg['content'] = {
  684. code: 'hello',
  685. cursor_pos: 4,
  686. detail_level: 0
  687. };
  688. await defaultKernel.requestInspect(options);
  689. });
  690. });
  691. describe('#requestIsComplete()', () => {
  692. it('should resolve the promise', async () => {
  693. const options: KernelMessage.IIsCompleteRequestMsg['content'] = {
  694. code: 'hello'
  695. };
  696. await defaultKernel.requestIsComplete(options);
  697. });
  698. });
  699. describe('#requestHistory()', () => {
  700. it('range messages should resolve the promise', async () => {
  701. const options: KernelMessage.IHistoryRequestMsg['content'] = {
  702. output: true,
  703. raw: true,
  704. hist_access_type: 'range',
  705. session: 0,
  706. start: 1,
  707. stop: 2
  708. };
  709. await defaultKernel.requestHistory(options);
  710. });
  711. it('tail messages should resolve the promise', async () => {
  712. const options: KernelMessage.IHistoryRequestMsg['content'] = {
  713. output: true,
  714. raw: true,
  715. hist_access_type: 'tail',
  716. n: 1
  717. };
  718. await defaultKernel.requestHistory(options);
  719. });
  720. it('search messages should resolve the promise', async () => {
  721. const options: KernelMessage.IHistoryRequestMsg['content'] = {
  722. output: true,
  723. raw: true,
  724. hist_access_type: 'search',
  725. n: 1,
  726. pattern: '*',
  727. unique: true
  728. };
  729. await defaultKernel.requestHistory(options);
  730. });
  731. });
  732. describe('#sendInputReply()', () => {
  733. it('should send an input_reply message', async () => {
  734. const tester = new KernelTester();
  735. const kernel = await tester.start();
  736. const done = new PromiseDelegate<void>();
  737. tester.onMessage(msg => {
  738. expect(msg.header.msg_type).toBe('input_reply');
  739. done.resolve(undefined);
  740. });
  741. kernel.sendInputReply({ status: 'ok', value: 'test' });
  742. await done.promise;
  743. await tester.shutdown();
  744. tester.dispose();
  745. });
  746. it('should fail if the kernel is dead', async () => {
  747. const tester = new KernelTester();
  748. const kernel = await tester.start();
  749. // Create a promise that resolves when the kernel's status changes to dead
  750. const dead = testEmission(kernel.statusChanged, {
  751. find: () => kernel.status === 'dead'
  752. });
  753. tester.sendStatus(UUID.uuid4(), 'dead');
  754. await dead;
  755. expect(() => {
  756. kernel.sendInputReply({ status: 'ok', value: 'test' });
  757. }).toThrowError(/Kernel is dead/);
  758. tester.dispose();
  759. });
  760. });
  761. describe('#requestExecute()', () => {
  762. it('should send and handle incoming messages', async () => {
  763. const content: KernelMessage.IExecuteRequestMsg['content'] = {
  764. code: 'test',
  765. silent: false,
  766. store_history: true,
  767. user_expressions: {},
  768. allow_stdin: false,
  769. stop_on_error: false
  770. };
  771. const options = {
  772. username: defaultKernel.username,
  773. session: defaultKernel.clientId
  774. };
  775. let future: Kernel.IShellFuture;
  776. const tester = new KernelTester();
  777. tester.onMessage(msg => {
  778. expect(msg.channel).toBe('shell');
  779. // send a reply
  780. tester.send(
  781. KernelMessage.createMessage<KernelMessage.IExecuteReplyMsg>({
  782. ...options,
  783. msgType: 'execute_reply',
  784. channel: 'shell',
  785. content: {
  786. execution_count: 1,
  787. status: 'ok',
  788. user_expressions: {}
  789. },
  790. parentHeader: msg.header as KernelMessage.IExecuteRequestMsg['header']
  791. })
  792. );
  793. future.onReply = () => {
  794. // trigger onStdin
  795. tester.send(
  796. KernelMessage.createMessage({
  797. ...options,
  798. channel: 'stdin',
  799. msgType: 'input_request',
  800. content: {
  801. prompt: 'prompt',
  802. password: false
  803. },
  804. parentHeader: msg.header
  805. })
  806. );
  807. };
  808. future.onStdin = () => {
  809. // trigger onIOPub with a 'stream' message
  810. tester.send(
  811. KernelMessage.createMessage<KernelMessage.IStreamMsg>({
  812. ...options,
  813. channel: 'iopub',
  814. msgType: 'stream',
  815. content: { name: 'stdout', text: '' },
  816. parentHeader: msg.header
  817. })
  818. );
  819. };
  820. future.onIOPub = ioMsg => {
  821. if (ioMsg.header.msg_type === 'stream') {
  822. // trigger onDone
  823. tester.send(
  824. KernelMessage.createMessage<KernelMessage.IStatusMsg>({
  825. ...options,
  826. channel: 'iopub',
  827. msgType: 'status',
  828. content: {
  829. execution_state: 'idle'
  830. },
  831. parentHeader: msg.header
  832. })
  833. );
  834. }
  835. };
  836. });
  837. const kernel = await tester.start();
  838. future = kernel.requestExecute(content);
  839. await future.done;
  840. expect(future.isDisposed).toBe(true);
  841. await tester.shutdown();
  842. tester.dispose();
  843. });
  844. it('should not dispose of KernelFuture when disposeOnDone=false', async () => {
  845. const options: KernelMessage.IExecuteRequestMsg['content'] = {
  846. code: 'test',
  847. silent: false,
  848. store_history: true,
  849. user_expressions: {},
  850. allow_stdin: false,
  851. stop_on_error: false
  852. };
  853. const future = defaultKernel.requestExecute(options, false);
  854. await future.done;
  855. expect(future.isDisposed).toBe(false);
  856. future.dispose();
  857. expect(future.isDisposed).toBe(true);
  858. });
  859. });
  860. describe('#checkExecuteMetadata()', () => {
  861. it('should accept cell metadata as part of request', async () => {
  862. const options: KernelMessage.IExecuteRequestMsg['content'] = {
  863. code: 'test',
  864. silent: false,
  865. store_history: true,
  866. user_expressions: {},
  867. allow_stdin: false,
  868. stop_on_error: false
  869. };
  870. const metadata = { cellId: 'test' };
  871. const future = defaultKernel.requestExecute(options, false, metadata);
  872. await future.done;
  873. expect((future.msg.metadata = metadata));
  874. });
  875. });
  876. describe('#registerMessageHook()', () => {
  877. it('should have the most recently registered hook run first', async () => {
  878. const options: KernelMessage.IExecuteRequestMsg['content'] = {
  879. code: 'test',
  880. silent: false,
  881. store_history: true,
  882. user_expressions: {},
  883. allow_stdin: false,
  884. stop_on_error: false
  885. };
  886. const calls: string[] = [];
  887. let future: Kernel.IShellFuture;
  888. let kernel: Kernel.IKernelConnection;
  889. const tester = new KernelTester();
  890. tester.onMessage(message => {
  891. // send a reply
  892. const parentHeader = message.header;
  893. const session = 'session';
  894. tester.send(
  895. KernelMessage.createMessage({
  896. parentHeader,
  897. session,
  898. channel: 'shell',
  899. msgType: 'comm_open',
  900. content: { comm_id: 'B', data: {}, target_name: 'C' }
  901. })
  902. );
  903. future.onReply = () => {
  904. // trigger onIOPub with a 'stream' message
  905. tester.send(
  906. KernelMessage.createMessage({
  907. parentHeader,
  908. session,
  909. channel: 'iopub',
  910. msgType: 'stream',
  911. content: { name: 'stdout', text: 'foo' }
  912. })
  913. );
  914. // trigger onDone
  915. tester.send(
  916. KernelMessage.createMessage({
  917. parentHeader,
  918. session,
  919. channel: 'iopub',
  920. msgType: 'status',
  921. content: { execution_state: 'idle' }
  922. })
  923. );
  924. };
  925. kernel.registerMessageHook(parentHeader.msg_id, async msg => {
  926. // Make this hook call asynchronous
  927. // tslint:disable-next-line:await-promise
  928. await calls.push('last');
  929. return true;
  930. });
  931. kernel.registerMessageHook(parentHeader.msg_id, msg => {
  932. calls.push('first');
  933. // not returning should also continue handling
  934. return void 0 as any;
  935. });
  936. future.onIOPub = () => {
  937. calls.push('iopub');
  938. };
  939. });
  940. kernel = await tester.start();
  941. future = kernel.requestExecute(options, false);
  942. await future.done;
  943. // the last hook was called for the stream and the status message.
  944. expect(calls).toEqual([
  945. 'first',
  946. 'last',
  947. 'iopub',
  948. 'first',
  949. 'last',
  950. 'iopub'
  951. ]);
  952. await tester.shutdown();
  953. tester.dispose();
  954. });
  955. it('should abort processing if a hook returns false, but the done logic should still work', async () => {
  956. const options: KernelMessage.IExecuteRequestMsg['content'] = {
  957. code: 'test',
  958. silent: false,
  959. store_history: true,
  960. user_expressions: {},
  961. allow_stdin: false,
  962. stop_on_error: false
  963. };
  964. const calls: string[] = [];
  965. const tester = new KernelTester();
  966. let future: Kernel.IShellFuture;
  967. let kernel: Kernel.IKernelConnection;
  968. tester.onMessage(message => {
  969. // send a reply
  970. const parentHeader = message.header;
  971. const session = 'session';
  972. tester.send(
  973. KernelMessage.createMessage({
  974. parentHeader,
  975. session,
  976. channel: 'shell',
  977. msgType: 'comm_open',
  978. content: { comm_id: 'B', data: {}, target_name: 'C' }
  979. })
  980. );
  981. future.onReply = () => {
  982. // trigger onIOPub with a 'stream' message
  983. tester.send(
  984. KernelMessage.createMessage({
  985. parentHeader,
  986. session,
  987. channel: 'iopub',
  988. msgType: 'stream',
  989. content: { name: 'stdout', text: 'foo' }
  990. })
  991. );
  992. // trigger onDone
  993. tester.send(
  994. KernelMessage.createMessage({
  995. parentHeader,
  996. session,
  997. channel: 'iopub',
  998. msgType: 'status',
  999. content: { execution_state: 'idle' }
  1000. })
  1001. );
  1002. };
  1003. kernel.registerMessageHook(parentHeader.msg_id, msg => {
  1004. calls.push('last');
  1005. return true;
  1006. });
  1007. kernel.registerMessageHook(parentHeader.msg_id, msg => {
  1008. calls.push('first');
  1009. return false;
  1010. });
  1011. future.onIOPub = async () => {
  1012. // tslint:disable-next-line:await-promise
  1013. await calls.push('iopub');
  1014. };
  1015. });
  1016. kernel = await tester.start();
  1017. future = kernel.requestExecute(options, false);
  1018. await future.done;
  1019. // the last hook was called for the stream and the status message.
  1020. expect(calls).toEqual(['first', 'first']);
  1021. await tester.shutdown();
  1022. tester.dispose();
  1023. });
  1024. it('should process additions on the next run', async () => {
  1025. const options: KernelMessage.IExecuteRequestMsg['content'] = {
  1026. code: 'test',
  1027. silent: false,
  1028. store_history: true,
  1029. user_expressions: {},
  1030. allow_stdin: false,
  1031. stop_on_error: false
  1032. };
  1033. const calls: string[] = [];
  1034. const tester = new KernelTester();
  1035. let future: Kernel.IShellFuture;
  1036. let kernel: Kernel.IKernelConnection;
  1037. tester.onMessage(message => {
  1038. // send a reply
  1039. const parentHeader = message.header;
  1040. const session = 'session';
  1041. tester.send(
  1042. KernelMessage.createMessage({
  1043. parentHeader,
  1044. session,
  1045. channel: 'shell',
  1046. msgType: 'comm_open',
  1047. content: { comm_id: 'B', data: {}, target_name: 'C' }
  1048. })
  1049. );
  1050. future.onReply = () => {
  1051. // trigger onIOPub with a 'stream' message
  1052. tester.send(
  1053. KernelMessage.createMessage({
  1054. parentHeader,
  1055. session,
  1056. channel: 'iopub',
  1057. msgType: 'stream',
  1058. content: { name: 'stdout', text: 'foo' }
  1059. })
  1060. );
  1061. // trigger onDone
  1062. tester.send(
  1063. KernelMessage.createMessage({
  1064. parentHeader,
  1065. session,
  1066. channel: 'iopub',
  1067. msgType: 'status',
  1068. content: { execution_state: 'idle' }
  1069. })
  1070. );
  1071. };
  1072. kernel.registerMessageHook(parentHeader.msg_id, msg => {
  1073. calls.push('last');
  1074. kernel.registerMessageHook(parentHeader.msg_id, msg => {
  1075. calls.push('first');
  1076. return true;
  1077. });
  1078. return true;
  1079. });
  1080. future.onIOPub = () => {
  1081. calls.push('iopub');
  1082. };
  1083. });
  1084. kernel = await tester.start();
  1085. future = kernel.requestExecute(options, false);
  1086. await future.done;
  1087. expect(calls).toEqual(['last', 'iopub', 'first', 'last', 'iopub']);
  1088. await tester.shutdown();
  1089. tester.dispose();
  1090. });
  1091. it('should deactivate a hook immediately on removal', async () => {
  1092. const options: KernelMessage.IExecuteRequestMsg['content'] = {
  1093. code: 'test',
  1094. silent: false,
  1095. store_history: true,
  1096. user_expressions: {},
  1097. allow_stdin: false,
  1098. stop_on_error: false
  1099. };
  1100. const calls: string[] = [];
  1101. const tester = new KernelTester();
  1102. let future: Kernel.IShellFuture;
  1103. let kernel: Kernel.IKernelConnection;
  1104. tester.onMessage(message => {
  1105. // send a reply
  1106. const parentHeader = message.header;
  1107. const session = 'session';
  1108. tester.send(
  1109. KernelMessage.createMessage({
  1110. parentHeader,
  1111. session,
  1112. channel: 'shell',
  1113. msgType: 'comm_open',
  1114. content: { comm_id: 'B', data: {}, target_name: 'C' }
  1115. })
  1116. );
  1117. future.onReply = () => {
  1118. // trigger onIOPub with a 'stream' message
  1119. tester.send(
  1120. KernelMessage.createMessage({
  1121. parentHeader,
  1122. session,
  1123. channel: 'iopub',
  1124. msgType: 'stream',
  1125. content: { name: 'stdout', text: 'foo' }
  1126. })
  1127. );
  1128. // trigger onDone
  1129. tester.send(
  1130. KernelMessage.createMessage({
  1131. parentHeader,
  1132. session,
  1133. channel: 'iopub',
  1134. msgType: 'status',
  1135. content: { execution_state: 'idle' }
  1136. })
  1137. );
  1138. };
  1139. const toDelete = (msg: KernelMessage.IIOPubMessage) => {
  1140. calls.push('delete');
  1141. return true;
  1142. };
  1143. kernel.registerMessageHook(parentHeader.msg_id, toDelete);
  1144. kernel.registerMessageHook(parentHeader.msg_id, msg => {
  1145. if (calls.length > 0) {
  1146. // delete the hook the second time around
  1147. kernel.removeMessageHook(parentHeader.msg_id, toDelete);
  1148. }
  1149. calls.push('first');
  1150. return true;
  1151. });
  1152. future.onIOPub = () => {
  1153. calls.push('iopub');
  1154. };
  1155. });
  1156. kernel = await tester.start();
  1157. future = kernel.requestExecute(options, false);
  1158. await future.done;
  1159. expect(calls).toEqual(['first', 'delete', 'iopub', 'first', 'iopub']);
  1160. await tester.shutdown();
  1161. tester.dispose();
  1162. });
  1163. });
  1164. describe('handles messages asynchronously', () => {
  1165. // TODO: Also check that messages are canceled appropriately. In particular, when
  1166. // a kernel is restarted, then a message is sent for a comm open from the
  1167. // old session, the comm open should be canceled.
  1168. it('should run handlers in order', async () => {
  1169. const options: KernelMessage.IExecuteRequestMsg['content'] = {
  1170. code: 'test',
  1171. silent: false,
  1172. store_history: true,
  1173. user_expressions: {},
  1174. allow_stdin: true,
  1175. stop_on_error: false
  1176. };
  1177. const tester = new KernelTester();
  1178. const kernel = await tester.start();
  1179. const future = kernel.requestExecute(options, false);
  1180. // The list of emissions from the anyMessage signal.
  1181. const msgSignal: string[][] = [];
  1182. const msgSignalExpected: string[][] = [];
  1183. // The list of message processing calls
  1184. const calls: string[][] = [];
  1185. const callsExpected: string[][] = [];
  1186. function pushIopub(msgId: string) {
  1187. callsExpected.push([msgId, 'future hook a']);
  1188. callsExpected.push([msgId, 'future hook b']);
  1189. callsExpected.push([msgId, 'kernel hook a']);
  1190. callsExpected.push([msgId, 'kernel hook b']);
  1191. callsExpected.push([msgId, 'iopub']);
  1192. msgSignalExpected.push([msgId, 'iopub']);
  1193. }
  1194. function pushCommOpen(msgId: string) {
  1195. pushIopub(msgId);
  1196. callsExpected.push([msgId, 'comm open']);
  1197. }
  1198. function pushCommMsg(msgId: string) {
  1199. pushIopub(msgId);
  1200. callsExpected.push([msgId, 'comm msg']);
  1201. }
  1202. function pushCommClose(msgId: string) {
  1203. pushIopub(msgId);
  1204. callsExpected.push([msgId, 'comm close']);
  1205. }
  1206. function pushStdin(msgId: string) {
  1207. callsExpected.push([msgId, 'stdin']);
  1208. msgSignalExpected.push([msgId, 'stdin']);
  1209. }
  1210. function pushReply(msgId: string) {
  1211. callsExpected.push([msgId, 'reply']);
  1212. msgSignalExpected.push([msgId, 'shell']);
  1213. }
  1214. const anyMessageDone = new PromiseDelegate();
  1215. const handlingBlock = new PromiseDelegate();
  1216. tester.onMessage(message => {
  1217. tester.onMessage(() => {
  1218. return;
  1219. });
  1220. tester.parentHeader = message.header;
  1221. pushIopub(tester.sendStatus('busy', 'busy'));
  1222. pushIopub(tester.sendStream('stdout', { name: 'stdout', text: 'foo' }));
  1223. pushCommOpen(
  1224. tester.sendCommOpen('comm open', {
  1225. target_name: 'commtarget',
  1226. comm_id: 'commid',
  1227. data: {}
  1228. })
  1229. );
  1230. pushIopub(
  1231. tester.sendDisplayData('display 1', { data: {}, metadata: {} })
  1232. );
  1233. pushCommMsg(
  1234. tester.sendCommMsg('comm 1', { comm_id: 'commid', data: {} })
  1235. );
  1236. pushCommMsg(
  1237. tester.sendCommMsg('comm 2', { comm_id: 'commid', data: {} })
  1238. );
  1239. pushCommClose(
  1240. tester.sendCommClose('comm close', { comm_id: 'commid', data: {} })
  1241. );
  1242. pushStdin(
  1243. tester.sendInputRequest('stdin', { prompt: '', password: false })
  1244. );
  1245. pushIopub(
  1246. tester.sendDisplayData('display 2', {
  1247. data: {},
  1248. metadata: {},
  1249. transient: { display_id: 'displayid' }
  1250. })
  1251. );
  1252. pushIopub(
  1253. tester.sendUpdateDisplayData('update display', {
  1254. data: {},
  1255. metadata: {},
  1256. transient: { display_id: 'displayid' }
  1257. })
  1258. );
  1259. pushIopub(
  1260. tester.sendExecuteResult('execute result', {
  1261. execution_count: 1,
  1262. data: {},
  1263. metadata: {}
  1264. })
  1265. );
  1266. pushIopub(tester.sendStatus('idle', 'idle'));
  1267. pushReply(
  1268. tester.sendExecuteReply('execute reply', {
  1269. status: 'ok',
  1270. execution_count: 1,
  1271. user_expressions: {}
  1272. })
  1273. );
  1274. tester.parentHeader = undefined;
  1275. });
  1276. kernel.anyMessage.connect((k, args) => {
  1277. msgSignal.push([args.msg.header.msg_id, args.msg.channel]);
  1278. if (args.msg.header.msg_id === 'execute reply') {
  1279. anyMessageDone.resolve(undefined);
  1280. }
  1281. });
  1282. kernel.registerMessageHook(future.msg.header.msg_id, async msg => {
  1283. // Make this hook call asynchronous
  1284. // tslint:disable-next-line:await-promise
  1285. await calls.push([msg.header.msg_id, 'kernel hook b']);
  1286. return true;
  1287. });
  1288. kernel.registerMessageHook(future.msg.header.msg_id, async msg => {
  1289. calls.push([msg.header.msg_id, 'kernel hook a']);
  1290. return true;
  1291. });
  1292. kernel.registerCommTarget('commtarget', async (comm, msg) => {
  1293. // tslint:disable-next-line:await-promise
  1294. await calls.push([msg.header.msg_id, 'comm open']);
  1295. comm.onMsg = async msg => {
  1296. // tslint:disable-next-line:await-promise
  1297. await calls.push([msg.header.msg_id, 'comm msg']);
  1298. };
  1299. comm.onClose = async msg => {
  1300. // tslint:disable-next-line:await-promise
  1301. await calls.push([msg.header.msg_id, 'comm close']);
  1302. };
  1303. });
  1304. future.registerMessageHook(async msg => {
  1305. // tslint:disable-next-line:await-promise
  1306. await calls.push([msg.header.msg_id, 'future hook b']);
  1307. return true;
  1308. });
  1309. future.registerMessageHook(async msg => {
  1310. // Delay processing until after we've checked the anyMessage results.
  1311. await handlingBlock.promise;
  1312. // tslint:disable-next-line:await-promise
  1313. await calls.push([msg.header.msg_id, 'future hook a']);
  1314. return true;
  1315. });
  1316. future.onIOPub = async msg => {
  1317. // tslint:disable-next-line:await-promise
  1318. await calls.push([msg.header.msg_id, 'iopub']);
  1319. };
  1320. future.onStdin = async msg => {
  1321. // tslint:disable-next-line:await-promise
  1322. await calls.push([msg.header.msg_id, 'stdin']);
  1323. };
  1324. future.onReply = async msg => {
  1325. // tslint:disable-next-line:await-promise
  1326. await calls.push([msg.header.msg_id, 'reply']);
  1327. };
  1328. // Give the kernel time to receive and queue up the messages.
  1329. await anyMessageDone.promise;
  1330. // At this point, the synchronous anyMessage signal should have been
  1331. // emitted for every message, but no actual message handling should have
  1332. // happened.
  1333. expect(msgSignal).toEqual(msgSignalExpected);
  1334. expect(calls).toEqual([]);
  1335. // Release the lock on message processing.
  1336. handlingBlock.resolve(undefined);
  1337. await future.done;
  1338. expect(calls).toEqual(callsExpected);
  1339. await tester.shutdown();
  1340. tester.dispose();
  1341. });
  1342. });
  1343. });