store: 'with-store' returns as many values as its body.
[jackhill/guix/guix.git] / tests / store.scm
CommitLineData
233e7676 1;;; GNU Guix --- Functional package management for GNU
041b340d 2;;; Copyright © 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org>
3259877d 3;;;
233e7676 4;;; This file is part of GNU Guix.
3259877d 5;;;
233e7676 6;;; GNU Guix is free software; you can redistribute it and/or modify it
3259877d
LC
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;;;
233e7676 11;;; GNU Guix is distributed in the hope that it will be useful, but
3259877d
LC
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
233e7676 17;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
3259877d 18
3259877d 19(define-module (test-store)
c1bc358f 20 #:use-module (guix tests)
81c580c8 21 #:use-module (guix config)
3259877d
LC
22 #:use-module (guix store)
23 #:use-module (guix utils)
320ca999 24 #:use-module (guix monads)
ce0be567 25 #:use-module ((gcrypt hash) #:prefix gcrypt:)
3259877d 26 #:use-module (guix base32)
0f3d2504
LC
27 #:use-module (guix packages)
28 #:use-module (guix derivations)
0363991a 29 #:use-module (guix serialization)
895d1eda 30 #:use-module (guix build utils)
ce72c780 31 #:use-module (guix gexp)
fae31edc 32 #:use-module (gnu packages)
1ffa7090 33 #:use-module (gnu packages bootstrap)
3259877d 34 #:use-module (ice-9 match)
6ef61cc4 35 #:use-module (ice-9 regex)
526382ff 36 #:use-module (rnrs bytevectors)
fe0cff14 37 #:use-module (rnrs io ports)
f65cf81a 38 #:use-module (web uri)
3259877d
LC
39 #:use-module (srfi srfi-1)
40 #:use-module (srfi srfi-11)
526382ff 41 #:use-module (srfi srfi-26)
c3eb878f 42 #:use-module (srfi srfi-34)
3259877d
LC
43 #:use-module (srfi srfi-64))
44
45;; Test the (guix store) module.
46
47(define %store
c1bc358f 48 (open-connection-for-tests))
3259877d 49
3d430170
LC
50(define %shell
51 (or (getenv "SHELL") (getenv "CONFIG_SHELL")))
52
3259877d
LC
53\f
54(test-begin "store")
55
1397b422
LC
56(test-assert "open-connection with file:// URI"
57 (let ((store (open-connection (string-append "file://"
58 (%daemon-socket-uri)))))
59 (and (add-text-to-store store "foo" "bar")
60 (begin
61 (close-connection store)
62 #t))))
63
13d5e8da
LC
64(test-equal "connection handshake error"
65 EPROTO
66 (let ((port (%make-void-port "rw")))
f9e8a123
LC
67 (guard (c ((store-connection-error? c)
68 (and (eq? port (store-connection-error-file c))
69 (store-connection-error-code c))))
13d5e8da
LC
70 (open-connection #f #:port port)
71 'broken)))
72
2c6ab6cc
LC
73(test-equal "store-path-hash-part"
74 "283gqy39v3g9dxjy26rynl0zls82fmcg"
75 (store-path-hash-part
76 (string-append (%store-prefix)
77 "/283gqy39v3g9dxjy26rynl0zls82fmcg-guile-2.0.7")))
78
79(test-equal "store-path-hash-part #f"
80 #f
81 (store-path-hash-part
82 (string-append (%store-prefix)
83 "/foo/bar/283gqy39v3g9dxjy26rynl0zls82fmcg-guile-2.0.7")))
84
c61a5b4a
LC
85(test-equal "store-path-package-name"
86 "guile-2.0.7"
87 (store-path-package-name
88 (string-append (%store-prefix)
89 "/283gqy39v3g9dxjy26rynl0zls82fmcg-guile-2.0.7")))
90
91(test-equal "store-path-package-name #f"
92 #f
93 (store-path-package-name
94 "/foo/bar/283gqy39v3g9dxjy26rynl0zls82fmcg-guile-2.0.7"))
95
9336e5b5
LC
96(test-assert "direct-store-path?"
97 (and (direct-store-path?
98 (string-append (%store-prefix)
99 "/283gqy39v3g9dxjy26rynl0zls82fmcg-guile-2.0.7"))
100 (not (direct-store-path?
101 (string-append
102 (%store-prefix)
eee21271
LC
103 "/283gqy39v3g9dxjy26rynl0zls82fmcg-guile-2.0.7/bin/guile")))
104 (not (direct-store-path? (%store-prefix)))))
9336e5b5 105
81c580c8
LC
106(test-skip (if %store 0 15))
107
108(test-equal "profiles/per-user exists and is not writable"
109 #o755
110 (stat:perms (stat (string-append %state-directory "/profiles/per-user"))))
111
112(test-equal "profiles/per-user/$USER exists"
113 (list (getuid) #o755)
114 (let ((s (stat (string-append %state-directory "/profiles/per-user/"
115 (passwd:name (getpwuid (getuid)))))))
116 (list (stat:uid s) (stat:perms s))))
e297d8fc 117
73b27eaa 118(test-equal "add-to-store"
8e6c1415 119 '("sha1" "sha256" "sha512" "sha3-256" "sha3-512" "blake2s-256")
73b27eaa
LC
120 (let* ((file (search-path %load-path "guix.scm"))
121 (content (call-with-input-file file get-bytevector-all)))
122 (map (lambda (hash-algo)
123 (let ((file (add-to-store %store "guix.scm" #f hash-algo file)))
124 (and (direct-store-path? file)
125 (bytevector=? (call-with-input-file file get-bytevector-all)
126 content)
127 hash-algo)))
8e6c1415 128 '("sha1" "sha256" "sha512" "sha3-256" "sha3-512" "blake2s-256"))))
73b27eaa 129
0d268c5d
LC
130(test-equal "add-data-to-store"
131 #vu8(1 2 3 4 5)
132 (call-with-input-file (add-data-to-store %store "data" #vu8(1 2 3 4 5))
133 get-bytevector-all))
134
e297d8fc
LC
135(test-assert "valid-path? live"
136 (let ((p (add-text-to-store %store "hello" "hello, world")))
137 (valid-path? %store p)))
138
139(test-assert "valid-path? false"
140 (not (valid-path? %store
141 (string-append (%store-prefix) "/"
142 (make-string 32 #\e) "-foobar"))))
143
3d9ea605
LC
144(test-equal "with-store, multiple values" ;<https://bugs.gnu.org/42912>
145 '(1 2 3)
146 (call-with-values
147 (lambda ()
148 (with-store s
149 (add-text-to-store s "foo" "bar")
150 (values 1 2 3)))
151 list))
152
e297d8fc
LC
153(test-assert "valid-path? error"
154 (with-store s
f9e8a123 155 (guard (c ((store-protocol-error? c) #t))
e297d8fc
LC
156 (valid-path? s "foo")
157 #f)))
158
159(test-assert "valid-path? recovery"
160 ;; Prior to Nix commit 51800e0 (18 Mar. 2014), the daemon would immediately
161 ;; close the connection after receiving a 'valid-path?' RPC with a non-store
162 ;; file name. See
163 ;; <http://article.gmane.org/gmane.linux.distributions.nixos/12411> for
164 ;; details.
165 (with-store s
166 (let-syntax ((true-if-error (syntax-rules ()
167 ((_ exp)
f9e8a123 168 (guard (c ((store-protocol-error? c) #t))
e297d8fc
LC
169 exp #f)))))
170 (and (true-if-error (valid-path? s "foo"))
171 (true-if-error (valid-path? s "bar"))
172 (true-if-error (valid-path? s "baz"))
173 (true-if-error (valid-path? s "chbouib"))
174 (valid-path? s (add-text-to-store s "valid" "yeah"))))))
11e7a6cf
LC
175
176(test-assert "hash-part->path"
177 (let ((p (add-text-to-store %store "hello" "hello, world")))
178 (equal? (hash-part->path %store (store-path-hash-part p))
179 p)))
3259877d
LC
180
181(test-assert "dead-paths"
cfbf9160 182 (let ((p (add-text-to-store %store "random-text" (random-text))))
5f1f10c5 183 (->bool (member p (dead-paths %store)))))
3259877d
LC
184
185;; FIXME: Find a test for `live-paths'.
186;;
187;; (test-assert "temporary root is in live-paths"
188;; (let* ((p1 (add-text-to-store %store "random-text"
189;; (random-text) '()))
190;; (b (add-text-to-store %store "link-builder"
191;; (format #f "echo ~a > $out" p1)
192;; '()))
a987d2c0
LC
193;; (d1 (derivation %store "link"
194;; "/bin/sh" `("-e" ,b)
195;; #:inputs `((,b) (,p1))))
59688fc4 196;; (p2 (derivation->output-path d1)))
3259877d
LC
197;; (and (add-temp-root %store p2)
198;; (build-derivations %store (list d1))
199;; (valid-path? %store p1)
200;; (member (pk p2) (live-paths %store)))))
201
a9d2a105
LC
202(test-assert "permanent root"
203 (let* ((p (with-store store
204 (let ((p (add-text-to-store store "random-text"
205 (random-text))))
206 (add-permanent-root p)
207 (add-permanent-root p) ; should not throw
208 p))))
209 (and (member p (live-paths %store))
210 (begin
211 (remove-permanent-root p)
212 (->bool (member p (dead-paths %store)))))))
213
3259877d
LC
214(test-assert "dead path can be explicitly collected"
215 (let ((p (add-text-to-store %store "random-text"
216 (random-text) '())))
217 (let-values (((paths freed) (delete-paths %store (list p))))
218 (and (equal? paths (list p))
953c2de7
MB
219 ;; XXX: On some file systems (notably Btrfs), freed
220 ;; may return 0. See <https://bugs.gnu.org/29363>.
221 ;;(> freed 0)
3259877d
LC
222 (not (file-exists? p))))))
223
000c59b6
LC
224(test-assert "add-text-to-store vs. delete-paths"
225 ;; Before, 'add-text-to-store' would return PATH2 without noticing that it
226 ;; is no longer valid.
227 (with-store store
228 (let* ((text (random-text))
229 (path (add-text-to-store store "delete-me" text))
230 (deleted (delete-paths store (list path)))
231 (path2 (add-text-to-store store "delete-me" text)))
232 (and (string=? path path2)
233 (equal? deleted (list path))
234 (valid-path? store path)
235 (file-exists? path)))))
236
237(test-assert "add-to-store vs. delete-paths"
238 ;; Same as above.
239 (with-store store
240 (let* ((file (search-path %load-path "guix.scm"))
241 (path (add-to-store store "delete-me" #t "sha256" file))
242 (deleted (delete-paths store (list path)))
243 (path2 (add-to-store store "delete-me" #t "sha256" file)))
244 (and (string=? path path2)
245 (equal? deleted (list path))
246 (valid-path? store path)
247 (file-exists? path)))))
248
7f11efba
LC
249(test-equal "add-file-tree-to-store"
250 `(42
251 ("." directory #t)
252 ("./bar" directory #t)
253 ("./foo" directory #t)
254 ("./foo/a" regular "file a")
255 ("./foo/b" symlink "a")
256 ("./foo/c" directory #t)
257 ("./foo/c/p" regular "file p")
258 ("./foo/c/q" directory #t)
3d430170
LC
259 ("./foo/c/q/x" regular
260 ,(string-append "#!" %shell "\nexit 42"))
7f11efba
LC
261 ("./foo/c/q/y" symlink "..")
262 ("./foo/c/q/z" directory #t))
263 (let* ((tree `("file-tree" directory
264 ("foo" directory
265 ("a" regular (data "file a"))
266 ("b" symlink "a")
267 ("c" directory
268 ("p" regular (data ,(string->utf8 "file p")))
269 ("q" directory
270 ("x" executable
3d430170 271 (data ,(string-append "#!" %shell "\nexit 42")))
7f11efba
LC
272 ("y" symlink "..")
273 ("z" directory))))
274 ("bar" directory)))
275 (result (add-file-tree-to-store %store tree)))
276 (cons (status:exit-val (system* (string-append result "/foo/c/q/x")))
277 (with-directory-excursion result
278 (map (lambda (file)
279 (let ((type (stat:type (lstat file))))
280 `(,file ,type
281 ,(match type
282 ((or 'regular 'executable)
283 (call-with-input-file file
284 get-string-all))
285 ('symlink (readlink file))
286 ('directory #t)))))
287 (find-files "." #:directories? #t))))))
288
289(test-equal "add-file-tree-to-store, flat"
290 "Hello, world!"
291 (let* ((tree `("flat-file" regular (data "Hello, world!")))
292 (result (add-file-tree-to-store %store tree)))
293 (and (file-exists? result)
294 (call-with-input-file result get-string-all))))
295
fae31edc
LC
296(test-assert "references"
297 (let* ((t1 (add-text-to-store %store "random1"
cfbf9160 298 (random-text)))
fae31edc
LC
299 (t2 (add-text-to-store %store "random2"
300 (random-text) (list t1))))
301 (and (equal? (list t1) (references %store t2))
302 (equal? (list t2) (referrers %store t1))
303 (null? (references %store t1))
304 (null? (referrers %store t2)))))
305
6581ec9a
LC
306(test-assert "references/substitutes missing reference info"
307 (with-store s
308 (set-build-options s #:use-substitutes? #f)
f9e8a123 309 (guard (c ((store-protocol-error? c) #t))
6581ec9a
LC
310 (let* ((b (add-to-store s "bash" #t "sha256"
311 (search-bootstrap-binary "bash"
312 (%current-system))))
313 (d (derivation s "the-thing" b '("--help")
314 #:inputs `((,b)))))
151afd84
LC
315 (references/substitutes s (list (derivation->output-path d) b))
316 #f))))
6581ec9a
LC
317
318(test-assert "references/substitutes with substitute info"
319 (with-store s
320 (set-build-options s #:use-substitutes? #t)
321 (let* ((t1 (add-text-to-store s "random1" (random-text)))
322 (t2 (add-text-to-store s "random2" (random-text)
323 (list t1)))
324 (t3 (add-text-to-store s "build" "echo -n $t2 > $out"))
325 (b (add-to-store s "bash" #t "sha256"
326 (search-bootstrap-binary "bash"
327 (%current-system))))
328 (d (derivation s "the-thing" b `("-e" ,t3)
329 #:inputs `((,b) (,t3) (,t2))
330 #:env-vars `(("t2" . ,t2))))
331 (o (derivation->output-path d)))
332 (with-derivation-narinfo d
ce0be567 333 (sha256 => (gcrypt:sha256 (string->utf8 t2)))
6581ec9a
LC
334 (references => (list t2))
335
336 (equal? (references/substitutes s (list o t3 t2 t1))
337 `((,t2) ;refs of O
338 () ;refs of T3
339 (,t1) ;refs of T2
340 ())))))) ;refs of T1
341
151afd84
LC
342(test-equal "substitutable-path-info when substitutes are turned off"
343 '()
344 (with-store s
345 (set-build-options s #:use-substitutes? #f)
346 (let* ((b (add-to-store s "bash" #t "sha256"
347 (search-bootstrap-binary "bash"
348 (%current-system))))
349 (d (derivation s "the-thing" b '("--version")
350 #:inputs `((,b))))
351 (o (derivation->output-path d)))
352 (with-derivation-narinfo d
353 (substitutable-path-info s (list o))))))
354
355(test-equal "substitutable-paths when substitutes are turned off"
356 '()
357 (with-store s
358 (set-build-options s #:use-substitutes? #f)
359 (let* ((b (add-to-store s "bash" #t "sha256"
360 (search-bootstrap-binary "bash"
361 (%current-system))))
362 (d (derivation s "the-thing" b '("--version")
363 #:inputs `((,b))))
364 (o (derivation->output-path d)))
365 (with-derivation-narinfo d
366 (substitutable-paths s (list o))))))
367
3f1e6939
LC
368(test-assert "requisites"
369 (let* ((t1 (add-text-to-store %store "random1"
370 (random-text) '()))
371 (t2 (add-text-to-store %store "random2"
372 (random-text) (list t1)))
373 (t3 (add-text-to-store %store "random3"
374 (random-text) (list t2)))
375 (t4 (add-text-to-store %store "random4"
376 (random-text) (list t1 t3))))
377 (define (same? x y)
378 (and (= (length x) (length y))
379 (lset= equal? x y)))
380
f6fee16e
LC
381 (and (same? (requisites %store (list t1)) (list t1))
382 (same? (requisites %store (list t2)) (list t1 t2))
383 (same? (requisites %store (list t3)) (list t1 t2 t3))
384 (same? (requisites %store (list t4)) (list t1 t2 t3 t4))
385 (same? (requisites %store (list t1 t2 t3 t4))
386 (list t1 t2 t3 t4)))))
3f1e6939 387
fae31edc
LC
388(test-assert "derivers"
389 (let* ((b (add-text-to-store %store "build" "echo $foo > $out" '()))
390 (s (add-to-store %store "bash" #t "sha256"
391 (search-bootstrap-binary "bash"
392 (%current-system))))
a987d2c0
LC
393 (d (derivation %store "the-thing"
394 s `("-e" ,b)
395 #:env-vars `(("foo" . ,(random-text)))
396 #:inputs `((,b) (,s))))
59688fc4 397 (o (derivation->output-path d)))
fae31edc 398 (and (build-derivations %store (list d))
59688fc4 399 (equal? (query-derivation-outputs %store (derivation-file-name d))
fae31edc
LC
400 (list o))
401 (equal? (valid-derivers %store o)
59688fc4 402 (list (derivation-file-name d))))))
fae31edc 403
041b340d
LC
404(test-equal "with-build-handler"
405 'success
406 (let* ((b (add-text-to-store %store "build" "echo $foo > $out" '()))
407 (s (add-to-store %store "bash" #t "sha256"
408 (search-bootstrap-binary "bash"
409 (%current-system))))
410 (d1 (derivation %store "the-thing"
411 s `("-e" ,b)
412 #:env-vars `(("foo" . ,(random-text)))
413 #:sources (list b s)))
414 (d2 (derivation %store "the-thing"
415 s `("-e" ,b)
416 #:env-vars `(("foo" . ,(random-text))
417 ("bar" . "baz"))
418 #:sources (list b s)))
419 (o1 (derivation->output-path d1))
420 (o2 (derivation->output-path d2)))
421 (with-build-handler
422 (let ((counter 0))
423 (lambda (continue store things mode)
424 (match things
425 ((drv)
426 (set! counter (+ 1 counter))
427 (if (string=? drv (derivation-file-name d1))
428 (continue #t)
429 (and (string=? drv (derivation-file-name d2))
430 (= counter 2)
431 'success))))))
432 (build-derivations %store (list d1))
433 (build-derivations %store (list d2))
434 'fail)))
435
8ed597f4
LC
436(test-equal "with-build-handler + with-store"
437 'success
438 ;; Check that STORE remains valid when the build handler invokes CONTINUE,
439 ;; even though 'with-build-handler' is outside the dynamic extent of
440 ;; 'with-store'.
441 (with-build-handler (lambda (continue store things mode)
442 (match things
443 ((drv)
444 (and (string-suffix? "thingie.drv" drv)
445 (not (port-closed?
446 (store-connection-socket store)))
447 (continue #t)))))
448 (with-store store
449 (let* ((b (add-text-to-store store "build" "echo $foo > $out" '()))
450 (s (add-to-store store "bash" #t "sha256"
451 (search-bootstrap-binary "bash"
452 (%current-system))))
453 (d (derivation store "thingie"
454 s `("-e" ,b)
455 #:env-vars `(("foo" . ,(random-text)))
456 #:sources (list b s))))
457 (build-derivations store (list d))
458
459 ;; Here STORE's socket should still be open.
460 (and (valid-path? store (derivation->output-path d))
461 'success)))))
462
c40bf581
LC
463(test-assert "map/accumulate-builds"
464 (let* ((b (add-text-to-store %store "build" "echo $foo > $out" '()))
465 (s (add-to-store %store "bash" #t "sha256"
466 (search-bootstrap-binary "bash"
467 (%current-system))))
468 (d1 (derivation %store "the-thing"
469 s `("-e" ,b)
470 #:env-vars `(("foo" . ,(random-text)))
471 #:sources (list b s)))
472 (d2 (derivation %store "the-thing"
473 s `("-e" ,b)
474 #:env-vars `(("foo" . ,(random-text))
475 ("bar" . "baz"))
476 #:sources (list b s))))
477 (with-build-handler (lambda (continue store things mode)
478 (equal? (map derivation-file-name (list d1 d2))
479 things))
480 (map/accumulate-builds %store
481 (lambda (drv)
482 (build-derivations %store (list drv))
483 (add-to-store %store "content-addressed"
484 #t "sha256"
485 (derivation->output-path drv)))
486 (list d1 d2)))))
487
488(test-assert "mapm/accumulate-builds"
489 (let* ((d1 (run-with-store %store
490 (gexp->derivation "foo" #~(mkdir #$output))))
491 (d2 (run-with-store %store
492 (gexp->derivation "bar" #~(mkdir #$output)))))
493 (with-build-handler (lambda (continue store things mode)
494 (equal? (map derivation-file-name (pk 'zz (list d1 d2)))
495 (pk 'XX things)))
496 (run-with-store %store
497 (mapm/accumulate-builds built-derivations `((,d1) (,d2)))))))
498
80963744
LC
499(test-equal "mapm/accumulate-builds, %current-target-system"
500 (make-list 2 '("i586-pc-gnu" "i586-pc-gnu"))
501 ;; Both the 'mapm' and 'mapm/accumulate-builds' procedures should see the
502 ;; right #:target.
503 (run-with-store %store
504 (mlet %store-monad ((lst1 (mapm %store-monad
505 (lambda _
506 (current-target-system))
507 '(a b)))
508 (lst2 (mapm/accumulate-builds
509 (lambda _
510 (current-target-system))
511 '(a b))))
512 (return (list lst1 lst2)))
513 #:system system
514 #:target "i586-pc-gnu"))
515
50add477
LC
516(test-assert "topologically-sorted, one item"
517 (let* ((a (add-text-to-store %store "a" "a"))
518 (b (add-text-to-store %store "b" "b" (list a)))
519 (c (add-text-to-store %store "c" "c" (list b)))
520 (d (add-text-to-store %store "d" "d" (list c)))
521 (s (topologically-sorted %store (list d))))
522 (equal? s (list a b c d))))
523
524(test-assert "topologically-sorted, several items"
525 (let* ((a (add-text-to-store %store "a" "a"))
526 (b (add-text-to-store %store "b" "b" (list a)))
527 (c (add-text-to-store %store "c" "c" (list b)))
528 (d (add-text-to-store %store "d" "d" (list c)))
529 (s1 (topologically-sorted %store (list d a c b)))
530 (s2 (topologically-sorted %store (list b d c a b d))))
531 (equal? s1 s2 (list a b c d))))
532
533(test-assert "topologically-sorted, more difficult"
534 (let* ((a (add-text-to-store %store "a" "a"))
535 (b (add-text-to-store %store "b" "b" (list a)))
536 (c (add-text-to-store %store "c" "c" (list b)))
537 (d (add-text-to-store %store "d" "d" (list c)))
538 (w (add-text-to-store %store "w" "w"))
539 (x (add-text-to-store %store "x" "x" (list w)))
540 (y (add-text-to-store %store "y" "y" (list x d)))
541 (s1 (topologically-sorted %store (list y)))
542 (s2 (topologically-sorted %store (list c y)))
543 (s3 (topologically-sorted %store (cons y (references %store y)))))
58cbbe4b
LC
544 ;; The order in which 'references' returns the references of Y is
545 ;; unspecified, so accommodate.
546 (let* ((x-then-d? (equal? (references %store y) (list x d))))
547 (and (equal? s1
548 (if x-then-d?
549 (list w x a b c d y)
550 (list a b c d w x y)))
551 (equal? s2
552 (if x-then-d?
553 (list a b c w x d y)
554 (list a b c d w x y)))
555 (lset= string=? s1 s3)))))
50add477 556
ce72c780
LC
557(test-assert "current-build-output-port, UTF-8"
558 ;; Are UTF-8 strings in the build log properly interpreted?
559 (string-contains
560 (with-fluids ((%default-port-encoding "UTF-8")) ;for the string port
561 (call-with-output-string
562 (lambda (port)
563 (parameterize ((current-build-output-port port))
564 (let* ((s "Here’s a Greek letter: λ.")
565 (d (build-expression->derivation
566 %store "foo" `(display ,s)
567 #:guile-for-build
568 (package-derivation s %bootstrap-guile (%current-system)))))
f9e8a123 569 (guard (c ((store-protocol-error? c) #t))
ce72c780
LC
570 (build-derivations %store (list d))))))))
571 "Here’s a Greek letter: λ."))
572
573(test-assert "current-build-output-port, UTF-8 + garbage"
574 ;; What about a mixture of UTF-8 + garbage?
575 (string-contains
576 (with-fluids ((%default-port-encoding "UTF-8")) ;for the string port
577 (call-with-output-string
578 (lambda (port)
579 (parameterize ((current-build-output-port port))
580 (let ((d (build-expression->derivation
581 %store "foo"
582 `(begin
583 (use-modules (rnrs io ports))
584 (display "garbage: ")
585 (put-bytevector (current-output-port) #vu8(128))
586 (display "lambda: λ\n"))
587 #:guile-for-build
588 (package-derivation %store %bootstrap-guile))))
f9e8a123 589 (guard (c ((store-protocol-error? c) #t))
ce72c780 590 (build-derivations %store (list d))))))))
a65177a6 591 "garbage: �lambda: λ"))
ce72c780 592
eddd4077
LC
593(test-assert "log-file, derivation"
594 (let* ((b (add-text-to-store %store "build" "echo $foo > $out" '()))
595 (s (add-to-store %store "bash" #t "sha256"
596 (search-bootstrap-binary "bash"
597 (%current-system))))
598 (d (derivation %store "the-thing"
599 s `("-e" ,b)
600 #:env-vars `(("foo" . ,(random-text)))
601 #:inputs `((,b) (,s)))))
602 (and (build-derivations %store (list d))
603 (file-exists? (pk (log-file %store (derivation-file-name d)))))))
604
605(test-assert "log-file, output file name"
606 (let* ((b (add-text-to-store %store "build" "echo $foo > $out" '()))
607 (s (add-to-store %store "bash" #t "sha256"
608 (search-bootstrap-binary "bash"
609 (%current-system))))
610 (d (derivation %store "the-thing"
611 s `("-e" ,b)
612 #:env-vars `(("foo" . ,(random-text)))
613 #:inputs `((,b) (,s))))
614 (o (derivation->output-path d)))
615 (and (build-derivations %store (list d))
616 (file-exists? (pk (log-file %store o)))
617 (string=? (log-file %store (derivation-file-name d))
618 (log-file %store o)))))
619
0f3d2504 620(test-assert "no substitutes"
2d53df66
LC
621 (with-store s
622 (let* ((d1 (package-derivation s %bootstrap-guile (%current-system)))
623 (d2 (package-derivation s %bootstrap-glibc (%current-system)))
624 (o (map derivation->output-path (list d1 d2))))
625 (set-build-options s #:use-substitutes? #f)
626 (and (not (has-substitutes? s (derivation-file-name d1)))
627 (not (has-substitutes? s (derivation-file-name d2)))
628 (null? (substitutable-paths s o))
629 (null? (substitutable-path-info s o))))))
0f3d2504 630
abac874b
LC
631(test-assert "build-things with output path"
632 (with-store s
633 (let* ((c (random-text)) ;contents of the output
634 (d (build-expression->derivation
635 s "substitute-me"
636 `(call-with-output-file %output
637 (lambda (p)
638 (display ,c p)))
639 #:guile-for-build
640 (package-derivation s %bootstrap-guile (%current-system))))
641 (o (derivation->output-path d)))
642 (set-build-options s #:use-substitutes? #f)
643
644 ;; Pass 'build-things' the output file name, O. However, since there
645 ;; are no substitutes for O, it will just do nothing.
646 (build-things s (list o))
647 (not (valid-path? s o)))))
648
f65cf81a
LC
649(test-skip (if (getenv "GUIX_BINARY_SUBSTITUTE_URL") 0 1))
650
651(test-assert "substitute query"
2d53df66 652 (with-store s
6eebbab5
LC
653 (let* ((d (package-derivation s %bootstrap-guile (%current-system)))
654 (o (derivation->output-path d)))
2c74fde0 655 ;; Create fake substituter data, to be read by 'guix substitute'.
6eebbab5
LC
656 (with-derivation-narinfo d
657 ;; Remove entry from the local cache.
658 (false-if-exception
895d1eda
LC
659 (delete-file-recursively (string-append (getenv "XDG_CACHE_HOME")
660 "/guix/substitute")))
6eebbab5 661
2c74fde0 662 ;; Make sure 'guix substitute' correctly communicates the above
6eebbab5 663 ;; data.
24f5aaaf
LC
664 (set-build-options s #:use-substitutes? #t
665 #:substitute-urls (%test-substitute-urls))
6eebbab5
LC
666 (and (has-substitutes? s o)
667 (equal? (list o) (substitutable-paths s (list o)))
668 (match (pk 'spi (substitutable-path-info s (list o)))
669 (((? substitutable? s))
670 (and (string=? (substitutable-deriver s)
671 (derivation-file-name d))
672 (null? (substitutable-references s))
673 (equal? (substitutable-nar-size s) 1234)))))))))
f65cf81a 674
24f5aaaf
LC
675(test-assert "substitute query, alternating URLs"
676 (let* ((d (with-store s
677 (package-derivation s %bootstrap-guile (%current-system))))
678 (o (derivation->output-path d)))
679 (with-derivation-narinfo d
680 ;; Remove entry from the local cache.
681 (false-if-exception
682 (delete-file-recursively (string-append (getenv "XDG_CACHE_HOME")
683 "/guix/substitute")))
684
685 ;; Note: We reconnect to the daemon to force a new instance of 'guix
686 ;; substitute' to be used; otherwise the #:substitute-urls of
687 ;; 'set-build-options' would have no effect.
688
689 (and (with-store s ;the right substitute URL
690 (set-build-options s #:use-substitutes? #t
691 #:substitute-urls (%test-substitute-urls))
692 (has-substitutes? s o))
693 (with-store s ;the wrong one
694 (set-build-options s #:use-substitutes? #t
695 #:substitute-urls (list
696 "http://does-not-exist"))
697 (not (has-substitutes? s o)))
698 (with-store s ;the right one again
699 (set-build-options s #:use-substitutes? #t
700 #:substitute-urls (%test-substitute-urls))
71e2065a
LC
701 (has-substitutes? s o))
702 (with-store s ;empty list of URLs
703 (set-build-options s #:use-substitutes? #t
704 #:substitute-urls '())
705 (not (has-substitutes? s o)))))))
24f5aaaf 706
fe0cff14 707(test-assert "substitute"
2d53df66
LC
708 (with-store s
709 (let* ((c (random-text)) ; contents of the output
710 (d (build-expression->derivation
711 s "substitute-me"
712 `(call-with-output-file %output
713 (lambda (p)
714 (exit 1) ; would actually fail
715 (display ,c p)))
716 #:guile-for-build
717 (package-derivation s %bootstrap-guile (%current-system))))
e6c8839c
LC
718 (o (derivation->output-path d)))
719 (with-derivation-substitute d c
24f5aaaf
LC
720 (set-build-options s #:use-substitutes? #t
721 #:substitute-urls (%test-substitute-urls))
e6c8839c
LC
722 (and (has-substitutes? s o)
723 (build-derivations s (list d))
724 (equal? c (call-with-input-file o get-string-all)))))))
fe0cff14 725
abac874b
LC
726(test-assert "substitute + build-things with output path"
727 (with-store s
728 (let* ((c (random-text)) ;contents of the output
729 (d (build-expression->derivation
730 s "substitute-me"
731 `(call-with-output-file %output
732 (lambda (p)
733 (exit 1) ;would actually fail
734 (display ,c p)))
735 #:guile-for-build
736 (package-derivation s %bootstrap-guile (%current-system))))
737 (o (derivation->output-path d)))
738 (with-derivation-substitute d c
24f5aaaf
LC
739 (set-build-options s #:use-substitutes? #t
740 #:substitute-urls (%test-substitute-urls))
abac874b
LC
741 (and (has-substitutes? s o)
742 (build-things s (list o)) ;give the output path
743 (valid-path? s o)
744 (equal? c (call-with-input-file o get-string-all)))))))
745
f8a9f99c
LC
746(test-assert "substitute + build-things with specific output"
747 (with-store s
748 (let* ((c (random-text)) ;contents of the output
749 (d (build-expression->derivation
750 s "substitute-me" `(begin ,c (exit 1)) ;would fail
751 #:outputs '("out" "one" "two")
752 #:guile-for-build
753 (package-derivation s %bootstrap-guile (%current-system))))
754 (o (derivation->output-path d)))
755 (with-derivation-substitute d c
756 (set-build-options s #:use-substitutes? #t
757 #:substitute-urls (%test-substitute-urls))
758 (and (has-substitutes? s o)
759
760 ;; Ask for nothing but the "out" output of D.
761 (build-things s `((,(derivation-file-name d) . "out")))
762
763 (valid-path? s o)
764 (equal? c (call-with-input-file o get-string-all)))))))
765
491e6de7
LC
766(test-assert "substitute, corrupt output hash"
767 ;; Tweak the substituter into installing a substitute whose hash doesn't
768 ;; match the one announced in the narinfo. The daemon must notice this and
769 ;; raise an error.
2d53df66
LC
770 (with-store s
771 (let* ((c "hello, world") ; contents of the output
772 (d (build-expression->derivation
773 s "corrupt-substitute"
774 `(mkdir %output)
775 #:guile-for-build
776 (package-derivation s %bootstrap-guile (%current-system))))
e6c8839c
LC
777 (o (derivation->output-path d)))
778 (with-derivation-substitute d c
779 (sha256 => (make-bytevector 32 0)) ;select a hash that doesn't match C
780
2c74fde0 781 ;; Make sure we use 'guix substitute'.
e6c8839c
LC
782 (set-build-options s
783 #:use-substitutes? #t
24f5aaaf
LC
784 #:fallback? #f
785 #:substitute-urls (%test-substitute-urls))
e6c8839c 786 (and (has-substitutes? s o)
f9e8a123 787 (guard (c ((store-protocol-error? c)
e6c8839c
LC
788 ;; XXX: the daemon writes "hash mismatch in downloaded
789 ;; path", but the actual error returned to the client
790 ;; doesn't mention that.
791 (pk 'corrupt c)
f9e8a123 792 (not (zero? (store-protocol-error-status c)))))
e6c8839c
LC
793 (build-derivations s (list d))
794 #f))))))
491e6de7 795
c3eb878f 796(test-assert "substitute --fallback"
2d53df66 797 (with-store s
6eebbab5 798 (let* ((t (random-text)) ; contents of the output
2d53df66
LC
799 (d (build-expression->derivation
800 s "substitute-me-not"
801 `(call-with-output-file %output
802 (lambda (p)
803 (display ,t p)))
804 #:guile-for-build
805 (package-derivation s %bootstrap-guile (%current-system))))
6eebbab5 806 (o (derivation->output-path d)))
2c74fde0 807 ;; Create fake substituter data, to be read by 'guix substitute'.
6eebbab5 808 (with-derivation-narinfo d
2c74fde0 809 ;; Make sure we use 'guix substitute'.
24f5aaaf
LC
810 (set-build-options s #:use-substitutes? #t
811 #:substitute-urls (%test-substitute-urls))
6eebbab5 812 (and (has-substitutes? s o)
f9e8a123 813 (guard (c ((store-protocol-error? c)
6eebbab5
LC
814 ;; The substituter failed as expected. Now make
815 ;; sure that #:fallback? #t works correctly.
816 (set-build-options s
817 #:use-substitutes? #t
24f5aaaf
LC
818 #:substitute-urls
819 (%test-substitute-urls)
6eebbab5
LC
820 #:fallback? #t)
821 (and (build-derivations s (list d))
822 (equal? t (call-with-input-file o
823 get-string-all)))))
824 ;; Should fail.
825 (build-derivations s (list d))
826 #f))))))
c3eb878f 827
526382ff
LC
828(test-assert "export/import several paths"
829 (let* ((texts (unfold (cut >= <> 10)
830 (lambda _ (random-text))
831 1+
832 0))
833 (files (map (cut add-text-to-store %store "text" <>) texts))
834 (dump (call-with-bytevector-output-port
835 (cut export-paths %store files <>))))
836 (delete-paths %store files)
837 (and (every (negate file-exists?) files)
838 (let* ((source (open-bytevector-input-port dump))
839 (imported (import-paths %store source)))
840 (and (equal? imported files)
841 (every file-exists? files)
842 (equal? texts
843 (map (lambda (file)
844 (call-with-input-file file
845 get-string-all))
846 files)))))))
847
99fbddf9 848(test-assert "export/import paths, ensure topological order"
cafb92d8
LC
849 (let* ((file0 (add-text-to-store %store "baz" (random-text)))
850 (file1 (add-text-to-store %store "foo" (random-text)
851 (list file0)))
99fbddf9
LC
852 (file2 (add-text-to-store %store "bar" (random-text)
853 (list file1)))
854 (files (list file1 file2))
855 (dump1 (call-with-bytevector-output-port
856 (cute export-paths %store (list file1 file2) <>)))
857 (dump2 (call-with-bytevector-output-port
858 (cute export-paths %store (list file2 file1) <>))))
859 (delete-paths %store files)
860 (and (every (negate file-exists?) files)
861 (bytevector=? dump1 dump2)
862 (let* ((source (open-bytevector-input-port dump1))
863 (imported (import-paths %store source)))
cafb92d8 864 ;; DUMP1 should contain exactly FILE1 and FILE2, not FILE0.
99fbddf9
LC
865 (and (equal? imported (list file1 file2))
866 (every file-exists? files)
cafb92d8 867 (equal? (list file0) (references %store file1))
99fbddf9
LC
868 (equal? (list file1) (references %store file2)))))))
869
5b3d863f
LC
870(test-assert "export/import incomplete"
871 (let* ((file0 (add-text-to-store %store "baz" (random-text)))
872 (file1 (add-text-to-store %store "foo" (random-text)
873 (list file0)))
874 (file2 (add-text-to-store %store "bar" (random-text)
875 (list file1)))
876 (dump (call-with-bytevector-output-port
877 (cute export-paths %store (list file2) <>))))
878 (delete-paths %store (list file0 file1 file2))
f9e8a123
LC
879 (guard (c ((store-protocol-error? c)
880 (and (not (zero? (store-protocol-error-status c)))
881 (string-contains (store-protocol-error-message c)
5b3d863f
LC
882 "not valid"))))
883 ;; Here we get an exception because DUMP does not include FILE0 and
884 ;; FILE1, which are dependencies of FILE2.
885 (import-paths %store (open-bytevector-input-port dump)))))
886
887(test-assert "export/import recursive"
888 (let* ((file0 (add-text-to-store %store "baz" (random-text)))
889 (file1 (add-text-to-store %store "foo" (random-text)
890 (list file0)))
891 (file2 (add-text-to-store %store "bar" (random-text)
892 (list file1)))
893 (dump (call-with-bytevector-output-port
894 (cute export-paths %store (list file2) <>
895 #:recursive? #t))))
896 (delete-paths %store (list file0 file1 file2))
897 (let ((imported (import-paths %store (open-bytevector-input-port dump))))
898 (and (equal? imported (list file0 file1 file2))
899 (every file-exists? (list file0 file1 file2))
900 (equal? (list file0) (references %store file1))
901 (equal? (list file1) (references %store file2))))))
902
320ca999
LC
903(test-assert "write-file & export-path yield the same result"
904 ;; Here we compare 'write-file' and the daemon's own implementation.
905 ;; 'write-file' is the reference because we know it sorts file
906 ;; deterministically. Conversely, the daemon uses 'readdir' and the entries
907 ;; currently happen to be sorted as a side-effect of some unrelated
908 ;; operation (search for 'unhacked' in archive.cc.) Make sure we detect any
909 ;; changes there.
910 (run-with-store %store
911 (mlet* %store-monad ((drv1 (package->derivation %bootstrap-guile))
912 (out1 -> (derivation->output-path drv1))
913 (data -> (unfold (cut >= <> 26)
914 (lambda (i)
915 (random-bytevector 128))
916 1+ 0))
917 (build
918 -> #~(begin
919 (use-modules (rnrs io ports) (srfi srfi-1))
920 (let ()
921 (define letters
922 (map (lambda (i)
923 (string
924 (integer->char
925 (+ i (char->integer #\a)))))
926 (iota 26)))
927 (define (touch file data)
928 (call-with-output-file file
929 (lambda (port)
930 (put-bytevector port data))))
931
932 (mkdir #$output)
933 (chdir #$output)
934
935 ;; The files must be different so they have
936 ;; different inode numbers, and the inode
937 ;; order must differ from the lexicographic
938 ;; order.
939 (for-each touch
940 (append (drop letters 10)
941 (take letters 10))
942 (list #$@data))
943 #t)))
944 (drv2 (gexp->derivation "bunch" build))
945 (out2 -> (derivation->output-path drv2))
946 (item-info -> (store-lift query-path-info)))
947 (mbegin %store-monad
948 (built-derivations (list drv1 drv2))
949 (foldm %store-monad
950 (lambda (item result)
951 (define ref-hash
ce0be567 952 (let-values (((port get) (gcrypt:open-sha256-port)))
320ca999
LC
953 (write-file item port)
954 (close-port port)
955 (get)))
956
957 ;; 'query-path-info' returns a hash produced by using the
958 ;; daemon's C++ 'dump' function, which is the implementation
959 ;; under test.
960 (>>= (item-info item)
961 (lambda (info)
962 (return
963 (and result
964 (bytevector=? (path-info-hash info) ref-hash))))))
965 #t
966 (list out1 out2))))
967 #:guile-for-build (%guile-for-build)))
968
526382ff
LC
969(test-assert "import corrupt path"
970 (let* ((text (random-text))
971 (file (add-text-to-store %store "text" text))
972 (dump (call-with-bytevector-output-port
973 (cut export-paths %store (list file) <>))))
974 (delete-paths %store (list file))
975
1a0adad8
LC
976 ;; Flip a bit in the stream's payload. INDEX here falls in the middle of
977 ;; the file contents in DUMP, regardless of the store prefix.
978 (let* ((index #x70)
526382ff
LC
979 (byte (bytevector-u8-ref dump index)))
980 (bytevector-u8-set! dump index (logxor #xff byte)))
981
982 (and (not (file-exists? file))
f9e8a123 983 (guard (c ((store-protocol-error? c)
526382ff 984 (pk 'c c)
f9e8a123
LC
985 (and (not (zero? (store-protocol-error-status c)))
986 (string-contains (store-protocol-error-message c)
526382ff
LC
987 "corrupt"))))
988 (let* ((source (open-bytevector-input-port dump))
989 (imported (import-paths %store source)))
990 (pk 'corrupt-imported imported)
991 #f)))))
992
c63d9403
LC
993(test-assert "verify-store"
994 (let* ((text (random-text))
995 (file1 (add-text-to-store %store "foo" text))
996 (file2 (add-text-to-store %store "bar" (random-text)
997 (list file1))))
998 (and (pk 'verify1 (verify-store %store)) ;hopefully OK ;
999 (begin
1000 (delete-file file1)
1001 (not (pk 'verify2 (verify-store %store)))) ;bad! ;
1002 (begin
1003 ;; Using 'add-text-to-store' here wouldn't work: It would succeed ;
1004 ;; without actually creating the file. ;
1005 (call-with-output-file file1
1006 (lambda (port)
1007 (display text port)))
1008 (pk 'verify3 (verify-store %store)))))) ;OK again
1009
1010(test-assert "verify-store + check-contents"
1011 ;; XXX: This test is I/O intensive.
1012 (with-store s
1013 (let* ((text (random-text))
1014 (drv (build-expression->derivation
1015 s "corrupt"
1016 `(let ((out (assoc-ref %outputs "out")))
1017 (call-with-output-file out
1018 (lambda (port)
1019 (display ,text port)))
1020 #t)
1021 #:guile-for-build
1022 (package-derivation s %bootstrap-guile (%current-system))))
1023 (file (derivation->output-path drv)))
1024 (with-derivation-substitute drv text
1025 (and (build-derivations s (list drv))
1026 (verify-store s #:check-contents? #t) ;should be OK
1027 (begin
1028 (chmod file #o644)
1029 (call-with-output-file file
1030 (lambda (port)
1031 (display "corrupt!" port)))
1032 #t)
1033
1034 ;; Make sure the corruption is detected. We don't test repairing
1035 ;; because only "trusted" users are allowed to do it, but we
1036 ;; don't expose that notion of trusted users that nix-daemon
1037 ;; supports because it seems dubious and redundant with what the
1038 ;; OS provides (in Nix "trusted" users have additional
1039 ;; privileges, such as overriding the set of substitute URLs, but
1040 ;; we instead want to allow anyone to modify them, provided
1041 ;; substitutes are signed by a root-approved key.)
1042 (not (verify-store s #:check-contents? #t))
1043
1044 ;; Delete the corrupt item to leave the store in a clean state.
1045 (delete-paths s (list file)))))))
1046
07e70f48
LC
1047(test-assert "build-things, check mode"
1048 (with-store store
1049 (call-with-temporary-output-file
1050 (lambda (entropy entropy-port)
1051 (write (random-text) entropy-port)
1052 (force-output entropy-port)
1053 (let* ((drv (build-expression->derivation
1054 store "non-deterministic"
1055 `(begin
1056 (use-modules (rnrs io ports))
1057 (let ((out (assoc-ref %outputs "out")))
1058 (call-with-output-file out
1059 (lambda (port)
2fba87ac
LC
1060 ;; Rely on the fact that tests do not use the
1061 ;; chroot, and thus ENTROPY is readable.
07e70f48
LC
1062 (display (call-with-input-file ,entropy
1063 get-string-all)
1064 port)))
1065 #t))
1066 #:guile-for-build
1067 (package-derivation store %bootstrap-guile (%current-system))))
1068 (file (derivation->output-path drv)))
1069 (and (build-things store (list (derivation-file-name drv)))
1070 (begin
1071 (write (random-text) entropy-port)
1072 (force-output entropy-port)
f9e8a123 1073 (guard (c ((store-protocol-error? c)
07e70f48 1074 (pk 'determinism-exception c)
f9e8a123
LC
1075 (and (not (zero? (store-protocol-error-status c)))
1076 (string-contains (store-protocol-error-message c)
07e70f48
LC
1077 "deterministic"))))
1078 ;; This one will produce a different result. Since we're in
1079 ;; 'check' mode, this must fail.
1080 (build-things store (list (derivation-file-name drv))
1081 (build-mode check))
1082 #f))))))))
1083
d5912428
LC
1084(test-assert "build-succeeded trace in check mode"
1085 (string-contains
1086 (call-with-output-string
1087 (lambda (port)
1088 (let ((d (build-expression->derivation
1089 %store "foo" '(mkdir (assoc-ref %outputs "out"))
1090 #:guile-for-build
1091 (package-derivation %store %bootstrap-guile))))
1092 (build-derivations %store (list d))
1093 (parameterize ((current-build-output-port port))
1094 (build-derivations %store (list d) (build-mode check))))))
1095 "@ build-succeeded"))
1096
2fba87ac
LC
1097(test-assert "build multiple times"
1098 (with-store store
1099 ;; Ask to build twice.
1100 (set-build-options store #:rounds 2 #:use-substitutes? #f)
1101
1102 (call-with-temporary-output-file
1103 (lambda (entropy entropy-port)
1104 (write (random-text) entropy-port)
1105 (force-output entropy-port)
1106 (let* ((drv (build-expression->derivation
1107 store "non-deterministic"
1108 `(begin
1109 (use-modules (rnrs io ports))
1110 (let ((out (assoc-ref %outputs "out")))
1111 (call-with-output-file out
1112 (lambda (port)
1113 ;; Rely on the fact that tests do not use the
1114 ;; chroot, and thus ENTROPY is accessible.
1115 (display (call-with-input-file ,entropy
1116 get-string-all)
1117 port)
1118 (call-with-output-file ,entropy
1119 (lambda (port)
1120 (write 'foobar port)))))
1121 #t))
1122 #:guile-for-build
1123 (package-derivation store %bootstrap-guile (%current-system))))
1124 (file (derivation->output-path drv)))
f9e8a123 1125 (guard (c ((store-protocol-error? c)
2fba87ac 1126 (pk 'multiple-build c)
f9e8a123
LC
1127 (and (not (zero? (store-protocol-error-status c)))
1128 (string-contains (store-protocol-error-message c)
2fba87ac
LC
1129 "deterministic"))))
1130 ;; This one will produce a different result on the second run.
1131 (current-build-output-port (current-error-port))
1132 (build-things store (list (derivation-file-name drv)))
1133 #f))))))
1134
023d9892
LC
1135(test-equal "store-lower"
1136 "Lowered."
1137 (let* ((add (store-lower text-file))
1138 (file (add %store "foo" "Lowered.")))
1139 (call-with-input-file file get-string-all)))
1140
98a7b528
LC
1141(test-equal "current-system"
1142 "bar"
1143 (parameterize ((%current-system "frob"))
1144 (run-with-store %store
1145 (mbegin %store-monad
1146 (set-current-system "bar")
1147 (current-system))
1148 #:system "foo")))
1149
533d1768
DT
1150(test-assert "query-path-info"
1151 (let* ((ref (add-text-to-store %store "ref" "foo"))
1152 (item (add-text-to-store %store "item" "bar" (list ref)))
1153 (info (query-path-info %store item)))
1154 (and (equal? (path-info-references info) (list ref))
1155 (equal? (path-info-hash info)
ce0be567 1156 (gcrypt:sha256
533d1768
DT
1157 (string->utf8
1158 (call-with-output-string (cut write-file item <>))))))))
1159
22572d56
LC
1160(test-assert "path-info-deriver"
1161 (let* ((b (add-text-to-store %store "build" "echo $foo > $out" '()))
1162 (s (add-to-store %store "bash" #t "sha256"
1163 (search-bootstrap-binary "bash"
1164 (%current-system))))
1165 (d (derivation %store "the-thing"
1166 s `("-e" ,b)
1167 #:env-vars `(("foo" . ,(random-text)))
1168 #:inputs `((,b) (,s))))
1169 (o (derivation->output-path d)))
1170 (and (build-derivations %store (list d))
1171 (not (path-info-deriver (query-path-info %store b)))
1172 (string=? (derivation-file-name d)
1173 (path-info-deriver (query-path-info %store o))))))
1174
deac976d
LC
1175(test-equal "build-cores"
1176 (list 0 42)
1177 (with-store store
1178 (let* ((build (add-text-to-store store "build.sh"
1179 "echo $NIX_BUILD_CORES > $out"))
1180 (bash (add-to-store store "bash" #t "sha256"
1181 (search-bootstrap-binary "bash"
1182 (%current-system))))
1183 (drv1 (derivation store "the-thing" bash
1184 `("-e" ,build)
1185 #:inputs `((,bash) (,build))
1186 #:env-vars `(("x" . ,(random-text)))))
1187 (drv2 (derivation store "the-thing" bash
1188 `("-e" ,build)
1189 #:inputs `((,bash) (,build))
1190 #:env-vars `(("x" . ,(random-text))))))
1191 (and (build-derivations store (list drv1))
1192 (begin
1193 (set-build-options store #:build-cores 42)
1194 (build-derivations store (list drv2)))
1195 (list (call-with-input-file (derivation->output-path drv1)
1196 read)
1197 (call-with-input-file (derivation->output-path drv2)
1198 read))))))
1199
6ef61cc4
LC
1200(test-equal "multiplexed-build-output"
1201 '("Hello from first." "Hello from second.")
1202 (with-store store
1203 (let* ((build (add-text-to-store store "build.sh"
1204 "echo Hello from $NAME.; echo > $out"))
1205 (bash (add-to-store store "bash" #t "sha256"
1206 (search-bootstrap-binary "bash"
1207 (%current-system))))
1208 (drv1 (derivation store "one" bash
1209 `("-e" ,build)
1210 #:inputs `((,bash) (,build))
1211 #:env-vars `(("NAME" . "first")
1212 ("x" . ,(random-text)))))
1213 (drv2 (derivation store "two" bash
1214 `("-e" ,build)
1215 #:inputs `((,bash) (,build))
1216 #:env-vars `(("NAME" . "second")
1217 ("x" . ,(random-text))))))
1218 (set-build-options store
1219 #:print-build-trace #t
1220 #:multiplexed-build-output? #t
1221 #:max-build-jobs 10)
1222 (let ((port (open-output-string)))
1223 ;; Send the build log to PORT.
1224 (parameterize ((current-build-output-port port))
1225 (build-derivations store (list drv1 drv2)))
1226
1227 ;; Retrieve the build log; make sure it contains valid "@ build-log"
1228 ;; traces that allow us to retrieve each builder's output (we assume
1229 ;; there's exactly one "build-output" trace for each builder, which is
1230 ;; reasonable.)
1231 (let* ((log (get-output-string port))
1232 (started (fold-matches
1233 (make-regexp "@ build-started ([^ ]+) - ([^ ]+) ([^ ]+) ([0-9]+)")
1234 log '() cons))
1235 (done (fold-matches
1236 (make-regexp "@ build-succeeded (.*) - (.*) (.*) (.*)")
1237 log '() cons))
1238 (output (fold-matches
1239 (make-regexp "@ build-log ([[:digit:]]+) ([[:digit:]]+)\n([A-Za-z .*]+)\n")
1240 log '() cons))
1241 (drv-pid (lambda (name)
1242 (lambda (m)
1243 (let ((drv (match:substring m 1))
1244 (pid (string->number
1245 (match:substring m 4))))
1246 (and (string-suffix? name drv) pid)))))
1247 (pid-log (lambda (pid)
1248 (lambda (m)
1249 (let ((n (string->number
1250 (match:substring m 1)))
1251 (len (string->number
1252 (match:substring m 2)))
1253 (str (match:substring m 3)))
1254 (and (= pid n)
1255 (= (string-length str) (- len 1))
1256 str)))))
1257 (pid1 (any (drv-pid "one.drv") started))
1258 (pid2 (any (drv-pid "two.drv") started)))
1259 (list (any (pid-log pid1) output)
1260 (any (pid-log pid2) output)))))))
1261
3259877d 1262(test-end "store")