summaryrefslogtreecommitdiffstats
path: root/devtools/shared/heapsnapshot/tests/xpcshell/Census.sys.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/shared/heapsnapshot/tests/xpcshell/Census.sys.mjs')
-rw-r--r--devtools/shared/heapsnapshot/tests/xpcshell/Census.sys.mjs176
1 files changed, 176 insertions, 0 deletions
diff --git a/devtools/shared/heapsnapshot/tests/xpcshell/Census.sys.mjs b/devtools/shared/heapsnapshot/tests/xpcshell/Census.sys.mjs
new file mode 100644
index 0000000000..0e86f8b055
--- /dev/null
+++ b/devtools/shared/heapsnapshot/tests/xpcshell/Census.sys.mjs
@@ -0,0 +1,176 @@
+// 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);
+};