this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
  const { gStore, document } = panel.panelWin;

  const { dispatch } = panel.panelWin.gStore;

  function $$(selector) {
    return [...document.querySelectorAll(selector)];
  }
  dispatch(changeView(viewState.CENSUS));

  yield takeSnapshot(panel.panelWin);

  yield waitUntilState(gStore, state =>
    state.snapshots[0].census &&
    state.snapshots[0].census.state === censusState.SAVED);

  info("Check coarse type heap view");

  ["Function", "js::Shape", "Object", "strings"].forEach(findNameCell);

  yield setCensusDisplay(panel.panelWin, censusDisplays.allocationStack);
  info("Check allocation stack heap view");
  [L10N.getStr("tree-item.nostack")].forEach(findNameCell);

  function findNameCell(name) {
    const el = $$(".tree .heap-tree-item-name")
      .find(e => e.textContent === name);
    ok(el, `Found heap tree item cell for ${name}.`);
  }
});
add_task(function* () {
  let front = new StubbedMemoryFront();
  let heapWorker = new HeapAnalysesClient();
  yield front.attach();
  let store = Store();
  let { getState, dispatch } = store;

  dispatch(changeView(viewState.CENSUS));

  // Test default display with no snapshots
  equal(getState().censusDisplay.breakdown.by, "coarseType",
        "default coarseType display selected at start.");

  dispatch(setCensusDisplay(censusDisplays.allocationStack));
  equal(getState().censusDisplay.breakdown.by, "allocationStack",
        "display changed with no snapshots");

  // Test invalid displays
  try {
    dispatch(setCensusDisplay({}));
    ok(false, "Throws when passing in an invalid display object");
  } catch (e) {
    ok(true, "Throws when passing in an invalid display object");
  }
  equal(getState().censusDisplay.breakdown.by, "allocationStack",
    "current display unchanged when passing invalid display");

  // Test new snapshots
  dispatch(takeSnapshotAndCensus(front, heapWorker));
  yield waitUntilCensusState(store, s => s.census, [censusState.SAVED]);
  equal(getState().snapshots[0].census.display, censusDisplays.allocationStack,
        "New snapshots use the current, non-default display");
});
add_task(function* () {
  let front = new StubbedMemoryFront();
  let heapWorker = new HeapAnalysesClient();
  yield front.attach();
  let store = Store();
  let { getState, dispatch } = store;

  dispatch(changeView(viewState.CENSUS));

  // Select a non-inverted display.
  dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.allocationStack));
  equal(getState().censusDisplay.inverted, false, "not inverted by default");

  dispatch(takeSnapshotAndCensus(front, heapWorker));
  dispatch(takeSnapshotAndCensus(front, heapWorker));
  dispatch(takeSnapshotAndCensus(front, heapWorker));

  yield waitUntilCensusState(store, s => s.census, [censusState.SAVED,
                                                    censusState.SAVED,
                                                    censusState.SAVED]);
  ok(true, "saved 3 snapshots and took a census of each of them");

  // Select an inverted display.
  dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.invertedAllocationStack));

  yield waitUntilCensusState(store, s => s.census, [censusState.SAVED,
                                                    censusState.SAVED,
                                                    censusState.SAVING]);
  ok(true, "toggling inverted should recompute the selected snapshot's census");

  equal(getState().censusDisplay.inverted, true, "now inverted");

  yield waitUntilCensusState(store, s => s.census, [censusState.SAVED,
                                                    censusState.SAVED,
                                                    censusState.SAVED]);

  equal(getState().snapshots[0].census.display.inverted, false);
  equal(getState().snapshots[1].census.display.inverted, false);
  equal(getState().snapshots[2].census.display.inverted, true);

  dispatch(selectSnapshotAndRefresh(heapWorker, getState().snapshots[1].id));
  yield waitUntilCensusState(store, s => s.census, [censusState.SAVED,
                                                    censusState.SAVING,
                                                    censusState.SAVED]);
  ok(true, "selecting non-inverted census should trigger a recompute");

  yield waitUntilCensusState(store, s => s.census, [censusState.SAVED,
                                                    censusState.SAVED,
                                                    censusState.SAVED]);

  equal(getState().snapshots[0].census.display.inverted, false);
  equal(getState().snapshots[1].census.display.inverted, true);
  equal(getState().snapshots[2].census.display.inverted, true);

  heapWorker.destroy();
  yield front.detach();
});
Пример #4
0
add_task(async function() {
  const front = new StubbedMemoryFront();
  const heapWorker = new HeapAnalysesClient();
  await front.attach();
  const store = Store();
  const { getState, dispatch } = store;

  dispatch(changeView(viewState.DOMINATOR_TREE));

  dispatch(takeSnapshotAndCensus(front, heapWorker));
  await waitUntilCensusState(store, s => s.treeMap, [treeMapState.SAVED]);
  ok(!getState().snapshots[0].dominatorTree,
     "There shouldn't be a dominator tree model yet since it is not computed " +
     "until we switch to the dominators view.");

  // Wait for the dominator tree to finish being fetched.
  await waitUntilState(store, state =>
    state.snapshots[0] &&
    state.snapshots[0].dominatorTree &&
    state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);

  ok(getState().labelDisplay,
     "We have a default display for describing nodes in a dominator tree");
  equal(getState().labelDisplay,
        labelDisplays.coarseType,
        "and the default is coarse type");
  equal(getState().labelDisplay,
        getState().snapshots[0].dominatorTree.display,
        "and the newly computed dominator tree has that display");

  // Switch to the allocationStack display.
  dispatch(setLabelDisplayAndRefresh(
    heapWorker,
    labelDisplays.allocationStack));

  await waitUntilState(store, state =>
    state.snapshots[0].dominatorTree.state === dominatorTreeState.FETCHING);
  ok(true,
     "switching display types caused the dominator tree to be fetched " +
     "again.");

  await waitUntilState(store, state =>
    state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
  equal(getState().snapshots[0].dominatorTree.display,
        labelDisplays.allocationStack,
        "The new dominator tree's display is allocationStack");
  equal(getState().labelDisplay,
        labelDisplays.allocationStack,
        "as is our requested dominator tree display");

  heapWorker.destroy();
  await front.detach();
});
Пример #5
0
add_task(async function() {
  let front = new StubbedMemoryFront();
  let heapWorker = new HeapAnalysesClient();
  await front.attach();
  let store = Store();
  const { getState, dispatch } = store;

  equal(getState().individuals, null,
        "no individuals state by default");

  dispatch(changeView(viewState.CENSUS));
  dispatch(takeSnapshotAndCensus(front, heapWorker));
  await waitUntilCensusState(store, s => s.census, [censusState.SAVED]);

  const root = getState().snapshots[0].census.report;
  ok(root, "Should have a census");

  const reportLeafIndex = findReportLeafIndex(root);
  ok(reportLeafIndex, "Should get a reportLeafIndex");

  const snapshotId = getState().snapshots[0].id;
  ok(snapshotId, "Should have a snapshot id");

  const breakdown = getState().snapshots[0].census.display.breakdown;
  ok(breakdown, "Should have a breakdown");

  dispatch(fetchIndividuals(heapWorker, snapshotId, breakdown,
                            reportLeafIndex));

  // Wait for each expected state.
  for (let state of EXPECTED_INDIVIDUAL_STATES) {
    await waitUntilState(store, s => {
      return s.view.state === viewState.INDIVIDUALS &&
             s.individuals &&
             s.individuals.state === state;
    });
    ok(true, `Reached state = ${state}`);
  }

  ok(getState().individuals, "Should have individuals state");
  ok(getState().individuals.nodes, "Should have individuals nodes");
  ok(getState().individuals.nodes.length > 0,
     "Should have a positive number of nodes");

  heapWorker.destroy();
  await front.detach();
});
add_task(function* () {
  let front = new StubbedMemoryFront();
  let heapWorker = new HeapAnalysesClient();
  yield front.attach();
  let store = Store();
  let { getState, dispatch } = store;

  dispatch(changeView(viewState.CENSUS));

  yield dispatch(setCensusDisplayAndRefresh(heapWorker,
                                            censusDisplays.allocationStack));

  dispatch(takeSnapshotAndCensus(front, heapWorker));
  yield waitUntilCensusState(store, s => s.census, [censusState.SAVED]);

  ok(!getState().snapshots[0].census.display.inverted, "Snapshot is not inverted");

  let census = getState().snapshots[0].census;
  let result = aggregate(census.report);
  let totalBytes = result.bytes;
  let totalCount = result.count;

  ok(totalBytes > 0, "counted up bytes in the census");
  ok(totalCount > 0, "counted up count in the census");

  result = getSnapshotTotals(getState().snapshots[0].census);
  equal(totalBytes, result.bytes, "getSnapshotTotals reuslted in correct bytes");
  equal(totalCount, result.count, "getSnapshotTotals reuslted in correct count");

  dispatch(setCensusDisplayAndRefresh(heapWorker,
                                      censusDisplays.invertedAllocationStack));

  yield waitUntilCensusState(store, s => s.census, [censusState.SAVING]);
  yield waitUntilCensusState(store, s => s.census, [censusState.SAVED]);
  ok(getState().snapshots[0].census.display.inverted, "Snapshot is inverted");

  result = getSnapshotTotals(getState().snapshots[0].census);
  equal(totalBytes, result.bytes,
        "getSnapshotTotals reuslted in correct bytes when inverted");
  equal(totalCount, result.count,
        "getSnapshotTotals reuslted in correct count when inverted");
});
add_task(function* () {
  let front = new StubbedMemoryFront();
  let heapWorker = new HeapAnalysesClient();
  yield front.attach();
  let store = Store();
  let { getState, dispatch } = store;

  dispatch(changeView(viewState.CENSUS));

  dispatch(setCensusDisplay(censusDisplays.allocationStack));
  equal(getState().censusDisplay.inverted, false,
        "Should not have an inverted census display");

  dispatch(takeSnapshotAndCensus(front, heapWorker));
  yield waitUntilSnapshotState(store, [states.SAVING]);

  dispatch(setCensusDisplayAndRefresh(heapWorker,
                                      censusDisplays.invertedAllocationStack));

  yield waitUntilCensusState(store, s => s.census, [censusState.SAVED]);

  ok(getState().censusDisplay.inverted,
     "should want inverted trees");
  ok(getState().snapshots[0].census.display.inverted,
     "snapshot-we-were-in-the-middle-of-saving's census should be inverted");

  dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.allocationStack));
  yield waitUntilCensusState(store, s => s.census, [censusState.SAVING]);
  ok(true, "toggling inverted retriggers census");
  ok(!getState().censusDisplay.inverted, "no longer inverted");

  dispatch(setCensusDisplayAndRefresh(heapWorker,
                                      censusDisplays.invertedAllocationStack));
  yield waitUntilCensusState(store, s => s.census, [censusState.SAVED]);
  ok(getState().censusDisplay.inverted, "inverted again");
  ok(getState().snapshots[0].census.display.inverted,
     "census-we-were-in-the-middle-of-recomputing should be inverted again");

  heapWorker.destroy();
  yield front.detach();
});
Пример #8
0
add_task(async function() {
  let front = new StubbedMemoryFront();
  let heapWorker = new HeapAnalysesClient();
  await front.attach();
  let store = Store();
  let { getState, dispatch } = store;

  dispatch(changeView(viewState.DOMINATOR_TREE));
  equal(getState().view.state, viewState.DOMINATOR_TREE,
        "We should now be in the DOMINATOR_TREE view");

  dispatch(takeSnapshotAndCensus(front, heapWorker));

  // Wait for the dominator tree to start being computed.
  await waitUntilState(store, state =>
    state.snapshots[0] && state.snapshots[0].dominatorTree);
  equal(getState().snapshots[0].dominatorTree.state,
        dominatorTreeState.COMPUTING,
        "The dominator tree started computing");
  ok(!getState().snapshots[0].dominatorTree.root,
     "When the dominator tree is computing, we should not have its root");

  // Wait for the dominator tree to finish computing and start being fetched.
  await waitUntilState(store, state =>
    state.snapshots[0].dominatorTree.state === dominatorTreeState.FETCHING);
  ok(true, "The dominator tree started fetching");
  ok(!getState().snapshots[0].dominatorTree.root,
     "When the dominator tree is fetching, we should not have its root");

  // Wait for the dominator tree to finish being fetched.
  await waitUntilState(store, state =>
    state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
  ok(true, "The dominator tree was fetched");
  ok(getState().snapshots[0].dominatorTree.root,
     "When the dominator tree is loaded, we should have its root");

  heapWorker.destroy();
  await front.detach();
});
add_task(function *() {
  let front = new StubbedMemoryFront();
  let heapWorker = new HeapAnalysesClient();
  yield front.attach();
  let store = Store();

  store.dispatch(changeView(viewState.CENSUS));

  store.dispatch(actions.takeSnapshot(front));
  yield waitUntilState(store, () => {
    let snapshots = store.getState().snapshots;
    return snapshots.length === 1 && snapshots[0].state === states.SAVED;
  });

  let snapshot = store.getState().snapshots[0];
  equal(snapshot.census, null, "No census data exists yet on the snapshot.");

  // Test error case of wrong state.
  store.dispatch(actions.takeCensus(heapWorker, snapshot.id));
  yield waitUntilState(store, () => store.getState().errors.length === 1);

  dumpn("Found error: " + store.getState().errors[0]);
  ok(/Assertion failure/.test(store.getState().errors[0]),
    "Error thrown when taking a census of a snapshot that has not been read.");

  store.dispatch(actions.readSnapshot(heapWorker, snapshot.id));
  yield waitUntilState(store, () => store.getState().snapshots[0].state === states.READ);

  store.dispatch(actions.takeCensus(heapWorker, snapshot.id));
  yield waitUntilCensusState(store, s => s.census, [censusState.SAVING]);
  yield waitUntilCensusState(store, s => s.census, [censusState.SAVED]);

  snapshot = store.getState().snapshots[0];
  ok(snapshot.census, "Snapshot has census after saved census");
  ok(snapshot.census.report.children.length, "Census is in tree node form");
  equal(snapshot.census.display, censusDisplays.coarseType,
        "Snapshot stored correct display used for the census");

});
this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
  const heapWorker = panel.panelWin.gHeapAnalysesClient;
  const front = panel.panelWin.gFront;
  const { getState, dispatch } = panel.panelWin.gStore;
  const doc = panel.panelWin.document;

  dispatch(changeView(viewState.CENSUS));

  yield dispatch(takeSnapshotAndCensus(front, heapWorker));

  is(getState().allocations.recording, false);
  const recordingCheckbox = doc.getElementById("record-allocation-stacks-checkbox");
  EventUtils.synthesizeMouseAtCenter(recordingCheckbox, {}, panel.panelWin);
  is(getState().allocations.recording, true);

  const nameElems = [...doc.querySelectorAll(".heap-tree-item-field.heap-tree-item-name")];

  for (let el of nameElems) {
    dumpn(`Found ${el.textContent.trim()}`);
    is(el.style.marginInlineStart, "0px",
       "None of the elements should be an indented/expanded child");
  }
});
add_task(function* () {
  let front = new StubbedMemoryFront();
  let heapWorker = new HeapAnalysesClient();
  yield front.attach();
  let store = Store();
  let { getState, dispatch } = store;

  dispatch(changeView(viewState.CENSUS));

  dispatch(takeSnapshotAndCensus(front, heapWorker));
  yield waitUntilSnapshotState(store, [states.SAVING]);

  dispatch(setFilterString("str"));

  yield waitUntilCensusState(store, snapshot => snapshot.census,
                             [censusState.SAVED]);
  equal(getState().filter, "str",
        "should want filtered trees");
  equal(getState().snapshots[0].census.filter, "str",
        "snapshot-we-were-in-the-middle-of-saving's census should be filtered");

  dispatch(setFilterStringAndRefresh("", heapWorker));
  yield waitUntilCensusState(store, snapshot => snapshot.census,
                             [censusState.SAVING]);
  ok(true, "changing filter string retriggers census");
  ok(!getState().filter, "no longer filtering");

  dispatch(setFilterString("obj"));
  yield waitUntilCensusState(store, snapshot => snapshot.census,
                             [censusState.SAVED]);
  equal(getState().filter, "obj", "filtering for obj now");
  equal(getState().snapshots[0].census.filter, "obj",
        "census-we-were-in-the-middle-of-recomputing should be filtered again");

  heapWorker.destroy();
  yield front.detach();
});
Пример #12
0
add_task(function *() {
  let front = new StubbedMemoryFront();
  let heapWorker = new HeapAnalysesClient();
  yield front.attach();
  let store = Store();
  let { getState, dispatch } = store;

  dispatch(takeSnapshotAndCensus(front, heapWorker));
  dispatch(takeSnapshotAndCensus(front, heapWorker));
  yield waitUntilSnapshotState(store, [states.SAVED_CENSUS,
                                       states.SAVED_CENSUS]);

  ok(getState().snapshots[1].selected, "The second snapshot is selected");

  // Change to the dominator tree view.
  dispatch(changeView(viewState.DOMINATOR_TREE));

  // Wait for the dominator tree to finish being fetched.
  yield waitUntilState(store, state =>
    state.snapshots[1].dominatorTree &&
    state.snapshots[1].dominatorTree.state === dominatorTreeState.LOADED);
  ok(true, "The second snapshot's dominator tree was fetched");

  // Select the first snapshot.
  dispatch(selectSnapshotAndRefresh(heapWorker, getState().snapshots[0].id));

  // And now the first snapshot should have its dominator tree fetched and
  // computed because of the new selection.
  yield waitUntilState(store, state =>
    state.snapshots[0].dominatorTree &&
    state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
  ok(true, "The first snapshot's dominator tree was fetched");

  heapWorker.destroy();
  yield front.detach();
});
this.test = makeMemoryTest(TEST_URL, async function({ tab, panel }) {
  const heapWorker = panel.panelWin.gHeapAnalysesClient;
  const front = panel.panelWin.gFront;
  const { getState, dispatch } = panel.panelWin.gStore;
  const doc = panel.panelWin.document;

  dispatch(changeView(viewState.CENSUS));

  dispatch(censusDisplayActions.setCensusDisplay(censusDisplays.invertedAllocationStack));
  is(getState().censusDisplay.breakdown.by, "allocationStack");

  await dispatch(toggleRecordingAllocationStacks(front));
  ok(getState().allocations.recording);

  // Let some allocations build up.
  await waitForTime(500);

  await dispatch(takeSnapshotAndCensus(front, heapWorker));

  const names = [...doc.querySelectorAll(".frame-link-function-display-name")];
  ok(names.length, "Should have rendered some allocation stack tree items");
  ok(names.some(e => !!e.textContent.trim()),
     "And at least some of them should have functionDisplayNames");
});
add_task(function* () {
  let front = new StubbedMemoryFront();
  let heapWorker = new HeapAnalysesClient();
  yield front.attach();
  let store = Store();
  let { getState, dispatch } = store;

  dispatch(changeView(viewState.CENSUS));

  // Test default display with no snapshots
  equal(getState().censusDisplay.breakdown.by, "coarseType",
        "default coarseType display selected at start.");
  dispatch(setCensusDisplayAndRefresh(heapWorker,
                                      censusDisplays.allocationStack));
  equal(getState().censusDisplay.breakdown.by, "allocationStack",
        "display changed with no snapshots");

  // Test invalid displays
  ok(getState().errors.length === 0, "No error actions in the queue.");
  dispatch(setCensusDisplayAndRefresh(heapWorker, {}));
  yield waitUntilState(store, () => getState().errors.length === 1);
  ok(true, "Emits an error action when passing in an invalid display object");

  equal(getState().censusDisplay.breakdown.by, "allocationStack",
    "current display unchanged when passing invalid display");

  // Test new snapshots
  dispatch(takeSnapshotAndCensus(front, heapWorker));
  yield waitUntilCensusState(store, snapshot => snapshot.census,
                             [censusState.SAVED]);

  equal(getState().snapshots[0].census.display, censusDisplays.allocationStack,
        "New snapshot's census uses correct display");

  // Updates when changing display during `SAVING`
  dispatch(takeSnapshotAndCensus(front, heapWorker));
  yield waitUntilCensusState(store, snapshot => snapshot.census,
                             [censusState.SAVED, censusState.SAVING]);
  dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.coarseType));
  yield waitUntilCensusState(store, snapshot => snapshot.census,
                             [censusState.SAVED, censusState.SAVED]);
  equal(getState().snapshots[1].census.display, censusDisplays.coarseType,
        "Changing display while saving a snapshot results in a census using the new display");


  // Updates when changing display during `SAVING_CENSUS`
  dispatch(takeSnapshotAndCensus(front, heapWorker));
  yield waitUntilCensusState(store, snapshot => snapshot.census,
                             [censusState.SAVED,
                              censusState.SAVED,
                              censusState.SAVING]);
  dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.allocationStack));
  yield waitUntilCensusState(store, snapshot => snapshot.census,
                             [censusState.SAVED,
                              censusState.SAVED,
                              censusState.SAVED]);
  equal(getState().snapshots[2].census.display, censusDisplays.allocationStack,
        "Display can be changed while saving census, stores updated display in snapshot");

  // Updates census on currently selected snapshot when changing display
  ok(getState().snapshots[2].selected, "Third snapshot currently selected");
  dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.coarseType));
  yield waitUntilState(store, state => state.snapshots[2].census.state === censusState.SAVING);
  yield waitUntilState(store, state => state.snapshots[2].census.state === censusState.SAVED);
  equal(getState().snapshots[2].census.display, censusDisplays.coarseType,
        "Snapshot census updated when changing displays after already generating one census");

  dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.allocationStack));
  yield waitUntilState(store, state => state.snapshots[2].census.state === censusState.SAVED);
  equal(getState().snapshots[2].census.display, censusDisplays.allocationStack,
        "Snapshot census updated when changing displays after already generating one census");

  // Does not update unselected censuses.
  ok(!getState().snapshots[1].selected, "Second snapshot selected currently");
  equal(getState().snapshots[1].census.display, censusDisplays.coarseType,
        "Second snapshot using `coarseType` display still and not yet updated to correct display");

  // Updates to current display when switching to stale snapshot.
  dispatch(selectSnapshotAndRefresh(heapWorker, getState().snapshots[1].id));
  yield waitUntilCensusState(store, snapshot => snapshot.census,
                             [censusState.SAVED,
                              censusState.SAVING,
                              censusState.SAVED]);
  yield waitUntilCensusState(store, snapshot => snapshot.census,
                             [censusState.SAVED,
                              censusState.SAVED,
                              censusState.SAVED]);

  ok(getState().snapshots[1].selected, "Second snapshot selected currently");
  equal(getState().snapshots[1].census.display, censusDisplays.allocationStack,
        "Second snapshot using `allocationStack` display and updated to correct display");

  heapWorker.destroy();
  yield front.detach();
});
this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
  // Taking snapshots and computing dominator trees is slow :-/
  requestLongerTimeout(4);

  const store = panel.panelWin.gStore;
  const { getState, dispatch } = store;
  const doc = panel.panelWin.document;

  dispatch(changeView(viewState.DOMINATOR_TREE));

  // Take a snapshot.

  const takeSnapshotButton = doc.getElementById("take-snapshot");
  EventUtils.synthesizeMouseAtCenter(takeSnapshotButton, {}, panel.panelWin);

  // Wait for the dominator tree to be computed and fetched.

  yield waitUntilDominatorTreeState(store, [dominatorTreeState.LOADED]);
  ok(true, "Computed and fetched the dominator tree.");

  // Expand all the dominator tree nodes that are eagerly fetched, except for
  // the leaves which will trigger fetching their lazily loaded subtrees.

  const id = getState().snapshots[0].id;
  const root = getState().snapshots[0].dominatorTree.root;
  (function expandAllEagerlyFetched(node = root) {
    if (!node.moreChildrenAvailable || node.children) {
      dispatch(expandDominatorTreeNode(id, node));
    }

    if (node.children) {
      for (let child of node.children) {
        expandAllEagerlyFetched(child);
      }
    }
  }());

  // Find the deepest eagerly loaded node: one which has more children but none
  // of them are loaded.

  const deepest = (function findDeepest(node = root) {
    if (node.moreChildrenAvailable && !node.children) {
      return node;
    }

    if (node.children) {
      for (let child of node.children) {
        const found = findDeepest(child);
        if (found) {
          return found;
        }
      }
    }

    return null;
  }());

  ok(deepest, "Found the deepest node");
  ok(!getState().snapshots[0].dominatorTree.expanded.has(deepest.nodeId),
     "The deepest node should not be expanded");

  // Select the deepest node.

  EventUtils.synthesizeMouseAtCenter(doc.querySelector(`.node-${deepest.nodeId}`),
                                     {},
                                     panel.panelWin);
  yield waitUntilState(store, state =>
    state.snapshots[0].dominatorTree.focused.nodeId === deepest.nodeId);
  ok(doc.querySelector(`.node-${deepest.nodeId}`).classList.contains("focused"),
     "The deepest node should be focused now");

  // Expand the deepest node, which triggers an incremental fetch of its lazily
  // loaded subtree.

  EventUtils.synthesizeKey("VK_RIGHT", {}, panel.panelWin);
  yield waitUntilState(store, state =>
    state.snapshots[0].dominatorTree.expanded.has(deepest.nodeId));
  is(getState().snapshots[0].dominatorTree.state,
     dominatorTreeState.INCREMENTAL_FETCHING,
     "Expanding the deepest node should start an incremental fetch of its subtree");
  ok(doc.querySelector(`.node-${deepest.nodeId}`).classList.contains("focused"),
     "The deepest node should still be focused after expansion");

  // Wait for the incremental fetch to complete.

  yield waitUntilState(store, state =>
    state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
  ok(true, "And the incremental fetch completes.");
  ok(doc.querySelector(`.node-${deepest.nodeId}`).classList.contains("focused"),
     "The deepest node should still be focused after we have loaded its children");

  // Find the most up-to-date version of the node whose children we just
  // incrementally fetched.

  const newDeepest = (function findNewDeepest(node = getState().snapshots[0]
                                                               .dominatorTree
                                                               .root) {
    if (node.nodeId === deepest.nodeId) {
      return node;
    }

    if (node.children) {
      for (let child of node.children) {
        const found = findNewDeepest(child);
        if (found) {
          return found;
        }
      }
    }

    return null;
  }());

  ok(newDeepest, "We found the up-to-date version of deepest");
  ok(newDeepest.children, "And its children are loaded");
  ok(newDeepest.children.length, "And there are more than 0 children");

  const firstChild = newDeepest.children[0];
  ok(firstChild, "deepest should have a first child");
  ok(doc.querySelector(`.node-${firstChild.nodeId}`),
     "and the first child should exist in the dom");

  // Select the newly loaded first child by pressing the right arrow once more.

  EventUtils.synthesizeKey("VK_RIGHT", {}, panel.panelWin);
  yield waitUntilState(store, state =>
    state.snapshots[0].dominatorTree.focused === firstChild);
  ok(doc.querySelector(`.node-${firstChild.nodeId}`).classList.contains("focused"),
     "The first child should now be focused");
});
Пример #16
0
add_task(async function() {
  const front = new StubbedMemoryFront();
  const heapWorker = new HeapAnalysesClient();
  await front.attach();
  const store = Store();
  const { getState, dispatch } = store;

  dispatch(changeView(viewState.DOMINATOR_TREE));
  dispatch(takeSnapshotAndCensus(front, heapWorker));

  // Wait for the dominator tree to finish being fetched.
  await waitUntilState(store, state =>
    state.snapshots[0] &&
    state.snapshots[0].dominatorTree &&
    state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
  ok(getState().snapshots[0].dominatorTree.root,
     "The dominator tree was fetched");

  // Find a node that has more children.

  function findNode(node) {
    if (node.moreChildrenAvailable && !node.children) {
      return node;
    }

    if (node.children) {
      for (const child of node.children) {
        const found = findNode(child);
        if (found) {
          return found;
        }
      }
    }

    return null;
  }

  const oldRoot = getState().snapshots[0].dominatorTree.root;
  const oldNode = findNode(oldRoot);
  ok(oldNode, "Should have found a node with more children.");

  // Find another node that has more children.
  function findNodeRev(node) {
    if (node.moreChildrenAvailable && !node.children) {
      return node;
    }

    if (node.children) {
      for (const child of node.children.slice().reverse()) {
        const found = findNodeRev(child);
        if (found) {
          return found;
        }
      }
    }

    return null;
  }

  const oldNode2 = findNodeRev(oldRoot);
  ok(oldNode2, "Should have found another node with more children.");
  ok(oldNode !== oldNode2,
     "The second node should not be the same as the first one");

  // Fetch both subtrees concurrently.
  dispatch(fetchImmediatelyDominated(heapWorker, getState().snapshots[0].id,
                                     new DominatorTreeLazyChildren(oldNode.nodeId, 0)));
  dispatch(fetchImmediatelyDominated(heapWorker, getState().snapshots[0].id,
                                     new DominatorTreeLazyChildren(oldNode2.nodeId, 0)));

  equal(getState().snapshots[0].dominatorTree.state,
        dominatorTreeState.INCREMENTAL_FETCHING,
        "Fetching immediately dominated children should put us in the " +
        "INCREMENTAL_FETCHING state");

  await waitUntilState(store, state =>
    state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
  ok(true,
     "The dominator tree should go back to LOADED after the incremental " +
     "fetching is done.");

  const newRoot = getState().snapshots[0].dominatorTree.root;
  ok(oldRoot !== newRoot,
     "When we insert new nodes, we get a new tree");

  // Find the new node which has the children inserted.

  function findNodeWithId(id, node) {
    if (node.nodeId === id) {
      return node;
    }

    if (node.children) {
      for (const child of node.children) {
        const found = findNodeWithId(id, child);
        if (found) {
          return found;
        }
      }
    }

    return null;
  }

  const newNode = findNodeWithId(oldNode.nodeId, newRoot);
  ok(newNode, "Should find the node in the new tree again");
  ok(newNode !== oldNode,
     "We did not mutate the old node in place, instead created a new node");
  ok(newNode.children.length,
     "And the new node should have the new children attached");

  const newNode2 = findNodeWithId(oldNode2.nodeId, newRoot);
  ok(newNode2, "Should find the second node in the new tree again");
  ok(newNode2 !== oldNode2,
     "We did not mutate the second old node in place, instead created a new node");
  ok(newNode2.children,
     "And the new node should have the new children attached");

  heapWorker.destroy();
  await front.detach();
});
Пример #17
0
add_task(async function() {
  let front = new StubbedMemoryFront();
  let heapWorker = new HeapAnalysesClient();
  await front.attach();
  let store = Store();
  const { getState, dispatch } = store;

  dispatch(changeView(viewState.CENSUS));

  // Take two snapshots and diff them from each other.

  dispatch(takeSnapshotAndCensus(front, heapWorker));
  dispatch(takeSnapshotAndCensus(front, heapWorker));
  await waitUntilCensusState(store, s => s.census,
                             [censusState.SAVED, censusState.SAVED]);

  dispatch(changeView(viewState.DIFFING));
  dispatch(selectSnapshotForDiffingAndRefresh(heapWorker, getState().snapshots[0]));
  dispatch(selectSnapshotForDiffingAndRefresh(heapWorker, getState().snapshots[1]));

  await waitUntilState(store, state => {
    return state.diffing &&
           state.diffing.state === diffingState.TOOK_DIFF;
  });
  ok(getState().diffing.census);

  // Fetch individuals.

  const root = getState().diffing.census.report;
  ok(root, "Should have a census");

  const reportLeafIndex = findReportLeafIndex(root);
  ok(reportLeafIndex, "Should get a reportLeafIndex");

  const snapshotId = getState().diffing.secondSnapshotId;
  ok(snapshotId, "Should have a snapshot id");

  const breakdown = getState().censusDisplay.breakdown;
  ok(breakdown, "Should have a breakdown");

  dispatch(fetchIndividuals(heapWorker, snapshotId, breakdown,
                            reportLeafIndex));

  for (let state of EXPECTED_INDIVIDUAL_STATES) {
    await waitUntilState(store, s => {
      return s.view.state === viewState.INDIVIDUALS &&
             s.individuals &&
             s.individuals.state === state;
    });
    ok(true, `Reached state = ${state}`);
  }

  ok(getState().individuals, "Should have individuals state");
  ok(getState().individuals.nodes, "Should have individuals nodes");
  ok(getState().individuals.nodes.length > 0,
     "Should have a positive number of nodes");

  // Pop the view back to the diffing.

  dispatch(popViewAndRefresh(heapWorker));

  await waitUntilState(store, state => {
    return state.diffing &&
      state.diffing.state === diffingState.TOOK_DIFF;
  });

  ok(getState().diffing.census.report,
     "We have our census diff again after popping back to the last view");

  heapWorker.destroy();
  await front.detach();
});
Пример #18
0
this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
  const heapWorker = panel.panelWin.gHeapAnalysesClient;
  const front = panel.panelWin.gFront;
  const store = panel.panelWin.gStore;
  const { getState, dispatch } = store;
  const doc = panel.panelWin.document;

  dispatch(changeView(viewState.CENSUS));

  is(getState().censusDisplay.breakdown.by, "coarseType");

  yield dispatch(takeSnapshotAndCensus(front, heapWorker));
  let census = getState().snapshots[0].census;
  let root1 = census.report.children[0];
  let root2 = census.report.children[0];
  let root3 = census.report.children[0];
  let root4 = census.report.children[0];
  let child1 = root1.children[0];

  info("Click on first node.");
  let firstNode = doc.querySelector(".tree .heap-tree-item-name");
  EventUtils.synthesizeMouseAtCenter(firstNode, {}, panel.panelWin);
  yield waitUntilFocused(store, root1);
  ok(true, "First root is selected after click.");

  info("Press DOWN key, expect second root focused.");
  EventUtils.synthesizeKey("VK_DOWN", {}, panel.panelWin);
  yield waitUntilFocused(store, root2);
  ok(true, "Second root is selected after pressing DOWN arrow.");

  info("Press DOWN key, expect third root focused.");
  EventUtils.synthesizeKey("VK_DOWN", {}, panel.panelWin);
  yield waitUntilFocused(store, root3);
  ok(true, "Third root is selected after pressing DOWN arrow.");

  info("Press DOWN key, expect fourth root focused.");
  EventUtils.synthesizeKey("VK_DOWN", {}, panel.panelWin);
  yield waitUntilFocused(store, root4);
  ok(true, "Fourth root is selected after pressing DOWN arrow.");

  info("Press UP key, expect third root focused.");
  EventUtils.synthesizeKey("VK_UP", {}, panel.panelWin);
  yield waitUntilFocused(store, root3);
  ok(true, "Third root is selected after pressing UP arrow.");

  info("Press UP key, expect second root focused.");
  EventUtils.synthesizeKey("VK_UP", {}, panel.panelWin);
  yield waitUntilFocused(store, root2);
  ok(true, "Second root is selected after pressing UP arrow.");

  info("Press UP key, expect first root focused.");
  EventUtils.synthesizeKey("VK_UP", {}, panel.panelWin);
  yield waitUntilFocused(store, root1);
  ok(true, "First root is selected after pressing UP arrow.");

  info("Press RIGHT key");
  EventUtils.synthesizeKey("VK_RIGHT", {}, panel.panelWin);
  yield waitUntilExpanded(store, root1);
  ok(true, "Root node is expanded.");

  info("Press RIGHT key, expect first child focused.");
  EventUtils.synthesizeKey("VK_RIGHT", {}, panel.panelWin);
  yield waitUntilFocused(store, child1);
  ok(true, "First child is selected after pressing RIGHT arrow.");

  info("Press LEFT key, expect first root focused.");
  EventUtils.synthesizeKey("VK_LEFT", {}, panel.panelWin);
  yield waitUntilFocused(store, root1);
  ok(true, "First root is selected after pressing LEFT arrow.");
});
Пример #19
0
add_task(function *() {
  let front = new StubbedMemoryFront();
  let heapWorker = new HeapAnalysesClient();
  yield front.attach();
  let store = Store();
  const { getState, dispatch } = store;
  dispatch(changeView(viewState.CENSUS));

  yield dispatch(setCensusDisplayAndRefresh(heapWorker,
                                        censusDisplays.allocationStack));
  equal(getState().censusDisplay.inverted, false,
        "not inverted at start");

  equal(getState().diffing, null, "not diffing by default");

  const s1 = yield dispatch(takeSnapshot(front, heapWorker));
  const s2 = yield dispatch(takeSnapshot(front, heapWorker));
  const s3 = yield dispatch(takeSnapshot(front, heapWorker));
  dispatch(readSnapshot(heapWorker, s1));
  dispatch(readSnapshot(heapWorker, s2));
  dispatch(readSnapshot(heapWorker, s3));
  yield waitUntilSnapshotState(store, [snapshotState.READ,
                                       snapshotState.READ,
                                       snapshotState.READ]);

  yield dispatch(toggleDiffing());
  dispatch(selectSnapshotForDiffingAndRefresh(heapWorker,
                                              getState().snapshots[0]));
  dispatch(selectSnapshotForDiffingAndRefresh(heapWorker,
                                              getState().snapshots[1]));
  yield waitUntilState(store,
                       state => state.diffing.state === diffingState.TOOK_DIFF);

  const shouldTriggerRecompute = [
    {
      name: "toggling inversion",
      func: () => dispatch(setCensusDisplayAndRefresh(
        heapWorker,
        censusDisplays.invertedAllocationStack))
    },
    {
      name: "filtering",
      func: () => dispatch(setFilterStringAndRefresh("scr", heapWorker))
    },
    {
      name: "changing displays",
      func: () =>
        dispatch(setCensusDisplayAndRefresh(heapWorker,
                                            censusDisplays.coarseType))
    }
  ];

  for (let { name, func } of shouldTriggerRecompute) {
    dumpn(`Testing that "${name}" triggers a diff recompute`);
    func();

    yield waitUntilState(store,
                         state =>
                           state.diffing.state === diffingState.TAKING_DIFF);
    ok(true, "triggered diff recompute.");

    yield waitUntilState(store,
                         state =>
                           state.diffing.state === diffingState.TOOK_DIFF);
    ok(true, "And then the diff should complete.");
    ok(getState().diffing.census, "And we should have a census.");
    ok(getState().diffing.census.report,
       "And that census should have a report.");
    equal(getState().diffing.census.display,
          getState().censusDisplay,
          "And that census should have the correct display");
    equal(getState().diffing.census.filter, getState().filter,
          "And that census should have the correct filter");
    equal(getState().diffing.census.display.inverted,
          getState().censusDisplay.inverted,
          "And that census should have the correct inversion");
  }

  heapWorker.destroy();
  yield front.detach();
});
Пример #20
0
add_task(async function() {
  const front = new StubbedMemoryFront();
  const heapWorker = new HeapAnalysesClient();
  await front.attach();
  const store = Store();
  const { getState, dispatch } = store;

  dispatch(changeView(viewState.DOMINATOR_TREE));
  dispatch(takeSnapshotAndCensus(front, heapWorker));

  // Wait for the dominator tree to finish being fetched.
  await waitUntilState(store, state =>
    state.snapshots[0] &&
    state.snapshots[0].dominatorTree &&
    state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
  ok(getState().snapshots[0].dominatorTree.root,
     "The dominator tree was fetched");

  // Find a node that has children, but none of them are loaded.

  function findNode(node) {
    if (node.moreChildrenAvailable && !node.children) {
      return node;
    }

    if (node.children) {
      for (const child of node.children) {
        const found = findNode(child);
        if (found) {
          return found;
        }
      }
    }

    return null;
  }

  const oldRoot = getState().snapshots[0].dominatorTree.root;
  const oldNode = findNode(oldRoot);
  ok(oldNode,
     "Should have found a node with children that are not loaded since we " +
     "only send partial dominator trees across initially and load the rest " +
     "on demand");
  ok(oldNode !== oldRoot, "But the node should not be the root");

  const lazyChildren = new DominatorTreeLazyChildren(oldNode.nodeId, 0);
  dispatch(fetchImmediatelyDominated(heapWorker, getState().snapshots[0].id,
                                     lazyChildren));

  equal(getState().snapshots[0].dominatorTree.state,
        dominatorTreeState.INCREMENTAL_FETCHING,
        "Fetching immediately dominated children should put us in the " +
        "INCREMENTAL_FETCHING state");

  await waitUntilState(store, state =>
    state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
  ok(true,
     "The dominator tree should go back to LOADED after the incremental " +
     "fetching is done.");

  const newRoot = getState().snapshots[0].dominatorTree.root;
  ok(oldRoot !== newRoot,
     "When we insert new nodes, we get a new tree");
  equal(oldRoot.children.length, newRoot.children.length,
        "The new tree's root should have the same number of children as the " +
        "old root's");

  let differentChildrenCount = 0;
  for (let i = 0; i < oldRoot.children.length; i++) {
    if (oldRoot.children[i] !== newRoot.children[i]) {
      differentChildrenCount++;
    }
  }
  equal(differentChildrenCount, 1,
        "All subtrees except the subtree we inserted incrementally fetched " +
        "children into should be the same because we use persistent updates");

  // Find the new node which has the children inserted.

  function findNewNode(node) {
    if (node.nodeId === oldNode.nodeId) {
      return node;
    }

    if (node.children) {
      for (const child of node.children) {
        const found = findNewNode(child);
        if (found) {
          return found;
        }
      }
    }

    return null;
  }

  const newNode = findNewNode(newRoot);
  ok(newNode, "Should find the node in the new tree again");
  ok(newNode !== oldNode,
    "We did not mutate the old node in place, instead created a new node");
  ok(newNode.children, "And the new node should have the children attached");

  heapWorker.destroy();
  await front.detach();
});
Пример #21
0
add_task(async function() {
  const front = new StubbedMemoryFront();
  const heapWorker = new HeapAnalysesClient();
  await front.attach();
  const store = Store();
  const { getState, dispatch } = store;

  dispatch(changeView(viewState.CENSUS));
  equal(getState().diffing, null, "not diffing by default");

  dispatch(takeSnapshot(front, heapWorker));
  dispatch(takeSnapshot(front, heapWorker));
  dispatch(takeSnapshot(front, heapWorker));

  await waitUntilSnapshotState(store,
        [snapshotState.SAVED, snapshotState.SAVED, snapshotState.SAVED]);
  dispatch(takeSnapshot(front));

  // Start diffing.
  dispatch(toggleDiffing());
  ok(getState().diffing, "now diffing after toggling");
  equal(getState().diffing.firstSnapshotId, null,
        "no first snapshot selected");
  equal(getState().diffing.secondSnapshotId, null,
        "no second snapshot selected");
  equal(getState().diffing.state, diffingState.SELECTING,
        "should be in diffing state SELECTING");

  // Can't select a snapshot that is not in a diffable state.
  equal(getState().snapshots[3].state, snapshotState.SAVING,
        "the last snapshot is still in the process of being saved");
  dumpn("Expecting exception:");
  let threw = false;
  try {
    dispatch(selectSnapshotForDiffing(getState().snapshots[3]));
  } catch (error) {
    threw = true;
  }
  ok(threw, "Should not be able to select snapshots that aren't ready for diffing");

  // Select first snapshot for diffing.
  dispatch(selectSnapshotForDiffing(getState().snapshots[0]));
  ok(getState().diffing, "now diffing after toggling");
  equal(getState().diffing.firstSnapshotId, getState().snapshots[0].id,
        "first snapshot selected");
  equal(getState().diffing.secondSnapshotId, null,
        "no second snapshot selected");
  equal(getState().diffing.state, diffingState.SELECTING,
        "should still be in diffing state SELECTING");

  // Can't diff first snapshot with itself; this is a noop.
  dispatch(selectSnapshotForDiffing(getState().snapshots[0]));
  ok(getState().diffing, "still diffing");
  equal(getState().diffing.firstSnapshotId, getState().snapshots[0].id,
        "first snapshot still selected");
  equal(getState().diffing.secondSnapshotId, null,
        "still no second snapshot selected");
  equal(getState().diffing.state, diffingState.SELECTING,
        "should still be in diffing state SELECTING");

  // Select second snapshot for diffing.
  dispatch(selectSnapshotForDiffing(getState().snapshots[1]));
  ok(getState().diffing, "still diffing");
  equal(getState().diffing.firstSnapshotId, getState().snapshots[0].id,
        "first snapshot still selected");
  equal(getState().diffing.secondSnapshotId, getState().snapshots[1].id,
        "second snapshot selected");

  // Can't select more than two snapshots for diffing.
  dumpn("Expecting exception:");
  threw = false;
  try {
    dispatch(selectSnapshotForDiffing(getState().snapshots[2]));
  } catch (error) {
    threw = true;
  }
  ok(threw, "Can't select more than two snapshots for diffing");

  heapWorker.destroy();
  await front.detach();
});
Пример #22
0
this.test = makeMemoryTest(TEST_URL, async function({ tab, panel }) {
  const heapWorker = panel.panelWin.gHeapAnalysesClient;
  const store = panel.panelWin.gStore;
  const { dispatch } = store;
  const doc = panel.panelWin.document;

  dispatch(changeView(viewState.CENSUS));

  const takeSnapshotButton = doc.getElementById("take-snapshot");
  EventUtils.synthesizeMouseAtCenter(takeSnapshotButton, {}, panel.panelWin);

  await waitUntilState(store, state =>
    state.snapshots.length === 1 &&
    state.snapshots[0].census &&
    state.snapshots[0].census.state === censusState.SAVING);

  let filterInput = doc.getElementById("filter");
  EventUtils.synthesizeMouseAtCenter(filterInput, {}, panel.panelWin);
  EventUtils.sendString("js::Shape", panel.panelWin);

  await waitUntilState(store, state =>
    state.snapshots.length === 1 &&
    state.snapshots[0].census &&
    state.snapshots[0].census.state === censusState.SAVING);
  ok(true, "adding a filter string should trigger census recompute");

  await waitUntilState(store, state =>
    state.snapshots.length === 1 &&
    state.snapshots[0].census &&
    state.snapshots[0].census.state === censusState.SAVED);

  let nameElem = doc.querySelector(".heap-tree-item-field.heap-tree-item-name");
  ok(nameElem, "Should get a tree item row with a name");
  is(nameElem.textContent.trim(), "js::Shape",
    "the tree item should be the one we filtered for");
  is(filterInput.value, "js::Shape",
    "and filter input contains the user value");

  // Now switch the dominator view, then switch back to census view
  // and check that the filter word is still correctly applied
  dispatch(changeViewAndRefresh(viewState.DOMINATOR_TREE, heapWorker));
  ok(true, "change view to dominator tree");

  // Wait for the dominator tree to be computed and fetched.
  await waitUntilDominatorTreeState(store, [dominatorTreeState.LOADED]);
  ok(true, "computed and fetched the dominator tree.");

  dispatch(changeViewAndRefresh(viewState.CENSUS, heapWorker));
  ok(true, "change view back to census");

  await waitUntilState(store, state =>
    state.snapshots.length === 1 &&
    state.snapshots[0].census &&
    state.snapshots[0].census.state === censusState.SAVED);

  nameElem = doc.querySelector(".heap-tree-item-field.heap-tree-item-name");
  filterInput = doc.getElementById("filter");

  ok(nameElem, "Should still get a tree item row with a name");
  is(nameElem.textContent.trim(), "js::Shape",
    "the tree item should still be the one we filtered for");
  is(filterInput.value, "js::Shape",
    "and filter input still contains the user value");
});