Merge branch 'master' into core-updates
[jackhill/guix/guix.git] / tests / graph.scm
index ed5849f..4028471 100644 (file)
@@ -1,5 +1,5 @@
 ;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2015 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2015, 2016, 2017, 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
   #:use-module (guix derivations)
   #:use-module (guix store)
   #:use-module (guix monads)
+  #:use-module (guix grafts)
   #:use-module (guix build-system gnu)
+  #:use-module (guix build-system trivial)
   #:use-module (guix gexp)
+  #:use-module (guix utils)
   #:use-module (gnu packages)
+  #:use-module (gnu packages base)
+  #:use-module (gnu packages guile)
+  #:use-module (gnu packages libunistring)
   #:use-module (gnu packages bootstrap)
   #:use-module (ice-9 match)
   #:use-module (srfi srfi-1)
@@ -37,6 +43,9 @@
 (define %store
   (open-connection-for-tests))
 
+;; Globally disable grafts because they can trigger early builds.
+(%graft? #f)
+
 (define (make-recording-backend)
   "Return a <graph-backend> and a thunk that returns the recorded nodes and
 edges."
@@ -49,7 +58,8 @@ edges."
     (define (return)
       (values (reverse nodes) (reverse edges)))
 
-    (values (graph-backend (const #t) (const #t)
+    (values (graph-backend "test" "This is the test backend."
+                           (const #t) (const #t)
                            record-node record-edge)
             return)))
 
@@ -83,23 +93,40 @@ edges."
                           (list p3 p3 p2)
                           (list p2 p1 p1))))))))
 
+(test-assert "reverse package DAG"
+  (let-values (((backend nodes+edges) (make-recording-backend)))
+    (run-with-store %store
+      (export-graph (list libunistring) 'port
+                    #:node-type %reverse-package-node-type
+                    #:backend backend))
+    ;; We should see nothing more than these 3 packages.
+    (let-values (((nodes edges) (nodes+edges)))
+      (and (member (package->tuple guile-2.0) nodes)
+           (->bool (member (edge->tuple libunistring guile-2.0) edges))))))
+
 (test-assert "bag-emerged DAG"
   (let-values (((backend nodes+edges) (make-recording-backend)))
-    (let ((p        (dummy-package "p"))
-          (implicit (map (match-lambda
-                           ((label package) package))
-                         (standard-packages))))
+    (let* ((o        (dummy-origin (method (lambda _
+                                             (text-file "foo" "bar")))))
+           (p        (dummy-package "p" (source o)))
+           (implicit (map (match-lambda
+                            ((label package) package)
+                            ((label package output) package))
+                          (standard-packages))))
       (run-with-store %store
         (export-graph (list p) 'port
                       #:node-type %bag-emerged-node-type
                       #:backend backend))
       ;; We should see exactly P and IMPLICIT, with one edge from P to each
-      ;; element of IMPLICIT.
+      ;; element of IMPLICIT.  O must not appear among NODES.  Note: IMPLICIT
+      ;; contains "glibc" twice, once for "out" and a second time for
+      ;; "static", hence the 'delete-duplicates' call below.
       (let-values (((nodes edges) (nodes+edges)))
         (and (equal? (match nodes
                        (((labels names) ...)
                         names))
-                     (map package-full-name (cons p implicit)))
+                     (map package-full-name
+                          (cons p (delete-duplicates implicit))))
              (equal? (match edges
                        (((sources destinations) ...)
                         (zip (map store-path-package-name sources)
@@ -107,11 +134,11 @@ edges."
                      (map (lambda (destination)
                             (list "p-0.drv"
                                   (string-append
-                                   (package-full-name destination)
+                                   (package-full-name destination "-")
                                    ".drv")))
                           implicit)))))))
 
-(test-assert "bag DAG"
+(test-assert "bag DAG"                            ;a big town in Iraq
   (let-values (((backend nodes+edges) (make-recording-backend)))
     (let ((p (dummy-package "p")))
       (run-with-store %store
@@ -126,9 +153,69 @@ edges."
                        (match nodes
                          (((labels names) ...)
                           names))))
-               (match %bootstrap-inputs
+               (match (%bootstrap-inputs)
                  (((labels packages) ...)
-                  (map package-full-name packages))))))))
+                  (map package-full-name (filter package? packages)))))))))
+
+(test-assert "bag DAG, including origins"
+  (let-values (((backend nodes+edges) (make-recording-backend)))
+    (let* ((m (lambda* (uri hash-type hash name #:key system)
+                (text-file "foo-1.2.3.tar.gz" "This is a fake!")))
+           (o (origin (method m) (uri "the-uri") (sha256 #vu8(0 1 2))))
+           (p (dummy-package "p" (source o))))
+      (run-with-store %store
+        (export-graph (list p) 'port
+                      #:node-type %bag-with-origins-node-type
+                      #:backend backend))
+      ;; We should see O among the nodes, with an edge coming from P.
+      (let-values (((nodes edges) (nodes+edges)))
+        (run-with-store %store
+          (mlet %store-monad ((o* (lower-object o))
+                              (p* (lower-object p))
+                              (g  (lower-object (default-guile))))
+            (return
+             (and (find (match-lambda
+                          ((file "the-uri") #t)
+                          (_                #f))
+                        nodes)
+                  (find (match-lambda
+                          ((source target)
+                           (and (string=? source (derivation-file-name p*))
+                                (string=? target o*))))
+                        edges)
+
+                  ;; There must also be an edge from O to G.
+                  (find (match-lambda
+                          ((source target)
+                           (and (string=? source o*)
+                                (string=? target (derivation-file-name g)))))
+                        edges)))))))))
+
+(test-assert "reverse bag DAG"
+  (let-values (((dune bap ocaml-base)
+                (values (specification->package "dune")
+                        (specification->package "bap")
+                        (specification->package "ocaml4.07-base")))
+               ((backend nodes+edges) (make-recording-backend)))
+    (run-with-store %store
+      (export-graph (list dune) 'port
+                    #:node-type %reverse-bag-node-type
+                    #:backend backend))
+
+    (run-with-store %store
+      (mlet %store-monad ((dune-drv       (package->derivation dune))
+                          (bap-drv        (package->derivation bap))
+                          (ocaml-base-drv (package->derivation ocaml-base)))
+        ;; OCAML-BASE uses 'dune-build-system' so DUNE is a direct dependency.
+        ;; BAP is much higher in the stack but it should be there.
+        (let-values (((nodes edges) (nodes+edges)))
+          (return
+           (and (member `(,(derivation-file-name bap-drv)
+                          ,(package-full-name bap))
+                        nodes)
+                (->bool (member (map derivation-file-name
+                                     (list dune-drv ocaml-base-drv))
+                                edges)))))))))
 
 (test-assert "derivation DAG"
   (let-values (((backend nodes+edges) (make-recording-backend)))
@@ -188,7 +275,100 @@ edges."
                           (list out txt))
                   (equal? edges `((,out ,txt)))))))))))
 
-(test-end "graph")
+(test-assert "referrer DAG"
+  (let-values (((backend nodes+edges) (make-recording-backend)))
+    (run-with-store %store
+      (mlet* %store-monad ((txt   (text-file "referrer-node" (random-text)))
+                           (drv   (gexp->derivation "referrer"
+                                                    #~(symlink #$txt #$output)))
+                           (out -> (derivation->output-path drv)))
+        ;; We should see only TXT and OUT, with an edge from the former to the
+        ;; latter.
+        (mbegin %store-monad
+          (built-derivations (list drv))
+          (export-graph (list txt) 'port
+                        #:node-type %referrer-node-type
+                        #:backend backend)
+          (let-values (((nodes edges) (nodes+edges)))
+            (return
+             (and (equal? (match nodes
+                            (((ids labels) ...)
+                             ids))
+                          (list txt out))
+                  (equal? edges `((,txt ,out)))))))))))
 
-\f
-(exit (= (test-runner-fail-count (test-runner-current)) 0))
+(test-assert "module graph"
+  (let-values (((backend nodes+edges) (make-recording-backend)))
+    (run-with-store %store
+      (export-graph '((gnu packages guile)) 'port
+                    #:node-type %module-node-type
+                    #:backend backend))
+
+    (let-values (((nodes edges) (nodes+edges)))
+      (and (member '(gnu packages guile)
+                   (match nodes
+                     (((ids labels) ...) ids)))
+           (->bool (and (member (list '(gnu packages guile)
+                                      '(gnu packages libunistring))
+                                edges)
+                        (member (list '(gnu packages guile)
+                                      '(gnu packages bdw-gc))
+                                edges)))))))
+
+(test-assert "node-edges"
+  (run-with-store %store
+    (let ((packages (fold-packages cons '())))
+      (mlet %store-monad ((edges (node-edges %package-node-type packages)))
+        (return (and (null? (edges hello))
+                     (lset= eq?
+                            (edges guile-2.0)
+                            (match (package-direct-inputs guile-2.0)
+                              (((labels packages _ ...) ...)
+                               packages)))))))))
+
+(test-assert "node-transitive-edges + node-back-edges"
+  (run-with-store %store
+    (let ((packages   (fold-packages cons '()))
+          (bootstrap? (lambda (package)
+                        (string-contains
+                         (location-file (package-location package))
+                         "bootstrap.scm")))
+          (trivial?   (lambda (package)
+                        (eq? (package-build-system package)
+                             trivial-build-system))))
+      (mlet %store-monad ((edges (node-back-edges %bag-node-type packages)))
+        (let* ((glibc      (canonical-package glibc))
+               (dependents (node-transitive-edges (list glibc) edges))
+               (diff       (lset-difference eq? packages dependents)))
+          ;; All the packages depend on libc, except bootstrap packages and
+          ;; some that use TRIVIAL-BUILD-SYSTEM.
+          (return (null? (remove (lambda (package)
+                                   (or (trivial? package)
+                                       (bootstrap? package)))
+                                 diff))))))))
+
+(test-assert "node-transitive-edges, no duplicates"
+  (run-with-store %store
+    (let* ((p0  (dummy-package "p0"))
+           (p1a (dummy-package "p1a" (inputs `(("p0" ,p0)))))
+           (p1b (dummy-package "p1b" (inputs `(("p0" ,p0)))))
+           (p2  (dummy-package "p2" (inputs `(("p1a" ,p1a) ("p1b" ,p1b))))))
+      (mlet %store-monad ((edges (node-edges %package-node-type
+                                             (list p2 p1a p1b p0))))
+        (return (lset= eq? (node-transitive-edges (list p2) edges)
+                       (list p1a p1b p0)))))))
+
+(test-equal "node-reachable-count"
+  '(3 3)
+  (run-with-store %store
+    (let* ((p0  (dummy-package "p0"))
+           (p1a (dummy-package "p1a" (inputs `(("p0" ,p0)))))
+           (p1b (dummy-package "p1b" (inputs `(("p0" ,p0)))))
+           (p2  (dummy-package "p2" (inputs `(("p1a" ,p1a) ("p1b" ,p1b))))))
+      (mlet* %store-monad ((all -> (list p2 p1a p1b p0))
+                           (edges  (node-edges %package-node-type all))
+                           (back   (node-back-edges %package-node-type all)))
+        (return (list (node-reachable-count (list p2) edges)
+                      (node-reachable-count (list p0) back)))))))
+
+(test-end "graph")