test-ein-notebook.el 54 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309
  1. (eval-when-compile (require 'cl))
  2. (require 'ert)
  3. (when load-file-name
  4. (add-to-list 'load-path
  5. (concat (file-name-directory load-file-name) "mocker")))
  6. (require 'mocker)
  7. (require 'ein-notebook)
  8. (require 'ein-testing-notebook)
  9. (require 'ein-testing-cell)
  10. ;; Test utils
  11. (defvar eintest:notebook-data-simple-json
  12. "{
  13. \"metadata\": {
  14. \"name\": \"Untitled0\"
  15. },
  16. \"name\": \"Untitled0\",
  17. \"nbformat\": 2,
  18. \"worksheets\": [
  19. {
  20. \"cells\": [
  21. {
  22. \"cell_type\": \"code\",
  23. \"collapsed\": false,
  24. \"input\": \"1 + 1\",
  25. \"language\": \"python\",
  26. \"outputs\": [
  27. {
  28. \"output_type\": \"pyout\",
  29. \"prompt_number\": 1,
  30. \"text\": \"2\"
  31. }
  32. ],
  33. \"prompt_number\": 1
  34. }
  35. ]
  36. }
  37. ]
  38. }
  39. ")
  40. (defun eintest:notebook-enable-mode (buffer)
  41. (with-current-buffer buffer (ein:notebook-plain-mode) buffer))
  42. (defun eintest:kernel-fake-execute-reply (kernel msg-id execution-count)
  43. (let* ((payload nil)
  44. (content (list :execution_count 1 :payload payload))
  45. (packet (list :header (list :msg_type "execute_reply")
  46. :parent_header (list :msg_id msg-id)
  47. :content content)))
  48. (ein:kernel--handle-shell-reply kernel (json-encode packet))))
  49. (defun eintest:kernel-fake-stream (kernel msg-id data)
  50. (let* ((content (list :data data
  51. :name "stdout"))
  52. (packet (list :header (list :msg_type "stream")
  53. :parent_header (list :msg_id msg-id)
  54. :content content)))
  55. (ein:kernel--handle-iopub-reply kernel (json-encode packet))))
  56. (defun eintest:check-search-forward-from (start string &optional null-string)
  57. "Search STRING from START and check it is found.
  58. When non-`nil' NULL-STRING is given, it is searched from the
  59. position where the search of the STRING ends and check that it
  60. is not found."
  61. (save-excursion
  62. (goto-char start)
  63. (should (search-forward string nil t))
  64. (when null-string
  65. (should-not (search-forward null-string nil t)))))
  66. (defun eintest:cell-check-output (cell regexp)
  67. (save-excursion
  68. (goto-char (ein:cell-location cell :after-input))
  69. (should (looking-at-p (concat "\\=" regexp "\n")))))
  70. ;; from-json
  71. (ert-deftest ein:notebook-from-json-simple ()
  72. (with-current-buffer (ein:testing-notebook-from-json
  73. eintest:notebook-data-simple-json)
  74. (should (ein:$notebook-p ein:%notebook%))
  75. (should (equal (ein:$notebook-notebook-id ein:%notebook%) "NOTEBOOK-ID"))
  76. (should (equal (ein:$notebook-notebook-name ein:%notebook%) "Untitled0"))
  77. (should (equal (ein:worksheet-ncells ein:%worksheet%) 1))
  78. (let ((cell (car (ein:worksheet-get-cells ein:%worksheet%))))
  79. (should (ein:codecell-p cell))
  80. (should (equal (oref cell :input) "1 + 1"))
  81. (should (equal (oref cell :input-prompt-number) 1))
  82. (let ((outputs (oref cell :outputs)))
  83. (should (equal (length outputs) 1))
  84. (let ((o1 (car outputs)))
  85. (should (equal (plist-get o1 :output_type) "pyout"))
  86. (should (equal (plist-get o1 :prompt_number) 1))
  87. (should (equal (plist-get o1 :text) "2")))))))
  88. (ert-deftest ein:notebook-from-json-empty ()
  89. (with-current-buffer (ein:testing-notebook-make-empty)
  90. (should (ein:$notebook-p ein:%notebook%))
  91. (should (equal (ein:$notebook-notebook-id ein:%notebook%) "NOTEBOOK-ID"))
  92. (should (equal (ein:$notebook-notebook-name ein:%notebook%) "Dummy Name"))
  93. (should (equal (ein:worksheet-ncells ein:%worksheet%) 0))))
  94. (ert-deftest ein:notebook-from-json-all-cell-types ()
  95. (with-current-buffer
  96. (ein:testing-notebook-make-new
  97. nil nil (list (ein:testing-codecell-data "import numpy")
  98. (ein:testing-markdowncell-data "*markdown* text")
  99. (ein:testing-rawcell-data "`raw` cell text")
  100. (ein:testing-htmlcell-data "<b>HTML</b> text")
  101. (ein:testing-headingcell-data "Heading 1" 1)
  102. (ein:testing-headingcell-data "Heading 2" 2)
  103. (ein:testing-headingcell-data "Heading 3" 3)
  104. (ein:testing-headingcell-data "Heading 4" 4)
  105. (ein:testing-headingcell-data "Heading 5" 5)
  106. (ein:testing-headingcell-data "Heading 6" 6)))
  107. (should (ein:$notebook-p ein:%notebook%))
  108. (should (equal (ein:$notebook-notebook-id ein:%notebook%) "NOTEBOOK-ID"))
  109. (should (equal (ein:$notebook-notebook-name ein:%notebook%) "Dummy Name"))
  110. (should (equal (ein:worksheet-ncells ein:%worksheet%) 10))
  111. (let ((cells (ein:worksheet-get-cells ein:%worksheet%)))
  112. (should (ein:codecell-p (nth 0 cells)))
  113. (should (ein:markdowncell-p (nth 1 cells)))
  114. (should (ein:rawcell-p (nth 2 cells)))
  115. (should (ein:htmlcell-p (nth 3 cells)))
  116. (should (equal (ein:cell-get-text (nth 0 cells)) "import numpy"))
  117. (should (equal (ein:cell-get-text (nth 1 cells)) "*markdown* text"))
  118. (should (equal (ein:cell-get-text (nth 2 cells)) "`raw` cell text"))
  119. (should (equal (ein:cell-get-text (nth 3 cells)) "<b>HTML</b> text"))
  120. (loop for i from 4 to 9
  121. for level from 1
  122. for cell = (nth i cells)
  123. do (should (ein:headingcell-p cell))
  124. do (should (equal (ein:cell-get-text cell)
  125. (format "Heading %s" level)))
  126. do (should (= (oref cell :level) level))))))
  127. ;;; Destructor
  128. (defvar ein:testing-notebook-del-args-log 'nolog)
  129. (defadvice ein:notebook-del (before ein:testing-notebook-del activate)
  130. "Log argument passed to"
  131. (when (listp ein:testing-notebook-del-args-log)
  132. (push (ad-get-args 0) ein:testing-notebook-del-args-log)))
  133. (defun ein:testing-assert-notebook-del-not-called ()
  134. (should-not ein:testing-notebook-del-args-log))
  135. (defun ein:testing-assert-notebook-del-called-once-with (notebook)
  136. (should (= (length ein:testing-notebook-del-args-log) 1))
  137. (mapc (lambda (arg) (should (= (length arg) 1)))
  138. ein:testing-notebook-del-args-log)
  139. (should (eq (caar ein:testing-notebook-del-args-log) notebook)))
  140. (defun ein:testing-notebook-close-scratchsheet-open-and-close
  141. (num-open num-close)
  142. "Test for closing scratch sheet using `ein:notebook-close-worksheet'.
  143. 1. Open NUM-OPEN scratch sheets.
  144. 2. Close an existing worksheet.
  145. 3. Close NUM-CLOSE scratch sheets.
  146. When NUM-OPEN = NUM-CLOSE, notebook should be closed."
  147. (should (> num-open 0))
  148. (let ((notebook (buffer-local-value 'ein:%notebook%
  149. (ein:testing-notebook-make-empty)))
  150. ein:testing-notebook-del-args-log)
  151. (symbol-macrolet ((ss-list (ein:$notebook-scratchsheets notebook)))
  152. ;; Add scratchsheets. They can be just empty instance for this test.
  153. (dotimes (_ num-open)
  154. (setq ss-list
  155. (append ss-list (list (make-instance 'ein:scratchsheet)))))
  156. ;; Close worksheet
  157. (let ((ws (car (ein:$notebook-worksheets notebook)))
  158. (ein:notebook-kill-buffer-ask nil))
  159. (ein:notebook-close-worksheet notebook ws)
  160. (kill-buffer (ein:worksheet-buffer ws)))
  161. ;; Make sure adding scratchsheet work.
  162. (should (= (length ss-list) num-open))
  163. (mapc (lambda (ws) (should (ein:scratchsheet-p ws))) ss-list)
  164. ;; Close scratchsheets
  165. (dotimes (_ num-close)
  166. (ein:notebook-close-worksheet notebook (car ss-list)))
  167. ;; Actual tests:
  168. (should (= (length ss-list) (- num-open num-close)))
  169. (if (= num-open num-close)
  170. (ein:testing-assert-notebook-del-called-once-with notebook)
  171. (ein:testing-assert-notebook-del-not-called)))))
  172. (ert-deftest ein:notebook-close-scratchsheet/open-one-close-one ()
  173. (ein:testing-notebook-close-scratchsheet-open-and-close 1 1))
  174. (ert-deftest ein:notebook-close-scratchsheet/open-two-close-two ()
  175. (ein:testing-notebook-close-scratchsheet-open-and-close 2 2))
  176. (ert-deftest ein:notebook-close-scratchsheet/open-two-close-one ()
  177. (ein:testing-notebook-close-scratchsheet-open-and-close 2 1))
  178. ;;; Insertion and deletion of cells
  179. (ert-deftest ein:notebook-insert-cell-below-command-simple ()
  180. (with-current-buffer (ein:testing-notebook-make-empty)
  181. (call-interactively #'ein:worksheet-insert-cell-below)
  182. (call-interactively #'ein:worksheet-insert-cell-below)
  183. (call-interactively #'ein:worksheet-insert-cell-below)
  184. (should (equal (ein:worksheet-ncells ein:%worksheet%) 3))))
  185. (ert-deftest ein:notebook-insert-cell-above-command-simple ()
  186. (with-current-buffer (ein:testing-notebook-make-empty)
  187. (call-interactively #'ein:worksheet-insert-cell-above)
  188. (call-interactively #'ein:worksheet-insert-cell-above)
  189. (call-interactively #'ein:worksheet-insert-cell-above)
  190. (should (equal (ein:worksheet-ncells ein:%worksheet%) 3))))
  191. (ert-deftest ein:notebook-delete-cell-command-simple ()
  192. (with-current-buffer (ein:testing-notebook-make-empty)
  193. (loop repeat 3
  194. do (call-interactively #'ein:worksheet-insert-cell-above))
  195. (should (equal (ein:worksheet-ncells ein:%worksheet%) 3))
  196. (loop repeat 3
  197. do (call-interactively #'ein:worksheet-delete-cell))
  198. (should (equal (ein:worksheet-ncells ein:%worksheet%) 0))))
  199. (ert-deftest ein:notebook-delete-cell-command-no-undo ()
  200. (with-current-buffer (ein:testing-notebook-make-empty)
  201. (call-interactively #'ein:worksheet-insert-cell-above)
  202. (insert "some text")
  203. (should (equal (buffer-string) "
  204. In [ ]:
  205. some text
  206. "))
  207. (call-interactively #'ein:worksheet-delete-cell)
  208. (should (equal (buffer-string) "\n"))
  209. (should-error (undo)) ; should be ignore-error?
  210. (should (equal (buffer-string) "\n"))))
  211. (ert-deftest ein:notebook-kill-cell-command-simple ()
  212. (with-current-buffer (ein:testing-notebook-make-empty)
  213. (let (ein:kill-ring ein:kill-ring-yank-pointer)
  214. (loop repeat 3
  215. do (call-interactively #'ein:worksheet-insert-cell-above))
  216. (should (equal (ein:worksheet-ncells ein:%worksheet%) 3))
  217. (loop for i from 1 to 3
  218. do (call-interactively #'ein:worksheet-kill-cell)
  219. do (should (equal (length ein:kill-ring) i))
  220. do (should (equal (ein:worksheet-ncells ein:%worksheet%) (- 3 i)))))))
  221. (ert-deftest ein:notebook-copy-cell-command-simple ()
  222. (with-current-buffer (ein:testing-notebook-make-empty)
  223. (let (ein:kill-ring ein:kill-ring-yank-pointer)
  224. (loop repeat 3
  225. do (call-interactively #'ein:worksheet-insert-cell-above))
  226. (should (equal (ein:worksheet-ncells ein:%worksheet%) 3))
  227. (loop repeat 3
  228. do (call-interactively #'ein:worksheet-copy-cell))
  229. (should (equal (ein:worksheet-ncells ein:%worksheet%) 3))
  230. (should (equal (length ein:kill-ring) 3)))))
  231. (ert-deftest ein:notebook-yank-cell-command-simple ()
  232. (with-current-buffer (ein:testing-notebook-make-empty)
  233. (let (ein:kill-ring ein:kill-ring-yank-pointer)
  234. (loop repeat 3
  235. do (call-interactively #'ein:worksheet-insert-cell-above))
  236. (should (equal (ein:worksheet-ncells ein:%worksheet%) 3))
  237. (loop repeat 3
  238. do (call-interactively #'ein:worksheet-kill-cell))
  239. (should (equal (ein:worksheet-ncells ein:%worksheet%) 0))
  240. (should (equal (length ein:kill-ring) 3))
  241. (loop repeat 3
  242. do (call-interactively #'ein:worksheet-yank-cell))
  243. (should (equal (ein:worksheet-ncells ein:%worksheet%) 3))
  244. (loop for cell in (ein:worksheet-get-cells ein:%worksheet%)
  245. do (should (ein:codecell-p cell))
  246. do (should (slot-boundp cell :kernel))
  247. do (should (slot-boundp cell :events))))))
  248. (ert-deftest ein:notebook-yank-cell-command-two-buffers ()
  249. (let (ein:kill-ring ein:kill-ring-yank-pointer)
  250. (with-current-buffer (ein:testing-notebook-make-empty "NB1")
  251. (call-interactively #'ein:worksheet-insert-cell-above)
  252. (should (equal (ein:worksheet-ncells ein:%worksheet%) 1))
  253. (call-interactively #'ein:worksheet-kill-cell)
  254. (should (equal (ein:worksheet-ncells ein:%worksheet%) 0))
  255. (flet ((y-or-n-p (&rest ignore) t)
  256. (ein:notebook-del (&rest ignore)))
  257. ;; FIXME: are there anyway to skip confirmation?
  258. (kill-buffer)))
  259. (with-current-buffer (ein:testing-notebook-make-empty "NB2")
  260. (call-interactively #'ein:worksheet-yank-cell)
  261. (should (equal (ein:worksheet-ncells ein:%worksheet%) 1)))))
  262. (ert-deftest ein:notebook-toggle-cell-type-simple ()
  263. (with-current-buffer (ein:testing-notebook-make-empty)
  264. (call-interactively #'ein:worksheet-insert-cell-above)
  265. (insert "some text")
  266. (should (ein:codecell-p (ein:worksheet-get-current-cell)))
  267. (should (slot-boundp (ein:worksheet-get-current-cell) :kernel))
  268. ;; toggle to markdown
  269. (call-interactively #'ein:worksheet-toggle-cell-type)
  270. (should (ein:markdowncell-p (ein:worksheet-get-current-cell)))
  271. (should (looking-back "some text"))
  272. ;; toggle to code
  273. (call-interactively #'ein:worksheet-toggle-cell-type)
  274. (should (ein:codecell-p (ein:worksheet-get-current-cell)))
  275. (should (slot-boundp (ein:worksheet-get-current-cell) :kernel))
  276. (should (looking-back "some text"))))
  277. (ert-deftest ein:notebook-change-cell-type-cycle-through ()
  278. (with-current-buffer (ein:testing-notebook-make-empty)
  279. (call-interactively #'ein:worksheet-insert-cell-above)
  280. (insert "some text")
  281. ;; start with code cell
  282. (should (ein:codecell-p (ein:worksheet-get-current-cell)))
  283. (should (slot-boundp (ein:worksheet-get-current-cell) :kernel))
  284. (let ((check
  285. (lambda (type &optional level)
  286. (let ((cell-p (intern (format "ein:%scell-p" type)))
  287. (cell (ein:worksheet-get-current-cell)))
  288. (ein:worksheet-change-cell-type ein:%worksheet% cell
  289. type level t)
  290. (let ((new (ein:worksheet-get-current-cell)))
  291. (should-not (eq new cell))
  292. (should (funcall cell-p new)))
  293. (should (looking-back "some text"))))))
  294. ;; change type: code (no change) -> markdown -> raw
  295. (loop for type in '("code" "markdown" "raw")
  296. do (funcall check type))
  297. ;; change level: 1 to 6
  298. (loop for level from 1 to 6
  299. do (funcall check "heading" level))
  300. ;; back to code
  301. (funcall check "code")
  302. (should (slot-boundp (ein:worksheet-get-current-cell) :kernel)))))
  303. (defun eintest:notebook-split-cell-at-point
  304. (insert-text search-text head-text tail-text &optional no-trim)
  305. "Test `ein:notebook-split-cell-at-point' by the following procedure.
  306. 1. Insert, INSERT-TEXT.
  307. 2. Split cell just before SEARCH-TEXT.
  308. 3. Check that head cell has HEAD-TEXT.
  309. 4. Check that tail cell has TAIL-TEXT.
  310. NO-TRIM is passed to `ein:notebook-split-cell-at-point'."
  311. (with-current-buffer (ein:testing-notebook-make-empty)
  312. (call-interactively #'ein:worksheet-insert-cell-above)
  313. (insert insert-text)
  314. (when search-text
  315. (search-backward search-text))
  316. ;; do it
  317. (let ((current-prefix-arg no-trim))
  318. (call-interactively #'ein:worksheet-split-cell-at-point))
  319. ;; check the "tail" cell
  320. (let ((cell (ein:worksheet-get-current-cell)))
  321. (ein:cell-goto cell)
  322. (should (equal (ein:cell-get-text cell) tail-text))
  323. (should (ein:codecell-p cell))
  324. (should (slot-boundp cell :kernel)))
  325. ;; check the "head" cell
  326. (call-interactively #'ein:worksheet-goto-prev-input)
  327. (let ((cell (ein:worksheet-get-current-cell)))
  328. (ein:cell-goto cell)
  329. (should (equal (ein:cell-get-text cell) head-text))
  330. (should (ein:codecell-p cell))
  331. (should (slot-boundp cell :kernel)))))
  332. (ert-deftest ein:notebook-split-cell-at-point-before-newline ()
  333. (eintest:notebook-split-cell-at-point
  334. "some\ntext" "text" "some" "text"))
  335. (ert-deftest ein:notebook-split-cell-at-point-after-newline ()
  336. (eintest:notebook-split-cell-at-point
  337. "some\ntext" "\ntext" "some" "text"))
  338. (ert-deftest ein:notebook-split-cell-at-point-before-newline-no-trim ()
  339. (eintest:notebook-split-cell-at-point
  340. "some\ntext" "text" "some\n" "text" t))
  341. (ert-deftest ein:notebook-split-cell-at-point-after-newline-no-trim ()
  342. (eintest:notebook-split-cell-at-point
  343. "some\ntext" "\ntext" "some" "\ntext" t))
  344. (ert-deftest ein:notebook-split-cell-at-point-no-head ()
  345. (eintest:notebook-split-cell-at-point
  346. "some" "some" "" "some"))
  347. (ert-deftest ein:notebook-split-cell-at-point-no-tail ()
  348. (eintest:notebook-split-cell-at-point
  349. "some" nil "some" ""))
  350. (ert-deftest ein:notebook-merge-cell-command-next ()
  351. (with-current-buffer (ein:testing-notebook-make-empty)
  352. (call-interactively #'ein:worksheet-insert-cell-above)
  353. (insert "Cell 1")
  354. (call-interactively #'ein:worksheet-insert-cell-above)
  355. (insert "Cell 0")
  356. (let ((current-prefix-arg t))
  357. (call-interactively #'ein:worksheet-merge-cell))
  358. (ein:cell-goto (ein:worksheet-get-current-cell))
  359. (should (looking-at "Cell 0\nCell 1"))))
  360. (ert-deftest ein:notebook-merge-cell-command-prev ()
  361. (with-current-buffer (ein:testing-notebook-make-empty)
  362. (call-interactively #'ein:worksheet-insert-cell-below)
  363. (insert "Cell 0")
  364. (call-interactively #'ein:worksheet-insert-cell-below)
  365. (insert "Cell 1")
  366. (call-interactively #'ein:worksheet-merge-cell)
  367. (ein:cell-goto (ein:worksheet-get-current-cell))
  368. (should (looking-at "Cell 0\nCell 1"))))
  369. ;;; Cell selection.
  370. (ert-deftest ein:notebook-goto-next-input-command-simple ()
  371. (with-current-buffer (ein:testing-notebook-make-empty)
  372. (loop for i downfrom 2 to 0
  373. do (call-interactively #'ein:worksheet-insert-cell-above)
  374. do (insert (format "Cell %s" i)))
  375. (should (equal (ein:worksheet-ncells ein:%worksheet%) 3))
  376. ;; (message "%s" (buffer-string))
  377. (loop for i from 0 below 2
  378. do (beginning-of-line) ; This is required, I need to check why
  379. do (should (looking-at (format "Cell %s" i)))
  380. do (call-interactively #'ein:worksheet-goto-next-input)
  381. do (should (looking-at (format "Cell %s" (1+ i)))))))
  382. (ert-deftest ein:notebook-goto-prev-input-command-simple ()
  383. (with-current-buffer (ein:testing-notebook-make-empty)
  384. (loop for i from 0 below 3
  385. do (call-interactively #'ein:worksheet-insert-cell-below)
  386. do (insert (format "Cell %s" i)))
  387. (should (equal (ein:worksheet-ncells ein:%worksheet%) 3))
  388. ;; (message "%s" (buffer-string))
  389. (loop for i downfrom 2 to 1
  390. do (beginning-of-line) ; This is required, I need to check why
  391. do (should (looking-at (format "Cell %s" i)))
  392. do (call-interactively #'ein:worksheet-goto-prev-input)
  393. do (should (looking-at (format "Cell %s" (1- i)))))))
  394. (defun ein:testing-beginning-of-cell-input (num-cells
  395. initial-point
  396. before-callback
  397. arg
  398. should-looking-at-this)
  399. (with-current-buffer (ein:testing-notebook-make-empty)
  400. (ein:testing-insert-cells-with-format num-cells)
  401. (goto-char (point-min))
  402. (search-forward initial-point)
  403. (when before-callback (funcall before-callback))
  404. (should-not (looking-at-p should-looking-at-this))
  405. (ein:worksheet-beginning-of-cell-input arg)
  406. (should (looking-at-p should-looking-at-this))))
  407. (ert-deftest ein:worksheet-beginning-of-cell-input-with-no-arg ()
  408. (ein:testing-beginning-of-cell-input 1 "Cell 0" nil nil "Cell 0"))
  409. (ert-deftest ein:worksheet-beginning-of-cell-input-with-arg-two ()
  410. (ein:testing-beginning-of-cell-input 2 "Cell 1" nil 2 "Cell 0"))
  411. (ert-deftest ein:worksheet-beginning-of-cell-input-with-arg-minus-one ()
  412. (ein:testing-beginning-of-cell-input 2 "Cell 0" nil -1 "Cell 1"))
  413. (ert-deftest ein:worksheet-beginning-of-cell-input-with-no-arg-at-footer ()
  414. (ein:testing-beginning-of-cell-input 1 "Cell 0"
  415. #'forward-line
  416. nil "Cell 0"))
  417. (ert-deftest ein:worksheet-beginning-of-cell-input-with-arg-two-at-footer ()
  418. (ein:testing-beginning-of-cell-input 2 "Cell 1"
  419. #'forward-line
  420. 2 "Cell 0"))
  421. (ert-deftest ein:worksheet-beginning-of-cell-input-with-arg-minus-one-at-footer
  422. ()
  423. (ein:testing-beginning-of-cell-input 2 "Cell 0"
  424. #'forward-line
  425. -1 "Cell 1"))
  426. (ert-deftest ein:worksheet-beginning-of-cell-input-with-no-arg-at-prompt ()
  427. (ein:testing-beginning-of-cell-input 2 "Cell 1"
  428. (lambda () (forward-line -1))
  429. nil "Cell 0"))
  430. (ert-deftest ein:worksheet-beginning-of-cell-input-with-arg-two-at-prompt ()
  431. (ein:testing-beginning-of-cell-input 2 "Cell 1"
  432. (lambda () (forward-line -1))
  433. 2 "Cell 0"))
  434. (ert-deftest ein:worksheet-beginning-of-cell-input-with-arg-minus-one-at-prompt
  435. ()
  436. (ein:testing-beginning-of-cell-input 2 "Cell 0"
  437. ;; I need two cells to make it fail
  438. ;; without (forward-line -1)
  439. (lambda () (forward-line -1))
  440. -1 "Cell 0"))
  441. (ert-deftest ein:worksheet-beginning-of-cell-input-repeat ()
  442. (with-current-buffer (ein:testing-notebook-make-empty)
  443. (ein:testing-insert-cells-with-format 3)
  444. (goto-char (point-min))
  445. (search-forward "Cell 2")
  446. (should-not (looking-at-p "Cell 2"))
  447. (ein:worksheet-beginning-of-cell-input)
  448. (should (looking-at-p "Cell 2"))
  449. (should-not (looking-at-p "Cell 1"))
  450. (ein:worksheet-beginning-of-cell-input)
  451. (should (looking-at-p "Cell 1"))
  452. (should-not (looking-at-p "Cell 0"))
  453. (ein:worksheet-beginning-of-cell-input)
  454. (should (looking-at-p "Cell 0"))))
  455. (defun ein:testing-end-of-cell-input (num-cells
  456. initial-point
  457. before-callback
  458. arg
  459. should-looking-back-this)
  460. (with-current-buffer (ein:testing-notebook-make-empty)
  461. (ein:testing-insert-cells-with-format num-cells)
  462. (goto-char (point-min))
  463. (search-forward initial-point)
  464. (beginning-of-line)
  465. (when before-callback (funcall before-callback))
  466. (should-not (looking-back should-looking-back-this))
  467. (ein:worksheet-end-of-cell-input arg)
  468. (should (looking-back should-looking-back-this))))
  469. (ert-deftest ein:worksheet-end-of-cell-input-with-no-arg ()
  470. (ein:testing-end-of-cell-input 1 "Cell 0" nil nil "Cell 0"))
  471. (ert-deftest ein:worksheet-end-of-cell-input-with-arg-two ()
  472. (ein:testing-end-of-cell-input 2 "Cell 0" nil 2 "Cell 1"))
  473. (ert-deftest ein:worksheet-end-of-cell-input-with-arg-minus-one ()
  474. (ein:testing-end-of-cell-input 2 "Cell 1" nil -1 "Cell 0"))
  475. (ert-deftest ein:worksheet-end-of-cell-input-with-no-arg-at-footer ()
  476. (ein:testing-end-of-cell-input 2 "Cell 0"
  477. #'forward-line
  478. nil "Cell 1"))
  479. (ert-deftest ein:worksheet-end-of-cell-input-with-arg-two-at-footer ()
  480. (ein:testing-end-of-cell-input 3 "Cell 0"
  481. #'forward-line
  482. 2 "Cell 2"))
  483. (ert-deftest ein:worksheet-end-of-cell-input-with-arg-minus-one-at-footer
  484. ()
  485. (ein:testing-end-of-cell-input 2 "Cell 0"
  486. #'forward-line
  487. -1 "Cell 0"))
  488. (ert-deftest ein:worksheet-end-of-cell-input-with-no-arg-at-prompt ()
  489. (ein:testing-end-of-cell-input 2 "Cell 1"
  490. (lambda () (forward-line -1))
  491. nil "Cell 1"))
  492. (ert-deftest ein:worksheet-end-of-cell-input-with-arg-two-at-prompt ()
  493. (ein:testing-end-of-cell-input 3 "Cell 0"
  494. (lambda () (forward-line -1))
  495. 2 "Cell 1"))
  496. (ert-deftest ein:worksheet-end-of-cell-input-with-arg-minus-one-at-prompt
  497. ()
  498. (ein:testing-end-of-cell-input 2 "Cell 1"
  499. (lambda () (forward-line -1))
  500. -1 "Cell 0"))
  501. ;;; Cell movement
  502. (ert-deftest ein:notebook-move-cell-up-command-simple ()
  503. (with-current-buffer (ein:testing-notebook-make-empty)
  504. (loop for i from 0 below 3
  505. do (call-interactively #'ein:worksheet-insert-cell-below)
  506. do (insert (format "Cell %s" i)))
  507. (beginning-of-line)
  508. (should (looking-at "Cell 2"))
  509. (loop repeat 2
  510. do (call-interactively #'ein:worksheet-move-cell-up))
  511. ;; (message "%s" (buffer-string))
  512. (beginning-of-line)
  513. (should (looking-at "Cell 2"))
  514. (should (search-forward "Cell 0" nil t))
  515. (should (search-forward "Cell 1" nil t))
  516. (should-not (search-forward "Cell 2" nil t))))
  517. (ert-deftest ein:notebook-move-cell-down-command-simple ()
  518. (with-current-buffer (ein:testing-notebook-make-empty)
  519. (loop for i from 0 below 3
  520. do (call-interactively #'ein:worksheet-insert-cell-above)
  521. do (insert (format "Cell %s" i)))
  522. (loop repeat 2
  523. do (call-interactively #'ein:worksheet-move-cell-down))
  524. (beginning-of-line)
  525. (should (looking-at "Cell 2"))
  526. (should (search-backward "Cell 0" nil t))
  527. (should (search-backward "Cell 1" nil t))
  528. (should-not (search-backward "Cell 2" nil t))))
  529. ;;; Cell collapsing and output clearing
  530. (ert-deftest ein:worksheet-toggle-output ()
  531. (with-current-buffer (ein:testing-notebook-make-empty)
  532. (let ((cell (call-interactively #'ein:worksheet-insert-cell-below)))
  533. (should-not (oref cell :collapsed))
  534. (call-interactively #'ein:worksheet-toggle-output)
  535. (should (oref cell :collapsed))
  536. (call-interactively #'ein:worksheet-toggle-output)
  537. (should-not (oref cell :collapsed)))))
  538. (defun ein:testing-insert-cells (list-type-or-cell &optional pivot callback)
  539. (loop with ws = ein:%worksheet%
  540. with cell = pivot
  541. for type in list-type-or-cell
  542. for i from 0
  543. do (setq cell (ein:worksheet-insert-cell-below ws type cell t))
  544. if callback
  545. do (funcall callback i cell)))
  546. (defun* ein:testing-insert-cells-with-format (num &optional
  547. (format "Cell %s")
  548. (type 'code))
  549. (ein:testing-insert-cells (loop repeat num collect type)
  550. nil
  551. (lambda (i &rest _) (insert (format format i))))
  552. (should (equal (ein:worksheet-ncells ein:%worksheet%) num)))
  553. (defun ein:testing-test-output-visibility-all (collapsed)
  554. (mapc (lambda (cell) (should (eq (oref cell :collapsed) collapsed)))
  555. (ein:worksheet-get-cells ein:%worksheet%)))
  556. (ert-deftest ein:worksheet-set-output-visibility-all/visible-from-all-hidden ()
  557. (with-current-buffer (ein:testing-notebook-make-empty)
  558. ;; Prepare cells
  559. (ein:testing-insert-cells '(code code code))
  560. (mapc (lambda (cell) (ein:cell-set-collapsed cell nil))
  561. (ein:worksheet-get-cells ein:%worksheet%))
  562. ;; Call the command
  563. (call-interactively #'ein:worksheet-set-output-visibility-all)
  564. ;; Check it worked
  565. (ein:testing-test-output-visibility-all nil)))
  566. (ert-deftest ein:worksheet-set-output-visibility-all/hidden-from-all-visible ()
  567. (with-current-buffer (ein:testing-notebook-make-empty)
  568. ;; Prepare cells
  569. (ein:testing-insert-cells '(code code code))
  570. ;; Call the command
  571. (let ((current-prefix-arg t))
  572. (call-interactively #'ein:worksheet-set-output-visibility-all))
  573. ;; Check it worked
  574. (ein:testing-test-output-visibility-all t)))
  575. (ert-deftest ein:worksheet-set-output-visibility-all/visible-from-part-hidden ()
  576. (with-current-buffer (ein:testing-notebook-make-empty)
  577. ;; Prepare cells
  578. (ein:testing-insert-cells '(code code code))
  579. (ein:cell-set-collapsed
  580. (nth 1 (ein:worksheet-get-cells ein:%worksheet%)) nil)
  581. ;; Call the command
  582. (call-interactively #'ein:worksheet-set-output-visibility-all)
  583. ;; Check it worked
  584. (ein:testing-test-output-visibility-all nil)))
  585. (ert-deftest ein:worksheet-set-output-visibility-all/hidden-from-part-visible ()
  586. (with-current-buffer (ein:testing-notebook-make-empty)
  587. ;; Prepare cells
  588. (ein:testing-insert-cells '(code code code))
  589. (ein:cell-set-collapsed
  590. (nth 1 (ein:worksheet-get-cells ein:%worksheet%)) nil)
  591. ;; Call the command
  592. (call-interactively #'ein:worksheet-set-output-visibility-all)
  593. (let ((current-prefix-arg t))
  594. (call-interactively #'ein:worksheet-set-output-visibility-all))
  595. ;; Check it worked
  596. (ein:testing-test-output-visibility-all t)))
  597. (defun ein:testing-assert-cell-output-num (cell num-outputs)
  598. (should (ein:codecell-p cell))
  599. (should (= (length (oref cell :outputs)) num-outputs)))
  600. (ert-deftest ein:worksheet-clear-output/simple ()
  601. (with-current-buffer
  602. (ein:testing-make-notebook-with-outputs '(("'cell output'")
  603. ("'cell output'")))
  604. (should (= (ein:worksheet-ncells ein:%worksheet%) 2))
  605. (let* ((cells (ein:worksheet-get-cells ein:%worksheet%)))
  606. (ein:testing-assert-cell-output-num (nth 0 cells) 1)
  607. (ein:testing-assert-cell-output-num (nth 1 cells) 1)
  608. (ein:cell-goto (nth 0 cells))
  609. (call-interactively #'ein:worksheet-clear-output)
  610. (ein:testing-assert-cell-output-num (nth 0 cells) 0) ; cleared
  611. (ein:testing-assert-cell-output-num (nth 1 cells) 1))))
  612. (ert-deftest ein:worksheet-clear-output/preserve-input-prompt ()
  613. (with-current-buffer
  614. (ein:testing-make-notebook-with-outputs '(("'cell output'")
  615. ("'cell output'")
  616. ("'cell output'")))
  617. (should (= (ein:worksheet-ncells ein:%worksheet%) 3))
  618. (let* ((cells (ein:worksheet-get-cells ein:%worksheet%)))
  619. (ein:cell-set-input-prompt (nth 0 cells) 111)
  620. (ein:cell-set-input-prompt (nth 1 cells) 222)
  621. (ein:cell-set-input-prompt (nth 2 cells) 333)
  622. ;; Call `ein:worksheet-clear-output' with/without prefix argument.
  623. (ein:cell-goto (nth 0 cells))
  624. (call-interactively #'ein:worksheet-clear-output)
  625. (ein:cell-goto (nth 2 cells))
  626. (let ((current-prefix-arg '(4)))
  627. (call-interactively #'ein:worksheet-clear-output))
  628. ;; Check cells' prompt number
  629. (should (eq (oref (nth 0 cells) :input-prompt-number) nil))
  630. (should (eq (oref (nth 1 cells) :input-prompt-number) 222))
  631. (should (eq (oref (nth 2 cells) :input-prompt-number) 333)))))
  632. (ert-deftest ein:worksheet-clear-all-output/simple ()
  633. (with-current-buffer
  634. (ein:testing-make-notebook-with-outputs '(("'cell output'")
  635. ("'cell output'")))
  636. (should (= (ein:worksheet-ncells ein:%worksheet%) 2))
  637. (let* ((cells (ein:worksheet-get-cells ein:%worksheet%)))
  638. (ein:testing-assert-cell-output-num (nth 0 cells) 1)
  639. (ein:testing-assert-cell-output-num (nth 1 cells) 1)
  640. (call-interactively #'ein:worksheet-clear-all-output)
  641. (ein:testing-assert-cell-output-num (nth 0 cells) 0)
  642. (ein:testing-assert-cell-output-num (nth 1 cells) 0))))
  643. ;; Kernel related things
  644. (defun eintest:notebook-check-kernel-and-codecell (kernel cell)
  645. (should (ein:$kernel-p kernel))
  646. (should (ein:codecell-p cell))
  647. (should (ein:$kernel-p (oref cell :kernel))))
  648. (defun eintest:notebook-fake-execution (kernel text msg-id callbacks)
  649. (mocker-let ((ein:kernel-execute
  650. (kernel code callbacks kwd-silent silent)
  651. ((:input (list kernel text callbacks :silent nil))))
  652. (ein:kernel-live-p
  653. (kernel)
  654. ((:input (list kernel) :output t))))
  655. (call-interactively #'ein:worksheet-execute-cell))
  656. (ein:kernel-set-callbacks-for-msg kernel msg-id callbacks))
  657. (ert-deftest ein:notebook-execute-current-cell ()
  658. (with-current-buffer (ein:testing-notebook-make-empty)
  659. (call-interactively #'ein:worksheet-insert-cell-below)
  660. (let* ((text "print 'Hello World'")
  661. (cell (ein:worksheet-get-current-cell))
  662. (kernel (ein:$notebook-kernel ein:%notebook%))
  663. (msg-id "DUMMY-MSG-ID")
  664. (callbacks (ein:cell-make-callbacks cell)))
  665. (eintest:notebook-check-kernel-and-codecell kernel cell)
  666. ;; Execute
  667. (insert text)
  668. (eintest:notebook-fake-execution kernel text msg-id callbacks)
  669. ;; Execute reply
  670. (should-error (eintest:check-search-forward-from (point-min) "In [1]:"))
  671. (eintest:kernel-fake-execute-reply kernel msg-id 1)
  672. (should (= (oref cell :input-prompt-number) 1))
  673. (eintest:check-search-forward-from (point-min) "In [1]:")
  674. ;; Stream output
  675. (eintest:kernel-fake-stream kernel msg-id "Hello World")
  676. (should (= (ein:cell-num-outputs cell) 1))
  677. (save-excursion
  678. (goto-char (point-min))
  679. (should (search-forward "In [1]:" nil t))
  680. (should (search-forward "print 'Hello World'" nil t))
  681. (should (search-forward "\nHello World\n" nil t)) ; stream output
  682. (should-not (search-forward "Hello World" nil t))))))
  683. (defmacro eintest:worksheet-execute-cell-and-*-deftest
  684. (do-this cell-type has-next-p insert-p)
  685. "Define:
  686. ein:worksheet-execute-cell-and-{DO-THIS}/on-{CELL-TYPE}cell-{no,with}-next
  687. For example, when `goto-next', \"code\", `nil', `nil' is given,
  688. `ein:worksheet-execute-cell-and-goto-next/on-codecell-no-next' is
  689. defined."
  690. (let ((test-name
  691. (intern (format "ein:worksheet-execute-cell-and-%s/on-%scell-%s"
  692. do-this cell-type
  693. (if has-next-p "with-next" "no-next"))))
  694. (command
  695. (intern (format "ein:worksheet-execute-cell-and-%s" do-this))))
  696. `(ert-deftest ,test-name ()
  697. (with-current-buffer (ein:testing-notebook-make-empty)
  698. (let* ((ws ein:%worksheet%)
  699. (current (ein:worksheet-insert-cell-below ws ,cell-type nil t))
  700. ,@(when has-next-p
  701. '((next
  702. (ein:worksheet-insert-cell-below ws "code" current)))))
  703. (mocker-let ((ein:worksheet-execute-cell
  704. (ws cell)
  705. (,@(when (equal cell-type "code")
  706. '((:input (list ein:%worksheet% current)))))))
  707. (call-interactively #',command)
  708. (let ((cell (ein:worksheet-get-current-cell)))
  709. (should (eq (ein:cell-prev cell) current))
  710. ,(when has-next-p
  711. (if insert-p
  712. '(should-not (eq cell next))
  713. '(should (eq cell next)))))))))))
  714. (eintest:worksheet-execute-cell-and-*-deftest goto-next "code" nil t )
  715. (eintest:worksheet-execute-cell-and-*-deftest goto-next "code" t nil)
  716. (eintest:worksheet-execute-cell-and-*-deftest goto-next "markdown" nil t )
  717. (eintest:worksheet-execute-cell-and-*-deftest goto-next "markdown" t nil)
  718. (eintest:worksheet-execute-cell-and-*-deftest insert-below "code" nil t )
  719. (eintest:worksheet-execute-cell-and-*-deftest insert-below "code" t t )
  720. (eintest:worksheet-execute-cell-and-*-deftest insert-below "markdown" nil t )
  721. (eintest:worksheet-execute-cell-and-*-deftest insert-below "markdown" t t )
  722. ;;; Persistence and loading
  723. (defun ein:testin-notebook-close (num-ws num-ss)
  724. (should (= num-ws 1)) ; currently EIN only supports 1 WS
  725. (should (>= num-ss 0))
  726. (let ((notebook (buffer-local-value 'ein:%notebook%
  727. (ein:testing-notebook-make-empty)))
  728. ein:testing-notebook-del-args-log)
  729. (dotimes (_ num-ss)
  730. (ein:notebook-scratchsheet-render-new notebook))
  731. (let ((buffers (ein:notebook-buffer-list notebook)))
  732. (should (= (length buffers) (+ num-ws num-ss)))
  733. (ein:notebook-close notebook)
  734. (mapc (lambda (b) (should-not (buffer-live-p b))) buffers)
  735. (ein:testing-assert-notebook-del-called-once-with notebook))))
  736. (ert-deftest ein:notebook-close/one-ws-no-ss ()
  737. (ein:testin-notebook-close 1 0))
  738. (ert-deftest ein:notebook-close/one-ws-one-ss ()
  739. (ein:testin-notebook-close 1 1))
  740. (ert-deftest ein:notebook-close/one-ws-five-ss ()
  741. (ein:testin-notebook-close 1 5))
  742. (defun ein:testing-notebook-data-assert-one-worksheet-one-cell (notebook text)
  743. (let* ((data (ein:notebook-to-json notebook))
  744. (worksheets (assoc-default 'worksheets data #'eq))
  745. (cells (assoc-default 'cells (elt worksheets 0) #'eq))
  746. (cell-0 (elt cells 0))
  747. (input (assoc-default 'input cell-0 #'eq)))
  748. (should (= (length worksheets) 1))
  749. (should (= (length cells) 1))
  750. (should (equal input text))))
  751. (defun ein:testing-notebook-data-assert-one-worksheet-no-cell (notebook)
  752. (let* ((data (ein:notebook-to-json notebook))
  753. (worksheets (assoc-default 'worksheets data #'eq))
  754. (cells (assoc-default 'cells (elt worksheets 0) #'eq)))
  755. (should (= (length worksheets) 1))
  756. (should (= (length cells) 0))))
  757. (ert-deftest ein:notebook-to-json-after-closing-a-worksheet ()
  758. (with-current-buffer (ein:testing-notebook-make-new)
  759. (let ((buffer (current-buffer))
  760. (notebook ein:%notebook%))
  761. ;; Edit notebook.
  762. (ein:cell-goto (ein:get-cell-at-point))
  763. (insert "some text")
  764. (ein:testing-notebook-data-assert-one-worksheet-one-cell notebook
  765. "some text")
  766. (should (ein:notebook-modified-p notebook))
  767. ;; Open scratch sheet.
  768. (ein:notebook-scratchsheet-open notebook)
  769. ;; Pretend that notebook is saved
  770. (ein:notebook-save-notebook-success notebook)
  771. (should-not (ein:notebook-modified-p notebook))
  772. ;; Kill a worksheet buffer
  773. (kill-buffer buffer)
  774. (should (ein:notebook-live-p notebook))
  775. ;; to-json should still work
  776. (ein:testing-notebook-data-assert-one-worksheet-one-cell notebook
  777. "some text"))))
  778. (ert-deftest ein:notebook-to-json-after-discarding-a-worksheet ()
  779. (with-current-buffer (ein:testing-notebook-make-new)
  780. (let ((buffer (current-buffer))
  781. (notebook ein:%notebook%))
  782. ;; Edit notebook.
  783. (ein:cell-goto (ein:get-cell-at-point))
  784. (insert "some text")
  785. (ein:testing-notebook-data-assert-one-worksheet-one-cell notebook
  786. "some text")
  787. (should (ein:notebook-modified-p notebook))
  788. ;; Open scratch sheet.
  789. (ein:notebook-scratchsheet-open notebook)
  790. ;; Discard a worksheet buffer
  791. (should (ein:notebook-modified-p notebook))
  792. (let (ein:notebook-kill-buffer-ask)
  793. (kill-buffer buffer))
  794. (should (ein:notebook-live-p notebook))
  795. ;; to-json should still work
  796. (ein:testing-notebook-data-assert-one-worksheet-no-cell notebook))))
  797. (defun ein:testing-notebook-should-be-closed (notebook buffer)
  798. (should-not (buffer-live-p buffer))
  799. (should-not (ein:notebook-live-p notebook)))
  800. (ert-deftest ein:notebook-kill-kernel-then-close-when-its-alive ()
  801. (with-current-buffer (ein:testing-notebook-make-new)
  802. (let ((buffer (current-buffer))
  803. (notebook ein:%notebook%)
  804. (kernel (ein:$notebook-kernel ein:%notebook%))
  805. (ein:notebook-kill-buffer-ask nil))
  806. (mocker-let
  807. ((ein:kernel-live-p
  808. (kernel)
  809. ((:input (list kernel) :output t)))
  810. (ein:kernel-kill
  811. (kernel &optional callback cbargs)
  812. ((:input (list kernel #'ein:notebook-close (list notebook))))))
  813. (call-interactively #'ein:notebook-kill-kernel-then-close-command))
  814. (should (buffer-live-p buffer))
  815. ;; Pretend that `ein:notebook-close' is called.
  816. (ein:notebook-close notebook)
  817. (ein:testing-notebook-should-be-closed notebook buffer))))
  818. (ert-deftest ein:notebook-kill-kernel-then-close-when-already-dead ()
  819. (with-current-buffer (ein:testing-notebook-make-new)
  820. (let ((buffer (current-buffer))
  821. (notebook ein:%notebook%)
  822. (kernel (ein:$notebook-kernel ein:%notebook%))
  823. (ein:notebook-kill-buffer-ask nil))
  824. (mocker-let
  825. ((ein:kernel-live-p
  826. (kernel)
  827. ((:input (list kernel) :output nil))))
  828. (call-interactively #'ein:notebook-kill-kernel-then-close-command))
  829. (ein:testing-notebook-should-be-closed notebook buffer))))
  830. ;; Notebook undo
  831. (defun eintest:notebook-undo-after-insert-above ()
  832. (with-current-buffer (ein:testing-notebook-make-empty)
  833. (let ((text "some text"))
  834. (call-interactively #'ein:worksheet-insert-cell-above)
  835. (insert text)
  836. (undo-boundary)
  837. (call-interactively #'ein:worksheet-insert-cell-above)
  838. (call-interactively #'ein:worksheet-goto-next-input)
  839. (should (equal (ein:cell-get-text (ein:worksheet-get-current-cell)) text))
  840. (if (eq ein:worksheet-enable-undo 'full)
  841. (undo)
  842. (should-error (undo)))
  843. (when (eq ein:worksheet-enable-undo 'full)
  844. ;; FIXME: Known bug. (this must succeed.)
  845. (should-error (should (equal (buffer-string) "
  846. In [ ]:
  847. In [ ]:
  848. ")))))))
  849. (defun eintest:notebook-undo-after-split ()
  850. (with-current-buffer (ein:testing-notebook-make-empty)
  851. (let ((line-1 "first line")
  852. (line-2 "second line"))
  853. (call-interactively #'ein:worksheet-insert-cell-below)
  854. (insert line-1 "\n" line-2)
  855. (undo-boundary)
  856. (move-beginning-of-line 1)
  857. (call-interactively #'ein:worksheet-split-cell-at-point)
  858. (undo-boundary)
  859. (should (equal (ein:cell-get-text (ein:worksheet-get-current-cell))
  860. line-2))
  861. (if (eq ein:worksheet-enable-undo 'full)
  862. (undo)
  863. (should-error (undo)))
  864. (when (eq ein:worksheet-enable-undo 'full)
  865. (should (equal (buffer-string) "
  866. In [ ]:
  867. In [ ]:
  868. first line
  869. second line
  870. "))))))
  871. (defun eintest:notebook-undo-after-merge ()
  872. (with-current-buffer (ein:testing-notebook-make-empty)
  873. (let ((line-1 "first line")
  874. (line-2 "second line"))
  875. (call-interactively #'ein:worksheet-insert-cell-below)
  876. (call-interactively #'ein:worksheet-insert-cell-below)
  877. ;; Extra cells to avoid "Changes to be undone are outside visible
  878. ;; portion of buffer" user-error:
  879. (call-interactively #'ein:worksheet-insert-cell-below)
  880. (call-interactively #'ein:worksheet-insert-cell-below)
  881. (goto-char (point-min))
  882. (call-interactively #'ein:worksheet-goto-next-input)
  883. (insert line-1)
  884. (undo-boundary)
  885. (call-interactively #'ein:worksheet-goto-next-input)
  886. (insert line-2)
  887. (undo-boundary)
  888. (call-interactively #'ein:worksheet-merge-cell)
  889. (undo-boundary)
  890. (should (equal (ein:cell-get-text (ein:worksheet-get-current-cell))
  891. (concat line-1 "\n" line-2)))
  892. (if (not (eq ein:worksheet-enable-undo 'full))
  893. (should-error (undo))
  894. (undo)
  895. (should (equal (buffer-string) "
  896. In [ ]:
  897. second line
  898. In [ ]:
  899. In [ ]:
  900. ")))
  901. (when (eq ein:worksheet-enable-undo 'yes)
  902. ;; FIXME: `undo' should work...
  903. (should-error (undo-more 1)))
  904. (when (eq ein:worksheet-enable-undo 'full)
  905. (undo)
  906. ;; FIXME: Known bug... What should the result be?
  907. (should-error (should (equal (buffer-string) "
  908. In [ ]:
  909. In [ ]:
  910. In [ ]:
  911. ")))))))
  912. (defun eintest:notebook-undo-after-execution-1-cell ()
  913. (with-current-buffer (ein:testing-notebook-make-empty)
  914. (call-interactively #'ein:worksheet-insert-cell-below)
  915. (let* ((text "print 'Hello World'")
  916. (output-text "Hello World\n")
  917. (cell (ein:worksheet-get-current-cell))
  918. (kernel (ein:$notebook-kernel ein:%notebook%))
  919. (msg-id "DUMMY-MSG-ID")
  920. (callbacks (ein:cell-make-callbacks cell))
  921. (check-output
  922. (lambda ()
  923. (eintest:cell-check-output cell output-text))))
  924. (eintest:notebook-check-kernel-and-codecell kernel cell)
  925. ;; Execute
  926. (insert text)
  927. (undo-boundary)
  928. (eintest:notebook-fake-execution kernel text msg-id callbacks)
  929. (ein:kernel-set-callbacks-for-msg kernel msg-id callbacks)
  930. ;; Stream output
  931. (eintest:kernel-fake-stream kernel msg-id output-text)
  932. (funcall check-output)
  933. ;; Undo
  934. (should (equal (ein:cell-get-text cell) text))
  935. (if (eq ein:worksheet-enable-undo 'full)
  936. (undo)
  937. (should-error (undo)))
  938. (when (eq ein:worksheet-enable-undo 'full)
  939. (should (equal (ein:cell-get-text cell) ""))
  940. ;; FIXME: Known bug. (it must succeed.)
  941. (should-error (funcall check-output))))))
  942. (defun eintest:notebook-undo-after-execution-2-cells ()
  943. (with-current-buffer (ein:testing-notebook-make-empty)
  944. (call-interactively #'ein:worksheet-insert-cell-below)
  945. (call-interactively #'ein:worksheet-insert-cell-above)
  946. (let* ((text "print 'Hello World\\n' * 10")
  947. (next-text "something")
  948. (output-text
  949. (apply #'concat (loop repeat 10 collect "Hello World\n")))
  950. (cell (ein:worksheet-get-current-cell))
  951. (next-cell (ein:cell-next cell))
  952. (kernel (ein:$notebook-kernel ein:%notebook%))
  953. (msg-id "DUMMY-MSG-ID")
  954. (callbacks (ein:cell-make-callbacks cell))
  955. (check-output
  956. (lambda ()
  957. (eintest:cell-check-output cell output-text))))
  958. (eintest:notebook-check-kernel-and-codecell kernel cell)
  959. ;; Execute
  960. (insert text)
  961. (undo-boundary)
  962. (let ((pos (point)))
  963. ;; Do not use `save-excursion' because it does not record undo.
  964. (call-interactively #'ein:worksheet-goto-next-input)
  965. (insert next-text)
  966. (undo-boundary)
  967. (goto-char pos))
  968. (eintest:notebook-fake-execution kernel text msg-id callbacks)
  969. (ein:kernel-set-callbacks-for-msg kernel msg-id callbacks)
  970. ;; Stream output
  971. (eintest:kernel-fake-stream kernel msg-id output-text)
  972. (funcall check-output)
  973. ;; Undo
  974. (should (equal (ein:cell-get-text cell) text))
  975. (should (equal (ein:cell-get-text next-cell) next-text))
  976. (if (eq ein:worksheet-enable-undo 'full)
  977. (undo)
  978. (should-error (undo)))
  979. (when (eq ein:worksheet-enable-undo 'full)
  980. (should (equal (ein:cell-get-text cell) text))
  981. ;; FIXME: Known bug. (these two must succeed.)
  982. (should-error (should (equal (ein:cell-get-text next-cell) "")))
  983. (should-error (funcall check-output))))))
  984. (defmacro eintest:notebook-undo-make-tests (name)
  985. "Define three tests ein:NANE/no, ein:NANE/yes and ein:NANE/full
  986. from a function named eintest:NAME where `no'/`yes'/`full' is the
  987. value of `ein:worksheet-enable-undo'."
  988. (let ((func (intern (format "eintest:%s" name)))
  989. (test/no (intern (format "ein:%s/no" name)))
  990. (test/yes (intern (format "ein:%s/yes" name)))
  991. (test/full (intern (format "ein:%s/full" name))))
  992. `(progn
  993. (ert-deftest ,test/no ()
  994. (let ((ein:worksheet-enable-undo 'no))
  995. (,func)))
  996. (ert-deftest ,test/yes ()
  997. (let ((ein:worksheet-enable-undo 'yes))
  998. (,func)))
  999. (ert-deftest ,test/full ()
  1000. (let ((ein:worksheet-enable-undo 'full))
  1001. (,func))))))
  1002. (eintest:notebook-undo-make-tests notebook-undo-after-insert-above)
  1003. (eintest:notebook-undo-make-tests notebook-undo-after-split)
  1004. (eintest:notebook-undo-make-tests notebook-undo-after-merge)
  1005. (eintest:notebook-undo-make-tests notebook-undo-after-execution-1-cell)
  1006. (eintest:notebook-undo-make-tests notebook-undo-after-execution-2-cells)
  1007. (ert-deftest ein:notebook-undo-via-events ()
  1008. (with-current-buffer (ein:testing-notebook-make-empty)
  1009. (call-interactively #'ein:worksheet-insert-cell-below)
  1010. (loop with events = (ein:$notebook-events ein:%notebook%)
  1011. for ein:worksheet-enable-undo in '(no yes full) do
  1012. (let ((buffer-undo-list '(dummy))
  1013. (cell (ein:worksheet-get-current-cell)))
  1014. (with-temp-buffer
  1015. (should-not (equal buffer-undo-list '(dummy)))
  1016. (ein:events-trigger events 'maybe_reset_undo.Worksheet cell))
  1017. (if (eq ein:worksheet-enable-undo 'yes)
  1018. (should (equal buffer-undo-list nil))
  1019. (should (equal buffer-undo-list '(dummy))))))))
  1020. ;; Generic getter
  1021. (ert-deftest ein:get-url-or-port--notebook ()
  1022. (with-current-buffer (ein:testing-notebook-make-empty)
  1023. (should (equal (ein:get-url-or-port) "DUMMY-URL"))))
  1024. (ert-deftest ein:get-notebook--notebook ()
  1025. (with-current-buffer (ein:testing-notebook-make-empty)
  1026. (should (eq (ein:get-notebook) ein:%notebook%))))
  1027. (ert-deftest ein:get-kernel--notebook ()
  1028. (with-current-buffer (ein:testing-notebook-make-empty)
  1029. (let ((kernel (ein:$notebook-kernel ein:%notebook%)))
  1030. (should (ein:$kernel-p kernel))
  1031. (should (eq (ein:get-kernel) kernel)))))
  1032. (ert-deftest ein:get-cell-at-point--notebook ()
  1033. (with-current-buffer (ein:testing-notebook-make-empty)
  1034. ;; FIXME: write test with non-empty worksheet
  1035. (should-not (ein:get-cell-at-point))))
  1036. (ert-deftest ein:get-traceback-data--notebook ()
  1037. (with-current-buffer (ein:testing-notebook-make-empty)
  1038. ;; FIXME: write test with non-empty TB
  1039. (should-not (ein:get-traceback-data))))
  1040. ;; Notebook mode
  1041. (ert-deftest ein:notebook-ask-before-kill-emacs-simple ()
  1042. (let ((ein:notebook--opened-map (make-hash-table :test 'equal)))
  1043. (should (ein:notebook-ask-before-kill-emacs))
  1044. (with-current-buffer
  1045. (eintest:notebook-enable-mode
  1046. (ein:testing-notebook-make-empty "Modified Notebook" "NOTEBOOK-ID-1"))
  1047. (call-interactively #'ein:worksheet-insert-cell-below)
  1048. (should (ein:notebook-modified-p)))
  1049. (with-current-buffer
  1050. (eintest:notebook-enable-mode
  1051. (ein:testing-notebook-make-empty "Saved Notebook" "NOTEBOOK-ID-2"))
  1052. (ein:notebook-save-notebook-success ein:%notebook%)
  1053. (should-not (ein:notebook-modified-p)))
  1054. (flet ((y-or-n-p (&rest ignore) t)
  1055. (ein:notebook-del (&rest ignore)))
  1056. (kill-buffer
  1057. (eintest:notebook-enable-mode
  1058. (ein:testing-notebook-make-empty "Killed Notebook" "NOTEBOOK-ID-3"))))
  1059. (should (gethash '("DUMMY-URL" "NOTEBOOK-ID-1") ein:notebook--opened-map))
  1060. (should (gethash '("DUMMY-URL" "NOTEBOOK-ID-2") ein:notebook--opened-map))
  1061. (should (gethash '("DUMMY-URL" "NOTEBOOK-ID-3") ein:notebook--opened-map))
  1062. (should (= (hash-table-count ein:notebook--opened-map) 3))
  1063. (mocker-let ((y-or-n-p
  1064. (prompt)
  1065. ((:input '("You have 1 unsaved notebook(s). Discard changes?")
  1066. :output t))))
  1067. (should (ein:notebook-ask-before-kill-emacs)))))
  1068. ;;; Buffer and kill hooks
  1069. (ert-deftest ein:notebook-ask-before-kill-buffer/no-ein-buffer ()
  1070. (with-temp-buffer
  1071. (mocker-let ((y-or-n-p (prompt) ()))
  1072. (should (ein:notebook-ask-before-kill-buffer)))))
  1073. (ert-deftest ein:notebook-ask-before-kill-buffer/new-notebook ()
  1074. (with-current-buffer (ein:testing-make-notebook-with-outputs '(nil))
  1075. (mocker-let ((y-or-n-p (prompt) ()))
  1076. (should (ein:notebook-ask-before-kill-buffer)))))
  1077. (ert-deftest ein:notebook-ask-before-kill-buffer/modified-notebook ()
  1078. (with-current-buffer (ein:testing-make-notebook-with-outputs '(nil))
  1079. (call-interactively #'ein:worksheet-insert-cell-below)
  1080. (mocker-let ((y-or-n-p
  1081. (prompt)
  1082. ((:input '("You have unsaved changes. Discard changes?")
  1083. :output t))))
  1084. (should (ein:notebook-ask-before-kill-buffer)))))
  1085. (ert-deftest ein:notebook-ask-before-kill-buffer/modified-scratchsheet ()
  1086. (with-current-buffer (ein:testing-make-notebook-with-outputs '(nil))
  1087. (with-current-buffer (ein:worksheet-buffer
  1088. (ein:notebook-scratchsheet-open ein:%notebook%))
  1089. (should (= (ein:worksheet-ncells ein:%worksheet%) 1))
  1090. (call-interactively #'ein:worksheet-insert-cell-below)
  1091. (should (= (ein:worksheet-ncells ein:%worksheet%) 2))
  1092. (should (ein:worksheet-modified-p ein:%worksheet%))
  1093. (mocker-let ((y-or-n-p (prompt) ()))
  1094. (should (ein:notebook-ask-before-kill-buffer))))
  1095. (should-not (ein:worksheet-modified-p ein:%worksheet%))
  1096. (mocker-let ((y-or-n-p (prompt) ()))
  1097. (should (ein:notebook-ask-before-kill-buffer)))))
  1098. ;; Misc unit tests
  1099. (ert-deftest ein:notebook-test-notebook-name-simple ()
  1100. (should-not (ein:notebook-test-notebook-name nil))
  1101. (should-not (ein:notebook-test-notebook-name ""))
  1102. (should-not (ein:notebook-test-notebook-name "/"))
  1103. (should-not (ein:notebook-test-notebook-name "\\"))
  1104. (should-not (ein:notebook-test-notebook-name ":"))
  1105. (should-not (ein:notebook-test-notebook-name "a/b"))
  1106. (should-not (ein:notebook-test-notebook-name "a\\b"))
  1107. (should-not (ein:notebook-test-notebook-name "a:b"))
  1108. (should (ein:notebook-test-notebook-name "This is a OK notebook name")))
  1109. (defun* eintest:notebook--check-nbformat (&optional orig_nbformat
  1110. orig_nbformat_minor
  1111. nbformat
  1112. nbformat_minor
  1113. &key data)
  1114. (let ((data (or data
  1115. (list :nbformat nbformat :nbformat_minor nbformat_minor
  1116. :orig_nbformat orig_nbformat
  1117. :orig_nbformat_minor orig_nbformat_minor))))
  1118. (ein:notebook--check-nbformat data)))
  1119. (ert-deftest ein:notebook--check-nbformat-nothing ()
  1120. (mocker-let ((ein:display-warning (message) ()))
  1121. (eintest:notebook--check-nbformat)
  1122. (eintest:notebook--check-nbformat :data nil)
  1123. (eintest:notebook--check-nbformat 2 0)
  1124. (eintest:notebook--check-nbformat 2 0 2)
  1125. (eintest:notebook--check-nbformat 2 0 2 0)))
  1126. (defmacro ein:notebook--check-nbformat-assert-match (regexp &rest args)
  1127. `(mocker-let ((ein:display-warning
  1128. (message)
  1129. ((:input-matcher
  1130. (lambda (m) (string-match ,regexp m))))))
  1131. (eintest:notebook--check-nbformat ,@args)))
  1132. (ert-deftest ein:notebook--check-nbformat-warn-major ()
  1133. (ein:notebook--check-nbformat-assert-match "v2 -> v3" 2 nil 3)
  1134. (ein:notebook--check-nbformat-assert-match "v2 -> v3" 2 0 3 0))
  1135. (ert-deftest ein:notebook--check-nbformat-warn-minor ()
  1136. (ein:notebook--check-nbformat-assert-match
  1137. "version v2\\.1, [^\\.]* up to v2.0" 2 1 2 0))