1 ;;; GNU Guix --- Functional package management for GNU
2 ;;; Copyright © 2014 David Thompson <davet@gnu.org>
3 ;;; Copyright © 2016 Ricardo Wurmus <rekado@elephly.net>
5 ;;; This file is part of GNU Guix.
7 ;;; GNU Guix is free software; you can redistribute it and/or modify it
8 ;;; under the terms of the GNU General Public License as published by
9 ;;; the Free Software Foundation; either version 3 of the License, or (at
10 ;;; your option) any later version.
12 ;;; GNU Guix is distributed in the hope that it will be useful, but
13 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
14 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 ;;; GNU General Public License for more details.
17 ;;; You should have received a copy of the GNU General Public License
18 ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
20 (define-module (test-pypi)
21 #:use-module (guix import pypi)
22 #:use-module (guix base32)
23 #:use-module (guix memoization)
24 #:use-module (gcrypt hash)
25 #:use-module (guix tests)
26 #:use-module (guix build-system python)
27 #:use-module ((guix build utils) #:select (delete-file-recursively which mkdir-p))
28 #:use-module (srfi srfi-64)
29 #:use-module (ice-9 match))
34 \"version\": \"1.0.0\",
36 \"license\": \"GNU LGPL\",
37 \"summary\": \"summary\",
38 \"home_page\": \"http://example.com\",
43 \"url\": \"https://example.com/foo-1.0.0.egg\",
44 \"packagetype\": \"bdist_egg\",
46 \"url\": \"https://example.com/foo-1.0.0.tar.gz\",
47 \"packagetype\": \"sdist\",
49 \"url\": \"https://example.com/foo-1.0.0-py2.py3-none-any.whl\",
50 \"packagetype\": \"bdist_wheel\",
56 (define test-source-hash
59 (define test-specifications
61 "PickyThing<1.6,>1.9,!=1.9.6,<2.0a0,==2.4c1"
62 "SomethingWithMarker[foo]>1.0;python_version<\"2.7\""
63 "requests [security,tests] >= 2.8.1, == 2.8.* ; python_version < \"2.7\""
64 "pip @ https://github.com/pypa/pip/archive/1.3.1.zip#\
65 sha1=da9234ee9982d4bbb3c72346a6de940a148ea686"))
67 (define test-requires.txt "\
69 # A comment after a space
74 (define test-requires-with-sections "\
96 (test-equal "guix-package->pypi-name, old URL style"
98 (guix-package->pypi-name
100 (source (dummy-origin
102 "https://pypi.org/packages/source/p/psutil/psutil-4.3.0.tar.gz"))))))
104 (test-equal "guix-package->pypi-name, new URL style"
106 (guix-package->pypi-name
108 (source (dummy-origin
110 "https://pypi.org/packages/a2/3b/4756e6a0ceb14e084042a2a65c615d68d25621c6fd446d0fc10d14c4ce7d/certbot-0.8.1.tar.gz"))))))
112 (test-equal "guix-package->pypi-name, several URLs"
114 (guix-package->pypi-name
118 (uri (list "https://bitheap.org/cram/cram-0.7.tar.gz"
119 (pypi-uri "cram" "0.7"))))))))
121 (test-equal "specification->requirement-name"
122 '("Fizzy" "PickyThing" "SomethingWithMarker" "requests" "pip")
123 (map specification->requirement-name test-specifications))
125 (test-equal "parse-requires.txt, with sections"
127 (mock ((ice-9 ports) call-with-input-file
128 call-with-input-string)
129 (parse-requires.txt test-requires-with-sections)))
131 (test-assert "pypi->guix-package"
132 ;; Replace network resources with sample data.
133 (mock ((guix import utils) url-fetch
134 (lambda (url file-name)
136 ("https://example.com/foo-1.0.0.tar.gz"
138 ;; Unusual requires.txt location should still be found.
139 (mkdir-p "foo-1.0.0/src/bizarre.egg-info")
140 (with-output-to-file "foo-1.0.0/src/bizarre.egg-info/requires.txt"
142 (display test-requires.txt)))
143 (parameterize ((current-output-port (%make-void-port "rw+")))
144 (system* "tar" "czvf" file-name "foo-1.0.0/"))
145 (delete-file-recursively "foo-1.0.0")
146 (set! test-source-hash
147 (call-with-input-file file-name port-sha256))))
148 ("https://example.com/foo-1.0.0-py2.py3-none-any.whl" #f)
149 (_ (error "Unexpected URL: " url)))))
150 (mock ((guix http-client) http-fetch
153 ("https://pypi.org/pypi/foo/json"
154 (values (open-input-string test-json)
155 (string-length test-json)))
156 ("https://example.com/foo-1.0.0-py2.py3-none-any.whl" #f)
157 (_ (error "Unexpected URL: " url)))))
158 (match (pypi->guix-package "foo")
164 ('uri ('pypi-uri "foo" 'version))
168 ('build-system 'python-build-system)
171 (("python-bar" ('unquote 'python-bar))
172 ("python-baz" ('unquote 'python-baz)))))
173 ('home-page "http://example.com")
174 ('synopsis "summary")
175 ('description "summary")
176 ('license 'license:lgpl2.0))
177 (string=? (bytevector->nix-base32-string
183 (test-skip (if (which "zip") 0 1))
184 (test-assert "pypi->guix-package, wheels"
185 ;; Replace network resources with sample data.
186 (mock ((guix import utils) url-fetch
187 (lambda (url file-name)
189 ("https://example.com/foo-1.0.0.tar.gz"
191 (mkdir-p "foo-1.0.0/foo.egg-info/")
192 (with-output-to-file "foo-1.0.0/foo.egg-info/requires.txt"
194 (display test-requires.txt)))
195 (parameterize ((current-output-port (%make-void-port "rw+")))
196 (system* "tar" "czvf" file-name "foo-1.0.0/"))
197 (delete-file-recursively "foo-1.0.0")
198 (set! test-source-hash
199 (call-with-input-file file-name port-sha256))))
200 ("https://example.com/foo-1.0.0-py2.py3-none-any.whl"
202 (mkdir "foo-1.0.0.dist-info")
203 (with-output-to-file "foo-1.0.0.dist-info/metadata.json"
205 (display test-metadata)))
206 (let ((zip-file (string-append file-name ".zip")))
207 ;; zip always adds a "zip" extension to the file it creates,
208 ;; so we need to rename it.
209 (system* "zip" zip-file "foo-1.0.0.dist-info/metadata.json")
210 (rename-file zip-file file-name))
211 (delete-file-recursively "foo-1.0.0.dist-info")))
212 (_ (error "Unexpected URL: " url)))))
213 (mock ((guix http-client) http-fetch
216 ("https://pypi.org/pypi/foo/json"
217 (values (open-input-string test-json)
218 (string-length test-json)))
219 ("https://example.com/foo-1.0.0-py2.py3-none-any.whl" #f)
220 (_ (error "Unexpected URL: " url)))))
221 (match (pypi->guix-package "foo")
227 ('uri ('pypi-uri "foo" 'version))
231 ('build-system 'python-build-system)
234 (("python-bar" ('unquote 'python-bar))
235 ("python-baz" ('unquote 'python-baz)))))
236 ('home-page "http://example.com")
237 ('synopsis "summary")
238 ('description "summary")
239 ('license 'license:lgpl2.0))
240 (string=? (bytevector->nix-base32-string
246 (test-assert "pypi->guix-package, no usable requirement file."
247 ;; Replace network resources with sample data.
248 (mock ((guix import utils) url-fetch
249 (lambda (url file-name)
251 ("https://example.com/foo-1.0.0.tar.gz"
252 (mkdir-p "foo-1.0.0/foo.egg-info/")
253 (parameterize ((current-output-port (%make-void-port "rw+")))
254 (system* "tar" "czvf" file-name "foo-1.0.0/"))
255 (delete-file-recursively "foo-1.0.0")
256 (set! test-source-hash
257 (call-with-input-file file-name port-sha256)))
258 ("https://example.com/foo-1.0.0-py2.py3-none-any.whl" #f)
259 (_ (error "Unexpected URL: " url)))))
260 (mock ((guix http-client) http-fetch
263 ("https://pypi.org/pypi/foo/json"
264 (values (open-input-string test-json)
265 (string-length test-json)))
266 ("https://example.com/foo-1.0.0-py2.py3-none-any.whl" #f)
267 (_ (error "Unexpected URL: " url)))))
268 ;; Not clearing the memoization cache here would mean returning the value
269 ;; computed in the previous test.
270 (invalidate-memoization! pypi->guix-package)
271 (match (pypi->guix-package "foo")
277 ('uri ('pypi-uri "foo" 'version))
281 ('build-system 'python-build-system)
282 ('home-page "http://example.com")
283 ('synopsis "summary")
284 ('description "summary")
285 ('license 'license:lgpl2.0))
286 (string=? (bytevector->nix-base32-string