pocket-casts: update electron pin; move to pkgs/by-name/ (#372095)
[NixPkgs.git] / lib / debug.nix
blobc2226f16c6a39e6dc9f72d1dee0310d7482d7aaa
1 /**
2   Collection of functions useful for debugging
3   broken nix expressions.
5   * `trace`-like functions take two values, print
6     the first to stderr and return the second.
7   * `traceVal`-like functions take one argument
8     which both printed and returned.
9   * `traceSeq`-like functions fully evaluate their
10     traced value before printing (not just to “weak
11     head normal form” like trace does by default).
12   * Functions that end in `-Fn` take an additional
13     function as their first argument, which is applied
14     to the traced value before it is printed.
16 { lib }:
17 let
18   inherit (lib)
19     isList
20     isAttrs
21     substring
22     attrValues
23     concatLists
24     const
25     elem
26     generators
27     id
28     mapAttrs
29     trace
30     ;
33 rec {
35   # -- TRACING --
37   /**
38     Conditionally trace the supplied message, based on a predicate.
40     # Inputs
42     `pred`
44     : Predicate to check
46     `msg`
48     : Message that should be traced
50     `x`
52     : Value to return
54     # Type
56     ```
57     traceIf :: bool -> string -> a -> a
58     ```
60     # Examples
61     :::{.example}
62     ## `lib.debug.traceIf` usage example
64     ```nix
65     traceIf true "hello" 3
66     trace: hello
67     => 3
68     ```
70     :::
71   */
72   traceIf =
73     pred: msg: x:
74     if pred then trace msg x else x;
76   /**
77     Trace the supplied value after applying a function to it, and
78     return the original value.
80     # Inputs
82     `f`
84     : Function to apply
86     `x`
88     : Value to trace and return
90     # Type
92     ```
93     traceValFn :: (a -> b) -> a -> a
94     ```
96     # Examples
97     :::{.example}
98     ## `lib.debug.traceValFn` usage example
100     ```nix
101     traceValFn (v: "mystring ${v}") "foo"
102     trace: mystring foo
103     => "foo"
104     ```
106     :::
107   */
108   traceValFn = f: x: trace (f x) x;
110   /**
111     Trace the supplied value and return it.
113     # Inputs
115     `x`
117     : Value to trace and return
119     # Type
121     ```
122     traceVal :: a -> a
123     ```
125     # Examples
126     :::{.example}
127     ## `lib.debug.traceVal` usage example
129     ```nix
130     traceVal 42
131     # trace: 42
132     => 42
133     ```
135     :::
136   */
137   traceVal = traceValFn id;
139   /**
140     `builtins.trace`, but the value is `builtins.deepSeq`ed first.
142     # Inputs
144     `x`
146     : The value to trace
148     `y`
150     : The value to return
152     # Type
154     ```
155     traceSeq :: a -> b -> b
156     ```
158     # Examples
159     :::{.example}
160     ## `lib.debug.traceSeq` usage example
162     ```nix
163     trace { a.b.c = 3; } null
164     trace: { a = <CODE>; }
165     => null
166     traceSeq { a.b.c = 3; } null
167     trace: { a = { b = { c = 3; }; }; }
168     => null
169     ```
171     :::
172   */
173   traceSeq = x: y: trace (builtins.deepSeq x x) y;
175   /**
176     Like `traceSeq`, but only evaluate down to depth n.
177     This is very useful because lots of `traceSeq` usages
178     lead to an infinite recursion.
180     # Inputs
182     `depth`
184     : 1\. Function argument
186     `x`
188     : 2\. Function argument
190     `y`
192     : 3\. Function argument
194     # Type
196     ```
197     traceSeqN :: Int -> a -> b -> b
198     ```
200     # Examples
201     :::{.example}
202     ## `lib.debug.traceSeqN` usage example
204     ```nix
205     traceSeqN 2 { a.b.c = 3; } null
206     trace: { a = { b = {…}; }; }
207     => null
208     ```
210     :::
211   */
212   traceSeqN =
213     depth: x: y:
214     let
215       snip =
216         v:
217         if isList v then
218           noQuotes "[…]" v
219         else if isAttrs v then
220           noQuotes "{…}" v
221         else
222           v;
223       noQuotes = str: v: {
224         __pretty = const str;
225         val = v;
226       };
227       modify =
228         n: fn: v:
229         if (n == 0) then
230           fn v
231         else if isList v then
232           map (modify (n - 1) fn) v
233         else if isAttrs v then
234           mapAttrs (const (modify (n - 1) fn)) v
235         else
236           v;
237     in
238     trace (generators.toPretty { allowPrettyValues = true; } (modify depth snip x)) y;
240   /**
241     A combination of `traceVal` and `traceSeq` that applies a
242     provided function to the value to be traced after `deepSeq`ing
243     it.
245     # Inputs
247     `f`
249     : Function to apply
251     `v`
253     : Value to trace
254   */
255   traceValSeqFn = f: v: traceValFn f (builtins.deepSeq v v);
257   /**
258     A combination of `traceVal` and `traceSeq`.
260     # Inputs
262     `v`
264     : Value to trace
265   */
266   traceValSeq = traceValSeqFn id;
268   /**
269     A combination of `traceVal` and `traceSeqN` that applies a
270     provided function to the value to be traced.
272     # Inputs
274     `f`
276     : Function to apply
278     `depth`
280     : 2\. Function argument
282     `v`
284     : Value to trace
285   */
286   traceValSeqNFn =
287     f: depth: v:
288     traceSeqN depth (f v) v;
290   /**
291     A combination of `traceVal` and `traceSeqN`.
293     # Inputs
295     `depth`
297     : 1\. Function argument
299     `v`
301     : Value to trace
302   */
303   traceValSeqN = traceValSeqNFn id;
305   /**
306     Trace the input and output of a function `f` named `name`,
307     both down to `depth`.
309     This is useful for adding around a function call,
310     to see the before/after of values as they are transformed.
312     # Inputs
314     `depth`
316     : 1\. Function argument
318     `name`
320     : 2\. Function argument
322     `f`
324     : 3\. Function argument
326     `v`
328     : 4\. Function argument
330     # Examples
331     :::{.example}
332     ## `lib.debug.traceFnSeqN` usage example
334     ```nix
335     traceFnSeqN 2 "id" (x: x) { a.b.c = 3; }
336     trace: { fn = "id"; from = { a.b = {…}; }; to = { a.b = {…}; }; }
337     => { a.b.c = 3; }
338     ```
340     :::
341   */
342   traceFnSeqN =
343     depth: name: f: v:
344     let
345       res = f v;
346     in
347     lib.traceSeqN (depth + 1) {
348       fn = name;
349       from = v;
350       to = res;
351     } res;
353   # -- TESTING --
355   /**
356     Evaluates a set of tests.
358     A test is an attribute set `{expr, expected}`,
359     denoting an expression and its expected result.
361     The result is a `list` of __failed tests__, each represented as
362     `{name, expected, result}`,
364     - expected
365       - What was passed as `expected`
366     - result
367       - The actual `result` of the test
369     Used for regression testing of the functions in lib; see
370     tests.nix for more examples.
372     Important: Only attributes that start with `test` are executed.
374     - If you want to run only a subset of the tests add the attribute `tests = ["testName"];`
376     # Inputs
378     `tests`
380     : Tests to run
382     # Type
384     ```
385     runTests :: {
386       tests = [ String ];
387       ${testName} :: {
388         expr :: a;
389         expected :: a;
390       };
391     }
392     ->
393     [
394       {
395         name :: String;
396         expected :: a;
397         result :: a;
398       }
399     ]
400     ```
402     # Examples
403     :::{.example}
404     ## `lib.debug.runTests` usage example
406     ```nix
407     runTests {
408       testAndOk = {
409         expr = lib.and true false;
410         expected = false;
411       };
412       testAndFail = {
413         expr = lib.and true false;
414         expected = true;
415       };
416     }
417     ->
418     [
419       {
420         name = "testAndFail";
421         expected = true;
422         result = false;
423       }
424     ]
425     ```
427     :::
428   */
429   runTests =
430     tests:
431     concatLists (
432       attrValues (
433         mapAttrs (
434           name: test:
435           let
436             testsToRun = if tests ? tests then tests.tests else [ ];
437           in
438           if
439             (substring 0 4 name == "test" || elem name testsToRun)
440             && ((testsToRun == [ ]) || elem name tests.tests)
441             && (test.expr != test.expected)
443           then
444             [
445               {
446                 inherit name;
447                 expected = test.expected;
448                 result = test.expr;
449               }
450             ]
451           else
452             [ ]
453         ) tests
454       )
455     );
457   /**
458     Create a test assuming that list elements are `true`.
460     # Inputs
462     `expr`
464     : 1\. Function argument
466     # Examples
467     :::{.example}
468     ## `lib.debug.testAllTrue` usage example
470     ```nix
471     { testX = allTrue [ true ]; }
472     ```
474     :::
475   */
476   testAllTrue = expr: {
477     inherit expr;
478     expected = map (x: true) expr;
479   };