| 1 | ;;; GNU Guix --- Functional package management for GNU |
| 2 | ;;; Copyright © 2012, 2013, 2014, 2015, 2016, 2017 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 | |
| 19 | (define-module (test-store) |
| 20 | #:use-module (guix tests) |
| 21 | #:use-module (guix store) |
| 22 | #:use-module (guix utils) |
| 23 | #:use-module (guix monads) |
| 24 | #:use-module (guix hash) |
| 25 | #:use-module (guix base32) |
| 26 | #:use-module (guix packages) |
| 27 | #:use-module (guix derivations) |
| 28 | #:use-module (guix serialization) |
| 29 | #:use-module (guix build utils) |
| 30 | #:use-module (guix gexp) |
| 31 | #:use-module (gnu packages) |
| 32 | #:use-module (gnu packages bootstrap) |
| 33 | #:use-module (ice-9 match) |
| 34 | #:use-module (rnrs bytevectors) |
| 35 | #:use-module (rnrs io ports) |
| 36 | #:use-module (web uri) |
| 37 | #:use-module (srfi srfi-1) |
| 38 | #:use-module (srfi srfi-11) |
| 39 | #:use-module (srfi srfi-26) |
| 40 | #:use-module (srfi srfi-34) |
| 41 | #:use-module (srfi srfi-64)) |
| 42 | |
| 43 | ;; Test the (guix store) module. |
| 44 | |
| 45 | (define %store |
| 46 | (open-connection-for-tests)) |
| 47 | |
| 48 | \f |
| 49 | (test-begin "store") |
| 50 | |
| 51 | (test-assert "open-connection with file:// URI" |
| 52 | (let ((store (open-connection (string-append "file://" |
| 53 | (%daemon-socket-uri))))) |
| 54 | (and (add-text-to-store store "foo" "bar") |
| 55 | (begin |
| 56 | (close-connection store) |
| 57 | #t)))) |
| 58 | |
| 59 | (test-equal "connection handshake error" |
| 60 | EPROTO |
| 61 | (let ((port (%make-void-port "rw"))) |
| 62 | (guard (c ((nix-connection-error? c) |
| 63 | (and (eq? port (nix-connection-error-file c)) |
| 64 | (nix-connection-error-code c)))) |
| 65 | (open-connection #f #:port port) |
| 66 | 'broken))) |
| 67 | |
| 68 | (test-equal "store-path-hash-part" |
| 69 | "283gqy39v3g9dxjy26rynl0zls82fmcg" |
| 70 | (store-path-hash-part |
| 71 | (string-append (%store-prefix) |
| 72 | "/283gqy39v3g9dxjy26rynl0zls82fmcg-guile-2.0.7"))) |
| 73 | |
| 74 | (test-equal "store-path-hash-part #f" |
| 75 | #f |
| 76 | (store-path-hash-part |
| 77 | (string-append (%store-prefix) |
| 78 | "/foo/bar/283gqy39v3g9dxjy26rynl0zls82fmcg-guile-2.0.7"))) |
| 79 | |
| 80 | (test-equal "store-path-package-name" |
| 81 | "guile-2.0.7" |
| 82 | (store-path-package-name |
| 83 | (string-append (%store-prefix) |
| 84 | "/283gqy39v3g9dxjy26rynl0zls82fmcg-guile-2.0.7"))) |
| 85 | |
| 86 | (test-equal "store-path-package-name #f" |
| 87 | #f |
| 88 | (store-path-package-name |
| 89 | "/foo/bar/283gqy39v3g9dxjy26rynl0zls82fmcg-guile-2.0.7")) |
| 90 | |
| 91 | (test-assert "direct-store-path?" |
| 92 | (and (direct-store-path? |
| 93 | (string-append (%store-prefix) |
| 94 | "/283gqy39v3g9dxjy26rynl0zls82fmcg-guile-2.0.7")) |
| 95 | (not (direct-store-path? |
| 96 | (string-append |
| 97 | (%store-prefix) |
| 98 | "/283gqy39v3g9dxjy26rynl0zls82fmcg-guile-2.0.7/bin/guile"))) |
| 99 | (not (direct-store-path? (%store-prefix))))) |
| 100 | |
| 101 | (test-skip (if %store 0 13)) |
| 102 | |
| 103 | (test-equal "add-data-to-store" |
| 104 | #vu8(1 2 3 4 5) |
| 105 | (call-with-input-file (add-data-to-store %store "data" #vu8(1 2 3 4 5)) |
| 106 | get-bytevector-all)) |
| 107 | |
| 108 | (test-assert "valid-path? live" |
| 109 | (let ((p (add-text-to-store %store "hello" "hello, world"))) |
| 110 | (valid-path? %store p))) |
| 111 | |
| 112 | (test-assert "valid-path? false" |
| 113 | (not (valid-path? %store |
| 114 | (string-append (%store-prefix) "/" |
| 115 | (make-string 32 #\e) "-foobar")))) |
| 116 | |
| 117 | (test-assert "valid-path? error" |
| 118 | (with-store s |
| 119 | (guard (c ((nix-protocol-error? c) #t)) |
| 120 | (valid-path? s "foo") |
| 121 | #f))) |
| 122 | |
| 123 | (test-assert "valid-path? recovery" |
| 124 | ;; Prior to Nix commit 51800e0 (18 Mar. 2014), the daemon would immediately |
| 125 | ;; close the connection after receiving a 'valid-path?' RPC with a non-store |
| 126 | ;; file name. See |
| 127 | ;; <http://article.gmane.org/gmane.linux.distributions.nixos/12411> for |
| 128 | ;; details. |
| 129 | (with-store s |
| 130 | (let-syntax ((true-if-error (syntax-rules () |
| 131 | ((_ exp) |
| 132 | (guard (c ((nix-protocol-error? c) #t)) |
| 133 | exp #f))))) |
| 134 | (and (true-if-error (valid-path? s "foo")) |
| 135 | (true-if-error (valid-path? s "bar")) |
| 136 | (true-if-error (valid-path? s "baz")) |
| 137 | (true-if-error (valid-path? s "chbouib")) |
| 138 | (valid-path? s (add-text-to-store s "valid" "yeah")))))) |
| 139 | |
| 140 | (test-assert "hash-part->path" |
| 141 | (let ((p (add-text-to-store %store "hello" "hello, world"))) |
| 142 | (equal? (hash-part->path %store (store-path-hash-part p)) |
| 143 | p))) |
| 144 | |
| 145 | (test-assert "dead-paths" |
| 146 | (let ((p (add-text-to-store %store "random-text" (random-text)))) |
| 147 | (->bool (member p (dead-paths %store))))) |
| 148 | |
| 149 | ;; FIXME: Find a test for `live-paths'. |
| 150 | ;; |
| 151 | ;; (test-assert "temporary root is in live-paths" |
| 152 | ;; (let* ((p1 (add-text-to-store %store "random-text" |
| 153 | ;; (random-text) '())) |
| 154 | ;; (b (add-text-to-store %store "link-builder" |
| 155 | ;; (format #f "echo ~a > $out" p1) |
| 156 | ;; '())) |
| 157 | ;; (d1 (derivation %store "link" |
| 158 | ;; "/bin/sh" `("-e" ,b) |
| 159 | ;; #:inputs `((,b) (,p1)))) |
| 160 | ;; (p2 (derivation->output-path d1))) |
| 161 | ;; (and (add-temp-root %store p2) |
| 162 | ;; (build-derivations %store (list d1)) |
| 163 | ;; (valid-path? %store p1) |
| 164 | ;; (member (pk p2) (live-paths %store))))) |
| 165 | |
| 166 | (test-assert "permanent root" |
| 167 | (let* ((p (with-store store |
| 168 | (let ((p (add-text-to-store store "random-text" |
| 169 | (random-text)))) |
| 170 | (add-permanent-root p) |
| 171 | (add-permanent-root p) ; should not throw |
| 172 | p)))) |
| 173 | (and (member p (live-paths %store)) |
| 174 | (begin |
| 175 | (remove-permanent-root p) |
| 176 | (->bool (member p (dead-paths %store))))))) |
| 177 | |
| 178 | (test-assert "dead path can be explicitly collected" |
| 179 | (let ((p (add-text-to-store %store "random-text" |
| 180 | (random-text) '()))) |
| 181 | (let-values (((paths freed) (delete-paths %store (list p)))) |
| 182 | (and (equal? paths (list p)) |
| 183 | ;; XXX: On some file systems (notably Btrfs), freed |
| 184 | ;; may return 0. See <https://bugs.gnu.org/29363>. |
| 185 | ;;(> freed 0) |
| 186 | (not (file-exists? p)))))) |
| 187 | |
| 188 | (test-assert "add-text-to-store vs. delete-paths" |
| 189 | ;; Before, 'add-text-to-store' would return PATH2 without noticing that it |
| 190 | ;; is no longer valid. |
| 191 | (with-store store |
| 192 | (let* ((text (random-text)) |
| 193 | (path (add-text-to-store store "delete-me" text)) |
| 194 | (deleted (delete-paths store (list path))) |
| 195 | (path2 (add-text-to-store store "delete-me" text))) |
| 196 | (and (string=? path path2) |
| 197 | (equal? deleted (list path)) |
| 198 | (valid-path? store path) |
| 199 | (file-exists? path))))) |
| 200 | |
| 201 | (test-assert "add-to-store vs. delete-paths" |
| 202 | ;; Same as above. |
| 203 | (with-store store |
| 204 | (let* ((file (search-path %load-path "guix.scm")) |
| 205 | (path (add-to-store store "delete-me" #t "sha256" file)) |
| 206 | (deleted (delete-paths store (list path))) |
| 207 | (path2 (add-to-store store "delete-me" #t "sha256" file))) |
| 208 | (and (string=? path path2) |
| 209 | (equal? deleted (list path)) |
| 210 | (valid-path? store path) |
| 211 | (file-exists? path))))) |
| 212 | |
| 213 | (test-assert "references" |
| 214 | (let* ((t1 (add-text-to-store %store "random1" |
| 215 | (random-text))) |
| 216 | (t2 (add-text-to-store %store "random2" |
| 217 | (random-text) (list t1)))) |
| 218 | (and (equal? (list t1) (references %store t2)) |
| 219 | (equal? (list t2) (referrers %store t1)) |
| 220 | (null? (references %store t1)) |
| 221 | (null? (referrers %store t2))))) |
| 222 | |
| 223 | (test-assert "references/substitutes missing reference info" |
| 224 | (with-store s |
| 225 | (set-build-options s #:use-substitutes? #f) |
| 226 | (guard (c ((nix-protocol-error? c) #t)) |
| 227 | (let* ((b (add-to-store s "bash" #t "sha256" |
| 228 | (search-bootstrap-binary "bash" |
| 229 | (%current-system)))) |
| 230 | (d (derivation s "the-thing" b '("--help") |
| 231 | #:inputs `((,b))))) |
| 232 | (references/substitutes s (list (derivation->output-path d) b)) |
| 233 | #f)))) |
| 234 | |
| 235 | (test-assert "references/substitutes with substitute info" |
| 236 | (with-store s |
| 237 | (set-build-options s #:use-substitutes? #t) |
| 238 | (let* ((t1 (add-text-to-store s "random1" (random-text))) |
| 239 | (t2 (add-text-to-store s "random2" (random-text) |
| 240 | (list t1))) |
| 241 | (t3 (add-text-to-store s "build" "echo -n $t2 > $out")) |
| 242 | (b (add-to-store s "bash" #t "sha256" |
| 243 | (search-bootstrap-binary "bash" |
| 244 | (%current-system)))) |
| 245 | (d (derivation s "the-thing" b `("-e" ,t3) |
| 246 | #:inputs `((,b) (,t3) (,t2)) |
| 247 | #:env-vars `(("t2" . ,t2)))) |
| 248 | (o (derivation->output-path d))) |
| 249 | (with-derivation-narinfo d |
| 250 | (sha256 => (sha256 (string->utf8 t2))) |
| 251 | (references => (list t2)) |
| 252 | |
| 253 | (equal? (references/substitutes s (list o t3 t2 t1)) |
| 254 | `((,t2) ;refs of O |
| 255 | () ;refs of T3 |
| 256 | (,t1) ;refs of T2 |
| 257 | ())))))) ;refs of T1 |
| 258 | |
| 259 | (test-equal "substitutable-path-info when substitutes are turned off" |
| 260 | '() |
| 261 | (with-store s |
| 262 | (set-build-options s #:use-substitutes? #f) |
| 263 | (let* ((b (add-to-store s "bash" #t "sha256" |
| 264 | (search-bootstrap-binary "bash" |
| 265 | (%current-system)))) |
| 266 | (d (derivation s "the-thing" b '("--version") |
| 267 | #:inputs `((,b)))) |
| 268 | (o (derivation->output-path d))) |
| 269 | (with-derivation-narinfo d |
| 270 | (substitutable-path-info s (list o)))))) |
| 271 | |
| 272 | (test-equal "substitutable-paths when substitutes are turned off" |
| 273 | '() |
| 274 | (with-store s |
| 275 | (set-build-options s #:use-substitutes? #f) |
| 276 | (let* ((b (add-to-store s "bash" #t "sha256" |
| 277 | (search-bootstrap-binary "bash" |
| 278 | (%current-system)))) |
| 279 | (d (derivation s "the-thing" b '("--version") |
| 280 | #:inputs `((,b)))) |
| 281 | (o (derivation->output-path d))) |
| 282 | (with-derivation-narinfo d |
| 283 | (substitutable-paths s (list o)))))) |
| 284 | |
| 285 | (test-assert "requisites" |
| 286 | (let* ((t1 (add-text-to-store %store "random1" |
| 287 | (random-text) '())) |
| 288 | (t2 (add-text-to-store %store "random2" |
| 289 | (random-text) (list t1))) |
| 290 | (t3 (add-text-to-store %store "random3" |
| 291 | (random-text) (list t2))) |
| 292 | (t4 (add-text-to-store %store "random4" |
| 293 | (random-text) (list t1 t3)))) |
| 294 | (define (same? x y) |
| 295 | (and (= (length x) (length y)) |
| 296 | (lset= equal? x y))) |
| 297 | |
| 298 | (and (same? (requisites %store (list t1)) (list t1)) |
| 299 | (same? (requisites %store (list t2)) (list t1 t2)) |
| 300 | (same? (requisites %store (list t3)) (list t1 t2 t3)) |
| 301 | (same? (requisites %store (list t4)) (list t1 t2 t3 t4)) |
| 302 | (same? (requisites %store (list t1 t2 t3 t4)) |
| 303 | (list t1 t2 t3 t4))))) |
| 304 | |
| 305 | (test-assert "derivers" |
| 306 | (let* ((b (add-text-to-store %store "build" "echo $foo > $out" '())) |
| 307 | (s (add-to-store %store "bash" #t "sha256" |
| 308 | (search-bootstrap-binary "bash" |
| 309 | (%current-system)))) |
| 310 | (d (derivation %store "the-thing" |
| 311 | s `("-e" ,b) |
| 312 | #:env-vars `(("foo" . ,(random-text))) |
| 313 | #:inputs `((,b) (,s)))) |
| 314 | (o (derivation->output-path d))) |
| 315 | (and (build-derivations %store (list d)) |
| 316 | (equal? (query-derivation-outputs %store (derivation-file-name d)) |
| 317 | (list o)) |
| 318 | (equal? (valid-derivers %store o) |
| 319 | (list (derivation-file-name d)))))) |
| 320 | |
| 321 | (test-assert "topologically-sorted, one item" |
| 322 | (let* ((a (add-text-to-store %store "a" "a")) |
| 323 | (b (add-text-to-store %store "b" "b" (list a))) |
| 324 | (c (add-text-to-store %store "c" "c" (list b))) |
| 325 | (d (add-text-to-store %store "d" "d" (list c))) |
| 326 | (s (topologically-sorted %store (list d)))) |
| 327 | (equal? s (list a b c d)))) |
| 328 | |
| 329 | (test-assert "topologically-sorted, several items" |
| 330 | (let* ((a (add-text-to-store %store "a" "a")) |
| 331 | (b (add-text-to-store %store "b" "b" (list a))) |
| 332 | (c (add-text-to-store %store "c" "c" (list b))) |
| 333 | (d (add-text-to-store %store "d" "d" (list c))) |
| 334 | (s1 (topologically-sorted %store (list d a c b))) |
| 335 | (s2 (topologically-sorted %store (list b d c a b d)))) |
| 336 | (equal? s1 s2 (list a b c d)))) |
| 337 | |
| 338 | (test-assert "topologically-sorted, more difficult" |
| 339 | (let* ((a (add-text-to-store %store "a" "a")) |
| 340 | (b (add-text-to-store %store "b" "b" (list a))) |
| 341 | (c (add-text-to-store %store "c" "c" (list b))) |
| 342 | (d (add-text-to-store %store "d" "d" (list c))) |
| 343 | (w (add-text-to-store %store "w" "w")) |
| 344 | (x (add-text-to-store %store "x" "x" (list w))) |
| 345 | (y (add-text-to-store %store "y" "y" (list x d))) |
| 346 | (s1 (topologically-sorted %store (list y))) |
| 347 | (s2 (topologically-sorted %store (list c y))) |
| 348 | (s3 (topologically-sorted %store (cons y (references %store y))))) |
| 349 | ;; The order in which 'references' returns the references of Y is |
| 350 | ;; unspecified, so accommodate. |
| 351 | (let* ((x-then-d? (equal? (references %store y) (list x d)))) |
| 352 | (and (equal? s1 |
| 353 | (if x-then-d? |
| 354 | (list w x a b c d y) |
| 355 | (list a b c d w x y))) |
| 356 | (equal? s2 |
| 357 | (if x-then-d? |
| 358 | (list a b c w x d y) |
| 359 | (list a b c d w x y))) |
| 360 | (lset= string=? s1 s3))))) |
| 361 | |
| 362 | (test-assert "current-build-output-port, UTF-8" |
| 363 | ;; Are UTF-8 strings in the build log properly interpreted? |
| 364 | (string-contains |
| 365 | (with-fluids ((%default-port-encoding "UTF-8")) ;for the string port |
| 366 | (call-with-output-string |
| 367 | (lambda (port) |
| 368 | (parameterize ((current-build-output-port port)) |
| 369 | (let* ((s "Here’s a Greek letter: λ.") |
| 370 | (d (build-expression->derivation |
| 371 | %store "foo" `(display ,s) |
| 372 | #:guile-for-build |
| 373 | (package-derivation s %bootstrap-guile (%current-system))))) |
| 374 | (guard (c ((nix-protocol-error? c) #t)) |
| 375 | (build-derivations %store (list d)))))))) |
| 376 | "Here’s a Greek letter: λ.")) |
| 377 | |
| 378 | (test-assert "current-build-output-port, UTF-8 + garbage" |
| 379 | ;; What about a mixture of UTF-8 + garbage? |
| 380 | (string-contains |
| 381 | (with-fluids ((%default-port-encoding "UTF-8")) ;for the string port |
| 382 | (call-with-output-string |
| 383 | (lambda (port) |
| 384 | (parameterize ((current-build-output-port port)) |
| 385 | (let ((d (build-expression->derivation |
| 386 | %store "foo" |
| 387 | `(begin |
| 388 | (use-modules (rnrs io ports)) |
| 389 | (display "garbage: ") |
| 390 | (put-bytevector (current-output-port) #vu8(128)) |
| 391 | (display "lambda: λ\n")) |
| 392 | #:guile-for-build |
| 393 | (package-derivation %store %bootstrap-guile)))) |
| 394 | (guard (c ((nix-protocol-error? c) #t)) |
| 395 | (build-derivations %store (list d)))))))) |
| 396 | (cond-expand |
| 397 | (guile-2.2 "garbage: �lambda: λ") |
| 398 | (else "garbage: ?lambda: λ")))) |
| 399 | |
| 400 | (test-assert "log-file, derivation" |
| 401 | (let* ((b (add-text-to-store %store "build" "echo $foo > $out" '())) |
| 402 | (s (add-to-store %store "bash" #t "sha256" |
| 403 | (search-bootstrap-binary "bash" |
| 404 | (%current-system)))) |
| 405 | (d (derivation %store "the-thing" |
| 406 | s `("-e" ,b) |
| 407 | #:env-vars `(("foo" . ,(random-text))) |
| 408 | #:inputs `((,b) (,s))))) |
| 409 | (and (build-derivations %store (list d)) |
| 410 | (file-exists? (pk (log-file %store (derivation-file-name d))))))) |
| 411 | |
| 412 | (test-assert "log-file, output file name" |
| 413 | (let* ((b (add-text-to-store %store "build" "echo $foo > $out" '())) |
| 414 | (s (add-to-store %store "bash" #t "sha256" |
| 415 | (search-bootstrap-binary "bash" |
| 416 | (%current-system)))) |
| 417 | (d (derivation %store "the-thing" |
| 418 | s `("-e" ,b) |
| 419 | #:env-vars `(("foo" . ,(random-text))) |
| 420 | #:inputs `((,b) (,s)))) |
| 421 | (o (derivation->output-path d))) |
| 422 | (and (build-derivations %store (list d)) |
| 423 | (file-exists? (pk (log-file %store o))) |
| 424 | (string=? (log-file %store (derivation-file-name d)) |
| 425 | (log-file %store o))))) |
| 426 | |
| 427 | (test-assert "no substitutes" |
| 428 | (with-store s |
| 429 | (let* ((d1 (package-derivation s %bootstrap-guile (%current-system))) |
| 430 | (d2 (package-derivation s %bootstrap-glibc (%current-system))) |
| 431 | (o (map derivation->output-path (list d1 d2)))) |
| 432 | (set-build-options s #:use-substitutes? #f) |
| 433 | (and (not (has-substitutes? s (derivation-file-name d1))) |
| 434 | (not (has-substitutes? s (derivation-file-name d2))) |
| 435 | (null? (substitutable-paths s o)) |
| 436 | (null? (substitutable-path-info s o)))))) |
| 437 | |
| 438 | (test-assert "build-things with output path" |
| 439 | (with-store s |
| 440 | (let* ((c (random-text)) ;contents of the output |
| 441 | (d (build-expression->derivation |
| 442 | s "substitute-me" |
| 443 | `(call-with-output-file %output |
| 444 | (lambda (p) |
| 445 | (display ,c p))) |
| 446 | #:guile-for-build |
| 447 | (package-derivation s %bootstrap-guile (%current-system)))) |
| 448 | (o (derivation->output-path d))) |
| 449 | (set-build-options s #:use-substitutes? #f) |
| 450 | |
| 451 | ;; Pass 'build-things' the output file name, O. However, since there |
| 452 | ;; are no substitutes for O, it will just do nothing. |
| 453 | (build-things s (list o)) |
| 454 | (not (valid-path? s o))))) |
| 455 | |
| 456 | (test-skip (if (getenv "GUIX_BINARY_SUBSTITUTE_URL") 0 1)) |
| 457 | |
| 458 | (test-assert "substitute query" |
| 459 | (with-store s |
| 460 | (let* ((d (package-derivation s %bootstrap-guile (%current-system))) |
| 461 | (o (derivation->output-path d))) |
| 462 | ;; Create fake substituter data, to be read by 'guix substitute'. |
| 463 | (with-derivation-narinfo d |
| 464 | ;; Remove entry from the local cache. |
| 465 | (false-if-exception |
| 466 | (delete-file-recursively (string-append (getenv "XDG_CACHE_HOME") |
| 467 | "/guix/substitute"))) |
| 468 | |
| 469 | ;; Make sure 'guix substitute' correctly communicates the above |
| 470 | ;; data. |
| 471 | (set-build-options s #:use-substitutes? #t |
| 472 | #:substitute-urls (%test-substitute-urls)) |
| 473 | (and (has-substitutes? s o) |
| 474 | (equal? (list o) (substitutable-paths s (list o))) |
| 475 | (match (pk 'spi (substitutable-path-info s (list o))) |
| 476 | (((? substitutable? s)) |
| 477 | (and (string=? (substitutable-deriver s) |
| 478 | (derivation-file-name d)) |
| 479 | (null? (substitutable-references s)) |
| 480 | (equal? (substitutable-nar-size s) 1234))))))))) |
| 481 | |
| 482 | (test-assert "substitute query, alternating URLs" |
| 483 | (let* ((d (with-store s |
| 484 | (package-derivation s %bootstrap-guile (%current-system)))) |
| 485 | (o (derivation->output-path d))) |
| 486 | (with-derivation-narinfo d |
| 487 | ;; Remove entry from the local cache. |
| 488 | (false-if-exception |
| 489 | (delete-file-recursively (string-append (getenv "XDG_CACHE_HOME") |
| 490 | "/guix/substitute"))) |
| 491 | |
| 492 | ;; Note: We reconnect to the daemon to force a new instance of 'guix |
| 493 | ;; substitute' to be used; otherwise the #:substitute-urls of |
| 494 | ;; 'set-build-options' would have no effect. |
| 495 | |
| 496 | (and (with-store s ;the right substitute URL |
| 497 | (set-build-options s #:use-substitutes? #t |
| 498 | #:substitute-urls (%test-substitute-urls)) |
| 499 | (has-substitutes? s o)) |
| 500 | (with-store s ;the wrong one |
| 501 | (set-build-options s #:use-substitutes? #t |
| 502 | #:substitute-urls (list |
| 503 | "http://does-not-exist")) |
| 504 | (not (has-substitutes? s o))) |
| 505 | (with-store s ;the right one again |
| 506 | (set-build-options s #:use-substitutes? #t |
| 507 | #:substitute-urls (%test-substitute-urls)) |
| 508 | (has-substitutes? s o)) |
| 509 | (with-store s ;empty list of URLs |
| 510 | (set-build-options s #:use-substitutes? #t |
| 511 | #:substitute-urls '()) |
| 512 | (not (has-substitutes? s o))))))) |
| 513 | |
| 514 | (test-assert "substitute" |
| 515 | (with-store s |
| 516 | (let* ((c (random-text)) ; contents of the output |
| 517 | (d (build-expression->derivation |
| 518 | s "substitute-me" |
| 519 | `(call-with-output-file %output |
| 520 | (lambda (p) |
| 521 | (exit 1) ; would actually fail |
| 522 | (display ,c p))) |
| 523 | #:guile-for-build |
| 524 | (package-derivation s %bootstrap-guile (%current-system)))) |
| 525 | (o (derivation->output-path d))) |
| 526 | (with-derivation-substitute d c |
| 527 | (set-build-options s #:use-substitutes? #t |
| 528 | #:substitute-urls (%test-substitute-urls)) |
| 529 | (and (has-substitutes? s o) |
| 530 | (build-derivations s (list d)) |
| 531 | (equal? c (call-with-input-file o get-string-all))))))) |
| 532 | |
| 533 | (test-assert "substitute + build-things with output path" |
| 534 | (with-store s |
| 535 | (let* ((c (random-text)) ;contents of the output |
| 536 | (d (build-expression->derivation |
| 537 | s "substitute-me" |
| 538 | `(call-with-output-file %output |
| 539 | (lambda (p) |
| 540 | (exit 1) ;would actually fail |
| 541 | (display ,c p))) |
| 542 | #:guile-for-build |
| 543 | (package-derivation s %bootstrap-guile (%current-system)))) |
| 544 | (o (derivation->output-path d))) |
| 545 | (with-derivation-substitute d c |
| 546 | (set-build-options s #:use-substitutes? #t |
| 547 | #:substitute-urls (%test-substitute-urls)) |
| 548 | (and (has-substitutes? s o) |
| 549 | (build-things s (list o)) ;give the output path |
| 550 | (valid-path? s o) |
| 551 | (equal? c (call-with-input-file o get-string-all))))))) |
| 552 | |
| 553 | (test-assert "substitute, corrupt output hash" |
| 554 | ;; Tweak the substituter into installing a substitute whose hash doesn't |
| 555 | ;; match the one announced in the narinfo. The daemon must notice this and |
| 556 | ;; raise an error. |
| 557 | (with-store s |
| 558 | (let* ((c "hello, world") ; contents of the output |
| 559 | (d (build-expression->derivation |
| 560 | s "corrupt-substitute" |
| 561 | `(mkdir %output) |
| 562 | #:guile-for-build |
| 563 | (package-derivation s %bootstrap-guile (%current-system)))) |
| 564 | (o (derivation->output-path d))) |
| 565 | (with-derivation-substitute d c |
| 566 | (sha256 => (make-bytevector 32 0)) ;select a hash that doesn't match C |
| 567 | |
| 568 | ;; Make sure we use 'guix substitute'. |
| 569 | (set-build-options s |
| 570 | #:use-substitutes? #t |
| 571 | #:fallback? #f |
| 572 | #:substitute-urls (%test-substitute-urls)) |
| 573 | (and (has-substitutes? s o) |
| 574 | (guard (c ((nix-protocol-error? c) |
| 575 | ;; XXX: the daemon writes "hash mismatch in downloaded |
| 576 | ;; path", but the actual error returned to the client |
| 577 | ;; doesn't mention that. |
| 578 | (pk 'corrupt c) |
| 579 | (not (zero? (nix-protocol-error-status c))))) |
| 580 | (build-derivations s (list d)) |
| 581 | #f)))))) |
| 582 | |
| 583 | (test-assert "substitute --fallback" |
| 584 | (with-store s |
| 585 | (let* ((t (random-text)) ; contents of the output |
| 586 | (d (build-expression->derivation |
| 587 | s "substitute-me-not" |
| 588 | `(call-with-output-file %output |
| 589 | (lambda (p) |
| 590 | (display ,t p))) |
| 591 | #:guile-for-build |
| 592 | (package-derivation s %bootstrap-guile (%current-system)))) |
| 593 | (o (derivation->output-path d))) |
| 594 | ;; Create fake substituter data, to be read by 'guix substitute'. |
| 595 | (with-derivation-narinfo d |
| 596 | ;; Make sure we use 'guix substitute'. |
| 597 | (set-build-options s #:use-substitutes? #t |
| 598 | #:substitute-urls (%test-substitute-urls)) |
| 599 | (and (has-substitutes? s o) |
| 600 | (guard (c ((nix-protocol-error? c) |
| 601 | ;; The substituter failed as expected. Now make |
| 602 | ;; sure that #:fallback? #t works correctly. |
| 603 | (set-build-options s |
| 604 | #:use-substitutes? #t |
| 605 | #:substitute-urls |
| 606 | (%test-substitute-urls) |
| 607 | #:fallback? #t) |
| 608 | (and (build-derivations s (list d)) |
| 609 | (equal? t (call-with-input-file o |
| 610 | get-string-all))))) |
| 611 | ;; Should fail. |
| 612 | (build-derivations s (list d)) |
| 613 | #f)))))) |
| 614 | |
| 615 | (test-assert "export/import several paths" |
| 616 | (let* ((texts (unfold (cut >= <> 10) |
| 617 | (lambda _ (random-text)) |
| 618 | 1+ |
| 619 | 0)) |
| 620 | (files (map (cut add-text-to-store %store "text" <>) texts)) |
| 621 | (dump (call-with-bytevector-output-port |
| 622 | (cut export-paths %store files <>)))) |
| 623 | (delete-paths %store files) |
| 624 | (and (every (negate file-exists?) files) |
| 625 | (let* ((source (open-bytevector-input-port dump)) |
| 626 | (imported (import-paths %store source))) |
| 627 | (and (equal? imported files) |
| 628 | (every file-exists? files) |
| 629 | (equal? texts |
| 630 | (map (lambda (file) |
| 631 | (call-with-input-file file |
| 632 | get-string-all)) |
| 633 | files))))))) |
| 634 | |
| 635 | (test-assert "export/import paths, ensure topological order" |
| 636 | (let* ((file0 (add-text-to-store %store "baz" (random-text))) |
| 637 | (file1 (add-text-to-store %store "foo" (random-text) |
| 638 | (list file0))) |
| 639 | (file2 (add-text-to-store %store "bar" (random-text) |
| 640 | (list file1))) |
| 641 | (files (list file1 file2)) |
| 642 | (dump1 (call-with-bytevector-output-port |
| 643 | (cute export-paths %store (list file1 file2) <>))) |
| 644 | (dump2 (call-with-bytevector-output-port |
| 645 | (cute export-paths %store (list file2 file1) <>)))) |
| 646 | (delete-paths %store files) |
| 647 | (and (every (negate file-exists?) files) |
| 648 | (bytevector=? dump1 dump2) |
| 649 | (let* ((source (open-bytevector-input-port dump1)) |
| 650 | (imported (import-paths %store source))) |
| 651 | ;; DUMP1 should contain exactly FILE1 and FILE2, not FILE0. |
| 652 | (and (equal? imported (list file1 file2)) |
| 653 | (every file-exists? files) |
| 654 | (equal? (list file0) (references %store file1)) |
| 655 | (equal? (list file1) (references %store file2))))))) |
| 656 | |
| 657 | (test-assert "export/import incomplete" |
| 658 | (let* ((file0 (add-text-to-store %store "baz" (random-text))) |
| 659 | (file1 (add-text-to-store %store "foo" (random-text) |
| 660 | (list file0))) |
| 661 | (file2 (add-text-to-store %store "bar" (random-text) |
| 662 | (list file1))) |
| 663 | (dump (call-with-bytevector-output-port |
| 664 | (cute export-paths %store (list file2) <>)))) |
| 665 | (delete-paths %store (list file0 file1 file2)) |
| 666 | (guard (c ((nix-protocol-error? c) |
| 667 | (and (not (zero? (nix-protocol-error-status c))) |
| 668 | (string-contains (nix-protocol-error-message c) |
| 669 | "not valid")))) |
| 670 | ;; Here we get an exception because DUMP does not include FILE0 and |
| 671 | ;; FILE1, which are dependencies of FILE2. |
| 672 | (import-paths %store (open-bytevector-input-port dump))))) |
| 673 | |
| 674 | (test-assert "export/import recursive" |
| 675 | (let* ((file0 (add-text-to-store %store "baz" (random-text))) |
| 676 | (file1 (add-text-to-store %store "foo" (random-text) |
| 677 | (list file0))) |
| 678 | (file2 (add-text-to-store %store "bar" (random-text) |
| 679 | (list file1))) |
| 680 | (dump (call-with-bytevector-output-port |
| 681 | (cute export-paths %store (list file2) <> |
| 682 | #:recursive? #t)))) |
| 683 | (delete-paths %store (list file0 file1 file2)) |
| 684 | (let ((imported (import-paths %store (open-bytevector-input-port dump)))) |
| 685 | (and (equal? imported (list file0 file1 file2)) |
| 686 | (every file-exists? (list file0 file1 file2)) |
| 687 | (equal? (list file0) (references %store file1)) |
| 688 | (equal? (list file1) (references %store file2)))))) |
| 689 | |
| 690 | (test-assert "write-file & export-path yield the same result" |
| 691 | ;; Here we compare 'write-file' and the daemon's own implementation. |
| 692 | ;; 'write-file' is the reference because we know it sorts file |
| 693 | ;; deterministically. Conversely, the daemon uses 'readdir' and the entries |
| 694 | ;; currently happen to be sorted as a side-effect of some unrelated |
| 695 | ;; operation (search for 'unhacked' in archive.cc.) Make sure we detect any |
| 696 | ;; changes there. |
| 697 | (run-with-store %store |
| 698 | (mlet* %store-monad ((drv1 (package->derivation %bootstrap-guile)) |
| 699 | (out1 -> (derivation->output-path drv1)) |
| 700 | (data -> (unfold (cut >= <> 26) |
| 701 | (lambda (i) |
| 702 | (random-bytevector 128)) |
| 703 | 1+ 0)) |
| 704 | (build |
| 705 | -> #~(begin |
| 706 | (use-modules (rnrs io ports) (srfi srfi-1)) |
| 707 | (let () |
| 708 | (define letters |
| 709 | (map (lambda (i) |
| 710 | (string |
| 711 | (integer->char |
| 712 | (+ i (char->integer #\a))))) |
| 713 | (iota 26))) |
| 714 | (define (touch file data) |
| 715 | (call-with-output-file file |
| 716 | (lambda (port) |
| 717 | (put-bytevector port data)))) |
| 718 | |
| 719 | (mkdir #$output) |
| 720 | (chdir #$output) |
| 721 | |
| 722 | ;; The files must be different so they have |
| 723 | ;; different inode numbers, and the inode |
| 724 | ;; order must differ from the lexicographic |
| 725 | ;; order. |
| 726 | (for-each touch |
| 727 | (append (drop letters 10) |
| 728 | (take letters 10)) |
| 729 | (list #$@data)) |
| 730 | #t))) |
| 731 | (drv2 (gexp->derivation "bunch" build)) |
| 732 | (out2 -> (derivation->output-path drv2)) |
| 733 | (item-info -> (store-lift query-path-info))) |
| 734 | (mbegin %store-monad |
| 735 | (built-derivations (list drv1 drv2)) |
| 736 | (foldm %store-monad |
| 737 | (lambda (item result) |
| 738 | (define ref-hash |
| 739 | (let-values (((port get) (open-sha256-port))) |
| 740 | (write-file item port) |
| 741 | (close-port port) |
| 742 | (get))) |
| 743 | |
| 744 | ;; 'query-path-info' returns a hash produced by using the |
| 745 | ;; daemon's C++ 'dump' function, which is the implementation |
| 746 | ;; under test. |
| 747 | (>>= (item-info item) |
| 748 | (lambda (info) |
| 749 | (return |
| 750 | (and result |
| 751 | (bytevector=? (path-info-hash info) ref-hash)))))) |
| 752 | #t |
| 753 | (list out1 out2)))) |
| 754 | #:guile-for-build (%guile-for-build))) |
| 755 | |
| 756 | (test-assert "import corrupt path" |
| 757 | (let* ((text (random-text)) |
| 758 | (file (add-text-to-store %store "text" text)) |
| 759 | (dump (call-with-bytevector-output-port |
| 760 | (cut export-paths %store (list file) <>)))) |
| 761 | (delete-paths %store (list file)) |
| 762 | |
| 763 | ;; Flip a bit in the stream's payload. INDEX here falls in the middle of |
| 764 | ;; the file contents in DUMP, regardless of the store prefix. |
| 765 | (let* ((index #x70) |
| 766 | (byte (bytevector-u8-ref dump index))) |
| 767 | (bytevector-u8-set! dump index (logxor #xff byte))) |
| 768 | |
| 769 | (and (not (file-exists? file)) |
| 770 | (guard (c ((nix-protocol-error? c) |
| 771 | (pk 'c c) |
| 772 | (and (not (zero? (nix-protocol-error-status c))) |
| 773 | (string-contains (nix-protocol-error-message c) |
| 774 | "corrupt")))) |
| 775 | (let* ((source (open-bytevector-input-port dump)) |
| 776 | (imported (import-paths %store source))) |
| 777 | (pk 'corrupt-imported imported) |
| 778 | #f))))) |
| 779 | |
| 780 | (test-assert "register-path" |
| 781 | (let ((file (string-append (%store-prefix) "/" (make-string 32 #\f) |
| 782 | "-fake"))) |
| 783 | (when (valid-path? %store file) |
| 784 | (delete-paths %store (list file))) |
| 785 | (false-if-exception (delete-file file)) |
| 786 | |
| 787 | (let ((ref (add-text-to-store %store "ref-of-fake" (random-text))) |
| 788 | (drv (string-append file ".drv"))) |
| 789 | (call-with-output-file file |
| 790 | (cut display "This is a fake store item.\n" <>)) |
| 791 | (register-path file |
| 792 | #:references (list ref) |
| 793 | #:deriver drv) |
| 794 | |
| 795 | (and (valid-path? %store file) |
| 796 | (equal? (references %store file) (list ref)) |
| 797 | (null? (valid-derivers %store file)) |
| 798 | (null? (referrers %store file)))))) |
| 799 | |
| 800 | (test-assert "verify-store" |
| 801 | (let* ((text (random-text)) |
| 802 | (file1 (add-text-to-store %store "foo" text)) |
| 803 | (file2 (add-text-to-store %store "bar" (random-text) |
| 804 | (list file1)))) |
| 805 | (and (pk 'verify1 (verify-store %store)) ;hopefully OK ; |
| 806 | (begin |
| 807 | (delete-file file1) |
| 808 | (not (pk 'verify2 (verify-store %store)))) ;bad! ; |
| 809 | (begin |
| 810 | ;; Using 'add-text-to-store' here wouldn't work: It would succeed ; |
| 811 | ;; without actually creating the file. ; |
| 812 | (call-with-output-file file1 |
| 813 | (lambda (port) |
| 814 | (display text port))) |
| 815 | (pk 'verify3 (verify-store %store)))))) ;OK again |
| 816 | |
| 817 | (test-assert "verify-store + check-contents" |
| 818 | ;; XXX: This test is I/O intensive. |
| 819 | (with-store s |
| 820 | (let* ((text (random-text)) |
| 821 | (drv (build-expression->derivation |
| 822 | s "corrupt" |
| 823 | `(let ((out (assoc-ref %outputs "out"))) |
| 824 | (call-with-output-file out |
| 825 | (lambda (port) |
| 826 | (display ,text port))) |
| 827 | #t) |
| 828 | #:guile-for-build |
| 829 | (package-derivation s %bootstrap-guile (%current-system)))) |
| 830 | (file (derivation->output-path drv))) |
| 831 | (with-derivation-substitute drv text |
| 832 | (and (build-derivations s (list drv)) |
| 833 | (verify-store s #:check-contents? #t) ;should be OK |
| 834 | (begin |
| 835 | (chmod file #o644) |
| 836 | (call-with-output-file file |
| 837 | (lambda (port) |
| 838 | (display "corrupt!" port))) |
| 839 | #t) |
| 840 | |
| 841 | ;; Make sure the corruption is detected. We don't test repairing |
| 842 | ;; because only "trusted" users are allowed to do it, but we |
| 843 | ;; don't expose that notion of trusted users that nix-daemon |
| 844 | ;; supports because it seems dubious and redundant with what the |
| 845 | ;; OS provides (in Nix "trusted" users have additional |
| 846 | ;; privileges, such as overriding the set of substitute URLs, but |
| 847 | ;; we instead want to allow anyone to modify them, provided |
| 848 | ;; substitutes are signed by a root-approved key.) |
| 849 | (not (verify-store s #:check-contents? #t)) |
| 850 | |
| 851 | ;; Delete the corrupt item to leave the store in a clean state. |
| 852 | (delete-paths s (list file))))))) |
| 853 | |
| 854 | (test-assert "build-things, check mode" |
| 855 | (with-store store |
| 856 | (call-with-temporary-output-file |
| 857 | (lambda (entropy entropy-port) |
| 858 | (write (random-text) entropy-port) |
| 859 | (force-output entropy-port) |
| 860 | (let* ((drv (build-expression->derivation |
| 861 | store "non-deterministic" |
| 862 | `(begin |
| 863 | (use-modules (rnrs io ports)) |
| 864 | (let ((out (assoc-ref %outputs "out"))) |
| 865 | (call-with-output-file out |
| 866 | (lambda (port) |
| 867 | ;; Rely on the fact that tests do not use the |
| 868 | ;; chroot, and thus ENTROPY is readable. |
| 869 | (display (call-with-input-file ,entropy |
| 870 | get-string-all) |
| 871 | port))) |
| 872 | #t)) |
| 873 | #:guile-for-build |
| 874 | (package-derivation store %bootstrap-guile (%current-system)))) |
| 875 | (file (derivation->output-path drv))) |
| 876 | (and (build-things store (list (derivation-file-name drv))) |
| 877 | (begin |
| 878 | (write (random-text) entropy-port) |
| 879 | (force-output entropy-port) |
| 880 | (guard (c ((nix-protocol-error? c) |
| 881 | (pk 'determinism-exception c) |
| 882 | (and (not (zero? (nix-protocol-error-status c))) |
| 883 | (string-contains (nix-protocol-error-message c) |
| 884 | "deterministic")))) |
| 885 | ;; This one will produce a different result. Since we're in |
| 886 | ;; 'check' mode, this must fail. |
| 887 | (build-things store (list (derivation-file-name drv)) |
| 888 | (build-mode check)) |
| 889 | #f)))))))) |
| 890 | |
| 891 | (test-assert "build multiple times" |
| 892 | (with-store store |
| 893 | ;; Ask to build twice. |
| 894 | (set-build-options store #:rounds 2 #:use-substitutes? #f) |
| 895 | |
| 896 | (call-with-temporary-output-file |
| 897 | (lambda (entropy entropy-port) |
| 898 | (write (random-text) entropy-port) |
| 899 | (force-output entropy-port) |
| 900 | (let* ((drv (build-expression->derivation |
| 901 | store "non-deterministic" |
| 902 | `(begin |
| 903 | (use-modules (rnrs io ports)) |
| 904 | (let ((out (assoc-ref %outputs "out"))) |
| 905 | (call-with-output-file out |
| 906 | (lambda (port) |
| 907 | ;; Rely on the fact that tests do not use the |
| 908 | ;; chroot, and thus ENTROPY is accessible. |
| 909 | (display (call-with-input-file ,entropy |
| 910 | get-string-all) |
| 911 | port) |
| 912 | (call-with-output-file ,entropy |
| 913 | (lambda (port) |
| 914 | (write 'foobar port))))) |
| 915 | #t)) |
| 916 | #:guile-for-build |
| 917 | (package-derivation store %bootstrap-guile (%current-system)))) |
| 918 | (file (derivation->output-path drv))) |
| 919 | (guard (c ((nix-protocol-error? c) |
| 920 | (pk 'multiple-build c) |
| 921 | (and (not (zero? (nix-protocol-error-status c))) |
| 922 | (string-contains (nix-protocol-error-message c) |
| 923 | "deterministic")))) |
| 924 | ;; This one will produce a different result on the second run. |
| 925 | (current-build-output-port (current-error-port)) |
| 926 | (build-things store (list (derivation-file-name drv))) |
| 927 | #f)))))) |
| 928 | |
| 929 | (test-equal "store-lower" |
| 930 | "Lowered." |
| 931 | (let* ((add (store-lower text-file)) |
| 932 | (file (add %store "foo" "Lowered."))) |
| 933 | (call-with-input-file file get-string-all))) |
| 934 | |
| 935 | (test-equal "current-system" |
| 936 | "bar" |
| 937 | (parameterize ((%current-system "frob")) |
| 938 | (run-with-store %store |
| 939 | (mbegin %store-monad |
| 940 | (set-current-system "bar") |
| 941 | (current-system)) |
| 942 | #:system "foo"))) |
| 943 | |
| 944 | (test-assert "query-path-info" |
| 945 | (let* ((ref (add-text-to-store %store "ref" "foo")) |
| 946 | (item (add-text-to-store %store "item" "bar" (list ref))) |
| 947 | (info (query-path-info %store item))) |
| 948 | (and (equal? (path-info-references info) (list ref)) |
| 949 | (equal? (path-info-hash info) |
| 950 | (sha256 |
| 951 | (string->utf8 |
| 952 | (call-with-output-string (cut write-file item <>)))))))) |
| 953 | |
| 954 | (test-assert "path-info-deriver" |
| 955 | (let* ((b (add-text-to-store %store "build" "echo $foo > $out" '())) |
| 956 | (s (add-to-store %store "bash" #t "sha256" |
| 957 | (search-bootstrap-binary "bash" |
| 958 | (%current-system)))) |
| 959 | (d (derivation %store "the-thing" |
| 960 | s `("-e" ,b) |
| 961 | #:env-vars `(("foo" . ,(random-text))) |
| 962 | #:inputs `((,b) (,s)))) |
| 963 | (o (derivation->output-path d))) |
| 964 | (and (build-derivations %store (list d)) |
| 965 | (not (path-info-deriver (query-path-info %store b))) |
| 966 | (string=? (derivation-file-name d) |
| 967 | (path-info-deriver (query-path-info %store o)))))) |
| 968 | |
| 969 | (test-equal "build-cores" |
| 970 | (list 0 42) |
| 971 | (with-store store |
| 972 | (let* ((build (add-text-to-store store "build.sh" |
| 973 | "echo $NIX_BUILD_CORES > $out")) |
| 974 | (bash (add-to-store store "bash" #t "sha256" |
| 975 | (search-bootstrap-binary "bash" |
| 976 | (%current-system)))) |
| 977 | (drv1 (derivation store "the-thing" bash |
| 978 | `("-e" ,build) |
| 979 | #:inputs `((,bash) (,build)) |
| 980 | #:env-vars `(("x" . ,(random-text))))) |
| 981 | (drv2 (derivation store "the-thing" bash |
| 982 | `("-e" ,build) |
| 983 | #:inputs `((,bash) (,build)) |
| 984 | #:env-vars `(("x" . ,(random-text)))))) |
| 985 | (and (build-derivations store (list drv1)) |
| 986 | (begin |
| 987 | (set-build-options store #:build-cores 42) |
| 988 | (build-derivations store (list drv2))) |
| 989 | (list (call-with-input-file (derivation->output-path drv1) |
| 990 | read) |
| 991 | (call-with-input-file (derivation->output-path drv2) |
| 992 | read)))))) |
| 993 | |
| 994 | (test-end "store") |