minetest.scm 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2021, 2022 Maxime Devos <maximedevos@telenet.be>
  3. ;;;
  4. ;;; This file is part of GNU Guix.
  5. ;;;
  6. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  7. ;;; under the terms of the GNU General Public License as published by
  8. ;;; the Free Software Foundation; either version 3 of the License, or (at
  9. ;;; your option) any later version.
  10. ;;;
  11. ;;; GNU Guix is distributed in the hope that it will be useful, but
  12. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. ;;; GNU General Public License for more details.
  15. ;;;
  16. ;;; You should have received a copy of the GNU General Public License
  17. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  18. (define-module (test-minetest)
  19. #:use-module (guix build-system minetest)
  20. #:use-module (guix upstream)
  21. #:use-module (guix memoization)
  22. #:use-module (guix import minetest)
  23. #:use-module (guix import utils)
  24. #:use-module (guix tests)
  25. #:use-module (guix tests http)
  26. #:use-module (guix packages)
  27. #:use-module (guix git-download)
  28. #:use-module ((gnu packages minetest)
  29. #:select (minetest minetest-technic))
  30. #:use-module ((gnu packages base)
  31. #:select (hello))
  32. #:use-module (json)
  33. #:use-module (web request)
  34. #:use-module (web uri)
  35. #:use-module (web client)
  36. #:use-module (ice-9 match)
  37. #:use-module (srfi srfi-1)
  38. #:use-module (srfi srfi-26)
  39. #:use-module (srfi srfi-34)
  40. #:use-module (srfi srfi-64))
  41. ;; Some procedures for populating a ‘fake’ ContentDB server.
  42. (define* (make-package-sexp #:key
  43. (guix-name "minetest-foo")
  44. ;; This is not a proper version number but
  45. ;; ContentDB often does not include version
  46. ;; numbers.
  47. (version "2021-07-25")
  48. (home-page "https://example.org/foo")
  49. (repo "https://example.org/foo.git")
  50. (synopsis "synopsis")
  51. (guix-description "description")
  52. (guix-license
  53. '(list license:cc-by-sa4.0 license:lgpl3+))
  54. (inputs '())
  55. (upstream-name "Author/foo")
  56. #:allow-other-keys)
  57. `(package
  58. (name ,guix-name)
  59. (version ,version)
  60. (source
  61. (origin
  62. (method git-fetch)
  63. (uri (git-reference
  64. (url ,(and (not (eq? repo 'null)) repo))
  65. (commit #f)))
  66. (sha256
  67. (base32 #f))
  68. (file-name (git-file-name name version))))
  69. (build-system minetest-mod-build-system)
  70. ,@(maybe-propagated-inputs inputs)
  71. (home-page ,home-page)
  72. (synopsis ,synopsis)
  73. (description ,guix-description)
  74. (license ,guix-license)
  75. (properties
  76. ,(list 'quasiquote
  77. `((upstream-name . ,upstream-name))))))
  78. (define* (make-package-json #:key
  79. (author "Author")
  80. (name "foo")
  81. (media-license "CC-BY-SA-4.0")
  82. (license "LGPL-3.0-or-later")
  83. (short-description "synopsis")
  84. (long-description "description")
  85. (repo "https://example.org/foo.git")
  86. (website "https://example.org/foo")
  87. (forums 321)
  88. (score 987.654)
  89. (downloads 123)
  90. (type "mod")
  91. #:allow-other-keys)
  92. `(("author" . ,author)
  93. ("content_warnings" . #())
  94. ("created_at" . "2018-05-23T19:58:07.422108")
  95. ("downloads" . ,downloads)
  96. ("forums" . ,forums)
  97. ("issue_tracker" . "https://example.org/foo/issues")
  98. ("license" . ,license)
  99. ("long_description" . ,long-description)
  100. ("maintainers" . #("maintainer"))
  101. ("media_license" . ,media-license)
  102. ("name" . ,name)
  103. ("provides" . #("stuff"))
  104. ("release" . 456)
  105. ("repo" . ,repo)
  106. ("score" . ,score)
  107. ("screenshots" . #())
  108. ("short_description" . ,short-description)
  109. ("state" . "APPROVED")
  110. ("tags" . #("some" "tags"))
  111. ("thumbnail" . null)
  112. ("title" . "The name")
  113. ("type" . ,type)
  114. ("url" . ,(string-append "https://content.minetest.net/packages/"
  115. author "/" name "/download/"))
  116. ("website" . ,website)))
  117. (define* (make-releases-json #:key (commit #f) (title "2021-07-25") #:allow-other-keys)
  118. `#((("commit" . ,commit)
  119. ("downloads" . 469)
  120. ("id" . 8614)
  121. ("max_minetest_version" . null)
  122. ("min_minetest_version" . null)
  123. ("release_date" . "2021-07-25T01:10:23.207584")
  124. ("title" . ,title))))
  125. (define* (make-dependencies-json #:key (author "Author")
  126. (name "foo")
  127. (requirements '(("default" #f ())))
  128. #:allow-other-keys)
  129. `((,(string-append author "/" name)
  130. . ,(list->vector
  131. (map (match-lambda
  132. ((symbolic-name optional? implementations)
  133. `(("is_optional" . ,optional?)
  134. ("name" . ,symbolic-name)
  135. ("packages" . ,(list->vector implementations)))))
  136. requirements)))
  137. ("something/else" . #())))
  138. (define* (make-packages-keys-json #:key (author "Author")
  139. (name "Name")
  140. (type "mod"))
  141. `(("author" . ,author)
  142. ("name" . ,name)
  143. ("type" . ,type)))
  144. (define (call-with-packages thunk . argument-lists)
  145. ;; Don't reuse results from previous tests.
  146. (invalidate-memoization! contentdb-fetch)
  147. (invalidate-memoization! minetest->guix-package)
  148. (define (scm->json-port scm)
  149. (open-input-string (scm->json-string scm)))
  150. (define (handle-package subresource requested-author requested-name . rest)
  151. (define relevant-argument-list
  152. (any (lambda (argument-list)
  153. (apply (lambda* (#:key (author "Author") (name "foo")
  154. #:allow-other-keys)
  155. (and (equal? requested-author author)
  156. (equal? requested-name name)
  157. argument-list))
  158. argument-list))
  159. argument-lists))
  160. (when (not relevant-argument-list)
  161. (error "the package ~a/~a should be irrelevant, but ~a is fetched"
  162. requested-author requested-name subresource))
  163. (define json (apply
  164. (match rest
  165. (("") make-package-json)
  166. (("dependencies" "") make-dependencies-json)
  167. (("releases" "") make-releases-json)
  168. (_ (error "TODO ~a" rest)))
  169. relevant-argument-list))
  170. (values '() (lambda (port) (scm->json json port))))
  171. (define (handle-mod-search sort)
  172. ;; Produce search results, sorted by SORT in descending order.
  173. (define arguments->key
  174. (match sort
  175. ("score" (lambda* (#:key (score 987.654) #:allow-other-keys)
  176. score))
  177. ("downloads" (lambda* (#:key (downloads 123) #:allow-other-keys)
  178. downloads))))
  179. (define argument-list->key (cut apply arguments->key <>))
  180. (define (greater x y)
  181. (> (argument-list->key x) (argument-list->key y)))
  182. (define sorted-argument-lists (sort-list argument-lists greater))
  183. (define* (arguments->json #:key (author "Author") (name "Foo") (type "mod")
  184. #:allow-other-keys)
  185. (and (string=? type "mod")
  186. `(("author" . ,author)
  187. ("name" . ,name)
  188. ("type" . ,type))))
  189. (define argument-list->json (cut apply arguments->json <>))
  190. (define json
  191. (list->vector (filter-map argument-list->json sorted-argument-lists)))
  192. (values '()
  193. (lambda (port) (scm->json json port))))
  194. (with-http-server*
  195. (lambda (request _)
  196. (unless (eq? 'GET (request-method request))
  197. (error "wrong HTTP method"))
  198. (define resource (uri-path (request-uri request)))
  199. (unless (string-prefix? "/api/packages/" resource)
  200. (error "the resource ~a should not be used" resource))
  201. (define subresource
  202. (substring resource (string-length "/api/packages/")))
  203. (define components (string-split subresource #\/))
  204. (match components
  205. ((author name . rest)
  206. (apply handle-package subresource author name rest))
  207. (("")
  208. (let ((query (uri-query (request-uri request))))
  209. (handle-mod-search
  210. (cond ((string-contains query "sort=score") "score")
  211. ((string-contains query "sort=downloads") "downloads")
  212. (#t (error "search query ~a has unknown sort key"
  213. query))))))
  214. (_
  215. (error "the resource ~a should have an author and name component"
  216. resource))))
  217. (parameterize ((%contentdb-api (%local-url "/api/"))
  218. (current-http-proxy #f))
  219. ;; XXX: for some unknown reason, parallelism causes ECONNREFUSED in
  220. ;; tests but not in the wild.
  221. (mock ((guix import minetest) par-map map)
  222. (thunk)))))
  223. (define* (minetest->guix-package* #:key (author "Author") (name "foo")
  224. (sort %default-sort-key)
  225. #:allow-other-keys)
  226. (minetest->guix-package (string-append author "/" name) #:sort sort))
  227. (define (imported-package-sexp* primary-arguments . secondary-arguments)
  228. "Ask the importer to import a package specified by PRIMARY-ARGUMENTS,
  229. during a dynamic where that package and the packages specified by
  230. SECONDARY-ARGUMENTS are available on ContentDB."
  231. (apply call-with-packages
  232. (lambda ()
  233. ;; The memoization cache is reset by call-with-packages
  234. (apply minetest->guix-package* primary-arguments))
  235. primary-arguments
  236. secondary-arguments))
  237. (define (imported-package-sexp . extra-arguments)
  238. "Ask the importer to import a package specified by EXTRA-ARGUMENTS,
  239. during a dynamic extent where that package is available on ContentDB."
  240. (imported-package-sexp* extra-arguments))
  241. (define-syntax-rule (test-package test-case . extra-arguments)
  242. (test-equal test-case
  243. (make-package-sexp . extra-arguments)
  244. (imported-package-sexp . extra-arguments)))
  245. (define-syntax-rule (test-package* test-case primary-arguments extra-arguments
  246. ...)
  247. (test-equal test-case
  248. (apply make-package-sexp primary-arguments)
  249. (imported-package-sexp* primary-arguments extra-arguments ...)))
  250. (test-begin "minetest")
  251. ;; Package names
  252. (test-package "minetest->guix-package")
  253. (test-package "minetest->guix-package, _ → - in package name"
  254. #:name "foo_bar"
  255. #:guix-name "minetest-foo-bar"
  256. #:upstream-name "Author/foo_bar")
  257. (test-equal "elaborate names, unambiguous"
  258. "Jeija/mesecons"
  259. (call-with-packages
  260. (cut elaborate-contentdb-name "mesecons")
  261. '(#:name "mesecons" #:author "Jeija")
  262. '(#:name "something" #:author "else")))
  263. (test-equal "elaborate name, ambiguous (highest score)"
  264. "Jeija/mesecons"
  265. (call-with-packages
  266. ;; #:sort "score" is the default
  267. (cut elaborate-contentdb-name "mesecons")
  268. '(#:name "mesecons" #:author "Jeijc" #:score 777)
  269. '(#:name "mesecons" #:author "Jeijb" #:score 888)
  270. '(#:name "mesecons" #:author "Jeija" #:score 999)))
  271. (test-equal "elaborate name, ambiguous (most downloads)"
  272. "Jeija/mesecons"
  273. (call-with-packages
  274. (cut elaborate-contentdb-name "mesecons" #:sort "downloads")
  275. '(#:name "mesecons" #:author "Jeijc" #:downloads 777)
  276. '(#:name "mesecons" #:author "Jeijb" #:downloads 888)
  277. '(#:name "mesecons" #:author "Jeija" #:downloads 999)))
  278. ;; Determining the home page
  279. (test-package "minetest->guix-package, website is used as home page"
  280. #:home-page "web://site"
  281. #:website "web://site")
  282. (test-package "minetest->guix-package, if absent, the forum is used"
  283. #:home-page '(minetest-topic 628)
  284. #:forums 628
  285. #:website 'null)
  286. (test-package "minetest->guix-package, if absent, the git repo is used"
  287. #:home-page "https://github.com/minetest-mods/mesecons"
  288. #:forums 'null
  289. #:website 'null
  290. #:repo "https://github.com/minetest-mods/mesecons")
  291. (test-package "minetest->guix-package, all home page information absent"
  292. #:home-page #f
  293. #:forums 'null
  294. #:website 'null
  295. #:repo 'null)
  296. ;; Determining the version number
  297. (test-package "conventional version number" #:version "1.2.3" #:title "1.2.3")
  298. ;; See e.g. orwell/basic_trains
  299. (test-package "v-prefixed version number" #:version "1.2.3" #:title "v1.2.3")
  300. ;; Many mods on ContentDB use dates as release titles. In that case, the date
  301. ;; will have to do.
  302. (test-package "dates as version number"
  303. #:version "2021-01-01" #:title "2021-01-01")
  304. ;; Dependencies
  305. (test-package* "minetest->guix-package, unambiguous dependency"
  306. (list #:requirements '(("mesecons" #f
  307. ("Jeija/mesecons"
  308. "some-modpack/containing-mese")))
  309. #:inputs '("minetest-mesecons"))
  310. (list #:author "Jeija" #:name "mesecons")
  311. (list #:author "some-modpack" #:name "containing-mese" #:type "modpack"))
  312. (test-package* "minetest->guix-package, ambiguous dependency (highest score)"
  313. (list #:name "frobnicate"
  314. #:guix-name "minetest-frobnicate"
  315. #:upstream-name "Author/frobnicate"
  316. #:requirements '(("frob" #f
  317. ("Author/foo" "Author/bar")))
  318. ;; #:sort "score" is the default
  319. #:inputs '("minetest-bar"))
  320. (list #:author "Author" #:name "foo" #:score 0)
  321. (list #:author "Author" #:name "bar" #:score 9999))
  322. (test-package* "minetest->guix-package, ambiguous dependency (most downloads)"
  323. (list #:name "frobnicate"
  324. #:guix-name "minetest-frobnicate"
  325. #:upstream-name "Author/frobnicate"
  326. #:requirements '(("frob" #f
  327. ("Author/foo" "Author/bar")))
  328. #:inputs '("minetest-bar")
  329. #:sort "downloads")
  330. (list #:author "Author" #:name "foo" #:downloads 0)
  331. (list #:author "Author" #:name "bar" #:downloads 9999))
  332. (test-package "minetest->guix-package, optional dependency"
  333. #:requirements '(("mesecons" #t
  334. ("Jeija/mesecons"
  335. "some-modpack/containing-mese")))
  336. #:inputs '())
  337. ;; See e.g. 'orwell/basic_trains'
  338. (test-package* "minetest->guix-package, multiple dependencies implemented by one mod"
  339. (list #:name "frobnicate"
  340. #:guix-name "minetest-frobnicate"
  341. #:upstream-name "Author/frobnicate"
  342. #:requirements '(("frob" #f ("Author/frob"))
  343. ("frob_x" #f ("Author/frob")))
  344. #:inputs '("minetest-frob"))
  345. (list #:author "Author" #:name "frob"))
  346. ;; License
  347. (test-package "minetest->guix-package, identical licenses"
  348. #:guix-license 'license:lgpl3+
  349. #:license "LGPL-3.0-or-later"
  350. #:media-license "LGPL-3.0-or-later")
  351. ;; Sorting
  352. (let* ((make-package
  353. (lambda arguments
  354. (json->package (apply make-package-json arguments))))
  355. (x (make-package #:score 0))
  356. (y (make-package #:score 1))
  357. (z (make-package #:score 2)))
  358. (test-equal "sort-packages, already sorted"
  359. (list z y x)
  360. (sort-packages (list z y x)))
  361. (test-equal "sort-packages, reverse"
  362. (list z y x)
  363. (sort-packages (list x y z))))
  364. ;; Update detection
  365. (define (upstream-source->sexp upstream-source)
  366. (define url (upstream-source-urls upstream-source))
  367. (unless (git-reference? url)
  368. (error "a <git-reference> is expected"))
  369. `(,(upstream-source-package upstream-source)
  370. ,(upstream-source-version upstream-source)
  371. ,(git-reference-url url)
  372. ,(git-reference-commit url)))
  373. (define* (expected-sexp #:key
  374. (repo "https://example.org/foo.git")
  375. (guix-name "minetest-foo")
  376. (new-version "0.8")
  377. (commit "44941798d222901b8f381b3210957d880b90a2fc")
  378. #:allow-other-keys)
  379. `(,guix-name ,new-version ,repo ,commit))
  380. (define* (example-package #:key
  381. (source 'auto)
  382. (repo "https://example.org/foo.git")
  383. (old-version "0.8")
  384. (commit "44941798d222901b8f381b3210957d880b90a2fc")
  385. #:allow-other-keys)
  386. (package
  387. (name "minetest-foo")
  388. (version old-version)
  389. (source
  390. (if (eq? source 'auto)
  391. (origin
  392. (method git-fetch)
  393. (uri (git-reference
  394. (url repo)
  395. (commit commit #;"808f9ffbd3106da4c92d2367b118b98196c9e81e")))
  396. (sha256 #f) ; not important for the following tests
  397. (file-name (git-file-name name version)))
  398. source))
  399. (build-system minetest-mod-build-system)
  400. (license #f)
  401. (synopsis #f)
  402. (description #f)
  403. (home-page #f)
  404. (properties '((upstream-name . "Author/foo")))))
  405. (define-syntax-rule (test-release test-case . arguments)
  406. (test-equal test-case
  407. (expected-sexp . arguments)
  408. (and=>
  409. (call-with-packages
  410. (cut latest-minetest-release (example-package . arguments))
  411. (list . arguments))
  412. upstream-source->sexp)))
  413. (define-syntax-rule (test-no-release test-case . arguments)
  414. (test-equal test-case
  415. #f
  416. (call-with-packages
  417. (cut latest-minetest-release (example-package . arguments))
  418. (list . arguments))))
  419. (test-release "same version"
  420. #:old-version "0.8" #:title "0.8" #:new-version "0.8"
  421. #:commit "44941798d222901b8f381b3210957d880b90a2fc")
  422. (test-release "new version (dotted)"
  423. #:old-version "0.8" #:title "0.9.0" #:new-version "0.9.0"
  424. #:commit "c8855b991880897b2658dc90164e29c96e2aeb3a")
  425. (test-release "new version (date)"
  426. #:old-version "2014-11-17" #:title "2015-11-04"
  427. #:new-version "2015-11-04"
  428. #:commit "c8855b991880897b2658dc90164e29c96e2aeb3a")
  429. (test-release "new version (git -> dotted)"
  430. #:old-version
  431. (git-version "0.8" "1" "90422555f114d3af35e7cc4b5b6d59a5c226adc4")
  432. #:title "0.9.0" #:new-version "0.9.0"
  433. #:commit "90422555f114d3af35e7cc4b5b6d59a5c226adc4")
  434. ;; There might actually be a new release, but guix cannot compare dates
  435. ;; with regular version numbers.
  436. (test-no-release "dotted -> date"
  437. #:old-version "0.8" #:title "2015-11-04"
  438. #:commit "c8855b991880897b2658dc90164e29c96e2aeb3a")
  439. (test-no-release "date -> dotted"
  440. #:old-version "2014-11-07" #:title "0.8"
  441. #:commit "c8855b991880897b2658dc90164e29c96e2aeb3a")
  442. ;; Don't let "guix refresh -t minetest" tell there are new versions
  443. ;; if Guix has insufficient information to actually perform the update,
  444. ;; when using --with-latest or "guix refresh -u".
  445. (test-no-release "no commit information, no new release"
  446. #:old-version "0.8" #:title "0.9.0" #:new-version "0.9.0"
  447. #:commit #false)
  448. (test-assert "minetest is not a minetest mod"
  449. (not (minetest-package? minetest)))
  450. (test-assert "GNU hello is not a minetest mod"
  451. (not (minetest-package? hello)))
  452. (test-assert "technic is a minetest mod"
  453. (minetest-package? minetest-technic))
  454. (test-assert "upstream-name is required"
  455. (not (minetest-package?
  456. (package (inherit minetest-technic)
  457. (properties '())))))
  458. (test-end "minetest")
  459. ;;; Local Variables:
  460. ;;; eval: (put 'test-package* 'scheme-indent-function 1)
  461. ;;; eval: (put 'test-release 'scheme-indent-function 1)
  462. ;;; eval: (put 'test-no-release 'scheme-indent-function 1)
  463. ;;; End: