aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/derivations.scm31
-rw-r--r--tests/go.scm6
-rw-r--r--tests/gremlin.scm2
-rw-r--r--tests/guix-build.sh11
-rw-r--r--tests/guix-package.sh4
-rw-r--r--tests/guix-shell-export-manifest.sh3
-rw-r--r--tests/import-utils.scm6
-rw-r--r--tests/lint.scm62
-rw-r--r--tests/pack.scm40
-rw-r--r--tests/packages.scm25
-rw-r--r--tests/pypi.scm101
-rw-r--r--tests/toml.scm469
-rw-r--r--tests/transformations.scm31
13 files changed, 767 insertions, 24 deletions
diff --git a/tests/derivations.scm b/tests/derivations.scm
index 0e87778981..efcd21f324 100644
--- a/tests/derivations.scm
+++ b/tests/derivations.scm
@@ -1,5 +1,5 @@
;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2012-2023 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2012-2024 Ludovic Courtès <ludo@gnu.org>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -29,7 +29,8 @@
#:use-module (guix tests git)
#:use-module (guix tests http)
#:use-module ((guix packages) #:select (package-derivation base32))
- #:use-module ((guix build utils) #:select (executable-file?))
+ #:use-module ((guix build utils)
+ #:select (executable-file? strip-store-file-name))
#:use-module ((guix hash) #:select (file-hash*))
#:use-module ((git oid) #:select (oid->string))
#:use-module ((git reference) #:select (reference-name->oid))
@@ -1157,6 +1158,32 @@
#:mode (build-mode check))
(list drv dep))))))
+(test-equal "derivation-build-plan, topological ordering"
+ (make-list 5 '("0.drv" "1.drv" "2.drv" "3.drv" "4.drv"))
+ (with-store store
+ (define (test _)
+ (let* ((simple-derivation
+ (lambda (name . deps)
+ (build-expression->derivation
+ store name
+ `(begin ,(random-text) (mkdir %output))
+ #:inputs (map (lambda (n dep)
+ (list (number->string n) dep))
+ (iota (length deps))
+ deps))))
+ (drv0 (simple-derivation "0"))
+ (drv1 (simple-derivation "1" drv0))
+ (drv2 (simple-derivation "2" drv1))
+ (drv3 (simple-derivation "3" drv2 drv0))
+ (drv4 (simple-derivation "4" drv3 drv1)))
+ (map (compose strip-store-file-name derivation-file-name)
+ (derivation-build-plan store (list (derivation-input drv4))))))
+
+ ;; This is probabilistic: if the traversal is buggy, it may or may not
+ ;; produce the wrong ordering, depending on a variety of actors. Thus,
+ ;; try multiple times.
+ (map test (iota 5))))
+
(test-assert "derivation-input-fold"
(let* ((builder (add-text-to-store %store "my-builder.sh"
"echo hello, world > \"$out\"\n"
diff --git a/tests/go.scm b/tests/go.scm
index f925c485c1..1ba089c788 100644
--- a/tests/go.scm
+++ b/tests/go.scm
@@ -136,7 +136,9 @@ replace (
(define fixtures-go-check-test
(let ((version
- "{\"Version\":\"v0.0.0-20201130134442-10cb98267c6c\",\"Time\":\"2020-11-30T13:44:42Z\"}")
+ "{\"Version\":\"v0.0.0-20201130134442-10cb98267c6c\",\"Time\":\"2020-11-30T13:44:42Z\"}")
+ (go.info
+ "{\"Version\":\"v0.0.0-20201130134442-10cb98267c6c\",\"Time\":\"2020-11-30T13:44:42Z\"}")
(go.mod
"module gopkg.in/check.v1
@@ -174,6 +176,8 @@ require github.com/kr/pretty v0.2.1
. ,go.mod)
("https://proxy.golang.org/github.com/go-check/check/@latest"
. ,version)
+ ("https://proxy.golang.org/github.com/go-check/check/@v/v0.0.0-20201130134442-10cb98267c6c.info"
+ . ,go.info)
("https://github.com/go-check/check?go-get=1"
. ,go-get)
("https://pkg.go.dev/github.com/go-check/check"
diff --git a/tests/gremlin.scm b/tests/gremlin.scm
index 3dbb8d3643..280b1d8819 100644
--- a/tests/gremlin.scm
+++ b/tests/gremlin.scm
@@ -136,6 +136,7 @@
(with-directory-excursion directory
(call-with-output-file "t.c"
(lambda (port)
+ (display "#include <stdio.h>\n" port)
(display "int main () { puts(\"hello\"); }" port)))
(invoke c-compiler "t.c"
"-Wl,--enable-new-dtags" "-Wl,-rpath=/foo" "-Wl,-rpath=/bar")
@@ -164,6 +165,7 @@
(with-directory-excursion directory
(call-with-output-file "t.c"
(lambda (port)
+ (display "#include <stdio.h>\n" port)
(display "int main () { puts(\"hello\"); }" port)))
(invoke c-compiler "t.c"
diff --git a/tests/guix-build.sh b/tests/guix-build.sh
index 36eac2b7e0..6d46d571a9 100644
--- a/tests/guix-build.sh
+++ b/tests/guix-build.sh
@@ -190,6 +190,17 @@ test `guix build -d --sources=transitive foo \
| grep -e 'foo\.tar\.gz' -e 'bar\.tar\.gz' -e 'bar\.dat' \
| wc -l` -eq 3
+# Building the inputs.
+guix build -D hello -n
+test `guix build -D hello -d \
+ | grep -e 'glibc.*\.drv$' -e 'gcc.*\.drv$' -e 'binutils.*\.drv$' \
+ | wc -l` -ge 3
+
+# Building the dependents.
+test `guix build -P1 libgit2 -P1 libssh -d \
+ | grep -e 'guile-git.*\.drv$' -e 'guile-ssh.*\.drv$' \
+ -e 'libgit2-[0-9].*\.drv$' -e 'libssh-[0-9].*\.drv$' \
+ | wc -l` -eq 4
# Unbound variable in thunked field.
cat > "$module_dir/foo.scm" <<EOF
diff --git a/tests/guix-package.sh b/tests/guix-package.sh
index 945d59cdfb..000cd4df11 100644
--- a/tests/guix-package.sh
+++ b/tests/guix-package.sh
@@ -2,6 +2,8 @@
# Copyright © 2012-2022 Ludovic Courtès <ludo@gnu.org>
# Copyright © 2013 Nikita Karetnikov <nikita@karetnikov.org>
# Copyright © 2022 Josselin Poiret <dev@jpoiret.xyz>
+# Copyright © 2022 Josselin Poiret <dev@jpoiret.xyz>
+# Copyright © 2024 Janneke Nieuwenhuizen <janneke@gnu.org>
#
# This file is part of GNU Guix.
#
@@ -124,7 +126,7 @@ guix package --show=emacs@42 && false
# Search.
LC_MESSAGES=C
export LC_MESSAGES
-test "`guix package -s "An example GNU package" | grep ^name:`" = \
+test "`guix package -s "Example GNU package" | grep ^name:`" = \
"name: hello"
test -z "`guix package -s "n0t4r341p4ck4g3"`"
diff --git a/tests/guix-shell-export-manifest.sh b/tests/guix-shell-export-manifest.sh
index 6c42c40f3b..fd0b4e47a9 100644
--- a/tests/guix-shell-export-manifest.sh
+++ b/tests/guix-shell-export-manifest.sh
@@ -1,5 +1,6 @@
# GNU Guix --- Functional package management for GNU
# Copyright © 2022 Ludovic Courtès <ludo@gnu.org>
+# Copyright © 2024 Janneke Nieuwenhuizen <janneke@gnu.org>
#
# This file is part of GNU Guix.
#
@@ -83,7 +84,7 @@ guix build -m "$manifest" -d | \
guix shell --export-manifest -D guile -D python-itsdangerous > "$manifest"
guix build -m "$manifest" -d | grep "$(guix build libffi -d)"
guix build -m "$manifest" -d | \
- grep "$(guix build -e '(@ (gnu packages python) python)' -d)"
+ grep "$(guix build -e '(@ (gnu packages python) python-sans-pip-wrapper)' -d)"
# Test various combinations to make sure generated code uses interfaces
# correctly.
diff --git a/tests/import-utils.scm b/tests/import-utils.scm
index bec38b0c30..607349203c 100644
--- a/tests/import-utils.scm
+++ b/tests/import-utils.scm
@@ -1,5 +1,5 @@
;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2015, 2017, 2022, 2023 Ricardo Wurmus <rekado@elephly.net>
+;;; Copyright © 2015, 2017, 2022, 2023, 2024 Ricardo Wurmus <rekado@elephly.net>
;;; Copyright © 2016 Ben Woodcroft <donttrustben@gmail.com>
;;; Copyright © 2020 Martin Becze <mjbecze@riseup.net>
;;; Copyright © 2021 Sarah Morgensen <iskarian@mgsn.dev>
@@ -65,6 +65,10 @@ Differences are hard to spot, e.g. in CLOS vs. GOOPS."))
"Code (@code{DelayedMatrix}, @code{MaMa}, or @code{MeMe}) should be wrapped."
(beautify-description "Code (DelayedMatrix, MaMa, or MeMe) should be wrapped."))
+(test-equal "beautify-description: wrap function names in @code"
+ "The main functions are: @code{haplo.em()} and @code{haplo.power()}; FYI."
+ (beautify-description "The main functions are: haplo.em() and haplo.power(); FYI."))
+
(test-equal "license->symbol"
'license:lgpl2.0
(license->symbol license:lgpl2.0))
diff --git a/tests/lint.scm b/tests/lint.scm
index 95d82d7490..3e9dbd29db 100644
--- a/tests/lint.scm
+++ b/tests/lint.scm
@@ -11,6 +11,7 @@
;;; Copyright © 2020 Tobias Geerinckx-Rice <me@tobias.gr>
;;; Copyright © 2021 Xinglu Chen <public@yoctocell.xyz>
;;; Copyright © 2021, 2023 Maxime Devos <maximedevos@telenet.be>
+;;; Copyright © 2024 Gabriel Wicki <gabriel@erlikon.ch>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -120,6 +121,11 @@
(description "bad description."))))
(check-description-style pkg))))
+(test-equal "description: may start with texinfo markup"
+ '()
+ (check-description-style
+ (dummy-package "x" (description "@emph{Maxwell Equations of Software}"))))
+
(test-equal "description: may start with a digit"
'()
(let ((pkg (dummy-package "x"
@@ -132,6 +138,30 @@
(description "x is a dummy package."))))
(check-description-style pkg)))
+(test-equal "description: may start with beginning of package name"
+ '()
+ (let ((pkg (dummy-package "xyz-0.1"
+ (description "xyz is a dummy package."))))
+ (check-description-style pkg)))
+
+(test-equal "description: may start with end of package name"
+ '()
+ (let ((pkg (dummy-package "foobar-xyz"
+ (description "xyz is a dummy package."))))
+ (check-description-style pkg)))
+
+(test-equal "description: may start with non-hyphenated package name"
+ '()
+ (let ((pkg (dummy-package "foobar-xyz-minimal"
+ (description "foobar_xyz is a dummy package."))))
+ (check-description-style pkg)))
+
+(test-equal "description: may start with end of package name"
+ '()
+ (let ((pkg (dummy-package "foo-bar"
+ (description "bar is some thing in foo."))))
+ (check-description-style pkg)))
+
(test-equal "description: two spaces after end of sentence"
"sentences in description should be followed by two spaces; possible infraction at 3"
(single-lint-warning-message
@@ -143,7 +173,7 @@
'()
(let ((pkg (dummy-package "x"
(description
- "E.g. Foo, i.e. Bar resp. Baz (a.k.a. DVD)."))))
+ "O. Person e.g. Foo, i.e. Bar resp. Baz (a.k.a. DVD). Name et al. cf. some paper."))))
(check-description-style pkg)))
(test-equal "description: may not contain trademark signs: ™"
@@ -171,14 +201,14 @@
"description contains leading whitespace"
(single-lint-warning-message
(let ((pkg (dummy-package "x"
- (description " Whitespace."))))
+ (description " Whitespace."))))
(check-description-style pkg))))
(test-equal "description: trailing whitespace"
"description contains trailing whitespace"
(single-lint-warning-message
(let ((pkg (dummy-package "x"
- (description "Whitespace. "))))
+ (description "Whitespace. "))))
(check-description-style pkg))))
(test-equal "description: pluralized 'This package'"
@@ -189,12 +219,18 @@
(check-description-style pkg))))
(test-equal "description: grammar 'allows to'"
- "description contains typo 'allows to'"
+ "description contains typo 'allows to '"
(single-lint-warning-message
(let ((pkg (dummy-package "x"
(description "This package allows to do stuff."))))
(check-description-style pkg))))
+(test-equal "description: grammar 'allows to' 2"
+ '()
+ (let ((pkg (dummy-package "x"
+ (description "This package allows tokenization."))))
+ (check-description-style pkg)))
+
(test-equal "synopsis: not a string"
"invalid synopsis: #f"
(single-lint-warning-message
@@ -277,6 +313,12 @@
(check-synopsis-style pkg)))
string<?))
+(test-equal "synopsis: starts with texinfo markup"
+ '()
+ (let ((pkg (dummy-package "x"
+ (synopsis "@code{help}"))))
+ (check-synopsis-style pkg)))
+
(test-equal "synopsis: too long"
"synopsis should be less than 80 characters long"
(single-lint-warning-message
@@ -359,18 +401,18 @@
'()
(check-compiler-for-target
(dummy-package "x"
- (arguments
- (list #:make-flags
- #~(list (string-append "CC=" (cc-for-target))))))))
+ (arguments
+ (list #:make-flags
+ #~(list (string-append "CC=" (cc-for-target))))))))
(test-equal "compiler-for-target: CC=gcc is acceptable when target=#false"
'()
(check-compiler-for-target
;; This (dummy) package consists purely of architecture-independent data.
(dummy-package "tzdata"
- (arguments
- (list #:target #false
- #:make-flags #~(list "CC=gcc"))))))
+ (arguments
+ (list #:target #false
+ #:make-flags #~(list "CC=gcc"))))))
;; The emacs-build-system sets #:tests? #f by default.
(test-equal "tests-true: #:tests? #t acceptable for emacs packages"
diff --git a/tests/pack.scm b/tests/pack.scm
index f8a9e09c28..1c1e312557 100644
--- a/tests/pack.scm
+++ b/tests/pack.scm
@@ -3,6 +3,7 @@
;;; Copyright © 2018 Ricardo Wurmus <rekado@elephly.net>
;;; Copyright © 2021, 2023 Maxim Cournoyer <maxim.cournoyer@gmail.com>
;;; Copyright © 2023 Oleg Pykhalov <go.wigust@gmail.com>
+;;; Copyright © 2024 Noé Lopez <noelopez@free.fr>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -32,7 +33,8 @@
#:use-module (guix utils)
#:use-module ((guix build utils) #:select (%store-directory))
#:use-module (gnu packages)
- #:use-module ((gnu packages base) #:select (libc-utf8-locales-for-target))
+ #:use-module ((gnu packages base) #:select (libc-utf8-locales-for-target
+ hello))
#:use-module (gnu packages bootstrap)
#:use-module ((gnu packages package-management) #:select (rpm))
#:use-module ((gnu packages compression) #:select (squashfs-tools))
@@ -341,6 +343,42 @@
(built-derivations (list check))))
(unless store (test-skip 1))
+ (test-assertm "appimage"
+ (mlet* %store-monad
+ ((guile (set-guile-for-build (default-guile)))
+ (profile -> (profile
+ (content (packages->manifest (list %bootstrap-guile hello)))
+ (hooks '())
+ (locales? #f)))
+ (image (self-contained-appimage "hello-appimage" profile
+ #:entry-point "bin/hello"
+ #:extra-options
+ (list #:relocatable? #t)))
+ (check (gexp->derivation
+ "check-appimage"
+ #~(invoke #$image))))
+ (built-derivations (list check))))
+
+ (unless store (test-skip 1))
+ (test-assertm "appimage + localstatedir"
+ (mlet* %store-monad
+ ((guile (set-guile-for-build (default-guile)))
+ (profile -> (profile
+ (content (packages->manifest (list %bootstrap-guile hello)))
+ (hooks '())
+ (locales? #f)))
+ (image (self-contained-appimage "hello-appimage" profile
+ #:entry-point "bin/hello"
+ #:localstatedir? #t
+ #:extra-options
+ (list #:relocatable? #t)))
+ (check (gexp->derivation
+ "check-appimage"
+ #~(begin
+ (invoke #$image)))))
+ (built-derivations (list check))))
+
+ (unless store (test-skip 1))
(test-assertm "deb archive with symlinks and control files"
(mlet* %store-monad
((guile (set-guile-for-build (default-guile)))
diff --git a/tests/packages.scm b/tests/packages.scm
index a623628447..1d901505aa 100644
--- a/tests/packages.scm
+++ b/tests/packages.scm
@@ -1,5 +1,5 @@
;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2012-2023 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2012-2024 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
;;; Copyright © 2021 Maxim Cournoyer <maxim.cournoyer@gmail.com>
;;; Copyright © 2021 Maxime Devos <maximedevos@telenet.be>
@@ -1628,6 +1628,21 @@
(match (delete-duplicates pythons eq?)
((p) (eq? p python)))))
+(test-assert "package-input-rewriting/spec, replace hidden package"
+ ;; Rewrite hidden packages when requested.
+ (let* ((python (hidden-package python))
+ (p0 (dummy-package "chbouib"
+ (build-system trivial-build-system)
+ (inputs (list python))))
+ (rewrite (package-input-rewriting/spec
+ `(("python" . ,(const sed)))
+ #:replace-hidden? #t))
+ (p1 (rewrite p0)))
+ (match (package-inputs p1)
+ ((("python" python))
+ (and (string=? (package-full-name python)
+ (package-full-name sed)))))))
+
(test-equal "package-input-rewriting/spec, graft"
(derivation-file-name (package-derivation %store sed))
@@ -2036,6 +2051,14 @@
(dummy-package "a"
(arguments (this-package-native-input "hello")))))
+(test-equal "this-package-input, origin"
+ "http://example.org/foo.tar.gz"
+ (origin-uri
+ (package-arguments
+ (dummy-package "a"
+ (inputs (list (dummy-origin (uri "http://example.org/foo.tar.gz"))))
+ (arguments (this-package-input "foo.tar.gz"))))))
+
(test-eq "modify-inputs, replace"
coreutils
;; Replace an input; notice that the label in unchanged.
diff --git a/tests/pypi.scm b/tests/pypi.scm
index c9aee34d8b..fe00e429b7 100644
--- a/tests/pypi.scm
+++ b/tests/pypi.scm
@@ -112,6 +112,20 @@ Mock
coverage
")
+(define test-pyproject.toml "\
+[build-system]
+requires = [\"dummy-build-dep-a\", \"dummy-build-dep-b\"]
+
+[project]
+dependencies = [
+ \"dummy-dep-a\",
+ \"dummy-dep-b\",
+]
+
+[project.optional-dependencies]
+test = [\"dummy-test-dep-a\", \"dummy-test-dep-b\"]
+")
+
(define test-metadata "\
Classifier: Programming Language :: Python :: 3.7
Requires-Dist: baz ~= 3
@@ -325,13 +339,90 @@ files specified by SPECS. Return its file name."
(x
(pk 'fail x #f))))))
+(test-assert "pypi->guix-package, no wheel, no requires.txt, but pyproject.toml"
+ (let ((tarball (pypi-tarball
+ "foo-1.0.0"
+ `(("pyproject.toml" ,test-pyproject.toml))))
+ (twice (lambda (lst) (append lst lst))))
+ (with-pypi (twice `(("/foo-1.0.0.tar.gz" 200 ,(file-dump tarball))
+ ("/foo-1.0.0-py2.py3-none-any.whl" 404 "")
+ ("/foo/json" 200 ,(lambda (port)
+ (display (foo-json) port)))))
+ ;; Not clearing the memoization cache here would mean returning the value
+ ;; computed in the previous test.
+ (invalidate-memoization! pypi->guix-package)
+ (match (pypi->guix-package "foo")
+ (`(package
+ (name "python-foo")
+ (version "1.0.0")
+ (source (origin
+ (method url-fetch)
+ (uri (pypi-uri "foo" version))
+ (sha256
+ (base32 ,(? string? hash)))))
+ (build-system pyproject-build-system)
+ (propagated-inputs (list python-dummy-dep-a python-dummy-dep-b))
+ (native-inputs (list python-dummy-build-dep-a python-dummy-build-dep-b
+ python-dummy-test-dep-a python-dummy-test-dep-b))
+ (home-page "http://example.com")
+ (synopsis "summary")
+ (description "summary.")
+ (license license:lgpl2.0))
+ (and (string=? default-sha256/base32 hash)
+ (equal? (pypi->guix-package "foo" #:version "1.0.0")
+ (pypi->guix-package "foo"))
+ (guard (c ((error? c) #t))
+ (pypi->guix-package "foo" #:version "42"))))
+ (x
+ (pk 'fail x #f))))))
+
+(test-assert "pypi->guix-package, no wheel, but requires.txt and pyproject.toml"
+ (let ((tarball (pypi-tarball
+ "foo-1.0.0"
+ `(("foo-1.0.0/pyproject.toml" ,test-pyproject.toml)
+ ("foo-1.0.0/bizarre.egg-info/requires.txt"
+ ,test-requires.txt))))
+ (twice (lambda (lst) (append lst lst))))
+ (with-pypi (twice `(("/foo-1.0.0.tar.gz" 200 ,(file-dump tarball))
+ ("/foo-1.0.0-py2.py3-none-any.whl" 404 "")
+ ("/foo/json" 200 ,(lambda (port)
+ (display (foo-json) port)))))
+ ;; Not clearing the memoization cache here would mean returning the value
+ ;; computed in the previous test.
+ (invalidate-memoization! pypi->guix-package)
+ (match (pypi->guix-package "foo")
+ (`(package
+ (name "python-foo")
+ (version "1.0.0")
+ (source (origin
+ (method url-fetch)
+ (uri (pypi-uri "foo" version))
+ (sha256
+ (base32 ,(? string? hash)))))
+ (build-system pyproject-build-system)
+ ;; Information from requires.txt and pyproject.toml is combined.
+ (propagated-inputs (list python-bar python-dummy-dep-a python-dummy-dep-b
+ python-foo))
+ (native-inputs (list python-dummy-build-dep-a python-dummy-build-dep-b
+ python-dummy-test-dep-a python-dummy-test-dep-b
+ python-pytest))
+ (home-page "http://example.com")
+ (synopsis "summary")
+ (description "summary.")
+ (license license:lgpl2.0))
+ (and (string=? default-sha256/base32 hash)
+ (equal? (pypi->guix-package "foo" #:version "1.0.0")
+ (pypi->guix-package "foo"))
+ (guard (c ((error? c) #t))
+ (pypi->guix-package "foo" #:version "42"))))
+ (x
+ (pk 'fail x #f))))))
+
(test-skip (if (which "zip") 0 1))
-(test-assert "pypi->guix-package, wheels"
+(test-assert "pypi->guix-package, no requires.txt, but wheel."
(let ((tarball (pypi-tarball
"foo-1.0.0"
- '(("foo-1.0.0/foo.egg-info/requires.txt"
- "wrong data \
-to make sure we're testing wheels"))))
+ '(("foo-1.0.0/foo.egg-info/.empty" ""))))
(wheel (wheel-file "foo-1.0.0"
`(("METADATA" ,test-metadata)))))
(with-pypi `(("/foo-1.0.0.tar.gz" 200 ,(file-dump tarball))
@@ -362,7 +453,7 @@ to make sure we're testing wheels"))))
(x
(pk 'fail x #f))))))
-(test-assert "pypi->guix-package, no usable requirement file."
+(test-assert "pypi->guix-package, no usable requirement file, no wheel."
(let ((tarball (pypi-tarball "foo-1.0.0"
'(("foo.egg-info/.empty" "")))))
(with-pypi `(("/foo-1.0.0.tar.gz" 200 ,(file-dump tarball))
diff --git a/tests/toml.scm b/tests/toml.scm
new file mode 100644
index 0000000000..cd8e4d2338
--- /dev/null
+++ b/tests/toml.scm
@@ -0,0 +1,469 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2023 Lars-Dominik Braun <lars@6xq.net>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (test-toml)
+ #:use-module (guix build toml)
+ #:use-module (guix tests)
+ #:use-module (srfi srfi-19) ; For datetime.
+ #:use-module (srfi srfi-64)
+ #:use-module (ice-9 match))
+
+(test-begin "toml")
+
+;; Tests taken from https://toml.io/en/v1.0.0
+
+(test-error "parse-toml: Unspecified key"
+ &file-not-consumed
+ (parse-toml "key = # INVALID"))
+
+(test-error "parse-toml: Missing EOL"
+ &file-not-consumed
+ (parse-toml "first = \"Tom\" last = \"Preston-Werner\" # INVALID"))
+
+(test-equal "parse-toml: Bare keys"
+ '(("key" . "value") ("bare_key" . "value") ("bare-key" . "value") ("1234" . "value"))
+ (parse-toml "key = \"value\"
+bare_key = \"value\"
+bare-key = \"value\"
+1234 = \"value\""))
+
+(test-equal "parse-toml: Quoted keys"
+ '(("127.0.0.1" . "value")
+ ("character encoding" . "value")
+ ("ʎǝʞ" . "value")
+ ("key2" . "value")
+ ("quoted \"value\"" . "value"))
+ (parse-toml "\"127.0.0.1\" = \"value\"
+\"character encoding\" = \"value\"
+\"ʎǝʞ\" = \"value\"
+'key2' = \"value\"
+'quoted \"value\"' = \"value\""))
+
+(test-equal "parse-toml: No key"
+ #f
+ (parse-toml "= \"no key name\""))
+
+(test-equal "parse-toml: Empty key"
+ '(("" . "blank"))
+ (parse-toml "\"\" = \"blank\""))
+
+(test-equal "parse-toml: Dotted keys"
+ '(("name" . "Orange")
+ ("physical" ("color" . "orange")
+ ("shape" . "round"))
+ ("site" ("google.com" . #t)))
+ (parse-toml "name = \"Orange\"
+physical.color = \"orange\"
+physical.shape = \"round\"
+site.\"google.com\" = true"))
+
+(test-equal "parse-toml: Dotted keys with whitespace"
+ '(("fruit" ("name" . "banana") ("color" . "yellow") ("flavor" . "banana")))
+ (parse-toml "fruit.name = \"banana\" # this is best practice
+fruit. color = \"yellow\" # same as fruit.color
+fruit . flavor = \"banana\" # same as fruit.flavor"))
+
+(test-error "parse-toml: Multiple keys"
+ &already-defined
+ (parse-toml "name = \"Tom\"
+name = \"Pradyun\""))
+
+(test-equal "parse-toml: Implicit tables"
+ '(("fruit" ("apple" ("smooth" . #t)) ("orange" . 2)))
+ (parse-toml "fruit.apple.smooth = true
+fruit.orange = 2"))
+
+(test-error "parse-toml: Write to value"
+ &already-defined
+ (parse-toml "fruit.apple = 1
+fruit.apple.smooth = true"))
+
+(test-equal "parse-toml: String"
+ '(("str" . "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF."))
+ (parse-toml "str = \"I'm a string. \\\"You can quote me\\\". Name\\tJos\\u00E9\\nLocation\\tSF.\""))
+
+(test-equal "parse-toml: Empty string"
+ '(("str1" . "")
+ ("str2" . "")
+ ("str3" . "")
+ ("str4" . ""))
+ (parse-toml "str1 = \"\"
+str2 = ''
+str3 = \"\"\"\"\"\"
+str4 = ''''''"))
+
+(test-equal "parse-toml: Multi-line basic strings"
+ '(("str1" . "Roses are red\nViolets are blue")
+ ("str2" . "The quick brown fox jumps over the lazy dog.")
+ ("str3" . "The quick brown fox jumps over the lazy dog.")
+ ("str4" . "Here are two quotation marks: \"\". Simple enough.")
+ ("str5" . "Here are three quotation marks: \"\"\".")
+ ("str6" . "Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".")
+ ("str7" . "\"This,\" she said, \"is just a pointless statement.\""))
+ (parse-toml "str1 = \"\"\"
+Roses are red
+Violets are blue\"\"\"
+
+str2 = \"\"\"
+The quick brown \\
+
+
+ fox jumps over \\
+ the lazy dog.\"\"\"
+
+str3 = \"\"\"\\
+ The quick brown \\
+ fox jumps over \\
+ the lazy dog.\\
+ \"\"\"
+
+str4 = \"\"\"Here are two quotation marks: \"\". Simple enough.\"\"\"
+# str5 = \"\"\"Here are three quotation marks: \"\"\".\"\"\" # INVALID
+str5 = \"\"\"Here are three quotation marks: \"\"\\\".\"\"\"
+str6 = \"\"\"Here are fifteen quotation marks: \"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\".\"\"\"
+
+# \"This,\" she said, \"is just a pointless statement.\"
+str7 = \"\"\"\"This,\" she said, \"is just a pointless statement.\"\"\"\""))
+
+(test-equal "parse-toml: Literal string"
+ '(("winpath" . "C:\\Users\\nodejs\\templates")
+ ("winpath2" . "\\\\ServerX\\admin$\\system32\\")
+ ("quoted" . "Tom \"Dubs\" Preston-Werner")
+ ("regex" . "<\\i\\c*\\s*>"))
+ (parse-toml "winpath = 'C:\\Users\\nodejs\\templates'
+winpath2 = '\\\\ServerX\\admin$\\system32\\'
+quoted = 'Tom \"Dubs\" Preston-Werner'
+regex = '<\\i\\c*\\s*>'"))
+
+(test-equal "parse-toml: Multi-line literal strings"
+ '(("regex2" . "I [dw]on't need \\d{2} apples")
+ ("lines" . "The first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n")
+ ("quot15" . "Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\"")
+ ("apos15" . "Here are fifteen apostrophes: '''''''''''''''")
+ ("str" . "'That,' she said, 'is still pointless.'"))
+ (parse-toml "regex2 = '''I [dw]on't need \\d{2} apples'''
+lines = '''
+The first newline is
+trimmed in raw strings.
+ All other whitespace
+ is preserved.
+'''
+quot15 = '''Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\"'''
+
+# apos15 = '''Here are fifteen apostrophes: '''''''''''''''''' # INVALID
+apos15 = \"Here are fifteen apostrophes: '''''''''''''''\"
+
+# 'That,' she said, 'is still pointless.'
+str = ''''That,' she said, 'is still pointless.''''"))
+
+(test-equal "parse-toml: Decimal integer"
+ '(("int1" . 99) ("int2" . 42) ("int3" . 0) ("int4" . -17))
+ (parse-toml "int1 = +99
+int2 = 42
+int3 = 0
+int4 = -17"))
+
+(test-equal "parse-toml: Decimal integer underscores"
+ '(("int5" . 1000) ("int6" . 5349221) ("int7" . 5349221) ("int8" . 12345))
+ (parse-toml "int5 = 1_000
+int6 = 5_349_221
+int7 = 53_49_221 # Indian number system grouping
+int8 = 1_2_3_4_5 # VALID but discouraged"))
+
+(test-equal "parse-toml: Hexadecimal"
+ `(("hex1" . ,#xdeadbeef) ("hex2" . ,#xdeadbeef) ("hex3" . ,#xdeadbeef))
+ (parse-toml "hex1 = 0xDEADBEEF
+hex2 = 0xdeadbeef
+hex3 = 0xdead_beef"))
+
+(test-equal "parse-toml: Octal"
+ `(("oct1" . ,#o01234567) ("oct2" . #o755))
+ (parse-toml "oct1 = 0o01234567
+oct2 = 0o755"))
+
+(test-equal "parse-toml: Binary"
+ `(("bin1" . ,#b11010110))
+ (parse-toml "bin1 = 0b11010110"))
+
+(test-equal "parse-toml: Float"
+ '(("flt1" . 1.0)
+ ("flt2" . 3.1415)
+ ("flt3" . -0.01)
+ ("flt4" . 5e+22)
+ ("flt5" . 1e06)
+ ("flt6" . -2e-2)
+ ("flt7" . 6.626e-34)
+ ("flt8" . 224617.445991228))
+ (parse-toml "# fractional
+flt1 = +1.0
+flt2 = 3.1415
+flt3 = -0.01
+
+# exponent
+flt4 = 5e+22
+flt5 = 1e06
+flt6 = -2E-2
+
+# both
+flt7 = 6.626e-34
+
+flt8 = 224_617.445_991_228"))
+
+(test-equal "parse-toml: Float"
+ '(("sf1" . +inf.0)
+ ("sf2" . +inf.0)
+ ("sf3" . -inf.0)
+ ("sf4" . +nan.0)
+ ("sf5" . +nan.0)
+ ("sf6" . -nan.0))
+ (parse-toml "# infinity
+sf1 = inf # positive infinity
+sf2 = +inf # positive infinity
+sf3 = -inf # negative infinity
+
+# not a number
+sf4 = nan # actual sNaN/qNaN encoding is implementation-specific
+sf5 = +nan # same as `nan`
+sf6 = -nan # valid, actual encoding is implementation-specific"))
+
+(test-equal "parse-toml: Boolean"
+ '(("bool1" . #t)
+ ("bool2" . #f))
+ (parse-toml "bool1 = true
+bool2 = false"))
+
+(test-equal "parse-toml: Offset date-time"
+ `(("odt1" . ,(make-date #f 0 32 7 27 5 1979 0))
+ ("odt2" . ,(make-date #f 0 32 0 27 5 1979 (* -7 60 60)))
+ ("odt3" . ,(make-date 999999 0 32 0 27 5 1979 (* 7 60 60)))
+ ("odt4" . ,(make-date #f 0 32 7 27 5 1979 0)))
+ (parse-toml "odt1 = 1979-05-27T07:32:00Z
+odt2 = 1979-05-27T00:32:00-07:00
+odt3 = 1979-05-27T00:32:00.999999+07:00
+odt4 = 1979-05-27 07:32:00Z"))
+
+(test-equal "parse-toml: Local date-time"
+ `(("ldt1" . ,(make-date #f 0 32 7 27 5 1979 #f))
+ ("ldt2" . ,(make-date 999999 0 32 0 27 5 1979 #f)))
+ (parse-toml "ldt1 = 1979-05-27T07:32:00
+ldt2 = 1979-05-27T00:32:00.999999"))
+
+(test-equal "parse-toml: Local date"
+ `(("ld1" . ,(make-date #f #f #f #f 27 5 1979 #f)))
+ (parse-toml "ld1 = 1979-05-27"))
+
+(test-equal "parse-toml: Local time"
+ `(("lt1" . ,(make-date #f 0 32 7 #f #f #f #f))
+ ("lt2" . ,(make-date 999999 0 32 0 #f #f #f #f)))
+ (parse-toml "lt1 = 07:32:00
+lt2 = 00:32:00.999999"))
+
+(test-equal "parse-toml: Arrays"
+ '(("integers" 1 2 3)
+ ("colors" "red" "yellow" "green")
+ ("nested_arrays_of_ints" (1 2) (3 4 5))
+ ("nested_mixed_array" (1 2) ("a" "b" "c"))
+ ("string_array" "all" "strings")
+ ("numbers" 0.1 0.2 0.5 1 2 5)
+ ("contributors" "Foo Bar <foo@example.com>" (("name" . "Baz Qux") ("email" . "bazqux@example.com") ("url" . "https://example.com/bazqux")))
+ ("integers2" 1 2 3)
+ ("integers3" 1 2))
+ (parse-toml "integers = [ 1, 2, 3 ]
+colors = [ \"red\", \"yellow\", \"green\" ]
+nested_arrays_of_ints = [ [ 1, 2 ], [3, 4, 5] ]
+nested_mixed_array = [ [ 1, 2 ], [\"a\", \"b\", \"c\"] ]
+string_array = [ \"all\", 'strings' ]
+
+# Mixed-type arrays are allowed
+numbers = [ 0.1, 0.2, 0.5, 1, 2, 5 ]
+contributors = [
+ \"Foo Bar <foo@example.com>\",
+ { name = \"Baz Qux\", email = \"bazqux@example.com\", url = \"https://example.com/bazqux\" }
+]
+
+integers2 = [
+ 1, 2, 3
+]
+
+integers3 = [
+ 1,
+ 2, # this is ok
+]"))
+
+(test-equal "parse-toml: Arrays of empty strings"
+ '(("empty1" "")
+ ("empty2" "" "")
+ ("empty3" "" "" "")
+ ("emptyraw1" "")
+ ("emptyraw2" "" "")
+ ("emptyraw3" "" "" "")
+ ("emptyml1" "")
+ ("emptyml2" "" "")
+ ("emptyml3" "" "" "")
+ ("emptyrawml1" "")
+ ("emptyrawml2" "" "")
+ ("emptyrawml3" "" "" ""))
+ (parse-toml "empty1 = [ \"\" ]
+empty2 = [ \"\", \"\" ]
+empty3 = [ \"\", \"\", \"\" ]
+emptyraw1 = [ '' ]
+emptyraw2 = [ '', '' ]
+emptyraw3 = [ '', '', '' ]
+emptyml1 = [ \"\"\"\"\"\" ]
+emptyml2 = [ \"\"\"\"\"\", \"\"\"\"\"\" ]
+emptyml3 = [ \"\"\"\"\"\", \"\"\"\"\"\", \"\"\"\"\"\" ]
+emptyrawml1 = [ '''''' ]
+emptyrawml2 = [ '''''', '''''' ]
+emptyrawml3 = [ '''''', '''''', '''''' ]
+"))
+
+(test-equal "parse-toml: Tables"
+ '(("table-1" ("key1" . "some string")
+ ("key2" . 123))
+ ("table-2" ("key1" . "another string")
+ ("key2" . 456)))
+ (parse-toml "[table-1]
+key1 = \"some string\"
+key2 = 123
+
+[table-2]
+key1 = \"another string\"
+key2 = 456"))
+
+
+(test-equal "parse-toml: Dotted table"
+ '(("dog" ("tater.man" ("type" ("name" . "pug")))))
+ (parse-toml "[dog.\"tater.man\"]
+type.name = \"pug\""))
+
+
+(test-equal "parse-toml: Dotted table with whitespace"
+ '(("a" ("b" ("c" ("x" . 1))))
+ ("d" ("e" ("f" ("x" . 1))))
+ ("g" ("h" ("i" ("x" . 1))))
+ ("j" ("ʞ" ("l" ("x" . 1)))))
+ (parse-toml "[a.b.c] # this is best practice
+x=1
+[ d.e.f ] # same as [d.e.f]
+x=1
+[ g . h . i ] # same as [g.h.i]
+x=1
+[ j . \"ʞ\" . 'l' ] # same as [j.\"ʞ\".'l']
+x=1"))
+
+;; XXX: technically this is not allowed, but we permit it.
+(test-equal "parse-toml: Multiple tables"
+ '(("fruit" ("apple" . "red") ("orange" . "orange")))
+ (parse-toml "[fruit]
+apple = \"red\"
+
+[fruit]
+orange = \"orange\""))
+
+(test-equal "parse-toml: Assignment to non-table"
+ #f
+ (parse-toml "[fruit]
+apple = \"red\"
+
+[fruit.apple]
+texture = \"smooth\""))
+
+(test-equal "parse-toml: Dotted keys create tables"
+ '(("fruit" ("apple" ("color" . "red") ("taste" ("sweet" . #t)))))
+ (parse-toml "fruit.apple.color = \"red\"
+fruit.apple.taste.sweet = true"))
+
+(test-equal "parse-toml: Inline tables"
+ '(("name" ("first" . "Tom") ("last" . "Preston-Werner"))
+ ("point" ("x" . 1) ("y" . 2))
+ ("animal" ("type" ("name" . "pug"))))
+ (parse-toml "name = { first = \"Tom\", last = \"Preston-Werner\" }
+point = { x = 1, y = 2 }
+animal = { type.name = \"pug\" }"))
+
+(test-error "parse-toml: Invalid assignment to inline table"
+ #t
+ (parse-toml "[product]
+type = { name = \"Nail\" }
+type.edible = false # INVALID"))
+
+;; We do not catch this semantic error yet.
+(test-expect-fail 1)
+(test-error "parse-toml: Invalid assignment to implicit table"
+ #f
+ (parse-toml "[product]
+type.name = \"Nail\"
+type = { edible = false } # INVALID"))
+
+;; Not implemented.
+(test-expect-fail 1)
+(test-equal "parse-toml: Array of tables"
+ '(("products" (("name" . "Hammer") ("sku" . 738594937))
+ ()
+ (("name" . "Nail") ("sku" . 284758393) ("color" . "gray"))))
+ (parse-toml "[[products]]
+name = \"Hammer\"
+sku = 738594937
+
+[[products]] # empty table within the array
+
+[[products]]
+name = \"Nail\"
+sku = 284758393
+
+color = \"gray\""))
+
+;; Not implemented.
+(test-expect-fail 1)
+(test-equal "parse-toml: Array of tables"
+ '(("fruits" ((("name" . "apple")
+ ("physical" (("color" . "red") ("shape" . "round")))
+ ("varieties" ((("name" . "red delicious")) (("name" . "granny smith")))))
+ (("name" . "banana")
+ ("varieties" (((("name" . "plantain")))))))))
+ (parse-toml "[[fruits]]
+name = \"apple\"
+
+[fruits.physical] # subtable
+color = \"red\"
+shape = \"round\"
+
+[[fruits.varieties]] # nested array of tables
+name = \"red delicious\"
+
+[[fruits.varieties]]
+name = \"granny smith\"
+
+
+[[fruits]]
+name = \"banana\"
+
+[[fruits.varieties]]
+name = \"plantain\""))
+
+;; Not implemented.
+(test-expect-fail 1)
+(test-error "parse-toml: Assignment to statically defined array"
+ #f
+ (parse-toml "fruits = []
+
+[[fruits]]
+x=1"))
+
+(test-end "toml")
+
diff --git a/tests/transformations.scm b/tests/transformations.scm
index 755211d65d..5285d98f17 100644
--- a/tests/transformations.scm
+++ b/tests/transformations.scm
@@ -1,5 +1,5 @@
;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2016-2017, 2019-2023 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2016-2017, 2019-2024 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2021 Marius Bakke <marius@gnu.org>
;;;
;;; This file is part of GNU Guix.
@@ -497,6 +497,35 @@
(let ((new (t coreutils)))
(assq-ref (package-properties new) 'transformations))))
+(test-equal "package-with-upstream-version"
+ '("42.0" "42.0"
+ ("http://example.org")
+ ("a" "b") (do something))
+ (mock ((guix upstream) %updaters
+ (delay (list (upstream-updater
+ (name 'dummy)
+ (pred (const #t))
+ (description "")
+ (import (const (upstream-source
+ (package "foo")
+ (version "42.0")
+ (urls '("http://example.org")))))))))
+ (let* ((old (dummy-package "foo" (version "1.0")
+ (source (dummy-origin
+ (patches '("a" "b"))
+ (snippet '(do something))))))
+ (new (package-with-upstream-version old))
+ (new+patches (package-with-upstream-version
+ old #:preserve-patches? #t)))
+ (list (package-version new) (package-version new+patches)
+
+ ;; Source of NEW is directly an <upstream-source>.
+ (upstream-source-urls (package-source new))
+
+ ;; Check that #:preserve-patches? #t gave us an origin.
+ (origin-patches (package-source new+patches))
+ (origin-snippet (package-source new+patches))))))
+
(test-equal "options->transformation, with-latest"
"42.0"
(mock ((guix upstream) %updaters