pull: Suggest running 'hash guix' if needed.
[jackhill/guix/guix.git] / guix / tests.scm
CommitLineData
c1bc358f 1;;; GNU Guix --- Functional package management for GNU
19c924af 2;;; Copyright © 2013, 2014, 2015, 2016, 2017, 2018 Ludovic Courtès <ludo@gnu.org>
c1bc358f
LC
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
19(define-module (guix tests)
19c924af 20 #:use-module ((guix config) #:select (%storedir %localstatedir))
c1bc358f
LC
21 #:use-module (guix store)
22 #:use-module (guix derivations)
23 #:use-module (guix packages)
6eebbab5 24 #:use-module (guix base32)
e6c8839c 25 #:use-module (guix serialization)
ca719424 26 #:use-module (gcrypt hash)
2bba832f 27 #:use-module (guix build-system gnu)
c1bc358f
LC
28 #:use-module (gnu packages bootstrap)
29 #:use-module (srfi srfi-34)
9ed86fe1 30 #:use-module (srfi srfi-64)
c1bc358f 31 #:use-module (rnrs bytevectors)
2535635f 32 #:use-module (ice-9 binary-ports)
e6740741 33 #:use-module (web uri)
c1bc358f 34 #:export (open-connection-for-tests
19c924af 35 with-external-store
c1bc358f 36 random-text
e6740741 37 random-bytevector
8de3df72 38 file=?
83908698 39 canonical-file?
12d720fd 40 network-reachable?
b69c5c2c 41 shebang-too-long?
694b317c 42 mock
24f5aaaf 43 %test-substitute-urls
9ed86fe1
LC
44 test-assertm
45 test-equalm
e6c8839c 46 %substitute-directory
8b385969 47 with-derivation-narinfo
e6c8839c 48 with-derivation-substitute
f77bcbc3
EB
49 dummy-package
50 dummy-origin))
c1bc358f
LC
51
52;;; Commentary:
53;;;
54;;; This module provide shared infrastructure for the test suite. For
55;;; internal use only.
56;;;
57;;; Code:
58
24f5aaaf
LC
59(define %test-substitute-urls
60 ;; URLs where to look for substitutes during tests.
61 (make-parameter
62 (or (and=> (getenv "GUIX_BINARY_SUBSTITUTE_URL") list)
63 '())))
64
1397b422 65(define* (open-connection-for-tests #:optional (uri (%daemon-socket-uri)))
c1bc358f
LC
66 "Open a connection to the build daemon for tests purposes and return it."
67 (guard (c ((nix-error? c)
68 (format (current-error-port)
69 "warning: build daemon error: ~s~%" c)
70 #f))
1397b422 71 (let ((store (open-connection uri)))
c1bc358f 72 ;; Make sure we build everything by ourselves.
24f5aaaf
LC
73 (set-build-options store
74 #:use-substitutes? #f
75 #:substitute-urls (%test-substitute-urls))
c1bc358f
LC
76
77 ;; Use the bootstrap Guile when running tests, so we don't end up
78 ;; building everything in the temporary test store.
79 (%guile-for-build (package-derivation store %bootstrap-guile))
80
81 store)))
82
19c924af
LC
83(define (call-with-external-store proc)
84 "Call PROC with an open connection to the external store or #f it there is
85no external store to talk to."
86 (parameterize ((%daemon-socket-uri
87 (string-append %localstatedir
88 "/guix/daemon-socket/socket"))
89 (%store-prefix %storedir))
90 (define store
91 (catch #t
92 (lambda ()
93 (open-connection))
94 (const #f)))
95
96 (dynamic-wind
97 (const #t)
98 (lambda ()
99 ;; Since we're using a different store we must clear the
100 ;; package-derivation cache.
101 (hash-clear! (@@ (guix packages) %derivation-cache))
102
103 (proc store))
104 (lambda ()
105 (when store
106 (close-connection store))))))
107
108(define-syntax-rule (with-external-store store exp ...)
109 "Evaluate EXP with STORE bound to the external store rather than the
110temporary test store, or #f if there is no external store to talk to.
111
112This is meant to be used for tests that need to build packages that would be
113too expensive to build entirely in the test store."
114 (call-with-external-store (lambda (store) exp ...)))
115
79477def
LC
116(define (random-seed)
117 (or (and=> (getenv "GUIX_TESTS_RANDOM_SEED")
118 number->string)
119 (logxor (getpid) (car (gettimeofday)))))
120
c1bc358f 121(define %seed
79477def
LC
122 (let ((seed (random-seed)))
123 (format (current-error-port) "random seed for tests: ~a~%"
124 seed)
125 (seed->random-state seed)))
c1bc358f
LC
126
127(define (random-text)
128 "Return the hexadecimal representation of a random number."
129 (number->string (random (expt 2 256) %seed) 16))
130
131(define (random-bytevector n)
132 "Return a random bytevector of N bytes."
133 (let ((bv (make-bytevector n)))
134 (let loop ((i 0))
135 (if (< i n)
136 (begin
137 (bytevector-u8-set! bv i (random 256 %seed))
138 (loop (1+ i)))
139 bv))))
140
8de3df72
LC
141(define (file=? a b)
142 "Return true if files A and B have the same type and same content."
143 (and (eq? (stat:type (lstat a)) (stat:type (lstat b)))
144 (case (stat:type (lstat a))
145 ((regular)
146 (equal?
147 (call-with-input-file a get-bytevector-all)
148 (call-with-input-file b get-bytevector-all)))
149 ((symlink)
150 (string=? (readlink a) (readlink b)))
151 (else
152 (error "what?" (lstat a))))))
153
83908698
LC
154(define (canonical-file? file)
155 "Return #t if FILE is in the store, is read-only, and its mtime is 1."
156 (let ((st (lstat file)))
157 (or (not (string-prefix? (%store-prefix) file))
158 (eq? 'symlink (stat:type st))
159 (and (= 1 (stat:mtime st))
160 (zero? (logand #o222 (stat:mode st)))))))
161
12d720fd
LC
162(define (network-reachable?)
163 "Return true if we can reach the Internet."
164 (false-if-exception (getaddrinfo "www.gnu.org" "80" AI_NUMERICSERV)))
165
694b317c
EB
166(define-syntax-rule (mock (module proc replacement) body ...)
167 "Within BODY, replace the definition of PROC from MODULE with the definition
168given by REPLACEMENT."
169 (let* ((m (resolve-module 'module))
170 (original (module-ref m 'proc)))
171 (dynamic-wind
172 (lambda () (module-set! m 'proc replacement))
173 (lambda () body ...)
174 (lambda () (module-set! m 'proc original)))))
175
9ed86fe1
LC
176(define-syntax-rule (test-assertm name exp)
177 "Like 'test-assert', but EXP is a monadic value. A new connection to the
178store is opened."
179 (test-assert name
180 (let ((store (open-connection-for-tests)))
181 (dynamic-wind
182 (const #t)
183 (lambda ()
184 (run-with-store store exp
185 #:guile-for-build (%guile-for-build)))
186 (lambda ()
187 (close-connection store))))))
188
189(define-syntax-rule (test-equalm name value exp)
190 "Like 'test-equal', but EXP is a monadic value. A new connection to the
191store is opened."
192 (test-equal name
193 value
194 (with-store store
195 (run-with-store store exp
196 #:guile-for-build (%guile-for-build)))))
197
e6740741
LC
198\f
199;;;
200;;; Narinfo files, as used by the substituter.
201;;;
202
6eebbab5 203(define* (derivation-narinfo drv #:key (nar "example.nar")
7bfeb9df
LC
204 (sha256 (make-bytevector 32 0))
205 (references '()))
206 "Return the contents of the narinfo corresponding to DRV, with the specified
207REFERENCES (a list of store items); NAR should be the file name of the archive
208containing the substitute for DRV, and SHA256 is the expected hash."
e6740741
LC
209 (format #f "StorePath: ~a
210URL: ~a
211Compression: none
212NarSize: 1234
6eebbab5 213NarHash: sha256:~a
7bfeb9df 214References: ~a
e6740741
LC
215System: ~a
216Deriver: ~a~%"
217 (derivation->output-path drv) ; StorePath
218 nar ; URL
6eebbab5 219 (bytevector->nix-base32-string sha256) ; NarHash
7bfeb9df 220 (string-join (map basename references)) ; References
e6740741
LC
221 (derivation-system drv) ; System
222 (basename
223 (derivation-file-name drv)))) ; Deriver
224
e6c8839c
LC
225(define %substitute-directory
226 (make-parameter
227 (and=> (getenv "GUIX_BINARY_SUBSTITUTE_URL")
228 (compose uri-path string->uri))))
229
6eebbab5 230(define* (call-with-derivation-narinfo drv thunk
7bfeb9df
LC
231 #:key
232 (sha256 (make-bytevector 32 0))
233 (references '()))
e6740741 234 "Call THUNK in a context where fake substituter data, as read by 'guix
2c74fde0 235substitute', has been installed for DRV. SHA256 is the hash of the
6eebbab5 236expected output of DRV."
e6740741 237 (let* ((output (derivation->output-path drv))
e6c8839c 238 (dir (%substitute-directory))
e6740741
LC
239 (info (string-append dir "/nix-cache-info"))
240 (narinfo (string-append dir "/" (store-path-hash-part output)
241 ".narinfo")))
242 (dynamic-wind
243 (lambda ()
244 (call-with-output-file info
245 (lambda (p)
246 (format p "StoreDir: ~a\nWantMassQuery: 0\n"
247 (%store-prefix))))
248 (call-with-output-file narinfo
249 (lambda (p)
7bfeb9df
LC
250 (display (derivation-narinfo drv #:sha256 sha256
251 #:references references)
252 p))))
e6740741
LC
253 thunk
254 (lambda ()
255 (delete-file narinfo)
256 (delete-file info)))))
257
6eebbab5 258(define-syntax with-derivation-narinfo
7bfeb9df 259 (syntax-rules (sha256 references =>)
6eebbab5 260 "Evaluate BODY in a context where DRV looks substitutable from the
e6740741 261substituter's viewpoint."
7bfeb9df 262 ((_ drv (sha256 => hash) (references => refs) body ...)
6eebbab5
LC
263 (call-with-derivation-narinfo drv
264 (lambda () body ...)
7bfeb9df
LC
265 #:sha256 hash
266 #:references refs))
267 ((_ drv (sha256 => hash) body ...)
268 (with-derivation-narinfo drv
269 (sha256 => hash) (references => '())
270 body ...))
6eebbab5
LC
271 ((_ drv body ...)
272 (call-with-derivation-narinfo drv
273 (lambda ()
274 body ...)))))
e6740741 275
e6c8839c 276(define* (call-with-derivation-substitute drv contents thunk
7bfeb9df
LC
277 #:key
278 sha256
279 (references '()))
e6c8839c
LC
280 "Call THUNK in a context where a substitute for DRV has been installed,
281using CONTENTS, a string, as its contents. If SHA256 is true, use it as the
282expected hash of the substitute; otherwise use the hash of the nar containing
283CONTENTS."
284 (define dir (%substitute-directory))
285 (dynamic-wind
286 (lambda ()
287 (call-with-output-file (string-append dir "/example.out")
288 (lambda (port)
289 (display contents port)))
290 (call-with-output-file (string-append dir "/example.nar")
291 (lambda (p)
292 (write-file (string-append dir "/example.out") p))))
293 (lambda ()
294 (let ((hash (call-with-input-file (string-append dir "/example.nar")
295 port-sha256)))
2c74fde0 296 ;; Create fake substituter data, to be read by 'guix substitute'.
e6c8839c
LC
297 (call-with-derivation-narinfo drv
298 thunk
7bfeb9df
LC
299 #:sha256 (or sha256 hash)
300 #:references references)))
e6c8839c
LC
301 (lambda ()
302 (delete-file (string-append dir "/example.out"))
303 (delete-file (string-append dir "/example.nar")))))
304
b69c5c2c
LC
305(define (shebang-too-long?)
306 "Return true if the typical shebang in the current store would exceed
307Linux's static limit---the BINPRM_BUF_SIZE constant, normally 128 characters
308all included."
309 (define shebang
310 (string-append "#!" (%store-prefix) "/"
311 (make-string 32 #\a)
312 "-bootstrap-binaries-0/bin/bash\0"))
313
314 (> (string-length shebang) 128))
315
e6c8839c 316(define-syntax with-derivation-substitute
7bfeb9df 317 (syntax-rules (sha256 references =>)
e6c8839c
LC
318 "Evaluate BODY in a context where DRV is substitutable with the given
319CONTENTS."
7bfeb9df 320 ((_ drv contents (sha256 => hash) (references => refs) body ...)
e6c8839c
LC
321 (call-with-derivation-substitute drv contents
322 (lambda () body ...)
7bfeb9df
LC
323 #:sha256 hash
324 #:references refs))
325 ((_ drv contents (sha256 => hash) body ...)
326 (with-derivation-substitute drv contents
327 (sha256 => hash) (references => '())
328 body ...))
e6c8839c
LC
329 ((_ drv contents body ...)
330 (call-with-derivation-substitute drv contents
331 (lambda ()
332 body ...)))))
333
8b385969
LC
334(define-syntax-rule (dummy-package name* extra-fields ...)
335 "Return a \"dummy\" package called NAME*, with all its compulsory fields
336initialized with default values, and with EXTRA-FIELDS set as specified."
337 (package extra-fields ...
338 (name name*) (version "0") (source #f)
339 (build-system gnu-build-system)
340 (synopsis #f) (description #f)
341 (home-page #f) (license #f)))
342
f77bcbc3
EB
343(define-syntax-rule (dummy-origin extra-fields ...)
344 "Return a \"dummy\" origin, with all its compulsory fields initialized with
345default values, and with EXTRA-FIELDS set as specified."
346 (origin extra-fields ...
347 (method #f) (uri "http://www.example.com")
348 (sha256 (base32 (make-string 52 #\x)))))
349
e6740741
LC
350;; Local Variables:
351;; eval: (put 'call-with-derivation-narinfo 'scheme-indent-function 1)
e6c8839c 352;; eval: (put 'call-with-derivation-substitute 'scheme-indent-function 2)
e6740741
LC
353;; End:
354
c1bc358f 355;;; tests.scm ends here