summaryrefslogtreecommitdiff
path: root/test/lisp/progmodes
diff options
context:
space:
mode:
Diffstat (limited to 'test/lisp/progmodes')
-rw-r--r--test/lisp/progmodes/cc-mode-tests.el72
-rw-r--r--test/lisp/progmodes/compile-tests.el382
-rw-r--r--test/lisp/progmodes/elisp-mode-tests.el676
-rw-r--r--test/lisp/progmodes/etags-tests.el91
-rw-r--r--test/lisp/progmodes/f90.el276
-rw-r--r--test/lisp/progmodes/flymake-resources/Makefile13
-rw-r--r--test/lisp/progmodes/flymake-resources/test.c5
-rw-r--r--test/lisp/progmodes/flymake-resources/test.pl2
-rw-r--r--test/lisp/progmodes/flymake-tests.el80
-rw-r--r--test/lisp/progmodes/python-tests.el5295
-rw-r--r--test/lisp/progmodes/ruby-mode-tests.el752
-rw-r--r--test/lisp/progmodes/sql-tests.el47
-rw-r--r--test/lisp/progmodes/subword-tests.el81
-rw-r--r--test/lisp/progmodes/xref-tests.el91
14 files changed, 7863 insertions, 0 deletions
diff --git a/test/lisp/progmodes/cc-mode-tests.el b/test/lisp/progmodes/cc-mode-tests.el
new file mode 100644
index 00000000000..62e0a738fbd
--- /dev/null
+++ b/test/lisp/progmodes/cc-mode-tests.el
@@ -0,0 +1,72 @@
+;;; cc-mode-tests.el --- Test suite for cc-mode. -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2016 Free Software Foundation, Inc.
+
+;; Author: Michal Nazarewicz <mina86@mina86.com>
+;; Keywords: internal
+;; Human-Keywords: internal
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
+
+
+;;; Commentary:
+
+;; Unit tests for cc-mode.el.
+
+;;; Code:
+
+(require 'ert)
+(require 'ert-x)
+(require 'cc-mode)
+
+(ert-deftest c-or-c++-mode ()
+ "Test c-or-c++-mode language detection."
+ (cl-letf* ((mode nil)
+ (do-test (lambda (content expected)
+ (delete-region (point-min) (point-max))
+ (insert content)
+ (setq mode nil)
+ (c-or-c++-mode)
+ (unless(eq expected mode)
+ (ert-fail
+ (format "expected %s but got %s when testing '%s'"
+ expected mode content)))))
+ ((symbol-function 'c-mode) (lambda () (setq mode 'c-mode)))
+ ((symbol-function 'c++-mode) (lambda () (setq mode 'c++-mode))))
+ (with-temp-buffer
+ (mapc (lambda (content)
+ (funcall do-test content 'c++-mode)
+ (funcall do-test (concat "// " content) 'c-mode)
+ (funcall do-test (concat " * " content) 'c-mode))
+ '("using \t namespace \t std;"
+ "using \t std::string;"
+ "namespace \t {"
+ "namespace \t foo \t {"
+ "class \t Blah_42 \t {"
+ "class \t Blah_42 \t \n"
+ "class \t _42_Blah:public Foo {"
+ "template \t < class T >"
+ "template< class T >"
+ "#include <string>"
+ "#include<iostream>"
+ "#include \t <map>"))
+
+ (mapc (lambda (content) (funcall do-test content 'c-mode))
+ '("struct \t Blah_42 \t {"
+ "struct template {"
+ "#include <string.h>")))))
+
+;;; cc-mode-tests.el ends here
diff --git a/test/lisp/progmodes/compile-tests.el b/test/lisp/progmodes/compile-tests.el
new file mode 100644
index 00000000000..9f61c20fd5e
--- /dev/null
+++ b/test/lisp/progmodes/compile-tests.el
@@ -0,0 +1,382 @@
+;;; compile-tests.el --- Test suite for compile.el. -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2011-2016 Free Software Foundation, Inc.
+
+;; Author: Chong Yidong <cyd@stupidchicken.com>
+;; Keywords: internal
+;; Human-Keywords: internal
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Unit tests for lisp/progmodes/compile.el.
+
+;;; Code:
+
+(require 'ert)
+(require 'compile)
+
+(defvar compile-tests--test-regexps-data
+ ;; The computed column numbers are zero-indexed, so subtract 1 from
+ ;; what's reported in the string. The end column numbers are for
+ ;; the character after, so it matches what's reported in the string.
+ '(;; absoft
+ ("Error on line 3 of t.f: Execution error unclassifiable statement"
+ 1 nil 3 "t.f")
+ ("Line 45 of \"foo.c\": bloofle undefined"
+ 1 nil 45 "foo.c")
+ ("error on line 19 of fplot.f: spelling error?"
+ 1 nil 19 "fplot.f")
+ ("warning on line 17 of fplot.f: data type is undefined for variable d"
+ 1 nil 17 "fplot.f")
+ ;; Ada & Mpatrol
+ ("foo.adb:61:11: [...] in call to size declared at foo.ads:11"
+ 1 11 61 "foo.adb")
+ ("foo.adb:61:11: [...] in call to size declared at foo.ads:11"
+ 52 nil 11 "foo.ads")
+ (" 0x8008621 main+16 at error.c:17"
+ 23 nil 17 "error.c")
+ ;; aix
+ ("****** Error number 140 in line 8 of file errors.c ******"
+ 25 nil 8 "errors.c")
+ ;; ant
+ ("[javac] /src/DataBaseTestCase.java:27: unreported exception ..."
+ 13 nil 27 "/src/DataBaseTestCase.java")
+ ("[javac] /src/DataBaseTestCase.java:49: warning: finally clause cannot complete normally"
+ 13 nil 49 "/src/DataBaseTestCase.java")
+ ("[jikes] foo.java:3:5:7:9: blah blah"
+ 14 (5 . 10) (3 . 7) "foo.java")
+ ;; bash
+ ("a.sh: line 1: ls-l: command not found"
+ 1 nil 1 "a.sh")
+ ;; borland
+ ("Error ping.c 15: Unable to open include file 'sys/types.h'"
+ 1 nil 15 "ping.c")
+ ("Warning pong.c 68: Call to function 'func' with no prototype"
+ 1 nil 68 "pong.c")
+ ("Error E2010 ping.c 15: Unable to open include file 'sys/types.h'"
+ 1 nil 15 "ping.c")
+ ("Warning W1022 pong.c 68: Call to function 'func' with no prototype"
+ 1 nil 68 "pong.c")
+ ;; caml
+ ("File \"foobar.ml\", lines 5-8, characters 20-155: blah blah"
+ 1 (20 . 156) (5 . 8) "foobar.ml")
+ ("File \"F:\\ocaml\\sorting.ml\", line 65, characters 2-145:\nWarning 26: unused variable equ."
+ 1 (2 . 146) 65 "F:\\ocaml\\sorting.ml")
+ ("File \"/usr/share/gdesklets/display/TargetGauge.py\", line 41, in add_children"
+ 1 nil 41 "/usr/share/gdesklets/display/TargetGauge.py")
+ ("File \\lib\\python\\Products\\PythonScripts\\PythonScript.py, line 302, in _exec"
+ 1 nil 302 "\\lib\\python\\Products\\PythonScripts\\PythonScript.py")
+ ("File \"/tmp/foo.py\", line 10"
+ 1 nil 10 "/tmp/foo.py")
+ ;; clang-include
+ ("In file included from foo.cpp:2:"
+ 1 nil 2 "foo.cpp" 0)
+ ;; cmake cmake-info
+ ("CMake Error at CMakeLists.txt:23 (hurz):"
+ 1 nil 23 "CMakeLists.txt")
+ ("CMake Warning at cmake/modules/UseUG.cmake:73 (find_package):"
+ 1 nil 73 "cmake/modules/UseUG.cmake")
+ (" cmake/modules/DuneGridMacros.cmake:19 (include)"
+ 1 nil 19 "cmake/modules/DuneGridMacros.cmake")
+ ;; comma
+ ("\"foo.f\", line 3: Error: syntax error near end of statement"
+ 1 nil 3 "foo.f")
+ ("\"vvouch.c\", line 19.5: 1506-046 (S) Syntax error."
+ 1 5 19 "vvouch.c")
+ ("\"foo.c\", line 32 pos 1; (E) syntax error; unexpected symbol: \"lossage\""
+ 1 1 32 "foo.c")
+ ("\"foo.adb\", line 2(11): warning: file name does not match ..."
+ 1 11 2 "foo.adb")
+ ("\"src/swapping.c\", line 30.34: 1506-342 (W) \"/*\" detected in comment."
+ 1 34 30 "src/swapping.c")
+ ;; cucumber
+ ("Scenario: undefined step # features/cucumber.feature:3"
+ 29 nil 3 "features/cucumber.feature")
+ (" /home/gusev/.rvm/foo/bar.rb:500:in `_wrap_assertion'"
+ 1 nil 500 "/home/gusev/.rvm/foo/bar.rb")
+ ;; edg-1 edg-2
+ ("build/intel/debug/../../../struct.cpp(42): error: identifier \"foo\" is undefined"
+ 1 nil 42 "build/intel/debug/../../../struct.cpp")
+ ("build/intel/debug/struct.cpp(44): warning #1011: missing return statement at end of"
+ 1 nil 44 "build/intel/debug/struct.cpp")
+ ("build/intel/debug/iptr.h(302): remark #981: operands are evaluated in unspecified order"
+ 1 nil 302 "build/intel/debug/iptr.h")
+ (" detected during ... at line 62 of \"build/intel/debug/../../../trace.h\""
+ 31 nil 62 "build/intel/debug/../../../trace.h")
+ ;; epc
+ ("Error 24 at (2:progran.f90) : syntax error"
+ 1 nil 2 "progran.f90")
+ ;; ftnchek
+ (" Dummy arg W in module SUBA line 8 file arrayclash.f is array"
+ 32 nil 8 "arrayclash.f")
+ (" L4 used at line 55 file test/assign.f; never set"
+ 16 nil 55 "test/assign.f")
+ ("Warning near line 10 file arrayclash.f: Module contains no executable"
+ 1 nil 10 "arrayclash.f")
+ ("Nonportable usage near line 31 col 9 file assign.f: mixed default and explicit"
+ 24 9 31 "assign.f")
+ ;; iar
+ ("\"foo.c\",3 Error[32]: Error message"
+ 1 nil 3 "foo.c")
+ ("\"foo.c\",3 Warning[32]: Error message"
+ 1 nil 3 "foo.c")
+ ;; ibm
+ ("foo.c(2:0) : informational EDC0804: Function foo is not referenced."
+ 1 0 2 "foo.c")
+ ("foo.c(3:8) : warning EDC0833: Implicit return statement encountered."
+ 1 8 3 "foo.c")
+ ("foo.c(5:5) : error EDC0350: Syntax error."
+ 1 5 5 "foo.c")
+ ;; irix
+ ("ccom: Error: foo.c, line 2: syntax error"
+ 1 nil 2 "foo.c")
+ ("cc: Severe: /src/Python-2.3.3/Modules/_curses_panel.c, line 17: Cannot find file <panel.h> ..."
+ 1 nil 17 "/src/Python-2.3.3/Modules/_curses_panel.c")
+ ("cc: Info: foo.c, line 27: ..."
+ 1 nil 27 "foo.c")
+ ("cfe: Warning 712: foo.c, line 2: illegal combination of pointer and ..."
+ 1 nil 2 "foo.c")
+ ("cfe: Warning 600: xfe.c: 170: Not in a conditional directive while ..."
+ 1 nil 170 "xfe.c")
+ ("/usr/lib/cmplrs/cc/cfe: Error: foo.c: 1: blah blah"
+ 1 nil 1 "foo.c")
+ ("/usr/lib/cmplrs/cc/cfe: warning: foo.c: 1: blah blah"
+ 1 nil 1 "foo.c")
+ ("foo bar: baz.f, line 27: ..."
+ 1 nil 27 "baz.f")
+ ;; java
+ ("\tat org.foo.ComponentGateway.doGet(ComponentGateway.java:172)"
+ 5 nil 172 "ComponentGateway.java")
+ ("\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:740)"
+ 5 nil 740 "HttpServlet.java")
+ ("==1332== at 0x4040743C: System::getErrorString() (../src/Lib/System.cpp:217)"
+ 13 nil 217 "../src/Lib/System.cpp")
+ ("==1332== by 0x8008621: main (vtest.c:180)"
+ 13 nil 180 "vtest.c")
+ ;; jikes-file jikes-line
+ ("Found 2 semantic errors compiling \"../javax/swing/BorderFactory.java\":"
+ 1 nil nil "../javax/swing/BorderFactory.java")
+ ("Issued 1 semantic warning compiling \"java/awt/Toolkit.java\":"
+ 1 nil nil "java/awt/Toolkit.java")
+ ;; gcc-include
+ ("In file included from /usr/include/c++/3.3/backward/warn.h:4,"
+ 1 nil 4 "/usr/include/c++/3.3/backward/warn.h")
+ (" from /usr/include/c++/3.3/backward/iostream.h:31:0,"
+ 1 0 31 "/usr/include/c++/3.3/backward/iostream.h")
+ (" from test_clt.cc:1:"
+ 1 nil 1 "test_clt.cc")
+ ;; gnu
+ ("foo.c:8: message" 1 nil 8 "foo.c")
+ ("../foo.c:8: W: message" 1 nil 8 "../foo.c")
+ ("/tmp/foo.c:8:warning message" 1 nil 8 "/tmp/foo.c")
+ ("foo/bar.py:8: FutureWarning message" 1 nil 8 "foo/bar.py")
+ ("foo.py:8: RuntimeWarning message" 1 nil 8 "foo.py")
+ ("foo.c:8:I: message" 1 nil 8 "foo.c")
+ ("foo.c:8.23: note: message" 1 23 8 "foo.c")
+ ("foo.c:8.23: info: message" 1 23 8 "foo.c")
+ ("foo.c:8:23:information: message" 1 23 8 "foo.c")
+ ("foo.c:8.23-45: Informational: message" 1 (23 . 46) (8 . nil) "foo.c")
+ ("foo.c:8-23: message" 1 nil (8 . 23) "foo.c")
+ ;; The next one is not in the GNU standards AFAICS.
+ ;; Here we seem to interpret it as LINE1-LINE2.COL2.
+ ("foo.c:8-45.3: message" 1 (nil . 4) (8 . 45) "foo.c")
+ ("foo.c:8.23-9.1: message" 1 (23 . 2) (8 . 9) "foo.c")
+ ("jade:dbcommon.dsl:133:17:E: missing argument for function call"
+ 1 17 133 "dbcommon.dsl")
+ ("G:/cygwin/dev/build-myproj.xml:54: Compiler Adapter 'javac' can't be found."
+ 1 nil 54 "G:/cygwin/dev/build-myproj.xml")
+ ("file:G:/cygwin/dev/build-myproj.xml:54: Compiler Adapter 'javac' can't be found."
+ 1 nil 54 "G:/cygwin/dev/build-myproj.xml")
+ ("{standard input}:27041: Warning: end of file not at end of a line; newline inserted"
+ 1 nil 27041 "{standard input}")
+ ;; Guile
+ ("In foo.scm:\n" 1 nil nil "foo.scm")
+ (" 63:4 [call-with-prompt prompt0 ...]" 1 4 63 nil)
+ ("1038: 1 [main (\"gud-break.scm\")]" 1 1 1038 nil)
+ ;; lcc
+ ("E, file.cc(35,52) Illegal operation on pointers" 1 52 35 "file.cc")
+ ("W, file.cc(36,52) blah blah" 1 52 36 "file.cc")
+ ;; makepp
+ ("makepp: Scanning `/foo/bar.c'" 19 nil nil "/foo/bar.c")
+ ("makepp: warning: bla bla `/foo/bar.c' and `/foo/bar.h'" 27 nil nil "/foo/bar.c")
+ ("makepp: bla bla `/foo/Makeppfile:12' bla" 18 nil 12 "/foo/Makeppfile")
+ ("makepp: bla bla `/foo/bar.c' and `/foo/bar.h'" 35 nil nil "/foo/bar.h")
+ ;; maven
+ ("FooBar.java:[111,53] no interface expected here"
+ 1 53 111 "FooBar.java" 2)
+ (" [ERROR] /Users/cinsk/hello.java:[651,96] ';' expected"
+ 15 96 651 "/Users/cinsk/hello.java" 2) ;Bug#11517.
+ ("[WARNING] /foo/bar/Test.java:[27,43] unchecked conversion"
+ 11 43 27 "/foo/bar/Test.java" 1) ;Bug#20556
+ ;; mips-1 mips-2
+ ("TrimMask (255) in solomon.c may be indistinguishable from TrimMasks (93) in solomo.c due to truncation"
+ 11 nil 255 "solomon.c")
+ ("TrimMask (255) in solomon.c may be indistinguishable from TrimMasks (93) in solomo.c due to truncation"
+ 70 nil 93 "solomo.c")
+ ("name defined but never used: LinInt in cmap_calc.c(199)"
+ 40 nil 199 "cmap_calc.c")
+ ;; msft
+ ("keyboard handler.c(537) : warning C4005: 'min' : macro redefinition"
+ 1 nil 537 "keyboard handler.c")
+ ("d:\\tmp\\test.c(23) : error C2143: syntax error : missing ';' before 'if'"
+ 1 nil 23 "d:\\tmp\\test.c")
+ ("d:\\tmp\\test.c(1145) : see declaration of 'nsRefPtr'"
+ 1 nil 1145 "d:\\tmp\\test.c")
+ ("1>test_main.cpp(29): error C2144: syntax error : 'int' should be preceded by ';'"
+ 3 nil 29 "test_main.cpp")
+ ("1>test_main.cpp(29): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int"
+ 3 nil 29 "test_main.cpp")
+ ;; watcom
+ ("..\\src\\ctrl\\lister.c(109): Error! E1009: Expecting ';' but found '{'"
+ 1 nil 109 "..\\src\\ctrl\\lister.c")
+ ("..\\src\\ctrl\\lister.c(120): Warning! W201: Unreachable code"
+ 1 nil 120 "..\\src\\ctrl\\lister.c")
+ ;; oracle
+ ("Semantic error at line 528, column 5, file erosacqdb.pc:"
+ 1 5 528 "erosacqdb.pc")
+ ("Error at line 41, column 10 in file /usr/src/sb/ODBI_BHP.hpp"
+ 1 10 41 "/usr/src/sb/ODBI_BHP.hpp")
+ ("PCC-02150: error at line 49, column 27 in file /usr/src/sb/ODBI_dxfgh.pc"
+ 1 27 49 "/usr/src/sb/ODBI_dxfgh.pc")
+ ("PCC-00003: invalid SQL Identifier at column name in line 12 of file /usr/src/sb/ODBI_BHP.hpp"
+ 1 nil 12 "/usr/src/sb/ODBI_BHP.hpp")
+ ("PCC-00004: mismatched IF/ELSE/ENDIF block at line 27 in file /usr/src/sb/ODBI_BHP.hpp"
+ 1 nil 27 "/usr/src/sb/ODBI_BHP.hpp")
+ ("PCC-02151: line 21 column 40 file /usr/src/sb/ODBI_BHP.hpp:"
+ 1 40 21 "/usr/src/sb/ODBI_BHP.hpp")
+ ;; perl
+ ("syntax error at automake line 922, near \"':'\""
+ 14 nil 922 "automake")
+ ("Died at test.pl line 27."
+ 6 nil 27 "test.pl")
+ ("store::odrecall('File_A', 'x2') called at store.pm line 90"
+ 40 nil 90 "store.pm")
+ ("\t(in cleanup) something bad at foo.pl line 3 during global destruction."
+ 29 nil 3 "foo.pl")
+ ("GLib-GObject-WARNING **: /build/buildd/glib2.0-2.14.5/gobject/gsignal.c:1741: instance `0x8206790' has no handler with id `1234' at t-compilation-perl-gtk.pl line 3."
+ 130 nil 3 "t-compilation-perl-gtk.pl")
+ ;; php
+ ("Parse error: parse error, unexpected $ in main.php on line 59"
+ 1 nil 59 "main.php")
+ ("Fatal error: Call to undefined function: mysql_pconnect() in db.inc on line 66"
+ 1 nil 66 "db.inc")
+ ;; ruby
+ ("plain-exception.rb:7:in `fun': unhandled exception"
+ 1 nil 7 "plain-exception.rb")
+ ("\tfrom plain-exception.rb:3:in `proxy'" 2 nil 3 "plain-exception.rb")
+ ("\tfrom plain-exception.rb:12" 2 nil 12 "plain-exception.rb")
+ ;; ruby-Test::Unit
+ ;; FIXME
+ (" [examples/test-unit.rb:28:in `here_is_a_deep_assert'"
+ 5 nil 28 "examples/test-unit.rb")
+ (" examples/test-unit.rb:19:in `test_a_deep_assert']:"
+ 6 nil 19 "examples/test-unit.rb")
+ ("examples/test-unit.rb:10:in `test_assert_raise'"
+ 1 nil 10 "examples/test-unit.rb")
+ ;; rxp
+ ("Error: Mismatched end tag: expected </geroup>, got </group>\nin unnamed entity at line 71 char 8 of file:///home/reto/test/group.xml"
+ 1 8 71 "/home/reto/test/group.xml")
+ ("Warning: Start tag for undeclared element geroup\nin unnamed entity at line 4 char 8 of file:///home/reto/test/group.xml"
+ 1 8 4 "/home/reto/test/group.xml")
+ ;; sparc-pascal-file sparc-pascal-line sparc-pascal-example
+ ("Thu May 14 10:46:12 1992 mom3.p:"
+ 1 nil nil "mom3.p")
+ ;; sun
+ ("cc-1020 CC: REMARK File = CUI_App.h, Line = 735"
+ 13 nil 735 "CUI_App.h")
+ ("cc-1070 cc: WARNING File = linkl.c, Line = 38"
+ 13 nil 38 "linkl.c")
+ ("cf90-113 f90comp: ERROR NSE, File = Hoved.f90, Line = 16, Column = 3"
+ 18 3 16 "Hoved.f90")
+ ;; sun-ada
+ ("/home3/xdhar/rcds_rc/main.a, line 361, char 6:syntax error: \",\" inserted"
+ 1 6 361 "/home3/xdhar/rcds_rc/main.a")
+ ;; 4bsd
+ ("/usr/src/foo/foo.c(8): warning: w may be used before set"
+ 1 nil 8 "/usr/src/foo/foo.c")
+ ("/usr/src/foo/foo.c(9): error: w is used before set"
+ 1 nil 9 "/usr/src/foo/foo.c")
+ ("strcmp: variable # of args. llib-lc(359) :: /usr/src/foo/foo.c(8)"
+ 44 nil 8 "/usr/src/foo/foo.c")
+ ("bloofle defined( /users/wolfgang/foo.c(4) ), but never used"
+ 18 nil 4 "/users/wolfgang/foo.c")
+ ;; perl--Pod::Checker
+ ;; FIXME
+ ;; *** ERROR: Spurious text after =cut at line 193 in file foo.pm
+ ;; *** ERROR: =over on line 37 without closing =back at line EOF in file bar.pm
+ ;; *** ERROR: =over on line 1 without closing =back (at head1) at line 3 in file x.pod
+ ;; perl--Test
+ ("# Failed test 1 in foo.t at line 6"
+ 1 nil 6 "foo.t")
+ ;; perl--Test::Harness
+ ("NOK 1# Test 1 got: \"1234\" (t/foo.t at line 46)"
+ 1 nil 46 "t/foo.t")
+ ;; weblint
+ ("index.html (13:1) Unknown element <fdjsk>"
+ 1 1 13 "index.html"))
+ "List of tests for `compilation-error-regexp-alist'.
+Each element has the form (STR POS COLUMN LINE FILENAME [TYPE]),
+where STR is an error string, POS is the position of the error in
+STR, COLUMN and LINE are the reported column and line numbers (or
+nil) for that error, FILENAME is the reported filename, and TYPE
+is 0 for an information message, 1 for a warning, and 2 for an
+error.
+
+LINE can also be of the form (LINE . END-LINE) meaning a range of
+lines. COLUMN can also be of the form (COLUMN . END-COLUMN)
+meaning a range of columns starting on LINE and ending on
+END-LINE, if that matched. TYPE can be left out, in which case
+any message type is accepted.")
+
+(defun compile--test-error-line (test)
+ (erase-buffer)
+ (setq compilation-locs (make-hash-table))
+ (insert (car test))
+ (compilation-parse-errors (point-min) (point-max))
+ (let ((msg (get-text-property (nth 1 test) 'compilation-message)))
+ (should msg)
+ (let ((loc (compilation--message->loc msg))
+ (col (nth 2 test))
+ (line (nth 3 test))
+ (file (nth 4 test))
+ (type (nth 5 test))
+ end-col end-line)
+ (if (consp col)
+ (setq end-col (cdr col) col (car col)))
+ (if (consp line)
+ (setq end-line (cdr line) line (car line)))
+ (should (equal (compilation--loc->col loc) col))
+ (should (equal (compilation--loc->line loc) line))
+ (when file
+ (should (equal (caar (compilation--loc->file-struct loc)) file)))
+ (when end-col
+ (should (equal (car (cadr (nth 2 (compilation--loc->file-struct loc))))
+ end-col)))
+ (should (equal (car (nth 2 (compilation--loc->file-struct loc)))
+ (or end-line line)))
+ (when type
+ (should (equal type (compilation--message->type msg)))))))
+
+(ert-deftest compile-test-error-regexps ()
+ "Test the `compilation-error-regexp-alist' regexps.
+The test data is in `compile-tests--test-regexps-data'."
+ (with-temp-buffer
+ (font-lock-mode -1)
+ (mapc #'compile--test-error-line compile-tests--test-regexps-data)))
+
+;;; compile-tests.el ends here
diff --git a/test/lisp/progmodes/elisp-mode-tests.el b/test/lisp/progmodes/elisp-mode-tests.el
new file mode 100644
index 00000000000..12e61cf8d18
--- /dev/null
+++ b/test/lisp/progmodes/elisp-mode-tests.el
@@ -0,0 +1,676 @@
+;;; elisp-mode-tests.el --- Tests for emacs-lisp-mode -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2015-2016 Free Software Foundation, Inc.
+
+;; Author: Dmitry Gutov <dgutov@yandex.ru>
+;; Author: Stephen Leake <stephen_leake@member.fsf.org>
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Code:
+
+(require 'ert)
+(require 'xref)
+
+;;; Completion
+
+(defun elisp--test-completions ()
+ (let ((data (elisp-completion-at-point)))
+ (all-completions (buffer-substring (nth 0 data) (nth 1 data))
+ (nth 2 data)
+ (plist-get (nthcdr 3 data) :predicate))))
+
+(ert-deftest elisp-completes-functions ()
+ (with-temp-buffer
+ (emacs-lisp-mode)
+ (insert "(ba")
+ (let ((comps (elisp--test-completions)))
+ (should (member "backup-buffer" comps))
+ (should-not (member "backup-inhibited" comps)))))
+
+(ert-deftest elisp-completes-variables ()
+ (with-temp-buffer
+ (emacs-lisp-mode)
+ (insert "(foo ba")
+ (let ((comps (elisp--test-completions)))
+ (should (member "backup-inhibited" comps))
+ (should-not (member "backup-buffer" comps)))))
+
+(ert-deftest elisp-completes-anything-quoted ()
+ (dolist (text '("`(foo ba" "(foo 'ba"
+ "`(,foo ba" "`,(foo `ba"
+ "'(foo (ba"))
+ (with-temp-buffer
+ (emacs-lisp-mode)
+ (insert text)
+ (let ((comps (elisp--test-completions)))
+ (should (member "backup-inhibited" comps))
+ (should (member "backup-buffer" comps))
+ (should (member "backup" comps))))))
+
+(ert-deftest elisp-completes-variables-unquoted ()
+ (dolist (text '("`(foo ,ba" "`(,(foo ba" "`(,ba"))
+ (with-temp-buffer
+ (emacs-lisp-mode)
+ (insert text)
+ (let ((comps (elisp--test-completions)))
+ (should (member "backup-inhibited" comps))
+ (should-not (member "backup-buffer" comps))))))
+
+(ert-deftest elisp-completes-functions-in-special-macros ()
+ (dolist (text '("(declare-function ba" "(cl-callf2 ba"))
+ (with-temp-buffer
+ (emacs-lisp-mode)
+ (insert text)
+ (let ((comps (elisp--test-completions)))
+ (should (member "backup-buffer" comps))
+ (should-not (member "backup-inhibited" comps))))))
+
+(ert-deftest elisp-completes-functions-after-hash-quote ()
+ (ert-deftest elisp-completes-functions-after-let-bindings ()
+ (with-temp-buffer
+ (emacs-lisp-mode)
+ (insert "#'ba")
+ (let ((comps (elisp--test-completions)))
+ (should (member "backup-buffer" comps))
+ (should-not (member "backup-inhibited" comps))))))
+
+(ert-deftest elisp-completes-local-variables ()
+ (with-temp-buffer
+ (emacs-lisp-mode)
+ (insert "(let ((bar 1) baz) (foo ba")
+ (let ((comps (elisp--test-completions)))
+ (should (member "backup-inhibited" comps))
+ (should (member "bar" comps))
+ (should (member "baz" comps)))))
+
+(ert-deftest elisp-completest-variables-in-let-bindings ()
+ (dolist (text '("(let (ba" "(let* ((ba"))
+ (with-temp-buffer
+ (emacs-lisp-mode)
+ (insert text)
+ (let ((comps (elisp--test-completions)))
+ (should (member "backup-inhibited" comps))
+ (should-not (member "backup-buffer" comps))))))
+
+(ert-deftest elisp-completes-functions-after-let-bindings ()
+ (with-temp-buffer
+ (emacs-lisp-mode)
+ (insert "(let ((bar 1) (baz 2)) (ba")
+ (let ((comps (elisp--test-completions)))
+ (should (member "backup-buffer" comps))
+ (should-not (member "backup-inhibited" comps)))))
+
+;;; xref
+
+(defun xref-elisp-test-descr-to-target (xref)
+ "Return an appropriate `looking-at' match string for XREF."
+ (let* ((loc (xref-item-location xref))
+ (type (or (xref-elisp-location-type loc)
+ 'defun)))
+
+ (cl-case type
+ (defalias
+ ;; summary: "(defalias xref)"
+ ;; target : "(defalias 'xref"
+ (concat "(defalias '" (substring (xref-item-summary xref) 10 -1)))
+
+ (defun
+ (let ((summary (xref-item-summary xref))
+ (file (xref-elisp-location-file loc)))
+ (cond
+ ((string= "c" (file-name-extension file))
+ ;; summary: "(defun buffer-live-p)"
+ ;; target : "DEFUN (buffer-live-p"
+ (concat
+ (upcase (substring summary 1 6))
+ " (\""
+ (substring summary 7 -1)
+ "\""))
+
+ (t
+ (substring summary 0 -1))
+ )))
+
+ (defvar
+ (let ((summary (xref-item-summary xref))
+ (file (xref-elisp-location-file loc)))
+ (cond
+ ((string= "c" (file-name-extension file))
+ ;; summary: "(defvar system-name)"
+ ;; target : "DEFVAR_LISP ("system-name", "
+ ;; summary: "(defvar abbrev-mode)"
+ ;; target : DEFVAR_PER_BUFFER ("abbrev-mode"
+ (concat
+ (upcase (substring summary 1 7))
+ (if (bufferp (variable-binding-locus (xref-elisp-location-symbol loc)))
+ "_PER_BUFFER (\""
+ "_LISP (\"")
+ (substring summary 8 -1)
+ "\""))
+
+ (t
+ (substring summary 0 -1))
+ )))
+
+ (feature
+ ;; summary: "(feature xref)"
+ ;; target : "(provide 'xref)"
+ (concat "(provide '" (substring (xref-item-summary xref) 9 -1)))
+
+ (otherwise
+ (substring (xref-item-summary xref) 0 -1))
+ )))
+
+
+(defun xref-elisp-test-run (xrefs expected-xrefs)
+ (should (= (length xrefs) (length expected-xrefs)))
+ (while xrefs
+ (let* ((xref (pop xrefs))
+ (expected (pop expected-xrefs))
+ (expected-xref (or (when (consp expected) (car expected)) expected))
+ (expected-source (when (consp expected) (cdr expected))))
+
+ ;; Downcase the filenames for case-insensitive file systems.
+ (setf (xref-elisp-location-file (oref xref location))
+ (downcase (xref-elisp-location-file (oref xref location))))
+
+ (setf (xref-elisp-location-file (oref expected-xref location))
+ (downcase (xref-elisp-location-file (oref expected-xref location))))
+
+ (should (equal xref expected-xref))
+
+ (xref--goto-location (xref-item-location xref))
+ (back-to-indentation)
+ (should (looking-at (or expected-source
+ (xref-elisp-test-descr-to-target expected)))))
+ ))
+
+(defmacro xref-elisp-deftest (name computed-xrefs expected-xrefs)
+ "Define an ert test for an xref-elisp feature.
+COMPUTED-XREFS and EXPECTED-XREFS are lists of xrefs, except if
+an element of EXPECTED-XREFS is a cons (XREF . TARGET), TARGET is
+matched to the found location; otherwise, match
+to (xref-elisp-test-descr-to-target xref)."
+ (declare (indent defun)
+ (debug (symbolp "name")))
+ `(ert-deftest ,(intern (concat "xref-elisp-test-" (symbol-name name))) ()
+ (let ((find-file-suppress-same-file-warnings t))
+ (xref-elisp-test-run ,computed-xrefs ,expected-xrefs)
+ )))
+
+;; When tests are run from the Makefile, 'default-directory' is $HOME,
+;; so we must provide this dir to expand-file-name in the expected
+;; results. This also allows running these tests from other
+;; directories.
+;;
+;; We add 'downcase' here to deliberately cause a potential problem on
+;; case-insensitive file systems. On such systems, `load-file-name'
+;; may not have the same case as the real file system, since the user
+;; can set `load-path' to have the wrong case (on my Windows system,
+;; `load-path' has the correct case, so this causes the expected test
+;; values to have the wrong case). This is handled in
+;; `xref-elisp-test-run'.
+(defconst emacs-test-dir (downcase (file-name-directory (or load-file-name (buffer-file-name)))))
+
+
+;; alphabetical by test name
+
+;; Autoloads require no special support; they are handled as functions.
+
+;; FIXME: defalias-defun-c cmpl-prefix-entry-head
+;; FIXME: defalias-defvar-el allout-mode-map
+
+(xref-elisp-deftest find-defs-constructor
+ (elisp--xref-find-definitions 'xref-make-elisp-location)
+ ;; 'xref-make-elisp-location' is just a name for the default
+ ;; constructor created by the cl-defstruct, so the location is the
+ ;; cl-defstruct location.
+ (list
+ (cons
+ (xref-make "(cl-defstruct (xref-elisp-location (:constructor xref-make-elisp-location)))"
+ (xref-make-elisp-location
+ 'xref-elisp-location 'define-type
+ (expand-file-name "../../../lisp/progmodes/elisp-mode.el" emacs-test-dir)))
+ ;; It's not worth adding another special case to `xref-elisp-test-descr-to-target' for this
+ "(cl-defstruct (xref-elisp-location")
+ ))
+
+(xref-elisp-deftest find-defs-defalias-defun-el
+ (elisp--xref-find-definitions 'Buffer-menu-sort)
+ (list
+ (xref-make "(defalias Buffer-menu-sort)"
+ (xref-make-elisp-location
+ 'Buffer-menu-sort 'defalias
+ (expand-file-name "../../../lisp/buff-menu.elc" emacs-test-dir)))
+ (xref-make "(defun tabulated-list-sort)"
+ (xref-make-elisp-location
+ 'tabulated-list-sort nil
+ (expand-file-name "../../../lisp/emacs-lisp/tabulated-list.el" emacs-test-dir)))
+ ))
+
+;; FIXME: defconst
+
+;; FIXME: eieio defclass
+
+;; Possible ways of defining the default method implementation for a
+;; generic function. We declare these here, so we know we cover all
+;; cases, and we don't rely on other code not changing.
+;;
+;; When the generic and default method are declared in the same place,
+;; elisp--xref-find-definitions only returns one.
+
+(cl-defstruct (xref-elisp-root-type)
+ slot-1)
+
+(cl-defgeneric xref-elisp-generic-no-methods (arg1 arg2)
+ "doc string generic no-methods"
+ ;; No default implementation, no methods, but fboundp is true for
+ ;; this symbol; it calls cl-no-applicable-method
+ )
+
+;; WORKAROUND: ‘this’ is unused, and the byte compiler complains, so
+;; it should be spelled ‘_this’. But for some unknown reason, that
+;; causes the batch mode test to fail; the symbol shows up as
+;; ‘this’. It passes in interactive tests, so I haven't been able to
+;; track down the problem.
+(cl-defmethod xref-elisp-generic-no-default ((this xref-elisp-root-type) arg2)
+ "doc string generic no-default xref-elisp-root-type"
+ "non-default for no-default")
+
+;; defgeneric after defmethod in file to ensure the fallback search
+;; method of just looking for the function name will fail.
+(cl-defgeneric xref-elisp-generic-no-default (arg1 arg2)
+ "doc string generic no-default generic"
+ ;; No default implementation; this function calls the cl-generic
+ ;; dispatching code.
+ )
+
+(cl-defgeneric xref-elisp-generic-co-located-default (arg1 arg2)
+ "doc string generic co-located-default"
+ "co-located default")
+
+(cl-defmethod xref-elisp-generic-co-located-default ((this xref-elisp-root-type) arg2)
+ "doc string generic co-located-default xref-elisp-root-type"
+ "non-default for co-located-default")
+
+(cl-defgeneric xref-elisp-generic-separate-default (arg1 arg2)
+ "doc string generic separate-default"
+ ;; default implementation provided separately
+ )
+
+(cl-defmethod xref-elisp-generic-separate-default (arg1 arg2)
+ "doc string generic separate-default default"
+ "separate default")
+
+(cl-defmethod xref-elisp-generic-separate-default ((this xref-elisp-root-type) arg2)
+ "doc string generic separate-default xref-elisp-root-type"
+ "non-default for separate-default")
+
+(cl-defmethod xref-elisp-generic-implicit-generic (arg1 arg2)
+ "doc string generic implicit-generic default"
+ "default for implicit generic")
+
+(cl-defmethod xref-elisp-generic-implicit-generic ((this xref-elisp-root-type) arg2)
+ "doc string generic implicit-generic xref-elisp-root-type"
+ "non-default for implicit generic")
+
+
+(xref-elisp-deftest find-defs-defgeneric-no-methods
+ (elisp--xref-find-definitions 'xref-elisp-generic-no-methods)
+ (list
+ (xref-make "(cl-defgeneric xref-elisp-generic-no-methods)"
+ (xref-make-elisp-location
+ 'xref-elisp-generic-no-methods 'cl-defgeneric
+ (expand-file-name "elisp-mode-tests.el" emacs-test-dir)))
+ ))
+
+(xref-elisp-deftest find-defs-defgeneric-no-default
+ (elisp--xref-find-definitions 'xref-elisp-generic-no-default)
+ (list
+ (xref-make "(cl-defgeneric xref-elisp-generic-no-default)"
+ (xref-make-elisp-location
+ 'xref-elisp-generic-no-default 'cl-defgeneric
+ (expand-file-name "elisp-mode-tests.el" emacs-test-dir)))
+ (xref-make "(cl-defmethod xref-elisp-generic-no-default ((this xref-elisp-root-type) arg2))"
+ (xref-make-elisp-location
+ (cl--generic-load-hist-format
+ 'xref-elisp-generic-no-default nil '(xref-elisp-root-type t))
+ 'cl-defmethod
+ (expand-file-name "elisp-mode-tests.el" emacs-test-dir)))
+ ))
+
+(xref-elisp-deftest find-defs-defgeneric-co-located-default
+ (elisp--xref-find-definitions 'xref-elisp-generic-co-located-default)
+ (list
+ (xref-make "(cl-defgeneric xref-elisp-generic-co-located-default)"
+ (xref-make-elisp-location
+ 'xref-elisp-generic-co-located-default 'cl-defgeneric
+ (expand-file-name "elisp-mode-tests.el" emacs-test-dir)))
+ (xref-make "(cl-defmethod xref-elisp-generic-co-located-default ((this xref-elisp-root-type) arg2))"
+ (xref-make-elisp-location
+ (cl--generic-load-hist-format
+ 'xref-elisp-generic-co-located-default nil
+ '(xref-elisp-root-type t))
+ 'cl-defmethod
+ (expand-file-name "elisp-mode-tests.el" emacs-test-dir)))
+ ))
+
+(xref-elisp-deftest find-defs-defgeneric-separate-default
+ (elisp--xref-find-definitions 'xref-elisp-generic-separate-default)
+ (list
+ (xref-make "(cl-defgeneric xref-elisp-generic-separate-default)"
+ (xref-make-elisp-location
+ 'xref-elisp-generic-separate-default 'cl-defgeneric
+ (expand-file-name "elisp-mode-tests.el" emacs-test-dir)))
+ (xref-make "(cl-defmethod xref-elisp-generic-separate-default (arg1 arg2))"
+ (xref-make-elisp-location
+ (cl--generic-load-hist-format
+ 'xref-elisp-generic-separate-default nil '(t t))
+ 'cl-defmethod
+ (expand-file-name "elisp-mode-tests.el" emacs-test-dir)))
+ (xref-make "(cl-defmethod xref-elisp-generic-separate-default ((this xref-elisp-root-type) arg2))"
+ (xref-make-elisp-location
+ (cl--generic-load-hist-format
+ 'xref-elisp-generic-separate-default nil
+ '(xref-elisp-root-type t))
+ 'cl-defmethod
+ (expand-file-name "elisp-mode-tests.el" emacs-test-dir)))
+ ))
+
+(xref-elisp-deftest find-defs-defgeneric-implicit-generic
+ (elisp--xref-find-definitions 'xref-elisp-generic-implicit-generic)
+ (list
+ (xref-make "(cl-defmethod xref-elisp-generic-implicit-generic (arg1 arg2))"
+ (xref-make-elisp-location
+ (cl--generic-load-hist-format
+ 'xref-elisp-generic-implicit-generic nil '(t t))
+ 'cl-defmethod
+ (expand-file-name "elisp-mode-tests.el" emacs-test-dir)))
+ (xref-make "(cl-defmethod xref-elisp-generic-implicit-generic ((this xref-elisp-root-type) arg2))"
+ (xref-make-elisp-location
+ (cl--generic-load-hist-format
+ 'xref-elisp-generic-implicit-generic nil
+ '(xref-elisp-root-type t))
+ 'cl-defmethod
+ (expand-file-name "elisp-mode-tests.el" emacs-test-dir)))
+ ))
+
+;; Test that we handle more than one method
+
+;; When run from the Makefile, etags is not loaded at compile time,
+;; but it is by the time this test is run. interactively; don't fail
+;; for that.
+(require 'etags)
+(xref-elisp-deftest find-defs-defgeneric-el
+ (elisp--xref-find-definitions 'xref-location-marker)
+ (list
+ (xref-make "(cl-defgeneric xref-location-marker)"
+ (xref-make-elisp-location
+ 'xref-location-marker 'cl-defgeneric
+ (expand-file-name "../../../lisp/progmodes/xref.el" emacs-test-dir)))
+ (xref-make "(cl-defmethod xref-location-marker ((l xref-elisp-location)))"
+ (xref-make-elisp-location
+ (cl--generic-load-hist-format
+ 'xref-location-marker nil '(xref-elisp-location))
+ 'cl-defmethod
+ (expand-file-name "../../../lisp/progmodes/elisp-mode.el" emacs-test-dir)))
+ (xref-make "(cl-defmethod xref-location-marker ((l xref-file-location)))"
+ (xref-make-elisp-location
+ (cl--generic-load-hist-format
+ 'xref-location-marker nil '(xref-file-location))
+ 'cl-defmethod
+ (expand-file-name "../../../lisp/progmodes/xref.el" emacs-test-dir)))
+ (xref-make "(cl-defmethod xref-location-marker ((l xref-buffer-location)))"
+ (xref-make-elisp-location
+ (cl--generic-load-hist-format
+ 'xref-location-marker nil '(xref-buffer-location))
+ 'cl-defmethod
+ (expand-file-name "../../../lisp/progmodes/xref.el" emacs-test-dir)))
+ (xref-make "(cl-defmethod xref-location-marker ((l xref-bogus-location)))"
+ (xref-make-elisp-location
+ (cl--generic-load-hist-format
+ 'xref-location-marker nil '(xref-bogus-location))
+ 'cl-defmethod
+ (expand-file-name "../../../lisp/progmodes/xref.el" emacs-test-dir)))
+ (xref-make "(cl-defmethod xref-location-marker ((l xref-etags-location)))"
+ (xref-make-elisp-location
+ (cl--generic-load-hist-format
+ 'xref-location-marker nil '(xref-etags-location))
+ 'cl-defmethod
+ (expand-file-name "../../../lisp/progmodes/etags.el" emacs-test-dir)))
+ ))
+
+(xref-elisp-deftest find-defs-defgeneric-eval
+ (elisp--xref-find-definitions (eval '(cl-defgeneric stephe-leake-cl-defgeneric ())))
+ nil)
+
+;; Define some mode-local overloadable/overridden functions for xref to find
+(require 'mode-local)
+
+(define-overloadable-function xref-elisp-overloadable-no-methods ()
+ "doc string overloadable no-methods")
+
+(define-overloadable-function xref-elisp-overloadable-no-default ()
+ "doc string overloadable no-default")
+
+;; FIXME: byte compiler complains about unused lexical arguments
+;; generated by this macro.
+(define-mode-local-override xref-elisp-overloadable-no-default c-mode
+ (start end &optional nonterminal depth returnonerror)
+ "doc string overloadable no-default c-mode."
+ "result overloadable no-default c-mode.")
+
+(define-overloadable-function xref-elisp-overloadable-co-located-default ()
+ "doc string overloadable co-located-default"
+ "result overloadable co-located-default.")
+
+(define-mode-local-override xref-elisp-overloadable-co-located-default c-mode
+ (start end &optional nonterminal depth returnonerror)
+ "doc string overloadable co-located-default c-mode."
+ "result overloadable co-located-default c-mode.")
+
+(define-overloadable-function xref-elisp-overloadable-separate-default ()
+ "doc string overloadable separate-default.")
+
+(defun xref-elisp-overloadable-separate-default-default ()
+ "doc string overloadable separate-default default"
+ "result overloadable separate-default.")
+
+(define-mode-local-override xref-elisp-overloadable-separate-default c-mode
+ (start end &optional nonterminal depth returnonerror)
+ "doc string overloadable separate-default c-mode."
+ "result overloadable separate-default c-mode.")
+
+(xref-elisp-deftest find-defs-define-overload-no-methods
+ (elisp--xref-find-definitions 'xref-elisp-overloadable-no-methods)
+ (list
+ (xref-make "(define-overloadable-function xref-elisp-overloadable-no-methods)"
+ (xref-make-elisp-location
+ 'xref-elisp-overloadable-no-methods 'define-overloadable-function
+ (expand-file-name "elisp-mode-tests.el" emacs-test-dir)))
+ ))
+
+(xref-elisp-deftest find-defs-define-overload-no-default
+ (elisp--xref-find-definitions 'xref-elisp-overloadable-no-default)
+ (list
+ (xref-make "(define-overloadable-function xref-elisp-overloadable-no-default)"
+ (xref-make-elisp-location
+ 'xref-elisp-overloadable-no-default 'define-overloadable-function
+ (expand-file-name "elisp-mode-tests.el" emacs-test-dir)))
+ (xref-make "(define-mode-local-override xref-elisp-overloadable-no-default c-mode)"
+ (xref-make-elisp-location
+ '(xref-elisp-overloadable-no-default-c-mode . c-mode) 'define-mode-local-override
+ (expand-file-name "elisp-mode-tests.el" emacs-test-dir)))
+ ))
+
+(xref-elisp-deftest find-defs-define-overload-co-located-default
+ (elisp--xref-find-definitions 'xref-elisp-overloadable-co-located-default)
+ (list
+ (xref-make "(define-overloadable-function xref-elisp-overloadable-co-located-default)"
+ (xref-make-elisp-location
+ 'xref-elisp-overloadable-co-located-default 'define-overloadable-function
+ (expand-file-name "elisp-mode-tests.el" emacs-test-dir)))
+ (xref-make "(define-mode-local-override xref-elisp-overloadable-co-located-default c-mode)"
+ (xref-make-elisp-location
+ '(xref-elisp-overloadable-co-located-default-c-mode . c-mode) 'define-mode-local-override
+ (expand-file-name "elisp-mode-tests.el" emacs-test-dir)))
+ ))
+
+(xref-elisp-deftest find-defs-define-overload-separate-default
+ (elisp--xref-find-definitions 'xref-elisp-overloadable-separate-default)
+ (list
+ (xref-make "(define-overloadable-function xref-elisp-overloadable-separate-default)"
+ (xref-make-elisp-location
+ 'xref-elisp-overloadable-separate-default 'define-overloadable-function
+ (expand-file-name "elisp-mode-tests.el" emacs-test-dir)))
+ (xref-make "(defun xref-elisp-overloadable-separate-default-default)"
+ (xref-make-elisp-location
+ 'xref-elisp-overloadable-separate-default-default nil
+ (expand-file-name "elisp-mode-tests.el" emacs-test-dir)))
+ (xref-make "(define-mode-local-override xref-elisp-overloadable-separate-default c-mode)"
+ (xref-make-elisp-location
+ '(xref-elisp-overloadable-separate-default-c-mode . c-mode) 'define-mode-local-override
+ (expand-file-name "elisp-mode-tests.el" emacs-test-dir)))
+ ))
+
+(xref-elisp-deftest find-defs-defun-el
+ (elisp--xref-find-definitions 'xref-find-definitions)
+ (list
+ (xref-make "(defun xref-find-definitions)"
+ (xref-make-elisp-location
+ 'xref-find-definitions nil
+ (expand-file-name "../../../lisp/progmodes/xref.el" emacs-test-dir)))))
+
+(xref-elisp-deftest find-defs-defun-eval
+ (elisp--xref-find-definitions (eval '(defun stephe-leake-defun ())))
+ nil)
+
+(xref-elisp-deftest find-defs-defun-c
+ (elisp--xref-find-definitions 'buffer-live-p)
+ (list
+ (xref-make "(defun buffer-live-p)"
+ (xref-make-elisp-location 'buffer-live-p nil "src/buffer.c"))))
+
+;; FIXME: deftype
+
+(xref-elisp-deftest find-defs-defun-c-defvar-c
+ (xref-backend-definitions 'elisp "system-name")
+ (list
+ (xref-make "(defvar system-name)"
+ (xref-make-elisp-location 'system-name 'defvar "src/editfns.c"))
+ (xref-make "(defun system-name)"
+ (xref-make-elisp-location 'system-name nil "src/editfns.c")))
+ )
+
+(xref-elisp-deftest find-defs-defun-el-defvar-c
+ (xref-backend-definitions 'elisp "abbrev-mode")
+ ;; It's a minor mode, but the variable is defined in buffer.c
+ (list
+ (xref-make "(defvar abbrev-mode)"
+ (xref-make-elisp-location 'abbrev-mode 'defvar "src/buffer.c"))
+ (cons
+ (xref-make "(defun abbrev-mode)"
+ (xref-make-elisp-location
+ 'abbrev-mode nil
+ (expand-file-name "../../../lisp/abbrev.el" emacs-test-dir)))
+ "(define-minor-mode abbrev-mode"))
+ )
+
+;; Source for both variable and defun is "(define-minor-mode
+;; compilation-minor-mode". There is no way to tell that directly from
+;; the symbol, but we can use (memq sym minor-mode-list) to detect
+;; that the symbol is a minor mode. See `elisp--xref-find-definitions'
+;; for more comments.
+;;
+;; IMPROVEME: return defvar instead of defun if source near starting
+;; point indicates the user is searching for a variable, not a
+;; function.
+(require 'compile) ;; not loaded by default at test time
+(xref-elisp-deftest find-defs-defun-defvar-el
+ (elisp--xref-find-definitions 'compilation-minor-mode)
+ (list
+ (cons
+ (xref-make "(defun compilation-minor-mode)"
+ (xref-make-elisp-location
+ 'compilation-minor-mode nil
+ (expand-file-name "../../../lisp/progmodes/compile.el" emacs-test-dir)))
+ "(define-minor-mode compilation-minor-mode")
+ ))
+
+(xref-elisp-deftest find-defs-defvar-el
+ (elisp--xref-find-definitions 'xref--marker-ring)
+ (list
+ (xref-make "(defvar xref--marker-ring)"
+ (xref-make-elisp-location
+ 'xref--marker-ring 'defvar
+ (expand-file-name "../../../lisp/progmodes/xref.el" emacs-test-dir)))
+ ))
+
+(xref-elisp-deftest find-defs-defvar-c
+ (elisp--xref-find-definitions 'default-directory)
+ (list
+ (cons
+ (xref-make "(defvar default-directory)"
+ (xref-make-elisp-location 'default-directory 'defvar "src/buffer.c"))
+ ;; IMPROVEME: we might be able to compute this target
+ "DEFVAR_PER_BUFFER (\"default-directory\"")))
+
+(xref-elisp-deftest find-defs-defvar-eval
+ (elisp--xref-find-definitions (eval '(defvar stephe-leake-defvar nil)))
+ nil)
+
+(xref-elisp-deftest find-defs-face-el
+ (elisp--xref-find-definitions 'font-lock-keyword-face)
+ ;; 'font-lock-keyword-face is both a face and a var
+ (list
+ (xref-make "(defvar font-lock-keyword-face)"
+ (xref-make-elisp-location
+ 'font-lock-keyword-face 'defvar
+ (expand-file-name "../../../lisp/font-lock.el" emacs-test-dir)))
+ (xref-make "(defface font-lock-keyword-face)"
+ (xref-make-elisp-location
+ 'font-lock-keyword-face 'defface
+ (expand-file-name "../../../lisp/font-lock.el" emacs-test-dir)))
+ ))
+
+(xref-elisp-deftest find-defs-face-eval
+ (elisp--xref-find-definitions (eval '(defface stephe-leake-defface nil "")))
+ nil)
+
+(xref-elisp-deftest find-defs-feature-el
+ (elisp--xref-find-definitions 'xref)
+ (list
+ (cons
+ (xref-make "(feature xref)"
+ (xref-make-elisp-location
+ 'xref 'feature
+ (expand-file-name "../../../lisp/progmodes/xref.el" emacs-test-dir)))
+ ";;; Code:")
+ ))
+
+(xref-elisp-deftest find-defs-feature-eval
+ (elisp--xref-find-definitions (eval '(provide 'stephe-leake-feature)))
+ nil)
+
+(ert-deftest elisp--preceding-sexp--char-name ()
+ (with-temp-buffer
+ (emacs-lisp-mode)
+ (insert "?\\N{HEAVY CHECK MARK}")
+ (should (equal (elisp--preceding-sexp) ?\N{HEAVY CHECK MARK}))))
+
+(provide 'elisp-mode-tests)
+;;; elisp-mode-tests.el ends here
diff --git a/test/lisp/progmodes/etags-tests.el b/test/lisp/progmodes/etags-tests.el
new file mode 100644
index 00000000000..a992a17dc46
--- /dev/null
+++ b/test/lisp/progmodes/etags-tests.el
@@ -0,0 +1,91 @@
+;;; etags-tests.el --- Test suite for etags.el.
+
+;; Copyright (C) 2016 Free Software Foundation, Inc.
+
+;; Author: Eli Zaretskii <eliz@gnu.org>
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Code:
+
+(require 'ert)
+(require 'etags)
+
+(defvar his-masters-voice t)
+
+(defun y-or-n-p (_prompt)
+ "Replacement for `y-or-n-p' that returns what we tell it to."
+ his-masters-voice)
+
+(ert-deftest etags-bug-158 ()
+ "Test finding tags with local and global tags tables."
+ (let ((buf-with-global-tags (get-buffer-create "*buf-global*"))
+ (buf-with-local-tags (get-buffer-create "*buf-local*"))
+ xref-buf)
+ (set-buffer buf-with-global-tags)
+ (setq default-directory (expand-file-name "."))
+ (visit-tags-table
+ (expand-file-name "manual/etags/ETAGS.good_1"
+ (getenv "EMACS_TEST_DIRECTORY")))
+ ;; Check that tags in ETAGS.good_1 are recognized.
+ (setq xref-buf (xref-find-definitions "LL_Task_Procedure_Access/t"))
+ (should (bufferp xref-buf))
+ (kill-buffer xref-buf)
+ (setq xref-buf (xref-find-definitions "PrintAdd"))
+ (should (bufferp xref-buf))
+ (kill-buffer xref-buf)
+ ;; Check that tags not in ETAGS.good_1, but in ETAGS.good_3, are
+ ;; NOT recognized.
+ (should-error (xref-find-definitions "intNumber") :type 'user-error)
+ (kill-buffer xref-buf)
+ (set-buffer buf-with-local-tags)
+ (setq default-directory (expand-file-name "."))
+ (let (his-masters-voice)
+ (visit-tags-table
+ (expand-file-name "manual/etags/ETAGS.good_3"
+ (getenv "EMACS_TEST_DIRECTORY"))
+ t))
+ ;; Check that tags in ETAGS.good_1 are recognized.
+ (setq xref-buf (xref-find-definitions "LL_Task_Procedure_Access/t"))
+ (should (bufferp xref-buf))
+ (kill-buffer xref-buf)
+ (setq xref-buf (xref-find-definitions "PrintAdd"))
+ (should (bufferp xref-buf))
+ (kill-buffer xref-buf)
+ ;; Check that tags in ETAGS.good_3 are recognized. This is a test
+ ;; for bug#158.
+ (setq xref-buf (xref-find-definitions "intNumber"))
+ (should (or (null xref-buf)
+ (bufferp xref-buf)))
+ ;; Bug #17326
+ (should (string= (file-name-nondirectory
+ (buffer-local-value 'tags-file-name buf-with-local-tags))
+ "ETAGS.good_3"))
+ (should (string= (file-name-nondirectory
+ (default-value 'tags-file-name))
+ "ETAGS.good_1"))
+ (if (bufferp xref-buf) (kill-buffer xref-buf))))
+
+(ert-deftest etags-bug-23164 ()
+ "Test that setting a local value of tags table doesn't signal errors."
+ (set-buffer (get-buffer-create "*foobar*"))
+ (fundamental-mode)
+ (visit-tags-table
+ (expand-file-name "manual/etags/ETAGS.good_3"
+ (getenv "EMACS_TEST_DIRECTORY"))
+ t)
+ (should (equal (should-error (xref-find-definitions "foobar123"))
+ '(user-error "No definitions found for: foobar123"))))
diff --git a/test/lisp/progmodes/f90.el b/test/lisp/progmodes/f90.el
new file mode 100644
index 00000000000..29c608847f1
--- /dev/null
+++ b/test/lisp/progmodes/f90.el
@@ -0,0 +1,276 @@
+;;; f90.el --- tests for progmodes/f90.el
+
+;; Copyright (C) 2011-2016 Free Software Foundation, Inc.
+
+;; Author: Glenn Morris <rgm@gnu.org>
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This file does not have "test" in the name, because it lives under
+;; a test/ directory, so that would be superfluous.
+
+;;; Code:
+
+(require 'ert)
+(require 'f90)
+
+(defconst f90-test-indent "\
+!! Comment before code.
+!!! Comments before code.
+#preprocessor before code
+
+program progname
+
+ implicit none
+
+ integer :: i
+
+ !! Comment.
+
+ do i = 1, 10
+
+#preprocessor
+
+ !! Comment.
+ if ( i % 2 == 0 ) then
+ !! Comment.
+ cycle
+ else
+ write(*,*) i
+ end if
+ end do
+
+!!! Comment.
+
+end program progname
+"
+ "Test string for F90 indentation.")
+
+(ert-deftest f90-test-indent ()
+ "Test F90 indentation."
+ (with-temp-buffer
+ (f90-mode)
+ (insert f90-test-indent)
+ (indent-rigidly (point-min) (point-max) -999)
+ (f90-indent-region (point-min) (point-max))
+ (should (string-equal (buffer-string) f90-test-indent))))
+
+(ert-deftest f90-test-bug3729 ()
+ "Test for http://debbugs.gnu.org/3729 ."
+ :expected-result :failed
+ (with-temp-buffer
+ (f90-mode)
+ (insert "!! Comment
+
+include \"file.f90\"
+
+subroutine test (x)
+ real x
+ x = x+1.
+ return
+end subroutine test")
+ (goto-char (point-min))
+ (forward-line 2)
+ (f90-indent-subprogram)
+ (should (= 0 (current-indentation)))))
+
+(ert-deftest f90-test-bug3730 ()
+ "Test for http://debbugs.gnu.org/3730 ."
+ (with-temp-buffer
+ (f90-mode)
+ (insert "a" )
+ (move-to-column 68 t)
+ (insert "(/ x /)")
+ (f90-do-auto-fill)
+ (beginning-of-line)
+ (skip-chars-forward "[ \t]")
+ (should (equal "&(/" (buffer-substring (point) (+ 3 (point)))))))
+
+;; TODO bug#5593
+
+(ert-deftest f90-test-bug8691 ()
+ "Test for http://debbugs.gnu.org/8691 ."
+ (with-temp-buffer
+ (f90-mode)
+ (insert "module modname
+type, bind(c) :: type1
+integer :: part1
+end type type1
+end module modname")
+ (f90-indent-subprogram)
+ (forward-line -1)
+ (should (= 2 (current-indentation)))))
+
+;; TODO bug#8812
+
+(ert-deftest f90-test-bug8820 ()
+ "Test for http://debbugs.gnu.org/8820 ."
+ (with-temp-buffer
+ (f90-mode)
+ (should (eq (char-syntax ?%) (string-to-char ".")))))
+
+(ert-deftest f90-test-bug9553a ()
+ "Test for http://debbugs.gnu.org/9553 ."
+ (with-temp-buffer
+ (f90-mode)
+ (insert "!!!")
+ (dotimes (_i 20) (insert " aaaa"))
+ (f90-do-auto-fill)
+ (beginning-of-line)
+ ;; This gives a more informative failure than looking-at.
+ (should (equal "!!! a" (buffer-substring (point) (+ 5 (point)))))))
+
+(ert-deftest f90-test-bug9553b ()
+ "Test for http://debbugs.gnu.org/9553 ."
+ (with-temp-buffer
+ (f90-mode)
+ (insert "!!!")
+ (dotimes (_i 13) (insert " aaaa"))
+ (insert "a, aaaa")
+ (f90-do-auto-fill)
+ (beginning-of-line)
+ (should (equal "!!! a" (buffer-substring (point) (+ 5 (point)))))))
+
+(ert-deftest f90-test-bug9690 ()
+ "Test for http://debbugs.gnu.org/9690 ."
+ (with-temp-buffer
+ (f90-mode)
+ (insert "#include \"foo.h\"")
+ (f90-indent-line)
+ (should (= 0 (current-indentation)))))
+
+(ert-deftest f90-test-bug13138 ()
+ "Test for http://debbugs.gnu.org/13138 ."
+ (with-temp-buffer
+ (f90-mode)
+ (insert "program prog
+ integer :: i = &
+#ifdef foo
+ & 1
+#else
+ & 2
+#endif
+
+ write(*,*) i
+end program prog")
+ (goto-char (point-min))
+ (forward-line 2)
+ (f90-indent-subprogram)
+ (should (= 0 (current-indentation)))))
+
+(ert-deftest f90-test-bug-19809 ()
+ "Test for http://debbugs.gnu.org/19809 ."
+ (with-temp-buffer
+ (f90-mode)
+ ;; The Fortran standard says that continued strings should have
+ ;; '&' at the start of continuation lines, but it seems gfortran
+ ;; allows them to be absent (albeit with a warning).
+ (insert "program prog
+ write (*,*), '&
+end program prog'
+end program prog")
+ (goto-char (point-min))
+ (f90-end-of-subprogram)
+ (should (= (point) (point-max)))))
+
+(ert-deftest f90-test-bug20680 ()
+ "Test for http://debbugs.gnu.org/20680 ."
+ (with-temp-buffer
+ (f90-mode)
+ (insert "module modname
+type, extends ( sometype ) :: type1
+integer :: part1
+end type type1
+end module modname")
+ (f90-indent-subprogram)
+ (forward-line -1)
+ (should (= 2 (current-indentation)))))
+
+(ert-deftest f90-test-bug20680b ()
+ "Test for http://debbugs.gnu.org/20680 ."
+ (with-temp-buffer
+ (f90-mode)
+ (insert "module modname
+enum, bind(c)
+enumerator :: e1 = 0
+end enum
+end module modname")
+ (f90-indent-subprogram)
+ (forward-line -1)
+ (should (= 2 (current-indentation)))))
+
+(ert-deftest f90-test-bug20969 ()
+ "Test for http://debbugs.gnu.org/20969 ."
+ (with-temp-buffer
+ (f90-mode)
+ (insert "module modname
+type, extends ( sometype ), private :: type1
+integer :: part1
+end type type1
+end module modname")
+ (f90-indent-subprogram)
+ (forward-line -1)
+ (should (= 2 (current-indentation)))))
+
+(ert-deftest f90-test-bug20969b ()
+ "Test for http://debbugs.gnu.org/20969 ."
+ (with-temp-buffer
+ (f90-mode)
+ (insert "module modname
+type, private, extends ( sometype ) :: type1
+integer :: part1
+end type type1
+end module modname")
+ (f90-indent-subprogram)
+ (forward-line -1)
+ (should (= 2 (current-indentation)))))
+
+(ert-deftest f90-test-bug21794 ()
+ "Test for http://debbugs.gnu.org/21794 ."
+ (with-temp-buffer
+ (f90-mode)
+ (insert "program prog
+do i=1,10
+associate (x => xa(i), y => ya(i))
+a(x,y,i) = fun(x,y,i)
+end associate
+end do
+end program prog")
+ (f90-indent-subprogram)
+ (forward-line -2)
+ (should (= 5 (current-indentation)))))
+
+(ert-deftest f90-test-bug25039 ()
+ "Test for http://debbugs.gnu.org/25039 ."
+ (with-temp-buffer
+ (f90-mode)
+ (insert "program prog
+select type (a)
+class is (c1)
+x = 1
+type is (t1)
+x = 2
+end select
+end program prog")
+ (f90-indent-subprogram)
+ (forward-line -3)
+ (should (= 2 (current-indentation))) ; type is
+ (forward-line -2)
+ (should (= 2 (current-indentation))))) ; class is
+
+;;; f90.el ends here
diff --git a/test/lisp/progmodes/flymake-resources/Makefile b/test/lisp/progmodes/flymake-resources/Makefile
new file mode 100644
index 00000000000..0f3f39791c8
--- /dev/null
+++ b/test/lisp/progmodes/flymake-resources/Makefile
@@ -0,0 +1,13 @@
+# Makefile for flymake tests
+
+CC_OPTS = -Wall
+
+## Recent gcc (e.g. 4.8.2 on RHEL7) can automatically colorize their output,
+## which can confuse flymake. Set GCC_COLORS to disable that.
+## This only seems to be an issue in batch mode, where you would not
+## normally use flymake, so it seems like just avoiding the issue
+## in this test is fine. Set flymake-log-level to 3 to investigate.
+check-syntax:
+ GCC_COLORS= $(CC) $(CC_OPTS) ${CHK_SOURCES}
+
+# eof
diff --git a/test/lisp/progmodes/flymake-resources/test.c b/test/lisp/progmodes/flymake-resources/test.c
new file mode 100644
index 00000000000..3a3926131f5
--- /dev/null
+++ b/test/lisp/progmodes/flymake-resources/test.c
@@ -0,0 +1,5 @@
+int main()
+{
+ char c = 1000;
+ return c;
+}
diff --git a/test/lisp/progmodes/flymake-resources/test.pl b/test/lisp/progmodes/flymake-resources/test.pl
new file mode 100644
index 00000000000..d5abcb47e7f
--- /dev/null
+++ b/test/lisp/progmodes/flymake-resources/test.pl
@@ -0,0 +1,2 @@
+@arr = [1,2,3,4];
+my $b = @arr[1];
diff --git a/test/lisp/progmodes/flymake-tests.el b/test/lisp/progmodes/flymake-tests.el
new file mode 100644
index 00000000000..386516190bb
--- /dev/null
+++ b/test/lisp/progmodes/flymake-tests.el
@@ -0,0 +1,80 @@
+;;; flymake-tests.el --- Test suite for flymake
+
+;; Copyright (C) 2011-2016 Free Software Foundation, Inc.
+
+;; Author: Eduard Wiebe <usenet@pusto.de>
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;; Code:
+(require 'ert)
+(require 'flymake)
+
+(defvar flymake-tests-data-directory
+ (expand-file-name "lisp/progmodes/flymake-resources" (getenv "EMACS_TEST_DIRECTORY"))
+ "Directory containing flymake test data.")
+
+
+;; Warning predicate
+(defun flymake-tests--current-face (file predicate)
+ (let ((buffer (find-file-noselect
+ (expand-file-name file flymake-tests-data-directory)))
+ (process-environment (cons "LC_ALL=C" process-environment))
+ (i 0))
+ (unwind-protect
+ (with-current-buffer buffer
+ (setq-local flymake-warning-predicate predicate)
+ (goto-char (point-min))
+ (flymake-mode 1)
+ ;; Weirdness here... http://debbugs.gnu.org/17647#25
+ (while (and flymake-is-running (< (setq i (1+ i)) 10))
+ (sleep-for (+ 0.5 flymake-no-changes-timeout)))
+ (flymake-goto-next-error)
+ (face-at-point))
+ (and buffer (let (kill-buffer-query-functions) (kill-buffer buffer))))))
+
+(ert-deftest warning-predicate-rx-gcc ()
+ "Test GCC warning via regexp predicate."
+ (skip-unless (and (executable-find "gcc") (executable-find "make")))
+ (should (eq 'flymake-warnline
+ (flymake-tests--current-face "test.c" "^[Ww]arning"))))
+
+(ert-deftest warning-predicate-function-gcc ()
+ "Test GCC warning via function predicate."
+ (skip-unless (and (executable-find "gcc") (executable-find "make")))
+ (should (eq 'flymake-warnline
+ (flymake-tests--current-face "test.c"
+ (lambda (msg) (string-match "^[Ww]arning" msg))))))
+
+(ert-deftest warning-predicate-rx-perl ()
+ "Test perl warning via regular expression predicate."
+ (skip-unless (executable-find "perl"))
+ (should (eq 'flymake-warnline
+ (flymake-tests--current-face "test.pl" "^Scalar value"))))
+
+(ert-deftest warning-predicate-function-perl ()
+ "Test perl warning via function predicate."
+ (skip-unless (executable-find "perl"))
+ (should (eq 'flymake-warnline
+ (flymake-tests--current-face
+ "test.pl"
+ (lambda (msg) (string-match "^Scalar value" msg))))))
+
+(provide 'flymake-tests)
+
+;;; flymake.el ends here
diff --git a/test/lisp/progmodes/python-tests.el b/test/lisp/progmodes/python-tests.el
new file mode 100644
index 00000000000..f6564dd58cc
--- /dev/null
+++ b/test/lisp/progmodes/python-tests.el
@@ -0,0 +1,5295 @@
+;;; python-tests.el --- Test suite for python.el
+
+;; Copyright (C) 2013-2016 Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;; Code:
+
+(require 'ert)
+(require 'python)
+
+;; Dependencies for testing:
+(require 'electric)
+(require 'hideshow)
+(require 'tramp-sh)
+
+
+(defmacro python-tests-with-temp-buffer (contents &rest body)
+ "Create a `python-mode' enabled temp buffer with CONTENTS.
+BODY is code to be executed within the temp buffer. Point is
+always located at the beginning of buffer."
+ (declare (indent 1) (debug t))
+ `(with-temp-buffer
+ (let ((python-indent-guess-indent-offset nil))
+ (python-mode)
+ (insert ,contents)
+ (goto-char (point-min))
+ ,@body)))
+
+(defmacro python-tests-with-temp-file (contents &rest body)
+ "Create a `python-mode' enabled file with CONTENTS.
+BODY is code to be executed within the temp buffer. Point is
+always located at the beginning of buffer."
+ (declare (indent 1) (debug t))
+ ;; temp-file never actually used for anything?
+ `(let* ((temp-file (make-temp-file "python-tests" nil ".py"))
+ (buffer (find-file-noselect temp-file))
+ (python-indent-guess-indent-offset nil))
+ (unwind-protect
+ (with-current-buffer buffer
+ (python-mode)
+ (insert ,contents)
+ (goto-char (point-min))
+ ,@body)
+ (and buffer (kill-buffer buffer))
+ (delete-file temp-file))))
+
+(defun python-tests-look-at (string &optional num restore-point)
+ "Move point at beginning of STRING in the current buffer.
+Optional argument NUM defaults to 1 and is an integer indicating
+how many occurrences must be found, when positive the search is
+done forwards, otherwise backwards. When RESTORE-POINT is
+non-nil the point is not moved but the position found is still
+returned. When searching forward and point is already looking at
+STRING, it is skipped so the next STRING occurrence is selected."
+ (let* ((num (or num 1))
+ (starting-point (point))
+ (string (regexp-quote string))
+ (search-fn (if (> num 0) #'re-search-forward #'re-search-backward))
+ (deinc-fn (if (> num 0) #'1- #'1+))
+ (found-point))
+ (prog2
+ (catch 'exit
+ (while (not (= num 0))
+ (when (and (> num 0)
+ (looking-at string))
+ ;; Moving forward and already looking at STRING, skip it.
+ (forward-char (length (match-string-no-properties 0))))
+ (and (not (funcall search-fn string nil t))
+ (throw 'exit t))
+ (when (> num 0)
+ ;; `re-search-forward' leaves point at the end of the
+ ;; occurrence, move back so point is at the beginning
+ ;; instead.
+ (forward-char (- (length (match-string-no-properties 0)))))
+ (setq
+ num (funcall deinc-fn num)
+ found-point (point))))
+ found-point
+ (and restore-point (goto-char starting-point)))))
+
+(defun python-tests-self-insert (char-or-str)
+ "Call `self-insert-command' for chars in CHAR-OR-STR."
+ (let ((chars
+ (cond
+ ((characterp char-or-str)
+ (list char-or-str))
+ ((stringp char-or-str)
+ (string-to-list char-or-str))
+ ((not
+ (cl-remove-if #'characterp char-or-str))
+ char-or-str)
+ (t (error "CHAR-OR-STR must be a char, string, or list of char")))))
+ (mapc
+ (lambda (char)
+ (let ((last-command-event char))
+ (call-interactively 'self-insert-command)))
+ chars)))
+
+(defun python-tests-visible-string (&optional min max)
+ "Return the buffer string excluding invisible overlays.
+Argument MIN and MAX delimit the region to be returned and
+default to `point-min' and `point-max' respectively."
+ (let* ((min (or min (point-min)))
+ (max (or max (point-max)))
+ (buffer (current-buffer))
+ (buffer-contents (buffer-substring-no-properties min max))
+ (overlays
+ (sort (overlays-in min max)
+ (lambda (a b)
+ (let ((overlay-end-a (overlay-end a))
+ (overlay-end-b (overlay-end b)))
+ (> overlay-end-a overlay-end-b))))))
+ (with-temp-buffer
+ (insert buffer-contents)
+ (dolist (overlay overlays)
+ (if (overlay-get overlay 'invisible)
+ (delete-region (overlay-start overlay)
+ (overlay-end overlay))))
+ (buffer-substring-no-properties (point-min) (point-max)))))
+
+
+;;; Tests for your tests, so you can test while you test.
+
+(ert-deftest python-tests-look-at-1 ()
+ "Test forward movement."
+ (python-tests-with-temp-buffer
+ "Lorem ipsum dolor sit amet, consectetur adipisicing elit,
+sed do eiusmod tempor incididunt ut labore et dolore magna
+aliqua."
+ (let ((expected (save-excursion
+ (dotimes (i 3)
+ (re-search-forward "et" nil t))
+ (forward-char -2)
+ (point))))
+ (should (= (python-tests-look-at "et" 3 t) expected))
+ ;; Even if NUM is bigger than found occurrences the point of last
+ ;; one should be returned.
+ (should (= (python-tests-look-at "et" 6 t) expected))
+ ;; If already looking at STRING, it should skip it.
+ (dotimes (i 2) (re-search-forward "et"))
+ (forward-char -2)
+ (should (= (python-tests-look-at "et") expected)))))
+
+(ert-deftest python-tests-look-at-2 ()
+ "Test backward movement."
+ (python-tests-with-temp-buffer
+ "Lorem ipsum dolor sit amet, consectetur adipisicing elit,
+sed do eiusmod tempor incididunt ut labore et dolore magna
+aliqua."
+ (let ((expected
+ (save-excursion
+ (re-search-forward "et" nil t)
+ (forward-char -2)
+ (point))))
+ (dotimes (i 3)
+ (re-search-forward "et" nil t))
+ (should (= (python-tests-look-at "et" -3 t) expected))
+ (should (= (python-tests-look-at "et" -6 t) expected)))))
+
+
+;;; Bindings
+
+
+;;; Python specialized rx
+
+
+;;; Font-lock and syntax
+
+(ert-deftest python-syntax-after-python-backspace ()
+ ;; `python-indent-dedent-line-backspace' garbles syntax
+ :expected-result :failed
+ (python-tests-with-temp-buffer
+ "\"\"\""
+ (goto-char (point-max))
+ (python-indent-dedent-line-backspace 1)
+ (should (string= (buffer-string) "\"\""))
+ (should (null (nth 3 (syntax-ppss))))))
+
+
+;;; Indentation
+
+;; See: http://www.python.org/dev/peps/pep-0008/#indentation
+
+(ert-deftest python-indent-pep8-1 ()
+ "First pep8 case."
+ (python-tests-with-temp-buffer
+ "# Aligned with opening delimiter
+foo = long_function_name(var_one, var_two,
+ var_three, var_four)
+"
+ (should (eq (car (python-indent-context)) :no-indent))
+ (should (= (python-indent-calculate-indentation) 0))
+ (python-tests-look-at "foo = long_function_name(var_one, var_two,")
+ (should (eq (car (python-indent-context)) :after-comment))
+ (should (= (python-indent-calculate-indentation) 0))
+ (python-tests-look-at "var_three, var_four)")
+ (should (eq (car (python-indent-context)) :inside-paren))
+ (should (= (python-indent-calculate-indentation) 25))))
+
+(ert-deftest python-indent-pep8-2 ()
+ "Second pep8 case."
+ (python-tests-with-temp-buffer
+ "# More indentation included to distinguish this from the rest.
+def long_function_name(
+ var_one, var_two, var_three,
+ var_four):
+ print (var_one)
+"
+ (should (eq (car (python-indent-context)) :no-indent))
+ (should (= (python-indent-calculate-indentation) 0))
+ (python-tests-look-at "def long_function_name(")
+ (should (eq (car (python-indent-context)) :after-comment))
+ (should (= (python-indent-calculate-indentation) 0))
+ (python-tests-look-at "var_one, var_two, var_three,")
+ (should (eq (car (python-indent-context))
+ :inside-paren-newline-start-from-block))
+ (should (= (python-indent-calculate-indentation) 8))
+ (python-tests-look-at "var_four):")
+ (should (eq (car (python-indent-context))
+ :inside-paren-newline-start-from-block))
+ (should (= (python-indent-calculate-indentation) 8))
+ (python-tests-look-at "print (var_one)")
+ (should (eq (car (python-indent-context))
+ :after-block-start))
+ (should (= (python-indent-calculate-indentation) 4))))
+
+(ert-deftest python-indent-pep8-3 ()
+ "Third pep8 case."
+ (python-tests-with-temp-buffer
+ "# Extra indentation is not necessary.
+foo = long_function_name(
+ var_one, var_two,
+ var_three, var_four)
+"
+ (should (eq (car (python-indent-context)) :no-indent))
+ (should (= (python-indent-calculate-indentation) 0))
+ (python-tests-look-at "foo = long_function_name(")
+ (should (eq (car (python-indent-context)) :after-comment))
+ (should (= (python-indent-calculate-indentation) 0))
+ (python-tests-look-at "var_one, var_two,")
+ (should (eq (car (python-indent-context)) :inside-paren-newline-start))
+ (should (= (python-indent-calculate-indentation) 4))
+ (python-tests-look-at "var_three, var_four)")
+ (should (eq (car (python-indent-context)) :inside-paren-newline-start))
+ (should (= (python-indent-calculate-indentation) 4))))
+
+(ert-deftest python-indent-base-case ()
+ "Check base case does not trigger errors."
+ (python-tests-with-temp-buffer
+ "
+
+"
+ (goto-char (point-min))
+ (should (eq (car (python-indent-context)) :no-indent))
+ (should (= (python-indent-calculate-indentation) 0))
+ (forward-line 1)
+ (should (eq (car (python-indent-context)) :no-indent))
+ (should (= (python-indent-calculate-indentation) 0))
+ (forward-line 1)
+ (should (eq (car (python-indent-context)) :no-indent))
+ (should (= (python-indent-calculate-indentation) 0))))
+
+(ert-deftest python-indent-after-comment-1 ()
+ "The most simple after-comment case that shouldn't fail."
+ (python-tests-with-temp-buffer
+ "# Contents will be modified to correct indentation
+class Blag(object):
+ def _on_child_complete(self, child_future):
+ if self.in_terminal_state():
+ pass
+ # We only complete when all our async children have entered a
+ # terminal state. At that point, if any child failed, we fail
+# with the exception with which the first child failed.
+"
+ (python-tests-look-at "# We only complete")
+ (should (eq (car (python-indent-context)) :after-block-end))
+ (should (= (python-indent-calculate-indentation) 8))
+ (python-tests-look-at "# terminal state")
+ (should (eq (car (python-indent-context)) :after-comment))
+ (should (= (python-indent-calculate-indentation) 8))
+ (python-tests-look-at "# with the exception")
+ (should (eq (car (python-indent-context)) :after-comment))
+ ;; This one indents relative to previous block, even given the fact
+ ;; that it was under-indented.
+ (should (= (python-indent-calculate-indentation) 4))
+ (python-tests-look-at "# terminal state" -1)
+ ;; It doesn't hurt to check again.
+ (should (eq (car (python-indent-context)) :after-comment))
+ (python-indent-line)
+ (should (= (current-indentation) 8))
+ (python-tests-look-at "# with the exception")
+ (should (eq (car (python-indent-context)) :after-comment))
+ ;; Now everything should be lined up.
+ (should (= (python-indent-calculate-indentation) 8))))
+
+(ert-deftest python-indent-after-comment-2 ()
+ "Test after-comment in weird cases."
+ (python-tests-with-temp-buffer
+ "# Contents will be modified to correct indentation
+def func(arg):
+ # I don't do much
+ return arg
+ # This comment is badly indented because the user forced so.
+ # At this line python.el wont dedent, user is always right.
+
+comment_wins_over_ender = True
+
+# yeah, that.
+"
+ (python-tests-look-at "# I don't do much")
+ (should (eq (car (python-indent-context)) :after-block-start))
+ (should (= (python-indent-calculate-indentation) 4))
+ (python-tests-look-at "return arg")
+ ;; Comment here just gets ignored, this line is not a comment so
+ ;; the rules won't apply here.
+ (should (eq (car (python-indent-context)) :after-block-start))
+ (should (= (python-indent-calculate-indentation) 4))
+ (python-tests-look-at "# This comment is badly indented")
+ (should (eq (car (python-indent-context)) :after-block-end))
+ ;; The return keyword do make indentation lose a level...
+ (should (= (python-indent-calculate-indentation) 0))
+ ;; ...but the current indentation was forced by the user.
+ (python-tests-look-at "# At this line python.el wont dedent")
+ (should (eq (car (python-indent-context)) :after-comment))
+ (should (= (python-indent-calculate-indentation) 4))
+ ;; Should behave the same for blank lines: potentially a comment.
+ (forward-line 1)
+ (should (eq (car (python-indent-context)) :after-comment))
+ (should (= (python-indent-calculate-indentation) 4))
+ (python-tests-look-at "comment_wins_over_ender")
+ ;; The comment won over the ender because the user said so.
+ (should (eq (car (python-indent-context)) :after-comment))
+ (should (= (python-indent-calculate-indentation) 4))
+ ;; The indentation calculated fine for the assignment, but the user
+ ;; choose to force it back to the first column. Next line should
+ ;; be aware of that.
+ (python-tests-look-at "# yeah, that.")
+ (should (eq (car (python-indent-context)) :after-line))
+ (should (= (python-indent-calculate-indentation) 0))))
+
+(ert-deftest python-indent-after-comment-3 ()
+ "Test after-comment in buggy case."
+ (python-tests-with-temp-buffer
+ "
+class A(object):
+
+ def something(self, arg):
+ if True:
+ return arg
+
+ # A comment
+
+ @adecorator
+ def method(self, a, b):
+ pass
+"
+ (python-tests-look-at "@adecorator")
+ (should (eq (car (python-indent-context)) :after-comment))
+ (should (= (python-indent-calculate-indentation) 4))))
+
+(ert-deftest python-indent-inside-paren-1 ()
+ "The most simple inside-paren case that shouldn't fail."
+ (python-tests-with-temp-buffer
+ "
+data = {
+ 'key':
+ {
+ 'objlist': [
+ {
+ 'pk': 1,
+ 'name': 'first',
+ },
+ {
+ 'pk': 2,
+ 'name': 'second',
+ }
+ ]
+ }
+}
+"
+ (python-tests-look-at "data = {")
+ (should (eq (car (python-indent-context)) :no-indent))
+ (should (= (python-indent-calculate-indentation) 0))
+ (python-tests-look-at "'key':")
+ (should (eq (car (python-indent-context)) :inside-paren-newline-start))
+ (should (= (python-indent-calculate-indentation) 4))
+ (python-tests-look-at "{")
+ (should (eq (car (python-indent-context)) :inside-paren-newline-start))
+ (should (= (python-indent-calculate-indentation) 4))
+ (python-tests-look-at "'objlist': [")
+ (should (eq (car (python-indent-context)) :inside-paren-newline-start))
+ (should (= (python-indent-calculate-indentation) 8))
+ (python-tests-look-at "{")
+ (should (eq (car (python-indent-context)) :inside-paren-newline-start))
+ (should (= (python-indent-calculate-indentation) 12))
+ (python-tests-look-at "'pk': 1,")
+ (should (eq (car (python-indent-context)) :inside-paren-newline-start))
+ (should (= (python-indent-calculate-indentation) 16))
+ (python-tests-look-at "'name': 'first',")
+ (should (eq (car (python-indent-context)) :inside-paren-newline-start))
+ (should (= (python-indent-calculate-indentation) 16))
+ (python-tests-look-at "},")
+ (should (eq (car (python-indent-context))
+ :inside-paren-at-closing-nested-paren))
+ (should (= (python-indent-calculate-indentation) 12))
+ (python-tests-look-at "{")
+ (should (eq (car (python-indent-context)) :inside-paren-newline-start))
+ (should (= (python-indent-calculate-indentation) 12))
+ (python-tests-look-at "'pk': 2,")
+ (should (eq (car (python-indent-context)) :inside-paren-newline-start))
+ (should (= (python-indent-calculate-indentation) 16))
+ (python-tests-look-at "'name': 'second',")
+ (should (eq (car (python-indent-context)) :inside-paren-newline-start))
+ (should (= (python-indent-calculate-indentation) 16))
+ (python-tests-look-at "}")
+ (should (eq (car (python-indent-context))
+ :inside-paren-at-closing-nested-paren))
+ (should (= (python-indent-calculate-indentation) 12))
+ (python-tests-look-at "]")
+ (should (eq (car (python-indent-context))
+ :inside-paren-at-closing-nested-paren))
+ (should (= (python-indent-calculate-indentation) 8))
+ (python-tests-look-at "}")
+ (should (eq (car (python-indent-context))
+ :inside-paren-at-closing-nested-paren))
+ (should (= (python-indent-calculate-indentation) 4))
+ (python-tests-look-at "}")
+ (should (eq (car (python-indent-context)) :inside-paren-at-closing-paren))
+ (should (= (python-indent-calculate-indentation) 0))))
+
+(ert-deftest python-indent-inside-paren-2 ()
+ "Another more compact paren group style."
+ (python-tests-with-temp-buffer
+ "
+data = {'key': {
+ 'objlist': [
+ {'pk': 1,
+ 'name': 'first'},
+ {'pk': 2,
+ 'name': 'second'}
+ ]
+}}
+"
+ (python-tests-look-at "data = {")
+ (should (eq (car (python-indent-context)) :no-indent))
+ (should (= (python-indent-calculate-indentation) 0))
+ (python-tests-look-at "'objlist': [")
+ (should (eq (car (python-indent-context)) :inside-paren-newline-start))
+ (should (= (python-indent-calculate-indentation) 4))
+ (python-tests-look-at "{'pk': 1,")
+ (should (eq (car (python-indent-context)) :inside-paren-newline-start))
+ (should (= (python-indent-calculate-indentation) 8))
+ (python-tests-look-at "'name': 'first'},")
+ (should (eq (car (python-indent-context)) :inside-paren))
+ (should (= (python-indent-calculate-indentation) 9))
+ (python-tests-look-at "{'pk': 2,")
+ (should (eq (car (python-indent-context)) :inside-paren-newline-start))
+ (should (= (python-indent-calculate-indentation) 8))
+ (python-tests-look-at "'name': 'second'}")
+ (should (eq (car (python-indent-context)) :inside-paren))
+ (should (= (python-indent-calculate-indentation) 9))
+ (python-tests-look-at "]")
+ (should (eq (car (python-indent-context))
+ :inside-paren-at-closing-nested-paren))
+ (should (= (python-indent-calculate-indentation) 4))
+ (python-tests-look-at "}}")
+ (should (eq (car (python-indent-context))
+ :inside-paren-at-closing-nested-paren))
+ (should (= (python-indent-calculate-indentation) 0))
+ (python-tests-look-at "}")
+ (should (eq (car (python-indent-context)) :inside-paren-at-closing-paren))
+ (should (= (python-indent-calculate-indentation) 0))))
+
+(ert-deftest python-indent-inside-paren-3 ()
+ "The simplest case possible."
+ (python-tests-with-temp-buffer
+ "
+data = ('these',
+ 'are',
+ 'the',
+ 'tokens')
+"
+ (python-tests-look-at "data = ('these',")
+ (should (eq (car (python-indent-context)) :no-indent))
+ (should (= (python-indent-calculate-indentation) 0))
+ (forward-line 1)
+ (should (eq (car (python-indent-context)) :inside-paren))
+ (should (= (python-indent-calculate-indentation) 8))
+ (forward-line 1)
+ (should (eq (car (python-indent-context)) :inside-paren))
+ (should (= (python-indent-calculate-indentation) 8))
+ (forward-line 1)
+ (should (eq (car (python-indent-context)) :inside-paren))
+ (should (= (python-indent-calculate-indentation) 8))))
+
+(ert-deftest python-indent-inside-paren-4 ()
+ "Respect indentation of first column."
+ (python-tests-with-temp-buffer
+ "
+data = [ [ 'these', 'are'],
+ ['the', 'tokens' ] ]
+"
+ (python-tests-look-at "data = [ [ 'these', 'are'],")
+ (should (eq (car (python-indent-context)) :no-indent))
+ (should (= (python-indent-calculate-indentation) 0))
+ (forward-line 1)
+ (should (eq (car (python-indent-context)) :inside-paren))
+ (should (= (python-indent-calculate-indentation) 9))))
+
+(ert-deftest python-indent-inside-paren-5 ()
+ "Test when :inside-paren initial parens are skipped in context start."
+ (python-tests-with-temp-buffer
+ "
+while ((not some_condition) and
+ another_condition):
+ do_something_interesting(
+ with_some_arg)
+"
+ (python-tests-look-at "while ((not some_condition) and")
+ (should (eq (car (python-indent-context)) :no-indent))
+ (should (= (python-indent-calculate-indentation) 0))
+ (forward-line 1)
+ (should (eq (car (python-indent-context)) :inside-paren))
+ (should (= (python-indent-calculate-indentation) 7))
+ (forward-line 1)
+ (should (eq (car (python-indent-context)) :after-block-start))
+ (should (= (python-indent-calculate-indentation) 4))
+ (forward-line 1)
+ (should (eq (car (python-indent-context)) :inside-paren-newline-start))
+ (should (= (python-indent-calculate-indentation) 8))))
+
+(ert-deftest python-indent-inside-paren-6 ()
+ "This should be aligned.."
+ (python-tests-with-temp-buffer
+ "
+CHOICES = (('some', 'choice'),
+ ('another', 'choice'),
+ ('more', 'choices'))
+"
+ (python-tests-look-at "CHOICES = (('some', 'choice'),")
+ (should (eq (car (python-indent-context)) :no-indent))
+ (should (= (python-indent-calculate-indentation) 0))
+ (forward-line 1)
+ (should (eq (car (python-indent-context)) :inside-paren))
+ (should (= (python-indent-calculate-indentation) 11))
+ (forward-line 1)
+ (should (eq (car (python-indent-context)) :inside-paren))
+ (should (= (python-indent-calculate-indentation) 11))))
+
+(ert-deftest python-indent-inside-paren-7 ()
+ "Test for Bug#21762."
+ (python-tests-with-temp-buffer
+ "import re as myre\nvar = [\n"
+ (goto-char (point-max))
+ ;; This signals an error if the test fails
+ (should (eq (car (python-indent-context)) :inside-paren-newline-start))))
+
+(ert-deftest python-indent-after-block-1 ()
+ "The most simple after-block case that shouldn't fail."
+ (python-tests-with-temp-buffer
+ "
+def foo(a, b, c=True):
+"
+ (should (eq (car (python-indent-context)) :no-indent))
+ (should (= (python-indent-calculate-indentation) 0))
+ (goto-char (point-max))
+ (should (eq (car (python-indent-context)) :after-block-start))
+ (should (= (python-indent-calculate-indentation) 4))))
+
+(ert-deftest python-indent-after-block-2 ()
+ "A weird (malformed) multiline block statement."
+ (python-tests-with-temp-buffer
+ "
+def foo(a, b, c={
+ 'a':
+}):
+"
+ (goto-char (point-max))
+ (should (eq (car (python-indent-context)) :after-block-start))
+ (should (= (python-indent-calculate-indentation) 4))))
+
+(ert-deftest python-indent-after-block-3 ()
+ "A weird (malformed) sample, usually found in python shells."
+ (python-tests-with-temp-buffer
+ "
+In [1]:
+def func():
+pass
+
+In [2]:
+something
+"
+ (python-tests-look-at "pass")
+ (should (eq (car (python-indent-context)) :after-block-start))
+ (should (= (python-indent-calculate-indentation) 4))
+ (python-tests-look-at "something")
+ (end-of-line)
+ (should (eq (car (python-indent-context)) :after-line))
+ (should (= (python-indent-calculate-indentation) 0))))
+
+(ert-deftest python-indent-after-async-block-1 ()
+ "Test PEP492 async def."
+ (python-tests-with-temp-buffer
+ "
+async def foo(a, b, c=True):
+"
+ (should (eq (car (python-indent-context)) :no-indent))
+ (should (= (python-indent-calculate-indentation) 0))
+ (goto-char (point-max))
+ (should (eq (car (python-indent-context)) :after-block-start))
+ (should (= (python-indent-calculate-indentation) 4))))
+
+(ert-deftest python-indent-after-async-block-2 ()
+ "Test PEP492 async with."
+ (python-tests-with-temp-buffer
+ "
+async with foo(a) as mgr:
+"
+ (should (eq (car (python-indent-context)) :no-indent))
+ (should (= (python-indent-calculate-indentation) 0))
+ (goto-char (point-max))
+ (should (eq (car (python-indent-context)) :after-block-start))
+ (should (= (python-indent-calculate-indentation) 4))))
+
+(ert-deftest python-indent-after-async-block-3 ()
+ "Test PEP492 async for."
+ (python-tests-with-temp-buffer
+ "
+async for a in sequencer():
+"
+ (should (eq (car (python-indent-context)) :no-indent))
+ (should (= (python-indent-calculate-indentation) 0))
+ (goto-char (point-max))
+ (should (eq (car (python-indent-context)) :after-block-start))
+ (should (= (python-indent-calculate-indentation) 4))))
+
+(ert-deftest python-indent-after-backslash-1 ()
+ "The most common case."
+ (python-tests-with-temp-buffer
+ "
+from foo.bar.baz import something, something_1 \\\\
+ something_2 something_3, \\\\
+ something_4, something_5
+"
+ (python-tests-look-at "from foo.bar.baz import something, something_1")
+ (should (eq (car (python-indent-context)) :no-indent))
+ (should (= (python-indent-calculate-indentation) 0))
+ (python-tests-look-at "something_2 something_3,")
+ (should (eq (car (python-indent-context)) :after-backslash-first-line))
+ (should (= (python-indent-calculate-indentation) 4))
+ (python-tests-look-at "something_4, something_5")
+ (should (eq (car (python-indent-context)) :after-backslash))
+ (should (= (python-indent-calculate-indentation) 4))
+ (goto-char (point-max))
+ (should (eq (car (python-indent-context)) :after-line))
+ (should (= (python-indent-calculate-indentation) 0))))
+
+(ert-deftest python-indent-after-backslash-2 ()
+ "A pretty extreme complicated case."
+ (python-tests-with-temp-buffer
+ "
+objects = Thing.objects.all() \\\\
+ .filter(
+ type='toy',
+ status='bought'
+ ) \\\\
+ .aggregate(
+ Sum('amount')
+ ) \\\\
+ .values_list()
+"
+ (python-tests-look-at "objects = Thing.objects.all()")
+ (should (eq (car (python-indent-context)) :no-indent))
+ (should (= (python-indent-calculate-indentation) 0))
+ (python-tests-look-at ".filter(")
+ (should (eq (car (python-indent-context))
+ :after-backslash-dotted-continuation))
+ (should (= (python-indent-calculate-indentation) 23))
+ (python-tests-look-at "type='toy',")
+ (should (eq (car (python-indent-context)) :inside-paren-newline-start))
+ (should (= (python-indent-calculate-indentation) 27))
+ (python-tests-look-at "status='bought'")
+ (should (eq (car (python-indent-context)) :inside-paren-newline-start))
+ (should (= (python-indent-calculate-indentation) 27))
+ (python-tests-look-at ") \\\\")
+ (should (eq (car (python-indent-context)) :inside-paren-at-closing-paren))
+ (should (= (python-indent-calculate-indentation) 23))
+ (python-tests-look-at ".aggregate(")
+ (should (eq (car (python-indent-context))
+ :after-backslash-dotted-continuation))
+ (should (= (python-indent-calculate-indentation) 23))
+ (python-tests-look-at "Sum('amount')")
+ (should (eq (car (python-indent-context)) :inside-paren-newline-start))
+ (should (= (python-indent-calculate-indentation) 27))
+ (python-tests-look-at ") \\\\")
+ (should (eq (car (python-indent-context)) :inside-paren-at-closing-paren))
+ (should (= (python-indent-calculate-indentation) 23))
+ (python-tests-look-at ".values_list()")
+ (should (eq (car (python-indent-context))
+ :after-backslash-dotted-continuation))
+ (should (= (python-indent-calculate-indentation) 23))
+ (forward-line 1)
+ (should (eq (car (python-indent-context)) :after-line))
+ (should (= (python-indent-calculate-indentation) 0))))
+
+(ert-deftest python-indent-after-backslash-3 ()
+ "Backslash continuation from block start."
+ (python-tests-with-temp-buffer
+ "
+with open('/path/to/some/file/you/want/to/read') as file_1, \\\\
+ open('/path/to/some/file/being/written', 'w') as file_2:
+ file_2.write(file_1.read())
+"
+ (python-tests-look-at
+ "with open('/path/to/some/file/you/want/to/read') as file_1, \\\\")
+ (should (eq (car (python-indent-context)) :no-indent))
+ (should (= (python-indent-calculate-indentation) 0))
+ (python-tests-look-at
+ "open('/path/to/some/file/being/written', 'w') as file_2")
+ (should (eq (car (python-indent-context))
+ :after-backslash-block-continuation))
+ (should (= (python-indent-calculate-indentation) 5))
+ (python-tests-look-at "file_2.write(file_1.read())")
+ (should (eq (car (python-indent-context)) :after-block-start))
+ (should (= (python-indent-calculate-indentation) 4))))
+
+(ert-deftest python-indent-after-backslash-4 ()
+ "Backslash continuation from assignment."
+ (python-tests-with-temp-buffer
+ "
+super_awful_assignment = some_calculation() and \\\\
+ another_calculation() and \\\\
+ some_final_calculation()
+"
+ (python-tests-look-at
+ "super_awful_assignment = some_calculation() and \\\\")
+ (should (eq (car (python-indent-context)) :no-indent))
+ (should (= (python-indent-calculate-indentation) 0))
+ (python-tests-look-at "another_calculation() and \\\\")
+ (should (eq (car (python-indent-context))
+ :after-backslash-assignment-continuation))
+ (should (= (python-indent-calculate-indentation) 25))
+ (python-tests-look-at "some_final_calculation()")
+ (should (eq (car (python-indent-context)) :after-backslash))
+ (should (= (python-indent-calculate-indentation) 25))))
+
+(ert-deftest python-indent-after-backslash-5 ()
+ "Dotted continuation bizarre example."
+ (python-tests-with-temp-buffer
+ "
+def delete_all_things():
+ Thing \\\\
+ .objects.all() \\\\
+ .delete()
+"
+ (python-tests-look-at "Thing \\\\")
+ (should (eq (car (python-indent-context)) :after-block-start))
+ (should (= (python-indent-calculate-indentation) 4))
+ (python-tests-look-at ".objects.all() \\\\")
+ (should (eq (car (python-indent-context)) :after-backslash-first-line))
+ (should (= (python-indent-calculate-indentation) 8))
+ (python-tests-look-at ".delete()")
+ (should (eq (car (python-indent-context))
+ :after-backslash-dotted-continuation))
+ (should (= (python-indent-calculate-indentation) 16))))
+
+(ert-deftest python-indent-block-enders-1 ()
+ "Test de-indentation for pass keyword."
+ (python-tests-with-temp-buffer
+ "
+Class foo(object):
+
+ def bar(self):
+ if self.baz:
+ return (1,
+ 2,
+ 3)
+
+ else:
+ pass
+"
+ (python-tests-look-at "3)")
+ (forward-line 1)
+ (should (= (python-indent-calculate-indentation) 8))
+ (python-tests-look-at "pass")
+ (forward-line 1)
+ (should (eq (car (python-indent-context)) :after-block-end))
+ (should (= (python-indent-calculate-indentation) 8))))
+
+(ert-deftest python-indent-block-enders-2 ()
+ "Test de-indentation for return keyword."
+ (python-tests-with-temp-buffer
+ "
+Class foo(object):
+ '''raise lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do
+
+ eiusmod tempor incididunt ut labore et dolore magna aliqua.
+ '''
+ def bar(self):
+ \"return (1, 2, 3).\"
+ if self.baz:
+ return (1,
+ 2,
+ 3)
+"
+ (python-tests-look-at "def")
+ (should (= (python-indent-calculate-indentation) 4))
+ (python-tests-look-at "if")
+ (should (= (python-indent-calculate-indentation) 8))
+ (python-tests-look-at "return")
+ (should (= (python-indent-calculate-indentation) 12))
+ (goto-char (point-max))
+ (should (eq (car (python-indent-context)) :after-block-end))
+ (should (= (python-indent-calculate-indentation) 8))))
+
+(ert-deftest python-indent-block-enders-3 ()
+ "Test de-indentation for continue keyword."
+ (python-tests-with-temp-buffer
+ "
+for element in lst:
+ if element is None:
+ continue
+"
+ (python-tests-look-at "if")
+ (should (= (python-indent-calculate-indentation) 4))
+ (python-tests-look-at "continue")
+ (should (= (python-indent-calculate-indentation) 8))
+ (forward-line 1)
+ (should (eq (car (python-indent-context)) :after-block-end))
+ (should (= (python-indent-calculate-indentation) 4))))
+
+(ert-deftest python-indent-block-enders-4 ()
+ "Test de-indentation for break keyword."
+ (python-tests-with-temp-buffer
+ "
+for element in lst:
+ if element is None:
+ break
+"
+ (python-tests-look-at "if")
+ (should (= (python-indent-calculate-indentation) 4))
+ (python-tests-look-at "break")
+ (should (= (python-indent-calculate-indentation) 8))
+ (forward-line 1)
+ (should (eq (car (python-indent-context)) :after-block-end))
+ (should (= (python-indent-calculate-indentation) 4))))
+
+(ert-deftest python-indent-block-enders-5 ()
+ "Test de-indentation for raise keyword."
+ (python-tests-with-temp-buffer
+ "
+for element in lst:
+ if element is None:
+ raise ValueError('Element cannot be None')
+"
+ (python-tests-look-at "if")
+ (should (= (python-indent-calculate-indentation) 4))
+ (python-tests-look-at "raise")
+ (should (= (python-indent-calculate-indentation) 8))
+ (forward-line 1)
+ (should (eq (car (python-indent-context)) :after-block-end))
+ (should (= (python-indent-calculate-indentation) 4))))
+
+(ert-deftest python-indent-dedenters-1 ()
+ "Test de-indentation for the elif keyword."
+ (python-tests-with-temp-buffer
+ "
+if save:
+ try:
+ write_to_disk(data)
+ finally:
+ cleanup()
+ elif
+"
+ (python-tests-look-at "elif\n")
+ (should (eq (car (python-indent-context)) :at-dedenter-block-start))
+ (should (= (python-indent-calculate-indentation) 0))
+ (should (= (python-indent-calculate-indentation t) 0))))
+
+(ert-deftest python-indent-dedenters-2 ()
+ "Test de-indentation for the else keyword."
+ (python-tests-with-temp-buffer
+ "
+if save:
+ try:
+ write_to_disk(data)
+ except IOError:
+ msg = 'Error saving to disk'
+ message(msg)
+ logger.exception(msg)
+ except Exception:
+ if hide_details:
+ logger.exception('Unhandled exception')
+ else
+ finally:
+ data.free()
+"
+ (python-tests-look-at "else\n")
+ (should (eq (car (python-indent-context)) :at-dedenter-block-start))
+ (should (= (python-indent-calculate-indentation) 8))
+ (python-indent-line t)
+ (should (= (python-indent-calculate-indentation t) 4))
+ (python-indent-line t)
+ (should (= (python-indent-calculate-indentation t) 0))
+ (python-indent-line t)
+ (should (= (python-indent-calculate-indentation t) 8))))
+
+(ert-deftest python-indent-dedenters-3 ()
+ "Test de-indentation for the except keyword."
+ (python-tests-with-temp-buffer
+ "
+if save:
+ try:
+ write_to_disk(data)
+ except
+"
+ (python-tests-look-at "except\n")
+ (should (eq (car (python-indent-context)) :at-dedenter-block-start))
+ (should (= (python-indent-calculate-indentation) 4))
+ (python-indent-line t)
+ (should (= (python-indent-calculate-indentation t) 4))))
+
+(ert-deftest python-indent-dedenters-4 ()
+ "Test de-indentation for the finally keyword."
+ (python-tests-with-temp-buffer
+ "
+if save:
+ try:
+ write_to_disk(data)
+ finally
+"
+ (python-tests-look-at "finally\n")
+ (should (eq (car (python-indent-context)) :at-dedenter-block-start))
+ (should (= (python-indent-calculate-indentation) 4))
+ (python-indent-line t)
+ (should (= (python-indent-calculate-indentation) 4))))
+
+(ert-deftest python-indent-dedenters-5 ()
+ "Test invalid levels are skipped in a complex example."
+ (python-tests-with-temp-buffer
+ "
+if save:
+ try:
+ write_to_disk(data)
+ except IOError:
+ msg = 'Error saving to disk'
+ message(msg)
+ logger.exception(msg)
+ finally:
+ if cleanup:
+ do_cleanup()
+ else
+"
+ (python-tests-look-at "else\n")
+ (should (eq (car (python-indent-context)) :at-dedenter-block-start))
+ (should (= (python-indent-calculate-indentation) 8))
+ (should (= (python-indent-calculate-indentation t) 0))
+ (python-indent-line t)
+ (should (= (python-indent-calculate-indentation t) 8))))
+
+(ert-deftest python-indent-dedenters-6 ()
+ "Test indentation is zero when no opening block for dedenter."
+ (python-tests-with-temp-buffer
+ "
+try:
+ # if save:
+ write_to_disk(data)
+ else
+"
+ (python-tests-look-at "else\n")
+ (should (eq (car (python-indent-context)) :at-dedenter-block-start))
+ (should (= (python-indent-calculate-indentation) 0))
+ (should (= (python-indent-calculate-indentation t) 0))))
+
+(ert-deftest python-indent-dedenters-7 ()
+ "Test indentation case from Bug#15163."
+ (python-tests-with-temp-buffer
+ "
+if a:
+ if b:
+ pass
+ else:
+ pass
+ else:
+"
+ (python-tests-look-at "else:" 2)
+ (should (eq (car (python-indent-context)) :at-dedenter-block-start))
+ (should (= (python-indent-calculate-indentation) 0))
+ (should (= (python-indent-calculate-indentation t) 0))))
+
+(ert-deftest python-indent-dedenters-8 ()
+ "Test indentation for Bug#18432."
+ (python-tests-with-temp-buffer
+ "
+if (a == 1 or
+ a == 2):
+ pass
+elif (a == 3 or
+a == 4):
+"
+ (python-tests-look-at "elif (a == 3 or")
+ (should (eq (car (python-indent-context)) :at-dedenter-block-start))
+ (should (= (python-indent-calculate-indentation) 0))
+ (should (= (python-indent-calculate-indentation t) 0))
+ (python-tests-look-at "a == 4):\n")
+ (should (eq (car (python-indent-context)) :inside-paren))
+ (should (= (python-indent-calculate-indentation) 6))
+ (python-indent-line)
+ (should (= (python-indent-calculate-indentation t) 4))
+ (python-indent-line t)
+ (should (= (python-indent-calculate-indentation t) 0))
+ (python-indent-line t)
+ (should (= (python-indent-calculate-indentation t) 6))))
+
+(ert-deftest python-indent-inside-string-1 ()
+ "Test indentation for strings."
+ (python-tests-with-temp-buffer
+ "
+multiline = '''
+bunch
+of
+lines
+'''
+"
+ (python-tests-look-at "multiline = '''")
+ (should (eq (car (python-indent-context)) :no-indent))
+ (should (= (python-indent-calculate-indentation) 0))
+ (python-tests-look-at "bunch")
+ (should (eq (car (python-indent-context)) :inside-string))
+ (should (= (python-indent-calculate-indentation) 0))
+ (python-tests-look-at "of")
+ (should (eq (car (python-indent-context)) :inside-string))
+ (should (= (python-indent-calculate-indentation) 0))
+ (python-tests-look-at "lines")
+ (should (eq (car (python-indent-context)) :inside-string))
+ (should (= (python-indent-calculate-indentation) 0))
+ (python-tests-look-at "'''")
+ (should (eq (car (python-indent-context)) :inside-string))
+ (should (= (python-indent-calculate-indentation) 0))))
+
+(ert-deftest python-indent-inside-string-2 ()
+ "Test indentation for docstrings."
+ (python-tests-with-temp-buffer
+ "
+def fn(a, b, c=True):
+ '''docstring
+ bunch
+ of
+ lines
+ '''
+"
+ (python-tests-look-at "'''docstring")
+ (should (eq (car (python-indent-context)) :after-block-start))
+ (should (= (python-indent-calculate-indentation) 4))
+ (python-tests-look-at "bunch")
+ (should (eq (car (python-indent-context)) :inside-docstring))
+ (should (= (python-indent-calculate-indentation) 4))
+ (python-tests-look-at "of")
+ (should (eq (car (python-indent-context)) :inside-docstring))
+ ;; Any indentation deeper than the base-indent must remain unmodified.
+ (should (= (python-indent-calculate-indentation) 8))
+ (python-tests-look-at "lines")
+ (should (eq (car (python-indent-context)) :inside-docstring))
+ (should (= (python-indent-calculate-indentation) 4))
+ (python-tests-look-at "'''")
+ (should (eq (car (python-indent-context)) :inside-docstring))
+ (should (= (python-indent-calculate-indentation) 4))))
+
+(ert-deftest python-indent-inside-string-3 ()
+ "Test indentation for nested strings."
+ (python-tests-with-temp-buffer
+ "
+def fn(a, b, c=True):
+ some_var = '''
+ bunch
+ of
+ lines
+ '''
+"
+ (python-tests-look-at "some_var = '''")
+ (should (eq (car (python-indent-context)) :after-block-start))
+ (should (= (python-indent-calculate-indentation) 4))
+ (python-tests-look-at "bunch")
+ (should (eq (car (python-indent-context)) :inside-string))
+ (should (= (python-indent-calculate-indentation) 4))
+ (python-tests-look-at "of")
+ (should (eq (car (python-indent-context)) :inside-string))
+ (should (= (python-indent-calculate-indentation) 4))
+ (python-tests-look-at "lines")
+ (should (eq (car (python-indent-context)) :inside-string))
+ (should (= (python-indent-calculate-indentation) 4))
+ (python-tests-look-at "'''")
+ (should (eq (car (python-indent-context)) :inside-string))
+ (should (= (python-indent-calculate-indentation) 4))))
+
+(ert-deftest python-indent-electric-colon-1 ()
+ "Test indentation case from Bug#18228."
+ (python-tests-with-temp-buffer
+ "
+def a():
+ pass
+
+def b()
+"
+ (python-tests-look-at "def b()")
+ (goto-char (line-end-position))
+ (python-tests-self-insert ":")
+ (should (= (current-indentation) 0))))
+
+(ert-deftest python-indent-electric-colon-2 ()
+ "Test indentation case for dedenter."
+ (python-tests-with-temp-buffer
+ "
+if do:
+ something()
+ else
+"
+ (python-tests-look-at "else")
+ (goto-char (line-end-position))
+ (python-tests-self-insert ":")
+ (should (= (current-indentation) 0))))
+
+(ert-deftest python-indent-electric-colon-3 ()
+ "Test indentation case for multi-line dedenter."
+ (python-tests-with-temp-buffer
+ "
+if do:
+ something()
+ elif (this
+ and
+ that)
+"
+ (python-tests-look-at "that)")
+ (goto-char (line-end-position))
+ (python-tests-self-insert ":")
+ (python-tests-look-at "elif" -1)
+ (should (= (current-indentation) 0))
+ (python-tests-look-at "and")
+ (should (= (current-indentation) 6))
+ (python-tests-look-at "that)")
+ (should (= (current-indentation) 6))))
+
+(ert-deftest python-indent-region-1 ()
+ "Test indentation case from Bug#18843."
+ (let ((contents "
+def foo ():
+ try:
+ pass
+ except:
+ pass
+"))
+ (python-tests-with-temp-buffer
+ contents
+ (python-indent-region (point-min) (point-max))
+ (should (string= (buffer-substring-no-properties (point-min) (point-max))
+ contents)))))
+
+(ert-deftest python-indent-region-2 ()
+ "Test region indentation on comments."
+ (let ((contents "
+def f():
+ if True:
+ pass
+
+# This is
+# some multiline
+# comment
+"))
+ (python-tests-with-temp-buffer
+ contents
+ (python-indent-region (point-min) (point-max))
+ (should (string= (buffer-substring-no-properties (point-min) (point-max))
+ contents)))))
+
+(ert-deftest python-indent-region-3 ()
+ "Test region indentation on comments."
+ (let ((contents "
+def f():
+ if True:
+ pass
+# This is
+# some multiline
+# comment
+")
+ (expected "
+def f():
+ if True:
+ pass
+ # This is
+ # some multiline
+ # comment
+"))
+ (python-tests-with-temp-buffer
+ contents
+ (python-indent-region (point-min) (point-max))
+ (should (string= (buffer-substring-no-properties (point-min) (point-max))
+ expected)))))
+
+(ert-deftest python-indent-region-4 ()
+ "Test region indentation block starts, dedenters and enders."
+ (let ((contents "
+def f():
+ if True:
+a = 5
+ else:
+ a = 10
+ return a
+")
+ (expected "
+def f():
+ if True:
+ a = 5
+ else:
+ a = 10
+ return a
+"))
+ (python-tests-with-temp-buffer
+ contents
+ (python-indent-region (point-min) (point-max))
+ (should (string= (buffer-substring-no-properties (point-min) (point-max))
+ expected)))))
+
+(ert-deftest python-indent-region-5 ()
+ "Test region indentation for docstrings."
+ (let ((contents "
+def f():
+'''
+this is
+ a multiline
+string
+'''
+ x = \\
+ '''
+this is an arbitrarily
+ indented multiline
+ string
+'''
+")
+ (expected "
+def f():
+ '''
+ this is
+ a multiline
+ string
+ '''
+ x = \\
+ '''
+this is an arbitrarily
+ indented multiline
+ string
+'''
+"))
+ (python-tests-with-temp-buffer
+ contents
+ (python-indent-region (point-min) (point-max))
+ (should (string= (buffer-substring-no-properties (point-min) (point-max))
+ expected)))))
+
+
+;;; Mark
+
+(ert-deftest python-mark-defun-1 ()
+ """Test `python-mark-defun' with point at defun symbol start."""
+ (python-tests-with-temp-buffer
+ "
+def foo(x):
+ return x
+
+class A:
+ pass
+
+class B:
+
+ def __init__(self):
+ self.b = 'b'
+
+ def fun(self):
+ return self.b
+
+class C:
+ '''docstring'''
+"
+ (let ((expected-mark-beginning-position
+ (progn
+ (python-tests-look-at "class A:")
+ (1- (point))))
+ (expected-mark-end-position-1
+ (save-excursion
+ (python-tests-look-at "pass")
+ (forward-line)
+ (point)))
+ (expected-mark-end-position-2
+ (save-excursion
+ (python-tests-look-at "return self.b")
+ (forward-line)
+ (point)))
+ (expected-mark-end-position-3
+ (save-excursion
+ (python-tests-look-at "'''docstring'''")
+ (forward-line)
+ (point))))
+ ;; Select class A only, with point at bol.
+ (python-mark-defun 1)
+ (should (= (point) expected-mark-beginning-position))
+ (should (= (marker-position (mark-marker))
+ expected-mark-end-position-1))
+ ;; expand to class B, start position should remain the same.
+ (python-mark-defun 1)
+ (should (= (point) expected-mark-beginning-position))
+ (should (= (marker-position (mark-marker))
+ expected-mark-end-position-2))
+ ;; expand to class C, start position should remain the same.
+ (python-mark-defun 1)
+ (should (= (point) expected-mark-beginning-position))
+ (should (= (marker-position (mark-marker))
+ expected-mark-end-position-3)))))
+
+(ert-deftest python-mark-defun-2 ()
+ """Test `python-mark-defun' with point at nested defun symbol start."""
+ (python-tests-with-temp-buffer
+ "
+def foo(x):
+ return x
+
+class A:
+ pass
+
+class B:
+
+ def __init__(self):
+ self.b = 'b'
+
+ def fun(self):
+ return self.b
+
+class C:
+ '''docstring'''
+"
+ (let ((expected-mark-beginning-position
+ (progn
+ (python-tests-look-at "def __init__(self):")
+ (1- (line-beginning-position))))
+ (expected-mark-end-position-1
+ (save-excursion
+ (python-tests-look-at "self.b = 'b'")
+ (forward-line)
+ (point)))
+ (expected-mark-end-position-2
+ (save-excursion
+ (python-tests-look-at "return self.b")
+ (forward-line)
+ (point)))
+ (expected-mark-end-position-3
+ (save-excursion
+ (python-tests-look-at "'''docstring'''")
+ (forward-line)
+ (point))))
+ ;; Select B.__init only, with point at its start.
+ (python-mark-defun 1)
+ (should (= (point) expected-mark-beginning-position))
+ (should (= (marker-position (mark-marker))
+ expected-mark-end-position-1))
+ ;; expand to B.fun, start position should remain the same.
+ (python-mark-defun 1)
+ (should (= (point) expected-mark-beginning-position))
+ (should (= (marker-position (mark-marker))
+ expected-mark-end-position-2))
+ ;; expand to class C, start position should remain the same.
+ (python-mark-defun 1)
+ (should (= (point) expected-mark-beginning-position))
+ (should (= (marker-position (mark-marker))
+ expected-mark-end-position-3)))))
+
+(ert-deftest python-mark-defun-3 ()
+ """Test `python-mark-defun' with point inside defun symbol."""
+ (python-tests-with-temp-buffer
+ "
+def foo(x):
+ return x
+
+class A:
+ pass
+
+class B:
+
+ def __init__(self):
+ self.b = 'b'
+
+ def fun(self):
+ return self.b
+
+class C:
+ '''docstring'''
+"
+ (let ((expected-mark-beginning-position
+ (progn
+ (python-tests-look-at "def fun(self):")
+ (python-tests-look-at "(self):")
+ (1- (line-beginning-position))))
+ (expected-mark-end-position
+ (save-excursion
+ (python-tests-look-at "return self.b")
+ (forward-line)
+ (point))))
+ ;; Should select B.fun, despite point is inside the defun symbol.
+ (python-mark-defun 1)
+ (should (= (point) expected-mark-beginning-position))
+ (should (= (marker-position (mark-marker))
+ expected-mark-end-position)))))
+
+
+;;; Navigation
+
+(ert-deftest python-nav-beginning-of-defun-1 ()
+ (python-tests-with-temp-buffer
+ "
+def decoratorFunctionWithArguments(arg1, arg2, arg3):
+ '''print decorated function call data to stdout.
+
+ Usage:
+
+ @decoratorFunctionWithArguments('arg1', 'arg2')
+ def func(a, b, c=True):
+ pass
+ '''
+
+ def wwrap(f):
+ print 'Inside wwrap()'
+ def wrapped_f(*args):
+ print 'Inside wrapped_f()'
+ print 'Decorator arguments:', arg1, arg2, arg3
+ f(*args)
+ print 'After f(*args)'
+ return wrapped_f
+ return wwrap
+"
+ (python-tests-look-at "return wrap")
+ (should (= (save-excursion
+ (python-nav-beginning-of-defun)
+ (point))
+ (save-excursion
+ (python-tests-look-at "def wrapped_f(*args):" -1)
+ (beginning-of-line)
+ (point))))
+ (python-tests-look-at "def wrapped_f(*args):" -1)
+ (should (= (save-excursion
+ (python-nav-beginning-of-defun)
+ (point))
+ (save-excursion
+ (python-tests-look-at "def wwrap(f):" -1)
+ (beginning-of-line)
+ (point))))
+ (python-tests-look-at "def wwrap(f):" -1)
+ (should (= (save-excursion
+ (python-nav-beginning-of-defun)
+ (point))
+ (save-excursion
+ (python-tests-look-at "def decoratorFunctionWithArguments" -1)
+ (beginning-of-line)
+ (point))))))
+
+(ert-deftest python-nav-beginning-of-defun-2 ()
+ (python-tests-with-temp-buffer
+ "
+class C(object):
+
+ def m(self):
+ self.c()
+
+ def b():
+ pass
+
+ def a():
+ pass
+
+ def c(self):
+ pass
+"
+ ;; Nested defuns, are handled with care.
+ (python-tests-look-at "def c(self):")
+ (should (= (save-excursion
+ (python-nav-beginning-of-defun)
+ (point))
+ (save-excursion
+ (python-tests-look-at "def m(self):" -1)
+ (beginning-of-line)
+ (point))))
+ ;; Defuns on same levels should be respected.
+ (python-tests-look-at "def a():" -1)
+ (should (= (save-excursion
+ (python-nav-beginning-of-defun)
+ (point))
+ (save-excursion
+ (python-tests-look-at "def b():" -1)
+ (beginning-of-line)
+ (point))))
+ ;; Jump to a top level defun.
+ (python-tests-look-at "def b():" -1)
+ (should (= (save-excursion
+ (python-nav-beginning-of-defun)
+ (point))
+ (save-excursion
+ (python-tests-look-at "def m(self):" -1)
+ (beginning-of-line)
+ (point))))
+ ;; Jump to a top level defun again.
+ (python-tests-look-at "def m(self):" -1)
+ (should (= (save-excursion
+ (python-nav-beginning-of-defun)
+ (point))
+ (save-excursion
+ (python-tests-look-at "class C(object):" -1)
+ (beginning-of-line)
+ (point))))))
+
+(ert-deftest python-nav-beginning-of-defun-3 ()
+ (python-tests-with-temp-buffer
+ "
+class C(object):
+
+ async def m(self):
+ return await self.c()
+
+ async def c(self):
+ pass
+"
+ (python-tests-look-at "self.c()")
+ (should (= (save-excursion
+ (python-nav-beginning-of-defun)
+ (point))
+ (save-excursion
+ (python-tests-look-at "async def m" -1)
+ (beginning-of-line)
+ (point))))))
+
+(ert-deftest python-nav-end-of-defun-1 ()
+ (python-tests-with-temp-buffer
+ "
+class C(object):
+
+ def m(self):
+ self.c()
+
+ def b():
+ pass
+
+ def a():
+ pass
+
+ def c(self):
+ pass
+"
+ (should (= (save-excursion
+ (python-tests-look-at "class C(object):")
+ (python-nav-end-of-defun)
+ (point))
+ (save-excursion
+ (point-max))))
+ (should (= (save-excursion
+ (python-tests-look-at "def m(self):")
+ (python-nav-end-of-defun)
+ (point))
+ (save-excursion
+ (python-tests-look-at "def c(self):")
+ (forward-line -1)
+ (point))))
+ (should (= (save-excursion
+ (python-tests-look-at "def b():")
+ (python-nav-end-of-defun)
+ (point))
+ (save-excursion
+ (python-tests-look-at "def b():")
+ (forward-line 2)
+ (point))))
+ (should (= (save-excursion
+ (python-tests-look-at "def c(self):")
+ (python-nav-end-of-defun)
+ (point))
+ (save-excursion
+ (point-max))))))
+
+(ert-deftest python-nav-end-of-defun-2 ()
+ (python-tests-with-temp-buffer
+ "
+def decoratorFunctionWithArguments(arg1, arg2, arg3):
+ '''print decorated function call data to stdout.
+
+ Usage:
+
+ @decoratorFunctionWithArguments('arg1', 'arg2')
+ def func(a, b, c=True):
+ pass
+ '''
+
+ def wwrap(f):
+ print 'Inside wwrap()'
+ def wrapped_f(*args):
+ print 'Inside wrapped_f()'
+ print 'Decorator arguments:', arg1, arg2, arg3
+ f(*args)
+ print 'After f(*args)'
+ return wrapped_f
+ return wwrap
+"
+ (should (= (save-excursion
+ (python-tests-look-at "def decoratorFunctionWithArguments")
+ (python-nav-end-of-defun)
+ (point))
+ (save-excursion
+ (point-max))))
+ (should (= (save-excursion
+ (python-tests-look-at "@decoratorFunctionWithArguments")
+ (python-nav-end-of-defun)
+ (point))
+ (save-excursion
+ (point-max))))
+ (should (= (save-excursion
+ (python-tests-look-at "def wwrap(f):")
+ (python-nav-end-of-defun)
+ (point))
+ (save-excursion
+ (python-tests-look-at "return wwrap")
+ (line-beginning-position))))
+ (should (= (save-excursion
+ (python-tests-look-at "def wrapped_f(*args):")
+ (python-nav-end-of-defun)
+ (point))
+ (save-excursion
+ (python-tests-look-at "return wrapped_f")
+ (line-beginning-position))))
+ (should (= (save-excursion
+ (python-tests-look-at "f(*args)")
+ (python-nav-end-of-defun)
+ (point))
+ (save-excursion
+ (python-tests-look-at "return wrapped_f")
+ (line-beginning-position))))))
+
+(ert-deftest python-nav-backward-defun-1 ()
+ (python-tests-with-temp-buffer
+ "
+class A(object): # A
+
+ def a(self): # a
+ pass
+
+ def b(self): # b
+ pass
+
+ class B(object): # B
+
+ class C(object): # C
+
+ def d(self): # d
+ pass
+
+ # def e(self): # e
+ # pass
+
+ def c(self): # c
+ pass
+
+ # def d(self): # d
+ # pass
+"
+ (goto-char (point-max))
+ (should (= (save-excursion (python-nav-backward-defun))
+ (python-tests-look-at " def c(self): # c" -1)))
+ (should (= (save-excursion (python-nav-backward-defun))
+ (python-tests-look-at " def d(self): # d" -1)))
+ (should (= (save-excursion (python-nav-backward-defun))
+ (python-tests-look-at " class C(object): # C" -1)))
+ (should (= (save-excursion (python-nav-backward-defun))
+ (python-tests-look-at " class B(object): # B" -1)))
+ (should (= (save-excursion (python-nav-backward-defun))
+ (python-tests-look-at " def b(self): # b" -1)))
+ (should (= (save-excursion (python-nav-backward-defun))
+ (python-tests-look-at " def a(self): # a" -1)))
+ (should (= (save-excursion (python-nav-backward-defun))
+ (python-tests-look-at "class A(object): # A" -1)))
+ (should (not (python-nav-backward-defun)))))
+
+(ert-deftest python-nav-backward-defun-2 ()
+ (python-tests-with-temp-buffer
+ "
+def decoratorFunctionWithArguments(arg1, arg2, arg3):
+ '''print decorated function call data to stdout.
+
+ Usage:
+
+ @decoratorFunctionWithArguments('arg1', 'arg2')
+ def func(a, b, c=True):
+ pass
+ '''
+
+ def wwrap(f):
+ print 'Inside wwrap()'
+ def wrapped_f(*args):
+ print 'Inside wrapped_f()'
+ print 'Decorator arguments:', arg1, arg2, arg3
+ f(*args)
+ print 'After f(*args)'
+ return wrapped_f
+ return wwrap
+"
+ (goto-char (point-max))
+ (should (= (save-excursion (python-nav-backward-defun))
+ (python-tests-look-at " def wrapped_f(*args):" -1)))
+ (should (= (save-excursion (python-nav-backward-defun))
+ (python-tests-look-at " def wwrap(f):" -1)))
+ (should (= (save-excursion (python-nav-backward-defun))
+ (python-tests-look-at "def decoratorFunctionWithArguments(arg1, arg2, arg3):" -1)))
+ (should (not (python-nav-backward-defun)))))
+
+(ert-deftest python-nav-backward-defun-3 ()
+ (python-tests-with-temp-buffer
+ "
+'''
+ def u(self):
+ pass
+
+ def v(self):
+ pass
+
+ def w(self):
+ pass
+'''
+
+class A(object):
+ pass
+"
+ (goto-char (point-min))
+ (let ((point (python-tests-look-at "class A(object):")))
+ (should (not (python-nav-backward-defun)))
+ (should (= point (point))))))
+
+(ert-deftest python-nav-forward-defun-1 ()
+ (python-tests-with-temp-buffer
+ "
+class A(object): # A
+
+ def a(self): # a
+ pass
+
+ def b(self): # b
+ pass
+
+ class B(object): # B
+
+ class C(object): # C
+
+ def d(self): # d
+ pass
+
+ # def e(self): # e
+ # pass
+
+ def c(self): # c
+ pass
+
+ # def d(self): # d
+ # pass
+"
+ (goto-char (point-min))
+ (should (= (save-excursion (python-nav-forward-defun))
+ (python-tests-look-at "(object): # A")))
+ (should (= (save-excursion (python-nav-forward-defun))
+ (python-tests-look-at "(self): # a")))
+ (should (= (save-excursion (python-nav-forward-defun))
+ (python-tests-look-at "(self): # b")))
+ (should (= (save-excursion (python-nav-forward-defun))
+ (python-tests-look-at "(object): # B")))
+ (should (= (save-excursion (python-nav-forward-defun))
+ (python-tests-look-at "(object): # C")))
+ (should (= (save-excursion (python-nav-forward-defun))
+ (python-tests-look-at "(self): # d")))
+ (should (= (save-excursion (python-nav-forward-defun))
+ (python-tests-look-at "(self): # c")))
+ (should (not (python-nav-forward-defun)))))
+
+(ert-deftest python-nav-forward-defun-2 ()
+ (python-tests-with-temp-buffer
+ "
+def decoratorFunctionWithArguments(arg1, arg2, arg3):
+ '''print decorated function call data to stdout.
+
+ Usage:
+
+ @decoratorFunctionWithArguments('arg1', 'arg2')
+ def func(a, b, c=True):
+ pass
+ '''
+
+ def wwrap(f):
+ print 'Inside wwrap()'
+ def wrapped_f(*args):
+ print 'Inside wrapped_f()'
+ print 'Decorator arguments:', arg1, arg2, arg3
+ f(*args)
+ print 'After f(*args)'
+ return wrapped_f
+ return wwrap
+"
+ (goto-char (point-min))
+ (should (= (save-excursion (python-nav-forward-defun))
+ (python-tests-look-at "(arg1, arg2, arg3):")))
+ (should (= (save-excursion (python-nav-forward-defun))
+ (python-tests-look-at "(f):")))
+ (should (= (save-excursion (python-nav-forward-defun))
+ (python-tests-look-at "(*args):")))
+ (should (not (python-nav-forward-defun)))))
+
+(ert-deftest python-nav-forward-defun-3 ()
+ (python-tests-with-temp-buffer
+ "
+class A(object):
+ pass
+
+'''
+ def u(self):
+ pass
+
+ def v(self):
+ pass
+
+ def w(self):
+ pass
+'''
+"
+ (goto-char (point-min))
+ (let ((point (python-tests-look-at "(object):")))
+ (should (not (python-nav-forward-defun)))
+ (should (= point (point))))))
+
+(ert-deftest python-nav-beginning-of-statement-1 ()
+ (python-tests-with-temp-buffer
+ "
+v1 = 123 + \
+ 456 + \
+ 789
+v2 = (value1,
+ value2,
+
+ value3,
+ value4)
+v3 = ('this is a string'
+
+ 'that is continued'
+ 'between lines'
+ 'within a paren',
+ # this is a comment, yo
+ 'continue previous line')
+v4 = '''
+a very long
+string
+'''
+"
+ (python-tests-look-at "v2 =")
+ (python-util-forward-comment -1)
+ (should (= (save-excursion
+ (python-nav-beginning-of-statement)
+ (point))
+ (python-tests-look-at "v1 =" -1 t)))
+ (python-tests-look-at "v3 =")
+ (python-util-forward-comment -1)
+ (should (= (save-excursion
+ (python-nav-beginning-of-statement)
+ (point))
+ (python-tests-look-at "v2 =" -1 t)))
+ (python-tests-look-at "v4 =")
+ (python-util-forward-comment -1)
+ (should (= (save-excursion
+ (python-nav-beginning-of-statement)
+ (point))
+ (python-tests-look-at "v3 =" -1 t)))
+ (goto-char (point-max))
+ (python-util-forward-comment -1)
+ (should (= (save-excursion
+ (python-nav-beginning-of-statement)
+ (point))
+ (python-tests-look-at "v4 =" -1 t)))))
+
+(ert-deftest python-nav-end-of-statement-1 ()
+ (python-tests-with-temp-buffer
+ "
+v1 = 123 + \
+ 456 + \
+ 789
+v2 = (value1,
+ value2,
+
+ value3,
+ value4)
+v3 = ('this is a string'
+
+ 'that is continued'
+ 'between lines'
+ 'within a paren',
+ # this is a comment, yo
+ 'continue previous line')
+v4 = '''
+a very long
+string
+'''
+"
+ (python-tests-look-at "v1 =")
+ (should (= (save-excursion
+ (python-nav-end-of-statement)
+ (point))
+ (save-excursion
+ (python-tests-look-at "789")
+ (line-end-position))))
+ (python-tests-look-at "v2 =")
+ (should (= (save-excursion
+ (python-nav-end-of-statement)
+ (point))
+ (save-excursion
+ (python-tests-look-at "value4)")
+ (line-end-position))))
+ (python-tests-look-at "v3 =")
+ (should (= (save-excursion
+ (python-nav-end-of-statement)
+ (point))
+ (save-excursion
+ (python-tests-look-at
+ "'continue previous line')")
+ (line-end-position))))
+ (python-tests-look-at "v4 =")
+ (should (= (save-excursion
+ (python-nav-end-of-statement)
+ (point))
+ (save-excursion
+ (goto-char (point-max))
+ (python-util-forward-comment -1)
+ (point))))))
+
+(ert-deftest python-nav-forward-statement-1 ()
+ (python-tests-with-temp-buffer
+ "
+v1 = 123 + \
+ 456 + \
+ 789
+v2 = (value1,
+ value2,
+
+ value3,
+ value4)
+v3 = ('this is a string'
+
+ 'that is continued'
+ 'between lines'
+ 'within a paren',
+ # this is a comment, yo
+ 'continue previous line')
+v4 = '''
+a very long
+string
+'''
+"
+ (python-tests-look-at "v1 =")
+ (should (= (save-excursion
+ (python-nav-forward-statement)
+ (point))
+ (python-tests-look-at "v2 =")))
+ (should (= (save-excursion
+ (python-nav-forward-statement)
+ (point))
+ (python-tests-look-at "v3 =")))
+ (should (= (save-excursion
+ (python-nav-forward-statement)
+ (point))
+ (python-tests-look-at "v4 =")))
+ (should (= (save-excursion
+ (python-nav-forward-statement)
+ (point))
+ (point-max)))))
+
+(ert-deftest python-nav-backward-statement-1 ()
+ (python-tests-with-temp-buffer
+ "
+v1 = 123 + \
+ 456 + \
+ 789
+v2 = (value1,
+ value2,
+
+ value3,
+ value4)
+v3 = ('this is a string'
+
+ 'that is continued'
+ 'between lines'
+ 'within a paren',
+ # this is a comment, yo
+ 'continue previous line')
+v4 = '''
+a very long
+string
+'''
+"
+ (goto-char (point-max))
+ (should (= (save-excursion
+ (python-nav-backward-statement)
+ (point))
+ (python-tests-look-at "v4 =" -1)))
+ (should (= (save-excursion
+ (python-nav-backward-statement)
+ (point))
+ (python-tests-look-at "v3 =" -1)))
+ (should (= (save-excursion
+ (python-nav-backward-statement)
+ (point))
+ (python-tests-look-at "v2 =" -1)))
+ (should (= (save-excursion
+ (python-nav-backward-statement)
+ (point))
+ (python-tests-look-at "v1 =" -1)))))
+
+(ert-deftest python-nav-backward-statement-2 ()
+ :expected-result :failed
+ (python-tests-with-temp-buffer
+ "
+v1 = 123 + \
+ 456 + \
+ 789
+v2 = (value1,
+ value2,
+
+ value3,
+ value4)
+"
+ ;; FIXME: For some reason `python-nav-backward-statement' is moving
+ ;; back two sentences when starting from 'value4)'.
+ (goto-char (point-max))
+ (python-util-forward-comment -1)
+ (should (= (save-excursion
+ (python-nav-backward-statement)
+ (point))
+ (python-tests-look-at "v2 =" -1 t)))))
+
+(ert-deftest python-nav-beginning-of-block-1 ()
+ (python-tests-with-temp-buffer
+ "
+def decoratorFunctionWithArguments(arg1, arg2, arg3):
+ '''print decorated function call data to stdout.
+
+ Usage:
+
+ @decoratorFunctionWithArguments('arg1', 'arg2')
+ def func(a, b, c=True):
+ pass
+ '''
+
+ def wwrap(f):
+ print 'Inside wwrap()'
+ def wrapped_f(*args):
+ print 'Inside wrapped_f()'
+ print 'Decorator arguments:', arg1, arg2, arg3
+ f(*args)
+ print 'After f(*args)'
+ return wrapped_f
+ return wwrap
+"
+ (python-tests-look-at "return wwrap")
+ (should (= (save-excursion
+ (python-nav-beginning-of-block)
+ (point))
+ (python-tests-look-at "def decoratorFunctionWithArguments" -1)))
+ (python-tests-look-at "print 'Inside wwrap()'")
+ (should (= (save-excursion
+ (python-nav-beginning-of-block)
+ (point))
+ (python-tests-look-at "def wwrap(f):" -1)))
+ (python-tests-look-at "print 'After f(*args)'")
+ (end-of-line)
+ (should (= (save-excursion
+ (python-nav-beginning-of-block)
+ (point))
+ (python-tests-look-at "def wrapped_f(*args):" -1)))
+ (python-tests-look-at "return wrapped_f")
+ (should (= (save-excursion
+ (python-nav-beginning-of-block)
+ (point))
+ (python-tests-look-at "def wwrap(f):" -1)))))
+
+(ert-deftest python-nav-end-of-block-1 ()
+ (python-tests-with-temp-buffer
+ "
+def decoratorFunctionWithArguments(arg1, arg2, arg3):
+ '''print decorated function call data to stdout.
+
+ Usage:
+
+ @decoratorFunctionWithArguments('arg1', 'arg2')
+ def func(a, b, c=True):
+ pass
+ '''
+
+ def wwrap(f):
+ print 'Inside wwrap()'
+ def wrapped_f(*args):
+ print 'Inside wrapped_f()'
+ print 'Decorator arguments:', arg1, arg2, arg3
+ f(*args)
+ print 'After f(*args)'
+ return wrapped_f
+ return wwrap
+"
+ (python-tests-look-at "def decoratorFunctionWithArguments")
+ (should (= (save-excursion
+ (python-nav-end-of-block)
+ (point))
+ (save-excursion
+ (goto-char (point-max))
+ (python-util-forward-comment -1)
+ (point))))
+ (python-tests-look-at "def wwrap(f):")
+ (should (= (save-excursion
+ (python-nav-end-of-block)
+ (point))
+ (save-excursion
+ (python-tests-look-at "return wrapped_f")
+ (line-end-position))))
+ (end-of-line)
+ (should (= (save-excursion
+ (python-nav-end-of-block)
+ (point))
+ (save-excursion
+ (python-tests-look-at "return wrapped_f")
+ (line-end-position))))
+ (python-tests-look-at "f(*args)")
+ (should (= (save-excursion
+ (python-nav-end-of-block)
+ (point))
+ (save-excursion
+ (python-tests-look-at "print 'After f(*args)'")
+ (line-end-position))))))
+
+(ert-deftest python-nav-forward-block-1 ()
+ "This also accounts as a test for `python-nav-backward-block'."
+ (python-tests-with-temp-buffer
+ "
+if request.user.is_authenticated():
+ # def block():
+ # pass
+ try:
+ profile = request.user.get_profile()
+ except Profile.DoesNotExist:
+ profile = Profile.objects.create(user=request.user)
+ else:
+ if profile.stats:
+ profile.recalculate_stats()
+ else:
+ profile.clear_stats()
+ finally:
+ profile.views += 1
+ profile.save()
+"
+ (should (= (save-excursion (python-nav-forward-block))
+ (python-tests-look-at "if request.user.is_authenticated():")))
+ (should (= (save-excursion (python-nav-forward-block))
+ (python-tests-look-at "try:")))
+ (should (= (save-excursion (python-nav-forward-block))
+ (python-tests-look-at "except Profile.DoesNotExist:")))
+ (should (= (save-excursion (python-nav-forward-block))
+ (python-tests-look-at "else:")))
+ (should (= (save-excursion (python-nav-forward-block))
+ (python-tests-look-at "if profile.stats:")))
+ (should (= (save-excursion (python-nav-forward-block))
+ (python-tests-look-at "else:")))
+ (should (= (save-excursion (python-nav-forward-block))
+ (python-tests-look-at "finally:")))
+ ;; When point is at the last block, leave it there and return nil
+ (should (not (save-excursion (python-nav-forward-block))))
+ ;; Move backwards, and even if the number of moves is less than the
+ ;; provided argument return the point.
+ (should (= (save-excursion (python-nav-forward-block -10))
+ (python-tests-look-at
+ "if request.user.is_authenticated():" -1)))))
+
+(ert-deftest python-nav-forward-sexp-1 ()
+ (python-tests-with-temp-buffer
+ "
+a()
+b()
+c()
+"
+ (python-tests-look-at "a()")
+ (python-nav-forward-sexp)
+ (should (looking-at "$"))
+ (should (save-excursion
+ (beginning-of-line)
+ (looking-at "a()")))
+ (python-nav-forward-sexp)
+ (should (looking-at "$"))
+ (should (save-excursion
+ (beginning-of-line)
+ (looking-at "b()")))
+ (python-nav-forward-sexp)
+ (should (looking-at "$"))
+ (should (save-excursion
+ (beginning-of-line)
+ (looking-at "c()")))
+ ;; The default behavior when next to a paren should do what lisp
+ ;; does and, otherwise `blink-matching-open' breaks.
+ (python-nav-forward-sexp -1)
+ (should (looking-at "()"))
+ (should (save-excursion
+ (beginning-of-line)
+ (looking-at "c()")))
+ (end-of-line)
+ ;; Skipping parens should jump to `bolp'
+ (python-nav-forward-sexp -1 nil t)
+ (should (looking-at "c()"))
+ (forward-line -1)
+ (end-of-line)
+ ;; b()
+ (python-nav-forward-sexp -1)
+ (should (looking-at "()"))
+ (python-nav-forward-sexp -1)
+ (should (looking-at "b()"))
+ (end-of-line)
+ (python-nav-forward-sexp -1 nil t)
+ (should (looking-at "b()"))
+ (forward-line -1)
+ (end-of-line)
+ ;; a()
+ (python-nav-forward-sexp -1)
+ (should (looking-at "()"))
+ (python-nav-forward-sexp -1)
+ (should (looking-at "a()"))
+ (end-of-line)
+ (python-nav-forward-sexp -1 nil t)
+ (should (looking-at "a()"))))
+
+(ert-deftest python-nav-forward-sexp-2 ()
+ (python-tests-with-temp-buffer
+ "
+def func():
+ if True:
+ aaa = bbb
+ ccc = ddd
+ eee = fff
+ return ggg
+"
+ (python-tests-look-at "aa =")
+ (python-nav-forward-sexp)
+ (should (looking-at " = bbb"))
+ (python-nav-forward-sexp)
+ (should (looking-at "$"))
+ (should (save-excursion
+ (back-to-indentation)
+ (looking-at "aaa = bbb")))
+ (python-nav-forward-sexp)
+ (should (looking-at "$"))
+ (should (save-excursion
+ (back-to-indentation)
+ (looking-at "ccc = ddd")))
+ (python-nav-forward-sexp)
+ (should (looking-at "$"))
+ (should (save-excursion
+ (back-to-indentation)
+ (looking-at "eee = fff")))
+ (python-nav-forward-sexp)
+ (should (looking-at "$"))
+ (should (save-excursion
+ (back-to-indentation)
+ (looking-at "return ggg")))
+ (python-nav-forward-sexp -1)
+ (should (looking-at "def func():"))))
+
+(ert-deftest python-nav-forward-sexp-3 ()
+ (python-tests-with-temp-buffer
+ "
+from some_module import some_sub_module
+from another_module import another_sub_module
+
+def another_statement():
+ pass
+"
+ (python-tests-look-at "some_module")
+ (python-nav-forward-sexp)
+ (should (looking-at " import"))
+ (python-nav-forward-sexp)
+ (should (looking-at " some_sub_module"))
+ (python-nav-forward-sexp)
+ (should (looking-at "$"))
+ (should
+ (save-excursion
+ (back-to-indentation)
+ (looking-at
+ "from some_module import some_sub_module")))
+ (python-nav-forward-sexp)
+ (should (looking-at "$"))
+ (should
+ (save-excursion
+ (back-to-indentation)
+ (looking-at
+ "from another_module import another_sub_module")))
+ (python-nav-forward-sexp)
+ (should (looking-at "$"))
+ (should
+ (save-excursion
+ (back-to-indentation)
+ (looking-at
+ "pass")))
+ (python-nav-forward-sexp -1)
+ (should (looking-at "def another_statement():"))
+ (python-nav-forward-sexp -1)
+ (should (looking-at "from another_module import another_sub_module"))
+ (python-nav-forward-sexp -1)
+ (should (looking-at "from some_module import some_sub_module"))))
+
+(ert-deftest python-nav-forward-sexp-safe-1 ()
+ (python-tests-with-temp-buffer
+ "
+profile = Profile.objects.create(user=request.user)
+profile.notify()
+"
+ (python-tests-look-at "profile =")
+ (python-nav-forward-sexp-safe 1)
+ (should (looking-at "$"))
+ (beginning-of-line 1)
+ (python-tests-look-at "user=request.user")
+ (python-nav-forward-sexp-safe -1)
+ (should (looking-at "(user=request.user)"))
+ (python-nav-forward-sexp-safe -4)
+ (should (looking-at "profile ="))
+ (python-tests-look-at "user=request.user")
+ (python-nav-forward-sexp-safe 3)
+ (should (looking-at ")"))
+ (python-nav-forward-sexp-safe 1)
+ (should (looking-at "$"))
+ (python-nav-forward-sexp-safe 1)
+ (should (looking-at "$"))))
+
+(ert-deftest python-nav-up-list-1 ()
+ (python-tests-with-temp-buffer
+ "
+def f():
+ if True:
+ return [i for i in range(3)]
+"
+ (python-tests-look-at "3)]")
+ (python-nav-up-list)
+ (should (looking-at "]"))
+ (python-nav-up-list)
+ (should (looking-at "$"))))
+
+(ert-deftest python-nav-backward-up-list-1 ()
+ :expected-result :failed
+ (python-tests-with-temp-buffer
+ "
+def f():
+ if True:
+ return [i for i in range(3)]
+"
+ (python-tests-look-at "3)]")
+ (python-nav-backward-up-list)
+ (should (looking-at "(3)\\]"))
+ (python-nav-backward-up-list)
+ (should (looking-at
+ "\\[i for i in range(3)\\]"))
+ ;; FIXME: Need to move to beginning-of-statement.
+ (python-nav-backward-up-list)
+ (should (looking-at
+ "return \\[i for i in range(3)\\]"))
+ (python-nav-backward-up-list)
+ (should (looking-at "if True:"))
+ (python-nav-backward-up-list)
+ (should (looking-at "def f():"))))
+
+(ert-deftest python-indent-dedent-line-backspace-1 ()
+ "Check de-indentation on first call. Bug#18319."
+ (python-tests-with-temp-buffer
+ "
+if True:
+ x ()
+ if False:
+"
+ (python-tests-look-at "if False:")
+ (call-interactively #'python-indent-dedent-line-backspace)
+ (should (zerop (current-indentation)))
+ ;; XXX: This should be a call to `undo' but it's triggering errors.
+ (insert " ")
+ (should (= (current-indentation) 4))
+ (call-interactively #'python-indent-dedent-line-backspace)
+ (should (zerop (current-indentation)))))
+
+(ert-deftest python-indent-dedent-line-backspace-2 ()
+ "Check de-indentation with tabs. Bug#19730."
+ (let ((tab-width 8))
+ (python-tests-with-temp-buffer
+ "
+if x:
+\tabcdefg
+"
+ (python-tests-look-at "abcdefg")
+ (goto-char (line-end-position))
+ (call-interactively #'python-indent-dedent-line-backspace)
+ (should
+ (string= (buffer-substring-no-properties
+ (line-beginning-position) (line-end-position))
+ "\tabcdef")))))
+
+(ert-deftest python-indent-dedent-line-backspace-3 ()
+ "Paranoid check of de-indentation with tabs. Bug#19730."
+ (let ((tab-width 8))
+ (python-tests-with-temp-buffer
+ "
+if x:
+\tif y:
+\t abcdefg
+"
+ (python-tests-look-at "abcdefg")
+ (goto-char (line-end-position))
+ (call-interactively #'python-indent-dedent-line-backspace)
+ (should
+ (string= (buffer-substring-no-properties
+ (line-beginning-position) (line-end-position))
+ "\t abcdef"))
+ (back-to-indentation)
+ (call-interactively #'python-indent-dedent-line-backspace)
+ (should
+ (string= (buffer-substring-no-properties
+ (line-beginning-position) (line-end-position))
+ "\tabcdef"))
+ (call-interactively #'python-indent-dedent-line-backspace)
+ (should
+ (string= (buffer-substring-no-properties
+ (line-beginning-position) (line-end-position))
+ " abcdef"))
+ (call-interactively #'python-indent-dedent-line-backspace)
+ (should
+ (string= (buffer-substring-no-properties
+ (line-beginning-position) (line-end-position))
+ "abcdef")))))
+
+(ert-deftest python-bob-infloop-avoid ()
+ "Test that strings at BOB don't confuse syntax analysis. Bug#24905"
+ (python-tests-with-temp-buffer
+ " \"\n"
+ (goto-char (point-min))
+ (font-lock-fontify-buffer)))
+
+
+;;; Shell integration
+
+(defvar python-tests-shell-interpreter "python")
+
+(ert-deftest python-shell-get-process-name-1 ()
+ "Check process name calculation sans `buffer-file-name'."
+ (python-tests-with-temp-buffer
+ ""
+ (should (string= (python-shell-get-process-name nil)
+ python-shell-buffer-name))
+ (should (string= (python-shell-get-process-name t)
+ (format "%s[%s]" python-shell-buffer-name (buffer-name))))))
+
+(ert-deftest python-shell-get-process-name-2 ()
+ "Check process name calculation with `buffer-file-name'."
+ (python-tests-with-temp-file
+ ""
+ ;; `buffer-file-name' is non-nil but the dedicated flag is nil and
+ ;; should be respected.
+ (should (string= (python-shell-get-process-name nil)
+ python-shell-buffer-name))
+ (should (string=
+ (python-shell-get-process-name t)
+ (format "%s[%s]" python-shell-buffer-name (buffer-name))))))
+
+(ert-deftest python-shell-internal-get-process-name-1 ()
+ "Check the internal process name is buffer-unique sans `buffer-file-name'."
+ (python-tests-with-temp-buffer
+ ""
+ (should (string= (python-shell-internal-get-process-name)
+ (format "%s[%s]" python-shell-internal-buffer-name (buffer-name))))))
+
+(ert-deftest python-shell-internal-get-process-name-2 ()
+ "Check the internal process name is buffer-unique with `buffer-file-name'."
+ (python-tests-with-temp-file
+ ""
+ (should (string= (python-shell-internal-get-process-name)
+ (format "%s[%s]" python-shell-internal-buffer-name (buffer-name))))))
+
+(ert-deftest python-shell-calculate-command-1 ()
+ "Check the command to execute is calculated correctly.
+Using `python-shell-interpreter' and
+`python-shell-interpreter-args'."
+ (skip-unless (executable-find python-tests-shell-interpreter))
+ (let ((python-shell-interpreter (executable-find
+ python-tests-shell-interpreter))
+ (python-shell-interpreter-args "-B"))
+ (should (string=
+ (format "%s %s"
+ (shell-quote-argument python-shell-interpreter)
+ python-shell-interpreter-args)
+ (python-shell-calculate-command)))))
+
+(ert-deftest python-shell-calculate-pythonpath-1 ()
+ "Test PYTHONPATH calculation."
+ (let ((process-environment '("PYTHONPATH=/path0"))
+ (python-shell-extra-pythonpaths '("/path1" "/path2")))
+ (should (string= (python-shell-calculate-pythonpath)
+ (concat "/path1" path-separator
+ "/path2" path-separator "/path0")))))
+
+(ert-deftest python-shell-calculate-pythonpath-2 ()
+ "Test existing paths are moved to front."
+ (let ((process-environment
+ (list (concat "PYTHONPATH=/path0" path-separator "/path1")))
+ (python-shell-extra-pythonpaths '("/path1" "/path2")))
+ (should (string= (python-shell-calculate-pythonpath)
+ (concat "/path1" path-separator
+ "/path2" path-separator "/path0")))))
+
+(ert-deftest python-shell-calculate-process-environment-1 ()
+ "Test `python-shell-process-environment' modification."
+ (let* ((python-shell-process-environment
+ '("TESTVAR1=value1" "TESTVAR2=value2"))
+ (process-environment (python-shell-calculate-process-environment)))
+ (should (equal (getenv "TESTVAR1") "value1"))
+ (should (equal (getenv "TESTVAR2") "value2"))))
+
+(ert-deftest python-shell-calculate-process-environment-2 ()
+ "Test `python-shell-extra-pythonpaths' modification."
+ (let* ((process-environment process-environment)
+ (original-pythonpath (setenv "PYTHONPATH" "/path0"))
+ (python-shell-extra-pythonpaths '("/path1" "/path2"))
+ (process-environment (python-shell-calculate-process-environment)))
+ (should (equal (getenv "PYTHONPATH")
+ (concat "/path1" path-separator
+ "/path2" path-separator "/path0")))))
+
+(ert-deftest python-shell-calculate-process-environment-3 ()
+ "Test `python-shell-virtualenv-root' modification."
+ (let* ((python-shell-virtualenv-root "/env")
+ (process-environment
+ (let (process-environment process-environment)
+ (setenv "PYTHONHOME" "/home")
+ (setenv "VIRTUAL_ENV")
+ (python-shell-calculate-process-environment))))
+ (should (not (getenv "PYTHONHOME")))
+ (should (string= (getenv "VIRTUAL_ENV") "/env"))))
+
+(ert-deftest python-shell-calculate-process-environment-4 ()
+ "Test PYTHONUNBUFFERED when `python-shell-unbuffered' is non-nil."
+ (let* ((python-shell-unbuffered t)
+ (process-environment
+ (let ((process-environment process-environment))
+ (setenv "PYTHONUNBUFFERED")
+ (python-shell-calculate-process-environment))))
+ (should (string= (getenv "PYTHONUNBUFFERED") "1"))))
+
+(ert-deftest python-shell-calculate-process-environment-5 ()
+ "Test PYTHONUNBUFFERED when `python-shell-unbuffered' is nil."
+ (let* ((python-shell-unbuffered nil)
+ (process-environment
+ (let ((process-environment process-environment))
+ (setenv "PYTHONUNBUFFERED")
+ (python-shell-calculate-process-environment))))
+ (should (not (getenv "PYTHONUNBUFFERED")))))
+
+(ert-deftest python-shell-calculate-process-environment-6 ()
+ "Test PYTHONUNBUFFERED=1 when `python-shell-unbuffered' is nil."
+ (let* ((python-shell-unbuffered nil)
+ (process-environment
+ (let ((process-environment process-environment))
+ (setenv "PYTHONUNBUFFERED" "1")
+ (python-shell-calculate-process-environment))))
+ ;; User default settings must remain untouched:
+ (should (string= (getenv "PYTHONUNBUFFERED") "1"))))
+
+(ert-deftest python-shell-calculate-process-environment-7 ()
+ "Test no side-effects on `process-environment'."
+ (let* ((python-shell-process-environment
+ '("TESTVAR1=value1" "TESTVAR2=value2"))
+ (python-shell-virtualenv-root "/env")
+ (python-shell-unbuffered t)
+ (python-shell-extra-pythonpaths'("/path1" "/path2"))
+ (original-process-environment (copy-sequence process-environment)))
+ (python-shell-calculate-process-environment)
+ (should (equal process-environment original-process-environment))))
+
+(ert-deftest python-shell-calculate-process-environment-8 ()
+ "Test no side-effects on `tramp-remote-process-environment'."
+ (let* ((default-directory "/ssh::/example/dir/")
+ (python-shell-process-environment
+ '("TESTVAR1=value1" "TESTVAR2=value2"))
+ (python-shell-virtualenv-root "/env")
+ (python-shell-unbuffered t)
+ (python-shell-extra-pythonpaths'("/path1" "/path2"))
+ (original-process-environment
+ (copy-sequence tramp-remote-process-environment)))
+ (python-shell-calculate-process-environment)
+ (should (equal tramp-remote-process-environment original-process-environment))))
+
+(ert-deftest python-shell-calculate-exec-path-1 ()
+ "Test `python-shell-exec-path' modification."
+ (let* ((exec-path '("/path0"))
+ (python-shell-exec-path '("/path1" "/path2"))
+ (new-exec-path (python-shell-calculate-exec-path)))
+ (should (equal new-exec-path '("/path1" "/path2" "/path0")))))
+
+(ert-deftest python-shell-calculate-exec-path-2 ()
+ "Test `python-shell-virtualenv-root' modification."
+ (let* ((exec-path '("/path0"))
+ (python-shell-virtualenv-root "/env")
+ (new-exec-path (python-shell-calculate-exec-path)))
+ (should (equal new-exec-path
+ (list (expand-file-name "/env/bin") "/path0")))))
+
+(ert-deftest python-shell-calculate-exec-path-3 ()
+ "Test complete `python-shell-virtualenv-root' modification."
+ (let* ((exec-path '("/path0"))
+ (python-shell-exec-path '("/path1" "/path2"))
+ (python-shell-virtualenv-root "/env")
+ (new-exec-path (python-shell-calculate-exec-path)))
+ (should (equal new-exec-path
+ (list (expand-file-name "/env/bin")
+ "/path1" "/path2" "/path0")))))
+
+(ert-deftest python-shell-calculate-exec-path-4 ()
+ "Test complete `python-shell-virtualenv-root' with remote."
+ (let* ((default-directory "/ssh::/example/dir/")
+ (python-shell-remote-exec-path '("/path0"))
+ (python-shell-exec-path '("/path1" "/path2"))
+ (python-shell-virtualenv-root "/env")
+ (new-exec-path (python-shell-calculate-exec-path)))
+ (should (equal new-exec-path
+ (list (expand-file-name "/env/bin")
+ "/path1" "/path2" "/path0")))))
+
+(ert-deftest python-shell-calculate-exec-path-5 ()
+ "Test no side-effects on `exec-path'."
+ (let* ((exec-path '("/path0"))
+ (python-shell-exec-path '("/path1" "/path2"))
+ (python-shell-virtualenv-root "/env")
+ (original-exec-path (copy-sequence exec-path)))
+ (python-shell-calculate-exec-path)
+ (should (equal exec-path original-exec-path))))
+
+(ert-deftest python-shell-calculate-exec-path-6 ()
+ "Test no side-effects on `python-shell-remote-exec-path'."
+ (let* ((default-directory "/ssh::/example/dir/")
+ (python-shell-remote-exec-path '("/path0"))
+ (python-shell-exec-path '("/path1" "/path2"))
+ (python-shell-virtualenv-root "/env")
+ (original-exec-path (copy-sequence python-shell-remote-exec-path)))
+ (python-shell-calculate-exec-path)
+ (should (equal python-shell-remote-exec-path original-exec-path))))
+
+(ert-deftest python-shell-with-environment-1 ()
+ "Test environment with local `default-directory'."
+ (let* ((exec-path '("/path0"))
+ (python-shell-exec-path '("/path1" "/path2"))
+ (original-exec-path exec-path)
+ (python-shell-virtualenv-root "/env"))
+ (python-shell-with-environment
+ (should (equal exec-path
+ (list (expand-file-name "/env/bin")
+ "/path1" "/path2" "/path0")))
+ (should (not (getenv "PYTHONHOME")))
+ (should (string= (getenv "VIRTUAL_ENV") "/env")))
+ (should (equal exec-path original-exec-path))))
+
+(ert-deftest python-shell-with-environment-2 ()
+ "Test environment with remote `default-directory'."
+ (let* ((default-directory "/ssh::/example/dir/")
+ (python-shell-remote-exec-path '("/remote1" "/remote2"))
+ (python-shell-exec-path '("/path1" "/path2"))
+ (tramp-remote-process-environment '("EMACS=t"))
+ (original-process-environment (copy-sequence tramp-remote-process-environment))
+ (python-shell-virtualenv-root "/env"))
+ (python-shell-with-environment
+ (should (equal (python-shell-calculate-exec-path)
+ (list (expand-file-name "/env/bin")
+ "/path1" "/path2" "/remote1" "/remote2")))
+ (let ((process-environment (python-shell-calculate-process-environment)))
+ (should (not (getenv "PYTHONHOME")))
+ (should (string= (getenv "VIRTUAL_ENV") "/env"))
+ (should (equal tramp-remote-process-environment process-environment))))
+ (should (equal tramp-remote-process-environment original-process-environment))))
+
+(ert-deftest python-shell-with-environment-3 ()
+ "Test `python-shell-with-environment' is idempotent."
+ (let* ((python-shell-extra-pythonpaths '("/example/dir/"))
+ (python-shell-exec-path '("path1" "path2"))
+ (python-shell-virtualenv-root "/home/user/env")
+ (single-call
+ (python-shell-with-environment
+ (list exec-path process-environment)))
+ (nested-call
+ (python-shell-with-environment
+ (python-shell-with-environment
+ (list exec-path process-environment)))))
+ (should (equal single-call nested-call))))
+
+(ert-deftest python-shell-make-comint-1 ()
+ "Check comint creation for global shell buffer."
+ (skip-unless (executable-find python-tests-shell-interpreter))
+ ;; The interpreter can get killed too quickly to allow it to clean
+ ;; up the tempfiles that the default python-shell-setup-codes create,
+ ;; so it leaves tempfiles behind, which is a minor irritation.
+ (let* ((python-shell-setup-codes nil)
+ (python-shell-interpreter
+ (executable-find python-tests-shell-interpreter))
+ (proc-name (python-shell-get-process-name nil))
+ (shell-buffer
+ (python-tests-with-temp-buffer
+ "" (python-shell-make-comint
+ (python-shell-calculate-command) proc-name)))
+ (process (get-buffer-process shell-buffer)))
+ (unwind-protect
+ (progn
+ (set-process-query-on-exit-flag process nil)
+ (should (process-live-p process))
+ (with-current-buffer shell-buffer
+ (should (eq major-mode 'inferior-python-mode))
+ (should (string= (buffer-name) (format "*%s*" proc-name)))))
+ (kill-buffer shell-buffer))))
+
+(ert-deftest python-shell-make-comint-2 ()
+ "Check comint creation for internal shell buffer."
+ (skip-unless (executable-find python-tests-shell-interpreter))
+ (let* ((python-shell-setup-codes nil)
+ (python-shell-interpreter
+ (executable-find python-tests-shell-interpreter))
+ (proc-name (python-shell-internal-get-process-name))
+ (shell-buffer
+ (python-tests-with-temp-buffer
+ "" (python-shell-make-comint
+ (python-shell-calculate-command) proc-name nil t)))
+ (process (get-buffer-process shell-buffer)))
+ (unwind-protect
+ (progn
+ (set-process-query-on-exit-flag process nil)
+ (should (process-live-p process))
+ (with-current-buffer shell-buffer
+ (should (eq major-mode 'inferior-python-mode))
+ (should (string= (buffer-name) (format " *%s*" proc-name)))))
+ (kill-buffer shell-buffer))))
+
+(ert-deftest python-shell-make-comint-3 ()
+ "Check comint creation with overridden python interpreter and args.
+The command passed to `python-shell-make-comint' as argument must
+locally override global values set in `python-shell-interpreter'
+and `python-shell-interpreter-args' in the new shell buffer."
+ (skip-unless (executable-find python-tests-shell-interpreter))
+ (let* ((python-shell-setup-codes nil)
+ (python-shell-interpreter "interpreter")
+ (python-shell-interpreter-args "--some-args")
+ (proc-name (python-shell-get-process-name nil))
+ (interpreter-override
+ (concat (executable-find python-tests-shell-interpreter) " " "-i"))
+ (shell-buffer
+ (python-tests-with-temp-buffer
+ "" (python-shell-make-comint interpreter-override proc-name nil)))
+ (process (get-buffer-process shell-buffer)))
+ (unwind-protect
+ (progn
+ (set-process-query-on-exit-flag process nil)
+ (should (process-live-p process))
+ (with-current-buffer shell-buffer
+ (should (eq major-mode 'inferior-python-mode))
+ (should (file-equal-p
+ python-shell-interpreter
+ (executable-find python-tests-shell-interpreter)))
+ (should (string= python-shell-interpreter-args "-i"))))
+ (kill-buffer shell-buffer))))
+
+(ert-deftest python-shell-make-comint-4 ()
+ "Check shell calculated prompts regexps are set."
+ (skip-unless (executable-find python-tests-shell-interpreter))
+ (let* ((process-environment process-environment)
+ (python-shell-setup-codes nil)
+ (python-shell-interpreter
+ (executable-find python-tests-shell-interpreter))
+ (python-shell-interpreter-args "-i")
+ (python-shell--prompt-calculated-input-regexp nil)
+ (python-shell--prompt-calculated-output-regexp nil)
+ (python-shell-prompt-detect-enabled t)
+ (python-shell-prompt-input-regexps '("extralargeinputprompt" "sml"))
+ (python-shell-prompt-output-regexps '("extralargeoutputprompt" "sml"))
+ (python-shell-prompt-regexp "in")
+ (python-shell-prompt-block-regexp "block")
+ (python-shell-prompt-pdb-regexp "pdf")
+ (python-shell-prompt-output-regexp "output")
+ (startup-code (concat "import sys\n"
+ "sys.ps1 = 'py> '\n"
+ "sys.ps2 = '..> '\n"
+ "sys.ps3 = 'out '\n"))
+ (startup-file (python-shell--save-temp-file startup-code))
+ (proc-name (python-shell-get-process-name nil))
+ (shell-buffer
+ (progn
+ (setenv "PYTHONSTARTUP" startup-file)
+ (python-tests-with-temp-buffer
+ "" (python-shell-make-comint
+ (python-shell-calculate-command) proc-name nil))))
+ (process (get-buffer-process shell-buffer)))
+ (unwind-protect
+ (progn
+ (set-process-query-on-exit-flag process nil)
+ (should (process-live-p process))
+ (with-current-buffer shell-buffer
+ (should (eq major-mode 'inferior-python-mode))
+ (should (string=
+ python-shell--prompt-calculated-input-regexp
+ (concat "^\\(extralargeinputprompt\\|\\.\\.> \\|"
+ "block\\|py> \\|pdf\\|sml\\|in\\)")))
+ (should (string=
+ python-shell--prompt-calculated-output-regexp
+ "^\\(extralargeoutputprompt\\|output\\|out \\|sml\\)"))))
+ (delete-file startup-file)
+ (kill-buffer shell-buffer))))
+
+(ert-deftest python-shell-get-process-1 ()
+ "Check dedicated shell process preference over global."
+ (skip-unless (executable-find python-tests-shell-interpreter))
+ (python-tests-with-temp-file
+ ""
+ (let* ((python-shell-setup-codes nil)
+ (python-shell-interpreter
+ (executable-find python-tests-shell-interpreter))
+ (global-proc-name (python-shell-get-process-name nil))
+ (dedicated-proc-name (python-shell-get-process-name t))
+ (global-shell-buffer
+ (python-shell-make-comint
+ (python-shell-calculate-command) global-proc-name))
+ (dedicated-shell-buffer
+ (python-shell-make-comint
+ (python-shell-calculate-command) dedicated-proc-name))
+ (global-process (get-buffer-process global-shell-buffer))
+ (dedicated-process (get-buffer-process dedicated-shell-buffer)))
+ (unwind-protect
+ (progn
+ (set-process-query-on-exit-flag global-process nil)
+ (set-process-query-on-exit-flag dedicated-process nil)
+ ;; Prefer dedicated if global also exists.
+ (should (equal (python-shell-get-process) dedicated-process))
+ (kill-buffer dedicated-shell-buffer)
+ ;; If there's only global, use it.
+ (should (equal (python-shell-get-process) global-process))
+ (kill-buffer global-shell-buffer)
+ ;; No buffer available.
+ (should (not (python-shell-get-process))))
+ (ignore-errors (kill-buffer global-shell-buffer))
+ (ignore-errors (kill-buffer dedicated-shell-buffer))))))
+
+(ert-deftest python-shell-internal-get-or-create-process-1 ()
+ "Check internal shell process creation fallback."
+ (skip-unless (executable-find python-tests-shell-interpreter))
+ (python-tests-with-temp-file
+ ""
+ (should (not (process-live-p (python-shell-internal-get-process-name))))
+ (let* ((python-shell-interpreter
+ (executable-find python-tests-shell-interpreter))
+ (internal-process-name (python-shell-internal-get-process-name))
+ (internal-process (python-shell-internal-get-or-create-process))
+ (internal-shell-buffer (process-buffer internal-process)))
+ (unwind-protect
+ (progn
+ (set-process-query-on-exit-flag internal-process nil)
+ (should (equal (process-name internal-process)
+ internal-process-name))
+ (should (equal internal-process
+ (python-shell-internal-get-or-create-process)))
+ ;; Assert the internal process is not a user process
+ (should (not (python-shell-get-process)))
+ (kill-buffer internal-shell-buffer))
+ (ignore-errors (kill-buffer internal-shell-buffer))))))
+
+(ert-deftest python-shell-prompt-detect-1 ()
+ "Check prompt autodetection."
+ (skip-unless (executable-find python-tests-shell-interpreter))
+ (let ((process-environment process-environment))
+ ;; Ensure no startup file is enabled
+ (setenv "PYTHONSTARTUP" "")
+ (should python-shell-prompt-detect-enabled)
+ (should (equal (python-shell-prompt-detect) '(">>> " "... " "")))))
+
+(ert-deftest python-shell-prompt-detect-2 ()
+ "Check prompt autodetection with startup file. Bug#17370."
+ (skip-unless (executable-find python-tests-shell-interpreter))
+ (let* ((process-environment process-environment)
+ (startup-code (concat "import sys\n"
+ "sys.ps1 = 'py> '\n"
+ "sys.ps2 = '..> '\n"
+ "sys.ps3 = 'out '\n"))
+ (startup-file (python-shell--save-temp-file startup-code)))
+ (unwind-protect
+ (progn
+ ;; Ensure startup file is enabled
+ (setenv "PYTHONSTARTUP" startup-file)
+ (should python-shell-prompt-detect-enabled)
+ (should (equal (python-shell-prompt-detect) '("py> " "..> " "out "))))
+ (ignore-errors (delete-file startup-file)))))
+
+(ert-deftest python-shell-prompt-detect-3 ()
+ "Check prompts are not autodetected when feature is disabled."
+ (skip-unless (executable-find python-tests-shell-interpreter))
+ (let ((process-environment process-environment)
+ (python-shell-prompt-detect-enabled nil))
+ ;; Ensure no startup file is enabled
+ (should (not python-shell-prompt-detect-enabled))
+ (should (not (python-shell-prompt-detect)))))
+
+(ert-deftest python-shell-prompt-detect-4 ()
+ "Check warning is shown when detection fails."
+ (skip-unless (executable-find python-tests-shell-interpreter))
+ (let* ((process-environment process-environment)
+ ;; Trigger failure by removing prompts in the startup file
+ (startup-code (concat "import sys\n"
+ "sys.ps1 = ''\n"
+ "sys.ps2 = ''\n"
+ "sys.ps3 = ''\n"))
+ (startup-file (python-shell--save-temp-file startup-code)))
+ (unwind-protect
+ (progn
+ (kill-buffer (get-buffer-create "*Warnings*"))
+ (should (not (get-buffer "*Warnings*")))
+ (setenv "PYTHONSTARTUP" startup-file)
+ (should python-shell-prompt-detect-failure-warning)
+ (should python-shell-prompt-detect-enabled)
+ (should (not (python-shell-prompt-detect)))
+ (should (get-buffer "*Warnings*")))
+ (ignore-errors (delete-file startup-file)))))
+
+(ert-deftest python-shell-prompt-detect-5 ()
+ "Check disabled warnings are not shown when detection fails."
+ (skip-unless (executable-find python-tests-shell-interpreter))
+ (let* ((process-environment process-environment)
+ (startup-code (concat "import sys\n"
+ "sys.ps1 = ''\n"
+ "sys.ps2 = ''\n"
+ "sys.ps3 = ''\n"))
+ (startup-file (python-shell--save-temp-file startup-code))
+ (python-shell-prompt-detect-failure-warning nil))
+ (unwind-protect
+ (progn
+ (kill-buffer (get-buffer-create "*Warnings*"))
+ (should (not (get-buffer "*Warnings*")))
+ (setenv "PYTHONSTARTUP" startup-file)
+ (should (not python-shell-prompt-detect-failure-warning))
+ (should python-shell-prompt-detect-enabled)
+ (should (not (python-shell-prompt-detect)))
+ (should (not (get-buffer "*Warnings*"))))
+ (ignore-errors (delete-file startup-file)))))
+
+(ert-deftest python-shell-prompt-detect-6 ()
+ "Warnings are not shown when detection is disabled."
+ (skip-unless (executable-find python-tests-shell-interpreter))
+ (let* ((process-environment process-environment)
+ (startup-code (concat "import sys\n"
+ "sys.ps1 = ''\n"
+ "sys.ps2 = ''\n"
+ "sys.ps3 = ''\n"))
+ (startup-file (python-shell--save-temp-file startup-code))
+ (python-shell-prompt-detect-failure-warning t)
+ (python-shell-prompt-detect-enabled nil))
+ (unwind-protect
+ (progn
+ (kill-buffer (get-buffer-create "*Warnings*"))
+ (should (not (get-buffer "*Warnings*")))
+ (setenv "PYTHONSTARTUP" startup-file)
+ (should python-shell-prompt-detect-failure-warning)
+ (should (not python-shell-prompt-detect-enabled))
+ (should (not (python-shell-prompt-detect)))
+ (should (not (get-buffer "*Warnings*"))))
+ (ignore-errors (delete-file startup-file)))))
+
+(ert-deftest python-shell-prompt-validate-regexps-1 ()
+ "Check `python-shell-prompt-input-regexps' are validated."
+ (let* ((python-shell-prompt-input-regexps '("\\("))
+ (error-data (should-error (python-shell-prompt-validate-regexps)
+ :type 'user-error)))
+ (should
+ (string= (cadr error-data)
+ (format-message
+ "Invalid regexp \\( in `python-shell-prompt-input-regexps'")))))
+
+(ert-deftest python-shell-prompt-validate-regexps-2 ()
+ "Check `python-shell-prompt-output-regexps' are validated."
+ (let* ((python-shell-prompt-output-regexps '("\\("))
+ (error-data (should-error (python-shell-prompt-validate-regexps)
+ :type 'user-error)))
+ (should
+ (string= (cadr error-data)
+ (format-message
+ "Invalid regexp \\( in `python-shell-prompt-output-regexps'")))))
+
+(ert-deftest python-shell-prompt-validate-regexps-3 ()
+ "Check `python-shell-prompt-regexp' is validated."
+ (let* ((python-shell-prompt-regexp "\\(")
+ (error-data (should-error (python-shell-prompt-validate-regexps)
+ :type 'user-error)))
+ (should
+ (string= (cadr error-data)
+ (format-message
+ "Invalid regexp \\( in `python-shell-prompt-regexp'")))))
+
+(ert-deftest python-shell-prompt-validate-regexps-4 ()
+ "Check `python-shell-prompt-block-regexp' is validated."
+ (let* ((python-shell-prompt-block-regexp "\\(")
+ (error-data (should-error (python-shell-prompt-validate-regexps)
+ :type 'user-error)))
+ (should
+ (string= (cadr error-data)
+ (format-message
+ "Invalid regexp \\( in `python-shell-prompt-block-regexp'")))))
+
+(ert-deftest python-shell-prompt-validate-regexps-5 ()
+ "Check `python-shell-prompt-pdb-regexp' is validated."
+ (let* ((python-shell-prompt-pdb-regexp "\\(")
+ (error-data (should-error (python-shell-prompt-validate-regexps)
+ :type 'user-error)))
+ (should
+ (string= (cadr error-data)
+ (format-message
+ "Invalid regexp \\( in `python-shell-prompt-pdb-regexp'")))))
+
+(ert-deftest python-shell-prompt-validate-regexps-6 ()
+ "Check `python-shell-prompt-output-regexp' is validated."
+ (let* ((python-shell-prompt-output-regexp "\\(")
+ (error-data (should-error (python-shell-prompt-validate-regexps)
+ :type 'user-error)))
+ (should
+ (string= (cadr error-data)
+ (format-message
+ "Invalid regexp \\( in `python-shell-prompt-output-regexp'")))))
+
+(ert-deftest python-shell-prompt-validate-regexps-7 ()
+ "Check default regexps are valid."
+ ;; should not signal error
+ (python-shell-prompt-validate-regexps))
+
+(ert-deftest python-shell-prompt-set-calculated-regexps-1 ()
+ "Check regexps are validated."
+ (let* ((python-shell-prompt-output-regexp '("\\("))
+ (python-shell--prompt-calculated-input-regexp nil)
+ (python-shell--prompt-calculated-output-regexp nil)
+ (python-shell-prompt-detect-enabled nil)
+ (error-data (should-error (python-shell-prompt-set-calculated-regexps)
+ :type 'user-error)))
+ (should
+ (string= (cadr error-data)
+ (format-message
+ "Invalid regexp \\( in `python-shell-prompt-output-regexp'")))))
+
+(ert-deftest python-shell-prompt-set-calculated-regexps-2 ()
+ "Check `python-shell-prompt-input-regexps' are set."
+ (let* ((python-shell-prompt-input-regexps '("my" "prompt"))
+ (python-shell-prompt-output-regexps '(""))
+ (python-shell-prompt-regexp "")
+ (python-shell-prompt-block-regexp "")
+ (python-shell-prompt-pdb-regexp "")
+ (python-shell-prompt-output-regexp "")
+ (python-shell--prompt-calculated-input-regexp nil)
+ (python-shell--prompt-calculated-output-regexp nil)
+ (python-shell-prompt-detect-enabled nil))
+ (python-shell-prompt-set-calculated-regexps)
+ (should (string= python-shell--prompt-calculated-input-regexp
+ "^\\(prompt\\|my\\|\\)"))))
+
+(ert-deftest python-shell-prompt-set-calculated-regexps-3 ()
+ "Check `python-shell-prompt-output-regexps' are set."
+ (let* ((python-shell-prompt-input-regexps '(""))
+ (python-shell-prompt-output-regexps '("my" "prompt"))
+ (python-shell-prompt-regexp "")
+ (python-shell-prompt-block-regexp "")
+ (python-shell-prompt-pdb-regexp "")
+ (python-shell-prompt-output-regexp "")
+ (python-shell--prompt-calculated-input-regexp nil)
+ (python-shell--prompt-calculated-output-regexp nil)
+ (python-shell-prompt-detect-enabled nil))
+ (python-shell-prompt-set-calculated-regexps)
+ (should (string= python-shell--prompt-calculated-output-regexp
+ "^\\(prompt\\|my\\|\\)"))))
+
+(ert-deftest python-shell-prompt-set-calculated-regexps-4 ()
+ "Check user defined prompts are set."
+ (let* ((python-shell-prompt-input-regexps '(""))
+ (python-shell-prompt-output-regexps '(""))
+ (python-shell-prompt-regexp "prompt")
+ (python-shell-prompt-block-regexp "block")
+ (python-shell-prompt-pdb-regexp "pdb")
+ (python-shell-prompt-output-regexp "output")
+ (python-shell--prompt-calculated-input-regexp nil)
+ (python-shell--prompt-calculated-output-regexp nil)
+ (python-shell-prompt-detect-enabled nil))
+ (python-shell-prompt-set-calculated-regexps)
+ (should (string= python-shell--prompt-calculated-input-regexp
+ "^\\(prompt\\|block\\|pdb\\|\\)"))
+ (should (string= python-shell--prompt-calculated-output-regexp
+ "^\\(output\\|\\)"))))
+
+(ert-deftest python-shell-prompt-set-calculated-regexps-5 ()
+ "Check order of regexps (larger first)."
+ (let* ((python-shell-prompt-input-regexps '("extralargeinputprompt" "sml"))
+ (python-shell-prompt-output-regexps '("extralargeoutputprompt" "sml"))
+ (python-shell-prompt-regexp "in")
+ (python-shell-prompt-block-regexp "block")
+ (python-shell-prompt-pdb-regexp "pdf")
+ (python-shell-prompt-output-regexp "output")
+ (python-shell--prompt-calculated-input-regexp nil)
+ (python-shell--prompt-calculated-output-regexp nil)
+ (python-shell-prompt-detect-enabled nil))
+ (python-shell-prompt-set-calculated-regexps)
+ (should (string= python-shell--prompt-calculated-input-regexp
+ "^\\(extralargeinputprompt\\|block\\|pdf\\|sml\\|in\\)"))
+ (should (string= python-shell--prompt-calculated-output-regexp
+ "^\\(extralargeoutputprompt\\|output\\|sml\\)"))))
+
+(ert-deftest python-shell-prompt-set-calculated-regexps-6 ()
+ "Check detected prompts are included `regexp-quote'd."
+ (skip-unless (executable-find python-tests-shell-interpreter))
+ (let* ((python-shell-prompt-input-regexps '(""))
+ (python-shell-prompt-output-regexps '(""))
+ (python-shell-prompt-regexp "")
+ (python-shell-prompt-block-regexp "")
+ (python-shell-prompt-pdb-regexp "")
+ (python-shell-prompt-output-regexp "")
+ (python-shell--prompt-calculated-input-regexp nil)
+ (python-shell--prompt-calculated-output-regexp nil)
+ (python-shell-prompt-detect-enabled t)
+ (process-environment process-environment)
+ (startup-code (concat "import sys\n"
+ "sys.ps1 = 'p.> '\n"
+ "sys.ps2 = '..> '\n"
+ "sys.ps3 = 'o.t '\n"))
+ (startup-file (python-shell--save-temp-file startup-code)))
+ (unwind-protect
+ (progn
+ (setenv "PYTHONSTARTUP" startup-file)
+ (python-shell-prompt-set-calculated-regexps)
+ (should (string= python-shell--prompt-calculated-input-regexp
+ "^\\(\\.\\.> \\|p\\.> \\|\\)"))
+ (should (string= python-shell--prompt-calculated-output-regexp
+ "^\\(o\\.t \\|\\)")))
+ (ignore-errors (delete-file startup-file)))))
+
+(ert-deftest python-shell-buffer-substring-1 ()
+ "Selecting a substring of the whole buffer must match its contents."
+ (python-tests-with-temp-buffer
+ "
+class Foo(models.Model):
+ pass
+
+
+class Bar(models.Model):
+ pass
+"
+ (should (string= (buffer-string)
+ (python-shell-buffer-substring (point-min) (point-max))))))
+
+(ert-deftest python-shell-buffer-substring-2 ()
+ "Main block should be removed if NOMAIN is non-nil."
+ (python-tests-with-temp-buffer
+ "
+class Foo(models.Model):
+ pass
+
+class Bar(models.Model):
+ pass
+
+if __name__ == \"__main__\":
+ foo = Foo()
+ print (foo)
+"
+ (should (string= (python-shell-buffer-substring (point-min) (point-max) t)
+ "
+class Foo(models.Model):
+ pass
+
+class Bar(models.Model):
+ pass
+
+
+
+
+"))))
+
+(ert-deftest python-shell-buffer-substring-3 ()
+ "Main block should be removed if NOMAIN is non-nil."
+ (python-tests-with-temp-buffer
+ "
+class Foo(models.Model):
+ pass
+
+if __name__ == \"__main__\":
+ foo = Foo()
+ print (foo)
+
+class Bar(models.Model):
+ pass
+"
+ (should (string= (python-shell-buffer-substring (point-min) (point-max) t)
+ "
+class Foo(models.Model):
+ pass
+
+
+
+
+
+class Bar(models.Model):
+ pass
+"))))
+
+(ert-deftest python-shell-buffer-substring-4 ()
+ "Coding cookie should be added for substrings."
+ (python-tests-with-temp-buffer
+ "# coding: latin-1
+
+class Foo(models.Model):
+ pass
+
+if __name__ == \"__main__\":
+ foo = Foo()
+ print (foo)
+
+class Bar(models.Model):
+ pass
+"
+ (should (string= (python-shell-buffer-substring
+ (python-tests-look-at "class Foo(models.Model):")
+ (progn (python-nav-forward-sexp) (point)))
+ "# -*- coding: latin-1 -*-
+
+class Foo(models.Model):
+ pass"))))
+
+(ert-deftest python-shell-buffer-substring-5 ()
+ "The proper amount of blank lines is added for a substring."
+ (python-tests-with-temp-buffer
+ "# coding: latin-1
+
+class Foo(models.Model):
+ pass
+
+if __name__ == \"__main__\":
+ foo = Foo()
+ print (foo)
+
+class Bar(models.Model):
+ pass
+"
+ (should (string= (python-shell-buffer-substring
+ (python-tests-look-at "class Bar(models.Model):")
+ (progn (python-nav-forward-sexp) (point)))
+ "# -*- coding: latin-1 -*-
+
+
+
+
+
+
+
+
+class Bar(models.Model):
+ pass"))))
+
+(ert-deftest python-shell-buffer-substring-6 ()
+ "Handle substring with coding cookie in the second line."
+ (python-tests-with-temp-buffer
+ "
+# coding: latin-1
+
+class Foo(models.Model):
+ pass
+
+if __name__ == \"__main__\":
+ foo = Foo()
+ print (foo)
+
+class Bar(models.Model):
+ pass
+"
+ (should (string= (python-shell-buffer-substring
+ (python-tests-look-at "# coding: latin-1")
+ (python-tests-look-at "if __name__ == \"__main__\":"))
+ "# -*- coding: latin-1 -*-
+
+
+class Foo(models.Model):
+ pass
+
+"))))
+
+(ert-deftest python-shell-buffer-substring-7 ()
+ "Ensure first coding cookie gets precedence."
+ (python-tests-with-temp-buffer
+ "# coding: utf-8
+# coding: latin-1
+
+class Foo(models.Model):
+ pass
+
+if __name__ == \"__main__\":
+ foo = Foo()
+ print (foo)
+
+class Bar(models.Model):
+ pass
+"
+ (should (string= (python-shell-buffer-substring
+ (python-tests-look-at "# coding: latin-1")
+ (python-tests-look-at "if __name__ == \"__main__\":"))
+ "# -*- coding: utf-8 -*-
+
+
+class Foo(models.Model):
+ pass
+
+"))))
+
+(ert-deftest python-shell-buffer-substring-8 ()
+ "Ensure first coding cookie gets precedence when sending whole buffer."
+ (python-tests-with-temp-buffer
+ "# coding: utf-8
+# coding: latin-1
+
+class Foo(models.Model):
+ pass
+"
+ (should (string= (python-shell-buffer-substring (point-min) (point-max))
+ "# coding: utf-8
+
+
+class Foo(models.Model):
+ pass
+"))))
+
+(ert-deftest python-shell-buffer-substring-9 ()
+ "Check substring starting from `point-min'."
+ (python-tests-with-temp-buffer
+ "# coding: utf-8
+
+class Foo(models.Model):
+ pass
+
+class Bar(models.Model):
+ pass
+"
+ (should (string= (python-shell-buffer-substring
+ (point-min)
+ (python-tests-look-at "class Bar(models.Model):"))
+ "# coding: utf-8
+
+class Foo(models.Model):
+ pass
+
+"))))
+
+(ert-deftest python-shell-buffer-substring-10 ()
+ "Check substring from partial block."
+ (python-tests-with-temp-buffer
+ "
+def foo():
+ print ('a')
+"
+ (should (string= (python-shell-buffer-substring
+ (python-tests-look-at "print ('a')")
+ (point-max))
+ "if True:
+
+ print ('a')
+"))))
+
+(ert-deftest python-shell-buffer-substring-11 ()
+ "Check substring from partial block and point within indentation."
+ (python-tests-with-temp-buffer
+ "
+def foo():
+ print ('a')
+"
+ (should (string= (python-shell-buffer-substring
+ (progn
+ (python-tests-look-at "print ('a')")
+ (backward-char 1)
+ (point))
+ (point-max))
+ "if True:
+
+ print ('a')
+"))))
+
+(ert-deftest python-shell-buffer-substring-12 ()
+ "Check substring from partial block and point in whitespace."
+ (python-tests-with-temp-buffer
+ "
+def foo():
+
+ # Whitespace
+
+ print ('a')
+"
+ (should (string= (python-shell-buffer-substring
+ (python-tests-look-at "# Whitespace")
+ (point-max))
+ "if True:
+
+
+ # Whitespace
+
+ print ('a')
+"))))
+
+
+
+;;; Shell completion
+
+(ert-deftest python-shell-completion-native-interpreter-disabled-p-1 ()
+ (let* ((python-shell-completion-native-disabled-interpreters (list "pypy"))
+ (python-shell-interpreter "/some/path/to/bin/pypy"))
+ (should (python-shell-completion-native-interpreter-disabled-p))))
+
+
+
+
+;;; PDB Track integration
+
+
+;;; Symbol completion
+
+
+;;; Fill paragraph
+
+
+;;; Skeletons
+
+
+;;; FFAP
+
+
+;;; Code check
+
+
+;;; Eldoc
+
+(ert-deftest python-eldoc--get-symbol-at-point-1 ()
+ "Test paren handling."
+ (python-tests-with-temp-buffer
+ "
+map(xx
+map(codecs.open('somefile'
+"
+ (python-tests-look-at "ap(xx")
+ (should (string= (python-eldoc--get-symbol-at-point) "map"))
+ (goto-char (line-end-position))
+ (should (string= (python-eldoc--get-symbol-at-point) "map"))
+ (python-tests-look-at "('somefile'")
+ (should (string= (python-eldoc--get-symbol-at-point) "map"))
+ (goto-char (line-end-position))
+ (should (string= (python-eldoc--get-symbol-at-point) "codecs.open"))))
+
+(ert-deftest python-eldoc--get-symbol-at-point-2 ()
+ "Ensure self is replaced with the class name."
+ (python-tests-with-temp-buffer
+ "
+class TheClass:
+
+ def some_method(self, n):
+ return n
+
+ def other(self):
+ return self.some_method(1234)
+
+"
+ (python-tests-look-at "self.some_method")
+ (should (string= (python-eldoc--get-symbol-at-point)
+ "TheClass.some_method"))
+ (python-tests-look-at "1234)")
+ (should (string= (python-eldoc--get-symbol-at-point)
+ "TheClass.some_method"))))
+
+(ert-deftest python-eldoc--get-symbol-at-point-3 ()
+ "Ensure symbol is found when point is at end of buffer."
+ (python-tests-with-temp-buffer
+ "
+some_symbol
+
+"
+ (goto-char (point-max))
+ (should (string= (python-eldoc--get-symbol-at-point)
+ "some_symbol"))))
+
+(ert-deftest python-eldoc--get-symbol-at-point-4 ()
+ "Ensure symbol is found when point is at whitespace."
+ (python-tests-with-temp-buffer
+ "
+some_symbol some_other_symbol
+"
+ (python-tests-look-at " some_other_symbol")
+ (should (string= (python-eldoc--get-symbol-at-point)
+ "some_symbol"))))
+
+
+;;; Imenu
+
+(ert-deftest python-imenu-create-index-1 ()
+ (python-tests-with-temp-buffer
+ "
+class Foo(models.Model):
+ pass
+
+
+class Bar(models.Model):
+ pass
+
+
+def decorator(arg1, arg2, arg3):
+ '''print decorated function call data to stdout.
+
+ Usage:
+
+ @decorator('arg1', 'arg2')
+ def func(a, b, c=True):
+ pass
+ '''
+
+ def wrap(f):
+ print ('wrap')
+ def wrapped_f(*args):
+ print ('wrapped_f')
+ print ('Decorator arguments:', arg1, arg2, arg3)
+ f(*args)
+ print ('called f(*args)')
+ return wrapped_f
+ return wrap
+
+
+class Baz(object):
+
+ def a(self):
+ pass
+
+ def b(self):
+ pass
+
+ class Frob(object):
+
+ def c(self):
+ pass
+"
+ (goto-char (point-max))
+ (should (equal
+ (list
+ (cons "Foo (class)" (copy-marker 2))
+ (cons "Bar (class)" (copy-marker 38))
+ (list
+ "decorator (def)"
+ (cons "*function definition*" (copy-marker 74))
+ (list
+ "wrap (def)"
+ (cons "*function definition*" (copy-marker 254))
+ (cons "wrapped_f (def)" (copy-marker 294))))
+ (list
+ "Baz (class)"
+ (cons "*class definition*" (copy-marker 519))
+ (cons "a (def)" (copy-marker 539))
+ (cons "b (def)" (copy-marker 570))
+ (list
+ "Frob (class)"
+ (cons "*class definition*" (copy-marker 601))
+ (cons "c (def)" (copy-marker 626)))))
+ (python-imenu-create-index)))))
+
+(ert-deftest python-imenu-create-index-2 ()
+ (python-tests-with-temp-buffer
+ "
+class Foo(object):
+ def foo(self):
+ def foo1():
+ pass
+
+ def foobar(self):
+ pass
+"
+ (goto-char (point-max))
+ (should (equal
+ (list
+ (list
+ "Foo (class)"
+ (cons "*class definition*" (copy-marker 2))
+ (list
+ "foo (def)"
+ (cons "*function definition*" (copy-marker 21))
+ (cons "foo1 (def)" (copy-marker 40)))
+ (cons "foobar (def)" (copy-marker 78))))
+ (python-imenu-create-index)))))
+
+(ert-deftest python-imenu-create-index-3 ()
+ (python-tests-with-temp-buffer
+ "
+class Foo(object):
+ def foo(self):
+ def foo1():
+ pass
+ def foo2():
+ pass
+"
+ (goto-char (point-max))
+ (should (equal
+ (list
+ (list
+ "Foo (class)"
+ (cons "*class definition*" (copy-marker 2))
+ (list
+ "foo (def)"
+ (cons "*function definition*" (copy-marker 21))
+ (cons "foo1 (def)" (copy-marker 40))
+ (cons "foo2 (def)" (copy-marker 77)))))
+ (python-imenu-create-index)))))
+
+(ert-deftest python-imenu-create-index-4 ()
+ (python-tests-with-temp-buffer
+ "
+class Foo(object):
+ class Bar(object):
+ def __init__(self):
+ pass
+
+ def __str__(self):
+ pass
+
+ def __init__(self):
+ pass
+"
+ (goto-char (point-max))
+ (should (equal
+ (list
+ (list
+ "Foo (class)"
+ (cons "*class definition*" (copy-marker 2))
+ (list
+ "Bar (class)"
+ (cons "*class definition*" (copy-marker 21))
+ (cons "__init__ (def)" (copy-marker 44))
+ (cons "__str__ (def)" (copy-marker 90)))
+ (cons "__init__ (def)" (copy-marker 135))))
+ (python-imenu-create-index)))))
+
+(ert-deftest python-imenu-create-flat-index-1 ()
+ (python-tests-with-temp-buffer
+ "
+class Foo(models.Model):
+ pass
+
+
+class Bar(models.Model):
+ pass
+
+
+def decorator(arg1, arg2, arg3):
+ '''print decorated function call data to stdout.
+
+ Usage:
+
+ @decorator('arg1', 'arg2')
+ def func(a, b, c=True):
+ pass
+ '''
+
+ def wrap(f):
+ print ('wrap')
+ def wrapped_f(*args):
+ print ('wrapped_f')
+ print ('Decorator arguments:', arg1, arg2, arg3)
+ f(*args)
+ print ('called f(*args)')
+ return wrapped_f
+ return wrap
+
+
+class Baz(object):
+
+ def a(self):
+ pass
+
+ def b(self):
+ pass
+
+ class Frob(object):
+
+ def c(self):
+ pass
+"
+ (goto-char (point-max))
+ (should (equal
+ (list (cons "Foo" (copy-marker 2))
+ (cons "Bar" (copy-marker 38))
+ (cons "decorator" (copy-marker 74))
+ (cons "decorator.wrap" (copy-marker 254))
+ (cons "decorator.wrap.wrapped_f" (copy-marker 294))
+ (cons "Baz" (copy-marker 519))
+ (cons "Baz.a" (copy-marker 539))
+ (cons "Baz.b" (copy-marker 570))
+ (cons "Baz.Frob" (copy-marker 601))
+ (cons "Baz.Frob.c" (copy-marker 626)))
+ (python-imenu-create-flat-index)))))
+
+(ert-deftest python-imenu-create-flat-index-2 ()
+ (python-tests-with-temp-buffer
+ "
+class Foo(object):
+ class Bar(object):
+ def __init__(self):
+ pass
+
+ def __str__(self):
+ pass
+
+ def __init__(self):
+ pass
+"
+ (goto-char (point-max))
+ (should (equal
+ (list
+ (cons "Foo" (copy-marker 2))
+ (cons "Foo.Bar" (copy-marker 21))
+ (cons "Foo.Bar.__init__" (copy-marker 44))
+ (cons "Foo.Bar.__str__" (copy-marker 90))
+ (cons "Foo.__init__" (copy-marker 135)))
+ (python-imenu-create-flat-index)))))
+
+
+;;; Misc helpers
+
+(ert-deftest python-info-current-defun-1 ()
+ (python-tests-with-temp-buffer
+ "
+def foo(a, b):
+"
+ (forward-line 1)
+ (should (string= "foo" (python-info-current-defun)))
+ (should (string= "def foo" (python-info-current-defun t)))
+ (forward-line 1)
+ (should (not (python-info-current-defun)))
+ (indent-for-tab-command)
+ (should (string= "foo" (python-info-current-defun)))
+ (should (string= "def foo" (python-info-current-defun t)))))
+
+(ert-deftest python-info-current-defun-2 ()
+ (python-tests-with-temp-buffer
+ "
+class C(object):
+
+ def m(self):
+ if True:
+ return [i for i in range(3)]
+ else:
+ return []
+
+ def b():
+ do_b()
+
+ def a():
+ do_a()
+
+ def c(self):
+ do_c()
+"
+ (forward-line 1)
+ (should (string= "C" (python-info-current-defun)))
+ (should (string= "class C" (python-info-current-defun t)))
+ (python-tests-look-at "return [i for ")
+ (should (string= "C.m" (python-info-current-defun)))
+ (should (string= "def C.m" (python-info-current-defun t)))
+ (python-tests-look-at "def b():")
+ (should (string= "C.m.b" (python-info-current-defun)))
+ (should (string= "def C.m.b" (python-info-current-defun t)))
+ (forward-line 2)
+ (indent-for-tab-command)
+ (python-indent-dedent-line-backspace 1)
+ (should (string= "C.m" (python-info-current-defun)))
+ (should (string= "def C.m" (python-info-current-defun t)))
+ (python-tests-look-at "def c(self):")
+ (forward-line -1)
+ (indent-for-tab-command)
+ (should (string= "C.m.a" (python-info-current-defun)))
+ (should (string= "def C.m.a" (python-info-current-defun t)))
+ (python-indent-dedent-line-backspace 1)
+ (should (string= "C.m" (python-info-current-defun)))
+ (should (string= "def C.m" (python-info-current-defun t)))
+ (python-indent-dedent-line-backspace 1)
+ (should (string= "C" (python-info-current-defun)))
+ (should (string= "class C" (python-info-current-defun t)))
+ (python-tests-look-at "def c(self):")
+ (should (string= "C.c" (python-info-current-defun)))
+ (should (string= "def C.c" (python-info-current-defun t)))
+ (python-tests-look-at "do_c()")
+ (should (string= "C.c" (python-info-current-defun)))
+ (should (string= "def C.c" (python-info-current-defun t)))))
+
+(ert-deftest python-info-current-defun-3 ()
+ (python-tests-with-temp-buffer
+ "
+def decoratorFunctionWithArguments(arg1, arg2, arg3):
+ '''print decorated function call data to stdout.
+
+ Usage:
+
+ @decoratorFunctionWithArguments('arg1', 'arg2')
+ def func(a, b, c=True):
+ pass
+ '''
+
+ def wwrap(f):
+ print 'Inside wwrap()'
+ def wrapped_f(*args):
+ print 'Inside wrapped_f()'
+ print 'Decorator arguments:', arg1, arg2, arg3
+ f(*args)
+ print 'After f(*args)'
+ return wrapped_f
+ return wwrap
+"
+ (python-tests-look-at "def wwrap(f):")
+ (forward-line -1)
+ (should (not (python-info-current-defun)))
+ (indent-for-tab-command 1)
+ (should (string= (python-info-current-defun)
+ "decoratorFunctionWithArguments"))
+ (should (string= (python-info-current-defun t)
+ "def decoratorFunctionWithArguments"))
+ (python-tests-look-at "def wrapped_f(*args):")
+ (should (string= (python-info-current-defun)
+ "decoratorFunctionWithArguments.wwrap.wrapped_f"))
+ (should (string= (python-info-current-defun t)
+ "def decoratorFunctionWithArguments.wwrap.wrapped_f"))
+ (python-tests-look-at "return wrapped_f")
+ (should (string= (python-info-current-defun)
+ "decoratorFunctionWithArguments.wwrap"))
+ (should (string= (python-info-current-defun t)
+ "def decoratorFunctionWithArguments.wwrap"))
+ (end-of-line 1)
+ (python-tests-look-at "return wwrap")
+ (should (string= (python-info-current-defun)
+ "decoratorFunctionWithArguments"))
+ (should (string= (python-info-current-defun t)
+ "def decoratorFunctionWithArguments"))))
+
+(ert-deftest python-info-current-symbol-1 ()
+ (python-tests-with-temp-buffer
+ "
+class C(object):
+
+ def m(self):
+ self.c()
+
+ def c(self):
+ print ('a')
+"
+ (python-tests-look-at "self.c()")
+ (should (string= "self.c" (python-info-current-symbol)))
+ (should (string= "C.c" (python-info-current-symbol t)))))
+
+(ert-deftest python-info-current-symbol-2 ()
+ (python-tests-with-temp-buffer
+ "
+class C(object):
+
+ class M(object):
+
+ def a(self):
+ self.c()
+
+ def c(self):
+ pass
+"
+ (python-tests-look-at "self.c()")
+ (should (string= "self.c" (python-info-current-symbol)))
+ (should (string= "C.M.c" (python-info-current-symbol t)))))
+
+(ert-deftest python-info-current-symbol-3 ()
+ "Keywords should not be considered symbols."
+ :expected-result :failed
+ (python-tests-with-temp-buffer
+ "
+class C(object):
+ pass
+"
+ ;; FIXME: keywords are not symbols.
+ (python-tests-look-at "class C")
+ (should (not (python-info-current-symbol)))
+ (should (not (python-info-current-symbol t)))
+ (python-tests-look-at "C(object)")
+ (should (string= "C" (python-info-current-symbol)))
+ (should (string= "class C" (python-info-current-symbol t)))))
+
+(ert-deftest python-info-statement-starts-block-p-1 ()
+ (python-tests-with-temp-buffer
+ "
+def long_function_name(
+ var_one, var_two, var_three,
+ var_four):
+ print (var_one)
+"
+ (python-tests-look-at "def long_function_name")
+ (should (python-info-statement-starts-block-p))
+ (python-tests-look-at "print (var_one)")
+ (python-util-forward-comment -1)
+ (should (python-info-statement-starts-block-p))))
+
+(ert-deftest python-info-statement-starts-block-p-2 ()
+ (python-tests-with-temp-buffer
+ "
+if width == 0 and height == 0 and \\\\
+ color == 'red' and emphasis == 'strong' or \\\\
+ highlight > 100:
+ raise ValueError('sorry, you lose')
+"
+ (python-tests-look-at "if width == 0 and")
+ (should (python-info-statement-starts-block-p))
+ (python-tests-look-at "raise ValueError(")
+ (python-util-forward-comment -1)
+ (should (python-info-statement-starts-block-p))))
+
+(ert-deftest python-info-statement-ends-block-p-1 ()
+ (python-tests-with-temp-buffer
+ "
+def long_function_name(
+ var_one, var_two, var_three,
+ var_four):
+ print (var_one)
+"
+ (python-tests-look-at "print (var_one)")
+ (should (python-info-statement-ends-block-p))))
+
+(ert-deftest python-info-statement-ends-block-p-2 ()
+ (python-tests-with-temp-buffer
+ "
+if width == 0 and height == 0 and \\\\
+ color == 'red' and emphasis == 'strong' or \\\\
+ highlight > 100:
+ raise ValueError(
+'sorry, you lose'
+
+)
+"
+ (python-tests-look-at "raise ValueError(")
+ (should (python-info-statement-ends-block-p))))
+
+(ert-deftest python-info-beginning-of-statement-p-1 ()
+ (python-tests-with-temp-buffer
+ "
+def long_function_name(
+ var_one, var_two, var_three,
+ var_four):
+ print (var_one)
+"
+ (python-tests-look-at "def long_function_name")
+ (should (python-info-beginning-of-statement-p))
+ (forward-char 10)
+ (should (not (python-info-beginning-of-statement-p)))
+ (python-tests-look-at "print (var_one)")
+ (should (python-info-beginning-of-statement-p))
+ (goto-char (line-beginning-position))
+ (should (not (python-info-beginning-of-statement-p)))))
+
+(ert-deftest python-info-beginning-of-statement-p-2 ()
+ (python-tests-with-temp-buffer
+ "
+if width == 0 and height == 0 and \\\\
+ color == 'red' and emphasis == 'strong' or \\\\
+ highlight > 100:
+ raise ValueError(
+'sorry, you lose'
+
+)
+"
+ (python-tests-look-at "if width == 0 and")
+ (should (python-info-beginning-of-statement-p))
+ (forward-char 10)
+ (should (not (python-info-beginning-of-statement-p)))
+ (python-tests-look-at "raise ValueError(")
+ (should (python-info-beginning-of-statement-p))
+ (goto-char (line-beginning-position))
+ (should (not (python-info-beginning-of-statement-p)))))
+
+(ert-deftest python-info-end-of-statement-p-1 ()
+ (python-tests-with-temp-buffer
+ "
+def long_function_name(
+ var_one, var_two, var_three,
+ var_four):
+ print (var_one)
+"
+ (python-tests-look-at "def long_function_name")
+ (should (not (python-info-end-of-statement-p)))
+ (end-of-line)
+ (should (not (python-info-end-of-statement-p)))
+ (python-tests-look-at "print (var_one)")
+ (python-util-forward-comment -1)
+ (should (python-info-end-of-statement-p))
+ (python-tests-look-at "print (var_one)")
+ (should (not (python-info-end-of-statement-p)))
+ (end-of-line)
+ (should (python-info-end-of-statement-p))))
+
+(ert-deftest python-info-end-of-statement-p-2 ()
+ (python-tests-with-temp-buffer
+ "
+if width == 0 and height == 0 and \\\\
+ color == 'red' and emphasis == 'strong' or \\\\
+ highlight > 100:
+ raise ValueError(
+'sorry, you lose'
+
+)
+"
+ (python-tests-look-at "if width == 0 and")
+ (should (not (python-info-end-of-statement-p)))
+ (end-of-line)
+ (should (not (python-info-end-of-statement-p)))
+ (python-tests-look-at "raise ValueError(")
+ (python-util-forward-comment -1)
+ (should (python-info-end-of-statement-p))
+ (python-tests-look-at "raise ValueError(")
+ (should (not (python-info-end-of-statement-p)))
+ (end-of-line)
+ (should (not (python-info-end-of-statement-p)))
+ (goto-char (point-max))
+ (python-util-forward-comment -1)
+ (should (python-info-end-of-statement-p))))
+
+(ert-deftest python-info-beginning-of-block-p-1 ()
+ (python-tests-with-temp-buffer
+ "
+def long_function_name(
+ var_one, var_two, var_three,
+ var_four):
+ print (var_one)
+"
+ (python-tests-look-at "def long_function_name")
+ (should (python-info-beginning-of-block-p))
+ (python-tests-look-at "var_one, var_two, var_three,")
+ (should (not (python-info-beginning-of-block-p)))
+ (python-tests-look-at "print (var_one)")
+ (should (not (python-info-beginning-of-block-p)))))
+
+(ert-deftest python-info-beginning-of-block-p-2 ()
+ (python-tests-with-temp-buffer
+ "
+if width == 0 and height == 0 and \\\\
+ color == 'red' and emphasis == 'strong' or \\\\
+ highlight > 100:
+ raise ValueError(
+'sorry, you lose'
+
+)
+"
+ (python-tests-look-at "if width == 0 and")
+ (should (python-info-beginning-of-block-p))
+ (python-tests-look-at "color == 'red' and emphasis")
+ (should (not (python-info-beginning-of-block-p)))
+ (python-tests-look-at "raise ValueError(")
+ (should (not (python-info-beginning-of-block-p)))))
+
+(ert-deftest python-info-end-of-block-p-1 ()
+ (python-tests-with-temp-buffer
+ "
+def long_function_name(
+ var_one, var_two, var_three,
+ var_four):
+ print (var_one)
+"
+ (python-tests-look-at "def long_function_name")
+ (should (not (python-info-end-of-block-p)))
+ (python-tests-look-at "var_one, var_two, var_three,")
+ (should (not (python-info-end-of-block-p)))
+ (python-tests-look-at "var_four):")
+ (end-of-line)
+ (should (not (python-info-end-of-block-p)))
+ (python-tests-look-at "print (var_one)")
+ (should (not (python-info-end-of-block-p)))
+ (end-of-line 1)
+ (should (python-info-end-of-block-p))))
+
+(ert-deftest python-info-end-of-block-p-2 ()
+ (python-tests-with-temp-buffer
+ "
+if width == 0 and height == 0 and \\\\
+ color == 'red' and emphasis == 'strong' or \\\\
+ highlight > 100:
+ raise ValueError(
+'sorry, you lose'
+
+)
+"
+ (python-tests-look-at "if width == 0 and")
+ (should (not (python-info-end-of-block-p)))
+ (python-tests-look-at "color == 'red' and emphasis == 'strong' or")
+ (should (not (python-info-end-of-block-p)))
+ (python-tests-look-at "highlight > 100:")
+ (end-of-line)
+ (should (not (python-info-end-of-block-p)))
+ (python-tests-look-at "raise ValueError(")
+ (should (not (python-info-end-of-block-p)))
+ (end-of-line 1)
+ (should (not (python-info-end-of-block-p)))
+ (goto-char (point-max))
+ (python-util-forward-comment -1)
+ (should (python-info-end-of-block-p))))
+
+(ert-deftest python-info-dedenter-opening-block-position-1 ()
+ (python-tests-with-temp-buffer
+ "
+if request.user.is_authenticated():
+ try:
+ profile = request.user.get_profile()
+ except Profile.DoesNotExist:
+ profile = Profile.objects.create(user=request.user)
+ else:
+ if profile.stats:
+ profile.recalculate_stats()
+ else:
+ profile.clear_stats()
+ finally:
+ profile.views += 1
+ profile.save()
+"
+ (python-tests-look-at "try:")
+ (should (not (python-info-dedenter-opening-block-position)))
+ (python-tests-look-at "except Profile.DoesNotExist:")
+ (should (= (python-tests-look-at "try:" -1 t)
+ (python-info-dedenter-opening-block-position)))
+ (python-tests-look-at "else:")
+ (should (= (python-tests-look-at "except Profile.DoesNotExist:" -1 t)
+ (python-info-dedenter-opening-block-position)))
+ (python-tests-look-at "if profile.stats:")
+ (should (not (python-info-dedenter-opening-block-position)))
+ (python-tests-look-at "else:")
+ (should (= (python-tests-look-at "if profile.stats:" -1 t)
+ (python-info-dedenter-opening-block-position)))
+ (python-tests-look-at "finally:")
+ (should (= (python-tests-look-at "else:" -2 t)
+ (python-info-dedenter-opening-block-position)))))
+
+(ert-deftest python-info-dedenter-opening-block-position-2 ()
+ (python-tests-with-temp-buffer
+ "
+if request.user.is_authenticated():
+ profile = Profile.objects.get_or_create(user=request.user)
+ if profile.stats:
+ profile.recalculate_stats()
+
+data = {
+ 'else': 'do it'
+}
+ 'else'
+"
+ (python-tests-look-at "'else': 'do it'")
+ (should (not (python-info-dedenter-opening-block-position)))
+ (python-tests-look-at "'else'")
+ (should (not (python-info-dedenter-opening-block-position)))))
+
+(ert-deftest python-info-dedenter-opening-block-position-3 ()
+ (python-tests-with-temp-buffer
+ "
+if save:
+ try:
+ write_to_disk(data)
+ except IOError:
+ msg = 'Error saving to disk'
+ message(msg)
+ logger.exception(msg)
+ except Exception:
+ if hide_details:
+ logger.exception('Unhandled exception')
+ else
+ finally:
+ data.free()
+"
+ (python-tests-look-at "try:")
+ (should (not (python-info-dedenter-opening-block-position)))
+
+ (python-tests-look-at "except IOError:")
+ (should (= (python-tests-look-at "try:" -1 t)
+ (python-info-dedenter-opening-block-position)))
+
+ (python-tests-look-at "except Exception:")
+ (should (= (python-tests-look-at "except IOError:" -1 t)
+ (python-info-dedenter-opening-block-position)))
+
+ (python-tests-look-at "if hide_details:")
+ (should (not (python-info-dedenter-opening-block-position)))
+
+ ;; check indentation modifies the detected opening block
+ (python-tests-look-at "else")
+ (should (= (python-tests-look-at "if hide_details:" -1 t)
+ (python-info-dedenter-opening-block-position)))
+
+ (indent-line-to 8)
+ (should (= (python-tests-look-at "if hide_details:" -1 t)
+ (python-info-dedenter-opening-block-position)))
+
+ (indent-line-to 4)
+ (should (= (python-tests-look-at "except Exception:" -1 t)
+ (python-info-dedenter-opening-block-position)))
+
+ (indent-line-to 0)
+ (should (= (python-tests-look-at "if save:" -1 t)
+ (python-info-dedenter-opening-block-position)))))
+
+(ert-deftest python-info-dedenter-opening-block-positions-1 ()
+ (python-tests-with-temp-buffer
+ "
+if save:
+ try:
+ write_to_disk(data)
+ except IOError:
+ msg = 'Error saving to disk'
+ message(msg)
+ logger.exception(msg)
+ except Exception:
+ if hide_details:
+ logger.exception('Unhandled exception')
+ else
+ finally:
+ data.free()
+"
+ (python-tests-look-at "try:")
+ (should (not (python-info-dedenter-opening-block-positions)))
+
+ (python-tests-look-at "except IOError:")
+ (should
+ (equal (list
+ (python-tests-look-at "try:" -1 t))
+ (python-info-dedenter-opening-block-positions)))
+
+ (python-tests-look-at "except Exception:")
+ (should
+ (equal (list
+ (python-tests-look-at "except IOError:" -1 t))
+ (python-info-dedenter-opening-block-positions)))
+
+ (python-tests-look-at "if hide_details:")
+ (should (not (python-info-dedenter-opening-block-positions)))
+
+ ;; check indentation does not modify the detected opening blocks
+ (python-tests-look-at "else")
+ (should
+ (equal (list
+ (python-tests-look-at "if hide_details:" -1 t)
+ (python-tests-look-at "except Exception:" -1 t)
+ (python-tests-look-at "if save:" -1 t))
+ (python-info-dedenter-opening-block-positions)))
+
+ (indent-line-to 8)
+ (should
+ (equal (list
+ (python-tests-look-at "if hide_details:" -1 t)
+ (python-tests-look-at "except Exception:" -1 t)
+ (python-tests-look-at "if save:" -1 t))
+ (python-info-dedenter-opening-block-positions)))
+
+ (indent-line-to 4)
+ (should
+ (equal (list
+ (python-tests-look-at "if hide_details:" -1 t)
+ (python-tests-look-at "except Exception:" -1 t)
+ (python-tests-look-at "if save:" -1 t))
+ (python-info-dedenter-opening-block-positions)))
+
+ (indent-line-to 0)
+ (should
+ (equal (list
+ (python-tests-look-at "if hide_details:" -1 t)
+ (python-tests-look-at "except Exception:" -1 t)
+ (python-tests-look-at "if save:" -1 t))
+ (python-info-dedenter-opening-block-positions)))))
+
+(ert-deftest python-info-dedenter-opening-block-positions-2 ()
+ "Test detection of opening blocks for elif."
+ (python-tests-with-temp-buffer
+ "
+if var:
+ if var2:
+ something()
+ elif var3:
+ something_else()
+ elif
+"
+ (python-tests-look-at "elif var3:")
+ (should
+ (equal (list
+ (python-tests-look-at "if var2:" -1 t)
+ (python-tests-look-at "if var:" -1 t))
+ (python-info-dedenter-opening-block-positions)))
+
+ (python-tests-look-at "elif\n")
+ (should
+ (equal (list
+ (python-tests-look-at "elif var3:" -1 t)
+ (python-tests-look-at "if var:" -1 t))
+ (python-info-dedenter-opening-block-positions)))))
+
+(ert-deftest python-info-dedenter-opening-block-positions-3 ()
+ "Test detection of opening blocks for else."
+ (python-tests-with-temp-buffer
+ "
+try:
+ something()
+except:
+ if var:
+ if var2:
+ something()
+ elif var3:
+ something_else()
+ else
+
+if var4:
+ while var5:
+ var4.pop()
+ else
+
+ for value in var6:
+ if value > 0:
+ print value
+ else
+"
+ (python-tests-look-at "else\n")
+ (should
+ (equal (list
+ (python-tests-look-at "elif var3:" -1 t)
+ (python-tests-look-at "if var:" -1 t)
+ (python-tests-look-at "except:" -1 t))
+ (python-info-dedenter-opening-block-positions)))
+
+ (python-tests-look-at "else\n")
+ (should
+ (equal (list
+ (python-tests-look-at "while var5:" -1 t)
+ (python-tests-look-at "if var4:" -1 t))
+ (python-info-dedenter-opening-block-positions)))
+
+ (python-tests-look-at "else\n")
+ (should
+ (equal (list
+ (python-tests-look-at "if value > 0:" -1 t)
+ (python-tests-look-at "for value in var6:" -1 t)
+ (python-tests-look-at "if var4:" -1 t))
+ (python-info-dedenter-opening-block-positions)))))
+
+(ert-deftest python-info-dedenter-opening-block-positions-4 ()
+ "Test detection of opening blocks for except."
+ (python-tests-with-temp-buffer
+ "
+try:
+ something()
+except ValueError:
+ something_else()
+ except
+"
+ (python-tests-look-at "except ValueError:")
+ (should
+ (equal (list (python-tests-look-at "try:" -1 t))
+ (python-info-dedenter-opening-block-positions)))
+
+ (python-tests-look-at "except\n")
+ (should
+ (equal (list (python-tests-look-at "except ValueError:" -1 t))
+ (python-info-dedenter-opening-block-positions)))))
+
+(ert-deftest python-info-dedenter-opening-block-positions-5 ()
+ "Test detection of opening blocks for finally."
+ (python-tests-with-temp-buffer
+ "
+try:
+ something()
+ finally
+
+try:
+ something_else()
+except:
+ logger.exception('something went wrong')
+ finally
+
+try:
+ something_else_else()
+except Exception:
+ logger.exception('something else went wrong')
+else:
+ print ('all good')
+ finally
+"
+ (python-tests-look-at "finally\n")
+ (should
+ (equal (list (python-tests-look-at "try:" -1 t))
+ (python-info-dedenter-opening-block-positions)))
+
+ (python-tests-look-at "finally\n")
+ (should
+ (equal (list (python-tests-look-at "except:" -1 t))
+ (python-info-dedenter-opening-block-positions)))
+
+ (python-tests-look-at "finally\n")
+ (should
+ (equal (list (python-tests-look-at "else:" -1 t))
+ (python-info-dedenter-opening-block-positions)))))
+
+(ert-deftest python-info-dedenter-opening-block-message-1 ()
+ "Test dedenters inside strings are ignored."
+ (python-tests-with-temp-buffer
+ "'''
+try:
+ something()
+except:
+ logger.exception('something went wrong')
+'''
+"
+ (python-tests-look-at "except\n")
+ (should (not (python-info-dedenter-opening-block-message)))))
+
+(ert-deftest python-info-dedenter-opening-block-message-2 ()
+ "Test except keyword."
+ (python-tests-with-temp-buffer
+ "
+try:
+ something()
+except:
+ logger.exception('something went wrong')
+"
+ (python-tests-look-at "except:")
+ (should (string=
+ "Closes try:"
+ (substring-no-properties
+ (python-info-dedenter-opening-block-message))))
+ (end-of-line)
+ (should (string=
+ "Closes try:"
+ (substring-no-properties
+ (python-info-dedenter-opening-block-message))))))
+
+(ert-deftest python-info-dedenter-opening-block-message-3 ()
+ "Test else keyword."
+ (python-tests-with-temp-buffer
+ "
+try:
+ something()
+except:
+ logger.exception('something went wrong')
+else:
+ logger.debug('all good')
+"
+ (python-tests-look-at "else:")
+ (should (string=
+ "Closes except:"
+ (substring-no-properties
+ (python-info-dedenter-opening-block-message))))
+ (end-of-line)
+ (should (string=
+ "Closes except:"
+ (substring-no-properties
+ (python-info-dedenter-opening-block-message))))))
+
+(ert-deftest python-info-dedenter-opening-block-message-4 ()
+ "Test finally keyword."
+ (python-tests-with-temp-buffer
+ "
+try:
+ something()
+except:
+ logger.exception('something went wrong')
+else:
+ logger.debug('all good')
+finally:
+ clean()
+"
+ (python-tests-look-at "finally:")
+ (should (string=
+ "Closes else:"
+ (substring-no-properties
+ (python-info-dedenter-opening-block-message))))
+ (end-of-line)
+ (should (string=
+ "Closes else:"
+ (substring-no-properties
+ (python-info-dedenter-opening-block-message))))))
+
+(ert-deftest python-info-dedenter-opening-block-message-5 ()
+ "Test elif keyword."
+ (python-tests-with-temp-buffer
+ "
+if a:
+ something()
+elif b:
+"
+ (python-tests-look-at "elif b:")
+ (should (string=
+ "Closes if a:"
+ (substring-no-properties
+ (python-info-dedenter-opening-block-message))))
+ (end-of-line)
+ (should (string=
+ "Closes if a:"
+ (substring-no-properties
+ (python-info-dedenter-opening-block-message))))))
+
+
+(ert-deftest python-info-dedenter-statement-p-1 ()
+ "Test dedenters inside strings are ignored."
+ (python-tests-with-temp-buffer
+ "'''
+try:
+ something()
+except:
+ logger.exception('something went wrong')
+'''
+"
+ (python-tests-look-at "except\n")
+ (should (not (python-info-dedenter-statement-p)))))
+
+(ert-deftest python-info-dedenter-statement-p-2 ()
+ "Test except keyword."
+ (python-tests-with-temp-buffer
+ "
+try:
+ something()
+except:
+ logger.exception('something went wrong')
+"
+ (python-tests-look-at "except:")
+ (should (= (point) (python-info-dedenter-statement-p)))
+ (end-of-line)
+ (should (= (save-excursion
+ (back-to-indentation)
+ (point))
+ (python-info-dedenter-statement-p)))))
+
+(ert-deftest python-info-dedenter-statement-p-3 ()
+ "Test else keyword."
+ (python-tests-with-temp-buffer
+ "
+try:
+ something()
+except:
+ logger.exception('something went wrong')
+else:
+ logger.debug('all good')
+"
+ (python-tests-look-at "else:")
+ (should (= (point) (python-info-dedenter-statement-p)))
+ (end-of-line)
+ (should (= (save-excursion
+ (back-to-indentation)
+ (point))
+ (python-info-dedenter-statement-p)))))
+
+(ert-deftest python-info-dedenter-statement-p-4 ()
+ "Test finally keyword."
+ (python-tests-with-temp-buffer
+ "
+try:
+ something()
+except:
+ logger.exception('something went wrong')
+else:
+ logger.debug('all good')
+finally:
+ clean()
+"
+ (python-tests-look-at "finally:")
+ (should (= (point) (python-info-dedenter-statement-p)))
+ (end-of-line)
+ (should (= (save-excursion
+ (back-to-indentation)
+ (point))
+ (python-info-dedenter-statement-p)))))
+
+(ert-deftest python-info-dedenter-statement-p-5 ()
+ "Test elif keyword."
+ (python-tests-with-temp-buffer
+ "
+if a:
+ something()
+elif b:
+"
+ (python-tests-look-at "elif b:")
+ (should (= (point) (python-info-dedenter-statement-p)))
+ (end-of-line)
+ (should (= (save-excursion
+ (back-to-indentation)
+ (point))
+ (python-info-dedenter-statement-p)))))
+
+(ert-deftest python-info-line-ends-backslash-p-1 ()
+ (python-tests-with-temp-buffer
+ "
+objects = Thing.objects.all() \\\\
+ .filter(
+ type='toy',
+ status='bought'
+ ) \\\\
+ .aggregate(
+ Sum('amount')
+ ) \\\\
+ .values_list()
+"
+ (should (python-info-line-ends-backslash-p 2)) ; .filter(...
+ (should (python-info-line-ends-backslash-p 3))
+ (should (python-info-line-ends-backslash-p 4))
+ (should (python-info-line-ends-backslash-p 5))
+ (should (python-info-line-ends-backslash-p 6)) ; ) \...
+ (should (python-info-line-ends-backslash-p 7))
+ (should (python-info-line-ends-backslash-p 8))
+ (should (python-info-line-ends-backslash-p 9))
+ (should (not (python-info-line-ends-backslash-p 10))))) ; .values_list()...
+
+(ert-deftest python-info-beginning-of-backslash-1 ()
+ (python-tests-with-temp-buffer
+ "
+objects = Thing.objects.all() \\\\
+ .filter(
+ type='toy',
+ status='bought'
+ ) \\\\
+ .aggregate(
+ Sum('amount')
+ ) \\\\
+ .values_list()
+"
+ (let ((first 2)
+ (second (python-tests-look-at ".filter("))
+ (third (python-tests-look-at ".aggregate(")))
+ (should (= first (python-info-beginning-of-backslash 2)))
+ (should (= second (python-info-beginning-of-backslash 3)))
+ (should (= second (python-info-beginning-of-backslash 4)))
+ (should (= second (python-info-beginning-of-backslash 5)))
+ (should (= second (python-info-beginning-of-backslash 6)))
+ (should (= third (python-info-beginning-of-backslash 7)))
+ (should (= third (python-info-beginning-of-backslash 8)))
+ (should (= third (python-info-beginning-of-backslash 9)))
+ (should (not (python-info-beginning-of-backslash 10))))))
+
+(ert-deftest python-info-continuation-line-p-1 ()
+ (python-tests-with-temp-buffer
+ "
+if width == 0 and height == 0 and \\\\
+ color == 'red' and emphasis == 'strong' or \\\\
+ highlight > 100:
+ raise ValueError(
+'sorry, you lose'
+
+)
+"
+ (python-tests-look-at "if width == 0 and height == 0 and")
+ (should (not (python-info-continuation-line-p)))
+ (python-tests-look-at "color == 'red' and emphasis == 'strong' or")
+ (should (python-info-continuation-line-p))
+ (python-tests-look-at "highlight > 100:")
+ (should (python-info-continuation-line-p))
+ (python-tests-look-at "raise ValueError(")
+ (should (not (python-info-continuation-line-p)))
+ (python-tests-look-at "'sorry, you lose'")
+ (should (python-info-continuation-line-p))
+ (forward-line 1)
+ (should (python-info-continuation-line-p))
+ (python-tests-look-at ")")
+ (should (python-info-continuation-line-p))
+ (forward-line 1)
+ (should (not (python-info-continuation-line-p)))))
+
+(ert-deftest python-info-block-continuation-line-p-1 ()
+ (python-tests-with-temp-buffer
+ "
+if width == 0 and height == 0 and \\\\
+ color == 'red' and emphasis == 'strong' or \\\\
+ highlight > 100:
+ raise ValueError(
+'sorry, you lose'
+
+)
+"
+ (python-tests-look-at "if width == 0 and")
+ (should (not (python-info-block-continuation-line-p)))
+ (python-tests-look-at "color == 'red' and emphasis == 'strong' or")
+ (should (= (python-info-block-continuation-line-p)
+ (python-tests-look-at "if width == 0 and" -1 t)))
+ (python-tests-look-at "highlight > 100:")
+ (should (not (python-info-block-continuation-line-p)))))
+
+(ert-deftest python-info-block-continuation-line-p-2 ()
+ (python-tests-with-temp-buffer
+ "
+def foo(a,
+ b,
+ c):
+ pass
+"
+ (python-tests-look-at "def foo(a,")
+ (should (not (python-info-block-continuation-line-p)))
+ (python-tests-look-at "b,")
+ (should (= (python-info-block-continuation-line-p)
+ (python-tests-look-at "def foo(a," -1 t)))
+ (python-tests-look-at "c):")
+ (should (not (python-info-block-continuation-line-p)))))
+
+(ert-deftest python-info-assignment-statement-p-1 ()
+ (python-tests-with-temp-buffer
+ "
+data = foo(), bar() \\\\
+ baz(), 4 \\\\
+ 5, 6
+"
+ (python-tests-look-at "data = foo(), bar()")
+ (should (python-info-assignment-statement-p))
+ (should (python-info-assignment-statement-p t))
+ (python-tests-look-at "baz(), 4")
+ (should (python-info-assignment-statement-p))
+ (should (not (python-info-assignment-statement-p t)))
+ (python-tests-look-at "5, 6")
+ (should (python-info-assignment-statement-p))
+ (should (not (python-info-assignment-statement-p t)))))
+
+(ert-deftest python-info-assignment-statement-p-2 ()
+ (python-tests-with-temp-buffer
+ "
+data = (foo(), bar()
+ baz(), 4
+ 5, 6)
+"
+ (python-tests-look-at "data = (foo(), bar()")
+ (should (python-info-assignment-statement-p))
+ (should (python-info-assignment-statement-p t))
+ (python-tests-look-at "baz(), 4")
+ (should (python-info-assignment-statement-p))
+ (should (not (python-info-assignment-statement-p t)))
+ (python-tests-look-at "5, 6)")
+ (should (python-info-assignment-statement-p))
+ (should (not (python-info-assignment-statement-p t)))))
+
+(ert-deftest python-info-assignment-statement-p-3 ()
+ (python-tests-with-temp-buffer
+ "
+data '=' 42
+"
+ (python-tests-look-at "data '=' 42")
+ (should (not (python-info-assignment-statement-p)))
+ (should (not (python-info-assignment-statement-p t)))))
+
+(ert-deftest python-info-assignment-continuation-line-p-1 ()
+ (python-tests-with-temp-buffer
+ "
+data = foo(), bar() \\\\
+ baz(), 4 \\\\
+ 5, 6
+"
+ (python-tests-look-at "data = foo(), bar()")
+ (should (not (python-info-assignment-continuation-line-p)))
+ (python-tests-look-at "baz(), 4")
+ (should (= (python-info-assignment-continuation-line-p)
+ (python-tests-look-at "foo()," -1 t)))
+ (python-tests-look-at "5, 6")
+ (should (not (python-info-assignment-continuation-line-p)))))
+
+(ert-deftest python-info-assignment-continuation-line-p-2 ()
+ (python-tests-with-temp-buffer
+ "
+data = (foo(), bar()
+ baz(), 4
+ 5, 6)
+"
+ (python-tests-look-at "data = (foo(), bar()")
+ (should (not (python-info-assignment-continuation-line-p)))
+ (python-tests-look-at "baz(), 4")
+ (should (= (python-info-assignment-continuation-line-p)
+ (python-tests-look-at "(foo()," -1 t)))
+ (python-tests-look-at "5, 6)")
+ (should (not (python-info-assignment-continuation-line-p)))))
+
+(ert-deftest python-info-looking-at-beginning-of-defun-1 ()
+ (python-tests-with-temp-buffer
+ "
+def decorat0r(deff):
+ '''decorates stuff.
+
+ @decorat0r
+ def foo(arg):
+ ...
+ '''
+ def wrap():
+ deff()
+ return wwrap
+"
+ (python-tests-look-at "def decorat0r(deff):")
+ (should (python-info-looking-at-beginning-of-defun))
+ (python-tests-look-at "def foo(arg):")
+ (should (not (python-info-looking-at-beginning-of-defun)))
+ (python-tests-look-at "def wrap():")
+ (should (python-info-looking-at-beginning-of-defun))
+ (python-tests-look-at "deff()")
+ (should (not (python-info-looking-at-beginning-of-defun)))))
+
+(ert-deftest python-info-current-line-comment-p-1 ()
+ (python-tests-with-temp-buffer
+ "
+# this is a comment
+foo = True # another comment
+'#this is a string'
+if foo:
+ # more comments
+ print ('bar') # print bar
+"
+ (python-tests-look-at "# this is a comment")
+ (should (python-info-current-line-comment-p))
+ (python-tests-look-at "foo = True # another comment")
+ (should (not (python-info-current-line-comment-p)))
+ (python-tests-look-at "'#this is a string'")
+ (should (not (python-info-current-line-comment-p)))
+ (python-tests-look-at "# more comments")
+ (should (python-info-current-line-comment-p))
+ (python-tests-look-at "print ('bar') # print bar")
+ (should (not (python-info-current-line-comment-p)))))
+
+(ert-deftest python-info-current-line-empty-p ()
+ (python-tests-with-temp-buffer
+ "
+# this is a comment
+
+foo = True # another comment
+"
+ (should (python-info-current-line-empty-p))
+ (python-tests-look-at "# this is a comment")
+ (should (not (python-info-current-line-empty-p)))
+ (forward-line 1)
+ (should (python-info-current-line-empty-p))))
+
+(ert-deftest python-info-docstring-p-1 ()
+ "Test module docstring detection."
+ (python-tests-with-temp-buffer
+ "# -*- coding: utf-8 -*-
+#!/usr/bin/python
+
+'''
+Module Docstring Django style.
+'''
+u'''Additional module docstring.'''
+'''Not a module docstring.'''
+"
+ (python-tests-look-at "Module Docstring Django style.")
+ (should (python-info-docstring-p))
+ (python-tests-look-at "u'''Additional module docstring.'''")
+ (should (python-info-docstring-p))
+ (python-tests-look-at "'''Not a module docstring.'''")
+ (should (not (python-info-docstring-p)))))
+
+(ert-deftest python-info-docstring-p-2 ()
+ "Test variable docstring detection."
+ (python-tests-with-temp-buffer
+ "
+variable = 42
+U'''Variable docstring.'''
+'''Additional variable docstring.'''
+'''Not a variable docstring.'''
+"
+ (python-tests-look-at "Variable docstring.")
+ (should (python-info-docstring-p))
+ (python-tests-look-at "u'''Additional variable docstring.'''")
+ (should (python-info-docstring-p))
+ (python-tests-look-at "'''Not a variable docstring.'''")
+ (should (not (python-info-docstring-p)))))
+
+(ert-deftest python-info-docstring-p-3 ()
+ "Test function docstring detection."
+ (python-tests-with-temp-buffer
+ "
+def func(a, b):
+ r'''
+ Function docstring.
+
+ onetwo style.
+ '''
+ R'''Additional function docstring.'''
+ '''Not a function docstring.'''
+ return a + b
+"
+ (python-tests-look-at "Function docstring.")
+ (should (python-info-docstring-p))
+ (python-tests-look-at "R'''Additional function docstring.'''")
+ (should (python-info-docstring-p))
+ (python-tests-look-at "'''Not a function docstring.'''")
+ (should (not (python-info-docstring-p)))))
+
+(ert-deftest python-info-docstring-p-4 ()
+ "Test class docstring detection."
+ (python-tests-with-temp-buffer
+ "
+class Class:
+ ur'''
+ Class docstring.
+
+ symmetric style.
+ '''
+ uR'''
+ Additional class docstring.
+ '''
+ '''Not a class docstring.'''
+ pass
+"
+ (python-tests-look-at "Class docstring.")
+ (should (python-info-docstring-p))
+ (python-tests-look-at "uR'''") ;; Additional class docstring
+ (should (python-info-docstring-p))
+ (python-tests-look-at "'''Not a class docstring.'''")
+ (should (not (python-info-docstring-p)))))
+
+(ert-deftest python-info-docstring-p-5 ()
+ "Test class attribute docstring detection."
+ (python-tests-with-temp-buffer
+ "
+class Class:
+ attribute = 42
+ Ur'''
+ Class attribute docstring.
+
+ pep-257 style.
+
+ '''
+ UR'''
+ Additional class attribute docstring.
+ '''
+ '''Not a class attribute docstring.'''
+ pass
+"
+ (python-tests-look-at "Class attribute docstring.")
+ (should (python-info-docstring-p))
+ (python-tests-look-at "UR'''") ;; Additional class attr docstring
+ (should (python-info-docstring-p))
+ (python-tests-look-at "'''Not a class attribute docstring.'''")
+ (should (not (python-info-docstring-p)))))
+
+(ert-deftest python-info-docstring-p-6 ()
+ "Test class method docstring detection."
+ (python-tests-with-temp-buffer
+ "
+class Class:
+
+ def __init__(self, a, b):
+ self.a = a
+ self.b = b
+
+ def __call__(self):
+ '''Method docstring.
+
+ pep-257-nn style.
+ '''
+ '''Additional method docstring.'''
+ '''Not a method docstring.'''
+ return self.a + self.b
+"
+ (python-tests-look-at "Method docstring.")
+ (should (python-info-docstring-p))
+ (python-tests-look-at "'''Additional method docstring.'''")
+ (should (python-info-docstring-p))
+ (python-tests-look-at "'''Not a method docstring.'''")
+ (should (not (python-info-docstring-p)))))
+
+(ert-deftest python-info-encoding-from-cookie-1 ()
+ "Should detect it on first line."
+ (python-tests-with-temp-buffer
+ "# coding=latin-1
+
+foo = True # another comment
+"
+ (should (eq (python-info-encoding-from-cookie) 'latin-1))))
+
+(ert-deftest python-info-encoding-from-cookie-2 ()
+ "Should detect it on second line."
+ (python-tests-with-temp-buffer
+ "
+# coding=latin-1
+
+foo = True # another comment
+"
+ (should (eq (python-info-encoding-from-cookie) 'latin-1))))
+
+(ert-deftest python-info-encoding-from-cookie-3 ()
+ "Should not be detected on third line (and following ones)."
+ (python-tests-with-temp-buffer
+ "
+
+# coding=latin-1
+foo = True # another comment
+"
+ (should (not (python-info-encoding-from-cookie)))))
+
+(ert-deftest python-info-encoding-from-cookie-4 ()
+ "Should detect Emacs style."
+ (python-tests-with-temp-buffer
+ "# -*- coding: latin-1 -*-
+
+foo = True # another comment"
+ (should (eq (python-info-encoding-from-cookie) 'latin-1))))
+
+(ert-deftest python-info-encoding-from-cookie-5 ()
+ "Should detect Vim style."
+ (python-tests-with-temp-buffer
+ "# vim: set fileencoding=latin-1 :
+
+foo = True # another comment"
+ (should (eq (python-info-encoding-from-cookie) 'latin-1))))
+
+(ert-deftest python-info-encoding-from-cookie-6 ()
+ "First cookie wins."
+ (python-tests-with-temp-buffer
+ "# -*- coding: iso-8859-1 -*-
+# vim: set fileencoding=latin-1 :
+
+foo = True # another comment"
+ (should (eq (python-info-encoding-from-cookie) 'iso-8859-1))))
+
+(ert-deftest python-info-encoding-from-cookie-7 ()
+ "First cookie wins."
+ (python-tests-with-temp-buffer
+ "# vim: set fileencoding=latin-1 :
+# -*- coding: iso-8859-1 -*-
+
+foo = True # another comment"
+ (should (eq (python-info-encoding-from-cookie) 'latin-1))))
+
+(ert-deftest python-info-encoding-1 ()
+ "Should return the detected encoding from cookie."
+ (python-tests-with-temp-buffer
+ "# vim: set fileencoding=latin-1 :
+
+foo = True # another comment"
+ (should (eq (python-info-encoding) 'latin-1))))
+
+(ert-deftest python-info-encoding-2 ()
+ "Should default to utf-8."
+ (python-tests-with-temp-buffer
+ "# No encoding for you
+
+foo = True # another comment"
+ (should (eq (python-info-encoding) 'utf-8))))
+
+
+;;; Utility functions
+
+(ert-deftest python-util-goto-line-1 ()
+ (python-tests-with-temp-buffer
+ (concat
+ "# a comment
+# another comment
+def foo(a, b, c):
+ pass" (make-string 20 ?\n))
+ (python-util-goto-line 10)
+ (should (= (line-number-at-pos) 10))
+ (python-util-goto-line 20)
+ (should (= (line-number-at-pos) 20))))
+
+(ert-deftest python-util-clone-local-variables-1 ()
+ (let ((buffer (generate-new-buffer
+ "python-util-clone-local-variables-1"))
+ (varcons
+ '((python-fill-docstring-style . django)
+ (python-shell-interpreter . "python")
+ (python-shell-interpreter-args . "manage.py shell")
+ (python-shell-prompt-regexp . "In \\[[0-9]+\\]: ")
+ (python-shell-prompt-output-regexp . "Out\\[[0-9]+\\]: ")
+ (python-shell-extra-pythonpaths "/home/user/pylib/")
+ (python-shell-completion-setup-code
+ . "from IPython.core.completerlib import module_completion")
+ (python-shell-completion-string-code
+ . "';'.join(get_ipython().Completer.all_completions('''%s'''))\n")
+ (python-shell-virtualenv-root
+ . "/home/user/.virtualenvs/project"))))
+ (with-current-buffer buffer
+ (kill-all-local-variables)
+ (dolist (ccons varcons)
+ (set (make-local-variable (car ccons)) (cdr ccons))))
+ (python-tests-with-temp-buffer
+ ""
+ (python-util-clone-local-variables buffer)
+ (dolist (ccons varcons)
+ (should
+ (equal (symbol-value (car ccons)) (cdr ccons)))))
+ (kill-buffer buffer)))
+
+(ert-deftest python-util-strip-string-1 ()
+ (should (string= (python-util-strip-string "\t\r\n str") "str"))
+ (should (string= (python-util-strip-string "str \n\r") "str"))
+ (should (string= (python-util-strip-string "\t\r\n str \n\r ") "str"))
+ (should
+ (string= (python-util-strip-string "\n str \nin \tg \n\r") "str \nin \tg"))
+ (should (string= (python-util-strip-string "\n \t \n\r ") ""))
+ (should (string= (python-util-strip-string "") "")))
+
+(ert-deftest python-util-forward-comment-1 ()
+ (python-tests-with-temp-buffer
+ (concat
+ "# a comment
+# another comment
+ # bad indented comment
+# more comments" (make-string 9999 ?\n))
+ (python-util-forward-comment 1)
+ (should (= (point) (point-max)))
+ (python-util-forward-comment -1)
+ (should (= (point) (point-min)))))
+
+(ert-deftest python-util-valid-regexp-p-1 ()
+ (should (python-util-valid-regexp-p ""))
+ (should (python-util-valid-regexp-p python-shell-prompt-regexp))
+ (should (not (python-util-valid-regexp-p "\\("))))
+
+
+;;; Electricity
+
+(ert-deftest python-parens-electric-indent-1 ()
+ (let ((eim electric-indent-mode))
+ (unwind-protect
+ (progn
+ (python-tests-with-temp-buffer
+ "
+from django.conf.urls import patterns, include, url
+
+from django.contrib import admin
+
+from myapp import views
+
+
+urlpatterns = patterns('',
+ url(r'^$', views.index
+)
+"
+ (electric-indent-mode 1)
+ (python-tests-look-at "views.index")
+ (end-of-line)
+
+ ;; Inserting commas within the same line should leave
+ ;; indentation unchanged.
+ (python-tests-self-insert ",")
+ (should (= (current-indentation) 4))
+
+ ;; As well as any other input happening within the same
+ ;; set of parens.
+ (python-tests-self-insert " name='index')")
+ (should (= (current-indentation) 4))
+
+ ;; But a comma outside it, should trigger indentation.
+ (python-tests-self-insert ",")
+ (should (= (current-indentation) 23))
+
+ ;; Newline indents to the first argument column
+ (python-tests-self-insert "\n")
+ (should (= (current-indentation) 23))
+
+ ;; All this input must not change indentation
+ (indent-line-to 4)
+ (python-tests-self-insert "url(r'^/login$', views.login)")
+ (should (= (current-indentation) 4))
+
+ ;; But this comma does
+ (python-tests-self-insert ",")
+ (should (= (current-indentation) 23))))
+ (or eim (electric-indent-mode -1)))))
+
+(ert-deftest python-triple-quote-pairing ()
+ (let ((epm electric-pair-mode))
+ (unwind-protect
+ (progn
+ (python-tests-with-temp-buffer
+ "\"\"\n"
+ (or epm (electric-pair-mode 1))
+ (goto-char (1- (point-max)))
+ (python-tests-self-insert ?\")
+ (should (string= (buffer-string)
+ "\"\"\"\"\"\"\n"))
+ (should (= (point) 4)))
+ (python-tests-with-temp-buffer
+ "\n"
+ (python-tests-self-insert (list ?\" ?\" ?\"))
+ (should (string= (buffer-string)
+ "\"\"\"\"\"\"\n"))
+ (should (= (point) 4)))
+ (python-tests-with-temp-buffer
+ "\"\n\"\"\n"
+ (goto-char (1- (point-max)))
+ (python-tests-self-insert ?\")
+ (should (= (point) (1- (point-max))))
+ (should (string= (buffer-string)
+ "\"\n\"\"\"\n"))))
+ (or epm (electric-pair-mode -1)))))
+
+
+;;; Hideshow support
+
+(ert-deftest python-hideshow-hide-levels-1 ()
+ "Should hide all methods when called after class start."
+ (let ((enabled hs-minor-mode))
+ (unwind-protect
+ (progn
+ (python-tests-with-temp-buffer
+ "
+class SomeClass:
+
+ def __init__(self, arg, kwarg=1):
+ self.arg = arg
+ self.kwarg = kwarg
+
+ def filter(self, nums):
+ def fn(item):
+ return item in [self.arg, self.kwarg]
+ return filter(fn, nums)
+
+ def __str__(self):
+ return '%s-%s' % (self.arg, self.kwarg)
+"
+ (hs-minor-mode 1)
+ (python-tests-look-at "class SomeClass:")
+ (forward-line)
+ (hs-hide-level 1)
+ (should
+ (string=
+ (python-tests-visible-string)
+ "
+class SomeClass:
+
+ def __init__(self, arg, kwarg=1):
+ def filter(self, nums):
+ def __str__(self):"))))
+ (or enabled (hs-minor-mode -1)))))
+
+(ert-deftest python-hideshow-hide-levels-2 ()
+ "Should hide nested methods and parens at end of defun."
+ (let ((enabled hs-minor-mode))
+ (unwind-protect
+ (progn
+ (python-tests-with-temp-buffer
+ "
+class SomeClass:
+
+ def __init__(self, arg, kwarg=1):
+ self.arg = arg
+ self.kwarg = kwarg
+
+ def filter(self, nums):
+ def fn(item):
+ return item in [self.arg, self.kwarg]
+ return filter(fn, nums)
+
+ def __str__(self):
+ return '%s-%s' % (self.arg, self.kwarg)
+"
+ (hs-minor-mode 1)
+ (python-tests-look-at "def fn(item):")
+ (hs-hide-block)
+ (should
+ (string=
+ (python-tests-visible-string)
+ "
+class SomeClass:
+
+ def __init__(self, arg, kwarg=1):
+ self.arg = arg
+ self.kwarg = kwarg
+
+ def filter(self, nums):
+ def fn(item):
+ return filter(fn, nums)
+
+ def __str__(self):
+ return '%s-%s' % (self.arg, self.kwarg)
+"))))
+ (or enabled (hs-minor-mode -1)))))
+
+
+
+(provide 'python-tests)
+
+;; Local Variables:
+;; indent-tabs-mode: nil
+;; End:
+
+;;; python-tests.el ends here
diff --git a/test/lisp/progmodes/ruby-mode-tests.el b/test/lisp/progmodes/ruby-mode-tests.el
new file mode 100644
index 00000000000..97f277bff41
--- /dev/null
+++ b/test/lisp/progmodes/ruby-mode-tests.el
@@ -0,0 +1,752 @@
+;;; ruby-mode-tests.el --- Test suite for ruby-mode
+
+;; Copyright (C) 2012-2016 Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;; Code:
+
+(require 'ert)
+(require 'ruby-mode)
+
+(defmacro ruby-with-temp-buffer (contents &rest body)
+ (declare (indent 1) (debug t))
+ `(with-temp-buffer
+ (insert ,contents)
+ (ruby-mode)
+ ,@body))
+
+(defun ruby-should-indent (content column)
+ "Assert indentation COLUMN on the last line of CONTENT."
+ (ruby-with-temp-buffer content
+ (indent-according-to-mode)
+ (should (= (current-indentation) column))))
+
+(defun ruby-should-indent-buffer (expected content)
+ "Assert that CONTENT turns into EXPECTED after the buffer is re-indented.
+
+The whitespace before and including \"|\" on each line is removed."
+ (ruby-with-temp-buffer (ruby-test-string content)
+ (indent-region (point-min) (point-max))
+ (should (string= (ruby-test-string expected) (buffer-string)))))
+
+(defun ruby-test-string (s &rest args)
+ (apply 'format (replace-regexp-in-string "^[ \t]*|" "" s) args))
+
+(defun ruby-assert-state (content index value &optional point)
+ "Assert syntax state values at the end of CONTENT.
+
+VALUES-PLIST is a list with alternating index and value elements."
+ (ruby-with-temp-buffer content
+ (when point (goto-char point))
+ (syntax-propertize (point))
+ (should (eq (nth index
+ (parse-partial-sexp (point-min) (point)))
+ value))))
+
+(defun ruby-assert-face (content pos face)
+ (ruby-with-temp-buffer content
+ (font-lock-ensure nil nil)
+ (should (eq face (get-text-property pos 'face)))))
+
+(ert-deftest ruby-indent-after-symbol-made-from-string-interpolation ()
+ "It can indent the line after symbol made using string interpolation."
+ (ruby-should-indent "def foo(suffix)\n :\"bar#{suffix}\"\n"
+ ruby-indent-level))
+
+(ert-deftest ruby-indent-after-js-style-symbol-with-block-beg-name ()
+ "JS-style hash symbol can have keyword name."
+ (ruby-should-indent "link_to \"home\", home_path, class: \"foo\"\n" 0))
+
+(ert-deftest ruby-discern-singleton-class-from-heredoc ()
+ (ruby-assert-state "foo <<asd\n" 3 ?\n)
+ (ruby-assert-state "class <<asd\n" 3 nil))
+
+(ert-deftest ruby-heredoc-font-lock ()
+ (let ((s "foo <<eos.gsub('^ *', '')"))
+ (ruby-assert-face s 9 font-lock-string-face)
+ (ruby-assert-face s 10 nil)))
+
+(ert-deftest ruby-singleton-class-no-heredoc-font-lock ()
+ (ruby-assert-face "class<<a" 8 nil))
+
+(ert-deftest ruby-heredoc-highlights-interpolations ()
+ (ruby-assert-face "s = <<EOS\n #{foo}\nEOS" 15 font-lock-variable-name-face))
+
+(ert-deftest ruby-no-heredoc-inside-quotes ()
+ (ruby-assert-state "\"<<\", \"\",\nfoo" 3 nil))
+
+(ert-deftest ruby-no-heredoc-left-shift ()
+ ;; We can't really detect the left shift operator (like in similar
+ ;; cases, it depends on the type of foo), so we just require for <<
+ ;; to be preceded by a character from a known set.
+ (ruby-assert-state "foo(a<<b)" 3 nil))
+
+(ert-deftest ruby-no-heredoc-class-self ()
+ (ruby-assert-state "class <<self\nend" 3 nil))
+
+(ert-deftest ruby-exit!-font-lock ()
+ (ruby-assert-face "exit!" 5 font-lock-builtin-face))
+
+(ert-deftest ruby-deep-indent ()
+ (let ((ruby-deep-arglist nil)
+ (ruby-deep-indent-paren '(?\( ?\{ ?\[ ?\] t)))
+ (ruby-should-indent "foo = [1,\n2" 7)
+ (ruby-should-indent "foo = {a: b,\nc: d" 7)
+ (ruby-should-indent "foo(a,\nb" 4)))
+
+(ert-deftest ruby-deep-indent-disabled ()
+ (let ((ruby-deep-arglist nil)
+ (ruby-deep-indent-paren nil))
+ (ruby-should-indent "foo = [\n1" ruby-indent-level)
+ (ruby-should-indent "foo = {\na: b" ruby-indent-level)
+ (ruby-should-indent "foo(\na" ruby-indent-level)))
+
+(ert-deftest ruby-indent-after-keyword-in-a-string ()
+ (ruby-should-indent "a = \"abc\nif\"\n " 0)
+ (ruby-should-indent "a = %w[abc\n def]\n " 0)
+ (ruby-should-indent "a = \"abc\n def\"\n " 0))
+
+(ert-deftest ruby-regexp-doesnt-start-in-string ()
+ (ruby-assert-state "'(/', /\d+/" 3 nil))
+
+(ert-deftest ruby-regexp-starts-after-string ()
+ (ruby-assert-state "'(/', /\d+/" 3 ?/ 8))
+
+(ert-deftest ruby-regexp-interpolation-is-highlighted ()
+ (ruby-assert-face "/#{foobs}/" 4 font-lock-variable-name-face))
+
+(ert-deftest ruby-regexp-skips-over-interpolation ()
+ (ruby-assert-state "/#{foobs.join('/')}/" 3 nil))
+
+(ert-deftest ruby-regexp-continues-till-end-when-unclosed ()
+ (ruby-assert-state "/bars" 3 ?/))
+
+(ert-deftest ruby-regexp-can-be-multiline ()
+ (ruby-assert-state "/bars\ntees # toots \nfoos/" 3 nil))
+
+(ert-deftest ruby-slash-symbol-is-not-mistaken-for-regexp ()
+ (ruby-assert-state ":/" 3 nil))
+
+(ert-deftest ruby-slash-char-literal-is-not-mistaken-for-regexp ()
+ (ruby-assert-state "?/" 3 nil))
+
+(ert-deftest ruby-regexp-is-not-mistaken-for-slash-symbol ()
+ (ruby-assert-state "x = /foo:/" 3 nil))
+
+(ert-deftest ruby-indent-simple ()
+ (ruby-should-indent-buffer
+ "if foo
+ | bar
+ |end
+ |zot
+ |"
+ "if foo
+ |bar
+ | end
+ | zot
+ |"))
+
+(ert-deftest ruby-indent-keyword-label ()
+ (ruby-should-indent-buffer
+ "bar(class: XXX) do
+ | foo
+ |end
+ |bar
+ |"
+ "bar(class: XXX) do
+ | foo
+ | end
+ | bar
+ |"))
+
+(ert-deftest ruby-indent-method-with-question-mark ()
+ (ruby-should-indent-buffer
+ "if x.is_a?(XXX)
+ | foo
+ |end
+ |"
+ "if x.is_a?(XXX)
+ | foo
+ | end
+ |"))
+
+(ert-deftest ruby-indent-expr-in-regexp ()
+ (ruby-should-indent-buffer
+ "if /#{foo}/ =~ s
+ | x = 1
+ |end
+ |"
+ "if /#{foo}/ =~ s
+ | x = 1
+ | end
+ |"))
+
+(ert-deftest ruby-indent-singleton-class ()
+ (ruby-should-indent-buffer
+ "class<<bar
+ | foo
+ |end
+ |"
+ "class<<bar
+ |foo
+ | end
+ |"))
+
+(ert-deftest ruby-indent-inside-heredoc-after-operator ()
+ (ruby-should-indent-buffer
+ "b=<<eos
+ | 42"
+ "b=<<eos
+ | 42"))
+
+(ert-deftest ruby-indent-inside-heredoc-after-space ()
+ (ruby-should-indent-buffer
+ "foo <<eos.gsub(' ', '*')
+ | 42"
+ "foo <<eos.gsub(' ', '*')
+ | 42"))
+
+(ert-deftest ruby-indent-array-literal ()
+ (let ((ruby-deep-indent-paren nil))
+ (ruby-should-indent-buffer
+ "foo = [
+ | bar
+ |]
+ |"
+ "foo = [
+ | bar
+ | ]
+ |"))
+ (ruby-should-indent-buffer
+ "foo do
+ | [bar]
+ |end
+ |"
+ "foo do
+ |[bar]
+ | end
+ |"))
+
+(ert-deftest ruby-indent-begin-end ()
+ (ruby-should-indent-buffer
+ "begin
+ | a[b]
+ |end
+ |"
+ "begin
+ | a[b]
+ | end
+ |"))
+
+(ert-deftest ruby-indent-array-after-paren-and-space ()
+ (ruby-should-indent-buffer
+ "class A
+ | def foo
+ | foo( [])
+ | end
+ |end
+ |"
+ "class A
+ | def foo
+ |foo( [])
+ |end
+ | end
+ |"))
+
+(ert-deftest ruby-indent-after-block-in-continued-expression ()
+ (ruby-should-indent-buffer
+ "var =
+ | begin
+ | val
+ | end
+ |statement"
+ "var =
+ |begin
+ |val
+ |end
+ |statement"))
+
+(ert-deftest ruby-indent-spread-args-in-parens ()
+ (let ((ruby-deep-indent-paren '(?\()))
+ (ruby-should-indent-buffer
+ "foo(1,
+ | 2,
+ | 3)
+ |"
+ "foo(1,
+ | 2,
+ | 3)
+ |")))
+
+(ert-deftest ruby-align-to-stmt-keywords-t ()
+ (let ((ruby-align-to-stmt-keywords t))
+ (ruby-should-indent-buffer
+ "foo = if bar?
+ | 1
+ |else
+ | 2
+ |end
+ |
+ |foo || begin
+ | bar
+ |end
+ |
+ |foo ||
+ | begin
+ | bar
+ | end
+ |"
+ "foo = if bar?
+ | 1
+ |else
+ | 2
+ | end
+ |
+ | foo || begin
+ | bar
+ |end
+ |
+ | foo ||
+ | begin
+ |bar
+ | end
+ |")
+ ))
+
+(ert-deftest ruby-align-to-stmt-keywords-case ()
+ (let ((ruby-align-to-stmt-keywords '(case)))
+ (ruby-should-indent-buffer
+ "b = case a
+ |when 13
+ | 6
+ |else
+ | 42
+ |end"
+ "b = case a
+ | when 13
+ | 6
+ | else
+ | 42
+ | end")))
+
+(ert-deftest ruby-align-chained-calls ()
+ (let ((ruby-align-chained-calls t))
+ (ruby-should-indent-buffer
+ "one.two.three
+ | .four
+ |
+ |my_array.select { |str| str.size > 5 }
+ | .map { |str| str.downcase }"
+ "one.two.three
+ | .four
+ |
+ |my_array.select { |str| str.size > 5 }
+ | .map { |str| str.downcase }")))
+
+(ert-deftest ruby-move-to-block-stops-at-indentation ()
+ (ruby-with-temp-buffer "def f\nend"
+ (beginning-of-line)
+ (ruby-move-to-block -1)
+ (should (looking-at "^def"))))
+
+(ert-deftest ruby-toggle-block-to-do-end ()
+ (ruby-with-temp-buffer "foo {|b|\n}"
+ (beginning-of-line)
+ (ruby-toggle-block)
+ (should (string= "foo do |b|\nend" (buffer-string)))))
+
+(ert-deftest ruby-toggle-block-to-brace ()
+ (let ((pairs '((17 . "foo { |b| b + 2 }")
+ (16 . "foo { |b|\n b + 2\n}"))))
+ (dolist (pair pairs)
+ (with-temp-buffer
+ (let ((fill-column (car pair)))
+ (insert "foo do |b|\n b + 2\nend")
+ (ruby-mode)
+ (beginning-of-line)
+ (ruby-toggle-block)
+ (should (string= (cdr pair) (buffer-string))))))))
+
+(ert-deftest ruby-toggle-block-to-multiline ()
+ (ruby-with-temp-buffer "foo {|b| b + 1}"
+ (beginning-of-line)
+ (ruby-toggle-block)
+ (should (string= "foo do |b|\n b + 1\nend" (buffer-string)))))
+
+(ert-deftest ruby-toggle-block-with-interpolation ()
+ (ruby-with-temp-buffer "foo do\n \"#{bar}\"\nend"
+ (beginning-of-line)
+ (ruby-toggle-block)
+ (should (string= "foo { \"#{bar}\" }" (buffer-string)))))
+
+(ert-deftest ruby-recognize-symbols-starting-with-at-character ()
+ (ruby-assert-face ":@abc" 3 font-lock-constant-face))
+
+(ert-deftest ruby-hash-character-not-interpolation ()
+ (ruby-assert-face "\"This is #{interpolation}\"" 15
+ font-lock-variable-name-face)
+ (ruby-assert-face "\"This is \\#{no interpolation} despite the #\""
+ 15 font-lock-string-face)
+ (ruby-assert-face "\n#@comment, not ruby code" 5 font-lock-comment-face)
+ (ruby-assert-state "\n#@comment, not ruby code" 4 t)
+ (ruby-assert-face "# A comment cannot have #{an interpolation} in it"
+ 30 font-lock-comment-face)
+ (ruby-assert-face "# #{comment}\n \"#{interpolation}\"" 16
+ font-lock-variable-name-face))
+
+(ert-deftest ruby-interpolation-suppresses-quotes-inside ()
+ (let ((s "\"<ul><li>#{@files.join(\"</li><li>\")}</li></ul>\""))
+ (ruby-assert-state s 8 nil)
+ (ruby-assert-face s 9 font-lock-string-face)
+ (ruby-assert-face s 10 font-lock-variable-name-face)
+ (ruby-assert-face s 41 font-lock-string-face)))
+
+(ert-deftest ruby-interpolation-suppresses-one-double-quote ()
+ (let ((s "\"foo#{'\"'}\""))
+ (ruby-assert-state s 8 nil)
+ (ruby-assert-face s 8 font-lock-variable-name-face)
+ (ruby-assert-face s 11 font-lock-string-face)))
+
+(ert-deftest ruby-interpolation-suppresses-one-backtick ()
+ (let ((s "`as#{'`'}das`"))
+ (ruby-assert-state s 8 nil)))
+
+(ert-deftest ruby-interpolation-keeps-non-quote-syntax ()
+ (let ((s "\"foo#{baz.tee}bar\""))
+ (ruby-with-temp-buffer s
+ (goto-char (point-min))
+ (ruby-mode)
+ (syntax-propertize (point-max))
+ (search-forward "tee")
+ (should (string= (thing-at-point 'symbol) "tee")))))
+
+(ert-deftest ruby-interpolation-inside-percent-literal ()
+ (let ((s "%( #{boo} )"))
+ (ruby-assert-face s 1 font-lock-string-face)
+ (ruby-assert-face s 4 font-lock-variable-name-face)
+ (ruby-assert-face s 10 font-lock-string-face)
+ (ruby-assert-state s 8 nil)))
+
+(ert-deftest ruby-interpolation-inside-percent-literal-with-paren ()
+ :expected-result :failed
+ (let ((s "%(^#{\")\"}^)"))
+ (ruby-assert-face s 3 font-lock-string-face)
+ (ruby-assert-face s 4 font-lock-variable-name-face)
+ (ruby-assert-face s 10 font-lock-string-face)
+ ;; It's confused by the closing paren in the middle.
+ (ruby-assert-state s 8 nil)))
+
+(ert-deftest ruby-interpolation-inside-another-interpolation ()
+ :expected-result :failed
+ (let ((s "\"#{[a, b, c].map { |v| \"#{v}\" }.join}\""))
+ (ruby-assert-face s 1 font-lock-string-face)
+ (ruby-assert-face s 2 font-lock-variable-name-face)
+ (ruby-assert-face s 38 font-lock-string-face)
+ (ruby-assert-state s 8 nil)))
+
+(ert-deftest ruby-interpolation-inside-double-quoted-percent-literals ()
+ (ruby-assert-face "%Q{foo #@bar}" 8 font-lock-variable-name-face)
+ (ruby-assert-face "%W{foo #@bar}" 8 font-lock-variable-name-face)
+ (ruby-assert-face "%r{foo #@bar}" 8 font-lock-variable-name-face)
+ (ruby-assert-face "%x{foo #@bar}" 8 font-lock-variable-name-face))
+
+(ert-deftest ruby-no-interpolation-in-single-quoted-literals ()
+ (ruby-assert-face "'foo #@bar'" 7 font-lock-string-face)
+ (ruby-assert-face "%q{foo #@bar}" 8 font-lock-string-face)
+ (ruby-assert-face "%w{foo #@bar}" 8 font-lock-string-face)
+ (ruby-assert-face "%s{foo #@bar}" 8 font-lock-string-face))
+
+(ert-deftest ruby-interpolation-after-dollar-sign ()
+ (ruby-assert-face "\"$#{balance}\"" 2 'font-lock-string-face)
+ (ruby-assert-face "\"$#{balance}\"" 3 'font-lock-variable-name-face))
+
+(ert-deftest ruby-no-unknown-percent-literals ()
+ ;; No folding of case.
+ (ruby-assert-face "%S{foo}" 4 nil)
+ (ruby-assert-face "%R{foo}" 4 nil))
+
+(ert-deftest ruby-no-nested-percent-literals ()
+ (ruby-with-temp-buffer "a = %w[b %()]"
+ (syntax-propertize (point))
+ (should (null (nth 8 (syntax-ppss))))
+ (should (eq t (nth 3 (syntax-ppss (1- (point-max))))))
+ (search-backward "[")
+ (should (eq t (nth 3 (syntax-ppss))))))
+
+(ert-deftest ruby-add-log-current-method-examples ()
+ (let ((pairs '(("foo" . "#foo")
+ ("C.foo" . ".foo")
+ ("self.foo" . ".foo"))))
+ (dolist (pair pairs)
+ (let ((name (car pair))
+ (value (cdr pair)))
+ (ruby-with-temp-buffer (ruby-test-string
+ "module M
+ | class C
+ | def %s
+ | _
+ | end
+ | end
+ |end"
+ name)
+ (search-backward "_")
+ (forward-line)
+ (should (string= (ruby-add-log-current-method)
+ (format "M::C%s" value))))))))
+
+(ert-deftest ruby-add-log-current-method-outside-of-method ()
+ (ruby-with-temp-buffer (ruby-test-string
+ "module M
+ | class C
+ | def foo
+ | end
+ | _
+ | end
+ |end")
+ (search-backward "_")
+ (should (string= (ruby-add-log-current-method)"M::C"))))
+
+(ert-deftest ruby-add-log-current-method-in-singleton-class ()
+ (ruby-with-temp-buffer (ruby-test-string
+ "class C
+ | class << self
+ | def foo
+ | _
+ | end
+ | end
+ |end")
+ (search-backward "_")
+ (should (string= (ruby-add-log-current-method) "C.foo"))))
+
+(ert-deftest ruby-add-log-current-method-namespace-shorthand ()
+ (ruby-with-temp-buffer (ruby-test-string
+ "class C::D
+ | def foo
+ | _
+ | end
+ |end")
+ (search-backward "_")
+ (should (string= (ruby-add-log-current-method) "C::D#foo"))))
+
+(ert-deftest ruby-add-log-current-method-after-inner-class ()
+ (ruby-with-temp-buffer (ruby-test-string
+ "module M
+ | class C
+ | class D
+ | end
+ | def foo
+ | _
+ | end
+ | end
+ |end")
+ (search-backward "_")
+ (should (string= (ruby-add-log-current-method) "M::C#foo"))))
+
+(defvar ruby-block-test-example
+ (ruby-test-string
+ "class C
+ | def foo
+ | 1
+ | end
+ |
+ | def bar
+ | 2
+ | end
+ |
+ | def baz
+ |some do
+ |3
+ | end
+ | end
+ |end"))
+
+(defmacro ruby-deftest-move-to-block (name &rest body)
+ (declare (indent defun))
+ `(ert-deftest ,(intern (format "ruby-move-to-block-%s" name)) ()
+ (with-temp-buffer
+ (insert ruby-block-test-example)
+ (ruby-mode)
+ (goto-char (point-min))
+ ,@body)))
+
+(ruby-deftest-move-to-block works-on-do
+ (forward-line 10)
+ (ruby-end-of-block)
+ (should (= 13 (line-number-at-pos)))
+ (ruby-beginning-of-block)
+ (should (= 11 (line-number-at-pos))))
+
+(ruby-deftest-move-to-block zero-is-noop
+ (forward-line 4)
+ (ruby-move-to-block 0)
+ (should (= 5 (line-number-at-pos))))
+
+(ruby-deftest-move-to-block ok-with-three
+ (forward-line 1)
+ (ruby-move-to-block 3)
+ (should (= 14 (line-number-at-pos))))
+
+(ruby-deftest-move-to-block ok-with-minus-two
+ (forward-line 9)
+ (ruby-move-to-block -2)
+ (should (= 2 (line-number-at-pos))))
+
+(ert-deftest ruby-move-to-block-skips-percent-literal ()
+ (dolist (s (list (ruby-test-string
+ "foo do
+ | a = %%w(
+ | def yaa
+ | )
+ |end")
+ (ruby-test-string
+ "foo do
+ | a = %%w|
+ | end
+ | |
+ |end")))
+ (ruby-with-temp-buffer s
+ (goto-char (point-min))
+ (ruby-end-of-block)
+ (should (= 5 (line-number-at-pos)))
+ (ruby-beginning-of-block)
+ (should (= 1 (line-number-at-pos))))))
+
+(ert-deftest ruby-move-to-block-skips-heredoc ()
+ (ruby-with-temp-buffer
+ (ruby-test-string
+ "if something_wrong?
+ | ActiveSupport::Deprecation.warn(<<-eowarn)
+ | boo hoo
+ | end
+ | eowarn
+ |end")
+ (goto-char (point-min))
+ (ruby-end-of-block)
+ (should (= 6 (line-number-at-pos)))
+ (ruby-beginning-of-block)
+ (should (= 1 (line-number-at-pos)))))
+
+(ert-deftest ruby-move-to-block-does-not-fold-case ()
+ (ruby-with-temp-buffer
+ (ruby-test-string
+ "foo do
+ | Module.to_s
+ |end")
+ (let ((case-fold-search t))
+ (ruby-beginning-of-block))
+ (should (= 1 (line-number-at-pos)))))
+
+(ert-deftest ruby-move-to-block-moves-from-else-to-if ()
+ (ruby-with-temp-buffer (ruby-test-string
+ "if true
+ | nested_block do
+ | end
+ |else
+ |end")
+ (goto-char (point-min))
+ (forward-line 3)
+ (ruby-beginning-of-block)
+ (should (= 1 (line-number-at-pos)))))
+
+(ert-deftest ruby-beginning-of-defun-does-not-fold-case ()
+ (ruby-with-temp-buffer
+ (ruby-test-string
+ "class C
+ | def bar
+ | Class.to_s
+ | end
+ |end")
+ (goto-char (point-min))
+ (forward-line 3)
+ (let ((case-fold-search t))
+ (beginning-of-defun))
+ (should (= 2 (line-number-at-pos)))))
+
+(ert-deftest ruby-end-of-defun-skips-to-next-line-after-the-method ()
+ (ruby-with-temp-buffer
+ (ruby-test-string
+ "class D
+ | def tee
+ | 'ho hum'
+ | end
+ |end")
+ (goto-char (point-min))
+ (forward-line 1)
+ (end-of-defun)
+ (should (= 5 (line-number-at-pos)))))
+
+(defvar ruby-sexp-test-example
+ (ruby-test-string
+ "class C
+ | def foo
+ | self.end
+ | D.new.class
+ | [1, 2, 3].map do |i|
+ | i + 1
+ | end.sum
+ | end
+ |end"))
+
+(ert-deftest ruby-forward-sexp-skips-method-calls-with-keyword-names ()
+ (ruby-with-temp-buffer ruby-sexp-test-example
+ (goto-line 2)
+ (ruby-forward-sexp)
+ (should (= 8 (line-number-at-pos)))))
+
+(ert-deftest ruby-backward-sexp-skips-method-calls-with-keyword-names ()
+ (ruby-with-temp-buffer ruby-sexp-test-example
+ (goto-line 8)
+ (end-of-line)
+ (ruby-backward-sexp)
+ (should (= 2 (line-number-at-pos)))))
+
+(ert-deftest ruby-toggle-string-quotes-quotes-correctly ()
+ (let ((pairs
+ '(("puts '\"foo\"\\''" . "puts \"\\\"foo\\\"'\"")
+ ("puts \"'foo'\\\"\"" . "puts '\\'foo\\'\"'"))))
+ (dolist (pair pairs)
+ (ruby-with-temp-buffer (car pair)
+ (beginning-of-line)
+ (search-forward "foo")
+ (ruby-toggle-string-quotes)
+ (should (string= (buffer-string) (cdr pair)))))))
+
+(ert-deftest ruby--insert-coding-comment-ruby-style ()
+ (with-temp-buffer
+ (let ((ruby-encoding-magic-comment-style 'ruby))
+ (ruby--insert-coding-comment "utf-8")
+ (should (string= "# coding: utf-8\n" (buffer-string))))))
+
+(ert-deftest ruby--insert-coding-comment-emacs-style ()
+ (with-temp-buffer
+ (let ((ruby-encoding-magic-comment-style 'emacs))
+ (ruby--insert-coding-comment "utf-8")
+ (should (string= "# -*- coding: utf-8 -*-\n" (buffer-string))))))
+
+(ert-deftest ruby--insert-coding-comment-custom-style ()
+ (with-temp-buffer
+ (let ((ruby-encoding-magic-comment-style 'custom)
+ (ruby-custom-encoding-magic-comment-template "# encoding: %s\n"))
+ (ruby--insert-coding-comment "utf-8")
+ (should (string= "# encoding: utf-8\n\n" (buffer-string))))))
+
+
+(provide 'ruby-mode-tests)
+
+;;; ruby-mode-tests.el ends here
diff --git a/test/lisp/progmodes/sql-tests.el b/test/lisp/progmodes/sql-tests.el
new file mode 100644
index 00000000000..e05247a60ed
--- /dev/null
+++ b/test/lisp/progmodes/sql-tests.el
@@ -0,0 +1,47 @@
+;;; sql-tests.el --- Tests for sql.el -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2016 Free Software Foundation, Inc.
+
+;; Author: Simen Heggestøyl <simenheg@gmail.com>
+;; Keywords:
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;
+
+;;; Code:
+
+(require 'cl-lib)
+(require 'ert)
+(require 'sql)
+
+(ert-deftest sql-tests-postgres-list-databases ()
+ "Test that output from `psql -ltX' is parsed correctly."
+ (cl-letf
+ (((symbol-function 'executable-find)
+ (lambda (_command) t))
+ ((symbol-function 'process-lines)
+ (lambda (_program &rest _args)
+ '(" db-name-1 | foo-user | UTF8 | en_US.UTF-8 | en_US.UTF-8 | "
+ " db_name_2 | foo-user | UTF8 | en_US.UTF-8 | en_US.UTF-8 | "
+ ""))))
+ (should (equal (sql-postgres-list-databases)
+ '("db-name-1" "db_name_2")))))
+
+(provide 'sql-tests)
+;;; sql-tests.el ends here
diff --git a/test/lisp/progmodes/subword-tests.el b/test/lisp/progmodes/subword-tests.el
new file mode 100644
index 00000000000..5a562765bb1
--- /dev/null
+++ b/test/lisp/progmodes/subword-tests.el
@@ -0,0 +1,81 @@
+;;; subword-tests.el --- Testing the subword rules
+
+;; Copyright (C) 2011-2016 Free Software Foundation, Inc.
+
+;; Author: Stefan Monnier <monnier@iro.umontreal.ca>
+;; Keywords:
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;
+
+;;; Code:
+
+(require 'ert)
+(require 'subword)
+
+(defconst subword-tests-strings
+ '("ABC^" ;;Bug#13758
+ "ABC^ ABC^Foo^ ABC^-Foo^ toto^ ABC^"))
+
+(ert-deftest subword-tests ()
+ "Test the `subword-mode' rules."
+ (with-temp-buffer
+ (dolist (str subword-tests-strings)
+ (erase-buffer)
+ (insert str)
+ (goto-char (point-min))
+ (while (search-forward "^" nil t)
+ (replace-match ""))
+ (goto-char (point-min))
+ (while (not (eobp))
+ (subword-forward 1)
+ (insert "^"))
+ (should (equal (buffer-string) str)))))
+
+(ert-deftest subword-tests2 ()
+ "Test that motion in subword-mode stops at the right places."
+
+ (let* ((line "fooBarBAZ quXD g_TESTThingAbc word BLAH test")
+ (fwrd "* * * * * * * * * * * * *")
+ (bkwd "* * * * * * * * * * * * *"))
+
+ (with-temp-buffer
+ (subword-mode 1)
+ (insert line)
+
+ ;; Test forward motion.
+
+ (goto-char (point-min))
+ (let ((stops (make-string (length fwrd) ?\ )))
+ (while (progn
+ (aset stops (1- (point)) ?\*)
+ (not (eobp)))
+ (forward-word))
+ (should (equal stops fwrd)))
+
+ ;; Test backward motion.
+
+ (goto-char (point-max))
+ (let ((stops (make-string (length bkwd) ?\ )))
+ (while (progn
+ (aset stops (1- (point)) ?\*)
+ (not (bobp)))
+ (backward-word))
+ (should (equal stops bkwd))))))
+
+(provide 'subword-tests)
+;;; subword-tests.el ends here
diff --git a/test/lisp/progmodes/xref-tests.el b/test/lisp/progmodes/xref-tests.el
new file mode 100644
index 00000000000..2b745816c62
--- /dev/null
+++ b/test/lisp/progmodes/xref-tests.el
@@ -0,0 +1,91 @@
+;;; xref-tests.el --- tests for xref
+
+;; Copyright (C) 2016 Free Software Foundation, Inc.
+
+;; Author: Dmitry Gutov <dgutov@yandex.ru>
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;; Code:
+
+(require 'xref)
+(require 'cl-lib)
+
+(defvar xref-tests-data-dir
+ (expand-file-name "data/xref/"
+ (getenv "EMACS_TEST_DIRECTORY")))
+
+(ert-deftest xref-collect-matches-finds-none-for-some-regexp ()
+ (should (null (xref-collect-matches "zzz" "*" xref-tests-data-dir nil))))
+
+(ert-deftest xref-collect-matches-finds-some-for-bar ()
+ (let* ((matches (xref-collect-matches "bar" "*" xref-tests-data-dir nil))
+ (locs (cl-sort (mapcar #'xref-item-location matches)
+ #'string<
+ :key #'xref-location-group)))
+ (should (= 2 (length matches)))
+ (should (string-match-p "file1\\.txt\\'" (xref-location-group (nth 0 locs))))
+ (should (string-match-p "file2\\.txt\\'" (xref-location-group (nth 1 locs))))))
+
+(ert-deftest xref-collect-matches-finds-two-matches-on-the-same-line ()
+ (let* ((matches (xref-collect-matches "foo" "*" xref-tests-data-dir nil))
+ (locs (mapcar #'xref-item-location matches)))
+ (should (= 2 (length matches)))
+ (should (string-match-p "file1\\.txt\\'" (xref-location-group (nth 0 locs))))
+ (should (string-match-p "file1\\.txt\\'" (xref-location-group (nth 1 locs))))
+ (should (equal 1 (xref-location-line (nth 0 locs))))
+ (should (equal 1 (xref-location-line (nth 1 locs))))
+ (should (equal 0 (xref-file-location-column (nth 0 locs))))
+ (should (equal 4 (xref-file-location-column (nth 1 locs))))))
+
+(ert-deftest xref-collect-matches-finds-an-empty-line-regexp-match ()
+ (let* ((matches (xref-collect-matches "^$" "*" xref-tests-data-dir nil))
+ (locs (mapcar #'xref-item-location matches)))
+ (should (= 1 (length matches)))
+ (should (string-match-p "file2\\.txt\\'" (xref-location-group (nth 0 locs))))
+ (should (equal 1 (xref-location-line (nth 0 locs))))
+ (should (equal 0 (xref-file-location-column (nth 0 locs))))))
+
+(ert-deftest xref--buf-pairs-iterator-groups-markers-by-buffers-1 ()
+ (let* ((xrefs (xref-collect-matches "foo" "*" xref-tests-data-dir nil))
+ (iter (xref--buf-pairs-iterator xrefs))
+ (cons (funcall iter :next)))
+ (should (null (funcall iter :next)))
+ (should (string-match "file1\\.txt\\'" (buffer-file-name (car cons))))
+ (should (= 2 (length (cdr cons))))))
+
+(ert-deftest xref--buf-pairs-iterator-groups-markers-by-buffers-2 ()
+ (let* ((xrefs (xref-collect-matches "bar" "*" xref-tests-data-dir nil))
+ (iter (xref--buf-pairs-iterator xrefs))
+ (cons1 (funcall iter :next))
+ (cons2 (funcall iter :next)))
+ (should (null (funcall iter :next)))
+ (should-not (equal (car cons1) (car cons2)))
+ (should (= 1 (length (cdr cons1))))
+ (should (= 1 (length (cdr cons2))))))
+
+(ert-deftest xref--buf-pairs-iterator-cleans-up-markers ()
+ (let* ((xrefs (xref-collect-matches "bar" "*" xref-tests-data-dir nil))
+ (iter (xref--buf-pairs-iterator xrefs))
+ (cons1 (funcall iter :next))
+ (cons2 (funcall iter :next)))
+ (funcall iter :cleanup)
+ (should (null (marker-position (car (nth 0 (cdr cons1))))))
+ (should (null (marker-position (cdr (nth 0 (cdr cons1))))))
+ (should (null (marker-position (car (nth 0 (cdr cons2))))))
+ (should (null (marker-position (cdr (nth 0 (cdr cons2))))))))