mirror of
https://github.com/NixOS/nixpkgs.git
synced 2024-11-25 23:20:55 +00:00
325 lines
18 KiB
Bash
Executable file
325 lines
18 KiB
Bash
Executable file
#!/bin/sh
|
|
#
|
|
# This script is used to test that the module system is working as expected.
|
|
# By default it test the version of nixpkgs which is defined in the NIX_PATH.
|
|
|
|
# https://stackoverflow.com/a/246128/6605742
|
|
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
|
|
|
cd "$DIR"/modules
|
|
|
|
pass=0
|
|
fail=0
|
|
|
|
evalConfig() {
|
|
local attr=$1
|
|
shift;
|
|
local script="import ./default.nix { modules = [ $@ ];}"
|
|
nix-instantiate --timeout 1 -E "$script" -A "$attr" --eval-only --show-trace --read-write-mode
|
|
}
|
|
|
|
reportFailure() {
|
|
local attr=$1
|
|
shift;
|
|
local script="import ./default.nix { modules = [ $@ ];}"
|
|
echo 2>&1 "$ nix-instantiate -E '$script' -A '$attr' --eval-only"
|
|
evalConfig "$attr" "$@"
|
|
fail=$((fail + 1))
|
|
}
|
|
|
|
checkConfigCodeOutErr() {
|
|
local expectedExit=$1
|
|
shift;
|
|
local outputContains=$1
|
|
shift;
|
|
local errorContains=$1
|
|
shift;
|
|
out=$(mktemp)
|
|
err=$(mktemp)
|
|
evalConfig "$@" 1>"$out" 2>"$err"
|
|
if [[ "$?" -ne "$expectedExit" ]]; then
|
|
echo 2>&1 "error: Expected exit code $expectedExit while evaluating"
|
|
reportFailure "$@"
|
|
return 1
|
|
fi
|
|
|
|
if [[ -n "$outputContains" ]] && ! grep -zP --silent "$outputContains" "$out"; then
|
|
echo 2>&1 "error: Expected output matching '$outputContains', while evaluating"
|
|
reportFailure "$@"
|
|
return 1
|
|
fi
|
|
|
|
if [[ -n "$errorContains" ]] && ! grep -zP --silent "$errorContains" "$err"; then
|
|
echo 2>&1 "error: Expected error matching '$errorContains', while evaluating"
|
|
reportFailure "$@"
|
|
return 1
|
|
fi
|
|
|
|
pass=$((pass + 1))
|
|
|
|
# Clean up temp files
|
|
rm "$out" "$err"
|
|
}
|
|
|
|
checkConfigOutput() {
|
|
local outputContains=$1
|
|
shift;
|
|
checkConfigCodeOutErr 0 "$outputContains" "" "$@"
|
|
}
|
|
|
|
checkConfigError() {
|
|
local errorContains=$1
|
|
shift;
|
|
checkConfigCodeOutErr 1 "" "$errorContains" "$@"
|
|
}
|
|
|
|
# Check boolean option.
|
|
checkConfigOutput "false" config.enable ./declare-enable.nix
|
|
checkConfigError 'The option .* does not exist. Definition values:\n- In .*: true' config.enable ./define-enable.nix
|
|
|
|
# Check integer types.
|
|
# unsigned
|
|
checkConfigOutput "42" config.value ./declare-int-unsigned-value.nix ./define-value-int-positive.nix
|
|
checkConfigError 'A definition for option .* is not of type.*unsigned integer.*. Definition values:\n- In .*: -23' config.value ./declare-int-unsigned-value.nix ./define-value-int-negative.nix
|
|
# positive
|
|
checkConfigError 'A definition for option .* is not of type.*positive integer.*. Definition values:\n- In .*: 0' config.value ./declare-int-positive-value.nix ./define-value-int-zero.nix
|
|
# between
|
|
checkConfigOutput "42" config.value ./declare-int-between-value.nix ./define-value-int-positive.nix
|
|
checkConfigError 'A definition for option .* is not of type.*between.*-21 and 43.*inclusive.*. Definition values:\n- In .*: -23' config.value ./declare-int-between-value.nix ./define-value-int-negative.nix
|
|
|
|
# Check either types
|
|
# types.either
|
|
checkConfigOutput "42" config.value ./declare-either.nix ./define-value-int-positive.nix
|
|
checkConfigOutput "\"24\"" config.value ./declare-either.nix ./define-value-string.nix
|
|
# types.oneOf
|
|
checkConfigOutput "42" config.value ./declare-oneOf.nix ./define-value-int-positive.nix
|
|
checkConfigOutput "[ ]" config.value ./declare-oneOf.nix ./define-value-list.nix
|
|
checkConfigOutput "\"24\"" config.value ./declare-oneOf.nix ./define-value-string.nix
|
|
|
|
# Check mkForce without submodules.
|
|
set -- config.enable ./declare-enable.nix ./define-enable.nix
|
|
checkConfigOutput "true" "$@"
|
|
checkConfigOutput "false" "$@" ./define-force-enable.nix
|
|
checkConfigOutput "false" "$@" ./define-enable-force.nix
|
|
|
|
# Check mkForce with option and submodules.
|
|
checkConfigError 'attribute .*foo.* .* not found' config.attrsOfSub.foo.enable ./declare-attrsOfSub-any-enable.nix
|
|
checkConfigOutput 'false' config.attrsOfSub.foo.enable ./declare-attrsOfSub-any-enable.nix ./define-attrsOfSub-foo.nix
|
|
set -- config.attrsOfSub.foo.enable ./declare-attrsOfSub-any-enable.nix ./define-attrsOfSub-foo-enable.nix
|
|
checkConfigOutput 'true' "$@"
|
|
checkConfigOutput 'false' "$@" ./define-force-attrsOfSub-foo-enable.nix
|
|
checkConfigOutput 'false' "$@" ./define-attrsOfSub-force-foo-enable.nix
|
|
checkConfigOutput 'false' "$@" ./define-attrsOfSub-foo-force-enable.nix
|
|
checkConfigOutput 'false' "$@" ./define-attrsOfSub-foo-enable-force.nix
|
|
|
|
# Check overriding effect of mkForce on submodule definitions.
|
|
checkConfigError 'attribute .*bar.* .* not found' config.attrsOfSub.bar.enable ./declare-attrsOfSub-any-enable.nix ./define-attrsOfSub-foo.nix
|
|
checkConfigOutput 'false' config.attrsOfSub.bar.enable ./declare-attrsOfSub-any-enable.nix ./define-attrsOfSub-foo.nix ./define-attrsOfSub-bar.nix
|
|
set -- config.attrsOfSub.bar.enable ./declare-attrsOfSub-any-enable.nix ./define-attrsOfSub-foo.nix ./define-attrsOfSub-bar-enable.nix
|
|
checkConfigOutput 'true' "$@"
|
|
checkConfigError 'attribute .*bar.* .* not found' "$@" ./define-force-attrsOfSub-foo-enable.nix
|
|
checkConfigError 'attribute .*bar.* .* not found' "$@" ./define-attrsOfSub-force-foo-enable.nix
|
|
checkConfigOutput 'true' "$@" ./define-attrsOfSub-foo-force-enable.nix
|
|
checkConfigOutput 'true' "$@" ./define-attrsOfSub-foo-enable-force.nix
|
|
|
|
# Check mkIf with submodules.
|
|
checkConfigError 'attribute .*foo.* .* not found' config.attrsOfSub.foo.enable ./declare-enable.nix ./declare-attrsOfSub-any-enable.nix
|
|
set -- config.attrsOfSub.foo.enable ./declare-enable.nix ./declare-attrsOfSub-any-enable.nix
|
|
checkConfigError 'attribute .*foo.* .* not found' "$@" ./define-if-attrsOfSub-foo-enable.nix
|
|
checkConfigError 'attribute .*foo.* .* not found' "$@" ./define-attrsOfSub-if-foo-enable.nix
|
|
checkConfigError 'attribute .*foo.* .* not found' "$@" ./define-attrsOfSub-foo-if-enable.nix
|
|
checkConfigOutput 'false' "$@" ./define-attrsOfSub-foo-enable-if.nix
|
|
checkConfigOutput 'true' "$@" ./define-enable.nix ./define-if-attrsOfSub-foo-enable.nix
|
|
checkConfigOutput 'true' "$@" ./define-enable.nix ./define-attrsOfSub-if-foo-enable.nix
|
|
checkConfigOutput 'true' "$@" ./define-enable.nix ./define-attrsOfSub-foo-if-enable.nix
|
|
checkConfigOutput 'true' "$@" ./define-enable.nix ./define-attrsOfSub-foo-enable-if.nix
|
|
|
|
# Check disabledModules with config definitions and option declarations.
|
|
set -- config.enable ./define-enable.nix ./declare-enable.nix
|
|
checkConfigOutput "true" "$@"
|
|
checkConfigOutput "false" "$@" ./disable-define-enable.nix
|
|
checkConfigError "The option .*enable.* does not exist. Definition values:\n- In .*: true" "$@" ./disable-declare-enable.nix
|
|
checkConfigError "attribute .*enable.* in selection path .*config.enable.* not found" "$@" ./disable-define-enable.nix ./disable-declare-enable.nix
|
|
checkConfigError "attribute .*enable.* in selection path .*config.enable.* not found" "$@" ./disable-enable-modules.nix
|
|
|
|
# Check _module.args.
|
|
set -- config.enable ./declare-enable.nix ./define-enable-with-custom-arg.nix
|
|
checkConfigError 'while evaluating the module argument .*custom.* in .*define-enable-with-custom-arg.nix.*:' "$@"
|
|
checkConfigOutput "true" "$@" ./define-_module-args-custom.nix
|
|
|
|
# Check that using _module.args on imports cause infinite recursions, with
|
|
# the proper error context.
|
|
set -- "$@" ./define-_module-args-custom.nix ./import-custom-arg.nix
|
|
checkConfigError 'while evaluating the module argument .*custom.* in .*import-custom-arg.nix.*:' "$@"
|
|
checkConfigError 'infinite recursion encountered' "$@"
|
|
|
|
# Check _module.check.
|
|
set -- config.enable ./declare-enable.nix ./define-enable.nix ./define-attrsOfSub-foo.nix
|
|
checkConfigError 'The option .* does not exist. Definition values:\n- In .*' "$@"
|
|
checkConfigOutput "true" "$@" ./define-module-check.nix
|
|
|
|
# Check coerced value.
|
|
checkConfigOutput "\"42\"" config.value ./declare-coerced-value.nix
|
|
checkConfigOutput "\"24\"" config.value ./declare-coerced-value.nix ./define-value-string.nix
|
|
checkConfigError 'A definition for option .* is not.*string or signed integer convertible to it.*. Definition values:\n- In .*: \[ \]' config.value ./declare-coerced-value.nix ./define-value-list.nix
|
|
|
|
# Check coerced value with unsound coercion
|
|
checkConfigOutput "12" config.value ./declare-coerced-value-unsound.nix
|
|
checkConfigError 'A definition for option .* is not of type .*. Definition values:\n- In .*: "1000"' config.value ./declare-coerced-value-unsound.nix ./define-value-string-bigint.nix
|
|
checkConfigError 'unrecognised JSON value' config.value ./declare-coerced-value-unsound.nix ./define-value-string-arbitrary.nix
|
|
|
|
# Check mkAliasOptionModule.
|
|
checkConfigOutput "true" config.enable ./alias-with-priority.nix
|
|
checkConfigOutput "true" config.enableAlias ./alias-with-priority.nix
|
|
checkConfigOutput "false" config.enable ./alias-with-priority-can-override.nix
|
|
checkConfigOutput "false" config.enableAlias ./alias-with-priority-can-override.nix
|
|
|
|
# submoduleWith
|
|
|
|
## specialArgs should work
|
|
checkConfigOutput "foo" config.submodule.foo ./declare-submoduleWith-special.nix
|
|
|
|
## shorthandOnlyDefines config behaves as expected
|
|
checkConfigOutput "true" config.submodule.config ./declare-submoduleWith-shorthand.nix ./define-submoduleWith-shorthand.nix
|
|
checkConfigError 'is not of type `boolean' config.submodule.config ./declare-submoduleWith-shorthand.nix ./define-submoduleWith-noshorthand.nix
|
|
checkConfigError 'value is a boolean while a set was expected' config.submodule.config ./declare-submoduleWith-noshorthand.nix ./define-submoduleWith-shorthand.nix
|
|
checkConfigOutput "true" config.submodule.config ./declare-submoduleWith-noshorthand.nix ./define-submoduleWith-noshorthand.nix
|
|
|
|
## submoduleWith should merge all modules in one swoop
|
|
checkConfigOutput "true" config.submodule.inner ./declare-submoduleWith-modules.nix
|
|
checkConfigOutput "true" config.submodule.outer ./declare-submoduleWith-modules.nix
|
|
|
|
## Paths should be allowed as values and work as expected
|
|
checkConfigOutput "true" config.submodule.enable ./declare-submoduleWith-path.nix
|
|
|
|
# Check that disabledModules works recursively and correctly
|
|
checkConfigOutput "true" config.enable ./disable-recursive/main.nix
|
|
checkConfigOutput "true" config.enable ./disable-recursive/{main.nix,disable-foo.nix}
|
|
checkConfigOutput "true" config.enable ./disable-recursive/{main.nix,disable-bar.nix}
|
|
checkConfigError 'The option .* does not exist. Definition values:\n- In .*: true' config.enable ./disable-recursive/{main.nix,disable-foo.nix,disable-bar.nix}
|
|
|
|
# Check that imports can depend on derivations
|
|
checkConfigOutput "true" config.enable ./import-from-store.nix
|
|
|
|
# Check that configs can be conditional on option existence
|
|
checkConfigOutput true config.enable ./define-option-dependently.nix ./declare-enable.nix ./declare-int-positive-value.nix
|
|
checkConfigOutput 360 config.value ./define-option-dependently.nix ./declare-enable.nix ./declare-int-positive-value.nix
|
|
checkConfigOutput 7 config.value ./define-option-dependently.nix ./declare-int-positive-value.nix
|
|
checkConfigOutput true config.set.enable ./define-option-dependently-nested.nix ./declare-enable-nested.nix ./declare-int-positive-value-nested.nix
|
|
checkConfigOutput 360 config.set.value ./define-option-dependently-nested.nix ./declare-enable-nested.nix ./declare-int-positive-value-nested.nix
|
|
checkConfigOutput 7 config.set.value ./define-option-dependently-nested.nix ./declare-int-positive-value-nested.nix
|
|
|
|
# Check attrsOf and lazyAttrsOf. Only lazyAttrsOf should be lazy, and only
|
|
# attrsOf should work with conditional definitions
|
|
# In addition, lazyAttrsOf should honor an options emptyValue
|
|
checkConfigError "is not lazy" config.isLazy ./declare-attrsOf.nix ./attrsOf-lazy-check.nix
|
|
checkConfigOutput "true" config.isLazy ./declare-lazyAttrsOf.nix ./attrsOf-lazy-check.nix
|
|
checkConfigOutput "true" config.conditionalWorks ./declare-attrsOf.nix ./attrsOf-conditional-check.nix
|
|
checkConfigOutput "false" config.conditionalWorks ./declare-lazyAttrsOf.nix ./attrsOf-conditional-check.nix
|
|
checkConfigOutput "empty" config.value.foo ./declare-lazyAttrsOf.nix ./attrsOf-conditional-check.nix
|
|
|
|
|
|
# Even with multiple assignments, a type error should be thrown if any of them aren't valid
|
|
checkConfigError 'A definition for option .* is not of type .*' \
|
|
config.value ./declare-int-unsigned-value.nix ./define-value-list.nix ./define-value-int-positive.nix
|
|
|
|
## Freeform modules
|
|
# Assigning without a declared option should work
|
|
checkConfigOutput 24 config.value ./freeform-attrsOf.nix ./define-value-string.nix
|
|
# No freeform assigments shouldn't make it error
|
|
checkConfigOutput '{ }' config ./freeform-attrsOf.nix
|
|
# but only if the type matches
|
|
checkConfigError 'A definition for option .* is not of type .*' config.value ./freeform-attrsOf.nix ./define-value-list.nix
|
|
# and properties should be applied
|
|
checkConfigOutput yes config.value ./freeform-attrsOf.nix ./define-value-string-properties.nix
|
|
# Options should still be declarable, and be able to have a type that doesn't match the freeform type
|
|
checkConfigOutput false config.enable ./freeform-attrsOf.nix ./define-value-string.nix ./declare-enable.nix
|
|
checkConfigOutput 24 config.value ./freeform-attrsOf.nix ./define-value-string.nix ./declare-enable.nix
|
|
# and this should work too with nested values
|
|
checkConfigOutput false config.nest.foo ./freeform-attrsOf.nix ./freeform-nested.nix
|
|
checkConfigOutput bar config.nest.bar ./freeform-attrsOf.nix ./freeform-nested.nix
|
|
# Check whether a declared option can depend on an freeform-typed one
|
|
checkConfigOutput null config.foo ./freeform-attrsOf.nix ./freeform-str-dep-unstr.nix
|
|
checkConfigOutput 24 config.foo ./freeform-attrsOf.nix ./freeform-str-dep-unstr.nix ./define-value-string.nix
|
|
# Check whether an freeform-typed value can depend on a declared option, this can only work with lazyAttrsOf
|
|
checkConfigError 'infinite recursion encountered' config.foo ./freeform-attrsOf.nix ./freeform-unstr-dep-str.nix
|
|
checkConfigError 'The option .* is used but not defined' config.foo ./freeform-lazyAttrsOf.nix ./freeform-unstr-dep-str.nix
|
|
checkConfigOutput 24 config.foo ./freeform-lazyAttrsOf.nix ./freeform-unstr-dep-str.nix ./define-value-string.nix
|
|
|
|
## types.anything
|
|
# Check that attribute sets are merged recursively
|
|
checkConfigOutput null config.value.foo ./types-anything/nested-attrs.nix
|
|
checkConfigOutput null config.value.l1.foo ./types-anything/nested-attrs.nix
|
|
checkConfigOutput null config.value.l1.l2.foo ./types-anything/nested-attrs.nix
|
|
checkConfigOutput null config.value.l1.l2.l3.foo ./types-anything/nested-attrs.nix
|
|
# Attribute sets that are coercible to strings shouldn't be recursed into
|
|
checkConfigOutput foo config.value.outPath ./types-anything/attrs-coercible.nix
|
|
# Multiple lists aren't concatenated together
|
|
checkConfigError 'The option .* has conflicting definitions' config.value ./types-anything/lists.nix
|
|
# Check that all equalizable atoms can be used as long as all definitions are equal
|
|
checkConfigOutput 0 config.value.int ./types-anything/equal-atoms.nix
|
|
checkConfigOutput false config.value.bool ./types-anything/equal-atoms.nix
|
|
checkConfigOutput '""' config.value.string ./types-anything/equal-atoms.nix
|
|
checkConfigOutput / config.value.path ./types-anything/equal-atoms.nix
|
|
checkConfigOutput null config.value.null ./types-anything/equal-atoms.nix
|
|
checkConfigOutput 0.1 config.value.float ./types-anything/equal-atoms.nix
|
|
# Functions can't be merged together
|
|
checkConfigError "The option .* has conflicting definition values" config.value.multiple-lambdas ./types-anything/functions.nix
|
|
checkConfigOutput '<LAMBDA>' config.value.single-lambda ./types-anything/functions.nix
|
|
# Check that all mk* modifiers are applied
|
|
checkConfigError 'attribute .* not found' config.value.mkiffalse ./types-anything/mk-mods.nix
|
|
checkConfigOutput '{ }' config.value.mkiftrue ./types-anything/mk-mods.nix
|
|
checkConfigOutput 1 config.value.mkdefault ./types-anything/mk-mods.nix
|
|
checkConfigOutput '{ }' config.value.mkmerge ./types-anything/mk-mods.nix
|
|
checkConfigOutput true config.value.mkbefore ./types-anything/mk-mods.nix
|
|
checkConfigOutput 1 config.value.nested.foo ./types-anything/mk-mods.nix
|
|
checkConfigOutput baz config.value.nested.bar.baz ./types-anything/mk-mods.nix
|
|
|
|
## Module assertions
|
|
# Check that assertions are triggered by default for just evaluating config
|
|
checkConfigError 'Failed assertions:\n- \[test\] Assertion failed' config ./assertions/simple.nix
|
|
# Check that assertions are only triggered if they have a triggerPath that's evaluated
|
|
checkConfigError 'Failed assertions:\n- \[test\] Assertion failed' config.foo ./assertions/trigger-lazy.nix
|
|
checkConfigOutput true config.bar ./assertions/trigger-lazy.nix
|
|
|
|
# The assertions enable condition should only be evaluated if the trigger is evaluated
|
|
checkConfigError 'enable evaluated' config.foo ./assertions/enable-lazy.nix
|
|
checkConfigOutput true config.bar ./assertions/enable-lazy.nix
|
|
|
|
# Assertion is not triggered when enable is false
|
|
checkConfigOutput '{ }' config ./assertions/enable-false.nix
|
|
|
|
# Warnings should be displayed on standard error
|
|
checkConfigCodeOutErr 0 '{ }' 'warning: \[test\] Warning message' config ./assertions/warning.nix
|
|
|
|
# A triggerPath can be set to a submodule path
|
|
checkConfigOutput '{ baz = <CODE>; }' config.foo.bar ./assertions/trigger-submodule.nix
|
|
checkConfigError 'Failed assertions:\n- \[test\] Assertion failed' config.foo.bar.baz ./assertions/trigger-submodule.nix
|
|
|
|
# Check that multiple assertions and warnings can be triggered at once
|
|
checkConfigError 'Failed assertions:\n- \[test1\] Assertion 1 failed\n- \[test2\] Assertion 2 failed' config ./assertions/multi.nix
|
|
checkConfigError 'trace: warning: \[test3\] Warning 3 failed\ntrace: warning: \[test4\] Warning 4 failed' config ./assertions/multi.nix
|
|
|
|
# Submodules should be able to trigger assertions and display the submodule prefix in their error
|
|
checkConfigError 'Failed assertions:\n- \[foo/test\] Assertion failed' config.foo ./assertions/submodule.nix
|
|
checkConfigError 'Failed assertions:\n- \[foo.bar/test\] Assertion failed' config.foo.bar ./assertions/submodule-attrsOf.nix
|
|
checkConfigError 'Failed assertions:\n- \[foo.bar.baz/test\] Assertion failed' config.foo.bar.baz ./assertions/submodule-attrsOf-attrsOf.nix
|
|
|
|
# Assertions aren't triggered when the trigger path is only evaluated from within the same module evaluation
|
|
# This behavior is necessary to allow assertions to depend on config values. This could potentially be changed in the future if all of NixOS' assertions are rewritten to not depend on any config values
|
|
checkConfigOutput true config.bar ./assertions/non-cascading.nix
|
|
|
|
# Assertions with an attribute starting with _ shouldn't have their name displayed
|
|
checkConfigError 'Failed assertions:\n- Assertion failed' config ./assertions/underscore-attributes.nix
|
|
|
|
cat <<EOF
|
|
====== module tests ======
|
|
$pass Pass
|
|
$fail Fail
|
|
EOF
|
|
|
|
if test $fail -ne 0; then
|
|
exit 1
|
|
fi
|
|
exit 0
|