ikernel.spec.ts 47 KB

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