profiles: Represent propagated inputs as manifest entries.
[jackhill/guix/guix.git] / tests / profiles.scm
CommitLineData
a2078770 1;;; GNU Guix --- Functional package management for GNU
176febe3 2;;; Copyright © 2013, 2014, 2015, 2016, 2017 Ludovic Courtès <ludo@gnu.org>
343745c8 3;;; Copyright © 2014 Alex Kost <alezost@gmail.com>
a2078770
LC
4;;;
5;;; This file is part of GNU Guix.
6;;;
7;;; GNU Guix is free software; you can redistribute it and/or modify it
8;;; under the terms of the GNU General Public License as published by
9;;; the Free Software Foundation; either version 3 of the License, or (at
10;;; your option) any later version.
11;;;
12;;; GNU Guix is distributed in the hope that it will be useful, but
13;;; WITHOUT ANY WARRANTY; without even the implied warranty of
14;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15;;; GNU General Public License for more details.
16;;;
17;;; You should have received a copy of the GNU General Public License
18;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
19
20(define-module (test-profiles)
c1bc358f 21 #:use-module (guix tests)
a2078770 22 #:use-module (guix profiles)
462f5cca
LC
23 #:use-module (guix store)
24 #:use-module (guix monads)
ef8de985 25 #:use-module (guix grafts)
462f5cca
LC
26 #:use-module (guix packages)
27 #:use-module (guix derivations)
a0dac7a0 28 #:use-module (guix build-system trivial)
462f5cca 29 #:use-module (gnu packages bootstrap)
e39d1461 30 #:use-module ((gnu packages base) #:prefix packages:)
dedb17ad 31 #:use-module ((gnu packages guile) #:prefix packages:)
a2078770 32 #:use-module (ice-9 match)
ef8993e2 33 #:use-module (ice-9 regex)
d664f1b4
LC
34 #:use-module (ice-9 popen)
35 #:use-module (rnrs io ports)
ccda8f7d 36 #:use-module (srfi srfi-1)
79601521 37 #:use-module (srfi srfi-11)
a2078770
LC
38 #:use-module (srfi srfi-64))
39
343745c8 40;; Test the (guix profiles) module.
a2078770 41
462f5cca 42(define %store
c1bc358f 43 (open-connection-for-tests))
a2078770 44
ef8de985
LC
45;; Globally disable grafts because they can trigger early builds.
46(%graft? #f)
47
ebf5ad46
LC
48(define-syntax-rule (test-assertm name exp)
49 (test-assert name
50 (run-with-store %store exp
51 #:guile-for-build (%guile-for-build))))
52
22ef06b8
LC
53(define-syntax-rule (test-equalm name value exp)
54 (test-equal name
55 value
56 (run-with-store %store exp
57 #:guile-for-build (%guile-for-build))))
58
a2078770
LC
59;; Example manifest entries.
60
f7554030
AK
61(define guile-1.8.8
62 (manifest-entry
63 (name "guile")
64 (version "1.8.8")
65 (item "/gnu/store/...")
66 (output "out")))
67
a2078770
LC
68(define guile-2.0.9
69 (manifest-entry
70 (name "guile")
71 (version "2.0.9")
a54c94a4 72 (item "/gnu/store/...")
a2078770
LC
73 (output "out")))
74
75(define guile-2.0.9:debug
76 (manifest-entry (inherit guile-2.0.9)
77 (output "debug")))
78
79601521
LC
79(define glibc
80 (manifest-entry
81 (name "glibc")
82 (version "2.19")
83 (item "/gnu/store/...")
84 (output "out")))
85
a2078770
LC
86\f
87(test-begin "profiles")
88
89(test-assert "manifest-installed?"
90 (let ((m (manifest (list guile-2.0.9 guile-2.0.9:debug))))
91 (and (manifest-installed? m (manifest-pattern (name "guile")))
92 (manifest-installed? m (manifest-pattern
93 (name "guile") (output "debug")))
94 (manifest-installed? m (manifest-pattern
95 (name "guile") (output "out")
96 (version "2.0.9")))
97 (not (manifest-installed?
98 m (manifest-pattern (name "guile") (version "1.8.8"))))
99 (not (manifest-installed?
100 m (manifest-pattern (name "guile") (output "foobar")))))))
101
102(test-assert "manifest-matching-entries"
103 (let* ((e (list guile-2.0.9 guile-2.0.9:debug))
104 (m (manifest e)))
105 (and (null? (manifest-matching-entries m
106 (list (manifest-pattern
107 (name "python")))))
108 (equal? e
109 (manifest-matching-entries m
110 (list (manifest-pattern
111 (name "guile")
112 (output #f)))))
113 (equal? (list guile-2.0.9)
114 (manifest-matching-entries m
115 (list (manifest-pattern
116 (name "guile")
117 (version "2.0.9"))))))))
118
119(test-assert "manifest-remove"
120 (let* ((m0 (manifest (list guile-2.0.9 guile-2.0.9:debug)))
121 (m1 (manifest-remove m0
122 (list (manifest-pattern (name "guile")))))
123 (m2 (manifest-remove m1
124 (list (manifest-pattern (name "guile"))))) ; same
125 (m3 (manifest-remove m2
126 (list (manifest-pattern
127 (name "guile") (output "debug")))))
128 (m4 (manifest-remove m3
129 (list (manifest-pattern (name "guile"))))))
130 (match (manifest-entries m2)
131 ((($ <manifest-entry> "guile" "2.0.9" "debug"))
132 (and (equal? m1 m2)
133 (null? (manifest-entries m3))
134 (null? (manifest-entries m4)))))))
135
f7554030
AK
136(test-assert "manifest-add"
137 (let* ((m0 (manifest '()))
138 (m1 (manifest-add m0 (list guile-1.8.8)))
139 (m2 (manifest-add m1 (list guile-2.0.9)))
140 (m3 (manifest-add m2 (list guile-2.0.9:debug)))
141 (m4 (manifest-add m3 (list guile-2.0.9:debug))))
142 (and (match (manifest-entries m1)
143 ((($ <manifest-entry> "guile" "1.8.8" "out")) #t)
144 (_ #f))
145 (match (manifest-entries m2)
146 ((($ <manifest-entry> "guile" "2.0.9" "out")) #t)
147 (_ #f))
148 (equal? m3 m4))))
149
343745c8
AK
150(test-assert "manifest-perform-transaction"
151 (let* ((m0 (manifest (list guile-2.0.9 guile-2.0.9:debug)))
152 (t1 (manifest-transaction
153 (install (list guile-1.8.8))
154 (remove (list (manifest-pattern (name "guile")
155 (output "debug"))))))
156 (t2 (manifest-transaction
157 (remove (list (manifest-pattern (name "guile")
158 (version "2.0.9")
159 (output #f))))))
160 (m1 (manifest-perform-transaction m0 t1))
161 (m2 (manifest-perform-transaction m1 t2))
162 (m3 (manifest-perform-transaction m0 t2)))
163 (and (match (manifest-entries m1)
164 ((($ <manifest-entry> "guile" "1.8.8" "out")) #t)
165 (_ #f))
166 (equal? m1 m2)
167 (null? (manifest-entries m3)))))
168
79601521
LC
169(test-assert "manifest-transaction-effects"
170 (let* ((m0 (manifest (list guile-1.8.8)))
171 (t (manifest-transaction
172 (install (list guile-2.0.9 glibc))
173 (remove (list (manifest-pattern (name "coreutils")))))))
46b23e1a 174 (let-values (((remove install upgrade downgrade)
79601521 175 (manifest-transaction-effects m0 t)))
46b23e1a 176 (and (null? remove) (null? downgrade)
79601521 177 (equal? (list glibc) install)
ef8993e2
LC
178 (equal? (list (cons guile-1.8.8 guile-2.0.9)) upgrade)))))
179
46b23e1a
LC
180(test-assert "manifest-transaction-effects and downgrades"
181 (let* ((m0 (manifest (list guile-2.0.9)))
182 (t (manifest-transaction (install (list guile-1.8.8)))))
183 (let-values (((remove install upgrade downgrade)
184 (manifest-transaction-effects m0 t)))
185 (and (null? remove) (null? install) (null? upgrade)
186 (equal? (list (cons guile-2.0.9 guile-1.8.8)) downgrade)))))
187
3bea13bb
LC
188(test-assert "manifest-transaction-effects and pseudo-upgrades"
189 (let* ((m0 (manifest (list guile-2.0.9)))
190 (t (manifest-transaction (install (list guile-2.0.9)))))
191 (let-values (((remove install upgrade downgrade)
192 (manifest-transaction-effects m0 t)))
193 (and (null? remove) (null? install) (null? downgrade)
194 (equal? (list (cons guile-2.0.9 guile-2.0.9)) upgrade)))))
195
c8c25704
LC
196(test-assert "manifest-transaction-null?"
197 (manifest-transaction-null? (manifest-transaction)))
198
ebf5ad46
LC
199(test-assertm "profile-derivation"
200 (mlet* %store-monad
201 ((entry -> (package->manifest-entry %bootstrap-guile))
202 (guile (package->derivation %bootstrap-guile))
203 (drv (profile-derivation (manifest (list entry))
a6562c7e
LC
204 #:hooks '()
205 #:locales? #f))
ebf5ad46
LC
206 (profile -> (derivation->output-path drv))
207 (bindir -> (string-append profile "/bin"))
208 (_ (built-derivations (list drv))))
209 (return (and (file-exists? (string-append bindir "/guile"))
210 (string=? (dirname (readlink bindir))
211 (derivation->output-path guile))))))
462f5cca 212
e39d1461
LC
213(test-assertm "profile-derivation, inputs"
214 (mlet* %store-monad
215 ((entry -> (package->manifest-entry packages:glibc "debug"))
216 (drv (profile-derivation (manifest (list entry))
a6562c7e
LC
217 #:hooks '()
218 #:locales? #f)))
e39d1461
LC
219 (return (derivation-inputs drv))))
220
176febe3
LC
221(test-assertm "profile-derivation, cross-compilation"
222 (mlet* %store-monad
223 ((manifest -> (packages->manifest (list packages:sed packages:grep)))
224 (target -> "arm-linux-gnueabihf")
225 (grep (package->cross-derivation packages:grep target))
226 (sed (package->cross-derivation packages:sed target))
227 (locales (package->derivation packages:glibc-utf8-locales))
228 (drv (profile-derivation manifest
229 #:hooks '()
230 #:locales? #t
231 #:target target)))
232 (define (find-input name)
233 (let ((name (string-append name ".drv")))
234 (any (lambda (input)
235 (let ((input (derivation-input-path input)))
236 (and (string-suffix? name input) input)))
237 (derivation-inputs drv))))
238
239 ;; The inputs for grep and sed should be cross-build derivations, but that
240 ;; for the glibc-utf8-locales should be a native build.
241 (return (and (string=? (derivation-system drv) (%current-system))
242 (string=? (find-input (package-full-name packages:grep))
243 (derivation-file-name grep))
244 (string=? (find-input (package-full-name packages:sed))
245 (derivation-file-name sed))
246 (string=? (find-input
247 (package-full-name packages:glibc-utf8-locales))
248 (derivation-file-name locales))))))
249
9e90fc77
LC
250(test-assert "package->manifest-entry defaults to \"out\""
251 (let ((outputs (package-outputs packages:glibc)))
252 (equal? (manifest-entry-output
253 (package->manifest-entry (package
254 (inherit packages:glibc)
255 (outputs (reverse outputs)))))
256 (manifest-entry-output
257 (package->manifest-entry packages:glibc))
258 "out")))
259
dedb17ad
LC
260(test-assertm "profile-manifest, search-paths"
261 (mlet* %store-monad
262 ((guile -> (package
263 (inherit %bootstrap-guile)
264 (native-search-paths
265 (package-native-search-paths packages:guile-2.0))))
266 (entry -> (package->manifest-entry guile))
267 (drv (profile-derivation (manifest (list entry))
a6562c7e
LC
268 #:hooks '()
269 #:locales? #f))
dedb17ad
LC
270 (profile -> (derivation->output-path drv)))
271 (mbegin %store-monad
272 (built-derivations (list drv))
273
274 ;; Read the manifest back and make sure search paths are preserved.
275 (let ((manifest (profile-manifest profile)))
276 (match (manifest-entries manifest)
277 ((result)
278 (return (equal? (manifest-entry-search-paths result)
279 (manifest-entry-search-paths entry)
280 (package-native-search-paths
281 packages:guile-2.0)))))))))
d664f1b4 282
ccda8f7d
LC
283(test-assert "package->manifest-entry, search paths"
284 ;; See <http://bugs.gnu.org/22073>.
285 (let ((mpl (@ (gnu packages python) python2-matplotlib)))
286 (lset= eq?
287 (package-transitive-native-search-paths mpl)
288 (manifest-entry-search-paths
289 (package->manifest-entry mpl)))))
290
55b4715f
LC
291(test-equal "packages->manifest, propagated inputs"
292 (map (match-lambda
293 ((label package)
294 (list (package-name package) (package-version package)
295 package)))
296 (package-propagated-inputs packages:guile-2.2))
297 (map (lambda (entry)
298 (list (manifest-entry-name entry)
299 (manifest-entry-version entry)
300 (manifest-entry-item entry)))
301 (manifest-entry-dependencies
302 (package->manifest-entry packages:guile-2.2))))
303
304(test-assertm "read-manifest"
305 (mlet* %store-monad ((manifest -> (packages->manifest
306 (list (package
307 (inherit %bootstrap-guile)
308 (native-search-paths
309 (package-native-search-paths
310 packages:guile-2.0))))))
311 (drv (profile-derivation manifest
312 #:hooks '()
313 #:locales? #f))
314 (out -> (derivation->output-path drv)))
315 (define (entry->sexp entry)
316 (list (manifest-entry-name entry)
317 (manifest-entry-version entry)
318 (manifest-entry-search-paths entry)
319 (manifest-entry-dependencies entry)))
320
321 (mbegin %store-monad
322 (built-derivations (list drv))
323 (let ((manifest2 (profile-manifest out)))
324 (return (equal? (map entry->sexp (manifest-entries manifest))
325 (map entry->sexp (manifest-entries manifest2))))))))
326
d664f1b4
LC
327(test-assertm "etc/profile"
328 ;; Make sure we get an 'etc/profile' file that at least defines $PATH.
329 (mlet* %store-monad
330 ((guile -> (package
331 (inherit %bootstrap-guile)
332 (native-search-paths
333 (package-native-search-paths packages:guile-2.0))))
334 (entry -> (package->manifest-entry guile))
335 (drv (profile-derivation (manifest (list entry))
a6562c7e
LC
336 #:hooks '()
337 #:locales? #f))
d664f1b4
LC
338 (profile -> (derivation->output-path drv)))
339 (mbegin %store-monad
340 (built-derivations (list drv))
341 (let* ((pipe (open-input-pipe
9e006fb3
TUBK
342 (string-append "unset GUIX_PROFILE; "
343 ;; 'source' is a Bashism; use '.' (dot).
344 ". " profile "/etc/profile; "
345 ;; Don't try to parse set(1) output because
346 ;; it differs among shells; just use echo.
347 "echo $PATH")))
348 (path (get-string-all pipe)))
d664f1b4
LC
349 (return
350 (and (zero? (close-pipe pipe))
9e006fb3 351 (string-contains path (string-append profile "/bin"))))))))
d664f1b4 352
a0dac7a0
LC
353(test-assertm "etc/profile when etc/ already exists"
354 ;; Here 'union-build' makes the profile's etc/ a symlink to the package's
355 ;; etc/ directory, which makes it read-only. Make sure the profile build
356 ;; handles that.
357 (mlet* %store-monad
358 ((thing -> (dummy-package "dummy"
359 (build-system trivial-build-system)
360 (arguments
361 `(#:guile ,%bootstrap-guile
362 #:builder
363 (let ((out (assoc-ref %outputs "out")))
364 (mkdir out)
365 (mkdir (string-append out "/etc"))
366 (call-with-output-file (string-append out "/etc/foo")
367 (lambda (port)
368 (display "foo!" port))))))))
369 (entry -> (package->manifest-entry thing))
370 (drv (profile-derivation (manifest (list entry))
a6562c7e
LC
371 #:hooks '()
372 #:locales? #f))
a0dac7a0
LC
373 (profile -> (derivation->output-path drv)))
374 (mbegin %store-monad
375 (built-derivations (list drv))
376 (return (and (file-exists? (string-append profile "/etc/profile"))
377 (string=? (call-with-input-file
378 (string-append profile "/etc/foo")
379 get-string-all)
380 "foo!"))))))
381
113c17a0
LC
382(test-assertm "etc/profile when etc/ is a symlink"
383 ;; When etc/ is a symlink, the unsymlink code in 0.8.2 would fail
384 ;; gracelessly because 'scandir' would return #f.
385 (mlet* %store-monad
386 ((thing -> (dummy-package "dummy"
387 (build-system trivial-build-system)
388 (arguments
389 `(#:guile ,%bootstrap-guile
390 #:builder
391 (let ((out (assoc-ref %outputs "out")))
392 (mkdir out)
393 (mkdir (string-append out "/foo"))
394 (symlink "foo" (string-append out "/etc"))
395 (call-with-output-file (string-append out "/etc/bar")
396 (lambda (port)
397 (display "foo!" port))))))))
398 (entry -> (package->manifest-entry thing))
399 (drv (profile-derivation (manifest (list entry))
a6562c7e
LC
400 #:hooks '()
401 #:locales? #f))
113c17a0
LC
402 (profile -> (derivation->output-path drv)))
403 (mbegin %store-monad
404 (built-derivations (list drv))
405 (return (and (file-exists? (string-append profile "/etc/profile"))
406 (string=? (call-with-input-file
407 (string-append profile "/etc/bar")
408 get-string-all)
409 "foo!"))))))
410
22ef06b8
LC
411(test-equalm "union vs. dangling symlink" ;<https://bugs.gnu.org/26949>
412 "does-not-exist"
413 (mlet* %store-monad
414 ((thing1 -> (dummy-package "dummy"
415 (build-system trivial-build-system)
416 (arguments
417 `(#:guile ,%bootstrap-guile
418 #:builder
419 (let ((out (assoc-ref %outputs "out")))
420 (mkdir out)
421 (symlink "does-not-exist"
422 (string-append out "/dangling"))
423 #t)))))
424 (thing2 -> (package (inherit thing1) (name "dummy2")))
425 (drv (profile-derivation (packages->manifest
426 (list thing1 thing2))
427 #:hooks '()
428 #:locales? #f))
429 (profile -> (derivation->output-path drv)))
430 (mbegin %store-monad
431 (built-derivations (list drv))
432 (return (readlink (readlink (string-append profile "/dangling")))))))
433
a2078770
LC
434(test-end "profiles")
435
a2078770
LC
436;;; Local Variables:
437;;; eval: (put 'dummy-package 'scheme-indent-function 1)
438;;; End: