summaryrefslogtreecommitdiffstats
path: root/devtools/shared/heapsnapshot/tests/unit/Census.jsm
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/shared/heapsnapshot/tests/unit/Census.jsm')
-rw-r--r--devtools/shared/heapsnapshot/tests/unit/Census.jsm165
1 files changed, 165 insertions, 0 deletions
diff --git a/devtools/shared/heapsnapshot/tests/unit/Census.jsm b/devtools/shared/heapsnapshot/tests/unit/Census.jsm
new file mode 100644
index 000000000..f8fb1ce44
--- /dev/null
+++ b/devtools/shared/heapsnapshot/tests/unit/Census.jsm
@@ -0,0 +1,165 @@
+// Functions for checking results returned by
+// Debugger.Memory.prototype.takeCensus and
+// HeapSnapshot.prototype.takeCensus. Adapted from js/src/jit-test/lib/census.js.
+
+this.EXPORTED_SYMBOLS = ["Census"];
+
+this.Census = (function () {
+ 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 (let 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") {
+ 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: () => unvisited.forEach(prop => missing(prop, basis[prop])),
+ check: expectedObject
+ };
+ } else {
+ 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, basis) => ok(Math.abs(subject - basis) <= fudge),
+ missing: missingProp,
+ extra: () => Census.walkAnything
+ })(basis);
+ };
+
+ return Census;
+}());