r6rs-ports.test 65 KB

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