2019-09-06 09:25:22 +02:00
|
|
|
{ system
|
|
|
|
, pkgs ? import ../.. { inherit system config; }
|
|
|
|
# Use a minimal kernel?
|
|
|
|
, minimal ? false
|
|
|
|
# Ignored
|
2020-10-23 23:13:38 +02:00
|
|
|
, config ? { }
|
2020-06-02 16:27:07 +02:00
|
|
|
# !!! See comment about args in lib/modules.nix
|
2020-10-23 23:13:38 +02:00
|
|
|
, specialArgs ? { }
|
2019-09-06 09:25:22 +02:00
|
|
|
# Modules to add to each VM
|
2020-10-23 23:13:38 +02:00
|
|
|
, extraConfigurations ? [ ]
|
|
|
|
}:
|
2019-09-06 09:25:22 +02:00
|
|
|
|
|
|
|
with pkgs;
|
|
|
|
|
2020-05-14 14:34:50 +02:00
|
|
|
rec {
|
2019-09-06 09:25:22 +02:00
|
|
|
|
|
|
|
inherit pkgs;
|
|
|
|
|
2021-06-06 18:36:07 +02:00
|
|
|
# Reifies and correctly wraps the python test driver for
|
|
|
|
# the respective qemu version and with or without ocr support
|
|
|
|
pythonTestDriver = {
|
|
|
|
qemu_pkg ? pkgs.qemu_test
|
|
|
|
, enableOCR ? false
|
|
|
|
}:
|
2020-10-23 23:13:38 +02:00
|
|
|
let
|
|
|
|
name = "nixos-test-driver";
|
2021-06-06 18:36:07 +02:00
|
|
|
testDriverScript = ./test-driver/test-driver.py;
|
|
|
|
ocrProg = tesseract4.override { enableLanguages = [ "eng" ]; };
|
|
|
|
imagemagick_tiff = imagemagick_light.override { inherit libtiff; };
|
|
|
|
in stdenv.mkDerivation {
|
|
|
|
inherit name;
|
2019-09-06 09:25:22 +02:00
|
|
|
|
2020-10-23 23:13:38 +02:00
|
|
|
nativeBuildInputs = [ makeWrapper ];
|
2021-05-15 01:57:11 +02:00
|
|
|
buildInputs = [ (python3.withPackages (p: [ p.ptpython p.colorama ])) ];
|
2020-10-23 23:13:38 +02:00
|
|
|
checkInputs = with python3Packages; [ pylint black mypy ];
|
2019-09-06 09:25:22 +02:00
|
|
|
|
2020-10-23 23:13:38 +02:00
|
|
|
dontUnpack = true;
|
2019-09-06 09:25:22 +02:00
|
|
|
|
2020-10-23 23:13:38 +02:00
|
|
|
preferLocalBuild = true;
|
2019-09-06 09:25:22 +02:00
|
|
|
|
2021-05-08 17:52:22 +02:00
|
|
|
buildPhase = ''
|
|
|
|
python <<EOF
|
|
|
|
from pydoc import importfile
|
2021-06-06 18:36:07 +02:00
|
|
|
with open('driver-symbols', 'w') as fp:
|
2021-06-06 20:50:02 +02:00
|
|
|
t = importfile('${testDriverScript}')
|
2021-06-13 00:47:25 +02:00
|
|
|
d = t.Driver([],[],"")
|
|
|
|
test_symbols = d.test_symbols()
|
2021-06-06 20:50:02 +02:00
|
|
|
fp.write(','.join(test_symbols.keys()))
|
2021-05-08 17:52:22 +02:00
|
|
|
EOF
|
|
|
|
'';
|
|
|
|
|
2020-10-23 23:13:38 +02:00
|
|
|
doCheck = true;
|
|
|
|
checkPhase = ''
|
|
|
|
mypy --disallow-untyped-defs \
|
|
|
|
--no-implicit-optional \
|
|
|
|
--ignore-missing-imports ${testDriverScript}
|
|
|
|
pylint --errors-only ${testDriverScript}
|
|
|
|
black --check --diff ${testDriverScript}
|
|
|
|
'';
|
2019-09-06 09:25:22 +02:00
|
|
|
|
2020-10-23 23:13:38 +02:00
|
|
|
installPhase =
|
|
|
|
''
|
|
|
|
mkdir -p $out/bin
|
|
|
|
cp ${testDriverScript} $out/bin/nixos-test-driver
|
|
|
|
chmod u+x $out/bin/nixos-test-driver
|
|
|
|
# TODO: copy user script part into this file (append)
|
2019-09-06 09:25:22 +02:00
|
|
|
|
2020-10-23 23:13:38 +02:00
|
|
|
wrapProgram $out/bin/nixos-test-driver \
|
2021-06-06 18:36:07 +02:00
|
|
|
--argv0 ${name} \
|
2021-06-22 08:51:39 +02:00
|
|
|
--prefix PATH : "${lib.makeBinPath [ qemu_pkg vde2 netpbm coreutils socat ]}" \
|
2021-06-06 18:36:07 +02:00
|
|
|
${lib.optionalString enableOCR
|
|
|
|
"--prefix PATH : '${ocrProg}/bin:${imagemagick_tiff}/bin'"} \
|
2021-05-08 17:52:22 +02:00
|
|
|
|
2021-06-06 18:36:07 +02:00
|
|
|
install -m 0644 -vD driver-symbols $out/nix-support/driver-symbols
|
2020-10-23 23:13:38 +02:00
|
|
|
'';
|
|
|
|
};
|
2019-09-06 09:25:22 +02:00
|
|
|
|
|
|
|
# Run an automated test suite in the given virtual network.
|
2021-06-06 18:36:07 +02:00
|
|
|
runTests = { driver, pos }:
|
2019-09-06 09:25:22 +02:00
|
|
|
stdenv.mkDerivation {
|
|
|
|
name = "vm-test-run-${driver.testName}";
|
|
|
|
|
|
|
|
requiredSystemFeatures = [ "kvm" "nixos-test" ];
|
|
|
|
|
|
|
|
buildCommand =
|
|
|
|
''
|
2020-05-07 15:56:30 +02:00
|
|
|
mkdir -p $out
|
2019-09-06 09:25:22 +02:00
|
|
|
|
2021-06-06 19:00:12 +02:00
|
|
|
# effectively mute the XMLLogger
|
|
|
|
export LOGFILE=/dev/null
|
|
|
|
|
|
|
|
${driver}/bin/nixos-test-driver
|
2019-09-06 09:25:22 +02:00
|
|
|
'';
|
2020-12-09 12:59:39 +01:00
|
|
|
|
2021-05-08 17:24:47 +02:00
|
|
|
passthru = driver.passthru // {
|
|
|
|
inherit driver;
|
|
|
|
};
|
2021-02-09 12:02:35 +01:00
|
|
|
|
2021-06-06 18:36:07 +02:00
|
|
|
inherit pos; # for better debugging
|
2019-09-06 09:25:22 +02:00
|
|
|
};
|
|
|
|
|
2021-06-06 18:36:07 +02:00
|
|
|
# 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
|
|
|
|
, passthru ? {}
|
|
|
|
}:
|
|
|
|
let
|
|
|
|
# FIXME: get this pkg from the module system
|
|
|
|
testDriver = pythonTestDriver { inherit qemu_pkg enableOCR;};
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2021-11-19 23:02:30 +01:00
|
|
|
nodeHostNames = let
|
|
|
|
nodesList = map (c: c.config.system.name) (lib.attrValues nodes);
|
|
|
|
in nodesList ++ lib.optional (lib.length nodesList == 1) "machine";
|
2021-06-06 18:36:07 +02:00
|
|
|
|
2021-07-28 02:38:10 +02:00
|
|
|
# TODO: This is an implementation error and needs fixing
|
|
|
|
# the testing famework cannot legitimately restrict hostnames further
|
|
|
|
# beyond RFC1035
|
2021-06-06 18:36:07 +02:00
|
|
|
invalidNodeNames = lib.filter
|
|
|
|
(node: builtins.match "^[A-z_]([A-z0-9_]+)?$" node == null)
|
2021-07-28 02:38:10 +02:00
|
|
|
nodeHostNames;
|
2021-06-06 18:36:07 +02:00
|
|
|
|
|
|
|
testScript' =
|
|
|
|
# Call the test script with the computed nodes.
|
|
|
|
if lib.isFunction testScript
|
|
|
|
then testScript { inherit nodes; }
|
|
|
|
else testScript;
|
2019-09-06 09:25:22 +02:00
|
|
|
|
2021-06-06 18:36:07 +02:00
|
|
|
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.
|
2021-07-28 02:38:10 +02:00
|
|
|
|
|
|
|
This is an IMPLEMENTATION ERROR and needs to be fixed. Meanwhile,
|
|
|
|
please stick to alphanumeric chars and underscores as separation.
|
2021-06-06 18:36:07 +02:00
|
|
|
''
|
|
|
|
else lib.warnIf skipLint "Linting is disabled" (runCommand testDriverName
|
|
|
|
{
|
|
|
|
inherit testName;
|
|
|
|
nativeBuildInputs = [ makeWrapper ];
|
|
|
|
testScript = testScript';
|
|
|
|
preferLocalBuild = true;
|
|
|
|
passthru = passthru // {
|
|
|
|
inherit nodes;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
''
|
|
|
|
mkdir -p $out/bin
|
|
|
|
|
2021-06-06 19:00:12 +02:00
|
|
|
vmStartScripts=($(for i in ${toString vms}; do echo $i/bin/run-*-vm; done))
|
2021-06-06 18:36:07 +02:00
|
|
|
echo -n "$testScript" > $out/test-script
|
2021-06-06 19:00:12 +02:00
|
|
|
ln -s ${testDriver}/bin/nixos-test-driver $out/bin/nixos-test-driver
|
|
|
|
|
2021-06-06 18:36:07 +02:00
|
|
|
${lib.optionalString (!skipLint) ''
|
|
|
|
PYFLAKES_BUILTINS="$(
|
|
|
|
echo -n ${lib.escapeShellArg (lib.concatStringsSep "," nodeHostNames)},
|
|
|
|
< ${lib.escapeShellArg "${testDriver}/nix-support/driver-symbols"}
|
|
|
|
)" ${python3Packages.pyflakes}/bin/pyflakes $out/test-script
|
|
|
|
''}
|
|
|
|
|
2021-06-06 19:00:12 +02:00
|
|
|
# set defaults through environment
|
|
|
|
# see: ./test-driver/test-driver.py argparse implementation
|
2021-06-06 18:36:07 +02:00
|
|
|
wrapProgram $out/bin/nixos-test-driver \
|
2021-06-06 19:00:12 +02:00
|
|
|
--set startScripts "''${vmStartScripts[*]}" \
|
|
|
|
--set testScript "$out/test-script" \
|
|
|
|
--set vlans '${toString vlans}'
|
2021-06-06 18:36:07 +02:00
|
|
|
'');
|
|
|
|
|
|
|
|
# Make a full-blown test
|
2019-09-06 09:25:22 +02:00
|
|
|
makeTest =
|
|
|
|
{ testScript
|
|
|
|
, enableOCR ? false
|
|
|
|
, name ? "unnamed"
|
2020-10-23 23:13:38 +02:00
|
|
|
# Skip linting (mainly intended for faster dev cycles)
|
2019-12-22 14:50:08 +01:00
|
|
|
, skipLint ? false
|
2020-12-09 12:59:39 +01:00
|
|
|
, passthru ? {}
|
2021-02-09 12:02:35 +01:00
|
|
|
, # For meta.position
|
|
|
|
pos ? # position used in error messages and for meta.position
|
|
|
|
(if t.meta.description or null != null
|
|
|
|
then builtins.unsafeGetAttrPos "description" t.meta
|
|
|
|
else builtins.unsafeGetAttrPos "testScript" t)
|
2019-09-06 09:25:22 +02:00
|
|
|
, ...
|
|
|
|
} @ t:
|
|
|
|
let
|
2021-06-06 18:36:07 +02:00
|
|
|
nodes = qemu_pkg:
|
2020-10-23 23:13:38 +02:00
|
|
|
let
|
2021-10-06 18:47:56 +02:00
|
|
|
testScript' =
|
|
|
|
# Call the test script with the computed nodes.
|
|
|
|
if lib.isFunction testScript
|
|
|
|
then testScript { nodes = nodes qemu_pkg; }
|
|
|
|
else testScript;
|
|
|
|
|
2020-10-23 23:09:18 +02:00
|
|
|
build-vms = import ./build-vms.nix {
|
2021-06-23 17:46:46 +02:00
|
|
|
inherit system lib pkgs minimal specialArgs;
|
2021-06-06 18:36:07 +02:00
|
|
|
extraConfigurations = extraConfigurations ++ [(
|
2021-10-31 18:22:25 +01:00
|
|
|
{ config, ... }:
|
2020-10-23 23:09:18 +02:00
|
|
|
{
|
|
|
|
virtualisation.qemu.package = qemu_pkg;
|
2021-10-06 18:47:56 +02:00
|
|
|
|
|
|
|
# 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
|
2021-10-31 18:22:25 +01:00
|
|
|
# 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')
|
2021-10-06 18:47:56 +02:00
|
|
|
(pkgs.writeStringReferencesToFile testScript');
|
|
|
|
|
2020-11-07 13:04:50 +01:00
|
|
|
# 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;
|
|
|
|
}
|
|
|
|
)];
|
2020-10-23 23:09:18 +02:00
|
|
|
};
|
2020-10-05 15:52:27 +02:00
|
|
|
in
|
2021-06-06 18:36:07 +02:00
|
|
|
build-vms.buildVirtualNetwork (
|
|
|
|
t.nodes or (if t ? machine then { machine = t.machine; } else { })
|
|
|
|
);
|
2019-09-06 09:25:22 +02:00
|
|
|
|
2021-06-06 18:36:07 +02:00
|
|
|
driver = setupDriverForTest {
|
2021-06-18 14:16:27 +02:00
|
|
|
inherit testScript enableOCR skipLint passthru;
|
2021-06-06 18:36:07 +02:00
|
|
|
testName = name;
|
|
|
|
qemu_pkg = pkgs.qemu_test;
|
|
|
|
nodes = nodes pkgs.qemu_test;
|
|
|
|
};
|
|
|
|
driverInteractive = setupDriverForTest {
|
2021-06-18 14:16:27 +02:00
|
|
|
inherit testScript enableOCR skipLint passthru;
|
2021-06-06 18:36:07 +02:00
|
|
|
testName = name;
|
|
|
|
qemu_pkg = pkgs.qemu;
|
|
|
|
nodes = nodes pkgs.qemu;
|
|
|
|
};
|
2019-09-06 09:25:22 +02:00
|
|
|
|
2021-06-06 18:36:07 +02:00
|
|
|
test =
|
|
|
|
let
|
|
|
|
passMeta = drv: drv // lib.optionalAttrs (t ? meta) {
|
|
|
|
meta = (drv.meta or { }) // t.meta;
|
|
|
|
};
|
|
|
|
in passMeta (runTests { inherit driver pos; });
|
2021-05-08 17:52:22 +02:00
|
|
|
|
2019-09-06 09:25:22 +02:00
|
|
|
in
|
2020-10-23 23:09:18 +02:00
|
|
|
test // {
|
2021-06-06 18:36:07 +02:00
|
|
|
inherit test driver driverInteractive nodes;
|
2020-10-23 23:13:38 +02:00
|
|
|
};
|
2019-09-06 09:25:22 +02:00
|
|
|
|
2021-09-07 18:36:11 +02:00
|
|
|
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
|
|
|
|
'';
|
2019-09-06 09:25:22 +02:00
|
|
|
|
2021-09-07 18:36:11 +02:00
|
|
|
runInMachine = abortForFunction "runInMachine";
|
2019-09-06 09:25:22 +02:00
|
|
|
|
2021-09-07 18:36:11 +02:00
|
|
|
runInMachineWithX = abortForFunction "runInMachineWithX";
|
2019-09-06 09:25:22 +02:00
|
|
|
|
|
|
|
simpleTest = as: (makeTest as).test;
|
|
|
|
|
|
|
|
}
|