Compare commits
2 Commits
feat/home-
...
feat/tui_r
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2b54e7cf79 | ||
|
|
23005fe3e4 |
39
README.md
39
README.md
@@ -44,45 +44,6 @@ nix profile install github:karol-broda/snitch
|
|||||||
# then use: inputs.snitch.packages.${system}.default
|
# then use: inputs.snitch.packages.${system}.default
|
||||||
```
|
```
|
||||||
|
|
||||||
### home-manager (flake)
|
|
||||||
|
|
||||||
add snitch to your flake inputs and import the home-manager module:
|
|
||||||
|
|
||||||
```nix
|
|
||||||
{
|
|
||||||
inputs = {
|
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
|
||||||
home-manager.url = "github:nix-community/home-manager";
|
|
||||||
snitch.url = "github:karol-broda/snitch";
|
|
||||||
};
|
|
||||||
|
|
||||||
outputs = { nixpkgs, home-manager, snitch, ... }: {
|
|
||||||
homeConfigurations."user" = home-manager.lib.homeManagerConfiguration {
|
|
||||||
pkgs = nixpkgs.legacyPackages.x86_64-linux;
|
|
||||||
modules = [
|
|
||||||
snitch.homeManagerModules.default
|
|
||||||
{
|
|
||||||
programs.snitch = {
|
|
||||||
enable = true;
|
|
||||||
# optional: use the flake's package instead of nixpkgs
|
|
||||||
# package = snitch.packages.x86_64-linux.default;
|
|
||||||
settings = {
|
|
||||||
defaults = {
|
|
||||||
theme = "catppuccin-mocha";
|
|
||||||
interval = "2s";
|
|
||||||
resolve = true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
available themes: `ansi`, `catppuccin-mocha`, `catppuccin-macchiato`, `catppuccin-frappe`, `catppuccin-latte`, `gruvbox-dark`, `gruvbox-light`, `dracula`, `nord`, `tokyo-night`, `tokyo-night-storm`, `tokyo-night-light`, `solarized-dark`, `solarized-light`, `one-dark`, `mono`
|
|
||||||
|
|
||||||
### arch linux (aur)
|
### arch linux (aur)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
27
flake.nix
27
flake.nix
@@ -106,32 +106,5 @@
|
|||||||
overlays.default = final: _prev: {
|
overlays.default = final: _prev: {
|
||||||
snitch = mkSnitch final;
|
snitch = mkSnitch final;
|
||||||
};
|
};
|
||||||
|
|
||||||
homeManagerModules.default = import ./nix/hm-module.nix;
|
|
||||||
homeManagerModules.snitch = self.homeManagerModules.default;
|
|
||||||
|
|
||||||
# alias for flake-parts compatibility
|
|
||||||
homeModules.default = self.homeManagerModules.default;
|
|
||||||
homeModules.snitch = self.homeManagerModules.default;
|
|
||||||
|
|
||||||
checks = eachSystem (system:
|
|
||||||
let
|
|
||||||
pkgs = import nixpkgs {
|
|
||||||
inherit system;
|
|
||||||
overlays = [ self.overlays.default ];
|
|
||||||
};
|
|
||||||
in
|
|
||||||
{
|
|
||||||
# home manager module tests
|
|
||||||
hm-module = import ./nix/tests/hm-module-test.nix {
|
|
||||||
inherit pkgs;
|
|
||||||
lib = pkgs.lib;
|
|
||||||
hmModule = self.homeManagerModules.default;
|
|
||||||
};
|
|
||||||
|
|
||||||
# package builds correctly
|
|
||||||
package = self.packages.${system}.default;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,177 +0,0 @@
|
|||||||
{
|
|
||||||
config,
|
|
||||||
lib,
|
|
||||||
pkgs,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
cfg = config.programs.snitch;
|
|
||||||
|
|
||||||
themes = [
|
|
||||||
"ansi"
|
|
||||||
"catppuccin-mocha"
|
|
||||||
"catppuccin-macchiato"
|
|
||||||
"catppuccin-frappe"
|
|
||||||
"catppuccin-latte"
|
|
||||||
"gruvbox-dark"
|
|
||||||
"gruvbox-light"
|
|
||||||
"dracula"
|
|
||||||
"nord"
|
|
||||||
"tokyo-night"
|
|
||||||
"tokyo-night-storm"
|
|
||||||
"tokyo-night-light"
|
|
||||||
"solarized-dark"
|
|
||||||
"solarized-light"
|
|
||||||
"one-dark"
|
|
||||||
"mono"
|
|
||||||
"auto"
|
|
||||||
];
|
|
||||||
|
|
||||||
defaultFields = [
|
|
||||||
"pid"
|
|
||||||
"process"
|
|
||||||
"user"
|
|
||||||
"proto"
|
|
||||||
"state"
|
|
||||||
"laddr"
|
|
||||||
"lport"
|
|
||||||
"raddr"
|
|
||||||
"rport"
|
|
||||||
];
|
|
||||||
|
|
||||||
tomlFormat = pkgs.formats.toml { };
|
|
||||||
|
|
||||||
settingsType = lib.types.submodule {
|
|
||||||
freeformType = tomlFormat.type;
|
|
||||||
|
|
||||||
options = {
|
|
||||||
defaults = lib.mkOption {
|
|
||||||
type = lib.types.submodule {
|
|
||||||
freeformType = tomlFormat.type;
|
|
||||||
|
|
||||||
options = {
|
|
||||||
interval = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
default = "1s";
|
|
||||||
example = "2s";
|
|
||||||
description = "Default refresh interval for watch/stats/trace commands.";
|
|
||||||
};
|
|
||||||
|
|
||||||
numeric = lib.mkOption {
|
|
||||||
type = lib.types.bool;
|
|
||||||
default = false;
|
|
||||||
description = "Disable name/service resolution by default.";
|
|
||||||
};
|
|
||||||
|
|
||||||
fields = lib.mkOption {
|
|
||||||
type = lib.types.listOf lib.types.str;
|
|
||||||
default = defaultFields;
|
|
||||||
example = [ "pid" "process" "proto" "state" "laddr" "lport" ];
|
|
||||||
description = "Default fields to display.";
|
|
||||||
};
|
|
||||||
|
|
||||||
theme = lib.mkOption {
|
|
||||||
type = lib.types.enum themes;
|
|
||||||
default = "ansi";
|
|
||||||
description = ''
|
|
||||||
Color theme for the TUI. "ansi" inherits terminal colors.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
units = lib.mkOption {
|
|
||||||
type = lib.types.enum [ "auto" "si" "iec" ];
|
|
||||||
default = "auto";
|
|
||||||
description = "Default units for byte display.";
|
|
||||||
};
|
|
||||||
|
|
||||||
color = lib.mkOption {
|
|
||||||
type = lib.types.enum [ "auto" "always" "never" ];
|
|
||||||
default = "auto";
|
|
||||||
description = "Default color mode.";
|
|
||||||
};
|
|
||||||
|
|
||||||
resolve = lib.mkOption {
|
|
||||||
type = lib.types.bool;
|
|
||||||
default = true;
|
|
||||||
description = "Enable name resolution by default.";
|
|
||||||
};
|
|
||||||
|
|
||||||
dns_cache = lib.mkOption {
|
|
||||||
type = lib.types.bool;
|
|
||||||
default = true;
|
|
||||||
description = "Enable DNS caching.";
|
|
||||||
};
|
|
||||||
|
|
||||||
ipv4 = lib.mkOption {
|
|
||||||
type = lib.types.bool;
|
|
||||||
default = false;
|
|
||||||
description = "Filter to IPv4 only by default.";
|
|
||||||
};
|
|
||||||
|
|
||||||
ipv6 = lib.mkOption {
|
|
||||||
type = lib.types.bool;
|
|
||||||
default = false;
|
|
||||||
description = "Filter to IPv6 only by default.";
|
|
||||||
};
|
|
||||||
|
|
||||||
no_headers = lib.mkOption {
|
|
||||||
type = lib.types.bool;
|
|
||||||
default = false;
|
|
||||||
description = "Omit headers in output by default.";
|
|
||||||
};
|
|
||||||
|
|
||||||
output_format = lib.mkOption {
|
|
||||||
type = lib.types.enum [ "table" "json" "csv" ];
|
|
||||||
default = "table";
|
|
||||||
description = "Default output format.";
|
|
||||||
};
|
|
||||||
|
|
||||||
sort_by = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
default = "";
|
|
||||||
example = "pid";
|
|
||||||
description = "Default sort field.";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
default = { };
|
|
||||||
description = "Default settings for snitch commands.";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options.programs.snitch = {
|
|
||||||
enable = lib.mkEnableOption "snitch, a friendlier ss/netstat for humans";
|
|
||||||
|
|
||||||
package = lib.mkPackageOption pkgs "snitch" { };
|
|
||||||
|
|
||||||
settings = lib.mkOption {
|
|
||||||
type = settingsType;
|
|
||||||
default = { };
|
|
||||||
example = lib.literalExpression ''
|
|
||||||
{
|
|
||||||
defaults = {
|
|
||||||
theme = "catppuccin-mocha";
|
|
||||||
interval = "2s";
|
|
||||||
resolve = true;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
'';
|
|
||||||
description = ''
|
|
||||||
Configuration written to {file}`$XDG_CONFIG_HOME/snitch/snitch.toml`.
|
|
||||||
|
|
||||||
See <https://github.com/karol-broda/snitch> for available options.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
|
||||||
home.packages = [ cfg.package ];
|
|
||||||
|
|
||||||
xdg.configFile."snitch/snitch.toml" = lib.mkIf (cfg.settings != { }) {
|
|
||||||
source = tomlFormat.generate "snitch.toml" cfg.settings;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,429 +0,0 @@
|
|||||||
# home manager module tests
|
|
||||||
#
|
|
||||||
# run with: nix build .#checks.x86_64-linux.hm-module
|
|
||||||
#
|
|
||||||
# tests cover:
|
|
||||||
# - module evaluation with various configurations
|
|
||||||
# - type validation for all options
|
|
||||||
# - generated TOML content verification
|
|
||||||
# - edge cases (disabled, empty settings, full settings)
|
|
||||||
{ pkgs, lib, hmModule }:
|
|
||||||
|
|
||||||
let
|
|
||||||
# minimal home-manager stub for standalone module testing
|
|
||||||
hmLib = {
|
|
||||||
hm.types.dagOf = lib.types.attrsOf;
|
|
||||||
dag.entryAnywhere = x: x;
|
|
||||||
};
|
|
||||||
|
|
||||||
# evaluate the hm module with a given config
|
|
||||||
evalModule = testConfig:
|
|
||||||
lib.evalModules {
|
|
||||||
modules = [
|
|
||||||
hmModule
|
|
||||||
# stub home-manager's expected structure
|
|
||||||
{
|
|
||||||
options = {
|
|
||||||
home.packages = lib.mkOption {
|
|
||||||
type = lib.types.listOf lib.types.package;
|
|
||||||
default = [ ];
|
|
||||||
};
|
|
||||||
xdg.configFile = lib.mkOption {
|
|
||||||
type = lib.types.attrsOf (lib.types.submodule {
|
|
||||||
options = {
|
|
||||||
source = lib.mkOption { type = lib.types.path; };
|
|
||||||
text = lib.mkOption { type = lib.types.str; default = ""; };
|
|
||||||
};
|
|
||||||
});
|
|
||||||
default = { };
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
testConfig
|
|
||||||
];
|
|
||||||
specialArgs = { inherit pkgs lib; };
|
|
||||||
};
|
|
||||||
|
|
||||||
# read generated TOML file content
|
|
||||||
readGeneratedToml = evalResult:
|
|
||||||
let
|
|
||||||
configFile = evalResult.config.xdg.configFile."snitch/snitch.toml" or null;
|
|
||||||
in
|
|
||||||
if configFile != null && configFile ? source
|
|
||||||
then builtins.readFile configFile.source
|
|
||||||
else null;
|
|
||||||
|
|
||||||
# test cases
|
|
||||||
tests = {
|
|
||||||
# test 1: module evaluates when disabled
|
|
||||||
moduleDisabled = {
|
|
||||||
name = "module-disabled";
|
|
||||||
config = {
|
|
||||||
programs.snitch.enable = false;
|
|
||||||
};
|
|
||||||
assertions = evalResult: [
|
|
||||||
{
|
|
||||||
assertion = evalResult.config.home.packages == [ ];
|
|
||||||
message = "packages should be empty when disabled";
|
|
||||||
}
|
|
||||||
{
|
|
||||||
assertion = !(evalResult.config.xdg.configFile ? "snitch/snitch.toml");
|
|
||||||
message = "config file should not exist when disabled";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
# test 2: module evaluates with enable only (defaults)
|
|
||||||
moduleEnabledDefaults = {
|
|
||||||
name = "module-enabled-defaults";
|
|
||||||
config = {
|
|
||||||
programs.snitch.enable = true;
|
|
||||||
};
|
|
||||||
assertions = evalResult: [
|
|
||||||
{
|
|
||||||
assertion = builtins.length evalResult.config.home.packages == 1;
|
|
||||||
message = "package should be installed when enabled";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
# test 3: all theme values are valid
|
|
||||||
themeValidation = {
|
|
||||||
name = "theme-validation";
|
|
||||||
config = {
|
|
||||||
programs.snitch = {
|
|
||||||
enable = true;
|
|
||||||
settings.defaults.theme = "catppuccin-mocha";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
assertions = evalResult:
|
|
||||||
let
|
|
||||||
toml = readGeneratedToml evalResult;
|
|
||||||
in
|
|
||||||
[
|
|
||||||
{
|
|
||||||
assertion = toml != null;
|
|
||||||
message = "TOML config should be generated";
|
|
||||||
}
|
|
||||||
{
|
|
||||||
assertion = lib.hasInfix "catppuccin-mocha" toml;
|
|
||||||
message = "theme should be set in TOML";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
# test 4: full configuration with all options
|
|
||||||
fullConfiguration = {
|
|
||||||
name = "full-configuration";
|
|
||||||
config = {
|
|
||||||
programs.snitch = {
|
|
||||||
enable = true;
|
|
||||||
settings.defaults = {
|
|
||||||
interval = "2s";
|
|
||||||
numeric = true;
|
|
||||||
fields = [ "pid" "process" "proto" ];
|
|
||||||
theme = "nord";
|
|
||||||
units = "si";
|
|
||||||
color = "always";
|
|
||||||
resolve = false;
|
|
||||||
dns_cache = false;
|
|
||||||
ipv4 = true;
|
|
||||||
ipv6 = false;
|
|
||||||
no_headers = true;
|
|
||||||
output_format = "json";
|
|
||||||
sort_by = "pid";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
assertions = evalResult:
|
|
||||||
let
|
|
||||||
toml = readGeneratedToml evalResult;
|
|
||||||
in
|
|
||||||
[
|
|
||||||
{
|
|
||||||
assertion = toml != null;
|
|
||||||
message = "TOML config should be generated";
|
|
||||||
}
|
|
||||||
{
|
|
||||||
assertion = lib.hasInfix "interval = \"2s\"" toml;
|
|
||||||
message = "interval should be 2s";
|
|
||||||
}
|
|
||||||
{
|
|
||||||
assertion = lib.hasInfix "numeric = true" toml;
|
|
||||||
message = "numeric should be true";
|
|
||||||
}
|
|
||||||
{
|
|
||||||
assertion = lib.hasInfix "theme = \"nord\"" toml;
|
|
||||||
message = "theme should be nord";
|
|
||||||
}
|
|
||||||
{
|
|
||||||
assertion = lib.hasInfix "units = \"si\"" toml;
|
|
||||||
message = "units should be si";
|
|
||||||
}
|
|
||||||
{
|
|
||||||
assertion = lib.hasInfix "color = \"always\"" toml;
|
|
||||||
message = "color should be always";
|
|
||||||
}
|
|
||||||
{
|
|
||||||
assertion = lib.hasInfix "resolve = false" toml;
|
|
||||||
message = "resolve should be false";
|
|
||||||
}
|
|
||||||
{
|
|
||||||
assertion = lib.hasInfix "output_format = \"json\"" toml;
|
|
||||||
message = "output_format should be json";
|
|
||||||
}
|
|
||||||
{
|
|
||||||
assertion = lib.hasInfix "sort_by = \"pid\"" toml;
|
|
||||||
message = "sort_by should be pid";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
# test 5: output format enum validation
|
|
||||||
outputFormatCsv = {
|
|
||||||
name = "output-format-csv";
|
|
||||||
config = {
|
|
||||||
programs.snitch = {
|
|
||||||
enable = true;
|
|
||||||
settings.defaults.output_format = "csv";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
assertions = evalResult:
|
|
||||||
let
|
|
||||||
toml = readGeneratedToml evalResult;
|
|
||||||
in
|
|
||||||
[
|
|
||||||
{
|
|
||||||
assertion = lib.hasInfix "output_format = \"csv\"" toml;
|
|
||||||
message = "output_format should accept csv";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
# test 6: units enum validation
|
|
||||||
unitsIec = {
|
|
||||||
name = "units-iec";
|
|
||||||
config = {
|
|
||||||
programs.snitch = {
|
|
||||||
enable = true;
|
|
||||||
settings.defaults.units = "iec";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
assertions = evalResult:
|
|
||||||
let
|
|
||||||
toml = readGeneratedToml evalResult;
|
|
||||||
in
|
|
||||||
[
|
|
||||||
{
|
|
||||||
assertion = lib.hasInfix "units = \"iec\"" toml;
|
|
||||||
message = "units should accept iec";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
# test 7: color never value
|
|
||||||
colorNever = {
|
|
||||||
name = "color-never";
|
|
||||||
config = {
|
|
||||||
programs.snitch = {
|
|
||||||
enable = true;
|
|
||||||
settings.defaults.color = "never";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
assertions = evalResult:
|
|
||||||
let
|
|
||||||
toml = readGeneratedToml evalResult;
|
|
||||||
in
|
|
||||||
[
|
|
||||||
{
|
|
||||||
assertion = lib.hasInfix "color = \"never\"" toml;
|
|
||||||
message = "color should accept never";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
# test 8: freeform type allows custom keys
|
|
||||||
freeformCustomKeys = {
|
|
||||||
name = "freeform-custom-keys";
|
|
||||||
config = {
|
|
||||||
programs.snitch = {
|
|
||||||
enable = true;
|
|
||||||
settings = {
|
|
||||||
defaults.theme = "dracula";
|
|
||||||
custom_section = {
|
|
||||||
custom_key = "custom_value";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
assertions = evalResult:
|
|
||||||
let
|
|
||||||
toml = readGeneratedToml evalResult;
|
|
||||||
in
|
|
||||||
[
|
|
||||||
{
|
|
||||||
assertion = lib.hasInfix "custom_key" toml;
|
|
||||||
message = "freeform type should allow custom keys";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
# test 9: all themes evaluate correctly
|
|
||||||
allThemes =
|
|
||||||
let
|
|
||||||
themes = [
|
|
||||||
"ansi"
|
|
||||||
"catppuccin-mocha"
|
|
||||||
"catppuccin-macchiato"
|
|
||||||
"catppuccin-frappe"
|
|
||||||
"catppuccin-latte"
|
|
||||||
"gruvbox-dark"
|
|
||||||
"gruvbox-light"
|
|
||||||
"dracula"
|
|
||||||
"nord"
|
|
||||||
"tokyo-night"
|
|
||||||
"tokyo-night-storm"
|
|
||||||
"tokyo-night-light"
|
|
||||||
"solarized-dark"
|
|
||||||
"solarized-light"
|
|
||||||
"one-dark"
|
|
||||||
"mono"
|
|
||||||
"auto"
|
|
||||||
];
|
|
||||||
in
|
|
||||||
{
|
|
||||||
name = "all-themes";
|
|
||||||
# use the last theme as the test config
|
|
||||||
config = {
|
|
||||||
programs.snitch = {
|
|
||||||
enable = true;
|
|
||||||
settings.defaults.theme = "auto";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
assertions = evalResult:
|
|
||||||
let
|
|
||||||
# verify all themes can be set by evaluating them
|
|
||||||
themeResults = map
|
|
||||||
(theme:
|
|
||||||
let
|
|
||||||
result = evalModule {
|
|
||||||
programs.snitch = {
|
|
||||||
enable = true;
|
|
||||||
settings.defaults.theme = theme;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
toml = readGeneratedToml result;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
inherit theme;
|
|
||||||
success = toml != null && lib.hasInfix theme toml;
|
|
||||||
}
|
|
||||||
)
|
|
||||||
themes;
|
|
||||||
allSucceeded = lib.all (r: r.success) themeResults;
|
|
||||||
in
|
|
||||||
[
|
|
||||||
{
|
|
||||||
assertion = allSucceeded;
|
|
||||||
message = "all themes should evaluate correctly: ${
|
|
||||||
lib.concatMapStringsSep ", "
|
|
||||||
(r: "${r.theme}=${if r.success then "ok" else "fail"}")
|
|
||||||
themeResults
|
|
||||||
}";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
# test 10: fields list serialization
|
|
||||||
fieldsListSerialization = {
|
|
||||||
name = "fields-list-serialization";
|
|
||||||
config = {
|
|
||||||
programs.snitch = {
|
|
||||||
enable = true;
|
|
||||||
settings.defaults.fields = [ "pid" "process" "proto" "state" ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
assertions = evalResult:
|
|
||||||
let
|
|
||||||
toml = readGeneratedToml evalResult;
|
|
||||||
in
|
|
||||||
[
|
|
||||||
{
|
|
||||||
assertion = lib.hasInfix "pid" toml && lib.hasInfix "process" toml;
|
|
||||||
message = "fields list should be serialized correctly";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# run all tests and collect results
|
|
||||||
runTests =
|
|
||||||
let
|
|
||||||
testResults = lib.mapAttrsToList
|
|
||||||
(name: test:
|
|
||||||
let
|
|
||||||
evalResult = evalModule test.config;
|
|
||||||
assertions = test.assertions evalResult;
|
|
||||||
failures = lib.filter (a: !a.assertion) assertions;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
inherit name;
|
|
||||||
testName = test.name;
|
|
||||||
passed = failures == [ ];
|
|
||||||
failures = map (f: f.message) failures;
|
|
||||||
}
|
|
||||||
)
|
|
||||||
tests;
|
|
||||||
|
|
||||||
allPassed = lib.all (r: r.passed) testResults;
|
|
||||||
failedTests = lib.filter (r: !r.passed) testResults;
|
|
||||||
|
|
||||||
summary = ''
|
|
||||||
========================================
|
|
||||||
home manager module test results
|
|
||||||
========================================
|
|
||||||
total tests: ${toString (builtins.length testResults)}
|
|
||||||
passed: ${toString (builtins.length (lib.filter (r: r.passed) testResults))}
|
|
||||||
failed: ${toString (builtins.length failedTests)}
|
|
||||||
========================================
|
|
||||||
${lib.concatMapStringsSep "\n" (r:
|
|
||||||
if r.passed
|
|
||||||
then "[yes] ${r.testName}"
|
|
||||||
else "[no] ${r.testName}\n ${lib.concatStringsSep "\n " r.failures}"
|
|
||||||
) testResults}
|
|
||||||
========================================
|
|
||||||
'';
|
|
||||||
in
|
|
||||||
{
|
|
||||||
inherit testResults allPassed failedTests summary;
|
|
||||||
};
|
|
||||||
|
|
||||||
results = runTests;
|
|
||||||
|
|
||||||
in
|
|
||||||
pkgs.runCommand "hm-module-test"
|
|
||||||
{
|
|
||||||
passthru = {
|
|
||||||
inherit results;
|
|
||||||
# expose for debugging
|
|
||||||
inherit evalModule tests;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
(
|
|
||||||
if results.allPassed
|
|
||||||
then ''
|
|
||||||
echo "${results.summary}"
|
|
||||||
echo "all tests passed"
|
|
||||||
touch $out
|
|
||||||
''
|
|
||||||
else ''
|
|
||||||
echo "${results.summary}"
|
|
||||||
echo ""
|
|
||||||
echo "failed tests:"
|
|
||||||
${lib.concatMapStringsSep "\n" (t: ''
|
|
||||||
echo " - ${t.testName}: ${lib.concatStringsSep ", " t.failures}"
|
|
||||||
'') results.failedTests}
|
|
||||||
exit 1
|
|
||||||
''
|
|
||||||
)
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user