| 
 | 1 | +;;; lsp-dart-test-output.el --- Test output features and decorations -*- lexical-binding: t; -*-  | 
 | 2 | +;;  | 
 | 3 | +;; Copyright (C) 2020 Eric Dallo  | 
 | 4 | +;;  | 
 | 5 | +;; This program is free software; you can redistribute it and/or modify  | 
 | 6 | +;; it under the terms of the GNU General Public License as published by  | 
 | 7 | +;; the Free Software Foundation, either version 3 of the License, or  | 
 | 8 | +;; (at your option) any later version.  | 
 | 9 | + | 
 | 10 | +;; This program is distributed in the hope that it will be useful,  | 
 | 11 | +;; but WITHOUT ANY WARRANTY; without even the implied warranty of  | 
 | 12 | +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the  | 
 | 13 | +;; GNU General Public License for more details.  | 
 | 14 | + | 
 | 15 | +;; You should have received a copy of the GNU General Public License  | 
 | 16 | +;; along with this program.  If not, see <https://www.gnu.org/licenses/>.  | 
 | 17 | +;;  | 
 | 18 | +;;; Commentary:  | 
 | 19 | +;;  | 
 | 20 | +;;  Test output features and decorations  | 
 | 21 | +;;  | 
 | 22 | +;;; Code:  | 
 | 23 | + | 
 | 24 | + | 
 | 25 | +(require 'rx)  | 
 | 26 | + | 
 | 27 | +(require 'lsp-dart-protocol)  | 
 | 28 | +(require 'lsp-dart-utils)  | 
 | 29 | + | 
 | 30 | +(defcustom lsp-dart-test-pop-to-buffer-on-run 'display-only  | 
 | 31 | +  "Controls whether to pop to the tests buffer on run.  | 
 | 32 | +
  | 
 | 33 | +When set to nil the buffer will only be created, and not displayed.  | 
 | 34 | +When set to `display-only' the buffer will be displayed, but it will  | 
 | 35 | +not become focused, otherwise the buffer is displayed and focused."  | 
 | 36 | +  :group 'lsp-dart  | 
 | 37 | +  :type '(choice (const :tag "Create the buffer, but don't display it" nil)  | 
 | 38 | +                 (const :tag "Create and display the buffer, but don't focus it" display-only)  | 
 | 39 | +                 (const :tag "Create, display, and focus the buffer" t)))  | 
 | 40 | + | 
 | 41 | + | 
 | 42 | +;;; Internal  | 
 | 43 | + | 
 | 44 | +(defconst lsp-dart-test-output--passed-icon "★")  | 
 | 45 | +(defconst lsp-dart-test-output--success-icon "✔")  | 
 | 46 | +(defconst lsp-dart-test-output--skipped-icon "•")  | 
 | 47 | +(defconst lsp-dart-test-output--error-icon "✖")  | 
 | 48 | + | 
 | 49 | +(defvar lsp-dart-test-output--tests-count 0)  | 
 | 50 | +(defvar lsp-dart-test-output--tests-passed 0)  | 
 | 51 | + | 
 | 52 | +(defconst lsp-dart-test-output--buffer-name "*LSP Dart tests*")  | 
 | 53 | + | 
 | 54 | +(defconst lsp-dart-test-output--exception-re  | 
 | 55 | +  (rx (or (and (zero-or-more any)  | 
 | 56 | +               (or "exception" "EXCEPTION")  | 
 | 57 | +               (zero-or-more any))  | 
 | 58 | +          "<asynchronous suspension>"  | 
 | 59 | +          (and "#"  | 
 | 60 | +               (one-or-more  | 
 | 61 | +                any)))))  | 
 | 62 | + | 
 | 63 | +(defconst lsp-dart-test-output--expected-actual-re  | 
 | 64 | +  (rx (or (and (zero-or-more blank)  | 
 | 65 | +               "Expected:"  | 
 | 66 | +               (zero-or-more any))  | 
 | 67 | +          (and (zero-or-more blank)  | 
 | 68 | +               "Actual:"  | 
 | 69 | +               (zero-or-more any)))))  | 
 | 70 | + | 
 | 71 | +(defconst lsp-dart-test--font-lock  | 
 | 72 | +  `((,lsp-dart-test-output--exception-re . 'error)  | 
 | 73 | +    (,lsp-dart-test-output--expected-actual-re . 'warning)))  | 
 | 74 | + | 
 | 75 | +(defvar lsp-dart-test--output-font-lock  | 
 | 76 | +  '((lsp-dart-test--font-lock)))  | 
 | 77 | + | 
 | 78 | +(lsp-defun lsp-dart-test-output--get-icon ((&TestDoneNotification :result :skipped))  | 
 | 79 | +  "Return the icon for test done notification."  | 
 | 80 | +  (if (string= result "success")  | 
 | 81 | +      (if skipped  | 
 | 82 | +          lsp-dart-test-output--skipped-icon  | 
 | 83 | +        lsp-dart-test-output--success-icon)  | 
 | 84 | +    lsp-dart-test-output--error-icon))  | 
 | 85 | + | 
 | 86 | +(lsp-defun lsp-dart-test-output--get-face ((&TestDoneNotification :result :skipped))  | 
 | 87 | +  "Return the icon for test done notification."  | 
 | 88 | +  (if (string= result "success")  | 
 | 89 | +      (if skipped  | 
 | 90 | +          'homoglyph  | 
 | 91 | +        'success)  | 
 | 92 | +    'error))  | 
 | 93 | + | 
 | 94 | +(defun lsp-dart-test-output--send (message &rest args)  | 
 | 95 | +  "Send MESSAGE with ARGS to test buffer."  | 
 | 96 | +  (let* ((inhibit-read-only t))  | 
 | 97 | +    (with-current-buffer (lsp-dart-test-output--get-buffer-create)  | 
 | 98 | +      (save-excursion  | 
 | 99 | +        (goto-char (point-max))  | 
 | 100 | +        (insert (apply #'format (concat message "\n") args))))))  | 
 | 101 | + | 
 | 102 | +(defun lsp-dart-test-output--get-buffer-create ()  | 
 | 103 | +  "Create a buffer for test display."  | 
 | 104 | +  (let ((buffer (get-buffer-create lsp-dart-test-output--buffer-name)))  | 
 | 105 | +    (with-current-buffer buffer  | 
 | 106 | +      (setq-local default-directory (or (lsp-dart-get-project-root) default-directory))  | 
 | 107 | +      (unless (derived-mode-p 'lsp-dart-test-output-content-mode)  | 
 | 108 | +        (lsp-dart-test-output-content-mode))  | 
 | 109 | +      (current-buffer))))  | 
 | 110 | + | 
 | 111 | +(defun lsp-dart-test-output--show-buffer ()  | 
 | 112 | +  "Show test buffer."  | 
 | 113 | +  (let ((test-buffer (lsp-dart-test-output--get-buffer-create))  | 
 | 114 | +        (inhibit-read-only t))  | 
 | 115 | +    (with-current-buffer test-buffer  | 
 | 116 | +      (erase-buffer))  | 
 | 117 | +    (pcase lsp-dart-test-pop-to-buffer-on-run  | 
 | 118 | +      (`display-only  | 
 | 119 | +       (let ((orig-buffer (current-buffer)))  | 
 | 120 | +         (display-buffer test-buffer)  | 
 | 121 | +         (set-buffer orig-buffer)))  | 
 | 122 | +      ((pred identity) (pop-to-buffer test-buffer)))))  | 
 | 123 | + | 
 | 124 | +(defun lsp-dart-test-output--handle-run-started ()  | 
 | 125 | +  "Handle test run started."  | 
 | 126 | +  (lsp-dart-test-output--send "Running tests...\n")  | 
 | 127 | +  (lsp-dart-test-output--show-buffer))  | 
 | 128 | + | 
 | 129 | +(defun lsp-dart-test-output--handle-all-start (_notification)  | 
 | 130 | +  "Handle all start notification."  | 
 | 131 | +  (setq lsp-dart-test-output--tests-count 0)  | 
 | 132 | +  (setq lsp-dart-test-output--tests-passed 0))  | 
 | 133 | + | 
 | 134 | +(lsp-defun lsp-dart-test-output--handle-start ((&TestStartNotification :test (&Test :group-i-ds)))  | 
 | 135 | +  (unless (seq-empty-p group-i-ds)  | 
 | 136 | +    (setq lsp-dart-test-output--tests-count (1+ lsp-dart-test-output--tests-count))))  | 
 | 137 | + | 
 | 138 | +(lsp-defun lsp-dart-test-output--handle-done ((notification &as &TestDoneNotification :result :time :hidden) test-name test-start-time)  | 
 | 139 | +  "Handle test done notification."  | 
 | 140 | +  (unless hidden  | 
 | 141 | +    (when (string= result "success")  | 
 | 142 | +      (setq lsp-dart-test-output--tests-passed (1+ lsp-dart-test-output--tests-passed))))  | 
 | 143 | +  (let* ((formatted-time (propertize (format "(%s ms)"  | 
 | 144 | +                                             (- time test-start-time))  | 
 | 145 | +                                     'font-lock-face 'font-lock-comment-face))  | 
 | 146 | +         (text (propertize (concat (lsp-dart-test-output--get-icon notification)  | 
 | 147 | +                                   " "  | 
 | 148 | +                                   test-name)  | 
 | 149 | +                           'font-lock-face (lsp-dart-test-output--get-face notification))))  | 
 | 150 | +    (lsp-dart-test-output--send "%s %s" text formatted-time)))  | 
 | 151 | + | 
 | 152 | +(lsp-defun lsp-dart-test-output--handle-all-done ((&DoneNotification :success))  | 
 | 153 | +  "Handle all tests done notification."  | 
 | 154 | +  (if success  | 
 | 155 | +      (lsp-dart-test-output--send (propertize (format "\n%s All ran tests passed %s" lsp-dart-test-output--passed-icon lsp-dart-test-output--passed-icon)  | 
 | 156 | +                                              'font-lock-face 'success))  | 
 | 157 | +    (lsp-dart-test-output--send (propertize (format "\n● %s/%s tests passed" lsp-dart-test-output--tests-passed lsp-dart-test-output--tests-count)  | 
 | 158 | +                                            'font-lock-face font-lock-warning-face))))  | 
 | 159 | + | 
 | 160 | +(lsp-defun lsp-dart-test-output--handle-print ((&PrintNotification :message))  | 
 | 161 | +  "Handle test print notification."  | 
 | 162 | +  (lsp-dart-test-output--send "%s" message))  | 
 | 163 | + | 
 | 164 | +(lsp-defun lsp-dart-test-output--handle-error ((&ErrorNotification :error :stack-trace))  | 
 | 165 | +  "Handle test error notification."  | 
 | 166 | +  (lsp-dart-test-output--send "%s" error)  | 
 | 167 | +  (lsp-dart-test-output--send "%s" stack-trace))  | 
 | 168 | + | 
 | 169 | +(define-derived-mode lsp-dart-test-output-content-mode special-mode lsp-dart-test-output--buffer-name  | 
 | 170 | +  "Major mode for buffer running tests."  | 
 | 171 | +  (setq font-lock-defaults lsp-dart-test--output-font-lock))  | 
 | 172 | + | 
 | 173 | +(add-hook 'lsp-dart-test-run-started-hook #'lsp-dart-test-output--handle-run-started)  | 
 | 174 | +(add-hook 'lsp-dart-test-all-start-notification-hook #'lsp-dart-test-output--handle-all-start)  | 
 | 175 | +(add-hook 'lsp-dart-test-start-notification-hook #'lsp-dart-test-output--handle-start)  | 
 | 176 | +(add-hook 'lsp-dart-test-done-notification-hook #'lsp-dart-test-output--handle-done)  | 
 | 177 | +(add-hook 'lsp-dart-test-all-done-notification-hook #'lsp-dart-test-output--handle-all-done)  | 
 | 178 | +(add-hook 'lsp-dart-test-print-notification-hook #'lsp-dart-test-output--handle-print)  | 
 | 179 | +(add-hook 'lsp-dart-test-error-notification-hook #'lsp-dart-test-output--handle-error)  | 
 | 180 | + | 
 | 181 | +(provide 'lsp-dart-test-output)  | 
 | 182 | +;;; lsp-dart-test-output.el ends here  | 
0 commit comments