r6rs-ports.test 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004
  1. ;;;; r6rs-ports.test --- R6RS I/O port tests. -*- coding: utf-8; -*-
  2. ;;;;
  3. ;;;; Copyright (C) 2009-2012, 2013-2015 Free Software Foundation, Inc.
  4. ;;;; Ludovic Courtès
  5. ;;;;
  6. ;;;; This library is free software; you can redistribute it and/or
  7. ;;;; modify it under the terms of the GNU Lesser General Public
  8. ;;;; License as published by the Free Software Foundation; either
  9. ;;;; version 3 of the License, or (at your option) any later version.
  10. ;;;;
  11. ;;;; This library is distributed in the hope that it will be useful,
  12. ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. ;;;; Lesser General Public License for more details.
  15. ;;;;
  16. ;;;; You should have received a copy of the GNU Lesser General Public
  17. ;;;; License along with this library; if not, write to the Free Software
  18. ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  19. (define-module (test-io-ports)
  20. #:use-module (test-suite lib)
  21. #:use-module (test-suite guile-test)
  22. #:use-module (srfi srfi-1)
  23. #:use-module (srfi srfi-11)
  24. #:use-module (ice-9 match)
  25. #:use-module (rnrs io ports)
  26. #:use-module (rnrs io simple)
  27. #:use-module (rnrs exceptions)
  28. #:use-module (rnrs bytevectors))
  29. (define-syntax pass-if-condition
  30. (syntax-rules ()
  31. ((_ name predicate body0 body ...)
  32. (let ((cookie (list 'cookie)))
  33. (pass-if name
  34. (eq? cookie (guard (c ((predicate c) cookie))
  35. body0 body ...)))))))
  36. (define (test-file)
  37. (data-file-name "ports-test.tmp"))
  38. ;; A input/output port that swallows all output, and produces just
  39. ;; spaces on input. Reading and writing beyond `failure-position'
  40. ;; produces `system-error' exceptions. Used for testing exception
  41. ;; behavior.
  42. (define* (make-failing-port #:optional (failure-position 0))
  43. (define (maybe-fail index errno)
  44. (if (> index failure-position)
  45. (scm-error 'system-error
  46. 'failing-port
  47. "I/O beyond failure position" '()
  48. (list errno))))
  49. (let ((read-index 0)
  50. (write-index 0))
  51. (define (write-char chr)
  52. (set! write-index (+ 1 write-index))
  53. (maybe-fail write-index ENOSPC))
  54. (make-soft-port
  55. (vector write-char
  56. (lambda (str) ;; write-string
  57. (for-each write-char (string->list str)))
  58. (lambda () #t) ;; flush-output
  59. (lambda () ;; read-char
  60. (set! read-index (+ read-index 1))
  61. (maybe-fail read-index EIO)
  62. #\space)
  63. (lambda () #t)) ;; close-port
  64. "rw")))
  65. (define (call-with-bytevector-output-port/transcoded transcoder receiver)
  66. (call-with-bytevector-output-port
  67. (lambda (bv-port)
  68. (call-with-port (transcoded-port bv-port transcoder)
  69. receiver))))
  70. (with-test-prefix "7.2.5 End-of-File Object"
  71. (pass-if "eof-object"
  72. (and (eqv? (eof-object) (eof-object))
  73. (eq? (eof-object) (eof-object))))
  74. (pass-if "port-eof?"
  75. (port-eof? (open-input-string ""))))
  76. (with-test-prefix "7.2.8 Binary Input"
  77. (pass-if "get-u8"
  78. (let ((port (open-input-string "A")))
  79. (and (= (char->integer #\A) (get-u8 port))
  80. (eof-object? (get-u8 port)))))
  81. (pass-if "lookahead-u8"
  82. (let ((port (open-input-string "A")))
  83. (and (= (char->integer #\A) (lookahead-u8 port))
  84. (= (char->integer #\A) (lookahead-u8 port))
  85. (= (char->integer #\A) (get-u8 port))
  86. (eof-object? (get-u8 port)))))
  87. (pass-if "lookahead-u8 non-ASCII"
  88. (let ((port (open-input-string "λ")))
  89. (and (= 206 (lookahead-u8 port))
  90. (= 206 (lookahead-u8 port))
  91. (= 206 (get-u8 port))
  92. (= 187 (lookahead-u8 port))
  93. (= 187 (lookahead-u8 port))
  94. (= 187 (get-u8 port))
  95. (eof-object? (lookahead-u8 port))
  96. (eof-object? (get-u8 port)))))
  97. (pass-if "lookahead-u8: result is unsigned"
  98. ;; Bug #31081.
  99. (let ((port (open-bytevector-input-port #vu8(255))))
  100. (= (lookahead-u8 port) 255)))
  101. (pass-if "get-bytevector-n [short]"
  102. (let* ((port (open-input-string "GNU Guile"))
  103. (bv (get-bytevector-n port 4)))
  104. (and (bytevector? bv)
  105. (equal? (bytevector->u8-list bv)
  106. (map char->integer (string->list "GNU "))))))
  107. (pass-if "get-bytevector-n [long]"
  108. (let* ((port (open-input-string "GNU Guile"))
  109. (bv (get-bytevector-n port 256)))
  110. (and (bytevector? bv)
  111. (equal? (bytevector->u8-list bv)
  112. (map char->integer (string->list "GNU Guile"))))))
  113. (pass-if-exception "get-bytevector-n with closed port"
  114. exception:wrong-type-arg
  115. (let ((port (%make-void-port "r")))
  116. (close-port port)
  117. (get-bytevector-n port 3)))
  118. (let ((expected (make-bytevector 20 (char->integer #\a))))
  119. (pass-if-equal "http://bugs.gnu.org/17466"
  120. ;; <http://bugs.gnu.org/17466> is about a memory corruption
  121. ;; whereas bytevector shrunk in 'get-bytevector-n' would keep
  122. ;; referring to the previous (larger) bytevector.
  123. expected
  124. (let loop ((count 50))
  125. (if (zero? count)
  126. expected
  127. (let ((bv (call-with-input-string "aaaaaaaaaaaaaaaaaaaa"
  128. (lambda (port)
  129. (get-bytevector-n port 4096)))))
  130. ;; Cause the 4 KiB bytevector initially created by
  131. ;; 'get-bytevector-n' to be reclaimed.
  132. (make-bytevector 4096)
  133. (if (equal? bv expected)
  134. (loop (- count 1))
  135. bv))))))
  136. (pass-if "get-bytevector-n! [short]"
  137. (let* ((port (open-input-string "GNU Guile"))
  138. (bv (make-bytevector 4))
  139. (read (get-bytevector-n! port bv 0 4)))
  140. (and (equal? read 4)
  141. (equal? (bytevector->u8-list bv)
  142. (map char->integer (string->list "GNU "))))))
  143. (pass-if "get-bytevector-n! [long]"
  144. (let* ((str "GNU Guile")
  145. (port (open-input-string str))
  146. (bv (make-bytevector 256))
  147. (read (get-bytevector-n! port bv 0 256)))
  148. (and (equal? read (string-length str))
  149. (equal? (map (lambda (i)
  150. (bytevector-u8-ref bv i))
  151. (iota read))
  152. (map char->integer (string->list str))))))
  153. (pass-if "get-bytevector-some [simple]"
  154. (let* ((str "GNU Guile")
  155. (port (open-input-string str))
  156. (bv (get-bytevector-some port)))
  157. (and (bytevector? bv)
  158. (equal? (bytevector->u8-list bv)
  159. (map char->integer (string->list str))))))
  160. (pass-if "get-bytevector-all"
  161. (let* ((str "GNU Guile")
  162. (index 0)
  163. (port (make-soft-port
  164. (vector #f #f #f
  165. (lambda ()
  166. (if (>= index (string-length str))
  167. (eof-object)
  168. (let ((c (string-ref str index)))
  169. (set! index (+ index 1))
  170. c)))
  171. (lambda () #t)
  172. (let ((cont? #f))
  173. (lambda ()
  174. ;; Number of readily available octets: falls to
  175. ;; zero after 4 octets have been read and then
  176. ;; starts again.
  177. (let ((a (if cont?
  178. (- (string-length str) index)
  179. (- 4 (modulo index 5)))))
  180. (if (= 0 a) (set! cont? #t))
  181. a))))
  182. "r"))
  183. (bv (get-bytevector-all port)))
  184. (and (bytevector? bv)
  185. (= index (string-length str))
  186. (= (bytevector-length bv) (string-length str))
  187. (equal? (bytevector->u8-list bv)
  188. (map char->integer (string->list str)))))))
  189. (define (make-soft-output-port)
  190. (let* ((bv (make-bytevector 1024))
  191. (read-index 0)
  192. (write-index 0)
  193. (write-char (lambda (chr)
  194. (bytevector-u8-set! bv write-index
  195. (char->integer chr))
  196. (set! write-index (+ 1 write-index)))))
  197. (make-soft-port
  198. (vector write-char
  199. (lambda (str) ;; write-string
  200. (for-each write-char (string->list str)))
  201. (lambda () #t) ;; flush-output
  202. (lambda () ;; read-char
  203. (if (>= read-index (bytevector-length bv))
  204. (eof-object)
  205. (let ((c (bytevector-u8-ref bv read-index)))
  206. (set! read-index (+ read-index 1))
  207. (integer->char c))))
  208. (lambda () #t)) ;; close-port
  209. "rw")))
  210. (with-test-prefix "7.2.11 Binary Output"
  211. (pass-if "put-u8"
  212. (let ((port (make-soft-output-port)))
  213. (put-u8 port 77)
  214. (equal? (get-u8 port) 77)))
  215. ;; Note: The `put-bytevector' tests below require a Latin-1 locale so
  216. ;; that the `scm_from_locale_stringn' call in `sf_write' will let all
  217. ;; the bytes through, unmodified. This is hacky, but we can't use
  218. ;; "custom binary output ports" here because they're only tested
  219. ;; later.
  220. (pass-if "put-bytevector [2 args]"
  221. (with-latin1-locale
  222. (let ((port (make-soft-output-port))
  223. (bv (make-bytevector 256)))
  224. (put-bytevector port bv)
  225. (equal? (bytevector->u8-list bv)
  226. (bytevector->u8-list
  227. (get-bytevector-n port (bytevector-length bv)))))))
  228. (pass-if "put-bytevector [3 args]"
  229. (with-latin1-locale
  230. (let ((port (make-soft-output-port))
  231. (bv (make-bytevector 256))
  232. (start 10))
  233. (put-bytevector port bv start)
  234. (equal? (drop (bytevector->u8-list bv) start)
  235. (bytevector->u8-list
  236. (get-bytevector-n port (- (bytevector-length bv) start)))))))
  237. (pass-if "put-bytevector [4 args]"
  238. (with-latin1-locale
  239. (let ((port (make-soft-output-port))
  240. (bv (make-bytevector 256))
  241. (start 10)
  242. (count 77))
  243. (put-bytevector port bv start count)
  244. (equal? (take (drop (bytevector->u8-list bv) start) count)
  245. (bytevector->u8-list
  246. (get-bytevector-n port count))))))
  247. (pass-if-exception "put-bytevector with closed port"
  248. exception:wrong-type-arg
  249. (let* ((bv (make-bytevector 4))
  250. (port (%make-void-port "w")))
  251. (close-port port)
  252. (put-bytevector port bv)))
  253. (pass-if "put-bytevector with UTF-16 string port"
  254. (let* ((str "hello, world")
  255. (bv (string->utf16 str)))
  256. (equal? str
  257. (call-with-output-string
  258. (lambda (port)
  259. (set-port-encoding! port "UTF-16BE")
  260. (put-bytevector port bv))))))
  261. (pass-if "put-bytevector with wrong-encoding string port"
  262. (let* ((str "hello, world")
  263. (bv (string->utf16 str)))
  264. (catch 'decoding-error
  265. (lambda ()
  266. (with-fluids ((%default-port-conversion-strategy 'error))
  267. (call-with-output-string
  268. (lambda (port)
  269. (set-port-encoding! port "UTF-32")
  270. (put-bytevector port bv)))
  271. #f)) ; fail if we reach this point
  272. (lambda (key subr message errno port)
  273. (string? (strerror errno)))))))
  274. (define (test-input-file-opener open filename)
  275. (let ((contents (string->utf8 "GNU λ")))
  276. ;; Create file
  277. (call-with-output-file filename
  278. (lambda (port) (put-bytevector port contents)))
  279. (pass-if "opens binary input port with correct contents"
  280. (with-fluids ((%default-port-encoding "UTF-8"))
  281. (call-with-port (open-file-input-port filename)
  282. (lambda (port)
  283. (and (binary-port? port)
  284. (input-port? port)
  285. (bytevector=? contents (get-bytevector-all port))))))))
  286. (delete-file filename))
  287. (with-test-prefix "7.2.7 Input Ports"
  288. (with-test-prefix "open-file-input-port"
  289. (test-input-file-opener open-file-input-port (test-file)))
  290. ;; This section appears here so that it can use the binary input
  291. ;; primitives.
  292. (pass-if "open-bytevector-input-port [1 arg]"
  293. (let* ((str "Hello Port!")
  294. (bv (u8-list->bytevector (map char->integer
  295. (string->list str))))
  296. (port (open-bytevector-input-port bv))
  297. (read-to-string
  298. (lambda (port)
  299. (let loop ((chr (read-char port))
  300. (result '()))
  301. (if (eof-object? chr)
  302. (apply string (reverse! result))
  303. (loop (read-char port)
  304. (cons chr result)))))))
  305. (equal? (read-to-string port) str)))
  306. (pass-if "bytevector-input-port is binary"
  307. (with-fluids ((%default-port-encoding "UTF-8"))
  308. (binary-port? (open-bytevector-input-port #vu8(1 2 3)))))
  309. (pass-if-exception "bytevector-input-port is read-only"
  310. exception:wrong-type-arg
  311. (let* ((str "Hello Port!")
  312. (bv (u8-list->bytevector (map char->integer
  313. (string->list str))))
  314. (port (open-bytevector-input-port bv #f)))
  315. (write "hello" port)))
  316. (pass-if "bytevector input port supports seeking"
  317. (let* ((str "Hello Port!")
  318. (bv (u8-list->bytevector (map char->integer
  319. (string->list str))))
  320. (port (open-bytevector-input-port bv #f)))
  321. (and (port-has-port-position? port)
  322. (= 0 (port-position port))
  323. (port-has-set-port-position!? port)
  324. (begin
  325. (set-port-position! port 6)
  326. (= 6 (port-position port)))
  327. (bytevector=? (get-bytevector-all port)
  328. (u8-list->bytevector
  329. (map char->integer (string->list "Port!")))))))
  330. (pass-if "bytevector input port can seek to very end"
  331. (let ((empty (open-bytevector-input-port '#vu8()))
  332. (not-empty (open-bytevector-input-port '#vu8(1 2 3))))
  333. (and (begin (set-port-position! empty (port-position empty))
  334. (= 0 (port-position empty)))
  335. (begin (get-bytevector-n not-empty 3)
  336. (set-port-position! not-empty (port-position not-empty))
  337. (= 3 (port-position not-empty))))))
  338. (pass-if-exception "make-custom-binary-input-port [wrong-num-args]"
  339. exception:wrong-num-args
  340. ;; Prior to Guile-R6RS-Libs 0.2, the last 3 arguments were wrongfully
  341. ;; optional.
  342. (make-custom-binary-input-port "port" (lambda args #t)))
  343. (pass-if "make-custom-binary-input-port"
  344. (let* ((source (make-bytevector 7777))
  345. (read! (let ((pos 0)
  346. (len (bytevector-length source)))
  347. (lambda (bv start count)
  348. (let ((amount (min count (- len pos))))
  349. (if (> amount 0)
  350. (bytevector-copy! source pos
  351. bv start amount))
  352. (set! pos (+ pos amount))
  353. amount))))
  354. (port (make-custom-binary-input-port "the port" read!
  355. #f #f #f)))
  356. (and (binary-port? port)
  357. (input-port? port)
  358. (bytevector=? (get-bytevector-all port) source))))
  359. (pass-if "custom binary input port does not support `port-position'"
  360. (let* ((str "Hello Port!")
  361. (source (open-bytevector-input-port
  362. (u8-list->bytevector
  363. (map char->integer (string->list str)))))
  364. (read! (lambda (bv start count)
  365. (let ((r (get-bytevector-n! source bv start count)))
  366. (if (eof-object? r)
  367. 0
  368. r))))
  369. (port (make-custom-binary-input-port "the port" read!
  370. #f #f #f)))
  371. (not (or (port-has-port-position? port)
  372. (port-has-set-port-position!? port)))))
  373. (pass-if-exception "custom binary input port 'read!' returns too much"
  374. exception:out-of-range
  375. ;; In Guile <= 2.0.9 this would segfault.
  376. (let* ((read! (lambda (bv start count)
  377. (+ count 4242)))
  378. (port (make-custom-binary-input-port "the port" read!
  379. #f #f #f)))
  380. (get-bytevector-all port)))
  381. (pass-if-equal "custom binary input port supports `port-position', \
  382. not `set-port-position!'"
  383. 42
  384. (let ((port (make-custom-binary-input-port "the port" (const 0)
  385. (const 42) #f #f)))
  386. (and (port-has-port-position? port)
  387. (not (port-has-set-port-position!? port))
  388. (port-position port))))
  389. (pass-if "custom binary input port supports `port-position'"
  390. (let* ((str "Hello Port!")
  391. (source (open-bytevector-input-port
  392. (u8-list->bytevector
  393. (map char->integer (string->list str)))))
  394. (read! (lambda (bv start count)
  395. (let ((r (get-bytevector-n! source bv start count)))
  396. (if (eof-object? r)
  397. 0
  398. r))))
  399. (get-pos (lambda ()
  400. (port-position source)))
  401. (set-pos! (lambda (pos)
  402. (set-port-position! source pos)))
  403. (port (make-custom-binary-input-port "the port" read!
  404. get-pos set-pos! #f)))
  405. (and (port-has-port-position? port)
  406. (= 0 (port-position port))
  407. (port-has-set-port-position!? port)
  408. (begin
  409. (set-port-position! port 6)
  410. (= 6 (port-position port)))
  411. (bytevector=? (get-bytevector-all port)
  412. (u8-list->bytevector
  413. (map char->integer (string->list "Port!")))))))
  414. (pass-if-equal "custom binary input port buffered partial reads"
  415. "Hello Port!"
  416. ;; Check what happens when READ! returns less than COUNT bytes.
  417. (let* ((src (string->utf8 "Hello Port!"))
  418. (chunks '(2 4 5)) ; provide 2 bytes, then 4, etc.
  419. (offset 0)
  420. (read! (lambda (bv start count)
  421. (match chunks
  422. ((count rest ...)
  423. (bytevector-copy! src offset bv start count)
  424. (set! chunks rest)
  425. (set! offset (+ offset count))
  426. count)
  427. (()
  428. 0))))
  429. (port (make-custom-binary-input-port "the port"
  430. read! #f #f #f)))
  431. (get-string-all port)))
  432. (pass-if-equal "custom binary input port unbuffered & 'port-position'"
  433. '(0 2 5 11)
  434. ;; Check that the value returned by 'port-position' is correct, and
  435. ;; that each 'port-position' call leads one call to the
  436. ;; 'get-position' method.
  437. (let* ((str "Hello Port!")
  438. (output (make-bytevector (string-length str)))
  439. (source (with-fluids ((%default-port-encoding "UTF-8"))
  440. (open-string-input-port str)))
  441. (read! (lambda (bv start count)
  442. (let ((r (get-bytevector-n! source bv start count)))
  443. (if (eof-object? r)
  444. 0
  445. r))))
  446. (pos '())
  447. (get-pos (lambda ()
  448. (let ((p (port-position source)))
  449. (set! pos (cons p pos))
  450. p)))
  451. (port (make-custom-binary-input-port "the port" read!
  452. get-pos #f #f)))
  453. (setvbuf port _IONBF)
  454. (and (= 0 (port-position port))
  455. (begin
  456. (get-bytevector-n! port output 0 2)
  457. (= 2 (port-position port)))
  458. (begin
  459. (get-bytevector-n! port output 2 3)
  460. (= 5 (port-position port)))
  461. (let ((bv (string->utf8 (get-string-all port))))
  462. (bytevector-copy! bv 0 output 5 (bytevector-length bv))
  463. (= (string-length str) (port-position port)))
  464. (bytevector=? output (string->utf8 str))
  465. (reverse pos))))
  466. (pass-if-equal "custom binary input port unbuffered & 'read!' calls"
  467. `((2 "He") (3 "llo") (42 " Port!"))
  468. (let* ((str "Hello Port!")
  469. (source (with-fluids ((%default-port-encoding "UTF-8"))
  470. (open-string-input-port str)))
  471. (reads '())
  472. (read! (lambda (bv start count)
  473. (set! reads (cons count reads))
  474. (let ((r (get-bytevector-n! source bv start count)))
  475. (if (eof-object? r)
  476. 0
  477. r))))
  478. (port (make-custom-binary-input-port "the port" read!
  479. #f #f #f)))
  480. (setvbuf port _IONBF)
  481. (let ((ret (list (get-bytevector-n port 2)
  482. (get-bytevector-n port 3)
  483. (get-bytevector-n port 42))))
  484. (zip (reverse reads)
  485. (map (lambda (obj)
  486. (if (bytevector? obj)
  487. (utf8->string obj)
  488. obj))
  489. ret)))))
  490. (pass-if-equal "custom binary input port unbuffered & 'get-string-all'"
  491. (make-string 1000 #\a)
  492. ;; In Guile 2.0.11 this test would lead to a buffer overrun followed
  493. ;; by an assertion failure. See <http://bugs.gnu.org/19621>.
  494. (let* ((input (with-fluids ((%default-port-encoding #f))
  495. (open-input-string (make-string 1000 #\a))))
  496. (read! (lambda (bv index count)
  497. (let ((n (get-bytevector-n! input bv index
  498. count)))
  499. (if (eof-object? n) 0 n))))
  500. (port (make-custom-binary-input-port "foo" read!
  501. #f #f #f)))
  502. (setvbuf port _IONBF)
  503. (get-string-all port)))
  504. (pass-if-equal "custom binary input port unbuffered UTF-8 & 'get-string-all'"
  505. (make-string 1000 #\λ)
  506. ;; In Guile 2.0.11 this test would lead to a buffer overrun followed
  507. ;; by an assertion failure. See <http://bugs.gnu.org/19621>.
  508. (let* ((input (with-fluids ((%default-port-encoding "UTF-8"))
  509. (open-input-string (make-string 1000 #\λ))))
  510. (read! (lambda (bv index count)
  511. (let ((n (get-bytevector-n! input bv index
  512. count)))
  513. (if (eof-object? n) 0 n))))
  514. (port (make-custom-binary-input-port "foo" read!
  515. #f #f #f)))
  516. (setvbuf port _IONBF)
  517. (set-port-encoding! port "UTF-8")
  518. (get-string-all port)))
  519. (pass-if-equal "custom binary input port, unbuffered then buffered"
  520. `((6 "Lorem ") (12 "ipsum dolor ") (777 "sit amet, consectetur…")
  521. (777 ,(eof-object)))
  522. (let* ((str "Lorem ipsum dolor sit amet, consectetur…")
  523. (source (with-fluids ((%default-port-encoding "UTF-8"))
  524. (open-string-input-port str)))
  525. (reads '())
  526. (read! (lambda (bv start count)
  527. (set! reads (cons count reads))
  528. (let ((r (get-bytevector-n! source bv start count)))
  529. (if (eof-object? r)
  530. 0
  531. r))))
  532. (port (make-custom-binary-input-port "the port" read!
  533. #f #f #f)))
  534. (setvbuf port _IONBF)
  535. (let ((ret (list (get-bytevector-n port 6)
  536. (get-bytevector-n port 12)
  537. (begin
  538. (setvbuf port _IOFBF 777)
  539. (get-bytevector-n port 42))
  540. (get-bytevector-n port 42))))
  541. (zip (reverse reads)
  542. (map (lambda (obj)
  543. (if (bytevector? obj)
  544. (utf8->string obj)
  545. obj))
  546. ret)))))
  547. (pass-if-equal "custom binary input port, buffered then unbuffered"
  548. `((18
  549. 42 14 ; scm_c_read tries to fill the 42-byte buffer
  550. 42)
  551. ("Lorem " "ipsum dolor " "sit amet, consectetur bla…" ,(eof-object)))
  552. (let* ((str "Lorem ipsum dolor sit amet, consectetur bla…")
  553. (source (with-fluids ((%default-port-encoding "UTF-8"))
  554. (open-string-input-port str)))
  555. (reads '())
  556. (read! (lambda (bv start count)
  557. (set! reads (cons count reads))
  558. (let ((r (get-bytevector-n! source bv start count)))
  559. (if (eof-object? r)
  560. 0
  561. r))))
  562. (port (make-custom-binary-input-port "the port" read!
  563. #f #f #f)))
  564. (setvbuf port _IOFBF 18)
  565. (let ((ret (list (get-bytevector-n port 6)
  566. (get-bytevector-n port 12)
  567. (begin
  568. (setvbuf port _IONBF)
  569. (get-bytevector-n port 42))
  570. (get-bytevector-n port 42))))
  571. (list (reverse reads)
  572. (map (lambda (obj)
  573. (if (bytevector? obj)
  574. (utf8->string obj)
  575. obj))
  576. ret)))))
  577. (pass-if "custom binary input port `close-proc' is called"
  578. (let* ((closed? #f)
  579. (read! (lambda (bv start count) 0))
  580. (get-pos (lambda () 0))
  581. (set-pos! (lambda (pos) #f))
  582. (close! (lambda () (set! closed? #t)))
  583. (port (make-custom-binary-input-port "the port" read!
  584. get-pos set-pos!
  585. close!)))
  586. (close-port port)
  587. (gc) ; Test for marking a closed port.
  588. closed?))
  589. (pass-if "standard-input-port is binary"
  590. (with-fluids ((%default-port-encoding "UTF-8"))
  591. (binary-port? (standard-input-port)))))
  592. (define (test-output-file-opener open filename)
  593. (with-fluids ((%default-port-encoding "UTF-8"))
  594. (pass-if "opens binary output port"
  595. (call-with-port (open filename)
  596. (lambda (port)
  597. (put-bytevector port '#vu8(1 2 3))
  598. (and (binary-port? port)
  599. (output-port? port))))))
  600. (pass-if-condition "exception: already-exists"
  601. i/o-file-already-exists-error?
  602. (open filename))
  603. (pass-if "no-fail no-truncate"
  604. (and
  605. (call-with-port (open filename (file-options no-fail no-truncate))
  606. (lambda (port)
  607. (= 0 (port-position port))))
  608. (= 3 (stat:size (stat filename)))))
  609. (pass-if "no-fail"
  610. (and
  611. (call-with-port (open filename (file-options no-fail))
  612. binary-port?)
  613. (= 0 (stat:size (stat filename)))))
  614. (delete-file filename)
  615. (pass-if-condition "exception: does-not-exist"
  616. i/o-file-does-not-exist-error?
  617. (open filename (file-options no-create))))
  618. (with-test-prefix "8.2.10 Output ports"
  619. (with-test-prefix "open-file-output-port"
  620. (test-output-file-opener open-file-output-port (test-file)))
  621. (pass-if "open-bytevector-output-port"
  622. (let-values (((port get-content)
  623. (open-bytevector-output-port #f)))
  624. (let ((source (make-bytevector 7777)))
  625. (put-bytevector port source)
  626. (and (bytevector=? (get-content) source)
  627. (bytevector=? (get-content) (make-bytevector 0))))))
  628. (pass-if "bytevector-output-port is binary"
  629. (binary-port? (open-bytevector-output-port)))
  630. (pass-if "open-bytevector-output-port [extract after close]"
  631. (let-values (((port get-content)
  632. (open-bytevector-output-port)))
  633. (let ((source (make-bytevector 12345 #xFE)))
  634. (put-bytevector port source)
  635. (close-port port)
  636. (bytevector=? (get-content) source))))
  637. (pass-if "open-bytevector-output-port [put-u8]"
  638. (let-values (((port get-content)
  639. (open-bytevector-output-port)))
  640. (put-u8 port 77)
  641. (and (bytevector=? (get-content) (make-bytevector 1 77))
  642. (bytevector=? (get-content) (make-bytevector 0)))))
  643. (pass-if "open-bytevector-output-port [display]"
  644. (let-values (((port get-content)
  645. (open-bytevector-output-port)))
  646. (display "hello" port)
  647. (and (bytevector=? (get-content) (string->utf8 "hello"))
  648. (bytevector=? (get-content) (make-bytevector 0)))))
  649. (pass-if "bytevector output port supports `port-position'"
  650. (let-values (((port get-content)
  651. (open-bytevector-output-port)))
  652. (let ((source (make-bytevector 7777))
  653. (overwrite (make-bytevector 33)))
  654. (and (port-has-port-position? port)
  655. (port-has-set-port-position!? port)
  656. (begin
  657. (put-bytevector port source)
  658. (= (bytevector-length source)
  659. (port-position port)))
  660. (begin
  661. (set-port-position! port 10)
  662. (= 10 (port-position port)))
  663. (begin
  664. (put-bytevector port overwrite)
  665. (bytevector-copy! overwrite 0 source 10
  666. (bytevector-length overwrite))
  667. (= (port-position port)
  668. (+ 10 (bytevector-length overwrite))))
  669. (bytevector=? (get-content) source)
  670. (bytevector=? (get-content) (make-bytevector 0))))))
  671. (pass-if "make-custom-binary-output-port"
  672. (let ((port (make-custom-binary-output-port "cbop"
  673. (lambda (x y z) 0)
  674. #f #f #f)))
  675. (and (output-port? port)
  676. (binary-port? port)
  677. (not (port-has-port-position? port))
  678. (not (port-has-set-port-position!? port)))))
  679. (pass-if "make-custom-binary-output-port [partial writes]"
  680. (let* ((source (uint-list->bytevector (iota 333)
  681. (native-endianness) 2))
  682. (sink (make-bytevector (bytevector-length source)))
  683. (sink-pos 0)
  684. (eof? #f)
  685. (write! (lambda (bv start count)
  686. (if (= 0 count)
  687. (begin
  688. (set! eof? #t)
  689. 0)
  690. (let ((u8 (bytevector-u8-ref bv start)))
  691. ;; Get one byte at a time.
  692. (bytevector-u8-set! sink sink-pos u8)
  693. (set! sink-pos (+ 1 sink-pos))
  694. 1))))
  695. (port (make-custom-binary-output-port "cbop" write!
  696. #f #f #f)))
  697. (put-bytevector port source)
  698. (and (= sink-pos (bytevector-length source))
  699. (not eof?)
  700. (bytevector=? sink source))))
  701. (pass-if "make-custom-binary-output-port [full writes]"
  702. (let* ((source (uint-list->bytevector (iota 333)
  703. (native-endianness) 2))
  704. (sink (make-bytevector (bytevector-length source)))
  705. (sink-pos 0)
  706. (eof? #f)
  707. (write! (lambda (bv start count)
  708. (if (= 0 count)
  709. (begin
  710. (set! eof? #t)
  711. 0)
  712. (begin
  713. (bytevector-copy! bv start
  714. sink sink-pos
  715. count)
  716. (set! sink-pos (+ sink-pos count))
  717. count))))
  718. (port (make-custom-binary-output-port "cbop" write!
  719. #f #f #f)))
  720. (put-bytevector port source)
  721. (and (= sink-pos (bytevector-length source))
  722. (not eof?)
  723. (bytevector=? sink source))))
  724. (pass-if "standard-output-port is binary"
  725. (with-fluids ((%default-port-encoding "UTF-8"))
  726. (binary-port? (standard-output-port))))
  727. (pass-if "standard-error-port is binary"
  728. (with-fluids ((%default-port-encoding "UTF-8"))
  729. (binary-port? (standard-error-port)))))
  730. (with-test-prefix "8.2.6 Input and output ports"
  731. (pass-if "transcoded-port [output]"
  732. (let ((s "Hello\nÄÖÜ"))
  733. (bytevector=?
  734. (string->utf8 s)
  735. (call-with-bytevector-output-port/transcoded (make-transcoder (utf-8-codec))
  736. (lambda (utf8-port)
  737. (put-string utf8-port s))))))
  738. (pass-if "transcoded-port [input]"
  739. (let ((s "Hello\nÄÖÜ"))
  740. (string=?
  741. s
  742. (get-string-all
  743. (transcoded-port (open-bytevector-input-port (string->utf8 s))
  744. (make-transcoder (utf-8-codec)))))))
  745. (pass-if "transcoded-port [input line]"
  746. (string=? "ÄÖÜ"
  747. (get-line (transcoded-port
  748. (open-bytevector-input-port (string->utf8 "ÄÖÜ\nFooBar"))
  749. (make-transcoder (utf-8-codec))))))
  750. (pass-if "transcoded-port [error handling mode = raise]"
  751. (let* ((t (make-transcoder (utf-8-codec) (native-eol-style)
  752. (error-handling-mode raise)))
  753. (b (open-bytevector-input-port #vu8(255 2 1)))
  754. (tp (transcoded-port b t)))
  755. (guard (c ((i/o-decoding-error? c)
  756. (eq? (i/o-error-port c) tp)))
  757. (get-line tp)
  758. #f))) ; fail if we reach this point
  759. (pass-if "transcoded-port [error handling mode = replace]"
  760. (let* ((t (make-transcoder (utf-8-codec) (native-eol-style)
  761. (error-handling-mode replace)))
  762. (b (open-bytevector-input-port #vu8(255 1 2 3 103 110 117)))
  763. (tp (transcoded-port b t)))
  764. (string-suffix? "gnu" (get-line tp))))
  765. (pass-if "transcoded-port, output [error handling mode = raise]"
  766. (let-values (((p get)
  767. (open-bytevector-output-port)))
  768. (let* ((t (make-transcoder (latin-1-codec) (native-eol-style)
  769. (error-handling-mode raise)))
  770. (tp (transcoded-port p t)))
  771. (guard (c ((i/o-encoding-error? c)
  772. (and (eq? (i/o-error-port c) tp)
  773. (char=? (i/o-encoding-error-char c) #\λ)
  774. (bytevector=? (get) (string->utf8 "The letter ")))))
  775. (put-string tp "The letter λ cannot be represented in Latin-1.")
  776. #f))))
  777. (pass-if "port-transcoder [transcoded port]"
  778. (let* ((p (transcoded-port (open-bytevector-input-port (string->utf8 "foo"))
  779. (make-transcoder (utf-8-codec))))
  780. (t (port-transcoder p)))
  781. (and t
  782. (transcoder-codec t)
  783. (eq? (native-eol-style)
  784. (transcoder-eol-style t))
  785. (eq? (error-handling-mode replace)
  786. (transcoder-error-handling-mode t))))))
  787. (with-test-prefix "8.2.9 Textual input"
  788. (pass-if "get-string-n [short]"
  789. (let ((port (open-input-string "GNU Guile")))
  790. (string=? "GNU " (get-string-n port 4))))
  791. (pass-if "get-string-n [long]"
  792. (let ((port (open-input-string "GNU Guile")))
  793. (string=? "GNU Guile" (get-string-n port 256))))
  794. (pass-if "get-string-n [eof]"
  795. (let ((port (open-input-string "")))
  796. (eof-object? (get-string-n port 4))))
  797. (pass-if "get-string-n! [short]"
  798. (let ((port (open-input-string "GNU Guile"))
  799. (s (string-copy "Isn't XXX great?")))
  800. (and (= 3 (get-string-n! port s 6 3))
  801. (string=? s "Isn't GNU great?"))))
  802. (with-test-prefix "read error"
  803. (pass-if-condition "get-char" i/o-read-error?
  804. (get-char (make-failing-port)))
  805. (pass-if-condition "lookahead-char" i/o-read-error?
  806. (lookahead-char (make-failing-port)))
  807. ;; FIXME: these are not yet exception-correct
  808. #|
  809. (pass-if-condition "get-string-n" i/o-read-error?
  810. (get-string-n (make-failing-port) 5))
  811. (pass-if-condition "get-string-n!" i/o-read-error?
  812. (get-string-n! (make-failing-port) (make-string 5) 0 5))
  813. |#
  814. (pass-if-condition "get-string-all" i/o-read-error?
  815. (get-string-all (make-failing-port 100)))
  816. (pass-if-condition "get-line" i/o-read-error?
  817. (get-line (make-failing-port)))
  818. (pass-if-condition "get-datum" i/o-read-error?
  819. (get-datum (make-failing-port)))))
  820. (define (encoding-error-predicate char)
  821. (lambda (c)
  822. (and (i/o-encoding-error? c)
  823. (char=? char (i/o-encoding-error-char c)))))
  824. (with-test-prefix "8.2.12 Textual Output"
  825. (with-test-prefix "write error"
  826. (pass-if-condition "put-char" i/o-write-error?
  827. (put-char (make-failing-port) #\G))
  828. (pass-if-condition "put-string" i/o-write-error?
  829. (put-string (make-failing-port) "Hello World!"))
  830. (pass-if-condition "put-datum" i/o-write-error?
  831. (put-datum (make-failing-port) '(hello world!))))
  832. (with-test-prefix "encoding error"
  833. (pass-if-condition "put-char" (encoding-error-predicate #\λ)
  834. (call-with-bytevector-output-port/transcoded
  835. (make-transcoder (latin-1-codec)
  836. (native-eol-style)
  837. (error-handling-mode raise))
  838. (lambda (port)
  839. (put-char port #\λ))))
  840. (pass-if-condition "put-string" (encoding-error-predicate #\λ)
  841. (call-with-bytevector-output-port/transcoded
  842. (make-transcoder (latin-1-codec)
  843. (native-eol-style)
  844. (error-handling-mode raise))
  845. (lambda (port)
  846. (put-string port "FooλBar"))))))
  847. (with-test-prefix "8.3 Simple I/O"
  848. (with-test-prefix "read error"
  849. (pass-if-condition "read-char" i/o-read-error?
  850. (read-char (make-failing-port)))
  851. (pass-if-condition "peek-char" i/o-read-error?
  852. (peek-char (make-failing-port)))
  853. (pass-if-condition "read" i/o-read-error?
  854. (read (make-failing-port))))
  855. (with-test-prefix "write error"
  856. (pass-if-condition "display" i/o-write-error?
  857. (display "Hi there!" (make-failing-port)))
  858. (pass-if-condition "write" i/o-write-error?
  859. (write '(hi there!) (make-failing-port)))
  860. (pass-if-condition "write-char" i/o-write-error?
  861. (write-char #\G (make-failing-port)))
  862. (pass-if-condition "newline" i/o-write-error?
  863. (newline (make-failing-port))))
  864. (let ((filename (test-file)))
  865. ;; ensure the test file exists
  866. (call-with-output-file filename
  867. (lambda (port) (write "foo" port)))
  868. (pass-if "call-with-input-file [port is textual]"
  869. (call-with-input-file filename textual-port?))
  870. (pass-if-condition "call-with-input-file [exception: not-found]"
  871. i/o-file-does-not-exist-error?
  872. (call-with-input-file ",this-is-highly-unlikely-to-exist!"
  873. values))
  874. (pass-if-condition "call-with-output-file [exception: already-exists]"
  875. i/o-file-already-exists-error?
  876. (call-with-output-file filename
  877. values))
  878. (delete-file filename)))
  879. (with-test-prefix "8.2.13 Input/output ports"
  880. (with-test-prefix "open-file-input/output-port [output]"
  881. (test-output-file-opener open-file-input/output-port (test-file)))
  882. (with-test-prefix "open-file-input/output-port [input]"
  883. (test-input-file-opener open-file-input/output-port (test-file))))
  884. ;;; Local Variables:
  885. ;;; mode: scheme
  886. ;;; eval: (put 'guard 'scheme-indent-function 1)
  887. ;;; End: