]> code.delx.au - gnu-emacs/blobdiff - test/automated/python-tests.el
Merge from gnulib.
[gnu-emacs] / test / automated / python-tests.el
index b38fbdd122f0ccfdd1b7c333abe9eb32c22cf787..dc58138ced418672ad8274f4b47185786229e6d1 100644 (file)
@@ -1,6 +1,6 @@
 ;;; python-tests.el --- Test suite for python.el
 
-;; Copyright (C) 2013 Free Software Foundation, Inc.
+;; Copyright (C) 2013-2014 Free Software Foundation, Inc.
 
 ;; This file is part of GNU Emacs.
 
@@ -21,6 +21,7 @@
 
 ;;; Code:
 
+(require 'ert)
 (require 'python)
 
 (defmacro python-tests-with-temp-buffer (contents &rest body)
@@ -34,6 +35,23 @@ always located at the beginning of buffer."
      (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)))
+     (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
@@ -181,6 +199,83 @@ foo = long_function_name(
    (should (eq (car (python-indent-context)) 'inside-paren))
    (should (= (python-indent-calculate-indentation) 4))))
 
+(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-line))
+   (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 just because.
+    # But we won't mess with the user in this line.
+
+now_we_do_mess_cause_this_is_not_a_comment = 1
+
+# yeah, that.
+"
+   (python-tests-look-at "# I don't do much")
+   (should (eq (car (python-indent-context)) 'after-beginning-of-block))
+   (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-beginning-of-block))
+   (should (= (python-indent-calculate-indentation) 4))
+   (python-tests-look-at "# This comment is badly")
+   (should (eq (car (python-indent-context)) 'after-line))
+   ;; The return keyword moves indentation backwards 4 spaces, but
+   ;; let's assume this comment was placed there because the user
+   ;; wanted to (manually adding spaces or whatever).
+   (should (= (python-indent-calculate-indentation) 0))
+   (python-tests-look-at "# but we won't mess")
+   (should (eq (car (python-indent-context)) 'after-comment))
+   (should (= (python-indent-calculate-indentation) 4))
+   ;; 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 "now_we_do_mess")
+   ;; Here is where comment indentation starts to get ignored and
+   ;; where the user can't freely indent anymore.
+   (should (eq (car (python-indent-context)) 'after-line))
+   (should (= (python-indent-calculate-indentation) 0))
+   (python-tests-look-at "# yeah, that.")
+   (should (eq (car (python-indent-context)) 'after-line))
+   (should (= (python-indent-calculate-indentation) 0))))
+
 (ert-deftest python-indent-inside-paren-1 ()
   "The most simple inside-paren case that shouldn't fail."
   (python-tests-with-temp-buffer
@@ -363,6 +458,28 @@ def foo(a, b, c):
    (should (eq (car (python-indent-context)) 'after-beginning-of-block))
    (should (= (python-indent-calculate-indentation) 12))))
 
+(ert-deftest python-indent-dedenters-2 ()
+  "Check one-liner block special case.."
+  (python-tests-with-temp-buffer
+   "
+cond = True
+if cond:
+
+    if cond: print 'True'
+else: print 'False'
+
+else:
+    return
+"
+   (python-tests-look-at "else: print 'False'")
+   ;; When a block has code after ":" it's just considered a simple
+   ;; line as that's a common thing to happen in one-liners.
+   (should (eq (car (python-indent-context)) 'after-line))
+   (should (= (python-indent-calculate-indentation) 4))
+   (python-tests-look-at "else:")
+   (should (eq (car (python-indent-context)) 'after-line))
+   (should (= (python-indent-calculate-indentation) 0))))
+
 (ert-deftest python-indent-after-backslash-1 ()
   "The most common case."
   (python-tests-with-temp-buffer
@@ -429,6 +546,49 @@ objects = Thing.objects.all() \\\\
    (should (eq (car (python-indent-context)) 'after-line))
    (should (= (python-indent-calculate-indentation) 0))))
 
+(ert-deftest python-indent-block-enders-1 ()
+  "Test `python-indent-block-enders' value honoring."
+  (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 (= (python-indent-calculate-indentation) 8))))
+
+(ert-deftest python-indent-block-enders-2 ()
+  "Test `python-indent-block-enders' value honoring."
+  (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))))
+
 \f
 ;;; Navigation
 
@@ -637,6 +797,201 @@ def decoratorFunctionWithArguments(arg1, arg2, arg3):
                 (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
@@ -984,28 +1339,6 @@ if request.user.is_authenticated():
               (python-tests-look-at
                "if request.user.is_authenticated():" -1)))))
 
-(ert-deftest python-nav-lisp-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-lisp-forward-sexp-safe 4)
-   (should (looking-at "(user=request.user)"))
-   (python-tests-look-at "user=request.user")
-   (python-nav-lisp-forward-sexp-safe -1)
-   (should (looking-at "(user=request.user)"))
-   (python-nav-lisp-forward-sexp-safe -4)
-   (should (looking-at "profile ="))
-   (python-tests-look-at "user=request.user")
-   (python-nav-lisp-forward-sexp-safe 3)
-   (should (looking-at ")"))
-   (python-nav-lisp-forward-sexp-safe 1)
-   (should (looking-at "$"))
-   (python-nav-lisp-forward-sexp-safe 1)
-   (should (looking-at ".notify()"))))
-
 (ert-deftest python-nav-forward-sexp-1 ()
   (python-tests-with-temp-buffer
    "
@@ -1122,6 +1455,29 @@ def another_statement():
    (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
    "
@@ -1161,6 +1517,256 @@ def f():
 \f
 ;;; Shell integration
 
+(defvar python-tests-shell-interpreter "python")
+
+(ert-deftest python-shell-get-process-name-1 ()
+  "Check process name calculation on different scenarios."
+  (python-tests-with-temp-buffer
+      ""
+    (should (string= (python-shell-get-process-name nil)
+                     python-shell-buffer-name))
+    ;; When the `current-buffer' doesn't have `buffer-file-name', even
+    ;; if dedicated flag is non-nil should not include its name.
+    (should (string= (python-shell-get-process-name t)
+                     python-shell-buffer-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-file-name)))))
+
+(ert-deftest python-shell-internal-get-process-name-1 ()
+  "Check the internal process name is config-unique."
+  (let* ((python-shell-interpreter python-tests-shell-interpreter)
+         (python-shell-interpreter-args "")
+         (python-shell-prompt-regexp ">>> ")
+         (python-shell-prompt-block-regexp "[.][.][.] ")
+         (python-shell-setup-codes "")
+         (python-shell-process-environment "")
+         (python-shell-extra-pythonpaths "")
+         (python-shell-exec-path "")
+         (python-shell-virtualenv-path "")
+         (expected (python-tests-with-temp-buffer
+                       "" (python-shell-internal-get-process-name))))
+    ;; Same configurations should match.
+    (should
+     (string= expected
+              (python-tests-with-temp-buffer
+                  "" (python-shell-internal-get-process-name))))
+    (let ((python-shell-interpreter-args "-B"))
+      ;; A minimal change should generate different names.
+      (should
+       (not (string=
+             expected
+             (python-tests-with-temp-buffer
+                 "" (python-shell-internal-get-process-name))))))))
+
+(ert-deftest python-shell-parse-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"
+                     python-shell-interpreter
+                     python-shell-interpreter-args)
+             (python-shell-parse-command)))))
+
+(ert-deftest python-shell-calculate-process-environment-1 ()
+  "Test `python-shell-process-environment' modification."
+  (let* ((original-process-environment process-environment)
+         (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* ((original-process-environment process-environment)
+         (original-pythonpath (getenv "PYTHONPATH"))
+         (paths '("path1" "path2"))
+         (python-shell-extra-pythonpaths paths)
+         (process-environment
+          (python-shell-calculate-process-environment)))
+    (should (equal (getenv "PYTHONPATH")
+                   (concat
+                    (mapconcat 'identity paths path-separator)
+                    path-separator original-pythonpath)))))
+
+(ert-deftest python-shell-calculate-process-environment-3 ()
+  "Test `python-shell-virtualenv-path' modification."
+  (let* ((original-process-environment process-environment)
+         (original-path (or (getenv "PATH") ""))
+         (python-shell-virtualenv-path
+          (directory-file-name user-emacs-directory))
+         (process-environment
+          (python-shell-calculate-process-environment)))
+    (should (not (getenv "PYTHONHOME")))
+    (should (string= (getenv "VIRTUAL_ENV") python-shell-virtualenv-path))
+    (should (equal (getenv "PATH")
+                   (format "%s/bin%s%s"
+                           python-shell-virtualenv-path
+                           path-separator original-path)))))
+
+(ert-deftest python-shell-calculate-exec-path-1 ()
+  "Test `python-shell-exec-path' modification."
+  (let* ((original-exec-path exec-path)
+         (python-shell-exec-path '("path1" "path2"))
+         (exec-path (python-shell-calculate-exec-path)))
+    (should (equal
+             exec-path
+             (append python-shell-exec-path
+                     original-exec-path)))))
+
+(ert-deftest python-shell-calculate-exec-path-2 ()
+  "Test `python-shell-exec-path' modification."
+  (let* ((original-exec-path exec-path)
+         (python-shell-virtualenv-path
+          (directory-file-name user-emacs-directory))
+         (exec-path (python-shell-calculate-exec-path)))
+    (should (equal
+             exec-path
+             (append (cons
+                      (format "%s/bin" python-shell-virtualenv-path)
+                      original-exec-path))))))
+
+(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-parse-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-parse-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-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-parse-command) global-proc-name))
+           (dedicated-shell-buffer
+            (python-shell-make-comint
+             (python-shell-parse-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-get-or-create-process-1 ()
+  "Check shell process creation fallback."
+  :expected-result :failed
+  (python-tests-with-temp-file
+      ""
+    ;; XXX: Break early until we can skip stuff.  We need to mimic
+    ;; user interaction because `python-shell-get-or-create-process'
+    ;; asks for all arguments interactively when a shell process
+    ;; doesn't exist.
+    (should nil)
+    (let* ((python-shell-interpreter
+            (executable-find python-tests-shell-interpreter))
+           (use-dialog-box)
+           (dedicated-process-name (python-shell-get-process-name t))
+           (dedicated-process (python-shell-get-or-create-process))
+           (dedicated-shell-buffer (process-buffer dedicated-process)))
+      (unwind-protect
+          (progn
+            (set-process-query-on-exit-flag dedicated-process nil)
+            ;; Prefer dedicated if not buffer exist.
+            (should (equal (process-name dedicated-process)
+                           dedicated-process-name))
+            (kill-buffer dedicated-shell-buffer)
+            ;; No buffer available.
+            (should (not (python-shell-get-process))))
+        (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)))
+            ;; No user buffer available.
+            (should (not (python-shell-get-process)))
+            (kill-buffer internal-shell-buffer))
+        (ignore-errors (kill-buffer internal-shell-buffer))))))
+
 \f
 ;;; Shell completion
 
@@ -1187,66 +1793,232 @@ def f():
 
 \f
 ;;; Imenu
-(ert-deftest python-imenu-prev-index-position-1 ()
-  (require 'imenu)
+
+(ert-deftest python-imenu-create-index-1 ()
   (python-tests-with-temp-buffer
    "
-def decoratorFunctionWithArguments(arg1, arg2, arg3):
+class Foo(models.Model):
+    pass
+
+
+class Bar(models.Model):
+    pass
+
+
+def decorator(arg1, arg2, arg3):
     '''print decorated function call data to stdout.
 
     Usage:
 
-    @decoratorFunctionWithArguments('arg1', 'arg2')
+    @decorator('arg1', 'arg2')
     def func(a, b, c=True):
         pass
     '''
 
-    def wwrap(f):
-        print 'Inside wwrap()'
+    def wrap(f):
+        print ('wrap')
         def wrapped_f(*args):
-            print 'Inside wrapped_f()'
-            print 'Decorator arguments:', arg1, arg2, arg3
+            print ('wrapped_f')
+            print ('Decorator arguments:', arg1, arg2, arg3)
             f(*args)
-            print 'After f(*args)'
+            print ('called f(*args)')
         return wrapped_f
-    return wwrap
+    return wrap
 
-def test(): # Some comment
-    'This is a test function'
-    print 'test'
 
-class C(object):
+class Baz(object):
 
-    def m(self):
-        self.c()
+    def a(self):
+        pass
 
-        def b():
+    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 a():
+    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 c(self):
+        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
 "
-   (let ((expected
-          '(("*Rescan*" . -99)
-            ("decoratorFunctionWithArguments" . 2)
-            ("decoratorFunctionWithArguments.wwrap" . 224)
-            ("decoratorFunctionWithArguments.wwrap.wrapped_f" . 273)
-            ("test" . 500)
-            ("C" . 575)
-            ("C.m" . 593)
-            ("C.m.b" . 628)
-            ("C.m.a" . 663)
-            ("C.c" . 698))))
-     (mapc
-      (lambda (elt)
-        (should (= (cdr (assoc-string (car elt) expected))
-                   (if (markerp (cdr elt))
-                       (marker-position (cdr elt))
-                     (cdr elt)))))
-      (imenu--make-index-alist)))))
+   (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)))))
 
 \f
 ;;; Misc helpers
@@ -1277,13 +2049,13 @@ class C(object):
             return []
 
         def b():
-            pass
+            do_b()
 
         def a():
-            pass
+            do_a()
 
     def c(self):
-        pass
+        do_c()
 "
    (forward-line 1)
    (should (string= "C" (python-info-current-defun)))
@@ -1313,7 +2085,7 @@ class C(object):
    (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 "pass")
+   (python-tests-look-at "do_c()")
    (should (string= "C.c" (python-info-current-defun)))
    (should (string= "def C.c" (python-info-current-defun t)))))