gnu: julia-pdmats: Update to 0.11.1.
[jackhill/guix/guix.git] / tests / lint.scm
CommitLineData
b4f5e0e8
CR
1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2012, 2013 Cyril Roelandt <tipecaml@gmail.com>
83f18e06 3;;; Copyright © 2014, 2015, 2016 Eric Bavier <bavier@member.fsf.org>
bc4d81d2 4;;; Copyright © 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021 Ludovic Courtès <ludo@gnu.org>
052d53df 5;;; Copyright © 2015, 2016 Mathieu Lirzin <mthl@gnu.org>
891a843d 6;;; Copyright © 2016 Hartmut Goebel <h.goebel@crazy-compilers.com>
689db38e 7;;; Copyright © 2017 Alex Kost <alezost@gmail.com>
f4007b25 8;;; Copyright © 2017 Efraim Flashner <efraim@flashner.co.il>
40fa21c2 9;;; Copyright © 2018, 2019 Arun Isaac <arunisaac@systemreboot.net>
464b1fff 10;;; Copyright © 2020 Timothy Sample <samplet@ngyro.com>
c68070e4 11;;; Copyright © 2021 Xinglu Chen <public@yoctocell.xyz>
b4f5e0e8
CR
12;;;
13;;; This file is part of GNU Guix.
14;;;
15;;; GNU Guix is free software; you can redistribute it and/or modify it
16;;; under the terms of the GNU General Public License as published by
17;;; the Free Software Foundation; either version 3 of the License, or (at
18;;; your option) any later version.
19;;;
20;;; GNU Guix is distributed in the hope that it will be useful, but
21;;; WITHOUT ANY WARRANTY; without even the implied warranty of
22;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23;;; GNU General Public License for more details.
24;;;
25;;; You should have received a copy of the GNU General Public License
26;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
27
c74f0cb2
LC
28;; Avoid interference.
29(unsetenv "http_proxy")
30
4e7b6b48 31(define-module (test-lint)
8b385969 32 #:use-module (guix tests)
17ab08bc 33 #:use-module (guix tests http)
754e5be2 34 #:use-module (guix download)
50f5c46d 35 #:use-module (guix git-download)
b4f5e0e8
CR
36 #:use-module (guix build-system gnu)
37 #:use-module (guix packages)
f363c836 38 #:use-module (guix lint)
b4f5e0e8 39 #:use-module (guix ui)
55549c7b 40 #:use-module (guix swh)
4f156c25
LC
41 #:use-module ((guix gexp) #:select (local-file))
42 #:use-module ((guix utils) #:select (call-with-temporary-directory))
464b1fff
TS
43 #:use-module ((guix import hackage) #:select (%hackage-url))
44 #:use-module ((guix import stackage) #:select (%stackage-url))
b4f5e0e8 45 #:use-module (gnu packages)
99fe215c 46 #:use-module (gnu packages glib)
b4f5e0e8 47 #:use-module (gnu packages pkg-config)
3b98522b 48 #:use-module (gnu packages python-xyz)
61f28fe7 49 #:use-module (web uri)
907c98ac
LC
50 #:use-module (web server)
51 #:use-module (web server http)
52 #:use-module (web response)
9bee2bd1 53 #:use-module (ice-9 match)
50fc2384
CB
54 #:use-module (ice-9 regex)
55 #:use-module (ice-9 getopt-long)
56 #:use-module (ice-9 pretty-print)
55549c7b 57 #:use-module (rnrs bytevectors)
50fc2384 58 #:use-module (srfi srfi-1)
907c98ac 59 #:use-module (srfi srfi-9 gnu)
50fc2384 60 #:use-module (srfi srfi-26)
b4f5e0e8
CR
61 #:use-module (srfi srfi-64))
62
63;; Test the linter.
64
950d2ea4
LC
65(define %null-sha256
66 ;; SHA256 of the empty string.
67 (base32
68 "0mdqa9w1p6cmli6976v4wi0sw9r4p5prkj7lzfd1877wk11c9c73"))
69
bfcb3d76
LC
70(define %long-string
71 (make-string 2000 #\a))
907c98ac 72
50fc2384
CB
73(define (string-match-or-error pattern str)
74 (or (string-match pattern str)
75 (error str "did not match" pattern)))
76
77(define single-lint-warning-message
78 (match-lambda
79 (((and (? lint-warning?) warning))
80 (lint-warning-message warning))))
81
37592014
LC
82(define (warning-contains? str warnings)
83 "Return true if WARNINGS is a singleton with a warning that contains STR."
84 (match warnings
85 (((? lint-warning? warning))
86 (string-contains (lint-warning-message warning) str))))
87
b4f5e0e8
CR
88\f
89(test-begin "lint")
90
50fc2384
CB
91(test-equal "description: not a string"
92 "invalid description: foobar"
93 (single-lint-warning-message
94 (check-description-style
95 (dummy-package "x" (description 'foobar)))))
96
97(test-equal "description: not empty"
98 "description should not be empty"
99 (single-lint-warning-message
100 (check-description-style
101 (dummy-package "x" (description "")))))
102
103(test-equal "description: invalid Texinfo markup"
104 "Texinfo markup in description is invalid"
105 (single-lint-warning-message
106 (check-description-style
107 (dummy-package "x" (description "f{oo}b@r")))))
108
109(test-equal "description: does not start with an upper-case letter"
110 "description should start with an upper-case letter or digit"
111 (single-lint-warning-message
112 (let ((pkg (dummy-package "x"
113 (description "bad description."))))
114 (check-description-style pkg))))
115
116(test-equal "description: may start with a digit"
117 '()
118 (let ((pkg (dummy-package "x"
119 (description "2-component library."))))
120 (check-description-style pkg)))
121
122(test-equal "description: may start with lower-case package name"
123 '()
124 (let ((pkg (dummy-package "x"
125 (description "x is a dummy package."))))
126 (check-description-style pkg)))
127
128(test-equal "description: two spaces after end of sentence"
129 "sentences in description should be followed by two spaces; possible infraction at 3"
130 (single-lint-warning-message
131 (let ((pkg (dummy-package "x"
132 (description "Bad. Quite bad."))))
133 (check-description-style pkg))))
134
135(test-equal "description: end-of-sentence detection with abbreviations"
136 '()
137 (let ((pkg (dummy-package "x"
138 (description
139 "E.g. Foo, i.e. Bar resp. Baz (a.k.a. DVD)."))))
140 (check-description-style pkg)))
141
142(test-equal "description: may not contain trademark signs: ™"
143 "description should not contain trademark sign '™' at 20"
144 (single-lint-warning-message
145 (let ((pkg (dummy-package "x"
146 (description "Does The Right Thing™"))))
147 (check-description-style pkg))))
148
149(test-equal "description: may not contain trademark signs: ®"
150 "description should not contain trademark sign '®' at 17"
151 (single-lint-warning-message
152 (let ((pkg (dummy-package "x"
153 (description "Works with Format®"))))
154 (check-description-style pkg))))
155
156(test-equal "description: suggest ornament instead of quotes"
157 "use @code or similar ornament instead of quotes"
158 (single-lint-warning-message
159 (let ((pkg (dummy-package "x"
160 (description "This is a 'quoted' thing."))))
161 (check-description-style pkg))))
162
93d85dea
XC
163(test-equal "description: trailing whitespace"
164 "description contains trailing whitespace"
165 (single-lint-warning-message
166 (let ((pkg (dummy-package "x"
167 (description "Whitespace. "))))
168 (check-description-style pkg))))
169
50fc2384
CB
170(test-equal "synopsis: not a string"
171 "invalid synopsis: #f"
172 (single-lint-warning-message
173 (let ((pkg (dummy-package "x"
174 (synopsis #f))))
175 (check-synopsis-style pkg))))
176
177(test-equal "synopsis: not empty"
178 "synopsis should not be empty"
179 (single-lint-warning-message
180 (let ((pkg (dummy-package "x"
181 (synopsis ""))))
182 (check-synopsis-style pkg))))
183
184(test-equal "synopsis: valid Texinfo markup"
185 "Texinfo markup in synopsis is invalid"
186 (single-lint-warning-message
187 (check-synopsis-style
188 (dummy-package "x" (synopsis "Bad $@ texinfo")))))
189
190(test-equal "synopsis: does not start with an upper-case letter"
191 "synopsis should start with an upper-case letter or digit"
192 (single-lint-warning-message
193 (let ((pkg (dummy-package "x"
194 (synopsis "bad synopsis"))))
195 (check-synopsis-style pkg))))
196
197(test-equal "synopsis: may start with a digit"
198 '()
199 (let ((pkg (dummy-package "x"
200 (synopsis "5-dimensional frobnicator"))))
201 (check-synopsis-style pkg)))
202
203(test-equal "synopsis: ends with a period"
204 "no period allowed at the end of the synopsis"
205 (single-lint-warning-message
206 (let ((pkg (dummy-package "x"
207 (synopsis "Bad synopsis."))))
208 (check-synopsis-style pkg))))
209
210(test-equal "synopsis: ends with 'etc.'"
211 '()
212 (let ((pkg (dummy-package "x"
213 (synopsis "Foo, bar, etc."))))
214 (check-synopsis-style pkg)))
215
216(test-equal "synopsis: starts with 'A'"
217 "no article allowed at the beginning of the synopsis"
218 (single-lint-warning-message
219 (let ((pkg (dummy-package "x"
220 (synopsis "A bad synopŝis"))))
221 (check-synopsis-style pkg))))
222
223(test-equal "synopsis: starts with 'An'"
224 "no article allowed at the beginning of the synopsis"
225 (single-lint-warning-message
226 (let ((pkg (dummy-package "x"
227 (synopsis "An awful synopsis"))))
228 (check-synopsis-style pkg))))
229
230(test-equal "synopsis: starts with 'a'"
231 '("no article allowed at the beginning of the synopsis"
232 "synopsis should start with an upper-case letter or digit")
233 (sort
234 (map
235 lint-warning-message
236 (let ((pkg (dummy-package "x"
237 (synopsis "a bad synopsis"))))
238 (check-synopsis-style pkg)))
239 string<?))
240
241(test-equal "synopsis: starts with 'an'"
242 '("no article allowed at the beginning of the synopsis"
243 "synopsis should start with an upper-case letter or digit")
244 (sort
245 (map
246 lint-warning-message
247 (let ((pkg (dummy-package "x"
248 (synopsis "an awful synopsis"))))
249 (check-synopsis-style pkg)))
250 string<?))
251
252(test-equal "synopsis: too long"
253 "synopsis should be less than 80 characters long"
254 (single-lint-warning-message
255 (let ((pkg (dummy-package "x"
256 (synopsis (make-string 80 #\X)))))
257 (check-synopsis-style pkg))))
258
259(test-equal "synopsis: start with package name"
260 "synopsis should not start with the package name"
261 (single-lint-warning-message
262 (let ((pkg (dummy-package "x"
263 (name "Foo")
264 (synopsis "Foo, a nice package"))))
265 (check-synopsis-style pkg))))
266
267(test-equal "synopsis: start with package name prefix"
268 '()
269 (let ((pkg (dummy-package "arb"
270 (synopsis "Arbitrary precision"))))
271 (check-synopsis-style pkg)))
272
273(test-equal "synopsis: start with abbreviation"
274 '()
275 (let ((pkg (dummy-package "uucp"
276 ;; Same problem with "APL interpreter", etc.
277 (synopsis "UUCP implementation")
278 (description "Imagine this is Taylor UUCP."))))
279 (check-synopsis-style pkg)))
280
04afb769
XC
281(test-equal "synopsis: contains trailing whitespace"
282 "synopsis contains trailing whitespace"
283 (single-lint-warning-message
284 (let ((pkg (dummy-package "x"
285 (synopsis "Whitespace "))))
286 (check-synopsis-style pkg))))
287
c68070e4
XC
288(test-equal "name: use underscore in package name"
289 "name should use hyphens instead of underscores"
290 (single-lint-warning-message
291 (let ((pkg (dummy-package "under_score")))
292 (check-name pkg))))
293
82b0e27d
MD
294(test-equal "tests-true: #:tests? must not be set to #t"
295 "#:tests? must not be explicitly set to #t"
296 (single-lint-warning-message
297 (let ((pkg (dummy-package "x" (arguments '(#:tests? #t)))))
298 (check-tests-true pkg))))
299
300(test-equal "tests-true: absent #:tests? is acceptable"
301 '()
302 (let ((pkg (dummy-package "x")))
303 (check-tests-true pkg)))
304
305(test-equal "tests-true: #:tests? #f is acceptable"
306 '()
307 (let ((pkg (dummy-package "x" (arguments '(#:tests? #f)))))
308 (check-tests-true pkg)))
309
310(test-equal "tests-true: #:tests? #t acceptable when compiling natively"
311 '()
312 (let ((pkg (dummy-package "x"
313 (arguments
314 `(#:tests? ,(not (%current-target-system)))))))
315 (check-tests-true pkg)))
316
50fc2384
CB
317(test-equal "inputs: pkg-config is probably a native input"
318 "'pkg-config' should probably be a native input"
319 (single-lint-warning-message
320 (let ((pkg (dummy-package "x"
321 (inputs `(("pkg-config" ,pkg-config))))))
322 (check-inputs-should-be-native pkg))))
323
324(test-equal "inputs: glib:bin is probably a native input"
325 "'glib:bin' should probably be a native input"
326 (single-lint-warning-message
327 (let ((pkg (dummy-package "x"
328 (inputs `(("glib" ,glib "bin"))))))
329 (check-inputs-should-be-native pkg))))
330
331(test-equal
891a843d 332 "inputs: python-setuptools should not be an input at all (input)"
50fc2384
CB
333 "'python-setuptools' should probably not be an input at all"
334 (single-lint-warning-message
335 (let ((pkg (dummy-package "x"
336 (inputs `(("python-setuptools"
337 ,python-setuptools))))))
338 (check-inputs-should-not-be-an-input-at-all pkg))))
339
340(test-equal
891a843d 341 "inputs: python-setuptools should not be an input at all (native-input)"
50fc2384
CB
342 "'python-setuptools' should probably not be an input at all"
343 (single-lint-warning-message
344 (let ((pkg (dummy-package "x"
345 (native-inputs
346 `(("python-setuptools"
347 ,python-setuptools))))))
348 (check-inputs-should-not-be-an-input-at-all pkg))))
349
350(test-equal
891a843d 351 "inputs: python-setuptools should not be an input at all (propagated-input)"
50fc2384
CB
352 "'python-setuptools' should probably not be an input at all"
353 (single-lint-warning-message
354 (let ((pkg (dummy-package "x"
355 (propagated-inputs
356 `(("python-setuptools" ,python-setuptools))))))
357 (check-inputs-should-not-be-an-input-at-all pkg))))
358
d8ae7852 359(test-equal "file patches: different file name -> warning"
50fc2384
CB
360 "file names of patches should start with the package name"
361 (single-lint-warning-message
362 (let ((pkg (dummy-package "x"
363 (source
364 (dummy-origin
365 (patches (list "/path/to/y.patch")))))))
366 (check-patch-file-names pkg))))
367
d8ae7852
CM
368(test-equal "file patches: same file name -> no warnings"
369 '()
370 (let ((pkg (dummy-package "x"
371 (source
372 (dummy-origin
373 (patches (list "/path/to/x.patch")))))))
374 (check-patch-file-names pkg)))
375
376(test-equal "<origin> patches: different file name -> warning"
377 "file names of patches should start with the package name"
378 (single-lint-warning-message
379 (let ((pkg (dummy-package "x"
380 (source
381 (dummy-origin
382 (patches
383 (list
384 (dummy-origin
385 (file-name "y.patch")))))))))
386 (check-patch-file-names pkg))))
387
388(test-equal "<origin> patches: same file name -> no warnings"
389 '()
390 (let ((pkg (dummy-package "x"
391 (source
392 (dummy-origin
393 (patches
394 (list
395 (dummy-origin
396 (file-name "x.patch")))))))))
397 (check-patch-file-names pkg)))
398
50fc2384
CB
399(test-equal "patches: file name too long"
400 (string-append "x-"
401 (make-string 100 #\a)
402 ".patch: file name is too long")
403 (single-lint-warning-message
404 (let ((pkg (dummy-package
405 "x"
406 (source
407 (dummy-origin
408 (patches (list (string-append "x-"
409 (make-string 100 #\a)
410 ".patch"))))))))
411 (check-patch-file-names pkg))))
412
413(test-equal "patches: not found"
d51bfe24 414 "this-patch-does-not-exist!: patch not found\n"
50fc2384
CB
415 (single-lint-warning-message
416 (let ((pkg (dummy-package
417 "x"
418 (source
419 (dummy-origin
420 (patches
421 (list (search-patch "this-patch-does-not-exist!"))))))))
422 (check-patch-file-names pkg))))
423
4f156c25
LC
424(test-assert "patch headers: no warnings"
425 (call-with-temporary-directory
426 (lambda (directory)
427 (call-with-output-file (string-append directory "/t.patch")
428 (lambda (port)
429 (display "This is a patch.\n\n--- a\n+++ b\n"
430 port)))
431
432 (parameterize ((%patch-path (list directory)))
433 (let ((pkg (dummy-package "x"
434 (source (dummy-origin
435 (patches (search-patches "t.patch")))))))
436 (null? (check-patch-headers pkg)))))))
437
438(test-equal "patch headers: missing comment"
439 "t.patch: patch lacks comment and upstream status"
440 (call-with-temporary-directory
441 (lambda (directory)
442 (call-with-output-file (string-append directory "/t.patch")
443 (lambda (port)
444 (display "\n--- a\n+++ b\n"
445 port)))
446
447 (parameterize ((%patch-path (list directory)))
448 (let ((pkg (dummy-package "x"
449 (source (dummy-origin
450 (patches (search-patches "t.patch")))))))
451 (single-lint-warning-message (check-patch-headers pkg)))))))
452
453(test-equal "patch headers: empty"
454 "t.patch: empty patch"
455 (call-with-temporary-directory
456 (lambda (directory)
457 (call-with-output-file (string-append directory "/t.patch")
458 (const #t))
459
460 (parameterize ((%patch-path '()))
461 (let ((pkg (dummy-package "x"
462 (source (dummy-origin
463 (patches
464 (list (local-file
465 (string-append directory
466 "/t.patch")))))))))
467 (single-lint-warning-message (check-patch-headers pkg)))))))
468
469(test-equal "patch headers: patch not found"
470 "does-not-exist.patch: patch not found\n"
471 (parameterize ((%patch-path '()))
472 (let ((pkg (dummy-package "x"
473 (source (dummy-origin
474 (patches
475 (search-patches "does-not-exist.patch")))))))
476 (single-lint-warning-message (check-patch-headers pkg)))))
477
50fc2384
CB
478(test-equal "derivation: invalid arguments"
479 "failed to create x86_64-linux derivation: (wrong-type-arg \"map\" \"Wrong type argument: ~S\" (invalid-module) ())"
480 (match (let ((pkg (dummy-package "x"
481 (arguments
482 '(#:imported-modules (invalid-module))))))
483 (check-derivation pkg))
484 (((and (? lint-warning?) first-warning) others ...)
485 (lint-warning-message first-warning))))
486
993023a2
LC
487(test-equal "profile-collisions: no warnings"
488 '()
489 (check-profile-collisions (dummy-package "x")))
490
491(test-equal "profile-collisions: propagated inputs collide"
492 "propagated inputs p0@1 and p0@2 collide"
493 (let* ((p0 (dummy-package "p0" (version "1")))
494 (p0* (dummy-package "p0" (version "2")))
495 (p1 (dummy-package "p1" (propagated-inputs `(("p0" ,p0)))))
496 (p2 (dummy-package "p2" (propagated-inputs `(("p1" ,p1)))))
497 (p3 (dummy-package "p3" (propagated-inputs `(("p0" ,p0*)))))
498 (p4 (dummy-package "p4" (propagated-inputs
499 `(("p2" ,p2) ("p3", p3))))))
500 (single-lint-warning-message
501 (check-profile-collisions p4))))
502
503(test-assert "profile-collisions: propagated inputs collide, store items"
504 (string-match-or-error
505 "propagated inputs /[[:graph:]]+-p0-1 and /[[:graph:]]+-p0-1 collide"
506 (let* ((p0 (dummy-package "p0" (version "1")))
507 (p0* (dummy-package "p0" (version "1")
508 (inputs `(("x" ,(dummy-package "x"))))))
509 (p1 (dummy-package "p1" (propagated-inputs `(("p0" ,p0)))))
510 (p2 (dummy-package "p2" (propagated-inputs `(("p1" ,p1)))))
511 (p3 (dummy-package "p3" (propagated-inputs `(("p0" ,p0*)))))
512 (p4 (dummy-package "p4" (propagated-inputs
513 `(("p2" ,p2) ("p3", p3))))))
514 (single-lint-warning-message
515 (check-profile-collisions p4)))))
516
50fc2384
CB
517(test-equal "license: invalid license"
518 "invalid license field"
519 (single-lint-warning-message
520 (check-license (dummy-package "x" (license #f)))))
521
522(test-equal "home-page: wrong home-page"
523 "invalid value for home page"
524 (let ((pkg (package
525 (inherit (dummy-package "x"))
526 (home-page #f))))
527 (single-lint-warning-message
528 (check-home-page pkg))))
529
530(test-equal "home-page: invalid URI"
531 "invalid home page URL: \"foobar\""
532 (let ((pkg (package
533 (inherit (dummy-package "x"))
534 (home-page "foobar"))))
535 (single-lint-warning-message
536 (check-home-page pkg))))
537
37592014 538(test-assert "home-page: host not found"
50fc2384
CB
539 (let ((pkg (package
540 (inherit (dummy-package "x"))
541 (home-page "http://does-not-exist"))))
37592014 542 (warning-contains? "domain not found" (check-home-page pkg))))
907c98ac 543
c05ceaf2
MD
544(parameterize ((%http-server-port 9999))
545 ;; TODO skip this test if some process is currently listening at 9999
546 (test-equal "home-page: Connection refused"
547 "URI http://localhost:9999/foo/bar unreachable: Connection refused"
548 (let ((pkg (package
549 (inherit (dummy-package "x"))
550 (home-page (%local-url)))))
551 (single-lint-warning-message
552 (check-home-page pkg)))))
907c98ac 553
907c98ac 554(test-equal "home-page: 200"
50fc2384 555 '()
9323ab55 556 (with-http-server `((200 ,%long-string))
50fc2384
CB
557 (let ((pkg (package
558 (inherit (dummy-package "x"))
559 (home-page (%local-url)))))
560 (check-home-page pkg))))
907c98ac 561
c05ceaf2
MD
562(with-http-server `((200 "This is too small."))
563 (test-equal "home-page: 200 but short length"
564 (format #f "URI ~a returned suspiciously small file (18 bytes)"
565 (%local-url))
50fc2384
CB
566 (let ((pkg (package
567 (inherit (dummy-package "x"))
568 (home-page (%local-url)))))
569
570 (single-lint-warning-message
571 (check-home-page pkg)))))
bfcb3d76 572
c05ceaf2
MD
573(with-http-server `((404 ,%long-string))
574 (test-equal "home-page: 404"
575 (format #f "URI ~a not reachable: 404 (\"Such is life\")" (%local-url))
50fc2384
CB
576 (let ((pkg (package
577 (inherit (dummy-package "x"))
578 (home-page (%local-url)))))
579 (single-lint-warning-message
580 (check-home-page pkg)))))
b4f5e0e8 581
c05ceaf2
MD
582(with-http-server `((301 ,%long-string))
583 (test-equal "home-page: 301, invalid"
584 (format #f "invalid permanent redirect from ~a" (%local-url))
50fc2384
CB
585 (let ((pkg (package
586 (inherit (dummy-package "x"))
587 (home-page (%local-url)))))
588 (single-lint-warning-message
589 (check-home-page pkg)))))
61f28fe7 590
c05ceaf2
MD
591(with-http-server `((200 ,%long-string))
592 (let* ((initial-url (%local-url))
593 (redirect (build-response #:code 301
594 #:headers
595 `((location
596 . ,(string->uri initial-url))))))
597 (parameterize ((%http-server-port 0))
598 (with-http-server `((,redirect ""))
599 (test-equal "home-page: 301 -> 200"
600 (format #f "permanent redirect from ~a to ~a"
601 (%local-url) initial-url)
50fc2384
CB
602 (let ((pkg (package
603 (inherit (dummy-package "x"))
604 (home-page (%local-url)))))
605 (single-lint-warning-message
606 (check-home-page pkg))))))))
61f28fe7 607
c05ceaf2
MD
608(with-http-server `((404 "booh!"))
609 (let* ((initial-url (%local-url))
610 (redirect (build-response #:code 301
611 #:headers
612 `((location
613 . ,(string->uri initial-url))))))
614 (parameterize ((%http-server-port 0))
615 (with-http-server `((,redirect ""))
616 (test-equal "home-page: 301 -> 404"
617 (format #f "URI ~a not reachable: 404 (\"Such is life\")" (%local-url))
50fc2384
CB
618 (let ((pkg (package
619 (inherit (dummy-package "x"))
620 (home-page (%local-url)))))
621 (single-lint-warning-message
622 (check-home-page pkg))))))))
623
624
625(test-equal "source-file-name"
626 "the source file name should contain the package name"
627 (let ((pkg (dummy-package "x"
628 (version "3.2.1")
629 (source
630 (origin
631 (method url-fetch)
632 (uri "http://www.example.com/3.2.1.tar.gz")
633 (sha256 %null-sha256))))))
634 (single-lint-warning-message
635 (check-source-file-name pkg))))
636
637(test-equal "source-file-name: v prefix"
638 "the source file name should contain the package name"
639 (let ((pkg (dummy-package "x"
640 (version "3.2.1")
641 (source
642 (origin
643 (method url-fetch)
644 (uri "http://www.example.com/v3.2.1.tar.gz")
645 (sha256 %null-sha256))))))
646 (single-lint-warning-message
647 (check-source-file-name pkg))))
648
649(test-equal "source-file-name: bad checkout"
650 "the source file name should contain the package name"
651 (let ((pkg (dummy-package "x"
652 (version "3.2.1")
653 (source
654 (origin
655 (method git-fetch)
656 (uri (git-reference
657 (url "http://www.example.com/x.git")
658 (commit "0")))
659 (sha256 %null-sha256))))))
660 (single-lint-warning-message
661 (check-source-file-name pkg))))
662
663(test-equal "source-file-name: good checkout"
664 '()
665 (let ((pkg (dummy-package "x"
666 (version "3.2.1")
667 (source
668 (origin
669 (method git-fetch)
670 (uri (git-reference
671 (url "http://git.example.com/x.git")
672 (commit "0")))
673 (file-name (string-append "x-" version))
674 (sha256 %null-sha256))))))
675 (check-source-file-name pkg)))
676
677(test-equal "source-file-name: valid"
678 '()
679 (let ((pkg (dummy-package "x"
680 (version "3.2.1")
681 (source
682 (origin
683 (method url-fetch)
684 (uri "http://www.example.com/x-3.2.1.tar.gz")
685 (sha256 %null-sha256))))))
686 (check-source-file-name pkg)))
c180017b 687
50fc2384
CB
688(test-equal "source-unstable-tarball"
689 "the source URI should not be an autogenerated tarball"
690 (let ((pkg (dummy-package "x"
691 (source
692 (origin
693 (method url-fetch)
694 (uri "https://github.com/example/example/archive/v0.0.tar.gz")
695 (sha256 %null-sha256))))))
696 (single-lint-warning-message
697 (check-source-unstable-tarball pkg))))
698
699(test-equal "source-unstable-tarball: source #f"
700 '()
701 (let ((pkg (dummy-package "x"
702 (source #f))))
703 (check-source-unstable-tarball pkg)))
704
705(test-equal "source-unstable-tarball: valid"
706 '()
707 (let ((pkg (dummy-package "x"
708 (source
709 (origin
710 (method url-fetch)
711 (uri "https://github.com/example/example/releases/download/x-0.0/x-0.0.tar.gz")
712 (sha256 %null-sha256))))))
713 (check-source-unstable-tarball pkg)))
950d2ea4 714
50fc2384
CB
715(test-equal "source-unstable-tarball: package named archive"
716 '()
717 (let ((pkg (dummy-package "x"
718 (source
719 (origin
bfcb3d76 720 (method url-fetch)
50fc2384 721 (uri "https://github.com/example/archive/releases/download/x-0.0/x-0.0.tar.gz")
bfcb3d76 722 (sha256 %null-sha256))))))
50fc2384 723 (check-source-unstable-tarball pkg)))
bfcb3d76 724
50fc2384
CB
725(test-equal "source-unstable-tarball: not-github"
726 '()
727 (let ((pkg (dummy-package "x"
728 (source
729 (origin
950d2ea4 730 (method url-fetch)
50fc2384 731 (uri "https://bitbucket.org/archive/example/download/x-0.0.tar.gz")
950d2ea4 732 (sha256 %null-sha256))))))
50fc2384
CB
733 (check-source-unstable-tarball pkg)))
734
735(test-equal "source-unstable-tarball: git-fetch"
736 '()
737 (let ((pkg (dummy-package "x"
738 (source
739 (origin
740 (method git-fetch)
741 (uri (git-reference
b0e7b699 742 (url "https://github.com/archive/example")
50fc2384
CB
743 (commit "0")))
744 (sha256 %null-sha256))))))
745 (check-source-unstable-tarball pkg)))
746
50fc2384
CB
747(test-equal "source: 200"
748 '()
9323ab55 749 (with-http-server `((200 ,%long-string))
50fc2384
CB
750 (let ((pkg (package
751 (inherit (dummy-package "x"))
752 (source (origin
753 (method url-fetch)
754 (uri (%local-url))
755 (sha256 %null-sha256))))))
756 (check-source pkg))))
757
c05ceaf2
MD
758(with-http-server '((200 "This is too small."))
759 (test-equal "source: 200 but short length"
760 (format #f "URI ~a returned suspiciously small file (18 bytes)"
761 (%local-url))
50fc2384
CB
762 (let ((pkg (package
763 (inherit (dummy-package "x"))
764 (source (origin
765 (method url-fetch)
766 (uri (%local-url))
767 (sha256 %null-sha256))))))
768 (match (check-source pkg)
769 ((first-warning ; All source URIs are unreachable
770 (and (? lint-warning?) second-warning))
771 (lint-warning-message second-warning))))))
772
c05ceaf2
MD
773(with-http-server `((404 ,%long-string))
774 (test-equal "source: 404"
775 (format #f "URI ~a not reachable: 404 (\"Such is life\")"
776 (%local-url))
50fc2384
CB
777 (let ((pkg (package
778 (inherit (dummy-package "x"))
779 (source (origin
780 (method url-fetch)
781 (uri (%local-url))
782 (sha256 %null-sha256))))))
783 (match (check-source pkg)
784 ((first-warning ; All source URIs are unreachable
785 (and (? lint-warning?) second-warning))
786 (lint-warning-message second-warning))))))
950d2ea4 787
99b20428
LC
788(test-equal "source: 404 and 200"
789 '()
9323ab55 790 (with-http-server `((404 ,%long-string))
99b20428
LC
791 (let ((bad-url (%local-url)))
792 (parameterize ((%http-server-port (+ 1 (%http-server-port))))
9323ab55 793 (with-http-server `((200 ,%long-string))
99b20428
LC
794 (let ((pkg (package
795 (inherit (dummy-package "x"))
796 (source (origin
797 (method url-fetch)
798 (uri (list bad-url (%local-url)))
799 (sha256 %null-sha256))))))
800 ;; Since one of the two URLs is good, this should return the empty
801 ;; list.
802 (check-source pkg)))))))
803
c05ceaf2
MD
804(with-http-server `((200 ,%long-string))
805 (let* ((initial-url (%local-url))
806 (redirect (build-response #:code 301
807 #:headers
808 `((location
809 . ,(string->uri initial-url))))))
810 (parameterize ((%http-server-port 0))
811 (with-http-server `((,redirect ""))
812 (test-equal "source: 301 -> 200"
813 (format #f "permanent redirect from ~a to ~a"
814 (%local-url) initial-url)
50fc2384
CB
815 (let ((pkg (package
816 (inherit (dummy-package "x"))
817 (source (origin
818 (method url-fetch)
819 (uri (%local-url))
820 (sha256 %null-sha256))))))
821 (match (check-source pkg)
822 ((first-warning ; All source URIs are unreachable
823 (and (? lint-warning?) second-warning))
824 (lint-warning-message second-warning)))))))))
61f28fe7 825
c05ceaf2
MD
826(with-http-server `((200 ,%long-string))
827 (let* ((initial-url (%local-url))
828 (redirect (build-response #:code 301
829 #:headers
830 `((location
831 . ,(string->uri initial-url))))))
832 (parameterize ((%http-server-port 0))
833 (with-http-server `((,redirect ""))
834 (test-equal "source, git-reference: 301 -> 200"
835 (format #f "permanent redirect from ~a to ~a"
836 (%local-url) initial-url)
c1052667
LC
837 (let ((pkg (dummy-package
838 "x"
839 (source (origin
840 (method git-fetch)
841 (uri (git-reference (url (%local-url))
842 (commit "v1.0.0")))
843 (sha256 %null-sha256))))))
844 (single-lint-warning-message (check-source pkg))))))))
845
c05ceaf2
MD
846(with-http-server '((404 "booh!"))
847 (let* ((initial-url (%local-url))
848 (redirect (build-response #:code 301
849 #:headers
850 `((location
851 . ,(string->uri initial-url))))))
852 (parameterize ((%http-server-port 0))
853 (with-http-server `((,redirect ""))
854 (test-equal "source: 301 -> 404"
855 (format #f "URI ~a not reachable: 404 (\"Such is life\")"
856 (%local-url))
50fc2384
CB
857 (let ((pkg (package
858 (inherit (dummy-package "x"))
859 (source (origin
860 (method url-fetch)
861 (uri (%local-url))
862 (sha256 %null-sha256))))))
863 (match (check-source pkg)
864 ((first-warning ; The first warning says that all URI's are
865 ; unreachable
866 (and (? lint-warning?) second-warning))
867 (lint-warning-message second-warning)))))))))
868
869(test-equal "mirror-url"
870 '()
871 (let ((source (origin
872 (method url-fetch)
873 (uri "http://example.org/foo/bar.tar.gz")
874 (sha256 %null-sha256))))
875 (check-mirror-url (dummy-package "x" (source source)))))
876
877(test-equal "mirror-url: one suggestion"
878 "URL should be 'mirror://gnu/foo/foo.tar.gz'"
879 (let ((source (origin
880 (method url-fetch)
881 (uri "http://ftp.gnu.org/pub/gnu/foo/foo.tar.gz")
882 (sha256 %null-sha256))))
883 (single-lint-warning-message
884 (check-mirror-url (dummy-package "x" (source source))))))
885
886(test-equal "github-url"
887 '()
9323ab55 888 (with-http-server `((200 ,%long-string))
50fc2384
CB
889 (check-github-url
890 (dummy-package "x" (source
891 (origin
892 (method url-fetch)
893 (uri (%local-url))
894 (sha256 %null-sha256)))))))
0865d8a8
AI
895
896(let ((github-url "https://github.com/foo/bar/bar-1.0.tar.gz"))
50fc2384
CB
897 (test-equal "github-url: one suggestion"
898 (string-append
899 "URL should be '" github-url "'")
9323ab55
LC
900 (let ((redirect (build-response #:code 301
901 #:headers
902 `((location
903 . ,(string->uri github-url))))))
904 (with-http-server `((,redirect ""))
905 (let* ((initial-url (%local-url))
906 (redirect (build-response #:code 302
907 #:headers
908 `((location
909 . ,(string->uri initial-url))))))
c05ceaf2 910 (parameterize ((%http-server-port 0))
9323ab55
LC
911 (with-http-server `((,redirect ""))
912 (single-lint-warning-message
913 (check-github-url
914 (dummy-package "x" (source
915 (origin
916 (method url-fetch)
917 (uri (%local-url))
918 (sha256 %null-sha256))))))))))))
95c2bc49 919
50fc2384
CB
920 (test-equal "github-url: already the correct github url"
921 '()
922 (check-github-url
923 (dummy-package "x" (source
924 (origin
925 (method url-fetch)
926 (uri github-url)
927 (sha256 %null-sha256)))))))
928
929(test-equal "cve"
930 '()
571f6e7f 931 (mock ((guix lint) package-vulnerabilities (const '()))
50fc2384 932 (check-vulnerabilities (dummy-package "x"))))
5432734b 933
50fc2384
CB
934(test-equal "cve: one vulnerability"
935 "probably vulnerable to CVE-2015-1234"
fcb2318e 936 (let ((dummy-vulnerabilities
5432734b 937 (lambda (package)
fcb2318e
LC
938 (list (make-struct/no-tail
939 (@@ (guix cve) <vulnerability>)
940 "CVE-2015-1234"
941 (list (cons (package-name package)
942 (package-version package))))))))
943 (single-lint-warning-message
944 (check-vulnerabilities (dummy-package "pi" (version "3.14"))
945 dummy-vulnerabilities))))
5432734b 946
50fc2384
CB
947(test-equal "cve: one patched vulnerability"
948 '()
571f6e7f 949 (mock ((guix lint) package-vulnerabilities
4e70fe4d 950 (lambda (package)
79c03e55
LC
951 (list (make-struct/no-tail (@@ (guix cve) <vulnerability>)
952 "CVE-2015-1234"
953 (list (cons (package-name package)
954 (package-version package)))))))
50fc2384
CB
955 (check-vulnerabilities
956 (dummy-package "pi"
957 (version "3.14")
958 (source
959 (dummy-origin
960 (patches
961 (list "/a/b/pi-CVE-2015-1234.patch"))))))))
962
963(test-equal "cve: known safe from vulnerability"
964 '()
571f6e7f 965 (mock ((guix lint) package-vulnerabilities
f4007b25 966 (lambda (package)
79c03e55
LC
967 (list (make-struct/no-tail (@@ (guix cve) <vulnerability>)
968 "CVE-2015-1234"
969 (list (cons (package-name package)
970 (package-version package)))))))
50fc2384
CB
971 (check-vulnerabilities
972 (dummy-package "pi"
973 (version "3.14")
974 (properties `((lint-hidden-cve . ("CVE-2015-1234"))))))))
975
976(test-equal "cve: vulnerability fixed in replacement version"
977 '()
571f6e7f 978 (mock ((guix lint) package-vulnerabilities
9bee2bd1
LC
979 (lambda (package)
980 (match (package-version package)
981 ("0"
79c03e55
LC
982 (list (make-struct/no-tail (@@ (guix cve) <vulnerability>)
983 "CVE-2015-1234"
984 (list (cons (package-name package)
985 (package-version package))))))
9bee2bd1
LC
986 ("1"
987 '()))))
50fc2384
CB
988 (check-vulnerabilities
989 (dummy-package
990 "foo" (version "0")
991 (replacement (dummy-package "foo" (version "1")))))))
992
993(test-equal "cve: patched vulnerability in replacement"
994 '()
571f6e7f 995 (mock ((guix lint) package-vulnerabilities
5c6a062d 996 (lambda (package)
79c03e55
LC
997 (list (make-struct/no-tail (@@ (guix cve) <vulnerability>)
998 "CVE-2015-1234"
999 (list (cons (package-name package)
1000 (package-version package)))))))
50fc2384
CB
1001 (check-vulnerabilities
1002 (dummy-package
1003 "pi" (version "3.14") (source (dummy-origin))
1004 (replacement (dummy-package
1005 "pi" (version "3.14")
1006 (source
1007 (dummy-origin
1008 (patches
1009 (list "/a/b/pi-CVE-2015-1234.patch"))))))))))
1010
1011(test-equal "formatting: lonely parentheses"
1012 "parentheses feel lonely, move to the previous or next line"
1013 (single-lint-warning-message
1014 (check-formatting
1015 (dummy-package "ugly as hell!"
1016 )
1017 )))
e0566f12 1018
40a7d4e5 1019(test-assert "formatting: tabulation"
50fc2384
CB
1020 (string-match-or-error
1021 "tabulation on line [0-9]+, column [0-9]+"
1022 (single-lint-warning-message
1023 (check-formatting (dummy-package "leave the tab here: ")))))
40a7d4e5
LC
1024
1025(test-assert "formatting: trailing white space"
50fc2384
CB
1026 (string-match-or-error
1027 "trailing white space .*"
1028 ;; Leave the trailing white space on the next line!
1029 (single-lint-warning-message
1030 (check-formatting (dummy-package "x")))))
40a7d4e5
LC
1031
1032(test-assert "formatting: long line"
50fc2384
CB
1033 (string-match-or-error
1034 "line [0-9]+ is way too long \\([0-9]+ characters\\)"
1035 (single-lint-warning-message (check-formatting
1036 (dummy-package "x")) ;here is a stupid comment just to make a long line
1037 )))
1038
1039(test-equal "formatting: alright"
1040 '()
1041 (check-formatting (dummy-package "x")))
40a7d4e5 1042
55549c7b
LC
1043(test-assert "archival: missing content"
1044 (let* ((origin (origin
1045 (method url-fetch)
1046 (uri "http://example.org/foo.tgz")
1047 (sha256 (make-bytevector 32))))
bc4d81d2
LC
1048 (warnings (with-http-server '((404 "Not archived.")
1049 (404 "Not in Disarchive database."))
55549c7b 1050 (parameterize ((%swh-base-url (%local-url)))
bc4d81d2
LC
1051 (mock ((guix download) %disarchive-mirrors
1052 (list (%local-url)))
1053 (check-archival (dummy-package "x"
1054 (source origin))))))))
55549c7b
LC
1055 (warning-contains? "not archived" warnings)))
1056
1057(test-equal "archival: content available"
1058 '()
1059 (let* ((origin (origin
1060 (method url-fetch)
1061 (uri "http://example.org/foo.tgz")
1062 (sha256 (make-bytevector 32))))
1063 ;; https://archive.softwareheritage.org/api/1/content/
1064 (content "{ \"checksums\": {}, \"data_url\": \"xyz\",
1065 \"length\": 42 }"))
1066 (with-http-server `((200 ,content))
1067 (parameterize ((%swh-base-url (%local-url)))
1068 (check-archival (dummy-package "x" (source origin)))))))
1069
bc4d81d2
LC
1070(test-equal "archival: content unavailable but disarchive available"
1071 '()
1072 (let* ((origin (origin
1073 (method url-fetch)
1074 (uri "http://example.org/foo.tgz")
1075 (sha256 (make-bytevector 32))))
1076 (disarchive (object->string
1077 '(disarchive (version 0)
1078 ...
1079 "swh:1:dir:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")))
1080 ;; https://archive.softwareheritage.org/api/1/directory/
1081 (directory "[ { \"checksums\": {},
1082 \"dir_id\": \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",
1083 \"type\": \"file\",
1084 \"name\": \"README\"
1085 \"length\": 42 } ]"))
1086 (with-http-server `((404 "") ;lookup-content
1087 (200 ,disarchive) ;Disarchive database lookup
1088 (200 ,directory)) ;lookup-directory
1089 (mock ((guix download) %disarchive-mirrors (list (%local-url)))
1090 (parameterize ((%swh-base-url (%local-url)))
1091 (check-archival (dummy-package "x" (source origin))))))))
1092
55549c7b
LC
1093(test-assert "archival: missing revision"
1094 (let* ((origin (origin
1095 (method git-fetch)
1096 (uri (git-reference
1097 (url "http://example.org/foo.git")
1098 (commit "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")))
1099 (sha256 (make-bytevector 32))))
1100 ;; https://archive.softwareheritage.org/api/1/origin/save/
1101 (save "{ \"origin_url\": \"http://example.org/foo.git\",
1102 \"save_request_date\": \"2014-11-17T22:09:38+01:00\",
1103 \"save_request_status\": \"accepted\",
1104 \"save_task_status\": \"scheduled\" }")
1105 (warnings (with-http-server `((404 "No revision.") ;lookup-revision
1106 (404 "No origin.") ;lookup-origin
1107 (200 ,save)) ;save-origin
1108 (parameterize ((%swh-base-url (%local-url)))
1109 (check-archival (dummy-package "x" (source origin)))))))
1110 (warning-contains? "scheduled" warnings)))
1111
1112(test-equal "archival: revision available"
1113 '()
1114 (let* ((origin (origin
1115 (method git-fetch)
1116 (uri (git-reference
1117 (url "http://example.org/foo.git")
1118 (commit "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")))
1119 (sha256 (make-bytevector 32))))
1120 ;; https://archive.softwareheritage.org/api/1/revision/
1121 (revision "{ \"author\": {}, \"parents\": [],
1122 \"date\": \"2014-11-17T22:09:38+01:00\" }"))
1123 (with-http-server `((200 ,revision))
1124 (parameterize ((%swh-base-url (%local-url)))
1125 (check-archival (dummy-package "x" (source origin)))))))
1126
1127(test-assert "archival: rate limit reached"
1128 ;; We should get a single warning stating that the rate limit was reached,
1129 ;; and nothing more, in particular no other HTTP requests.
1130 (let* ((origin (origin
1131 (method url-fetch)
1132 (uri "http://example.org/foo.tgz")
1133 (sha256 (make-bytevector 32))))
1134 (too-many (build-response
1135 #:code 429
1136 #:reason-phrase "Too many requests"
1137 #:headers '((x-ratelimit-remaining . "0")
1138 (x-ratelimit-reset . "3000000000"))))
1139 (warnings (with-http-server `((,too-many "Rate limit reached."))
1140 (parameterize ((%swh-base-url (%local-url)))
1141 (append-map (lambda (name)
1142 (check-archival
1143 (dummy-package name (source origin))))
1144 '("x" "y" "z"))))))
1145 (string-contains (single-lint-warning-message warnings)
1146 "rate limit reached")))
1147
464b1fff
TS
1148(test-assert "haskell-stackage"
1149 (let* ((stackage (string-append "{ \"packages\": [{"
1150 " \"name\":\"x\","
1151 " \"version\":\"1.0\" }]}"))
1152 (packages (map (lambda (version)
1153 (dummy-package
1154 (string-append "ghc-x")
1155 (version version)
1156 (source
1157 (dummy-origin
1158 (method url-fetch)
1159 (uri (string-append
1160 "https://hackage.haskell.org/package/"
1161 "x-" version "/x-" version ".tar.gz"))))))
1162 '("0.9" "1.0" "2.0")))
1163 (warnings (pk (with-http-server `((200 ,stackage) ; memoized
1164 (200 "name: x\nversion: 1.0\n")
1165 (200 "name: x\nversion: 1.0\n")
1166 (200 "name: x\nversion: 1.0\n"))
1167 (parameterize ((%hackage-url (%local-url))
1168 (%stackage-url (%local-url)))
1169 (append-map check-haskell-stackage packages))))))
1170 (match warnings
1171 (((? lint-warning? warning))
1172 (and (string=? (package-version (lint-warning-package warning)) "2.0")
1173 (string-contains (lint-warning-message warning)
1174 "ahead of Stackage LTS version"))))))
1175
b4f5e0e8
CR
1176(test-end "lint")
1177
907c98ac 1178;; Local Variables:
9323ab55 1179;; eval: (put 'with-http-server 'scheme-indent-function 1)
4fbf4ca5 1180;; eval: (put 'with-warnings 'scheme-indent-function 0)
907c98ac 1181;; End: