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 :unspecific :type :unspecific
16 "Directory for temporary test files.")
18 (defparameter *test-file*
19 (merge-pathnames #p"test-data.tmp" *test-path*))
21 (eval-when (:load-toplevel) (ensure-directories-exist *test-path* :verbose t))
23 ;;; Non-destructive functional analog of REMF
24 (defun remove-key (key list)
25 (loop for (current-key val . rest) on list by #'cddr
26 until (eql current-key key)
27 collect current-key into result
28 collect val into result
29 finally (return (nconc result rest))))
31 (defun create-test-file (&key (filename *test-file*) (content *dumb-string*))
32 (with-open-file (s filename :direction :output
33 :if-does-not-exist :create
34 :if-exists :supersede)
35 (write-sequence content s)))
37 (defun remove-test-file (&key (filename *test-file*))
38 (delete-file filename))
40 (defmacro with-test-file ((stream file &rest open-arguments
41 &key (delete-afterwards t)
45 (setq open-arguments (remove-key :delete-afterwards open-arguments))
46 (setq open-arguments (remove-key :initial-content open-arguments))
48 (let ((create-file-stream (gensym)))
50 (with-open-file (,create-file-stream ,file :direction :output
52 :if-does-not-exist :create)
53 (write-sequence ,initial-content ,create-file-stream))
55 (with-open-file (,stream ,file ,@open-arguments)
57 ,(when delete-afterwards `(ignore-errors (delete-file ,file))))))
59 (with-open-file (,stream ,file ,@open-arguments)
61 ,(when delete-afterwards `(ignore-errors (delete-file ,file))))))
63 (deftest create-file-1
64 ;; Create a file-simple-stream, write data.
66 (with-open-stream (s (make-instance 'file-simple-stream
70 :if-does-not-exist :create))
71 (string= (write-string *dumb-string* s) *dumb-string*))
72 (delete-file *test-file*))
75 (deftest create-file-2
76 ;; Create a file-simple-stream via :class argument to open, write data.
77 (with-test-file (s *test-file* :class 'file-simple-stream
78 :direction :output :if-exists :overwrite
79 :if-does-not-exist :create)
80 (string= (write-string *dumb-string* s) *dumb-string*))
83 (deftest create-read-file-1
84 ;; Via file-simple-stream objects, write and then re-read data.
86 (with-test-file (s *test-file* :class 'file-simple-stream
87 :direction :output :if-exists :overwrite
88 :if-does-not-exist :create :delete-afterwards nil)
89 (write-line *dumb-string* s)
90 (setf result (and result (string= (write-string *dumb-string* s)
93 (with-test-file (s *test-file* :class 'file-simple-stream
94 :direction :input :if-does-not-exist :error)
96 (multiple-value-bind (string missing-newline-p)
98 (setf result (and result (string= string *dumb-string*)
99 (not missing-newline-p))))
101 (multiple-value-bind (string missing-newline-p)
103 (setf result (and result (string= string *dumb-string*)
104 missing-newline-p))))
108 (deftest create-read-mapped-file-1
109 ;; Read data via a mapped-file-simple-stream object.
111 (with-test-file (s *test-file* :class 'mapped-file-simple-stream
112 :direction :input :if-does-not-exist :error
113 :initial-content *dumb-string*)
114 (setf result (and result (string= (read-line s) *dumb-string*))))
118 (deftest write-read-inet
120 (with-open-stream (s (make-instance 'socket-simple-stream
121 :remote-host #(127 0 0 1)
124 (string= (prog1 (write-line "Got it!" s) (finish-output s))
126 ;; Fail gracefully if echo isn't activated on the system
127 (sb-bsd-sockets::connection-refused-error () t))
130 (deftest write-read-large-sc-1
131 ;; Do write and read with more data than the buffer will hold
132 ;; (single-channel simple-stream)
133 (let* ((stream (make-instance 'file-simple-stream
134 :filename *test-file* :direction :output
135 :if-exists :overwrite
136 :if-does-not-exist :create))
137 (content (make-string (1+ (device-buffer-length stream))
138 :initial-element #\x)))
139 (with-open-stream (s stream)
140 (write-string content s))
141 (with-test-file (s *test-file* :class 'file-simple-stream
142 :direction :input :if-does-not-exist :error)
143 (string= content (read-line s))))
146 (deftest write-read-large-sc-2
147 (let* ((stream (make-instance 'file-simple-stream
148 :filename *test-file* :direction :output
149 :if-exists :overwrite
150 :if-does-not-exist :create))
151 (length (1+ (* 3 (device-buffer-length stream))))
152 (content (make-string length)))
153 (dotimes (i (length content))
154 (setf (aref content i) (code-char (random 256))))
155 (with-open-stream (s stream)
156 (write-string content s))
157 (with-test-file (s *test-file* :class 'file-simple-stream
158 :direction :input :if-does-not-exist :error)
159 (let ((seq (make-string length)))
160 #+nil (read-sequence seq s)
161 #-nil (dotimes (i length)
162 (setf (char seq i) (read-char s)))
163 (string= content seq))))
166 (deftest write-read-large-sc-3
167 (let* ((stream (make-instance 'file-simple-stream
168 :filename *test-file* :direction :output
169 :if-exists :overwrite
170 :if-does-not-exist :create))
171 (length (1+ (* 3 (device-buffer-length stream))))
172 (content (make-array length :element-type '(unsigned-byte 8))))
173 (dotimes (i (length content))
174 (setf (aref content i) (random 256)))
175 (with-open-stream (s stream)
176 (write-sequence content s))
177 (with-test-file (s *test-file* :class 'file-simple-stream
178 :direction :input :if-does-not-exist :error)
179 (let ((seq (make-array length :element-type '(unsigned-byte 8))))
180 #+nil (read-sequence seq s)
181 #-nil (dotimes (i length)
182 (setf (aref seq i) (read-byte s)))
183 (equalp content seq))))
186 (deftest write-read-large-dc-1
187 ;; Do write and read with more data than the buffer will hold
188 ;; (dual-channel simple-stream; we only have socket streams atm)
190 (let* ((stream (make-instance 'socket-simple-stream
191 :remote-host #(127 0 0 1)
194 (content (make-string (1+ (device-buffer-length stream))
195 :initial-element #\x)))
196 (with-open-stream (s stream)
197 (string= (prog1 (write-line content s) (finish-output s))
199 ;; Fail gracefully if echo isn't activated on the system
200 (sb-bsd-sockets::connection-refused-error () t))
204 (deftest file-position-1
205 ;; Test reading of file-position
206 (with-test-file (s *test-file* :class 'file-simple-stream :direction :input
207 :initial-content *dumb-string*)
211 (deftest file-position-2
212 ;; Test reading of file-position
213 (with-test-file (s *test-file* :class 'file-simple-stream :direction :input
214 :initial-content *dumb-string*)
219 (deftest file-position-3
220 ;; Test reading of file-position in the presence of unsaved data
221 (with-test-file (s *test-file* :class 'file-simple-stream
222 :direction :output :if-exists :supersede
223 :if-does-not-exist :create)
228 (deftest file-position-4
229 ;; Test reading of file-position in the presence of unsaved data and
231 (with-test-file (s *test-file* :class 'file-simple-stream :direction :io
232 :if-exists :overwrite :if-does-not-exist :create
233 :initial-content *dumb-string*)
234 (read-byte s) ; fill buffer
235 (write-byte 50 s) ; advance file-position
239 (deftest file-position-5
240 ;; Test file position when opening with :if-exists :append
241 (with-test-file (s *test-file* :class 'file-simple-stream :direction :io
242 :if-exists :append :if-does-not-exist :create
243 :initial-content *dumb-string*)
244 (= (file-length s) (file-position s)))
247 (deftest write-read-unflushed-sc-1
248 ;; Write something into a single-channel stream and read it back
249 ;; without explicitly flushing the buffer in-between
250 (with-test-file (s *test-file* :class 'file-simple-stream :direction :io
251 :if-does-not-exist :create :if-exists :supersede)
253 (file-position s :start)
257 (deftest write-read-unflushed-sc-2
258 ;; Write something into a single-channel stream, try to read back too much
260 (with-test-file (s *test-file* :class 'file-simple-stream
261 :direction :io :if-does-not-exist :create
262 :if-exists :supersede)
264 (file-position s :start)
271 (deftest write-read-unflushed-sc-3
272 ;; Test writing in a buffer filled with previous file contents
274 (with-test-file (s *test-file* :class 'file-simple-stream :direction :io
275 :if-exists :overwrite :if-does-not-exist :create
276 :initial-content *dumb-string*)
277 (setq result (and result (char= (read-char s) (schar *dumb-string* 0))))
278 (setq result (and result (= (file-position s) 1)))
279 (let ((pos (file-position s)))
281 (file-position s pos)
282 (setq result (and result (char= (read-char s) #\x)))))
286 (deftest write-read-unflushed-sc-4
287 ;; Test flushing of buffers
289 (with-test-file (s *test-file* :class 'file-simple-stream :direction :io
290 :if-exists :overwrite :if-does-not-exist :create
291 :initial-content "Foo"
292 :delete-afterwards nil)
293 (read-char s) ; Fill the buffer.
294 (file-position s :start) ; Change existing data.
296 (file-position s :end) ; Extend file.
298 (with-test-file (s *test-file* :class 'file-simple-stream
299 :direction :input :if-does-not-exist :error)
304 (deftest write-read-append-sc-1
305 ;; Test writing in the middle of a stream opened in append mode
307 (with-test-file (s *test-file* :class 'file-simple-stream :direction :io
308 :if-exists :append :if-does-not-exist :create
309 :initial-content "Foo"
310 :delete-afterwards nil)
311 (file-position s :start) ; Jump to beginning.
313 (file-position s :end) ; Extend file.
315 (with-test-file (s *test-file* :class 'file-simple-stream
316 :direction :input :if-does-not-exist :error)
321 (deftest write-read-mixed-sc-1
322 ;; Test read/write-sequence of types string and (unsigned-byte 8)
323 (let ((uvector (make-array '(10) :element-type '(unsigned-byte 8)
324 :initial-element 64))
325 (svector (make-array '(10) :element-type '(signed-byte 8)
326 :initial-element -1))
327 (result-uvector (make-array '(10) :element-type '(unsigned-byte 8)
329 (result-svector (make-array '(10) :element-type '(signed-byte 8)
331 (result-string (make-string (length *dumb-string*)
332 :initial-element #\Space)))
333 (with-test-file (s *test-file* :class 'file-simple-stream :direction :io
334 :if-exists :overwrite :if-does-not-exist :create
335 :delete-afterwards nil)
336 (write-sequence svector s)
337 (write-sequence uvector s)
338 (write-sequence *dumb-string* s))
339 (with-test-file (s *test-file* :class 'file-simple-stream
340 :direction :input :if-does-not-exist :error
341 :delete-afterwards nil)
342 (read-sequence result-svector s)
343 (read-sequence result-uvector s)
344 (read-sequence result-string s))
345 (and (string= *dumb-string* result-string)
346 (equalp uvector result-uvector)
347 (equalp svector result-svector)))