challenge.scm 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2015, 2017, 2019, 2020 Ludovic Courtès <ludo@gnu.org>
  3. ;;;
  4. ;;; This file is part of GNU Guix.
  5. ;;;
  6. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  7. ;;; under the terms of the GNU General Public License as published by
  8. ;;; the Free Software Foundation; either version 3 of the License, or (at
  9. ;;; your option) any later version.
  10. ;;;
  11. ;;; GNU Guix is distributed in the hope that it will be useful, but
  12. ;;; 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 GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  18. (define-module (test-challenge)
  19. #:use-module (guix tests)
  20. #:use-module (guix tests http)
  21. #:use-module ((gcrypt hash) #:prefix gcrypt:)
  22. #:use-module (guix store)
  23. #:use-module (guix monads)
  24. #:use-module (guix derivations)
  25. #:use-module (guix serialization)
  26. #:use-module (guix packages)
  27. #:use-module (guix gexp)
  28. #:use-module (guix base32)
  29. #:use-module (guix scripts challenge)
  30. #:use-module (guix scripts substitute)
  31. #:use-module ((guix build utils) #:select (find-files))
  32. #:use-module (gnu packages bootstrap)
  33. #:use-module (srfi srfi-1)
  34. #:use-module (srfi srfi-26)
  35. #:use-module (srfi srfi-64)
  36. #:use-module (rnrs bytevectors)
  37. #:use-module (rnrs io ports)
  38. #:use-module (ice-9 match))
  39. (define query-path-hash*
  40. (store-lift query-path-hash))
  41. (define (query-path-size item)
  42. (mlet %store-monad ((info (query-path-info* item)))
  43. (return (path-info-nar-size info))))
  44. (define* (call-with-derivation-narinfo* drv thunk hash)
  45. (lambda (store)
  46. (with-derivation-narinfo drv (sha256 => hash)
  47. (values (run-with-store store (thunk)) store))))
  48. (define-syntax with-derivation-narinfo*
  49. (syntax-rules (sha256 =>)
  50. ((_ drv (sha256 => hash) body ...)
  51. (call-with-derivation-narinfo* drv
  52. (lambda () body ...)
  53. hash))))
  54. (test-begin "challenge")
  55. (test-assertm "no discrepancies"
  56. (let ((text (random-text)))
  57. (mlet* %store-monad ((drv (gexp->derivation "something"
  58. #~(call-with-output-file
  59. #$output
  60. (lambda (port)
  61. (display #$text port)))))
  62. (out -> (derivation->output-path drv)))
  63. (mbegin %store-monad
  64. (built-derivations (list drv))
  65. (mlet %store-monad ((hash (query-path-hash* out)))
  66. (with-derivation-narinfo* drv (sha256 => hash)
  67. (>>= (compare-contents (list out) (%test-substitute-urls))
  68. (match-lambda
  69. ((report)
  70. (return
  71. (and (string=? out (comparison-report-item report))
  72. (bytevector=?
  73. (comparison-report-local-sha256 report)
  74. hash)
  75. (comparison-report-match? report))))))))))))
  76. (test-assertm "one discrepancy"
  77. (let ((text (random-text)))
  78. (mlet* %store-monad ((drv (gexp->derivation "something"
  79. #~(call-with-output-file
  80. #$output
  81. (lambda (port)
  82. (display #$text port)))))
  83. (out -> (derivation->output-path drv)))
  84. (mbegin %store-monad
  85. (built-derivations (list drv))
  86. (mlet* %store-monad ((hash (query-path-hash* out))
  87. (wrong-hash
  88. -> (let* ((w (bytevector-copy hash))
  89. (b (bytevector-u8-ref w 0)))
  90. (bytevector-u8-set! w 0
  91. (modulo (+ b 1) 128))
  92. w)))
  93. (with-derivation-narinfo* drv (sha256 => wrong-hash)
  94. (>>= (compare-contents (list out) (%test-substitute-urls))
  95. (match-lambda
  96. ((report)
  97. (return
  98. (and (string=? out (comparison-report-item (pk report)))
  99. (eq? 'mismatch (comparison-report-result report))
  100. (bytevector=? hash
  101. (comparison-report-local-sha256
  102. report))
  103. (match (comparison-report-narinfos report)
  104. ((bad)
  105. (bytevector=? wrong-hash
  106. (narinfo-hash->sha256
  107. (narinfo-hash bad))))))))))))))))
  108. (test-assertm "inconclusive: no substitutes"
  109. (mlet* %store-monad ((drv (gexp->derivation "foo" #~(mkdir #$output)))
  110. (out -> (derivation->output-path drv))
  111. (_ (built-derivations (list drv)))
  112. (hash (query-path-hash* out)))
  113. (>>= (compare-contents (list out) (%test-substitute-urls))
  114. (match-lambda
  115. ((report)
  116. (return
  117. (and (string=? out (comparison-report-item report))
  118. (comparison-report-inconclusive? report)
  119. (null? (comparison-report-narinfos report))
  120. (bytevector=? (comparison-report-local-sha256 report)
  121. hash))))))))
  122. (test-assertm "inconclusive: no local build"
  123. (let ((text (random-text)))
  124. (mlet* %store-monad ((drv (gexp->derivation "something"
  125. #~(list #$output #$text)))
  126. (out -> (derivation->output-path drv))
  127. (hash -> (gcrypt:sha256 #vu8())))
  128. (with-derivation-narinfo* drv (sha256 => hash)
  129. (>>= (compare-contents (list out) (%test-substitute-urls))
  130. (match-lambda
  131. ((report)
  132. (return
  133. (and (string=? out (comparison-report-item report))
  134. (comparison-report-inconclusive? report)
  135. (not (comparison-report-local-sha256 report))
  136. (match (comparison-report-narinfos report)
  137. ((narinfo)
  138. (bytevector=? (narinfo-hash->sha256
  139. (narinfo-hash narinfo))
  140. hash))))))))))))
  141. (define (make-narinfo item size hash)
  142. (format #f "StorePath: ~a
  143. Compression: none
  144. URL: nar/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-foo
  145. NarSize: ~d
  146. NarHash: sha256:~a
  147. References: ~%" item size (bytevector->nix-base32-string hash)))
  148. (define (call-mismatch-test proc)
  149. "Pass PROC a <comparison-report> for a mismatch and return its return
  150. value."
  151. ;; Pretend we have two different results for the same store item, ITEM, with
  152. ;; "/bin/guile" differing between the two nars.
  153. (mlet* %store-monad
  154. ((drv1 (package->derivation %bootstrap-guile))
  155. (drv2 (gexp->derivation
  156. "broken-guile"
  157. (with-imported-modules '((guix build utils))
  158. #~(begin
  159. (use-modules (guix build utils))
  160. (copy-recursively #$drv1 #$output)
  161. (chmod (string-append #$output "/bin/guile")
  162. #o755)
  163. (call-with-output-file (string-append
  164. #$output
  165. "/bin/guile")
  166. (lambda (port)
  167. (display "corrupt!" port)))))))
  168. (out1 -> (derivation->output-path drv1))
  169. (out2 -> (derivation->output-path drv2))
  170. (item -> (string-append (%store-prefix) "/"
  171. (bytevector->nix-base32-string
  172. (random-bytevector 32))
  173. "-foo"
  174. (number->string (current-time) 16))))
  175. (mbegin %store-monad
  176. (built-derivations (list drv1 drv2))
  177. (mlet* %store-monad ((size1 (query-path-size out1))
  178. (size2 (query-path-size out2))
  179. (hash1 (query-path-hash* out1))
  180. (hash2 (query-path-hash* out2))
  181. (nar1 -> (call-with-bytevector-output-port
  182. (lambda (port)
  183. (write-file out1 port))))
  184. (nar2 -> (call-with-bytevector-output-port
  185. (lambda (port)
  186. (write-file out2 port)))))
  187. (parameterize ((%http-server-port 9000))
  188. (with-http-server `((200 ,(make-narinfo item size1 hash1))
  189. (200 ,nar1))
  190. (parameterize ((%http-server-port 9001))
  191. (with-http-server `((200 ,(make-narinfo item size2 hash2))
  192. (200 ,nar2))
  193. (mlet* %store-monad ((urls -> (list (%local-url 9000)
  194. (%local-url 9001)))
  195. (reports (compare-contents (list item)
  196. urls)))
  197. (pk 'report reports)
  198. (return (proc (car reports))))))))))))
  199. (test-assertm "differing-files"
  200. (call-mismatch-test
  201. (lambda (report)
  202. (equal? (differing-files report) '("/bin/guile")))))
  203. (test-assertm "call-with-mismatches"
  204. (call-mismatch-test
  205. (lambda (report)
  206. (call-with-mismatches
  207. report
  208. (lambda (directory1 directory2)
  209. (let* ((files1 (find-files directory1))
  210. (files2 (find-files directory2))
  211. (files (map (cute string-drop <> (string-length directory1))
  212. files1)))
  213. (and (equal? files
  214. (map (cute string-drop <> (string-length directory2))
  215. files2))
  216. (equal? (remove (lambda (file)
  217. (file=? (string-append directory1 "/" file)
  218. (string-append directory2 "/" file)))
  219. files)
  220. '("/bin/guile")))))))))
  221. (test-end)
  222. ;;; Local Variables:
  223. ;;; eval: (put 'with-derivation-narinfo* 'scheme-indent-function 2)
  224. ;;; End: