projects/guix·blob javascript-team · refs

1615 lines · 64.3 KiB

1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2012, 2013 Cyril Roelandt <[email protected]>
3;;; Copyright © 2014, 2015, 2016 Eric Bavier <[email protected]>
4;;; Copyright © 2014-2025 Ludovic Courtès <[email protected]>
5;;; Copyright © 2015, 2016 Mathieu Lirzin <[email protected]>
6;;; Copyright © 2016 Hartmut Goebel <[email protected]>
7;;; Copyright © 2017 Alex Kost <[email protected]>
8;;; Copyright © 2017, 2022, 2025 Efraim Flashner <[email protected]>
9;;; Copyright © 2018, 2019 Arun Isaac <[email protected]>
10;;; Copyright © 2020 Timothy Sample <[email protected]>
11;;; Copyright © 2020 Tobias Geerinckx-Rice <[email protected]>
12;;; Copyright © 2021 Xinglu Chen <[email protected]>
13;;; Copyright © 2021, 2023 Maxime Devos <[email protected]>
14;;; Copyright © 2024 Gabriel Wicki <[email protected]>
15;;;
16;;; This file is part of GNU Guix.
17;;;
18;;; GNU Guix is free software; you can redistribute it and/or modify it
19;;; under the terms of the GNU General Public License as published by
20;;; the Free Software Foundation; either version 3 of the License, or (at
21;;; your option) any later version.
22;;;
23;;; GNU Guix is distributed in the hope that it will be useful, but
24;;; WITHOUT ANY WARRANTY; without even the implied warranty of
25;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26;;; GNU General Public License for more details.
27;;;
28;;; You should have received a copy of the GNU General Public License
29;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
30
31;; Avoid interference.
32(unsetenv "http_proxy")
33
34(define-module (test-lint)
35 #:use-module (guix tests)
36 #:use-module (guix tests http)
37 #:use-module (guix download)
38 #:use-module (guix git-download)
39 #:use-module (guix svn-download)
40 #:use-module (guix build-system texlive)
41 #:use-module (guix build-system emacs)
42 #:use-module (guix build-system gnu)
43 #:use-module (guix packages)
44 #:use-module (guix lint)
45 #:use-module (guix ui)
46 #:use-module (guix swh)
47 #:use-module ((guix gexp)
48 #:select (gexp local-file computed-file gexp?))
49 #:use-module ((guix utils) #:select (call-with-temporary-directory))
50 #:use-module ((guix import hackage) #:select (%hackage-url))
51 #:use-module ((guix import stackage) #:select (%stackage-url))
52 #:use-module (gnu packages)
53 #:use-module (gnu packages glib)
54 #:use-module (gnu packages pkg-config)
55 #:use-module (gnu packages python-build)
56 #:use-module ((gnu packages bash) #:select (bash bash-minimal))
57 #:use-module (web uri)
58 #:use-module (web server)
59 #:use-module (web server http)
60 #:use-module (web response)
61 #:use-module (ice-9 match)
62 #:use-module (ice-9 regex)
63 #:use-module (ice-9 getopt-long)
64 #:use-module (ice-9 pretty-print)
65 #:use-module (rnrs bytevectors)
66 #:use-module (srfi srfi-1)
67 #:use-module (srfi srfi-9 gnu)
68 #:use-module (srfi srfi-26)
69 #:use-module (srfi srfi-64))
70
71;; Test the linter.
72
73(define %null-sha256
74 ;; SHA256 of the empty string.
75 (base32
76 "0mdqa9w1p6cmli6976v4wi0sw9r4p5prkj7lzfd1877wk11c9c73"))
77
78(define %long-string
79 (make-string 2000 #\a))
80
81(define (string-match-or-error pattern str)
82 (or (string-match pattern str)
83 (error str "did not match" pattern)))
84
85(define single-lint-warning-message
86 (match-lambda
87 (((and (? lint-warning?) warning))
88 (lint-warning-message warning))))
89
90(define (warning-contains? str warnings)
91 "Return true if WARNINGS is a singleton with a warning that contains STR."
92 (match warnings
93 (((? lint-warning? warning))
94 (string-contains (lint-warning-message warning) str))))
95
96
97(test-begin "lint")
98
99(test-equal "description: not a string"
100 "invalid description: foobar"
101 (single-lint-warning-message
102 (check-description-style
103 (dummy-package "x" (description 'foobar)))))
104
105(test-equal "description: not empty"
106 "description should not be empty"
107 (single-lint-warning-message
108 (check-description-style
109 (dummy-package "x" (description "")))))
110
111(test-equal "description: does not end with a period"
112 "description should end with a period"
113 (single-lint-warning-message
114 (let ((pkg (dummy-package "x"
115 (description "This package provides a bad description"))))
116 (check-description-style pkg))))
117
118(test-equal "description: invalid Texinfo markup"
119 "Texinfo markup in description is invalid"
120 (single-lint-warning-message
121 (check-description-style
122 (dummy-package "x" (description (identity "f{oo}b@r."))))))
123
124(test-equal "description: does not start with an upper-case letter"
125 "description should start with an upper-case letter or digit"
126 (single-lint-warning-message
127 (let ((pkg (dummy-package "x"
128 (description "bad description."))))
129 (check-description-style pkg))))
130
131(test-equal "description: may start with texinfo markup"
132 '()
133 (check-description-style
134 (dummy-package "x" (description "@emph{Maxwell Equations of Software}."))))
135
136(test-equal "description: may start with a digit"
137 '()
138 (let ((pkg (dummy-package "x"
139 (description "2-component library."))))
140 (check-description-style pkg)))
141
142(test-equal "description: may start with lower-case package name"
143 '()
144 (let ((pkg (dummy-package "x"
145 (description "x is a dummy package."))))
146 (check-description-style pkg)))
147
148(test-equal "description: may start with beginning of package name"
149 '()
150 (let ((pkg (dummy-package "xyz-0.1"
151 (description "xyz is a dummy package."))))
152 (check-description-style pkg)))
153
154(test-equal "description: may start with end of package name"
155 '()
156 (let ((pkg (dummy-package "foobar-xyz"
157 (description "xyz is a dummy package."))))
158 (check-description-style pkg)))
159
160(test-equal "description: may start with non-hyphenated package name"
161 '()
162 (let ((pkg (dummy-package "foobar-xyz-minimal"
163 (description "foobar_xyz is a dummy package."))))
164 (check-description-style pkg)))
165
166(test-equal "description: may start with end of package name"
167 '()
168 (let ((pkg (dummy-package "foo-bar"
169 (description "bar is some thing in foo."))))
170 (check-description-style pkg)))
171
172(test-equal "description: two spaces after end of sentence"
173 "sentences in description should be followed by two spaces; possible infraction at 3"
174 (single-lint-warning-message
175 (let ((pkg (dummy-package "x"
176 (description "Bad. Quite bad."))))
177 (check-description-style pkg))))
178
179(test-equal "description: end-of-sentence detection with abbreviations"
180 '()
181 (let ((pkg (dummy-package "x"
182 (description
183 "O. Person e.g. Foo, i.e. Bar resp. Baz (a.k.a. DVD). Name et al. cf. some paper."))))
184 (check-description-style pkg)))
185
186(test-equal "description: may not contain trademark signs: ™"
187 "description should not contain trademark sign '™' at 20"
188 (single-lint-warning-message
189 (let ((pkg (dummy-package "x"
190 (description "Does The Right Thing™."))))
191 (check-description-style pkg))))
192
193(test-equal "description: may not contain trademark signs: ®"
194 "description should not contain trademark sign '®' at 17"
195 (single-lint-warning-message
196 (let ((pkg (dummy-package "x"
197 (description "Works with Format®."))))
198 (check-description-style pkg))))
199
200(test-equal "description: suggest ornament instead of quotes"
201 "use @code or similar ornament instead of quotes"
202 (single-lint-warning-message
203 (let ((pkg (dummy-package "x"
204 (description "This is a 'quoted' thing."))))
205 (check-description-style pkg))))
206
207(test-equal "description: leading whitespace"
208 "description contains leading whitespace"
209 (single-lint-warning-message
210 (let ((pkg (dummy-package "x"
211 (description " Whitespace."))))
212 (check-description-style pkg))))
213
214(test-equal "description: trailing whitespace"
215 "description contains trailing whitespace"
216 (single-lint-warning-message
217 (let ((pkg (dummy-package "x"
218 (description "Whitespace. "))))
219 (check-description-style pkg))))
220
221(test-equal "description: pluralized 'This package'"
222 "description contains typo 'This packages', should be 'This package'"
223 (single-lint-warning-message
224 (let ((pkg (dummy-package "x"
225 (description "This packages is a typo."))))
226 (check-description-style pkg))))
227
228(test-equal "description: grammar 'allows to'"
229 "description contains typo 'allows to '"
230 (single-lint-warning-message
231 (let ((pkg (dummy-package "x"
232 (description "This package allows to do stuff."))))
233 (check-description-style pkg))))
234
235(test-equal "description: grammar 'allows to' 2"
236 '()
237 (let ((pkg (dummy-package "x"
238 (description "This package allows tokenization."))))
239 (check-description-style pkg)))
240
241(test-equal "synopsis: not a string"
242 "invalid synopsis: #f"
243 (single-lint-warning-message
244 (let ((pkg (dummy-package "x"
245 (synopsis #f))))
246 (check-synopsis-style pkg))))
247
248(test-equal "synopsis: not empty"
249 "synopsis should not be empty"
250 (single-lint-warning-message
251 (let ((pkg (dummy-package "x"
252 (synopsis ""))))
253 (check-synopsis-style pkg))))
254
255(test-equal "synopsis: valid Texinfo markup"
256 "Texinfo markup in synopsis is invalid"
257 (single-lint-warning-message
258 (check-synopsis-style
259 (dummy-package "x" (synopsis (identity "Bad $@ texinfo"))))))
260
261(test-equal "synopsis: does not start with an upper-case letter"
262 "synopsis should start with an upper-case letter or digit"
263 (single-lint-warning-message
264 (let ((pkg (dummy-package "x"
265 (synopsis "bad synopsis"))))
266 (check-synopsis-style pkg))))
267
268(test-equal "synopsis: may start with a digit"
269 '()
270 (let ((pkg (dummy-package "x"
271 (synopsis "5-dimensional frobnicator"))))
272 (check-synopsis-style pkg)))
273
274(test-equal "synopsis: ends with a period"
275 "no period allowed at the end of the synopsis"
276 (single-lint-warning-message
277 (let ((pkg (dummy-package "x"
278 (synopsis "Bad synopsis."))))
279 (check-synopsis-style pkg))))
280
281(test-equal "synopsis: ends with 'etc.'"
282 '()
283 (let ((pkg (dummy-package "x"
284 (synopsis "Foo, bar, etc."))))
285 (check-synopsis-style pkg)))
286
287(test-equal "synopsis: starts with 'A'"
288 "no article allowed at the beginning of the synopsis"
289 (single-lint-warning-message
290 (let ((pkg (dummy-package "x"
291 (synopsis "A bad synopŝis"))))
292 (check-synopsis-style pkg))))
293
294(test-equal "synopsis: starts with 'An'"
295 "no article allowed at the beginning of the synopsis"
296 (single-lint-warning-message
297 (let ((pkg (dummy-package "x"
298 (synopsis "An awful synopsis"))))
299 (check-synopsis-style pkg))))
300
301(test-equal "synopsis: starts with 'a'"
302 '("no article allowed at the beginning of the synopsis"
303 "synopsis should start with an upper-case letter or digit")
304 (sort
305 (map
306 lint-warning-message
307 (let ((pkg (dummy-package "x"
308 (synopsis "a bad synopsis"))))
309 (check-synopsis-style pkg)))
310 string<?))
311
312(test-equal "synopsis: starts with 'an'"
313 '("no article allowed at the beginning of the synopsis"
314 "synopsis should start with an upper-case letter or digit")
315 (sort
316 (map
317 lint-warning-message
318 (let ((pkg (dummy-package "x"
319 (synopsis "an awful synopsis"))))
320 (check-synopsis-style pkg)))
321 string<?))
322
323(test-equal "synopsis: starts with texinfo markup"
324 '()
325 (let ((pkg (dummy-package "x"
326 (synopsis "@code{help}"))))
327 (check-synopsis-style pkg)))
328
329(test-equal "synopsis: too long"
330 "synopsis should be less than 80 characters long"
331 (single-lint-warning-message
332 (let ((pkg (dummy-package "x"
333 (synopsis (make-string 80 #\X)))))
334 (check-synopsis-style pkg))))
335
336(test-equal "synopsis: start with package name"
337 "synopsis should not start with the package name"
338 (single-lint-warning-message
339 (let ((pkg (dummy-package "x"
340 (name "Foo")
341 (synopsis "Foo, a nice package"))))
342 (check-synopsis-style pkg))))
343
344(test-equal "synopsis: start with package name prefix"
345 '()
346 (let ((pkg (dummy-package "arb"
347 (synopsis "Arbitrary precision"))))
348 (check-synopsis-style pkg)))
349
350(test-equal "synopsis: start with abbreviation"
351 '()
352 (let ((pkg (dummy-package "uucp"
353 ;; Same problem with "APL interpreter", etc.
354 (synopsis "UUCP implementation")
355 (description "Imagine this is Taylor UUCP."))))
356 (check-synopsis-style pkg)))
357
358(test-equal "synopsis: contains trailing whitespace"
359 "synopsis contains trailing whitespace"
360 (single-lint-warning-message
361 (let ((pkg (dummy-package "x"
362 (synopsis "Whitespace "))))
363 (check-synopsis-style pkg))))
364
365(test-equal "name: use underscore in package name"
366 "name should use hyphens instead of underscores"
367 (single-lint-warning-message
368 (let ((pkg (dummy-package "under_score")))
369 (check-name pkg))))
370
371(test-equal "tests-true: #:tests? must not be set to #t"
372 "#:tests? must not be explicitly set to #t"
373 (single-lint-warning-message
374 (let ((pkg (dummy-package "x" (arguments '(#:tests? #t)))))
375 (check-tests-true pkg))))
376
377(test-equal "tests-true: absent #:tests? is acceptable"
378 '()
379 (let ((pkg (dummy-package "x")))
380 (check-tests-true pkg)))
381
382(test-equal "tests-true: #:tests? #f is acceptable"
383 '()
384 (let ((pkg (dummy-package "x" (arguments '(#:tests? #f)))))
385 (check-tests-true pkg)))
386
387(test-equal "tests-true: #:tests? #t acceptable when compiling natively"
388 '()
389 (let ((pkg (dummy-package "x"
390 (arguments
391 `(#:tests? ,(not (%current-target-system)))))))
392 (check-tests-true pkg)))
393
394(test-equal "compiler-for-target: unconditional CC=gcc is unacceptable"
395 "'CC' should be set to '(cc-for-target)' instead of 'gcc'"
396 (single-lint-warning-message
397 (check-compiler-for-target
398 (dummy-package "x" (arguments '(#:make-flags '("CC=gcc")))))))
399
400(test-equal "compiler-for-target: looks through G-expressions"
401 "'CC' should be set to '(cc-for-target)' instead of 'gcc'"
402 (single-lint-warning-message
403 (check-compiler-for-target
404 (dummy-package "x" (arguments
405 (list #:make-flags #~(list "CC=gcc")))))))
406
407(test-equal "compiler-for-target: (cc-for-target) is acceptable"
408 '()
409 (check-compiler-for-target
410 (dummy-package "x"
411 (arguments
412 (list #:make-flags
413 #~(list (string-append "CC=" (cc-for-target))))))))
414
415(test-equal "compiler-for-target: CC=gcc is acceptable when target=#false"
416 '()
417 (check-compiler-for-target
418 ;; This (dummy) package consists purely of architecture-independent data.
419 (dummy-package "tzdata"
420 (arguments
421 (list #:target #false
422 #:make-flags #~(list "CC=gcc"))))))
423
424(test-equal "misplaced-flags: make-flag is incorrect"
425 "'CMAKE_BUILD_TYPE' should be set with the 'build-type' flag"
426 (single-lint-warning-message
427 (check-misplaced-flags
428 (dummy-package "x"
429 (arguments
430 (list #:make-flags #~(list "-DCMAKE_BUILD_TYPE=RELEASE"
431 "-DTESTS=ON")))))))
432
433(test-equal "misplaced-flags: configure-flag is incorrect"
434 "'buildtype' should be set with the 'build-type' flag"
435 (single-lint-warning-message
436 (check-misplaced-flags
437 (dummy-package "x"
438 (arguments
439 (list #:configure-flags #~(list "--buildtype=release"
440 "--with-tests")))))))
441
442(test-equal "misplaced-flags: cargo feature flags"
443 "'features' should be set with the 'features' flag"
444 (single-lint-warning-message
445 (check-misplaced-flags
446 (dummy-package "x"
447 (arguments
448 '(#:cargo-test-flags
449 '("--features" "force_system_lib")))))))
450
451(test-equal "misplaced-flags: flags without g-exp is incorrect"
452 "'CMAKE_BUILD_TYPE' should be set with the 'build-type' flag"
453 (single-lint-warning-message
454 (check-misplaced-flags
455 (dummy-package "x"
456 (arguments
457 '(#:make-flags '("-DCMAKE_BUILD_TYPE=RELEASE"
458 "-DTESTS=ON")))))))
459
460(test-equal "misplaced-flags: build-type set correctly"
461 '()
462 (check-misplaced-flags
463 (dummy-package "x"
464 (arguments
465 (list #:build-type "RELEASE"
466 #:make-flags #~(list "-DTESTS=ON"))))))
467
468;; The emacs-build-system sets #:tests? #f by default.
469(test-equal "tests-true: #:tests? #t acceptable for emacs packages"
470 '()
471 (let ((pkg (dummy-package "x"
472 (build-system emacs-build-system)
473 (arguments
474 `(#:tests? #t)))))
475 (check-tests-true pkg)))
476
477;; Likewise, though the 'check' phase is deleted by default,
478;; so #:tests? #t won't be useful by itself.
479(test-equal "tests-true: #:tests? #t acceptable for texlive packages"
480 '()
481 (let ((pkg (dummy-package "x"
482 (build-system texlive-build-system)
483 (arguments
484 `(#:tests? #t)))))
485 (check-tests-true pkg)))
486
487(test-equal "inputs: pkg-config is probably a native input"
488 "'pkg-config' should probably be a native input"
489 (single-lint-warning-message
490 (let ((pkg (dummy-package "x"
491 (inputs `(("pkg-config" ,pkg-config))))))
492 (check-inputs-should-be-native pkg))))
493
494(test-equal "inputs: glib:bin is probably a native input"
495 "'glib:bin' should probably be a native input"
496 (single-lint-warning-message
497 (let ((pkg (dummy-package "x"
498 (inputs `(("glib" ,glib "bin"))))))
499 (check-inputs-should-be-native pkg))))
500
501(test-equal
502 "inputs: python-pip should not be an input at all (input)"
503 "'python-pip' should probably not be an input at all"
504 (single-lint-warning-message
505 (let ((pkg (dummy-package "x"
506 (inputs `(("python-pip" ,python-pip))))))
507 (check-inputs-should-not-be-an-input-at-all pkg))))
508
509(test-equal
510 "inputs: python-pip should not be an input at all (native-input)"
511 "'python-pip' should probably not be an input at all"
512 (single-lint-warning-message
513 (let ((pkg (dummy-package "x"
514 (native-inputs
515 `(("python-pip" ,python-pip))))))
516 (check-inputs-should-not-be-an-input-at-all pkg))))
517
518(test-equal
519 "inputs: python-pip should not be an input at all (propagated-input)"
520 "'python-pip' should probably not be an input at all"
521 (single-lint-warning-message
522 (let ((pkg (dummy-package "x"
523 (propagated-inputs
524 `(("python-pip" ,python-pip))))))
525 (check-inputs-should-not-be-an-input-at-all pkg))))
526
527(test-assert "input labels: no warnings"
528 (let ((pkg (dummy-package "x"
529 (inputs `(("glib" ,glib)
530 ("pkg-config" ,pkg-config))))))
531 (null? (check-input-labels pkg))))
532
533(test-equal "input labels: one warning"
534 "label 'pkgkonfig' does not match package name 'pkg-config'"
535 (single-lint-warning-message
536 (let ((pkg (dummy-package "x"
537 (inputs `(("glib" ,glib)
538 ("pkgkonfig" ,pkg-config))))))
539 (check-input-labels pkg))))
540
541(test-equal "explicit #:sh argument to 'wrap-program' is acceptable"
542 '()
543 (let* ((phases
544 ;; Loosely based on the "catfish" package
545 `(modify-phases %standard-phases
546 (add-after 'install 'wrap
547 (lambda* (#:key inputs outputs #:allow-other-keys)
548 (define catfish (string-append (assoc-ref outputs "out")
549 "/bin/catfish"))
550 (define hsab (string-append (assoc-ref inputs "hsab")
551 "/bin/hsab"))
552 (wrap-program catfish #:sh hsab
553 `("PYTHONPATH" = (,"blabla")))))))
554 (pkg (dummy-package "x" (arguments `(#:phases ,phases)))))
555 (check-wrapper-inputs pkg)))
556
557(test-equal
558 "'check-wrapper-inputs' detects 'wrap-program' without \"bash\" in inputs"
559 "\"bash-minimal\" should be in 'inputs' when 'wrap-program' is used"
560 (let* ((phases
561 `(modify-phases %standard-phases
562 (add-after 'install 'wrap
563 (lambda _
564 (wrap-program the-binary bla-bla)))))
565 (pkg (dummy-package "x" (arguments `(#:phases ,phases)))))
566 (single-lint-warning-message (check-wrapper-inputs pkg))))
567
568(test-equal
569 "'check-wrapper-inputs' detects 'wrap-qt-program' without \"bash\" in inputs"
570 "\"bash-minimal\" should be in 'inputs' when 'wrap-qt-program' is used"
571 (let* ((phases
572 `(modify-phases %standard-phases
573 (add-after 'install 'qtwrap
574 (lambda _
575 (wrap-qt-program the-binary bla-bla)))))
576 (pkg (dummy-package "x" (arguments `(#:phases ,phases)))))
577 (single-lint-warning-message (check-wrapper-inputs pkg))))
578
579(test-equal "\"bash\" in 'inputs' satisfies 'check-wrapper-inputs'"
580 '()
581 (let* ((phases
582 `(modify-phases %standard-phases
583 (add-after 'install 'wrap
584 (lambda _
585 (wrap-program the-binary bla-bla)))))
586 (pkg (dummy-package "x" (arguments `(#:phases ,phases))
587 (inputs `(("bash" ,bash))))))
588 (check-wrapper-inputs pkg)))
589
590(test-equal "\"bash-minimal\" in 'inputs' satisfies 'check-wrapper-inputs'"
591 '()
592 (let* ((phases
593 `(modify-phases %standard-phases
594 (add-after 'install 'wrap
595 (lambda _
596 (wrap-program THE-BINARY bla-bla)))))
597 (pkg (dummy-package "x" (arguments `(#:phases ,phases))
598 (inputs `(("bash-minimal" ,bash-minimal))))))
599 (check-wrapper-inputs pkg)))
600
601(test-equal "'cut' doesn't hide bad usages of 'wrap-program'"
602 "\"bash-minimal\" should be in 'inputs' when 'wrap-program' is used"
603 (let* ((phases
604 ;; Taken from the "straw-viewer" package
605 `(modify-phases %standard-phases
606 (add-after 'install 'wrap-program
607 (lambda* (#:key outputs #:allow-other-keys)
608 (let* ((out (assoc-ref outputs "out"))
609 (bin-dir (string-append out "/bin/"))
610 (site-dir (string-append out "/lib/perl5/site_perl/"))
611 (lib-path (getenv "PERL5LIB")))
612 (for-each (cut wrap-program <>
613 `("PERL5LIB" ":" prefix
614 (,lib-path ,site-dir)))
615 (find-files bin-dir)))))))
616 (pkg (dummy-package "x" (arguments `(#:phases ,phases)))))
617 (single-lint-warning-message (check-wrapper-inputs pkg))))
618
619(test-equal "bogus phase specifications don't crash the linter"
620 "invalid phase clause"
621 (let* ((phases
622 `(modify-phases %standard-phases
623 (add-invalid)))
624 (pkg (dummy-package "x" (arguments `(#:phases ,phases)))))
625 (single-lint-warning-message (check-wrapper-inputs pkg))))
626
627(test-equal "file patches: different file name -> warning"
628 "file names of patches should start with the package name"
629 (single-lint-warning-message
630 (let ((pkg (dummy-package "x"
631 (source
632 (dummy-origin
633 (patches (list "/path/to/y.patch")))))))
634 (check-patch-file-names pkg))))
635
636(test-equal "file patches: same file name -> no warnings"
637 '()
638 (let ((pkg (dummy-package "x"
639 (source
640 (dummy-origin
641 (patches (list "/path/to/x.patch")))))))
642 (check-patch-file-names pkg)))
643
644(test-equal "<origin> patches: different file name -> warning"
645 "file names of patches should start with the package name"
646 (single-lint-warning-message
647 (let ((pkg (dummy-package "x"
648 (source
649 (dummy-origin
650 (patches
651 (list
652 (dummy-origin
653 (file-name "y.patch")))))))))
654 (check-patch-file-names pkg))))
655
656(test-equal "<origin> patches: same file name -> no warnings"
657 '()
658 (let ((pkg (dummy-package "x"
659 (source
660 (dummy-origin
661 (patches
662 (list
663 (dummy-origin
664 (file-name "x.patch")))))))))
665 (check-patch-file-names pkg)))
666
667(test-equal "patches: file name too long, which may break 'make dist'"
668 (string-append "x-"
669 (make-string 152 #\a)
670 ".patch: file name is too long, which may break 'make dist'")
671 (single-lint-warning-message
672 (let ((pkg (dummy-package
673 "x"
674 (source
675 (dummy-origin
676 (patches (list (string-append "x-"
677 (make-string 152 #\a)
678 ".patch"))))))))
679 (check-patch-file-names pkg))))
680
681(test-equal "patches: not found"
682 "this-patch-does-not-exist!: patch not found\n"
683 (single-lint-warning-message
684 (let ((pkg (dummy-package
685 "x"
686 (source
687 (dummy-origin
688 (patches
689 (list (search-patch "this-patch-does-not-exist!"))))))))
690 (check-patch-file-names pkg))))
691
692(test-assert "patch headers: no warnings"
693 (call-with-temporary-directory
694 (lambda (directory)
695 (call-with-output-file (string-append directory "/t.patch")
696 (lambda (port)
697 (display "This is a patch.\n\n--- a\n+++ b\n"
698 port)))
699
700 (parameterize ((%patch-path (list directory)))
701 (let ((pkg (dummy-package "x"
702 (source (dummy-origin
703 (patches (search-patches "t.patch")))))))
704 (null? (check-patch-headers pkg)))))))
705
706(test-equal "patch headers: missing comment"
707 "t.patch: patch lacks comment and upstream status"
708 (call-with-temporary-directory
709 (lambda (directory)
710 (call-with-output-file (string-append directory "/t.patch")
711 (lambda (port)
712 (display "\n--- a\n+++ b\n"
713 port)))
714
715 (parameterize ((%patch-path (list directory)))
716 (let ((pkg (dummy-package "x"
717 (source (dummy-origin
718 (patches (search-patches "t.patch")))))))
719 (single-lint-warning-message (check-patch-headers pkg)))))))
720
721(test-equal "patch headers: empty"
722 "t.patch: empty patch"
723 (call-with-temporary-directory
724 (lambda (directory)
725 (call-with-output-file (string-append directory "/t.patch")
726 (const #t))
727
728 (parameterize ((%patch-path '()))
729 (let ((pkg (dummy-package "x"
730 (source (dummy-origin
731 (patches
732 (list (local-file
733 (string-append directory
734 "/t.patch")))))))))
735 (single-lint-warning-message (check-patch-headers pkg)))))))
736
737(test-equal "patch headers: patch not found"
738 "does-not-exist.patch: patch not found\n"
739 (parameterize ((%patch-path '()))
740 (let ((pkg (dummy-package "x"
741 (source (dummy-origin
742 (patches
743 (search-patches "does-not-exist.patch")))))))
744 (single-lint-warning-message (check-patch-headers pkg)))))
745
746(test-equal "derivation: invalid arguments"
747 "failed to create x86_64-linux derivation: (match-error \"match\" \"no matching pattern\" invalid-module)"
748 (match (let ((pkg (dummy-package "x"
749 (arguments
750 '(#:imported-modules (invalid-module))))))
751 (check-derivation pkg))
752 (((and (? lint-warning?) first-warning) others ...)
753 (lint-warning-message first-warning))))
754
755(test-equal "profile-collisions: no warnings"
756 '()
757 (check-profile-collisions (dummy-package "x")))
758
759(test-equal "profile-collisions: propagated inputs collide"
760 "propagated inputs p0@1 and p0@2 collide"
761 (let* ((p0 (dummy-package "p0" (version "1")))
762 (p0* (dummy-package "p0" (version "2")))
763 (p1 (dummy-package "p1" (propagated-inputs `(("p0" ,p0)))))
764 (p2 (dummy-package "p2" (propagated-inputs `(("p1" ,p1)))))
765 (p3 (dummy-package "p3" (propagated-inputs `(("p0" ,p0*)))))
766 (p4 (dummy-package "p4" (propagated-inputs
767 `(("p2" ,p2) ("p3", p3))))))
768 (single-lint-warning-message
769 (check-profile-collisions p4))))
770
771(test-assert "profile-collisions: propagated inputs collide, store items"
772 (string-match-or-error
773 "propagated inputs /[[:graph:]]+-p0-1 and /[[:graph:]]+-p0-1 collide"
774 (let* ((p0 (dummy-package "p0" (version "1")))
775 (p0* (dummy-package "p0" (version "1")
776 (inputs `(("x" ,(dummy-package "x"))))))
777 (p1 (dummy-package "p1" (propagated-inputs `(("p0" ,p0)))))
778 (p2 (dummy-package "p2" (propagated-inputs `(("p1" ,p1)))))
779 (p3 (dummy-package "p3" (propagated-inputs `(("p0" ,p0*)))))
780 (p4 (dummy-package "p4" (propagated-inputs
781 `(("p2" ,p2) ("p3", p3))))))
782 (single-lint-warning-message
783 (check-profile-collisions p4)))))
784
785(test-equal "license: invalid license"
786 "invalid license field"
787 (single-lint-warning-message
788 (check-license (dummy-package "x" (license #f)))))
789
790(test-equal "home-page: wrong home-page"
791 "invalid value for home page"
792 (let ((pkg (package
793 (inherit (dummy-package "x"))
794 (home-page #f))))
795 (single-lint-warning-message
796 (check-home-page pkg))))
797
798(test-equal "home-page: invalid URI"
799 "invalid home page URL: \"foobar\""
800 (let ((pkg (package
801 (inherit (dummy-package "x"))
802 (home-page "foobar"))))
803 (single-lint-warning-message
804 (check-home-page pkg))))
805
806(test-assert "home-page: host not found"
807 (let ((pkg (package
808 (inherit (dummy-package "x"))
809 (home-page "http://does-not-exist"))))
810 (warning-contains? "domain not found" (check-home-page pkg))))
811
812(parameterize ((%http-server-port 9999))
813 ;; TODO skip this test if some process is currently listening at 9999
814 (test-equal "home-page: Connection refused"
815 "URI http://localhost:9999/foo/bar unreachable: Connection refused"
816 (let ((pkg (package
817 (inherit (dummy-package "x"))
818 (home-page (%local-url)))))
819 (single-lint-warning-message
820 (check-home-page pkg)))))
821
822(test-equal "home-page: 200"
823 '()
824 (with-http-server `((200 ,%long-string))
825 (let ((pkg (package
826 (inherit (dummy-package "x"))
827 (home-page (%local-url)))))
828 (check-home-page pkg))))
829
830(with-http-server `((200 "This is too small."))
831 (test-equal "home-page: 200 but short length"
832 (format #f "URI ~a returned suspiciously small file (18 bytes)"
833 (%local-url))
834 (let ((pkg (package
835 (inherit (dummy-package "x"))
836 (home-page (%local-url)))))
837
838 (single-lint-warning-message
839 (check-home-page pkg)))))
840
841(with-http-server `((404 ,%long-string))
842 (test-equal "home-page: 404"
843 (format #f "URI ~a not reachable: 404 (\"Such is life\")" (%local-url))
844 (let ((pkg (package
845 (inherit (dummy-package "x"))
846 (home-page (%local-url)))))
847 (single-lint-warning-message
848 (check-home-page pkg)))))
849
850(with-http-server `((301 ,%long-string))
851 (test-equal "home-page: 301, invalid"
852 (format #f "invalid permanent redirect from ~a" (%local-url))
853 (let ((pkg (package
854 (inherit (dummy-package "x"))
855 (home-page (%local-url)))))
856 (single-lint-warning-message
857 (check-home-page pkg)))))
858
859(with-http-server `((200 ,%long-string))
860 (let* ((initial-url (%local-url))
861 (redirect (build-response #:code 301
862 #:headers
863 `((location
864 . ,(string->uri initial-url))))))
865 (parameterize ((%http-server-port 0))
866 (with-http-server `((,redirect ""))
867 (test-equal "home-page: 301 -> 200"
868 (format #f "permanent redirect from ~a to ~a"
869 (%local-url) initial-url)
870 (let ((pkg (package
871 (inherit (dummy-package "x"))
872 (home-page (%local-url)))))
873 (single-lint-warning-message
874 (check-home-page pkg))))))))
875
876(with-http-server `((404 "booh!"))
877 (let* ((initial-url (%local-url))
878 (redirect (build-response #:code 301
879 #:headers
880 `((location
881 . ,(string->uri initial-url))))))
882 (parameterize ((%http-server-port 0))
883 (with-http-server `((,redirect ""))
884 (test-equal "home-page: 301 -> 404"
885 (format #f "URI ~a not reachable: 404 (\"Such is life\")" (%local-url))
886 (let ((pkg (package
887 (inherit (dummy-package "x"))
888 (home-page (%local-url)))))
889 (single-lint-warning-message
890 (check-home-page pkg))))))))
891
892
893(test-equal "source-file-name"
894 "the source file name should contain the package name"
895 (let ((pkg (dummy-package "x"
896 (version "3.2.1")
897 (source
898 (origin
899 (method url-fetch)
900 (uri "http://www.example.com/3.2.1.tar.gz")
901 (sha256 %null-sha256))))))
902 (single-lint-warning-message
903 (check-source-file-name pkg))))
904
905(test-equal "source-file-name: v prefix"
906 "the source file name should contain the package name"
907 (let ((pkg (dummy-package "x"
908 (version "3.2.1")
909 (source
910 (origin
911 (method url-fetch)
912 (uri "http://www.example.com/v3.2.1.tar.gz")
913 (sha256 %null-sha256))))))
914 (single-lint-warning-message
915 (check-source-file-name pkg))))
916
917(test-equal "source-file-name: bad checkout"
918 "the source file name should contain the package name"
919 (let ((pkg (dummy-package "x"
920 (version "3.2.1")
921 (source
922 (origin
923 (method git-fetch)
924 (uri (git-reference
925 (url "http://www.example.com/x.git")
926 (commit "0")))
927 (sha256 %null-sha256))))))
928 (single-lint-warning-message
929 (check-source-file-name pkg))))
930
931(test-equal "source-file-name: good checkout"
932 '()
933 (let ((pkg (dummy-package "x"
934 (version "3.2.1")
935 (source
936 (origin
937 (method git-fetch)
938 (uri (git-reference
939 (url "http://git.example.com/x.git")
940 (commit "0")))
941 (file-name (string-append "x-" version))
942 (sha256 %null-sha256))))))
943 (check-source-file-name pkg)))
944
945(test-equal "source-file-name: valid"
946 '()
947 (let ((pkg (dummy-package "x"
948 (version "3.2.1")
949 (source
950 (origin
951 (method url-fetch)
952 (uri "http://www.example.com/x-3.2.1.tar.gz")
953 (sha256 %null-sha256))))))
954 (check-source-file-name pkg)))
955
956(test-equal "source-unstable-tarball"
957 "the source URI should not be an autogenerated tarball"
958 (let ((pkg (dummy-package "x"
959 (source
960 (origin
961 (method url-fetch)
962 (uri "https://github.com/example/example/archive/v0.0.tar.gz")
963 (sha256 %null-sha256))))))
964 (single-lint-warning-message
965 (check-source-unstable-tarball pkg))))
966
967(test-equal "source-unstable-tarball: source #f"
968 '()
969 (let ((pkg (dummy-package "x"
970 (source #f))))
971 (check-source-unstable-tarball pkg)))
972
973(test-equal "source-unstable-tarball: valid"
974 '()
975 (let ((pkg (dummy-package "x"
976 (source
977 (origin
978 (method url-fetch)
979 (uri "https://github.com/example/example/releases/download/x-0.0/x-0.0.tar.gz")
980 (sha256 %null-sha256))))))
981 (check-source-unstable-tarball pkg)))
982
983(test-equal "source-unstable-tarball: package named archive"
984 '()
985 (let ((pkg (dummy-package "x"
986 (source
987 (origin
988 (method url-fetch)
989 (uri "https://github.com/example/archive/releases/download/x-0.0/x-0.0.tar.gz")
990 (sha256 %null-sha256))))))
991 (check-source-unstable-tarball pkg)))
992
993(test-equal "source-unstable-tarball: not-github"
994 '()
995 (let ((pkg (dummy-package "x"
996 (source
997 (origin
998 (method url-fetch)
999 (uri "https://bitbucket.org/archive/example/download/x-0.0.tar.gz")
1000 (sha256 %null-sha256))))))
1001 (check-source-unstable-tarball pkg)))
1002
1003(test-equal "source-unstable-tarball: git-fetch"
1004 '()
1005 (let ((pkg (dummy-package "x"
1006 (source
1007 (origin
1008 (method git-fetch)
1009 (uri (git-reference
1010 (url "https://github.com/archive/example")
1011 (commit "0")))
1012 (sha256 %null-sha256))))))
1013 (check-source-unstable-tarball pkg)))
1014
1015(define (package-with-phase-changes changes)
1016 (dummy-package "x"
1017 (arguments `(#:phases
1018 ,(if (gexp? changes)
1019 #~(modify-phases %standard-phases
1020 #$@changes)
1021 `(modify-phases %standard-phases
1022 ,@changes))))))
1023
1024(test-equal "optional-tests: no check phase"
1025 '()
1026 (let ((pkg (package-with-phase-changes '())))
1027 (check-optional-tests pkg)))
1028
1029(test-equal "optional-tests: check phase respects #:tests?"
1030 '()
1031 (let ((pkg (package-with-phase-changes
1032 '((replace 'check
1033 (lambda* (#:key tests? #:allow-other-keys?)
1034 (when tests?
1035 (invoke "./the-test-suite"))))))))
1036 (check-optional-tests pkg)))
1037
1038(test-equal "optional-tests: check phase ignores #:tests?"
1039 "the 'check' phase should respect #:tests?"
1040 (let ((pkg (package-with-phase-changes
1041 '((replace 'check
1042 (lambda _
1043 (invoke "./the-test-suite")))))))
1044 (single-lint-warning-message
1045 (check-optional-tests pkg))))
1046
1047(test-equal "optional-tests: do not crash when #:phases is invalid"
1048 "incorrect call to ‘modify-phases’"
1049 (let ((pkg (package-with-phase-changes 'this-is-not-a-list)))
1050 (single-lint-warning-message
1051 (check-optional-tests pkg))))
1052
1053(test-equal "optional-tests: allow G-exps (no warning)"
1054 '()
1055 (let ((pkg (package-with-phase-changes #~())))
1056 (check-optional-tests pkg)))
1057
1058(test-equal "optional-tests: allow G-exps (warning)"
1059 "the 'check' phase should respect #:tests?"
1060 (let ((pkg (package-with-phase-changes
1061 #~((replace 'check
1062 (lambda _
1063 (invoke "/the-test-suite")))))))
1064 (single-lint-warning-message
1065 (check-optional-tests pkg))))
1066
1067(test-equal "optional-tests: complicated 'check' phase"
1068 "the 'check' phase should respect #:tests?"
1069 (let ((pkg (package-with-phase-changes
1070 '((replace 'check
1071 (lambda* (#:key inputs tests? #:allow-other-keys)
1072 (let ((something (stuff from inputs or native-inputs)))
1073 (delete-file "dateutil/test/test_utils.py")
1074 (invoke "pytest" "-vv"))))))))
1075 (single-lint-warning-message
1076 (check-optional-tests pkg))))
1077
1078(test-equal "optional-tests: 'check' phase is not first phase"
1079 "the 'check' phase should respect #:tests?"
1080 (let ((pkg (package-with-phase-changes
1081 '((add-after 'unpack
1082 (lambda _
1083 (chdir "libtestcase-0.0.0")))
1084 (replace 'check
1085 (lambda _ (invoke "./test-suite")))))))
1086 (single-lint-warning-message
1087 (check-optional-tests pkg))))
1088
1089(test-equal "source: 200"
1090 '()
1091 (with-http-server `((200 ,%long-string))
1092 (let ((pkg (package
1093 (inherit (dummy-package "x"))
1094 (source (origin
1095 (method url-fetch)
1096 (uri (%local-url))
1097 (sha256 %null-sha256))))))
1098 (check-source pkg))))
1099
1100(with-http-server '((200 "This is too small."))
1101 (test-equal "source: 200 but short length"
1102 (format #f "URI ~a returned suspiciously small file (18 bytes)"
1103 (%local-url))
1104 (let ((pkg (package
1105 (inherit (dummy-package "x"))
1106 (source (origin
1107 (method url-fetch)
1108 (uri (%local-url))
1109 (sha256 %null-sha256))))))
1110 (match (check-source pkg)
1111 ((first-warning ; All source URIs are unreachable
1112 (and (? lint-warning?) second-warning))
1113 (lint-warning-message second-warning))))))
1114
1115(with-http-server `((404 ,%long-string))
1116 (test-equal "source: 404"
1117 (format #f "URI ~a not reachable: 404 (\"Such is life\")"
1118 (%local-url))
1119 (let ((pkg (package
1120 (inherit (dummy-package "x"))
1121 (source (origin
1122 (method url-fetch)
1123 (uri (%local-url))
1124 (sha256 %null-sha256))))))
1125 (match (check-source pkg)
1126 ((first-warning ; All source URIs are unreachable
1127 (and (? lint-warning?) second-warning))
1128 (lint-warning-message second-warning))))))
1129
1130(test-equal "source: 404 and 200"
1131 '()
1132 (with-http-server `((404 ,%long-string))
1133 (let ((bad-url (%local-url)))
1134 (parameterize ((%http-server-port (+ 1 (%http-server-port))))
1135 (with-http-server `((200 ,%long-string))
1136 (let ((pkg (package
1137 (inherit (dummy-package "x"))
1138 (source (origin
1139 (method url-fetch)
1140 (uri (list bad-url (%local-url)))
1141 (sha256 %null-sha256))))))
1142 ;; Since one of the two URLs is good, this should return the empty
1143 ;; list.
1144 (check-source pkg)))))))
1145
1146(with-http-server `((200 ,%long-string))
1147 (let* ((initial-url (%local-url))
1148 (redirect (build-response #:code 301
1149 #:headers
1150 `((location
1151 . ,(string->uri initial-url))))))
1152 (parameterize ((%http-server-port 0))
1153 (with-http-server `((,redirect ""))
1154 (test-equal "source: 301 -> 200"
1155 (format #f "permanent redirect from ~a to ~a"
1156 (%local-url) initial-url)
1157 (let ((pkg (package
1158 (inherit (dummy-package "x"))
1159 (source (origin
1160 (method url-fetch)
1161 (uri (%local-url))
1162 (sha256 %null-sha256))))))
1163 (match (check-source pkg)
1164 ((first-warning ; All source URIs are unreachable
1165 (and (? lint-warning?) second-warning))
1166 (lint-warning-message second-warning)))))))))
1167
1168(with-http-server `((200 ,%long-string))
1169 (let* ((initial-url (%local-url))
1170 (redirect (build-response #:code 301
1171 #:headers
1172 `((location
1173 . ,(string->uri initial-url))))))
1174 (parameterize ((%http-server-port 0))
1175 (with-http-server `((,redirect ""))
1176 (test-equal "source, git-reference: 301 -> 200"
1177 (format #f "permanent redirect from ~a to ~a"
1178 (%local-url) initial-url)
1179 (let ((pkg (dummy-package
1180 "x"
1181 (source (origin
1182 (method git-fetch)
1183 (uri (git-reference (url (%local-url))
1184 (commit "v1.0.0")))
1185 (sha256 %null-sha256))))))
1186 (single-lint-warning-message (check-source pkg))))))))
1187
1188(with-http-server '((404 "booh!"))
1189 (let* ((initial-url (%local-url))
1190 (redirect (build-response #:code 301
1191 #:headers
1192 `((location
1193 . ,(string->uri initial-url))))))
1194 (parameterize ((%http-server-port 0))
1195 (with-http-server `((,redirect ""))
1196 (test-equal "source: 301 -> 404"
1197 (format #f "URI ~a not reachable: 404 (\"Such is life\")"
1198 (%local-url))
1199 (let ((pkg (package
1200 (inherit (dummy-package "x"))
1201 (source (origin
1202 (method url-fetch)
1203 (uri (%local-url))
1204 (sha256 %null-sha256))))))
1205 (match (check-source pkg)
1206 ((first-warning ; The first warning says that all URI's are
1207 ; unreachable
1208 (and (? lint-warning?) second-warning))
1209 (lint-warning-message second-warning)))))))))
1210
1211(test-equal "source: svn-reference, HTTP 200"
1212 '()
1213 (with-http-server `((200 ,%long-string))
1214 (let ((pkg (package
1215 (inherit (dummy-package "x"))
1216 (source (origin
1217 (method svn-fetch)
1218 (uri (svn-reference
1219 (url (%local-url))
1220 (revision 1234)))
1221 (sha256 %null-sha256))))))
1222 (check-source pkg))))
1223
1224(with-http-server `((404 ,%long-string))
1225 (test-equal "source: svn-reference, HTTP 404"
1226 (format #f "URI ~a not reachable: 404 (\"Such is life\")"
1227 (%local-url))
1228 (let ((pkg (package
1229 (inherit (dummy-package "x"))
1230 (source (origin
1231 (method svn-fetch)
1232 (uri (svn-reference
1233 (url (%local-url))
1234 (revision 1234)))
1235 (sha256 %null-sha256))))))
1236 (match (check-source pkg)
1237 ((warning)
1238 (lint-warning-message warning))))))
1239
1240(test-equal "mirror-url"
1241 '()
1242 (let ((source (origin
1243 (method url-fetch)
1244 (uri "http://example.org/foo/bar.tar.gz")
1245 (sha256 %null-sha256))))
1246 (check-mirror-url (dummy-package "x" (source source)))))
1247
1248(test-equal "mirror-url: one suggestion"
1249 "URL should be 'mirror://gnu/foo/foo.tar.gz'"
1250 (let ((source (origin
1251 (method url-fetch)
1252 (uri "http://ftp.gnu.org/pub/gnu/foo/foo.tar.gz")
1253 (sha256 %null-sha256))))
1254 (single-lint-warning-message
1255 (check-mirror-url (dummy-package "x" (source source))))))
1256
1257(test-equal "mirror-url: kde suggestion"
1258 "URL should be 'mirror://kde/stable/gcompris/qt/src/gcompris-qt-2.3.tar.xz'"
1259 (let ((source (origin
1260 (method url-fetch)
1261 (uri "https://download.kde.org/stable/gcompris/qt/src/gcompris-qt-2.3.tar.xz")
1262 (sha256 %null-sha256))))
1263 (single-lint-warning-message
1264 (check-mirror-url (dummy-package "x" (source source))))))
1265
1266(test-equal "github-url"
1267 '()
1268 (with-http-server `((200 ,%long-string))
1269 (check-github-url
1270 (dummy-package "x" (source
1271 (origin
1272 (method url-fetch)
1273 (uri (%local-url))
1274 (sha256 %null-sha256)))))))
1275
1276(let ((github-url "https://github.com/foo/bar/bar-1.0.tar.gz"))
1277 (test-equal "github-url: one suggestion"
1278 (string-append
1279 "URL should be '" github-url "'")
1280 (let ((redirect (build-response #:code 301
1281 #:headers
1282 `((location
1283 . ,(string->uri github-url))))))
1284 (with-http-server `((,redirect ""))
1285 (let* ((initial-url (%local-url))
1286 (redirect (build-response #:code 302
1287 #:headers
1288 `((location
1289 . ,(string->uri initial-url))))))
1290 (parameterize ((%http-server-port 0))
1291 (with-http-server `((,redirect ""))
1292 (single-lint-warning-message
1293 (check-github-url
1294 (dummy-package "x" (source
1295 (origin
1296 (method url-fetch)
1297 (uri (%local-url))
1298 (sha256 %null-sha256))))))))))))
1299
1300 (test-equal "github-url: already the correct github url"
1301 '()
1302 (check-github-url
1303 (dummy-package "x" (source
1304 (origin
1305 (method url-fetch)
1306 (uri github-url)
1307 (sha256 %null-sha256)))))))
1308
1309(test-equal "cve"
1310 '()
1311 (mock ((guix lint) package-vulnerabilities (const '()))
1312 (check-vulnerabilities (dummy-package "x"))))
1313
1314(test-equal "cve: one vulnerability"
1315 "probably vulnerable to CVE-2015-1234"
1316 (let ((dummy-vulnerabilities
1317 (lambda (package)
1318 (list (make-struct/no-tail
1319 (@@ (guix cve) <vulnerability>)
1320 "CVE-2015-1234"
1321 (list (cons (package-name package)
1322 (package-version package))))))))
1323 (single-lint-warning-message
1324 (check-vulnerabilities (dummy-package "pi" (version "3.14"))
1325 dummy-vulnerabilities))))
1326
1327(test-equal "cve: one patched vulnerability"
1328 '()
1329 (mock ((guix lint) package-vulnerabilities
1330 (lambda (package)
1331 (list (make-struct/no-tail (@@ (guix cve) <vulnerability>)
1332 "CVE-2015-1234"
1333 (list (cons (package-name package)
1334 (package-version package)))))))
1335 (check-vulnerabilities
1336 (dummy-package "pi"
1337 (version "3.14")
1338 (source
1339 (dummy-origin
1340 (patches
1341 (list "/a/b/pi-CVE-2015-1234.patch"))))))))
1342
1343(test-equal "cve: known safe from vulnerability"
1344 '()
1345 (mock ((guix lint) package-vulnerabilities
1346 (lambda (package)
1347 (list (make-struct/no-tail (@@ (guix cve) <vulnerability>)
1348 "CVE-2015-1234"
1349 (list (cons (package-name package)
1350 (package-version package)))))))
1351 (check-vulnerabilities
1352 (dummy-package "pi"
1353 (version "3.14")
1354 (properties `((lint-hidden-cve . ("CVE-2015-1234"))))))))
1355
1356(test-equal "cve: vulnerability fixed in replacement version"
1357 '()
1358 (mock ((guix lint) package-vulnerabilities
1359 (lambda (package)
1360 (match (package-version package)
1361 ("0"
1362 (list (make-struct/no-tail (@@ (guix cve) <vulnerability>)
1363 "CVE-2015-1234"
1364 (list (cons (package-name package)
1365 (package-version package))))))
1366 ("1"
1367 '()))))
1368 (check-vulnerabilities
1369 (dummy-package
1370 "foo" (version "0")
1371 (replacement (dummy-package "foo" (version "1")))))))
1372
1373(test-equal "cve: patched vulnerability in replacement"
1374 '()
1375 (mock ((guix lint) package-vulnerabilities
1376 (lambda (package)
1377 (list (make-struct/no-tail (@@ (guix cve) <vulnerability>)
1378 "CVE-2015-1234"
1379 (list (cons (package-name package)
1380 (package-version package)))))))
1381 (check-vulnerabilities
1382 (dummy-package
1383 "pi" (version "3.14") (source (dummy-origin))
1384 (replacement (dummy-package
1385 "pi" (version "3.14")
1386 (source
1387 (dummy-origin
1388 (patches
1389 (list "/a/b/pi-CVE-2015-1234.patch"))))))))))
1390
1391(test-equal "formatting: lonely parentheses"
1392 "parentheses feel lonely, move to the previous or next line"
1393 (single-lint-warning-message
1394 (check-formatting
1395 (dummy-package "ugly as hell!"
1396 )
1397 )))
1398
1399(test-assert "formatting: tabulation"
1400 (string-match-or-error
1401 "tabulation on line [0-9]+, column [0-9]+"
1402 (single-lint-warning-message
1403 (check-formatting (dummy-package "leave the tab here: ")))))
1404
1405(test-assert "formatting: trailing white space"
1406 (string-match-or-error
1407 "trailing white space .*"
1408 ;; Leave the trailing white space on the next line!
1409 (single-lint-warning-message
1410 (check-formatting (dummy-package "x")))))
1411
1412(test-assert "formatting: long line"
1413 (string-match-or-error
1414 "line [0-9]+ is way too long \\([0-9]+ characters\\)"
1415 (single-lint-warning-message (check-formatting
1416 (dummy-package "x")) ;here is a stupid comment just to make a long line
1417 )))
1418
1419(test-equal "formatting: alright"
1420 '()
1421 (check-formatting (dummy-package "x")))
1422
1423(test-assert "archival: not an origin"
1424 (warning-contains? "not an origin"
1425 (check-archival
1426 (dummy-package
1427 "x" (source (computed-file "x-src" #t))))))
1428
1429(test-assert "archival: missing content"
1430 (let* ((origin (origin
1431 (method url-fetch)
1432 (uri "http://example.org/foo.tgz")
1433 (sha256 (make-bytevector 32))))
1434 (warnings (with-http-server '((404 "Not archived.")
1435 (404 "Not in Disarchive database."))
1436 (parameterize ((%swh-base-url (%local-url)))
1437 (mock ((guix download) %disarchive-mirrors
1438 (list (%local-url)))
1439 (check-archival (dummy-package "x"
1440 (source origin))))))))
1441 (warning-contains? "not archived" warnings)))
1442
1443(test-equal "archival: content available"
1444 '()
1445 (let* ((origin (origin
1446 (method url-fetch)
1447 (uri "http://example.org/foo.tgz")
1448 (sha256 (make-bytevector 32))))
1449 ;; https://archive.softwareheritage.org/api/1/content/
1450 (content "{ \"checksums\": {}, \"data_url\": \"xyz\",
1451 \"length\": 42 }"))
1452 (with-http-server `((404 "") ;extid
1453 (200 ,content))
1454 (parameterize ((%swh-base-url (%local-url)))
1455 (check-archival (dummy-package "x" (source origin)))))))
1456
1457(test-equal "archival: content unavailable but disarchive available"
1458 '()
1459 (let* ((origin (origin
1460 (method url-fetch)
1461 (uri "http://example.org/foo.tgz")
1462 (sha256 (make-bytevector 32))))
1463 (disarchive (object->string
1464 '(disarchive (version 0)
1465 ...
1466 "swh:1:dir:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")))
1467 ;; https://archive.softwareheritage.org/api/1/directory/
1468 (directory "[ { \"checksums\": {},
1469 \"dir_id\": \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",
1470 \"type\": \"file\",
1471 \"name\": \"README\"
1472 \"length\": 42 } ]"))
1473 (with-http-server `((404 "") ;lookup-directory-by-nar-hash
1474 (404 "") ;lookup-content
1475 (200 ,disarchive) ;Disarchive database lookup
1476 (200 ,directory)) ;lookup-directory
1477 (mock ((guix download) %disarchive-mirrors (list (%local-url)))
1478 (parameterize ((%swh-base-url (%local-url)))
1479 (check-archival (dummy-package "x" (source origin))))))))
1480
1481(test-assert "archival: missing revision"
1482 (let* ((origin (origin
1483 (method git-fetch)
1484 (uri (git-reference
1485 (url "http://example.org/foo.git")
1486 (commit "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")))
1487 (sha256 (make-bytevector 32))))
1488 ;; https://archive.softwareheritage.org/api/1/origin/save/
1489 (save "{ \"origin_url\": \"http://example.org/foo.git\",
1490 \"save_request_date\": \"2014-11-17T22:09:38+01:00\",
1491 \"save_request_status\": \"accepted\",
1492 \"save_task_status\": \"scheduled\" }")
1493 (warnings (with-http-server `((404 "No extid.") ;lookup-directory-by-nar-hash
1494 (404 "No revision.") ;lookup-revision
1495 (404 "No origin.") ;lookup-origin
1496 (200 ,save)) ;save-origin
1497 (parameterize ((%swh-base-url (%local-url)))
1498 (check-archival (dummy-package "x" (source origin)))))))
1499 (warning-contains? "scheduled" warnings)))
1500
1501(test-assert "archival: missing svn revision"
1502 (let* ((origin (origin
1503 (method svn-fetch)
1504 (uri (svn-reference
1505 (url "http://example.org/svn/foo")
1506 (revision "1234")))
1507 (sha256 (make-bytevector 32))))
1508 ;; https://archive.softwareheritage.org/api/1/origin/save/
1509 (save "{ \"origin_url\": \"http://example.org/svn/foo\",
1510 \"save_request_date\": \"2014-11-17T22:09:38+01:00\",
1511 \"save_request_status\": \"accepted\",
1512 \"save_task_status\": \"scheduled\" }")
1513 (warnings (with-http-server `((404 "No extid.") ;lookup-directory-by-nar-hash
1514 (404 "No revision.") ;lookup-revision
1515 (404 "No origin.") ;lookup-origin
1516 (200 ,save)) ;save-origin
1517 (parameterize ((%swh-base-url (%local-url)))
1518 (check-archival (dummy-package "x" (source origin)))))))
1519 (warning-contains? "scheduled" warnings)))
1520
1521(test-equal "archival: revision available"
1522 '()
1523 (let* ((origin (origin
1524 (method git-fetch)
1525 (uri (git-reference
1526 (url "http://example.org/foo.git")
1527 (commit "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")))
1528 (sha256 (make-bytevector 32))))
1529 ;; https://archive.softwareheritage.org/api/1/revision/
1530 (revision "{ \"author\": {}, \"parents\": [],
1531 \"date\": \"2014-11-17T22:09:38+01:00\" }"))
1532 (with-http-server `((404 "No directory.") ;lookup-directory-by-nar-hash
1533 (200 ,revision))
1534 (parameterize ((%swh-base-url (%local-url)))
1535 (check-archival (dummy-package "x" (source origin)))))))
1536
1537(test-equal "archival: nar-sha256 extid available"
1538 '()
1539 (let* ((origin (origin
1540 (method git-fetch)
1541 (uri (git-reference
1542 (url "http://example.org/foo.git")
1543 (commit "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")))
1544 (sha256 (make-bytevector 32))))
1545 ;; https://archive.softwareheritage.org/api/1/extid/doc/
1546 (extid "{ \"extid_type\": \"nar-sha256\",
1547 \"extid\": \"1234\",
1548 \"extid_version\": 0,
1549 \"target\": \"swh:1:dir:cabba93\",
1550 \"target_url\": \"boo\"
1551 }"))
1552 (with-http-server `((200 ,extid))
1553 (parameterize ((%swh-base-url (%local-url)))
1554 (check-archival (dummy-package "x" (source origin)))))))
1555
1556(test-assert "archival: rate limit reached"
1557 ;; We should get a single warning stating that the rate limit was reached,
1558 ;; and nothing more, in particular no other HTTP requests.
1559 (let* ((origin (origin
1560 (method url-fetch)
1561 (uri "http://example.org/foo.tgz")
1562 (sha256 (make-bytevector 32))))
1563 (too-many (build-response
1564 #:code 429
1565 #:reason-phrase "Too many requests"
1566 #:headers '((x-ratelimit-remaining . "0")
1567 (x-ratelimit-reset . "3000000000"))))
1568 (warnings (with-http-server `((,too-many "Rate limit reached."))
1569 (parameterize ((%swh-base-url (%local-url)))
1570 (append-map (lambda (name)
1571 (check-archival
1572 (dummy-package name (source origin))))
1573 '("x" "y" "z"))))))
1574 (string-contains (single-lint-warning-message warnings)
1575 "rate limit reached")))
1576
1577(test-assert "haskell-stackage"
1578 (let* ((stackage (string-append "{ \"packages\": [{"
1579 " \"name\":\"pandoc\","
1580 " \"synopsis\":\"synopsis\","
1581 " \"version\":\"1.0\" }],"
1582 " \"snapshot\": {"
1583 " \"ghc\": \"8.6.5\","
1584 " \"name\": \"lts-14.27\""
1585 " }}"))
1586 (packages (map (lambda (version)
1587 (dummy-package
1588 "ghc-pandoc"
1589 (version version)
1590 (source
1591 (dummy-origin
1592 (method url-fetch)
1593 (uri (string-append
1594 "https://hackage.haskell.org/package/"
1595 "pandoc-" version "/pandoc-" version ".tar.gz"))))))
1596 '("0.9" "1.0" "100.0")))
1597 (warnings (pk (with-http-server `((200 ,stackage) ; memoized
1598 (200 "name: pandoc\nversion: 1.0\n")
1599 (200 "name: pandoc\nversion: 1.0\n")
1600 (200 "name: pandoc\nversion: 1.0\n"))
1601 (parameterize ((%hackage-url (%local-url))
1602 (%stackage-url (%local-url)))
1603 (append-map check-haskell-stackage packages))))))
1604 (match warnings
1605 (((? lint-warning? warning))
1606 (and (string=? (package-version (lint-warning-package warning)) "100.0")
1607 (string-contains (lint-warning-message warning)
1608 "ahead of Stackage LTS version"))))))
1609
1610(test-end "lint")
1611
1612;; Local Variables:
1613;; eval: (put 'with-http-server 'scheme-indent-function 1)
1614;; eval: (put 'with-warnings 'scheme-indent-function 0)
1615;; End: