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