// Functions for checking results returned by // Debugger.Memory.prototype.takeCensus and // HeapSnapshot.prototype.takeCensus. Adapted from js/src/jit-test/lib/census.js. export const Census = {}; function dumpn(msg) { dump("DBG-TEST: Census.jsm: " + msg + "\n"); } // Census.walkCensus(subject, name, walker) // // Use |walker| to check |subject|, a census object of the sort returned by // Debugger.Memory.prototype.takeCensus: a tree of objects with integers at the // leaves. Use |name| as the name for |subject| in diagnostic messages. Return // the number of leaves of |subject| we visited. // // A walker is an object with three methods: // // - enter(prop): Return the walker we should use to check the property of the // subject census named |prop|. This is for recursing into the subobjects of // the subject. // // - done(): Called after we have called 'enter' on every property of the // subject. // // - check(value): Check |value|, a leaf in the subject. // // Walker methods are expected to simply throw if a node we visit doesn't look // right. Census.walkCensus = (subject, name, walker) => walk(subject, name, walker, 0); function walk(subject, name, walker, count) { if (typeof subject === "object") { dumpn(name); for (const prop in subject) { count = walk( subject[prop], name + "[" + uneval(prop) + "]", walker.enter(prop), count ); } walker.done(); } else { dumpn(name + " = " + uneval(subject)); walker.check(subject); count++; } return count; } // A walker that doesn't check anything. Census.walkAnything = { enter: () => Census.walkAnything, done: () => undefined, check: () => undefined, }; // A walker that requires all leaves to be zeros. Census.assertAllZeros = { enter: () => Census.assertAllZeros, done: () => undefined, check: elt => { if (elt !== 0) { throw new Error("Census mismatch: expected zero, found " + elt); } }, }; function expectedObject() { throw new Error( "Census mismatch: subject has leaf where basis has nested object" ); } function expectedLeaf() { throw new Error( "Census mismatch: subject has nested object where basis has leaf" ); } // Return a function that, given a 'basis' census, returns a census walker that // compares the subject census against the basis. The returned walker calls the // given |compare|, |missing|, and |extra| functions as follows: // // - compare(subjectLeaf, basisLeaf): Check a leaf of the subject against the // corresponding leaf of the basis. // // - missing(prop, value): Called when the subject is missing a property named // |prop| which is present in the basis with value |value|. // // - extra(prop): Called when the subject has a property named |prop|, but the // basis has no such property. This should return a walker that can check // the subject's value. function makeBasisChecker({ compare, missing, extra }) { return function makeWalker(basis) { if (typeof basis === "object") { const unvisited = new Set(Object.getOwnPropertyNames(basis)); return { enter: prop => { unvisited.delete(prop); if (prop in basis) { return makeWalker(basis[prop]); } return extra(prop); }, done: () => unvisited.forEach(prop => missing(prop, basis[prop])), check: expectedObject, }; } return { enter: expectedLeaf, done: expectedLeaf, check: elt => compare(elt, basis), }; }; } function missingProp(prop) { throw new Error( "Census mismatch: subject lacks property present in basis: " + prop ); } function extraProp(prop) { throw new Error( "Census mismatch: subject has property not present in basis: " + prop ); } // Return a walker that checks that the subject census has counts all equal to // |basis|. Census.assertAllEqual = makeBasisChecker({ compare: (a, b) => { if (a !== b) { throw new Error("Census mismatch: expected " + a + " got " + b); } }, missing: missingProp, extra: extraProp, }); function ok(val) { if (!val) { throw new Error("Census mismatch: expected truthy, got " + val); } } // Return a walker that checks that the subject census has at least as many // items of each category as |basis|. Census.assertAllNotLessThan = makeBasisChecker({ compare: (subject, basis) => ok(subject >= basis), missing: missingProp, extra: () => Census.walkAnything, }); // Return a walker that checks that the subject census has at most as many // items of each category as |basis|. Census.assertAllNotMoreThan = makeBasisChecker({ compare: (subject, basis) => ok(subject <= basis), missing: missingProp, extra: () => Census.walkAnything, }); // Return a walker that checks that the subject census has within |fudge| // items of each category of the count in |basis|. Census.assertAllWithin = function(fudge, basis) { return makeBasisChecker({ compare: (subject, base) => ok(Math.abs(subject - base) <= fudge), missing: missingProp, extra: () => Census.walkAnything, })(basis); };