lint: Verify if #:tests? is respected in the 'check' phase.
There have been a few patches to the mailing list lately not respecting this, and this linter detects 630 package definitions that could be modified to support the --without-tests package transformation. * guix/lint.scm (check-optional-tests): New linter. (%local-checkers)[optional-tests]: Add it. * tests/lint.scm (package-with-phase-changes): New procedure. ("optional-tests: no check phase") ("optional-tests: check hase respects #:tests?") ("optional-tests: check phase ignores #:tests?") ("optional-tests: do not crash when #:phases is invalid") ("optional-tests: allow G-exps (no warning)") ("optional-tests: allow G-exps (warning)") ("optional-tests: complicated 'check' phase") ("optional-tests: 'check' phase is not first phase"): New tests. Signed-off-by: Mathieu Othacehe <othacehe@gnu.org>
This commit is contained in:
parent
d9e0ae07db
commit
5532371a3a
2 changed files with 135 additions and 2 deletions
|
@ -40,7 +40,8 @@
|
||||||
#:use-module (guix packages)
|
#:use-module (guix packages)
|
||||||
#:use-module (guix i18n)
|
#:use-module (guix i18n)
|
||||||
#:use-module ((guix gexp)
|
#:use-module ((guix gexp)
|
||||||
#:select (local-file? local-file-absolute-file-name))
|
#:select (gexp? local-file? local-file-absolute-file-name
|
||||||
|
gexp->approximate-sexp))
|
||||||
#:use-module (guix licenses)
|
#:use-module (guix licenses)
|
||||||
#:use-module (guix records)
|
#:use-module (guix records)
|
||||||
#:use-module (guix grafts)
|
#:use-module (guix grafts)
|
||||||
|
@ -89,6 +90,7 @@
|
||||||
check-source
|
check-source
|
||||||
check-source-file-name
|
check-source-file-name
|
||||||
check-source-unstable-tarball
|
check-source-unstable-tarball
|
||||||
|
check-optional-tests
|
||||||
check-mirror-url
|
check-mirror-url
|
||||||
check-github-url
|
check-github-url
|
||||||
check-license
|
check-license
|
||||||
|
@ -1098,6 +1100,58 @@ descriptions maintained upstream."
|
||||||
(define exception-with-kind-and-args?
|
(define exception-with-kind-and-args?
|
||||||
(exception-predicate &exception-with-kind-and-args))
|
(exception-predicate &exception-with-kind-and-args))
|
||||||
|
|
||||||
|
(define (check-optional-tests package)
|
||||||
|
"Emit a warning if the test suite is run unconditionally."
|
||||||
|
(define (sexp-contains-atom? sexp atom)
|
||||||
|
"Test if SEXP contains ATOM."
|
||||||
|
(if (pair? sexp)
|
||||||
|
(or (sexp-contains-atom? (car sexp) atom)
|
||||||
|
(sexp-contains-atom? (cdr sexp) atom))
|
||||||
|
(eq? sexp atom)))
|
||||||
|
(define (sexp-uses-tests?? sexp)
|
||||||
|
"Test if SEXP contains the symbol 'tests?'."
|
||||||
|
(sexp-contains-atom? sexp 'tests?))
|
||||||
|
(define (check-check-procedure expression)
|
||||||
|
(match expression
|
||||||
|
(`(,(or 'let 'let*) . ,_)
|
||||||
|
(check-check-procedure (car (last-pair expression))))
|
||||||
|
(`(,(or 'lambda 'lambda*) ,_ . ,code)
|
||||||
|
(if (sexp-uses-tests?? code)
|
||||||
|
'()
|
||||||
|
(list (make-warning package
|
||||||
|
;; TRANSLATORS: check and #:tests? are a
|
||||||
|
;; Scheme symbol and keyword respectively
|
||||||
|
;; and should not be translated.
|
||||||
|
(G_ "the 'check' phase should respect #:tests?")
|
||||||
|
#:field 'arguments))))
|
||||||
|
(_ '())))
|
||||||
|
(define (check-phases-delta delta)
|
||||||
|
(match delta
|
||||||
|
(`(replace 'check ,expression)
|
||||||
|
(check-check-procedure expression))
|
||||||
|
(_ '())))
|
||||||
|
(define (check-phases-deltas deltas)
|
||||||
|
(match deltas
|
||||||
|
(() '())
|
||||||
|
((head . tail)
|
||||||
|
(append (check-phases-delta head)
|
||||||
|
(check-phases-deltas tail)))
|
||||||
|
(_ (list (make-warning package
|
||||||
|
;; TRANSLATORS: modify-phases is a Scheme
|
||||||
|
;; syntax and must not be translated.
|
||||||
|
(G_ "incorrect call to ‘modify-phases’")
|
||||||
|
#:field 'arguments)))))
|
||||||
|
(apply (lambda* (#:key phases #:allow-other-keys)
|
||||||
|
(define phases/sexp
|
||||||
|
(if (gexp? phases)
|
||||||
|
(gexp->approximate-sexp phases)
|
||||||
|
phases))
|
||||||
|
(match phases/sexp
|
||||||
|
(`(modify-phases ,_ . ,changes)
|
||||||
|
(check-phases-deltas changes))
|
||||||
|
(_ '())))
|
||||||
|
(package-arguments package)))
|
||||||
|
|
||||||
(define* (check-derivation package #:key store)
|
(define* (check-derivation package #:key store)
|
||||||
"Emit a warning if we fail to compile PACKAGE to a derivation."
|
"Emit a warning if we fail to compile PACKAGE to a derivation."
|
||||||
(define (try store system)
|
(define (try store system)
|
||||||
|
@ -1598,6 +1652,10 @@ them for PACKAGE."
|
||||||
(description "Make sure the 'license' field is a <license> \
|
(description "Make sure the 'license' field is a <license> \
|
||||||
or a list thereof")
|
or a list thereof")
|
||||||
(check check-license))
|
(check check-license))
|
||||||
|
(lint-checker
|
||||||
|
(name 'optional-tests)
|
||||||
|
(description "Make sure tests are only run when requested")
|
||||||
|
(check check-optional-tests))
|
||||||
(lint-checker
|
(lint-checker
|
||||||
(name 'mirror-url)
|
(name 'mirror-url)
|
||||||
(description "Suggest 'mirror://' URLs")
|
(description "Suggest 'mirror://' URLs")
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
;;; Copyright © 2018, 2019 Arun Isaac <arunisaac@systemreboot.net>
|
;;; Copyright © 2018, 2019 Arun Isaac <arunisaac@systemreboot.net>
|
||||||
;;; Copyright © 2020 Timothy Sample <samplet@ngyro.com>
|
;;; Copyright © 2020 Timothy Sample <samplet@ngyro.com>
|
||||||
;;; Copyright © 2021 Xinglu Chen <public@yoctocell.xyz>
|
;;; Copyright © 2021 Xinglu Chen <public@yoctocell.xyz>
|
||||||
|
;;; Copyright © 2021 Maxime Devos <maximedevos@telenet.be>
|
||||||
;;;
|
;;;
|
||||||
;;; This file is part of GNU Guix.
|
;;; This file is part of GNU Guix.
|
||||||
;;;
|
;;;
|
||||||
|
@ -38,7 +39,7 @@
|
||||||
#:use-module (guix lint)
|
#:use-module (guix lint)
|
||||||
#:use-module (guix ui)
|
#:use-module (guix ui)
|
||||||
#:use-module (guix swh)
|
#:use-module (guix swh)
|
||||||
#:use-module ((guix gexp) #:select (local-file))
|
#:use-module ((guix gexp) #:select (gexp local-file gexp?))
|
||||||
#:use-module ((guix utils) #:select (call-with-temporary-directory))
|
#:use-module ((guix utils) #:select (call-with-temporary-directory))
|
||||||
#:use-module ((guix import hackage) #:select (%hackage-url))
|
#:use-module ((guix import hackage) #:select (%hackage-url))
|
||||||
#:use-module ((guix import stackage) #:select (%stackage-url))
|
#:use-module ((guix import stackage) #:select (%stackage-url))
|
||||||
|
@ -744,6 +745,80 @@
|
||||||
(sha256 %null-sha256))))))
|
(sha256 %null-sha256))))))
|
||||||
(check-source-unstable-tarball pkg)))
|
(check-source-unstable-tarball pkg)))
|
||||||
|
|
||||||
|
(define (package-with-phase-changes changes)
|
||||||
|
(dummy-package "x"
|
||||||
|
(arguments `(#:phases
|
||||||
|
,(if (gexp? changes)
|
||||||
|
#~(modify-phases %standard-phases
|
||||||
|
#$@changes)
|
||||||
|
`(modify-phases %standard-phases
|
||||||
|
,@changes))))))
|
||||||
|
|
||||||
|
(test-equal "optional-tests: no check phase"
|
||||||
|
'()
|
||||||
|
(let ((pkg (package-with-phase-changes '())))
|
||||||
|
(check-optional-tests pkg)))
|
||||||
|
|
||||||
|
(test-equal "optional-tests: check phase respects #:tests?"
|
||||||
|
'()
|
||||||
|
(let ((pkg (package-with-phase-changes
|
||||||
|
'((replace 'check
|
||||||
|
(lambda* (#:key tests? #:allow-other-keys?)
|
||||||
|
(when tests?
|
||||||
|
(invoke "./the-test-suite"))))))))
|
||||||
|
(check-optional-tests pkg)))
|
||||||
|
|
||||||
|
(test-equal "optional-tests: check phase ignores #:tests?"
|
||||||
|
"the 'check' phase should respect #:tests?"
|
||||||
|
(let ((pkg (package-with-phase-changes
|
||||||
|
'((replace 'check
|
||||||
|
(lambda _
|
||||||
|
(invoke "./the-test-suite")))))))
|
||||||
|
(single-lint-warning-message
|
||||||
|
(check-optional-tests pkg))))
|
||||||
|
|
||||||
|
(test-equal "optional-tests: do not crash when #:phases is invalid"
|
||||||
|
"incorrect call to ‘modify-phases’"
|
||||||
|
(let ((pkg (package-with-phase-changes 'this-is-not-a-list)))
|
||||||
|
(single-lint-warning-message
|
||||||
|
(check-optional-tests pkg))))
|
||||||
|
|
||||||
|
(test-equal "optional-tests: allow G-exps (no warning)"
|
||||||
|
'()
|
||||||
|
(let ((pkg (package-with-phase-changes #~())))
|
||||||
|
(check-optional-tests pkg)))
|
||||||
|
|
||||||
|
(test-equal "optional-tests: allow G-exps (warning)"
|
||||||
|
"the 'check' phase should respect #:tests?"
|
||||||
|
(let ((pkg (package-with-phase-changes
|
||||||
|
#~((replace 'check
|
||||||
|
(lambda _
|
||||||
|
(invoke "/the-test-suite")))))))
|
||||||
|
(single-lint-warning-message
|
||||||
|
(check-optional-tests pkg))))
|
||||||
|
|
||||||
|
(test-equal "optional-tests: complicated 'check' phase"
|
||||||
|
"the 'check' phase should respect #:tests?"
|
||||||
|
(let ((pkg (package-with-phase-changes
|
||||||
|
'((replace 'check
|
||||||
|
(lambda* (#:key inputs tests? #:allow-other-keys)
|
||||||
|
(let ((something (stuff from inputs or native-inputs)))
|
||||||
|
(delete-file "dateutil/test/test_utils.py")
|
||||||
|
(invoke "pytest" "-vv"))))))))
|
||||||
|
(single-lint-warning-message
|
||||||
|
(check-optional-tests pkg))))
|
||||||
|
|
||||||
|
(test-equal "optional-tests: 'check' phase is not first phase"
|
||||||
|
"the 'check' phase should respect #:tests?"
|
||||||
|
(let ((pkg (package-with-phase-changes
|
||||||
|
'((add-after 'unpack
|
||||||
|
(lambda _
|
||||||
|
(chdir "libtestcase-0.0.0")))
|
||||||
|
(replace 'check
|
||||||
|
(lambda _ (invoke "./test-suite")))))))
|
||||||
|
(single-lint-warning-message
|
||||||
|
(check-optional-tests pkg))))
|
||||||
|
|
||||||
(test-equal "source: 200"
|
(test-equal "source: 200"
|
||||||
'()
|
'()
|
||||||
(with-http-server `((200 ,%long-string))
|
(with-http-server `((200 ,%long-string))
|
||||||
|
|
Reference in a new issue