lint.scm 44 KB

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