electron-source.electron_29: remove as it's EOL
[NixPkgs.git] / lib / debug.nix
blobb10023d02616c31a783c10fd1b953aa0d63982f9
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;
32 rec {
34   # -- TRACING --
36   /**
37     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:
74     msg:
75     x: if pred then trace msg x else x;
77   /**
78     Trace the supplied value after applying a function to it, and
79     return the original value.
82     # Inputs
84     `f`
86     : Function to apply
88     `x`
90     : Value to trace and return
92     # Type
94     ```
95     traceValFn :: (a -> b) -> a -> a
96     ```
98     # Examples
99     :::{.example}
100     ## `lib.debug.traceValFn` usage example
102     ```nix
103     traceValFn (v: "mystring ${v}") "foo"
104     trace: mystring foo
105     => "foo"
106     ```
108     :::
109   */
110   traceValFn =
111     f:
112     x: trace (f x) x;
114   /**
115     Trace the supplied value and return it.
117     # Inputs
119     `x`
121     : Value to trace and return
123     # Type
125     ```
126     traceVal :: a -> a
127     ```
129     # Examples
130     :::{.example}
131     ## `lib.debug.traceVal` usage example
133     ```nix
134     traceVal 42
135     # trace: 42
136     => 42
137     ```
139     :::
140   */
141   traceVal = traceValFn id;
143   /**
144     `builtins.trace`, but the value is `builtins.deepSeq`ed first.
147     # Inputs
149     `x`
151     : The value to trace
153     `y`
155     : The value to return
157     # Type
159     ```
160     traceSeq :: a -> b -> b
161     ```
163     # Examples
164     :::{.example}
165     ## `lib.debug.traceSeq` usage example
167     ```nix
168     trace { a.b.c = 3; } null
169     trace: { a = <CODE>; }
170     => null
171     traceSeq { a.b.c = 3; } null
172     trace: { a = { b = { c = 3; }; }; }
173     => null
174     ```
176     :::
177   */
178   traceSeq =
179     x:
180     y: trace (builtins.deepSeq x x) y;
182   /**
183     Like `traceSeq`, but only evaluate down to depth n.
184     This is very useful because lots of `traceSeq` usages
185     lead to an infinite recursion.
188     # Inputs
190     `depth`
192     : 1\. Function argument
194     `x`
196     : 2\. Function argument
198     `y`
200     : 3\. Function argument
202     # Type
204     ```
205     traceSeqN :: Int -> a -> b -> b
206     ```
208     # Examples
209     :::{.example}
210     ## `lib.debug.traceSeqN` usage example
212     ```nix
213     traceSeqN 2 { a.b.c = 3; } null
214     trace: { a = { b = {…}; }; }
215     => null
216     ```
218     :::
219   */
220   traceSeqN = depth: x: y:
221     let snip = v: if      isList  v then noQuotes "[…]" v
222                   else if isAttrs v then noQuotes "{…}" v
223                   else v;
224         noQuotes = str: v: { __pretty = const str; val = v; };
225         modify = n: fn: v: if (n == 0) then fn v
226                       else if isList  v then map (modify (n - 1) fn) v
227                       else if isAttrs v then mapAttrs
228                         (const (modify (n - 1) fn)) v
229                       else v;
230     in trace (generators.toPretty { allowPrettyValues = true; }
231                (modify depth snip x)) y;
233   /**
234     A combination of `traceVal` and `traceSeq` that applies a
235     provided function to the value to be traced after `deepSeq`ing
236     it.
239     # Inputs
241     `f`
243     : Function to apply
245     `v`
247     : Value to trace
248   */
249   traceValSeqFn =
250     f:
251     v: traceValFn f (builtins.deepSeq v v);
253   /**
254     A combination of `traceVal` and `traceSeq`.
256     # Inputs
258     `v`
260     : Value to trace
262   */
263   traceValSeq = traceValSeqFn id;
265   /**
266     A combination of `traceVal` and `traceSeqN` that applies a
267     provided function to the value to be traced.
270     # Inputs
272     `f`
274     : Function to apply
276     `depth`
278     : 2\. Function argument
280     `v`
282     : Value to trace
283   */
284   traceValSeqNFn =
285     f:
286     depth:
287     v: traceSeqN depth (f v) v;
289   /**
290     A combination of `traceVal` and `traceSeqN`.
292     # Inputs
294     `depth`
296     : 1\. Function argument
298     `v`
300     : Value to trace
301   */
302   traceValSeqN = traceValSeqNFn id;
304   /**
305     Trace the input and output of a function `f` named `name`,
306     both down to `depth`.
308     This is useful for adding around a function call,
309     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
331     # Examples
332     :::{.example}
333     ## `lib.debug.traceFnSeqN` usage example
335     ```nix
336     traceFnSeqN 2 "id" (x: x) { a.b.c = 3; }
337     trace: { fn = "id"; from = { a.b = {…}; }; to = { a.b = {…}; }; }
338     => { a.b.c = 3; }
339     ```
341     :::
342   */
343   traceFnSeqN = depth: name: f: v:
344     let res = f v;
345     in lib.traceSeqN
346         (depth + 1)
347         {
348           fn = name;
349           from = v;
350           to = res;
351         }
352         res;
355   # -- TESTING --
357   /**
358     Evaluates a set of tests.
360     A test is an attribute set `{expr, expected}`,
361     denoting an expression and its expected result.
363     The result is a `list` of __failed tests__, each represented as
364     `{name, expected, result}`,
366     - expected
367       - What was passed as `expected`
368     - result
369       - The actual `result` of the test
371     Used for regression testing of the functions in lib; see
372     tests.nix for more examples.
374     Important: Only attributes that start with `test` are executed.
376     - If you want to run only a subset of the tests add the attribute `tests = ["testName"];`
379     # Inputs
381     `tests`
383     : Tests to run
385     # Type
387     ```
388     runTests :: {
389       tests = [ String ];
390       ${testName} :: {
391         expr :: a;
392         expected :: a;
393       };
394     }
395     ->
396     [
397       {
398         name :: String;
399         expected :: a;
400         result :: a;
401       }
402     ]
403     ```
405     # Examples
406     :::{.example}
407     ## `lib.debug.runTests` usage example
409     ```nix
410     runTests {
411       testAndOk = {
412         expr = lib.and true false;
413         expected = false;
414       };
415       testAndFail = {
416         expr = lib.and true false;
417         expected = true;
418       };
419     }
420     ->
421     [
422       {
423         name = "testAndFail";
424         expected = true;
425         result = false;
426       }
427     ]
428     ```
430     :::
431   */
432   runTests =
433     tests: concatLists (attrValues (mapAttrs (name: test:
434     let testsToRun = if tests ? tests then tests.tests else [];
435     in if (substring 0 4 name == "test" ||  elem name testsToRun)
436        && ((testsToRun == []) || elem name tests.tests)
437        && (test.expr != test.expected)
439       then [ { inherit name; expected = test.expected; result = test.expr; } ]
440       else [] ) tests));
442   /**
443     Create a test assuming that list elements are `true`.
446     # Inputs
448     `expr`
450     : 1\. Function argument
453     # Examples
454     :::{.example}
455     ## `lib.debug.testAllTrue` usage example
457     ```nix
458     { testX = allTrue [ true ]; }
459     ```
461     :::
462   */
463   testAllTrue = expr: { inherit expr; expected = map (x: true) expr; };