mirror of
https://github.com/vale981/emacs-ipython-notebook
synced 2025-03-04 16:51:38 -05:00
1286 lines
52 KiB
EmacsLisp
1286 lines
52 KiB
EmacsLisp
(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)
|
||
(require 'ein-testing-notebook)
|
||
(require 'ein-testing-cell)
|
||
|
||
;; Test utils
|
||
|
||
;;; This is the content portion of a response fromt he content API.
|
||
(defvar eintest:notebook-data-simple-json
|
||
"{
|
||
\"name\": \"Untitled0.ipynb\",
|
||
\"path\": \"\",
|
||
\"type\": \"notebook\",
|
||
\"content\" : {
|
||
\"metadata\": {
|
||
},
|
||
\"nbformat\": 3,
|
||
\"nbformat_minor\": 0,
|
||
|
||
\"worksheets\": [
|
||
{
|
||
\"cells\": [
|
||
{
|
||
\"cell_type\": \"code\",
|
||
\"collapsed\": false,
|
||
\"input\": \"1 + 1\",
|
||
\"language\": \"python\",
|
||
\"outputs\": [
|
||
{
|
||
\"output_type\": \"pyout\",
|
||
\"prompt_number\": 1,
|
||
\"text\": \"2\"
|
||
}
|
||
],
|
||
\"prompt_number\": 1
|
||
}
|
||
]
|
||
}
|
||
]
|
||
}
|
||
}
|
||
")
|
||
|
||
(defvar eintest:notebook-data-simple-json-v4
|
||
"{
|
||
\"metadata\": {
|
||
\"name\": \"Untitled0.ipynb\"
|
||
},
|
||
\"nbformat\": 4,
|
||
\"nbformat_minor\": 0,
|
||
\"cells\": [
|
||
{
|
||
\"cell_type\": \"code\",
|
||
\"metadata\" : {
|
||
\"collapsed\": false,
|
||
},
|
||
\"source\": \"1 + 1\",
|
||
\"outputs\": [
|
||
{
|
||
\"name\": \"stdout\",
|
||
\"output_type\": \"stream\",
|
||
\"text\": \"2\"
|
||
}
|
||
],
|
||
\"prompt_number\": 1
|
||
}
|
||
]
|
||
}
|
||
")
|
||
|
||
|
||
(defun eintest:kernel-fake-execute-reply (kernel msg-id execution-count)
|
||
(let* ((payload nil)
|
||
(content (list :execution_count 1 :payload payload))
|
||
(packet (list :header (list :msg_type "execute_reply")
|
||
:parent_header (list :msg_id msg-id)
|
||
:content content)))
|
||
(ein:kernel--handle-shell-reply kernel (json-encode packet))))
|
||
|
||
(defun eintest:kernel-fake-stream (kernel msg-id data)
|
||
(let* ((content (list :text data
|
||
:name "stdout"))
|
||
(packet (list :header (list :msg_type "stream")
|
||
:parent_header (list :msg_id msg-id)
|
||
:content content)))
|
||
(ein:kernel--handle-iopub-reply kernel (json-encode packet))))
|
||
|
||
(defun eintest:check-search-forward-from (start string &optional null-string)
|
||
"Search STRING from START and check it is found.
|
||
When non-`nil' NULL-STRING is given, it is searched from the
|
||
position where the search of the STRING ends and check that it
|
||
is not found."
|
||
(save-excursion
|
||
(goto-char start)
|
||
(should (search-forward string nil t))
|
||
(when null-string
|
||
(should-not (search-forward null-string nil t)))))
|
||
|
||
(defun eintest:cell-check-output (cell regexp)
|
||
(save-excursion
|
||
(goto-char (ein:cell-location cell :after-input))
|
||
(should (looking-at-p (concat "\\=" regexp "\n")))))
|
||
|
||
|
||
;; from-json
|
||
|
||
(ert-deftest ein:notebook-from-json-simple ()
|
||
(with-current-buffer (ein:testing-notebook-from-json
|
||
eintest:notebook-data-simple-json)
|
||
(should (ein:$notebook-p ein:%notebook%))
|
||
(should (equal (ein:$notebook-notebook-name ein:%notebook%) "Untitled0.ipynb"))
|
||
(should (equal (ein:worksheet-ncells ein:%worksheet%) 1))
|
||
(let ((cell (car (ein:worksheet-get-cells ein:%worksheet%))))
|
||
(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 (ein:testing-notebook-make-empty)
|
||
(should (ein:$notebook-p ein:%notebook%))
|
||
(should (equal (ein:$notebook-notebook-name ein:%notebook%) ein:testing-notebook-dummy-name))
|
||
(should (equal (ein:worksheet-ncells ein:%worksheet%) 0))))
|
||
|
||
(ert-deftest ein:notebook-from-json-all-cell-types ()
|
||
(with-current-buffer
|
||
(ein:testing-notebook-make-new
|
||
ein:testing-notebook-dummy-name
|
||
nil
|
||
(list (ein:testing-codecell-data "import numpy")
|
||
(ein:testing-markdowncell-data "*markdown* text")
|
||
(ein:testing-rawcell-data "`raw` cell text")
|
||
(ein:testing-htmlcell-data "<b>HTML</b> text")))
|
||
(should (ein:$notebook-p ein:%notebook%))
|
||
(should (equal (ein:$notebook-notebook-name ein:%notebook%)
|
||
ein:testing-notebook-dummy-name))
|
||
(should (equal (ein:worksheet-ncells ein:%worksheet%) 4))
|
||
(let ((cells (ein:worksheet-get-cells ein:%worksheet%)))
|
||
(should (ein:codecell-p (nth 0 cells)))
|
||
(should (ein:markdowncell-p (nth 1 cells)))
|
||
(should (ein:rawcell-p (nth 2 cells)))
|
||
(should (ein:htmlcell-p (nth 3 cells)))
|
||
(should (equal (ein:cell-get-text (nth 0 cells)) "import numpy"))
|
||
(should (equal (ein:cell-get-text (nth 1 cells)) "*markdown* text"))
|
||
(should (equal (ein:cell-get-text (nth 2 cells)) "`raw` cell text"))
|
||
(should (equal (ein:cell-get-text (nth 3 cells)) "<b>HTML</b> text")))))
|
||
|
||
;;; Destructor
|
||
|
||
(defun ein:testing-notebook-close-scratchsheet-open-and-close
|
||
(num-open num-close)
|
||
"Test for closing scratch sheet using `ein:notebook-close-worksheet'.
|
||
|
||
1. Open NUM-OPEN scratch sheets.
|
||
2. Close an existing worksheet.
|
||
3. Close NUM-CLOSE scratch sheets.
|
||
|
||
When NUM-OPEN = NUM-CLOSE, notebook should be closed."
|
||
(should (> num-open 0))
|
||
(with-current-buffer (ein:testing-notebook-make-empty)
|
||
(symbol-macrolet ((ss-list (ein:$notebook-scratchsheets ein:%notebook%)))
|
||
;; Add scratchsheets. They can be just empty instance for this test.
|
||
(dotimes (_ num-open)
|
||
(ein:notebook-scratchsheet-render-new ein:%notebook%))
|
||
(let ((ss (car ss-list)))
|
||
(kill-buffer (ein:worksheet-buffer ss)))
|
||
(should (= (length ss-list) (1- num-open)))
|
||
(dotimes (_ (1- num-close))
|
||
(kill-buffer (ein:worksheet-buffer (car ss-list))))
|
||
(should (= (length ss-list) (- num-open num-close)))
|
||
(let ((my-buffer (buffer-name)))
|
||
(kill-buffer (current-buffer))
|
||
(should-not (seq-some (lambda (b)
|
||
(cl-search my-buffer (buffer-name b)))
|
||
(buffer-list)))))))
|
||
|
||
(ert-deftest ein:notebook-close-scratchsheet/open-one-close-one ()
|
||
(ein:testing-notebook-close-scratchsheet-open-and-close 1 1))
|
||
|
||
(ert-deftest ein:notebook-close-scratchsheet/open-two-close-two ()
|
||
(ein:testing-notebook-close-scratchsheet-open-and-close 2 2))
|
||
|
||
(ert-deftest ein:notebook-close-scratchsheet/open-two-close-one ()
|
||
(ein:testing-notebook-close-scratchsheet-open-and-close 2 1))
|
||
|
||
;;; Insertion and deletion of cells
|
||
|
||
(ert-deftest ein:notebook-insert-cell-below-command-simple ()
|
||
(with-current-buffer (ein:testing-notebook-make-empty)
|
||
(call-interactively #'ein:worksheet-insert-cell-below)
|
||
(call-interactively #'ein:worksheet-insert-cell-below)
|
||
(call-interactively #'ein:worksheet-insert-cell-below)
|
||
(should (equal (ein:worksheet-ncells ein:%worksheet%) 3))))
|
||
|
||
(ert-deftest ein:notebook-insert-cell-above-command-simple ()
|
||
(with-current-buffer (ein:testing-notebook-make-empty)
|
||
(call-interactively #'ein:worksheet-insert-cell-above)
|
||
(call-interactively #'ein:worksheet-insert-cell-above)
|
||
(call-interactively #'ein:worksheet-insert-cell-above)
|
||
(should (equal (ein:worksheet-ncells ein:%worksheet%) 3))))
|
||
|
||
(ert-deftest ein:notebook-delete-cell-command-simple ()
|
||
(with-current-buffer (ein:testing-notebook-make-empty)
|
||
(cl-loop repeat 3
|
||
do (call-interactively #'ein:worksheet-insert-cell-above))
|
||
(should (equal (ein:worksheet-ncells ein:%worksheet%) 3))
|
||
(cl-loop repeat 3
|
||
do (call-interactively #'ein:worksheet-delete-cell))
|
||
(should (equal (ein:worksheet-ncells ein:%worksheet%) 0))))
|
||
|
||
(ert-deftest ein:notebook-delete-cell-command-no-undo ()
|
||
(with-current-buffer (ein:testing-notebook-make-empty)
|
||
(call-interactively #'ein:worksheet-insert-cell-above)
|
||
(insert "some text")
|
||
(should (equal (buffer-string) "
|
||
In [ ]:
|
||
some text
|
||
|
||
"))
|
||
(call-interactively #'ein:worksheet-delete-cell)
|
||
(should (equal (buffer-string) "\n"))
|
||
(should-error (undo)) ; should be ignore-error?
|
||
(should (equal (buffer-string) "\n"))))
|
||
|
||
(ert-deftest ein:notebook-kill-cell-command-simple ()
|
||
(with-current-buffer (ein:testing-notebook-make-empty)
|
||
(let (ein:kill-ring ein:kill-ring-yank-pointer)
|
||
(cl-loop repeat 3
|
||
do (call-interactively #'ein:worksheet-insert-cell-above))
|
||
(should (equal (ein:worksheet-ncells ein:%worksheet%) 3))
|
||
(cl-loop for i from 1 to 3
|
||
do (call-interactively #'ein:worksheet-kill-cell)
|
||
do (should (equal (length ein:kill-ring) i))
|
||
do (should (equal (ein:worksheet-ncells ein:%worksheet%) (- 3 i)))))))
|
||
|
||
(ert-deftest ein:notebook-copy-cell-command-simple ()
|
||
(with-current-buffer (ein:testing-notebook-make-empty)
|
||
(let (ein:kill-ring ein:kill-ring-yank-pointer)
|
||
(cl-loop repeat 3
|
||
do (call-interactively #'ein:worksheet-insert-cell-above))
|
||
(should (equal (ein:worksheet-ncells ein:%worksheet%) 3))
|
||
(cl-loop repeat 3
|
||
do (call-interactively #'ein:worksheet-copy-cell))
|
||
(should (equal (ein:worksheet-ncells ein:%worksheet%) 3))
|
||
(should (equal (length ein:kill-ring) 3)))))
|
||
|
||
(ert-deftest ein:notebook-yank-cell-command-simple ()
|
||
(with-current-buffer (ein:testing-notebook-make-empty)
|
||
(let (ein:kill-ring ein:kill-ring-yank-pointer)
|
||
(cl-loop repeat 3
|
||
do (call-interactively #'ein:worksheet-insert-cell-above))
|
||
(should (equal (ein:worksheet-ncells ein:%worksheet%) 3))
|
||
(cl-loop repeat 3
|
||
do (call-interactively #'ein:worksheet-kill-cell))
|
||
(should (equal (ein:worksheet-ncells ein:%worksheet%) 0))
|
||
(should (equal (length ein:kill-ring) 3))
|
||
(cl-loop repeat 3
|
||
do (call-interactively #'ein:worksheet-yank-cell))
|
||
(should (equal (ein:worksheet-ncells ein:%worksheet%) 3))
|
||
(cl-loop for cell in (ein:worksheet-get-cells ein:%worksheet%)
|
||
do (should (ein:codecell-p cell))
|
||
do (should (slot-boundp cell :kernel))
|
||
do (should (slot-boundp cell :events))))))
|
||
|
||
(ert-deftest ein:notebook-yank-cell-command-two-buffers ()
|
||
(let (ein:kill-ring ein:kill-ring-yank-pointer)
|
||
(with-current-buffer (ein:testing-notebook-make-empty "NB1")
|
||
(call-interactively #'ein:worksheet-insert-cell-above)
|
||
(should (equal (ein:worksheet-ncells ein:%worksheet%) 1))
|
||
(call-interactively #'ein:worksheet-kill-cell)
|
||
(should (equal (ein:worksheet-ncells ein:%worksheet%) 0))
|
||
(kill-buffer))
|
||
(with-current-buffer (ein:testing-notebook-make-empty "NB2")
|
||
(call-interactively #'ein:worksheet-yank-cell)
|
||
(should (equal (ein:worksheet-ncells ein:%worksheet%) 1)))))
|
||
|
||
(ert-deftest ein:notebook-toggle-cell-type-simple ()
|
||
(with-current-buffer (ein:testing-notebook-make-empty)
|
||
(call-interactively #'ein:worksheet-insert-cell-above)
|
||
(insert "some text")
|
||
(should (ein:codecell-p (ein:worksheet-get-current-cell)))
|
||
(should (slot-boundp (ein:worksheet-get-current-cell) :kernel))
|
||
;; toggle to markdown
|
||
(call-interactively #'ein:worksheet-toggle-cell-type)
|
||
(should (ein:markdowncell-p (ein:worksheet-get-current-cell)))
|
||
(should (looking-back "some text"))
|
||
;; toggle to raw
|
||
(call-interactively #'ein:worksheet-toggle-cell-type)
|
||
(should (ein:rawcell-p (ein:worksheet-get-current-cell)))
|
||
(should (looking-back "some text"))
|
||
(when (< (ein:$notebook-nbformat ein:%notebook%) 4)
|
||
;; toggle to heading
|
||
(call-interactively #'ein:worksheet-toggle-cell-type)
|
||
(should (looking-back "some text"))
|
||
;; toggle to code
|
||
(call-interactively #'ein:worksheet-toggle-cell-type)
|
||
(should (ein:codecell-p (ein:worksheet-get-current-cell)))
|
||
(should (slot-boundp (ein:worksheet-get-current-cell) :kernel))
|
||
(should (looking-back "some text")))))
|
||
|
||
(ert-deftest ein:notebook-change-cell-type-cycle-through ()
|
||
(with-current-buffer (ein:testing-notebook-make-empty)
|
||
(call-interactively #'ein:worksheet-insert-cell-above)
|
||
(insert "some text")
|
||
;; start with code cell
|
||
(should (ein:codecell-p (ein:worksheet-get-current-cell)))
|
||
(should (slot-boundp (ein:worksheet-get-current-cell) :kernel))
|
||
(let ((check
|
||
(lambda (type)
|
||
(let ((cell-p (intern (format "ein:%scell-p" type)))
|
||
(cell (ein:worksheet-get-current-cell)))
|
||
(ein:worksheet-change-cell-type ein:%worksheet% cell t)
|
||
(let ((new (ein:worksheet-get-current-cell)))
|
||
(should-not (eq new cell))
|
||
(should (funcall cell-p new)))
|
||
(should (looking-back "some text"))))))
|
||
;; change type: code (no change) -> markdown -> raw
|
||
(cl-loop for type in '("markdown" "raw" "code")
|
||
do (funcall check type))
|
||
;; back to code
|
||
(should (slot-boundp (ein:worksheet-get-current-cell) :kernel)))))
|
||
|
||
(defun eintest:notebook-split-cell-at-point
|
||
(insert-text search-text head-text tail-text &optional no-trim)
|
||
"Test `ein:notebook-split-cell-at-point' by the following procedure.
|
||
|
||
1. Insert, INSERT-TEXT.
|
||
2. Split cell just before SEARCH-TEXT.
|
||
3. Check that head cell has HEAD-TEXT.
|
||
4. Check that tail cell has TAIL-TEXT.
|
||
|
||
NO-TRIM is passed to `ein:notebook-split-cell-at-point'."
|
||
(with-current-buffer (ein:testing-notebook-make-empty)
|
||
(call-interactively #'ein:worksheet-insert-cell-above)
|
||
(insert insert-text)
|
||
(when search-text
|
||
(search-backward search-text))
|
||
;; do it
|
||
(let ((current-prefix-arg no-trim))
|
||
(call-interactively #'ein:worksheet-split-cell-at-point))
|
||
;; check the "tail" cell
|
||
(let ((cell (ein:worksheet-get-current-cell)))
|
||
(ein:cell-goto cell)
|
||
(should (equal (ein:cell-get-text cell) tail-text))
|
||
(should (ein:codecell-p cell))
|
||
(should (slot-boundp cell :kernel)))
|
||
;; check the "head" cell
|
||
(call-interactively #'ein:worksheet-goto-prev-input)
|
||
(let ((cell (ein:worksheet-get-current-cell)))
|
||
(ein:cell-goto cell)
|
||
(should (equal (ein:cell-get-text cell) head-text))
|
||
(should (ein:codecell-p cell))
|
||
(should (slot-boundp cell :kernel)))))
|
||
|
||
(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-split-cell-at-point-no-head ()
|
||
(eintest:notebook-split-cell-at-point
|
||
"some" "some" "" "some"))
|
||
|
||
(ert-deftest ein:notebook-split-cell-at-point-no-tail ()
|
||
(eintest:notebook-split-cell-at-point
|
||
"some" nil "some" ""))
|
||
|
||
(ert-deftest ein:notebook-merge-cell-command-next ()
|
||
(with-current-buffer (ein:testing-notebook-make-empty)
|
||
(call-interactively #'ein:worksheet-insert-cell-above)
|
||
(insert "Cell 1")
|
||
(call-interactively #'ein:worksheet-insert-cell-above)
|
||
(insert "Cell 0")
|
||
(let ((current-prefix-arg t))
|
||
(call-interactively #'ein:worksheet-merge-cell))
|
||
(ein:cell-goto (ein:worksheet-get-current-cell))
|
||
(should (looking-at "Cell 0\nCell 1"))))
|
||
|
||
(ert-deftest ein:notebook-merge-cell-command-prev ()
|
||
(with-current-buffer (ein:testing-notebook-make-empty)
|
||
(call-interactively #'ein:worksheet-insert-cell-below)
|
||
(insert "Cell 0")
|
||
(call-interactively #'ein:worksheet-insert-cell-below)
|
||
(insert "Cell 1")
|
||
(call-interactively #'ein:worksheet-merge-cell)
|
||
(ein:cell-goto (ein:worksheet-get-current-cell))
|
||
(should (looking-at "Cell 0\nCell 1"))))
|
||
|
||
|
||
;;; Cell selection.
|
||
|
||
(ert-deftest ein:notebook-goto-next-input-command-simple ()
|
||
(with-current-buffer (ein:testing-notebook-make-empty)
|
||
(cl-loop for i downfrom 2 to 0
|
||
do (call-interactively #'ein:worksheet-insert-cell-above)
|
||
do (insert (format "Cell %s" i)))
|
||
(should (equal (ein:worksheet-ncells ein:%worksheet%) 3))
|
||
;; (message "%s" (buffer-string))
|
||
(cl-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 (call-interactively #'ein:worksheet-goto-next-input)
|
||
do (should (looking-at (format "Cell %s" (1+ i)))))))
|
||
|
||
(ert-deftest ein:notebook-goto-prev-input-command-simple ()
|
||
(with-current-buffer (ein:testing-notebook-make-empty)
|
||
(cl-loop for i from 0 below 3
|
||
do (call-interactively #'ein:worksheet-insert-cell-below)
|
||
do (insert (format "Cell %s" i)))
|
||
(should (equal (ein:worksheet-ncells ein:%worksheet%) 3))
|
||
;; (message "%s" (buffer-string))
|
||
(cl-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 (call-interactively #'ein:worksheet-goto-prev-input)
|
||
do (should (looking-at (format "Cell %s" (1- i)))))))
|
||
|
||
(defun ein:testing-beginning-of-cell-input (num-cells
|
||
initial-point
|
||
before-callback
|
||
arg
|
||
should-looking-at-this)
|
||
(with-current-buffer (ein:testing-notebook-make-empty)
|
||
(ein:testing-insert-cells-with-format num-cells)
|
||
(goto-char (point-min))
|
||
(search-forward initial-point)
|
||
(when before-callback (funcall before-callback))
|
||
(should-not (looking-at-p should-looking-at-this))
|
||
(ein:worksheet-beginning-of-cell-input arg)
|
||
(should (looking-at-p should-looking-at-this))))
|
||
|
||
(ert-deftest ein:worksheet-beginning-of-cell-input-with-no-arg ()
|
||
(ein:testing-beginning-of-cell-input 1 "Cell 0" nil nil "Cell 0"))
|
||
|
||
(ert-deftest ein:worksheet-beginning-of-cell-input-with-arg-two ()
|
||
(ein:testing-beginning-of-cell-input 2 "Cell 1" nil 2 "Cell 0"))
|
||
|
||
(ert-deftest ein:worksheet-beginning-of-cell-input-with-arg-minus-one ()
|
||
(ein:testing-beginning-of-cell-input 2 "Cell 0" nil -1 "Cell 1"))
|
||
|
||
(ert-deftest ein:worksheet-beginning-of-cell-input-with-no-arg-at-footer ()
|
||
(ein:testing-beginning-of-cell-input 1 "Cell 0"
|
||
#'forward-line
|
||
nil "Cell 0"))
|
||
|
||
(ert-deftest ein:worksheet-beginning-of-cell-input-with-arg-two-at-footer ()
|
||
(ein:testing-beginning-of-cell-input 2 "Cell 1"
|
||
#'forward-line
|
||
2 "Cell 0"))
|
||
|
||
(ert-deftest ein:worksheet-beginning-of-cell-input-with-arg-minus-one-at-footer
|
||
()
|
||
(ein:testing-beginning-of-cell-input 2 "Cell 0"
|
||
#'forward-line
|
||
-1 "Cell 1"))
|
||
|
||
(ert-deftest ein:worksheet-beginning-of-cell-input-with-no-arg-at-prompt ()
|
||
(ein:testing-beginning-of-cell-input 2 "Cell 1"
|
||
(lambda () (forward-line -1))
|
||
nil "Cell 0"))
|
||
|
||
(ert-deftest ein:worksheet-beginning-of-cell-input-with-arg-two-at-prompt ()
|
||
(ein:testing-beginning-of-cell-input 2 "Cell 1"
|
||
(lambda () (forward-line -1))
|
||
2 "Cell 0"))
|
||
|
||
(ert-deftest ein:worksheet-beginning-of-cell-input-with-arg-minus-one-at-prompt
|
||
()
|
||
(ein:testing-beginning-of-cell-input 2 "Cell 0"
|
||
;; I need two cells to make it fail
|
||
;; without (forward-line -1)
|
||
(lambda () (forward-line -1))
|
||
-1 "Cell 0"))
|
||
|
||
(ert-deftest ein:worksheet-beginning-of-cell-input-repeat ()
|
||
(with-current-buffer (ein:testing-notebook-make-empty)
|
||
(ein:testing-insert-cells-with-format 3)
|
||
(goto-char (point-min))
|
||
(search-forward "Cell 2")
|
||
(should-not (looking-at-p "Cell 2"))
|
||
(ein:worksheet-beginning-of-cell-input)
|
||
(should (looking-at-p "Cell 2"))
|
||
(should-not (looking-at-p "Cell 1"))
|
||
(ein:worksheet-beginning-of-cell-input)
|
||
(should (looking-at-p "Cell 1"))
|
||
(should-not (looking-at-p "Cell 0"))
|
||
(ein:worksheet-beginning-of-cell-input)
|
||
(should (looking-at-p "Cell 0"))))
|
||
|
||
(defun ein:testing-end-of-cell-input (num-cells
|
||
initial-point
|
||
before-callback
|
||
arg
|
||
should-looking-back-this)
|
||
(with-current-buffer (ein:testing-notebook-make-empty)
|
||
(ein:testing-insert-cells-with-format num-cells)
|
||
(goto-char (point-min))
|
||
(search-forward initial-point)
|
||
(beginning-of-line)
|
||
(when before-callback (funcall before-callback))
|
||
(should-not (looking-back should-looking-back-this))
|
||
(ein:worksheet-end-of-cell-input arg)
|
||
(should (looking-back should-looking-back-this))))
|
||
|
||
(ert-deftest ein:worksheet-end-of-cell-input-with-no-arg ()
|
||
(ein:testing-end-of-cell-input 1 "Cell 0" nil nil "Cell 0"))
|
||
|
||
(ert-deftest ein:worksheet-end-of-cell-input-with-arg-two ()
|
||
(ein:testing-end-of-cell-input 2 "Cell 0" nil 2 "Cell 1"))
|
||
|
||
(ert-deftest ein:worksheet-end-of-cell-input-with-arg-minus-one ()
|
||
(ein:testing-end-of-cell-input 2 "Cell 1" nil -1 "Cell 0"))
|
||
|
||
(ert-deftest ein:worksheet-end-of-cell-input-with-no-arg-at-footer ()
|
||
(ein:testing-end-of-cell-input 2 "Cell 0"
|
||
#'forward-line
|
||
nil "Cell 1"))
|
||
|
||
(ert-deftest ein:worksheet-end-of-cell-input-with-arg-two-at-footer ()
|
||
(ein:testing-end-of-cell-input 3 "Cell 0"
|
||
#'forward-line
|
||
2 "Cell 2"))
|
||
|
||
(ert-deftest ein:worksheet-end-of-cell-input-with-arg-minus-one-at-footer
|
||
()
|
||
(ein:testing-end-of-cell-input 2 "Cell 0"
|
||
#'forward-line
|
||
-1 "Cell 0"))
|
||
|
||
(ert-deftest ein:worksheet-end-of-cell-input-with-no-arg-at-prompt ()
|
||
(ein:testing-end-of-cell-input 2 "Cell 1"
|
||
(lambda () (forward-line -1))
|
||
nil "Cell 1"))
|
||
|
||
(ert-deftest ein:worksheet-end-of-cell-input-with-arg-two-at-prompt ()
|
||
(ein:testing-end-of-cell-input 3 "Cell 0"
|
||
(lambda () (forward-line -1))
|
||
2 "Cell 1"))
|
||
|
||
(ert-deftest ein:worksheet-end-of-cell-input-with-arg-minus-one-at-prompt
|
||
()
|
||
(ein:testing-end-of-cell-input 2 "Cell 1"
|
||
(lambda () (forward-line -1))
|
||
-1 "Cell 0"))
|
||
|
||
|
||
;;; Cell movement
|
||
|
||
(ert-deftest ein:notebook-move-cell-up-command-simple ()
|
||
(with-current-buffer (ein:testing-notebook-make-empty)
|
||
(cl-loop for i from 0 below 3
|
||
do (call-interactively #'ein:worksheet-insert-cell-below)
|
||
do (insert (format "Cell %s" i)))
|
||
(beginning-of-line)
|
||
(should (looking-at "Cell 2"))
|
||
(cl-loop repeat 2
|
||
do (call-interactively #'ein:worksheet-move-cell-up))
|
||
;; (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 (ein:testing-notebook-make-empty)
|
||
(cl-loop for i from 0 below 3
|
||
do (call-interactively #'ein:worksheet-insert-cell-above)
|
||
do (insert (format "Cell %s" i)))
|
||
(cl-loop repeat 2
|
||
do (call-interactively #'ein:worksheet-move-cell-down))
|
||
(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))))
|
||
|
||
|
||
;;; Cell collapsing and output clearing
|
||
|
||
(ert-deftest ein:worksheet-toggle-output ()
|
||
(with-current-buffer (ein:testing-notebook-make-empty)
|
||
(let ((cell (call-interactively #'ein:worksheet-insert-cell-below)))
|
||
(should-not (oref cell :collapsed))
|
||
(call-interactively #'ein:worksheet-toggle-output)
|
||
(should (oref cell :collapsed))
|
||
(call-interactively #'ein:worksheet-toggle-output)
|
||
(should-not (oref cell :collapsed)))))
|
||
|
||
(defun ein:testing-insert-cells (list-type-or-cell &optional pivot callback)
|
||
(cl-loop with ws = ein:%worksheet%
|
||
with cell = pivot
|
||
for type in list-type-or-cell
|
||
for i from 0
|
||
do (setq cell (ein:worksheet-insert-cell-below ws type cell t))
|
||
if callback
|
||
do (funcall callback i cell)))
|
||
|
||
(defun* ein:testing-insert-cells-with-format (num &optional
|
||
(format "Cell %s")
|
||
(type 'code))
|
||
(ein:testing-insert-cells (cl-loop repeat num collect type)
|
||
nil
|
||
(lambda (i &rest _) (insert (format format i))))
|
||
(should (equal (ein:worksheet-ncells ein:%worksheet%) num)))
|
||
|
||
(defun ein:testing-test-output-visibility-all (collapsed)
|
||
(mapc (lambda (cell) (should (eq (oref cell :collapsed) collapsed)))
|
||
(ein:worksheet-get-cells ein:%worksheet%)))
|
||
|
||
(ert-deftest ein:worksheet-set-output-visibility-all/visible-from-all-hidden ()
|
||
(with-current-buffer (ein:testing-notebook-make-empty)
|
||
;; Prepare cells
|
||
(ein:testing-insert-cells '(code code code))
|
||
(mapc (lambda (cell) (ein:cell-set-collapsed cell nil))
|
||
(ein:worksheet-get-cells ein:%worksheet%))
|
||
;; Call the command
|
||
(call-interactively #'ein:worksheet-set-output-visibility-all)
|
||
;; Check it worked
|
||
(ein:testing-test-output-visibility-all nil)))
|
||
|
||
(ert-deftest ein:worksheet-set-output-visibility-all/hidden-from-all-visible ()
|
||
(with-current-buffer (ein:testing-notebook-make-empty)
|
||
;; Prepare cells
|
||
(ein:testing-insert-cells '(code code code))
|
||
;; Call the command
|
||
(let ((current-prefix-arg t))
|
||
(call-interactively #'ein:worksheet-set-output-visibility-all))
|
||
;; Check it worked
|
||
(ein:testing-test-output-visibility-all t)))
|
||
|
||
(ert-deftest ein:worksheet-set-output-visibility-all/visible-from-part-hidden ()
|
||
(with-current-buffer (ein:testing-notebook-make-empty)
|
||
;; Prepare cells
|
||
(ein:testing-insert-cells '(code code code))
|
||
(ein:cell-set-collapsed
|
||
(nth 1 (ein:worksheet-get-cells ein:%worksheet%)) nil)
|
||
;; Call the command
|
||
(call-interactively #'ein:worksheet-set-output-visibility-all)
|
||
;; Check it worked
|
||
(ein:testing-test-output-visibility-all nil)))
|
||
|
||
(ert-deftest ein:worksheet-set-output-visibility-all/hidden-from-part-visible ()
|
||
(with-current-buffer (ein:testing-notebook-make-empty)
|
||
;; Prepare cells
|
||
(ein:testing-insert-cells '(code code code))
|
||
(ein:cell-set-collapsed
|
||
(nth 1 (ein:worksheet-get-cells ein:%worksheet%)) nil)
|
||
;; Call the command
|
||
(call-interactively #'ein:worksheet-set-output-visibility-all)
|
||
(let ((current-prefix-arg t))
|
||
(call-interactively #'ein:worksheet-set-output-visibility-all))
|
||
;; Check it worked
|
||
(ein:testing-test-output-visibility-all t)))
|
||
|
||
(defun ein:testing-assert-cell-output-num (cell num-outputs)
|
||
(should (ein:codecell-p cell))
|
||
(should (= (length (oref cell :outputs)) num-outputs)))
|
||
|
||
(ert-deftest ein:worksheet-clear-output/simple ()
|
||
(with-current-buffer
|
||
(ein:testing-make-notebook-with-outputs '(("'cell output'")
|
||
("'cell output'")))
|
||
(should (= (ein:worksheet-ncells ein:%worksheet%) 2))
|
||
(let* ((cells (ein:worksheet-get-cells ein:%worksheet%)))
|
||
(ein:testing-assert-cell-output-num (nth 0 cells) 1)
|
||
(ein:testing-assert-cell-output-num (nth 1 cells) 1)
|
||
(ein:cell-goto (nth 0 cells))
|
||
(call-interactively #'ein:worksheet-clear-output)
|
||
(ein:testing-assert-cell-output-num (nth 0 cells) 0) ; cleared
|
||
(ein:testing-assert-cell-output-num (nth 1 cells) 1))))
|
||
|
||
(ert-deftest ein:worksheet-clear-output/preserve-input-prompt ()
|
||
(with-current-buffer
|
||
(ein:testing-make-notebook-with-outputs '(("'cell output'")
|
||
("'cell output'")
|
||
("'cell output'")))
|
||
(should (= (ein:worksheet-ncells ein:%worksheet%) 3))
|
||
(let* ((cells (ein:worksheet-get-cells ein:%worksheet%)))
|
||
(ein:cell-set-input-prompt (nth 0 cells) 111)
|
||
(ein:cell-set-input-prompt (nth 1 cells) 222)
|
||
(ein:cell-set-input-prompt (nth 2 cells) 333)
|
||
;; Call `ein:worksheet-clear-output' with/without prefix argument.
|
||
(ein:cell-goto (nth 0 cells))
|
||
(call-interactively #'ein:worksheet-clear-output)
|
||
(ein:cell-goto (nth 2 cells))
|
||
(let ((current-prefix-arg '(4)))
|
||
(call-interactively #'ein:worksheet-clear-output))
|
||
;; Check cells' prompt number
|
||
(should (eq (oref (nth 0 cells) :input-prompt-number) nil))
|
||
(should (eq (oref (nth 1 cells) :input-prompt-number) 222))
|
||
(should (eq (oref (nth 2 cells) :input-prompt-number) 333)))))
|
||
|
||
(ert-deftest ein:worksheet-clear-all-output/simple ()
|
||
(with-current-buffer
|
||
(ein:testing-make-notebook-with-outputs '(("'cell output'")
|
||
("'cell output'")))
|
||
(should (= (ein:worksheet-ncells ein:%worksheet%) 2))
|
||
(let* ((cells (ein:worksheet-get-cells ein:%worksheet%)))
|
||
(ein:testing-assert-cell-output-num (nth 0 cells) 1)
|
||
(ein:testing-assert-cell-output-num (nth 1 cells) 1)
|
||
(call-interactively #'ein:worksheet-clear-all-output)
|
||
(ein:testing-assert-cell-output-num (nth 0 cells) 0)
|
||
(ein:testing-assert-cell-output-num (nth 1 cells) 0))))
|
||
|
||
|
||
;; Kernel related things
|
||
|
||
(defun eintest:notebook-check-kernel-and-codecell (kernel cell)
|
||
(should (ein:$kernel-p kernel))
|
||
(should (ein:codecell-p cell))
|
||
(should (ein:$kernel-p (oref cell :kernel))))
|
||
|
||
(defun eintest:notebook-fake-execution (kernel text msg-id callbacks)
|
||
(mocker-let ((ein:kernel-execute
|
||
(kernel code callbacks kwd-silent silent)
|
||
((:input (list kernel text callbacks :silent nil))))
|
||
(ein:kernel-live-p
|
||
(kernel)
|
||
((:input (list kernel) :output t))))
|
||
(call-interactively #'ein:worksheet-execute-cell))
|
||
(ein:kernel-set-callbacks-for-msg kernel msg-id callbacks))
|
||
|
||
(ert-deftest ein:notebook-execute-current-cell ()
|
||
(with-current-buffer (ein:testing-notebook-make-empty)
|
||
(call-interactively #'ein:worksheet-insert-cell-below)
|
||
(let* ((text "print 'Hello World'")
|
||
(cell (ein:worksheet-get-current-cell))
|
||
(kernel (ein:$notebook-kernel ein:%notebook%))
|
||
(msg-id "DUMMY-MSG-ID")
|
||
(callbacks (ein:cell-make-callbacks cell)))
|
||
(eintest:notebook-check-kernel-and-codecell kernel cell)
|
||
;; Execute
|
||
(insert text)
|
||
(eintest:notebook-fake-execution kernel text msg-id callbacks)
|
||
;; Execute reply
|
||
(should-error (eintest:check-search-forward-from (point-min) "In [1]:"))
|
||
(eintest:kernel-fake-execute-reply kernel msg-id 1)
|
||
(should (= (oref cell :input-prompt-number) 1))
|
||
(eintest:check-search-forward-from (point-min) "In [1]:")
|
||
;; Stream output
|
||
(eintest:kernel-fake-stream kernel msg-id "Hello World")
|
||
(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 "\nHello World\n" nil t)) ; stream output
|
||
(should-not (search-forward "Hello World" nil t))))))
|
||
|
||
(defmacro eintest:worksheet-execute-cell-and-*-deftest
|
||
(do-this cell-type has-next-p insert-p)
|
||
"Define:
|
||
ein:worksheet-execute-cell-and-{DO-THIS}/on-{CELL-TYPE}cell-{no,with}-next
|
||
|
||
For example, when `goto-next', \"code\", `nil', `nil' is given,
|
||
`ein:worksheet-execute-cell-and-goto-next/on-codecell-no-next' is
|
||
defined."
|
||
(let ((test-name
|
||
(intern (format "ein:worksheet-execute-cell-and-%s/on-%scell-%s"
|
||
do-this cell-type
|
||
(if has-next-p "with-next" "no-next"))))
|
||
(command
|
||
(intern (format "ein:worksheet-execute-cell-and-%s" do-this))))
|
||
`(ert-deftest ,test-name ()
|
||
(with-current-buffer (ein:testing-notebook-make-empty)
|
||
(let* ((ws ein:%worksheet%)
|
||
(current (ein:worksheet-insert-cell-below ws ,cell-type nil t))
|
||
,@(when has-next-p
|
||
'((next
|
||
(ein:worksheet-insert-cell-below ws "code" current)))))
|
||
(mocker-let ((ein:worksheet-execute-cell
|
||
(ws cell)
|
||
(,@(when (equal cell-type "code")
|
||
'((:input (list ein:%worksheet% current)))))))
|
||
(call-interactively #',command)
|
||
(let ((cell (ein:worksheet-get-current-cell)))
|
||
(should (eq (ein:cell-prev cell) current))
|
||
,(when has-next-p
|
||
(if insert-p
|
||
'(should-not (eq cell next))
|
||
'(should (eq cell next)))))))))))
|
||
|
||
(eintest:worksheet-execute-cell-and-*-deftest goto-next "code" nil t )
|
||
(eintest:worksheet-execute-cell-and-*-deftest goto-next "code" t nil)
|
||
(eintest:worksheet-execute-cell-and-*-deftest goto-next "markdown" nil t )
|
||
(eintest:worksheet-execute-cell-and-*-deftest goto-next "markdown" t nil)
|
||
(eintest:worksheet-execute-cell-and-*-deftest insert-below "code" nil t )
|
||
(eintest:worksheet-execute-cell-and-*-deftest insert-below "code" t t )
|
||
(eintest:worksheet-execute-cell-and-*-deftest insert-below "markdown" nil t )
|
||
(eintest:worksheet-execute-cell-and-*-deftest insert-below "markdown" t t )
|
||
|
||
|
||
;;; Persistence and loading
|
||
|
||
(defun ein:testin-notebook-close (num-ws num-ss)
|
||
(should (= num-ws 1)) ; currently EIN only supports 1 WS
|
||
(should (>= num-ss 0))
|
||
(let* ((buf (ein:testing-notebook-make-empty))
|
||
(buffers (with-current-buffer buf
|
||
(ein:notebook-buffer-list ein:%notebook%))))
|
||
(with-current-buffer buf
|
||
(dotimes (_ num-ss)
|
||
(ein:notebook-scratchsheet-render-new ein:%notebook%))
|
||
(should (= (+ num-ws num-ss)
|
||
(length (seq-filter
|
||
(lambda (b) (not (buffer-base-buffer (get-buffer b))))
|
||
(ein:notebook-buffer-list ein:%notebook%))))))
|
||
|
||
(kill-buffer buf)
|
||
(mapc (lambda (b) (should-not (get-buffer b))) buffers)))
|
||
|
||
(ert-deftest ein:notebook-close/one-ws-no-ss ()
|
||
(ein:testin-notebook-close 1 0))
|
||
|
||
(ert-deftest ein:notebook-close/one-ws-one-ss ()
|
||
(ein:testin-notebook-close 1 1))
|
||
|
||
(ert-deftest ein:notebook-close/one-ws-five-ss ()
|
||
(ein:testin-notebook-close 1 5))
|
||
|
||
(defun ein:testing-notebook-data-assert-nb3-worksheet-contents (notebook &optional text)
|
||
(let* ((data (ein:notebook-to-json notebook))
|
||
(worksheets (assoc-default 'worksheets data #'eq))
|
||
(cells (assoc-default 'cells (elt worksheets 0) #'eq)))
|
||
(should (= (length worksheets) 1))
|
||
(if text
|
||
(let ((cell-0 (elt cells 0))
|
||
(input (assoc-default 'input cell-0 #'eq)))
|
||
(should (equal input text)))
|
||
(should (= (length cells) 0)))))
|
||
|
||
(defun ein:testing-notebook-data-assert-nb4-worksheet-contents (notebook &optional text)
|
||
(let* ((data (ein:notebook-to-json notebook))
|
||
(cells (assoc-default 'cells data)))
|
||
(if text
|
||
(progn
|
||
(should (= (length cells) 1))
|
||
(should (equal (assoc-default 'source (elt cells 0) #'eq) text)))
|
||
(should (zerop (length cells))))))
|
||
|
||
(ert-deftest ein:notebook-saves-latin ()
|
||
(with-current-buffer (ein:testing-notebook-make-new)
|
||
(let ((latin "«utf-8 cannot handle these»"))
|
||
;; Edit notebook.
|
||
(ein:cell-goto (ein:get-cell-at-point))
|
||
(insert latin)
|
||
(should (ein:notebook-save-notebook (ein:get-notebook))))))
|
||
|
||
(ert-deftest ein:notebook-to-json-after-closing-a-worksheet ()
|
||
(with-current-buffer (ein:testing-notebook-make-new)
|
||
(let ((buffer (current-buffer))
|
||
(notebook ein:%notebook%))
|
||
;; Edit notebook.
|
||
(ein:cell-goto (ein:get-cell-at-point))
|
||
(insert "some text")
|
||
(if (< (ein:$notebook-nbformat notebook) 4)
|
||
(ein:testing-notebook-data-assert-nb3-worksheet-contents notebook "some text")
|
||
(ein:testing-notebook-data-assert-nb4-worksheet-contents notebook "some text"))
|
||
(should (ein:notebook-modified-p notebook))
|
||
(ein:notebook-save-notebook-success notebook)
|
||
(should-not (ein:notebook-modified-p notebook))
|
||
;; Kill scratch sheet.
|
||
(kill-buffer (ein:worksheet-buffer (ein:notebook-scratchsheet-open notebook)))
|
||
(should (ein:notebook-live-p notebook))
|
||
;; to-json should still work
|
||
(if (< (ein:$notebook-nbformat notebook) 4)
|
||
(ein:testing-notebook-data-assert-nb3-worksheet-contents notebook "some text")
|
||
(ein:testing-notebook-data-assert-nb4-worksheet-contents notebook "some text")))))
|
||
|
||
(ert-deftest ein:notebook-to-json-after-discarding-a-worksheet ()
|
||
(with-current-buffer (ein:testing-notebook-make-new)
|
||
(lexical-let (asked
|
||
(buffer (current-buffer))
|
||
(notebook ein:%notebook%))
|
||
;; Edit notebook.
|
||
(ein:cell-goto (ein:get-cell-at-point))
|
||
(insert "some text")
|
||
(if (< (ein:$notebook-nbformat notebook) 4)
|
||
(ein:testing-notebook-data-assert-nb3-worksheet-contents notebook "some text")
|
||
(ein:testing-notebook-data-assert-nb4-worksheet-contents notebook "some text"))
|
||
(should (ein:notebook-modified-p notebook))
|
||
(let ((ss-buf (save-current-buffer (ein:notebook-scratchsheet-open notebook))))
|
||
;; Discard a worksheet buffer
|
||
(cl-letf (((symbol-function 'y-or-n-p)
|
||
(lambda (&rest _args) (setq asked t) nil)))
|
||
(should-not (kill-buffer buffer)))
|
||
(should asked)
|
||
(should-not (buffer-live-p buffer))
|
||
(should-not (ein:notebook-live-p notebook))
|
||
(should-not (buffer-live-p ss-buf))))))
|
||
|
||
(defun ein:testing-notebook-should-be-closed (notebook buffer)
|
||
(should-not (buffer-live-p buffer))
|
||
(should-not (ein:notebook-live-p notebook)))
|
||
|
||
(ert-deftest ein:notebook-kill-kernel-then-close-when-its-alive ()
|
||
(with-current-buffer (ein:testing-notebook-make-new)
|
||
(let ((buffer (current-buffer))
|
||
(notebook ein:%notebook%))
|
||
(cl-letf (((symbol-function 'ein:kernel-live-p) (lambda (&rest args) t))
|
||
((symbol-function 'ein:kernel-delete-session)
|
||
(cl-function (lambda (callback &key kernel) (funcall callback kernel)))))
|
||
(call-interactively #'ein:notebook-kill-kernel-then-close-command))
|
||
(ein:testing-notebook-should-be-closed notebook buffer))))
|
||
|
||
(ert-deftest ein:notebook-kill-kernel-then-close-when-already-dead ()
|
||
(with-current-buffer (ein:testing-notebook-make-new)
|
||
(let ((buffer (current-buffer))
|
||
(notebook ein:%notebook%)
|
||
(kernel (ein:$notebook-kernel ein:%notebook%)))
|
||
(mocker-let
|
||
((ein:kernel-live-p
|
||
(kernel)
|
||
((:input (list kernel) :output nil))))
|
||
(call-interactively #'ein:notebook-kill-kernel-then-close-command))
|
||
(ein:testing-notebook-should-be-closed notebook buffer))))
|
||
|
||
|
||
;; Notebook undo
|
||
|
||
(defun eintest:notebook-undo-after-insert-above ()
|
||
(with-current-buffer (ein:testing-notebook-make-empty)
|
||
(let ((text "some text"))
|
||
(call-interactively #'ein:worksheet-insert-cell-above)
|
||
(insert text)
|
||
(undo-boundary)
|
||
(call-interactively #'ein:worksheet-insert-cell-above)
|
||
(call-interactively #'ein:worksheet-goto-next-input)
|
||
(should (equal (ein:cell-get-text (ein:worksheet-get-current-cell)) text))
|
||
(if ein:worksheet-enable-undo
|
||
(undo)
|
||
(should-error (undo)))
|
||
(when ein:worksheet-enable-undo
|
||
(should (equal (buffer-string) "
|
||
In [ ]:
|
||
|
||
|
||
In [ ]:
|
||
|
||
|
||
"))))))
|
||
|
||
(defun eintest:notebook-undo-after-split ()
|
||
(with-current-buffer (ein:testing-notebook-make-empty)
|
||
(let ((line-1 "first line")
|
||
(line-2 "second line"))
|
||
(call-interactively #'ein:worksheet-insert-cell-below)
|
||
(insert line-1 "\n" line-2)
|
||
(undo-boundary)
|
||
(move-beginning-of-line 1)
|
||
(call-interactively #'ein:worksheet-split-cell-at-point)
|
||
(undo-boundary)
|
||
(should (equal (ein:cell-get-text (ein:worksheet-get-current-cell))
|
||
line-2))
|
||
(if ein:worksheet-enable-undo
|
||
(undo)
|
||
(should-error (undo)))
|
||
(when ein:worksheet-enable-undo
|
||
(should (equal (buffer-string) "
|
||
In [ ]:
|
||
|
||
|
||
In [ ]:
|
||
first line
|
||
second line
|
||
|
||
"))))))
|
||
|
||
(defun eintest:notebook-undo-after-merge ()
|
||
(with-current-buffer (ein:testing-notebook-make-empty)
|
||
(let ((line-1 "first line")
|
||
(line-2 "second line"))
|
||
(call-interactively #'ein:worksheet-insert-cell-below)
|
||
(call-interactively #'ein:worksheet-insert-cell-below)
|
||
;; Extra cells to avoid "Changes to be undone are outside visible
|
||
;; portion of buffer" user-error:
|
||
(call-interactively #'ein:worksheet-insert-cell-below)
|
||
(call-interactively #'ein:worksheet-insert-cell-below)
|
||
(goto-char (point-min))
|
||
(call-interactively #'ein:worksheet-goto-next-input)
|
||
|
||
(insert line-1)
|
||
(undo-boundary)
|
||
|
||
(call-interactively #'ein:worksheet-goto-next-input)
|
||
(insert line-2)
|
||
(undo-boundary)
|
||
|
||
(call-interactively #'ein:worksheet-merge-cell)
|
||
(undo-boundary)
|
||
|
||
(should (equal (ein:cell-get-text (ein:worksheet-get-current-cell))
|
||
(concat line-1 "\n" line-2)))
|
||
(if (not ein:worksheet-enable-undo)
|
||
(should-error (undo))
|
||
(undo)
|
||
(should (equal (buffer-string) "
|
||
In [ ]:
|
||
second line
|
||
|
||
In [ ]:
|
||
|
||
|
||
In [ ]:
|
||
|
||
|
||
"))
|
||
(undo-more 1)
|
||
(should (equal (buffer-string) "
|
||
In [ ]:
|
||
|
||
|
||
In [ ]:
|
||
|
||
|
||
In [ ]:
|
||
|
||
|
||
"))))))
|
||
|
||
(defun eintest:notebook-undo-after-execution-1-cell ()
|
||
(with-current-buffer (ein:testing-notebook-make-empty)
|
||
(call-interactively #'ein:worksheet-insert-cell-below)
|
||
(let* ((text "print 'Hello World'")
|
||
(output-text "Hello World\n")
|
||
(cell (ein:worksheet-get-current-cell))
|
||
(kernel (ein:$notebook-kernel ein:%notebook%))
|
||
(msg-id "DUMMY-MSG-ID")
|
||
(callbacks (ein:cell-make-callbacks cell))
|
||
(check-output
|
||
(lambda ()
|
||
(eintest:cell-check-output cell output-text))))
|
||
(eintest:notebook-check-kernel-and-codecell kernel cell)
|
||
;; Execute
|
||
(insert text)
|
||
(undo-boundary)
|
||
(eintest:notebook-fake-execution kernel text msg-id callbacks)
|
||
(ein:kernel-set-callbacks-for-msg kernel msg-id callbacks)
|
||
;; Stream output
|
||
(eintest:kernel-fake-stream kernel msg-id output-text)
|
||
(funcall check-output)
|
||
;; Undo
|
||
(should (equal (ein:cell-get-text cell) text))
|
||
(if ein:worksheet-enable-undo
|
||
(undo)
|
||
(should-error (undo)))
|
||
(when ein:worksheet-enable-undo
|
||
(should (equal (ein:cell-get-text cell) ""))
|
||
(should (funcall check-output))))))
|
||
|
||
(defun eintest:notebook-undo-after-execution-2-cells ()
|
||
(with-current-buffer (ein:testing-notebook-make-empty)
|
||
(call-interactively #'ein:worksheet-insert-cell-below)
|
||
(call-interactively #'ein:worksheet-insert-cell-above)
|
||
(let* ((text "print 'Hello World\\n' * 10")
|
||
(next-text "something")
|
||
(output-text
|
||
(apply #'concat (cl-loop repeat 10 collect "Hello World\n")))
|
||
(cell (ein:worksheet-get-current-cell))
|
||
(next-cell (ein:cell-next cell))
|
||
(kernel (ein:$notebook-kernel ein:%notebook%))
|
||
(msg-id "DUMMY-MSG-ID")
|
||
(callbacks (ein:cell-make-callbacks cell))
|
||
(check-output
|
||
(lambda ()
|
||
(eintest:cell-check-output cell output-text))))
|
||
(eintest:notebook-check-kernel-and-codecell kernel cell)
|
||
;; Execute
|
||
(insert text)
|
||
(undo-boundary)
|
||
(let ((pos (point)))
|
||
;; Do not use `save-excursion' because it does not record undo.
|
||
(call-interactively #'ein:worksheet-goto-next-input)
|
||
(insert next-text)
|
||
(undo-boundary)
|
||
(goto-char pos))
|
||
(eintest:notebook-fake-execution kernel text msg-id callbacks)
|
||
(ein:kernel-set-callbacks-for-msg kernel msg-id callbacks)
|
||
;; Stream output
|
||
(eintest:kernel-fake-stream kernel msg-id output-text)
|
||
(funcall check-output)
|
||
;; Undo
|
||
(should (equal (ein:cell-get-text cell) text))
|
||
(should (equal (ein:cell-get-text next-cell) next-text))
|
||
(if ein:worksheet-enable-undo
|
||
(undo)
|
||
(should-error (undo)))
|
||
(when ein:worksheet-enable-undo
|
||
(should (equal (ein:cell-get-text cell) text))
|
||
(should (equal (ein:cell-get-text next-cell) ""))
|
||
(should (funcall check-output))))))
|
||
|
||
(defmacro eintest:notebook-undo-make-tests (name)
|
||
"Define two tests ein:NANE/no, ein:NANE/yes
|
||
from a function named eintest:NAME where `no'/`yes' is the
|
||
value of `ein:worksheet-enable-undo'."
|
||
(let ((func (intern (format "eintest:%s" name)))
|
||
(test/no (intern (format "ein:%s/no" name)))
|
||
(test/yes (intern (format "ein:%s/yes" name))))
|
||
`(progn
|
||
(ert-deftest ,test/no ()
|
||
(let ((ein:worksheet-enable-undo nil))
|
||
(,func)))
|
||
(ert-deftest ,test/yes ()
|
||
(let ((ein:worksheet-enable-undo t))
|
||
(,func)))
|
||
)))
|
||
|
||
(eintest:notebook-undo-make-tests notebook-undo-after-insert-above)
|
||
(eintest:notebook-undo-make-tests notebook-undo-after-split)
|
||
(eintest:notebook-undo-make-tests notebook-undo-after-merge)
|
||
(eintest:notebook-undo-make-tests notebook-undo-after-execution-1-cell)
|
||
(eintest:notebook-undo-make-tests notebook-undo-after-execution-2-cells)
|
||
|
||
|
||
;; Generic getter
|
||
|
||
(ert-deftest ein:get-url-or-port--notebook ()
|
||
(with-current-buffer (ein:testing-notebook-make-empty)
|
||
(should (equal (ein:get-url-or-port) ein:testing-notebook-dummy-url))))
|
||
|
||
(ert-deftest ein:get-notebook--notebook ()
|
||
(with-current-buffer (ein:testing-notebook-make-empty)
|
||
(should (eq (ein:get-notebook) ein:%notebook%))))
|
||
|
||
(ert-deftest ein:get-kernel--notebook ()
|
||
(with-current-buffer (ein:testing-notebook-make-empty)
|
||
(let ((kernel (ein:$notebook-kernel ein:%notebook%)))
|
||
(should (ein:$kernel-p kernel))
|
||
(should (eq (ein:get-kernel) kernel)))))
|
||
|
||
(ert-deftest ein:get-cell-at-point--notebook ()
|
||
(with-current-buffer (ein:testing-notebook-make-empty)
|
||
;; FIXME: write test with non-empty worksheet
|
||
(should-not (ein:get-cell-at-point))))
|
||
|
||
(ert-deftest ein:get-traceback-data--notebook ()
|
||
(with-current-buffer (ein:testing-notebook-make-empty)
|
||
;; FIXME: write test with non-empty TB
|
||
(should-not (ein:get-traceback-data))))
|
||
|
||
|
||
;; Notebook mode
|
||
|
||
(ert-deftest ein:notebook-ask-before-kill-emacs-simple ()
|
||
(let ((ein:notebook--opened-map (make-hash-table :test 'equal)))
|
||
(with-current-buffer
|
||
(ein:testing-notebook-make-empty "Modified Notebook.ipynb")
|
||
(call-interactively #'ein:worksheet-insert-cell-below)
|
||
(should (ein:notebook-modified-p)))
|
||
(with-current-buffer
|
||
(ein:testing-notebook-make-empty "Saved Notebook.ipynb")
|
||
(ein:notebook-save-notebook-success ein:%notebook%)
|
||
(should-not (ein:notebook-modified-p)))
|
||
(kill-buffer (ein:testing-notebook-make-empty "Killed Notebook.ipynb"))
|
||
(should (gethash `(,ein:testing-notebook-dummy-url "Modified Notebook.ipynb") ein:notebook--opened-map))
|
||
(should (gethash `(,ein:testing-notebook-dummy-url "Saved Notebook.ipynb") ein:notebook--opened-map))
|
||
(should-not (gethash `(,ein:testing-notebook-dummy-url "Killed Notebook.ipynb") ein:notebook--opened-map))
|
||
(should (= (hash-table-count ein:notebook--opened-map) 2))
|
||
(cl-letf (((symbol-function 'y-or-n-p) (lambda (&rest args) nil)))
|
||
(ein:notebook-close-notebooks t)
|
||
(should-not (ein:notebook-opened-notebooks)))))
|
||
|
||
;;; Buffer and kill hooks
|
||
|
||
(ert-deftest ein:notebook-ask-before-kill-buffer/no-ein-buffer ()
|
||
(with-temp-buffer
|
||
(should (kill-buffer))))
|
||
|
||
(ert-deftest ein:notebook-ask-before-kill-buffer/new-notebook ()
|
||
(with-current-buffer (ein:testing-make-notebook-with-outputs '(nil))
|
||
(lexical-let (asked
|
||
(buf (current-buffer)))
|
||
(cl-letf (((symbol-function 'y-or-n-p)
|
||
(lambda (&rest _args) (setq asked t) nil)))
|
||
(should-not (kill-buffer buf))
|
||
(should-not asked)
|
||
(should-not (buffer-live-p buf))))))
|
||
|
||
(ert-deftest ein:notebook-ask-before-kill-buffer/modified-notebook ()
|
||
(with-current-buffer (ein:testing-make-notebook-with-outputs '(nil))
|
||
(lexical-let (asked
|
||
(buffer (current-buffer)))
|
||
(cl-letf (((symbol-function 'y-or-n-p)
|
||
(lambda (&rest _args) (setq asked t) nil)))
|
||
(call-interactively #'ein:worksheet-insert-cell-below)
|
||
(should-not (kill-buffer buffer))
|
||
(should asked)
|
||
(should-not (buffer-live-p buffer))))))
|
||
|
||
(ert-deftest ein:notebook-ask-before-kill-buffer/modified-scratchsheet ()
|
||
(with-current-buffer (ein:testing-make-notebook-with-outputs '(nil))
|
||
(lexical-let (asked
|
||
(buf (current-buffer)))
|
||
(should (buffer-live-p buf))
|
||
(with-current-buffer (ein:worksheet-buffer
|
||
(ein:notebook-scratchsheet-open ein:%notebook%))
|
||
(lexical-let (asked
|
||
(buf2 (current-buffer)))
|
||
(should-not (eq buf buf2))
|
||
(should (= (ein:worksheet-ncells ein:%worksheet%) 1))
|
||
(call-interactively #'ein:worksheet-insert-cell-below)
|
||
(should (= (ein:worksheet-ncells ein:%worksheet%) 2))
|
||
(should (ein:worksheet-modified-p ein:%worksheet%))
|
||
(cl-letf (((symbol-function 'y-or-n-p)
|
||
(lambda (&rest _args) (setq asked t) nil)))
|
||
(should-not (kill-buffer buf2)))
|
||
(should-not asked)
|
||
(should-not (buffer-live-p buf2))))
|
||
(should-not (ein:worksheet-modified-p ein:%worksheet%))
|
||
(cl-letf (((symbol-function 'y-or-n-p)
|
||
(lambda (&rest _args) (setq asked t) nil)))
|
||
(should-not (kill-buffer buf)))
|
||
(should-not asked)
|
||
(should-not (buffer-live-p buf)))))
|
||
|
||
;; 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 ":"))
|
||
(should-not (ein:notebook-test-notebook-name "a/b"))
|
||
(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")))
|
||
|
||
(defun* eintest:notebook--check-nbformat (&optional orig_nbformat
|
||
orig_nbformat_minor
|
||
nbformat
|
||
nbformat_minor
|
||
&key data)
|
||
(let ((data (or data
|
||
(list :nbformat nbformat :nbformat_minor nbformat_minor
|
||
:orig_nbformat orig_nbformat
|
||
:orig_nbformat_minor orig_nbformat_minor))))
|
||
(ein:notebook--check-nbformat data)))
|
||
|
||
(ert-deftest ein:notebook--check-nbformat-nothing ()
|
||
(mocker-let ((ein:display-warning (message) ()))
|
||
(eintest:notebook--check-nbformat)
|
||
(eintest:notebook--check-nbformat :data nil)
|
||
(eintest:notebook--check-nbformat 2 0)
|
||
(eintest:notebook--check-nbformat 2 0 2)
|
||
(eintest:notebook--check-nbformat 2 0 2 0)))
|
||
|
||
(defmacro ein:notebook--check-nbformat-assert-match (regexp &rest args)
|
||
`(mocker-let ((ein:display-warning
|
||
(message)
|
||
((:input-matcher
|
||
(lambda (m) (string-match ,regexp m))))))
|
||
(eintest:notebook--check-nbformat ,@args)))
|
||
|
||
(ert-deftest ein:notebook--check-nbformat-warn-major ()
|
||
(ein:notebook--check-nbformat-assert-match "v2 -> v3" 2 nil 3)
|
||
(ein:notebook--check-nbformat-assert-match "v2 -> v3" 2 0 3 0))
|
||
|
||
(ert-deftest ein:notebook--check-nbformat-warn-minor ()
|
||
(ein:notebook--check-nbformat-assert-match
|
||
"version v2\\.1, [^\\.]* up to v2.0" 2 1 2 0))
|