maav / guix-mirror (public) (License: GPLv3+) (since 2019-11-02) (hash sha1)
Mirror of GNU Guix (https://git.savannah.gnu.org/git/guix.git) with personal branches integrated into master branch.

/tests/graph.scm (0663d13b49307a6a06a8593ce12aa8b83ef491c1) (22214 bytes) (mode 100644) (type blob)

;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2015, 2016, 2017, 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org>
;;;
;;; This file is part of GNU Guix.
;;;
;;; GNU Guix is free software; you can redistribute it and/or modify it
;;; under the terms of the GNU General Public License as published by
;;; the Free Software Foundation; either version 3 of the License, or (at
;;; your option) any later version.
;;;
;;; GNU Guix is distributed in the hope that it will be useful, but
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;;; GNU General Public License for more details.
;;;
;;; 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-graph)
  #:use-module (guix tests)
  #:use-module (guix graph)
  #:use-module (guix scripts graph)
  #:use-module (guix packages)
  #: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 bootstrap)
  #: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)
  #:use-module (srfi srfi-11)
  #:use-module (srfi srfi-26)
  #:use-module (srfi srfi-64))

(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."
  (let ((nodes '())
        (edges '()))
    (define (record-node id label port)
      (set! nodes (cons (list id label) nodes)))
    (define (record-edge source target port)
      (set! edges (cons (list source target) edges)))
    (define (return)
      (values (reverse nodes) (reverse edges)))

    (values (graph-backend "test" "This is the test backend."
                           (const #t) (const #t)
                           record-node record-edge)
            return)))

(define (package->tuple package)
  "Return a tuple representing PACKAGE as produced by %PACKAGE-NODE-TYPE."
  (list (object-address package)
        (package-full-name package)))

(define (edge->tuple source target)
  "Likewise for an edge from SOURCE to TARGET."
  (list (object-address source)
        (object-address target)))


(test-begin "graph")

(test-assert "package DAG"
  (let-values (((backend nodes+edges) (make-recording-backend)))
    (let* ((p1 (dummy-package "p1"))
           (p2 (dummy-package "p2" (inputs `(("p1" ,p1)))))
           (p3 (dummy-package "p3" (inputs `(("p2" ,p2) ("p1", p1))))))
      (run-with-store %store
        (export-graph (list p3) 'port
                      #:node-type %package-node-type
                      #:backend backend))
      ;; We should see nothing more than these 3 packages.
      (let-values (((nodes edges) (nodes+edges)))
        (and (equal? nodes (map package->tuple (list p3 p2 p1)))
             (equal? edges
                     (map edge->tuple
                          (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* ((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.  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 (delete-duplicates implicit))))
             (equal? (match edges
                       (((sources destinations) ...)
                        (zip (map store-path-package-name sources)
                             (map store-path-package-name destinations))))
                     (map (lambda (destination)
                            (list "p-0.drv"
                                  (string-append
                                   (package-full-name destination "-")
                                   ".drv")))
                          implicit)))))))

(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
        (export-graph (list p) 'port
                      #:node-type %bag-node-type
                      #:backend backend))
      ;; We should see P, its implicit inputs as well as the whole DAG, which
      ;; should include bootstrap binaries.
      (let-values (((nodes edges) (nodes+edges)))
        (every (lambda (name)
                 (find (cut string=? name <>)
                       (match nodes
                         (((labels names) ...)
                          names))))
               (match (%bootstrap-inputs)
                 (((labels 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
                 (base32
                  "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"))))
           (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)))
    (run-with-store %store
      (mlet* %store-monad ((txt   (text-file "text-file" "Hello!"))
                           (guile (package->derivation %bootstrap-guile))
                           (drv   (gexp->derivation "output"
                                                    #~(symlink #$txt #$output)
                                                    #:guile-for-build
                                                    guile)))
        ;; We should get at least these 3 nodes and corresponding edges.
        (mbegin %store-monad
          (export-graph (list drv) 'port
                        #:node-type %derivation-node-type
                        #:backend backend)
          (let-values (((nodes edges) (nodes+edges)))
            ;; XXX: For some reason we need to throw in some 'basename'.
            (return (and (match nodes
                           (((ids labels) ...)
                            (let ((ids (map basename ids)))
                              (every (lambda (item)
                                       (member (basename item) ids))
                                     (list txt
                                           (derivation-file-name drv)
                                           (derivation-file-name guile))))))
                         (every (cut member <>
                                     (map (lambda (edge)
                                            (map basename edge))
                                          edges))
                                (list (map (compose basename derivation-file-name)
                                           (list drv guile))
                                      (list (basename (derivation-file-name drv))
                                            (basename txt))))))))))))

(test-assert "reference DAG"
  (let-values (((backend nodes+edges) (make-recording-backend)))
    (run-with-store %store
      (mlet* %store-monad ((txt   (text-file "text-file" "Hello!"))
                           (guile (package->derivation %bootstrap-guile))
                           (drv   (gexp->derivation "output"
                                                    #~(symlink #$txt #$output)
                                                    #:guile-for-build
                                                    guile))
                           (out -> (derivation->output-path drv)))
        ;; We should see only OUT and TXT, with an edge from the former to the
        ;; latter.
        (mbegin %store-monad
          (built-derivations (list drv))
          (export-graph (list (derivation->output-path drv)) 'port
                        #:node-type %reference-node-type
                        #:backend backend)
          (let-values (((nodes edges) (nodes+edges)))
            (return
             (and (equal? (match nodes
                            (((ids labels) ...)
                             ids))
                          (list out txt))
                  (equal? edges `((,out ,txt)))))))))))

(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)))))))))))

(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-assert "node-transitive-edges, references"
  (run-with-store %store
    (mlet* %store-monad ((d0 (package->derivation %bootstrap-guile))
                         (d1 (gexp->derivation "d1"
                                               #~(begin
                                                   (mkdir #$output)
                                                   (symlink #$%bootstrap-guile
                                                            (string-append
                                                             #$output "/l")))))
                         (d2 (gexp->derivation "d2"
                                               #~(begin
                                                   (mkdir #$output)
                                                   (symlink #$d1
                                                            (string-append
                                                             #$output "/l")))))
                         (_  (built-derivations (list d2)))
                         (->node -> (node-type-convert %reference-node-type))
                         (o2      (->node (derivation->output-path d2)))
                         (o1      (->node (derivation->output-path d1)))
                         (o0      (->node (derivation->output-path d0)))
                         (edges   (node-edges %reference-node-type
                                              (append o0 o1 o2)))
                         (reqs    ((store-lift requisites) o2)))
      (return (lset= string=?
                     (append o2 (node-transitive-edges o2 edges)) reqs)))))

(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-equal "shortest-path, packages + derivations"
  '(("p5" "p4" "p1" "p0")
    ("p3" "p2" "p1" "p0")
    #f
    ("p5-0.drv" "p4-0.drv" "p1-0.drv" "p0-0.drv"))
  (run-with-store %store
    (let* ((p0 (dummy-package "p0"))
           (p1 (dummy-package "p1" (inputs `(("p0" ,p0)))))
           (p2 (dummy-package "p2" (inputs `(("p1" ,p1)))))
           (p3 (dummy-package "p3" (inputs `(("p2" ,p2)))))
           (p4 (dummy-package "p4" (inputs `(("p1" ,p1)))))
           (p5 (dummy-package "p5" (inputs `(("p4" ,p4) ("p3" ,p3))))))
      (mlet* %store-monad ((path1 (shortest-path p5 p0 %package-node-type))
                           (path2 (shortest-path p3 p0 %package-node-type))
                           (nope  (shortest-path p3 p4 %package-node-type))
                           (drv5  (package->derivation p5))
                           (drv0  (package->derivation p0))
                           (path3 (shortest-path drv5 drv0
                                                 %derivation-node-type)))
        (return (append (map (lambda (path)
                               (and path (map package-name path)))
                             (list path1 path2 nope))
                        (list (map (node-type-label %derivation-node-type)
                                   path3))))))))

(test-equal "shortest-path, reverse packages"
  '("libffi" "guile" "guile-json")
  (run-with-store %store
    (mlet %store-monad ((path (shortest-path (specification->package "libffi")
                                             guile-json
                                             %reverse-package-node-type)))
      (return (map package-name path)))))

(test-equal "shortest-path, references"
  `(("d2" "d1" ,(package-full-name %bootstrap-guile "-"))
    (,(package-full-name %bootstrap-guile "-") "d1" "d2"))
  (run-with-store %store
    (mlet* %store-monad ((d0 (package->derivation %bootstrap-guile))
                         (d1 (gexp->derivation "d1"
                                               #~(begin
                                                   (mkdir #$output)
                                                   (symlink #$%bootstrap-guile
                                                            (string-append
                                                             #$output "/l")))))
                         (d2 (gexp->derivation "d2"
                                               #~(begin
                                                   (mkdir #$output)
                                                   (symlink #$d1
                                                            (string-append
                                                             #$output "/l")))))
                         (_  (built-derivations (list d2)))
                         (->node -> (node-type-convert %reference-node-type))
                         (o2   (->node (derivation->output-path d2)))
                         (o0   (->node (derivation->output-path d0)))
                         (path (shortest-path (first o2) (first o0)
                                              %reference-node-type))
                         (rpath (shortest-path (first o0) (first o2)
                                               %referrer-node-type)))
      (return (list (map (node-type-label %reference-node-type) path)
                    (map (node-type-label %referrer-node-type) rpath))))))

(test-end "graph")


Mode Type Size Ref File
100644 blob 6139 7f310d2612983845551d0a641b730a269a180a3f .dir-locals.el
100644 blob 2533 e2f745b42a5a1591a93a3e9a7ccfc107a6178eff .gitignore
100644 blob 6179 ee164083c8379d90c6e7ab3b38576266f458f40c .guix-authorizations
100644 blob 182 b852180cf2563ec7b74b93c954de38d77237f23f .guix-channel
100644 blob 4499 146e65184a2f987b0307d0ec7300c93e84110625 .mailmap
100644 blob 472 1e30a74a64f51ec735dcc44ff4dfe5fa4fa13c6f AUTHORS
100644 blob 3273 ef90330cdacb9ecf7dbf38a03cdb490db131a4ad CODE-OF-CONDUCT
100644 blob 35147 94a9ed024d3859793618152ea559a168bbcbb5e2 COPYING
100644 blob 163 d6ea6943261fcae51c095ad39fe59140fc62de22 ChangeLog
100644 blob 749 aaa673fc93b0bb74feca4783ae427b9ea1b604ea HACKING
100644 blob 33109 a75d9c1ffc4237c0478b62a235b9b521fd840517 Makefile.am
100644 blob 359348 bb1de1e93802064ff44392db56d05cd0a11fcc15 NEWS
100644 blob 5260 5e9069f80f58d3946cdd588f30919a177eaccb55 README
100644 blob 3237 2475cb637ceb6eb43f54d080c56e5793041b76e5 ROADMAP
100644 blob 2381 af7afd3576f2e6aa5cbafc3c6354bbab1ae00774 THANKS
100644 blob 4360 f854f7fa98e09c7b512f3efb702c290b615186a0 TODO
100755 blob 906 a47269d87f1d6fd27bbaf634ac7439b38b32cca3 bootstrap
040000 tree - b15b9ede344760715e240528bc322c7b0194bbe7 build-aux
100644 blob 4808 50ead355a81edebf5c9419bd76a1dd69e85f5adf config-daemon.ac
100644 blob 8760 6861112eafaed85e107f8976f12e0ddb795571b7 configure.ac
100644 blob 339545 d234c4ec8668cead20279d903589d29c513b4cb6 d3.v3.js
040000 tree - 3429d151d1aa398d5f16df794830da129964d217 doc
040000 tree - 1b48bfe2c716d39c5f4e7bf962dbc5ad4540b961 etc
100644 blob 5289 f139531ef3ecf56a790ae73934e2d91016c1aba4 gnu.scm
040000 tree - 6580445118b0be8a7dcf78a96a35b76de3bd1eaa gnu
100644 blob 4207 ad8279395d8eb1fe5a836d54ec563a4577f4d135 graph.js
100644 blob 1357 8753c21e423f880e7a6d9f7f6f6ff1139f8b7254 guix.scm
040000 tree - 74d74de00060be23e3d5280d0300a8fe69a58387 guix
040000 tree - 8df9aaabfb400159e2559fd4331fb861cb0a5adc m4
040000 tree - d0ec05821e49fa1536a9c19a33ad13b5ba3ea0c2 nix
040000 tree - 8dac6dd305591d733ef087c35eee3b3acb1daee2 po
040000 tree - 8c4db11917d51c4d71a841813cf8951000b76687 scripts
040000 tree - 867e6957c2eed1c47764e42093bb26ae37b221e3 tests
Hints:
Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://rocketgit.com/user/maav/guix-mirror

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@ssh.rocketgit.com/user/maav/guix-mirror

Clone this repository using git:
git clone git://git.rocketgit.com/user/maav/guix-mirror

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a merge request:
... clone the repository ...
... make some changes and some commits ...
git push origin main