ports.test 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. ;;;; ports.test --- test suite for Guile I/O ports -*- scheme -*-
  2. ;;;; Jim Blandy <jimb@red-bean.com> --- May 1999
  3. ;;;;
  4. ;;;; Copyright (C) 1999 Free Software Foundation, Inc.
  5. ;;;;
  6. ;;;; This program is free software; you can redistribute it and/or modify
  7. ;;;; it under the terms of the GNU General Public License as published by
  8. ;;;; the Free Software Foundation; either version 2, or (at your option)
  9. ;;;; any later version.
  10. ;;;;
  11. ;;;; This program 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
  14. ;;;; GNU General Public License for more details.
  15. ;;;;
  16. ;;;; You should have received a copy of the GNU General Public License
  17. ;;;; along with this software; see the file COPYING. If not, write to
  18. ;;;; the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  19. ;;;; Boston, MA 02111-1307 USA
  20. (use-modules (test-suite lib)
  21. (ice-9 popen))
  22. (define (display-line . args)
  23. (for-each display args)
  24. (newline))
  25. (define (test-file)
  26. (tmpnam))
  27. ;;;; Some general utilities for testing ports.
  28. ;;; Read from PORT until EOF, and return the result as a string.
  29. (define (read-all port)
  30. (let loop ((chars '()))
  31. (let ((char (read-char port)))
  32. (if (eof-object? char)
  33. (list->string (reverse! chars))
  34. (loop (cons char chars))))))
  35. (define (read-file filename)
  36. (let* ((port (open-input-file filename))
  37. (string (read-all port)))
  38. (close-port port)
  39. string))
  40. ;;;; Normal file ports.
  41. ;;; Write out an s-expression, and read it back.
  42. (let ((string '("From fairest creatures we desire increase,"
  43. "That thereby beauty's rose might never die,"))
  44. (filename (test-file)))
  45. (let ((port (open-output-file filename)))
  46. (write string port)
  47. (close-port port))
  48. (let ((port (open-input-file filename)))
  49. (let ((in-string (read port)))
  50. (pass-if "file: write and read back list of strings"
  51. (equal? string in-string)))
  52. (close-port port))
  53. (delete-file filename))
  54. ;;; Write out a string, and read it back a character at a time.
  55. (let ((string "This is a test string\nwith no newline at the end")
  56. (filename (test-file)))
  57. (let ((port (open-output-file filename)))
  58. (display string port)
  59. (close-port port))
  60. (let ((in-string (read-file filename)))
  61. (pass-if "file: write and read back characters"
  62. (equal? string in-string)))
  63. (delete-file filename))
  64. ;;; Buffered input/output port with seeking.
  65. (let* ((filename (test-file))
  66. (port (open-file filename "w+")))
  67. (display "J'Accuse" port)
  68. (seek port -1 SEEK_CUR)
  69. (pass-if "file: r/w 1"
  70. (char=? (read-char port) #\e))
  71. (pass-if "file: r/w 2"
  72. (eof-object? (read-char port)))
  73. (seek port -1 SEEK_CUR)
  74. (write-char #\x port)
  75. (seek port 7 SEEK_SET)
  76. (pass-if "file: r/w 3"
  77. (char=? (read-char port) #\x))
  78. (seek port -2 SEEK_END)
  79. (pass-if "file: r/w 4"
  80. (char=? (read-char port) #\s))
  81. (delete-file filename))
  82. ;;; Unbuffered input/output port with seeking.
  83. (let* ((filename (test-file))
  84. (port (open-file filename "w+0")))
  85. (display "J'Accuse" port)
  86. (seek port -1 SEEK_CUR)
  87. (pass-if "file: ub r/w 1"
  88. (char=? (read-char port) #\e))
  89. (pass-if "file: ub r/w 2"
  90. (eof-object? (read-char port)))
  91. (seek port -1 SEEK_CUR)
  92. (write-char #\x port)
  93. (seek port 7 SEEK_SET)
  94. (pass-if "file: ub r/w 3"
  95. (char=? (read-char port) #\x))
  96. (seek port -2 SEEK_END)
  97. (pass-if "file: ub r/w 4"
  98. (char=? (read-char port) #\s))
  99. (delete-file filename))
  100. ;;; Buffered output-only and input-only ports with seeking.
  101. (let* ((filename (test-file))
  102. (port (open-output-file filename)))
  103. (display "J'Accuse" port)
  104. (pass-if "file: out tell"
  105. (= (seek port 0 SEEK_CUR) 8))
  106. (seek port -1 SEEK_CUR)
  107. (write-char #\x port)
  108. (close-port port)
  109. (let ((iport (open-input-file filename)))
  110. (pass-if "file: in tell 0"
  111. (= (seek iport 0 SEEK_CUR) 0))
  112. (read-char iport)
  113. (pass-if "file: in tell 1"
  114. (= (seek iport 0 SEEK_CUR) 1))
  115. (unread-char #\z iport)
  116. (pass-if "file: in tell 0 after unread"
  117. (= (seek iport 0 SEEK_CUR) 0))
  118. (pass-if "file: unread char still there"
  119. (char=? (read-char iport) #\z))
  120. (seek iport 7 SEEK_SET)
  121. (pass-if "file: in last char"
  122. (char=? (read-char iport) #\x))
  123. (close-port iport))
  124. (delete-file filename))
  125. ;;; unusual characters.
  126. (let* ((filename (test-file))
  127. (port (open-output-file filename)))
  128. (display (string #\nul (integer->char 255) (integer->char 128)
  129. #\nul) port)
  130. (close-port port)
  131. (let* ((port (open-input-file filename))
  132. (line (read-line port)))
  133. (pass-if "file: read back NUL 1"
  134. (char=? (string-ref line 0) #\nul))
  135. (pass-if "file: read back 255"
  136. (char=? (string-ref line 1) (integer->char 255)))
  137. (pass-if "file: read back 128"
  138. (char=? (string-ref line 2) (integer->char 128)))
  139. (pass-if "file: read back NUL 2"
  140. (char=? (string-ref line 3) #\nul))
  141. (pass-if "file: EOF"
  142. (eof-object? (read-char port))))
  143. (delete-file filename))
  144. ;;; line buffering mode.
  145. (let* ((filename (test-file))
  146. (port (open-file filename "wl"))
  147. (test-string "one line more or less"))
  148. (write-line test-string port)
  149. (let* ((in-port (open-input-file filename))
  150. (line (read-line in-port)))
  151. (close-port in-port)
  152. (close-port port)
  153. (pass-if "file: line buffering"
  154. (string=? line test-string)))
  155. (delete-file filename))
  156. ;;; ungetting characters and strings.
  157. (with-input-from-string "walk on the moon\nmoon"
  158. (lambda ()
  159. (read-char)
  160. (unread-char #\a (current-input-port))
  161. (pass-if "unread-char"
  162. (char=? (read-char) #\a))
  163. (read-line)
  164. (let ((replacenoid "chicken enchilada"))
  165. (unread-char #\newline (current-input-port))
  166. (unread-string replacenoid (current-input-port))
  167. (pass-if "unread-string"
  168. (string=? (read-line) replacenoid)))
  169. (pass-if "unread residue"
  170. (string=? (read-line) "moon"))))
  171. ;;; non-blocking mode on a port. create a pipe and set O_NONBLOCK on
  172. ;;; the reading end. try to read a byte: should get EAGAIN or
  173. ;;; EWOULDBLOCK error.
  174. (let* ((p (pipe))
  175. (r (car p)))
  176. (fcntl r F_SETFL (logior (fcntl r F_GETFL) O_NONBLOCK))
  177. (pass-if "non-blocking-I/O"
  178. (catch 'system-error
  179. (lambda () (read-char r) #f)
  180. (lambda (key . args)
  181. (and (eq? key 'system-error)
  182. (let ((errno (car (list-ref args 3))))
  183. (or (= errno EAGAIN)
  184. (= errno EWOULDBLOCK))))))))
  185. ;;;; Pipe (popen) ports.
  186. ;;; Run a command, and read its output.
  187. (let* ((pipe (open-pipe "echo 'Howdy there, partner!'" "r"))
  188. (in-string (read-all pipe)))
  189. (close-pipe pipe)
  190. (pass-if "pipe: read"
  191. (equal? in-string "Howdy there, partner!\n")))
  192. ;;; Run a command, send some output to it, and see if it worked.
  193. (let* ((filename (test-file))
  194. (pipe (open-pipe (string-append "grep Mommy > " filename) "w")))
  195. (display "Now Jimmy lives on a mushroom cloud\n" pipe)
  196. (display "Mommy, why does everybody have a bomb?\n" pipe)
  197. (close-pipe pipe)
  198. (let ((in-string (read-file filename)))
  199. (pass-if "pipe: write"
  200. (equal? in-string "Mommy, why does everybody have a bomb?\n")))
  201. (delete-file filename))
  202. ;;;; Void ports. These are so trivial we don't test them.
  203. ;;;; String ports.
  204. (with-test-prefix "string ports"
  205. ;; Write text to a string port.
  206. (let* ((string "Howdy there, partner!")
  207. (in-string (call-with-output-string
  208. (lambda (port)
  209. (display string port)
  210. (newline port)))))
  211. (pass-if "display text"
  212. (equal? in-string (string-append string "\n"))))
  213. ;; Write an s-expression to a string port.
  214. (let* ((sexpr '("more utterly random text" 1729 #(a vector) 3.1415926))
  215. (in-sexpr
  216. (call-with-input-string (call-with-output-string
  217. (lambda (port)
  218. (write sexpr port)))
  219. read)))
  220. (pass-if "write/read sexpr"
  221. (equal? in-sexpr sexpr)))
  222. ;; seeking and unreading from an input string.
  223. (let ((text "that text didn't look random to me"))
  224. (call-with-input-string text
  225. (lambda (p)
  226. (pass-if "input tell 0"
  227. (= (seek p 0 SEEK_CUR) 0))
  228. (read-char p)
  229. (pass-if "input tell 1"
  230. (= (seek p 0 SEEK_CUR) 1))
  231. (unread-char #\x p)
  232. (pass-if "input tell back to 0"
  233. (= (seek p 0 SEEK_CUR) 0))
  234. (pass-if "input ungetted char"
  235. (char=? (read-char p) #\x))
  236. (seek p 0 SEEK_END)
  237. (pass-if "input seek to end"
  238. (= (seek p 0 SEEK_CUR)
  239. (string-length text)))
  240. (unread-char #\x p)
  241. (pass-if "input seek to beginning"
  242. (= (seek p 0 SEEK_SET) 0))
  243. (pass-if "input reread first char"
  244. (char=? (read-char p)
  245. (string-ref text 0))))))
  246. ;; seeking an output string.
  247. (let* ((text "123456789")
  248. (len (string-length text))
  249. (result (call-with-output-string
  250. (lambda (p)
  251. (pass-if "output tell 0"
  252. (= (seek p 0 SEEK_CUR) 0))
  253. (display text p)
  254. (pass-if "output tell end"
  255. (= (seek p 0 SEEK_CUR) len))
  256. (pass-if "output seek to beginning"
  257. (= (seek p 0 SEEK_SET) 0))
  258. (write-char #\a p)
  259. (seek p -1 SEEK_END)
  260. (pass-if "output seek to last char"
  261. (= (seek p 0 SEEK_CUR)
  262. (- len 1)))
  263. (write-char #\b p)))))
  264. (string-set! text 0 #\a)
  265. (string-set! text (- len 1) #\b)
  266. (pass-if "output check"
  267. (string=? text result))))
  268. ;;;; Soft ports. No tests implemented yet.
  269. ;;;; Generic operations across all port types.
  270. (let ((port-loop-temp (test-file)))
  271. ;; Return a list of input ports that all return the same text.
  272. ;; We map tests over this list.
  273. (define (input-port-list text)
  274. ;; Create a text file some of the ports will use.
  275. (let ((out-port (open-output-file port-loop-temp)))
  276. (display text out-port)
  277. (close-port out-port))
  278. (list (open-input-file port-loop-temp)
  279. (open-input-pipe (string-append "cat " port-loop-temp))
  280. (call-with-input-string text (lambda (x) x))
  281. ;; We don't test soft ports at the moment.
  282. ))
  283. (define port-list-names '("file" "pipe" "string"))
  284. ;; Test the line counter.
  285. (define (test-line-counter text second-line final-column)
  286. (with-test-prefix "line counter"
  287. (let ((ports (input-port-list text)))
  288. (for-each
  289. (lambda (port port-name)
  290. (with-test-prefix port-name
  291. (pass-if "at beginning of input"
  292. (= (port-line port) 0))
  293. (pass-if "read first character"
  294. (eqv? (read-char port) #\x))
  295. (pass-if "after reading one character"
  296. (= (port-line port) 0))
  297. (pass-if "read first newline"
  298. (eqv? (read-char port) #\newline))
  299. (pass-if "after reading first newline char"
  300. (= (port-line port) 1))
  301. (pass-if "second line read correctly"
  302. (equal? (read-line port) second-line))
  303. (pass-if "read-line increments line number"
  304. (= (port-line port) 2))
  305. (pass-if "read-line returns EOF"
  306. (let loop ((i 0))
  307. (cond
  308. ((eof-object? (read-line port)) #t)
  309. ((> i 20) #f)
  310. (else (loop (+ i 1))))))
  311. (pass-if "line count is 5 at EOF"
  312. (= (port-line port) 5))
  313. (pass-if "column is correct at EOF"
  314. (= (port-column port) final-column))))
  315. ports port-list-names)
  316. (for-each close-port ports)
  317. (delete-file port-loop-temp))))
  318. (with-test-prefix "newline"
  319. (test-line-counter
  320. (string-append "x\n"
  321. "He who receives an idea from me, receives instruction\n"
  322. "himself without lessening mine; as he who lights his\n"
  323. "taper at mine, receives light without darkening me.\n"
  324. " --- Thomas Jefferson\n")
  325. "He who receives an idea from me, receives instruction"
  326. 0))
  327. (with-test-prefix "no newline"
  328. (test-line-counter
  329. (string-append "x\n"
  330. "He who receives an idea from me, receives instruction\n"
  331. "himself without lessening mine; as he who lights his\n"
  332. "taper at mine, receives light without darkening me.\n"
  333. " --- Thomas Jefferson\n"
  334. "no newline here")
  335. "He who receives an idea from me, receives instruction"
  336. 15)))
  337. ;;;; testing read-delimited and friends
  338. (with-test-prefix "read-delimited!"
  339. (let ((c (make-string 20 #\!)))
  340. (call-with-input-string
  341. "defdef\nghighi\n"
  342. (lambda (port)
  343. (read-delimited! "\n" c port 'concat)
  344. (pass-if "read-delimited! reads a first line"
  345. (string=? c "defdef\n!!!!!!!!!!!!!"))
  346. (read-delimited! "\n" c port 'concat 3)
  347. (pass-if "read-delimited! reads a first line"
  348. (string=? c "defghighi\n!!!!!!!!!!"))))))
  349. ;;;; char-ready?
  350. (call-with-input-string
  351. "howdy"
  352. (lambda (port)
  353. (pass-if "char-ready? returns true on string port"
  354. (char-ready? port))))
  355. ;;; This segfaults on some versions of Guile. We really should run
  356. ;;; the tests in a subprocess...
  357. (call-with-input-string
  358. "howdy"
  359. (lambda (port)
  360. (with-input-from-port
  361. port
  362. (lambda ()
  363. (pass-if "char-ready? returns true on string port as default port"
  364. (char-ready?))))))
  365. ;;;; Close current-input-port, and make sure everyone can handle it.
  366. (with-test-prefix "closing current-input-port"
  367. (for-each (lambda (procedure name)
  368. (with-input-from-port
  369. (call-with-input-string "foo" (lambda (p) p))
  370. (lambda ()
  371. (close-port (current-input-port))
  372. (pass-if name
  373. (signals-error? 'wrong-type-arg (procedure))))))
  374. (list read read-char read-line)
  375. '("read" "read-char" "read-line")))