summary refs log tree commit diff
diff options
context:
space:
mode:
authorLudovic Courtès <ludo@gnu.org>2017-10-12 23:19:09 +0200
committerLudovic Courtès <ludo@gnu.org>2017-10-12 23:47:48 +0200
commit6ea10db973d861cd8774938e40151c0f8b2d266f (patch)
treed86b6ca502a946097c2a610158fbad4caa91260e
parente37415917c4758142c052cae46b3d84517b54ec2 (diff)
downloadguix-6ea10db973d861cd8774938e40151c0f8b2d266f.tar.gz
tests: Support multiple HTTP server instances.
* guix/tests/http.scm (%http-server-socket): Turn into...
(open-http-server-socket): ... this procedure.
(http-server-can-listen?): New procedure.
(http-write, %http-server-lock, %http-server-ready)
(http-open, stub-http-server): Move to 'call-with-http-server' body.
(call-with-http-server): Add #:headers parameter.
(with-http-server): Add an additional pattern with headers.
* tests/derivations.scm: Use (http-server-can-listen?) instead
of (force %http-server-socket).
* tests/lint.scm: Likewise.
-rw-r--r--guix/tests/http.scm133
-rw-r--r--tests/derivations.scm8
-rw-r--r--tests/lint.scm14
3 files changed, 85 insertions, 70 deletions
diff --git a/guix/tests/http.scm b/guix/tests/http.scm
index fe1e120c5d..a56d6f213d 100644
--- a/guix/tests/http.scm
+++ b/guix/tests/http.scm
@@ -1,5 +1,5 @@
 ;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2014, 2015, 2016 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2014, 2015, 2016, 2017 Ludovic Courtès <ludo@gnu.org>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -25,7 +25,7 @@
   #:export (with-http-server
             call-with-http-server
             %http-server-port
-            %http-server-socket
+            http-server-can-listen?
             %local-url))
 
 ;;; Commentary:
@@ -38,75 +38,85 @@
   ;; TCP port to use for the stub HTTP server.
   (make-parameter 9999))
 
+(define (open-http-server-socket)
+  "Return a listening socket for the web server.  It is useful to export it so
+that tests can check whether we succeeded opening the socket and tests skip if
+needed."
+  (catch 'system-error
+    (lambda ()
+      (let ((sock (socket PF_INET SOCK_STREAM 0)))
+        (setsockopt sock SOL_SOCKET SO_REUSEADDR 1)
+        (bind sock
+              (make-socket-address AF_INET INADDR_LOOPBACK
+                                   (%http-server-port)))
+        sock))
+    (lambda args
+      (let ((err (system-error-errno args)))
+        (format (current-error-port)
+                "warning: cannot run Web server for tests: ~a~%"
+                (strerror err))
+        #f))))
+
+(define (http-server-can-listen?)
+  "Return #t if we managed to open a listening socket."
+  (and=> (open-http-server-socket)
+         (lambda (socket)
+           (close-port socket)
+           #t)))
+
 (define (%local-url)
   ;; URL to use for 'home-page' tests.
   (string-append "http://localhost:" (number->string (%http-server-port))
                  "/foo/bar"))
 
-(define %http-server-socket
-  ;; Listening socket for the web server.  It is useful to export it so that
-  ;; tests can check whether we succeeded opening the socket and tests skip if
-  ;; needed.
-  (delay
-    (catch 'system-error
-      (lambda ()
-        (let ((sock (socket PF_INET SOCK_STREAM 0)))
-          (setsockopt sock SOL_SOCKET SO_REUSEADDR 1)
-          (bind sock
-                (make-socket-address AF_INET INADDR_LOOPBACK
-                                     (%http-server-port)))
-          sock))
-      (lambda args
-        (let ((err (system-error-errno args)))
-          (format (current-error-port)
-                  "warning: cannot run Web server for tests: ~a~%"
-                  (strerror err))
-          #f)))))
-
-(define (http-write server client response body)
-  "Write RESPONSE."
-  (let* ((response (write-response response client))
-         (port     (response-port response)))
-    (cond
-     ((not body))                                 ;pass
-     (else
-      (write-response-body response body)))
-    (close-port port)
-    (quit #t)                                     ;exit the server thread
-    (values)))
+(define* (call-with-http-server code data thunk
+                                #:key (headers '()))
+  "Call THUNK with an HTTP server running and returning CODE and DATA (a
+string) on HTTP requests."
+  (define (http-write server client response body)
+    "Write RESPONSE."
+    (let* ((response (write-response response client))
+           (port     (response-port response)))
+      (cond
+       ((not body))                               ;pass
+       (else
+        (write-response-body response body)))
+      (close-port port)
+      (quit #t)                                   ;exit the server thread
+      (values)))
 
-;; Mutex and condition variable to synchronize with the HTTP server.
-(define %http-server-lock (make-mutex))
-(define %http-server-ready (make-condition-variable))
+  ;; Mutex and condition variable to synchronize with the HTTP server.
+  (define %http-server-lock (make-mutex))
+  (define %http-server-ready (make-condition-variable))
 
-(define (http-open . args)
-  "Start listening for HTTP requests and signal %HTTP-SERVER-READY."
-  (with-mutex %http-server-lock
-    (let ((result (apply (@@ (web server http) http-open) args)))
-      (signal-condition-variable %http-server-ready)
-      result)))
+  (define (http-open . args)
+    "Start listening for HTTP requests and signal %HTTP-SERVER-READY."
+    (with-mutex %http-server-lock
+      (let ((result (apply (@@ (web server http) http-open) args)))
+        (signal-condition-variable %http-server-ready)
+        result)))
 
-(define-server-impl stub-http-server
-  ;; Stripped-down version of Guile's built-in HTTP server.
-  http-open
-  (@@ (web server http) http-read)
-  http-write
-  (@@ (web server http) http-close))
+  (define-server-impl stub-http-server
+    ;; Stripped-down version of Guile's built-in HTTP server.
+    http-open
+    (@@ (web server http) http-read)
+    http-write
+    (@@ (web server http) http-close))
 
-(define (call-with-http-server code data thunk)
-  "Call THUNK with an HTTP server running and returning CODE and DATA (a
-string) on HTTP requests."
   (define (server-body)
     (define (handle request body)
       (values (build-response #:code code
-                              #:reason-phrase "Such is life")
+                              #:reason-phrase "Such is life"
+                              #:headers headers)
               data))
 
-    (catch 'quit
-      (lambda ()
-        (run-server handle stub-http-server
-                    `(#:socket ,(force %http-server-socket))))
-      (const #t)))
+    (let ((socket (open-http-server-socket)))
+      (catch 'quit
+        (lambda ()
+          (run-server handle stub-http-server
+                      `(#:socket ,socket)))
+        (lambda _
+          (close-port socket)))))
 
   (with-mutex %http-server-lock
     (let ((server (make-thread server-body)))
@@ -114,7 +124,12 @@ string) on HTTP requests."
       ;; Normally SERVER exits automatically once it has received a request.
       (thunk))))
 
-(define-syntax-rule (with-http-server code data body ...)
-  (call-with-http-server code data (lambda () body ...)))
+(define-syntax with-http-server
+  (syntax-rules ()
+    ((_ (code headers) data body ...)
+     (call-with-http-server code data (lambda () body ...)
+                            #:headers headers))
+    ((_ code data body ...)
+     (call-with-http-server code data (lambda () body ...)))))
 
 ;;; http.scm ends here
diff --git a/tests/derivations.scm b/tests/derivations.scm
index f3aad1b906..36afd42d05 100644
--- a/tests/derivations.scm
+++ b/tests/derivations.scm
@@ -222,7 +222,7 @@
       (build-derivations %store (list drv))
       #f)))
 
-(unless (force %http-server-socket)
+(unless (http-server-can-listen?)
   (test-skip 1))
 (test-assert "'download' built-in builder"
   (let ((text (random-text)))
@@ -238,7 +238,7 @@
                          get-string-all)
                        text))))))
 
-(unless (force %http-server-socket)
+(unless (http-server-can-listen?)
   (test-skip 1))
 (test-assert "'download' built-in builder, invalid hash"
   (with-http-server 200 "hello, world!"
@@ -253,7 +253,7 @@
         (build-derivations %store (list drv))
         #f))))
 
-(unless (force %http-server-socket)
+(unless (http-server-can-listen?)
   (test-skip 1))
 (test-assert "'download' built-in builder, not found"
   (with-http-server 404 "not found"
@@ -279,7 +279,7 @@
       (build-derivations %store (list drv))
       #f)))
 
-(unless (force %http-server-socket)
+(unless (http-server-can-listen?)
   (test-skip 1))
 (test-assert "'download' built-in builder, check mode"
   ;; Make sure rebuilding the 'builtin:download' derivation in check mode
diff --git a/tests/lint.scm b/tests/lint.scm
index 7610a91fd3..d7254bc070 100644
--- a/tests/lint.scm
+++ b/tests/lint.scm
@@ -388,7 +388,7 @@
         (check-home-page pkg)))
     "domain not found")))
 
-(test-skip (if (force %http-server-socket) 0 1))
+(test-skip (if (http-server-can-listen?) 0 1))
 (test-assert "home-page: Connection refused"
   (->bool
    (string-contains
@@ -399,7 +399,7 @@
         (check-home-page pkg)))
     "Connection refused")))
 
-(test-skip (if (force %http-server-socket) 0 1))
+(test-skip (if (http-server-can-listen?) 0 1))
 (test-equal "home-page: 200"
   ""
   (with-warnings
@@ -409,7 +409,7 @@
                   (home-page (%local-url)))))
        (check-home-page pkg)))))
 
-(test-skip (if (force %http-server-socket) 0 1))
+(test-skip (if (http-server-can-listen?) 0 1))
 (test-assert "home-page: 200 but short length"
   (->bool
    (string-contains
@@ -421,7 +421,7 @@
           (check-home-page pkg))))
     "suspiciously small")))
 
-(test-skip (if (force %http-server-socket) 0 1))
+(test-skip (if (http-server-can-listen?) 0 1))
 (test-assert "home-page: 404"
   (->bool
    (string-contains
@@ -510,7 +510,7 @@
          (check-source-file-name pkg)))
      "file name should contain the package name"))))
 
-(test-skip (if (force %http-server-socket) 0 1))
+(test-skip (if (http-server-can-listen?) 0 1))
 (test-equal "source: 200"
   ""
   (with-warnings
@@ -523,7 +523,7 @@
                             (sha256 %null-sha256))))))
        (check-source pkg)))))
 
-(test-skip (if (force %http-server-socket) 0 1))
+(test-skip (if (http-server-can-listen?) 0 1))
 (test-assert "source: 200 but short length"
   (->bool
    (string-contains
@@ -538,7 +538,7 @@
           (check-source pkg))))
     "suspiciously small")))
 
-(test-skip (if (force %http-server-socket) 0 1))
+(test-skip (if (http-server-can-listen?) 0 1))
 (test-assert "source: 404"
   (->bool
    (string-contains