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) | |
20 | #:use-module (guix memoization) | |
21 | #:use-module (guix import minetest) | |
22 | #:use-module (guix import utils) | |
23 | #:use-module (guix tests) | |
24 | #:use-module (json) | |
25 | #:use-module (ice-9 match) | |
26 | #:use-module (srfi srfi-1) | |
27 | #:use-module (srfi srfi-26) | |
28 | #:use-module (srfi srfi-34) | |
29 | #:use-module (srfi srfi-64)) | |
30 | ||
31 | \f | |
32 | ;; Some procedures for populating a ‘fake’ ContentDB server. | |
33 | ||
34 | (define* (make-package-sexp #:key | |
35 | (guix-name "minetest-foo") | |
808f9ffb MD |
36 | ;; This is not a proper version number but |
37 | ;; ContentDB often does not include version | |
38 | ;; numbers. | |
39 | (version "2021-07-25") | |
467e874a MD |
40 | (home-page "https://example.org/foo") |
41 | (repo "https://example.org/foo.git") | |
42 | (synopsis "synopsis") | |
43 | (guix-description "description") | |
44 | (guix-license | |
45 | '(list license:cc-by-sa4.0 license:lgpl3+)) | |
46 | (inputs '()) | |
47 | (upstream-name "Author/foo") | |
48 | #:allow-other-keys) | |
49 | `(package | |
50 | (name ,guix-name) | |
808f9ffb | 51 | (version ,version) |
467e874a MD |
52 | (source |
53 | (origin | |
54 | (method git-fetch) | |
55 | (uri (git-reference | |
56 | (url ,(and (not (eq? repo 'null)) repo)) | |
57 | (commit #f))) | |
58 | (sha256 | |
59 | (base32 #f)) | |
60 | (file-name (git-file-name name version)))) | |
61 | (build-system minetest-mod-build-system) | |
62 | ,@(maybe-propagated-inputs inputs) | |
63 | (home-page ,home-page) | |
64 | (synopsis ,synopsis) | |
65 | (description ,guix-description) | |
66 | (license ,guix-license) | |
67 | (properties | |
68 | ,(list 'quasiquote | |
69 | `((upstream-name . ,upstream-name)))))) | |
70 | ||
71 | (define* (make-package-json #:key | |
72 | (author "Author") | |
73 | (name "foo") | |
74 | (media-license "CC-BY-SA-4.0") | |
75 | (license "LGPL-3.0-or-later") | |
76 | (short-description "synopsis") | |
77 | (long-description "description") | |
78 | (repo "https://example.org/foo.git") | |
79 | (website "https://example.org/foo") | |
80 | (forums 321) | |
81 | (score 987.654) | |
82 | (downloads 123) | |
83 | (type "mod") | |
84 | #:allow-other-keys) | |
85 | `(("author" . ,author) | |
86 | ("content_warnings" . #()) | |
87 | ("created_at" . "2018-05-23T19:58:07.422108") | |
88 | ("downloads" . ,downloads) | |
89 | ("forums" . ,forums) | |
90 | ("issue_tracker" . "https://example.org/foo/issues") | |
91 | ("license" . ,license) | |
92 | ("long_description" . ,long-description) | |
93 | ("maintainers" . #("maintainer")) | |
94 | ("media_license" . ,media-license) | |
95 | ("name" . ,name) | |
96 | ("provides" . #("stuff")) | |
97 | ("release" . 456) | |
98 | ("repo" . ,repo) | |
99 | ("score" . ,score) | |
100 | ("screenshots" . #()) | |
101 | ("short_description" . ,short-description) | |
102 | ("state" . "APPROVED") | |
103 | ("tags" . #("some" "tags")) | |
104 | ("thumbnail" . null) | |
105 | ("title" . "The name") | |
106 | ("type" . ,type) | |
107 | ("url" . ,(string-append "https://content.minetest.net/packages/" | |
108 | author "/" name "/download/")) | |
109 | ("website" . ,website))) | |
110 | ||
808f9ffb | 111 | (define* (make-releases-json #:key (commit #f) (title "2021-07-25") #:allow-other-keys) |
467e874a MD |
112 | `#((("commit" . ,commit) |
113 | ("downloads" . 469) | |
114 | ("id" . 8614) | |
115 | ("max_minetest_version" . null) | |
116 | ("min_minetest_version" . null) | |
117 | ("release_date" . "2021-07-25T01:10:23.207584") | |
808f9ffb | 118 | ("title" . ,title)))) |
467e874a MD |
119 | |
120 | (define* (make-dependencies-json #:key (author "Author") | |
121 | (name "foo") | |
122 | (requirements '(("default" #f ()))) | |
123 | #:allow-other-keys) | |
124 | `((,(string-append author "/" name) | |
125 | . ,(list->vector | |
126 | (map (match-lambda | |
127 | ((symbolic-name optional? implementations) | |
128 | `(("is_optional" . ,optional?) | |
129 | ("name" . ,symbolic-name) | |
130 | ("packages" . ,(list->vector implementations))))) | |
131 | requirements))) | |
132 | ("something/else" . #()))) | |
133 | ||
134 | (define* (make-packages-keys-json #:key (author "Author") | |
135 | (name "Name") | |
136 | (type "mod")) | |
137 | `(("author" . ,author) | |
138 | ("name" . ,name) | |
139 | ("type" . ,type))) | |
140 | ||
141 | (define (call-with-packages thunk . argument-lists) | |
142 | ;; Don't reuse results from previous tests. | |
143 | (invalidate-memoization! contentdb-fetch) | |
144 | (invalidate-memoization! minetest->guix-package) | |
145 | (define (scm->json-port scm) | |
146 | (open-input-string (scm->json-string scm))) | |
147 | (define (handle-package url requested-author requested-name . rest) | |
148 | (define relevant-argument-list | |
149 | (any (lambda (argument-list) | |
150 | (apply (lambda* (#:key (author "Author") (name "foo") | |
151 | #:allow-other-keys) | |
152 | (and (equal? requested-author author) | |
153 | (equal? requested-name name) | |
154 | argument-list)) | |
155 | argument-list)) | |
156 | argument-lists)) | |
157 | (when (not relevant-argument-list) | |
158 | (error "the package ~a/~a should be irrelevant, but ~a is fetched" | |
159 | requested-author requested-name url)) | |
160 | (scm->json-port | |
161 | (apply (match rest | |
162 | (("") make-package-json) | |
163 | (("dependencies" "") make-dependencies-json) | |
164 | (("releases" "") make-releases-json) | |
165 | (_ (error "TODO ~a" rest))) | |
166 | relevant-argument-list))) | |
167 | (define (handle-mod-search sort) | |
168 | ;; Produce search results, sorted by SORT in descending order. | |
169 | (define arguments->key | |
170 | (match sort | |
171 | ("score" (lambda* (#:key (score 987.654) #:allow-other-keys) | |
172 | score)) | |
173 | ("downloads" (lambda* (#:key (downloads 123) #:allow-other-keys) | |
174 | downloads)))) | |
175 | (define argument-list->key (cut apply arguments->key <>)) | |
176 | (define (greater x y) | |
177 | (> (argument-list->key x) (argument-list->key y))) | |
178 | (define sorted-argument-lists (sort-list argument-lists greater)) | |
179 | (define* (arguments->json #:key (author "Author") (name "Foo") (type "mod") | |
180 | #:allow-other-keys) | |
181 | (and (string=? type "mod") | |
182 | `(("author" . ,author) | |
183 | ("name" . ,name) | |
184 | ("type" . ,type)))) | |
185 | (define argument-list->json (cut apply arguments->json <>)) | |
186 | (scm->json-port | |
187 | (list->vector (filter-map argument-list->json sorted-argument-lists)))) | |
188 | (mock ((guix http-client) http-fetch | |
189 | (lambda* (url #:key headers) | |
190 | (unless (string-prefix? "mock://api/packages/" url) | |
191 | (error "the URL ~a should not be used" url)) | |
192 | (define resource | |
193 | (substring url (string-length "mock://api/packages/"))) | |
194 | (define components (string-split resource #\/)) | |
195 | (match components | |
196 | ((author name . rest) | |
197 | (apply handle-package url author name rest)) | |
198 | (((? (cut string-prefix? "?type=mod&q=" <>) query)) | |
199 | (handle-mod-search | |
200 | (cond ((string-contains query "sort=score") "score") | |
201 | ((string-contains query "sort=downloads") "downloads") | |
202 | (#t (error "search query ~a has unknown sort key" | |
203 | query))))) | |
204 | (_ | |
205 | (error "the URL ~a should have an author and name component" | |
206 | url))))) | |
207 | (parameterize ((%contentdb-api "mock://api/")) | |
208 | (thunk)))) | |
209 | ||
210 | (define* (minetest->guix-package* #:key (author "Author") (name "foo") | |
211 | (sort %default-sort-key) | |
212 | #:allow-other-keys) | |
213 | (minetest->guix-package (string-append author "/" name) #:sort sort)) | |
214 | ||
215 | (define (imported-package-sexp* primary-arguments . secondary-arguments) | |
216 | "Ask the importer to import a package specified by PRIMARY-ARGUMENTS, | |
217 | during a dynamic where that package and the packages specified by | |
218 | SECONDARY-ARGUMENTS are available on ContentDB." | |
219 | (apply call-with-packages | |
220 | (lambda () | |
221 | ;; The memoization cache is reset by call-with-packages | |
222 | (apply minetest->guix-package* primary-arguments)) | |
223 | primary-arguments | |
224 | secondary-arguments)) | |
225 | ||
226 | (define (imported-package-sexp . extra-arguments) | |
227 | "Ask the importer to import a package specified by EXTRA-ARGUMENTS, | |
228 | during a dynamic extent where that package is available on ContentDB." | |
229 | (imported-package-sexp* extra-arguments)) | |
230 | ||
231 | (define-syntax-rule (test-package test-case . extra-arguments) | |
232 | (test-equal test-case | |
233 | (make-package-sexp . extra-arguments) | |
234 | (imported-package-sexp . extra-arguments))) | |
235 | ||
236 | (define-syntax-rule (test-package* test-case primary-arguments extra-arguments | |
237 | ...) | |
238 | (test-equal test-case | |
239 | (apply make-package-sexp primary-arguments) | |
240 | (imported-package-sexp* primary-arguments extra-arguments ...))) | |
241 | ||
242 | (test-begin "minetest") | |
243 | ||
244 | \f | |
245 | ;; Package names | |
246 | (test-package "minetest->guix-package") | |
247 | (test-package "minetest->guix-package, _ → - in package name" | |
248 | #:name "foo_bar" | |
249 | #:guix-name "minetest-foo-bar" | |
250 | #:upstream-name "Author/foo_bar") | |
251 | ||
2f5368d6 | 252 | (test-equal "elaborate names, unambiguous" |
467e874a MD |
253 | "Jeija/mesecons" |
254 | (call-with-packages | |
255 | (cut elaborate-contentdb-name "mesecons") | |
256 | '(#:name "mesecons" #:author "Jeija") | |
257 | '(#:name "something" #:author "else"))) | |
258 | ||
2f5368d6 | 259 | (test-equal "elaborate name, ambiguous (highest score)" |
467e874a MD |
260 | "Jeija/mesecons" |
261 | (call-with-packages | |
262 | ;; #:sort "score" is the default | |
263 | (cut elaborate-contentdb-name "mesecons") | |
264 | '(#:name "mesecons" #:author "Jeijc" #:score 777) | |
265 | '(#:name "mesecons" #:author "Jeijb" #:score 888) | |
266 | '(#:name "mesecons" #:author "Jeija" #:score 999))) | |
267 | ||
268 | ||
2f5368d6 | 269 | (test-equal "elaborate name, ambiguous (most downloads)" |
467e874a MD |
270 | "Jeija/mesecons" |
271 | (call-with-packages | |
272 | (cut elaborate-contentdb-name "mesecons" #:sort "downloads") | |
273 | '(#:name "mesecons" #:author "Jeijc" #:downloads 777) | |
274 | '(#:name "mesecons" #:author "Jeijb" #:downloads 888) | |
275 | '(#:name "mesecons" #:author "Jeija" #:downloads 999))) | |
276 | ||
277 | \f | |
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 | ||
297 | \f | |
808f9ffb MD |
298 | ;; Determining the version number |
299 | ||
300 | (test-package "conventional version number" #:version "1.2.3" #:title "1.2.3") | |
301 | ;; See e.g. orwell/basic_trains | |
302 | (test-package "v-prefixed version number" #:version "1.2.3" #:title "v1.2.3") | |
303 | ;; Many mods on ContentDB use dates as release titles. In that case, the date | |
304 | ;; will have to do. | |
305 | (test-package "dates as version number" | |
306 | #:version "2021-01-01" #:title "2021-01-01") | |
307 | ||
308 | \f | |
467e874a MD |
309 | |
310 | ;; Dependencies | |
2f5368d6 | 311 | (test-package* "minetest->guix-package, unambiguous dependency" |
467e874a MD |
312 | (list #:requirements '(("mesecons" #f |
313 | ("Jeija/mesecons" | |
314 | "some-modpack/containing-mese"))) | |
315 | #:inputs '("minetest-mesecons")) | |
316 | (list #:author "Jeija" #:name "mesecons") | |
317 | (list #:author "some-modpack" #:name "containing-mese" #:type "modpack")) | |
318 | ||
2f5368d6 | 319 | (test-package* "minetest->guix-package, ambiguous dependency (highest score)" |
467e874a MD |
320 | (list #:name "frobnicate" |
321 | #:guix-name "minetest-frobnicate" | |
322 | #:upstream-name "Author/frobnicate" | |
323 | #:requirements '(("frob" #f | |
324 | ("Author/foo" "Author/bar"))) | |
325 | ;; #:sort "score" is the default | |
326 | #:inputs '("minetest-bar")) | |
327 | (list #:author "Author" #:name "foo" #:score 0) | |
328 | (list #:author "Author" #:name "bar" #:score 9999)) | |
329 | ||
2f5368d6 | 330 | (test-package* "minetest->guix-package, ambiguous dependency (most downloads)" |
467e874a MD |
331 | (list #:name "frobnicate" |
332 | #:guix-name "minetest-frobnicate" | |
333 | #:upstream-name "Author/frobnicate" | |
334 | #:requirements '(("frob" #f | |
335 | ("Author/foo" "Author/bar"))) | |
336 | #:inputs '("minetest-bar") | |
337 | #:sort "downloads") | |
338 | (list #:author "Author" #:name "foo" #:downloads 0) | |
339 | (list #:author "Author" #:name "bar" #:downloads 9999)) | |
340 | ||
341 | (test-package "minetest->guix-package, optional dependency" | |
342 | #:requirements '(("mesecons" #t | |
343 | ("Jeija/mesecons" | |
344 | "some-modpack/containing-mese"))) | |
345 | #:inputs '()) | |
346 | ||
8480a2a5 MD |
347 | ;; See e.g. 'orwell/basic_trains' |
348 | (test-package* "minetest->guix-package, multiple dependencies implemented by one mod" | |
349 | (list #:name "frobnicate" | |
350 | #:guix-name "minetest-frobnicate" | |
351 | #:upstream-name "Author/frobnicate" | |
352 | #:requirements '(("frob" #f ("Author/frob")) | |
353 | ("frob_x" #f ("Author/frob"))) | |
354 | #:inputs '("minetest-frob")) | |
355 | (list #:author "Author" #:name "frob")) | |
356 | ||
467e874a MD |
357 | \f |
358 | ;; License | |
359 | (test-package "minetest->guix-package, identical licenses" | |
360 | #:guix-license 'license:lgpl3+ | |
361 | #:license "LGPL-3.0-or-later" | |
362 | #:media-license "LGPL-3.0-or-later") | |
363 | ||
364 | ;; Sorting | |
365 | (let* ((make-package | |
366 | (lambda arguments | |
367 | (json->package (apply make-package-json arguments)))) | |
368 | (x (make-package #:score 0)) | |
369 | (y (make-package #:score 1)) | |
370 | (z (make-package #:score 2))) | |
371 | (test-equal "sort-packages, already sorted" | |
372 | (list z y x) | |
373 | (sort-packages (list z y x))) | |
374 | (test-equal "sort-packages, reverse" | |
375 | (list z y x) | |
376 | (sort-packages (list x y z)))) | |
377 | ||
378 | (test-end "minetest") | |
8d4c0e31 MD |
379 | |
380 | ;;; Local Variables: | |
381 | ;;; eval: (put 'test-package* 'scheme-indent-function 1) | |
382 | ;;; End: |