From 827d556311b79d44fd67b4bd24cf17e5f781d502 Mon Sep 17 00:00:00 2001 From: Ludovic Courtès Date: Thu, 6 Mar 2014 18:38:19 +0100 Subject: tests: Rewrite 'fcntl-lock' test. * tests/utils.scm (temp-file): New variable. ("fcntl-flock"): Rewrite to actually test whether the child process waits for the lock to be released. The previous test was wrong because (1) it expected F_SETLK semantics, not F_SETLKW, and (2) it got EBADF because of a mismatch between the open mode and the lock style. --- tests/utils.scm | 43 +++++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 14 deletions(-) (limited to 'tests') diff --git a/tests/utils.scm b/tests/utils.scm index b5706aa792..5be7baf016 100644 --- a/tests/utils.scm +++ b/tests/utils.scm @@ -27,6 +27,9 @@ (define-module (test-utils) #:use-module (rnrs io ports) #:use-module (ice-9 match)) +(define temp-file + (string-append "t-utils-" (number->string (getpid)))) + (test-begin "utils") (test-assert "bytevector->base16-string->bytevector" @@ -139,33 +142,43 @@ (define-module (test-utils) (append pids1 pids2))) (equal? (get-bytevector-all decompressed) data))))) +(false-if-exception (delete-file temp-file)) (test-equal "fcntl-flock" - 0 ; the child's exit status - (let ((file (open-input-file (search-path %load-path "guix.scm")))) - (fcntl-flock file 'read-lock) + 42 ; the child's exit status + (let ((file (open-file temp-file "w0"))) + ;; Acquire an exclusive lock. + (fcntl-flock file 'write-lock) (match (primitive-fork) (0 (dynamic-wind (const #t) (lambda () - ;; Taking a read lock should be OK. - (fcntl-flock file 'read-lock) - (fcntl-flock file 'unlock) - - (catch 'flock-error - (lambda () - ;; Taking an exclusive lock should raise an exception. - (fcntl-flock file 'write-lock)) - (lambda args - (primitive-exit 0))) + ;; Reopen FILE read-only so we can have a read lock. + (let ((file (open-file temp-file "r"))) + ;; Wait until we can acquire the lock. + (fcntl-flock file 'read-lock) + (primitive-exit (read file))) (primitive-exit 1)) (lambda () (primitive-exit 2)))) (pid + ;; Write garbage and wait. + (display "hello, world!" file) + (force-output file) + (sleep 1) + + ;; Write the real answer. + (seek file 0 SEEK_SET) + (truncate-file file 0) + (write 42 file) + (force-output file) + + ;; Unlock, which should let the child continue. + (fcntl-flock file 'unlock) + (match (waitpid pid) ((_ . status) (let ((result (status:exit-val status))) - (fcntl-flock file 'unlock) (close-port file) result))))))) @@ -178,5 +191,7 @@ (define-module (test-utils) (test-end) +(false-if-exception (delete-file temp-file)) + (exit (= (test-runner-fail-count (test-runner-current)) 0)) -- cgit v1.2.3 From cafb92d853c66b677111594727c586b87bbdd58f Mon Sep 17 00:00:00 2001 From: Ludovic Courtès Date: Fri, 7 Mar 2014 00:18:28 +0100 Subject: store: 'export-paths' doesn't export references of the given files. This fixes a regression introduced in 99fbddf9a623757e39d88bfb431f8f7d6f24b75b ("store: Change 'export-paths' to always export in topological order.") * guix/store.scm (export-paths): Define 'ordered' variable. Iterate over it. * tests/store.scm ("export/import paths, ensure topological order"): Add 'file0'. Adjust accordingly. --- guix/store.scm | 7 ++++++- tests/store.scm | 7 +++++-- 2 files changed, 11 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/guix/store.scm b/guix/store.scm index 54ed31cbbc..e92e159ff4 100644 --- a/guix/store.scm +++ b/guix/store.scm @@ -734,8 +734,13 @@ (define* (export-path server path port #:key (sign? #t)) (define* (export-paths server paths port #:key (sign? #t)) "Export the store paths listed in PATHS to PORT, in topological order, signing them if SIGN? is true." + (define ordered + ;; Sort PATHS, but don't include their references. + (filter (cut member <> paths) + (topologically-sorted server paths))) + (let ((s (nix-server-socket server))) - (let loop ((paths (topologically-sorted server paths))) + (let loop ((paths ordered)) (match paths (() (write-int 0 port)) diff --git a/tests/store.scm b/tests/store.scm index 7b0f3249d2..cc76ea5500 100644 --- a/tests/store.scm +++ b/tests/store.scm @@ -399,7 +399,9 @@ (define (same? x y) files))))))) (test-assert "export/import paths, ensure topological order" - (let* ((file1 (add-text-to-store %store "foo" (random-text))) + (let* ((file0 (add-text-to-store %store "baz" (random-text))) + (file1 (add-text-to-store %store "foo" (random-text) + (list file0))) (file2 (add-text-to-store %store "bar" (random-text) (list file1))) (files (list file1 file2)) @@ -412,9 +414,10 @@ (define (same? x y) (bytevector=? dump1 dump2) (let* ((source (open-bytevector-input-port dump1)) (imported (import-paths %store source))) + ;; DUMP1 should contain exactly FILE1 and FILE2, not FILE0. (and (equal? imported (list file1 file2)) (every file-exists? files) - (null? (references %store file1)) + (equal? (list file0) (references %store file1)) (equal? (list file1) (references %store file2))))))) (test-assert "import corrupt path" -- cgit v1.2.3 From c7445833eb43ec621fb5a56f6bfbbf0a02a675c2 Mon Sep 17 00:00:00 2001 From: Ludovic Courtès Date: Fri, 7 Mar 2014 16:46:09 +0100 Subject: utils: Add a non-blocking option for 'fcntl-flock'. * guix/utils.scm (F_SETLK): New variable. (fcntl-flock): Add 'wait?' keyword parameter; honor it. * tests/utils.scm ("fcntl-flock non-blocking"): New test. --- guix/utils.scm | 17 ++++++++++++++--- tests/utils.scm | 44 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 57 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/guix/utils.scm b/guix/utils.scm index 38f9ad0f61..68329ec915 100644 --- a/guix/utils.scm +++ b/guix/utils.scm @@ -244,6 +244,13 @@ (define F_SETLKW ((string-contains %host-type "linux") 7) ; *-linux-gnu (else 9)))) ; *-gnu* +(define F_SETLK + ;; Likewise: GNU/Hurd and SPARC use 8, while the others typically use 6. + (compile-time-value + (cond ((string-contains %host-type "sparc") 8) ; sparc-*-linux-gnu + ((string-contains %host-type "linux") 6) ; *-linux-gnu + (else 8)))) ; *-gnu* + (define F_xxLCK ;; The F_RDLCK, F_WRLCK, and F_UNLCK constants. (compile-time-value @@ -271,9 +278,11 @@ (define (errno) (define fcntl-flock (let* ((ptr (dynamic-func "fcntl" (dynamic-link))) (proc (pointer->procedure int ptr `(,int ,int *)))) - (lambda (fd-or-port operation) + (lambda* (fd-or-port operation #:key (wait? #t)) "Perform locking OPERATION on the file beneath FD-OR-PORT. OPERATION -must be a symbol, one of 'read-lock, 'write-lock, or 'unlock." +must be a symbol, one of 'read-lock, 'write-lock, or 'unlock. When WAIT? is +true, block until the lock is acquired; otherwise, thrown an 'flock-error' +exception if it's already taken." (define (operation->int op) (case op ((read-lock) (vector-ref F_xxLCK 0)) @@ -289,7 +298,9 @@ (define fd ;; XXX: 'fcntl' is a vararg function, but here we happily use the ;; standard ABI; crossing fingers. (let ((err (proc fd - F_SETLKW ; lock & wait + (if wait? + F_SETLKW ; lock & wait + F_SETLK) ; non-blocking attempt (make-c-struct %struct-flock (list (operation->int operation) SEEK_SET diff --git a/tests/utils.scm b/tests/utils.scm index 5be7baf016..adac5d4381 100644 --- a/tests/utils.scm +++ b/tests/utils.scm @@ -143,7 +143,7 @@ (define temp-file (equal? (get-bytevector-all decompressed) data))))) (false-if-exception (delete-file temp-file)) -(test-equal "fcntl-flock" +(test-equal "fcntl-flock wait" 42 ; the child's exit status (let ((file (open-file temp-file "w0"))) ;; Acquire an exclusive lock. @@ -182,6 +182,48 @@ (define temp-file (close-port file) result))))))) +(test-equal "fcntl-flock non-blocking" + EAGAIN ; the child's exit status + (match (pipe) + ((input . output) + (match (primitive-fork) + (0 + (dynamic-wind + (const #t) + (lambda () + (close-port output) + + ;; Wait for the green light. + (read-char input) + + ;; Open FILE read-only so we can have a read lock. + (let ((file (open-file temp-file "w"))) + (catch 'flock-error + (lambda () + ;; This attempt should throw EAGAIN. + (fcntl-flock file 'write-lock #:wait? #f)) + (lambda (key errno) + (primitive-exit errno)))) + (primitive-exit -1)) + (lambda () + (primitive-exit -2)))) + (pid + (close-port input) + (let ((file (open-file temp-file "w"))) + ;; Acquire an exclusive lock. + (fcntl-flock file 'write-lock) + + ;; Tell the child to continue. + (write 'green-light output) + (force-output output) + + (match (waitpid pid) + ((_ . status) + (let ((result (status:exit-val status))) + (fcntl-flock file 'unlock) + (close-port file) + result))))))))) + ;; This is actually in (guix store). (test-equal "store-path-package-name" "bash-4.2-p24" -- cgit v1.2.3 From 6c20d1d0c3822c0332f3cca963121365133e6412 Mon Sep 17 00:00:00 2001 From: Ludovic Courtès Date: Sun, 9 Mar 2014 23:01:18 +0100 Subject: store: Add #:timeout build option. * guix/serialization.scm (write-string-pairs): New procedure. * guix/store.scm (write-arg): Add 'string-pairs' case. (set-build-options): Add 'timeout' keyword parameter. Honor it. * tests/derivations.scm ("build-expression->derivation and timeout"): New test. --- guix/serialization.scm | 12 +++++++++++- guix/store.scm | 16 +++++++++------- tests/derivations.scm | 14 ++++++++++++++ 3 files changed, 34 insertions(+), 8 deletions(-) (limited to 'tests') diff --git a/guix/serialization.scm b/guix/serialization.scm index 474dc69de5..284b174794 100644 --- a/guix/serialization.scm +++ b/guix/serialization.scm @@ -1,5 +1,5 @@ ;;; GNU Guix --- Functional package management for GNU -;;; Copyright © 2012, 2013 Ludovic Courtès +;;; Copyright © 2012, 2013, 2014 Ludovic Courtès ;;; ;;; This file is part of GNU Guix. ;;; @@ -22,11 +22,13 @@ (define-module (guix serialization) #:use-module (rnrs io ports) #:use-module (srfi srfi-1) #:use-module (srfi srfi-26) + #:use-module (ice-9 match) #:export (write-int read-int write-long-long read-long-long write-padding write-string read-string read-latin1-string write-string-list read-string-list + write-string-pairs write-store-path read-store-path write-store-path-list read-store-path-list)) @@ -94,6 +96,14 @@ (define (write-string-list l p) (write-int (length l) p) (for-each (cut write-string <> p) l)) +(define (write-string-pairs l p) + (write-int (length l) p) + (for-each (match-lambda + ((first . second) + (write-string first p) + (write-string second p))) + l)) + (define (read-string-list p) (let ((len (read-int p))) (unfold (cut >= <> len) diff --git a/guix/store.scm b/guix/store.scm index 75edb340ae..909ef195de 100644 --- a/guix/store.scm +++ b/guix/store.scm @@ -197,7 +197,7 @@ (define (read-substitutable-path-list p) result)))))) (define-syntax write-arg - (syntax-rules (integer boolean file string string-list + (syntax-rules (integer boolean file string string-list string-pairs store-path store-path-list base16) ((_ integer arg p) (write-int arg p)) @@ -209,6 +209,8 @@ (define-syntax write-arg (write-string arg p)) ((_ string-list arg p) (write-string-list arg p)) + ((_ string-pairs arg p) + (write-string-pairs arg p)) ((_ store-path arg p) (write-store-path arg p)) ((_ store-path-list arg p) @@ -430,6 +432,7 @@ (define* (set-build-options server #:key keep-failed? keep-going? fallback? (verbosity 0) (max-build-jobs (current-processor-count)) + timeout (max-silent-time 3600) (use-build-hook? #t) (build-verbosity 0) @@ -462,12 +465,11 @@ (define socket (when (>= (nix-server-minor-version server) 10) (send (boolean use-substitutes?))) (when (>= (nix-server-minor-version server) 12) - (send (string-list (fold-right (lambda (pair result) - (match pair - ((h . t) - (cons* h t result)))) - '() - binary-caches)))) + (let ((pairs (if timeout + `(("build-timeout" . ,(number->string timeout)) + ,@binary-caches) + binary-caches))) + (send (string-pairs pairs)))) (let loop ((done? (process-stderr server))) (or done? (process-stderr server))))) diff --git a/tests/derivations.scm b/tests/derivations.scm index f31b00b8a2..e87662a198 100644 --- a/tests/derivations.scm +++ b/tests/derivations.scm @@ -446,6 +446,20 @@ (define %coreutils (build-derivations store (list drv)) #f))) +(test-assert "build-expression->derivation and timeout" + (let* ((store (let ((s (open-connection))) + (set-build-options s #:timeout 1) + s)) + (builder '(begin (sleep 100) (mkdir %output) #t)) + (drv (build-expression->derivation store "slow" builder)) + (out-path (derivation->output-path drv))) + (guard (c ((nix-protocol-error? c) + (and (string-contains (nix-protocol-error-message c) + "failed") + (not (valid-path? store out-path))))) + (build-derivations store (list drv)) + #f))) + (test-assert "build-expression->derivation and derivation-prerequisites-to-build" (let ((drv (build-expression->derivation %store "fail" #f))) ;; The only direct dependency is (%guile-for-build) and it's already -- cgit v1.2.3 From 58cbbe4b5562ed8be5c7c6fbdf2b2d8384a5dc8a Mon Sep 17 00:00:00 2001 From: Ludovic Courtès Date: Mon, 10 Mar 2014 23:02:43 +0100 Subject: tests: 'topologically-sorted' test handles different references orders. * tests/store.scm ("topologically-sorted, more difficult"): Arrange to handle a different ordering of (references %store y). --- tests/store.scm | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/tests/store.scm b/tests/store.scm index cc76ea5500..8a25c7353b 100644 --- a/tests/store.scm +++ b/tests/store.scm @@ -190,9 +190,18 @@ (define (same? x y) (s1 (topologically-sorted %store (list y))) (s2 (topologically-sorted %store (list c y))) (s3 (topologically-sorted %store (cons y (references %store y))))) - (and (equal? s1 (list w x a b c d y)) - (equal? s2 (list a b c w x d y)) - (lset= string=? s1 s3)))) + ;; The order in which 'references' returns the references of Y is + ;; unspecified, so accommodate. + (let* ((x-then-d? (equal? (references %store y) (list x d)))) + (and (equal? s1 + (if x-then-d? + (list w x a b c d y) + (list a b c d w x y))) + (equal? s2 + (if x-then-d? + (list a b c w x d y) + (list a b c d w x y))) + (lset= string=? s1 s3))))) (test-assert "log-file, derivation" (let* ((b (add-text-to-store %store "build" "echo $foo > $out" '())) -- cgit v1.2.3