r6rs-ports.test 66 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657
  1. ;;;; r6rs-ports.test --- R6RS I/O port tests. -*- coding: utf-8; -*-
  2. ;;;;
  3. ;;;; Copyright (C) 2009-2012, 2013-2015, 2018 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 ((ice-9 binary-ports) #:select (get-bytevector-some!))
  26. #:use-module (rnrs io ports)
  27. #:use-module (rnrs io simple)
  28. #:use-module (rnrs exceptions)
  29. #:use-module (rnrs bytevectors))
  30. (define-syntax pass-if-condition
  31. (syntax-rules ()
  32. ((_ name predicate body0 body ...)
  33. (let ((cookie (list 'cookie)))
  34. (pass-if name
  35. (eq? cookie (guard (c ((predicate c) cookie))
  36. body0 body ...)))))))
  37. (define (test-file)
  38. (data-file-name "ports-test.tmp"))
  39. ;; A input/output port that swallows all output, and produces just
  40. ;; spaces on input. Reading and writing beyond `failure-position'
  41. ;; produces `system-error' exceptions. Used for testing exception
  42. ;; behavior.
  43. (define* (make-failing-port #:optional (failure-position 0))
  44. (define (maybe-fail index errno)
  45. (if (> index failure-position)
  46. (scm-error 'system-error
  47. 'failing-port
  48. "I/O beyond failure position" '()
  49. (list errno))))
  50. (let ((read-index 0)
  51. (write-index 0))
  52. (define (write-char chr)
  53. (set! write-index (+ 1 write-index))
  54. (maybe-fail write-index ENOSPC))
  55. (make-soft-port
  56. (vector write-char
  57. (lambda (str) ;; write-string
  58. (for-each write-char (string->list str)))
  59. (lambda () #t) ;; flush-output
  60. (lambda () ;; read-char
  61. (set! read-index (+ read-index 1))
  62. (maybe-fail read-index EIO)
  63. #\space)
  64. (lambda () #t)) ;; close-port
  65. "rw")))
  66. (define (call-with-bytevector-output-port/transcoded transcoder receiver)
  67. (call-with-bytevector-output-port
  68. (lambda (bv-port)
  69. (call-with-port (transcoded-port bv-port transcoder)
  70. receiver))))
  71. (with-test-prefix "8.2.5 End-of-File Object"
  72. (pass-if "eof-object"
  73. (and (eqv? (eof-object) (eof-object))
  74. (eq? (eof-object) (eof-object))))
  75. (pass-if "port-eof?"
  76. (port-eof? (open-input-string ""))))
  77. (with-test-prefix "8.2.8 Binary Input"
  78. (pass-if "get-u8"
  79. (let ((port (open-input-string "A")))
  80. (and (= (char->integer #\A) (get-u8 port))
  81. (eof-object? (get-u8 port)))))
  82. (pass-if "lookahead-u8"
  83. (let ((port (open-input-string "A")))
  84. (and (= (char->integer #\A) (lookahead-u8 port))
  85. (= (char->integer #\A) (lookahead-u8 port))
  86. (= (char->integer #\A) (get-u8 port))
  87. (eof-object? (get-u8 port)))))
  88. (pass-if "lookahead-u8 non-ASCII"
  89. (let ((port (open-input-string "λ")))
  90. (and (= 206 (lookahead-u8 port))
  91. (= 206 (lookahead-u8 port))
  92. (= 206 (get-u8 port))
  93. (= 187 (lookahead-u8 port))
  94. (= 187 (lookahead-u8 port))
  95. (= 187 (get-u8 port))
  96. (eof-object? (lookahead-u8 port))
  97. (eof-object? (get-u8 port)))))
  98. (pass-if "lookahead-u8: result is unsigned"
  99. ;; Bug #31081.
  100. (let ((port (open-bytevector-input-port #vu8(255))))
  101. (= (lookahead-u8 port) 255)))
  102. (pass-if "get-bytevector-n [short]"
  103. (let* ((port (open-input-string "GNU Guile"))
  104. (bv (get-bytevector-n port 4)))
  105. (and (bytevector? bv)
  106. (equal? (bytevector->u8-list bv)
  107. (map char->integer (string->list "GNU "))))))
  108. (pass-if "get-bytevector-n [long]"
  109. (let* ((port (open-input-string "GNU Guile"))
  110. (bv (get-bytevector-n port 256)))
  111. (and (bytevector? bv)
  112. (equal? (bytevector->u8-list bv)
  113. (map char->integer (string->list "GNU Guile"))))))
  114. (pass-if-exception "get-bytevector-n with closed port"
  115. exception:wrong-type-arg
  116. (let ((port (%make-void-port "r")))
  117. (close-port port)
  118. (get-bytevector-n port 3)))
  119. (let ((expected (make-bytevector 20 (char->integer #\a))))
  120. (pass-if-equal "http://bugs.gnu.org/17466"
  121. ;; <http://bugs.gnu.org/17466> is about a memory corruption
  122. ;; whereas bytevector shrunk in 'get-bytevector-n' would keep
  123. ;; referring to the previous (larger) bytevector.
  124. expected
  125. (let loop ((count 50))
  126. (if (zero? count)
  127. expected
  128. (let ((bv (call-with-input-string "aaaaaaaaaaaaaaaaaaaa"
  129. (lambda (port)
  130. (get-bytevector-n port 4096)))))
  131. ;; Cause the 4 KiB bytevector initially created by
  132. ;; 'get-bytevector-n' to be reclaimed.
  133. (make-bytevector 4096)
  134. (if (equal? bv expected)
  135. (loop (- count 1))
  136. bv))))))
  137. (pass-if "get-bytevector-n! [short]"
  138. (let* ((port (open-input-string "GNU Guile"))
  139. (bv (make-bytevector 4))
  140. (read (get-bytevector-n! port bv 0 4)))
  141. (and (equal? read 4)
  142. (equal? (bytevector->u8-list bv)
  143. (map char->integer (string->list "GNU "))))))
  144. (pass-if "get-bytevector-n! [long]"
  145. (let* ((str "GNU Guile")
  146. (port (open-input-string str))
  147. (bv (make-bytevector 256))
  148. (read (get-bytevector-n! port bv 0 256)))
  149. (and (equal? read (string-length str))
  150. (equal? (map (lambda (i)
  151. (bytevector-u8-ref bv i))
  152. (iota read))
  153. (map char->integer (string->list str))))))
  154. (pass-if "get-bytevector-some [simple]"
  155. (let* ((str "GNU Guile")
  156. (port (open-input-string str))
  157. (bv (get-bytevector-some port)))
  158. (and (bytevector? bv)
  159. (equal? (bytevector->u8-list bv)
  160. (map char->integer (string->list str))))))
  161. (pass-if "get-bytevector-some! [short]"
  162. (let* ((port (open-input-string "GNU Guile"))
  163. (bv (make-bytevector 4))
  164. (read (get-bytevector-some! port bv 0 4)))
  165. (and (equal? read 4)
  166. (equal? (bytevector->u8-list bv)
  167. (map char->integer (string->list "GNU "))))))
  168. (pass-if "get-bytevector-some! [long]"
  169. (let* ((str "GNU Guile")
  170. (port (open-input-string str))
  171. (bv (make-bytevector 256))
  172. (read (get-bytevector-some! port bv 0 256)))
  173. (and (equal? read (string-length str))
  174. (equal? (map (lambda (i)
  175. (bytevector-u8-ref bv i))
  176. (iota read))
  177. (map char->integer (string->list str))))))
  178. (pass-if "get-bytevector-all"
  179. (let* ((str "GNU Guile")
  180. (index 0)
  181. (port (make-soft-port
  182. (vector #f #f #f
  183. (lambda ()
  184. (if (>= index (string-length str))
  185. (eof-object)
  186. (let ((c (string-ref str index)))
  187. (set! index (+ index 1))
  188. c)))
  189. (lambda () #t)
  190. (let ((cont? #f))
  191. (lambda ()
  192. ;; Number of readily available octets: falls to
  193. ;; zero after 4 octets have been read and then
  194. ;; starts again.
  195. (let ((a (if cont?
  196. (- (string-length str) index)
  197. (- 4 (modulo index 5)))))
  198. (if (= 0 a) (set! cont? #t))
  199. a))))
  200. "r"))
  201. (bv (get-bytevector-all port)))
  202. (and (bytevector? bv)
  203. (= index (string-length str))
  204. (= (bytevector-length bv) (string-length str))
  205. (equal? (bytevector->u8-list bv)
  206. (map char->integer (string->list str)))))))
  207. (define (make-soft-output-port)
  208. (let* ((bv (make-bytevector 1024))
  209. (read-index 0)
  210. (write-index 0)
  211. (write-char (lambda (chr)
  212. (bytevector-u8-set! bv write-index
  213. (char->integer chr))
  214. (set! write-index (+ 1 write-index)))))
  215. (make-soft-port
  216. (vector write-char
  217. (lambda (str) ;; write-string
  218. (for-each write-char (string->list str)))
  219. (lambda () #t) ;; flush-output
  220. (lambda () ;; read-char
  221. (if (>= read-index (bytevector-length bv))
  222. (eof-object)
  223. (let ((c (bytevector-u8-ref bv read-index)))
  224. (set! read-index (+ read-index 1))
  225. (integer->char c))))
  226. (lambda () #t)) ;; close-port
  227. "rw")))
  228. (with-test-prefix "8.2.11 Binary Output"
  229. (pass-if "put-u8"
  230. (let ((port (make-soft-output-port)))
  231. (put-u8 port 77)
  232. (equal? (get-u8 port) 77)))
  233. ;; Note: The `put-bytevector' tests below temporarily set the default
  234. ;; port encoding to ISO-8859-1 so that the soft-port will let all the
  235. ;; bytes through, unmodified. This is hacky, but we can't use "custom
  236. ;; binary output ports" here because they're only tested later.
  237. (pass-if "put-bytevector [2 args]"
  238. (with-fluids ((%default-port-encoding "ISO-8859-1"))
  239. (let ((port (make-soft-output-port))
  240. (bv (make-bytevector 256)))
  241. (put-bytevector port bv)
  242. (equal? (bytevector->u8-list bv)
  243. (bytevector->u8-list
  244. (get-bytevector-n port (bytevector-length bv)))))))
  245. (pass-if "put-bytevector [3 args]"
  246. (with-fluids ((%default-port-encoding "ISO-8859-1"))
  247. (let ((port (make-soft-output-port))
  248. (bv (make-bytevector 256))
  249. (start 10))
  250. (put-bytevector port bv start)
  251. (equal? (drop (bytevector->u8-list bv) start)
  252. (bytevector->u8-list
  253. (get-bytevector-n port (- (bytevector-length bv) start)))))))
  254. (pass-if "put-bytevector [4 args]"
  255. (with-fluids ((%default-port-encoding "ISO-8859-1"))
  256. (let ((port (make-soft-output-port))
  257. (bv (make-bytevector 256))
  258. (start 10)
  259. (count 77))
  260. (put-bytevector port bv start count)
  261. (equal? (take (drop (bytevector->u8-list bv) start) count)
  262. (bytevector->u8-list
  263. (get-bytevector-n port count))))))
  264. (pass-if-exception "put-bytevector with closed port"
  265. exception:wrong-type-arg
  266. (let* ((bv (make-bytevector 4))
  267. (port (%make-void-port "w")))
  268. (close-port port)
  269. (put-bytevector port bv)))
  270. (pass-if "put-bytevector with UTF-16 string port"
  271. (let* ((str "hello, world")
  272. (bv (string->utf16 str)))
  273. (equal? str
  274. (call-with-output-string
  275. (lambda (port)
  276. (set-port-encoding! port "UTF-16BE")
  277. (put-bytevector port bv))))))
  278. (pass-if "put-bytevector with wrong-encoding string port"
  279. (let* ((str "hello, world")
  280. (bv (string->utf16 str)))
  281. (catch 'decoding-error
  282. (lambda ()
  283. (with-fluids ((%default-port-conversion-strategy 'error))
  284. (call-with-output-string
  285. (lambda (port)
  286. (set-port-encoding! port "UTF-32")
  287. (put-bytevector port bv)))
  288. #f)) ; fail if we reach this point
  289. (lambda (key subr message errno port)
  290. (string? (strerror errno)))))))
  291. (define (test-input-file-opener open filename)
  292. (let ((contents (string->utf8 "GNU λ")))
  293. ;; Create file
  294. (call-with-output-file filename
  295. (lambda (port) (put-bytevector port contents)))
  296. (pass-if "opens binary input port with correct contents"
  297. (with-fluids ((%default-port-encoding "UTF-8"))
  298. (call-with-port (open-file-input-port filename)
  299. (lambda (port)
  300. (and (binary-port? port)
  301. (input-port? port)
  302. (bytevector=? contents (get-bytevector-all port))))))))
  303. (delete-file filename))
  304. (with-test-prefix "8.2.7 Input Ports"
  305. (with-test-prefix "open-file-input-port"
  306. (test-input-file-opener open-file-input-port (test-file)))
  307. ;; This section appears here so that it can use the binary input
  308. ;; primitives.
  309. (pass-if "open-bytevector-input-port [1 arg]"
  310. (let* ((str "Hello Port!")
  311. (bv (u8-list->bytevector (map char->integer
  312. (string->list str))))
  313. (port (open-bytevector-input-port bv))
  314. (read-to-string
  315. (lambda (port)
  316. (let loop ((chr (read-char port))
  317. (result '()))
  318. (if (eof-object? chr)
  319. (apply string (reverse! result))
  320. (loop (read-char port)
  321. (cons chr result)))))))
  322. (equal? (read-to-string port) str)))
  323. (pass-if "bytevector-input-port is binary"
  324. (with-fluids ((%default-port-encoding "UTF-8"))
  325. (binary-port? (open-bytevector-input-port #vu8(1 2 3)))))
  326. (pass-if-equal "bytevector-input-port uses ISO-8859-1 (Guile extension)"
  327. "©©"
  328. (with-fluids ((%default-port-encoding "UTF-8"))
  329. (get-string-all (open-bytevector-input-port #vu8(194 169 194 169)))))
  330. (pass-if-exception "bytevector-input-port is read-only"
  331. exception:wrong-type-arg
  332. (let* ((str "Hello Port!")
  333. (bv (u8-list->bytevector (map char->integer
  334. (string->list str))))
  335. (port (open-bytevector-input-port bv #f)))
  336. (write "hello" port)))
  337. (pass-if "bytevector input port supports seeking"
  338. (let* ((str "Hello Port!")
  339. (bv (u8-list->bytevector (map char->integer
  340. (string->list str))))
  341. (port (open-bytevector-input-port bv #f)))
  342. (and (port-has-port-position? port)
  343. (= 0 (port-position port))
  344. (port-has-set-port-position!? port)
  345. (begin
  346. (set-port-position! port 6)
  347. (= 6 (port-position port)))
  348. (bytevector=? (get-bytevector-all port)
  349. (u8-list->bytevector
  350. (map char->integer (string->list "Port!")))))))
  351. (pass-if "bytevector input port can seek to very end"
  352. (let ((empty (open-bytevector-input-port '#vu8()))
  353. (not-empty (open-bytevector-input-port '#vu8(1 2 3))))
  354. (and (begin (set-port-position! empty (port-position empty))
  355. (= 0 (port-position empty)))
  356. (begin (get-bytevector-n not-empty 3)
  357. (set-port-position! not-empty (port-position not-empty))
  358. (= 3 (port-position not-empty))))))
  359. (pass-if-exception "make-custom-binary-input-port [wrong-num-args]"
  360. exception:wrong-num-args
  361. ;; Prior to Guile-R6RS-Libs 0.2, the last 3 arguments were wrongfully
  362. ;; optional.
  363. (make-custom-binary-input-port "port" (lambda args #t)))
  364. (pass-if "make-custom-binary-input-port"
  365. (let* ((source (make-bytevector 7777))
  366. (read! (let ((pos 0)
  367. (len (bytevector-length source)))
  368. (lambda (bv start count)
  369. (let ((amount (min count (- len pos))))
  370. (if (> amount 0)
  371. (bytevector-copy! source pos
  372. bv start amount))
  373. (set! pos (+ pos amount))
  374. amount))))
  375. (port (make-custom-binary-input-port "the port" read!
  376. #f #f #f)))
  377. (and (binary-port? port)
  378. (input-port? port)
  379. (bytevector=? (get-bytevector-all port) source))))
  380. (pass-if-equal "make-custom-binary-input-port uses ISO-8859-1 (Guile extension)"
  381. "©©"
  382. (with-fluids ((%default-port-encoding "UTF-8"))
  383. (let* ((source #vu8(194 169 194 169))
  384. (read! (let ((pos 0)
  385. (len (bytevector-length source)))
  386. (lambda (bv start count)
  387. (let ((amount (min count (- len pos))))
  388. (if (> amount 0)
  389. (bytevector-copy! source pos
  390. bv start amount))
  391. (set! pos (+ pos amount))
  392. amount))))
  393. (port (make-custom-binary-input-port "the port" read!
  394. #f #f #f)))
  395. (get-string-all port))))
  396. (pass-if "custom binary input port does not support `port-position'"
  397. (let* ((str "Hello Port!")
  398. (source (open-bytevector-input-port
  399. (u8-list->bytevector
  400. (map char->integer (string->list str)))))
  401. (read! (lambda (bv start count)
  402. (let ((r (get-bytevector-n! source bv start count)))
  403. (if (eof-object? r)
  404. 0
  405. r))))
  406. (port (make-custom-binary-input-port "the port" read!
  407. #f #f #f)))
  408. (not (or (port-has-port-position? port)
  409. (port-has-set-port-position!? port)))))
  410. (pass-if-exception "custom binary input port 'read!' returns too much"
  411. exception:out-of-range
  412. ;; In Guile <= 2.0.9 this would segfault.
  413. (let* ((read! (lambda (bv start count)
  414. (+ count 4242)))
  415. (port (make-custom-binary-input-port "the port" read!
  416. #f #f #f)))
  417. (get-bytevector-all port)))
  418. (pass-if-equal "custom binary input port supports `port-position', \
  419. not `set-port-position!'"
  420. 42
  421. (let ((port (make-custom-binary-input-port "the port" (const 0)
  422. (const 42) #f #f)))
  423. (and (port-has-port-position? port)
  424. (not (port-has-set-port-position!? port))
  425. (port-position port))))
  426. (pass-if "custom binary input port supports `port-position'"
  427. (let* ((str "Hello Port!")
  428. (source (open-bytevector-input-port
  429. (u8-list->bytevector
  430. (map char->integer (string->list str)))))
  431. (read! (lambda (bv start count)
  432. (let ((r (get-bytevector-n! source bv start count)))
  433. (if (eof-object? r)
  434. 0
  435. r))))
  436. (get-pos (lambda ()
  437. (port-position source)))
  438. (set-pos! (lambda (pos)
  439. (set-port-position! source pos)))
  440. (port (make-custom-binary-input-port "the port" read!
  441. get-pos set-pos! #f)))
  442. (and (port-has-port-position? port)
  443. (= 0 (port-position port))
  444. (port-has-set-port-position!? port)
  445. (begin
  446. (set-port-position! port 6)
  447. (= 6 (port-position port)))
  448. (bytevector=? (get-bytevector-all port)
  449. (u8-list->bytevector
  450. (map char->integer (string->list "Port!")))))))
  451. (pass-if-equal "custom binary input port position, long offset"
  452. (expt 2 42)
  453. ;; In Guile <= 2.2.4, 'seek' would throw to 'out-of-range'.
  454. (let* ((port (make-custom-binary-input-port "the port"
  455. (const 0)
  456. (const (expt 2 42))
  457. #f #f)))
  458. (port-position port)))
  459. (pass-if-equal "custom binary input port buffered partial reads"
  460. "Hello Port!"
  461. ;; Check what happens when READ! returns less than COUNT bytes.
  462. (let* ((src (string->utf8 "Hello Port!"))
  463. (chunks '(2 4 5)) ; provide 2 bytes, then 4, etc.
  464. (offset 0)
  465. (read! (lambda (bv start count)
  466. (match chunks
  467. ((count rest ...)
  468. (bytevector-copy! src offset bv start count)
  469. (set! chunks rest)
  470. (set! offset (+ offset count))
  471. count)
  472. (()
  473. 0))))
  474. (port (make-custom-binary-input-port "the port"
  475. read! #f #f #f)))
  476. (get-string-all port)))
  477. (pass-if-equal "custom binary input port unbuffered & 'port-position'"
  478. '(0 2 5 11)
  479. ;; Check that the value returned by 'port-position' is correct, and
  480. ;; that each 'port-position' call leads one call to the
  481. ;; 'get-position' method.
  482. (let* ((str "Hello Port!")
  483. (output (make-bytevector (string-length str)))
  484. (source (with-fluids ((%default-port-encoding "UTF-8"))
  485. (open-string-input-port str)))
  486. (read! (lambda (bv start count)
  487. (let ((r (get-bytevector-n! source bv start count)))
  488. (if (eof-object? r)
  489. 0
  490. r))))
  491. (pos '())
  492. (get-pos (lambda ()
  493. (let ((p (port-position source)))
  494. (set! pos (cons p pos))
  495. p)))
  496. (port (make-custom-binary-input-port "the port" read!
  497. get-pos #f #f)))
  498. (setvbuf port 'none)
  499. (and (= 0 (port-position port))
  500. (begin
  501. (get-bytevector-n! port output 0 2)
  502. (= 2 (port-position port)))
  503. (begin
  504. (get-bytevector-n! port output 2 3)
  505. (= 5 (port-position port)))
  506. (let ((bv (string->utf8 (get-string-all port))))
  507. (bytevector-copy! bv 0 output 5 (bytevector-length bv))
  508. (= (string-length str) (port-position port)))
  509. (bytevector=? output (string->utf8 str))
  510. (reverse pos))))
  511. (pass-if-equal "custom binary input port unbuffered & 'read!' calls"
  512. `((2 "He") (3 "llo") (42 " Port!"))
  513. (let* ((str "Hello Port!")
  514. (source (with-fluids ((%default-port-encoding "UTF-8"))
  515. (open-string-input-port str)))
  516. (reads '())
  517. (read! (lambda (bv start count)
  518. (set! reads (cons count reads))
  519. (let ((r (get-bytevector-n! source bv start count)))
  520. (if (eof-object? r)
  521. 0
  522. r))))
  523. (port (make-custom-binary-input-port "the port" read!
  524. #f #f #f)))
  525. (setvbuf port 'none)
  526. (let ((ret (list (get-bytevector-n port 2)
  527. (get-bytevector-n port 3)
  528. (get-bytevector-n port 42))))
  529. (zip (reverse reads)
  530. (map (lambda (obj)
  531. (if (bytevector? obj)
  532. (utf8->string obj)
  533. obj))
  534. ret)))))
  535. (pass-if-equal "custom binary input port unbuffered & 'get-string-all'"
  536. (make-string 1000 #\a)
  537. ;; In Guile 2.0.11 this test would lead to a buffer overrun followed
  538. ;; by an assertion failure. See <http://bugs.gnu.org/19621>.
  539. (let* ((input (with-fluids ((%default-port-encoding #f))
  540. (open-input-string (make-string 1000 #\a))))
  541. (read! (lambda (bv index count)
  542. (let ((n (get-bytevector-n! input bv index
  543. count)))
  544. (if (eof-object? n) 0 n))))
  545. (port (make-custom-binary-input-port "foo" read!
  546. #f #f #f)))
  547. (setvbuf port 'none)
  548. (get-string-all port)))
  549. (pass-if-equal "custom binary input port unbuffered UTF-8 & 'get-string-all'"
  550. (make-string 1000 #\λ)
  551. ;; In Guile 2.0.11 this test would lead to a buffer overrun followed
  552. ;; by an assertion failure. See <http://bugs.gnu.org/19621>.
  553. (let* ((input (with-fluids ((%default-port-encoding "UTF-8"))
  554. (open-input-string (make-string 1000 #\λ))))
  555. (read! (lambda (bv index count)
  556. (let ((n (get-bytevector-n! input bv index
  557. count)))
  558. (if (eof-object? n) 0 n))))
  559. (port (make-custom-binary-input-port "foo" read!
  560. #f #f #f)))
  561. (setvbuf port 'none)
  562. (set-port-encoding! port "UTF-8")
  563. (get-string-all port)))
  564. (pass-if-equal "custom binary input port, unbuffered then buffered"
  565. `((6 "Lorem ") (12 "ipsum dolor ") (777 "sit amet, consectetur…")
  566. (777 ,(eof-object)))
  567. (let* ((str "Lorem ipsum dolor sit amet, consectetur…")
  568. (source (with-fluids ((%default-port-encoding "UTF-8"))
  569. (open-string-input-port str)))
  570. (reads '())
  571. (read! (lambda (bv start count)
  572. (set! reads (cons count reads))
  573. (let ((r (get-bytevector-n! source bv start count)))
  574. (if (eof-object? r)
  575. 0
  576. r))))
  577. (port (make-custom-binary-input-port "the port" read!
  578. #f #f #f)))
  579. (setvbuf port 'none)
  580. (let ((ret (list (get-bytevector-n port 6)
  581. (get-bytevector-n port 12)
  582. (begin
  583. (setvbuf port 'block 777)
  584. (get-bytevector-n port 42))
  585. (get-bytevector-n port 42))))
  586. (zip (reverse reads)
  587. (map (lambda (obj)
  588. (if (bytevector? obj)
  589. (utf8->string obj)
  590. obj))
  591. ret)))))
  592. (pass-if-equal "custom binary input port, buffered then unbuffered"
  593. `((18
  594. 42 14 ; scm_c_read tries to fill the 42-byte buffer
  595. 42)
  596. ("Lorem " "ipsum dolor " "sit amet, consectetur bla…" ,(eof-object)))
  597. (let* ((str "Lorem ipsum dolor sit amet, consectetur bla…")
  598. (source (with-fluids ((%default-port-encoding "UTF-8"))
  599. (open-string-input-port str)))
  600. (reads '())
  601. (read! (lambda (bv start count)
  602. (set! reads (cons count reads))
  603. (let ((r (get-bytevector-n! source bv start count)))
  604. (if (eof-object? r)
  605. 0
  606. r))))
  607. (port (make-custom-binary-input-port "the port" read!
  608. #f #f #f)))
  609. (setvbuf port 'block 18)
  610. (let ((ret (list (get-bytevector-n port 6)
  611. (get-bytevector-n port 12)
  612. (begin
  613. (setvbuf port 'none)
  614. (get-bytevector-n port 42))
  615. (get-bytevector-n port 42))))
  616. (list (reverse reads)
  617. (map (lambda (obj)
  618. (if (bytevector? obj)
  619. (utf8->string obj)
  620. obj))
  621. ret)))))
  622. (pass-if "custom binary input port `close-proc' is called"
  623. (let* ((closed? #f)
  624. (read! (lambda (bv start count) 0))
  625. (get-pos (lambda () 0))
  626. (set-pos! (lambda (pos) #f))
  627. (close! (lambda () (set! closed? #t)))
  628. (port (make-custom-binary-input-port "the port" read!
  629. get-pos set-pos!
  630. close!)))
  631. (close-port port)
  632. (gc) ; Test for marking a closed port.
  633. closed?))
  634. (pass-if "standard-input-port is binary"
  635. (with-fluids ((%default-port-encoding "UTF-8"))
  636. (binary-port? (standard-input-port)))))
  637. (define (test-output-file-opener open filename)
  638. (with-fluids ((%default-port-encoding "UTF-8"))
  639. (pass-if "opens binary output port"
  640. (call-with-port (open filename)
  641. (lambda (port)
  642. (put-bytevector port '#vu8(1 2 3))
  643. (and (binary-port? port)
  644. (output-port? port))))))
  645. (pass-if-condition "exception: already-exists"
  646. i/o-file-already-exists-error?
  647. (open filename))
  648. (pass-if "no-fail no-truncate"
  649. (and
  650. (call-with-port (open filename (file-options no-fail no-truncate))
  651. (lambda (port)
  652. (= 0 (port-position port))))
  653. (= 3 (stat:size (stat filename)))))
  654. (pass-if "no-fail"
  655. (and
  656. (call-with-port (open filename (file-options no-fail))
  657. binary-port?)
  658. (= 0 (stat:size (stat filename)))))
  659. (pass-if "buffer-mode none"
  660. (call-with-port (open filename (file-options no-fail)
  661. (buffer-mode none))
  662. (lambda (port)
  663. (eq? (output-port-buffer-mode port) 'none))))
  664. (pass-if "buffer-mode line"
  665. (call-with-port (open filename (file-options no-fail)
  666. (buffer-mode line))
  667. (lambda (port)
  668. (eq? (output-port-buffer-mode port) 'line))))
  669. (pass-if "buffer-mode block"
  670. (call-with-port (open filename (file-options no-fail)
  671. (buffer-mode block))
  672. (lambda (port)
  673. (eq? (output-port-buffer-mode port) 'block))))
  674. (delete-file filename)
  675. (pass-if-condition "exception: does-not-exist"
  676. i/o-file-does-not-exist-error?
  677. (open filename (file-options no-create))))
  678. (with-test-prefix "8.2.10 Output ports"
  679. (with-test-prefix "open-file-output-port"
  680. (test-output-file-opener open-file-output-port (test-file)))
  681. (pass-if "open-string-output-port"
  682. (call-with-values open-string-output-port
  683. (lambda (port proc)
  684. (and (port? port) (thunk? proc)))))
  685. (pass-if-equal "calling string output port truncates port"
  686. '("hello" "" "world")
  687. (call-with-values open-string-output-port
  688. (lambda (port proc)
  689. (display "hello" port)
  690. (let* ((s1 (proc))
  691. (s2 (proc)))
  692. (display "world" port)
  693. (list s1 s2 (proc))))))
  694. (pass-if "open-bytevector-output-port"
  695. (let-values (((port get-content)
  696. (open-bytevector-output-port #f)))
  697. (let ((source (make-bytevector 7777)))
  698. (put-bytevector port source)
  699. (and (bytevector=? (get-content) source)
  700. (bytevector=? (get-content) (make-bytevector 0))))))
  701. (pass-if "bytevector-output-port is binary"
  702. (binary-port? (open-bytevector-output-port)))
  703. (pass-if-equal "bytevector-output-port uses ISO-8859-1 (Guile extension)"
  704. #vu8(194 169 194 169)
  705. (with-fluids ((%default-port-encoding "UTF-8"))
  706. (let-values (((port get-content)
  707. (open-bytevector-output-port)))
  708. (put-string port "©©")
  709. (get-content))))
  710. (pass-if "open-bytevector-output-port [extract after close]"
  711. (let-values (((port get-content)
  712. (open-bytevector-output-port)))
  713. (let ((source (make-bytevector 12345 #xFE)))
  714. (put-bytevector port source)
  715. (close-port port)
  716. (bytevector=? (get-content) source))))
  717. (pass-if "open-bytevector-output-port [put-u8]"
  718. (let-values (((port get-content)
  719. (open-bytevector-output-port)))
  720. (put-u8 port 77)
  721. (and (bytevector=? (get-content) (make-bytevector 1 77))
  722. (bytevector=? (get-content) (make-bytevector 0)))))
  723. (pass-if "open-bytevector-output-port [display]"
  724. (let-values (((port get-content)
  725. (open-bytevector-output-port)))
  726. (display "hello" port)
  727. (and (bytevector=? (get-content) (string->utf8 "hello"))
  728. (bytevector=? (get-content) (make-bytevector 0)))))
  729. (pass-if "bytevector output port supports `port-position'"
  730. (let-values (((port get-content)
  731. (open-bytevector-output-port)))
  732. (let ((source (make-bytevector 7777))
  733. (overwrite (make-bytevector 33)))
  734. (and (port-has-port-position? port)
  735. (port-has-set-port-position!? port)
  736. (begin
  737. (put-bytevector port source)
  738. (= (bytevector-length source)
  739. (port-position port)))
  740. (begin
  741. (set-port-position! port 10)
  742. (= 10 (port-position port)))
  743. (begin
  744. (put-bytevector port overwrite)
  745. (bytevector-copy! overwrite 0 source 10
  746. (bytevector-length overwrite))
  747. (= (port-position port)
  748. (+ 10 (bytevector-length overwrite))))
  749. (bytevector=? (get-content) source)
  750. (bytevector=? (get-content) (make-bytevector 0))))))
  751. (pass-if "make-custom-binary-output-port"
  752. (let ((port (make-custom-binary-output-port "cbop"
  753. (lambda (x y z) 0)
  754. #f #f #f)))
  755. (and (output-port? port)
  756. (binary-port? port)
  757. (not (port-has-port-position? port))
  758. (not (port-has-set-port-position!? port)))))
  759. (pass-if "make-custom-binary-output-port [partial writes]"
  760. (let* ((source (uint-list->bytevector (iota 333)
  761. (native-endianness) 2))
  762. (sink (make-bytevector (bytevector-length source)))
  763. (sink-pos 0)
  764. (eof? #f)
  765. (write! (lambda (bv start count)
  766. (if (= 0 count)
  767. (begin
  768. (set! eof? #t)
  769. 0)
  770. (let ((u8 (bytevector-u8-ref bv start)))
  771. ;; Get one byte at a time.
  772. (bytevector-u8-set! sink sink-pos u8)
  773. (set! sink-pos (+ 1 sink-pos))
  774. 1))))
  775. (port (make-custom-binary-output-port "cbop" write!
  776. #f #f #f)))
  777. (put-bytevector port source)
  778. (force-output port)
  779. (and (= sink-pos (bytevector-length source))
  780. (not eof?)
  781. (bytevector=? sink source))))
  782. (pass-if "make-custom-binary-output-port [full writes]"
  783. (let* ((source (uint-list->bytevector (iota 333)
  784. (native-endianness) 2))
  785. (sink (make-bytevector (bytevector-length source)))
  786. (sink-pos 0)
  787. (eof? #f)
  788. (write! (lambda (bv start count)
  789. (if (= 0 count)
  790. (begin
  791. (set! eof? #t)
  792. 0)
  793. (begin
  794. (bytevector-copy! bv start
  795. sink sink-pos
  796. count)
  797. (set! sink-pos (+ sink-pos count))
  798. count))))
  799. (port (make-custom-binary-output-port "cbop" write!
  800. #f #f #f)))
  801. (put-bytevector port source)
  802. (force-output port)
  803. (and (= sink-pos (bytevector-length source))
  804. (not eof?)
  805. (bytevector=? sink source))))
  806. (pass-if-equal "custom-binary-output-port uses ISO-8859-1 (Guile extension)"
  807. '(194 169 194 169)
  808. (with-fluids ((%default-port-encoding "UTF-8"))
  809. (let* ((sink '())
  810. (write! (lambda (bv start count)
  811. (if (= 0 count) ; EOF
  812. 0
  813. (let ((u8 (bytevector-u8-ref bv start)))
  814. ;; Get one byte at a time.
  815. (set! sink (cons u8 sink))
  816. 1))))
  817. (port (make-custom-binary-output-port "cbop" write!
  818. #f #f #f)))
  819. (put-string port "©©")
  820. (force-output port)
  821. (reverse sink))))
  822. (pass-if "standard-output-port is binary"
  823. (with-fluids ((%default-port-encoding "UTF-8"))
  824. (binary-port? (standard-output-port))))
  825. (pass-if "standard-error-port is binary"
  826. (with-fluids ((%default-port-encoding "UTF-8"))
  827. (binary-port? (standard-error-port)))))
  828. (with-test-prefix "8.2.6 Input and output ports"
  829. (define (check-transcoded-port-mode make-port pred)
  830. (let ((p (make-port "/dev/null" (file-options no-fail))))
  831. (dynamic-wind
  832. (lambda () #t)
  833. (lambda ()
  834. (set! p (transcoded-port p (native-transcoder)))
  835. (pred p))
  836. (lambda () (close-port p)))))
  837. (pass-if "transcoded-port preserves input mode"
  838. (check-transcoded-port-mode open-file-input-port
  839. (lambda (p)
  840. (and (input-port? p)
  841. (not (output-port? p))))))
  842. (pass-if "transcoded-port preserves output mode"
  843. (check-transcoded-port-mode open-file-output-port
  844. (lambda (p)
  845. (and (not (input-port? p))
  846. (output-port? p)))))
  847. (pass-if "transcoded-port preserves input/output mode"
  848. (check-transcoded-port-mode open-file-input/output-port
  849. (lambda (p)
  850. (and (input-port? p) (output-port? p)))))
  851. (pass-if "transcoded-port [output]"
  852. (let ((s "Hello\nÄÖÜ"))
  853. (bytevector=?
  854. (string->utf8 s)
  855. (call-with-bytevector-output-port/transcoded (make-transcoder (utf-8-codec))
  856. (lambda (utf8-port)
  857. (put-string utf8-port s))))))
  858. (pass-if "transcoded-port [input]"
  859. (let ((s "Hello\nÄÖÜ"))
  860. (string=?
  861. s
  862. (get-string-all
  863. (transcoded-port (open-bytevector-input-port (string->utf8 s))
  864. (make-transcoder (utf-8-codec)))))))
  865. (pass-if "transcoded-port [input line]"
  866. (string=? "ÄÖÜ"
  867. (get-line (transcoded-port
  868. (open-bytevector-input-port (string->utf8 "ÄÖÜ\nFooBar"))
  869. (make-transcoder (utf-8-codec))))))
  870. (pass-if "transcoded-port [error handling mode = raise]"
  871. (let* ((t (make-transcoder (utf-8-codec) (native-eol-style)
  872. (error-handling-mode raise)))
  873. (b (open-bytevector-input-port #vu8(255 2 1)))
  874. (tp (transcoded-port b t)))
  875. (guard (c ((i/o-decoding-error? c)
  876. (eq? (i/o-error-port c) tp)))
  877. (get-line tp)
  878. #f))) ; fail if we reach this point
  879. (pass-if "transcoded-port [error handling mode = replace]"
  880. (let* ((t (make-transcoder (utf-8-codec) (native-eol-style)
  881. (error-handling-mode replace)))
  882. (b (open-bytevector-input-port #vu8(255 1 2 3 103 110 117)))
  883. (tp (transcoded-port b t)))
  884. (string-suffix? "gnu" (get-line tp))))
  885. (pass-if "transcoded-port, output [error handling mode = raise]"
  886. (let-values (((p get)
  887. (open-bytevector-output-port)))
  888. (let* ((t (make-transcoder (latin-1-codec) (native-eol-style)
  889. (error-handling-mode raise)))
  890. (tp (transcoded-port p t)))
  891. (setvbuf tp 'none)
  892. (guard (c ((i/o-encoding-error? c)
  893. (and (eq? (i/o-error-port c) tp)
  894. (char=? (i/o-encoding-error-char c) #\λ)
  895. (bytevector=? (get) (string->utf8 "The letter ")))))
  896. (put-string tp "The letter λ cannot be represented in Latin-1.")
  897. #f))))
  898. (pass-if "port-transcoder [transcoded port]"
  899. (let* ((p (transcoded-port (open-bytevector-input-port (string->utf8 "foo"))
  900. (make-transcoder (utf-8-codec))))
  901. (t (port-transcoder p)))
  902. (and t
  903. (transcoder-codec t)
  904. (eq? (native-eol-style)
  905. (transcoder-eol-style t))
  906. (eq? (error-handling-mode replace)
  907. (transcoder-error-handling-mode t))))))
  908. (with-test-prefix "8.2.9 Textual input"
  909. (pass-if "get-string-n [short]"
  910. (let ((port (open-input-string "GNU Guile")))
  911. (string=? "GNU " (get-string-n port 4))))
  912. (pass-if "get-string-n [long]"
  913. (let ((port (open-input-string "GNU Guile")))
  914. (string=? "GNU Guile" (get-string-n port 256))))
  915. (pass-if "get-string-n [eof]"
  916. (let ((port (open-input-string "")))
  917. (eof-object? (get-string-n port 4))))
  918. (pass-if "get-string-n! [short]"
  919. (let ((port (open-input-string "GNU Guile"))
  920. (s (string-copy "Isn't XXX great?")))
  921. (and (= 3 (get-string-n! port s 6 3))
  922. (string=? s "Isn't GNU great?"))))
  923. (with-test-prefix "read error"
  924. (pass-if-condition "get-char" i/o-read-error?
  925. (get-char (make-failing-port)))
  926. (pass-if-condition "lookahead-char" i/o-read-error?
  927. (lookahead-char (make-failing-port)))
  928. ;; FIXME: these are not yet exception-correct
  929. #|
  930. (pass-if-condition "get-string-n" i/o-read-error?
  931. (get-string-n (make-failing-port) 5))
  932. (pass-if-condition "get-string-n!" i/o-read-error?
  933. (get-string-n! (make-failing-port) (make-string 5) 0 5))
  934. |#
  935. (pass-if-condition "get-string-all" i/o-read-error?
  936. (get-string-all (make-failing-port 100)))
  937. (pass-if-condition "get-line" i/o-read-error?
  938. (get-line (make-failing-port)))
  939. (pass-if-condition "get-datum" i/o-read-error?
  940. (get-datum (make-failing-port)))))
  941. (define (encoding-error-predicate char)
  942. (lambda (c)
  943. (and (i/o-encoding-error? c)
  944. (char=? char (i/o-encoding-error-char c)))))
  945. (with-test-prefix "8.2.12 Textual Output"
  946. (with-test-prefix "write error"
  947. (pass-if-condition "put-char" i/o-write-error?
  948. (put-char (make-failing-port) #\G))
  949. (pass-if-condition "put-string" i/o-write-error?
  950. (put-string (make-failing-port) "Hello World!"))
  951. (pass-if-condition "put-datum" i/o-write-error?
  952. (put-datum (make-failing-port) '(hello world!))))
  953. (with-test-prefix "encoding error"
  954. (pass-if-condition "put-char" (encoding-error-predicate #\λ)
  955. (call-with-bytevector-output-port/transcoded
  956. (make-transcoder (latin-1-codec)
  957. (native-eol-style)
  958. (error-handling-mode raise))
  959. (lambda (port)
  960. (put-char port #\λ))))
  961. (pass-if-condition "put-string" (encoding-error-predicate #\λ)
  962. (call-with-bytevector-output-port/transcoded
  963. (make-transcoder (latin-1-codec)
  964. (native-eol-style)
  965. (error-handling-mode raise))
  966. (lambda (port)
  967. (put-string port "FooλBar"))))))
  968. (with-test-prefix "8.3 Simple I/O"
  969. (with-test-prefix "read error"
  970. (pass-if-condition "read-char" i/o-read-error?
  971. (read-char (make-failing-port)))
  972. (pass-if-condition "peek-char" i/o-read-error?
  973. (peek-char (make-failing-port)))
  974. (pass-if-condition "read" i/o-read-error?
  975. (read (make-failing-port))))
  976. (with-test-prefix "write error"
  977. (pass-if-condition "display" i/o-write-error?
  978. (display "Hi there!" (make-failing-port)))
  979. (pass-if-condition "write" i/o-write-error?
  980. (write '(hi there!) (make-failing-port)))
  981. (pass-if-condition "write-char" i/o-write-error?
  982. (write-char #\G (make-failing-port)))
  983. (pass-if-condition "newline" i/o-write-error?
  984. (newline (make-failing-port))))
  985. (let ((filename (test-file)))
  986. ;; ensure the test file exists
  987. (call-with-output-file filename
  988. (lambda (port) (write "foo" port)))
  989. (pass-if "call-with-input-file [port is textual]"
  990. (call-with-input-file filename textual-port?))
  991. (pass-if-condition "call-with-input-file [exception: not-found]"
  992. i/o-file-does-not-exist-error?
  993. (call-with-input-file ",this-is-highly-unlikely-to-exist!"
  994. values))
  995. (pass-if-condition "call-with-output-file [exception: already-exists]"
  996. i/o-file-already-exists-error?
  997. (call-with-output-file filename
  998. values))
  999. (delete-file filename)))
  1000. ;; Used for a lot of the make-custom-input/output tests to stub out
  1001. ;; the read/write section for whatever part we're ignoring
  1002. (define dummy-write! (const 0))
  1003. (define dummy-read! (const 0))
  1004. (with-test-prefix "8.2.13 Input/output ports"
  1005. (with-test-prefix "open-file-input/output-port [output]"
  1006. (test-output-file-opener open-file-input/output-port (test-file)))
  1007. (with-test-prefix "open-file-input/output-port [input]"
  1008. (test-input-file-opener open-file-input/output-port (test-file)))
  1009. ;; Custom binary input/output tests. Most of these are simple
  1010. ;; ports of the custom-binary-input-port tests or custom-binary-ouput-port
  1011. ;; tests, simply ported to use a custom-binary-input/output port.
  1012. ;; The copy-pasta is strong here; a diet lighter in spaghetti may wish
  1013. ;; to make the previous tests more reusable.
  1014. (pass-if "make-custom-binary-input/output-port"
  1015. (let* ((source (make-bytevector 7777))
  1016. (read! (let ((pos 0)
  1017. (len (bytevector-length source)))
  1018. (lambda (bv start count)
  1019. (let ((amount (min count (- len pos))))
  1020. (if (> amount 0)
  1021. (bytevector-copy! source pos
  1022. bv start amount))
  1023. (set! pos (+ pos amount))
  1024. amount))))
  1025. (write! (lambda (x y z) 0))
  1026. (port (make-custom-binary-input/output-port
  1027. "the port" read! write!
  1028. #f #f #f)))
  1029. (and (binary-port? port)
  1030. (input-port? port)
  1031. (output-port? port)
  1032. (bytevector=? (get-bytevector-all port) source)
  1033. (not (port-has-port-position? port))
  1034. (not (port-has-set-port-position!? port)))))
  1035. (pass-if-equal "make-custom-binary-input/output-port uses ISO-8859-1 (Guile \
  1036. extension) [input]"
  1037. "©©"
  1038. (with-fluids ((%default-port-encoding "UTF-8"))
  1039. (let* ((source #vu8(194 169 194 169))
  1040. (read! (let ((pos 0)
  1041. (len (bytevector-length source)))
  1042. (lambda (bv start count)
  1043. (let ((amount (min count (- len pos))))
  1044. (if (> amount 0)
  1045. (bytevector-copy! source pos
  1046. bv start amount))
  1047. (set! pos (+ pos amount))
  1048. amount))))
  1049. (port (make-custom-binary-input/output-port
  1050. "the port" read! dummy-write!
  1051. #f #f #f)))
  1052. (get-string-all port))))
  1053. (pass-if "custom binary input/output port does not support `port-position'"
  1054. (let* ((str "Hello Port!")
  1055. (source (open-bytevector-input-port
  1056. (u8-list->bytevector
  1057. (map char->integer (string->list str)))))
  1058. (read! (lambda (bv start count)
  1059. (let ((r (get-bytevector-n! source bv start count)))
  1060. (if (eof-object? r)
  1061. 0
  1062. r))))
  1063. (port (make-custom-binary-input/output-port
  1064. "the port" read! dummy-write!
  1065. #f #f #f)))
  1066. (not (or (port-has-port-position? port)
  1067. (port-has-set-port-position!? port)))))
  1068. (pass-if-exception "custom binary input/output port 'read!' returns too much"
  1069. exception:out-of-range
  1070. ;; In Guile <= 2.0.9 this would segfault.
  1071. (let* ((read! (lambda (bv start count)
  1072. (+ count 4242)))
  1073. (port (make-custom-binary-input/output-port
  1074. "the port" read! dummy-write!
  1075. #f #f #f)))
  1076. (get-bytevector-all port)))
  1077. (pass-if-equal "custom binary input/output port supports `port-position', \
  1078. not `set-port-position!'"
  1079. 42
  1080. (let ((port (make-custom-binary-input/output-port
  1081. "the port" (const 0) dummy-write!
  1082. (const 42) #f #f)))
  1083. (and (port-has-port-position? port)
  1084. (not (port-has-set-port-position!? port))
  1085. (port-position port))))
  1086. (pass-if "custom binary input/output port supports `port-position'"
  1087. (let* ((str "Hello Port!")
  1088. (source (open-bytevector-input-port
  1089. (u8-list->bytevector
  1090. (map char->integer (string->list str)))))
  1091. (read! (lambda (bv start count)
  1092. (let ((r (get-bytevector-n! source bv start count)))
  1093. (if (eof-object? r)
  1094. 0
  1095. r))))
  1096. (get-pos (lambda ()
  1097. (port-position source)))
  1098. (set-pos! (lambda (pos)
  1099. (set-port-position! source pos)))
  1100. (port (make-custom-binary-input/output-port
  1101. "the port" read! dummy-write!
  1102. get-pos set-pos! #f)))
  1103. (and (port-has-port-position? port)
  1104. (= 0 (port-position port))
  1105. (port-has-set-port-position!? port)
  1106. (begin
  1107. (set-port-position! port 6)
  1108. (= 6 (port-position port)))
  1109. (bytevector=? (get-bytevector-all port)
  1110. (u8-list->bytevector
  1111. (map char->integer (string->list "Port!")))))))
  1112. (pass-if-equal "custom binary input/output port buffered partial reads"
  1113. "Hello Port!"
  1114. ;; Check what happens when READ! returns less than COUNT bytes.
  1115. (let* ((src (string->utf8 "Hello Port!"))
  1116. (chunks '(2 4 5)) ; provide 2 bytes, then 4, etc.
  1117. (offset 0)
  1118. (read! (lambda (bv start count)
  1119. (match chunks
  1120. ((count rest ...)
  1121. (bytevector-copy! src offset bv start count)
  1122. (set! chunks rest)
  1123. (set! offset (+ offset count))
  1124. count)
  1125. (()
  1126. 0))))
  1127. (port (make-custom-binary-input/output-port
  1128. "the port" read! dummy-write!
  1129. #f #f #f)))
  1130. (get-string-all port)))
  1131. (pass-if-equal "custom binary input/output port unbuffered & 'port-position'"
  1132. '(0 2 5 11)
  1133. ;; Check that the value returned by 'port-position' is correct, and
  1134. ;; that each 'port-position' call leads one call to the
  1135. ;; 'get-position' method.
  1136. (let* ((str "Hello Port!")
  1137. (output (make-bytevector (string-length str)))
  1138. (source (with-fluids ((%default-port-encoding "UTF-8"))
  1139. (open-string-input-port str)))
  1140. (read! (lambda (bv start count)
  1141. (let ((r (get-bytevector-n! source bv start count)))
  1142. (if (eof-object? r)
  1143. 0
  1144. r))))
  1145. (pos '())
  1146. (get-pos (lambda ()
  1147. (let ((p (port-position source)))
  1148. (set! pos (cons p pos))
  1149. p)))
  1150. (port (make-custom-binary-input/output-port
  1151. "the port" read! dummy-write!
  1152. get-pos #f #f)))
  1153. (setvbuf port 'none)
  1154. (and (= 0 (port-position port))
  1155. (begin
  1156. (get-bytevector-n! port output 0 2)
  1157. (= 2 (port-position port)))
  1158. (begin
  1159. (get-bytevector-n! port output 2 3)
  1160. (= 5 (port-position port)))
  1161. (let ((bv (string->utf8 (get-string-all port))))
  1162. (bytevector-copy! bv 0 output 5 (bytevector-length bv))
  1163. (= (string-length str) (port-position port)))
  1164. (bytevector=? output (string->utf8 str))
  1165. (reverse pos))))
  1166. (pass-if-equal "custom binary input/output port unbuffered & 'read!' calls"
  1167. `((2 "He") (3 "llo") (42 " Port!"))
  1168. (let* ((str "Hello Port!")
  1169. (source (with-fluids ((%default-port-encoding "UTF-8"))
  1170. (open-string-input-port str)))
  1171. (reads '())
  1172. (read! (lambda (bv start count)
  1173. (set! reads (cons count reads))
  1174. (let ((r (get-bytevector-n! source bv start count)))
  1175. (if (eof-object? r)
  1176. 0
  1177. r))))
  1178. (port (make-custom-binary-input/output-port
  1179. "the port" read! dummy-write!
  1180. #f #f #f)))
  1181. (setvbuf port 'none)
  1182. (let ((ret (list (get-bytevector-n port 2)
  1183. (get-bytevector-n port 3)
  1184. (get-bytevector-n port 42))))
  1185. (zip (reverse reads)
  1186. (map (lambda (obj)
  1187. (if (bytevector? obj)
  1188. (utf8->string obj)
  1189. obj))
  1190. ret)))))
  1191. (pass-if-equal "custom binary input/output port unbuffered & 'get-string-all'"
  1192. (make-string 1000 #\a)
  1193. ;; In Guile 2.0.11 this test would lead to a buffer overrun followed
  1194. ;; by an assertion failure. See <http://bugs.gnu.org/19621>.
  1195. (let* ((input (with-fluids ((%default-port-encoding #f))
  1196. (open-input-string (make-string 1000 #\a))))
  1197. (read! (lambda (bv index count)
  1198. (let ((n (get-bytevector-n! input bv index
  1199. count)))
  1200. (if (eof-object? n) 0 n))))
  1201. (port (make-custom-binary-input/output-port
  1202. "foo" read! dummy-write!
  1203. #f #f #f)))
  1204. (setvbuf port 'none)
  1205. (get-string-all port)))
  1206. (pass-if-equal "custom binary input/output port unbuffered UTF-8 & \
  1207. 'get-string-all'"
  1208. (make-string 1000 #\λ)
  1209. ;; In Guile 2.0.11 this test would lead to a buffer overrun followed
  1210. ;; by an assertion failure. See <http://bugs.gnu.org/19621>.
  1211. (let* ((input (with-fluids ((%default-port-encoding "UTF-8"))
  1212. (open-input-string (make-string 1000 #\λ))))
  1213. (read! (lambda (bv index count)
  1214. (let ((n (get-bytevector-n! input bv index
  1215. count)))
  1216. (if (eof-object? n) 0 n))))
  1217. (port (make-custom-binary-input/output-port
  1218. "foo" read! dummy-write!
  1219. #f #f #f)))
  1220. (setvbuf port 'none)
  1221. (set-port-encoding! port "UTF-8")
  1222. (get-string-all port)))
  1223. (pass-if-equal "custom binary input/output port, unbuffered then buffered"
  1224. `((6 "Lorem ") (12 "ipsum dolor ") (777 "sit amet, consectetur…")
  1225. (777 ,(eof-object)))
  1226. (let* ((str "Lorem ipsum dolor sit amet, consectetur…")
  1227. (source (with-fluids ((%default-port-encoding "UTF-8"))
  1228. (open-string-input-port str)))
  1229. (reads '())
  1230. (read! (lambda (bv start count)
  1231. (set! reads (cons count reads))
  1232. (let ((r (get-bytevector-n! source bv start count)))
  1233. (if (eof-object? r)
  1234. 0
  1235. r))))
  1236. (port (make-custom-binary-input/output-port
  1237. "the port" read! dummy-write!
  1238. #f #f #f)))
  1239. (setvbuf port 'none)
  1240. (let ((ret (list (get-bytevector-n port 6)
  1241. (get-bytevector-n port 12)
  1242. (begin
  1243. (setvbuf port 'block 777)
  1244. (get-bytevector-n port 42))
  1245. (get-bytevector-n port 42))))
  1246. (zip (reverse reads)
  1247. (map (lambda (obj)
  1248. (if (bytevector? obj)
  1249. (utf8->string obj)
  1250. obj))
  1251. ret)))))
  1252. (pass-if-equal "custom binary input/output port, buffered then unbuffered"
  1253. `((18
  1254. 42 14 ; scm_c_read tries to fill the 42-byte buffer
  1255. 42)
  1256. ("Lorem " "ipsum dolor " "sit amet, consectetur bla…" ,(eof-object)))
  1257. (let* ((str "Lorem ipsum dolor sit amet, consectetur bla…")
  1258. (source (with-fluids ((%default-port-encoding "UTF-8"))
  1259. (open-string-input-port str)))
  1260. (reads '())
  1261. (read! (lambda (bv start count)
  1262. (set! reads (cons count reads))
  1263. (let ((r (get-bytevector-n! source bv start count)))
  1264. (if (eof-object? r)
  1265. 0
  1266. r))))
  1267. (port (make-custom-binary-input/output-port
  1268. "the port" read! dummy-write!
  1269. #f #f #f)))
  1270. (setvbuf port 'block 18)
  1271. (let ((ret (list (get-bytevector-n port 6)
  1272. (get-bytevector-n port 12)
  1273. (begin
  1274. (setvbuf port 'none)
  1275. (get-bytevector-n port 42))
  1276. (get-bytevector-n port 42))))
  1277. (list (reverse reads)
  1278. (map (lambda (obj)
  1279. (if (bytevector? obj)
  1280. (utf8->string obj)
  1281. obj))
  1282. ret)))))
  1283. (pass-if "custom binary input/output port `close-proc' is called"
  1284. (let* ((closed? #f)
  1285. (read! (lambda (bv start count) 0))
  1286. (get-pos (lambda () 0))
  1287. (set-pos! (lambda (pos) #f))
  1288. (close! (lambda () (set! closed? #t)))
  1289. (port (make-custom-binary-input/output-port
  1290. "the port" read! dummy-write!
  1291. get-pos set-pos! close!)))
  1292. (close-port port)
  1293. (gc) ; Test for marking a closed port.
  1294. closed?))
  1295. (pass-if "make-custom-binary-input/output-port [partial writes]"
  1296. (let* ((source (uint-list->bytevector (iota 333)
  1297. (native-endianness) 2))
  1298. (sink (make-bytevector (bytevector-length source)))
  1299. (sink-pos 0)
  1300. (eof? #f)
  1301. (write! (lambda (bv start count)
  1302. (if (= 0 count)
  1303. (begin
  1304. (set! eof? #t)
  1305. 0)
  1306. (let ((u8 (bytevector-u8-ref bv start)))
  1307. ;; Get one byte at a time.
  1308. (bytevector-u8-set! sink sink-pos u8)
  1309. (set! sink-pos (+ 1 sink-pos))
  1310. 1))))
  1311. (port (make-custom-binary-input/output-port
  1312. "cbop" dummy-read! write!
  1313. #f #f #f)))
  1314. (put-bytevector port source)
  1315. (force-output port)
  1316. (and (= sink-pos (bytevector-length source))
  1317. (not eof?)
  1318. (bytevector=? sink source))))
  1319. (pass-if "make-custom-binary-input/output-port [full writes]"
  1320. (let* ((source (uint-list->bytevector (iota 333)
  1321. (native-endianness) 2))
  1322. (sink (make-bytevector (bytevector-length source)))
  1323. (sink-pos 0)
  1324. (eof? #f)
  1325. (write! (lambda (bv start count)
  1326. (if (= 0 count)
  1327. (begin
  1328. (set! eof? #t)
  1329. 0)
  1330. (begin
  1331. (bytevector-copy! bv start
  1332. sink sink-pos
  1333. count)
  1334. (set! sink-pos (+ sink-pos count))
  1335. count))))
  1336. (port (make-custom-binary-input/output-port
  1337. "cbop" dummy-read! write!
  1338. #f #f #f)))
  1339. (put-bytevector port source)
  1340. (force-output port)
  1341. (and (= sink-pos (bytevector-length source))
  1342. (not eof?)
  1343. (bytevector=? sink source))))
  1344. (pass-if-equal "custom-binary-output-port uses ISO-8859-1 (Guile extension)\
  1345. [output]"
  1346. '(194 169 194 169)
  1347. (with-fluids ((%default-port-encoding "UTF-8"))
  1348. (let* ((sink '())
  1349. (write! (lambda (bv start count)
  1350. (if (= 0 count) ; EOF
  1351. 0
  1352. (let ((u8 (bytevector-u8-ref bv start)))
  1353. ;; Get one byte at a time.
  1354. (set! sink (cons u8 sink))
  1355. 1))))
  1356. (port (make-custom-binary-input/output-port
  1357. "cbop" dummy-read! write!
  1358. #f #f #f)))
  1359. (put-string port "©©")
  1360. (force-output port)
  1361. (reverse sink))))
  1362. )
  1363. (define exception:encoding-error
  1364. '(encoding-error . ""))
  1365. (define exception:decoding-error
  1366. '(decoding-error . ""))
  1367. (with-test-prefix "ascii string"
  1368. (let ((s "Hello, World!"))
  1369. ;; For ASCII, all of these encodings should be the same.
  1370. (pass-if "to ascii bytevector"
  1371. (equal? (string->bytevector s (make-transcoder "ASCII"))
  1372. #vu8(72 101 108 108 111 44 32 87 111 114 108 100 33)))
  1373. (pass-if "to ascii bytevector (length check)"
  1374. (equal? (string-length s)
  1375. (bytevector-length
  1376. (string->bytevector s (make-transcoder "ascii")))))
  1377. (pass-if "from ascii bytevector"
  1378. (equal? s
  1379. (bytevector->string
  1380. (string->bytevector s (make-transcoder "ascii"))
  1381. (make-transcoder "ascii"))))
  1382. (pass-if "to utf-8 bytevector"
  1383. (equal? (string->bytevector s (make-transcoder "ASCII"))
  1384. (string->bytevector s (make-transcoder "utf-8"))))
  1385. (pass-if "to UTF-8 bytevector (testing encoding case sensitivity)"
  1386. (equal? (string->bytevector s (make-transcoder "ascii"))
  1387. (string->bytevector s (make-transcoder "UTF-8"))))
  1388. (pass-if "from utf-8 bytevector"
  1389. (equal? s
  1390. (bytevector->string
  1391. (string->bytevector s (make-transcoder "utf-8"))
  1392. (make-transcoder "utf-8"))))
  1393. (pass-if "to latin1 bytevector"
  1394. (equal? (string->bytevector s (make-transcoder "ASCII"))
  1395. (string->bytevector s (make-transcoder "latin1"))))
  1396. (pass-if "from latin1 bytevector"
  1397. (equal? s
  1398. (bytevector->string
  1399. (string->bytevector s (make-transcoder "utf-8"))
  1400. (make-transcoder "utf-8"))))))
  1401. (with-test-prefix "narrow non-ascii string"
  1402. (let ((s "été"))
  1403. (pass-if "to latin1 bytevector"
  1404. (equal? (string->bytevector s (make-transcoder "latin1"))
  1405. #vu8(233 116 233)))
  1406. (pass-if "to latin1 bytevector (length check)"
  1407. (equal? (string-length s)
  1408. (bytevector-length
  1409. (string->bytevector s (make-transcoder "latin1")))))
  1410. (pass-if "from latin1 bytevector"
  1411. (equal? s
  1412. (bytevector->string
  1413. (string->bytevector s (make-transcoder "latin1"))
  1414. (make-transcoder "latin1"))))
  1415. (pass-if "to utf-8 bytevector"
  1416. (equal? (string->bytevector s (make-transcoder "utf-8"))
  1417. #vu8(195 169 116 195 169)))
  1418. (pass-if "from utf-8 bytevector"
  1419. (equal? s
  1420. (bytevector->string
  1421. (string->bytevector s (make-transcoder "utf-8"))
  1422. (make-transcoder "utf-8"))))
  1423. (pass-if-exception "encode latin1 as ascii" exception:encoding-error
  1424. (string->bytevector s (make-transcoder "ascii"
  1425. (native-eol-style)
  1426. (error-handling-mode raise))))
  1427. (pass-if-exception "misparse latin1 as utf8" exception:decoding-error
  1428. (bytevector->string
  1429. (string->bytevector s (make-transcoder "latin1"))
  1430. (make-transcoder "utf-8"
  1431. (native-eol-style)
  1432. (error-handling-mode raise))))
  1433. (pass-if "misparse latin1 as utf8 with substitutions"
  1434. (equal? (bytevector->string
  1435. (string->bytevector s (make-transcoder "latin1"))
  1436. (make-transcoder "utf-8" (native-eol-style)
  1437. (error-handling-mode replace)))
  1438. "\uFFFDt\uFFFD"))
  1439. (pass-if-exception "misparse latin1 as ascii" exception:decoding-error
  1440. (bytevector->string (string->bytevector s (make-transcoder "latin1"))
  1441. (make-transcoder "ascii"
  1442. (native-eol-style)
  1443. (error-handling-mode raise))))))
  1444. (with-test-prefix "wide non-ascii string"
  1445. (let ((s "ΧΑΟΣ"))
  1446. (pass-if "to utf-8 bytevector"
  1447. (equal? (string->bytevector s (make-transcoder "utf-8"))
  1448. #vu8(206 167 206 145 206 159 206 163) ))
  1449. (pass-if "from utf-8 bytevector"
  1450. (equal? s
  1451. (bytevector->string
  1452. (string->bytevector s (make-transcoder "utf-8"))
  1453. (make-transcoder "utf-8"))))
  1454. (pass-if-exception "encode as ascii" exception:encoding-error
  1455. (string->bytevector s (make-transcoder "ascii"
  1456. (native-eol-style)
  1457. (error-handling-mode raise))))
  1458. (pass-if-exception "encode as latin1" exception:encoding-error
  1459. (string->bytevector s (make-transcoder "latin1"
  1460. (native-eol-style)
  1461. (error-handling-mode raise))))
  1462. (pass-if "encode as ascii with substitutions"
  1463. (equal? (make-string (string-length s) #\?)
  1464. (bytevector->string
  1465. (string->bytevector s (make-transcoder
  1466. "ascii"
  1467. (native-eol-style)
  1468. (error-handling-mode replace)))
  1469. (make-transcoder "ascii"))))))
  1470. ;;; Local Variables:
  1471. ;;; mode: scheme
  1472. ;;; eval: (put 'guard 'scheme-indent-function 1)
  1473. ;;; End: