1 ;;; rust-mode-tests.el --- ERT tests for rust-mode.el
7 (setq rust-test-fill-column 32)
9 (defun rust-compare-code-after-manip (original point-pos manip-func expected got)
12 (defun rust-test-explain-bad-manip (original point-pos manip-func expected got)
13 (if (equal expected got)
16 ;; The (goto-char) and (insert) business here is just for
17 ;; convenience--after an error, you can copy-paste that into emacs eval to
18 ;; insert the bare strings into a buffer
19 "Rust code was manipulated wrong after:"
21 `(goto-char ,point-pos)
22 'expected `(insert ,expected)
24 (loop for i from 0 to (max (length original) (length expected))
25 for oi = (if (< i (length got)) (elt got i))
26 for ei = (if (< i (length expected)) (elt expected i))
28 finally return `(first-difference-at
30 expected ,(char-to-string ei)
31 got ,(char-to-string oi))))))
32 (put 'rust-compare-code-after-manip 'ert-explainer
33 'rust-test-explain-bad-manip)
35 (defun rust-test-manip-code (original point-pos manip-func expected)
41 (should (rust-compare-code-after-manip
42 original point-pos manip-func expected (buffer-string)))))
44 (defun test-fill-paragraph (unfilled expected &optional start-pos end-pos)
45 "We're going to run through many scenarios here--the point should be able to be anywhere from the start-pos (defaults to 1) through end-pos (defaults to the length of what was passed in) and (fill-paragraph) should return the same result.
47 Also, the result should be the same regardless of whether the code is at the beginning or end of the file. (If you're not careful, that can make a difference.) So we test each position given above with the passed code at the beginning, the end, neither and both. So we do this a total of (end-pos - start-pos)*4 times. Oy."
48 (let* ((start-pos (or start-pos 1))
49 (end-pos (or end-pos (length unfilled)))
51 (padding-len (length padding)))
53 for pad-at-beginning from 0 to 1
54 do (loop for pad-at-end from 0 to 1
55 with padding-beginning = (if (= 0 pad-at-beginning) "" padding)
56 with padding-end = (if (= 0 pad-at-end) "" padding)
57 with padding-adjust = (* padding-len pad-at-beginning)
58 with padding-beginning = (if (= 0 pad-at-beginning) "" padding)
59 with padding-end = (if (= 0 pad-at-end) "" padding)
60 ;; If we're adding space to the beginning, and our start position
61 ;; is at the very beginning, we want to test within the added space.
62 ;; Otherwise adjust the start and end for the beginning padding.
63 with start-pos = (if (= 1 start-pos) 1 (+ padding-adjust start-pos))
64 with end-pos = (+ end-pos padding-adjust)
65 do (loop for pos from start-pos to end-pos
66 do (rust-test-manip-code
67 (concat padding-beginning unfilled padding-end)
70 (let ((fill-column rust-test-fill-column))
72 (concat padding-beginning expected padding-end)))))))
74 (ert-deftest fill-paragraph-top-level-multi-line-style-doc-comment-second-line ()
77 * This is a very very very very very very very long string
80 * This is a very very very very
81 * very very very long string
85 (ert-deftest fill-paragraph-top-level-multi-line-style-doc-comment-first-line ()
87 "/** This is a very very very very very very very long string
89 "/** This is a very very very
90 * very very very very long
94 (ert-deftest fill-paragraph-multi-paragraph-multi-line-style-doc-comment ()
96 ((multi-paragraph-unfilled
98 * This is the first really really really really really really really long paragraph
100 * This is the second really really really really really really long paragraph
103 multi-paragraph-unfilled
105 * This is the first really
106 * really really really really
107 * really really long paragraph
109 * This is the second really really really really really really long paragraph
113 multi-paragraph-unfilled
115 * This is the first really really really really really really really long paragraph
117 * This is the second really
118 * really really really really
119 * really long paragraph
123 (ert-deftest fill-paragraph-multi-paragraph-single-line-style-doc-comment ()
125 ((multi-paragraph-unfilled
126 "/// This is the first really really really really really really really long paragraph
128 /// This is the second really really really really really really long paragraph"))
130 multi-paragraph-unfilled
131 "/// This is the first really
132 /// really really really really
133 /// really really long paragraph
135 /// This is the second really really really really really really long paragraph"
138 multi-paragraph-unfilled
139 "/// This is the first really really really really really really really long paragraph
141 /// This is the second really
142 /// really really really really
143 /// really long paragraph"
146 (ert-deftest fill-paragraph-multi-paragraph-single-line-style-indented ()
148 " // This is the first really really really really really really really long paragraph
150 // This is the second really really really really really really long paragraph"
151 " // This is the first really
152 // really really really
153 // really really really
156 // This is the second really really really really really really long paragraph" 1 89))
158 (ert-deftest fill-paragraph-multi-line-style-inner-doc-comment ()
160 "/*! This is a very very very very very very very long string
162 "/*! This is a very very very
163 * very very very very long
167 (ert-deftest fill-paragraph-single-line-style-inner-doc-comment ()
169 "//! This is a very very very very very very very long string"
170 "//! This is a very very very
171 //! very very very very long
174 (ert-deftest fill-paragraph-prefixless-multi-line-doc-comment ()
177 This is my summary. Blah blah blah blah blah. Dilly dally dilly dally dilly dally doo.
179 This is some more text. Fee fie fo fum. Humpty dumpty sat on a wall.
182 This is my summary. Blah blah
183 blah blah blah. Dilly dally
184 dilly dally dilly dally doo.
186 This is some more text. Fee fie fo fum. Humpty dumpty sat on a wall.
189 (ert-deftest fill-paragraph-with-no-space-after-star-prefix ()
192 *This is a very very very very very very very long string
195 *This is a very very very very
196 *very very very long string
199 (ert-deftest fill-paragraph-single-line-style-with-code-before ()
202 /// This is my comment. This is more of my comment. This is even more."
204 /// This is my comment. This is
205 /// more of my comment. This is
208 (ert-deftest fill-paragraph-single-line-style-with-code-after ()
210 "/// This is my comment. This is more of my comment. This is even more.
212 "/// This is my comment. This is
213 /// more of my comment. This is
217 (ert-deftest fill-paragraph-single-line-style-code-before-and-after ()
220 /// This is my comment. This is more of my comment. This is even more.
223 /// This is my comment. This is
224 /// more of my comment. This is
226 fn bar() { }" 14 67))
228 (defun test-auto-fill (initial position inserted expected)
229 (rust-test-manip-code
235 (let ((fill-column rust-test-fill-column))
239 (syntax-ppss-flush-cache 1)
240 (funcall auto-fill-function)))
244 (ert-deftest auto-fill-multi-line-doc-comment ()
250 "This is a very very very very very very very long string"
252 * This is a very very very very
253 * very very very long string
256 (ert-deftest auto-fill-single-line-doc-comment ()
258 "/// This is the first really
259 /// really really really really
260 /// really really long paragraph
264 "This is the second really really really really really really long paragraph"
265 "/// This is the first really
266 /// really really really really
267 /// really really long paragraph
269 /// This is the second really
270 /// really really really really
271 /// really long paragraph"
274 (ert-deftest auto-fill-multi-line-prefixless ()
280 "This is a very very very very very very very long string"
282 This is a very very very very
283 very very very long string
287 (defun test-indent (indented)
288 (let ((deindented (replace-regexp-in-string "^[[:blank:]]*" " " indented)))
289 (rust-test-manip-code
292 (lambda () (indent-region 1 (buffer-size)))
296 (ert-deftest indent-struct-fields-aligned ()
299 struct Foo { bar: int,
306 (ert-deftest indent-doc-comments ()
310 * This is a doc comment
318 * this is a nested doc comment
324 (ert-deftest indent-inside-braces ()
327 // struct fields out one level:
334 fn bar(x:Box<int>) { // comment here should not affect the next indent
339 (ert-deftest indent-top-level ()
342 // Everything here is at the top level and should not be indented
346 pub static bar = Quux{a: b()}
353 (ert-deftest indent-params-no-align ()
356 // Indent out one level because no params appear on the first line
367 (ert-deftest indent-params-align ()
370 // Align the second line of params to the first
379 fn baz( a:int, // shoudl work with a comment here
385 (ert-deftest indent-square-bracket-alignment ()
388 fn args_on_the_next_line( // with a comment
395 let bbbbbbb = [1, 2, 3,
397 let ccc = [ 10, 9, 8,
402 (ert-deftest indent-nested-fns ()
405 fn nexted_fns(a: fn(b:int,
416 (ert-deftest indent-multi-line-expr ()
428 (ert-deftest indent-match ()
440 (ert-deftest indent-match-multiline-pattern ()
455 (ert-deftest indent-indented-match ()
472 (ert-deftest indent-curly-braces-within-parens ()
478 only_one_indent_here();
485 (ert-deftest indent-weirdly-indented-block ()
486 (rust-test-manip-code
490 this_block_is_over_to_the_left_for_some_reason();
496 #'indent-for-tab-command
500 this_block_is_over_to_the_left_for_some_reason();
507 (ert-deftest indent-multi-line-attrib ()
514 fn function_with_multiline_attribute() {}
519 ;; Make sure that in effort to cover match patterns we don't mistreat || or expressions
520 (ert-deftest indent-nonmatch-or-expression ()
530 (setq rust-test-motion-string
532 fn fn1(arg: int) -> bool {
538 fn fn2(arg: int) -> bool {
544 pub fn fn3(arg: int) -> bool {
554 rust-test-region-string rust-test-motion-string
555 rust-test-indent-motion-string
557 fn blank_line(arg:int) -> bool {
561 fn indenting_closing_brace() {
566 fn indenting_middle_of_line() {
574 fn indented_already() {
576 // The previous line already has its spaces
580 ;; Symbol -> (line column)
581 rust-test-positions-alist '((start-of-fn1 (2 0))
582 (start-of-fn1-middle-of-line (2 15))
583 (middle-of-fn1 (3 7))
585 (between-fn1-fn2 (7 0))
587 (middle-of-fn2 (10 4))
588 (before-start-of-fn1 (1 0))
589 (after-end-of-fn2 (13 0))
590 (beginning-of-fn3 (14 0))
591 (middle-of-fn3 (16 4))
592 (middle-of-struct (21 10))
593 (before-start-of-struct (19 0))
594 (after-end-of-struct (23 0))
595 (blank-line-indent-start (3 0))
596 (blank-line-indent-target (3 4))
597 (closing-brace-indent-start (8 1))
598 (closing-brace-indent-target (8 5))
599 (middle-push-indent-start (13 2))
600 (middle-push-indent-target (13 9))
601 (after-whitespace-indent-start (13 1))
602 (after-whitespace-indent-target (13 8))
603 (middle-pull-indent-start (15 19))
604 (middle-pull-indent-target (15 12))
605 (blank-line-indented-already-bol-start (20 0))
606 (blank-line-indented-already-bol-target (20 4))
607 (blank-line-indented-already-middle-start (20 2))
608 (blank-line-indented-already-middle-target (20 4))
609 (nonblank-line-indented-already-bol-start (21 0))
610 (nonblank-line-indented-already-bol-target (21 4))
611 (nonblank-line-indented-already-middle-start (21 2))
612 (nonblank-line-indented-already-middle-target (21 4))))
614 (defun rust-get-buffer-pos (pos-symbol)
615 "Get buffer position from POS-SYMBOL.
617 POS-SYMBOL is a symbol found in `rust-test-positions-alist'.
618 Convert the line-column information from that list into a buffer position value."
620 (pcase-let ((`(,line ,column) (cadr (assoc pos-symbol rust-test-positions-alist))))
623 (move-to-column column)
626 ;;; TODO: Maybe add an ERT explainer function (something that shows the
627 ;;; surrounding code of the final point, not just the position).
628 (defun rust-test-motion (source-code init-pos final-pos manip-func &optional &rest args)
629 "Test that MANIP-FUNC moves point from INIT-POS to FINAL-POS.
631 If ARGS are provided, send them to MANIP-FUNC.
633 INIT-POS, FINAL-POS are position symbols found in `rust-test-positions-alist'."
637 (goto-char (rust-get-buffer-pos init-pos))
638 (apply manip-func args)
639 (should (equal (point) (rust-get-buffer-pos final-pos)))))
641 (defun rust-test-region (source-code init-pos reg-beg reg-end manip-func &optional &rest args)
642 "Test that MANIP-FUNC marks region from REG-BEG to REG-END.
644 INIT-POS is the initial position of point.
645 If ARGS are provided, send them to MANIP-FUNC.
646 All positions are position symbols found in `rust-test-positions-alist'."
650 (goto-char (rust-get-buffer-pos init-pos))
651 (apply manip-func args)
652 (should (equal (list (region-beginning) (region-end))
653 (list (rust-get-buffer-pos reg-beg)
654 (rust-get-buffer-pos reg-end))))))
656 (ert-deftest rust-beginning-of-defun-from-middle-of-fn ()
658 rust-test-motion-string
661 #'beginning-of-defun))
663 (ert-deftest rust-beginning-of-defun-from-end ()
665 rust-test-motion-string
668 #'beginning-of-defun))
670 (ert-deftest rust-beginning-of-defun-before-open-brace ()
672 rust-test-motion-string
673 'start-of-fn1-middle-of-line
675 #'beginning-of-defun))
677 (ert-deftest rust-beginning-of-defun-between-fns ()
679 rust-test-motion-string
682 #'beginning-of-defun))
684 (ert-deftest rust-beginning-of-defun-with-arg ()
686 rust-test-motion-string
689 #'beginning-of-defun 2))
691 (ert-deftest rust-beginning-of-defun-with-negative-arg ()
693 rust-test-motion-string
696 #'beginning-of-defun -2))
698 (ert-deftest rust-beginning-of-defun-pub-fn ()
700 rust-test-motion-string
703 #'beginning-of-defun))
705 (ert-deftest rust-end-of-defun-from-middle-of-fn ()
707 rust-test-motion-string
712 (ert-deftest rust-end-of-defun-from-beg ()
714 rust-test-motion-string
719 (ert-deftest rust-end-of-defun-before-open-brace ()
721 rust-test-motion-string
722 'start-of-fn1-middle-of-line
726 (ert-deftest rust-end-of-defun-between-fns ()
728 rust-test-motion-string
733 (ert-deftest rust-end-of-defun-with-arg ()
735 rust-test-motion-string
740 (ert-deftest rust-end-of-defun-with-negative-arg ()
742 rust-test-motion-string
747 (ert-deftest rust-mark-defun-from-middle-of-fn ()
749 rust-test-region-string
751 'between-fn1-fn2 'after-end-of-fn2
754 (ert-deftest rust-mark-defun-from-end ()
756 rust-test-region-string
758 'before-start-of-fn1 'between-fn1-fn2
761 (ert-deftest rust-mark-defun-start-of-defun ()
763 rust-test-region-string
765 'between-fn1-fn2 'after-end-of-fn2
768 (ert-deftest rust-mark-defun-from-middle-of-struct ()
770 rust-test-region-string
772 'before-start-of-struct 'after-end-of-struct
775 (ert-deftest indent-line-blank-line-motion ()
777 rust-test-indent-motion-string
778 'blank-line-indent-start
779 'blank-line-indent-target
780 #'indent-for-tab-command))
782 (ert-deftest indent-line-closing-brace-motion ()
784 rust-test-indent-motion-string
785 'closing-brace-indent-start
786 'closing-brace-indent-target
787 #'indent-for-tab-command))
789 (ert-deftest indent-line-middle-push-motion ()
791 rust-test-indent-motion-string
792 'middle-push-indent-start
793 'middle-push-indent-target
794 #'indent-for-tab-command))
796 (ert-deftest indent-line-after-whitespace-motion ()
798 rust-test-indent-motion-string
799 'after-whitespace-indent-start
800 'after-whitespace-indent-target
801 #'indent-for-tab-command))
803 (ert-deftest indent-line-middle-pull-motion ()
805 rust-test-indent-motion-string
806 'middle-pull-indent-start
807 'middle-pull-indent-target
808 #'indent-for-tab-command))
810 (ert-deftest indent-line-blank-line-indented-already-bol ()
812 rust-test-indent-motion-string
813 'blank-line-indented-already-bol-start
814 'blank-line-indented-already-bol-target
815 #'indent-for-tab-command))
817 (ert-deftest indent-line-blank-line-indented-already-middle ()
819 rust-test-indent-motion-string
820 'blank-line-indented-already-middle-start
821 'blank-line-indented-already-middle-target
822 #'indent-for-tab-command))
824 (ert-deftest indent-line-nonblank-line-indented-already-bol ()
826 rust-test-indent-motion-string
827 'nonblank-line-indented-already-bol-start
828 'nonblank-line-indented-already-bol-target
829 #'indent-for-tab-command))
831 (ert-deftest indent-line-nonblank-line-indented-already-middle ()
833 rust-test-indent-motion-string
834 'nonblank-line-indented-already-middle-start
835 'nonblank-line-indented-already-middle-target
836 #'indent-for-tab-command))
838 (defun rust-test-fontify-string (str)
842 (font-lock-fontify-buffer)
845 (defun rust-test-group-str-by-face (str)
846 "Fontify `STR' in rust-mode and group it by face, returning a
847 list of substrings of `STR' each followed by its face."
848 (cl-loop with fontified = (rust-test-fontify-string str)
849 for start = 0 then end
851 for end = (next-single-property-change start 'face fontified)
852 for prop = (get-text-property start 'face fontified)
853 for text = (substring-no-properties fontified start end)
855 append (list text prop)))
857 (defun rust-test-font-lock (source face-groups)
858 "Test that `SOURCE' fontifies to the expected `FACE-GROUPS'"
859 (should (equal (rust-test-group-str-by-face source)
862 (ert-deftest font-lock-attribute-simple ()
865 '("#[foo]" font-lock-preprocessor-face)))
867 (ert-deftest font-lock-attribute-inner ()
870 '("#![foo]" font-lock-preprocessor-face)))
872 (ert-deftest font-lock-attribute-key-value ()
875 '("#[foo = " font-lock-preprocessor-face
876 "\"bar\"" font-lock-string-face
877 "]" font-lock-preprocessor-face)))
879 (ert-deftest font-lock-attribute-around-comment ()
882 '("#[foo " font-lock-preprocessor-face
883 "/* " font-lock-comment-delimiter-face
884 "bar */" font-lock-comment-face
885 "]" font-lock-preprocessor-face)))
887 (ert-deftest font-lock-attribute-inside-string ()
890 '("\"#[foo]\"" font-lock-string-face)))
892 (ert-deftest font-lock-attribute-inside-comment ()
895 '("/* " font-lock-comment-delimiter-face
896 "#[foo] */" font-lock-comment-face)))