summary refs log tree commit diff
path: root/lib/tests
diff options
context:
space:
mode:
Diffstat (limited to 'lib/tests')
-rw-r--r--lib/tests/misc.nix81
-rwxr-xr-xlib/tests/modules.sh23
-rw-r--r--lib/tests/modules/declaration-positions.nix49
-rw-r--r--lib/tests/modules/gvariant.nix142
4 files changed, 205 insertions, 90 deletions
diff --git a/lib/tests/misc.nix b/lib/tests/misc.nix
index 702be9529b4..6527c31e49c 100644
--- a/lib/tests/misc.nix
+++ b/lib/tests/misc.nix
@@ -509,6 +509,38 @@ runTests {
       };
     };
 
+  testFoldl'Empty = {
+    expr = foldl' (acc: el: abort "operation not called") 0 [ ];
+    expected = 0;
+  };
+
+  testFoldl'IntegerAdding = {
+    expr = foldl' (acc: el: acc + el) 0 [ 1 2 3 ];
+    expected = 6;
+  };
+
+  # The accumulator isn't forced deeply
+  testFoldl'NonDeep = {
+    expr = take 3 (foldl'
+      (acc: el: [ el ] ++ acc)
+      [ (abort "unevaluated list entry") ]
+      [ 1 2 3 ]);
+    expected = [ 3 2 1 ];
+  };
+
+  # Compared to builtins.foldl', lib.foldl' evaluates the first accumulator strictly too
+  testFoldl'StrictInitial = {
+    expr = (builtins.tryEval (foldl' (acc: el: el) (throw "hello") [])).success;
+    expected = false;
+  };
+
+  # Make sure we don't get a stack overflow for large lists
+  # This number of elements would notably cause a stack overflow if it was implemented without the `foldl'` builtin
+  testFoldl'Large = {
+    expr = foldl' (acc: el: acc + el) 0 (range 0 100000);
+    expected = 5000050000;
+  };
+
   testTake = testAllTrue [
     ([] == (take 0 [  1 2 3 ]))
     ([1] == (take 1 [  1 2 3 ]))
@@ -712,7 +744,7 @@ runTests {
       # should just return the initial value
       emptySet = foldlAttrs (throw "function not needed") 123 { };
       # should just evaluate to the last value
-      accNotNeeded = foldlAttrs (_acc: _name: v: v) (throw "accumulator not needed") { z = 3; a = 2; };
+      valuesNotNeeded = foldlAttrs (acc: _name: _v: acc) 3 { z = throw "value z not needed"; a = throw "value a not needed"; };
       # the accumulator doesnt have to be an attrset it can be as trivial as being just a number or string
       trivialAcc = foldlAttrs (acc: _name: v: acc * 10 + v) 1 { z = 1; a = 2; };
     };
@@ -722,7 +754,7 @@ runTests {
         names = [ "bar" "foo" ];
       };
       emptySet = 123;
-      accNotNeeded = 3;
+      valuesNotNeeded = 3;
       trivialAcc = 121;
     };
   };
@@ -972,6 +1004,51 @@ runTests {
     '';
   };
 
+  testToGitINI = {
+    expr = generators.toGitINI {
+      user = {
+        email = "user@example.org";
+        name = "John Doe";
+        signingKey = "00112233445566778899AABBCCDDEEFF";
+      };
+      gpg.program = "path-to-gpg";
+      tag.gpgSign = true;
+      include.path = "~/path/to/config.inc";
+      includeIf."gitdif:~/src/dir".path = "~/path/to/conditional.inc";
+      extra = {
+        boolean = true;
+        integer = 38;
+        name = "value";
+        subsection.value = "test";
+      };};
+    expected = ''
+      [extra]
+      ${"\t"}boolean = true
+      ${"\t"}integer = 38
+      ${"\t"}name = "value"
+
+      [extra "subsection"]
+      ${"\t"}value = "test"
+
+      [gpg]
+      ${"\t"}program = "path-to-gpg"
+
+      [include]
+      ${"\t"}path = "~/path/to/config.inc"
+
+      [includeIf "gitdif:~/src/dir"]
+      ${"\t"}path = "~/path/to/conditional.inc"
+
+      [tag]
+      ${"\t"}gpgSign = true
+
+      [user]
+      ${"\t"}email = "user@example.org"
+      ${"\t"}name = "John Doe"
+      ${"\t"}signingKey = "00112233445566778899AABBCCDDEEFF"
+    '';
+  };
+
   /* right now only invocation check */
   testToJSONSimple =
     let val = {
diff --git a/lib/tests/modules.sh b/lib/tests/modules.sh
index 2c5e4cdbcec..05c99e6de83 100755
--- a/lib/tests/modules.sh
+++ b/lib/tests/modules.sh
@@ -39,7 +39,7 @@ reportFailure() {
 checkConfigOutput() {
     local outputContains=$1
     shift
-    if evalConfig "$@" 2>/dev/null | grep --silent "$outputContains" ; then
+    if evalConfig "$@" 2>/dev/null | grep -E --silent "$outputContains" ; then
         ((++pass))
     else
         echo 2>&1 "error: Expected result matching '$outputContains', while evaluating"
@@ -91,6 +91,9 @@ checkConfigOutput '^true$' config.result ./test-mergeAttrDefinitionsWithPrio.nix
 # is the option.
 checkConfigOutput '^true$' config.result ./module-argument-default.nix
 
+# gvariant
+checkConfigOutput '^true$' config.assertion ./gvariant.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
@@ -444,6 +447,24 @@ checkConfigOutput '^"The option `a\.b. defined in `.*/doRename-warnings\.nix. ha
 checkConfigOutput '^"pear"$' config.once.raw ./merge-module-with-key.nix
 checkConfigOutput '^"pear\\npear"$' config.twice.raw ./merge-module-with-key.nix
 
+# Declaration positions
+# Line should be present for direct options
+checkConfigOutput '^10$' options.imported.line10.declarationPositions.0.line ./declaration-positions.nix
+checkConfigOutput '/declaration-positions.nix"$' options.imported.line10.declarationPositions.0.file ./declaration-positions.nix
+# Generated options may not have line numbers but they will at least get the
+# right file
+checkConfigOutput '/declaration-positions.nix"$' options.generated.line18.declarationPositions.0.file ./declaration-positions.nix
+checkConfigOutput '^null$' options.generated.line18.declarationPositions.0.line ./declaration-positions.nix
+# Submodules don't break it
+checkConfigOutput '^39$' config.submoduleLine34.submodDeclLine39.0.line ./declaration-positions.nix
+checkConfigOutput '/declaration-positions.nix"$' config.submoduleLine34.submodDeclLine39.0.file ./declaration-positions.nix
+# New options under freeform submodules get collected into the parent submodule
+# (consistent with .declarations behaviour, but weird; notably appears in system.build)
+checkConfigOutput '^34|23$' options.submoduleLine34.declarationPositions.0.line ./declaration-positions.nix
+checkConfigOutput '^34|23$' options.submoduleLine34.declarationPositions.1.line ./declaration-positions.nix
+# nested options work
+checkConfigOutput '^30$' options.nested.nestedLine30.declarationPositions.0.line ./declaration-positions.nix
+
 cat <<EOF
 ====== module tests ======
 $pass Pass
diff --git a/lib/tests/modules/declaration-positions.nix b/lib/tests/modules/declaration-positions.nix
new file mode 100644
index 00000000000..cefd3b4e516
--- /dev/null
+++ b/lib/tests/modules/declaration-positions.nix
@@ -0,0 +1,49 @@
+{ lib, options, ... }:
+let discardPositions = lib.mapAttrs (k: v: v);
+in
+# unsafeGetAttrPos is unspecified best-effort behavior, so we only want to consider this test on an evaluator that satisfies some basic assumptions about this function.
+assert builtins.unsafeGetAttrPos "a" { a = true; } != null;
+assert builtins.unsafeGetAttrPos "a" (discardPositions { a = true; }) == null;
+{
+  imports = [
+    {
+      options.imported.line10 = lib.mkOption {
+        type = lib.types.int;
+      };
+
+      # Simulates various patterns of generating modules such as
+      # programs.firefox.nativeMessagingHosts.ff2mpv. We don't expect to get
+      # line numbers for these, but we can fall back on knowing the file.
+      options.generated = discardPositions {
+        line18 = lib.mkOption {
+          type = lib.types.int;
+        };
+      };
+
+      options.submoduleLine34.extraOptLine23 = lib.mkOption {
+        default = 1;
+        type = lib.types.int;
+      };
+    }
+  ];
+
+  options.nested.nestedLine30 = lib.mkOption {
+    type = lib.types.int;
+  };
+
+  options.submoduleLine34 = lib.mkOption {
+    default = { };
+    type = lib.types.submoduleWith {
+      modules = [
+        ({ options, ... }: {
+          options.submodDeclLine39 = lib.mkOption { };
+        })
+        { freeformType = with lib.types; lazyAttrsOf (uniq unspecified); }
+      ];
+    };
+  };
+
+  config = {
+    submoduleLine34.submodDeclLine39 = (options.submoduleLine34.type.getSubOptions [ ]).submodDeclLine39.declarationPositions;
+  };
+}
diff --git a/lib/tests/modules/gvariant.nix b/lib/tests/modules/gvariant.nix
index a792ebf85b7..ba452c0287a 100644
--- a/lib/tests/modules/gvariant.nix
+++ b/lib/tests/modules/gvariant.nix
@@ -1,93 +1,61 @@
 { config, lib, ... }:
 
-let inherit (lib) concatStringsSep mapAttrsToList mkMerge mkOption types gvariant;
-in {
-  options.examples = mkOption { type = types.attrsOf gvariant; };
+{
+  options = {
+    examples = lib.mkOption { type = lib.types.attrs; };
+    assertion = lib.mkOption { type = lib.types.bool; };
+  };
 
   config = {
-    examples = with gvariant;
-      mkMerge [
-        { bool = true; }
-        { bool = true; }
-
-        { float = 3.14; }
-
-        { int32 = mkInt32 (- 42); }
-        { int32 = mkInt32 (- 42); }
-
-        { uint32 = mkUint32 42; }
-        { uint32 = mkUint32 42; }
-
-        { int16 = mkInt16 (-42); }
-        { int16 = mkInt16 (-42); }
-
-        { uint16 = mkUint16 42; }
-        { uint16 = mkUint16 42; }
-
-        { int64 = mkInt64 (-42); }
-        { int64 = mkInt64 (-42); }
-
-        { uint64 = mkUint64 42; }
-        { uint64 = mkUint64 42; }
-
-        { array1 = [ "one" ]; }
-        { array1 = mkArray [ "two" ]; }
-        { array2 = mkArray [ (mkInt32 1) ]; }
-        { array2 = mkArray [ (nkUint32 2) ]; }
-
-        { emptyArray1 = [ ]; }
-        { emptyArray2 = mkEmptyArray type.uint32; }
-
-        { string = "foo"; }
-        { string = "foo"; }
-        {
-          escapedString = ''
-            '\
-          '';
-        }
-
-        { tuple = mkTuple [ (mkInt32 1) [ "foo" ] ]; }
-
-        { maybe1 = mkNothing type.string; }
-        { maybe2 = mkJust (mkUint32 4); }
-
-        { variant1 = mkVariant "foo"; }
-        { variant2 = mkVariant 42; }
-
-        { dictionaryEntry = mkDictionaryEntry (mkInt32 1) [ "foo" ]; }
-      ];
-
-    assertions = [
-      {
-        assertion = (
-          let
-            mkLine = n: v: "${n} = ${toString (gvariant.mkValue v)}";
-            result = concatStringsSep "\n" (mapAttrsToList mkLine config.examples);
-          in
-          result + "\n"
-        ) == ''
-          array1 = @as ['one','two']
-          array2 = @au [1,2]
-          bool = true
-          dictionaryEntry = @{ias} {1,@as ['foo']}
-          emptyArray1 = @as []
-          emptyArray2 = @au []
-          escapedString = '\'\\\n'
-          float = 3.140000
-          int = -42
-          int16 = @n -42
-          int64 = @x -42
-          maybe1 = @ms nothing
-          maybe2 = just @u 4
-          string = 'foo'
-          tuple = @(ias) (1,@as ['foo'])
-          uint16 = @q 42
-          uint32 = @u 42
-          uint64 = @t 42
-          variant1 = @v <'foo'>
-          variant2 = @v <42>
-        '';
-      }
-    ];
+    examples = with lib.gvariant; {
+      bool = true;
+      float = 3.14;
+      int32 = mkInt32 (- 42);
+      uint32 = mkUint32 42;
+      int16 = mkInt16 (-42);
+      uint16 = mkUint16 42;
+      int64 = mkInt64 (-42);
+      uint64 = mkUint64 42;
+      array1 = [ "one" ];
+      array2 = mkArray [ (mkInt32 1) ];
+      array3 = mkArray [ (mkUint32 2) ];
+      emptyArray = mkEmptyArray type.uint32;
+      string = "foo";
+      escapedString = ''
+        '\
+      '';
+      tuple = mkTuple [ (mkInt32 1) [ "foo" ] ];
+      maybe1 = mkNothing type.string;
+      maybe2 = mkJust (mkUint32 4);
+      variant = mkVariant "foo";
+      dictionaryEntry = mkDictionaryEntry (mkInt32 1) [ "foo" ];
+    };
+
+    assertion =
+      let
+        mkLine = n: v: "${n} = ${toString (lib.gvariant.mkValue v)}";
+        result = lib.concatStringsSep "\n" (lib.mapAttrsToList mkLine config.examples);
+      in
+      (result + "\n") == ''
+        array1 = @as ['one']
+        array2 = @ai [1]
+        array3 = @au [@u 2]
+        bool = true
+        dictionaryEntry = @{ias} {1,@as ['foo']}
+        emptyArray = @au []
+        escapedString = '\'\\\n'
+        float = 3.140000
+        int16 = @n -42
+        int32 = -42
+        int64 = @x -42
+        maybe1 = @ms nothing
+        maybe2 = just @u 4
+        string = 'foo'
+        tuple = @(ias) (1,@as ['foo'])
+        uint16 = @q 42
+        uint32 = @u 42
+        uint64 = @t 42
+        variant = <'foo'>
+      '';
   };
 }