;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2012, 2013 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2012, 2013, 2014, 2015, 2017, 2018, 2019 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@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/>.
-
(define-module (test-union)
+ #:use-module (guix tests)
#:use-module (guix store)
#:use-module (guix utils)
#:use-module (guix derivations)
#:use-module (gnu packages bootstrap)
#:use-module (srfi srfi-1)
#:use-module (srfi srfi-64)
+ #:use-module (rnrs io ports)
#:use-module (ice-9 match))
;; Exercise the (guix build union) module.
(define %store
- (false-if-exception (open-connection)))
-
-(when %store
- ;; By default, use %BOOTSTRAP-GUILE for the current system.
- (let ((drv (package-derivation %store %bootstrap-guile)))
- (%guile-for-build drv)))
+ (open-connection-for-tests))
\f
(test-begin "union")
-(test-equal "tree-union, empty"
- '()
- (tree-union '()))
-
-(test-equal "tree-union, leaves only"
- '(a b c d)
- (tree-union '(a b c d)))
-
-(test-equal "tree-union, simple"
- '((bin ls touch make awk gawk))
- (tree-union '((bin ls touch)
- (bin make)
- (bin awk gawk))))
-
-(test-equal "tree-union, several levels"
- '((share (doc (make README) (coreutils README)))
- (bin ls touch make))
- (tree-union '((bin ls touch)
- (share (doc (coreutils README)))
- (bin make)
- (share (doc (make README))))))
-
-(test-equal "delete-duplicate-leaves, default"
- '(bin make touch ls)
- (delete-duplicate-leaves '(bin ls make touch ls)))
-
-(test-equal "delete-duplicate-leaves, file names"
- '("doc" ("info"
- "/binutils/ld.info"
- "/gcc/gcc.info"
- "/binutils/standards.info"))
- (let ((leaf=? (lambda (a b)
- (string=? (basename a) (basename b)))))
- (delete-duplicate-leaves '("doc"
- ("info"
- "/binutils/ld.info"
- "/binutils/standards.info"
- "/gcc/gcc.info"
- "/gcc/standards.info"))
- leaf=?)))
+(test-assert "union-build with symlink to directory"
+ ;; http://bugs.gnu.org/17083
+ ;; Here both ONE and TWO provide an element called 'foo', but in ONE it's a
+ ;; directory whereas in TWO it's a symlink to a directory.
+ (let* ((one (build-expression->derivation
+ %store "one"
+ '(begin
+ (use-modules (guix build utils) (srfi srfi-26))
+ (let ((foo (string-append %output "/foo")))
+ (mkdir-p foo)
+ (call-with-output-file (string-append foo "/one")
+ (cut display "one" <>))))
+ #:modules '((guix build utils))))
+ (two (build-expression->derivation
+ %store "two"
+ '(begin
+ (use-modules (guix build utils) (srfi srfi-26))
+ (let ((foo (string-append %output "/foo"))
+ (bar (string-append %output "/bar")))
+ (mkdir-p bar)
+ (call-with-output-file (string-append bar "/two")
+ (cut display "two" <>))
+ (symlink "bar" foo)))
+ #:modules '((guix build utils))))
+ (builder '(begin
+ (use-modules (guix build union))
-(test-skip (if (and %store
- (false-if-exception
- (getaddrinfo "www.gnu.org" "80" AI_NUMERICSERV)))
+ (union-build (assoc-ref %outputs "out")
+ (list (assoc-ref %build-inputs "one")
+ (assoc-ref %build-inputs "two")))))
+ (drv
+ (build-expression->derivation %store "union-collision-symlink"
+ builder
+ #:inputs `(("one" ,one) ("two" ,two))
+ #:modules '((guix build union)))))
+ (and (build-derivations %store (list drv))
+ (with-directory-excursion (pk (derivation->output-path drv))
+ (and (string=? "one"
+ (call-with-input-file "foo/one" get-string-all))
+ (string=? "two"
+ (call-with-input-file "foo/two" get-string-all))
+ (string=? "two"
+ (call-with-input-file "bar/two" get-string-all))
+ (not (file-exists? "bar/one")))))))
+
+(test-skip (if (and %store (network-reachable?))
0
1))
`(,name ,(package-derivation %store package))))
;; Purposefully leave duplicate entries.
- (append %bootstrap-inputs
- (take %bootstrap-inputs 3))))
+ (filter (compose package? cadr)
+ (append %bootstrap-inputs-for-tests
+ (take %bootstrap-inputs-for-tests 3)))))
(builder `(begin
(use-modules (guix build union))
(union-build (assoc-ref %outputs "out")
(map cdr %build-inputs))))
(drv
(build-expression->derivation %store "union-test"
- (%current-system)
- builder inputs
+ builder
+ #:inputs inputs
#:modules '((guix build union)))))
(and (build-derivations %store (list (pk 'drv drv)))
(with-directory-excursion (derivation->output-path drv)
(directory-exists? "lib/gcc")
(file-exists? "include/unistd.h")
- ;; The 'include' sub-directory is only found in
- ;; glibc-bootstrap, so it should be unified in a
+ ;; The 'include/c++' sub-directory is only found in
+ ;; gcc-bootstrap, so it should be unified in a
;; straightforward way, without traversing it.
- (eq? 'symlink (stat:type (lstat "include")))
+ (eq? 'symlink (stat:type (lstat "include/c++")))
;; Conversely, several inputs have a 'bin' sub-directory, so
;; unifying it requires traversing them all, and creating a
;; new 'bin' sub-directory in the profile.
(eq? 'directory (stat:type (lstat "bin"))))))))
-(test-end)
+(test-assert "union-build collision first & last"
+ (let* ((guile (package-derivation %store %bootstrap-guile))
+ (fake (build-expression->derivation
+ %store "fake-guile"
+ '(begin
+ (use-modules (guix build utils))
+ (let ((out (assoc-ref %outputs "out")))
+ (mkdir-p (string-append out "/bin"))
+ (call-with-output-file (string-append out "/bin/guile")
+ (const #t))))
+ #:modules '((guix build utils))))
+ (builder (lambda (policy)
+ `(begin
+ (use-modules (guix build union)
+ (srfi srfi-1))
+ (union-build (assoc-ref %outputs "out")
+ (map cdr %build-inputs)
+ #:resolve-collision ,policy))))
+ (drv1
+ (build-expression->derivation %store "union-first"
+ (builder 'first)
+ #:inputs `(("guile" ,guile)
+ ("fake" ,fake))
+ #:modules '((guix build union))))
+ (drv2
+ (build-expression->derivation %store "union-last"
+ (builder 'last)
+ #:inputs `(("guile" ,guile)
+ ("fake" ,fake))
+ #:modules '((guix build union)))))
+ (and (build-derivations %store (list drv1 drv2))
+ (with-directory-excursion (derivation->output-path drv1)
+ (string=? (readlink "bin/guile")
+ (string-append (derivation->output-path guile)
+ "/bin/guile")))
+ (with-directory-excursion (derivation->output-path drv2)
+ (string=? (readlink "bin/guile")
+ (string-append (derivation->output-path fake)
+ "/bin/guile"))))))
+
+(test-assert "union-build #:create-all-directories? #t"
+ (let* ((build `(begin
+ (use-modules (guix build union))
+ (union-build (assoc-ref %outputs "out")
+ (map cdr %build-inputs)
+ #:create-all-directories? #t)))
+ (input (package-derivation %store %bootstrap-guile))
+ (drv (build-expression->derivation %store "union-test-all-dirs"
+ build
+ #:modules '((guix build union))
+ #:inputs `(("g" ,input)))))
+ (and (build-derivations %store (list drv))
+ (with-directory-excursion (derivation->output-path drv)
+ ;; Even though there's only one input to the union,
+ ;; #:create-all-directories? #t must have created bin/ rather than
+ ;; making it a symlink to Guile's bin/.
+ (and (file-exists? "bin/guile")
+ (file-is-directory? "bin")
+ (eq? 'symlink (stat:type (lstat "bin/guile"))))))))
+
+(letrec-syntax ((test-relative-file-name
+ (syntax-rules (=>)
+ ((_ (reference file => expected) rest ...)
+ (begin
+ (test-equal (string-append "relative-file-name "
+ reference " " file)
+ expected
+ (relative-file-name reference file))
+ (test-relative-file-name rest ...)))
+ ((_)
+ #t))))
+ (test-relative-file-name
+ ("/a/b" "/a/c/d" => "../c/d")
+ ("/a/b" "/a/b" => "")
+ ("/a/b" "/a" => "..")
+ ("/a/b" "/a/b/c/d" => "c/d")
+ ("/a/b/c" "/a/d/e/f" => "../../d/e/f")))
-\f
-(exit (= (test-runner-fail-count (test-runner-current)) 0))
+(test-end)