nixtest/lib/module.nix

198 lines
5 KiB
Nix

{
pkgs,
lib,
...
}: let
inherit
(lib)
mkOptionType
mkOption
types
filterAttrs
isType
removePrefix
assertMsg
generators
;
nixtest-lib = import ./default.nix {inherit pkgs lib;};
unsetType = mkOptionType {
name = "unset";
description = "unset";
descriptionClass = "noun";
check = value: true;
};
unset = {
_type = "unset";
};
isUnset = isType "unset";
filterUnset = value:
if builtins.isAttrs value && !builtins.hasAttr "_type" value
then let
filteredAttrs = builtins.mapAttrs (n: v: filterUnset v) value;
in
filterAttrs (name: value: (!isUnset value)) filteredAttrs
else if builtins.isList value
then builtins.filter (elem: !isUnset elem) (map filterUnset value)
else value;
testsSubmodule = {
config,
testsBase,
pos,
...
}: {
options = {
pos = mkOption {
type = types.either types.attrs unsetType;
default = pos;
apply = val:
if isUnset val
then val
else let
fileRelative = removePrefix testsBase val.file;
in "${fileRelative}:${toString val.line}";
};
type = mkOption {
type = types.enum ["unit" "snapshot" "script"];
default = "unit";
apply = value:
assert assertMsg (value != "script" || !isUnset config.script)
"test '${config.name}' as type 'script' requires 'script' to be set";
assert assertMsg (value != "unit" || !isUnset config.expected)
"test '${config.name}' as type 'unit' requires 'expected' to be set";
assert assertMsg (
let
actualIsUnset = isUnset config.actual;
actualDrvIsUnset = isUnset config.actualDrv;
in
(value != "unit")
|| (!actualIsUnset && actualDrvIsUnset)
|| (actualIsUnset && !actualDrvIsUnset)
)
"test '${config.name}' as type 'unit' requires only 'actual' OR 'actualDrv' to be set"; value;
};
name = mkOption {
type = types.str;
};
description = mkOption {
type = types.either types.str unsetType;
default = unset;
};
format = mkOption {
type = types.enum ["json" "pretty"];
default = "json";
};
expected = mkOption {
type = types.anything;
default = unset;
apply = val:
if isUnset val || config.format == "json"
then val
else generators.toPretty {} val;
};
actual = mkOption {
type = types.anything;
default = unset;
apply = val:
if isUnset val || config.format == "json"
then val
else generators.toPretty {} val;
};
actualDrv = mkOption {
type = types.either types.package unsetType;
default = unset;
apply = val:
# keep unset value
if isUnset val
then val
else builtins.unsafeDiscardStringContext (val.drvPath or "");
};
script = mkOption {
type = types.either types.str unsetType;
default = unset;
apply = val:
if isUnset val
then val
else
builtins.unsafeDiscardStringContext
(pkgs.writeShellScript "nixtest-${config.name}" val).drvPath;
};
};
};
suitesSubmodule = {
name,
config,
testsBase,
...
}: {
options = {
name = mkOption {
type = types.str;
default = name;
};
pos = mkOption {
type = types.either types.attrs unsetType;
default = unset;
};
tests = mkOption {
type = types.listOf (types.submoduleWith {
modules = [testsSubmodule];
specialArgs = {
inherit (config) pos;
inherit testsBase;
};
});
default = [];
};
};
};
nixtestSubmodule = {config, ...}: {
options = {
base = mkOption {
description = "Base directory of the tests, will be removed from the test file path";
type = types.str;
default = "";
};
skip = mkOption {
type = types.str;
default = "";
};
suites = mkOption {
type = types.attrsOf (types.submoduleWith {
modules = [suitesSubmodule];
specialArgs = {
testsBase = config.base;
};
});
default = {};
apply = suites:
map (
n: filterUnset (builtins.removeAttrs suites.${n} ["pos"])
)
(builtins.attrNames suites);
};
finalConfigJson = mkOption {
internal = true;
type = types.package;
};
app = mkOption {
internal = true;
type = types.package;
};
};
config = {
finalConfigJson = nixtest-lib.exportSuites config.suites;
app = nixtest-lib.mkBinary {
nixtests = config.finalConfigJson;
extraParams = ''--skip="${config.skip}"'';
};
};
};
in
nixtestSubmodule