(eval-when-compile (require 'cl)) (require 'ert) (when load-file-name (add-to-list 'load-path (concat (file-name-directory load-file-name) "mocker"))) (require 'mocker) (require 'ein-notebook) (defvar eintest:notebook-data-simple-json "{ \"metadata\": { \"name\": \"Untitled0\" }, \"name\": \"Untitled0\", \"nbformat\": 2, \"worksheets\": [ { \"cells\": [ { \"cell_type\": \"code\", \"collapsed\": false, \"input\": \"1 + 1\", \"language\": \"python\", \"outputs\": [ { \"output_type\": \"pyout\", \"prompt_number\": 1, \"text\": \"2\" } ], \"prompt_number\": 1 } ] } ] } ") (defun eintest:notebook-from-json (json-string &optional notebook-id) (unless notebook-id (setq notebook-id "NOTEBOOK-ID")) (with-temp-buffer (erase-buffer) (insert json-string) (flet ((pop-to-buffer (buf) buf) (ein:notebook-start-kernel ())) (ein:notebook-url-retrieve-callback nil (ein:notebook-new "DUMMY-URL" notebook-id))))) (defun eintest:notebook-make-data (cells &optional name) (unless name (setq name "Dummy Name")) `((metadata . ((name . ,name))) (name . ,name) (worksheets . [((cells . ,(apply #'vector cells)))]))) (defun eintest:notebook-make-empty (&optional name) "Make empty notebook and return its buffer." (eintest:notebook-from-json (json-encode (eintest:notebook-make-data nil name)))) (ert-deftest ein:notebook-from-json-simple () (with-current-buffer (eintest:notebook-from-json eintest:notebook-data-simple-json) (should (ein:$notebook-p ein:notebook)) (should (equal (ein:$notebook-notebook-id ein:notebook) "NOTEBOOK-ID")) (should (equal (ein:$notebook-notebook-name ein:notebook) "Untitled0")) (should (equal (ein:notebook-ncells ein:notebook) 1)) (let ((cell (car (ein:notebook-get-cells ein:notebook)))) (should (ein:codecell-p cell)) (should (equal (oref cell :input) "1 + 1")) (should (equal (oref cell :input-prompt-number) 1)) (let ((outputs (oref cell :outputs))) (should (equal (length outputs) 1)) (let ((o1 (car outputs))) (should (equal (plist-get o1 :output_type) "pyout")) (should (equal (plist-get o1 :prompt_number) 1)) (should (equal (plist-get o1 :text) "2"))))))) (ert-deftest ein:notebook-from-json-empty () (with-current-buffer (eintest:notebook-make-empty) (should (ein:$notebook-p ein:notebook)) (should (equal (ein:$notebook-notebook-id ein:notebook) "NOTEBOOK-ID")) (should (equal (ein:$notebook-notebook-name ein:notebook) "Dummy Name")) (should (equal (ein:notebook-ncells ein:notebook) 0)))) (ert-deftest ein:notebook-insert-cell-below-command-simple () (with-current-buffer (eintest:notebook-make-empty) (ein:notebook-insert-cell-below-command) (ein:notebook-insert-cell-below-command) (ein:notebook-insert-cell-below-command) (should (equal (ein:notebook-ncells ein:notebook) 3)))) (ert-deftest ein:notebook-insert-cell-above-command-simple () (with-current-buffer (eintest:notebook-make-empty) (ein:notebook-insert-cell-above-command) (ein:notebook-insert-cell-above-command) (ein:notebook-insert-cell-above-command) (should (equal (ein:notebook-ncells ein:notebook) 3)))) (ert-deftest ein:notebook-delete-cell-command-simple () (with-current-buffer (eintest:notebook-make-empty) (loop repeat 3 do (ein:notebook-insert-cell-above-command)) (should (equal (ein:notebook-ncells ein:notebook) 3)) (loop repeat 3 do (ein:notebook-delete-cell-command)) (should (equal (ein:notebook-ncells ein:notebook) 0)))) (ert-deftest ein:notebook-kill-cell-command-simple () (with-current-buffer (eintest:notebook-make-empty) (let (ein:kill-ring ein:kill-ring-yank-pointer) (loop repeat 3 do (ein:notebook-insert-cell-above-command)) (should (equal (ein:notebook-ncells ein:notebook) 3)) (loop for i from 1 to 3 do (ein:notebook-kill-cell-command) do (should (equal (length ein:kill-ring) i)) do (should (equal (ein:notebook-ncells ein:notebook) (- 3 i))))))) (ert-deftest ein:notebook-copy-cell-command-simple () (with-current-buffer (eintest:notebook-make-empty) (let (ein:kill-ring ein:kill-ring-yank-pointer) (loop repeat 3 do (ein:notebook-insert-cell-above-command)) (should (equal (ein:notebook-ncells ein:notebook) 3)) (loop repeat 3 do (ein:notebook-copy-cell-command)) (should (equal (ein:notebook-ncells ein:notebook) 3)) (should (equal (length ein:kill-ring) 3))))) (ert-deftest ein:notebook-yank-cell-command-simple () (with-current-buffer (eintest:notebook-make-empty) (let (ein:kill-ring ein:kill-ring-yank-pointer) (loop repeat 3 do (ein:notebook-insert-cell-above-command)) (should (equal (ein:notebook-ncells ein:notebook) 3)) (loop repeat 3 do (ein:notebook-kill-cell-command)) (should (equal (ein:notebook-ncells ein:notebook) 0)) (should (equal (length ein:kill-ring) 3)) (loop repeat 3 do (ein:notebook-yank-cell-command)) (should (equal (ein:notebook-ncells ein:notebook) 3))))) (ert-deftest ein:notebook-yank-cell-command-two-buffers () (let (ein:kill-ring ein:kill-ring-yank-pointer) (with-current-buffer (eintest:notebook-make-empty "NB1") (ein:notebook-insert-cell-above-command) (should (equal (ein:notebook-ncells ein:notebook) 1)) (ein:notebook-kill-cell-command) (should (equal (ein:notebook-ncells ein:notebook) 0)) (flet ((y-or-n-p (&rest ignore) t)) ;; FIXME: are there anyway to skip confirmation? (kill-buffer))) (with-current-buffer (eintest:notebook-make-empty "NB2") (ein:notebook-yank-cell-command) (should (equal (ein:notebook-ncells ein:notebook) 1))))) (ert-deftest ein:notebook-toggle-cell-type-simple () (with-current-buffer (eintest:notebook-make-empty) (ein:notebook-insert-cell-above-command) (insert "some text") (should (ein:codecell-p (ein:notebook-get-current-cell))) ;; toggle to markdown (ein:notebook-toggle-cell-type) (should (ein:markdowncell-p (ein:notebook-get-current-cell))) (should (looking-at "some text")) ;; toggle to code (ein:notebook-toggle-cell-type) (should (ein:codecell-p (ein:notebook-get-current-cell))) (should (looking-at "some text")))) (defun eintest:notebook-split-cell-at-point (insert-text search-text head-text tail-text &optional no-trim) (with-current-buffer (eintest:notebook-make-empty) (ein:notebook-insert-cell-above-command) (insert insert-text) (search-backward search-text) ;; do it (ein:notebook-split-cell-at-point no-trim) ;; check the "tail" cell (let ((cell (ein:notebook-get-current-cell))) (ein:cell-goto cell) (should (equal (ein:cell-get-text cell) tail-text))) ;; check the "head" cell (ein:notebook-goto-prev-input-command) (let ((cell (ein:notebook-get-current-cell))) (ein:cell-goto cell) (should (equal (ein:cell-get-text cell) head-text))))) (ert-deftest ein:notebook-split-cell-at-point-before-newline () (eintest:notebook-split-cell-at-point "some\ntext" "text" "some" "text")) (ert-deftest ein:notebook-split-cell-at-point-after-newline () (eintest:notebook-split-cell-at-point "some\ntext" "\ntext" "some" "text")) (ert-deftest ein:notebook-split-cell-at-point-before-newline-no-trim () (eintest:notebook-split-cell-at-point "some\ntext" "text" "some\n" "text" t)) (ert-deftest ein:notebook-split-cell-at-point-after-newline-no-trim () (eintest:notebook-split-cell-at-point "some\ntext" "\ntext" "some" "\ntext" t)) (ert-deftest ein:notebook-merge-cell-command-simple () (with-current-buffer (eintest:notebook-make-empty) (ein:notebook-insert-cell-above-command) (insert "Cell 1") (ein:notebook-insert-cell-above-command) (insert "Cell 0") (ein:notebook-merge-cell-command) (ein:cell-goto (ein:notebook-get-current-cell)) (should (looking-at "Cell 0\nCell 1")))) (ert-deftest ein:notebook-merge-cell-command-prev () (with-current-buffer (eintest:notebook-make-empty) (ein:notebook-insert-cell-below-command) (insert "Cell 0") (ein:notebook-insert-cell-below-command) (insert "Cell 1") (ein:notebook-merge-cell-command t) (ein:cell-goto (ein:notebook-get-current-cell)) (should (looking-at "Cell 0\nCell 1")))) (ert-deftest ein:notebook-goto-next-input-command-simple () (with-current-buffer (eintest:notebook-make-empty) (loop for i downfrom 2 to 0 do (ein:notebook-insert-cell-above-command) do (insert (format "Cell %s" i))) (should (equal (ein:notebook-ncells ein:notebook) 3)) ;; (message "%s" (buffer-string)) (loop for i from 0 below 2 do (beginning-of-line) ; This is required, I need to check why do (should (looking-at (format "Cell %s" i))) do (ein:notebook-goto-next-input-command) do (should (looking-at (format "Cell %s" (1+ i))))))) (ert-deftest ein:notebook-goto-prev-input-command-simple () (with-current-buffer (eintest:notebook-make-empty) (loop for i from 0 below 3 do (ein:notebook-insert-cell-below-command) do (insert (format "Cell %s" i))) (should (equal (ein:notebook-ncells ein:notebook) 3)) ;; (message "%s" (buffer-string)) (loop for i downfrom 2 to 1 do (beginning-of-line) ; This is required, I need to check why do (should (looking-at (format "Cell %s" i))) do (ein:notebook-goto-prev-input-command) do (should (looking-at (format "Cell %s" (1- i))))))) (ert-deftest ein:notebook-move-cell-up-command-simple () (with-current-buffer (eintest:notebook-make-empty) (loop for i from 0 below 3 do (ein:notebook-insert-cell-below-command) do (insert (format "Cell %s" i))) (beginning-of-line) (should (looking-at "Cell 2")) (loop repeat 2 do (ein:notebook-move-cell-up-command)) ;; (message "%s" (buffer-string)) (beginning-of-line) (should (looking-at "Cell 2")) (should (search-forward "Cell 0" nil t)) (should (search-forward "Cell 1" nil t)) (should-not (search-forward "Cell 2" nil t)))) (ert-deftest ein:notebook-move-cell-down-command-simple () (with-current-buffer (eintest:notebook-make-empty) (loop for i from 0 below 3 do (ein:notebook-insert-cell-above-command) do (insert (format "Cell %s" i))) (loop repeat 2 do (ein:notebook-move-cell-down-command)) (beginning-of-line) (should (looking-at "Cell 2")) (should (search-backward "Cell 0" nil t)) (should (search-backward "Cell 1" nil t)) (should-not (search-backward "Cell 2" nil t)))) (ert-deftest ein:notebook-execute-current-cell () (with-current-buffer (eintest:notebook-make-empty) (ein:notebook-insert-cell-below-command) (let ((text "print 'Hello World'") (cell (ein:notebook-get-current-cell)) (msg-id "DUMMY-MSG-ID")) (setf (ein:$notebook-kernel ein:notebook) "DUMMY-KERNEL") (insert text) (mocker-let ((ein:kernel-execute (notebook text) ((:input (list (ein:$notebook-kernel ein:notebook) text) :output msg-id))) (ein:kernel-ready-p (kernel) ((:input (list (ein:$notebook-kernel ein:notebook)) :output t)))) (ein:notebook-execute-current-cell)) (should (equal (gethash msg-id (ein:$notebook-msg-cell-map ein:notebook)) (oref cell :cell-id))) (save-excursion (goto-char (point-min)) (should-not (search-forward "In [1]:" nil t))) (let* ((payload nil) (content (list :execution_count 1 :payload payload)) (packet (list :msg_type "execute_reply" :parent_header (list :msg_id msg-id) :content content))) (ein:notebook-handle-shell-reply ein:notebook (json-encode packet))) (should (= (oref cell :input-prompt-number) 1)) (save-excursion (goto-char (point-min)) (should (search-forward "In [1]:" nil t))) (let* ((content (list :data "'Hello World'" :name "stdout")) (packet (list :msg_type "stream" :parent_header (list :msg_id msg-id) :content content))) (ein:notebook-handle-iopub-reply ein:notebook (json-encode packet))) (should (= (ein:cell-num-outputs cell) 1)) (save-excursion (goto-char (point-min)) (should (search-forward "In [1]:" nil t)) (should (search-forward "print 'Hello World'" nil t)) (should (search-forward "Hello World" nil t)) ; stream output (should-not (search-forward "Hello World" nil t)))))) ;; Misc unit tests (ert-deftest ein:notebook-test-notebook-name-simple () (should-not (ein:notebook-test-notebook-name nil)) (should-not (ein:notebook-test-notebook-name "")) (should-not (ein:notebook-test-notebook-name "/")) (should-not (ein:notebook-test-notebook-name "\\")) (should-not (ein:notebook-test-notebook-name "a/b")) (should-not (ein:notebook-test-notebook-name "a\\b")) (should (ein:notebook-test-notebook-name "This is a OK notebook name"))) (ert-deftest ein:notebook-console-security-dir-string () (let ((ein:notebook-console-security-dir "/some/dir/") (notebook (ein:notebook-new "DUMMY-URL-OR-PORT" "DUMMY-NOTEBOOK-ID"))) (should (equal (ein:notebook-console-security-dir-get notebook) ein:notebook-console-security-dir)))) (ert-deftest ein:notebook-console-security-dir-list () (let ((ein:notebook-console-security-dir '((8888 . "/dir/8888/") ("htttp://dummy.org" . "/dir/http/") (7777 . my-secret-directory) (default . "/dir/default/"))) (my-secret-directory "/dir/secret/")) (let ((notebook (ein:notebook-new 8888 "DUMMY-NOTEBOOK-ID"))) (should (equal (ein:notebook-console-security-dir-get notebook) "/dir/8888/"))) (let ((notebook (ein:notebook-new "htttp://dummy.org" "DUMMY-NOTEBOOK-ID"))) (should (equal (ein:notebook-console-security-dir-get notebook) "/dir/http/"))) (let ((notebook (ein:notebook-new 7777 "DUMMY-NOTEBOOK-ID"))) (should (equal (ein:notebook-console-security-dir-get notebook) "/dir/secret/"))) (let ((notebook (ein:notebook-new 9999 "DUMMY-NOTEBOOK-ID"))) (should (equal (ein:notebook-console-security-dir-get notebook) "/dir/default/"))))) (ert-deftest ein:notebook-console-security-dir-func () (let ((ein:notebook-console-security-dir '(lambda (x) (should (equal x "DUMMY-URL-OR-PORT")) "/dir/")) (notebook (ein:notebook-new "DUMMY-URL-OR-PORT" "DUMMY-NOTEBOOK-ID"))) (should (equal (ein:notebook-console-security-dir-get notebook) "/dir/")))) (provide 'test-ein-notebook)