Loading lib/debug.nix +354 −136 Original line number Diff line number Diff line /* Collection of functions useful for debugging /** Collection of functions useful for debugging broken nix expressions. * `trace`-like functions take two values, print Loading Loading @@ -32,78 +33,189 @@ rec { # -- TRACING -- /* Conditionally trace the supplied message, based on a predicate. /** Conditionally trace the supplied message, based on a predicate. Type: traceIf :: bool -> string -> a -> a Example: # Inputs `pred` : Predicate to check `msg` : Message that should be traced `x` : Value to return # Type ``` traceIf :: bool -> string -> a -> a ``` # Examples :::{.example} ## `lib.debug.traceIf` usage example ```nix traceIf true "hello" 3 trace: hello => 3 ``` ::: */ traceIf = # Predicate to check pred: # Message that should be traced msg: # Value to return x: if pred then trace msg x else x; /* Trace the supplied value after applying a function to it, and /** Trace the supplied value after applying a function to it, and return the original value. Type: traceValFn :: (a -> b) -> a -> a Example: # Inputs `f` : Function to apply `x` : Value to trace and return # Type ``` traceValFn :: (a -> b) -> a -> a ``` # Examples :::{.example} ## `lib.debug.traceValFn` usage example ```nix traceValFn (v: "mystring ${v}") "foo" trace: mystring foo => "foo" ``` ::: */ traceValFn = # Function to apply f: # Value to trace and return x: trace (f x) x; /* Trace the supplied value and return it. /** Trace the supplied value and return it. # Inputs `x` : Value to trace and return # Type Type: traceVal :: a -> a ``` traceVal :: a -> a ``` Example: # Examples :::{.example} ## `lib.debug.traceVal` usage example ```nix traceVal 42 # trace: 42 => 42 ``` ::: */ traceVal = traceValFn id; /* `builtins.trace`, but the value is `builtins.deepSeq`ed first. /** `builtins.trace`, but the value is `builtins.deepSeq`ed first. # Inputs `x` : The value to trace `y` : The value to return Type: traceSeq :: a -> b -> b # Type Example: ``` traceSeq :: a -> b -> b ``` # Examples :::{.example} ## `lib.debug.traceSeq` usage example ```nix trace { a.b.c = 3; } null trace: { a = <CODE>; } => null traceSeq { a.b.c = 3; } null trace: { a = { b = { c = 3; }; }; } => null ``` ::: */ traceSeq = # The value to trace x: # The value to return y: trace (builtins.deepSeq x x) y; /* Like `traceSeq`, but only evaluate down to depth n. /** Like `traceSeq`, but only evaluate down to depth n. This is very useful because lots of `traceSeq` usages lead to an infinite recursion. Example: # Inputs `depth` : 1\. Function argument `x` : 2\. Function argument `y` : 3\. Function argument # Type ``` traceSeqN :: Int -> a -> b -> b ``` # Examples :::{.example} ## `lib.debug.traceSeqN` usage example ```nix traceSeqN 2 { a.b.c = 3; } null trace: { a = { b = {…}; }; } => null ``` Type: traceSeqN :: Int -> a -> b -> b ::: */ traceSeqN = depth: x: y: let snip = v: if isList v then noQuotes "[…]" v Loading @@ -118,41 +230,115 @@ rec { in trace (generators.toPretty { allowPrettyValues = true; } (modify depth snip x)) y; /* A combination of `traceVal` and `traceSeq` that applies a /** A combination of `traceVal` and `traceSeq` that applies a provided function to the value to be traced after `deepSeq`ing it. # Inputs `f` : Function to apply `v` : Value to trace */ traceValSeqFn = # Function to apply f: # Value to trace v: traceValFn f (builtins.deepSeq v v); /* A combination of `traceVal` and `traceSeq`. */ /** A combination of `traceVal` and `traceSeq`. # Inputs `v` : Value to trace */ traceValSeq = traceValSeqFn id; /* A combination of `traceVal` and `traceSeqN` that applies a provided function to the value to be traced. */ /** A combination of `traceVal` and `traceSeqN` that applies a provided function to the value to be traced. # Inputs `f` : Function to apply `depth` : 2\. Function argument `v` : Value to trace */ traceValSeqNFn = # Function to apply f: depth: # Value to trace v: traceSeqN depth (f v) v; /* A combination of `traceVal` and `traceSeqN`. */ /** A combination of `traceVal` and `traceSeqN`. # Inputs `depth` : 1\. Function argument `v` : Value to trace */ traceValSeqN = traceValSeqNFn id; /* Trace the input and output of a function `f` named `name`, /** Trace the input and output of a function `f` named `name`, both down to `depth`. This is useful for adding around a function call, to see the before/after of values as they are transformed. Example: # Inputs `depth` : 1\. Function argument `name` : 2\. Function argument `f` : 3\. Function argument `v` : 4\. Function argument # Examples :::{.example} ## `lib.debug.traceFnSeqN` usage example ```nix traceFnSeqN 2 "id" (x: x) { a.b.c = 3; } trace: { fn = "id"; from = { a.b = {…}; }; to = { a.b = {…}; }; } => { a.b.c = 3; } ``` ::: */ traceFnSeqN = depth: name: f: v: let res = f v; Loading @@ -168,7 +354,8 @@ rec { # -- TESTING -- /* Evaluates a set of tests. /** Evaluates a set of tests. A test is an attribute set `{expr, expected}`, denoting an expression and its expected result. Loading @@ -188,8 +375,38 @@ rec { - If you want to run only a subset of the tests add the attribute `tests = ["testName"];` Example: # Inputs `tests` : Tests to run # Type ``` runTests :: { tests = [ String ]; ${testName} :: { expr :: a; expected :: a; }; } -> [ { name :: String; expected :: a; result :: a; } ] ``` # Examples :::{.example} ## `lib.debug.runTests` usage example ```nix runTests { testAndOk = { expr = lib.and true false; Loading @@ -208,26 +425,11 @@ rec { result = false; } ] ``` Type: runTests :: { tests = [ String ]; ${testName} :: { expr :: a; expected :: a; }; } -> [ { name :: String; expected :: a; result :: a; } ] ::: */ runTests = # Tests to run tests: concatLists (attrValues (mapAttrs (name: test: let testsToRun = if tests ? tests then tests.tests else []; in if (substring 0 4 name == "test" || elem name testsToRun) Loading @@ -237,10 +439,26 @@ rec { then [ { inherit name; expected = test.expected; result = test.expr; } ] else [] ) tests)); /* Create a test assuming that list elements are `true`. /** Create a test assuming that list elements are `true`. # Inputs `expr` Example: : 1\. Function argument # Examples :::{.example} ## `lib.debug.testAllTrue` usage example ```nix { testX = allTrue [ true ]; } ``` ::: */ testAllTrue = expr: { inherit expr; expected = map (x: true) expr; }; } Loading
lib/debug.nix +354 −136 Original line number Diff line number Diff line /* Collection of functions useful for debugging /** Collection of functions useful for debugging broken nix expressions. * `trace`-like functions take two values, print Loading Loading @@ -32,78 +33,189 @@ rec { # -- TRACING -- /* Conditionally trace the supplied message, based on a predicate. /** Conditionally trace the supplied message, based on a predicate. Type: traceIf :: bool -> string -> a -> a Example: # Inputs `pred` : Predicate to check `msg` : Message that should be traced `x` : Value to return # Type ``` traceIf :: bool -> string -> a -> a ``` # Examples :::{.example} ## `lib.debug.traceIf` usage example ```nix traceIf true "hello" 3 trace: hello => 3 ``` ::: */ traceIf = # Predicate to check pred: # Message that should be traced msg: # Value to return x: if pred then trace msg x else x; /* Trace the supplied value after applying a function to it, and /** Trace the supplied value after applying a function to it, and return the original value. Type: traceValFn :: (a -> b) -> a -> a Example: # Inputs `f` : Function to apply `x` : Value to trace and return # Type ``` traceValFn :: (a -> b) -> a -> a ``` # Examples :::{.example} ## `lib.debug.traceValFn` usage example ```nix traceValFn (v: "mystring ${v}") "foo" trace: mystring foo => "foo" ``` ::: */ traceValFn = # Function to apply f: # Value to trace and return x: trace (f x) x; /* Trace the supplied value and return it. /** Trace the supplied value and return it. # Inputs `x` : Value to trace and return # Type Type: traceVal :: a -> a ``` traceVal :: a -> a ``` Example: # Examples :::{.example} ## `lib.debug.traceVal` usage example ```nix traceVal 42 # trace: 42 => 42 ``` ::: */ traceVal = traceValFn id; /* `builtins.trace`, but the value is `builtins.deepSeq`ed first. /** `builtins.trace`, but the value is `builtins.deepSeq`ed first. # Inputs `x` : The value to trace `y` : The value to return Type: traceSeq :: a -> b -> b # Type Example: ``` traceSeq :: a -> b -> b ``` # Examples :::{.example} ## `lib.debug.traceSeq` usage example ```nix trace { a.b.c = 3; } null trace: { a = <CODE>; } => null traceSeq { a.b.c = 3; } null trace: { a = { b = { c = 3; }; }; } => null ``` ::: */ traceSeq = # The value to trace x: # The value to return y: trace (builtins.deepSeq x x) y; /* Like `traceSeq`, but only evaluate down to depth n. /** Like `traceSeq`, but only evaluate down to depth n. This is very useful because lots of `traceSeq` usages lead to an infinite recursion. Example: # Inputs `depth` : 1\. Function argument `x` : 2\. Function argument `y` : 3\. Function argument # Type ``` traceSeqN :: Int -> a -> b -> b ``` # Examples :::{.example} ## `lib.debug.traceSeqN` usage example ```nix traceSeqN 2 { a.b.c = 3; } null trace: { a = { b = {…}; }; } => null ``` Type: traceSeqN :: Int -> a -> b -> b ::: */ traceSeqN = depth: x: y: let snip = v: if isList v then noQuotes "[…]" v Loading @@ -118,41 +230,115 @@ rec { in trace (generators.toPretty { allowPrettyValues = true; } (modify depth snip x)) y; /* A combination of `traceVal` and `traceSeq` that applies a /** A combination of `traceVal` and `traceSeq` that applies a provided function to the value to be traced after `deepSeq`ing it. # Inputs `f` : Function to apply `v` : Value to trace */ traceValSeqFn = # Function to apply f: # Value to trace v: traceValFn f (builtins.deepSeq v v); /* A combination of `traceVal` and `traceSeq`. */ /** A combination of `traceVal` and `traceSeq`. # Inputs `v` : Value to trace */ traceValSeq = traceValSeqFn id; /* A combination of `traceVal` and `traceSeqN` that applies a provided function to the value to be traced. */ /** A combination of `traceVal` and `traceSeqN` that applies a provided function to the value to be traced. # Inputs `f` : Function to apply `depth` : 2\. Function argument `v` : Value to trace */ traceValSeqNFn = # Function to apply f: depth: # Value to trace v: traceSeqN depth (f v) v; /* A combination of `traceVal` and `traceSeqN`. */ /** A combination of `traceVal` and `traceSeqN`. # Inputs `depth` : 1\. Function argument `v` : Value to trace */ traceValSeqN = traceValSeqNFn id; /* Trace the input and output of a function `f` named `name`, /** Trace the input and output of a function `f` named `name`, both down to `depth`. This is useful for adding around a function call, to see the before/after of values as they are transformed. Example: # Inputs `depth` : 1\. Function argument `name` : 2\. Function argument `f` : 3\. Function argument `v` : 4\. Function argument # Examples :::{.example} ## `lib.debug.traceFnSeqN` usage example ```nix traceFnSeqN 2 "id" (x: x) { a.b.c = 3; } trace: { fn = "id"; from = { a.b = {…}; }; to = { a.b = {…}; }; } => { a.b.c = 3; } ``` ::: */ traceFnSeqN = depth: name: f: v: let res = f v; Loading @@ -168,7 +354,8 @@ rec { # -- TESTING -- /* Evaluates a set of tests. /** Evaluates a set of tests. A test is an attribute set `{expr, expected}`, denoting an expression and its expected result. Loading @@ -188,8 +375,38 @@ rec { - If you want to run only a subset of the tests add the attribute `tests = ["testName"];` Example: # Inputs `tests` : Tests to run # Type ``` runTests :: { tests = [ String ]; ${testName} :: { expr :: a; expected :: a; }; } -> [ { name :: String; expected :: a; result :: a; } ] ``` # Examples :::{.example} ## `lib.debug.runTests` usage example ```nix runTests { testAndOk = { expr = lib.and true false; Loading @@ -208,26 +425,11 @@ rec { result = false; } ] ``` Type: runTests :: { tests = [ String ]; ${testName} :: { expr :: a; expected :: a; }; } -> [ { name :: String; expected :: a; result :: a; } ] ::: */ runTests = # Tests to run tests: concatLists (attrValues (mapAttrs (name: test: let testsToRun = if tests ? tests then tests.tests else []; in if (substring 0 4 name == "test" || elem name testsToRun) Loading @@ -237,10 +439,26 @@ rec { then [ { inherit name; expected = test.expected; result = test.expr; } ] else [] ) tests)); /* Create a test assuming that list elements are `true`. /** Create a test assuming that list elements are `true`. # Inputs `expr` Example: : 1\. Function argument # Examples :::{.example} ## `lib.debug.testAllTrue` usage example ```nix { testX = allTrue [ true ]; } ``` ::: */ testAllTrue = expr: { inherit expr; expected = map (x: true) expr; }; }