@Test
  public void byCommitsOnBranchNotMerged() throws Exception {
    TestRepository<Repo> repo = createProject("repo");
    int n = 10;
    List<String> shas = new ArrayList<>(n);
    List<Integer> expectedIds = new ArrayList<>(n);
    Branch.NameKey dest = null;
    for (int i = 0; i < n; i++) {
      ChangeInserter ins = newChange(repo, null, null, null, null);
      ins.insert();
      if (dest == null) {
        dest = ins.getChange().getDest();
      }
      shas.add(ins.getPatchSet().getRevision().get());
      expectedIds.add(ins.getChange().getId().get());
    }

    for (int i = 1; i <= 11; i++) {
      Iterable<ChangeData> cds =
          internalChangeQuery.byCommitsOnBranchNotMerged(
              indexes.getSearchIndex().getSchema(), dest, shas, i);
      Iterable<Integer> ids =
          FluentIterable.from(cds)
              .transform(
                  new Function<ChangeData, Integer>() {
                    @Override
                    public Integer apply(ChangeData in) {
                      return in.getId().get();
                    }
                  });
      String name = "batch size " + i;
      assertThat(ids).named(name).hasSize(n);
      assertThat(ids).named(name).containsExactlyElementsIn(expectedIds);
    }
  }
  @Test
  public void byStatusClosed() throws Exception {
    TestRepository<Repo> repo = createProject("repo");
    ChangeInserter ins1 = newChange(repo, null, null, null, null);
    Change change1 = ins1.getChange();
    change1.setStatus(Change.Status.MERGED);
    ins1.insert();
    ChangeInserter ins2 = newChange(repo, null, null, null, null);
    Change change2 = ins2.getChange();
    change2.setStatus(Change.Status.ABANDONED);
    ins2.insert();
    ChangeInserter ins3 = newChange(repo, null, null, null, null);
    Change change3 = ins3.getChange();
    change3.setStatus(Change.Status.NEW);
    ins3.insert();

    Change[] expected = new Change[] {change2, change1};
    assertQuery("status:closed", expected);
    assertQuery("status:CLOSED", expected);
    assertQuery("status:c", expected);
    assertQuery("status:cl", expected);
    assertQuery("status:clo", expected);
    assertQuery("status:clos", expected);
    assertQuery("status:close", expected);
    assertQuery("status:closed", expected);
    assertQuery("is:closed", expected);
  }
  @Test
  public void byStatusOpen() throws Exception {
    TestRepository<Repo> repo = createProject("repo");
    ChangeInserter ins1 = newChange(repo, null, null, null, null);
    Change change1 = ins1.getChange();
    change1.setStatus(Change.Status.NEW);
    ins1.insert();
    ChangeInserter ins2 = newChange(repo, null, null, null, null);
    Change change2 = ins2.getChange();
    change2.setStatus(Change.Status.DRAFT);
    ins2.insert();
    ChangeInserter ins3 = newChange(repo, null, null, null, null);
    Change change3 = ins3.getChange();
    change3.setStatus(Change.Status.MERGED);
    ins3.insert();

    Change[] expected = new Change[] {change2, change1};
    assertQuery("status:open", expected);
    assertQuery("status:OPEN", expected);
    assertQuery("status:o", expected);
    assertQuery("status:op", expected);
    assertQuery("status:ope", expected);
    assertQuery("status:pending", expected);
    assertQuery("status:PENDING", expected);
    assertQuery("status:p", expected);
    assertQuery("status:pe", expected);
    assertQuery("status:pen", expected);
    assertQuery("is:open", expected);
  }
  @Test
  public void byCommit() throws Exception {
    TestRepository<Repo> repo = createProject("repo");
    ChangeInserter ins = newChange(repo, null, null, null, null);
    ins.insert();
    String sha = ins.getPatchSet().getRevision().get();

    assertQuery("0000000000000000000000000000000000000000");
    for (int i = 0; i <= 36; i++) {
      String q = sha.substring(0, 40 - i);
      assertQuery(q, ins.getChange());
    }
  }
  @Test
  public void explicitVisibleTo() throws Exception {
    TestRepository<Repo> repo = createProject("repo");
    Change change1 = newChange(repo, null, null, userId.get(), null).insert();
    ChangeInserter ins2 = newChange(repo, null, null, userId.get(), null);
    Change change2 = ins2.getChange();
    change2.setStatus(Change.Status.DRAFT);
    ins2.insert();

    String q = "project:repo";
    assertQuery(q, change2, change1);

    // Second user cannot see first user's drafts.
    Account.Id user2 =
        accountManager.authenticate(AuthRequest.forUser("anotheruser")).getAccountId();
    assertQuery(q + " visibleto:" + user2.get(), change1);
  }
  @Test
  public void byLabel() throws Exception {
    accountManager.authenticate(AuthRequest.forUser("anotheruser"));
    TestRepository<Repo> repo = createProject("repo");
    ChangeInserter ins = newChange(repo, null, null, null, null);
    Change change = ins.insert();

    gApi.changes()
        .id(change.getId().get())
        .current()
        .review(new ReviewInput().label("Code-Review", 1));

    assertQuery("label:Code-Review=-2");
    assertQuery("label:Code-Review-2");
    assertQuery("label:Code-Review=-1");
    assertQuery("label:Code-Review-1");
    assertQuery("label:Code-Review=0");
    assertQuery("label:Code-Review=+1", change);
    assertQuery("label:Code-Review=1", change);
    assertQuery("label:Code-Review+1", change);
    assertQuery("label:Code-Review=+2");
    assertQuery("label:Code-Review=2");
    assertQuery("label:Code-Review+2");

    assertQuery("label:Code-Review>=0", change);
    assertQuery("label:Code-Review>0", change);
    assertQuery("label:Code-Review>=1", change);
    assertQuery("label:Code-Review>1");
    assertQuery("label:Code-Review>=2");

    assertQuery("label: Code-Review<=2", change);
    assertQuery("label: Code-Review<2", change);
    assertQuery("label: Code-Review<=1", change);
    assertQuery("label:Code-Review<1");
    assertQuery("label:Code-Review<=0");

    assertQuery("label:Code-Review=+1,anotheruser");
    assertQuery("label:Code-Review=+1,user", change);
    assertQuery("label:Code-Review=+1,user=user", change);
    assertQuery("label:Code-Review=+1,Administrators", change);
    assertQuery("label:Code-Review=+1,group=Administrators", change);
  }
  @Test
  public void byComment() throws Exception {
    TestRepository<Repo> repo = createProject("repo");
    ChangeInserter ins = newChange(repo, null, null, null, null);
    Change change = ins.insert();

    ReviewInput input = new ReviewInput();
    input.message = "toplevel";
    ReviewInput.CommentInput comment = new ReviewInput.CommentInput();
    comment.line = 1;
    comment.message = "inline";
    input.comments =
        ImmutableMap.<String, List<ReviewInput.CommentInput>>of(
            Patch.COMMIT_MSG, ImmutableList.<ReviewInput.CommentInput>of(comment));
    gApi.changes().id(change.getId().get()).current().review(input);

    assertQuery("comment:foo");
    assertQuery("comment:toplevel", change);
    assertQuery("comment:inline", change);
  }
  @Test
  public void updatedOrderWithSubMinuteResolution() throws Exception {
    TestRepository<Repo> repo = createProject("repo");
    ChangeInserter ins1 = newChange(repo, null, null, null, null);
    Change change1 = ins1.insert();
    Change change2 = newChange(repo, null, null, null, null).insert();

    assertThat(lastUpdatedMs(change1)).isLessThan(lastUpdatedMs(change2));

    assertQuery("status:new", change2, change1);

    gApi.changes().id(change1.getId().get()).current().review(new ReviewInput());
    change1 = db.changes().get(change1.getId());

    assertThat(lastUpdatedMs(change1)).isGreaterThan(lastUpdatedMs(change2));
    assertThat(lastUpdatedMs(change1) - lastUpdatedMs(change2))
        .isLessThan(MILLISECONDS.convert(1, MINUTES));

    // change1 moved to the top.
    assertQuery("status:new", change1, change2);
  }
  @Test
  public void byDefault() throws Exception {
    TestRepository<Repo> repo = createProject("repo");

    Change change1 = newChange(repo, null, null, null, null).insert();

    RevCommit commit2 = repo.parseBody(repo.commit().message("foosubject").create());
    Change change2 = newChange(repo, commit2, null, null, null).insert();

    RevCommit commit3 = repo.parseBody(repo.commit().add("Foo.java", "foo contents").create());
    Change change3 = newChange(repo, commit3, null, null, null).insert();

    ChangeInserter ins4 = newChange(repo, null, null, null, null);
    Change change4 = ins4.insert();
    ReviewInput ri4 = new ReviewInput();
    ri4.message = "toplevel";
    ri4.labels = ImmutableMap.<String, Short>of("Code-Review", (short) 1);
    gApi.changes().id(change4.getId().get()).current().review(ri4);

    ChangeInserter ins5 = newChange(repo, null, null, null, null);
    Change change5 = ins5.getChange();
    change5.setTopic("feature5");
    ins5.insert();

    Change change6 = newChange(repo, null, null, null, "branch6").insert();

    assertQuery(change1.getId().get(), change1);
    assertQuery(ChangeTriplet.format(change1), change1);
    assertQuery("foosubject", change2);
    assertQuery("Foo.java", change3);
    assertQuery("Code-Review+1", change4);
    assertQuery("toplevel", change4);
    assertQuery("feature5", change5);
    assertQuery("branch6", change6);
    assertQuery("refs/heads/branch6", change6);

    Change[] expected = new Change[] {change6, change5, change4, change3, change2, change1};
    assertQuery("*****@*****.**", expected);
    assertQuery("repo", expected);
  }
  @Test
  public void byFrom() throws Exception {
    TestRepository<Repo> repo = createProject("repo");
    Change change1 = newChange(repo, null, null, null, null).insert();

    int user2 =
        accountManager.authenticate(AuthRequest.forUser("anotheruser")).getAccountId().get();
    ChangeInserter ins2 = newChange(repo, null, null, user2, null);
    Change change2 = ins2.insert();

    ReviewInput input = new ReviewInput();
    input.message = "toplevel";
    ReviewInput.CommentInput comment = new ReviewInput.CommentInput();
    comment.line = 1;
    comment.message = "inline";
    input.comments =
        ImmutableMap.<String, List<ReviewInput.CommentInput>>of(
            Patch.COMMIT_MSG, ImmutableList.<ReviewInput.CommentInput>of(comment));
    gApi.changes().id(change2.getId().get()).current().review(input);

    assertQuery("from:" + userId.get(), change2, change1);
    assertQuery("from:" + user2, change2);
  }
  @Test
  public void byStatus() throws Exception {
    TestRepository<Repo> repo = createProject("repo");
    ChangeInserter ins1 = newChange(repo, null, null, null, null);
    Change change1 = ins1.getChange();
    change1.setStatus(Change.Status.NEW);
    ins1.insert();
    ChangeInserter ins2 = newChange(repo, null, null, null, null);
    Change change2 = ins2.getChange();
    change2.setStatus(Change.Status.MERGED);
    ins2.insert();

    assertQuery("status:new", change1);
    assertQuery("status:NEW", change1);
    assertQuery("is:new", change1);
    assertQuery("status:merged", change2);
    assertQuery("is:merged", change2);
  }
  @Test
  public void byTopic() throws Exception {
    TestRepository<Repo> repo = createProject("repo");
    ChangeInserter ins1 = newChange(repo, null, null, null, null);
    Change change1 = ins1.getChange();
    change1.setTopic("feature1");
    ins1.insert();

    ChangeInserter ins2 = newChange(repo, null, null, null, null);
    Change change2 = ins2.getChange();
    change2.setTopic("feature2");
    ins2.insert();

    ChangeInserter ins3 = newChange(repo, null, null, null, null);
    Change change3 = ins3.getChange();
    change3.setTopic("Cherrypick-feature2");
    ins3.insert();

    ChangeInserter ins4 = newChange(repo, null, null, null, null);
    Change change4 = ins4.getChange();
    change4.setTopic("feature2-fixup");
    ins4.insert();

    Change change5 = newChange(repo, null, null, null, null).insert();

    assertQuery("intopic:foo");
    assertQuery("intopic:feature1", change1);
    assertQuery("intopic:feature2", change4, change3, change2);
    assertQuery("topic:feature2", change2);
    assertQuery("intopic:feature2", change4, change3, change2);
    assertQuery("intopic:fixup", change4);
    assertQuery("topic:\"\"", change5);
    assertQuery("intopic:\"\"", change5);
  }