Census.sys.mjs (5210B)
1 // Functions for checking results returned by 2 // Debugger.Memory.prototype.takeCensus and 3 // HeapSnapshot.prototype.takeCensus. Adapted from js/src/jit-test/lib/census.js. 4 5 export const Census = {}; 6 function dumpn(msg) { 7 dump("DBG-TEST: Census.sys.mjs: " + msg + "\n"); 8 } 9 10 // Census.walkCensus(subject, name, walker) 11 // 12 // Use |walker| to check |subject|, a census object of the sort returned by 13 // Debugger.Memory.prototype.takeCensus: a tree of objects with integers at the 14 // leaves. Use |name| as the name for |subject| in diagnostic messages. Return 15 // the number of leaves of |subject| we visited. 16 // 17 // A walker is an object with three methods: 18 // 19 // - enter(prop): Return the walker we should use to check the property of the 20 // subject census named |prop|. This is for recursing into the subobjects of 21 // the subject. 22 // 23 // - done(): Called after we have called 'enter' on every property of the 24 // subject. 25 // 26 // - check(value): Check |value|, a leaf in the subject. 27 // 28 // Walker methods are expected to simply throw if a node we visit doesn't look 29 // right. 30 Census.walkCensus = (subject, name, walker) => walk(subject, name, walker, 0); 31 function walk(subject, name, walker, count) { 32 if (typeof subject === "object") { 33 dumpn(name); 34 for (const prop in subject) { 35 count = walk( 36 subject[prop], 37 name + "[" + uneval(prop) + "]", 38 walker.enter(prop), 39 count 40 ); 41 } 42 walker.done(); 43 } else { 44 dumpn(name + " = " + uneval(subject)); 45 walker.check(subject); 46 count++; 47 } 48 49 return count; 50 } 51 52 // A walker that doesn't check anything. 53 Census.walkAnything = { 54 enter: () => Census.walkAnything, 55 done: () => undefined, 56 check: () => undefined, 57 }; 58 59 // A walker that requires all leaves to be zeros. 60 Census.assertAllZeros = { 61 enter: () => Census.assertAllZeros, 62 done: () => undefined, 63 check: elt => { 64 if (elt !== 0) { 65 throw new Error("Census mismatch: expected zero, found " + elt); 66 } 67 }, 68 }; 69 70 function expectedObject() { 71 throw new Error( 72 "Census mismatch: subject has leaf where basis has nested object" 73 ); 74 } 75 76 function expectedLeaf() { 77 throw new Error( 78 "Census mismatch: subject has nested object where basis has leaf" 79 ); 80 } 81 82 // Return a function that, given a 'basis' census, returns a census walker that 83 // compares the subject census against the basis. The returned walker calls the 84 // given |compare|, |missing|, and |extra| functions as follows: 85 // 86 // - compare(subjectLeaf, basisLeaf): Check a leaf of the subject against the 87 // corresponding leaf of the basis. 88 // 89 // - missing(prop, value): Called when the subject is missing a property named 90 // |prop| which is present in the basis with value |value|. 91 // 92 // - extra(prop): Called when the subject has a property named |prop|, but the 93 // basis has no such property. This should return a walker that can check 94 // the subject's value. 95 function makeBasisChecker({ compare, missing, extra }) { 96 return function makeWalker(basis) { 97 if (typeof basis === "object") { 98 const unvisited = new Set(Object.getOwnPropertyNames(basis)); 99 return { 100 enter: prop => { 101 unvisited.delete(prop); 102 if (prop in basis) { 103 return makeWalker(basis[prop]); 104 } 105 106 return extra(prop); 107 }, 108 109 done: () => unvisited.forEach(prop => missing(prop, basis[prop])), 110 check: expectedObject, 111 }; 112 } 113 114 return { 115 enter: expectedLeaf, 116 done: expectedLeaf, 117 check: elt => compare(elt, basis), 118 }; 119 }; 120 } 121 122 function missingProp(prop) { 123 throw new Error( 124 "Census mismatch: subject lacks property present in basis: " + prop 125 ); 126 } 127 128 function extraProp(prop) { 129 throw new Error( 130 "Census mismatch: subject has property not present in basis: " + prop 131 ); 132 } 133 134 // Return a walker that checks that the subject census has counts all equal to 135 // |basis|. 136 Census.assertAllEqual = makeBasisChecker({ 137 compare: (a, b) => { 138 if (a !== b) { 139 throw new Error("Census mismatch: expected " + a + " got " + b); 140 } 141 }, 142 missing: missingProp, 143 extra: extraProp, 144 }); 145 146 function ok(val) { 147 if (!val) { 148 throw new Error("Census mismatch: expected truthy, got " + val); 149 } 150 } 151 /* eslint-disable mozilla/no-comparison-or-assignment-inside-ok */ 152 153 // Return a walker that checks that the subject census has at least as many 154 // items of each category as |basis|. 155 Census.assertAllNotLessThan = makeBasisChecker({ 156 compare: (subject, basis) => ok(subject >= basis), 157 missing: missingProp, 158 extra: () => Census.walkAnything, 159 }); 160 161 // Return a walker that checks that the subject census has at most as many 162 // items of each category as |basis|. 163 Census.assertAllNotMoreThan = makeBasisChecker({ 164 compare: (subject, basis) => ok(subject <= basis), 165 missing: missingProp, 166 extra: () => Census.walkAnything, 167 }); 168 169 // Return a walker that checks that the subject census has within |fudge| 170 // items of each category of the count in |basis|. 171 Census.assertAllWithin = function (fudge, basis) { 172 return makeBasisChecker({ 173 compare: (subject, base) => ok(Math.abs(subject - base) <= fudge), 174 missing: missingProp, 175 extra: () => Census.walkAnything, 176 })(basis); 177 };