diff options
Diffstat (limited to 'js/src/jit-test/lib/census.js')
-rw-r--r-- | js/src/jit-test/lib/census.js | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/js/src/jit-test/lib/census.js b/js/src/jit-test/lib/census.js new file mode 100644 index 000000000..20be2e12b --- /dev/null +++ b/js/src/jit-test/lib/census.js @@ -0,0 +1,161 @@ +// Functions for checking results returned by Debugger.Memory.prototype.takeCensus. + +const Census = {}; + +(function () { + + // Census.walkCensus(subject, name, walker[, ignore]) + // + // 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(ignore): Called after we have called 'enter' on every property of + // the subject. Passed the |ignore| set of properties. + // + // - 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. + // + // The optional |ignore| parameter allows you to specify a |Set| of property + // names which should be ignored. The walker will not traverse such + // properties. + Census.walkCensus = (subject, name, walker, ignore = new Set()) => + walk(subject, name, walker, ignore, 0); + + function walk(subject, name, walker, ignore, count) { + if (typeof subject === 'object') { + print(name); + for (let prop in subject) { + if (ignore.has(prop)) { + continue; + } + count = walk(subject[prop], + name + "[" + uneval(prop) + "]", + walker.enter(prop), + ignore, + count); + } + walker.done(ignore); + } else { + print(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 => assertEq(elt, 0) + }; + + function expectedObject() { + throw "Census mismatch: subject has leaf where basis has nested object"; + } + + function expectedLeaf() { + throw "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') { + var unvisited = new Set(Object.getOwnPropertyNames(basis)); + return { + enter: prop => { + unvisited.delete(prop); + if (prop in basis) { + return makeWalker(basis[prop]); + } else { + return extra(prop); + } + }, + + done: ignore => [...unvisited].filter(p => !ignore.has(p)).forEach(p => missing(p, basis[p])), + check: expectedObject + }; + } else { + return { + enter: expectedLeaf, + done: expectedLeaf, + check: elt => compare(elt, basis) + }; + } + }; + } + + function missingProp(prop) { + throw "Census mismatch: subject lacks property present in basis: " + uneval(prop); + } + + function extraProp(prop) { + throw "Census mismatch: subject has property not present in basis: " + uneval(prop); + } + + // Return a walker that checks that the subject census has counts all equal to + // |basis|. + Census.assertAllEqual = makeBasisChecker({ + compare: assertEq, + missing: missingProp, + extra: extraProp + }); + + // 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) => assertEq(subject >= basis, true), + 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) => assertEq(subject <= basis, true), + 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, basis) => assertEq(Math.abs(subject - basis) <= fudge, true), + missing: missingProp, + extra: () => Census.walkAnything + })(basis); + } + +})(); |