gnu: jack-2: Update to 1.9.14.
[jackhill/guix/guix.git] / tests / store.scm
index 2b14a4a..38051bf 100644 (file)
@@ -1,5 +1,5 @@
 ;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -22,7 +22,9 @@
   #:use-module (guix store)
   #:use-module (guix utils)
   #:use-module (guix monads)
-  #:use-module (gcrypt hash)
+  #:use-module ((gcrypt hash) #:prefix gcrypt:)
+  #:use-module ((gcrypt pk-crypto) #:prefix gcrypt:)
+  #:use-module (guix pki)
   #:use-module (guix base32)
   #:use-module (guix packages)
   #:use-module (guix derivations)
                                 (passwd:name (getpwuid (getuid)))))))
     (list (stat:uid s) (stat:perms s))))
 
+(test-equal "add-to-store"
+  '("sha1" "sha256" "sha512" "sha3-256" "sha3-512" "blake2s-256")
+  (let* ((file    (search-path %load-path "guix.scm"))
+         (content (call-with-input-file file get-bytevector-all)))
+    (map (lambda (hash-algo)
+           (let ((file (add-to-store %store "guix.scm" #f hash-algo file)))
+             (and (direct-store-path? file)
+                  (bytevector=? (call-with-input-file file get-bytevector-all)
+                                content)
+                  hash-algo)))
+         '("sha1" "sha256" "sha512" "sha3-256" "sha3-512" "blake2s-256"))))
+
 (test-equal "add-data-to-store"
   #vu8(1 2 3 4 5)
   (call-with-input-file (add-data-to-store %store "data" #vu8(1 2 3 4 5))
                     (string-append (%store-prefix) "/"
                                    (make-string 32 #\e) "-foobar"))))
 
+(test-equal "with-store, multiple values"        ;<https://bugs.gnu.org/42912>
+  '(1 2 3)
+  (call-with-values
+      (lambda ()
+        (with-store s
+          (add-text-to-store s "foo" "bar")
+          (values 1 2 3)))
+    list))
+
 (test-assert "valid-path? error"
   (with-store s
     (guard (c ((store-protocol-error? c) #t))
            ;;(> freed 0)
            (not (file-exists? p))))))
 
-(test-assert "add-text-to-store vs. delete-paths"
-  ;; Before, 'add-text-to-store' would return PATH2 without noticing that it
-  ;; is no longer valid.
+(test-assert "add-text-to-store/add-to-store vs. delete-paths"
+  ;; Before, 'add-text-to-store' and 'add-to-store' would return the same
+  ;; store item without noticing that it is no longer valid.
   (with-store store
     (let* ((text    (random-text))
-           (path    (add-text-to-store store "delete-me" text))
-           (deleted (delete-paths store (list path)))
-           (path2   (add-text-to-store store "delete-me" text)))
-      (and (string=? path path2)
-           (equal? deleted (list path))
-           (valid-path? store path)
-           (file-exists? path)))))
-
-(test-assert "add-to-store vs. delete-paths"
-  ;; Same as above.
-  (with-store store
-    (let* ((file    (search-path %load-path "guix.scm"))
-           (path    (add-to-store store "delete-me" #t "sha256" file))
-           (deleted (delete-paths store (list path)))
-           (path2   (add-to-store store "delete-me" #t "sha256" file)))
-      (and (string=? path path2)
-           (equal? deleted (list path))
-           (valid-path? store path)
-           (file-exists? path)))))
+           (file    (search-path %load-path "guix.scm"))
+           (path1   (add-text-to-store store "delete-me" text))
+           (path2   (add-to-store store "delete-me" #t "sha256" file))
+           (deleted (delete-paths store (list path1 path2))))
+      (and (string=? path1 (add-text-to-store store "delete-me" text))
+           (string=? path2 (add-to-store store "delete-me" #t "sha256" file))
+           (lset= string=? deleted (list path1 path2))
+           (valid-path? store path1)
+           (valid-path? store path2)
+           (file-exists? path1)
+           (file-exists? path2)))))
 
 (test-equal "add-file-tree-to-store"
   `(42
                            #:env-vars `(("t2" . ,t2))))
            (o  (derivation->output-path d)))
       (with-derivation-narinfo d
-        (sha256 => (sha256 (string->utf8 t2)))
+        (sha256 => (gcrypt:sha256 (string->utf8 t2)))
         (references => (list t2))
 
         (equal? (references/substitutes s (list o t3 t2 t1))
          (equal? (valid-derivers %store o)
                  (list (derivation-file-name d))))))
 
+(test-equal "with-build-handler"
+  'success
+  (let* ((b  (add-text-to-store %store "build" "echo $foo > $out" '()))
+         (s  (add-to-store %store "bash" #t "sha256"
+                           (search-bootstrap-binary "bash"
+                                                    (%current-system))))
+         (d1 (derivation %store "the-thing"
+                         s `("-e" ,b)
+                         #:env-vars `(("foo" . ,(random-text)))
+                         #:sources (list b s)))
+         (d2 (derivation %store "the-thing"
+                         s `("-e" ,b)
+                         #:env-vars `(("foo" . ,(random-text))
+                                      ("bar" . "baz"))
+                         #:sources (list b s)))
+         (o1 (derivation->output-path d1))
+         (o2 (derivation->output-path d2)))
+    (with-build-handler
+        (let ((counter 0))
+          (lambda (continue store things mode)
+            (match things
+              ((drv)
+               (set! counter (+ 1 counter))
+               (if (string=? drv (derivation-file-name d1))
+                   (continue #t)
+                   (and (string=? drv (derivation-file-name d2))
+                        (= counter 2)
+                        'success))))))
+      (build-derivations %store (list d1))
+      (build-derivations %store (list d2))
+      'fail)))
+
+(test-equal "with-build-handler + with-store"
+  'success
+  ;; Check that STORE remains valid when the build handler invokes CONTINUE,
+  ;; even though 'with-build-handler' is outside the dynamic extent of
+  ;; 'with-store'.
+  (with-build-handler (lambda (continue store things mode)
+                        (match things
+                          ((drv)
+                           (and (string-suffix? "thingie.drv" drv)
+                                (not (port-closed?
+                                      (store-connection-socket store)))
+                                (continue #t)))))
+    (with-store store
+      (let* ((b (add-text-to-store store "build" "echo $foo > $out" '()))
+             (s (add-to-store store "bash" #t "sha256"
+                              (search-bootstrap-binary "bash"
+                                                       (%current-system))))
+             (d (derivation store "thingie"
+                            s `("-e" ,b)
+                            #:env-vars `(("foo" . ,(random-text)))
+                            #:sources (list b s))))
+        (build-derivations store (list d))
+
+        ;; Here STORE's socket should still be open.
+        (and (valid-path? store (derivation->output-path d))
+             'success)))))
+
+(test-assert "map/accumulate-builds"
+  (let* ((b  (add-text-to-store %store "build" "echo $foo > $out" '()))
+         (s  (add-to-store %store "bash" #t "sha256"
+                           (search-bootstrap-binary "bash"
+                                                    (%current-system))))
+         (d1 (derivation %store "the-thing"
+                         s `("-e" ,b)
+                         #:env-vars `(("foo" . ,(random-text)))
+                         #:sources (list b s)))
+         (d2 (derivation %store "the-thing"
+                         s `("-e" ,b)
+                         #:env-vars `(("foo" . ,(random-text))
+                                      ("bar" . "baz"))
+                         #:sources (list b s))))
+    (with-build-handler (lambda (continue store things mode)
+                          (equal? (map derivation-file-name (list d1 d2))
+                                  things))
+      (map/accumulate-builds %store
+                             (lambda (drv)
+                               (build-derivations %store (list drv))
+                               (add-to-store %store "content-addressed"
+                                             #t "sha256"
+                                             (derivation->output-path drv)))
+                             (list d1 d2)))))
+
+(test-assert "mapm/accumulate-builds"
+  (let* ((d1 (run-with-store %store
+               (gexp->derivation "foo" #~(mkdir #$output))))
+         (d2 (run-with-store %store
+               (gexp->derivation "bar" #~(mkdir #$output)))))
+    (with-build-handler (lambda (continue store things mode)
+                          (equal? (map derivation-file-name (pk 'zz (list d1 d2)))
+                                  (pk 'XX things)))
+      (run-with-store %store
+        (mapm/accumulate-builds built-derivations `((,d1) (,d2)))))))
+
+(test-equal "mapm/accumulate-builds, %current-target-system"
+  (make-list 2 '("i586-pc-gnu" "i586-pc-gnu"))
+  ;; Both the 'mapm' and 'mapm/accumulate-builds' procedures should see the
+  ;; right #:target.
+  (run-with-store %store
+    (mlet %store-monad ((lst1 (mapm %store-monad
+                                    (lambda _
+                                      (current-target-system))
+                                    '(a b)))
+                        (lst2 (mapm/accumulate-builds
+                               (lambda _
+                                 (current-target-system))
+                               '(a b))))
+      (return (list lst1 lst2)))
+    #:system system
+    #:target "i586-pc-gnu"))
+
 (test-assert "topologically-sorted, one item"
   (let* ((a (add-text-to-store %store "a" "a"))
          (b (add-text-to-store %store "b" "b" (list a)))
         (foldm %store-monad
                (lambda (item result)
                  (define ref-hash
-                   (let-values (((port get) (open-sha256-port)))
+                   (let-values (((port get) (gcrypt:open-sha256-port)))
                      (write-file item port)
                      (close-port port)
                      (get)))
                (list out1 out2))))
     #:guile-for-build (%guile-for-build)))
 
+
+(test-assert "import not signed"
+  (let* ((text (random-text))
+         (file (add-file-tree-to-store %store
+                                       `("tree" directory
+                                         ("text" regular (data ,text))
+                                         ("link" symlink "text"))))
+         (dump (call-with-bytevector-output-port
+                (lambda (port)
+                  (write-int 1 port)              ;start
+
+                  (write-file file port)          ;contents
+                  (write-int #x4558494e port)     ;%export-magic
+                  (write-string file port)        ;store item
+                  (write-string-list '() port)    ;references
+                  (write-string "" port)          ;deriver
+                  (write-int 0 port)              ;not signed
+
+                  (write-int 0 port)))))          ;done
+
+    ;; Ensure 'import-paths' raises an exception.
+    (guard (c ((store-protocol-error? c)
+               (and (not (zero? (store-protocol-error-status c)))
+                    (string-contains (store-protocol-error-message c)
+                                     "lacks a signature"))))
+      (let* ((source   (open-bytevector-input-port dump))
+             (imported (import-paths %store source)))
+        (pk 'unsigned-imported imported)
+        #f))))
+
+(test-assert "import signed by unauthorized key"
+  (let* ((text (random-text))
+         (file (add-file-tree-to-store %store
+                                       `("tree" directory
+                                         ("text" regular (data ,text))
+                                         ("link" symlink "text"))))
+         (key  (gcrypt:generate-key
+                (gcrypt:string->canonical-sexp
+                 "(genkey (ecdsa (curve Ed25519) (flags rfc6979)))")))
+         (dump (call-with-bytevector-output-port
+                (lambda (port)
+                  (write-int 1 port)              ;start
+
+                  (write-file file port)          ;contents
+                  (write-int #x4558494e port)     ;%export-magic
+                  (write-string file port)        ;store item
+                  (write-string-list '() port)    ;references
+                  (write-string "" port)          ;deriver
+                  (write-int 1 port)              ;signed
+                  (write-string (gcrypt:canonical-sexp->string
+                                 (signature-sexp
+                                  (gcrypt:bytevector->hash-data
+                                   (gcrypt:sha256 #vu8(0 1 2))
+                                   #:key-type 'ecc)
+                                  (gcrypt:find-sexp-token key 'private-key)
+                                  (gcrypt:find-sexp-token key 'public-key)))
+                                port)
+
+                  (write-int 0 port)))))          ;done
+
+    ;; Ensure 'import-paths' raises an exception.
+    (guard (c ((store-protocol-error? c)
+               (and (not (zero? (store-protocol-error-status c)))
+                    (string-contains (store-protocol-error-message c)
+                                     "unauthorized public key"))))
+      (let* ((source   (open-bytevector-input-port dump))
+             (imported (import-paths %store source)))
+        (pk 'unauthorized-imported imported)
+        #f))))
+
 (test-assert "import corrupt path"
   (let* ((text (random-text))
          (file (add-text-to-store %store "text" text))
          (info (query-path-info %store item)))
     (and (equal? (path-info-references info) (list ref))
          (equal? (path-info-hash info)
-                 (sha256
+                 (gcrypt:sha256
                   (string->utf8
                    (call-with-output-string (cut write-file item <>))))))))