tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

census.js (5403B)


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