Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lib/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ let
options = callLibs ./options.nix;
types = callLibs ./types.nix;

# type checking plain nix values
types-simple = callLibs ./types-simple.nix;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use camelCase for attribute names.


# constants
licenses = callLibs ./licenses.nix;
systems = callLibs ./systems;
Expand Down
3 changes: 2 additions & 1 deletion lib/generators.nix
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,8 @@ rec {
if attrNames v == [ "__pretty" "val" ] && allowPrettyValues
then v.__pretty v.val
# TODO: there is probably a better representation?
else if v ? type && v.type == "derivation" then "<δ>"
else if v ? type && v.type == "derivation" then
"<δ:${v.name}>"
else "{ "
+ libStr.concatStringsSep " " (libAttr.mapAttrsToList
(name: value:
Expand Down
2 changes: 1 addition & 1 deletion lib/licenses.nix
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ let

in

lib.mapAttrs (n: v: v // { shortName = n; }) rec {
lib.mapAttrs (n: v: v // { shortName = n; free = v.free or true; }) rec {
/* License identifiers from spdx.org where possible.
* If you cannot find your license here, then look for a similar license or
* add it to this list. The URL mentioned above is a good source for inspiration.
Expand Down
11 changes: 11 additions & 0 deletions lib/tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# lib tests

Here you can find tests for the library.

* Tests for the module system are defined in `./modules.sh`.
* Tests for most other library functions are defined in `./misc.sh`.

You can build everything by running `nix build -f ./release.nix`, though calling
test suites directly is considerably faster. Consult the files listed above to
find out how to do that.

10 changes: 7 additions & 3 deletions lib/tests/misc.nix
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
# if the resulting list is empty, all tests passed
with import ../default.nix;

runTests {
# types-simple tests
import ./types-simple.nix

# misc tests
++ (runTests {


# TRIVIAL
Expand Down Expand Up @@ -308,7 +312,7 @@ runTests {
functionArgs = "<λ:{(arg),foo}>";
list = "[ 3 4 ${function} [ false ] ]";
attrs = "{ \"foo\" = null; \"foo bar\" = \"baz\"; }";
drv = "<δ>";
drv = "<δ:test>";
};
};

Expand Down Expand Up @@ -360,4 +364,4 @@ runTests {
expected = true;
};

}
})
270 changes: 270 additions & 0 deletions lib/tests/types-simple.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
# tests for /lib/types-simple
let lib = import ../default.nix;
in with lib.types-simple;

let

# Generate a type checker error.
# expectedType is the type expected at that position
# val is the value that was badly typed
err = expectedType: val: {
inherit val;
should = expectedType.description;
};
# a successful type check
ok = {};

# Test the checkType function results.
# type is the type to check for
# val is the value that should be of type type
# result is the expected checkType result
test = type: val: result: {
expr = checkType type val;
expected = result;
};

testDef = type: result: {
expr = defaults type;
expected = result;
};

# TODO test the return type of checkType to be
# nested attrs (product { should = string; val = any; })

in lib.runTests ({


# -- Scalars --

testVoid = test void 23 (err void 23);

testAnyInt = test any 42 ok;
testAnyString = test any "foo" ok;
testAnyList = test any [ 3 "45" { dont = "do this"; } "ever" ] ok;

testUnitOk = test unit {} ok;
testUnitFoo = test unit "foo" (err unit "foo");

testBoolOk = test bool true ok;
testBoolFoo = test bool 23 (err bool 23);

testStringOk = test string "foo" ok;
testStringFoo = test string false (err string false);

testPathOk = test path /. ok;
testPathFoo = test path "/nope" (err path "/nope");

testIntOk = test int 42 ok;
testIntFoo = test int {} (err int {});

testFloatOk = test float 3.14 ok;
testFloatOkStrange = test float 23. ok;
testFloatNotInt = test float 23 (err float 23);
testFloatFoo = test float [ "nope" ] (err float [ "nope" ]);


# -- Recursives --

testListEmpty = test (list void) [] ok;
testListIntOk = test (list int) [ 1 2 3 ] ok;
testListPosFoo = test (list int) [ 1 "ahh" 3 true ] {
"1" = (err int "ahh");
"3" = (err int true);
};
testListOfListUnitOk = test (list (list unit)) [ [] [{}] [{} {} {}] ] ok;
testListOfListUnitFoo = test (list (list unit)) [ {} [ {} "ups" ] [[]] ] {
"0" = err (list unit) {};
"1"."1" = err unit "ups";
"2"."0" = err unit [];
};

testAttrsEmpty = test (attrs void) {} ok;
testAttrsIntOk = test (attrs int) { foo = 1; bar = 2; } ok;
testAttrsIntListFoo = test (attrs int) [] (err (attrs int) []);
testAttrsIntFoo = test (attrs int) { foo.bar = 1; baz = 2; quux = true; } {
foo = err int { bar = 1; };
quux = err int true;
};
testAttrsOfAttrsOk = test (attrs (attrs unit)) { foo.bar = {}; baz.quux = {}; } ok;
testAttrsOfAttrsEmptyOk = test (attrs (attrs unit)) {} ok;
testAttrsOfAttrsFoo = test (attrs (attrs unit)) { a = []; b.c.d.e = false; } {
a = err (attrs unit) [];
b.c = err unit { d.e = false; };
};

testListOfAttrsOk1 = test (list (attrs unit)) [] ok;
testListOfAttrsOk2 = test (list (attrs unit)) [ { a = {}; } { b = {}; } ] ok;
testListOfAttrsFoo = test (list (attrs unit))
[ 42 { a = {}; b.c.d = "x"; } { x = []; } {} ]
{
"0" = err (attrs unit) 42;
"1".b = err unit { c.d = "x"; };
"2".x = err unit [];
};


# -- Products --

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are "products"? Seems like a pretty strange terminology.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

types-simple is essentially defining algebraic data types, like ML or Haskell have; "products" is the standard terminology for tuple- or record-like things in this setting.


testProductOk = test (product { name = string; age = int; })
{ name = "hans"; age = 42; } ok;
testProductWrongTypes = test (product { name = string; age = int; })
{ name = true; age = 23.5; }
{
name = err string true;
age = err int 23.5;
};
testProductWrongField = test (product { foo = bool; })
{ bar = "foo"; }
(err (product { foo = bool; }) { bar = "foo"; });
testProductTooManyFields = test (product { a = int; b = int; })
{ a = 1; b = 2; c = "hello"; }
(err (product { a = int; b = int; }) { a = 1; b = 2; c = "hello"; });
testProductEmptyOk = test (product {}) {} ok;
testProductEmptyFoo = test (product {}) { name = "hans"; }
(err (product {}) { name = "hans"; });

testProductOptOk = test
(productOpt { req = { a = unit; }; opt = { b = unit; }; })
{ a = {}; b = {}; } ok;
testProductOptNoOptOk = test
(productOpt { req = { a = unit; }; opt = { b = unit; }; })
{ a = {}; } ok;
testProductOnlyOptNoReq = test
(productOpt { req = { a = unit; }; opt = { b = unit; }; })
{ b = {}; }
(err (productOpt { req = { a = unit; }; opt = { b = unit; }; }) { b = {}; }) ;
testProductOnlyOptOk = test
(productOpt { req = {}; opt = { x = unit; y = unit; }; })
{ y = {}; } ok;
testProductInProductOpt = test
(productOpt { req = {}; opt = { p = product { x = int; y = bool; }; }; })
{ p = { x = 23; }; } # missing the required p.y
{ p = (err (product { x = int; y = bool; }) { x = 23; }); };

testProductOptOpenOk = test
(productOpt { req = { a = unit; }; opt = {}; open = true; })
{ a = {}; rest1 = 12; rest2 = "foo"; } ok;
testProductOptOpenIgnoreRest = test
(productOpt { req = { a = unit; }; opt = { b = int; }; open = true; })
{ a = 23; b = false; rest = "foo"; x = {}; y = 23; }
{ a = err unit 23; b = err int false; };


# -- Sums --

testSumLeftOk = test (sum { left = string; right = unit; })
{ left = "errör!"; } ok;
testSumRightOk = test (sum { left = string; right = unit; })
{ right = {}; } ok;
testSumWrongField = test (sum { a = int; b = bool; })
{ c = "ups"; }
(err (sum { a = int; b = bool; }) { c = "ups"; });
testSumIsNotUnion = test (sum { a = string; b = int; })
42
(err (sum { a = string; b = int; }) 42);
testSumTooManyFields = test (sum { a = int; b = unit; })
{ a = 21; b = {}; }
(err (sum { a = int; b = unit; }) { a = 21; b = {}; });


# -- Unions --

testUnionOk1 = test (union [ int string (list unit) ]) 23 ok;
testUnionOk2 = test (union [ int string (list unit) ]) "foo" ok;
testUnionOk3 = test (union [ int string (list unit) ]) [{}{}] ok;
testUnionWrongType = test (union [ int string ]) true
(err (union [ int string ]) true);
testUnionOne = test (union [ int ]) 23 ok;
testUnionSimilar = test (union [ (list string) (attrs string) ])
{ foo = "string"; } ok;

} //


# -- Restrictions --

(let
even = restrict {
type = int;
check = v: lib.mod v 2 == 0;
description = "even integer";
};

intBetween = min: max: restrict {
type = int;
check = v: v >= min && v <= max;
description = "int between ${toString min} ${toString max}";
};

thirdElementIsListOf23 = restrict {
type = list (list int);
check = v: builtins.length v >= 3 && builtins.elemAt v 2 == [23];
description = "third element is [23]";
};

enum = t: xs: restrict {
type = t;
check = v: lib.any (x: x == v) xs;
description = "one of values [ " +
lib.concatMapStringsSep ", " (lib.generators.toPretty {}) xs
+ " ]";
};

transitive = restrict {
description = "neither a nor b";
check = v: v != "b";
type = restrict {
description = "not a";
check = v: v != "a";
type = string;
};
};

in {
testRestrictEvenOk = test (list even)
[ 2 4 128 42 ] ok;
testRestrictEvenFoo = test (list even)
[ 42 23 ]
{ "1" = err even 23; };

testRestrictTypeCheckFirst =
let t = restrict {
type = void;
# will crash if "23" is checked here
# before the check for its type
check = v: (v + 19) == 42;
description = "is worthy";
};
# we actually want it to always give the type description
# of the restricted type when the general type check fails
# e.g. 23 should return "even integer" instead of "integer"
# when checking for the int restricted to even integers
in test t "23" (err t "23");

testDeepRestriction = test thirdElementIsListOf23
[ [1] ["foo"] [23] ]
{ "1"."0" = err int "foo"; };
testDeepRestrictionFoo = test thirdElementIsListOf23
[ [] [] [24] [] [] ]
(err thirdElementIsListOf23 [ [] [] [24] [] [] ]);

testRestrictTransitiveOk = test transitive "c" ok;
testRestrictTransitiveA = test transitive "a"
(err transitive "a");
testRestrictTransitiveB = test transitive "b"
(err transitive "b");

testRestrictIntBetweenOk = test (list (intBetween (-2) 3))
[ (-2) (-1) 0 1 2 3 ] ok;
testRestrictIntBetweenFoo = test (list (intBetween 0 0))
[ (-23) 42 ]
{ "0" = err (intBetween 0 0) (-23);
"1" = err (intBetween 0 0) 42; };

testRestrictEnumOk = test (list (enum string [ "a" "b" "c" ]))
[ "b" "c" "a" ] ok;
testRestrictEnumFoo = test (list (enum string [ "a" "b" "c" ]))
[ "b" "d" "a" ]
{ "1" = err (enum string [ "a" "b" "c" ]) "d"; };

}))
Loading