/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */

// This test ensures that download history (filtered by transition) queries
// don't invalidate (and requery) too often.

function accumulateNotifications(result) {
  let notifications = [];
  let resultObserver = new Proxy(NavHistoryResultObserver, {
    get(target, name) {
      if (name == "check") {
        result.removeObserver(resultObserver, false);
        return expectedNotifications =>
          Assert.deepEqual(notifications, expectedNotifications);
      }
      // ignore a few uninteresting notifications.
      if (["QueryInterface", "containerStateChanged"].includes(name)) {
        return () => {};
      }
      return () => {
        notifications.push(name);
      };
    },
  });
  result.addObserver(resultObserver, false);
  return resultObserver;
}

add_task(async function test_downloadhistory_query_notifications() {
  const MAX_RESULTS = 5;
  let query = PlacesUtils.history.getNewQuery();
  query.setTransitions([PlacesUtils.history.TRANSITIONS.DOWNLOAD]);
  let options = PlacesUtils.history.getNewQueryOptions();
  options.sortingMode = Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_DESCENDING;
  options.maxResults = MAX_RESULTS;
  let result = PlacesUtils.history.executeQuery(query, options);
  let notifications = accumulateNotifications(result);
  let root = PlacesUtils.asContainer(result.root);
  root.containerOpen = true;
  // Add more maxResults downloads in order.
  let transitions = Object.values(PlacesUtils.history.TRANSITIONS);
  for (let transition of transitions) {
    let uri = "http://fx-search.com/" + transition;
    await PlacesTestUtils.addVisits({
      uri,
      transition,
      title: "test " + transition,
    });
    // For each visit also set apart:
    //  - a bookmark
    //  - an annotation
    //  - an icon
    await PlacesUtils.bookmarks.insert({
      url: uri,
      parentGuid: PlacesUtils.bookmarks.unfiledGuid,
    });
    await PlacesUtils.history.update({
      url: uri,
      annotations: new Map([["test/anno", "testValue"]]),
    });
    await PlacesTestUtils.addFavicons(new Map([[uri, SMALLPNG_DATA_URI.spec]]));
  }
  // Remove all the visits one by one.
  for (let transition of transitions) {
    let uri = Services.io.newURI("http://fx-search.com/" + transition);
    await PlacesUtils.history.remove(uri);
  }
  root.containerOpen = false;
  // We pretty much don't want to see invalidateContainer here, because that
  // means we requeried.
  // We also don't want to see changes caused by filtered-out transition types.
  notifications.check([
    "nodeHistoryDetailsChanged",
    "nodeInserted",
    "nodeTitleChanged",
    "nodeIconChanged",
    "nodeRemoved",
  ]);
});

add_task(async function test_downloadhistory_query_filtering() {
  const MAX_RESULTS = 3;
  let query = PlacesUtils.history.getNewQuery();
  query.setTransitions([PlacesUtils.history.TRANSITIONS.DOWNLOAD]);
  let options = PlacesUtils.history.getNewQueryOptions();
  options.sortingMode = Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_DESCENDING;
  options.maxResults = MAX_RESULTS;
  let result = PlacesUtils.history.executeQuery(query, options);
  let root = PlacesUtils.asContainer(result.root);
  root.containerOpen = true;
  Assert.equal(root.childCount, 0, "No visits found");
  // Add more than maxResults downloads.
  let uris = [];
  // Define a monotonic visit date to ensure results order stability.
  let visitDate = Date.now() * 1000;
  for (let i = 0; i < MAX_RESULTS + 1; ++i, visitDate += 1000) {
    let uri = `http://fx-search.com/download/${i}`;
    await PlacesTestUtils.addVisits({
      uri,
      transition: PlacesUtils.history.TRANSITIONS.DOWNLOAD,
      visitDate,
    });
    uris.push(uri);
  }
  // Add an older download visit out of the maxResults timeframe.
  await PlacesTestUtils.addVisits({
    uri: `http://fx-search.com/download/unordered`,
    transition: PlacesUtils.history.TRANSITIONS.DOWNLOAD,
    visitDate: new Date(Date.now() - 7200000),
  });

  Assert.equal(root.childCount, MAX_RESULTS, "Result should be limited");
  // Invert the uris array because we are sorted by date descending.
  uris.reverse();
  for (let i = 0; i < root.childCount; ++i) {
    let node = root.getChild(i);
    Assert.equal(node.uri, uris[i], "Found the expected uri");
  }

  root.containerOpen = false;
});
