dotfiles/.config/emacs/site-lisp/python-tests.el

286 lines
11 KiB
EmacsLisp

;;; elpy.el --- Emacs Python Development Environment -*- lexical-binding: t -*-
;; Copyright (C) 2012-2019 Jorgen Schaefer
;; Author: Jorgen Schaefer <contact@jorgenschaefer.de>, Gaby Launay <gaby.launay@protonmail.com>
;; URL: https://github.com/jorgenschaefer/elpy
;; Version: 1.35.0
;; Keywords: Python, IDE, Languages, Tools
;; Package-Requires: ((company "0.9.10") (emacs "24.4") (highlight-indentation "0.7.0") (pyvenv "1.20") (yasnippet "0.13.0") (s "1.12.0"))
;; 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:
;; Copy of the content necessary to run elpy-test--current-test-name function from the Elpy project
(defun elpy-library-root ()
"Return the root of the Python package chain of the current buffer.
That is, if you have /foo/package/module.py, it will return /foo,
so that import package.module will pick up module.py."
(locate-dominating-file default-directory
(lambda (dir)
(not (file-exists-p
(format "%s/__init__.py"
dir))))))
(defcustom elpy-test-runner 'elpy-test-discover-runner
"The test runner to use to run tests."
:type '(choice (const :tag "Unittest Discover" elpy-test-discover-runner)
(const :tag "Green" elpy-test-green-runner)
(const :tag "Django Discover" elpy-test-django-runner)
(const :tag "Nose" elpy-test-nose-runner)
(const :tag "py.test" elpy-test-pytest-runner)
(const :tag "Twisted Trial" elpy-test-trial-runner))
:safe 'elpy-test-runner-p
:group 'elpy)
(defcustom elpy-test-discover-runner-command '("python-shell-interpreter" "-m" "unittest")
"The command to use for `elpy-test-discover-runner'.
If the string \"python-shell-interpreter\" is present, it will be replaced with
the value of `python-shell-interpreter'."
:type '(repeat string)
:group 'elpy)
(defcustom elpy-test-green-runner-command '("green")
"The command to use for `elpy-test-green-runner'."
:type '(repeat string)
:group 'elpy)
(defcustom elpy-test-nose-runner-command '("nosetests")
"The command to use for `elpy-test-nose-runner'."
:type '(repeat string)
:group 'elpy)
(defcustom elpy-test-trial-runner-command '("trial")
"The command to use for `elpy-test-trial-runner'."
:type '(repeat string)
:group 'elpy)
(defcustom elpy-test-pytest-runner-command '("py.test")
"The command to use for `elpy-test-pytest-runner'."
:type '(repeat string)
:group 'elpy)
(defcustom elpy-test-compilation-function 'compile
"Function used by `elpy-test-run' to run a test command.
The function should behave similarly to `compile'. Another good
option is `pdb'."
:type 'string
:group 'elpy)
(defvar elpy-set-test-runner-history nil
"History variable for `elpy-set-test-runner'.")
(defun elpy-test (&optional test-whole-project)
"Run tests on the current test, or the whole project.
If there is a test at point, run that test. If not, or if a
prefix is given, run all tests in the current project."
(interactive "P")
(let ((current-test (elpy-test-at-point)))
(if test-whole-project
;; With prefix arg, test the whole project.
(funcall elpy-test-runner
(car current-test)
nil nil nil)
;; Else, run only this test
(apply elpy-test-runner current-test))))
(defun elpy-set-test-runner (test-runner)
"Tell Elpy to use TEST-RUNNER to run tests.
See `elpy-test' for how to invoke it."
(interactive
(list
(let* ((runners (mapcar (lambda (value)
(cons (nth 2 value)
(nth 3 value)))
(cdr (get 'elpy-test-runner 'custom-type))))
(current (cdr (assq elpy-test-runner
(mapcar (lambda (cell)
(cons (cdr cell) (car cell)))
runners))))
(choice (completing-read (if current
(format "Test runner (currently %s): "
current)
"Test runner: ")
runners
nil t nil 'elpy-set-test-runner-history)))
(if (equal choice "")
elpy-test-runner
(cdr (assoc choice runners))))))
(setq elpy-test-runner test-runner))
(defun elpy-test-at-point ()
"Return a list specifying the test at point, if any.
This is used as the interactive
This list has four elements.
- Top level directory:
All test files should be importable from here.
- Test file:
The current file name.
- Test module:
The module name, relative to the top level directory.
- Test name:
The full name of the current test within the module, for
example TestClass.test_method
If there is no test at point, test name is nil.
If the current buffer is not visiting a file, only the top level
directory is not nil."
(if (not buffer-file-name)
(progn
(save-some-buffers)
(list (elpy-library-root) nil nil nil))
(let* ((top (elpy-library-root))
(file buffer-file-name)
(module (elpy-test--module-name-for-file top file))
(test (elpy-test--current-test-name)))
(if (and file (string-match "test" (or module test "")))
(progn
(save-buffer)
(list top file module test))
(save-some-buffers)
(list top nil nil nil)))))
(defun elpy-test--current-test-name ()
"Return the name of the test at point."
(let ((name (python-info-current-defun)))
(if (and name
(string-match "\\`\\([^.]+\\.[^.]+\\)\\." name))
(match-string 1 name)
name)))
(defun elpy-test--module-name-for-file (top-level module-file)
"Return the module name relative to TOP-LEVEL for MODULE-FILE.
For example, for a top level of /project/root/ and a module file
of /project/root/package/module.py, this would return
\"package.module\"."
(let* ((relative-name (file-relative-name module-file top-level))
(no-extension (replace-regexp-in-string "\\.py\\'" "" relative-name))
(no-init (replace-regexp-in-string "/__init__\\'" "" no-extension))
(dotted (replace-regexp-in-string "/" "." no-init)))
(if (string-match "^\\." dotted)
(concat "." (replace-regexp-in-string (regexp-quote "...") "." dotted))
dotted)))
(defun elpy-test-runner-p (obj)
"Return t iff OBJ is a test runner.
This uses the `elpy-test-runner-p' symbol property."
(get obj 'elpy-test-runner-p))
(defun elpy-test-run (working-directory command &rest args)
"Run COMMAND with ARGS in WORKING-DIRECTORY as a test command."
(let ((default-directory working-directory))
(funcall elpy-test-compilation-function
(mapconcat #'shell-quote-argument
(cons command args)
" "))))
(defun elpy-test-get-discover-runner ()
"Return the test discover runner from `elpy-test-discover-runner-command'."
(cl-loop
for string in elpy-test-discover-runner-command
if (string= string "python-shell-interpreter")
collect python-shell-interpreter
else
collect string))
(defun elpy-test-discover-runner (top _file module test)
"Test the project using the python unittest discover runner.
This requires Python 2.7 or later."
(interactive (elpy-test-at-point))
(let ((test (cond
(test (format "%s.%s" module test))
(module module)
(t "discover"))))
(apply #'elpy-test-run
top
(append (elpy-test-get-discover-runner)
(list test)))))
(put 'elpy-test-discover-runner 'elpy-test-runner-p t)
(defun elpy-test-green-runner (top _file module test)
"Test the project using the green runner."
(interactive (elpy-test-at-point))
(let* ((test (cond
(test (format "%s.%s" module test))
(module module)))
(command (if test
(append elpy-test-green-runner-command (list test))
elpy-test-green-runner-command)))
(apply #'elpy-test-run top command)))
(put 'elpy-test-green-runner 'elpy-test-runner-p t)
(defun elpy-test-nose-runner (top _file module test)
"Test the project using the nose test runner.
This requires the nose package to be installed."
(interactive (elpy-test-at-point))
(if module
(apply #'elpy-test-run
top
(append elpy-test-nose-runner-command
(list (if test
(format "%s:%s" module test)
module))))
(apply #'elpy-test-run
top
elpy-test-nose-runner-command)))
(put 'elpy-test-nose-runner 'elpy-test-runner-p t)
(defun elpy-test-trial-runner (top _file module test)
"Test the project using Twisted's Trial test runner.
This requires the twisted-core package to be installed."
(interactive (elpy-test-at-point))
(if module
(apply #'elpy-test-run
top
(append elpy-test-trial-runner-command
(list (if test
(format "%s.%s" module test)
module))))
(apply #'elpy-test-run top elpy-test-trial-runner-command)))
(put 'elpy-test-trial-runner 'elpy-test-runner-p t)
(defun elpy-test-pytest-runner (top file module test)
"Test the project using the py.test test runner.
This requires the pytest package to be installed."
(interactive (elpy-test-at-point))
(cond
(test
(let ((test-list (split-string test "\\.")))
(apply #'elpy-test-run
top
(append elpy-test-pytest-runner-command
(list (mapconcat #'identity
(cons file test-list)
"::"))))))
(module
(apply #'elpy-test-run top (append elpy-test-pytest-runner-command
(list file))))
(t
(apply #'elpy-test-run top elpy-test-pytest-runner-command))))
(put 'elpy-test-pytest-runner 'elpy-test-runner-p t)