tests: Fix out-of-source builds.
[jackhill/guix/guix.git] / tests / nar.scm
1 ;;; GNU Guix --- Functional package management for GNU
2 ;;; Copyright © 2012, 2013 Ludovic Courtès <ludo@gnu.org>
3 ;;;
4 ;;; This file is part of GNU Guix.
5 ;;;
6 ;;; GNU Guix is free software; you can redistribute it and/or modify it
7 ;;; under the terms of the GNU General Public License as published by
8 ;;; the Free Software Foundation; either version 3 of the License, or (at
9 ;;; your option) any later version.
10 ;;;
11 ;;; GNU Guix is distributed in the hope that it will be useful, but
12 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
13 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ;;; GNU General Public License for more details.
15 ;;;
16 ;;; You should have received a copy of the GNU General Public License
17 ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
18
19 (define-module (test-nar)
20 #:use-module (guix nar)
21 #:use-module (rnrs bytevectors)
22 #:use-module (rnrs io ports)
23 #:use-module (srfi srfi-26)
24 #:use-module (srfi srfi-64)
25 #:use-module (ice-9 ftw)
26 #:use-module (ice-9 match))
27
28 ;; Test the (guix nar) module.
29
30 \f
31 ;;;
32 ;;; File system testing tools, initially contributed to Guile, then libchop.
33 ;;;
34
35 (define (random-file-size)
36 (define %average (* 1024 512)) ; 512 KiB
37 (define %stddev (* 1024 64)) ; 64 KiB
38 (inexact->exact
39 (max 0 (round (+ %average (* %stddev (random:normal)))))))
40
41 (define (make-file-tree dir tree)
42 "Make file system TREE at DIR."
43 (let loop ((dir dir)
44 (tree tree))
45 (define (scope file)
46 (string-append dir "/" file))
47
48 (match tree
49 (('directory name (body ...))
50 (mkdir (scope name))
51 (for-each (cute loop (scope name) <>) body))
52 (('directory name (? integer? mode) (body ...))
53 (mkdir (scope name))
54 (for-each (cute loop (scope name) <>) body)
55 (chmod (scope name) mode))
56 ((file)
57 (populate-file (scope file) (random-file-size)))
58 ((file (? integer? mode))
59 (populate-file (scope file) (random-file-size))
60 (chmod (scope file) mode))
61 ((from '-> to)
62 (symlink to (scope from))))))
63
64 (define (delete-file-tree dir tree)
65 "Delete file TREE from DIR."
66 (let loop ((dir dir)
67 (tree tree))
68 (define (scope file)
69 (string-append dir "/" file))
70
71 (match tree
72 (('directory name (body ...))
73 (for-each (cute loop (scope name) <>) body)
74 (rmdir (scope name)))
75 (('directory name (? integer? mode) (body ...))
76 (chmod (scope name) #o755) ; make sure it can be entered
77 (for-each (cute loop (scope name) <>) body)
78 (rmdir (scope name)))
79 ((from '-> _)
80 (delete-file (scope from)))
81 ((file _ ...)
82 (delete-file (scope file))))))
83
84 (define-syntax-rule (with-file-tree dir tree body ...)
85 (dynamic-wind
86 (lambda ()
87 (make-file-tree dir 'tree))
88 (lambda ()
89 body ...)
90 (lambda ()
91 (delete-file-tree dir 'tree))))
92
93 (define (file-tree-equal? input output)
94 "Return #t if the file trees at INPUT and OUTPUT are equal."
95 (define strip
96 (cute string-drop <> (string-length input)))
97 (define sibling
98 (compose (cut string-append output <>) strip))
99 (define (file=? a b)
100 (and (eq? (stat:type (lstat a)) (stat:type (lstat b)))
101 (case (stat:type (lstat a))
102 ((regular)
103 (equal?
104 (call-with-input-file a get-bytevector-all)
105 (call-with-input-file b get-bytevector-all)))
106 ((symlink)
107 (string=? (readlink a) (readlink b)))
108 (else
109 (error "what?" (lstat a))))))
110
111 (file-system-fold (const #t)
112 (lambda (name stat result) ; leaf
113 (and result
114 (file=? name (sibling name))))
115 (lambda (name stat result) ; down
116 result)
117 (lambda (name stat result) ; up
118 result)
119 (const #f) ; skip
120 (lambda (name stat errno result)
121 (pk 'error name stat errno)
122 #f)
123 (> (stat:nlink (stat output)) 2)
124 input
125 lstat))
126
127 (define (make-random-bytevector n)
128 (let ((bv (make-bytevector n)))
129 (let loop ((i 0))
130 (if (< i n)
131 (begin
132 (bytevector-u8-set! bv i (random 256))
133 (loop (1+ i)))
134 bv))))
135
136 (define (populate-file file size)
137 (call-with-output-file file
138 (lambda (p)
139 (put-bytevector p (make-random-bytevector size)))))
140
141 (define (rm-rf dir)
142 (file-system-fold (const #t) ; enter?
143 (lambda (file stat result) ; leaf
144 (delete-file file))
145 (const #t) ; down
146 (lambda (dir stat result) ; up
147 (rmdir dir))
148 (const #t) ; skip
149 (const #t) ; error
150 #t
151 dir
152 lstat))
153
154 (define %test-dir
155 ;; An output directory under $top_builddir.
156 (string-append (dirname (search-path %load-path "pre-inst-env"))
157 "/test-nar-" (number->string (getpid))))
158
159 \f
160 (test-begin "nar")
161
162 (test-assert "write-file + restore-file"
163 (let* ((input (string-append (dirname (search-path %load-path "guix.scm"))
164 "/guix"))
165 (output %test-dir)
166 (nar (string-append output ".nar")))
167 (dynamic-wind
168 (lambda () #t)
169 (lambda ()
170 (call-with-output-file nar
171 (cut write-file input <>))
172 (call-with-input-file nar
173 (cut restore-file <> output))
174 (file-tree-equal? input output))
175 (lambda ()
176 (false-if-exception (delete-file nar))
177 (false-if-exception (rm-rf output))))))
178
179 (test-assert "write-file + restore-file with symlinks"
180 (let ((input (string-append %test-dir ".input")))
181 (mkdir input)
182 (dynamic-wind
183 (const #t)
184 (lambda ()
185 (with-file-tree input
186 (directory "root"
187 (("reg") ("exe" #o777) ("sym" -> "reg")))
188 (let* ((output %test-dir)
189 (nar (string-append output ".nar")))
190 (dynamic-wind
191 (lambda () #t)
192 (lambda ()
193 (call-with-output-file nar
194 (cut write-file input <>))
195 (call-with-input-file nar
196 (cut restore-file <> output))
197 (file-tree-equal? input output))
198 (lambda ()
199 (false-if-exception (delete-file nar))
200 (false-if-exception (rm-rf output)))))))
201 (lambda ()
202 (rmdir input)))))
203
204 (test-end "nar")
205
206 \f
207 (exit (= (test-runner-fail-count (test-runner-current)) 0))
208
209 ;;; Local Variables:
210 ;;; eval: (put 'with-file-tree 'scheme-indent-function 2)
211 ;;; End: