]> git.lizzy.rs Git - rust.git/blob - src/etc/emacs/rust-mode-tests.el
Removed some unnecessary RefCells from resolve
[rust.git] / src / etc / emacs / rust-mode-tests.el
1 ;;; rust-mode-tests.el --- ERT tests for rust-mode.el
2
3 (require 'rust-mode)
4 (require 'ert)
5 (require 'cl)
6
7 (setq rust-test-fill-column 32)
8
9 (defun rust-compare-code-after-manip (original point-pos manip-func expected got)
10   (equal expected got))
11
12 (defun rust-test-explain-bad-manip (original point-pos manip-func expected got)
13   (if (equal expected got)
14       nil
15     (list
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:"
20      `(insert ,original)
21      `(goto-char ,point-pos)
22      'expected `(insert ,expected)
23      'got `(insert ,got)
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))
27            while (equal oi ei)
28            finally return `(first-difference-at
29                             (goto-char ,(+ 1 i))
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)
34
35 (defun rust-test-manip-code (original point-pos manip-func expected)
36   (with-temp-buffer
37     (rust-mode)
38     (insert original)
39     (goto-char point-pos)
40     (funcall manip-func)
41     (should (rust-compare-code-after-manip
42              original point-pos manip-func expected (buffer-string)))))
43
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.
46
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)))
50          (padding "\n     \n")
51          (padding-len (length padding)))
52     (loop
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)
68                            pos
69                            (lambda ()
70                              (let ((fill-column rust-test-fill-column))
71                                (fill-paragraph)))
72                            (concat padding-beginning expected padding-end)))))))
73
74 (ert-deftest fill-paragraph-top-level-multi-line-style-doc-comment-second-line ()
75   (test-fill-paragraph
76    "/**
77  * This is a very very very very very very very long string
78  */"
79    "/**
80  * This is a very very very very
81  * very very very long string
82  */"))
83
84
85 (ert-deftest fill-paragraph-top-level-multi-line-style-doc-comment-first-line ()
86   (test-fill-paragraph
87    "/** This is a very very very very very very very long string
88  */"
89    "/** This is a very very very
90  * very very very very long
91  * string
92  */"))
93
94 (ert-deftest fill-paragraph-multi-paragraph-multi-line-style-doc-comment ()
95   (let
96       ((multi-paragraph-unfilled
97         "/**
98  * This is the first really really really really really really really long paragraph
99  *
100  * This is the second really really really really really really long paragraph
101  */"))
102     (test-fill-paragraph
103      multi-paragraph-unfilled
104      "/**
105  * This is the first really
106  * really really really really
107  * really really long paragraph
108  *
109  * This is the second really really really really really really long paragraph
110  */"
111      1 89)
112     (test-fill-paragraph
113      multi-paragraph-unfilled
114      "/**
115  * This is the first really really really really really really really long paragraph
116  *
117  * This is the second really
118  * really really really really
119  * really long paragraph
120  */"
121      90)))
122
123 (ert-deftest fill-paragraph-multi-paragraph-single-line-style-doc-comment ()
124   (let
125       ((multi-paragraph-unfilled
126         "/// This is the first really really really really really really really long paragraph
127 ///
128 /// This is the second really really really really really really long paragraph"))
129     (test-fill-paragraph
130      multi-paragraph-unfilled
131      "/// This is the first really
132 /// really really really really
133 /// really really long paragraph
134 ///
135 /// This is the second really really really really really really long paragraph"
136      1 86)
137     (test-fill-paragraph
138      multi-paragraph-unfilled
139      "/// This is the first really really really really really really really long paragraph
140 ///
141 /// This is the second really
142 /// really really really really
143 /// really long paragraph"
144      87)))
145
146 (ert-deftest fill-paragraph-multi-paragraph-single-line-style-indented ()
147   (test-fill-paragraph
148    "     // This is the first really really really really really really really long paragraph
149      //
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
154      // long paragraph
155      //
156      // This is the second really really really really really really long paragraph" 1 89))
157
158 (ert-deftest fill-paragraph-multi-line-style-inner-doc-comment ()
159   (test-fill-paragraph
160    "/*! This is a very very very very very very very long string
161  */"
162    "/*! This is a very very very
163  * very very very very long
164  * string
165  */"))
166
167 (ert-deftest fill-paragraph-single-line-style-inner-doc-comment ()
168   (test-fill-paragraph
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
172 //! string"))
173
174 (ert-deftest fill-paragraph-prefixless-multi-line-doc-comment ()
175   (test-fill-paragraph
176    "/**
177 This is my summary. Blah blah blah blah blah. Dilly dally dilly dally dilly dally doo.
178
179 This is some more text.  Fee fie fo fum.  Humpty dumpty sat on a wall.
180 */"
181    "/**
182 This is my summary. Blah blah
183 blah blah blah. Dilly dally
184 dilly dally dilly dally doo.
185
186 This is some more text.  Fee fie fo fum.  Humpty dumpty sat on a wall.
187 */" 4 90))
188
189 (ert-deftest fill-paragraph-with-no-space-after-star-prefix ()
190   (test-fill-paragraph
191    "/**
192  *This is a very very very very very very very long string
193  */"
194    "/**
195  *This is a very very very very
196  *very very very long string
197  */"))
198
199 (ert-deftest fill-paragraph-single-line-style-with-code-before ()
200   (test-fill-paragraph
201    "fn foo() { }
202 /// This is my comment.  This is more of my comment.  This is even more."
203    "fn foo() { }
204 /// This is my comment.  This is
205 /// more of my comment.  This is
206 /// even more." 14))
207
208 (ert-deftest fill-paragraph-single-line-style-with-code-after ()
209   (test-fill-paragraph
210    "/// This is my comment.  This is more of my comment.  This is even more.
211 fn foo() { }"
212    "/// This is my comment.  This is
213 /// more of my comment.  This is
214 /// even more.
215 fn foo() { }" 1 73))
216
217 (ert-deftest fill-paragraph-single-line-style-code-before-and-after ()
218   (test-fill-paragraph
219    "fn foo() { }
220 /// This is my comment.  This is more of my comment.  This is even more.
221 fn bar() { }"
222    "fn foo() { }
223 /// This is my comment.  This is
224 /// more of my comment.  This is
225 /// even more.
226 fn bar() { }" 14 67))
227
228 (defun test-auto-fill (initial position inserted expected)
229   (rust-test-manip-code
230    initial
231    position
232    (lambda ()
233      (unwind-protect
234          (progn
235            (let ((fill-column rust-test-fill-column))
236              (auto-fill-mode)
237              (goto-char position)
238              (insert inserted)
239              (syntax-ppss-flush-cache 1)
240              (funcall auto-fill-function)))
241        (auto-fill-mode t)))
242    expected))
243
244 (ert-deftest auto-fill-multi-line-doc-comment ()
245   (test-auto-fill
246    "/**
247  *
248  */"
249    8
250    "This is a very very very very very very very long string"
251    "/**
252  * This is a very very very very
253  * very very very long string
254  */"))
255
256 (ert-deftest auto-fill-single-line-doc-comment ()
257   (test-auto-fill
258    "/// This is the first really
259 /// really really really really
260 /// really really long paragraph
261 ///
262 /// "
263    103
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
268 ///
269 /// This is the second really
270 /// really really really really
271 /// really long paragraph"
272    ))
273
274 (ert-deftest auto-fill-multi-line-prefixless ()
275   (test-auto-fill
276    "/*
277
278  */"
279    4
280    "This is a very very very very very very very long string"
281    "/*
282 This is a very very very very
283 very very very long string
284  */"
285    ))
286
287 (defun test-indent (indented)
288   (let ((deindented (replace-regexp-in-string "^[[:blank:]]*" "      " indented)))
289     (rust-test-manip-code
290      deindented
291      1
292      (lambda () (indent-region 1 (buffer-size)))
293      indented)))
294
295
296 (ert-deftest indent-struct-fields-aligned ()
297   (test-indent
298    "
299 struct Foo { bar: int,
300              baz: int }
301
302 struct Blah {x:int,
303              y:int,
304              z:String"))
305
306 (ert-deftest indent-doc-comments ()
307   (test-indent
308    "
309 /**
310  * This is a doc comment
311  *
312  */
313
314 /// So is this
315
316 fn foo() {
317     /*!
318      * this is a nested doc comment
319      */
320
321     //! And so is this
322 }"))
323
324 (ert-deftest indent-inside-braces ()
325   (test-indent
326    "
327 // struct fields out one level:
328 struct foo {
329     a:int,
330     // comments too
331     b:char
332 }
333
334 fn bar(x:Box<int>) {   // comment here should not affect the next indent
335     bla();
336     bla();
337 }"))
338
339 (ert-deftest indent-top-level ()
340   (test-indent
341    "
342 // Everything here is at the top level and should not be indented
343 #[attrib]
344 mod foo;
345
346 pub static bar = Quux{a: b()}
347
348 use foo::bar::baz;
349
350 fn foo() { }
351 "))
352
353 (ert-deftest indent-params-no-align ()
354   (test-indent
355    "
356 // Indent out one level because no params appear on the first line
357 fn xyzzy(
358     a:int,
359     b:char) { }
360
361 fn abcdef(
362     a:int,
363     b:char)
364     -> char
365 { }"))
366
367 (ert-deftest indent-params-align ()
368   (test-indent
369    "
370 // Align the second line of params to the first
371 fn foo(a:int,
372        b:char) { }
373
374 fn bar(   a:int,
375           b:char)
376           -> int
377 { }
378
379 fn baz(   a:int,  // shoudl work with a comment here
380           b:char)
381           -> int
382 { }
383 "))
384
385 (ert-deftest indent-square-bracket-alignment ()
386   (test-indent
387    "
388 fn args_on_the_next_line( // with a comment
389     a:int,
390     b:String) {
391     let aaaaaa = [
392         1,
393         2,
394         3];
395     let bbbbbbb = [1, 2, 3,
396                    4, 5, 6];
397     let ccc = [   10, 9, 8,
398                   7, 6, 5];
399 }
400 "))
401
402 (ert-deftest indent-nested-fns ()
403   (test-indent
404    "
405 fn nexted_fns(a: fn(b:int,
406                     c:char)
407                     -> int,
408               d: int)
409               -> uint
410 {
411     0
412 }
413 "
414    ))
415
416 (ert-deftest indent-multi-line-expr ()
417   (test-indent
418    "
419 fn foo()
420 {
421     x();
422     let a =
423         b();
424 }
425 "
426    ))
427
428 (ert-deftest indent-match ()
429   (test-indent
430    "
431 fn foo() {
432     match blah {
433         Pattern => stuff(),
434         _ => whatever
435     }
436 }
437 "
438    ))
439
440 (ert-deftest indent-match-multiline-pattern ()
441   (test-indent
442    "
443 fn foo() {
444     match blah {
445         Pattern |
446         Pattern2 => {
447             hello()
448         },
449         _ => whatever
450     }
451 }
452 "
453    ))
454
455 (ert-deftest indent-indented-match ()
456   (test-indent
457    "
458 fn foo() {
459     let x =
460         match blah {
461             Pattern |
462             Pattern2 => {
463                 hello()
464             },
465             _ => whatever
466         };
467     y();
468 }
469 "
470    ))
471
472 (ert-deftest indent-curly-braces-within-parens ()
473   (test-indent
474    "
475 fn foo() {
476     let x =
477         foo(bar(|x| {
478             only_one_indent_here();
479         }));
480     y();
481 }
482 "
483    ))
484
485 (ert-deftest indent-weirdly-indented-block ()
486   (rust-test-manip-code
487    "
488 fn foo() {
489  {
490 this_block_is_over_to_the_left_for_some_reason();
491  }
492
493 }
494 "
495    16
496    #'indent-for-tab-command
497    "
498 fn foo() {
499  {
500      this_block_is_over_to_the_left_for_some_reason();
501  }
502
503 }
504 "
505    ))
506
507 (ert-deftest indent-multi-line-attrib ()
508   (test-indent
509    "
510 #[attrib(
511     this,
512     that,
513     theotherthing)]
514 fn function_with_multiline_attribute() {}
515 "
516    ))
517
518
519 ;; Make sure that in effort to cover match patterns we don't mistreat || or expressions
520 (ert-deftest indent-nonmatch-or-expression ()
521   (test-indent
522    "
523 fn foo() {
524     let x = foo() ||
525         bar();
526 }
527 "
528    ))
529
530 (setq rust-test-motion-string
531       "
532 fn fn1(arg: int) -> bool {
533     let x = 5;
534     let y = b();
535     true
536 }
537
538 fn fn2(arg: int) -> bool {
539     let x = 5;
540     let y = b();
541     true
542 }
543
544 pub fn fn3(arg: int) -> bool {
545     let x = 5;
546     let y = b();
547     true
548 }
549
550 struct Foo {
551     x: int
552 }
553 "
554       rust-test-region-string rust-test-motion-string
555       rust-test-indent-motion-string
556       "
557 fn blank_line(arg:int) -> bool {
558
559 }
560
561 fn indenting_closing_brace() {
562     if(true) {
563 }
564 }
565
566 fn indenting_middle_of_line() {
567     if(true) {
568  push_me_out();
569 } else {
570                pull_me_back_in();
571 }
572 }
573
574 fn indented_already() {
575
576     // The previous line already has its spaces
577 }
578 "
579
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))
584                                   (end-of-fn1 (6 0))
585                                   (between-fn1-fn2 (7 0))
586                                   (start-of-fn2 (8 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))))
613
614 (defun rust-get-buffer-pos (pos-symbol)
615   "Get buffer position from POS-SYMBOL.
616
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."
619   (interactive "P")
620   (pcase-let ((`(,line ,column) (cadr (assoc pos-symbol rust-test-positions-alist))))
621     (save-excursion
622       (goto-line line)
623       (move-to-column column)
624       (point))))
625
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.
630
631 If ARGS are provided, send them to MANIP-FUNC.
632
633 INIT-POS, FINAL-POS are position symbols found in `rust-test-positions-alist'."
634   (with-temp-buffer
635     (rust-mode)
636     (insert source-code)
637     (goto-char (rust-get-buffer-pos init-pos))
638     (apply manip-func args)
639     (should (equal (point) (rust-get-buffer-pos final-pos)))))
640
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.
643
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'."
647   (with-temp-buffer
648     (rust-mode)
649     (insert source-code)
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))))))
655
656 (ert-deftest rust-beginning-of-defun-from-middle-of-fn ()
657   (rust-test-motion
658    rust-test-motion-string
659    'middle-of-fn1
660    'start-of-fn1
661    #'beginning-of-defun))
662
663 (ert-deftest rust-beginning-of-defun-from-end ()
664   (rust-test-motion
665    rust-test-motion-string
666    'end-of-fn1
667    'start-of-fn1
668    #'beginning-of-defun))
669
670 (ert-deftest rust-beginning-of-defun-before-open-brace ()
671   (rust-test-motion
672    rust-test-motion-string
673    'start-of-fn1-middle-of-line
674    'start-of-fn1
675    #'beginning-of-defun))
676
677 (ert-deftest rust-beginning-of-defun-between-fns ()
678   (rust-test-motion
679    rust-test-motion-string
680    'between-fn1-fn2
681    'start-of-fn1
682    #'beginning-of-defun))
683
684 (ert-deftest rust-beginning-of-defun-with-arg ()
685   (rust-test-motion
686    rust-test-motion-string
687    'middle-of-fn2
688    'start-of-fn1
689    #'beginning-of-defun 2))
690
691 (ert-deftest rust-beginning-of-defun-with-negative-arg ()
692   (rust-test-motion
693    rust-test-motion-string
694    'middle-of-fn1
695    'beginning-of-fn3
696    #'beginning-of-defun -2))
697
698 (ert-deftest rust-beginning-of-defun-pub-fn ()
699   (rust-test-motion
700    rust-test-motion-string
701    'middle-of-fn3
702    'beginning-of-fn3
703    #'beginning-of-defun))
704
705 (ert-deftest rust-end-of-defun-from-middle-of-fn ()
706   (rust-test-motion
707    rust-test-motion-string
708    'middle-of-fn1
709    'between-fn1-fn2
710    #'end-of-defun))
711
712 (ert-deftest rust-end-of-defun-from-beg ()
713   (rust-test-motion
714    rust-test-motion-string
715    'start-of-fn1
716    'between-fn1-fn2
717    #'end-of-defun))
718
719 (ert-deftest rust-end-of-defun-before-open-brace ()
720   (rust-test-motion
721    rust-test-motion-string
722    'start-of-fn1-middle-of-line
723    'between-fn1-fn2
724    #'end-of-defun))
725
726 (ert-deftest rust-end-of-defun-between-fns ()
727   (rust-test-motion
728    rust-test-motion-string
729    'between-fn1-fn2
730    'after-end-of-fn2
731    #'end-of-defun))
732
733 (ert-deftest rust-end-of-defun-with-arg ()
734   (rust-test-motion
735    rust-test-motion-string
736    'middle-of-fn1
737    'after-end-of-fn2
738    #'end-of-defun 2))
739
740 (ert-deftest rust-end-of-defun-with-negative-arg ()
741   (rust-test-motion
742    rust-test-motion-string
743    'middle-of-fn3
744    'between-fn1-fn2
745    #'end-of-defun -2))
746
747 (ert-deftest rust-mark-defun-from-middle-of-fn ()
748   (rust-test-region
749    rust-test-region-string
750    'middle-of-fn2
751    'between-fn1-fn2 'after-end-of-fn2
752    #'mark-defun))
753
754 (ert-deftest rust-mark-defun-from-end ()
755   (rust-test-region
756    rust-test-region-string
757    'end-of-fn1
758    'before-start-of-fn1 'between-fn1-fn2
759    #'mark-defun))
760
761 (ert-deftest rust-mark-defun-start-of-defun ()
762   (rust-test-region
763    rust-test-region-string
764    'start-of-fn2
765    'between-fn1-fn2 'after-end-of-fn2
766    #'mark-defun))
767
768 (ert-deftest rust-mark-defun-from-middle-of-struct ()
769   (rust-test-region
770    rust-test-region-string
771    'middle-of-struct
772    'before-start-of-struct 'after-end-of-struct
773    #'mark-defun))
774
775 (ert-deftest indent-line-blank-line-motion ()
776   (rust-test-motion
777    rust-test-indent-motion-string
778    'blank-line-indent-start
779    'blank-line-indent-target
780    #'indent-for-tab-command))
781
782 (ert-deftest indent-line-closing-brace-motion ()
783   (rust-test-motion
784    rust-test-indent-motion-string
785    'closing-brace-indent-start
786    'closing-brace-indent-target
787    #'indent-for-tab-command))
788
789 (ert-deftest indent-line-middle-push-motion ()
790   (rust-test-motion
791    rust-test-indent-motion-string
792    'middle-push-indent-start
793    'middle-push-indent-target
794    #'indent-for-tab-command))
795
796 (ert-deftest indent-line-after-whitespace-motion ()
797   (rust-test-motion
798    rust-test-indent-motion-string
799    'after-whitespace-indent-start
800    'after-whitespace-indent-target
801    #'indent-for-tab-command))
802
803 (ert-deftest indent-line-middle-pull-motion ()
804   (rust-test-motion
805    rust-test-indent-motion-string
806    'middle-pull-indent-start
807    'middle-pull-indent-target
808    #'indent-for-tab-command))
809
810 (ert-deftest indent-line-blank-line-indented-already-bol ()
811   (rust-test-motion
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))
816
817 (ert-deftest indent-line-blank-line-indented-already-middle ()
818   (rust-test-motion
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))
823
824 (ert-deftest indent-line-nonblank-line-indented-already-bol ()
825   (rust-test-motion
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))
830
831 (ert-deftest indent-line-nonblank-line-indented-already-middle ()
832   (rust-test-motion
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))
837
838 (defun rust-test-fontify-string (str)
839   (with-temp-buffer
840     (rust-mode)
841     (insert str)
842     (font-lock-fontify-buffer)
843     (buffer-string)))
844
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
850            while start
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)
854            if prop
855            append (list text prop)))
856
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)
860                  face-groups)))
861
862 (ert-deftest font-lock-attribute-simple ()
863   (rust-test-font-lock
864    "#[foo]"
865    '("#[foo]" font-lock-preprocessor-face)))
866
867 (ert-deftest font-lock-attribute-inner ()
868   (rust-test-font-lock
869    "#![foo]"
870    '("#![foo]" font-lock-preprocessor-face)))
871
872 (ert-deftest font-lock-attribute-key-value ()
873   (rust-test-font-lock
874    "#[foo = \"bar\"]"
875    '("#[foo = " font-lock-preprocessor-face
876      "\"bar\"" font-lock-string-face
877      "]" font-lock-preprocessor-face)))
878
879 (ert-deftest font-lock-attribute-around-comment ()
880   (rust-test-font-lock
881    "#[foo /* bar */]"
882    '("#[foo " font-lock-preprocessor-face
883      "/* " font-lock-comment-delimiter-face
884      "bar */" font-lock-comment-face
885      "]" font-lock-preprocessor-face)))
886
887 (ert-deftest font-lock-attribute-inside-string ()
888   (rust-test-font-lock
889    "\"#[foo]\""
890    '("\"#[foo]\"" font-lock-string-face)))
891
892 (ert-deftest font-lock-attribute-inside-comment ()
893   (rust-test-font-lock
894    "/* #[foo] */"
895    '("/* " font-lock-comment-delimiter-face
896      "#[foo] */" font-lock-comment-face)))