;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2012, 2013, 2014, 2015 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 Ludovic Courtès <ludo@gnu.org>
;;;
;;; This file is part of GNU Guix.
;;;
;;; You should have received a copy of the GNU General Public License
;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
+(unsetenv "http_proxy")
+
(define-module (test-derivations)
#:use-module (guix derivations)
+ #:use-module (guix grafts)
#:use-module (guix store)
#:use-module (guix utils)
- #:use-module (guix hash)
+ #:use-module (gcrypt hash)
#:use-module (guix base32)
#:use-module (guix tests)
+ #:use-module (guix tests http)
#:use-module ((guix packages) #:select (package-derivation base32))
#:use-module ((guix build utils) #:select (executable-file?))
- #:use-module ((gnu packages) #:select (search-bootstrap-binary))
#:use-module (gnu packages bootstrap)
#:use-module ((gnu packages guile) #:select (guile-1.8))
#:use-module (srfi srfi-1)
(define %store
(open-connection-for-tests))
+;; Globally disable grafts because they can trigger early builds.
+(%graft? #f)
+
(define (bootstrap-binary name)
(let ((bin (search-bootstrap-binary name (%current-system))))
(and %store
(lambda (e1 e2)
(string<? (car e1) (car e2)))))
+;; Avoid collisions with other tests.
+(%http-server-port 10500)
+
+\f
(test-begin "derivations")
(test-assert "parse & export"
(test-skip (if %store 0 12))
(test-assert "add-to-store, flat"
- (let* ((file (search-path %load-path "language/tree-il/spec.scm"))
+ ;; Use 'readlink*' in case spec.scm is a symlink, as is the case when Guile
+ ;; was installed with Stow.
+ (let* ((file (readlink*
+ (search-path %load-path "language/tree-il/spec.scm")))
(drv (add-to-store %store "flat-test" #f "sha256" file)))
(and (eq? 'regular (stat:type (stat drv)))
(valid-path? %store drv)
(call-with-input-file drv get-bytevector-all)))))
(test-assert "add-to-store, recursive"
- (let* ((dir (dirname (search-path %load-path "language/tree-il/spec.scm")))
+ (let* ((dir (dirname
+ (readlink* (search-path %load-path
+ "language/tree-il/spec.scm"))))
(drv (add-to-store %store "dir-tree-test" #t "sha256" dir)))
(and (eq? 'directory (stat:type (stat drv)))
(valid-path? %store drv)
;; the contents.
(valid-path? %store (derivation->output-path drv)))))
+(test-assert "derivation fails but keep going"
+ ;; In keep-going mode, 'build-derivations' should fail because of D1, but it
+ ;; must return only after D2 has succeeded.
+ (with-store store
+ (let* ((d1 (derivation %store "fails"
+ %bash `("-c" "false")
+ #:inputs `((,%bash))))
+ (d2 (build-expression->derivation %store "sleep-then-succeed"
+ `(begin
+ ,(random-text)
+ ;; XXX: Hopefully that's long
+ ;; enough that D1 has already
+ ;; failed.
+ (sleep 2)
+ (mkdir %output)))))
+ (set-build-options %store
+ #:use-substitutes? #f
+ #:keep-going? #t)
+ (guard (c ((store-protocol-error? c)
+ (and (= 100 (store-protocol-error-status c))
+ (string-contains (store-protocol-error-message c)
+ (derivation-file-name d1))
+ (not (valid-path? %store (derivation->output-path d1)))
+ (valid-path? %store (derivation->output-path d2)))))
+ (build-derivations %store (list d1 d2))
+ #f))))
+
(test-assert "identical files are deduplicated"
(let* ((build1 (add-text-to-store %store "one.sh"
"echo hello, world > \"$out\"\n"
(= (stat:ino (lstat file1))
(stat:ino (lstat file2))))))))
+(test-equal "built-in-builders"
+ '("download")
+ (built-in-builders %store))
+
+(test-assert "unknown built-in builder"
+ (let ((drv (derivation %store "ohoh" "builtin:does-not-exist" '())))
+ (guard (c ((store-protocol-error? c)
+ (string-contains (store-protocol-error-message c) "failed")))
+ (build-derivations %store (list drv))
+ #f)))
+
+(unless (http-server-can-listen?)
+ (test-skip 1))
+(test-assert "'download' built-in builder"
+ (let ((text (random-text)))
+ (with-http-server 200 text
+ (let* ((drv (derivation %store "world"
+ "builtin:download" '()
+ #:env-vars `(("url"
+ . ,(object->string (%local-url))))
+ #:hash-algo 'sha256
+ #:hash (sha256 (string->utf8 text)))))
+ (and (build-derivations %store (list drv))
+ (string=? (call-with-input-file (derivation->output-path drv)
+ get-string-all)
+ text))))))
+
+(unless (http-server-can-listen?)
+ (test-skip 1))
+(test-assert "'download' built-in builder, invalid hash"
+ (with-http-server 200 "hello, world!"
+ (let* ((drv (derivation %store "world"
+ "builtin:download" '()
+ #:env-vars `(("url"
+ . ,(object->string (%local-url))))
+ #:hash-algo 'sha256
+ #:hash (sha256 (random-bytevector 100))))) ;wrong
+ (guard (c ((store-protocol-error? c)
+ (string-contains (store-protocol-error-message c) "failed")))
+ (build-derivations %store (list drv))
+ #f))))
+
+(unless (http-server-can-listen?)
+ (test-skip 1))
+(test-assert "'download' built-in builder, not found"
+ (with-http-server 404 "not found"
+ (let* ((drv (derivation %store "will-never-be-found"
+ "builtin:download" '()
+ #:env-vars `(("url"
+ . ,(object->string (%local-url))))
+ #:hash-algo 'sha256
+ #:hash (sha256 (random-bytevector 100)))))
+ (guard (c ((store-protocol-error? c)
+ (string-contains (store-protocol-error-message (pk c)) "failed")))
+ (build-derivations %store (list drv))
+ #f))))
+
+(test-assert "'download' built-in builder, not fixed-output"
+ (let* ((source (add-text-to-store %store "hello" "hi!"))
+ (url (string-append "file://" source))
+ (drv (derivation %store "world"
+ "builtin:download" '()
+ #:env-vars `(("url" . ,(object->string url))))))
+ (guard (c ((store-protocol-error? c)
+ (string-contains (store-protocol-error-message c) "failed")))
+ (build-derivations %store (list drv))
+ #f)))
+
+(unless (http-server-can-listen?)
+ (test-skip 1))
+(test-assert "'download' built-in builder, check mode"
+ ;; Make sure rebuilding the 'builtin:download' derivation in check mode
+ ;; works. See <http://bugs.gnu.org/25089>.
+ (let* ((text (random-text))
+ (drv (derivation %store "world"
+ "builtin:download" '()
+ #:env-vars `(("url"
+ . ,(object->string (%local-url))))
+ #:hash-algo 'sha256
+ #:hash (sha256 (string->utf8 text)))))
+ (and (with-http-server 200 text
+ (build-derivations %store (list drv)))
+ (with-http-server 200 text
+ (build-derivations %store (list drv)
+ (build-mode check)))
+ (string=? (call-with-input-file (derivation->output-path drv)
+ get-string-all)
+ text))))
+
(test-equal "derivation-name"
"foo-0.0"
(let ((drv (derivation %store "foo-0.0" %bash '())))
(and (eq? 'one (call-with-input-file one read))
(eq? 'two (call-with-input-file two read)))))))
+(test-assert "read-derivation vs. derivation"
+ ;; Make sure 'derivation' and 'read-derivation' return objects that are
+ ;; identical.
+ (let* ((sources (unfold (cut >= <> 10)
+ (lambda (n)
+ (add-text-to-store %store
+ (format #f "input~a" n)
+ (random-text)))
+ 1+
+ 0))
+ (inputs (map (lambda (file)
+ (derivation %store "derivation-input"
+ %bash '()
+ #:inputs `((,%bash) (,file))))
+ sources))
+ (builder (add-text-to-store %store "builder.sh"
+ "echo one > $one ; echo two > $two"
+ '()))
+ (drv (derivation %store "derivation"
+ %bash `(,builder)
+ #:inputs `((,%bash) (,builder)
+ ,@(map list (append sources inputs)))
+ #:outputs '("two" "one")))
+ (drv* (call-with-input-file (derivation-file-name drv)
+ read-derivation)))
+ (equal? drv* drv)))
+
(test-assert "multiple-output derivation, derivation-path->output-path"
(let* ((builder (add-text-to-store %store "builder.sh"
"echo one > $out ; echo two > $second"
`("-c" ,(string-append "echo " txt "> $out"))
#:inputs `((,%bash) (,txt))
#:allowed-references '())))
- (guard (c ((nix-protocol-error? c)
+ (guard (c ((store-protocol-error? c)
;; There's no specific error message to check for.
#t))
(build-derivations %store (list drv))
`("-c" ,"echo $out > $out")
#:inputs `((,%bash))
#:allowed-references '())))
- (guard (c ((nix-protocol-error? c)
+ (guard (c ((store-protocol-error? c)
;; There's no specific error message to check for.
#t))
(build-derivations %store (list drv))
#f)))
+(test-assert "derivation #:disallowed-references, ok"
+ (let ((drv (derivation %store "disallowed" %bash
+ '("-c" "echo hello > $out")
+ #:inputs `((,%bash))
+ #:disallowed-references '("out"))))
+ (build-derivations %store (list drv))))
+
+(test-assert "derivation #:disallowed-references, not ok"
+ (let* ((txt (add-text-to-store %store "foo" "Hello, world."))
+ (drv (derivation %store "disdisallowed" %bash
+ `("-c" ,(string-append "echo " txt "> $out"))
+ #:inputs `((,%bash) (,txt))
+ #:disallowed-references (list txt))))
+ (guard (c ((store-protocol-error? c)
+ ;; There's no specific error message to check for.
+ #t))
+ (build-derivations %store (list drv))
+ #f)))
+
+;; Here we should get the value of $GUIX_STATE_DIRECTORY that the daemon sees,
+;; which is a unique value for each test process; this value is the same as
+;; the one we see in the process executing this file since it is set by
+;; 'test-env'.
+(test-equal "derivation #:leaked-env-vars"
+ (getenv "GUIX_STATE_DIRECTORY")
+ (let* ((value (getenv "GUIX_STATE_DIRECTORY"))
+ (drv (derivation %store "leaked-env-vars" %bash
+ '("-c" "echo -n $GUIX_STATE_DIRECTORY > $out")
+ #:hash (sha256 (string->utf8 value))
+ #:hash-algo 'sha256
+ #:inputs `((,%bash))
+ #:leaked-env-vars '("GUIX_STATE_DIRECTORY"))))
+ (and (build-derivations %store (list drv))
+ (call-with-input-file (derivation->output-path drv)
+ get-string-all))))
+
\f
(define %coreutils
(false-if-exception
(test-skip (if (%guile-for-build) 0 8))
+(test-equal "build-expression->derivation and invalid module name"
+ '(file-search-error "guix/module/that/does/not/exist.scm")
+ (guard (c ((file-search-error? c)
+ (list 'file-search-error
+ (file-search-error-file-name c))))
+ (build-expression->derivation %store "foo" #t
+ #:modules '((guix module that
+ does not exist)))))
+
+(test-equal "build-expression->derivation and builder encoding"
+ '("UTF-8" #t)
+ (let* ((exp '(λ (α) (+ α 1)))
+ (drv (build-expression->derivation %store "foo" exp)))
+ (match (derivation-builder-arguments drv)
+ ((... builder)
+ (with-fluids ((%default-port-encoding "UTF-8"))
+ (call-with-input-file builder
+ (lambda (port)
+ (list (port-encoding port)
+ (->bool
+ (string-contains (get-string-all port)
+ "(λ (α) (+ α 1))"))))))))))
+
(test-assert "build-expression->derivation and derivation-prerequisites"
(let ((drv (build-expression->derivation %store "fail" #f)))
(any (match-lambda
(builder '(begin (sleep 100) (mkdir %output) #t))
(drv (build-expression->derivation store "silent" builder))
(out-path (derivation->output-path drv)))
- (guard (c ((nix-protocol-error? c)
- (and (string-contains (nix-protocol-error-message c)
+ (guard (c ((store-protocol-error? c)
+ (and (string-contains (store-protocol-error-message c)
"failed")
(not (valid-path? store out-path)))))
(build-derivations store (list drv))
(builder '(begin (sleep 100) (mkdir %output) #t))
(drv (build-expression->derivation store "slow" builder))
(out-path (derivation->output-path drv)))
- (guard (c ((nix-protocol-error? c)
- (and (string-contains (nix-protocol-error-message c)
+ (guard (c ((store-protocol-error? c)
+ (and (string-contains (store-protocol-error-message c)
"failed")
(not (valid-path? store out-path)))))
(build-derivations store (list drv))
#f)))
+(test-assert "build-derivations with specific output"
+ (with-store store
+ (let* ((content (random-text)) ;contents of the output
+ (drv (build-expression->derivation
+ store "substitute-me"
+ `(begin ,content (exit 1)) ;would fail
+ #:outputs '("out" "one" "two")
+ #:guile-for-build
+ (package-derivation store %bootstrap-guile)))
+ (out (derivation->output-path drv)))
+ (with-derivation-substitute drv content
+ (set-build-options store #:use-substitutes? #t
+ #:substitute-urls (%test-substitute-urls))
+ (and (has-substitutes? store out)
+
+ ;; Ask for nothing but the "out" output of DRV.
+ (build-derivations store `((,drv . "out")))
+
+ (valid-path? store out)
+ (equal? (pk 'x content) (pk 'y (call-with-input-file out get-string-all)))
+ )))))
+
(test-assert "build-expression->derivation and derivation-prerequisites-to-build"
(let ((drv (build-expression->derivation %store "fail" #f)))
;; The only direct dependency is (%guile-for-build) and it's already
(derivation-prerequisites-to-build store drv))
((build* download*)
(derivation-prerequisites-to-build store drv
- #:substitutable?
+ #:substitutable-info
(const #f))))
(and (null? build)
- (equal? download (list output))
+ (equal? (map substitutable-path download) (list output))
(null? download*)
(null? build*))))))
;; See <http://bugs.gnu.org/18747>.
(and (null? build)
(match download
- (((? string? item))
+ (((= substitutable-path item))
(string=? item (derivation->output-path drv))))))))))
+(test-assert "derivation-prerequisites-to-build in 'check' mode"
+ (with-store store
+ (let* ((dep (build-expression->derivation store "dep"
+ `(begin ,(random-text)
+ (mkdir %output))))
+ (drv (build-expression->derivation store "to-check"
+ '(mkdir %output)
+ #:inputs `(("dep" ,dep)))))
+ (build-derivations store (list drv))
+ (delete-paths store (list (derivation->output-path dep)))
+
+ ;; In 'check' mode, DEP must be rebuilt.
+ (and (null? (derivation-prerequisites-to-build store drv))
+ (match (derivation-prerequisites-to-build store drv
+ #:mode (build-mode
+ check))
+ ((input)
+ (string=? (derivation-input-path input)
+ (derivation-file-name dep))))))))
+
+(test-assert "substitution-oracle and #:substitute? #f"
+ (with-store store
+ (let* ((dep (build-expression->derivation store "dep"
+ `(begin ,(random-text)
+ (mkdir %output))))
+ (drv (build-expression->derivation store "not-subst"
+ `(begin ,(random-text)
+ (mkdir %output))
+ #:substitutable? #f
+ #:inputs `(("dep" ,dep))))
+ (query #f))
+ (define (record-substitutable-path-query store paths)
+ (when query
+ (error "already called!" query))
+ (set! query paths)
+ '())
+
+ (mock ((guix store) substitutable-path-info
+ record-substitutable-path-query)
+
+ (let ((pred (substitution-oracle store (list drv))))
+ (pred (derivation->output-path drv))))
+
+ ;; Make sure the oracle didn't try to get substitute info for DRV since
+ ;; DRV is mark as non-substitutable. Assume that GUILE-FOR-BUILD is
+ ;; already in store and thus not part of QUERY.
+ (equal? (pk 'query query)
+ (list (derivation->output-path dep))))))
+
(test-assert "build-expression->derivation with expression returning #f"
(let* ((builder '(begin
(mkdir %output)
#f)) ; fail!
(drv (build-expression->derivation %store "fail" builder))
(out-path (derivation->output-path drv)))
- (guard (c ((nix-protocol-error? c)
+ (guard (c ((store-protocol-error? c)
;; Note that the output path may exist at this point, but it
;; is invalid.
(and (string-match "build .* failed"
- (nix-protocol-error-message c))
+ (store-protocol-error-message c))
(not (valid-path? %store out-path)))))
(build-derivations %store (list drv))
#f)))
((p2 . _)
(string<? p1 p2)))))))))))))
-
-(test-assert "graft-derivation"
- (let* ((build `(begin
- (mkdir %output)
- (chdir %output)
- (symlink %output "self")
- (call-with-output-file "text"
- (lambda (output)
- (format output "foo/~a/bar" ,%mkdir)))
- (symlink ,%bash "sh")))
- (orig (build-expression->derivation %store "graft" build
- #:inputs `(("a" ,%bash)
- ("b" ,%mkdir))))
- (one (add-text-to-store %store "bash" "fake bash"))
- (two (build-expression->derivation %store "mkdir"
- '(call-with-output-file %output
- (lambda (port)
- (display "fake mkdir" port)))))
- (graft (graft-derivation %store "graft" orig
- (list (graft
- (origin %bash)
- (replacement one))
- (graft
- (origin %mkdir)
- (replacement two))))))
- (and (build-derivations %store (list graft))
- (let ((two (derivation->output-path two))
- (graft (derivation->output-path graft)))
- (and (string=? (format #f "foo/~a/bar" two)
- (call-with-input-file (string-append graft "/text")
- get-string-all))
- (string=? (readlink (string-append graft "/sh")) one)
- (string=? (readlink (string-append graft "/self")) graft))))))
+(test-equal "derivation-properties"
+ (list '() '((type . test)))
+ (let ((drv1 (build-expression->derivation %store "bar"
+ '(mkdir %output)))
+ (drv2 (build-expression->derivation %store "foo"
+ '(mkdir %output)
+ #:properties '((type . test)))))
+ (list (derivation-properties drv1)
+ (derivation-properties drv2))))
(test-equal "map-derivation"
"hello"
(test-end)
-\f
-(exit (= (test-runner-fail-count (test-runner-current)) 0))
+;; Local Variables:
+;; eval: (put 'with-http-server 'scheme-indent-function 2)
+;; End: