1#!/usr/bin/env bash
2
3# This script is used to test that the module system is working as expected.
4# Executing it runs tests for `lib.modules`, `lib.options` and `lib.types`.
5# By default it test the version of nixpkgs which is defined in the NIX_PATH.
6#
7# Run:
8# [nixpkgs]$ lib/tests/modules.sh
9# or:
10# [nixpkgs]$ nix-build lib/tests/release.nix
11
12set -o errexit -o noclobber -o nounset -o pipefail
13shopt -s failglob inherit_errexit
14
15# https://stackoverflow.com/a/246128/6605742
16DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
17
18cd "$DIR"/modules
19
20pass=0
21fail=0
22
23local-nix-instantiate() {
24 nix-instantiate --timeout 1 --eval-only --show-trace --read-write-mode --json "$@"
25}
26
27# loc
28# prints the location of the call of to the function that calls it
29# loc n
30# prints the location n levels up the call stack
31loc() {
32 local caller depth
33 depth=1
34 if [[ $# -gt 0 ]]; then
35 depth=$1
36 fi
37 # ( lineno fnname file ) of the caller
38 caller=( $(caller $depth) )
39 echo "${caller[2]}:${caller[0]}"
40}
41
42line() {
43 echo "----------------------------------------"
44}
45logStartFailure() {
46 line
47}
48logEndFailure() {
49 line
50 echo
51}
52
53logFailure() {
54 # bold red
55 printf '\033[1;31mTEST FAILED\033[0m at %s\n' "$(loc 2)"
56}
57
58evalConfig() {
59 local attr=$1
60 shift
61 local script="import ./default.nix { modules = [ $* ];}"
62 local-nix-instantiate -E "$script" -A "$attr"
63}
64
65reportFailure() {
66 local attr=$1
67 shift
68 local script="import ./default.nix { modules = [ $* ];}"
69 echo "$ nix-instantiate -E '$script' -A '$attr' --eval-only --json"
70 evalConfig "$attr" "$@" || true
71 ((++fail))
72}
73
74checkConfigOutput() {
75 local outputContains=$1
76 shift
77 if evalConfig "$@" 2>/dev/null | grep -E --silent "$outputContains" ; then
78 ((++pass))
79 else
80 logStartFailure
81 echo "ACTUAL:"
82 reportFailure "$@"
83 echo "EXPECTED: result matching '$outputContains'"
84 logFailure
85 logEndFailure
86 fi
87}
88
89invertIfUnset() {
90 gate="$1"
91 shift
92 if [[ -n "${!gate:-}" ]]; then
93 "$@"
94 else
95 ! "$@"
96 fi
97}
98
99globalErrorLogCheck() {
100 invertIfUnset "REQUIRE_INFINITE_RECURSION_HINT" \
101 grep -i 'if you get an infinite recursion here' \
102 <<<"$err" >/dev/null \
103 || {
104 if [[ -n "${REQUIRE_INFINITE_RECURSION_HINT:-}" ]]; then
105 echo "Unexpected infinite recursion hint"
106 else
107 echo "Expected infinite recursion hint, but none found"
108 fi
109 return 1
110 }
111}
112
113checkExpression() {
114 local path=$1
115 local output
116 {
117 output="$(local-nix-instantiate --strict "$path" 2>&1)" && ((++pass))
118 } || {
119 logStartFailure
120 echo "$output"
121 ((++fail))
122 logFailure
123 logEndFailure
124 }
125}
126
127checkConfigError() {
128 local errorContains=$1
129 local err=""
130 shift
131 if err="$(evalConfig "$@" 2>&1 >/dev/null)"; then
132 logStartFailure
133 echo "ACTUAL: exit code 0, output:"
134 reportFailure "$@"
135 echo "EXPECTED: non-zero exit code"
136 logFailure
137 logEndFailure
138 else
139 if ! globalErrorLogCheck "$err"; then
140 logStartFailure
141 echo "LOG:"
142 reportFailure "$@"
143 echo "GLOBAL ERROR LOG CHECK FAILED"
144 logFailure
145 logEndFailure
146 fi
147 if echo "$err" | grep -zP --silent "$errorContains" ; then
148 ((++pass))
149 else
150 logStartFailure
151 echo "ACTUAL:"
152 reportFailure "$@"
153 echo "EXPECTED: error matching '$errorContains'"
154 logFailure
155 logEndFailure
156 fi
157 fi
158}
159
160# Shorthand meta attribute does not duplicate the config
161checkConfigOutput '^"one two"$' config.result ./shorthand-meta.nix
162
163checkConfigOutput '^true$' config.result ./test-mergeAttrDefinitionsWithPrio.nix
164
165# Check that a module argument is passed, also when a default is available
166# (but not needed)
167#
168# When the default is needed, we currently fail to do what the users expect, as
169# we pass our own argument anyway, even if it *turns out* not to exist.
170#
171# The reason for this is that we don't know at invocation time what is in the
172# _module.args option. That value is only available *after* all modules have been
173# invoked.
174#
175# Hypothetically, Nix could help support this by giving access to the default
176# values, through a new built-in function.
177# However the default values are allowed to depend on other arguments, so those
178# would have to be passed in somehow, making this not just a getter but
179# something more complicated.
180#
181# At that point we have to wonder whether the extra complexity is worth the cost.
182# Another - subjective - reason not to support it is that default values
183# contradict the notion that an option has a single value, where _module.args
184# is the option.
185checkConfigOutput '^true$' config.result ./module-argument-default.nix
186
187# gvariant
188checkConfigOutput '^true$' config.assertion ./gvariant.nix
189
190checkConfigOutput '"ok"' config.result ./specialArgs-lib.nix
191
192# https://github.com/NixOS/nixpkgs/pull/131205
193# We currently throw this error already in `config`, but throwing in `config.wrong1` would be acceptable.
194checkConfigError 'It seems as if you.re trying to declare an option by placing it into .config. rather than .options.' config.wrong1 ./error-mkOption-in-config.nix
195# We currently throw this error already in `config`, but throwing in `config.nest.wrong2` would be acceptable.
196checkConfigError 'It seems as if you.re trying to declare an option by placing it into .config. rather than .options.' config.nest.wrong2 ./error-mkOption-in-config.nix
197checkConfigError 'The option .sub.wrong2. does not exist. Definition values:' config.sub ./error-mkOption-in-submodule-config.nix
198checkConfigError '.*This can happen if you e.g. declared your options in .types.submodule.' config.sub ./error-mkOption-in-submodule-config.nix
199checkConfigError '.*A definition for option .bad. is not of type .non-empty .list of .submodule...\.' config.bad ./error-nonEmptyListOf-submodule.nix
200
201# types.attrTag
202checkConfigOutput '^true$' config.okChecks ./types-attrTag.nix
203checkConfigError 'A definition for option .intStrings\.syntaxError. is not of type .attribute-tagged union' config.intStrings.syntaxError ./types-attrTag.nix
204checkConfigError 'A definition for option .intStrings\.syntaxError2. is not of type .attribute-tagged union' config.intStrings.syntaxError2 ./types-attrTag.nix
205checkConfigError 'A definition for option .intStrings\.syntaxError3. is not of type .attribute-tagged union' config.intStrings.syntaxError3 ./types-attrTag.nix
206checkConfigError 'A definition for option .intStrings\.syntaxError4. is not of type .attribute-tagged union' config.intStrings.syntaxError4 ./types-attrTag.nix
207checkConfigError 'A definition for option .intStrings\.mergeError. is not of type .attribute-tagged union' config.intStrings.mergeError ./types-attrTag.nix
208checkConfigError 'A definition for option .intStrings\.badTagError. is not of type .attribute-tagged union' config.intStrings.badTagError ./types-attrTag.nix
209checkConfigError 'A definition for option .intStrings\.badTagTypeError\.left. is not of type .signed integer.' config.intStrings.badTagTypeError.left ./types-attrTag.nix
210checkConfigError 'A definition for option .nested\.right\.left. is not of type .signed integer.' config.nested.right.left ./types-attrTag.nix
211checkConfigError 'In attrTag, each tag value must be an option, but tag int was a bare type, not wrapped in mkOption.' config.opt.int ./types-attrTag-wrong-decl.nix
212
213# types
214checkConfigOutput '"ok"' config.assertions ./types.nix
215
216# types.pathInStore
217checkConfigOutput '".*/store/0lz9p8xhf89kb1c1kk6jxrzskaiygnlh-bash-5.2-p15.drv"' config.pathInStore.ok1 ./types.nix
218checkConfigOutput '".*/store/0fb3ykw9r5hpayd05sr0cizwadzq1d8q-bash-5.2-p15"' config.pathInStore.ok2 ./types.nix
219checkConfigOutput '".*/store/0fb3ykw9r5hpayd05sr0cizwadzq1d8q-bash-5.2-p15/bin/bash"' config.pathInStore.ok3 ./types.nix
220checkConfigError 'A definition for option .* is not of type .path in the Nix store.. Definition values:\n\s*- In .*: ""' config.pathInStore.bad1 ./types.nix
221checkConfigError 'A definition for option .* is not of type .path in the Nix store.. Definition values:\n\s*- In .*: ".*/store"' config.pathInStore.bad2 ./types.nix
222checkConfigError 'A definition for option .* is not of type .path in the Nix store.. Definition values:\n\s*- In .*: ".*/store/"' config.pathInStore.bad3 ./types.nix
223checkConfigError 'A definition for option .* is not of type .path in the Nix store.. Definition values:\n\s*- In .*: ".*/store/.links"' config.pathInStore.bad4 ./types.nix
224checkConfigError 'A definition for option .* is not of type .path in the Nix store.. Definition values:\n\s*- In .*: "/foo/bar"' config.pathInStore.bad5 ./types.nix
225
226# Check boolean option.
227checkConfigOutput '^false$' config.enable ./declare-enable.nix
228checkConfigError 'The option .* does not exist. Definition values:\n\s*- In .*: true' config.enable ./define-enable.nix
229checkConfigError 'The option .* does not exist. Definition values:\n\s*- In .*' config.enable ./define-enable-throw.nix
230checkConfigError 'while evaluating a definition from `.*/define-enable-abort.nix' config.enable ./define-enable-abort.nix
231checkConfigError 'while evaluating the error message for definitions for .enable., which is an option that does not exist' config.enable ./define-enable-abort.nix
232
233# Check boolByOr type.
234checkConfigOutput '^false$' config.value.falseFalse ./boolByOr.nix
235checkConfigOutput '^true$' config.value.trueFalse ./boolByOr.nix
236checkConfigOutput '^true$' config.value.falseTrue ./boolByOr.nix
237checkConfigOutput '^true$' config.value.trueTrue ./boolByOr.nix
238
239checkConfigOutput '^1$' config.bare-submodule.nested ./declare-bare-submodule.nix ./declare-bare-submodule-nested-option.nix
240checkConfigOutput '^2$' config.bare-submodule.deep ./declare-bare-submodule.nix ./declare-bare-submodule-deep-option.nix
241checkConfigOutput '^42$' config.bare-submodule.nested ./declare-bare-submodule.nix ./declare-bare-submodule-nested-option.nix ./declare-bare-submodule-deep-option.nix ./define-bare-submodule-values.nix
242checkConfigOutput '^420$' config.bare-submodule.deep ./declare-bare-submodule.nix ./declare-bare-submodule-nested-option.nix ./declare-bare-submodule-deep-option.nix ./define-bare-submodule-values.nix
243checkConfigOutput '^2$' config.bare-submodule.deep ./declare-bare-submodule.nix ./declare-bare-submodule-deep-option.nix ./define-shorthandOnlyDefinesConfig-true.nix
244checkConfigError 'The option .bare-submodule.deep. in .*/declare-bare-submodule-deep-option.nix. is already declared in .*/declare-bare-submodule-deep-option-duplicate.nix' config.bare-submodule.deep ./declare-bare-submodule.nix ./declare-bare-submodule-deep-option.nix ./declare-bare-submodule-deep-option-duplicate.nix
245
246# Check that strMatching can be merged
247checkConfigOutput '^"strMatching.*"$' options.sm.type.name ./strMatching-merge.nix
248
249# Check integer types.
250# unsigned
251checkConfigOutput '^42$' config.value ./declare-int-unsigned-value.nix ./define-value-int-positive.nix
252checkConfigError 'A definition for option .* is not of type.*unsigned integer.*. Definition values:\n\s*- In .*: -23' config.value ./declare-int-unsigned-value.nix ./define-value-int-negative.nix
253# positive
254checkConfigError 'A definition for option .* is not of type.*positive integer.*. Definition values:\n\s*- In .*: 0' config.value ./declare-int-positive-value.nix ./define-value-int-zero.nix
255# between
256checkConfigOutput '^42$' config.value ./declare-int-between-value.nix ./define-value-int-positive.nix
257checkConfigError 'A definition for option .* is not of type.*between.*-21 and 43.*inclusive.*. Definition values:\n\s*- In .*: -23' config.value ./declare-int-between-value.nix ./define-value-int-negative.nix
258
259# Check either types
260# types.either
261checkConfigOutput '^42$' config.value ./declare-either.nix ./define-value-int-positive.nix
262checkConfigOutput '^"24"$' config.value ./declare-either.nix ./define-value-string.nix
263# types.oneOf
264checkConfigOutput '^42$' config.value ./declare-oneOf.nix ./define-value-int-positive.nix
265checkConfigOutput '^\[\]$' config.value ./declare-oneOf.nix ./define-value-list.nix
266checkConfigOutput '^"24"$' config.value ./declare-oneOf.nix ./define-value-string.nix
267
268# Check mkForce without submodules.
269set -- config.enable ./declare-enable.nix ./define-enable.nix
270checkConfigOutput '^true$' "$@"
271checkConfigOutput '^false$' "$@" ./define-force-enable.nix
272checkConfigOutput '^false$' "$@" ./define-enable-force.nix
273
274# Check mkForce with option and submodules.
275checkConfigError 'attribute .*foo.* .* not found' config.attrsOfSub.foo.enable ./declare-attrsOfSub-any-enable.nix
276checkConfigOutput '^false$' config.attrsOfSub.foo.enable ./declare-attrsOfSub-any-enable.nix ./define-attrsOfSub-foo.nix
277set -- config.attrsOfSub.foo.enable ./declare-attrsOfSub-any-enable.nix ./define-attrsOfSub-foo-enable.nix
278checkConfigOutput '^true$' "$@"
279checkConfigOutput '^false$' "$@" ./define-force-attrsOfSub-foo-enable.nix
280checkConfigOutput '^false$' "$@" ./define-attrsOfSub-force-foo-enable.nix
281checkConfigOutput '^false$' "$@" ./define-attrsOfSub-foo-force-enable.nix
282checkConfigOutput '^false$' "$@" ./define-attrsOfSub-foo-enable-force.nix
283
284# Check overriding effect of mkForce on submodule definitions.
285checkConfigError 'attribute .*bar.* .* not found' config.attrsOfSub.bar.enable ./declare-attrsOfSub-any-enable.nix ./define-attrsOfSub-foo.nix
286checkConfigOutput '^false$' config.attrsOfSub.bar.enable ./declare-attrsOfSub-any-enable.nix ./define-attrsOfSub-foo.nix ./define-attrsOfSub-bar.nix
287set -- config.attrsOfSub.bar.enable ./declare-attrsOfSub-any-enable.nix ./define-attrsOfSub-foo.nix ./define-attrsOfSub-bar-enable.nix
288checkConfigOutput '^true$' "$@"
289checkConfigError 'attribute .*bar.* .* not found' "$@" ./define-force-attrsOfSub-foo-enable.nix
290checkConfigError 'attribute .*bar.* .* not found' "$@" ./define-attrsOfSub-force-foo-enable.nix
291checkConfigOutput '^true$' "$@" ./define-attrsOfSub-foo-force-enable.nix
292checkConfigOutput '^true$' "$@" ./define-attrsOfSub-foo-enable-force.nix
293
294# Check mkIf with submodules.
295checkConfigError 'attribute .*foo.* .* not found' config.attrsOfSub.foo.enable ./declare-enable.nix ./declare-attrsOfSub-any-enable.nix
296set -- config.attrsOfSub.foo.enable ./declare-enable.nix ./declare-attrsOfSub-any-enable.nix
297checkConfigError 'attribute .*foo.* .* not found' "$@" ./define-if-attrsOfSub-foo-enable.nix
298checkConfigError 'attribute .*foo.* .* not found' "$@" ./define-attrsOfSub-if-foo-enable.nix
299checkConfigError 'attribute .*foo.* .* not found' "$@" ./define-attrsOfSub-foo-if-enable.nix
300checkConfigOutput '^false$' "$@" ./define-attrsOfSub-foo-enable-if.nix
301checkConfigOutput '^true$' "$@" ./define-enable.nix ./define-if-attrsOfSub-foo-enable.nix
302checkConfigOutput '^true$' "$@" ./define-enable.nix ./define-attrsOfSub-if-foo-enable.nix
303checkConfigOutput '^true$' "$@" ./define-enable.nix ./define-attrsOfSub-foo-if-enable.nix
304checkConfigOutput '^true$' "$@" ./define-enable.nix ./define-attrsOfSub-foo-enable-if.nix
305
306# Check importApply
307checkConfigOutput '"abc"' config.value ./importApply.nix
308# importApply does not set a key.
309# Disabling the function file is not sufficient, because importApply can't reasonably assume that the key is unique.
310# e.g. user may call it multiple times with different arguments and expect each of the module to apply.
311# While this is excusable for the disabledModules aspect, it is not for the deduplication of modules.
312checkConfigOutput '"abc"' config.value ./importApply-disabling.nix
313
314# Check disabledModules with config definitions and option declarations.
315set -- config.enable ./define-enable.nix ./declare-enable.nix
316checkConfigOutput '^true$' "$@"
317checkConfigOutput '^false$' "$@" ./disable-define-enable.nix
318checkConfigOutput '^false$' "$@" ./disable-define-enable-string-path.nix
319checkConfigError "The option .*enable.* does not exist. Definition values:\n\s*- In .*: true" "$@" ./disable-declare-enable.nix
320checkConfigError "attribute .*enable.* in selection path .*config.enable.* not found" "$@" ./disable-define-enable.nix ./disable-declare-enable.nix
321checkConfigError "attribute .*enable.* in selection path .*config.enable.* not found" "$@" ./disable-enable-modules.nix
322
323checkConfigOutput '^true$' 'config.positive.enable' ./disable-module-with-key.nix
324checkConfigOutput '^false$' 'config.negative.enable' ./disable-module-with-key.nix
325checkConfigError 'Module ..*disable-module-bad-key.nix. contains a disabledModules item that is an attribute set, presumably a module, that does not have a .key. attribute. .*' 'config.enable' ./disable-module-bad-key.nix
326
327# Not sure if we want to keep supporting module keys that aren't strings, paths or v?key, but we shouldn't remove support accidentally.
328checkConfigOutput '^true$' 'config.positive.enable' ./disable-module-with-toString-key.nix
329checkConfigOutput '^false$' 'config.negative.enable' ./disable-module-with-toString-key.nix
330
331# Check _module.args.
332set -- config.enable ./declare-enable.nix ./define-enable-with-custom-arg.nix
333checkConfigError 'while evaluating the module argument .*custom.* in .*define-enable-with-custom-arg.nix.*:' "$@"
334checkConfigOutput '^true$' "$@" ./define-_module-args-custom.nix
335
336# Check that using _module.args on imports cause infinite recursions, with
337# the proper error context.
338set -- "$@" ./define-_module-args-custom.nix ./import-custom-arg.nix
339REQUIRE_INFINITE_RECURSION_HINT=1 checkConfigError 'while evaluating the module argument .*custom.* in .*import-custom-arg.nix.*:' "$@"
340REQUIRE_INFINITE_RECURSION_HINT=1 checkConfigError 'infinite recursion encountered' "$@"
341
342# Check _module.check.
343set -- config.enable ./declare-enable.nix ./define-enable.nix ./define-attrsOfSub-foo.nix
344checkConfigError 'The option .* does not exist. Definition values:\n\s*- In .*' "$@"
345checkConfigOutput '^true$' "$@" ./define-module-check.nix
346
347# Check coerced value.
348set --
349checkConfigOutput '^"42"$' config.value ./declare-coerced-value.nix
350checkConfigOutput '^"24"$' config.value ./declare-coerced-value.nix ./define-value-string.nix
351checkConfigError 'A definition for option .*. is not of type .*.\n\s*- In .*: \[ \]' config.value ./declare-coerced-value.nix ./define-value-list.nix
352
353# Check coerced option merging.
354checkConfigError 'The option .value. in .*/declare-coerced-value.nix. is already declared in .*/declare-coerced-value-no-default.nix.' config.value ./declare-coerced-value.nix ./declare-coerced-value-no-default.nix
355
356# Check coerced value with unsound coercion
357checkConfigOutput '^12$' config.value ./declare-coerced-value-unsound.nix
358checkConfigError 'A definition for option .* is not of type .*.\n\s*- In .*: "1000"' config.value ./declare-coerced-value-unsound.nix ./define-value-string-bigint.nix
359checkConfigError 'toInt: Could not convert .* to int' config.value ./declare-coerced-value-unsound.nix ./define-value-string-arbitrary.nix
360
361# Check `graph` attribute
362checkExpression './graph/test.nix'
363
364# Check mkAliasOptionModule.
365checkConfigOutput '^true$' config.enable ./alias-with-priority.nix
366checkConfigOutput '^true$' config.enableAlias ./alias-with-priority.nix
367checkConfigOutput '^false$' config.enable ./alias-with-priority-can-override.nix
368checkConfigOutput '^false$' config.enableAlias ./alias-with-priority-can-override.nix
369
370# Check mkPackageOption
371checkConfigOutput '^"hello"$' config.package.pname ./declare-mkPackageOption.nix
372checkConfigOutput '^"hello"$' config.namedPackage.pname ./declare-mkPackageOption.nix
373checkConfigOutput '^".*Hello.*"$' options.namedPackage.description ./declare-mkPackageOption.nix
374checkConfigOutput '^"literalExpression"$' options.namedPackage.defaultText._type ./declare-mkPackageOption.nix
375checkConfigOutput '^"pkgs\.hello"$' options.namedPackage.defaultText.text ./declare-mkPackageOption.nix
376checkConfigOutput '^"hello"$' config.namedPackageSingletonDefault.pname ./declare-mkPackageOption.nix
377checkConfigOutput '^".*Hello.*"$' options.namedPackageSingletonDefault.description ./declare-mkPackageOption.nix
378checkConfigOutput '^"pkgs\.hello"$' options.namedPackageSingletonDefault.defaultText.text ./declare-mkPackageOption.nix
379checkConfigOutput '^"hello"$' config.pathPackage.pname ./declare-mkPackageOption.nix
380checkConfigOutput '^"literalExpression"$' options.packageWithExample.example._type ./declare-mkPackageOption.nix
381checkConfigOutput '^"pkgs\.hello\.override \{ stdenv = pkgs\.clangStdenv; \}"$' options.packageWithExample.example.text ./declare-mkPackageOption.nix
382checkConfigOutput '^"literalExpression"$' options.packageWithPathExample.example._type ./declare-mkPackageOption.nix
383checkConfigOutput '^"pkgs\.hello"$' options.packageWithPathExample.example.text ./declare-mkPackageOption.nix
384checkConfigOutput '^".*Example extra description\..*"$' options.packageWithExtraDescription.description ./declare-mkPackageOption.nix
385checkConfigError 'The option .undefinedPackage. was accessed but has no value defined. Try setting the option.' config.undefinedPackage ./declare-mkPackageOption.nix
386checkConfigOutput '^null$' config.nullablePackage ./declare-mkPackageOption.nix
387checkConfigOutput '^"null or package"$' options.nullablePackage.type.description ./declare-mkPackageOption.nix
388checkConfigOutput '^"hello"$' config.nullablePackageWithDefault.pname ./declare-mkPackageOption.nix
389checkConfigOutput '^"myPkgs\.hello"$' options.packageWithPkgsText.defaultText.text ./declare-mkPackageOption.nix
390checkConfigOutput '^"hello-other"$' options.packageFromOtherSet.default.pname ./declare-mkPackageOption.nix
391checkConfigOutput '^"hello"$' config.packageInvalidIdentifier.pname ./declare-mkPackageOption.nix
392checkConfigOutput '^"pkgs\.\\"123\\"\.\\"with\\\\\\"quote\\"\.hello"$' options.packageInvalidIdentifier.defaultText.text ./declare-mkPackageOption.nix
393checkConfigOutput '^"pkgs\.\\"123\\"\.\\"with\\\\\\"quote\\"\.hello"$' options.packageInvalidIdentifierExample.example.text ./declare-mkPackageOption.nix
394
395# submoduleWith
396
397## specialArgs should work
398checkConfigOutput '^"foo"$' config.submodule.foo ./declare-submoduleWith-special.nix
399
400## shorthandOnlyDefines config behaves as expected
401checkConfigOutput '^true$' config.submodule.config ./declare-submoduleWith-shorthand.nix ./define-submoduleWith-shorthand.nix
402checkConfigError 'is not of type `boolean' config.submodule.config ./declare-submoduleWith-shorthand.nix ./define-submoduleWith-noshorthand.nix
403checkConfigError "In module ..*define-submoduleWith-shorthand.nix., you're trying to define a value of type \`bool'\n\s*rather than an attribute set for the option" config.submodule.config ./declare-submoduleWith-noshorthand.nix ./define-submoduleWith-shorthand.nix
404checkConfigOutput '^true$' config.submodule.config ./declare-submoduleWith-noshorthand.nix ./define-submoduleWith-noshorthand.nix
405
406## submoduleWith should merge all modules in one swoop
407checkConfigOutput '^true$' config.submodule.inner ./declare-submoduleWith-modules.nix
408checkConfigOutput '^true$' config.submodule.outer ./declare-submoduleWith-modules.nix
409# Should also be able to evaluate the type name (which evaluates freeformType,
410# which evaluates all the modules defined by the type)
411checkConfigOutput '^"submodule"$' options.submodule.type.description ./declare-submoduleWith-modules.nix
412
413## submodules can be declared using (evalModules {...}).type
414checkConfigOutput '^true$' config.submodule.inner ./declare-submodule-via-evalModules.nix
415checkConfigOutput '^true$' config.submodule.outer ./declare-submodule-via-evalModules.nix
416# Should also be able to evaluate the type name (which evaluates freeformType,
417# which evaluates all the modules defined by the type)
418checkConfigOutput '^"submodule"$' options.submodule.type.description ./declare-submodule-via-evalModules.nix
419
420## Paths should be allowed as values and work as expected
421checkConfigOutput '^true$' config.submodule.enable ./declare-submoduleWith-path.nix
422
423## _prefix module argument is available at import time and contains the prefix
424checkConfigOutput '^true$' config.foo.ok ./prefix-module-argument.nix
425
426## deferredModule
427# default module is merged into nodes.foo
428checkConfigOutput '"beta"' config.nodes.foo.settingsDict.c ./deferred-module.nix
429# errors from the default module are reported with accurate location
430checkConfigError 'In `the-file-that-contains-the-bad-config.nix, via option default'\'': "bogus"' config.nodes.foo.bottom ./deferred-module.nix
431checkConfigError '.*lib/tests/modules/deferred-module-error.nix, via option deferred [(]:anon-1:anon-1:anon-1[)] does not look like a module.' config.result ./deferred-module-error.nix
432
433# Check the file location information is propagated into submodules
434checkConfigOutput the-file.nix config.submodule.internalFiles.0 ./submoduleFiles.nix
435
436
437# Check that disabledModules works recursively and correctly
438checkConfigOutput '^true$' config.enable ./disable-recursive/main.nix
439checkConfigOutput '^true$' config.enable ./disable-recursive/{main.nix,disable-foo.nix}
440checkConfigOutput '^true$' config.enable ./disable-recursive/{main.nix,disable-bar.nix}
441checkConfigError 'The option .* does not exist. Definition values:\n\s*- In .*: true' config.enable ./disable-recursive/{main.nix,disable-foo.nix,disable-bar.nix}
442
443# Check that imports can depend on derivations
444checkConfigOutput '^true$' config.enable ./import-from-store.nix
445
446# Check that configs can be conditional on option existence
447checkConfigOutput '^true$' config.enable ./define-option-dependently.nix ./declare-enable.nix ./declare-int-positive-value.nix
448checkConfigOutput '^360$' config.value ./define-option-dependently.nix ./declare-enable.nix ./declare-int-positive-value.nix
449checkConfigOutput '^7$' config.value ./define-option-dependently.nix ./declare-int-positive-value.nix
450checkConfigOutput '^true$' config.set.enable ./define-option-dependently-nested.nix ./declare-enable-nested.nix ./declare-int-positive-value-nested.nix
451checkConfigOutput '^360$' config.set.value ./define-option-dependently-nested.nix ./declare-enable-nested.nix ./declare-int-positive-value-nested.nix
452checkConfigOutput '^7$' config.set.value ./define-option-dependently-nested.nix ./declare-int-positive-value-nested.nix
453
454# Check attrsOf and lazyAttrsOf. Only lazyAttrsOf should be lazy, and only
455# attrsOf should work with conditional definitions
456# In addition, lazyAttrsOf should honor an options emptyValue
457checkConfigError "is not lazy" config.isLazy ./declare-attrsOf.nix ./attrsOf-lazy-check.nix
458checkConfigOutput '^true$' config.isLazy ./declare-lazyAttrsOf.nix ./attrsOf-lazy-check.nix
459checkConfigOutput '^true$' config.conditionalWorks ./declare-attrsOf.nix ./attrsOf-conditional-check.nix
460checkConfigOutput '^false$' config.conditionalWorks ./declare-lazyAttrsOf.nix ./attrsOf-conditional-check.nix
461checkConfigOutput '^"empty"$' config.value.foo ./declare-lazyAttrsOf.nix ./attrsOf-conditional-check.nix
462
463# Check attrsWith type merging
464checkConfigError 'The option `mergedLazyNonLazy'\'' in `.*'\'' is already declared in `.*'\''\.' options.mergedLazyNonLazy ./lazy-attrsWith.nix
465checkConfigOutput '^11$' config.lazyResult ./lazy-attrsWith.nix
466checkConfigError 'infinite recursion encountered' config.nonLazyResult ./lazy-attrsWith.nix
467
468# AttrsWith placeholder tests
469checkConfigOutput '^"mergedName.<id>.nested"$' config.result ./name-merge-attrsWith-1.nix
470checkConfigError 'The option .mergedName. in .*\.nix. is already declared in .*\.nix' config.mergedName ./name-merge-attrsWith-2.nix
471
472# Test type.functor.wrapped deprecation warning
473# should emit the warning on:
474# - merged types
475# - non-merged types
476# - nestedTypes elemType
477# attrsWith
478NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.attrsWith.type.functor.wrapped ./deprecated-wrapped.nix
479NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.mergedAttrsWith.type.functor.wrapped ./deprecated-wrapped.nix
480
481NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.attrsWith.type.nestedTypes.elemType.functor.wrapped ./deprecated-wrapped.nix
482NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.mergedAttrsWith.type.nestedTypes.elemType.functor.wrapped ./deprecated-wrapped.nix
483# listOf
484NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.listOf.type.functor.wrapped ./deprecated-wrapped.nix
485NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.mergedListOf.type.functor.wrapped ./deprecated-wrapped.nix
486
487NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.listOf.type.nestedTypes.elemType.functor.wrapped ./deprecated-wrapped.nix
488NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.mergedListOf.type.nestedTypes.elemType.functor.wrapped ./deprecated-wrapped.nix
489# unique / uniq
490NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.unique.type.functor.wrapped ./deprecated-wrapped.nix
491NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.mergedUnique.type.functor.wrapped ./deprecated-wrapped.nix
492
493NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.unique.type.nestedTypes.elemType.functor.wrapped ./deprecated-wrapped.nix
494NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.mergedUnique.type.nestedTypes.elemType.functor.wrapped ./deprecated-wrapped.nix
495# nullOr
496NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.nullOr.type.functor.wrapped ./deprecated-wrapped.nix
497NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.mergedNullOr.type.functor.wrapped ./deprecated-wrapped.nix
498
499NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.nullOr.type.nestedTypes.elemType.functor.wrapped ./deprecated-wrapped.nix
500NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.mergedNullOr.type.nestedTypes.elemType.functor.wrapped ./deprecated-wrapped.nix
501# functionTo
502NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.functionTo.type.functor.wrapped ./deprecated-wrapped.nix
503NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.mergedFunctionTo.type.functor.wrapped ./deprecated-wrapped.nix
504
505NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.functionTo.type.nestedTypes.elemType.functor.wrapped ./deprecated-wrapped.nix
506NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.mergedFunctionTo.type.nestedTypes.elemType.functor.wrapped ./deprecated-wrapped.nix
507
508# coercedTo
509# Note: test 'nestedTypes.finalType' and 'nestedTypes.coercedType'
510NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.coercedTo.type.functor.wrapped ./deprecated-wrapped.nix
511NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.coercedTo.type.nestedTypes.finalType.functor.wrapped ./deprecated-wrapped.nix
512NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.coercedTo.type.nestedTypes.coercedType.functor.wrapped ./deprecated-wrapped.nix
513# either
514NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.either.type.functor.wrapped ./deprecated-wrapped.nix
515NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.mergedEither.type.functor.wrapped ./deprecated-wrapped.nix
516
517NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.either.type.nestedTypes.left.functor.wrapped ./deprecated-wrapped.nix
518NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.either.type.nestedTypes.right.functor.wrapped ./deprecated-wrapped.nix
519NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.mergedEither.type.nestedTypes.left.functor.wrapped ./deprecated-wrapped.nix
520NIX_ABORT_ON_WARN=1 checkConfigError 'The deprecated `.*functor.wrapped` attribute .*is accessed, use `.*nestedTypes.elemType` instead.' options.mergedEither.type.nestedTypes.right.functor.wrapped ./deprecated-wrapped.nix
521
522# Even with multiple assignments, a type error should be thrown if any of them aren't valid
523checkConfigError 'A definition for option .* is not of type .*' \
524 config.value ./declare-int-unsigned-value.nix ./define-value-list.nix ./define-value-int-positive.nix
525
526## Freeform modules
527# Assigning without a declared option should work
528checkConfigOutput '^"24"$' config.value ./freeform-attrsOf.nix ./define-value-string.nix
529# Shorthand modules interpret `meta` and `class` as config items
530checkConfigOutput '^true$' options._module.args.value.result ./freeform-attrsOf.nix ./define-freeform-keywords-shorthand.nix
531# No freeform assignments shouldn't make it error
532checkConfigOutput '^{}$' config ./freeform-attrsOf.nix
533# but only if the type matches
534checkConfigError 'A definition for option .* is not of type .*' config.value ./freeform-attrsOf.nix ./define-value-list.nix
535# and properties should be applied
536checkConfigOutput '^"yes"$' config.value ./freeform-attrsOf.nix ./define-value-string-properties.nix
537# Options should still be declarable, and be able to have a type that doesn't match the freeform type
538checkConfigOutput '^false$' config.enable ./freeform-attrsOf.nix ./define-value-string.nix ./declare-enable.nix
539checkConfigOutput '^"24"$' config.value ./freeform-attrsOf.nix ./define-value-string.nix ./declare-enable.nix
540# and this should work too with nested values
541checkConfigOutput '^false$' config.nest.foo ./freeform-attrsOf.nix ./freeform-nested.nix
542checkConfigOutput '^"bar"$' config.nest.bar ./freeform-attrsOf.nix ./freeform-nested.nix
543# Check whether a declared option can depend on an freeform-typed one
544checkConfigOutput '^null$' config.foo ./freeform-attrsOf.nix ./freeform-str-dep-unstr.nix
545checkConfigOutput '^"24"$' config.foo ./freeform-attrsOf.nix ./freeform-str-dep-unstr.nix ./define-value-string.nix
546# Check whether an freeform-typed value can depend on a declared option, this can only work with lazyAttrsOf
547REQUIRE_INFINITE_RECURSION_HINT=1 checkConfigError 'infinite recursion encountered' config.foo ./freeform-attrsOf.nix ./freeform-unstr-dep-str.nix
548checkConfigError 'The option .* was accessed but has no value defined. Try setting the option.' config.foo ./freeform-lazyAttrsOf.nix ./freeform-unstr-dep-str.nix
549checkConfigOutput '^"24"$' config.foo ./freeform-lazyAttrsOf.nix ./freeform-unstr-dep-str.nix ./define-value-string.nix
550# submodules in freeformTypes should have their locations annotated
551checkConfigOutput '/freeform-submodules.nix"$' config.fooDeclarations.0 ./freeform-submodules.nix
552# freeformTypes can get merged using `types.type`, including submodules
553checkConfigOutput '^10$' config.free.xxx.foo ./freeform-submodules.nix
554checkConfigOutput '^10$' config.free.yyy.bar ./freeform-submodules.nix
555
556# Regression of either, due to freeform not beeing checked previously
557checkConfigOutput '^"foo"$' config.either.int ./freeform-deprecated-malicous.nix ./freeform-deprecated-malicous-wrong.nix
558NIX_ABORT_ON_WARN=1 checkConfigError "One or more definitions did not pass the type-check of the \'either\' type" config.either.int ./freeform-deprecated-malicous.nix ./freeform-deprecated-malicous-wrong.nix
559checkConfigOutput '^"foo"$' config.eitherBehindNullor.int ./freeform-deprecated-malicous.nix ./freeform-deprecated-malicous-wrong.nix
560NIX_ABORT_ON_WARN=1 checkConfigError "One or more definitions did not pass the type-check of the \'either\' type" config.eitherBehindNullor.int ./freeform-deprecated-malicous.nix ./freeform-deprecated-malicous-wrong.nix
561checkConfigOutput '^"foo"$' config.oneOf.int ./freeform-deprecated-malicous.nix ./freeform-deprecated-malicous-wrong.nix
562NIX_ABORT_ON_WARN=1 checkConfigError "One or more definitions did not pass the type-check of the \'either\' type" config.oneOf.int ./freeform-deprecated-malicous.nix ./freeform-deprecated-malicous-wrong.nix
563checkConfigOutput '^"foo"$' config.number.str ./freeform-deprecated-malicous.nix ./freeform-deprecated-malicous-wrong.nix
564NIX_ABORT_ON_WARN=1 checkConfigError "One or more definitions did not pass the type-check of the \'either\' type" config.number.str ./freeform-deprecated-malicous.nix ./freeform-deprecated-malicous-wrong.nix
565
566checkConfigOutput '^42$' config.either.int ./freeform-deprecated-malicous.nix ./freeform-deprecated-malicous-wrong2.nix
567NIX_ABORT_ON_WARN=1 checkConfigError "One or more definitions did not pass the type-check of the \'either\' type" config.either.int ./freeform-deprecated-malicous.nix ./freeform-deprecated-malicous-wrong2.nix
568checkConfigOutput '^42$' config.eitherBehindNullor.int ./freeform-deprecated-malicous.nix ./freeform-deprecated-malicous-wrong2.nix
569NIX_ABORT_ON_WARN=1 checkConfigError "One or more definitions did not pass the type-check of the \'either\' type" config.eitherBehindNullor.int ./freeform-deprecated-malicous.nix ./freeform-deprecated-malicous-wrong2.nix
570checkConfigOutput '^42$' config.oneOf.int ./freeform-deprecated-malicous.nix ./freeform-deprecated-malicous-wrong2.nix
571NIX_ABORT_ON_WARN=1 checkConfigError "One or more definitions did not pass the type-check of the \'either\' type" config.oneOf.int ./freeform-deprecated-malicous.nix ./freeform-deprecated-malicous-wrong2.nix
572checkConfigOutput '^42$' config.number.str ./freeform-deprecated-malicous.nix ./freeform-deprecated-malicous-wrong2.nix
573NIX_ABORT_ON_WARN=1 checkConfigError "One or more definitions did not pass the type-check of the \'either\' type" config.number.str ./freeform-deprecated-malicous.nix ./freeform-deprecated-malicous-wrong2.nix
574# Value OK: Fail if a warning is emitted
575NIX_ABORT_ON_WARN=1 checkConfigOutput "^42$" config.number.int ./freeform-attrsof-either.nix
576
577
578## types.anything
579# Check that attribute sets are merged recursively
580checkConfigOutput '^null$' config.value.foo ./types-anything/nested-attrs.nix
581checkConfigOutput '^null$' config.value.l1.foo ./types-anything/nested-attrs.nix
582checkConfigOutput '^null$' config.value.l1.l2.foo ./types-anything/nested-attrs.nix
583checkConfigOutput '^null$' config.value.l1.l2.l3.foo ./types-anything/nested-attrs.nix
584# Attribute sets that are coercible to strings shouldn't be recursed into
585checkConfigOutput '^"foo"$' config.value.outPath ./types-anything/attrs-coercible.nix
586# Multiple lists aren't concatenated together if their definitions are not equal
587checkConfigError 'The option .* has conflicting definition values' config.value ./types-anything/lists.nix
588# Check that all equalizable atoms can be used as long as all definitions are equal
589checkConfigOutput '^0$' config.value.int ./types-anything/equal-atoms.nix
590checkConfigOutput '^false$' config.value.bool ./types-anything/equal-atoms.nix
591checkConfigOutput '^""$' config.value.string ./types-anything/equal-atoms.nix
592checkConfigOutput '^"/[^"]+"$' config.value.path ./types-anything/equal-atoms.nix
593checkConfigOutput '^null$' config.value.null ./types-anything/equal-atoms.nix
594checkConfigOutput '^0.1$' config.value.float ./types-anything/equal-atoms.nix
595checkConfigOutput '^\[1,"a",{"x":null}\]$' config.value.list ./types-anything/equal-atoms.nix
596# Functions can't be merged together
597checkConfigError "The option .value.multiple-lambdas.<function body>. has conflicting option types" config.applied.multiple-lambdas ./types-anything/functions.nix
598checkConfigOutput '^true$' config.valueIsFunction.single-lambda ./types-anything/functions.nix
599checkConfigOutput '^null$' config.applied.merging-lambdas.x ./types-anything/functions.nix
600checkConfigOutput '^null$' config.applied.merging-lambdas.y ./types-anything/functions.nix
601# Check that all mk* modifiers are applied
602checkConfigError 'attribute .* not found' config.value.mkiffalse ./types-anything/mk-mods.nix
603checkConfigOutput '^{}$' config.value.mkiftrue ./types-anything/mk-mods.nix
604checkConfigOutput '^1$' config.value.mkdefault ./types-anything/mk-mods.nix
605checkConfigOutput '^{}$' config.value.mkmerge ./types-anything/mk-mods.nix
606checkConfigOutput '^true$' config.value.mkbefore ./types-anything/mk-mods.nix
607checkConfigOutput '^1$' config.value.nested.foo ./types-anything/mk-mods.nix
608checkConfigOutput '^"baz"$' config.value.nested.bar.baz ./types-anything/mk-mods.nix
609
610## types.functionTo
611checkConfigOutput '^"input is input"$' config.result ./functionTo/trivial.nix
612checkConfigOutput '^"a b"$' config.result ./functionTo/merging-list.nix
613checkConfigError 'A definition for option .fun.<function body>. is not of type .string.. Definition values:\n\s*- In .*wrong-type.nix' config.result ./functionTo/wrong-type.nix
614checkConfigOutput '^"b a"$' config.result ./functionTo/list-order.nix
615checkConfigOutput '^"a c"$' config.result ./functionTo/merging-attrs.nix
616checkConfigOutput '^"a bee"$' config.result ./functionTo/submodule-options.nix
617checkConfigOutput '^"fun.<function body>.a fun.<function body>.b"$' config.optionsResult ./functionTo/submodule-options.nix
618
619# moduleType
620checkConfigOutput '^"a b"$' config.resultFoo ./declare-variants.nix ./define-variant.nix
621checkConfigOutput '^"a b y z"$' config.resultFooBar ./declare-variants.nix ./define-variant.nix
622checkConfigOutput '^"a b c"$' config.resultFooFoo ./declare-variants.nix ./define-variant.nix
623
624## emptyValue's
625checkConfigOutput "\[\]" config.list.a ./emptyValues.nix
626checkConfigOutput "{}" config.attrs.a ./emptyValues.nix
627checkConfigOutput "null" config.null.a ./emptyValues.nix
628checkConfigOutput "{}" config.submodule.a ./emptyValues.nix
629# These types don't have empty values
630checkConfigError 'The option .int.a. was accessed but has no value defined. Try setting the option.' config.int.a ./emptyValues.nix
631checkConfigError 'The option .nonEmptyList.a. was accessed but has no value defined. Try setting the option.' config.nonEmptyList.a ./emptyValues.nix
632
633# types.unique
634# requires a single definition
635checkConfigError 'The option .examples\.merged. is defined multiple times while it.s expected to be unique' config.examples.merged.a ./types-unique.nix
636# user message is printed
637checkConfigError 'We require a single definition, because seeing the whole value at once helps us maintain critical invariants of our system.' config.examples.merged.a ./types-unique.nix
638# let the inner merge function check the values (on demand)
639checkConfigError 'A definition for option .examples\.badLazyType\.a. is not of type .string.' config.examples.badLazyType.a ./types-unique.nix
640# overriding still works (unlike option uniqueness)
641checkConfigOutput '^"bee"$' config.examples.override.b ./types-unique.nix
642
643## types.raw
644checkConfigOutput '^true$' config.unprocessedNestingEvaluates.success ./raw.nix
645checkConfigOutput "10" config.processedToplevel ./raw.nix
646checkConfigError "The option .multiple. is defined multiple times" config.multiple ./raw.nix
647checkConfigOutput "bar" config.priorities ./raw.nix
648
649## Option collision
650checkConfigError \
651 'The option .set. in module .*/declare-set.nix. would be a parent of the following options, but its type .attribute set of signed integer. does not support nested options.\n\s*- option[(]s[)] with prefix .set.enable. in module .*/declare-enable-nested.nix.' \
652 config.set \
653 ./declare-set.nix ./declare-enable-nested.nix
654
655# Options: accidental use of an option-type instead of option (or other tagged type; unlikely)
656checkConfigError 'In module .*/options-type-error-typical.nix: expected an option declaration at option path .result. but got an attribute set with type option-type' config.result ./options-type-error-typical.nix
657checkConfigError 'In module .*/options-type-error-typical-nested.nix: expected an option declaration at option path .result.here. but got an attribute set with type option-type' config.result.here ./options-type-error-typical-nested.nix
658checkConfigError 'In module .*/options-type-error-configuration.nix: expected an option declaration at option path .result. but got an attribute set with type configuration' config.result ./options-type-error-configuration.nix
659
660# Check that that merging of option collisions doesn't depend on type being set
661checkConfigError 'The option .group..*would be a parent of the following options, but its type .<no description>. does not support nested options.\n\s*- option.s. with prefix .group.enable..*' config.group.enable ./merge-typeless-option.nix
662
663# Test that types.optionType merges types correctly
664checkConfigOutput '^10$' config.theOption.int ./optionTypeMerging.nix
665checkConfigOutput '^"hello"$' config.theOption.str ./optionTypeMerging.nix
666
667# Test that types.optionType correctly annotates option locations
668checkConfigError 'The option .theOption.nested. in .other.nix. is already declared in .optionTypeFile.nix.' config.theOption.nested ./optionTypeFile.nix
669
670# Test that types.optionType leaves types untouched as long as they don't need to be merged
671checkConfigOutput 'ok' config.freeformItems.foo.bar ./adhoc-freeformType-survives-type-merge.nix
672
673# Test that specifying both functor.wrapped and functor.payload isn't allowed
674checkConfigError 'Type foo defines both `functor.payload` and `functor.wrapped` at the same time, which is not supported.' config.result ./default-type-merge-both.nix
675
676
677# Anonymous submodules don't get nixed by import resolution/deduplication
678# because of an `extendModules` bug, issue 168767.
679checkConfigOutput '^1$' config.sub.specialisation.value ./extendModules-168767-imports.nix
680
681# Class checks, evalModules
682checkConfigOutput '^{}$' config.ok.config ./class-check.nix
683checkConfigOutput '"nixos"' config.ok.class ./class-check.nix
684checkConfigError 'The module `.*/module-class-is-darwin.nix`.*?expects class "nixos".' config.fail.config ./class-check.nix
685checkConfigError 'The module `foo.nix#darwinModules.default`.*?expects class "nixos".' config.fail-anon.config ./class-check.nix
686
687# Class checks, submoduleWith
688checkConfigOutput '^{}$' config.sub.nixosOk ./class-check.nix
689checkConfigError 'The module `.*/module-class-is-darwin.nix`.*?expects class "nixos".' config.sub.nixosFail.config ./class-check.nix
690
691# submoduleWith type merge with different class
692checkConfigError 'A submoduleWith option is declared multiple times with conflicting class values "darwin" and "nixos".' config.sub.mergeFail.config ./class-check.nix
693
694# _type check
695checkConfigError 'Expected a module, but found a value of type .*"flake".*, while trying to load a module into .*/module-imports-_type-check.nix' config.ok.config ./module-imports-_type-check.nix
696checkConfigOutput '^true$' config.enable ./declare-enable.nix ./define-enable-with-top-level-mkIf.nix
697checkConfigError 'Expected a module, but found a value of type .*"configuration".*, while trying to load a module into .*/import-configuration.nix.' config ./import-configuration.nix
698checkConfigError 'please only import the modules that make up the configuration' config ./import-configuration.nix
699checkConfigError 'Expected a module, but found a value of type "configuration", while trying to load a module into .*/import-error-submodule.nix, while trying to load a module into .*foo.*\.' config.foo ./import-error-submodule.nix
700
701# doRename works when `warnings` does not exist.
702checkConfigOutput '^1234$' config.c.d.e ./doRename-basic.nix
703# doRename adds a warning.
704checkConfigOutput '^"The option `a\.b. defined in `.*/doRename-warnings\.nix. has been renamed to `c\.d\.e.\."$' \
705 config.result \
706 ./doRename-warnings.nix
707checkConfigOutput "^true$" config.result ./doRename-condition.nix ./doRename-condition-enable.nix
708checkConfigOutput "^true$" config.result ./doRename-condition.nix ./doRename-condition-no-enable.nix
709checkConfigOutput "^true$" config.result ./doRename-condition.nix ./doRename-condition-migrated.nix
710
711# Anonymous modules get deduplicated by key
712checkConfigOutput '^"pear"$' config.once.raw ./merge-module-with-key.nix
713checkConfigOutput '^"pear\\npear"$' config.twice.raw ./merge-module-with-key.nix
714
715# Declaration positions
716# Line should be present for direct options
717checkConfigOutput '^14$' options.imported.line14.declarationPositions.0.line ./declaration-positions.nix
718checkConfigOutput '/declaration-positions.nix"$' options.imported.line14.declarationPositions.0.file ./declaration-positions.nix
719# Generated options may not have line numbers but they will at least get the
720# right file
721checkConfigOutput '/declaration-positions.nix"$' options.generated.line22.declarationPositions.0.file ./declaration-positions.nix
722checkConfigOutput '^null$' options.generated.line22.declarationPositions.0.line ./declaration-positions.nix
723# Submodules don't break it
724checkConfigOutput '^45$' config.submoduleLine38.submodDeclLine45.0.line ./declaration-positions.nix
725checkConfigOutput '/declaration-positions.nix"$' config.submoduleLine38.submodDeclLine45.0.file ./declaration-positions.nix
726# New options under freeform submodules get collected into the parent submodule
727# (consistent with .declarations behaviour, but weird; notably appears in system.build)
728checkConfigOutput '^38|27$' options.submoduleLine38.declarationPositions.0.line ./declaration-positions.nix
729checkConfigOutput '^38|27$' options.submoduleLine38.declarationPositions.1.line ./declaration-positions.nix
730# nested options work
731checkConfigOutput '^34$' options.nested.nestedLine34.declarationPositions.0.line ./declaration-positions.nix
732
733# types.pathWith { inStore = true; }
734checkConfigOutput '".*/store/0lz9p8xhf89kb1c1kk6jxrzskaiygnlh-bash-5.2-p15.drv"' config.pathInStore.ok1 ./pathWith.nix
735checkConfigOutput '".*/store/0fb3ykw9r5hpayd05sr0cizwadzq1d8q-bash-5.2-p15"' config.pathInStore.ok2 ./pathWith.nix
736checkConfigOutput '".*/store/0fb3ykw9r5hpayd05sr0cizwadzq1d8q-bash-5.2-p15/bin/bash"' config.pathInStore.ok3 ./pathWith.nix
737checkConfigError 'A definition for option .* is not of type .path in the Nix store.. Definition values:\n\s*- In .*: ""' config.pathInStore.bad1 ./pathWith.nix
738checkConfigError 'A definition for option .* is not of type .path in the Nix store.. Definition values:\n\s*- In .*: ".*/store"' config.pathInStore.bad2 ./pathWith.nix
739checkConfigError 'A definition for option .* is not of type .path in the Nix store.. Definition values:\n\s*- In .*: ".*/store/"' config.pathInStore.bad3 ./pathWith.nix
740checkConfigError 'A definition for option .* is not of type .path in the Nix store.. Definition values:\n\s*- In .*: ".*/store/.links"' config.pathInStore.bad4 ./pathWith.nix
741checkConfigError 'A definition for option .* is not of type .path in the Nix store.. Definition values:\n\s*- In .*: "/foo/bar"' config.pathInStore.bad5 ./pathWith.nix
742
743# types.pathWith { inStore = false; }
744checkConfigOutput '"/foo/bar"' config.pathNotInStore.ok1 ./pathWith.nix
745checkConfigOutput '".*/store"' config.pathNotInStore.ok2 ./pathWith.nix
746checkConfigOutput '".*/store/"' config.pathNotInStore.ok3 ./pathWith.nix
747checkConfigOutput '""' config.pathNotInStore.ok4 ./pathWith.nix
748checkConfigOutput '".*/store/.links"' config.pathNotInStore.ok5 ./pathWith.nix
749checkConfigError 'A definition for option .* is not of type .path not in the Nix store.. Definition values:\n\s*- In .*: ".*/0lz9p8xhf89kb1c1kk6jxrzskaiygnlh-bash-5.2-p15.drv"' config.pathNotInStore.bad1 ./pathWith.nix
750checkConfigError 'A definition for option .* is not of type .path not in the Nix store.. Definition values:\n\s*- In .*: ".*/0fb3ykw9r5hpayd05sr0cizwadzq1d8q-bash-5.2-p15"' config.pathNotInStore.bad2 ./pathWith.nix
751checkConfigError 'A definition for option .* is not of type .path not in the Nix store.. Definition values:\n\s*- In .*: ".*/0fb3ykw9r5hpayd05sr0cizwadzq1d8q-bash-5.2-p15/bin/bash"' config.pathNotInStore.bad3 ./pathWith.nix
752checkConfigError 'A definition for option .* is not of type .path not in the Nix store.. Definition values:\n\s*- In .*: .*/pathWith.nix' config.pathNotInStore.bad4 ./pathWith.nix
753
754# types.pathWith { }
755checkConfigOutput '"/this/is/absolute"' config.anyPath.ok1 ./pathWith.nix
756checkConfigOutput '"./this/is/relative"' config.anyPath.ok2 ./pathWith.nix
757checkConfigError 'A definition for option .anyPath.bad1. is not of type .path.' config.anyPath.bad1 ./pathWith.nix
758
759# types.pathWith { absolute = true; }
760checkConfigOutput '"/this/is/absolute"' config.absolutePathNotInStore.ok1 ./pathWith.nix
761checkConfigError 'A definition for option .absolutePathNotInStore.bad1. is not of type .absolute path not in the Nix store.' config.absolutePathNotInStore.bad1 ./pathWith.nix
762checkConfigError 'A definition for option .absolutePathNotInStore.bad2. is not of type .absolute path not in the Nix store.' config.absolutePathNotInStore.bad2 ./pathWith.nix
763
764# types.pathWith failed type merge
765checkConfigError 'The option .conflictingPathOptionType. in .*/pathWith.nix. is already declared in .*/pathWith.nix' config.conflictingPathOptionType ./pathWith.nix
766
767# types.pathWith { inStore = true; absolute = false; }
768checkConfigError 'In pathWith, inStore means the path must be absolute' config.impossiblePathOptionType ./pathWith.nix
769
770# mkDefinition
771# check that mkDefinition 'file' is printed in the error message
772checkConfigError 'Cannot merge definitions.*\n\s*- In .file.*\n\s*- In .other.*' config.conflict ./mkDefinition.nix
773checkConfigError 'A definition for option .viaOptionDefault. is not of type .boolean.*' config.viaOptionDefault ./mkDefinition.nix
774checkConfigOutput '^true$' config.viaConfig ./mkDefinition.nix
775checkConfigOutput '^true$' config.mkMerge ./mkDefinition.nix
776checkConfigOutput '^true$' config.mkForce ./mkDefinition.nix
777
778# specialArgs._class
779checkConfigOutput '"nixos"' config.nixos.config.foo ./specialArgs-class.nix
780checkConfigOutput '"bar"' config.conditionalImportAsNixos.config.foo ./specialArgs-class.nix
781checkConfigError 'attribute .*bar.* not found' config.conditionalImportAsNixos.config.bar ./specialArgs-class.nix
782checkConfigError 'attribute .*foo.* not found' config.conditionalImportAsDarwin.config.foo ./specialArgs-class.nix
783checkConfigOutput '"foo"' config.conditionalImportAsDarwin.config.bar ./specialArgs-class.nix
784checkConfigOutput '"nixos"' config.sub.nixos.foo ./specialArgs-class.nix
785checkConfigOutput '"bar"' config.sub.conditionalImportAsNixos.foo ./specialArgs-class.nix
786checkConfigError 'attribute .*bar.* not found' config.sub.conditionalImportAsNixos.bar ./specialArgs-class.nix
787checkConfigError 'attribute .*foo.* not found' config.sub.conditionalImportAsDarwin.foo ./specialArgs-class.nix
788checkConfigOutput '"foo"' config.sub.conditionalImportAsDarwin.bar ./specialArgs-class.nix
789# Check that some types expose the 'valueMeta'
790checkConfigOutput '\{\}' options.str.valueMeta ./types-valueMeta.nix
791checkConfigOutput '["foo", "bar"]' config.attrsOfResult ./types-valueMeta.nix
792checkConfigOutput '2' config.listOfResult ./types-valueMeta.nix
793
794# Check that composed types expose the 'valueMeta'
795# attrsOf submodule (also on merged options,types)
796checkConfigOutput '42' options.attrsOfModule.valueMeta.attrs.foo.configuration.options.bar.value ./composed-types-valueMeta.nix
797checkConfigOutput '42' options.mergedAttrsOfModule.valueMeta.attrs.foo.configuration.options.bar.value ./composed-types-valueMeta.nix
798
799# listOf submodule (also on merged options,types)
800checkConfigOutput '42' config.listResult ./composed-types-valueMeta.nix
801checkConfigOutput '42' config.mergedListResult ./composed-types-valueMeta.nix
802
803# Add check
804checkConfigOutput '^0$' config.v1CheckedPass ./add-check.nix
805checkConfigError 'A definition for option .* is not of type .signed integer.*' config.v1CheckedFail ./add-check.nix
806checkConfigOutput '^true$' config.v2checkedPass ./add-check.nix
807checkConfigError 'A definition for option .* is not of type .attribute set of signed integer.*' config.v2checkedFail ./add-check.nix
808
809
810cat <<EOF
811====== module tests ======
812$pass Pass
813$fail Fail
814EOF
815
816if [ "$fail" -ne 0 ]; then
817 exit 1
818fi
819exit 0