Forráskód Böngészése

Finish up mock kernel and add execution test

Steven Silvester 9 éve
szülő
commit
fcb67aa026
2 módosított fájl, 130 hozzáadás és 282 törlés
  1. 102 281
      test/src/notebook/notebook/mock.ts
  2. 28 1
      test/src/notebook/notebook/model.spec.ts

+ 102 - 281
test/src/notebook/notebook/mock.ts

@@ -15,6 +15,10 @@ import {
   INotebookSession, ISessionOptions, IHistoryRequest, IHistoryReply
 } from 'jupyter-js-services';
 
+import {
+  KernelFutureHandler
+} from 'jupyter-js-services/lib/kernelfuture';
+
 import {
   DisposableDelegate, IDisposable
 } from 'phosphor-disposable';
@@ -24,197 +28,25 @@ import {
 } from 'phosphor-signaling';
 
 
-/**
- * Implementation of a kernel future.
- */
-export
-class KernelFutureHandler extends DisposableDelegate implements IKernelFuture {
-
-  /**
-   * Construct a new KernelFutureHandler.
-   */
-  constructor(cb: () => void, msg: IKernelMessage, expectShell: boolean, disposeOnDone: boolean) {
-    super(cb);
-    this._msg = msg;
-    if (!expectShell) {
-      this._setFlag(KernelPrivate.KernelFutureFlag.GotReply);
-    }
-    this._disposeOnDone = disposeOnDone;
-  }
-
-  /**
-   * Get the original outgoing message.
-   */
-  get msg(): IKernelMessage {
-    return this._msg;
-  }
-
-  /**
-   * Check for message done state.
-   */
-  get isDone(): boolean {
-    return this._testFlag(KernelPrivate.KernelFutureFlag.IsDone);
-  }
-
-  /**
-   * Get the reply handler.
-   */
-  get onReply(): (msg: IKernelMessage) => void {
-    return this._reply;
-  }
-
-  /**
-   * Set the reply handler.
-   */
-  set onReply(cb: (msg: IKernelMessage) => void) {
-    this._reply = cb;
-  }
-
-  /**
-   * Get the iopub handler.
-   */
-  get onIOPub(): (msg: IKernelMessage) => void {
-    return this._iopub;
-  }
-
-  /**
-   * Set the iopub handler.
-   */
-  set onIOPub(cb: (msg: IKernelMessage) => void) {
-    this._iopub = cb;
-  }
-
-  /**
-   * Get the done handler.
-   */
-  get onDone(): (msg: IKernelMessage) => void  {
-    return this._done;
-  }
-
-  /**
-   * Set the done handler.
-   */
-  set onDone(cb: (msg: IKernelMessage) => void) {
-    this._done = cb;
-  }
-
-  /**
-   * Get the stdin handler.
-   */
-  get onStdin(): (msg: IKernelMessage) => void {
-    return this._stdin;
-  }
-
-  /**
-   * Set the stdin handler.
-   */
-  set onStdin(cb: (msg: IKernelMessage) => void) {
-    this._stdin = cb;
-  }
-
-  /**
-   * Dispose and unregister the future.
-   */
-  dispose(): void {
-    this._stdin = null;
-    this._iopub = null;
-    this._reply = null;
-    this._done = null;
-    this._msg = null;
-    super.dispose();
-  }
-
-  /**
-   * Handle an incoming kernel message.
-   */
-  handleMsg(msg: IKernelMessage): void {
-    switch (msg.channel) {
-    case 'shell':
-      this._handleReply(msg);
-      break;
-    case 'stdin':
-      this._handleStdin(msg);
-      break;
-    case 'iopub':
-      this._handleIOPub(msg);
-      break;
-    }
-  }
-
-  private _handleReply(msg: IKernelMessage): void {
-    let reply = this._reply;
-    if (reply) reply(msg);
-    this._setFlag(KernelPrivate.KernelFutureFlag.GotReply);
-    if (this._testFlag(KernelPrivate.KernelFutureFlag.GotIdle)) {
-      this._handleDone(msg);
-    }
-  }
-
-  private _handleStdin(msg: IKernelMessage): void {
-    let stdin = this._stdin;
-    if (stdin) stdin(msg);
-  }
-
-  private _handleIOPub(msg: IKernelMessage): void {
-    let iopub = this._iopub;
-    if (iopub) iopub(msg);
-    if (msg.header.msg_type === 'status' &&
-        msg.content.execution_state === 'idle') {
-      this._setFlag(KernelPrivate.KernelFutureFlag.GotIdle);
-      if (this._testFlag(KernelPrivate.KernelFutureFlag.GotReply)) {
-        this._handleDone(msg);
-      }
-    }
-  }
-
-  private _handleDone(msg: IKernelMessage): void {
-    if (this.isDone) {
-      return;
-    }
-    this._setFlag(KernelPrivate.KernelFutureFlag.IsDone);
-    let done = this._done;
-    if (done) done(msg);
-    this._done = null;
-    if (this._disposeOnDone) {
-      this.dispose();
-    }
-  }
-
-  /**
-   * Test whether the given future flag is set.
-   */
-  private _testFlag(flag: KernelPrivate.KernelFutureFlag): boolean {
-    return (this._status & flag) !== 0;
-  }
-
-  /**
-   * Set the given future flag.
-   */
-  private _setFlag(flag: KernelPrivate.KernelFutureFlag): void {
-    this._status |= flag;
-  }
-
-  private _msg: IKernelMessage = null;
-  private _status = 0;
-  private _stdin: (msg: IKernelMessage) => void = null;
-  private _iopub: (msg: IKernelMessage) => void = null;
-  private _reply: (msg: IKernelMessage) => void = null;
-  private _done: (msg: IKernelMessage) => void = null;
-  private _disposeOnDone = true;
-}
-
-
 /**
  * A mock kernel object that only handles execution requests.
+ * It only keeps one kernel future at a time.
  */
 export
 class MockKernel implements IKernel {
 
+  id: string;
+  name: string;
+  username = '';
+  clientId = '';
+
   constructor(options?: IKernelId) {
     options = options || {};
-    this._id = options.id || '';
-    this._name = options.name || 'python';
-    this._status = KernelStatus.Idle;
+    this.id = options.id || '';
+    this.name = options.name || 'python';
+    Promise.resolve().then(() => {
+      this._changeStatus(KernelStatus.Idle);
+    });
   }
 
   /**
@@ -231,34 +63,6 @@ class MockKernel implements IKernel {
     return KernelPrivate.unhandledMessageSignal.bind(this);
   }
 
-  /**
-   * The id of the server-side kernel.
-   */
-  get id(): string {
-    return this._id;
-  }
-
-  /**
-   * The name of the server-side kernel.
-   */
-  get name(): string {
-    return this._name;
-  }
-
-  /**
-   * The client username.
-   */
-   get username(): string {
-     return '';
-   }
-
-  /**
-   * The client unique id.
-   */
-  get clientId(): string {
-    return '';
-  }
-
   /**
    * The current status of the kernel.
    */
@@ -266,18 +70,6 @@ class MockKernel implements IKernel {
     return this._status;
   }
 
-  /**
-   * Get a copy of the default ajax settings for the kernel.
-   */
-  get ajaxSettings(): IAjaxSettings {
-    return {};
-  }
-
-  /**
-   * Set the default ajax settings for the kernel.
-   */
-  set ajaxSettings(value: IAjaxSettings) { }
-
   /**
    * Test whether the kernel has been disposed.
    */
@@ -298,28 +90,67 @@ class MockKernel implements IKernel {
   /**
    * Send a shell message to the kernel.
    */
-  sendShellMessage(msg: IKernelMessage, expectReply=false, disposeOnDone=true): KernelFutureHandler {
-    return new KernelFutureHandler(() => {}, msg, expectReply, disposeOnDone);
+  sendShellMessage(msg: IKernelMessage, expectReply=false, disposeOnDone=true): IKernelFuture {
+    let future = new KernelFutureHandler(() => {}, msg, expectReply, disposeOnDone);
+    this._future = future;
+    return future;
+  }
+
+  /**
+   * Send a message to the kernel.
+   */
+  sendServerMessage(msgType: string, channel: string, contents: any): void {
+    let future = this._future;
+    if (!future) {
+      return;
+    }
+    let options = {
+      msgType,
+      channel,
+      username: this.username,
+      session: this.clientId
+    }
+    let msg = createKernelMessage(options, contents);
+    future.handleMsg(msg);
+  }
+
+  /**
+   * Send a shell reply message to the kernel.
+   */
+  sendShellReply(contents: any): void {
+    let future = this._future;
+    if (!future) {
+      return;
+    }
+    let msgType = future.msg.header.msg_type.replace('_request', '_reply');
+    this.sendServerMessage(msgType, 'shell', contents);
   }
 
   /**
    * Interrupt a kernel.
    */
   interrupt(): Promise<void> {
-    return Promise.resolve(void 0);
+    return Promise.resolve().then(() => {
+      this._changeStatus(KernelStatus.Idle);
+    });
   }
 
   /**
    * Restart a kernel.
    */
   restart(): Promise<void> {
-    return Promise.resolve(void 0);
+    this._changeStatus(KernelStatus.Restarting);
+    return Promise.resolve().then(() => {
+      this._changeStatus(KernelStatus.Idle);
+    });
   }
 
   /**
    * Shutdown a kernel.
    */
   shutdown(): Promise<void> {
+    this._changeStatus(KernelStatus.Dead);
+    this.dispose();
     return Promise.resolve(void 0);
   }
 
@@ -327,21 +158,21 @@ class MockKernel implements IKernel {
    * Send a `kernel_info_request` message.
    */
   kernelInfo(): Promise<IKernelInfo> {
-    return Promise.resolve(void 0);
+    return this._sendKernelMessage('kernel_info_request', 'shell', {});
   }
 
   /**
    * Send a `complete_request` message.
    */
   complete(contents: ICompleteRequest): Promise<ICompleteReply> {
-    return Promise.resolve(void 0);
+    return this._sendKernelMessage('complete_request', 'shell', contents);
   }
 
   /**
    * Send a `history_request` message.
    */
   history(contents: IHistoryRequest): Promise<IHistoryReply> {
-    return Promise.resolve(void 0);
+    return this._sendKernelMessage('history', 'shell', contents);
   }
 
 
@@ -349,7 +180,7 @@ class MockKernel implements IKernel {
    * Send an `inspect_request` message.
    */
   inspect(contents: IInspectRequest): Promise<IInspectReply> {
-    return Promise.resolve(void 0);
+    return this._sendKernelMessage('inspect_request', 'shell', contents);
   }
 
   /**
@@ -378,14 +209,14 @@ class MockKernel implements IKernel {
    * Send an `is_complete_request` message.
    */
   isComplete(contents: IIsCompleteRequest): Promise<IIsCompleteReply> {
-    return Promise.resolve(void 0);
+    return this._sendKernelMessage('is_complete_request', 'shell', contents);
   }
 
   /**
    * Send a `comm_info_request` message.
    */
   commInfo(contents: ICommInfoRequest): Promise<ICommInfoReply> {
-    return Promise.resolve(void 0);
+    return this._sendKernelMessage('comm_info_request', 'shell', contents);
   }
 
   /**
@@ -414,10 +245,38 @@ class MockKernel implements IKernel {
     return Promise.resolve(void 0);
   }
 
-  private _id = '';
-  private _name = '';
+  private _sendKernelMessage(msgType: string, channel: string, contents: any): Promise<any> {
+    let options: IKernelMessageOptions = {
+      msgType,
+      channel,
+      username: this.username,
+      session: this.clientId
+    };
+    let msg = createKernelMessage(options, contents);
+    let future: IKernelFuture;
+    try {
+      future = this.sendShellMessage(msg, true);
+    } catch (e) {
+      return Promise.reject(e);
+    }
+    return new Promise<IKernelInfo>((resolve, reject) => {
+      future.onReply = (msg: IKernelMessage) => {
+        resolve(msg.content);
+      };
+    });
+  }
+
+  private _changeStatus(status: KernelStatus): void {
+    if (this._status === status) {
+      return;
+    }
+    this._status = status;
+    this.statusChanged.emit(status);
+  }
+
   private _status = KernelStatus.Unknown;
   private _isDisposed = false;
+  private _future: KernelFutureHandler = null;
 }
 
 
@@ -427,8 +286,12 @@ class MockKernel implements IKernel {
 export
 class MockSession implements INotebookSession {
 
-  constructor(path: string, kernel?: MockKernel) {
-    this._notebookPath = path;
+  id: string;
+  notebookPath: string;
+  ajaxSettings: IAjaxSettings = {};
+
+  constructor(path: string, kernel?: IKernel) {
+    this.notebookPath = path;
     this._kernel = kernel || new MockKernel();
     this._kernel.statusChanged.connect(this.onKernelStatus, this);
     this._kernel.unhandledMessage.connect(this.onUnhandledMessage, this);
@@ -462,30 +325,13 @@ class MockSession implements INotebookSession {
     return SessionPrivate.unhandledMessageSignal.bind(this);
   }
 
-  /**
-   * Get the session id.
-   *
-   * #### Notes
-   * This is a read-only property.
-   */
-  get id(): string {
-    return this._id;
-  }
-
   /**
    * Get the session kernel object.
    */
-  get kernel() : IKernel {
+  get kernel(): IKernel {
     return this._kernel;
   }
 
-  /**
-   * Get the notebook path.
-   */
-  get notebookPath(): string {
-    return this._notebookPath;
-  }
-
   /**
    * The current status of the session.
    */
@@ -493,18 +339,6 @@ class MockSession implements INotebookSession {
     return this._kernel.status;
   }
 
-  /**
-   * Get a copy of the default ajax settings for the session.
-   */
-  get ajaxSettings(): IAjaxSettings {
-    return {};
-  }
-
-  /**
-   * Set the default ajax settings for the session.
-   */
-  set ajaxSettings(value: IAjaxSettings) { }
-
   /**
    * Test whether the session has been disposed.
    *
@@ -526,7 +360,7 @@ class MockSession implements INotebookSession {
    * Rename or move a notebook.
    */
   renameNotebook(path: string): Promise<void> {
-    this._notebookPath = path;
+    this.notebookPath = path;
     return Promise.resolve(void 0);
   }
 
@@ -565,8 +399,6 @@ class MockSession implements INotebookSession {
   }
 
   private _isDisposed = false;
-  private _id = '';
-  private _notebookPath = '';
   private _kernel: IKernel = null;
 }
 
@@ -613,15 +445,4 @@ namespace KernelPrivate {
    */
   export
   const unhandledMessageSignal = new Signal<IKernel, IKernelMessage>();
-
-  /**
-   * Bit flags for the kernel future state.
-   */
-  export
-  enum KernelFutureFlag {
-    GotReply = 0x1,
-    GotIdle = 0x2,
-    IsDone = 0x4,
-    DisposeOnDone = 0x8,
-  }
 }

+ 28 - 1
test/src/notebook/notebook/model.spec.ts

@@ -38,7 +38,7 @@ import {
 } from '../../../lib/notebook/model';
 
 import {
-  MockSession
+  MockSession, MockKernel
 } from './mock';
 
 
@@ -617,6 +617,33 @@ describe('jupyter-js-notebook', () => {
         expect(cell.input.prompt).to.be('');
       });
 
+      it('should execute on a code cell when there is a session', (done) => {
+        let model = new MyNotebookModel();
+        model.session = new MockSession('test.ipynb');
+        let cell = model.createCodeCell();
+        cell.input.textEditor.text = 'a = 1';
+        model.cells.add(cell);
+        let called = false;
+        model.session.statusChanged.connect(() => {
+          if (called) {
+            return;
+          }
+          called = true;
+          model.runActiveCell();
+          let kernel = model.session.kernel as MockKernel;
+          kernel.sendShellReply({
+            execution_count: 1,
+            data: {},
+            metadata: {}
+          });
+        });
+        cell.input.stateChanged.connect(() => {
+          if (cell.input.prompt === '1') {
+            done();
+          }
+        });
+      });
+
     });
 
     describe('#onEditorChanged()', () => {