3 (defpackage sb-simple-streams-test
4 (:use #:common-lisp #:sb-simple-streams #:sb-rt))
7 (in-package #:sb-simple-streams-test)
9 (defparameter *dumb-string*
10 "This file was created by simple-stream-tests.lisp. Nothing to see here, move along.")
12 (defparameter *test-path*
13 (merge-pathnames (make-pathname :name nil :type nil :version nil)
15 "Directory for temporary test files.")
17 (eval-when (:load-toplevel) (ensure-directories-exist *test-path* :verbose t))
19 ;;; Non-destructive functional analog of REMF
20 (defun remove-key (key list)
21 (loop for (current-key val . rest) on list by #'cddr
22 until (eql current-key key)
23 collect current-key into result
24 collect val into result
25 finally (return (nconc result rest))))
27 (defmacro with-test-file ((stream file &rest open-arguments
28 &key (delete-afterwards t)
32 (setq open-arguments (remove-key :delete-afterwards open-arguments))
33 (setq open-arguments (remove-key :initial-content open-arguments))
35 (let ((create-file-stream (gensym)))
37 (with-open-file (,create-file-stream ,file :direction :output
39 :if-does-not-exist :create)
40 (write-sequence ,initial-content ,create-file-stream))
42 (with-open-file (,stream ,file ,@open-arguments)
44 ,(when delete-afterwards `(ignore-errors (delete-file ,file))))))
46 (with-open-file (,stream ,file ,@open-arguments)
48 ,(when delete-afterwards `(ignore-errors (delete-file ,file))))))
51 (deftest create-file-1
52 ;; Create a file-simple-stream, write data.
53 (let* ((file (merge-pathnames #p"test-data.txt" *test-path*)))
55 (with-open-stream (s (make-instance 'file-simple-stream
59 :if-does-not-exist :create))
60 (string= (write-string *dumb-string* s) *dumb-string*))
64 (deftest create-file-2
65 ;; Create a file-simple-stream via :class argument to open, write data.
66 (let ((file (merge-pathnames #p"test-data.txt" *test-path*)))
67 (with-test-file (s file :class 'file-simple-stream :direction :output
68 :if-exists :overwrite :if-does-not-exist :create)
69 (string= (write-string *dumb-string* s) *dumb-string*)))
72 (deftest create-read-file-1
73 ;; Via file-simple-stream objects, write and then re-read data.
75 (file (merge-pathnames #p"test-data.txt" *test-path*)))
76 (with-test-file (s file :class 'file-simple-stream :direction :output
77 :if-exists :overwrite :if-does-not-exist :create
78 :delete-afterwards nil)
79 (write-line *dumb-string* s)
80 (setf result (and result (string= (write-string *dumb-string* s)
83 (with-test-file (s file :class 'file-simple-stream
84 :direction :input :if-does-not-exist :error)
86 (multiple-value-bind (string missing-newline-p)
88 (setf result (and result (string= string *dumb-string*)
89 (not missing-newline-p))))
91 (multiple-value-bind (string missing-newline-p)
93 (setf result (and result (string= string *dumb-string*)
98 (deftest create-read-mapped-file-1
99 ;; Read data via a mapped-file-simple-stream object.
101 (file (merge-pathnames #p"test-data.txt" *test-path*)))
102 (with-test-file (s file :class 'mapped-file-simple-stream
103 :direction :input :if-does-not-exist :error
104 :initial-content *dumb-string*)
105 (setf result (and result (string= (read-line s) *dumb-string*))))
109 (deftest write-read-inet
111 (with-open-stream (s (make-instance 'socket-simple-stream
112 :remote-host #(127 0 0 1)
114 (string= (prog1 (write-line "Got it!" s) (finish-output s))
116 (sb-bsd-sockets::connection-refused-error () t))
119 (deftest write-read-large-sc-1
120 ;; Do write and read with more data than the buffer will hold
121 ;; (single-channel simple-stream)
122 (let* ((file (merge-pathnames #p"test-data.txt" *test-path*))
123 (stream (make-instance 'file-simple-stream
124 :filename file :direction :output
125 :if-exists :overwrite
126 :if-does-not-exist :create))
127 (content (make-string (1+ (device-buffer-length stream))
128 :initial-element #\x)))
129 (with-open-stream (s stream)
130 (write-string content s))
131 (with-test-file (s file :class 'file-simple-stream
132 :direction :input :if-does-not-exist :error)
133 (string= content (read-line s))))
136 (deftest write-read-large-sc-2
137 (let* ((file (merge-pathnames #p"test-data.txt" *test-path*))
138 (stream (make-instance 'file-simple-stream
139 :filename file :direction :output
140 :if-exists :overwrite
141 :if-does-not-exist :create))
142 (length (1+ (* 3 (device-buffer-length stream))))
143 (content (make-string length)))
144 (dotimes (i (length content))
145 (setf (aref content i) (code-char (random 256))))
146 (with-open-stream (s stream)
147 (write-string content s))
148 (with-test-file (s file :class 'file-simple-stream
149 :direction :input :if-does-not-exist :error)
150 (let ((seq (make-string length)))
151 #+nil (read-sequence seq s)
152 #-nil (dotimes (i length)
153 (setf (char seq i) (read-char s)))
154 (string= content seq))))
157 (deftest write-read-large-sc-3
158 (let* ((file (merge-pathnames #p"test-data.txt" *test-path*))
159 (stream (make-instance 'file-simple-stream
160 :filename file :direction :output
161 :if-exists :overwrite
162 :if-does-not-exist :create))
163 (length (1+ (* 3 (device-buffer-length stream))))
164 (content (make-array length :element-type '(unsigned-byte 8))))
165 (dotimes (i (length content))
166 (setf (aref content i) (random 256)))
167 (with-open-stream (s stream)
168 (write-sequence content s))
169 (with-test-file (s file :class 'file-simple-stream
170 :direction :input :if-does-not-exist :error)
171 (let ((seq (make-array length :element-type '(unsigned-byte 8))))
172 #+nil (read-sequence seq s)
173 #-nil (dotimes (i length)
174 (setf (aref seq i) (read-byte s)))
175 (equalp content seq))))
178 (deftest write-read-large-dc-1
179 ;; Do write and read with more data than the buffer will hold
180 ;; (dual-channel simple-stream; we only have socket streams atm)
182 (let* ((stream (make-instance 'socket-simple-stream
183 :remote-host #(127 0 0 1)
185 (content (make-string (1+ (device-buffer-length stream))
186 :initial-element #\x)))
187 (with-open-stream (s stream)
188 (string= (prog1 (write-line content s) (finish-output s))
190 (sb-bsd-sockets::connection-refused-error () t))
194 (deftest file-position-1
195 ;; Test reading of file-position
196 (let* ((file (merge-pathnames #p"test-data.txt" *test-path*)))
197 (with-test-file (s file :class 'file-simple-stream :direction :input
198 :initial-content *dumb-string*)
202 ;;; file-position-2 fails ONLY when called with
203 ;;; (asdf:oos 'asdf:test-op :sb-simple-streams)
204 ;;; TODO: Find out why
205 (deftest file-position-2
206 ;; Test reading of file-position
207 (let* ((file (merge-pathnames #p"test-data.txt" *test-path*)))
208 (with-test-file (s file :class 'file-simple-stream :direction :input
209 :initial-content *dumb-string*)
214 (deftest file-position-3
215 ;; Test reading of file-position in the presence of unsaved data
216 (let* ((file (merge-pathnames #p"test-data.txt" *test-path*)))
217 (with-test-file (s file :class 'file-simple-stream :direction :output
218 :if-exists :supersede :if-does-not-exist :create)
223 (deftest file-position-4
224 ;; Test file position when opening with :if-exists :append
225 (let ((file (merge-pathnames #p"test-data.txt" *test-path*)))
226 (with-test-file (s file :class 'file-simple-stream :direction :io
227 :if-exists :append :if-does-not-exist :create
228 :initial-content "Foo")
229 (= (file-length s) (file-position s))))
232 (deftest write-read-unflushed-sc-1
233 ;; Write something into a single-channel stream and read it back
234 ;; without explicitly flushing the buffer in-between
235 (let* ((file (merge-pathnames #p"test-data.txt" *test-path*)))
236 (with-test-file (s file :class 'file-simple-stream :direction :io
237 :if-does-not-exist :create :if-exists :supersede)
239 (file-position s :start)
243 (deftest write-read-unflushed-sc-2
244 ;; Write something into a single-channel stream, try to read back too much
246 (let* ((file (merge-pathnames #p"test-data.txt" *test-path*)))
247 (with-test-file (s file :class 'file-simple-stream :direction :io
248 :if-does-not-exist :create :if-exists :supersede)
250 (file-position s :start)
257 (deftest write-read-unflushed-sc-3
258 (let ((file (merge-pathnames #p"test-data.txt" *test-path*))
260 (with-test-file (s file :class 'file-simple-stream :direction :io
261 :if-exists :overwrite :if-does-not-exist :create
262 :initial-content *dumb-string*)
263 (setq result (and result (char= (read-char s) (char *dumb-string* 0))))
264 (setq result (and result (= (file-position s) 1)))
265 (let ((pos (file-position s)))
267 (file-position s pos)
268 (setq result (and result (char= (read-char s) #\x)))))
272 (deftest write-read-unflushed-sc-4
273 ;; Test flushing of buffers
274 (let ((file (merge-pathnames #p"test-data.txt" *test-path*)))
275 (with-test-file (s file :class 'file-simple-stream :direction :io
276 :if-exists :overwrite :if-does-not-exist :create
277 :initial-content "Foo"
278 :delete-afterwards nil)
279 (read-char s) ; Fill the buffer.
280 (file-position s :start) ; Change existing data.
282 (file-position s :end) ; Extend file.
284 (with-test-file (s file :class 'file-simple-stream :direction :input
285 :if-does-not-exist :error)
290 (deftest write-read-append-sc-1
291 ;; Test writing in the middle of a stream opened in append mode
292 (let ((file (merge-pathnames #p"test-data.txt" *test-path*)))
293 (with-test-file (s file :class 'file-simple-stream :direction :io
294 :if-exists :append :if-does-not-exist :create
295 :initial-content "Foo"
296 :delete-afterwards nil)
297 (file-position s :start) ; Jump to beginning.
299 (file-position s :end) ; Extend file.
301 (with-test-file (s file :class 'file-simple-stream :direction :input
302 :if-does-not-exist :error)