Clone of https://github.com/NixOS/nixpkgs.git (to stress-test knotserver)
at litex 253 lines 6.2 kB view raw
1/* Collection of functions useful for debugging 2 broken nix expressions. 3 4 * `trace`-like functions take two values, print 5 the first to stderr and return the second. 6 * `traceVal`-like functions take one argument 7 which both printed and returned. 8 * `traceSeq`-like functions fully evaluate their 9 traced value before printing (not just to weak 10 head normal form like trace does by default). 11 * Functions that end in `-Fn` take an additional 12 function as their first argument, which is applied 13 to the traced value before it is printed. 14*/ 15{ lib }: 16let 17 inherit (lib) 18 isInt 19 attrNames 20 isList 21 isAttrs 22 substring 23 addErrorContext 24 attrValues 25 concatLists 26 concatStringsSep 27 const 28 elem 29 generators 30 head 31 id 32 isDerivation 33 isFunction 34 mapAttrs 35 trace; 36in 37 38rec { 39 40 # -- TRACING -- 41 42 /* Conditionally trace the supplied message, based on a predicate. 43 44 Type: traceIf :: bool -> string -> a -> a 45 46 Example: 47 traceIf true "hello" 3 48 trace: hello 49 => 3 50 */ 51 traceIf = 52 # Predicate to check 53 pred: 54 # Message that should be traced 55 msg: 56 # Value to return 57 x: if pred then trace msg x else x; 58 59 /* Trace the supplied value after applying a function to it, and 60 return the original value. 61 62 Type: traceValFn :: (a -> b) -> a -> a 63 64 Example: 65 traceValFn (v: "mystring ${v}") "foo" 66 trace: mystring foo 67 => "foo" 68 */ 69 traceValFn = 70 # Function to apply 71 f: 72 # Value to trace and return 73 x: trace (f x) x; 74 75 /* Trace the supplied value and return it. 76 77 Type: traceVal :: a -> a 78 79 Example: 80 traceVal 42 81 # trace: 42 82 => 42 83 */ 84 traceVal = traceValFn id; 85 86 /* `builtins.trace`, but the value is `builtins.deepSeq`ed first. 87 88 Type: traceSeq :: a -> b -> b 89 90 Example: 91 trace { a.b.c = 3; } null 92 trace: { a = <CODE>; } 93 => null 94 traceSeq { a.b.c = 3; } null 95 trace: { a = { b = { c = 3; }; }; } 96 => null 97 */ 98 traceSeq = 99 # The value to trace 100 x: 101 # The value to return 102 y: trace (builtins.deepSeq x x) y; 103 104 /* Like `traceSeq`, but only evaluate down to depth n. 105 This is very useful because lots of `traceSeq` usages 106 lead to an infinite recursion. 107 108 Example: 109 traceSeqN 2 { a.b.c = 3; } null 110 trace: { a = { b = {}; }; } 111 => null 112 113 Type: traceSeqN :: Int -> a -> b -> b 114 */ 115 traceSeqN = depth: x: y: 116 let snip = v: if isList v then noQuotes "[]" v 117 else if isAttrs v then noQuotes "{}" v 118 else v; 119 noQuotes = str: v: { __pretty = const str; val = v; }; 120 modify = n: fn: v: if (n == 0) then fn v 121 else if isList v then map (modify (n - 1) fn) v 122 else if isAttrs v then mapAttrs 123 (const (modify (n - 1) fn)) v 124 else v; 125 in trace (generators.toPretty { allowPrettyValues = true; } 126 (modify depth snip x)) y; 127 128 /* A combination of `traceVal` and `traceSeq` that applies a 129 provided function to the value to be traced after `deepSeq`ing 130 it. 131 */ 132 traceValSeqFn = 133 # Function to apply 134 f: 135 # Value to trace 136 v: traceValFn f (builtins.deepSeq v v); 137 138 /* A combination of `traceVal` and `traceSeq`. */ 139 traceValSeq = traceValSeqFn id; 140 141 /* A combination of `traceVal` and `traceSeqN` that applies a 142 provided function to the value to be traced. */ 143 traceValSeqNFn = 144 # Function to apply 145 f: 146 depth: 147 # Value to trace 148 v: traceSeqN depth (f v) v; 149 150 /* A combination of `traceVal` and `traceSeqN`. */ 151 traceValSeqN = traceValSeqNFn id; 152 153 /* Trace the input and output of a function `f` named `name`, 154 both down to `depth`. 155 156 This is useful for adding around a function call, 157 to see the before/after of values as they are transformed. 158 159 Example: 160 traceFnSeqN 2 "id" (x: x) { a.b.c = 3; } 161 trace: { fn = "id"; from = { a.b = {}; }; to = { a.b = {}; }; } 162 => { a.b.c = 3; } 163 */ 164 traceFnSeqN = depth: name: f: v: 165 let res = f v; 166 in lib.traceSeqN 167 (depth + 1) 168 { 169 fn = name; 170 from = v; 171 to = res; 172 } 173 res; 174 175 176 # -- TESTING -- 177 178 /* Evaluates a set of tests. 179 180 A test is an attribute set `{expr, expected}`, 181 denoting an expression and its expected result. 182 183 The result is a `list` of __failed tests__, each represented as 184 `{name, expected, result}`, 185 186 - expected 187 - What was passed as `expected` 188 - result 189 - The actual `result` of the test 190 191 Used for regression testing of the functions in lib; see 192 tests.nix for more examples. 193 194 Important: Only attributes that start with `test` are executed. 195 196 - If you want to run only a subset of the tests add the attribute `tests = ["testName"];` 197 198 Example: 199 200 runTests { 201 testAndOk = { 202 expr = lib.and true false; 203 expected = false; 204 }; 205 testAndFail = { 206 expr = lib.and true false; 207 expected = true; 208 }; 209 } 210 -> 211 [ 212 { 213 name = "testAndFail"; 214 expected = true; 215 result = false; 216 } 217 ] 218 219 Type: 220 runTests :: { 221 tests = [ String ]; 222 ${testName} :: { 223 expr :: a; 224 expected :: a; 225 }; 226 } 227 -> 228 [ 229 { 230 name :: String; 231 expected :: a; 232 result :: a; 233 } 234 ] 235 */ 236 runTests = 237 # Tests to run 238 tests: concatLists (attrValues (mapAttrs (name: test: 239 let testsToRun = if tests ? tests then tests.tests else []; 240 in if (substring 0 4 name == "test" || elem name testsToRun) 241 && ((testsToRun == []) || elem name tests.tests) 242 && (test.expr != test.expected) 243 244 then [ { inherit name; expected = test.expected; result = test.expr; } ] 245 else [] ) tests)); 246 247 /* Create a test assuming that list elements are `true`. 248 249 Example: 250 { testX = allTrue [ true ]; } 251 */ 252 testAllTrue = expr: { inherit expr; expected = map (x: true) expr; }; 253}