Files
komorebi/nix/linux/tests/nixos-module.nix
T
LGUG2Z 259575a1b3 build: route the workspace to crates/
The workspace members, the path dependencies, the include_str! and test
fixture paths, the recorder's indexed source keys, and the Nix source
filter and path literals follow the new layout. Cargo.lock is unchanged.
2026-11-05 15:03:27 -08:00

886 lines
30 KiB
Nix

# `nixos-module-eval`: `services.komorebi` in real NixOS systems, evaluated and never built. Each
# scenario is a `nixosSystem` with the module, and the checks read what it evaluates to: the
# assertions and warnings, the unit the module defines, and its read-only `execStart`. A scenario
# that must not evaluate is caught with `builtins.tryEval`. Nothing here builds a Linux package
# (the derivation below gets only check names and booleans), so a Mac runs it too.
{
pkgs,
# nixpkgs' `lib.nixosSystem`
nixosSystem,
# the module, as the flake exports it, with its overlay
module,
# the flake's komorebi-full for a system, which the overlay has to provide
komorebiFor,
# the flake's KDE Rounded Corners for a system, which the overlay has to provide as well
borderEffectFor,
# the flake's Geometry Change for a system, likewise
animationEffectFor,
}:
let
inherit (pkgs) lib;
# the systems are of the host's architecture
system = "${pkgs.stdenv.hostPlatform.parsed.cpu.name}-linux";
# enough of a machine for every NixOS assertion to hold, since no scenario is ever built
machine = {
nixpkgs.hostPlatform = system;
boot.loader.grub.enable = false;
fileSystems."/" = {
device = "/dev/vda";
fsType = "ext4";
};
system.stateVersion = lib.trivial.release;
};
plasma = {
services.desktopManager.plasma6.enable = true;
};
eval =
scenario:
(nixosSystem {
modules = [
module
machine
scenario
];
}).config;
enabled = eval {
imports = [ plasma ];
services.komorebi.enable = true;
};
disabled = eval plasma;
withoutPlasma = eval { services.komorebi.enable = true; };
debug = eval {
imports = [ plasma ];
services.komorebi = {
enable = true;
logLevel = "debug";
};
};
# a package that is not the overlay's: any package will do, since nothing is built
otherPackage = eval (
{ pkgs, ... }:
{
imports = [ plasma ];
services.komorebi = {
enable = true;
package = pkgs.hello;
};
}
);
withConfigFile = eval (
{ pkgs, ... }:
{
imports = [ plasma ];
services.komorebi = {
enable = true;
configFile = pkgs.writeText "komorebi.json" "{}";
};
}
);
withConfig = eval {
imports = [ plasma ];
services.komorebi = {
enable = true;
config = {
default_workspace_padding = 20;
window_container_behaviour = "Append";
float_override_placement = "Center";
monocle_focus_behaviour = "Cycle";
monitors = [
{
floating_layer_behaviour = "Tile";
workspaces = [
{
name = "web";
layout = "BSP";
}
{
name = "code";
layout = "Columns";
window_container_behaviour_rules."2" = "Append";
float_override = false;
floating_layer_behaviour = "Float";
}
];
}
];
};
};
};
# where a workspace puts the windows that open, in each form komorebi reads: a name, a number,
# text that is a number, and a map of places to rules
withPlacement = eval {
imports = [ plasma ];
services.komorebi = {
enable = true;
config.monitors = [
{
workspaces = [
{
name = "name";
initial_window_placement_rules = "Primary";
}
{
name = "number";
initial_window_placement_rules = 3;
}
{
name = "text";
initial_window_placement_rules = "3";
}
{
name = "map";
initial_window_placement_rules = {
Primary = {
kind = "Exe";
id = "firefox";
matching_strategy = "Equals";
};
};
}
];
}
];
};
};
# a layout komorebi does not have
badLayout = eval {
imports = [ plasma ];
services.komorebi = {
enable = true;
config.monitors = [
{
workspaces = [
{
name = "x";
layout = "Nope";
}
];
}
];
};
};
withSource = eval (
{ pkgs, ... }:
{
imports = [ plasma ];
services.komorebi = {
enable = true;
config.resize_delta = 70;
applicationSpecificConfiguration.sources = [
{
owner = "someone";
repo = "komorebi-rules";
rev = "0123456789abcdef0123456789abcdef01234567";
hash = pkgs.lib.fakeHash;
}
];
};
}
);
# the same source and config, with the sources turned off
withSourceOff = eval (
{ pkgs, ... }:
{
imports = [ plasma ];
services.komorebi = {
enable = true;
config.resize_delta = 70;
applicationSpecificConfiguration = {
enable = false;
sources = [
{
owner = "someone";
repo = "komorebi-rules";
rev = "0123456789abcdef0123456789abcdef01234567";
hash = pkgs.lib.fakeHash;
}
];
};
};
}
);
# the sources turned off, and no config: nothing is left to write
onlySourceOff = eval (
{ pkgs, ... }:
{
imports = [ plasma ];
services.komorebi = {
enable = true;
applicationSpecificConfiguration = {
enable = false;
sources = [
{
owner = "someone";
repo = "komorebi-rules";
rev = "0123456789abcdef0123456789abcdef01234567";
hash = pkgs.lib.fakeHash;
}
];
};
};
}
);
bothConfigs = eval (
{ pkgs, ... }:
{
imports = [ plasma ];
services.komorebi = {
enable = true;
configFile = pkgs.writeText "komorebi.json" "{}";
config.resize_delta = 70;
};
}
);
noBorderEffect = eval {
imports = [ plasma ];
services.komorebi = {
enable = true;
effects.borders.enable = false;
};
};
# borders asked for in the configuration, and the effect that draws them left out
borderWithoutEffect = eval {
imports = [ plasma ];
services.komorebi = {
enable = true;
config.border = true;
effects.borders.enable = false;
};
};
# the same, with the effect installed, and with borders turned off in the configuration
borderWithEffect = eval {
imports = [ plasma ];
services.komorebi = {
enable = true;
config.border = true;
};
};
noBorderWithoutEffect = eval {
imports = [ plasma ];
services.komorebi = {
enable = true;
config.border = false;
effects.borders.enable = false;
};
};
noAnimationEffect = eval {
imports = [ plasma ];
services.komorebi = {
enable = true;
effects.animation.enable = false;
};
};
# a package that is not the overlay's: any package will do, since nothing is built
otherBorderEffect = eval (
{ pkgs, ... }:
{
imports = [ plasma ];
services.komorebi = {
enable = true;
effects.borders.package = pkgs.hello;
};
}
);
unknownLevel = eval {
imports = [ plasma ];
services.komorebi = {
enable = true;
logLevel = "verbose";
};
};
execStartSet = eval {
imports = [ plasma ];
services.komorebi = {
enable = true;
execStart = "/bin/true";
};
};
keybindings =
settings:
eval {
imports = [ plasma ];
services.komorebi = {
enable = true;
keybindings = settings;
};
};
withText = keybindings {
format = "skhd";
text = "alt - h : komorebic focus left\n";
mode = "auto";
chordTimeout = 1500;
appAliases.terminal = [
"foot"
"konsole"
];
};
withFile = keybindings {
format = "sxhkd";
file = ../../../crates/komorebi-hotkeys/tests/fixtures/plain.sxhkdrc;
};
textAndFile = keybindings {
format = "skhd";
text = "alt - h : komorebic focus left\n";
file = ../../../crates/komorebi-hotkeys/tests/fixtures/plain.skhdrc;
};
# the typed `config` and the key bindings are two parts of one command line
withConfigAndKeybindings = eval {
imports = [ plasma ];
services.komorebi = {
enable = true;
config.default_workspace_padding = 20;
keybindings = {
format = "skhd";
text = "alt - h : komorebic focus left\n";
};
};
};
withoutFormat = keybindings { text = "alt - h : komorebic focus left\n"; };
withLoad = keybindings {
format = "skhd";
text = ".load \"/home/me/.config/skhd/more\"\nalt - h : komorebic focus left\n";
};
# a file with a line komorebic cannot read, which the build only reports unless `strict` is set.
# Evaluation does not read the bindings the way komorebic does (it looks for skhd's .load and no
# more), so what `strict` changes is the build of the compiled key bindings: its script is what
# these checks read, and the build that fails is not run here.
unreadableLine = "alt - h : komorebic focus left\nalt - nosuchkey : komorebic focus up\n";
withStrict = keybindings {
format = "skhd";
text = unreadableLine;
strict = true;
};
withoutStrict = keybindings {
format = "skhd";
text = unreadableLine;
};
# the package that compiles the key bindings, and the script that builds it
compiled =
config: lib.findFirst (p: p.name == "komorebi-keybindings") null config.environment.systemPackages;
compileScript = config: (compiled config).buildCommand;
installed = config: map (p: p.name) config.environment.systemPackages;
supportedKwin = import ../supported-kwin.nix;
# The scenarios above have the KWin of the flake's nixpkgs, which the weekly canary moves to the
# tip of nixpkgs-unstable, so no check depends on its version. These give the system a KWin of
# a version of their own: nothing is built, so the package keeps its store path and only the
# version the module reads changes.
kwinAt = version: {
nixpkgs.overlays = [
(_: prev: {
kdePackages = prev.kdePackages // {
kwin = prev.kdePackages.kwin // {
inherit version;
};
};
})
];
};
withKwin =
version: settings:
eval {
imports = [
plasma
(kwinAt version)
];
services.komorebi = {
enable = true;
}
// settings;
};
# a KWin of Plasma 6.7, and one of the next major version after the newest one tested
olderKwin = "6.7.5";
newerKwin = "${toString (lib.toInt (lib.versions.major supportedKwin.testedMax) + 1)}.0";
kwinTooOld = withKwin olderKwin { };
kwinAtMin = withKwin supportedKwin.min { };
kwinTested = withKwin supportedKwin.testedMax { };
kwinNewer = withKwin newerKwin { };
kwinNewerIsError = withKwin newerKwin { kwin.untestedIsError = true; };
kwinTestedIsError = withKwin supportedKwin.testedMax { kwin.untestedIsError = true; };
kwinTooOldDisabled = eval {
imports = [
plasma
(kwinAt olderKwin)
];
};
failed = config: map (a: a.message) (lib.filter (a: !a.assertion) config.assertions);
# the module's warnings, and not those other modules may have
kwinWarnings = config: lib.filter (lib.hasInfix "untestedIsError") config.warnings;
unit = config: config.systemd.user.services.komorebi;
evaluates = value: (builtins.tryEval value).success;
checks = [
{
name = "enabled with Plasma 6, every assertion holds";
ok = failed enabled == [ ];
got = failed enabled;
}
{
name = "the unit is started by plasma-workspace.target";
ok = (unit enabled).wantedBy == [ "plasma-workspace.target" ];
got = (unit enabled).wantedBy;
}
{
name = "the unit starts after plasma-core.target";
ok = lib.elem "plasma-core.target" (unit enabled).after;
got = (unit enabled).after;
}
{
name = "the unit is restarted when it fails";
ok = (unit enabled).serviceConfig.Restart == "on-failure";
}
{
name = "the unit counts the exit status of a SIGTERM (130) as success";
ok = (unit enabled).serviceConfig.SuccessExitStatus == 130;
}
{
name = "the unit releases the keys of the daemon when it stops";
ok =
(unit enabled).serviceConfig.ExecStopPost
== "${enabled.services.komorebi.package}/bin/komorebic hotkeys release";
got = (unit enabled).serviceConfig.ExecStopPost;
}
{
name = "the unit runs execStart";
ok = (unit enabled).serviceConfig.ExecStart == enabled.services.komorebi.execStart;
got = (unit enabled).serviceConfig.ExecStart;
}
{
name = "execStart is bin/komorebi of the package, at the info level";
ok =
enabled.services.komorebi.execStart
== "${enabled.services.komorebi.package}/bin/komorebi --log-level info";
got = enabled.services.komorebi.execStart;
}
{
name = "the default package is the flake's komorebi-full, from the overlay";
ok = enabled.services.komorebi.package.outPath == (komorebiFor system).outPath;
}
{
name = "the unit has the PATH of the user's session";
ok =
(unit enabled).environment.PATH
== "/run/wrappers/bin:/etc/profiles/per-user/%u/bin:/run/current-system/sw/bin";
got = (unit enabled).environment.PATH;
}
{
name = "the package is installed in the system profile";
ok = lib.elem enabled.services.komorebi.package enabled.environment.systemPackages;
}
{
name = "the default border effect is the flake's KDE Rounded Corners, from the overlay";
ok = enabled.services.komorebi.effects.borders.package.outPath == (borderEffectFor system).outPath;
}
{
name = "the border effect is installed in the system profile";
ok = lib.elem enabled.services.komorebi.effects.borders.package enabled.environment.systemPackages;
}
{
name = "with the border effect off, komorebi is installed and the effect is not";
ok =
lib.elem noBorderEffect.services.komorebi.package noBorderEffect.environment.systemPackages
&& !(lib.elem noBorderEffect.services.komorebi.effects.borders.package noBorderEffect.environment.systemPackages);
}
{
name = "another border effect is the one installed";
ok =
lib.elem otherBorderEffect.services.komorebi.effects.borders.package otherBorderEffect.environment.systemPackages
&& !(lib.elem enabled.services.komorebi.effects.borders.package otherBorderEffect.environment.systemPackages);
}
{
name = "the default animation effect is the flake's Geometry Change, from the overlay";
ok =
enabled.services.komorebi.effects.animation.package.outPath == (animationEffectFor system).outPath;
}
{
name = "the animation effect is installed in the system profile";
ok = lib.elem enabled.services.komorebi.effects.animation.package enabled.environment.systemPackages;
}
{
name = "with the animation effect off, the border effect is installed and the animation effect is not";
ok =
lib.elem noAnimationEffect.services.komorebi.effects.borders.package noAnimationEffect.environment.systemPackages
&& !(lib.elem noAnimationEffect.services.komorebi.effects.animation.package noAnimationEffect.environment.systemPackages);
}
{
name = "a configuration that draws borders, without the effect that draws them, is warned about";
ok = lib.any (lib.hasInfix "effects.borders.enable is false") borderWithoutEffect.warnings;
got = borderWithoutEffect.warnings;
}
{
name = "borders with the effect, and no borders without it, are not warned about";
ok =
!(lib.any (lib.hasInfix "effects.borders") borderWithEffect.warnings)
&& !(lib.any (lib.hasInfix "effects.borders") noBorderWithoutEffect.warnings)
&& !(lib.any (lib.hasInfix "effects.borders") noBorderEffect.warnings);
got = borderWithEffect.warnings ++ noBorderWithoutEffect.warnings ++ noBorderEffect.warnings;
}
{
name = "disabled, every assertion holds";
ok = failed disabled == [ ];
got = failed disabled;
}
{
name = "disabled, there is no unit";
ok = !(disabled.systemd.user.services ? komorebi);
}
{
name = "disabled, nothing is installed";
ok =
!(lib.elem disabled.services.komorebi.package disabled.environment.systemPackages)
&& !(lib.elem disabled.services.komorebi.effects.borders.package disabled.environment.systemPackages)
&& !(lib.elem disabled.services.komorebi.effects.animation.package disabled.environment.systemPackages);
}
{
name = "without Plasma 6, an assertion fails and names the option to set";
ok = lib.any (lib.hasInfix "services.desktopManager.plasma6.enable = true") (failed withoutPlasma);
got = failed withoutPlasma;
}
{
name = "logLevel reaches execStart and the unit";
ok =
debug.services.komorebi.execStart
== "${debug.services.komorebi.package}/bin/komorebi --log-level debug"
&& (unit debug).serviceConfig.ExecStart == debug.services.komorebi.execStart;
got = (unit debug).serviceConfig.ExecStart;
}
{
name = "another package is the one the unit runs and the one installed";
ok =
(unit otherPackage).serviceConfig.ExecStart
== "${otherPackage.services.komorebi.package}/bin/komorebi --log-level info"
&& lib.elem otherPackage.services.komorebi.package otherPackage.environment.systemPackages;
got = (unit otherPackage).serviceConfig.ExecStart;
}
{
name = "without a configFile, the unit passes no --config";
ok = !(lib.hasInfix "--config" enabled.services.komorebi.execStart);
got = enabled.services.komorebi.execStart;
}
{
name = "configFile reaches execStart and the unit as --config, after the log level";
ok =
lib.hasPrefix "${withConfigFile.services.komorebi.package}/bin/komorebi --log-level info --config /nix/store/" withConfigFile.services.komorebi.execStart
&& (unit withConfigFile).serviceConfig.ExecStart == withConfigFile.services.komorebi.execStart;
got = (unit withConfigFile).serviceConfig.ExecStart;
}
{
name = "with neither a configFile nor a config, there is nothing to write";
ok = enabled.services.komorebi.internal.configAttrs == null;
}
{
name = "config is the komorebi.json the daemon is given, with no null left in it";
ok =
withConfig.services.komorebi.internal.configAttrs.default_workspace_padding == 20
&&
map (w: w.name) (lib.head withConfig.services.komorebi.internal.configAttrs.monitors).workspaces
== [
"web"
"code"
]
&& !(lib.hasInfix "null" (builtins.toJSON withConfig.services.komorebi.internal.configAttrs));
got = withConfig.services.komorebi.internal.configAttrs;
}
{
name = "the keys of the stacks, the float override, monocle and the floating layer are typed options";
ok =
withConfig.services.komorebi.internal.configAttrs.window_container_behaviour == "Append"
&& withConfig.services.komorebi.internal.configAttrs.float_override_placement == "Center"
&& withConfig.services.komorebi.internal.configAttrs.monocle_focus_behaviour == "Cycle"
&&
(lib.head withConfig.services.komorebi.internal.configAttrs.monitors).floating_layer_behaviour
== "Tile"
&&
(lib.elemAt (lib.head withConfig.services.komorebi.internal.configAttrs.monitors).workspaces 1)
.window_container_behaviour_rules."2" == "Append";
got = withConfig.services.komorebi.internal.configAttrs;
}
{
name = "the place a workspace gives the windows that open is a typed option in each form it is read";
ok =
let
workspaces = (lib.head withPlacement.services.komorebi.internal.configAttrs.monitors).workspaces;
place = name: (lib.findFirst (w: w.name == name) null workspaces).initial_window_placement_rules;
in
place "name" == "Primary"
&& place "number" == 3
&& place "text" == "3"
&& (place "map").Primary.id == "firefox";
got = withPlacement.services.komorebi.internal.configAttrs;
}
{
name = "config reaches execStart as a komorebi.json that was checked against the schema";
ok =
lib.hasInfix " --config " withConfig.services.komorebi.execStart
&& lib.hasSuffix "-validated-komorebi.json" withConfig.services.komorebi.execStart
&& (unit withConfig).serviceConfig.ExecStart == withConfig.services.komorebi.execStart;
got = withConfig.services.komorebi.execStart;
}
{
name = "configFile is checked against the schema too";
ok = lib.hasSuffix "-validated-komorebi.json" withConfigFile.services.komorebi.execStart;
got = withConfigFile.services.komorebi.execStart;
}
{
name = "a layout komorebi does not have does not evaluate";
ok = !(evaluates (builtins.deepSeq badLayout.services.komorebi.internal.configAttrs true));
}
{
name = "a source of application specific configuration is the daemon's app_specific_configuration_path";
ok =
lib.hasSuffix "-applications.json" withSource.services.komorebi.internal.configAttrs.app_specific_configuration_path
&& withSource.services.komorebi.internal.configAttrs.resize_delta == 70;
got = withSource.services.komorebi.internal.configAttrs.app_specific_configuration_path;
}
{
name = "with applicationSpecificConfiguration.enable = false, the sources are left out of config";
ok =
withSource.services.komorebi.internal.configAttrs ? app_specific_configuration_path
&& !(withSourceOff.services.komorebi.internal.configAttrs ? app_specific_configuration_path)
&& withSourceOff.services.komorebi.internal.configAttrs.resize_delta == 70;
got = withSourceOff.services.komorebi.internal.configAttrs;
}
{
name = "with the sources turned off and no config, there is nothing to write and no --config";
ok =
onlySourceOff.services.komorebi.internal.configAttrs == null
&& !(lib.hasInfix "--config" onlySourceOff.services.komorebi.execStart);
got = onlySourceOff.services.komorebi.execStart;
}
{
name = "with a configFile, config is ignored, and the module says so";
ok =
lib.any (lib.hasInfix "services.komorebi.config is ignored") bothConfigs.warnings
&& lib.hasSuffix "-validated-komorebi.json" bothConfigs.services.komorebi.execStart;
got = bothConfigs.warnings;
}
{
name = "a log level the daemon does not have does not evaluate";
ok = !(evaluates unknownLevel.services.komorebi.logLevel);
}
{
name = "execStart cannot be set";
ok = !(evaluates execStartSet.services.komorebi.execStart);
}
{
name = "without key bindings, nothing is compiled";
ok = !(lib.elem "komorebi-keybindings" (installed enabled));
got = installed enabled;
}
{
name = "key bindings as text: every assertion holds";
ok = failed withText == [ ];
got = failed withText;
}
{
name = "key bindings as text reach execStart, with the mode, the chord timeout and the aliases";
# a regular expression cannot refer to a store path, so both lose their context
ok =
builtins.match (
lib.escapeRegex (
builtins.unsafeDiscardStringContext "${withText.services.komorebi.package}/bin/komorebi"
)
+ " --log-level info"
+ " --keybindings /nix/store/[a-z0-9]+-komorebi-skhdrc --keybindings-format skhd"
+ " --keybindings-mode auto --chord-timeout 1500 --app-alias 'terminal=foot,konsole'"
) (builtins.unsafeDiscardStringContext withText.services.komorebi.execStart) != null;
got = withText.services.komorebi.execStart;
}
{
name = "the compiled key bindings are installed in the system profile";
ok = lib.elem "komorebi-keybindings" (installed withText);
got = installed withText;
}
{
name = "key bindings from a file reach execStart, in the default mode, engine";
ok = lib.hasInfix "-plain.sxhkdrc --keybindings-format sxhkd --keybindings-mode engine --chord-timeout 3000" withFile.services.komorebi.execStart;
got = withFile.services.komorebi.execStart;
}
{
name = "a typed config and key bindings reach execStart together: the checked file, then the keys";
ok =
failed withConfigAndKeybindings == [ ]
&&
builtins.match (
".* --log-level info --config /nix/store/[a-z0-9]+-validated-komorebi.json"
+ " --keybindings /nix/store/[a-z0-9]+-komorebi-skhdrc --keybindings-format skhd"
+ " --keybindings-mode engine --chord-timeout 3000"
) (builtins.unsafeDiscardStringContext withConfigAndKeybindings.services.komorebi.execStart) != null
&& withConfigAndKeybindings.services.komorebi.internal.configAttrs.default_workspace_padding == 20;
got = withConfigAndKeybindings.services.komorebi.execStart;
}
{
name = "text and file together: an assertion fails";
ok = lib.any (lib.hasInfix "exactly one of text and file") (failed textAndFile);
got = failed textAndFile;
}
{
name = "key bindings without a format: an assertion fails";
ok = lib.any (lib.hasInfix "needs a format") (failed withoutFormat);
got = failed withoutFormat;
}
{
name = "skhd's .load: an assertion fails";
ok = lib.any (lib.hasInfix ".load is not supported") (failed withLoad);
got = failed withLoad;
}
{
name = "with strict, the build of the key bindings runs komorebic hotkeys compile --strict, and fails when it does";
ok =
failed withStrict == [ ]
&& lib.hasInfix "hotkeys compile" (compileScript withStrict)
&& lib.hasInfix "--strict" (compileScript withStrict)
&& lib.hasInfix "exit 1" (compileScript withStrict);
got = compileScript withStrict;
}
{
name = "without strict, a line komorebic cannot read is a warning in the build log: no --strict";
ok =
failed withoutStrict == [ ]
&& lib.hasInfix "hotkeys compile" (compileScript withoutStrict)
&& !(lib.hasInfix "--strict" (compileScript withoutStrict))
&& lib.hasInfix "cat $out/diagnostics.txt >&2" (compileScript withoutStrict);
got = compileScript withoutStrict;
}
{
name = "strict is for the build: the daemon is started with the same command line either way";
ok = withStrict.services.komorebi.execStart == withoutStrict.services.komorebi.execStart;
got = withStrict.services.komorebi.execStart;
}
{
name = "a layout other than us does not evaluate";
ok =
!(evaluates
(keybindings {
format = "skhd";
text = "";
layout = "de";
}).services.komorebi.keybindings.layout
);
}
{
name = "untestedIsError is false by default";
ok = enabled.services.komorebi.kwin.untestedIsError == false;
}
{
name = "a KWin older than min fails an assertion that names min and the version";
ok = lib.any (
message: lib.hasInfix "KWin ${supportedKwin.min} or newer" message && lib.hasInfix olderKwin message
) (failed kwinTooOld);
got = failed kwinTooOld;
}
{
name = "with a KWin older than min, the system does not evaluate";
ok = !(evaluates kwinTooOld.system.build.toplevel.drvPath);
}
{
name = "with the KWin of min, the system evaluates";
ok = evaluates kwinAtMin.system.build.toplevel.drvPath;
}
{
name = "the KWin of min is supported, with no warning";
ok = failed kwinAtMin == [ ] && kwinWarnings kwinAtMin == [ ];
got = failed kwinAtMin ++ kwinWarnings kwinAtMin;
}
{
name = "the KWin of testedMax is supported, with no warning";
ok = failed kwinTested == [ ] && kwinWarnings kwinTested == [ ];
got = failed kwinTested ++ kwinWarnings kwinTested;
}
{
name = "a KWin newer than testedMax is a warning that names it and testedMax";
ok =
failed kwinNewer == [ ]
&& lib.any (
warning: lib.hasInfix newerKwin warning && lib.hasInfix supportedKwin.testedMax warning
) (kwinWarnings kwinNewer);
got = failed kwinNewer ++ kwinWarnings kwinNewer;
}
{
name = "with untestedIsError, a KWin newer than testedMax fails an assertion, with no warning";
ok =
lib.any (lib.hasInfix "untestedIsError is true") (failed kwinNewerIsError)
&& kwinWarnings kwinNewerIsError == [ ];
got = failed kwinNewerIsError ++ kwinWarnings kwinNewerIsError;
}
{
name = "with untestedIsError, the KWin of testedMax is supported";
ok = failed kwinTestedIsError == [ ];
got = failed kwinTestedIsError;
}
{
name = "disabled, a KWin older than min is not refused";
ok = failed kwinTooOldDisabled == [ ] && kwinWarnings kwinTooOldDisabled == [ ];
got = failed kwinTooOldDisabled;
}
];
failures = lib.filter (check: !check.ok) checks;
# what a failed check got, without the store paths' context: this derivation must not depend on
# a Linux package
show =
check:
check.name
+ lib.optionalString (check ? got) (
": got " + builtins.unsafeDiscardStringContext (builtins.toJSON check.got)
);
in
pkgs.runCommand "nixos-module-eval" { } (
if failures == [ ] then
''
${lib.concatMapStringsSep "\n" (check: "echo ${lib.escapeShellArg "ok: ${check.name}"}") checks}
echo "${toString (lib.length checks)} checks of services.komorebi passed"
touch $out
''
else
''
${lib.concatMapStringsSep "\n" (
check: "echo ${lib.escapeShellArg "FAILED: ${show check}"}"
) failures}
exit 1
''
)