test-hashtables.scm 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. ;;; Copyright (C) 2024 David Thompson <dave@spritely.institute>
  2. ;;; Copyright (C) 2023, 2024 Robin Templeton
  3. ;;;
  4. ;;; Licensed under the Apache License, Version 2.0 (the "License");
  5. ;;; you may not use this file except in compliance with the License.
  6. ;;; You may obtain a copy of the License at
  7. ;;;
  8. ;;; http://www.apache.org/licenses/LICENSE-2.0
  9. ;;;
  10. ;;; Unless required by applicable law or agreed to in writing, software
  11. ;;; distributed under the License is distributed on an "AS IS" BASIS,
  12. ;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. ;;; See the License for the specific language governing permissions and
  14. ;;; limitations under the License.
  15. ;;; Commentary:
  16. ;;;
  17. ;;; Hashtable tests.
  18. ;;;
  19. ;;; Code:
  20. (use-modules (srfi srfi-64)
  21. (test utils))
  22. (test-begin "test-hashtables")
  23. (with-additional-imports
  24. ((hoot hashtables))
  25. ;; Hashing pairs and lists.
  26. (test-call "228" (lambda () (hash '(a . b) 389)))
  27. (test-call "94" (lambda () (hash '(a b) 389)))
  28. ;; Deeply nested list.
  29. (test-call "69" (lambda () (hash '(a (b (c (d (e (f (g (h (i))))))))) 389)))
  30. ;; Circular list!
  31. (test-call "65"
  32. (lambda ()
  33. (let ((x (list 'a 'b 'c)))
  34. (set-cdr! (cdr (cdr x)) x)
  35. (hash x 389))))
  36. ;; Hash composition should not be commutative.
  37. (test-call "#f" (lambda () (= (hash '(a . b) 389) (hash '(b . a) 389))))
  38. ;; Hashing vectors of different length.
  39. (test-call "200" (lambda () (hash #() 389)))
  40. (test-call "222" (lambda () (hash #(1 2 3) 389)))
  41. ;; Hashing bytevectors of different length.
  42. (test-call "51" (lambda () (hash #vu8() 389)))
  43. (test-call "155" (lambda () (hash #vu8(1) 389)))
  44. (test-call "224" (lambda () (hash #vu8(1 2) 389)))
  45. (test-call "294" (lambda () (hash #vu8(1 2 3) 389)))
  46. (test-call "206" (lambda () (hash #vu8(1 2 3 4) 389)))
  47. ;; Hashing bitvectors of different length.
  48. (test-call "173" (lambda () (hash #* 389)))
  49. (test-call "195" (lambda () (hash #*1010 389)))
  50. (test-call "119" (lambda () (hash #*01010 389)))
  51. ;; Empty bytevector should have different hash than empty bitvector.
  52. (test-call "#f" (lambda () (= (hash #vu8() 389) (hash #* 389))))
  53. ;; Hashing records.
  54. (test-call "222"
  55. (lambda ()
  56. (define-record-type q (make-q a) q? (a q-a))
  57. (hash (make-q 42) 389)))
  58. ;; Ref hit
  59. (test-call "b"
  60. (lambda ()
  61. (let ((ht (make-eq-hashtable)))
  62. (hashtable-set! ht 'a 'b)
  63. (hashtable-ref ht 'a))))
  64. ;; Ref miss
  65. (test-call "#f"
  66. (lambda ()
  67. (let ((ht (make-eq-hashtable)))
  68. (hashtable-set! ht 'x 'y)
  69. (hashtable-ref ht 'a))))
  70. ;; Ref miss with default
  71. (test-call "b"
  72. (lambda ()
  73. (let ((ht (make-eq-hashtable)))
  74. (hashtable-set! ht 'x 'y)
  75. (hashtable-ref ht 'a 'b))))
  76. ;; Key insertion increases size
  77. (test-call "1"
  78. (lambda ()
  79. (let ((ht (make-eq-hashtable)))
  80. (hashtable-set! ht 'a 'b)
  81. (hashtable-size ht))))
  82. ;; Key deletion
  83. (test-call "#f"
  84. (lambda ()
  85. (let ((ht (make-eq-hashtable)))
  86. (hashtable-set! ht 'a 'b)
  87. (hashtable-delete! ht 'a)
  88. (hashtable-contains? ht 'a))))
  89. ;; Key deletion decrements size
  90. (test-call "0"
  91. (lambda ()
  92. (let ((ht (make-eq-hashtable)))
  93. (hashtable-set! ht 'a 'b)
  94. (hashtable-delete! ht 'a)
  95. (hashtable-size ht))))
  96. ;; Key deletion miss does not decrement size
  97. (test-call "1"
  98. (lambda ()
  99. (let ((ht (make-eq-hashtable)))
  100. (hashtable-set! ht 'a 'b)
  101. (hashtable-delete! ht 'c)
  102. (hashtable-size ht))))
  103. ;; Check for existing key
  104. (test-call "#t"
  105. (lambda ()
  106. (let ((ht (make-eq-hashtable)))
  107. (hashtable-set! ht 'a 'b)
  108. (hashtable-contains? ht 'a))))
  109. ;; Copy
  110. (test-call "(2 b d)"
  111. (lambda ()
  112. (let ((ht (make-eq-hashtable)))
  113. (hashtable-set! ht 'a 'b)
  114. (hashtable-set! ht 'c 'd)
  115. (let ((ht* (hashtable-copy ht)))
  116. (list (hashtable-size ht*)
  117. (hashtable-ref ht* 'a)
  118. (hashtable-ref ht* 'c))))))
  119. ;; Clear sets size to 0
  120. (test-call "0"
  121. (lambda ()
  122. (let ((ht (make-eq-hashtable)))
  123. (hashtable-set! ht 'a 'b)
  124. (hashtable-clear! ht)
  125. (hashtable-size ht))))
  126. ;; Clear removes all associations
  127. (test-call "#f"
  128. (lambda ()
  129. (let ((ht (make-eq-hashtable)))
  130. (hashtable-set! ht 'a 'b)
  131. (hashtable-clear! ht)
  132. (hashtable-contains? ht 'a))))
  133. ;; Keys of an empty table
  134. (test-call "()"
  135. (lambda ()
  136. (hashtable-keys (make-eq-hashtable))))
  137. ;; Keys of a populated table
  138. (test-call "(a)"
  139. (lambda ()
  140. (let ((ht (make-eq-hashtable)))
  141. (hashtable-set! ht 'a 'b)
  142. (hashtable-keys ht))))
  143. ;; Values of an empty table
  144. (test-call "()"
  145. (lambda ()
  146. (hashtable-values (make-eq-hashtable))))
  147. ;; Values of a populated table
  148. (test-call "(b)"
  149. (lambda ()
  150. (let ((ht (make-eq-hashtable)))
  151. (hashtable-set! ht 'a 'b)
  152. (hashtable-values ht))))
  153. ;; For each iteration
  154. (test-call "(a b)"
  155. (lambda ()
  156. (let ((ht (make-eq-hashtable))
  157. (result #f))
  158. (hashtable-set! ht 'a 'b)
  159. (hashtable-for-each (lambda (k v)
  160. (set! result (list k v)))
  161. ht)
  162. result)))
  163. ;; Fold (result order is technically unspecified but we know what it
  164. ;; will be)
  165. (test-call "((a . b) (c . d))"
  166. (lambda ()
  167. (let ((ht (make-eq-hashtable))
  168. (result #f))
  169. (hashtable-set! ht 'a 'b)
  170. (hashtable-set! ht 'c 'd)
  171. (hashtable-fold (lambda (k v prev)
  172. (cons (cons k v) prev))
  173. '()
  174. ht))))
  175. ;; Grow/shrink
  176. (test-call "100"
  177. (lambda ()
  178. (let ((ht (make-eq-hashtable)))
  179. (do ((i 0 (+ i 1)))
  180. ((= i 100))
  181. (hashtable-set! ht i i))
  182. (do ((i 0 (+ i 1)))
  183. ((= i 100))
  184. (hashtable-delete! ht i))
  185. (do ((i 0 (+ i 1)))
  186. ((= i 100))
  187. (hashtable-set! ht i i))
  188. (hashtable-size ht))))
  189. ;; Weak key hashtables
  190. (test-call "42"
  191. (lambda ()
  192. (let ((table (make-weak-key-hashtable))
  193. (a (list 1 2 3))
  194. (b (list 1 2 3)))
  195. (weak-key-hashtable-set! table a 42)
  196. (weak-key-hashtable-set! table b 13)
  197. (weak-key-hashtable-ref table a))))
  198. (test-call "uh-oh"
  199. (lambda ()
  200. (let ((table (make-weak-key-hashtable)))
  201. (weak-key-hashtable-ref table 'foo 'uh-oh))))
  202. (test-call "#f"
  203. (lambda ()
  204. (let ((table (make-weak-key-hashtable)))
  205. (weak-key-hashtable-set! table 'foo 42)
  206. (weak-key-hashtable-delete! table 'foo)
  207. (weak-key-hashtable-ref table 'foo))))
  208. ;; Hash functions
  209. (test-call "6" (lambda () (hashq 42 37))))
  210. ;; Guile legacy API
  211. (with-imports ((guile))
  212. (test-call "42"
  213. (lambda ()
  214. (let ((table (make-hash-table)))
  215. (hashq-set! table 'foo 42)
  216. (hashq-ref table 'foo))))
  217. (test-call "#f"
  218. (lambda ()
  219. (let ((table (make-hash-table)))
  220. (hash-set! table "foo" 42)
  221. (hash-remove! table "foo")
  222. (hash-ref table "foo"))))
  223. (test-call "42"
  224. (lambda ()
  225. (let ((table (make-weak-key-hash-table)))
  226. (hashq-set! table 'foo 42)
  227. (hashq-ref table 'foo))))
  228. (test-call "((baz . 3) (bar . 2) (foo . 1))"
  229. (lambda ()
  230. (let ((table (make-hash-table)))
  231. (hashq-set! table 'foo 1)
  232. (hashq-set! table 'bar 2)
  233. (hashq-set! table 'baz 3)
  234. (hash-map->list cons table))))
  235. (test-call "3"
  236. (lambda ()
  237. (let ((table (make-hash-table)))
  238. (hash-set! table "foo" 1)
  239. (hash-set! table "bar" 2)
  240. (hash-set! table "baz" 3)
  241. (hash-count (lambda (key val) #t) table))))
  242. ;; clear, fold, and for-each on an empty table should no-op because
  243. ;; we don't yet know the concrete table type.
  244. (test-call "#t"
  245. (lambda ()
  246. (let ((table (make-hash-table)))
  247. (hash-clear! table)
  248. #t)))
  249. (test-call "0"
  250. (lambda ()
  251. (let ((table (make-hash-table)))
  252. (hash-fold (lambda (key val sum)
  253. (+ sum val))
  254. 0 table))))
  255. (test-call "0"
  256. (lambda ()
  257. (let ((count 0)
  258. (table (make-hash-table)))
  259. (hash-for-each (lambda (key val)
  260. (set! count (1+ count)))
  261. table)
  262. count))))
  263. (test-end* "test-hashtables")