gnu: emacs-org: Update to 9.4.
[jackhill/guix/guix.git] / guix / tests.scm
index 3cb4a67..3ccf049 100644 (file)
@@ -1,5 +1,5 @@
 ;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2013, 2014, 2015, 2016 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
 ;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
 
 (define-module (guix tests)
+  #:use-module ((guix config) #:select (%storedir %localstatedir))
   #:use-module (guix store)
   #:use-module (guix derivations)
   #:use-module (guix packages)
   #:use-module (guix base32)
   #:use-module (guix serialization)
-  #:use-module (guix hash)
+  #:use-module (guix monads)
+  #:use-module ((guix utils) #:select (substitute-keyword-arguments))
+  #:use-module ((guix build utils) #:select (mkdir-p))
+  #:use-module ((gcrypt hash) #:hide (sha256))
   #:use-module (guix build-system gnu)
+  #:use-module (gnu packages base)
   #:use-module (gnu packages bootstrap)
+  #:use-module (srfi srfi-26)
   #:use-module (srfi srfi-34)
+  #:use-module (srfi srfi-64)
   #:use-module (rnrs bytevectors)
-  #:use-module (rnrs io ports)
+  #:use-module (ice-9 match)
+  #:use-module (ice-9 binary-ports)
   #:use-module (web uri)
   #:export (open-connection-for-tests
+            with-external-store
+            %seed
             random-text
             random-bytevector
             file=?
+            canonical-file?
             network-reachable?
             shebang-too-long?
+            with-environment-variable
+
+            search-bootstrap-binary
+
             mock
             %test-substitute-urls
+            test-assertm
+            test-equalm
             %substitute-directory
             with-derivation-narinfo
             with-derivation-substitute
             dummy-package
-            dummy-origin))
+            dummy-origin
+
+            gnu-make-for-tests))
 
 ;;; Commentary:
 ;;;
    (or (and=> (getenv "GUIX_BINARY_SUBSTITUTE_URL") list)
        '())))
 
-(define* (open-connection-for-tests #:optional (file (%daemon-socket-file)))
+(define* (open-connection-for-tests #:optional (uri (%daemon-socket-uri)))
   "Open a connection to the build daemon for tests purposes and return it."
-  (guard (c ((nix-error? c)
+  (guard (c ((store-error? c)
              (format (current-error-port)
                      "warning: build daemon error: ~s~%" c)
              #f))
-    (let ((store (open-connection file)))
+    (let ((store (open-connection uri)))
       ;; Make sure we build everything by ourselves.
       (set-build-options store
                          #:use-substitutes? #f
 
       store)))
 
+(define (bootstrap-binary-file program system)
+  "Return the absolute file name where bootstrap binary PROGRAM for SYSTEM is
+stored."
+  (string-append (dirname (search-path %load-path
+                                       "gnu/packages/bootstrap.scm"))
+                 "/bootstrap/" system "/" program))
+
+(define (search-bootstrap-binary file-name system)
+  "Search the bootstrap binary FILE-NAME for SYSTEM.  Raise an error if not
+found."
+  ;; Note: Keep bootstrap binaries on the local file system so that the 'guix'
+  ;; package can provide them as inputs and copy them to the right place.
+  (let* ((system (match system
+                   ("x86_64-linux" "i686-linux")
+                   (_ system)))
+         (file   (bootstrap-binary-file file-name system)))
+    (if (file-exists? file)
+        file
+        (with-store store
+          (run-with-store store
+            (mlet %store-monad ((drv (origin->derivation
+                                      (bootstrap-executable file-name system))))
+              (mbegin %store-monad
+                (built-derivations (list drv))
+                (begin
+                  (mkdir-p (dirname file))
+                  (copy-file (derivation->output-path drv) file)
+                  (return file)))))))))
+
+(define (call-with-external-store proc)
+  "Call PROC with an open connection to the external store or #f it there is
+no external store to talk to."
+  (parameterize ((%daemon-socket-uri
+                  (string-append %localstatedir
+                                 "/guix/daemon-socket/socket"))
+                 (%store-prefix %storedir))
+    (define store
+      (catch #t
+        (lambda ()
+          (open-connection))
+        (const #f)))
+
+    (dynamic-wind
+      (const #t)
+      (lambda ()
+        ;; Since we're using a different store we must clear the
+        ;; package-derivation cache.
+        (hash-clear! (@@ (guix packages) %derivation-cache))
+
+        (proc store))
+      (lambda ()
+        (when store
+          (close-connection store))))))
+
+(define-syntax-rule (with-external-store store exp ...)
+  "Evaluate EXP with STORE bound to the external store rather than the
+temporary test store, or #f if there is no external store to talk to.
+
+This is meant to be used for tests that need to build packages that would be
+too expensive to build entirely in the test store."
+  (call-with-external-store (lambda (store) exp ...)))
+
 (define (random-seed)
   (or (and=> (getenv "GUIX_TESTS_RANDOM_SEED")
              number->string)
          (else
           (error "what?" (lstat a))))))
 
+(define (canonical-file? file)
+  "Return #t if FILE is in the store, is read-only, and its mtime is 1."
+  (let ((st (lstat file)))
+    (or (not (string-prefix? (%store-prefix) file))
+        (eq? 'symlink (stat:type st))
+        (and (= 1 (stat:mtime st))
+             (zero? (logand #o222 (stat:mode st)))))))
+
 (define (network-reachable?)
   "Return true if we can reach the Internet."
   (false-if-exception (getaddrinfo "www.gnu.org" "80" AI_NUMERICSERV)))
@@ -126,6 +215,41 @@ given by REPLACEMENT."
       (lambda () body ...)
       (lambda () (module-set! m 'proc original)))))
 
+(define-syntax-rule (test-assertm name exp)
+  "Like 'test-assert', but EXP is a monadic value.  A new connection to the
+store is opened."
+  (test-assert name
+    (let ((store (open-connection-for-tests)))
+      (dynamic-wind
+        (const #t)
+        (lambda ()
+          (run-with-store store exp
+                          #:guile-for-build (%guile-for-build)))
+        (lambda ()
+          (close-connection store))))))
+
+(define-syntax-rule (test-equalm name value exp)
+  "Like 'test-equal', but EXP is a monadic value.  A new connection to the
+store is opened."
+  (test-equal name
+    value
+    (with-store store
+      (run-with-store store exp
+                      #:guile-for-build (%guile-for-build)))))
+
+(define-syntax-rule (with-environment-variable variable value body ...)
+  "Run BODY with VARIABLE set to VALUE."
+  (let ((orig (getenv variable)))
+    (dynamic-wind
+      (lambda ()
+        (setenv variable value))
+      (lambda ()
+        body ...)
+      (lambda ()
+        (if orig
+            (setenv variable orig)
+            (unsetenv variable))))))
+
 \f
 ;;;
 ;;; Narinfo files, as used by the substituter.
@@ -265,18 +389,49 @@ CONTENTS."
 (define-syntax-rule (dummy-package name* extra-fields ...)
   "Return a \"dummy\" package called NAME*, with all its compulsory fields
 initialized with default values, and with EXTRA-FIELDS set as specified."
-  (package extra-fields ...
-           (name name*) (version "0") (source #f)
-           (build-system gnu-build-system)
-           (synopsis #f) (description #f)
-           (home-page #f) (license #f)))
+  (let ((p (package
+             (name name*) (version "0") (source #f)
+             (build-system gnu-build-system)
+             (synopsis #f) (description #f)
+             (home-page #f) (license #f))))
+    (package (inherit p) extra-fields ...)))
 
 (define-syntax-rule (dummy-origin extra-fields ...)
   "Return a \"dummy\" origin, with all its compulsory fields initialized with
 default values, and with EXTRA-FIELDS set as specified."
-  (origin extra-fields ...
-          (method #f) (uri "http://www.example.com")
-          (sha256 (base32 (make-string 52 #\x)))))
+  (let ((o (origin (method #f) (uri "http://www.example.com")
+                   (sha256 (base32 (make-string 52 #\x))))))
+    (origin (inherit o) extra-fields ...)))
+
+(define gnu-make-for-tests
+  ;; This is a variant of 'gnu-make-boot0' that can be built with minimal
+  ;; resources.
+  (package-with-bootstrap-guile
+   (package
+     (inherit gnu-make)
+     (name "make-test-boot0")
+     (arguments
+      `(#:guile ,%bootstrap-guile
+        #:implicit-inputs? #f
+        #:tests? #f                               ;cannot run "make check"
+        ,@(substitute-keyword-arguments (package-arguments gnu-make)
+            ((#:configure-flags flags ''())
+             ;; As in 'gnu-make-boot0', work around a 'config.status' defect.
+             `(cons "--disable-dependency-tracking" ,flags))
+            ((#:phases phases)
+             `(modify-phases ,phases
+                (replace 'build
+                  (lambda _
+                    (invoke "./build.sh")
+                    #t))
+                (replace 'install
+                  (lambda* (#:key outputs #:allow-other-keys)
+                    (let* ((out (assoc-ref outputs "out"))
+                           (bin (string-append out "/bin")))
+                      (install-file "make" bin)
+                      #t))))))))
+     (native-inputs '())                          ;no need for 'pkg-config'
+     (inputs %bootstrap-inputs-for-tests))))
 
 ;; Local Variables:
 ;; eval: (put 'call-with-derivation-narinfo 'scheme-indent-function 1)