about summary refs log tree commit diff
path: root/lib/tests
diff options
context:
space:
mode:
authorSilvan Mosberger <contact@infinisil.com>2024-04-09 20:48:44 +0200
committerGitHub <noreply@github.com>2024-04-09 20:48:44 +0200
commit4f1d724b82db61237b5d7c669841b5f596e08e7f (patch)
treee0a02af4d45c5155d83201cfd9b2049f2594cd6b /lib/tests
parent40d8229671633b1db7db5d28ae96969e3f52beb0 (diff)
parent35fe538330b738b90d527d908ee487e38d42de40 (diff)
Merge pull request #284551 from hercules-ci/types-attrTag
Add `types.attrTag`
Diffstat (limited to 'lib/tests')
-rwxr-xr-xlib/tests/modules.sh12
-rw-r--r--lib/tests/modules/docs.nix41
-rw-r--r--lib/tests/modules/types-attrTag-wrong-decl.nix14
-rw-r--r--lib/tests/modules/types-attrTag.nix135
4 files changed, 202 insertions, 0 deletions
diff --git a/lib/tests/modules.sh b/lib/tests/modules.sh
index b3bbdf9485ac0..750b1d025e026 100755
--- a/lib/tests/modules.sh
+++ b/lib/tests/modules.sh
@@ -103,6 +103,18 @@ checkConfigError 'The option .sub.wrong2. does not exist. Definition values:' co
 checkConfigError '.*This can happen if you e.g. declared your options in .types.submodule.' config.sub ./error-mkOption-in-submodule-config.nix
 checkConfigError '.*A definition for option .bad. is not of type .non-empty .list of .submodule...\.' config.bad ./error-nonEmptyListOf-submodule.nix
 
+# types.attrTag
+checkConfigOutput '^true$' config.okChecks ./types-attrTag.nix
+checkConfigError 'A definition for option .intStrings\.syntaxError. is not of type .attribute-tagged union' config.intStrings.syntaxError ./types-attrTag.nix
+checkConfigError 'A definition for option .intStrings\.syntaxError2. is not of type .attribute-tagged union' config.intStrings.syntaxError2 ./types-attrTag.nix
+checkConfigError 'A definition for option .intStrings\.syntaxError3. is not of type .attribute-tagged union' config.intStrings.syntaxError3 ./types-attrTag.nix
+checkConfigError 'A definition for option .intStrings\.syntaxError4. is not of type .attribute-tagged union' config.intStrings.syntaxError4 ./types-attrTag.nix
+checkConfigError 'A definition for option .intStrings\.mergeError. is not of type .attribute-tagged union' config.intStrings.mergeError ./types-attrTag.nix
+checkConfigError 'A definition for option .intStrings\.badTagError. is not of type .attribute-tagged union' config.intStrings.badTagError ./types-attrTag.nix
+checkConfigError 'A definition for option .intStrings\.badTagTypeError\.left. is not of type .signed integer.' config.intStrings.badTagTypeError.left ./types-attrTag.nix
+checkConfigError 'A definition for option .nested\.right\.left. is not of type .signed integer.' config.nested.right.left ./types-attrTag.nix
+checkConfigError 'In attrTag, each tag value must be an option, but tag int was a bare type, not wrapped in mkOption.' config.opt.int ./types-attrTag-wrong-decl.nix
+
 # types.pathInStore
 checkConfigOutput '".*/store/0lz9p8xhf89kb1c1kk6jxrzskaiygnlh-bash-5.2-p15.drv"' config.pathInStore.ok1 ./types.nix
 checkConfigOutput '".*/store/0fb3ykw9r5hpayd05sr0cizwadzq1d8q-bash-5.2-p15"' config.pathInStore.ok2 ./types.nix
diff --git a/lib/tests/modules/docs.nix b/lib/tests/modules/docs.nix
new file mode 100644
index 0000000000000..225aa7eac1de8
--- /dev/null
+++ b/lib/tests/modules/docs.nix
@@ -0,0 +1,41 @@
+/*
+  A basic documentation generating module.
+  Declares and defines a `docs` option, suitable for making assertions about
+  the extraction "phase" of documentation generation.
+ */
+{ lib, options, ... }:
+
+let
+  inherit (lib)
+    head
+    length
+    mkOption
+    types
+  ;
+
+  traceListSeq = l: v: lib.foldl' (a: b: lib.traceSeq b a) v l;
+
+in
+
+{
+  options.docs = mkOption {
+    type = types.lazyAttrsOf types.raw;
+    description = ''
+      All options to be rendered, without any visibility filtering applied.
+    '';
+  };
+  config.docs =
+    lib.zipAttrsWith
+      (name: values:
+        if length values > 1 then
+          traceListSeq values
+          abort "Multiple options with the same name: ${name}"
+        else
+          assert length values == 1;
+          head values
+      )
+      (map
+        (opt: { ${opt.name} = opt; })
+        (lib.optionAttrSetToDocList options)
+      );
+}
diff --git a/lib/tests/modules/types-attrTag-wrong-decl.nix b/lib/tests/modules/types-attrTag-wrong-decl.nix
new file mode 100644
index 0000000000000..d03370bc10da4
--- /dev/null
+++ b/lib/tests/modules/types-attrTag-wrong-decl.nix
@@ -0,0 +1,14 @@
+{ lib, ... }:
+let
+  inherit (lib) types mkOption;
+in
+{
+  options = {
+    opt = mkOption {
+      type = types.attrTag {
+        int = types.int;
+      };
+      default = { int = 1; };
+    };
+  };
+}
diff --git a/lib/tests/modules/types-attrTag.nix b/lib/tests/modules/types-attrTag.nix
new file mode 100644
index 0000000000000..b2e5158bb44b6
--- /dev/null
+++ b/lib/tests/modules/types-attrTag.nix
@@ -0,0 +1,135 @@
+{ lib, config, options, ... }:
+let
+  inherit (lib) mkOption types;
+  forceDeep = x: builtins.deepSeq x x;
+  mergedSubOption = (options.merged.type.getSubOptions options.merged.loc).extensible."merged.<name>";
+in
+{
+  options = {
+    intStrings = mkOption {
+      type = types.attrsOf
+        (types.attrTag {
+          left = mkOption {
+            type = types.int;
+          };
+          right = mkOption {
+            type = types.str;
+          };
+        });
+    };
+    nested = mkOption {
+      type = types.attrTag {
+        left = mkOption {
+          type = types.int;
+        };
+        right = mkOption {
+          type = types.attrTag {
+            left = mkOption {
+              type = types.int;
+            };
+            right = mkOption {
+              type = types.str;
+            };
+          };
+        };
+      };
+    };
+    merged = mkOption {
+      type = types.attrsOf (
+        types.attrTag {
+          yay = mkOption {
+            type = types.int;
+          };
+          extensible = mkOption {
+            type = types.enum [ "foo" ];
+          };
+        }
+      );
+    };
+    submodules = mkOption {
+      type = types.attrsOf (
+        types.attrTag {
+          foo = mkOption {
+            type = types.submodule {
+              options = {
+                bar = mkOption {
+                  type = types.int;
+                };
+              };
+            };
+          };
+          qux = mkOption {
+            type = types.str;
+            description = "A qux for when you don't want a foo";
+          };
+        }
+      );
+    };
+    okChecks = mkOption {};
+  };
+  imports = [
+    ./docs.nix
+    {
+      options.merged = mkOption {
+        type = types.attrsOf (
+          types.attrTag {
+            nay = mkOption {
+              type = types.bool;
+            };
+            extensible = mkOption {
+              type = types.enum [ "bar" ];
+            };
+          }
+        );
+      };
+    }
+  ];
+  config = {
+    intStrings.syntaxError = 1;
+    intStrings.syntaxError2 = {};
+    intStrings.syntaxError3 = { a = true; b = true; };
+    intStrings.syntaxError4 = lib.mkMerge [ { a = true; } { b = true; } ];
+    intStrings.mergeError = lib.mkMerge [ { int = throw "do not eval"; } { string = throw "do not eval"; } ];
+    intStrings.badTagError.rite = throw "do not eval";
+    intStrings.badTagTypeError.left = "bad";
+    intStrings.numberOne.left = 1;
+    intStrings.hello.right = "hello world";
+    nested.right.left = "not a number";
+    merged.negative.nay = false;
+    merged.positive.yay = 100;
+    merged.extensi-foo.extensible = "foo";
+    merged.extensi-bar.extensible = "bar";
+    okChecks = builtins.addErrorContext "while evaluating the assertions" (
+      assert config.intStrings.hello == { right = "hello world"; };
+      assert config.intStrings.numberOne == { left = 1; };
+      assert config.merged.negative == { nay = false; };
+      assert config.merged.positive == { yay = 100; };
+      assert config.merged.extensi-foo == { extensible = "foo"; };
+      assert config.merged.extensi-bar == { extensible = "bar"; };
+      assert config.docs."submodules.<name>.foo.bar".type == "signed integer";
+      assert config.docs."submodules.<name>.qux".type == "string";
+      assert config.docs."submodules.<name>.qux".declarations == [ __curPos.file ];
+      assert config.docs."submodules.<name>.qux".loc == [ "submodules" "<name>" "qux" ];
+      assert config.docs."submodules.<name>.qux".name == "submodules.<name>.qux";
+      assert config.docs."submodules.<name>.qux".description == "A qux for when you don't want a foo";
+      assert config.docs."submodules.<name>.qux".readOnly == false;
+      assert config.docs."submodules.<name>.qux".visible == true;
+      # Not available (yet?)
+      # assert config.docs."submodules.<name>.qux".declarationsWithPositions == [ ... ];
+      assert options.submodules.declarations == [ __curPos.file ];
+      assert lib.length options.submodules.declarationPositions == 1;
+      assert (lib.head options.submodules.declarationPositions).file == __curPos.file;
+      assert options.merged.declarations == [ __curPos.file __curPos.file ];
+      assert lib.length options.merged.declarationPositions == 2;
+      assert (lib.elemAt options.merged.declarationPositions 0).file == __curPos.file;
+      assert (lib.elemAt options.merged.declarationPositions 1).file == __curPos.file;
+      assert (lib.elemAt options.merged.declarationPositions 0).line != (lib.elemAt options.merged.declarationPositions 1).line;
+      assert mergedSubOption.declarations == [ __curPos.file __curPos.file ];
+      assert lib.length mergedSubOption.declarationPositions == 2;
+      assert (lib.elemAt mergedSubOption.declarationPositions 0).file == __curPos.file;
+      assert (lib.elemAt mergedSubOption.declarationPositions 1).file == __curPos.file;
+      assert (lib.elemAt mergedSubOption.declarationPositions 0).line != (lib.elemAt mergedSubOption.declarationPositions 1).line;
+      assert lib.length config.docs."merged.<name>.extensible".declarations == 2;
+      true);
+  };
+}