code:
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2015, 2016, 2017, 2018, 2019 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-services)
  #:use-module (gnu services)
  #:use-module (gnu services herd)
  #:use-module (gnu services shepherd)
  #:use-module (srfi srfi-1)
  #:use-module (srfi srfi-26)
  #:use-module (srfi srfi-34)
  #:use-module (srfi srfi-64)
  #:use-module (ice-9 match))

(test-begin "services")

(test-equal "services, default value"
  '(42 123 234 error)
  (let* ((t1 (service-type (name 't1) (extensions '())))
         (t2 (service-type (name 't2) (extensions '())
                           (default-value 42))))
    (list (service-value (service t2))
          (service-value (service t2 123))
          (service-value (service t1 234))
          (guard (c ((missing-value-service-error? c) 'error))
            (service t1)))))

(test-assert "service-back-edges"
  (let* ((t1 (service-type (name 't1) (extensions '())
                           (compose +) (extend *)))
         (t2 (service-type (name 't2)
                           (extensions
                            (list (service-extension t1 (const '()))))
                           (compose +) (extend *)))
         (t3 (service-type (name 't3)
                           (extensions
                            (list (service-extension t2 identity)
                                  (service-extension t1 list)))))
         (s1 (service t1 #t))
         (s2 (service t2 #t))
         (s3 (service t3 #t))
         (e  (service-back-edges (list s1 s2 s3))))
    (and (lset= eq? (e s1) (list s2 s3))
         (lset= eq? (e s2) (list s3))
         (null? (e s3)))))

(test-equal "fold-services"
  ;; Make sure 'fold-services' returns the right result.  The numbers come
  ;; from services of type T3; 'xyz 60' comes from the service of type T2,
  ;; where 60 = 15 × 4 = (1 + 2 + 3 + 4 + 5) × 4.
  '(initial-value 5 4 3 2 1 xyz 60)
  (let* ((t1 (service-type (name 't1) (extensions '())
                           (compose concatenate)
                           (extend cons)))
         (t2 (service-type (name 't2)
                           (extensions
                            (list (service-extension t1
                                                     (cut list 'xyz <>))))
                           (compose (cut reduce + 0 <>))
                           (extend *)))
         (t3 (service-type (name 't3)
                           (extensions
                            (list (service-extension t2 identity)
                                  (service-extension t1 list)))))
         (r  (fold-services (cons* (service t1 'initial-value)
                                   (service t2 4)
                                   (map (lambda (x)
                                          (service t3 x))
                                        (iota 5 1)))
                            #:target-type t1)))
    (and (eq? (service-kind r) t1)
         (service-value r))))

(test-assert "fold-services, ambiguity"
  (let* ((t1 (service-type (name 't1) (extensions '())
                           (compose concatenate)
                           (extend cons)))
         (t2 (service-type (name 't2)
                           (extensions
                            (list (service-extension t1 list)))))
         (s  (service t2 42)))
    (guard (c ((ambiguous-target-service-error? c)
               (and (eq? (ambiguous-target-service-error-target-type c)
                         t1)
                    (eq? (ambiguous-target-service-error-service c)
                         s))))
      (fold-services (list (service t1 'first)
                           (service t1 'second)
                           s)
                     #:target-type t1)
      #f)))

(test-assert "fold-services, missing target"
  (let* ((t1 (service-type (name 't1) (extensions '())))
         (t2 (service-type (name 't2)
                           (extensions
                            (list (service-extension t1 list)))))
         (s  (service t2 42)))
    (guard (c ((missing-target-service-error? c)
               (and (eq? (missing-target-service-error-target-type c)
                         t1)
                    (eq? (missing-target-service-error-service c)
                         s))))
      (fold-services (list s) #:target-type t1)
      #f)))

(test-assert "instantiate-missing-services"
  (let* ((t1 (service-type (name 't1) (extensions '())
                           (default-value 'dflt)
                           (compose concatenate)
                           (extend cons)))
         (t2 (service-type (name 't2)
                           (extensions
                            (list (service-extension t1 list)))))
         (s1 (service t1 'hey!))
         (s2 (service t2 42)))
    (and (lset= equal?
                (list (service t1) s2)
                (instantiate-missing-services (list s2)))
         (equal? (list s1 s2)
                 (instantiate-missing-services (list s1 s2))))))

(test-assert "instantiate-missing-services, indirect"
  (let* ((t1 (service-type (name 't1) (extensions '())
                           (default-value 'dflt)
                           (compose concatenate)
                           (extend cons)))
         (t2 (service-type (name 't2)
                           (default-value 'dflt2)
                           (compose concatenate)
                           (extend cons)
                           (extensions
                            (list (service-extension t1 list)))))
         (t3 (service-type (name 't3)
                           (extensions
                            (list (service-extension t2 list)))))
         (s1 (service t1))
         (s2 (service t2))
         (s3 (service t3 42))
         (== (cut lset= equal? <...>)))
    (and (== (list s1 s2 s3)
             (instantiate-missing-services (list s3)))
         (== (list s1 s2 s3)
             (instantiate-missing-services (list s1 s3)))
         (== (list s1 s2 s3)
             (instantiate-missing-services (list s2 s3))))))

(test-assert "instantiate-missing-services, no default value"
  (let* ((t1 (service-type (name 't1) (extensions '())))
         (t2 (service-type (name 't2)
                           (extensions
                            (list (service-extension t1 list)))))
         (s  (service t2 42)))
    (guard (c ((missing-target-service-error? c)
               (and (eq? (missing-target-service-error-target-type c)
                         t1)
                    (eq? (missing-target-service-error-service c)
                         s))))
      (instantiate-missing-services (list s))
      #f)))

(test-assert "shepherd-service-lookup-procedure"
  (let* ((s1 (shepherd-service (provision '(s1 s1b)) (start #f)))
         (s2 (shepherd-service (provision '(s2 s2b)) (start #f)))
         (s3 (shepherd-service (provision '(s3 s3b s3c)) (start #f)))
         (lookup (shepherd-service-lookup-procedure (list s1 s2 s3))))
    (and (eq? (lookup 's1) (lookup 's1b) s1)
         (eq? (lookup 's2) (lookup 's2b) s2)
         (eq? (lookup 's3) (lookup 's3b) s3))))

(test-assert "shepherd-service-back-edges"
  (let* ((s1 (shepherd-service (provision '(s1)) (start #f)))
         (s2 (shepherd-service (provision '(s2))
                               (requirement '(s1))
                               (start #f)))
         (s3 (shepherd-service (provision '(s3))
                               (requirement '(s1 s2))
                               (start #f)))
         (e  (shepherd-service-back-edges (list s1 s2 s3))))
    (and (lset= eq? (e s1) (list s2 s3))
         (lset= eq? (e s2) (list s3))
         (null? (e s3)))))

(test-equal "shepherd-service-upgrade: nothing to do"
  '(() ())
  (call-with-values
      (lambda ()
        (shepherd-service-upgrade '() '()))
    list))

(test-equal "shepherd-service-upgrade: one unchanged, one upgraded, one new"
  '(()                                            ;unload
    ((foo)))                                      ;restart
  (call-with-values
      (lambda ()
        ;; Here 'foo' is replaced and must be explicitly restarted later
        ;; because it is still running, whereas 'bar' is upgraded right away
        ;; because it is not currently running.  'baz' is loaded because it's
        ;; a new service.
        (shepherd-service-upgrade
         (list (live-service '(foo) '() #t)
               (live-service '(bar) '() #f)
               (live-service '(root) '() #t))     ;essential!
         (list (shepherd-service (provision '(foo))
                                 (start #t))
               (shepherd-service (provision '(bar))
                                 (start #t))
               (shepherd-service (provision '(baz))
                                 (start #t)))))
    (lambda (unload restart)
      (list (map live-service-provision unload)
            (map shepherd-service-provision restart)))))

(test-equal "shepherd-service-upgrade: service depended on is not unloaded"
  '(((baz))                                       ;unload
    ((foo)))                                      ;restart
  (call-with-values
      (lambda ()
        ;; Service 'bar' is not among the target services; yet, it must not be
        ;; unloaded because 'foo' depends on it.  'foo' gets replaced but it
        ;; must be restarted manually.
        (shepherd-service-upgrade
         (list (live-service '(foo) '(bar) #t)
               (live-service '(bar) '() #t)       ;still used!
               (live-service '(baz) '() #t))
         (list (shepherd-service (provision '(foo))
                                 (start #t)))))
    (lambda (unload restart)
      (list (map live-service-provision unload)
            (map shepherd-service-provision restart)))))

(test-equal "shepherd-service-upgrade: obsolete services that depend on each other"
  '(((foo) (bar) (baz))                           ;unload
    ())                                           ;restart
  (call-with-values
      (lambda ()
        ;; 'foo', 'bar', and 'baz' depend on each other, but all of them are
        ;; obsolete, and thus should be unloaded.
        (shepherd-service-upgrade
         (list (live-service '(foo) '(bar) #t)    ;obsolete
               (live-service '(bar) '(baz) #t)    ;obsolete
               (live-service '(baz) '() #t))      ;obsolete
         (list (shepherd-service (provision '(qux))
                                 (start #t)))))
    (lambda (unload restart)
      (list (map live-service-provision unload)
            (map shepherd-service-provision restart)))))

(test-eq "lookup-service-types"
  system-service-type
  (and (null? (lookup-service-types 'does-not-exist-at-all))
       (match (lookup-service-types 'system)
         ((one) one)
         (x x))))

(test-end)


code:
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2021 Maxim Cournoyer <maxim.cournoyer@gmail.com>
;;; Copyright © 2021 Xinglu Chen <public@yoctocell.xyz>
;;;
;;; 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 (tests services configuration)
  #:use-module (gnu services configuration)
  #:use-module (guix gexp)
  #:use-module (srfi srfi-34)
  #:use-module (srfi srfi-64))

;;; Tests for the (gnu services configuration) module.

(test-begin "services-configuration")

;;;
;;; define-configuration macro.
;;;

(define-configuration port-configuration
  (port (number 80) "The port number.")
  (no-serialization))

(test-equal "default value, no serialization"
  80
  (port-configuration-port (port-configuration)))

(define-configuration port-configuration-cs
  (port (number 80) "The port number." empty-serializer))

(test-equal "default value, custom serializer"
  80
  (port-configuration-cs-port (port-configuration-cs)))

(define serialize-number "")
(define-configuration port-configuration-ndv
  (port (number) "The port number."))

(test-equal "no default value, provided"
  55
  (port-configuration-ndv-port (port-configuration-ndv
                                (port 55))))

(test-assert "no default value, not provided"
  (guard (c ((configuration-error? c)
             #t))
    (port-configuration-ndv-port (port-configuration-ndv))))

(define (custom-number-serializer name value)
  (format #f "~a = ~a;" name value))

(define-configuration serializable-configuration
  (port (number 80) "The port number." custom-number-serializer))

(test-assert "serialize-configuration"
  (gexp?
   (let ((config (serializable-configuration)))
     (serialize-configuration config serializable-configuration-fields))))

(define-configuration serializable-configuration
  (port (number 80) "The port number." custom-number-serializer)
  (no-serialization))

(test-assert "serialize-configuration with no-serialization"
  ;; When serialization is disabled, the serializer is set to #f, so
  ;; attempting to use it fails with a 'wrong-type-arg' error.
  (not (false-if-exception
        (let ((config (serializable-configuration)))
          (serialize-configuration config serializable-configuration-fields)))))

(define (custom-prefix-serialize-integer field-name name) name)

(define-configuration configuration-with-prefix
  (port (integer 10) "The port number.")
  (prefix custom-prefix-))

(test-assert "serialize-configuration with prefix"
  (gexp?
   (let ((config (configuration-with-prefix)))
     (serialize-configuration config configuration-with-prefix-fields))))

;;;
;;; define-maybe macro.
;;;
(define-maybe number)

(define-configuration config-with-maybe-number
  (port (maybe-number 80) "The port number."))

(define (serialize-number field value)
  (format #f "~a=~a" field value))

(test-equal "maybe value serialization"
  "port=80"
  (serialize-maybe-number "port" 80))

(define-maybe/no-serialization string)

(define-configuration config-with-maybe-string/no-serialization
  (name (maybe-string) "The name of the item.")
  (no-serialization))

(test-assert "maybe value without serialization no procedure bound"
  (not (defined? 'serialize-maybe-string)))

repo_name: maximed/guix

license: mit


code:
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2020 Simon South <simon@simonsouth.net>
;;;
;;; 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 (tests services file-sharing)
  #:use-module (gnu services file-sharing)
  #:use-module (srfi srfi-64))

;;; Tests for the (gnu services file-sharing) module.

(test-begin "file-sharing")

;;;
;;; Transmission Daemon.
;;;

(define %transmission-salt-length 8)

(define (valid-transmission-salt? salt)
    (and (string? salt)
         (eqv? (string-length salt) %transmission-salt-length)))

(test-assert "transmission-random-salt"
  (valid-transmission-salt? (transmission-random-salt)))

(test-equal "transmission-password-hash, typical values"
  "{ef6fba106cdef3aac64d1410090cae353cbecde53ceVVQO2"
  (transmission-password-hash "transmission" "3ceVVQO2"))

(test-equal "transmission-password-hash, empty password"
  "{820f816515d8969d058d07a1de018650619ee7ffCp.I5SWg"
  (transmission-password-hash "" "Cp.I5SWg"))

(test-error "transmission-password-hash, salt value too short"
            (transmission-password-hash
             "transmission"
             (make-string (- %transmission-salt-length 1) #\a)))

(test-error "transmission-password-hash, salt value too long"
            (transmission-password-hash
             "transmission"
             (make-string (+ %transmission-salt-length 1) #\a)))

(test-end "file-sharing")


code:
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2020 Maxim Cournoyer <maxim.cournoyer@gmail.com>
;;; Copyright © 2020 Efraim Flashner <efraim@flashner.co.il>
;;;
;;; 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 (tests services linux)
  #:use-module (ice-9 match)
  #:use-module (gnu packages linux)
  #:use-module (gnu services linux)
  #:use-module (guix gexp)
  #:use-module (srfi srfi-64))

;;; Tests for the (gnu services linux) module.

(test-begin "linux-services")

;;;
;;; Early OOM daemon.
;;;

(define earlyoom-configuration->command-line-args
  (@@ (gnu services linux) earlyoom-configuration->command-line-args))

(define %earlyoom-configuration-sample
  (earlyoom-configuration
   (minimum-available-memory 10)
   (minimum-free-swap 20)
   (prefer-regexp "icecat")
   (avoid-regexp "guix-daemon")
   (memory-report-interval 60)
   (ignore-positive-oom-score-adj? #f)
   (run-with-higher-priority? #t)
   (show-debug-messages? #f)
   (send-notification-command "python \"/some/path/notify-all-users.py\"")))

(test-equal "earlyoom-configuration->command-line-args"
  (list (file-append earlyoom "/bin/earlyoom")
        "-m" "10" "-s" "20" "--prefer" "icecat"
        "--avoid" "guix-daemon" "-r" "60" "-p"
        "-N" "python \"/some/path/notify-all-users.py\"")
  (earlyoom-configuration->command-line-args %earlyoom-configuration-sample))

;;;
;;; Zram swap device.
;;;

(define zram-device-configuration->udev-string
  (@@ (gnu services linux) zram-device-configuration->udev-string))

(define %zram-swap-device-test-1
  (zram-device-configuration
    (size "2G")
    (compression-algorithm 'zstd)
    (memory-limit "1G")
    (priority 42)))

(test-equal "zram-swap-device-test-1"
  "KERNEL==\"zram0\", ATTR{comp_algorithm}=\"zstd\" ATTR{disksize}=\"2G\" ATTR{mem_limit}=\"1G\" RUN+=\"/run/current-system/profile/sbin/mkswap /dev/zram0\" RUN+=\"/run/current-system/profile/sbin/swapon --priority 42 /dev/zram0\"\n"
  (zram-device-configuration->udev-string %zram-swap-device-test-1))

(define %zram-swap-device-test-2
  (zram-device-configuration
    (size 1048576)  ; 1M
    (compression-algorithm 'lz4)))

(test-equal "zram-swap-device-test-2"
  "KERNEL==\"zram0\", ATTR{comp_algorithm}=\"lz4\" ATTR{disksize}=\"1048576\" RUN+=\"/run/current-system/profile/sbin/mkswap /dev/zram0\" RUN+=\"/run/current-system/profile/sbin/swapon /dev/zram0\"\n"
  (zram-device-configuration->udev-string %zram-swap-device-test-2))

(define %zram-swap-device-test-3
  (zram-device-configuration
    (memory-limit (* 512 1000))))

(test-equal "zram-swap-device-test-3"
  "KERNEL==\"zram0\", ATTR{comp_algorithm}=\"lzo\" ATTR{disksize}=\"1G\" ATTR{mem_limit}=\"512000\" RUN+=\"/run/current-system/profile/sbin/mkswap /dev/zram0\" RUN+=\"/run/current-system/profile/sbin/swapon /dev/zram0\"\n"
  (zram-device-configuration->udev-string %zram-swap-device-test-3))

(test-end "linux-services")


code:
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2015 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-sets)
  #:use-module (guix sets)
  #:use-module (srfi srfi-1)
  #:use-module (srfi srfi-26)
  #:use-module (srfi srfi-64))

(test-begin "sets")

(test-assert "set-contains?"
  (let* ((lst (iota 123))
         (set (list->set lst)))
    (and (every (cut set-contains? set <>)
                lst)
         (not (set-contains? set -1)))))

(test-assert "set->list"
  (let* ((lst (iota 123))
         (set (list->set lst)))
    (lset= = lst (set->list set))))

(test-assert "set-union"
  (let* ((a  (list 'a))
         (b  (list 'b))
         (s1 (setq a))
         (s2 (setq b))
         (s3 (set-union s1 s2)))
    (and (set-contains? s3 a)
         (set-contains? s3 b))))

(test-end)


code:
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2015, 2016, 2017 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-size)
  #:use-module (guix store)
  #:use-module (guix monads)
  #:use-module (guix packages)
  #:use-module (guix derivations)
  #:use-module (guix gexp)
  #:use-module (guix tests)
  #:use-module (guix scripts size)
  #:use-module (gnu packages)
  #:use-module (gnu packages bootstrap)
  #:use-module (ice-9 match)
  #:use-module (srfi srfi-1)
  #:use-module (srfi srfi-64))

(test-begin "size")

(test-assertm "store-profile"
  (mlet* %store-monad ((file1 (gexp->derivation "file1"
                                                #~(symlink #$%bootstrap-guile
                                                           #$output)))
                       (file2 (text-file* "file2"
                                          "the file => " file1)))
    (define (matching-profile item)
      (lambda (profile)
        (string=? item (profile-file profile))))

    (mbegin %store-monad
      (built-derivations (list file2))
      (mlet %store-monad ((profiles (store-profile
                                     (list (derivation->output-path file2))))
                          (bash     (interned-file
                                     (search-bootstrap-binary
                                      "bash" (%current-system)) "bash"
                                      #:recursive? #t))
                          (guile    (package->derivation %bootstrap-guile)))
        (define (lookup-profile item)
          (find (matching-profile (if (derivation? item)
                                      (derivation->output-path item)
                                      item))
                profiles))

        (letrec-syntax ((match* (syntax-rules (=>)
                                  ((_ ((drv => profile) rest ...) body)
                                   (match (lookup-profile drv)
                                     ((? profile? profile)
                                      (match* (rest ...) body))))
                                  ((_ () body)
                                   body))))
          ;; Make sure we get all three profiles with sensible values.
          (return (and (= (length profiles) 4)
                       (match* ((file1 => profile1)
                                (file2 => profile2)
                                (guile => profile3)
                                (bash  => profile4)) ;dependency of GUILE
                         (and (> (profile-closure-size profile2) 0)
                              (= (profile-closure-size profile2)
                                 (+ (profile-self-size profile1)
                                    (profile-self-size profile2)
                                    (profile-self-size profile3)
                                    (profile-self-size profile4))))))))))))

(test-assertm "store-profile with multiple items"
  (mlet* %store-monad ((file1 (gexp->derivation "file1"
                                                #~(symlink #$%bootstrap-guile
                                                           #$output)))
                       (file2 (text-file* "file2"
                                          "the file => " file1)))
    (mbegin %store-monad
      (built-derivations (list file2))
      (mlet %store-monad ((profiles  (store-profile
                                      (list (derivation->output-path file2)
                                            (derivation->output-path file1))))
                          (reference (store-profile
                                      (list (derivation->output-path file2)))))
        (return (and (= (length profiles) 4)
                     (lset= equal? profiles reference)))))))

(test-end "size")

;;; Local Variables:
;;; eval: (put 'match* 'scheme-indent-function 1)
;;; End:


code:
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2018, 2019 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-status)
  #:use-module (guix status)
  #:use-module (srfi srfi-1)
  #:use-module (srfi srfi-11)
  #:use-module (srfi srfi-64)
  #:use-module (rnrs bytevectors)
  #:use-module (rnrs io ports)
  #:use-module (ice-9 match))

(test-begin "status")

(test-equal "compute-status, no-op"
  (build-status)
  (let-values (((port get-status)
                (build-event-output-port compute-status)))
    (display "foo\nbar\n\baz\n" port)
    (get-status)))

(test-equal "compute-status, builds + substitutes"
  (list (build-status
         (building (list (build "foo.drv" "x86_64-linux")))
         (downloading (list (download "bar" "http://example.org/bar"
                                      #:size 500
                                      #:start 'now))))
        (build-status
         (building (list (build "foo.drv" "x86_64-linux")))
         (downloading (list (download "bar" "http://example.org/bar"
                                      #:size 500
                                      #:transferred 42
                                      #:start 'now))))
        (build-status
         (builds-completed (list (build "foo.drv" "x86_64-linux")))
         (downloads-completed (list (download "bar" "http://example.org/bar"
                                              #:size 500
                                              #:transferred 500
                                              #:start 'now
                                              #:end 'now)))))
  (let-values (((port get-status)
                (build-event-output-port (lambda (event status)
                                           (compute-status event status
                                                           #:current-time
                                                           (const 'now))))))
    (display "@ build-started foo.drv - x86_64-linux \n" port)
    (display "@ substituter-started bar\n" port)
    (display "@ download-started bar http://example.org/bar 500\n" port)
    (display "various\nthings\nget\nwritten\n" port)
    (let ((first (get-status)))
      (display "@ download-progress bar http://example.org/bar 500 42\n"
               port)
      (let ((second (get-status)))
        (display "@ download-progress bar http://example.org/bar 500 84\n"
                 port)
        (display "@ build-succeeded foo.drv\n" port)
        (display "@ download-succeeded bar http://example.org/bar 500\n" port)
        (display "Almost done!\n" port)
        (display "@ substituter-succeeded bar\n" port)
        (list first second (get-status))))))

(test-equal "compute-status, missing events"
  (list (build-status
         (building (list (build "foo.drv" "x86_64-linux"
                                #:log-file "foo.log")))
         (downloading (list (download "baz" "http://example.org/baz"
                                      #:size 500
                                      #:transferred 42
                                      #:start 'now)
                            (download "bar" "http://example.org/bar"
                                      #:size 999
                                      #:transferred 0
                                      #:start 'now))))
        (build-status
         (builds-completed (list (build "foo.drv" "x86_64-linux"
                                        #:log-file "foo.log")))
         (downloads-completed (list (download "baz" "http://example.org/baz"
                                              #:size 500
                                              #:transferred 500
                                              #:start 'now
                                              #:end 'now)
                                    (download "bar" "http://example.org/bar"
                                              #:size 999
                                              #:transferred 999
                                              #:start 'now
                                              #:end 'now)))))
  ;; Below we omit 'substituter-started' events and the like.
  (let-values (((port get-status)
                (build-event-output-port (lambda (event status)
                                           (compute-status event status
                                                           #:current-time
                                                           (const 'now))))))
    (display "@ build-started foo.drv - x86_64-linux foo.log\n" port)
    (display "@ download-started bar http://example.org/bar 999\n" port)
    (display "various\nthings\nget\nwritten\n" port)
    (display "@ download-progress baz http://example.org/baz 500 42\n"
             port)
    (let ((first (get-status)))
      (display "@ build-succeeded foo.drv\n" port)
      (display "@ download-succeeded bar http://example.org/bar 999\n" port)
      (display "Almost done!\n" port)
      (display "@ substituter-succeeded baz\n" port)
      (list first (get-status)))))

(test-equal "build-output-port, UTF-8"
  '((build-log #f "lambda is λ!\n"))
  (let-values (((port get-status) (build-event-output-port cons '()))
               ((bv)              (string->utf8 "lambda is λ!\n")))
    (put-bytevector port bv)
    (force-output port)
    (get-status)))

(test-equal "current-build-output-port, UTF-8 + garbage"
  ;; What about a mixture of UTF-8 + garbage?
  (let ((replacement "�"))
    `((build-log #f ,(string-append "garbage: " replacement "lambda: λ\n"))))
  (let-values (((port get-status) (build-event-output-port cons '())))
    (display "garbage: " port)
    (put-bytevector port #vu8(128))
    (put-bytevector port (string->utf8 "lambda: λ\n"))
    (force-output port)
    (get-status)))

(test-equal "compute-status, multiplexed build output"
  (list (build-status
         (building (list (build "foo.drv" "x86_64-linux" #:id 121)))
         (downloading (list (download "bar" "http://example.org/bar"
                                      #:size 999
                                      #:start 'now))))
        (build-status
         (building (list (build "foo.drv" "x86_64-linux" #:id 121)))
         (downloading (list (download "bar" "http://example.org/bar"
                                      #:size 999
                                      #:transferred 42
                                      #:start 'now))))
        (build-status
         ;; "bar" is now only listed as a download.
         (builds-completed (list (build "foo.drv" "x86_64-linux" #:id 121)))
         (downloads-completed (list (download "bar" "http://example.org/bar"
                                              #:size 999
                                              #:transferred 999
                                              #:start 'now
                                              #:end 'now)))))
  (let-values (((port get-status)
                (build-event-output-port (lambda (event status)
                                           (compute-status event status
                                                           #:current-time
                                                           (const 'now)
                                                           #:derivation-path->output-path
                                                           (match-lambda
                                                             ("bar.drv" "bar")))))))
    (display "@ build-started foo.drv - x86_64-linux  121\n" port)
    (display "@ build-started bar.drv - armhf-linux bar.log 144\n" port)
    (display "@ build-log 121 6\nHello!" port)
    (display "@ build-log 144 50
@ download-started bar http://example.org/bar 999\n" port)
    (let ((first (get-status)))
      (display "@ build-log 121 30\n@ build-started FAKE!.drv 555\n")
      (display "@ build-log 144 54
@ download-progress bar http://example.org/bar 999 42\n"
               port)
      (let ((second (get-status)))
        (display "@ download-succeeded bar http://example.org/bar 999\n" port)
        (display "@ build-succeeded foo.drv\n" port)
        (display "@ build-succeeded bar.drv\n" port)
        (list first second (get-status))))))

(test-equal "compute-status, build completion"
  (list (build-status
         (building (list (build "foo.drv" "x86_64-linux" #:id 121))))
        (build-status
         (building (list (build "foo.drv" "x86_64-linux" #:id 121
                                #:completion 0.))))
        (build-status
         (building (list (build "foo.drv" "x86_64-linux" #:id 121
                                #:completion 50.))))
        (build-status
         (builds-completed (list (build "foo.drv" "x86_64-linux" #:id 121
                                        #:completion 100.)))))
  (let-values (((port get-status)
                (build-event-output-port (lambda (event status)
                                           (compute-status event status
                                                           #:current-time
                                                           (const 'now))))))
    (display "@ build-started foo.drv - x86_64-linux  121\n" port)
    (display "@ build-log 121 6\nHello!" port)
    (let ((first (get-status)))
      (display "@ build-log 121 20\n[ 0/100] building X\n" port)
      (display "@ build-log 121 6\nHello!" port)
      (let ((second (get-status)))
        (display "@ build-log 121 20\n[50/100] building Y\n" port)
        (display "@ build-log 121 6\nHello!" port)
        (let ((third (get-status)))
          (display "@ build-log 121 21\n[100/100] building Z\n" port)
          (display "@ build-log 121 6\nHello!" port)
          (display "@ build-succeeded foo.drv\n" port)
          (list first second third (get-status)))))))

(test-equal "compute-status, build phase"
  (list (build-status
         (building (list (build "foo.drv" "x86_64-linux" #:id 121
                                #:phase 'configure))))
        (build-status
         (building (list (build "foo.drv" "x86_64-linux" #:id 121
                                #:phase 'configure
                                #:completion 50.))))
        (build-status
         (building (list (build "foo.drv" "x86_64-linux" #:id 121
                                #:phase 'install))))
        (build-status
         (builds-completed (list (build "foo.drv" "x86_64-linux" #:id 121
                                        #:phase 'install)))))
  (let-values (((port get-status)
                (build-event-output-port (lambda (event status)
                                           (compute-status event status
                                                           #:current-time
                                                           (const 'now))))))
    (display "@ build-started foo.drv - x86_64-linux  121\n" port)
    (display "@ build-log 121 27\nstarting phase `configure'\n" port)
    (display "@ build-log 121 6\nabcde!" port)
    (let ((first (get-status)))
      (display "@ build-log 121 20\n[50/100] building Y\n" port)
      (display "@ build-log 121 6\nfghik!" port)
      (let ((second (get-status)))
        (display "@ build-log 121 21\n[100/100] building Z\n" port)
        (display "@ build-log 121 25\nstarting phase `install'\n" port)
        (display "@ build-log 121 6\nlmnop!" port)
        (let ((third (get-status)))
          (display "@ build-succeeded foo.drv\n" port)
          (list first second third (get-status)))))))

(test-end "status")


code:
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2017, 2018, 2020, 2021 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-store-database)
  #:use-module (guix tests)
  #:use-module (guix store)
  #:use-module (guix store database)
  #:use-module (guix build store-copy)
  #:use-module ((guix utils) #:select (call-with-temporary-output-file))
  #:use-module ((guix build utils)
                #:select (mkdir-p delete-file-recursively))
  #:use-module (srfi srfi-26)
  #:use-module (srfi srfi-64))

;; Test the (guix store database) module.

(define %store
  (open-connection-for-tests))

(test-begin "store-database")

(test-assert "register-items"
  (let ((file (string-append (%store-prefix) "/" (make-string 32 #\f)
                             "-fake")))
    (when (valid-path? %store file)
      (delete-paths %store (list file)))
    (false-if-exception (delete-file file))

    (let ((ref (add-text-to-store %store "ref-of-fake" (random-text)))
          (drv (string-append file ".drv")))
      (call-with-output-file file
        (cut display "This is a fake store item.\n" <>))
      (reset-timestamps file)
      (with-database (store-database-file) db
        (register-items db (list (store-info file drv (list ref)))))

      (and (valid-path? %store file)
           (equal? (references %store file) (list ref))
           (null? (valid-derivers %store file))
           (null? (referrers %store file))
           (list (stat:mtime (lstat file))
                 (stat:mtime (lstat ref)))))))

(test-equal "register-items, directory"
  '(1 1 1)
  (let ((file (string-append (%store-prefix) "/" (make-string 32 #\f)
                             "-fake-directory")))
    (when (valid-path? %store file)
      (delete-paths %store (list file)))
    (false-if-exception (delete-file-recursively file))

    (let ((drv (string-append file ".drv")))
      (mkdir-p (string-append file "/a"))
      (call-with-output-file (string-append file "/a/b")
        (const #t))
      (reset-timestamps file)
      (with-database (store-database-file) db
        (register-items db (list (store-info file drv '()))))

      (and (valid-path? %store file)
           (null? (references %store file))
           (null? (valid-derivers %store file))
           (null? (referrers %store file))
           (list (stat:mtime (lstat file))
                 (stat:mtime (lstat (string-append file "/a")))
                 (stat:mtime (lstat (string-append file "/a/b"))))))))

(test-equal "new database"
  (list 1 2)
  (call-with-temporary-output-file
   (lambda (db-file port)
     (delete-file db-file)
     (with-database db-file db
       (sqlite-register db
                        #:path "/gnu/foo"
                        #:references '()
                        #:deriver "/gnu/foo.drv"
                        #:hash (string-append "sha256:" (make-string 64 #\e))
                        #:nar-size 1234)
       (sqlite-register db
                        #:path "/gnu/bar"
                        #:references '("/gnu/foo")
                        #:deriver "/gnu/bar.drv"
                        #:hash (string-append "sha256:" (make-string 64 #\a))
                        #:nar-size 4321)
       (let ((path-id (@@ (guix store database) path-id)))
         (list (path-id db "/gnu/foo")
               (path-id db "/gnu/bar")))))))

(test-assert "sqlite-register with unregistered references"
  ;; Make sure we get a "NOT NULL constraint failed: Refs.reference" error
  ;; when we try to add references that are not registered yet.  Better safe
  ;; than sorry.
  (call-with-temporary-output-file
   (lambda (db-file port)
     (delete-file db-file)
     (catch 'sqlite-error
       (lambda ()
         (with-database db-file db
           (sqlite-register db #:path "/gnu/foo"
                            #:references '("/gnu/bar")
                            #:deriver "/gnu/foo.drv"
                            #:hash (string-append "sha256:" (make-string 64 #\e))
                            #:nar-size 1234))
         #f)
       (lambda args
         (pk 'welcome-exception! args)
         #t)))))

(test-equal "sqlite-register with incorrect size"
  'out-of-range
  (call-with-temporary-output-file
   (lambda (db-file port)
     (delete-file db-file)
     (catch #t
       (lambda ()
         (with-database db-file db
           (sqlite-register db #:path "/gnu/foo"
                            #:references '("/gnu/bar")
                            #:deriver "/gnu/foo.drv"
                            #:hash (string-append "sha256:" (make-string 64 #\e))
                            #:nar-size -1234))
         #f)
       (lambda (key . _)
         key)))))

(test-end "store-database")


code:
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2018, 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-store-deduplication)
  #:use-module (guix tests)
  #:use-module (guix store deduplication)
  #:use-module (gcrypt hash)
  #:use-module ((guix utils) #:select (call-with-temporary-directory))
  #:use-module (guix build utils)
  #:use-module (rnrs bytevectors)
  #:use-module (ice-9 binary-ports)
  #:use-module (srfi srfi-1)
  #:use-module (srfi srfi-26)
  #:use-module (srfi srfi-64))

(test-begin "store-deduplication")

(test-equal "deduplicate"
  (cons* #t #f                                    ;inode comparisons
         2 (make-list 5 6))                       ;'nlink' values

  (call-with-temporary-directory
   (lambda (store)
     (let ((data      (string->utf8 "Hello, world!"))
           (identical (map (lambda (n)
                             (string-append store "/" (number->string n)
                                            "/a/b/c"))
                           (iota 5)))
           (unique    (string-append store "/unique")))
       (for-each (lambda (file)
                   (mkdir-p (dirname file))
                   (call-with-output-file file
                     (lambda (port)
                       (put-bytevector port data))))
                 identical)
       ;; Make the parent of IDENTICAL read-only.  This should not prevent
       ;; deduplication from inserting its hard link.
       (chmod (dirname (second identical)) #o544)

       (call-with-output-file unique
         (lambda (port)
           (put-bytevector port (string->utf8 "This is unique."))))

       (deduplicate store (nar-sha256 store) #:store store)

       ;; (system (string-append "ls -lRia " store))
       (cons* (apply = (map (compose stat:ino stat) identical))
              (= (stat:ino (stat unique))
                 (stat:ino (stat (car identical))))
              (stat:nlink (stat unique))
              (map (compose stat:nlink stat) identical))))))

(test-equal "deduplicate, ENOSPC"
  (cons* #f                                       ;inode comparison
         (append (make-list 3 4)
                 (make-list 7 1)))                ;'nlink' values

  ;; In this scenario the first 3 files are properly deduplicated and then we
  ;; simulate a full '.links' directory where link(2) gets ENOSPC, thereby
  ;; preventing deduplication of the subsequent files.
  (call-with-temporary-directory
   (lambda (store)
     (let ((true-link link)
           (links     0)
           (data1     (string->utf8 "Hello, world!"))
           (data2     (string->utf8 "Hi, world!"))
           (identical (map (lambda (n)
                             (string-append store "/" (number->string n)
                                            "/a/b/c"))
                           (iota 10)))
           (populate  (lambda (data)
                        (lambda (file)
                          (mkdir-p (dirname file))
                          (call-with-output-file file
                            (lambda (port)
                              (put-bytevector port data)))))))
       (for-each (populate data1) (take identical 5))
       (for-each (populate data2) (drop identical 5))
       (dynamic-wind
         (lambda ()
           (set! link (lambda (old new)
                        (set! links (+ links 1))
                        (if (<= links 4)
                            (true-link old new)
                            (throw 'system-error "link" "~A" '("Whaaat?!")
                                   (list ENOSPC))))))
         (lambda ()
           (deduplicate store (nar-sha256 store) #:store store))
         (lambda ()
           (set! link true-link)))

       (cons (apply = (map (compose stat:ino stat) identical))
             (map (compose stat:nlink stat) identical))))))

(test-assert "copy-file/deduplicate"
  (call-with-temporary-directory
   (lambda (store)
     (let ((source (search-path %load-path "gnu/packages/emacs-xyz.scm")))
       (for-each (lambda (target)
                   (copy-file/deduplicate source
                                          (string-append store target)
                                          #:store store))
                 '("/a" "/b" "/c"))
       (and (directory-exists? (string-append store "/.links"))
            (file=? source (string-append store "/a"))
            (apply = (map (compose stat:ino stat
                                   (cut string-append store <>))
                          '("/a" "/b" "/c"))))))))

(test-end "store-deduplication")


code:
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2019 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-store-deduplication)
  #:use-module (guix tests)
  #:use-module (guix store)
  #:use-module (guix store roots)
  #:use-module ((guix utils) #:select (call-with-temporary-directory))
  #:use-module (srfi srfi-1)
  #:use-module (srfi srfi-64))

(define %store
  (open-connection))

(test-begin "store-roots")

(test-assert "gc-roots, regular root"
  (let* ((item (add-text-to-store %store "something"
                                  (random-text)))
         (root (string-append %gc-roots-directory "/test-gc-root")))
    (symlink item root)
    (let ((result (member root (gc-roots))))
      (delete-file root)
      result)))

(test-assert "gc-roots, indirect root"
  (call-with-temporary-directory
   (lambda (directory)
     (let* ((item (add-text-to-store %store "something"
                                     (random-text)))
            (root (string-append directory "/gc-root")))
       (symlink item root)
       (add-indirect-root %store root)
       (let ((result (member root (gc-roots))))
         (delete-file root)
         result)))))

(test-end "store-roots")