1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
|
;; -*- flycheck-disabled-checkers: (emacs-lisp-checkdoc) ; lexical-binding:t -*-
(require 'lua-mode)
(require 'buttercup)
(defun to-be-fontified-as (text faces)
(let ((expected-faces (lua-mk-font-lock-faces faces))
(result-faces (lua-get-line-faces text))
(lineno 1))
(when (/= (length expected-faces) (length result-faces))
(buttercup-fail "\
Fontification check failed for:
%S
Text contains %d lines, face list contains %d lines"
text (length result-faces) (length expected-faces)))
(while expected-faces
(unless (equal (car expected-faces) (car result-faces))
(buttercup-fail "\
Fontification check failed on line %d for:
%S
Expected faces: %S
Actual faces: %S"
lineno text (car expected-faces) (car result-faces)))
(setq expected-faces (cdr expected-faces)
result-faces (cdr result-faces)
lineno (1+ lineno)))
(cons t "Fontification check passed")))
(buttercup-define-matcher :to-be-fontified-as (text faces)
(to-be-fontified-as (funcall text) (funcall faces)))
(buttercup-define-matcher :to-precede (pos regexp)
(save-excursion
(goto-char (funcall pos))
(let* ((precedes (looking-at (funcall regexp)))
(substr-begin (min (point-max) (funcall pos)))
(substr-end (min (point-max) (+ (funcall pos) 100)))
(found-after (format "%S" (buffer-substring-no-properties
substr-begin substr-end ))))
(goto-char substr-end)
(when (eobp) (setq found-after (concat found-after " (end-of-buffer)")))
(cons precedes (format "Expected %s to see after point at %s: %S. Found: %s"
(if precedes "NOT" "")
(funcall pos) (funcall regexp) found-after)))))
(defun get-str-faces (str)
"Find contiguous spans of non-default faces in STR.
E.g. for properly fontified Lua string \"local x = 100\" it should return
'(\"local\" font-lock-keyword-face
\"x\" font-lock-variable-name-face
\"100\" font-lock-constant-face)
"
(let* ((pos 0)
(prop (or (get-text-property pos 'face str)
(get-text-property pos 'font-lock-face str)))
(nextpos 0)
newprop
result)
(while nextpos
(setq nextpos (next-property-change nextpos str))
(setq newprop (when nextpos (or (get-text-property nextpos 'face str)
(get-text-property nextpos 'font-lock-face str))))
(when (not (equal prop newprop))
(when (listp prop)
(when (eq (car-safe (last prop)) 'default)
(setq prop (butlast prop)))
(when (= 1 (length prop))
(setq prop (car prop)))
(when (symbolp prop)
(when (eq prop 'default)
(setq prop nil))))
(when prop
(push (substring-no-properties str pos nextpos) result)
(push prop result))
(setq prop newprop
pos nextpos)))
(nreverse result)))
(defun lua-fontify-str (str)
"Return string fontified according to lua-mode's rules"
(with-temp-buffer
(lua-mode)
(insert str)
(font-lock-fontify-buffer)
(buffer-string)))
(defun get-buffer-line-faces ()
(font-lock-fontify-buffer)
(mapcar 'get-str-faces
(split-string (buffer-string) "\n" nil)))
(defun lua-get-line-faces (str)
"Find contiguous spans of non-default faces in each line of STR.
The result is a list of lists."
(mapcar
'get-str-faces
(split-string (lua-fontify-str str) "\n" nil)))
(defun lua-mk-font-lock-faces (sym)
"Decorate symbols with font-lock-%s-face recursively.
This is a mere typing/reading aid for lua-mode's font-lock tests."
(or (cond
((symbolp sym) (intern-soft (format "font-lock-%s-face" (symbol-name sym))))
((listp sym) (mapcar 'lua-mk-font-lock-faces sym)))
sym))
(defmacro should-lua-font-lock-equal (strs faces)
`(should (equal (lua-get-line-faces ,strs)
(lua-mk-font-lock-faces ,faces))))
;; suppress fontification messages in emacs23 output
(setq font-lock-verbose nil)
(defun lua-join-lines (strs)
(mapconcat (lambda (x) (concat x "\n")) strs ""))
(defmacro with-lua-buffer (&rest body)
(declare (debug (&rest form)))
`(with-temp-buffer
;; font-lock is not activated if buffer name is temporary (starts with a
;; space) and if `noninteractive' is non-nil. Ensure tests that use
;; font-lock still work.
(rename-buffer "temp-buffer.lua" t)
(let (noninteractive)
(lua-mode)
(font-lock-mode 1))
(set (make-local-variable 'lua-process) nil)
(set (make-local-variable 'lua-process-buffer) nil)
(pop-to-buffer (current-buffer))
(unwind-protect
(progn ,@body)
(when (buffer-live-p lua-process-buffer)
(lua-kill-process)))))
(defun lua-get-indented-strs (strs)
(let ((indent-tabs-mode nil)
(font-lock-verbose nil))
(butlast
(split-string
(with-lua-buffer
(let ((inhibit-message t))
(insert (replace-regexp-in-string "^\\s *" "" (lua-join-lines strs)))
(font-lock-fontify-buffer)
(indent-region (point-min) (point-max))
(buffer-substring-no-properties
(point-min) (point-max))))
"\n" nil))))
(defun lua-insert-goto-<> (strs)
"Insert sequence of strings and put point in place of \"<>\"."
(insert (lua-join-lines strs))
(goto-char (point-min))
(re-search-forward "<>")
(replace-match "")
;; Inserted text may contain multiline constructs which will only be
;; recognized after fontification.
(font-lock-fontify-buffer))
(defmacro lua-buffer-strs (&rest body)
`(butlast
(split-string
(with-lua-buffer
(progn ,@body)
(buffer-substring-no-properties (point-min) (point-max)))
"\n" nil)))
(defun lua--reindent-like (str)
(let ((strs (split-string str "\n")))
(equal strs (lua-get-indented-strs strs))))
(defun with-point-at-matcher (&rest args)
(let* (lua-code
origin-placeholder
(origin-marker (make-marker))
target-placeholder
(target-marker (make-marker))
body
last-elt
result
message
)
(cl-dolist (elt args)
(cond
((eq last-elt :lua-code)
(setq lua-code (funcall elt)
last-elt nil))
((eq last-elt :with-point-at)
(setq origin-placeholder (funcall elt)
last-elt nil))
((eq last-elt :to-end-up-at)
(setq target-placeholder (funcall elt)
last-elt nil))
((eq last-elt :after-executing)
;; No funcall here, funcall when the buffer is set up.
(setq body elt
last-elt nil))
(t
(setq last-elt (if (functionp elt) (funcall elt) elt)))))
(with-lua-buffer
(insert lua-code)
(goto-char (point-min))
(set-marker target-marker (search-forward target-placeholder))
(replace-match "")
(goto-char (point-min))
(set-marker origin-marker (search-forward origin-placeholder))
(replace-match "")
(funcall body)
(setq result (equal (point) (marker-position target-marker)))
(setq message
(if result
(format "Expected point not to be here:\n\n%s|%s"
(buffer-substring-no-properties (point-min) (point))
(buffer-substring-no-properties (point) (point-max)))
(format "Expected point to be here:\n============\n%s|%s\n============\n\nInstead it was here:\n============\n%s|%s\n============"
(buffer-substring-no-properties (point-min) (marker-position target-marker))
(buffer-substring-no-properties (marker-position target-marker) (point-max))
(buffer-substring-no-properties (point-min) (point))
(buffer-substring-no-properties (point) (point-max)))))
(cons result message))))
(buttercup-define-matcher :with-point-at (&rest args)
(apply #'with-point-at-matcher `(:lua-code ,(car args) :with-point-at ,@(cdr args))))
;;; Shortcut for with-point-at with <1> and <2> placeholders
(buttercup-define-matcher :to-move-point-from-1-to-2 (code-block lua-code)
(with-point-at-matcher
:lua-code lua-code
:with-point-at (lambda () "<1>")
:after-executing code-block
:to-end-up-at (lambda () "<2>")))
(defun lua--string-trim (string &optional trim-left trim-right)
;; Backport of string-trim for Emacs 24 that doesn't have subr-x lib.
(let ((sub-start 0) sub-end)
(or trim-left (setq trim-left "[ \t\n\r]+"))
(or trim-right (setq trim-right "[ \t\n\r]+"))
(save-match-data
(when (string-match (concat "\\`" trim-left) string)
(setq sub-start (match-end 0)))
(when (string-match (concat trim-right "\\'") string sub-start)
(setq sub-end (match-beginning 0))))
(if (or sub-start sub-end)
(substring string sub-start sub-end)
string)))
(buttercup-define-matcher :to-be-reindented-the-same-way (str)
(let* ((lines (split-string (funcall str) "\n"))
(indented-lines (lua-get-indented-strs lines)))
(buttercup--test-expectation (equal lines indented-lines)
:expect-match-phrase (format "Indentation check failed:\n=========\nExpected:\n---------\n%s\n---------\nActual:\n---------\n%s\n========="
(lua--string-trim (mapconcat 'identity lines "\n"))
(lua--string-trim (mapconcat 'identity indented-lines "\n")))
:expect-mismatch-phrase (format "Expected `%S' to not be reindented like that"
lines))))
(defmacro lua--parametrize-tests (variables param-values it description-form &rest body)
`(progn
,@(cl-loop
for params in param-values
for let-bindings = (cl-loop for var in variables
for param in params
collect `(,var (quote ,param)))
for description = (eval `(let ,let-bindings ,description-form))
for test-body = `(let ,let-bindings ,@body)
collect
(macroexpand `(it ,description ,test-body)))))
|