Commit | Line | Data |
---|---|---|
c28662a8 DG |
1 | ;;; ruby-mode-tests.el --- Test suite for ruby-mode |
2 | ||
ab422c4d | 3 | ;; Copyright (C) 2012-2013 Free Software Foundation, Inc. |
c28662a8 DG |
4 | |
5 | ;; This file is part of GNU Emacs. | |
6 | ||
7 | ;; GNU Emacs is free software: you can redistribute it and/or modify | |
8 | ;; it under the terms of the GNU General Public License as published by | |
9 | ;; the Free Software Foundation, either version 3 of the License, or | |
10 | ;; (at your option) any later version. | |
11 | ||
12 | ;; GNU Emacs is distributed in the hope that it will be useful, | |
13 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | ;; GNU General Public License for more details. | |
16 | ||
17 | ;; You should have received a copy of the GNU General Public License | |
18 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. | |
19 | ||
20 | ;;; Commentary: | |
21 | ||
22 | ;;; Code: | |
23 | ||
24 | (require 'ruby-mode) | |
25 | ||
9cd80478 | 26 | (defun ruby-should-indent (content column) |
9d2ed8a2 | 27 | "Assert indentation COLUMN on the last line of CONTENT." |
bb808526 | 28 | (ruby-with-temp-buffer content |
9cd80478 | 29 | (ruby-indent-line) |
9d2ed8a2 DG |
30 | (should (= (current-indentation) column)))) |
31 | ||
32 | (defun ruby-should-indent-buffer (expected content) | |
33 | "Assert that CONTENT turns into EXPECTED after the buffer is re-indented. | |
34 | ||
35 | The whitespace before and including \"|\" on each line is removed." | |
bb808526 | 36 | (ruby-with-temp-buffer (ruby-test-string content) |
5745cae6 DG |
37 | (indent-region (point-min) (point-max)) |
38 | (should (string= (ruby-test-string expected) (buffer-string))))) | |
39 | ||
bb808526 DG |
40 | (defmacro ruby-with-temp-buffer (contents &rest body) |
41 | (declare (indent 1) (debug t)) | |
42 | `(with-temp-buffer | |
43 | (insert ,contents) | |
44 | (ruby-mode) | |
45 | ,@body)) | |
46 | ||
5745cae6 DG |
47 | (defun ruby-test-string (s &rest args) |
48 | (apply 'format (replace-regexp-in-string "^[ \t]*|" "" s) args)) | |
9cd80478 | 49 | |
ab89e9f9 | 50 | (defun ruby-assert-state (content index value &optional point) |
9cd80478 DG |
51 | "Assert syntax state values at the end of CONTENT. |
52 | ||
53 | VALUES-PLIST is a list with alternating index and value elements." | |
bb808526 | 54 | (ruby-with-temp-buffer content |
ab89e9f9 | 55 | (when point (goto-char point)) |
9cd80478 | 56 | (syntax-propertize (point)) |
ab89e9f9 DG |
57 | (should (eq (nth index |
58 | (parse-partial-sexp (point-min) (point))) | |
59 | value)))) | |
9cd80478 | 60 | |
0ba2d4b6 | 61 | (defun ruby-assert-face (content pos face) |
bb808526 | 62 | (ruby-with-temp-buffer content |
0ba2d4b6 DG |
63 | (font-lock-fontify-buffer) |
64 | (should (eq face (get-text-property pos 'face))))) | |
65 | ||
9cd80478 | 66 | (ert-deftest ruby-indent-after-symbol-made-from-string-interpolation () |
c28662a8 | 67 | "It can indent the line after symbol made using string interpolation." |
9cd80478 DG |
68 | (ruby-should-indent "def foo(suffix)\n :\"bar#{suffix}\"\n" |
69 | ruby-indent-level)) | |
70 | ||
71 | (ert-deftest ruby-indent-after-js-style-symbol-with-block-beg-name () | |
72 | "JS-style hash symbol can have keyword name." | |
73 | (ruby-should-indent "link_to \"home\", home_path, class: \"foo\"\n" 0)) | |
74 | ||
75 | (ert-deftest ruby-discern-singleton-class-from-heredoc () | |
76 | (ruby-assert-state "foo <<asd\n" 3 ?\n) | |
77 | (ruby-assert-state "class <<asd\n" 3 nil)) | |
c28662a8 | 78 | |
f178c32d DG |
79 | (ert-deftest ruby-heredoc-font-lock () |
80 | (let ((s "foo <<eos.gsub('^ *', '')")) | |
c62792e7 | 81 | (ruby-assert-face s 9 font-lock-string-face) |
f178c32d DG |
82 | (ruby-assert-face s 10 nil))) |
83 | ||
84 | (ert-deftest ruby-singleton-class-no-heredoc-font-lock () | |
85 | (ruby-assert-face "class<<a" 8 nil)) | |
86 | ||
9d2ed8a2 DG |
87 | (ert-deftest ruby-deep-indent () |
88 | (let ((ruby-deep-arglist nil) | |
89 | (ruby-deep-indent-paren '(?\( ?\{ ?\[ ?\] t))) | |
90 | (ruby-should-indent "foo = [1,\n2" 7) | |
91 | (ruby-should-indent "foo = {a: b,\nc: d" 7) | |
92 | (ruby-should-indent "foo(a,\nb" 4))) | |
93 | ||
94 | (ert-deftest ruby-deep-indent-disabled () | |
95 | (let ((ruby-deep-arglist nil) | |
96 | (ruby-deep-indent-paren nil)) | |
97 | (ruby-should-indent "foo = [\n1" ruby-indent-level) | |
98 | (ruby-should-indent "foo = {\na: b" ruby-indent-level) | |
99 | (ruby-should-indent "foo(\na" ruby-indent-level))) | |
100 | ||
0ba2d4b6 DG |
101 | (ert-deftest ruby-indent-after-keyword-in-a-string () |
102 | (ruby-should-indent "a = \"abc\nif\"\n " 0) | |
103 | (ruby-should-indent "a = %w[abc\n def]\n " 0) | |
104 | (ruby-should-indent "a = \"abc\n def\"\n " 0)) | |
105 | ||
307d0e95 | 106 | (ert-deftest ruby-regexp-doesnt-start-in-string () |
ab89e9f9 DG |
107 | (ruby-assert-state "'(/', /\d+/" 3 nil)) |
108 | ||
109 | (ert-deftest ruby-regexp-starts-after-string () | |
110 | (ruby-assert-state "'(/', /\d+/" 3 ?/ 8)) | |
111 | ||
9d2ed8a2 DG |
112 | (ert-deftest ruby-indent-simple () |
113 | (ruby-should-indent-buffer | |
114 | "if foo | |
115 | | bar | |
116 | |end | |
117 | |zot | |
118 | |" | |
119 | "if foo | |
120 | |bar | |
121 | | end | |
122 | | zot | |
123 | |")) | |
124 | ||
125 | (ert-deftest ruby-indent-keyword-label () | |
126 | (ruby-should-indent-buffer | |
127 | "bar(class: XXX) do | |
128 | | foo | |
129 | |end | |
130 | |bar | |
131 | |" | |
132 | "bar(class: XXX) do | |
133 | | foo | |
134 | | end | |
135 | | bar | |
136 | |")) | |
137 | ||
138 | (ert-deftest ruby-indent-method-with-question-mark () | |
139 | (ruby-should-indent-buffer | |
140 | "if x.is_a?(XXX) | |
141 | | foo | |
142 | |end | |
143 | |" | |
144 | "if x.is_a?(XXX) | |
145 | | foo | |
146 | | end | |
147 | |")) | |
148 | ||
149 | (ert-deftest ruby-indent-expr-in-regexp () | |
150 | (ruby-should-indent-buffer | |
151 | "if /#{foo}/ =~ s | |
152 | | x = 1 | |
153 | |end | |
154 | |" | |
155 | "if /#{foo}/ =~ s | |
156 | | x = 1 | |
157 | | end | |
158 | |")) | |
159 | ||
160 | (ert-deftest ruby-indent-singleton-class () | |
9d2ed8a2 DG |
161 | (ruby-should-indent-buffer |
162 | "class<<bar | |
163 | | foo | |
164 | |end | |
165 | |" | |
166 | "class<<bar | |
167 | |foo | |
168 | | end | |
169 | |")) | |
170 | ||
8619323f DG |
171 | (ert-deftest ruby-indent-inside-heredoc-after-operator () |
172 | (ruby-should-indent-buffer | |
173 | "b=<<eos | |
174 | | 42" | |
175 | "b=<<eos | |
176 | | 42")) | |
177 | ||
178 | (ert-deftest ruby-indent-inside-heredoc-after-space () | |
179 | (ruby-should-indent-buffer | |
180 | "foo <<eos.gsub(' ', '*') | |
181 | | 42" | |
182 | "foo <<eos.gsub(' ', '*') | |
183 | | 42")) | |
184 | ||
9d2ed8a2 DG |
185 | (ert-deftest ruby-indent-array-literal () |
186 | (let ((ruby-deep-indent-paren nil)) | |
187 | (ruby-should-indent-buffer | |
188 | "foo = [ | |
189 | | bar | |
190 | |] | |
191 | |" | |
192 | "foo = [ | |
193 | | bar | |
194 | | ] | |
195 | |")) | |
196 | (ruby-should-indent-buffer | |
197 | "foo do | |
198 | | [bar] | |
199 | |end | |
200 | |" | |
201 | "foo do | |
202 | |[bar] | |
203 | | end | |
204 | |")) | |
205 | ||
206 | (ert-deftest ruby-indent-begin-end () | |
207 | (ruby-should-indent-buffer | |
208 | "begin | |
209 | | a[b] | |
210 | |end | |
211 | |" | |
212 | "begin | |
213 | | a[b] | |
214 | | end | |
215 | |")) | |
216 | ||
217 | (ert-deftest ruby-indent-array-after-paren-and-space () | |
218 | (ruby-should-indent-buffer | |
219 | "class A | |
220 | | def foo | |
221 | | foo( []) | |
222 | | end | |
223 | |end | |
224 | |" | |
225 | "class A | |
226 | | def foo | |
227 | |foo( []) | |
228 | |end | |
229 | | end | |
230 | |")) | |
231 | ||
db590ef6 DG |
232 | (ert-deftest ruby-indent-after-block-in-continued-expression () |
233 | (ruby-should-indent-buffer | |
234 | "var = | |
235 | | begin | |
236 | | val | |
237 | | end | |
238 | |statement" | |
239 | "var = | |
240 | |begin | |
241 | |val | |
242 | |end | |
243 | |statement")) | |
244 | ||
b1625024 DG |
245 | (ert-deftest ruby-indent-spread-args-in-parens () |
246 | (let ((ruby-deep-indent-paren '(?\())) | |
247 | (ruby-should-indent-buffer | |
248 | "foo(1, | |
249 | | 2, | |
250 | | 3) | |
251 | |" | |
252 | "foo(1, | |
253 | | 2, | |
254 | | 3) | |
255 | |"))) | |
256 | ||
d1e1e53d | 257 | (ert-deftest ruby-move-to-block-stops-at-indentation () |
bb808526 | 258 | (ruby-with-temp-buffer "def f\nend" |
0d9e2599 | 259 | (beginning-of-line) |
0d9e2599 | 260 | (ruby-move-to-block -1) |
d1e1e53d | 261 | (should (looking-at "^def")))) |
0d9e2599 NN |
262 | |
263 | (ert-deftest ruby-toggle-block-to-do-end () | |
bb808526 | 264 | (ruby-with-temp-buffer "foo {|b|\n}" |
c3268831 | 265 | (beginning-of-line) |
0d9e2599 | 266 | (ruby-toggle-block) |
c3268831 | 267 | (should (string= "foo do |b|\nend" (buffer-string))))) |
0d9e2599 NN |
268 | |
269 | (ert-deftest ruby-toggle-block-to-brace () | |
32fb8162 DG |
270 | (let ((pairs '((16 . "foo {|b| b + 2 }") |
271 | (15 . "foo {|b|\n b + 2\n}")))) | |
272 | (dolist (pair pairs) | |
273 | (with-temp-buffer | |
274 | (let ((fill-column (car pair))) | |
275 | (insert "foo do |b|\n b + 2\nend") | |
276 | (ruby-mode) | |
277 | (beginning-of-line) | |
278 | (ruby-toggle-block) | |
279 | (should (string= (cdr pair) (buffer-string)))))))) | |
c3268831 DG |
280 | |
281 | (ert-deftest ruby-toggle-block-to-multiline () | |
bb808526 | 282 | (ruby-with-temp-buffer "foo {|b| b + 1}" |
c3268831 | 283 | (beginning-of-line) |
0d9e2599 | 284 | (ruby-toggle-block) |
c3268831 | 285 | (should (string= "foo do |b|\n b + 1\nend" (buffer-string))))) |
0d9e2599 | 286 | |
0ba2d4b6 | 287 | (ert-deftest ruby-recognize-symbols-starting-with-at-character () |
c62792e7 | 288 | (ruby-assert-face ":@abc" 3 font-lock-constant-face)) |
0ba2d4b6 DG |
289 | |
290 | (ert-deftest ruby-hash-character-not-interpolation () | |
291 | (ruby-assert-face "\"This is #{interpolation}\"" 15 | |
c62792e7 | 292 | font-lock-variable-name-face) |
0ba2d4b6 | 293 | (ruby-assert-face "\"This is \\#{no interpolation} despite the #\"" |
c62792e7 DG |
294 | 15 font-lock-string-face) |
295 | (ruby-assert-face "\n#@comment, not ruby code" 5 font-lock-comment-face) | |
616c6c36 | 296 | (ruby-assert-state "\n#@comment, not ruby code" 4 t) |
0ba2d4b6 | 297 | (ruby-assert-face "# A comment cannot have #{an interpolation} in it" |
c62792e7 | 298 | 30 font-lock-comment-face) |
616c6c36 | 299 | (ruby-assert-face "# #{comment}\n \"#{interpolation}\"" 16 |
c62792e7 DG |
300 | font-lock-variable-name-face)) |
301 | ||
dbb530d9 | 302 | (ert-deftest ruby-interpolation-suppresses-quotes-inside () |
c62792e7 DG |
303 | (let ((s "\"<ul><li>#{@files.join(\"</li><li>\")}</li></ul>\"")) |
304 | (ruby-assert-state s 8 nil) | |
305 | (ruby-assert-face s 9 font-lock-string-face) | |
306 | (ruby-assert-face s 10 font-lock-variable-name-face) | |
307 | (ruby-assert-face s 41 font-lock-string-face))) | |
308 | ||
dbb530d9 DG |
309 | (ert-deftest ruby-interpolation-suppresses-one-double-quote () |
310 | (let ((s "\"foo#{'\"'}\"")) | |
311 | (ruby-assert-state s 8 nil) | |
312 | (ruby-assert-face s 8 font-lock-variable-name-face) | |
313 | (ruby-assert-face s 11 font-lock-string-face))) | |
314 | ||
315 | (ert-deftest ruby-interpolation-suppresses-one-backtick () | |
316 | (let ((s "`as#{'`'}das`")) | |
317 | (ruby-assert-state s 8 nil))) | |
318 | ||
319 | (ert-deftest ruby-interpolation-keeps-non-quote-syntax () | |
320 | (let ((s "\"foo#{baz.tee}bar\"")) | |
bb808526 DG |
321 | (ruby-with-temp-buffer s |
322 | (goto-char (point-min)) | |
dbb530d9 DG |
323 | (ruby-mode) |
324 | (font-lock-fontify-buffer) | |
325 | (search-forward "tee") | |
326 | (should (string= (thing-at-point 'symbol) "tee"))))) | |
327 | ||
c62792e7 DG |
328 | (ert-deftest ruby-interpolation-inside-percent-literal-with-paren () |
329 | :expected-result :failed | |
330 | (let ((s "%(^#{\")\"}^)")) | |
331 | (ruby-assert-face s 3 font-lock-string-face) | |
332 | (ruby-assert-face s 4 font-lock-variable-name-face) | |
333 | (ruby-assert-face s 10 font-lock-string-face) | |
334 | ;; It's confused by the closing paren in the middle. | |
335 | (ruby-assert-state s 8 nil))) | |
0ba2d4b6 | 336 | |
5745cae6 DG |
337 | (ert-deftest ruby-add-log-current-method-examples () |
338 | (let ((pairs '(("foo" . "#foo") | |
339 | ("C.foo" . ".foo") | |
340 | ("self.foo" . ".foo")))) | |
89eb3b0a CY |
341 | (dolist (pair pairs) |
342 | (let ((name (car pair)) | |
bb808526 DG |
343 | (value (cdr pair))) |
344 | (ruby-with-temp-buffer (ruby-test-string | |
345 | "module M | |
346 | | class C | |
347 | | def %s | |
348 | | _ | |
349 | | end | |
350 | | end | |
351 | |end" | |
352 | name) | |
353 | (search-backward "_") | |
354 | (forward-line) | |
355 | (should (string= (ruby-add-log-current-method) | |
356 | (format "M::C%s" value)))))))) | |
357 | ||
358 | (ert-deftest ruby-add-log-current-method-outside-of-method () | |
359 | (ruby-with-temp-buffer (ruby-test-string | |
360 | "module M | |
361 | | class C | |
362 | | def foo | |
363 | | end | |
364 | | _ | |
365 | | end | |
366 | |end") | |
367 | (search-backward "_") | |
368 | (should (string= (ruby-add-log-current-method)"M::C")))) | |
369 | ||
370 | (ert-deftest ruby-add-log-current-method-in-singleton-class () | |
371 | (ruby-with-temp-buffer (ruby-test-string | |
372 | "class C | |
373 | | class << self | |
374 | | def foo | |
375 | | _ | |
376 | | end | |
377 | | end | |
378 | |end") | |
379 | (search-backward "_") | |
380 | (should (string= (ruby-add-log-current-method) "C.foo")))) | |
381 | ||
382 | (ert-deftest ruby-add-log-current-method-namespace-shorthand () | |
383 | (ruby-with-temp-buffer (ruby-test-string | |
384 | "class C::D | |
385 | | def foo | |
386 | | _ | |
387 | | end | |
388 | |end") | |
389 | (search-backward "_") | |
390 | (should (string= (ruby-add-log-current-method) "C::D#foo")))) | |
391 | ||
392 | (ert-deftest ruby-add-log-current-method-after-inner-class () | |
393 | (ruby-with-temp-buffer (ruby-test-string | |
394 | "module M | |
395 | | class C | |
396 | | class D | |
397 | | end | |
71a048c1 DG |
398 | | def foo |
399 | | _ | |
400 | | end | |
bb808526 DG |
401 | | end |
402 | |end") | |
403 | (search-backward "_") | |
71a048c1 | 404 | (should (string= (ruby-add-log-current-method) "M::C#foo")))) |
5745cae6 | 405 | |
5e9419e8 DG |
406 | (defvar ruby-block-test-example |
407 | (ruby-test-string | |
408 | "class C | |
409 | | def foo | |
410 | | 1 | |
411 | | end | |
412 | | | |
413 | | def bar | |
414 | | 2 | |
415 | | end | |
416 | | | |
417 | | def baz | |
a324b8c7 DG |
418 | |some do |
419 | |3 | |
5e9419e8 DG |
420 | | end |
421 | | end | |
422 | |end")) | |
423 | ||
424 | (defmacro ruby-deftest-move-to-block (name &rest body) | |
425 | `(ert-deftest ,(intern (format "ruby-move-to-block-%s" name)) () | |
426 | (with-temp-buffer | |
427 | (insert ruby-block-test-example) | |
428 | (ruby-mode) | |
429 | ,@body))) | |
430 | ||
431 | (put 'ruby-deftest-move-to-block 'lisp-indent-function 'defun) | |
432 | ||
433 | (ruby-deftest-move-to-block works-on-do | |
434 | (goto-line 11) | |
435 | (ruby-end-of-block) | |
a324b8c7 | 436 | (should (= 13 (line-number-at-pos))) |
5e9419e8 DG |
437 | (ruby-beginning-of-block) |
438 | (should (= 11 (line-number-at-pos)))) | |
439 | ||
440 | (ruby-deftest-move-to-block zero-is-noop | |
441 | (goto-line 5) | |
442 | (ruby-move-to-block 0) | |
443 | (should (= 5 (line-number-at-pos)))) | |
444 | ||
445 | (ruby-deftest-move-to-block ok-with-three | |
446 | (goto-line 2) | |
447 | (ruby-move-to-block 3) | |
a324b8c7 | 448 | (should (= 14 (line-number-at-pos)))) |
5e9419e8 DG |
449 | |
450 | (ruby-deftest-move-to-block ok-with-minus-two | |
451 | (goto-line 10) | |
452 | (ruby-move-to-block -2) | |
453 | (should (= 2 (line-number-at-pos)))) | |
454 | ||
7132e457 DG |
455 | (ert-deftest ruby-move-to-block-skips-percent-literal () |
456 | (dolist (s (list (ruby-test-string | |
457 | "foo do | |
458 | | a = %%w( | |
53ca88c4 | 459 | | def yaa |
7132e457 DG |
460 | | ) |
461 | |end") | |
462 | (ruby-test-string | |
463 | "foo do | |
464 | | a = %%w| | |
53ca88c4 | 465 | | end |
7132e457 DG |
466 | | | |
467 | |end"))) | |
468 | (ruby-with-temp-buffer s | |
469 | (goto-line 1) | |
470 | (ruby-end-of-block) | |
53ca88c4 | 471 | (should (= 5 (line-number-at-pos))) |
7132e457 DG |
472 | (ruby-beginning-of-block) |
473 | (should (= 1 (line-number-at-pos)))))) | |
474 | ||
53ca88c4 DG |
475 | (ert-deftest ruby-move-to-block-skips-heredoc () |
476 | (ruby-with-temp-buffer | |
477 | (ruby-test-string | |
478 | "if something_wrong? | |
479 | | ActiveSupport::Deprecation.warn(<<-eowarn) | |
480 | | boo hoo | |
481 | | end | |
482 | | eowarn | |
483 | |end") | |
484 | (goto-line 1) | |
485 | (ruby-end-of-block) | |
486 | (should (= 6 (line-number-at-pos))) | |
487 | (ruby-beginning-of-block) | |
488 | (should (= 1 (line-number-at-pos))))) | |
489 | ||
fb549d64 DG |
490 | (ert-deftest ruby-move-to-block-does-not-fold-case () |
491 | (ruby-with-temp-buffer | |
492 | (ruby-test-string | |
493 | "foo do | |
494 | | Module.to_s | |
495 | |end") | |
496 | (end-of-buffer) | |
497 | (let ((case-fold-search t)) | |
498 | (ruby-beginning-of-block)) | |
499 | (should (= 1 (line-number-at-pos))))) | |
500 | ||
501 | (ert-deftest ruby-beginning-of-defun-does-not-fold-case () | |
502 | (ruby-with-temp-buffer | |
503 | (ruby-test-string | |
504 | "class C | |
505 | | def bar | |
506 | | Class.to_s | |
507 | | end | |
508 | |end") | |
509 | (goto-line 4) | |
510 | (let ((case-fold-search t)) | |
511 | (beginning-of-defun)) | |
512 | (should (= 2 (line-number-at-pos))))) | |
513 | ||
514 | (ert-deftest ruby-end-of-defun-skips-to-next-line-after-the-method () | |
515 | (ruby-with-temp-buffer | |
516 | (ruby-test-string | |
517 | "class D | |
518 | | def tee | |
519 | | 'ho hum' | |
520 | | end | |
521 | |end") | |
522 | (goto-line 2) | |
523 | (end-of-defun) | |
524 | (should (= 5 (line-number-at-pos))))) | |
525 | ||
c28662a8 DG |
526 | (provide 'ruby-mode-tests) |
527 | ||
528 | ;;; ruby-mode-tests.el ends here |