|
| 1 | +;; TODO ironically... there's no tests for this code... |
1 | 2 |
|
2 | 3 | (cl:in-package :cl-user) |
3 | 4 |
|
|
18 | 19 |
|
19 | 20 | (in-package #:breeze.test-file) |
20 | 21 |
|
| 22 | + |
| 23 | +;;; Classes |
| 24 | + |
| 25 | +(defclass test-spec () |
| 26 | + ((name |
| 27 | + :initform nil |
| 28 | + :initarg :name |
| 29 | + :accessor name |
| 30 | + :documentation "The name of the test.") |
| 31 | + (code |
| 32 | + :initform nil |
| 33 | + :initarg :code |
| 34 | + :accessor code |
| 35 | + :documentation "A string that specifies what to run for this test.") |
| 36 | + (point-char |
| 37 | + :initform nil |
| 38 | + :initarg :point-char |
| 39 | + :accessor point-char |
| 40 | + :documentation "The character that represents the point in the parts.") |
| 41 | + (before |
| 42 | + :initform nil |
| 43 | + :initarg :before |
| 44 | + :accessor before |
| 45 | + :documentation "A string representing the input of the test.") |
| 46 | + (after |
| 47 | + :initform nil |
| 48 | + :initarg :after |
| 49 | + :accessor after |
| 50 | + :documentation "A string representing the expected output of the test.") |
| 51 | + (skipp |
| 52 | + :initform nil |
| 53 | + :initarg :skipp |
| 54 | + :accessor skipp |
| 55 | + :documentation "Whether this test should be skipped.") |
| 56 | + (source-pathname |
| 57 | + :initform nil |
| 58 | + :initarg :source-pathname |
| 59 | + :accessor source-pathname |
| 60 | + :documentation "The pathname of the file from which this specification was read from.") |
| 61 | + (source-position |
| 62 | + :initform nil |
| 63 | + :initarg :source-position |
| 64 | + :accessor source-position |
| 65 | + :documentation "A cons (START . END) that corresponds to the file-position of this |
| 66 | +specification in the file it was read from.") |
| 67 | + ;; TODO perhaps "source": which file it was read from, which position in the file? |
| 68 | + ) |
| 69 | + (:documentation "A test that was specified in an \"erts\" file.")) |
| 70 | + |
| 71 | +(defmethod print-object ((test-spec test-spec) stream) |
| 72 | + (print-unreadable-object |
| 73 | + (test-spec stream :type t :identity nil) |
| 74 | + (destructuring-bind (start . end) |
| 75 | + (source-position test-spec) |
| 76 | + (format stream "~s ~s-~s" (name test-spec) start end)))) |
| 77 | + |
| 78 | + |
| 79 | +;;; Parsing utils |
| 80 | + |
21 | 81 | (defun string-bool (string) |
22 | 82 | "If string is a representation of T or NIL, then coerce it." |
23 | 83 | (cond |
|
34 | 94 | (string= (trim-whitespace string) "=-=-="))) |
35 | 95 |
|
36 | 96 |
|
| 97 | +;;; Low-level ERTS file parsing |
37 | 98 |
|
38 | | -(defun read-spec-file (pathname) |
| 99 | +(defun %read-spec-file (pathname) |
39 | 100 | (with |
40 | 101 | ((open-file (stream pathname)) |
41 | 102 | (collectors (tests parts)) |
42 | 103 | (let ((attributes (make-hash-table)) |
43 | | - (eof (gensym "eof")))) |
44 | | - (macrolet |
45 | | - ((push-char () `(write-char c out)))) |
| 104 | + (eof '#.(gensym "eof")) |
| 105 | + (start 0))) |
46 | 106 | (labels |
47 | 107 | ((peek (&optional (peek-type t)) |
48 | 108 | (peek-char peek-type stream nil eof)) |
49 | | - (get-char () (read-char stream)) |
| 109 | + (get-char () (read-char stream)) |
50 | 110 | (eofp (x) (eq eof x)) |
51 | 111 | (clean-attributes () (remhash :skip attributes)) |
52 | 112 | (trim-last-newline (string) |
|
58 | 118 | (when (char= #\; c) |
59 | 119 | (read-line stream nil t))) |
60 | 120 | (read-string (string) |
| 121 | + ;; TODO this doesn't actually check anything... |
61 | 122 | (loop :for c :across string |
62 | 123 | :do (char= c (get-char)))) |
63 | 124 | (read-test (c) |
64 | 125 | (when (char= #\= c) |
65 | 126 | (with-output-to-string (out) |
66 | | - (read-string #. (format nil "=-=~%")) |
| 127 | + (read-string #.(format nil "=-=~%")) |
67 | 128 | (loop :for line = (read-line stream) |
68 | 129 | :do (cond |
69 | 130 | ((part-delimiter-p line) |
70 | | - (push-parts (trim-last-newline (get-output-stream-string out)))) |
| 131 | + (push-parts (trim-last-newline |
| 132 | + (get-output-stream-string out)))) |
71 | 133 | ((end-delimiter-p line) |
72 | | - (push-parts (trim-last-newline (get-output-stream-string out))) |
73 | | - (push-tests `(,@(alexandria:hash-table-plist attributes) |
74 | | - :parts ,(drain-parts))) |
| 134 | + (push-parts (trim-last-newline |
| 135 | + (get-output-stream-string out))) |
| 136 | + (push-tests `(,@(alexandria:hash-table-alist attributes) |
| 137 | + (:parts ,@(drain-parts)) |
| 138 | + (:pathname ,@pathname) |
| 139 | + (:position ,@(cons start |
| 140 | + (file-position stream))))) |
75 | 141 | (peek) ;; skip whitespaces |
| 142 | + (setf start (file-position stream)) |
76 | 143 | (clean-attributes) |
77 | 144 | (return-from read-test t)) |
78 | 145 | ((string= "\\=-=" line) |
|
108 | 175 | (setf (gethash name attributes) value)))))) |
109 | 176 | (loop |
110 | 177 | :for c = (peek) |
111 | | - :repeat 250 ;; guard |
| 178 | + ;; :repeat 250 ;; guard |
112 | 179 | :until (eofp c) |
113 | 180 | :for part = (or |
114 | 181 | (whitespacep c) |
|
120 | 187 | ;; (format t "~&Final: ~% ~{~s~%~}" (tests)) |
121 | 188 | (tests))) |
122 | 189 |
|
| 190 | + |
| 191 | +;;; ERTS file parsing and validation |
| 192 | + |
| 193 | +;; TODO test-spec-condition instead of generic errors |
| 194 | +(defun read-spec-file (pathname) |
| 195 | + ;; N.B. the validatoin would almost never fail, because the |
| 196 | + ;; attributes are carried over, if a test doesn't specify a required |
| 197 | + ;; attribute, it'll just "inherit" the attribute's value from the |
| 198 | + ;; preceding test. I'm keeping this check in case there's a bug in |
| 199 | + ;; the parser. |
| 200 | + (let ((specs (%read-spec-file pathname))) |
| 201 | + (flet ((get-attribute (spec key) |
| 202 | + (cdr (assoc key spec)))) |
| 203 | + (loop :for test :in specs |
| 204 | + :for pathname = (get-attribute test :pathname) |
| 205 | + :for position = (get-attribute test :position) |
| 206 | + :for (start . end) = position |
| 207 | + :for name = (get-attribute test :name) |
| 208 | + :unless name |
| 209 | + :do (error "A test is missing a name (~s between char ~d and ~d)" |
| 210 | + pathname start end) |
| 211 | + :collect |
| 212 | + (let ((code (get-attribute test :code)) |
| 213 | + (point-char (get-attribute test :point-char)) |
| 214 | + (parts (get-attribute test :parts)) |
| 215 | + (skipp (get-attribute test :skip)) |
| 216 | + before after) |
| 217 | + (unless code |
| 218 | + (error "The test ~s is missing the \"Code:\" attribute." name)) |
| 219 | + (unless point-char |
| 220 | + (error "The test ~s is missing the \"Point-Char:\" attribute." name)) |
| 221 | + (unless parts |
| 222 | + (error "The test ~s is missing the actual test input(s)." name)) |
| 223 | + (let ((number-of-parts (length parts))) |
| 224 | + (case number-of-parts |
| 225 | + (1 |
| 226 | + (setf before (first parts) |
| 227 | + after (first parts))) |
| 228 | + (2 (setf before (first parts) |
| 229 | + after (second parts))) |
| 230 | + (t |
| 231 | + (unless (<= 1 number-of-parts 2) |
| 232 | + (error "The test ~s (defined in ~s between the chars ~d and ~d) has ~d parts, a test should have only 1 or 2." |
| 233 | + name pathname start end number-of-parts))))) |
| 234 | + ;; TODO Don't discard the "unknown" attributes The way |
| 235 | + ;; the parser is written, there's no limitation on the |
| 236 | + ;; valid attributes. Let's say that these here are the |
| 237 | + ;; "well-known" attributes. |
| 238 | + (make-instance 'test-spec |
| 239 | + :name name |
| 240 | + :source-pathname pathname |
| 241 | + :source-position position |
| 242 | + :code code |
| 243 | + :point-char point-char |
| 244 | + :before before |
| 245 | + :after after |
| 246 | + :skipp skipp)))))) |
| 247 | + |
123 | 248 | #++ |
124 | 249 | (defparameter *structural-editing-tests* |
125 | 250 | (read-spec-file |
126 | 251 | (asdf:system-relative-pathname |
127 | 252 | "breeze" "scratch-files/notes/strutural-editing.lisp"))) |
128 | 253 |
|
| 254 | +;; parachute:define-test |
| 255 | +;; parachute::ensure-test |
129 | 256 |
|
130 | 257 | #++ |
131 | 258 | (loop :for test :in *structural-editing-tests* |
132 | | - :do (format t "~&~a: ~a parts" |
133 | | - (getf test :name) |
134 | | - (length (getf test :parts))) |
135 | | - ;; :do (print ) |
136 | | - ) |
| 259 | + :do ) |
0 commit comments