forked from mirrors/nixpkgs
Merge pull request #191540 from hercules-ci/nixosTest-modular
nixosTest: make modular
This commit is contained in:
commit
7f0d934f9a
|
@ -213,6 +213,10 @@ runCommand "my-package-test" {
|
|||
|
||||
A timeout (in seconds) for building the derivation. If the derivation takes longer than this time to build, it can fail due to breaking the timeout. However, all computers do not have the same computing power, hence some builders may decide to apply a multiplicative factor to this value. When filling this value in, try to keep it approximately consistent with other values already present in `nixpkgs`.
|
||||
|
||||
`meta` attributes are not stored in the instantiated derivation.
|
||||
Therefore, this setting may be lost when the package is used as a dependency.
|
||||
To be effective, it must be presented directly to an evaluation process that handles the `meta.timeout` attribute.
|
||||
|
||||
### `hydraPlatforms` {#var-meta-hydraPlatforms}
|
||||
|
||||
The list of Nix platform types for which the Hydra instance at `hydra.nixos.org` will build the package. (Hydra is the Nix-based continuous build system.) It defaults to the value of `meta.platforms`. Thus, the only reason to set `meta.hydraPlatforms` is if you want `hydra.nixos.org` to build the package on a subset of `meta.platforms`, or not at all, e.g.
|
||||
|
|
|
@ -23,6 +23,7 @@ let
|
|||
|
||||
# packaging
|
||||
customisation = callLibs ./customisation.nix;
|
||||
derivations = callLibs ./derivations.nix;
|
||||
maintainers = import ../maintainers/maintainer-list.nix;
|
||||
teams = callLibs ../maintainers/team-list.nix;
|
||||
meta = callLibs ./meta.nix;
|
||||
|
@ -108,6 +109,7 @@ let
|
|||
inherit (self.customisation) overrideDerivation makeOverridable
|
||||
callPackageWith callPackagesWith extendDerivation hydraJob
|
||||
makeScope makeScopeWithSplicing;
|
||||
inherit (self.derivations) lazyDerivation;
|
||||
inherit (self.meta) addMetaAttrs dontDistribute setName updateName
|
||||
appendToName mapDerivationAttrset setPrio lowPrio lowPrioSet hiPrio
|
||||
hiPrioSet getLicenseFromSpdxId getExe;
|
||||
|
|
101
lib/derivations.nix
Normal file
101
lib/derivations.nix
Normal file
|
@ -0,0 +1,101 @@
|
|||
{ lib }:
|
||||
|
||||
let
|
||||
inherit (lib) throwIfNot;
|
||||
in
|
||||
{
|
||||
/*
|
||||
Restrict a derivation to a predictable set of attribute names, so
|
||||
that the returned attrset is not strict in the actual derivation,
|
||||
saving a lot of computation when the derivation is non-trivial.
|
||||
|
||||
This is useful in situations where a derivation might only be used for its
|
||||
passthru attributes, improving evaluation performance.
|
||||
|
||||
The returned attribute set is lazy in `derivation`. Specifically, this
|
||||
means that the derivation will not be evaluated in at least the
|
||||
situations below.
|
||||
|
||||
For illustration and/or testing, we define derivation such that its
|
||||
evaluation is very noticable.
|
||||
|
||||
let derivation = throw "This won't be evaluated.";
|
||||
|
||||
In the following expressions, `derivation` will _not_ be evaluated:
|
||||
|
||||
(lazyDerivation { inherit derivation; }).type
|
||||
|
||||
attrNames (lazyDerivation { inherit derivation; })
|
||||
|
||||
(lazyDerivation { inherit derivation; } // { foo = true; }).foo
|
||||
|
||||
(lazyDerivation { inherit derivation; meta.foo = true; }).meta
|
||||
|
||||
In these expressions, it `derivation` _will_ be evaluated:
|
||||
|
||||
"${lazyDerivation { inherit derivation }}"
|
||||
|
||||
(lazyDerivation { inherit derivation }).outPath
|
||||
|
||||
(lazyDerivation { inherit derivation }).meta
|
||||
|
||||
And the following expressions are not valid, because the refer to
|
||||
implementation details and/or attributes that may not be present on
|
||||
some derivations:
|
||||
|
||||
(lazyDerivation { inherit derivation }).buildInputs
|
||||
|
||||
(lazyDerivation { inherit derivation }).passthru
|
||||
|
||||
(lazyDerivation { inherit derivation }).pythonPath
|
||||
|
||||
*/
|
||||
lazyDerivation =
|
||||
args@{
|
||||
# The derivation to be wrapped.
|
||||
derivation
|
||||
, # Optional meta attribute.
|
||||
#
|
||||
# While this function is primarily about derivations, it can improve
|
||||
# the `meta` package attribute, which is usually specified through
|
||||
# `mkDerivation`.
|
||||
meta ? null
|
||||
, # Optional extra values to add to the returned attrset.
|
||||
#
|
||||
# This can be used for adding package attributes, such as `tests`.
|
||||
passthru ? { }
|
||||
}:
|
||||
let
|
||||
# These checks are strict in `drv` and some `drv` attributes, but the
|
||||
# attrset spine returned by lazyDerivation does not depend on it.
|
||||
# Instead, the individual derivation attributes do depend on it.
|
||||
checked =
|
||||
throwIfNot (derivation.type or null == "derivation")
|
||||
"lazySimpleDerivation: input must be a derivation."
|
||||
throwIfNot
|
||||
(derivation.outputs == [ "out" ])
|
||||
# Supporting multiple outputs should be a matter of inheriting more attrs.
|
||||
"The derivation ${derivation.name or "<unknown>"} has multiple outputs. This is not supported by lazySimpleDerivation yet. Support could be added, and be useful as long as the set of outputs is known in advance, without evaluating the actual derivation."
|
||||
derivation;
|
||||
in
|
||||
{
|
||||
# Hardcoded `type`
|
||||
#
|
||||
# `lazyDerivation` requires its `derivation` argument to be a derivation,
|
||||
# so if it is not, that is a programming error by the caller and not
|
||||
# something that `lazyDerivation` consumers should be able to correct
|
||||
# for after the fact.
|
||||
# So, to improve laziness, we assume correctness here and check it only
|
||||
# when actual derivation values are accessed later.
|
||||
type = "derivation";
|
||||
|
||||
# A fixed set of derivation values, so that `lazyDerivation` can return
|
||||
# its attrset before evaluating `derivation`.
|
||||
# This must only list attributes that are available on _all_ derivations.
|
||||
inherit (checked) outputs out outPath outputName drvPath name system;
|
||||
|
||||
# The meta attribute can either be taken from the derivation, or if the
|
||||
# `lazyDerivation` caller knew a shortcut, be taken from there.
|
||||
meta = args.meta or checked.meta;
|
||||
} // passthru;
|
||||
}
|
|
@ -440,13 +440,14 @@ rec {
|
|||
config = addFreeformType (addMeta (m.config or {}));
|
||||
}
|
||||
else
|
||||
# shorthand syntax
|
||||
lib.throwIfNot (isAttrs m) "module ${file} (${key}) does not look like a module."
|
||||
{ _file = toString m._file or file;
|
||||
key = toString m.key or key;
|
||||
disabledModules = m.disabledModules or [];
|
||||
imports = m.require or [] ++ m.imports or [];
|
||||
options = {};
|
||||
config = addFreeformType (addMeta (removeAttrs m ["_file" "key" "disabledModules" "require" "imports" "freeformType"]));
|
||||
config = addFreeformType (removeAttrs m ["_file" "key" "disabledModules" "require" "imports" "freeformType"]);
|
||||
};
|
||||
|
||||
applyModuleArgsIfFunction = key: f: args@{ config, options, lib, ... }: if isFunction f then
|
||||
|
|
|
@ -1207,6 +1207,59 @@ runTests {
|
|||
expected = true;
|
||||
};
|
||||
|
||||
# lazyDerivation
|
||||
|
||||
testLazyDerivationIsLazyInDerivationForAttrNames = {
|
||||
expr = attrNames (lazyDerivation {
|
||||
derivation = throw "not lazy enough";
|
||||
});
|
||||
# It's ok to add attribute names here when lazyDerivation is improved
|
||||
# in accordance with its inline comments.
|
||||
expected = [ "drvPath" "meta" "name" "out" "outPath" "outputName" "outputs" "system" "type" ];
|
||||
};
|
||||
|
||||
testLazyDerivationIsLazyInDerivationForPassthruAttr = {
|
||||
expr = (lazyDerivation {
|
||||
derivation = throw "not lazy enough";
|
||||
passthru.tests = "whatever is in tests";
|
||||
}).tests;
|
||||
expected = "whatever is in tests";
|
||||
};
|
||||
|
||||
testLazyDerivationIsLazyInDerivationForPassthruAttr2 = {
|
||||
# passthru.tests is not a special case. It works for any attr.
|
||||
expr = (lazyDerivation {
|
||||
derivation = throw "not lazy enough";
|
||||
passthru.foo = "whatever is in foo";
|
||||
}).foo;
|
||||
expected = "whatever is in foo";
|
||||
};
|
||||
|
||||
testLazyDerivationIsLazyInDerivationForMeta = {
|
||||
expr = (lazyDerivation {
|
||||
derivation = throw "not lazy enough";
|
||||
meta = "whatever is in meta";
|
||||
}).meta;
|
||||
expected = "whatever is in meta";
|
||||
};
|
||||
|
||||
testLazyDerivationReturnsDerivationAttrs = let
|
||||
derivation = {
|
||||
type = "derivation";
|
||||
outputs = ["out"];
|
||||
out = "test out";
|
||||
outPath = "test outPath";
|
||||
outputName = "out";
|
||||
drvPath = "test drvPath";
|
||||
name = "test name";
|
||||
system = "test system";
|
||||
meta = "test meta";
|
||||
};
|
||||
in {
|
||||
expr = lazyDerivation { inherit derivation; };
|
||||
expected = derivation;
|
||||
};
|
||||
|
||||
testTypeDescriptionInt = {
|
||||
expr = (with types; int).description;
|
||||
expected = "signed integer";
|
||||
|
|
|
@ -58,6 +58,9 @@ checkConfigError() {
|
|||
fi
|
||||
}
|
||||
|
||||
# Shorthand meta attribute does not duplicate the config
|
||||
checkConfigOutput '^"one two"$' config.result ./shorthand-meta.nix
|
||||
|
||||
# Check boolean option.
|
||||
checkConfigOutput '^false$' config.enable ./declare-enable.nix
|
||||
checkConfigError 'The option .* does not exist. Definition values:\n\s*- In .*: true' config.enable ./define-enable.nix
|
||||
|
|
19
lib/tests/modules/shorthand-meta.nix
Normal file
19
lib/tests/modules/shorthand-meta.nix
Normal file
|
@ -0,0 +1,19 @@
|
|||
{ lib, ... }:
|
||||
let
|
||||
inherit (lib) types mkOption;
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
({ config, ... }: {
|
||||
options = {
|
||||
meta.foo = mkOption {
|
||||
type = types.listOf types.str;
|
||||
};
|
||||
result = mkOption { default = lib.concatStringsSep " " config.meta.foo; };
|
||||
};
|
||||
})
|
||||
{
|
||||
meta.foo = [ "one" "two" ];
|
||||
}
|
||||
];
|
||||
}
|
|
@ -13,6 +13,8 @@
|
|||
with pkgs;
|
||||
|
||||
let
|
||||
inherit (lib) hasPrefix removePrefix;
|
||||
|
||||
lib = pkgs.lib;
|
||||
|
||||
docbook_xsl_ns = pkgs.docbook-xsl-ns.override {
|
||||
|
@ -36,6 +38,33 @@ let
|
|||
};
|
||||
};
|
||||
|
||||
nixos-lib = import ../../lib { };
|
||||
|
||||
testOptionsDoc = let
|
||||
eval = nixos-lib.evalTest {
|
||||
# Avoid evaluating a NixOS config prototype.
|
||||
config.node.type = lib.types.deferredModule;
|
||||
options._module.args = lib.mkOption { internal = true; };
|
||||
};
|
||||
in buildPackages.nixosOptionsDoc {
|
||||
inherit (eval) options;
|
||||
inherit (revision);
|
||||
transformOptions = opt: opt // {
|
||||
# Clean up declaration sites to not refer to the NixOS source tree.
|
||||
declarations =
|
||||
map
|
||||
(decl:
|
||||
if hasPrefix (toString ../../..) (toString decl)
|
||||
then
|
||||
let subpath = removePrefix "/" (removePrefix (toString ../../..) (toString decl));
|
||||
in { url = "https://github.com/NixOS/nixpkgs/blob/master/${subpath}"; name = subpath; }
|
||||
else decl)
|
||||
opt.declarations;
|
||||
};
|
||||
documentType = "none";
|
||||
variablelistId = "test-options-list";
|
||||
};
|
||||
|
||||
sources = lib.sourceFilesBySuffices ./. [".xml"];
|
||||
|
||||
modulesDoc = builtins.toFile "modules.xml" ''
|
||||
|
@ -50,6 +79,7 @@ let
|
|||
mkdir $out
|
||||
ln -s ${modulesDoc} $out/modules.xml
|
||||
ln -s ${optionsDoc.optionsDocBook} $out/options-db.xml
|
||||
ln -s ${testOptionsDoc.optionsDocBook} $out/test-options-db.xml
|
||||
printf "%s" "${version}" > $out/version
|
||||
'';
|
||||
|
||||
|
|
|
@ -24,6 +24,8 @@ back into the test driver command line upon its completion. This allows
|
|||
you to inspect the state of the VMs after the test (e.g. to debug the
|
||||
test script).
|
||||
|
||||
## Reuse VM state {#sec-nixos-test-reuse-vm-state}
|
||||
|
||||
You can re-use the VM states coming from a previous run by setting the
|
||||
`--keep-vm-state` flag.
|
||||
|
||||
|
@ -33,3 +35,15 @@ $ ./result/bin/nixos-test-driver --keep-vm-state
|
|||
|
||||
The machine state is stored in the `$TMPDIR/vm-state-machinename`
|
||||
directory.
|
||||
|
||||
## Interactive-only test configuration {#sec-nixos-test-interactive-configuration}
|
||||
|
||||
The `.driverInteractive` attribute combines the regular test configuration with
|
||||
definitions from the [`interactive` submodule](#opt-interactive). This gives you
|
||||
a more usable, graphical, but slightly different configuration.
|
||||
|
||||
You can add your own interactive-only test configuration by adding extra
|
||||
configuration to the [`interactive` submodule](#opt-interactive).
|
||||
|
||||
To interactively run only the regular configuration, build the `<test>.driver` attribute
|
||||
instead, and call it with the flag `result/bin/nixos-test-driver --interactive`.
|
||||
|
|
|
@ -2,22 +2,11 @@
|
|||
|
||||
You can run tests using `nix-build`. For example, to run the test
|
||||
[`login.nix`](https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/login.nix),
|
||||
you just do:
|
||||
you do:
|
||||
|
||||
```ShellSession
|
||||
$ nix-build '<nixpkgs/nixos/tests/login.nix>'
|
||||
```
|
||||
|
||||
or, if you don't want to rely on `NIX_PATH`:
|
||||
|
||||
```ShellSession
|
||||
$ cd /my/nixpkgs/nixos/tests
|
||||
$ nix-build login.nix
|
||||
…
|
||||
running the VM test script
|
||||
machine: QEMU running (pid 8841)
|
||||
…
|
||||
6 out of 6 tests succeeded
|
||||
$ cd /my/git/clone/of/nixpkgs
|
||||
$ nix-build -A nixosTests.login
|
||||
```
|
||||
|
||||
After building/downloading all required dependencies, this will perform
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
# Writing Tests {#sec-writing-nixos-tests}
|
||||
|
||||
A NixOS test is a Nix expression that has the following structure:
|
||||
A NixOS test is a module that has the following structure:
|
||||
|
||||
```nix
|
||||
import ./make-test-python.nix {
|
||||
{
|
||||
|
||||
# One or more machines:
|
||||
nodes =
|
||||
|
@ -21,10 +21,13 @@ import ./make-test-python.nix {
|
|||
}
|
||||
```
|
||||
|
||||
The attribute `testScript` is a bit of Python code that executes the
|
||||
We refer to the whole test above as a test module, whereas the values
|
||||
in [`nodes.<name>`](#opt-nodes) are NixOS modules themselves.
|
||||
|
||||
The option [`testScript`](#opt-testScript) is a piece of Python code that executes the
|
||||
test (described below). During the test, it will start one or more
|
||||
virtual machines, the configuration of which is described by
|
||||
the attribute `nodes`.
|
||||
the option [`nodes`](#opt-nodes).
|
||||
|
||||
An example of a single-node test is
|
||||
[`login.nix`](https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/login.nix).
|
||||
|
@ -34,7 +37,54 @@ when switching between consoles, and so on. An interesting multi-node test is
|
|||
[`nfs/simple.nix`](https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/nfs/simple.nix).
|
||||
It uses two client nodes to test correct locking across server crashes.
|
||||
|
||||
There are a few special NixOS configuration options for test VMs:
|
||||
## Calling a test {#sec-calling-nixos-tests}
|
||||
|
||||
Tests are invoked differently depending on whether the test is part of NixOS or lives in a different project.
|
||||
|
||||
### Testing within NixOS {#sec-call-nixos-test-in-nixos}
|
||||
|
||||
Tests that are part of NixOS are added to [`nixos/tests/all-tests.nix`](https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/all-tests.nix).
|
||||
|
||||
```nix
|
||||
hostname = runTest ./hostname.nix;
|
||||
```
|
||||
|
||||
Overrides can be added by defining an anonymous module in `all-tests.nix`.
|
||||
|
||||
```nix
|
||||
hostname = runTest {
|
||||
imports = [ ./hostname.nix ];
|
||||
defaults.networking.firewall.enable = false;
|
||||
};
|
||||
```
|
||||
|
||||
You can run a test with attribute name `hostname` in `nixos/tests/all-tests.nix` by invoking:
|
||||
|
||||
```shell
|
||||
cd /my/git/clone/of/nixpkgs
|
||||
nix-build -A nixosTests.hostname
|
||||
```
|
||||
|
||||
### Testing outside the NixOS project {#sec-call-nixos-test-outside-nixos}
|
||||
|
||||
Outside the `nixpkgs` repository, you can instantiate the test by first importing the NixOS library,
|
||||
|
||||
```nix
|
||||
let nixos-lib = import (nixpkgs + "/nixos/lib") { };
|
||||
in
|
||||
|
||||
nixos-lib.runTest {
|
||||
imports = [ ./test.nix ];
|
||||
hostPkgs = pkgs; # the Nixpkgs package set used outside the VMs
|
||||
defaults.services.foo.package = mypkg;
|
||||
}
|
||||
```
|
||||
|
||||
`runTest` returns a derivation that runs the test.
|
||||
|
||||
## Configuring the nodes {#sec-nixos-test-nodes}
|
||||
|
||||
There are a few special NixOS options for test VMs:
|
||||
|
||||
`virtualisation.memorySize`
|
||||
|
||||
|
@ -121,7 +171,7 @@ The following methods are available on machine objects:
|
|||
least one will be returned.
|
||||
|
||||
::: {.note}
|
||||
This requires passing `enableOCR` to the test attribute set.
|
||||
This requires [`enableOCR`](#opt-enableOCR) to be set to `true`.
|
||||
:::
|
||||
|
||||
`get_screen_text`
|
||||
|
@ -130,7 +180,7 @@ The following methods are available on machine objects:
|
|||
machine\'s screen using optical character recognition.
|
||||
|
||||
::: {.note}
|
||||
This requires passing `enableOCR` to the test attribute set.
|
||||
This requires [`enableOCR`](#opt-enableOCR) to be set to `true`.
|
||||
:::
|
||||
|
||||
`send_monitor_command`
|
||||
|
@ -241,7 +291,7 @@ The following methods are available on machine objects:
|
|||
`get_screen_text` and `get_screen_text_variants`).
|
||||
|
||||
::: {.note}
|
||||
This requires passing `enableOCR` to the test attribute set.
|
||||
This requires [`enableOCR`](#opt-enableOCR) to be set to `true`.
|
||||
:::
|
||||
|
||||
`wait_for_console_text`
|
||||
|
@ -304,7 +354,7 @@ For faster dev cycles it\'s also possible to disable the code-linters
|
|||
(this shouldn\'t be commited though):
|
||||
|
||||
```nix
|
||||
import ./make-test-python.nix {
|
||||
{
|
||||
skipLint = true;
|
||||
nodes.machine =
|
||||
{ config, pkgs, ... }:
|
||||
|
@ -336,7 +386,7 @@ Similarly, the type checking of test scripts can be disabled in the following
|
|||
way:
|
||||
|
||||
```nix
|
||||
import ./make-test-python.nix {
|
||||
{
|
||||
skipTypeCheck = true;
|
||||
nodes.machine =
|
||||
{ config, pkgs, ... }:
|
||||
|
@ -400,7 +450,6 @@ added using the parameter `extraPythonPackages`. For example, you could add
|
|||
`numpy` like this:
|
||||
|
||||
```nix
|
||||
import ./make-test-python.nix
|
||||
{
|
||||
extraPythonPackages = p: [ p.numpy ];
|
||||
|
||||
|
@ -417,3 +466,11 @@ import ./make-test-python.nix
|
|||
```
|
||||
|
||||
In that case, `numpy` is chosen from the generic `python3Packages`.
|
||||
|
||||
## Test Options Reference {#sec-test-options-reference}
|
||||
|
||||
The following options can be used when writing tests.
|
||||
|
||||
```{=docbook}
|
||||
<xi:include href="../../generated/test-options-db.xml" xpointer="test-options-list"/>
|
||||
```
|
||||
|
|
|
@ -25,15 +25,40 @@ $ ./result/bin/nixos-test-driver
|
|||
completion. This allows you to inspect the state of the VMs after
|
||||
the test (e.g. to debug the test script).
|
||||
</para>
|
||||
<para>
|
||||
You can re-use the VM states coming from a previous run by setting
|
||||
the <literal>--keep-vm-state</literal> flag.
|
||||
</para>
|
||||
<programlisting>
|
||||
<section xml:id="sec-nixos-test-reuse-vm-state">
|
||||
<title>Reuse VM state</title>
|
||||
<para>
|
||||
You can re-use the VM states coming from a previous run by setting
|
||||
the <literal>--keep-vm-state</literal> flag.
|
||||
</para>
|
||||
<programlisting>
|
||||
$ ./result/bin/nixos-test-driver --keep-vm-state
|
||||
</programlisting>
|
||||
<para>
|
||||
The machine state is stored in the
|
||||
<literal>$TMPDIR/vm-state-machinename</literal> directory.
|
||||
</para>
|
||||
<para>
|
||||
The machine state is stored in the
|
||||
<literal>$TMPDIR/vm-state-machinename</literal> directory.
|
||||
</para>
|
||||
</section>
|
||||
<section xml:id="sec-nixos-test-interactive-configuration">
|
||||
<title>Interactive-only test configuration</title>
|
||||
<para>
|
||||
The <literal>.driverInteractive</literal> attribute combines the
|
||||
regular test configuration with definitions from the
|
||||
<link linkend="opt-interactive"><literal>interactive</literal>
|
||||
submodule</link>. This gives you a more usable, graphical, but
|
||||
slightly different configuration.
|
||||
</para>
|
||||
<para>
|
||||
You can add your own interactive-only test configuration by adding
|
||||
extra configuration to the
|
||||
<link linkend="opt-interactive"><literal>interactive</literal>
|
||||
submodule</link>.
|
||||
</para>
|
||||
<para>
|
||||
To interactively run only the regular configuration, build the
|
||||
<literal><test>.driver</literal> attribute instead, and call
|
||||
it with the flag
|
||||
<literal>result/bin/nixos-test-driver --interactive</literal>.
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
|
|
|
@ -4,22 +4,11 @@
|
|||
You can run tests using <literal>nix-build</literal>. For example,
|
||||
to run the test
|
||||
<link xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/login.nix"><literal>login.nix</literal></link>,
|
||||
you just do:
|
||||
you do:
|
||||
</para>
|
||||
<programlisting>
|
||||
$ nix-build '<nixpkgs/nixos/tests/login.nix>'
|
||||
</programlisting>
|
||||
<para>
|
||||
or, if you don’t want to rely on <literal>NIX_PATH</literal>:
|
||||
</para>
|
||||
<programlisting>
|
||||
$ cd /my/nixpkgs/nixos/tests
|
||||
$ nix-build login.nix
|
||||
…
|
||||
running the VM test script
|
||||
machine: QEMU running (pid 8841)
|
||||
…
|
||||
6 out of 6 tests succeeded
|
||||
$ cd /my/git/clone/of/nixpkgs
|
||||
$ nix-build -A nixosTests.login
|
||||
</programlisting>
|
||||
<para>
|
||||
After building/downloading all required dependencies, this will
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-writing-nixos-tests">
|
||||
<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xi="http://www.w3.org/2001/XInclude" xml:id="sec-writing-nixos-tests">
|
||||
<title>Writing Tests</title>
|
||||
<para>
|
||||
A NixOS test is a Nix expression that has the following structure:
|
||||
A NixOS test is a module that has the following structure:
|
||||
</para>
|
||||
<programlisting language="bash">
|
||||
import ./make-test-python.nix {
|
||||
{
|
||||
|
||||
# One or more machines:
|
||||
nodes =
|
||||
|
@ -22,10 +22,18 @@ import ./make-test-python.nix {
|
|||
}
|
||||
</programlisting>
|
||||
<para>
|
||||
The attribute <literal>testScript</literal> is a bit of Python code
|
||||
that executes the test (described below). During the test, it will
|
||||
start one or more virtual machines, the configuration of which is
|
||||
described by the attribute <literal>nodes</literal>.
|
||||
We refer to the whole test above as a test module, whereas the
|
||||
values in
|
||||
<link linkend="opt-nodes"><literal>nodes.<name></literal></link>
|
||||
are NixOS modules themselves.
|
||||
</para>
|
||||
<para>
|
||||
The option
|
||||
<link linkend="opt-testScript"><literal>testScript</literal></link>
|
||||
is a piece of Python code that executes the test (described below).
|
||||
During the test, it will start one or more virtual machines, the
|
||||
configuration of which is described by the option
|
||||
<link linkend="opt-nodes"><literal>nodes</literal></link>.
|
||||
</para>
|
||||
<para>
|
||||
An example of a single-node test is
|
||||
|
@ -38,78 +46,138 @@ import ./make-test-python.nix {
|
|||
It uses two client nodes to test correct locking across server
|
||||
crashes.
|
||||
</para>
|
||||
<para>
|
||||
There are a few special NixOS configuration options for test VMs:
|
||||
</para>
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term>
|
||||
<literal>virtualisation.memorySize</literal>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
The memory of the VM in megabytes.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>
|
||||
<literal>virtualisation.vlans</literal>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
The virtual networks to which the VM is connected. See
|
||||
<link xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/nat.nix"><literal>nat.nix</literal></link>
|
||||
for an example.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>
|
||||
<literal>virtualisation.writableStore</literal>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
By default, the Nix store in the VM is not writable. If you
|
||||
enable this option, a writable union file system is mounted on
|
||||
top of the Nix store to make it appear writable. This is
|
||||
necessary for tests that run Nix operations that modify the
|
||||
store.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
<para>
|
||||
For more options, see the module
|
||||
<link xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/virtualisation/qemu-vm.nix"><literal>qemu-vm.nix</literal></link>.
|
||||
</para>
|
||||
<para>
|
||||
The test script is a sequence of Python statements that perform
|
||||
various actions, such as starting VMs, executing commands in the
|
||||
VMs, and so on. Each virtual machine is represented as an object
|
||||
stored in the variable <literal>name</literal> if this is also the
|
||||
identifier of the machine in the declarative config. If you
|
||||
specified a node <literal>nodes.machine</literal>, the following
|
||||
example starts the machine, waits until it has finished booting,
|
||||
then executes a command and checks that the output is more-or-less
|
||||
correct:
|
||||
</para>
|
||||
<programlisting language="python">
|
||||
<section xml:id="sec-calling-nixos-tests">
|
||||
<title>Calling a test</title>
|
||||
<para>
|
||||
Tests are invoked differently depending on whether the test is
|
||||
part of NixOS or lives in a different project.
|
||||
</para>
|
||||
<section xml:id="sec-call-nixos-test-in-nixos">
|
||||
<title>Testing within NixOS</title>
|
||||
<para>
|
||||
Tests that are part of NixOS are added to
|
||||
<link xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/all-tests.nix"><literal>nixos/tests/all-tests.nix</literal></link>.
|
||||
</para>
|
||||
<programlisting language="bash">
|
||||
hostname = runTest ./hostname.nix;
|
||||
</programlisting>
|
||||
<para>
|
||||
Overrides can be added by defining an anonymous module in
|
||||
<literal>all-tests.nix</literal>.
|
||||
</para>
|
||||
<programlisting language="bash">
|
||||
hostname = runTest {
|
||||
imports = [ ./hostname.nix ];
|
||||
defaults.networking.firewall.enable = false;
|
||||
};
|
||||
</programlisting>
|
||||
<para>
|
||||
You can run a test with attribute name
|
||||
<literal>hostname</literal> in
|
||||
<literal>nixos/tests/all-tests.nix</literal> by invoking:
|
||||
</para>
|
||||
<programlisting>
|
||||
cd /my/git/clone/of/nixpkgs
|
||||
nix-build -A nixosTests.hostname
|
||||
</programlisting>
|
||||
</section>
|
||||
<section xml:id="sec-call-nixos-test-outside-nixos">
|
||||
<title>Testing outside the NixOS project</title>
|
||||
<para>
|
||||
Outside the <literal>nixpkgs</literal> repository, you can
|
||||
instantiate the test by first importing the NixOS library,
|
||||
</para>
|
||||
<programlisting language="bash">
|
||||
let nixos-lib = import (nixpkgs + "/nixos/lib") { };
|
||||
in
|
||||
|
||||
nixos-lib.runTest {
|
||||
imports = [ ./test.nix ];
|
||||
hostPkgs = pkgs; # the Nixpkgs package set used outside the VMs
|
||||
defaults.services.foo.package = mypkg;
|
||||
}
|
||||
</programlisting>
|
||||
<para>
|
||||
<literal>runTest</literal> returns a derivation that runs the
|
||||
test.
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
<section xml:id="sec-nixos-test-nodes">
|
||||
<title>Configuring the nodes</title>
|
||||
<para>
|
||||
There are a few special NixOS options for test VMs:
|
||||
</para>
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term>
|
||||
<literal>virtualisation.memorySize</literal>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
The memory of the VM in megabytes.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>
|
||||
<literal>virtualisation.vlans</literal>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
The virtual networks to which the VM is connected. See
|
||||
<link xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/nat.nix"><literal>nat.nix</literal></link>
|
||||
for an example.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>
|
||||
<literal>virtualisation.writableStore</literal>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
By default, the Nix store in the VM is not writable. If you
|
||||
enable this option, a writable union file system is mounted
|
||||
on top of the Nix store to make it appear writable. This is
|
||||
necessary for tests that run Nix operations that modify the
|
||||
store.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
<para>
|
||||
For more options, see the module
|
||||
<link xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/virtualisation/qemu-vm.nix"><literal>qemu-vm.nix</literal></link>.
|
||||
</para>
|
||||
<para>
|
||||
The test script is a sequence of Python statements that perform
|
||||
various actions, such as starting VMs, executing commands in the
|
||||
VMs, and so on. Each virtual machine is represented as an object
|
||||
stored in the variable <literal>name</literal> if this is also the
|
||||
identifier of the machine in the declarative config. If you
|
||||
specified a node <literal>nodes.machine</literal>, the following
|
||||
example starts the machine, waits until it has finished booting,
|
||||
then executes a command and checks that the output is more-or-less
|
||||
correct:
|
||||
</para>
|
||||
<programlisting language="python">
|
||||
machine.start()
|
||||
machine.wait_for_unit("default.target")
|
||||
if not "Linux" in machine.succeed("uname"):
|
||||
raise Exception("Wrong OS")
|
||||
</programlisting>
|
||||
<para>
|
||||
The first line is technically unnecessary; machines are implicitly
|
||||
started when you first execute an action on them (such as
|
||||
<literal>wait_for_unit</literal> or <literal>succeed</literal>). If
|
||||
you have multiple machines, you can speed up the test by starting
|
||||
them in parallel:
|
||||
</para>
|
||||
<programlisting language="python">
|
||||
<para>
|
||||
The first line is technically unnecessary; machines are implicitly
|
||||
started when you first execute an action on them (such as
|
||||
<literal>wait_for_unit</literal> or <literal>succeed</literal>).
|
||||
If you have multiple machines, you can speed up the test by
|
||||
starting them in parallel:
|
||||
</para>
|
||||
<programlisting language="python">
|
||||
start_all()
|
||||
</programlisting>
|
||||
</section>
|
||||
<section xml:id="ssec-machine-objects">
|
||||
<title>Machine objects</title>
|
||||
<para>
|
||||
|
@ -194,8 +262,9 @@ start_all()
|
|||
</para>
|
||||
<note>
|
||||
<para>
|
||||
This requires passing <literal>enableOCR</literal> to the
|
||||
test attribute set.
|
||||
This requires
|
||||
<link linkend="opt-enableOCR"><literal>enableOCR</literal></link>
|
||||
to be set to <literal>true</literal>.
|
||||
</para>
|
||||
</note>
|
||||
</listitem>
|
||||
|
@ -211,8 +280,9 @@ start_all()
|
|||
</para>
|
||||
<note>
|
||||
<para>
|
||||
This requires passing <literal>enableOCR</literal> to the
|
||||
test attribute set.
|
||||
This requires
|
||||
<link linkend="opt-enableOCR"><literal>enableOCR</literal></link>
|
||||
to be set to <literal>true</literal>.
|
||||
</para>
|
||||
</note>
|
||||
</listitem>
|
||||
|
@ -451,8 +521,9 @@ start_all()
|
|||
</para>
|
||||
<note>
|
||||
<para>
|
||||
This requires passing <literal>enableOCR</literal> to the
|
||||
test attribute set.
|
||||
This requires
|
||||
<link linkend="opt-enableOCR"><literal>enableOCR</literal></link>
|
||||
to be set to <literal>true</literal>.
|
||||
</para>
|
||||
</note>
|
||||
</listitem>
|
||||
|
@ -563,7 +634,7 @@ machine.wait_for_unit("xautolock.service", "x-session-user")
|
|||
code-linters (this shouldn't be commited though):
|
||||
</para>
|
||||
<programlisting language="bash">
|
||||
import ./make-test-python.nix {
|
||||
{
|
||||
skipLint = true;
|
||||
nodes.machine =
|
||||
{ config, pkgs, ... }:
|
||||
|
@ -595,7 +666,7 @@ import ./make-test-python.nix {
|
|||
the following way:
|
||||
</para>
|
||||
<programlisting language="bash">
|
||||
import ./make-test-python.nix {
|
||||
{
|
||||
skipTypeCheck = true;
|
||||
nodes.machine =
|
||||
{ config, pkgs, ... }:
|
||||
|
@ -669,7 +740,6 @@ def foo_running():
|
|||
<literal>numpy</literal> like this:
|
||||
</para>
|
||||
<programlisting language="bash">
|
||||
import ./make-test-python.nix
|
||||
{
|
||||
extraPythonPackages = p: [ p.numpy ];
|
||||
|
||||
|
@ -689,4 +759,11 @@ import ./make-test-python.nix
|
|||
<literal>python3Packages</literal>.
|
||||
</para>
|
||||
</section>
|
||||
<section xml:id="sec-test-options-reference">
|
||||
<title>Test Options Reference</title>
|
||||
<para>
|
||||
The following options can be used when writing tests.
|
||||
</para>
|
||||
<xi:include href="../../generated/test-options-db.xml" xpointer="test-options-list"/>
|
||||
</section>
|
||||
</section>
|
||||
|
|
|
@ -1,113 +0,0 @@
|
|||
{ system
|
||||
, # Use a minimal kernel?
|
||||
minimal ? false
|
||||
, # Ignored
|
||||
config ? null
|
||||
, # Nixpkgs, for qemu, lib and more
|
||||
pkgs, lib
|
||||
, # !!! See comment about args in lib/modules.nix
|
||||
specialArgs ? {}
|
||||
, # NixOS configuration to add to the VMs
|
||||
extraConfigurations ? []
|
||||
}:
|
||||
|
||||
with lib;
|
||||
|
||||
rec {
|
||||
|
||||
inherit pkgs;
|
||||
|
||||
# Build a virtual network from an attribute set `{ machine1 =
|
||||
# config1; ... machineN = configN; }', where `machineX' is the
|
||||
# hostname and `configX' is a NixOS system configuration. Each
|
||||
# machine is given an arbitrary IP address in the virtual network.
|
||||
buildVirtualNetwork =
|
||||
nodes: let nodesOut = mapAttrs (n: buildVM nodesOut) (assignIPAddresses nodes); in nodesOut;
|
||||
|
||||
|
||||
buildVM =
|
||||
nodes: configurations:
|
||||
|
||||
import ./eval-config.nix {
|
||||
inherit system specialArgs;
|
||||
modules = configurations ++ extraConfigurations;
|
||||
baseModules = (import ../modules/module-list.nix) ++
|
||||
[ ../modules/virtualisation/qemu-vm.nix
|
||||
../modules/testing/test-instrumentation.nix # !!! should only get added for automated test runs
|
||||
{ key = "no-manual"; documentation.nixos.enable = false; }
|
||||
{ key = "no-revision";
|
||||
# Make the revision metadata constant, in order to avoid needless retesting.
|
||||
# The human version (e.g. 21.05-pre) is left as is, because it is useful
|
||||
# for external modules that test with e.g. testers.nixosTest and rely on that
|
||||
# version number.
|
||||
config.system.nixos.revision = mkForce "constant-nixos-revision";
|
||||
}
|
||||
{ key = "nodes"; _module.args.nodes = nodes; }
|
||||
] ++ optional minimal ../modules/testing/minimal-kernel.nix;
|
||||
};
|
||||
|
||||
|
||||
# Given an attribute set { machine1 = config1; ... machineN =
|
||||
# configN; }, sequentially assign IP addresses in the 192.168.1.0/24
|
||||
# range to each machine, and set the hostname to the attribute name.
|
||||
assignIPAddresses = nodes:
|
||||
|
||||
let
|
||||
|
||||
machines = attrNames nodes;
|
||||
|
||||
machinesNumbered = zipLists machines (range 1 254);
|
||||
|
||||
nodes_ = forEach machinesNumbered (m: nameValuePair m.fst
|
||||
[ ( { config, nodes, ... }:
|
||||
let
|
||||
interfacesNumbered = zipLists config.virtualisation.vlans (range 1 255);
|
||||
interfaces = forEach interfacesNumbered ({ fst, snd }:
|
||||
nameValuePair "eth${toString snd}" { ipv4.addresses =
|
||||
[ { address = "192.168.${toString fst}.${toString m.snd}";
|
||||
prefixLength = 24;
|
||||
} ];
|
||||
});
|
||||
|
||||
networkConfig =
|
||||
{ networking.hostName = mkDefault m.fst;
|
||||
|
||||
networking.interfaces = listToAttrs interfaces;
|
||||
|
||||
networking.primaryIPAddress =
|
||||
optionalString (interfaces != []) (head (head interfaces).value.ipv4.addresses).address;
|
||||
|
||||
# Put the IP addresses of all VMs in this machine's
|
||||
# /etc/hosts file. If a machine has multiple
|
||||
# interfaces, use the IP address corresponding to
|
||||
# the first interface (i.e. the first network in its
|
||||
# virtualisation.vlans option).
|
||||
networking.extraHosts = flip concatMapStrings machines
|
||||
(m': let config = (getAttr m' nodes).config; in
|
||||
optionalString (config.networking.primaryIPAddress != "")
|
||||
("${config.networking.primaryIPAddress} " +
|
||||
optionalString (config.networking.domain != null)
|
||||
"${config.networking.hostName}.${config.networking.domain} " +
|
||||
"${config.networking.hostName}\n"));
|
||||
|
||||
virtualisation.qemu.options =
|
||||
let qemu-common = import ../lib/qemu-common.nix { inherit lib pkgs; };
|
||||
in flip concatMap interfacesNumbered
|
||||
({ fst, snd }: qemu-common.qemuNICFlags snd fst m.snd);
|
||||
};
|
||||
|
||||
in
|
||||
{ key = "ip-address";
|
||||
config = networkConfig // {
|
||||
# Expose the networkConfig items for tests like nixops
|
||||
# that need to recreate the network config.
|
||||
system.build.networkConfig = networkConfig;
|
||||
};
|
||||
}
|
||||
)
|
||||
(getAttr m.fst nodes)
|
||||
] );
|
||||
|
||||
in listToAttrs nodes_;
|
||||
|
||||
}
|
|
@ -21,6 +21,8 @@ let
|
|||
seqAttrsIf = cond: a: lib.mapAttrs (_: v: seqIf cond a v);
|
||||
|
||||
eval-config-minimal = import ./eval-config-minimal.nix { inherit lib; };
|
||||
|
||||
testing-lib = import ./testing/default.nix { inherit lib; };
|
||||
in
|
||||
/*
|
||||
This attribute set appears as lib.nixos in the flake, or can be imported
|
||||
|
@ -30,4 +32,10 @@ in
|
|||
inherit (seqAttrsIf (!featureFlags?minimalModules) minimalModulesWarning eval-config-minimal)
|
||||
evalModules
|
||||
;
|
||||
|
||||
inherit (testing-lib)
|
||||
evalTest
|
||||
runTest
|
||||
;
|
||||
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@ evalConfigArgs@
|
|||
# be set modularly anyway.
|
||||
pkgs ? null
|
||||
, # !!! what do we gain by making this configurable?
|
||||
# we can add modules that are included in specialisations, regardless
|
||||
# of inheritParentConfig.
|
||||
baseModules ? import ../modules/module-list.nix
|
||||
, # !!! See comment about args in lib/modules.nix
|
||||
extraArgs ? {}
|
||||
|
|
|
@ -12,159 +12,22 @@
|
|||
|
||||
with pkgs;
|
||||
|
||||
let
|
||||
nixos-lib = import ./default.nix { inherit (pkgs) lib; };
|
||||
in
|
||||
|
||||
rec {
|
||||
|
||||
inherit pkgs;
|
||||
|
||||
# Run an automated test suite in the given virtual network.
|
||||
runTests = { driver, driverInteractive, pos }:
|
||||
stdenv.mkDerivation {
|
||||
name = "vm-test-run-${driver.testName}";
|
||||
evalTest = module: nixos-lib.evalTest { imports = [ extraTestModule module ]; };
|
||||
runTest = module: nixos-lib.runTest { imports = [ extraTestModule module ]; };
|
||||
|
||||
requiredSystemFeatures = [ "kvm" "nixos-test" ];
|
||||
|
||||
buildCommand =
|
||||
''
|
||||
mkdir -p $out
|
||||
|
||||
# effectively mute the XMLLogger
|
||||
export LOGFILE=/dev/null
|
||||
|
||||
${driver}/bin/nixos-test-driver -o $out
|
||||
'';
|
||||
|
||||
passthru = driver.passthru // {
|
||||
inherit driver driverInteractive;
|
||||
};
|
||||
|
||||
inherit pos; # for better debugging
|
||||
extraTestModule = {
|
||||
config = {
|
||||
hostPkgs = pkgs;
|
||||
};
|
||||
|
||||
# Generate convenience wrappers for running the test driver
|
||||
# has vlans, vms and test script defaulted through env variables
|
||||
# also instantiates test script with nodes, if it's a function (contract)
|
||||
setupDriverForTest = {
|
||||
testScript
|
||||
, testName
|
||||
, nodes
|
||||
, qemu_pkg ? pkgs.qemu_test
|
||||
, enableOCR ? false
|
||||
, skipLint ? false
|
||||
, skipTypeCheck ? false
|
||||
, passthru ? {}
|
||||
, interactive ? false
|
||||
, extraPythonPackages ? (_ :[])
|
||||
}:
|
||||
let
|
||||
# Reifies and correctly wraps the python test driver for
|
||||
# the respective qemu version and with or without ocr support
|
||||
testDriver = pkgs.callPackage ./test-driver {
|
||||
inherit enableOCR extraPythonPackages;
|
||||
qemu_pkg = qemu_test;
|
||||
imagemagick_light = imagemagick_light.override { inherit libtiff; };
|
||||
tesseract4 = tesseract4.override { enableLanguages = [ "eng" ]; };
|
||||
};
|
||||
|
||||
|
||||
testDriverName =
|
||||
let
|
||||
# A standard store path to the vm monitor is built like this:
|
||||
# /tmp/nix-build-vm-test-run-$name.drv-0/vm-state-machine/monitor
|
||||
# The max filename length of a unix domain socket is 108 bytes.
|
||||
# This means $name can at most be 50 bytes long.
|
||||
maxTestNameLen = 50;
|
||||
testNameLen = builtins.stringLength testName;
|
||||
in with builtins;
|
||||
if testNameLen > maxTestNameLen then
|
||||
abort
|
||||
("The name of the test '${testName}' must not be longer than ${toString maxTestNameLen} " +
|
||||
"it's currently ${toString testNameLen} characters long.")
|
||||
else
|
||||
"nixos-test-driver-${testName}";
|
||||
|
||||
vlans = map (m: m.config.virtualisation.vlans) (lib.attrValues nodes);
|
||||
vms = map (m: m.config.system.build.vm) (lib.attrValues nodes);
|
||||
|
||||
nodeHostNames = let
|
||||
nodesList = map (c: c.config.system.name) (lib.attrValues nodes);
|
||||
in nodesList ++ lib.optional (lib.length nodesList == 1 && !lib.elem "machine" nodesList) "machine";
|
||||
|
||||
# TODO: This is an implementation error and needs fixing
|
||||
# the testing famework cannot legitimately restrict hostnames further
|
||||
# beyond RFC1035
|
||||
invalidNodeNames = lib.filter
|
||||
(node: builtins.match "^[A-z_]([A-z0-9_]+)?$" node == null)
|
||||
nodeHostNames;
|
||||
|
||||
testScript' =
|
||||
# Call the test script with the computed nodes.
|
||||
if lib.isFunction testScript
|
||||
then testScript { inherit nodes; }
|
||||
else testScript;
|
||||
|
||||
uniqueVlans = lib.unique (builtins.concatLists vlans);
|
||||
vlanNames = map (i: "vlan${toString i}: VLan;") uniqueVlans;
|
||||
machineNames = map (name: "${name}: Machine;") nodeHostNames;
|
||||
in
|
||||
if lib.length invalidNodeNames > 0 then
|
||||
throw ''
|
||||
Cannot create machines out of (${lib.concatStringsSep ", " invalidNodeNames})!
|
||||
All machines are referenced as python variables in the testing framework which will break the
|
||||
script when special characters are used.
|
||||
|
||||
This is an IMPLEMENTATION ERROR and needs to be fixed. Meanwhile,
|
||||
please stick to alphanumeric chars and underscores as separation.
|
||||
''
|
||||
else lib.warnIf skipLint "Linting is disabled" (runCommand testDriverName
|
||||
{
|
||||
inherit testName;
|
||||
nativeBuildInputs = [ makeWrapper mypy ];
|
||||
buildInputs = [ testDriver ];
|
||||
testScript = testScript';
|
||||
preferLocalBuild = true;
|
||||
passthru = passthru // {
|
||||
inherit nodes;
|
||||
};
|
||||
meta.mainProgram = "nixos-test-driver";
|
||||
}
|
||||
''
|
||||
mkdir -p $out/bin
|
||||
|
||||
vmStartScripts=($(for i in ${toString vms}; do echo $i/bin/run-*-vm; done))
|
||||
|
||||
${lib.optionalString (!skipTypeCheck) ''
|
||||
# prepend type hints so the test script can be type checked with mypy
|
||||
cat "${./test-script-prepend.py}" >> testScriptWithTypes
|
||||
echo "${builtins.toString machineNames}" >> testScriptWithTypes
|
||||
echo "${builtins.toString vlanNames}" >> testScriptWithTypes
|
||||
echo -n "$testScript" >> testScriptWithTypes
|
||||
|
||||
mypy --no-implicit-optional \
|
||||
--pretty \
|
||||
--no-color-output \
|
||||
testScriptWithTypes
|
||||
''}
|
||||
|
||||
echo -n "$testScript" >> $out/test-script
|
||||
|
||||
ln -s ${testDriver}/bin/nixos-test-driver $out/bin/nixos-test-driver
|
||||
|
||||
${testDriver}/bin/generate-driver-symbols
|
||||
${lib.optionalString (!skipLint) ''
|
||||
PYFLAKES_BUILTINS="$(
|
||||
echo -n ${lib.escapeShellArg (lib.concatStringsSep "," nodeHostNames)},
|
||||
< ${lib.escapeShellArg "driver-symbols"}
|
||||
)" ${python3Packages.pyflakes}/bin/pyflakes $out/test-script
|
||||
''}
|
||||
|
||||
# set defaults through environment
|
||||
# see: ./test-driver/test-driver.py argparse implementation
|
||||
wrapProgram $out/bin/nixos-test-driver \
|
||||
--set startScripts "''${vmStartScripts[*]}" \
|
||||
--set testScript "$out/test-script" \
|
||||
--set vlans '${toString vlans}' \
|
||||
${lib.optionalString (interactive) "--add-flags --interactive"}
|
||||
'');
|
||||
};
|
||||
|
||||
# Make a full-blown test
|
||||
makeTest =
|
||||
|
@ -184,90 +47,19 @@ rec {
|
|||
then builtins.unsafeGetAttrPos "description" meta
|
||||
else builtins.unsafeGetAttrPos "testScript" t)
|
||||
, extraPythonPackages ? (_ : [])
|
||||
, interactive ? {}
|
||||
} @ t:
|
||||
let
|
||||
mkNodes = qemu_pkg:
|
||||
let
|
||||
testScript' =
|
||||
# Call the test script with the computed nodes.
|
||||
if lib.isFunction testScript
|
||||
then testScript { nodes = mkNodes qemu_pkg; }
|
||||
else testScript;
|
||||
|
||||
build-vms = import ./build-vms.nix {
|
||||
inherit system lib pkgs minimal specialArgs;
|
||||
extraConfigurations = extraConfigurations ++ [(
|
||||
{ config, ... }:
|
||||
{
|
||||
virtualisation.qemu.package = qemu_pkg;
|
||||
|
||||
# Make sure all derivations referenced by the test
|
||||
# script are available on the nodes. When the store is
|
||||
# accessed through 9p, this isn't important, since
|
||||
# everything in the store is available to the guest,
|
||||
# but when building a root image it is, as all paths
|
||||
# that should be available to the guest has to be
|
||||
# copied to the image.
|
||||
virtualisation.additionalPaths =
|
||||
lib.optional
|
||||
# A testScript may evaluate nodes, which has caused
|
||||
# infinite recursions. The demand cycle involves:
|
||||
# testScript -->
|
||||
# nodes -->
|
||||
# toplevel -->
|
||||
# additionalPaths -->
|
||||
# hasContext testScript' -->
|
||||
# testScript (ad infinitum)
|
||||
# If we don't need to build an image, we can break this
|
||||
# cycle by short-circuiting when useNixStoreImage is false.
|
||||
(config.virtualisation.useNixStoreImage && builtins.hasContext testScript')
|
||||
(pkgs.writeStringReferencesToFile testScript');
|
||||
|
||||
# Ensure we do not use aliases. Ideally this is only set
|
||||
# when the test framework is used by Nixpkgs NixOS tests.
|
||||
nixpkgs.config.allowAliases = false;
|
||||
}
|
||||
)];
|
||||
};
|
||||
in
|
||||
lib.warnIf (t?machine) "In test `${name}': The `machine' attribute in NixOS tests (pkgs.nixosTest / make-test-python.nix / testing-python.nix / makeTest) is deprecated. Please use the equivalent `nodes.machine'."
|
||||
build-vms.buildVirtualNetwork (
|
||||
nodes // lib.optionalAttrs (machine != null) { inherit machine; }
|
||||
);
|
||||
|
||||
driver = setupDriverForTest {
|
||||
inherit testScript enableOCR skipTypeCheck skipLint passthru extraPythonPackages;
|
||||
testName = name;
|
||||
qemu_pkg = pkgs.qemu_test;
|
||||
nodes = mkNodes pkgs.qemu_test;
|
||||
runTest {
|
||||
imports = [
|
||||
{ _file = "makeTest parameters"; config = t; }
|
||||
{
|
||||
defaults = {
|
||||
_file = "makeTest: extraConfigurations";
|
||||
imports = extraConfigurations;
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
driverInteractive = setupDriverForTest {
|
||||
inherit testScript enableOCR skipTypeCheck skipLint passthru extraPythonPackages;
|
||||
testName = name;
|
||||
qemu_pkg = pkgs.qemu;
|
||||
nodes = mkNodes pkgs.qemu;
|
||||
interactive = true;
|
||||
};
|
||||
|
||||
test = lib.addMetaAttrs meta (runTests { inherit driver pos driverInteractive; });
|
||||
|
||||
in
|
||||
test // {
|
||||
inherit test driver driverInteractive;
|
||||
inherit (driver) nodes;
|
||||
};
|
||||
|
||||
abortForFunction = functionName: abort ''The ${functionName} function was
|
||||
removed because it is not an essential part of the NixOS testing
|
||||
infrastructure. It had no usage in NixOS or Nixpkgs and it had no designated
|
||||
maintainer. You are free to reintroduce it by documenting it in the manual
|
||||
and adding yourself as maintainer. It was removed in
|
||||
https://github.com/NixOS/nixpkgs/pull/137013
|
||||
'';
|
||||
|
||||
runInMachine = abortForFunction "runInMachine";
|
||||
|
||||
runInMachineWithX = abortForFunction "runInMachineWithX";
|
||||
|
||||
simpleTest = as: (makeTest as).test;
|
||||
|
||||
|
|
16
nixos/lib/testing/call-test.nix
Normal file
16
nixos/lib/testing/call-test.nix
Normal file
|
@ -0,0 +1,16 @@
|
|||
{ config, lib, ... }:
|
||||
let
|
||||
inherit (lib) mkOption types;
|
||||
in
|
||||
{
|
||||
options = {
|
||||
callTest = mkOption {
|
||||
internal = true;
|
||||
type = types.functionTo types.raw;
|
||||
};
|
||||
result = mkOption {
|
||||
internal = true;
|
||||
default = config;
|
||||
};
|
||||
};
|
||||
}
|
24
nixos/lib/testing/default.nix
Normal file
24
nixos/lib/testing/default.nix
Normal file
|
@ -0,0 +1,24 @@
|
|||
{ lib }:
|
||||
let
|
||||
|
||||
evalTest = module: lib.evalModules { modules = testModules ++ [ module ]; };
|
||||
runTest = module: (evalTest module).config.result;
|
||||
|
||||
testModules = [
|
||||
./call-test.nix
|
||||
./driver.nix
|
||||
./interactive.nix
|
||||
./legacy.nix
|
||||
./meta.nix
|
||||
./name.nix
|
||||
./network.nix
|
||||
./nodes.nix
|
||||
./pkgs.nix
|
||||
./run.nix
|
||||
./testScript.nix
|
||||
];
|
||||
|
||||
in
|
||||
{
|
||||
inherit evalTest runTest testModules;
|
||||
}
|
188
nixos/lib/testing/driver.nix
Normal file
188
nixos/lib/testing/driver.nix
Normal file
|
@ -0,0 +1,188 @@
|
|||
{ config, lib, hostPkgs, ... }:
|
||||
let
|
||||
inherit (lib) mkOption types literalMD mdDoc;
|
||||
|
||||
# Reifies and correctly wraps the python test driver for
|
||||
# the respective qemu version and with or without ocr support
|
||||
testDriver = hostPkgs.callPackage ../test-driver {
|
||||
inherit (config) enableOCR extraPythonPackages;
|
||||
qemu_pkg = config.qemu.package;
|
||||
imagemagick_light = hostPkgs.imagemagick_light.override { inherit (hostPkgs) libtiff; };
|
||||
tesseract4 = hostPkgs.tesseract4.override { enableLanguages = [ "eng" ]; };
|
||||
};
|
||||
|
||||
|
||||
vlans = map (m: m.virtualisation.vlans) (lib.attrValues config.nodes);
|
||||
vms = map (m: m.system.build.vm) (lib.attrValues config.nodes);
|
||||
|
||||
nodeHostNames =
|
||||
let
|
||||
nodesList = map (c: c.system.name) (lib.attrValues config.nodes);
|
||||
in
|
||||
nodesList ++ lib.optional (lib.length nodesList == 1 && !lib.elem "machine" nodesList) "machine";
|
||||
|
||||
# TODO: This is an implementation error and needs fixing
|
||||
# the testing famework cannot legitimately restrict hostnames further
|
||||
# beyond RFC1035
|
||||
invalidNodeNames = lib.filter
|
||||
(node: builtins.match "^[A-z_]([A-z0-9_]+)?$" node == null)
|
||||
nodeHostNames;
|
||||
|
||||
uniqueVlans = lib.unique (builtins.concatLists vlans);
|
||||
vlanNames = map (i: "vlan${toString i}: VLan;") uniqueVlans;
|
||||
machineNames = map (name: "${name}: Machine;") nodeHostNames;
|
||||
|
||||
withChecks =
|
||||
if lib.length invalidNodeNames > 0 then
|
||||
throw ''
|
||||
Cannot create machines out of (${lib.concatStringsSep ", " invalidNodeNames})!
|
||||
All machines are referenced as python variables in the testing framework which will break the
|
||||
script when special characters are used.
|
||||
|
||||
This is an IMPLEMENTATION ERROR and needs to be fixed. Meanwhile,
|
||||
please stick to alphanumeric chars and underscores as separation.
|
||||
''
|
||||
else
|
||||
lib.warnIf config.skipLint "Linting is disabled";
|
||||
|
||||
driver =
|
||||
hostPkgs.runCommand "nixos-test-driver-${config.name}"
|
||||
{
|
||||
# inherit testName; TODO (roberth): need this?
|
||||
nativeBuildInputs = [
|
||||
hostPkgs.makeWrapper
|
||||
] ++ lib.optionals (!config.skipTypeCheck) [ hostPkgs.mypy ];
|
||||
buildInputs = [ testDriver ];
|
||||
testScript = config.testScriptString;
|
||||
preferLocalBuild = true;
|
||||
passthru = config.passthru;
|
||||
meta = config.meta // {
|
||||
mainProgram = "nixos-test-driver";
|
||||
};
|
||||
}
|
||||
''
|
||||
mkdir -p $out/bin
|
||||
|
||||
vmStartScripts=($(for i in ${toString vms}; do echo $i/bin/run-*-vm; done))
|
||||
|
||||
${lib.optionalString (!config.skipTypeCheck) ''
|
||||
# prepend type hints so the test script can be type checked with mypy
|
||||
cat "${../test-script-prepend.py}" >> testScriptWithTypes
|
||||
echo "${builtins.toString machineNames}" >> testScriptWithTypes
|
||||
echo "${builtins.toString vlanNames}" >> testScriptWithTypes
|
||||
echo -n "$testScript" >> testScriptWithTypes
|
||||
|
||||
cat -n testScriptWithTypes
|
||||
|
||||
mypy --no-implicit-optional \
|
||||
--pretty \
|
||||
--no-color-output \
|
||||
testScriptWithTypes
|
||||
''}
|
||||
|
||||
echo -n "$testScript" >> $out/test-script
|
||||
|
||||
ln -s ${testDriver}/bin/nixos-test-driver $out/bin/nixos-test-driver
|
||||
|
||||
${testDriver}/bin/generate-driver-symbols
|
||||
${lib.optionalString (!config.skipLint) ''
|
||||
PYFLAKES_BUILTINS="$(
|
||||
echo -n ${lib.escapeShellArg (lib.concatStringsSep "," nodeHostNames)},
|
||||
< ${lib.escapeShellArg "driver-symbols"}
|
||||
)" ${hostPkgs.python3Packages.pyflakes}/bin/pyflakes $out/test-script
|
||||
''}
|
||||
|
||||
# set defaults through environment
|
||||
# see: ./test-driver/test-driver.py argparse implementation
|
||||
wrapProgram $out/bin/nixos-test-driver \
|
||||
--set startScripts "''${vmStartScripts[*]}" \
|
||||
--set testScript "$out/test-script" \
|
||||
--set vlans '${toString vlans}' \
|
||||
${lib.escapeShellArgs (lib.concatMap (arg: ["--add-flags" arg]) config.extraDriverArgs)}
|
||||
'';
|
||||
|
||||
in
|
||||
{
|
||||
options = {
|
||||
|
||||
driver = mkOption {
|
||||
description = mdDoc "Package containing a script that runs the test.";
|
||||
type = types.package;
|
||||
defaultText = literalMD "set by the test framework";
|
||||
};
|
||||
|
||||
hostPkgs = mkOption {
|
||||
description = mdDoc "Nixpkgs attrset used outside the nodes.";
|
||||
type = types.raw;
|
||||
example = lib.literalExpression ''
|
||||
import nixpkgs { inherit system config overlays; }
|
||||
'';
|
||||
};
|
||||
|
||||
qemu.package = mkOption {
|
||||
description = mdDoc "Which qemu package to use for the virtualisation of [{option}`nodes`](#opt-nodes).";
|
||||
type = types.package;
|
||||
default = hostPkgs.qemu_test;
|
||||
defaultText = "hostPkgs.qemu_test";
|
||||
};
|
||||
|
||||
enableOCR = mkOption {
|
||||
description = mdDoc ''
|
||||
Whether to enable Optical Character Recognition functionality for
|
||||
testing graphical programs. See [Machine objects](`ssec-machine-objects`).
|
||||
'';
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
|
||||
extraPythonPackages = mkOption {
|
||||
description = mdDoc ''
|
||||
Python packages to add to the test driver.
|
||||
|
||||
The argument is a Python package set, similar to `pkgs.pythonPackages`.
|
||||
'';
|
||||
example = lib.literalExpression ''
|
||||
p: [ p.numpy ]
|
||||
'';
|
||||
type = types.functionTo (types.listOf types.package);
|
||||
default = ps: [ ];
|
||||
};
|
||||
|
||||
extraDriverArgs = mkOption {
|
||||
description = mdDoc ''
|
||||
Extra arguments to pass to the test driver.
|
||||
|
||||
They become part of [{option}`driver`](#opt-driver) via `wrapProgram`.
|
||||
'';
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
};
|
||||
|
||||
skipLint = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = mdDoc ''
|
||||
Do not run the linters. This may speed up your iteration cycle, but it is not something you should commit.
|
||||
'';
|
||||
};
|
||||
|
||||
skipTypeCheck = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = mdDoc ''
|
||||
Disable type checking. This must not be enabled for new NixOS tests.
|
||||
|
||||
This may speed up your iteration cycle, unless you're working on the [{option}`testScript`](#opt-testScript).
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
_module.args.hostPkgs = config.hostPkgs;
|
||||
|
||||
driver = withChecks driver;
|
||||
|
||||
# make available on the test runner
|
||||
passthru.driver = config.driver;
|
||||
};
|
||||
}
|
45
nixos/lib/testing/interactive.nix
Normal file
45
nixos/lib/testing/interactive.nix
Normal file
|
@ -0,0 +1,45 @@
|
|||
{ config, lib, moduleType, hostPkgs, ... }:
|
||||
let
|
||||
inherit (lib) mkOption types mdDoc;
|
||||
in
|
||||
{
|
||||
options = {
|
||||
interactive = mkOption {
|
||||
description = mdDoc ''
|
||||
Tests [can be run interactively](#sec-running-nixos-tests-interactively)
|
||||
using the program in the test derivation's `.driverInteractive` attribute.
|
||||
|
||||
When they are, the configuration will include anything set in this submodule.
|
||||
|
||||
You can set any top-level test option here.
|
||||
|
||||
Example test module:
|
||||
|
||||
```nix
|
||||
{ config, lib, ... }: {
|
||||
|
||||
nodes.rabbitmq = {
|
||||
services.rabbitmq.enable = true;
|
||||
};
|
||||
|
||||
# When running interactively ...
|
||||
interactive.nodes.rabbitmq = {
|
||||
# ... enable the web ui.
|
||||
services.rabbitmq.managementPlugin.enable = true;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
For details, see the section about [running tests interactively](#sec-running-nixos-tests-interactively).
|
||||
'';
|
||||
type = moduleType;
|
||||
visible = "shallow";
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
interactive.qemu.package = hostPkgs.qemu;
|
||||
interactive.extraDriverArgs = [ "--interactive" ];
|
||||
passthru.driverInteractive = config.interactive.driver;
|
||||
};
|
||||
}
|
25
nixos/lib/testing/legacy.nix
Normal file
25
nixos/lib/testing/legacy.nix
Normal file
|
@ -0,0 +1,25 @@
|
|||
{ config, options, lib, ... }:
|
||||
let
|
||||
inherit (lib) mkIf mkOption types;
|
||||
in
|
||||
{
|
||||
# This needs options.warnings, which we don't have (yet?).
|
||||
# imports = [
|
||||
# (lib.mkRenamedOptionModule [ "machine" ] [ "nodes" "machine" ])
|
||||
# ];
|
||||
|
||||
options = {
|
||||
machine = mkOption {
|
||||
internal = true;
|
||||
type = types.raw;
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
nodes = mkIf options.machine.isDefined (
|
||||
lib.warn
|
||||
"In test `${config.name}': The `machine' attribute in NixOS tests (pkgs.nixosTest / make-test-python.nix / testing-python.nix / makeTest) is deprecated. Please set the equivalent `nodes.machine'."
|
||||
{ inherit (config) machine; }
|
||||
);
|
||||
};
|
||||
}
|
42
nixos/lib/testing/meta.nix
Normal file
42
nixos/lib/testing/meta.nix
Normal file
|
@ -0,0 +1,42 @@
|
|||
{ lib, ... }:
|
||||
let
|
||||
inherit (lib) types mkOption mdDoc;
|
||||
in
|
||||
{
|
||||
options = {
|
||||
meta = lib.mkOption {
|
||||
description = mdDoc ''
|
||||
The [`meta`](https://nixos.org/manual/nixpkgs/stable/#chap-meta) attributes that will be set on the returned derivations.
|
||||
|
||||
Not all [`meta`](https://nixos.org/manual/nixpkgs/stable/#chap-meta) attributes are supported, but more can be added as desired.
|
||||
'';
|
||||
apply = lib.filterAttrs (k: v: v != null);
|
||||
type = types.submodule {
|
||||
options = {
|
||||
maintainers = lib.mkOption {
|
||||
type = types.listOf types.raw;
|
||||
default = [];
|
||||
description = mdDoc ''
|
||||
The [list of maintainers](https://nixos.org/manual/nixpkgs/stable/#var-meta-maintainers) for this test.
|
||||
'';
|
||||
};
|
||||
timeout = lib.mkOption {
|
||||
type = types.nullOr types.int;
|
||||
default = null; # NOTE: null values are filtered out by `meta`.
|
||||
description = mdDoc ''
|
||||
The [{option}`test`](#opt-test)'s [`meta.timeout`](https://nixos.org/manual/nixpkgs/stable/#var-meta-timeout) in seconds.
|
||||
'';
|
||||
};
|
||||
broken = lib.mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = mdDoc ''
|
||||
Sets the [`meta.broken`](https://nixos.org/manual/nixpkgs/stable/#var-meta-broken) attribute on the [{option}`test`](#opt-test) derivation.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
default = {};
|
||||
};
|
||||
};
|
||||
}
|
14
nixos/lib/testing/name.nix
Normal file
14
nixos/lib/testing/name.nix
Normal file
|
@ -0,0 +1,14 @@
|
|||
{ lib, ... }:
|
||||
let
|
||||
inherit (lib) mkOption types mdDoc;
|
||||
in
|
||||
{
|
||||
options.name = mkOption {
|
||||
description = mdDoc ''
|
||||
The name of the test.
|
||||
|
||||
This is used in the derivation names of the [{option}`driver`](#opt-driver) and [{option}`test`](#opt-test) runner.
|
||||
'';
|
||||
type = types.str;
|
||||
};
|
||||
}
|
117
nixos/lib/testing/network.nix
Normal file
117
nixos/lib/testing/network.nix
Normal file
|
@ -0,0 +1,117 @@
|
|||
{ lib, nodes, ... }:
|
||||
|
||||
let
|
||||
inherit (lib)
|
||||
attrNames concatMap concatMapStrings flip forEach head
|
||||
listToAttrs mkDefault mkOption nameValuePair optionalString
|
||||
range types zipListsWith zipLists
|
||||
mdDoc
|
||||
;
|
||||
|
||||
nodeNumbers =
|
||||
listToAttrs
|
||||
(zipListsWith
|
||||
nameValuePair
|
||||
(attrNames nodes)
|
||||
(range 1 254)
|
||||
);
|
||||
|
||||
networkModule = { config, nodes, pkgs, ... }:
|
||||
let
|
||||
interfacesNumbered = zipLists config.virtualisation.vlans (range 1 255);
|
||||
interfaces = forEach interfacesNumbered ({ fst, snd }:
|
||||
nameValuePair "eth${toString snd}" {
|
||||
ipv4.addresses =
|
||||
[{
|
||||
address = "192.168.${toString fst}.${toString config.virtualisation.test.nodeNumber}";
|
||||
prefixLength = 24;
|
||||
}];
|
||||
});
|
||||
|
||||
networkConfig =
|
||||
{
|
||||
networking.hostName = mkDefault config.virtualisation.test.nodeName;
|
||||
|
||||
networking.interfaces = listToAttrs interfaces;
|
||||
|
||||
networking.primaryIPAddress =
|
||||
optionalString (interfaces != [ ]) (head (head interfaces).value.ipv4.addresses).address;
|
||||
|
||||
# Put the IP addresses of all VMs in this machine's
|
||||
# /etc/hosts file. If a machine has multiple
|
||||
# interfaces, use the IP address corresponding to
|
||||
# the first interface (i.e. the first network in its
|
||||
# virtualisation.vlans option).
|
||||
networking.extraHosts = flip concatMapStrings (attrNames nodes)
|
||||
(m':
|
||||
let config = nodes.${m'}; in
|
||||
optionalString (config.networking.primaryIPAddress != "")
|
||||
("${config.networking.primaryIPAddress} " +
|
||||
optionalString (config.networking.domain != null)
|
||||
"${config.networking.hostName}.${config.networking.domain} " +
|
||||
"${config.networking.hostName}\n"));
|
||||
|
||||
virtualisation.qemu.options =
|
||||
let qemu-common = import ../qemu-common.nix { inherit lib pkgs; };
|
||||
in
|
||||
flip concatMap interfacesNumbered
|
||||
({ fst, snd }: qemu-common.qemuNICFlags snd fst config.virtualisation.test.nodeNumber);
|
||||
};
|
||||
|
||||
in
|
||||
{
|
||||
key = "ip-address";
|
||||
config = networkConfig // {
|
||||
# Expose the networkConfig items for tests like nixops
|
||||
# that need to recreate the network config.
|
||||
system.build.networkConfig = networkConfig;
|
||||
};
|
||||
};
|
||||
|
||||
nodeNumberModule = (regular@{ config, name, ... }: {
|
||||
options = {
|
||||
virtualisation.test.nodeName = mkOption {
|
||||
internal = true;
|
||||
default = name;
|
||||
# We need to force this in specilisations, otherwise it'd be
|
||||
# readOnly = true;
|
||||
description = mdDoc ''
|
||||
The `name` in `nodes.<name>`; stable across `specialisations`.
|
||||
'';
|
||||
};
|
||||
virtualisation.test.nodeNumber = mkOption {
|
||||
internal = true;
|
||||
type = types.int;
|
||||
readOnly = true;
|
||||
default = nodeNumbers.${config.virtualisation.test.nodeName};
|
||||
description = mdDoc ''
|
||||
A unique number assigned for each node in `nodes`.
|
||||
'';
|
||||
};
|
||||
|
||||
# specialisations override the `name` module argument,
|
||||
# so we push the real `virtualisation.test.nodeName`.
|
||||
specialisation = mkOption {
|
||||
type = types.attrsOf (types.submodule {
|
||||
options.configuration = mkOption {
|
||||
type = types.submoduleWith {
|
||||
modules = [
|
||||
{
|
||||
config.virtualisation.test.nodeName =
|
||||
# assert regular.config.virtualisation.test.nodeName != "configuration";
|
||||
regular.config.virtualisation.test.nodeName;
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
});
|
||||
};
|
||||
};
|
||||
});
|
||||
|
||||
in
|
||||
{
|
||||
config = {
|
||||
extraBaseModules = { imports = [ networkModule nodeNumberModule ]; };
|
||||
};
|
||||
}
|
23
nixos/lib/testing/nixos-test-base.nix
Normal file
23
nixos/lib/testing/nixos-test-base.nix
Normal file
|
@ -0,0 +1,23 @@
|
|||
# A module containing the base imports and overrides that
|
||||
# are always applied in NixOS VM tests, unconditionally,
|
||||
# even in `inheritParentConfig = false` specialisations.
|
||||
{ lib, ... }:
|
||||
let
|
||||
inherit (lib) mkForce;
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
../../modules/virtualisation/qemu-vm.nix
|
||||
../../modules/testing/test-instrumentation.nix # !!! should only get added for automated test runs
|
||||
{ key = "no-manual"; documentation.nixos.enable = false; }
|
||||
{
|
||||
key = "no-revision";
|
||||
# Make the revision metadata constant, in order to avoid needless retesting.
|
||||
# The human version (e.g. 21.05-pre) is left as is, because it is useful
|
||||
# for external modules that test with e.g. testers.nixosTest and rely on that
|
||||
# version number.
|
||||
config.system.nixos.revision = mkForce "constant-nixos-revision";
|
||||
}
|
||||
|
||||
];
|
||||
}
|
112
nixos/lib/testing/nodes.nix
Normal file
112
nixos/lib/testing/nodes.nix
Normal file
|
@ -0,0 +1,112 @@
|
|||
testModuleArgs@{ config, lib, hostPkgs, nodes, ... }:
|
||||
|
||||
let
|
||||
inherit (lib) mkOption mkForce optional types mapAttrs mkDefault mdDoc;
|
||||
|
||||
system = hostPkgs.stdenv.hostPlatform.system;
|
||||
|
||||
baseOS =
|
||||
import ../eval-config.nix {
|
||||
inherit system;
|
||||
inherit (config.node) specialArgs;
|
||||
modules = [ config.defaults ];
|
||||
baseModules = (import ../../modules/module-list.nix) ++
|
||||
[
|
||||
./nixos-test-base.nix
|
||||
{ key = "nodes"; _module.args.nodes = config.nodesCompat; }
|
||||
({ config, ... }:
|
||||
{
|
||||
virtualisation.qemu.package = testModuleArgs.config.qemu.package;
|
||||
|
||||
# Ensure we do not use aliases. Ideally this is only set
|
||||
# when the test framework is used by Nixpkgs NixOS tests.
|
||||
nixpkgs.config.allowAliases = false;
|
||||
})
|
||||
testModuleArgs.config.extraBaseModules
|
||||
] ++ optional config.minimal ../../modules/testing/minimal-kernel.nix;
|
||||
};
|
||||
|
||||
|
||||
in
|
||||
|
||||
{
|
||||
|
||||
options = {
|
||||
node.type = mkOption {
|
||||
type = types.raw;
|
||||
default = baseOS.type;
|
||||
internal = true;
|
||||
};
|
||||
|
||||
nodes = mkOption {
|
||||
type = types.lazyAttrsOf config.node.type;
|
||||
visible = "shallow";
|
||||
description = mdDoc ''
|
||||
An attribute set of NixOS configuration modules.
|
||||
|
||||
The configurations are augmented by the [`defaults`](#opt-defaults) option.
|
||||
|
||||
They are assigned network addresses according to the `nixos/lib/testing/network.nix` module.
|
||||
|
||||
A few special options are available, that aren't in a plain NixOS configuration. See [Configuring the nodes](#sec-nixos-test-nodes)
|
||||
'';
|
||||
};
|
||||
|
||||
defaults = mkOption {
|
||||
description = mdDoc ''
|
||||
NixOS configuration that is applied to all [{option}`nodes`](#opt-nodes).
|
||||
'';
|
||||
type = types.deferredModule;
|
||||
default = { };
|
||||
};
|
||||
|
||||
extraBaseModules = mkOption {
|
||||
description = mdDoc ''
|
||||
NixOS configuration that, like [{option}`defaults`](#opt-defaults), is applied to all [{option}`nodes`](#opt-nodes) and can not be undone with [`specialisation.<name>.inheritParentConfig`](https://search.nixos.org/options?show=specialisation.%3Cname%3E.inheritParentConfig&from=0&size=50&sort=relevance&type=packages&query=specialisation).
|
||||
'';
|
||||
type = types.deferredModule;
|
||||
default = { };
|
||||
};
|
||||
|
||||
node.specialArgs = mkOption {
|
||||
type = types.lazyAttrsOf types.raw;
|
||||
default = { };
|
||||
description = mdDoc ''
|
||||
An attribute set of arbitrary values that will be made available as module arguments during the resolution of module `imports`.
|
||||
|
||||
Note that it is not possible to override these from within the NixOS configurations. If you argument is not relevant to `imports`, consider setting {option}`defaults._module.args.<name>` instead.
|
||||
'';
|
||||
};
|
||||
|
||||
minimal = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = mdDoc ''
|
||||
Enable to configure all [{option}`nodes`](#opt-nodes) to run with a minimal kernel.
|
||||
'';
|
||||
};
|
||||
|
||||
nodesCompat = mkOption {
|
||||
internal = true;
|
||||
description = mdDoc ''
|
||||
Basically `_module.args.nodes`, but with backcompat and warnings added.
|
||||
|
||||
This will go away.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
_module.args.nodes = config.nodesCompat;
|
||||
nodesCompat =
|
||||
mapAttrs
|
||||
(name: config: config // {
|
||||
config = lib.warn
|
||||
"Module argument `nodes.${name}.config` is deprecated. Use `nodes.${name}` instead."
|
||||
config;
|
||||
})
|
||||
config.nodes;
|
||||
|
||||
passthru.nodes = config.nodesCompat;
|
||||
};
|
||||
}
|
11
nixos/lib/testing/pkgs.nix
Normal file
11
nixos/lib/testing/pkgs.nix
Normal file
|
@ -0,0 +1,11 @@
|
|||
{ config, lib, hostPkgs, ... }:
|
||||
{
|
||||
config = {
|
||||
# default pkgs for use in VMs
|
||||
_module.args.pkgs = hostPkgs;
|
||||
|
||||
defaults = {
|
||||
# TODO: a module to set a shared pkgs, if options.nixpkgs.* is untouched by user (highestPrio) */
|
||||
};
|
||||
};
|
||||
}
|
57
nixos/lib/testing/run.nix
Normal file
57
nixos/lib/testing/run.nix
Normal file
|
@ -0,0 +1,57 @@
|
|||
{ config, hostPkgs, lib, ... }:
|
||||
let
|
||||
inherit (lib) types mkOption mdDoc;
|
||||
in
|
||||
{
|
||||
options = {
|
||||
passthru = mkOption {
|
||||
type = types.lazyAttrsOf types.raw;
|
||||
description = mdDoc ''
|
||||
Attributes to add to the returned derivations,
|
||||
which are not necessarily part of the build.
|
||||
|
||||
This is a bit like doing `drv // { myAttr = true; }` (which would be lost by `overrideAttrs`).
|
||||
It does not change the actual derivation, but adds the attribute nonetheless, so that
|
||||
consumers of what would be `drv` have more information.
|
||||
'';
|
||||
};
|
||||
|
||||
test = mkOption {
|
||||
type = types.package;
|
||||
# TODO: can the interactive driver be configured to access the network?
|
||||
description = mdDoc ''
|
||||
Derivation that runs the test as its "build" process.
|
||||
|
||||
This implies that NixOS tests run isolated from the network, making them
|
||||
more dependable.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
test = lib.lazyDerivation { # lazyDerivation improves performance when only passthru items and/or meta are used.
|
||||
derivation = hostPkgs.stdenv.mkDerivation {
|
||||
name = "vm-test-run-${config.name}";
|
||||
|
||||
requiredSystemFeatures = [ "kvm" "nixos-test" ];
|
||||
|
||||
buildCommand = ''
|
||||
mkdir -p $out
|
||||
|
||||
# effectively mute the XMLLogger
|
||||
export LOGFILE=/dev/null
|
||||
|
||||
${config.driver}/bin/nixos-test-driver -o $out
|
||||
'';
|
||||
|
||||
passthru = config.passthru;
|
||||
|
||||
meta = config.meta;
|
||||
};
|
||||
inherit (config) passthru meta;
|
||||
};
|
||||
|
||||
# useful for inspection (debugging / exploration)
|
||||
passthru.config = config;
|
||||
};
|
||||
}
|
84
nixos/lib/testing/testScript.nix
Normal file
84
nixos/lib/testing/testScript.nix
Normal file
|
@ -0,0 +1,84 @@
|
|||
testModuleArgs@{ config, lib, hostPkgs, nodes, moduleType, ... }:
|
||||
let
|
||||
inherit (lib) mkOption types mdDoc;
|
||||
inherit (types) either str functionTo;
|
||||
in
|
||||
{
|
||||
options = {
|
||||
testScript = mkOption {
|
||||
type = either str (functionTo str);
|
||||
description = ''
|
||||
A series of python declarations and statements that you write to perform
|
||||
the test.
|
||||
'';
|
||||
};
|
||||
testScriptString = mkOption {
|
||||
type = str;
|
||||
readOnly = true;
|
||||
internal = true;
|
||||
};
|
||||
|
||||
includeTestScriptReferences = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
internal = true;
|
||||
};
|
||||
withoutTestScriptReferences = mkOption {
|
||||
type = moduleType;
|
||||
description = mdDoc ''
|
||||
A parallel universe where the testScript is invalid and has no references.
|
||||
'';
|
||||
internal = true;
|
||||
visible = false;
|
||||
};
|
||||
};
|
||||
config = {
|
||||
withoutTestScriptReferences.includeTestScriptReferences = false;
|
||||
withoutTestScriptReferences.testScript = lib.mkForce "testscript omitted";
|
||||
|
||||
testScriptString =
|
||||
if lib.isFunction config.testScript
|
||||
then
|
||||
config.testScript
|
||||
{
|
||||
nodes =
|
||||
lib.mapAttrs
|
||||
(k: v:
|
||||
if v.virtualisation.useNixStoreImage
|
||||
then
|
||||
# prevent infinite recursion when testScript would
|
||||
# reference v's toplevel
|
||||
config.withoutTestScriptReferences.nodesCompat.${k}
|
||||
else
|
||||
# reuse memoized config
|
||||
v
|
||||
)
|
||||
config.nodesCompat;
|
||||
}
|
||||
else config.testScript;
|
||||
|
||||
defaults = { config, name, ... }: {
|
||||
# Make sure all derivations referenced by the test
|
||||
# script are available on the nodes. When the store is
|
||||
# accessed through 9p, this isn't important, since
|
||||
# everything in the store is available to the guest,
|
||||
# but when building a root image it is, as all paths
|
||||
# that should be available to the guest has to be
|
||||
# copied to the image.
|
||||
virtualisation.additionalPaths =
|
||||
lib.optional
|
||||
# A testScript may evaluate nodes, which has caused
|
||||
# infinite recursions. The demand cycle involves:
|
||||
# testScript -->
|
||||
# nodes -->
|
||||
# toplevel -->
|
||||
# additionalPaths -->
|
||||
# hasContext testScript' -->
|
||||
# testScript (ad infinitum)
|
||||
# If we don't need to build an image, we can break this
|
||||
# cycle by short-circuiting when useNixStoreImage is false.
|
||||
(config.virtualisation.useNixStoreImage && builtins.hasContext testModuleArgs.config.testScriptString && testModuleArgs.config.includeTestScriptReferences)
|
||||
(hostPkgs.writeStringReferencesToFile testModuleArgs.config.testScriptString);
|
||||
};
|
||||
};
|
||||
}
|
|
@ -15,7 +15,7 @@ let
|
|||
inherit system pkgs;
|
||||
};
|
||||
|
||||
interactiveDriver = (testing.makeTest { inherit nodes; testScript = "start_all(); join_all();"; }).driverInteractive;
|
||||
interactiveDriver = (testing.makeTest { inherit nodes; name = "network"; testScript = "start_all(); join_all();"; }).driverInteractive;
|
||||
in
|
||||
|
||||
|
||||
|
|
|
@ -22,8 +22,8 @@ let
|
|||
import ./tests/all-tests.nix {
|
||||
inherit system;
|
||||
pkgs = import ./.. { inherit system; };
|
||||
callTest = t: {
|
||||
${system} = hydraJob t.test;
|
||||
callTest = config: {
|
||||
${system} = hydraJob config.test;
|
||||
};
|
||||
} // {
|
||||
# for typechecking of the scripts and evaluation of
|
||||
|
@ -32,8 +32,8 @@ let
|
|||
import ./tests/all-tests.nix {
|
||||
inherit system;
|
||||
pkgs = import ./.. { inherit system; };
|
||||
callTest = t: {
|
||||
${system} = hydraJob t.test.driver;
|
||||
callTest = config: {
|
||||
${system} = hydraJob config.driver;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import ./make-test-python.nix ({ pkgs, ...} : {
|
||||
{ lib, pkgs, ... }: {
|
||||
name = "3proxy";
|
||||
meta = with pkgs.lib.maintainers; {
|
||||
meta = with lib.maintainers; {
|
||||
maintainers = [ misuzu ];
|
||||
};
|
||||
|
||||
|
@ -92,7 +92,7 @@ import ./make-test-python.nix ({ pkgs, ...} : {
|
|||
networking.firewall.allowedTCPPorts = [ 3128 9999 ];
|
||||
};
|
||||
|
||||
peer3 = { lib, ... }: {
|
||||
peer3 = { lib, pkgs, ... }: {
|
||||
networking.useDHCP = false;
|
||||
networking.interfaces.eth1 = {
|
||||
ipv4.addresses = [
|
||||
|
@ -186,4 +186,4 @@ import ./make-test-python.nix ({ pkgs, ...} : {
|
|||
"${pkgs.wget}/bin/wget -e use_proxy=yes -e http_proxy=http://192.168.0.4:3128 -S -O /dev/null http://127.0.0.1:9999"
|
||||
)
|
||||
'';
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import ./make-test-python.nix ({ pkgs, lib, ... }: let
|
||||
{ pkgs, lib, ... }: let
|
||||
commonConfig = ./common/acme/client;
|
||||
|
||||
dnsServerIP = nodes: nodes.dnsserver.config.networking.primaryIPAddress;
|
||||
dnsServerIP = nodes: nodes.dnsserver.networking.primaryIPAddress;
|
||||
|
||||
dnsScript = nodes: let
|
||||
dnsAddress = dnsServerIP nodes;
|
||||
|
@ -153,7 +153,7 @@ in {
|
|||
description = "Pebble ACME challenge test server";
|
||||
wantedBy = [ "network.target" ];
|
||||
serviceConfig = {
|
||||
ExecStart = "${pkgs.pebble}/bin/pebble-challtestsrv -dns01 ':53' -defaultIPv6 '' -defaultIPv4 '${nodes.webserver.config.networking.primaryIPAddress}'";
|
||||
ExecStart = "${pkgs.pebble}/bin/pebble-challtestsrv -dns01 ':53' -defaultIPv6 '' -defaultIPv4 '${nodes.webserver.networking.primaryIPAddress}'";
|
||||
# Required to bind on privileged ports.
|
||||
AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
|
||||
};
|
||||
|
@ -175,7 +175,7 @@ in {
|
|||
specialisation = {
|
||||
# First derivation used to test general ACME features
|
||||
general.configuration = { ... }: let
|
||||
caDomain = nodes.acme.config.test-support.acme.caDomain;
|
||||
caDomain = nodes.acme.test-support.acme.caDomain;
|
||||
email = config.security.acme.defaults.email;
|
||||
# Exit 99 to make it easier to track if this is the reason a renew failed
|
||||
accountCreateTester = ''
|
||||
|
@ -316,7 +316,7 @@ in {
|
|||
|
||||
testScript = { nodes, ... }:
|
||||
let
|
||||
caDomain = nodes.acme.config.test-support.acme.caDomain;
|
||||
caDomain = nodes.acme.test-support.acme.caDomain;
|
||||
newServerSystem = nodes.webserver.config.system.build.toplevel;
|
||||
switchToNewServer = "${newServerSystem}/bin/switch-to-configuration test";
|
||||
in
|
||||
|
@ -438,7 +438,7 @@ in {
|
|||
client.wait_for_unit("default.target")
|
||||
|
||||
client.succeed(
|
||||
'curl --data \'{"host": "${caDomain}", "addresses": ["${nodes.acme.config.networking.primaryIPAddress}"]}\' http://${dnsServerIP nodes}:8055/add-a'
|
||||
'curl --data \'{"host": "${caDomain}", "addresses": ["${nodes.acme.networking.primaryIPAddress}"]}\' http://${dnsServerIP nodes}:8055/add-a'
|
||||
)
|
||||
|
||||
acme.wait_for_unit("network-online.target")
|
||||
|
@ -594,4 +594,4 @@ in {
|
|||
wait_for_server()
|
||||
check_connection_key_bits(client, test_domain, "384")
|
||||
'';
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import ./make-test-python.nix {
|
||||
{
|
||||
name = "adguardhome";
|
||||
|
||||
nodes = {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import ./make-test-python.nix ({ pkgs, lib, ... }: {
|
||||
{ pkgs, lib, ... }: {
|
||||
name = "aesmd";
|
||||
meta = {
|
||||
maintainers = with lib.maintainers; [ veehaitch ];
|
||||
|
@ -59,4 +59,4 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
|
|||
|
||||
assert aesmd_config == "whitelist url = http://nixos.org\nproxy type = direct\ndefault quoting type = ecdsa_256\n", "aesmd.conf differs"
|
||||
'';
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
{ system, pkgs, callTest }:
|
||||
{ system,
|
||||
pkgs,
|
||||
|
||||
# Projects the test configuration into a the desired value; usually
|
||||
# the test runner: `config: config.test`.
|
||||
callTest,
|
||||
|
||||
}:
|
||||
# The return value of this function will be an attrset with arbitrary depth and
|
||||
# the `anything` returned by callTest at its test leafs.
|
||||
# The tests not supported by `system` will be replaced with `{}`, so that
|
||||
|
@ -11,9 +18,18 @@ with pkgs.lib;
|
|||
|
||||
let
|
||||
discoverTests = val:
|
||||
if !isAttrs val then val
|
||||
else if hasAttr "test" val then callTest val
|
||||
else mapAttrs (n: s: discoverTests s) val;
|
||||
if isAttrs val
|
||||
then
|
||||
if hasAttr "test" val then callTest val
|
||||
else mapAttrs (n: s: discoverTests s) val
|
||||
else if isFunction val
|
||||
then
|
||||
# Tests based on make-test-python.nix will return the second lambda
|
||||
# in that file, which are then forwarded to the test definition
|
||||
# following the `import make-test-python.nix` expression
|
||||
# (if it is a function).
|
||||
discoverTests (val { inherit system pkgs; })
|
||||
else val;
|
||||
handleTest = path: args:
|
||||
discoverTests (import path ({ inherit system pkgs; } // args));
|
||||
handleTestOn = systems: path: args:
|
||||
|
@ -27,12 +43,34 @@ let
|
|||
};
|
||||
evalMinimalConfig = module: nixosLib.evalModules { modules = [ module ]; };
|
||||
|
||||
inherit
|
||||
(rec {
|
||||
doRunTest = arg: (import ../lib/testing-python.nix { inherit system pkgs; }).runTest {
|
||||
imports = [ arg { inherit callTest; } ];
|
||||
};
|
||||
findTests = tree:
|
||||
if tree?recurseForDerivations && tree.recurseForDerivations
|
||||
then
|
||||
mapAttrs
|
||||
(k: findTests)
|
||||
(builtins.removeAttrs tree ["recurseForDerivations"])
|
||||
else callTest tree;
|
||||
|
||||
runTest = arg: let r = doRunTest arg; in findTests r;
|
||||
runTestOn = systems: arg:
|
||||
if elem system systems then runTest arg
|
||||
else {};
|
||||
})
|
||||
runTest
|
||||
runTestOn
|
||||
;
|
||||
|
||||
in {
|
||||
_3proxy = handleTest ./3proxy.nix {};
|
||||
acme = handleTest ./acme.nix {};
|
||||
adguardhome = handleTest ./adguardhome.nix {};
|
||||
aesmd = handleTest ./aesmd.nix {};
|
||||
agate = handleTest ./web-servers/agate.nix {};
|
||||
_3proxy = runTest ./3proxy.nix;
|
||||
acme = runTest ./acme.nix;
|
||||
adguardhome = runTest ./adguardhome.nix;
|
||||
aesmd = runTest ./aesmd.nix;
|
||||
agate = runTest ./web-servers/agate.nix;
|
||||
agda = handleTest ./agda.nix {};
|
||||
airsonic = handleTest ./airsonic.nix {};
|
||||
allTerminfo = handleTest ./all-terminfo.nix {};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{ lib, nodes, pkgs, ... }:
|
||||
let
|
||||
caCert = nodes.acme.config.test-support.acme.caCert;
|
||||
caDomain = nodes.acme.config.test-support.acme.caDomain;
|
||||
caCert = nodes.acme.test-support.acme.caCert;
|
||||
caDomain = nodes.acme.test-support.acme.caDomain;
|
||||
|
||||
in {
|
||||
security.acme = {
|
||||
|
|
|
@ -18,10 +18,10 @@
|
|||
#
|
||||
# example = { nodes, ... }: {
|
||||
# networking.nameservers = [
|
||||
# nodes.acme.config.networking.primaryIPAddress
|
||||
# nodes.acme.networking.primaryIPAddress
|
||||
# ];
|
||||
# security.pki.certificateFiles = [
|
||||
# nodes.acme.config.test-support.acme.caCert
|
||||
# nodes.acme.test-support.acme.caCert
|
||||
# ];
|
||||
# };
|
||||
# }
|
||||
|
@ -36,7 +36,7 @@
|
|||
# acme = { nodes, lib, ... }: {
|
||||
# imports = [ ./common/acme/server ];
|
||||
# networking.nameservers = lib.mkForce [
|
||||
# nodes.myresolver.config.networking.primaryIPAddress
|
||||
# nodes.myresolver.networking.primaryIPAddress
|
||||
# ];
|
||||
# };
|
||||
#
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import ./make-test-python.nix (
|
||||
{
|
||||
name = "corerad";
|
||||
nodes = {
|
||||
router = {config, pkgs, ...}: {
|
||||
config = {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# This test runs CRI-O and verifies via critest
|
||||
import ./make-test-python.nix ({ pkgs, ... }: {
|
||||
name = "cri-o";
|
||||
meta.maintainers = with pkgs.lib.maintainers; teams.podman.members;
|
||||
meta.maintainers = with pkgs.lib; teams.podman.members;
|
||||
|
||||
nodes = {
|
||||
crio = {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import ./make-test-python.nix ({ pkgs, ... }: {
|
||||
name = "ghostunnel";
|
||||
nodes = {
|
||||
backend = { pkgs, ... }: {
|
||||
services.nginx.enable = true;
|
||||
|
|
|
@ -40,7 +40,7 @@ let
|
|||
name = tested.name;
|
||||
|
||||
meta = {
|
||||
maintainers = tested.meta.maintainers;
|
||||
maintainers = tested.meta.maintainers or [];
|
||||
};
|
||||
|
||||
nodes.machine = { ... }: {
|
||||
|
|
|
@ -324,6 +324,9 @@ let
|
|||
desktop-file-utils
|
||||
docbook5
|
||||
docbook_xsl_ns
|
||||
(docbook-xsl-ns.override {
|
||||
withManOptDedupPatch = true;
|
||||
})
|
||||
kmod.dev
|
||||
libarchive.dev
|
||||
libxml2.bin
|
||||
|
@ -333,6 +336,13 @@ let
|
|||
perlPackages.ListCompare
|
||||
perlPackages.XMLLibXML
|
||||
python3Minimal
|
||||
# make-options-doc/default.nix
|
||||
(let
|
||||
self = (pkgs.python3Minimal.override {
|
||||
inherit self;
|
||||
includeSiteCustomize = true;
|
||||
});
|
||||
in self.withPackages (p: [ p.mistune ]))
|
||||
shared-mime-info
|
||||
sudo
|
||||
texinfo
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import ../make-test-python.nix {
|
||||
name = "lorri";
|
||||
|
||||
nodes.machine = { pkgs, ... }: {
|
||||
imports = [ ../../modules/profiles/minimal.nix ];
|
||||
environment.systemPackages = [ pkgs.lorri ];
|
||||
|
|
|
@ -7,6 +7,8 @@ with pkgs.lib;
|
|||
let
|
||||
matomoTest = package:
|
||||
makeTest {
|
||||
name = "matomo";
|
||||
|
||||
nodes.machine = { config, pkgs, ... }: {
|
||||
services.matomo = {
|
||||
package = package;
|
||||
|
|
|
@ -3,6 +3,8 @@ import ../make-test-python.nix ({ pkgs, ... }:
|
|||
name = "conduit";
|
||||
in
|
||||
{
|
||||
name = "matrix-conduit";
|
||||
|
||||
nodes = {
|
||||
conduit = args: {
|
||||
services.matrix-conduit = {
|
||||
|
|
|
@ -19,6 +19,7 @@ let
|
|||
});
|
||||
|
||||
testLegacyNetwork = { nixopsPkg }: pkgs.nixosTest ({
|
||||
name = "nixops-legacy-network";
|
||||
nodes = {
|
||||
deployer = { config, lib, nodes, pkgs, ... }: {
|
||||
imports = [ ../../modules/installer/cd-dvd/channel.nix ];
|
||||
|
|
|
@ -2,6 +2,7 @@ let
|
|||
name = "pam";
|
||||
in
|
||||
import ../make-test-python.nix ({ pkgs, ... }: {
|
||||
name = "pam-file-contents";
|
||||
|
||||
nodes.machine = { ... }: {
|
||||
imports = [ ../../modules/profiles/minimal.nix ];
|
||||
|
|
|
@ -5,6 +5,8 @@ import ./make-test-python.nix (
|
|||
mode = "0640";
|
||||
};
|
||||
in {
|
||||
name = "pppd";
|
||||
|
||||
nodes = {
|
||||
server = {config, pkgs, ...}: {
|
||||
config = {
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import ./make-test-python.nix {
|
||||
name = "thelounge";
|
||||
|
||||
nodes = {
|
||||
private = { config, pkgs, ... }: {
|
||||
services.thelounge = {
|
||||
|
|
|
@ -1,29 +1,27 @@
|
|||
import ../make-test-python.nix (
|
||||
{ pkgs, lib, ... }:
|
||||
{
|
||||
name = "agate";
|
||||
meta = with lib.maintainers; { maintainers = [ jk ]; };
|
||||
{ pkgs, lib, ... }:
|
||||
{
|
||||
name = "agate";
|
||||
meta = with lib.maintainers; { maintainers = [ jk ]; };
|
||||
|
||||
nodes = {
|
||||
geminiserver = { pkgs, ... }: {
|
||||
services.agate = {
|
||||
enable = true;
|
||||
hostnames = [ "localhost" ];
|
||||
contentDir = pkgs.writeTextDir "index.gmi" ''
|
||||
# Hello NixOS!
|
||||
'';
|
||||
};
|
||||
nodes = {
|
||||
geminiserver = { pkgs, ... }: {
|
||||
services.agate = {
|
||||
enable = true;
|
||||
hostnames = [ "localhost" ];
|
||||
contentDir = pkgs.writeTextDir "index.gmi" ''
|
||||
# Hello NixOS!
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
testScript = { nodes, ... }: ''
|
||||
geminiserver.wait_for_unit("agate")
|
||||
geminiserver.wait_for_open_port(1965)
|
||||
testScript = { nodes, ... }: ''
|
||||
geminiserver.wait_for_unit("agate")
|
||||
geminiserver.wait_for_open_port(1965)
|
||||
|
||||
with subtest("check is serving over gemini"):
|
||||
response = geminiserver.succeed("${pkgs.gmni}/bin/gmni -j once -i -N gemini://localhost:1965")
|
||||
print(response)
|
||||
assert "Hello NixOS!" in response
|
||||
'';
|
||||
}
|
||||
)
|
||||
with subtest("check is serving over gemini"):
|
||||
response = geminiserver.succeed("${pkgs.gmni}/bin/gmni -j once -i -N gemini://localhost:1965")
|
||||
print(response)
|
||||
assert "Hello NixOS!" in response
|
||||
'';
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import ./make-test-python.nix (
|
||||
{
|
||||
name = "zrepl";
|
||||
|
||||
nodes.host = {config, pkgs, ...}: {
|
||||
config = {
|
||||
# Prerequisites for ZFS and tests.
|
||||
|
|
|
@ -140,14 +140,14 @@ with pkgs;
|
|||
nixosTests = import ../../nixos/tests/all-tests.nix {
|
||||
inherit pkgs;
|
||||
system = stdenv.hostPlatform.system;
|
||||
callTest = t: t.test;
|
||||
callTest = config: config.test;
|
||||
} // {
|
||||
# for typechecking of the scripts and evaluation of
|
||||
# the nodes, without running VMs.
|
||||
allDrivers = import ../../nixos/tests/all-tests.nix {
|
||||
inherit pkgs;
|
||||
system = stdenv.hostPlatform.system;
|
||||
callTest = t: t.test.driver;
|
||||
callTest = config: config.test.driver;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue