~ chicken-core (master) /tests/port-tests.scm


  1(import chicken.condition chicken.file chicken.file.posix
  2	chicken.flonum chicken.format chicken.io chicken.port
  3        chicken.bytevector
  4	chicken.process chicken.process.signal chicken.tcp chicken.number-vector)
  5
  6(import (only (scheme base) input-port-open? output-port-open? open-input-string
  7              write-string open-output-string get-output-string))
  8
  9(include "test.scm")
 10(test-begin "ports")
 11
 12(define-syntax assert-error
 13  (syntax-rules ()
 14    ((_ expr)
 15     (assert (handle-exceptions _ #t expr #f)))))
 16
 17(define *text* #<<EOF
 18this is a test
 19<foof> #;33> (let ((in (open-input-string ""))) (close-input-port in)
 20       (read-char in)) [09:40]
 21<foof> Error: (read-char) port already closed: #<input port "(string)">
 22<foof> #;33> (let ((in (open-input-string ""))) (close-input-port in)
 23       (read-line in))
 24<foof> Error: call of non-procedure: #t
 25<foof> ... that's a little odd
 26<Bunny351> yuck. [09:44]
 27<Bunny351> double yuck. [10:00]
 28<sjamaan> yuck squared! [10:01]
 29<Bunny351> yuck powered by yuck
 30<Bunny351> (to the power of yuck, of course) [10:02]
 31<pbusser3> My yuck is bigger than yours!!!
 32<foof> yuck!
 33<foof> (that's a factorial)
 34<sjamaan> heh
 35<sjamaan> I think you outyucked us all [10:03]
 36<foof> well, for large enough values of yuck, yuck! ~= yuck^yuck [10:04]
 37ERC>
 38EOF
 39)
 40
 41(define p (open-input-string *text*))
 42
 43(assert (string=? "this is a test" (read-line p)))
 44
 45(assert
 46 (string=?
 47  "<foof> #;33> (let ((in (open-input-string \"\"))) (close-input-port in)"
 48  (read-line p)))
 49(assert (= 20 (length (read-lines (open-input-string *text*)))))
 50
 51(assert (char-ready? (open-input-string "")))
 52
 53(let ((out (open-output-string)))
 54  (test-equal "Initially, output string is empty"
 55              (get-output-string out) "")
 56  (display "foo" out)
 57  (test-equal "output can be extracted from output string"
 58              (get-output-string out) "foo")
 59  (close-output-port out)
 60  (test-equal "closing a string output port has no effect on the returned data"
 61              (get-output-string out) "foo")
 62  (test-error "writing to a closed string output port is an error"
 63              (display "bar" out)))
 64
 65;;; copy-port
 66
 67(assert
 68 (string=?
 69  *text*
 70  (with-output-to-string
 71    (lambda ()
 72      (copy-port (open-input-string *text*) (current-output-port)))))) ; read-char -> write-char
 73
 74(assert
 75 (equal?
 76  '(3 2 1)
 77  (let ((out '()))
 78    (copy-port				; read -> custom
 79     (open-input-string "1 2 3")
 80     #f
 81     read
 82     (lambda (x port) (set! out (cons x out))))
 83    out)))
 84
 85(assert
 86 (equal?
 87  "abc"
 88  (let ((out (open-output-string)))
 89    (copy-port				; read-char -> custom
 90     (open-input-string "abc")
 91     out
 92     read-char
 93     (lambda (x out) (write-char x out)))
 94    (get-output-string out))))
 95
 96(assert
 97 (equal?
 98  "abc"
 99  (let ((in (open-input-string "abc") )
100	(out (open-output-string)))
101    (copy-port				; custom -> write-char
102     in out
103     (lambda (in) (read-char in)))
104    (get-output-string out))))
105
106;; {input,output}-port-open?
107
108(assert (input-port-open? (open-input-string "abc")))
109(assert (output-port-open? (open-output-string)))
110(assert-error (input-port-open? (open-output-string)))
111(assert-error (output-port-open? (open-input-string "abc")))
112
113;; direction-specific port closure
114
115(let* ((n 0)
116       (p (make-input-port (constantly #\a)
117			   (constantly #t)
118			   (lambda () (set! n (add1 n))))))
119  (close-output-port p)
120  (assert (input-port-open? p))
121  (assert (= n 0))
122  (close-input-port p)
123  (assert (not (input-port-open? p)))
124  (assert (= n 1))
125  (close-input-port p)
126  (assert (not (input-port-open? p)))
127  (assert (= n 1)))
128
129(let* ((n 0)
130       (p (make-output-port (lambda () (display #\a))
131			    (lambda () (set! n (add1 n))))))
132  (close-input-port p)
133  (assert (output-port-open? p))
134  (assert (= n 0))
135  (close-output-port p)
136  (assert (not (output-port-open? p)))
137  (assert (= n 1))
138  (close-output-port p)
139  (assert (not (output-port-open? p)))
140  (assert (= n 1)))
141
142;; bidirectional ports
143
144(let* ((b (string))
145       (w (lambda (s)
146	    (set! b (string-append b s))))
147       (e (lambda ()
148	    (positive? (string-length b))))
149       (r (lambda ()
150	    (let ((s b))
151	      (set! b (substring s 1))
152	      (string-ref s 0))))
153       (i (make-input-port r e void))
154       (o (make-output-port w void))
155       (p (make-bidirectional-port i o)))
156  (assert (input-port? p))
157  (assert (output-port? p))
158  (assert (input-port-open? p))
159  (assert (output-port-open? p))
160  (display "quartz ruby" p)
161  (newline p)
162  (assert (equal? (read p) 'quartz))
163  (assert (equal? (read i) 'ruby))
164  (display "emerald topaz" p)
165  (newline p)
166  (close-output-port p)
167  (assert (not (output-port-open? o)))
168  (assert (not (output-port-open? p)))
169  (assert (equal? (read p) 'emerald))
170  (assert (equal? (read i) 'topaz))
171  (close-input-port p)
172  (assert (not (input-port-open? i)))
173  (assert (not (input-port-open? p))))
174
175;; fill buffers
176(with-input-from-file "compiler.scm" read-string)
177
178(print "slow...")
179(time
180 (with-input-from-file "compiler.scm"
181   (lambda ()
182     (with-output-to-file "compiler.scm.2"
183       (lambda ()
184	 (copy-port
185	  (current-input-port) (current-output-port)
186	  (lambda (port) (read-char port))
187	  (lambda (x port) (write-char x port))))))))
188
189(print "fast...")
190(time
191 (with-input-from-file "compiler.scm"
192   (lambda ()
193     (with-output-to-file "compiler.scm.2"
194       (lambda ()
195	 (copy-port (current-input-port) (current-output-port)))))))
196
197(delete-file "compiler.scm.2")
198
199(define-syntax check
200  (syntax-rules ()
201    ((_ (expr-head expr-rest ...))
202     (check 'expr-head (expr-head expr-rest ...)))
203    ((_ name expr)
204     (let ((okay (list 'okay)))
205       (assert
206        (eq? okay
207             (condition-case
208                 (begin (print* name "...")
209                        (flush-output)
210                        (let ((output expr))
211                          (printf "FAIL [ ~S ]\n" output)))
212               ((exn i/o file) (printf "OK\n") okay))))))))
213
214(cond-expand
215  ((not mingw)
216
217   (define proc (process-fork (lambda () (tcp-accept (tcp-listen 8080)))))
218
219   (on-exit (lambda () (handle-exceptions exn #f (process-signal proc))))
220
221   (print "\n\nProcedures check on TCP ports being closed\n")
222
223   (receive (in out)
224       (let lp ()
225	 (condition-case (tcp-connect "localhost" 8080)
226	   ((exn i/o net) (lp))))
227     (close-output-port out)
228     (close-input-port in)
229     (check (tcp-addresses in))
230     (check (tcp-port-numbers in))
231     (check (tcp-abandon-port in)))	; Not sure about abandon-port
232
233
234   ;; This tests for two bugs which occurred on NetBSD and possibly
235   ;; other platforms, possibly due to multiprocessing:
236   ;; read-line with EINTR would loop endlessly and process-wait would
237   ;; signal a condition when interrupted rather than retrying.
238   (set-signal-handler! signal/chld void) ; Should be a noop but triggers EINTR
239   (receive (in out)
240     (create-pipe)
241     (receive (pid ok? status)
242       (process-wait
243        (process-fork
244         (lambda ()
245           (file-close in)              ; close receiving end
246           (with-output-to-port (open-output-file* out)
247             (lambda ()
248               (display "hello, world\n")
249               ;; exit prevents buffers from being discarded by implicit _exit
250               (exit 0))))))
251       (file-close out)                 ; close sending end
252       (assert (equal? '(#t 0 ("hello, world"))
253                       (list ok? status (read-lines (open-input-file* in)))))))
254   )
255  (else))
256
257(print "\n\nProcedures check on output ports being closed\n")
258
259(with-output-to-file "empty-file" void)
260
261(call-with-output-file "empty-file"
262  (lambda (out)
263    (close-output-port out)
264    (check (write '(foo) out))
265    (check (fprintf out "blabla"))
266    (check "print-call-chain" (begin (print-call-chain out) (void)))
267    (check (print-error-message (make-property-condition 'exn 'message "foo") out))
268    (check "print" (with-output-to-port out
269		     (lambda () (print "foo"))))
270    (check "print*" (with-output-to-port out
271		      (lambda () (print* "foo"))))
272    (check (display "foo" out))
273    (check (terminal-port? out))   ; Calls isatty() on C_SCHEME_FALSE?
274    (check (newline out))
275    (check (write-char #\x out))
276    (check (write-line "foo" out))
277    (check (write-bytevector '#u8(1 2 3) out))
278    ;;(check (port->fileno in))
279    (check (flush-output out))
280
281    (check (write-byte 120 out))
282    (check (write-string "foo" out))))
283
284
285(print "\n\nProcedures check on input ports being closed\n")
286(call-with-input-file "empty-file"
287  (lambda (in)
288    (close-input-port in)
289    (check (read in))
290    (check (read-char in))
291    (check (char-ready? in))
292    (check (peek-char in))
293    ;;(check (port->fileno in))
294    (check (terminal-port? in))	   ; Calls isatty() on C_SCHEME_FALSE?
295    (check (read-line in 5))
296    (check (read-bytevector 5 in))
297    (check "read-bytevector!" (let ((dest (make-u8vector 5)))
298                              (read-bytevector! dest in 0 5)))
299
300    (check (read-byte in))
301    (check (read-token (constantly #t) in))
302    (check (read-string 10 in))
303    (check "read-string!" (let ((buf (make-string 10)))
304                            (read-string! 10 buf in) buf))))
305
306(print "\nEmbedded NUL bytes in filenames are rejected\n")
307(assert-error (with-output-to-file "embedded\x00;null-byte" void))
308
309;;; #978 -- port-position checks for read-line
310
311(define (read-line/pos p limit)  ;; common
312  (let ((s (read-line p limit)))
313    (let-values (((row col) (port-position p)))
314      (list s row col))))
315
316(define (read-string-line/pos str limit)
317  (read-line/pos (open-input-string str) limit))
318
319(define (read-process-line/pos cmd args limit)
320  (let-values (((i o pid) (process cmd args)))
321    (let ((rc (read-line/pos i limit)))
322      (close-input-port i)
323      (close-output-port o)
324      rc)))
325(define (read-echo-line/pos str limit)
326  (read-process-line/pos "echo" (list "-n" str) limit))
327
328(define (test-port-position proc)
329  (test-equal "advance row when encountering delim"
330	      (proc "abcde\nfghi" 6)
331	      '("abcde" 2 0))
332  (test-equal "reaching limit sets col to limit, and does not advance row"
333	      (proc "abcdefghi" 6)
334	      '("abcdef" 1 6))
335  (test-equal "delimiter counted in limit" ;; observed behavior, strange
336	      (proc "abcdef\nghi" 6)
337	      '("abcdef" 1 6))
338  (test-equal "EOF reached"
339	      (proc "abcde" 6)
340	      '("abcde" 1 5)))
341
342(test-group
343 "read-line string port position tests"
344 (test-port-position read-string-line/pos))
345
346(test-group "read-string!"
347  (let ((in (open-input-string "1234567890"))
348        (buf (make-string 5)))
349    (test-equal "peek-char won't influence the result of read-string!"
350                (peek-char in)
351                #\1)
352    (test-equal "read-string! won't read past buffer if given #f"
353                (read-string! #f buf in)
354                5)
355    (test-equal "read-string! reads the requested bytes with #f"
356                buf
357                "12345")
358    (test-equal "read-string! won't read past buffer if given #f and offset"
359                (read-string! #f buf in 3)
360                2)
361    (test-equal "read-string! reads the requested bytes with #f and offset"
362                buf
363                "12367")
364    (test-equal "read-string! reads until the end correctly"
365                (read-string! #f buf in)
366                3)
367    (test-equal "read-string! leaves the buffer's tail intact"
368                buf
369                "89067")
370    (test-equal "after peek-char at EOF, read-string! doesn't mutate the buffer"
371                (begin (peek-char in)
372                       (read-string! #f buf in)
373                       buf)
374                "89067"))
375  (let ((in (open-input-string "1234567890"))
376        (buf (make-string 5)))
377    (test-equal "read-string! won't read past buffer if given size"
378                (read-string! 10 buf in)
379                5)
380    (test-equal "read-string! reads the requested bytes with buffer size"
381                buf
382                "12345")
383    (test-equal "read-string! won't read past buffer if given size and offset"
384                (read-string! 10 buf in 3)
385                2)
386    (test-equal "read-string! reads the requested bytes with buffer size and offset"
387                buf
388                "12367")
389    (test-equal "read-string! reads until the end correctly with buffer size"
390                (read-string! 10 buf in)
391                3)
392    (test-equal "read-string! leaves the buffer's tail intact"
393                buf
394                "89067")
395    (test-equal "read-string! at EOF reads nothing"
396                (read-string! 10 buf in)
397                0)
398    (test-equal "read-string! at EOF doesn't mutate the buffer"
399                buf
400                "89067")))
401
402(test-group "line endings"
403  (let ((s "foo\nbar\rbaz\r\nqux")
404	(f (lambda ()
405	     (test-equal "\\n" (read-line) "foo")
406	     (test-equal "\\r" (read-line) "bar")
407	     (test-equal "\\r\\n" (read-line) "baz")
408	     (test-equal "eof" (read-line) "qux"))))
409    (test-group "string port"
410      (with-input-from-string s f))
411    (test-group "file port"
412      (let ((file "mixed-line-endings"))
413	(with-output-to-file file (lambda () (display s)))
414	(with-input-from-file file f)
415	(delete-file* file)))
416    (test-group "custom port"
417      (let* ((p (open-input-string s))
418	     (p* (make-input-port (lambda () (read-char p))
419				  (lambda () (char-ready? p))
420				  (lambda () (close-input-port p)))))
421	(with-input-from-port p* f)))))
422
423;; Disabled because it requires `echo -n` for
424;; the EOF test, and that is not available on all systems.
425;; Uncomment locally to run.
426#;
427(test-group
428 "read-line process port position tests"
429 (test-port-position read-echo-line/pos))
430
431;; bytevector I/O, moved here from srf-4-tests.scm:
432;; Ticket #1124: read-u8vector! w/o length, dest smaller than source.
433(test-group
434 "bytevector I/O"
435(let ((input (open-input-string "abcdefghijklmnopqrstuvwxyz"))
436      (u8vec (make-bytevector 10)))
437  (assert (= 10 (read-bytevector! u8vec input)))
438  (assert (equal? u8vec #u8(97 98 99 100 101 102 103 104 105 106)))
439  (assert (= 5  (read-bytevector! u8vec input 5)))
440  (assert (equal? u8vec #u8(97 98 99 100 101 107 108 109 110 111)))
441  (assert (= 5  (read-bytevector! u8vec input 0 5)))
442  (assert (equal? u8vec #u8(112 113 114 115 116 107 108 109 110 111)))
443  (assert (= 6  (read-bytevector! u8vec input 0 10)))
444  (assert (equal? u8vec #u8(117 118 119 120 121 122 108 109 110 111))))
445
446(let ((input (open-input-string "abcdefghijklmnopqrs")))
447  (assert (equal? (read-bytevector 5 input)
448		  #u8(97 98 99 100 101)))
449  (assert (equal? (read-bytevector 5 input) #u8(102 103 104 105 106)))
450  (assert (equal? (read-bytevector #f input)
451		  #u8(107 108 109 110 111 112 113 114 115)))
452  (with-input-from-string "abcdefghijklmnopqrs"
453   (lambda ()
454     (assert (equal? (read-bytevector 5)
455		     #u8(97 98 99 100 101)))
456     (assert (equal? (read-bytevector 5) #u8(102 103 104 105 106)))
457     (assert (equal? (read-bytevector)
458		     #u8(107 108 109 110 111 112 113 114 115))))))
459
460(assert (string=?
461	 "abc"
462	 (with-output-to-string
463	   (lambda ()
464	     (write-bytevector #u8(97 98 99))))))
465
466(assert (string=?
467	 "bc"
468	 (with-output-to-string
469	   (lambda ()
470	     (write-bytevector #u8(97 98 99) (current-output-port) 1)))))
471
472(assert (string=?
473	 "a"
474	 (with-output-to-string
475	   (lambda ()
476	     (write-bytevector #u8(97 98 99) (current-output-port) 0 1)))))
477
478(assert (string=?
479	 "b"
480	 (with-output-to-string
481	   (lambda ()
482	     (write-bytevector #u8(97 98 99) (current-output-port) 1 2)))))
483
484(assert (string=?
485	 ""
486	 (with-output-to-string
487	   (lambda ()
488	     (write-bytevector #u8())))))
489)
490
491;;;
492
493(test-end)
494
495(test-exit)
Trap