blob: fb507446e61c90be61f23db15375c6212ee285e6 [file] [log] [blame]
// Copyright (C) 2013 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.server.query.change;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.concurrent.TimeUnit.HOURS;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.MINUTES;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.hash.Hashing;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.api.projects.ProjectInput;
import com.google.gerrit.extensions.restapi.TopLevelResource;
import com.google.gerrit.lifecycle.LifecycleManager;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Patch;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountManager;
import com.google.gerrit.server.account.AuthRequest;
import com.google.gerrit.server.change.ChangeInserter;
import com.google.gerrit.server.change.ChangeJson.ChangeInfo;
import com.google.gerrit.server.change.ChangesCollection;
import com.google.gerrit.server.change.PostReview;
import com.google.gerrit.server.change.RevisionResource;
import com.google.gerrit.server.project.CreateProject;
import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.schema.SchemaCreator;
import com.google.gerrit.server.util.RequestContext;
import com.google.gerrit.server.util.ThreadLocalRequestContext;
import com.google.gerrit.server.util.TimeUtil;
import com.google.gerrit.testutil.InMemoryDatabase;
import com.google.gerrit.testutil.InMemoryRepositoryManager;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Provider;
import com.google.inject.util.Providers;
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.joda.time.DateTime;
import org.joda.time.DateTimeUtils;
import org.joda.time.DateTimeUtils.MillisProvider;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
@Ignore
public abstract class AbstractQueryChangesTest {
private static final TopLevelResource TLR = TopLevelResource.INSTANCE;
@Inject protected AccountManager accountManager;
@Inject protected ChangeInserter.Factory changeFactory;
@Inject protected ChangesCollection changes;
@Inject protected CreateProject.Factory projectFactory;
@Inject protected IdentifiedUser.RequestFactory userFactory;
@Inject protected InMemoryDatabase schemaFactory;
@Inject protected InMemoryRepositoryManager repoManager;
@Inject protected PostReview postReview;
@Inject protected ProjectControl.GenericFactory projectControlFactory;
@Inject protected Provider<QueryChanges> queryProvider;
@Inject protected SchemaCreator schemaCreator;
@Inject protected ThreadLocalRequestContext requestContext;
protected LifecycleManager lifecycle;
protected ReviewDb db;
protected Account.Id userId;
protected CurrentUser user;
protected volatile long clockStepMs;
private String systemTimeZone;
protected abstract Injector createInjector();
@Before
public void setUpInjector() throws Exception {
Injector injector = createInjector();
injector.injectMembers(this);
lifecycle = new LifecycleManager();
lifecycle.add(injector);
lifecycle.start();
db = schemaFactory.open();
schemaCreator.create(db);
userId = accountManager.authenticate(AuthRequest.forUser("user"))
.getAccountId();
Account userAccount = db.accounts().get(userId);
userAccount.setPreferredEmail("user@example.com");
db.accounts().update(ImmutableList.of(userAccount));
user = userFactory.create(userId);
requestContext.setContext(new RequestContext() {
@Override
public CurrentUser getCurrentUser() {
return user;
}
@Override
public Provider<ReviewDb> getReviewDbProvider() {
return Providers.of(db);
}
});
}
@After
public void tearDownInjector() {
if (lifecycle != null) {
lifecycle.stop();
}
requestContext.setContext(null);
if (db != null) {
db.close();
}
InMemoryDatabase.drop(schemaFactory);
}
@Before
public void setTimeForTesting() {
systemTimeZone = System.setProperty("user.timezone", "US/Eastern");
clockStepMs = 1;
final AtomicLong clockMs = new AtomicLong(
new DateTime(2009, 9, 30, 17, 0, 0).getMillis());
DateTimeUtils.setCurrentMillisProvider(new MillisProvider() {
@Override
public long getMillis() {
return clockMs.getAndAdd(clockStepMs);
}
});
}
@After
public void resetTime() {
DateTimeUtils.setCurrentMillisSystem();
System.setProperty("user.timezone", systemTimeZone);
}
@Test
public void byId() throws Exception {
TestRepository<InMemoryRepository> repo = createProject("repo");
Change change1 = newChange(repo, null, null, null, null).insert();
Change change2 = newChange(repo, null, null, null, null).insert();
assertTrue(query("12345").isEmpty());
assertResultEquals(change1, queryOne(change1.getId().get()));
assertResultEquals(change2, queryOne(change2.getId().get()));
}
@Test
public void byKey() throws Exception {
TestRepository<InMemoryRepository> repo = createProject("repo");
Change change = newChange(repo, null, null, null, null).insert();
String key = change.getKey().get();
assertTrue(query("I0000000000000000000000000000000000000000").isEmpty());
for (int i = 0; i <= 36; i++) {
String q = key.substring(0, 41 - i);
assertResultEquals("result for " + q, change, queryOne(q));
}
}
@Test
public void byStatus() throws Exception {
TestRepository<InMemoryRepository> 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();
assertResultEquals(change1, queryOne("status:new"));
assertResultEquals(change1, queryOne("is:new"));
assertResultEquals(change2, queryOne("status:merged"));
assertResultEquals(change2, queryOne("is:merged"));
}
@Test
public void byStatusOpen() throws Exception {
TestRepository<InMemoryRepository> 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();
List<ChangeInfo> results;
results = query("status:open");
assertEquals(2, results.size());
assertResultEquals(change2, results.get(0));
assertResultEquals(change1, results.get(1));
results = query("is:open");
assertEquals(2, results.size());
assertResultEquals(change2, results.get(0));
assertResultEquals(change1, results.get(1));
}
@Test
public void byStatusClosed() throws Exception {
TestRepository<InMemoryRepository> 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();
List<ChangeInfo> results;
results = query("status:closed");
assertEquals(2, results.size());
assertResultEquals(change2, results.get(0));
assertResultEquals(change1, results.get(1));
results = query("is:closed");
assertEquals(2, results.size());
assertResultEquals(change2, results.get(0));
assertResultEquals(change1, results.get(1));
}
@Test
public void byCommit() throws Exception {
TestRepository<InMemoryRepository> repo = createProject("repo");
ChangeInserter ins = newChange(repo, null, null, null, null);
ins.insert();
String sha = ins.getPatchSet().getRevision().get();
assertTrue(query("0000000000000000000000000000000000000000").isEmpty());
for (int i = 0; i <= 36; i++) {
String q = sha.substring(0, 40 - i);
assertResultEquals("result for " + q, ins.getChange(), queryOne(q));
}
}
@Test
public void byOwner() throws Exception {
TestRepository<InMemoryRepository> repo = createProject("repo");
Change change1 = newChange(repo, null, null, userId.get(), null).insert();
int user2 = accountManager.authenticate(AuthRequest.forUser("anotheruser"))
.getAccountId().get();
Change change2 = newChange(repo, null, null, user2, null).insert();
assertResultEquals(change1, queryOne("owner:" + userId.get()));
assertResultEquals(change2, queryOne("owner:" + user2));
}
@Test
public void byOwnerIn() throws Exception {
TestRepository<InMemoryRepository> repo = createProject("repo");
Change change1 = newChange(repo, null, null, userId.get(), null).insert();
int user2 = accountManager.authenticate(AuthRequest.forUser("anotheruser"))
.getAccountId().get();
Change change2 = newChange(repo, null, null, user2, null).insert();
assertResultEquals(change1, queryOne("ownerin:Administrators"));
List<ChangeInfo> results = query("ownerin:\"Registered Users\"");
assertEquals(results.toString(), 2, results.size());
assertResultEquals(change2, results.get(0));
assertResultEquals(change1, results.get(1));
}
@Test
public void byProject() throws Exception {
TestRepository<InMemoryRepository> repo1 = createProject("repo1");
TestRepository<InMemoryRepository> repo2 = createProject("repo2");
Change change1 = newChange(repo1, null, null, null, null).insert();
Change change2 = newChange(repo2, null, null, null, null).insert();
assertTrue(query("project:foo").isEmpty());
assertTrue(query("project:repo").isEmpty());
assertResultEquals(change1, queryOne("project:repo1"));
assertResultEquals(change2, queryOne("project:repo2"));
}
@Test
public void byProjectPrefix() throws Exception {
TestRepository<InMemoryRepository> repo1 = createProject("repo1");
TestRepository<InMemoryRepository> repo2 = createProject("repo2");
Change change1 = newChange(repo1, null, null, null, null).insert();
Change change2 = newChange(repo2, null, null, null, null).insert();
assertTrue(query("projects:foo").isEmpty());
assertResultEquals(change1, queryOne("projects:repo1"));
assertResultEquals(change2, queryOne("projects:repo2"));
List<ChangeInfo> results;
results = query("projects:repo");
assertEquals(results.toString(), 2, results.size());
assertResultEquals(change2, results.get(0));
assertResultEquals(change1, results.get(1));
}
@Test
public void byBranchAndRef() throws Exception {
TestRepository<InMemoryRepository> repo = createProject("repo");
Change change1 = newChange(repo, null, null, null, "master").insert();
Change change2 = newChange(repo, null, null, null, "branch").insert();
assertTrue(query("branch:foo").isEmpty());
assertResultEquals(change1, queryOne("branch:master"));
assertResultEquals(change1, queryOne("branch:refs/heads/master"));
assertTrue(query("ref:master").isEmpty());
assertResultEquals(change1, queryOne("ref:refs/heads/master"));
assertResultEquals(change1, queryOne("branch:refs/heads/master"));
assertResultEquals(change2, queryOne("branch:branch"));
assertResultEquals(change2, queryOne("branch:refs/heads/branch"));
assertTrue(query("ref:branch").isEmpty());
assertResultEquals(change2, queryOne("ref:refs/heads/branch"));
}
@Test
public void byTopic() throws Exception {
TestRepository<InMemoryRepository> 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();
Change change3 = newChange(repo, null, null, null, null).insert();
assertTrue(query("topic:foo").isEmpty());
assertResultEquals(change1, queryOne("topic:feature1"));
assertResultEquals(change2, queryOne("topic:feature2"));
assertResultEquals(change3, queryOne("topic:\"\""));
}
@Test
public void byMessageExact() throws Exception {
TestRepository<InMemoryRepository> repo = createProject("repo");
RevCommit commit1 = repo.parseBody(repo.commit().message("one").create());
Change change1 = newChange(repo, commit1, null, null, null).insert();
RevCommit commit2 = repo.parseBody(repo.commit().message("two").create());
Change change2 = newChange(repo, commit2, null, null, null).insert();
assertTrue(query("message:foo").isEmpty());
assertResultEquals(change1, queryOne("message:one"));
assertResultEquals(change2, queryOne("message:two"));
}
@Test
public void fullTextWithNumbers() throws Exception {
TestRepository<InMemoryRepository> repo = createProject("repo");
RevCommit commit1 =
repo.parseBody(repo.commit().message("12345 67890").create());
Change change1 = newChange(repo, commit1, null, null, null).insert();
RevCommit commit2 =
repo.parseBody(repo.commit().message("12346 67891").create());
Change change2 = newChange(repo, commit2, null, null, null).insert();
assertTrue(query("message:1234").isEmpty());
assertResultEquals(change1, queryOne("message:12345"));
assertResultEquals(change2, queryOne("message:12346"));
}
@Test
public void byLabel() throws Exception {
accountManager.authenticate(AuthRequest.forUser("anotheruser"));
TestRepository<InMemoryRepository> repo = createProject("repo");
ChangeInserter ins = newChange(repo, null, null, null, null);
Change change = ins.insert();
ReviewInput input = new ReviewInput();
input.message = "toplevel";
input.labels = ImmutableMap.<String, Short> of("Code-Review", (short) 1);
postReview.apply(new RevisionResource(
changes.parse(change.getId()), ins.getPatchSet()), input);
assertTrue(query("label:Code-Review=-2").isEmpty());
assertTrue(query("label:Code-Review-2").isEmpty());
assertTrue(query("label:Code-Review=-1").isEmpty());
assertTrue(query("label:Code-Review-1").isEmpty());
assertTrue(query("label:Code-Review=0").isEmpty());
assertResultEquals(change, queryOne("label:Code-Review=+1"));
assertResultEquals(change, queryOne("label:Code-Review=1"));
assertResultEquals(change, queryOne("label:Code-Review+1"));
assertTrue(query("label:Code-Review=+2").isEmpty());
assertTrue(query("label:Code-Review=2").isEmpty());
assertTrue(query("label:Code-Review+2").isEmpty());
assertResultEquals(change, queryOne("label:Code-Review>=0"));
assertResultEquals(change, queryOne("label:Code-Review>0"));
assertResultEquals(change, queryOne("label:Code-Review>=1"));
assertTrue(query("label:Code-Review>1").isEmpty());
assertTrue(query("label:Code-Review>=2").isEmpty());
assertResultEquals(change, queryOne("label: Code-Review<=2"));
assertResultEquals(change, queryOne("label: Code-Review<2"));
assertResultEquals(change, queryOne("label: Code-Review<=1"));
assertTrue(query("label:Code-Review<1").isEmpty());
assertTrue(query("label:Code-Review<=0").isEmpty());
assertTrue(query("label:Code-Review=+1,anotheruser").isEmpty());
assertResultEquals(change, queryOne("label:Code-Review=+1,user"));
assertResultEquals(change, queryOne("label:Code-Review=+1,user=user"));
assertResultEquals(change, queryOne("label:Code-Review=+1,Administrators"));
assertResultEquals(change, queryOne("label:Code-Review=+1,group=Administrators"));
}
@Test
public void limit() throws Exception {
TestRepository<InMemoryRepository> repo = createProject("repo");
Change last = null;
int n = 5;
for (int i = 0; i < n; i++) {
last = newChange(repo, null, null, null, null).insert();
}
List<ChangeInfo> results;
for (int i = 1; i <= n + 2; i++) {
results = query("status:new limit:" + i);
assertEquals(Math.min(i, n), results.size());
assertResultEquals(last, results.get(0));
}
}
@Test
public void start() throws Exception {
TestRepository<InMemoryRepository> repo = createProject("repo");
List<Change> changes = Lists.newArrayList();
for (int i = 0; i < 2; i++) {
changes.add(newChange(repo, null, null, null, null).insert());
}
QueryChanges q;
List<ChangeInfo> results;
results = query("status:new");
assertEquals(2, results.size());
assertResultEquals(changes.get(1), results.get(0));
assertResultEquals(changes.get(0), results.get(1));
q = newQuery("status:new");
q.setStart(1);
results = query(q);
assertEquals(1, results.size());
assertResultEquals(changes.get(0), results.get(0));
q = newQuery("status:new");
q.setStart(2);
results = query(q);
assertEquals(0, results.size());
q = newQuery("status:new");
q.setStart(3);
results = query(q);
assertEquals(0, results.size());
}
@Test
public void startWithLimit() throws Exception {
TestRepository<InMemoryRepository> repo = createProject("repo");
List<Change> changes = Lists.newArrayList();
for (int i = 0; i < 3; i++) {
changes.add(newChange(repo, null, null, null, null).insert());
}
QueryChanges q;
List<ChangeInfo> results;
results = query("status:new limit:2");
assertEquals(2, results.size());
assertResultEquals(changes.get(2), results.get(0));
assertResultEquals(changes.get(1), results.get(1));
q = newQuery("status:new limit:2");
q.setStart(1);
results = query(q);
assertEquals(2, results.size());
assertResultEquals(changes.get(1), results.get(0));
assertResultEquals(changes.get(0), results.get(1));
q = newQuery("status:new limit:2");
q.setStart(2);
results = query(q);
assertEquals(1, results.size());
assertResultEquals(changes.get(0), results.get(0));
q = newQuery("status:new limit:2");
q.setStart(3);
results = query(q);
assertEquals(0, results.size());
}
@Test
public void updateOrder() throws Exception {
clockStepMs = MILLISECONDS.convert(2, MINUTES);
TestRepository<InMemoryRepository> repo = createProject("repo");
List<ChangeInserter> inserters = Lists.newArrayList();
List<Change> changes = Lists.newArrayList();
for (int i = 0; i < 5; i++) {
inserters.add(newChange(repo, null, null, null, null));
changes.add(inserters.get(i).insert());
}
for (int i : ImmutableList.of(2, 0, 1, 4, 3)) {
ReviewInput input = new ReviewInput();
input.message = "modifying " + i;
postReview.apply(
new RevisionResource(
this.changes.parse(changes.get(i).getId()),
inserters.get(i).getPatchSet()),
input);
changes.set(i, db.changes().get(changes.get(i).getId()));
}
List<ChangeInfo> results = query("status:new");
assertEquals(5, results.size());
assertResultEquals(changes.get(3), results.get(0));
assertResultEquals(changes.get(4), results.get(1));
assertResultEquals(changes.get(1), results.get(2));
assertResultEquals(changes.get(0), results.get(3));
assertResultEquals(changes.get(2), results.get(4));
}
@Test
public void updatedOrderWithMinuteResolution() throws Exception {
clockStepMs = MILLISECONDS.convert(2, MINUTES);
TestRepository<InMemoryRepository> repo = createProject("repo");
ChangeInserter ins1 = newChange(repo, null, null, null, null);
Change change1 = ins1.insert();
Change change2 = newChange(repo, null, null, null, null).insert();
assertTrue(lastUpdatedMs(change1) < lastUpdatedMs(change2));
List<ChangeInfo> results;
results = query("status:new");
assertEquals(2, results.size());
assertResultEquals(change2, results.get(0));
assertResultEquals(change1, results.get(1));
ReviewInput input = new ReviewInput();
input.message = "toplevel";
postReview.apply(new RevisionResource(
changes.parse(change1.getId()), ins1.getPatchSet()), input);
change1 = db.changes().get(change1.getId());
assertTrue(lastUpdatedMs(change1) > lastUpdatedMs(change2));
assertTrue(lastUpdatedMs(change1) - lastUpdatedMs(change2)
> MILLISECONDS.convert(1, MINUTES));
results = query("status:new");
assertEquals(2, results.size());
// change1 moved to the top.
assertResultEquals(change1, results.get(0));
assertResultEquals(change2, results.get(1));
}
@Test
public void updatedOrderWithSubMinuteResolution() throws Exception {
TestRepository<InMemoryRepository> repo = createProject("repo");
ChangeInserter ins1 = newChange(repo, null, null, null, null);
Change change1 = ins1.insert();
Change change2 = newChange(repo, null, null, null, null).insert();
assertTrue(lastUpdatedMs(change1) < lastUpdatedMs(change2));
List<ChangeInfo> results;
results = query("status:new");
assertEquals(2, results.size());
assertResultEquals(change2, results.get(0));
assertResultEquals(change1, results.get(1));
ReviewInput input = new ReviewInput();
input.message = "toplevel";
postReview.apply(new RevisionResource(
changes.parse(change1.getId()), ins1.getPatchSet()), input);
change1 = db.changes().get(change1.getId());
assertTrue(lastUpdatedMs(change1) > lastUpdatedMs(change2));
assertTrue(lastUpdatedMs(change1) - lastUpdatedMs(change2)
< MILLISECONDS.convert(1, MINUTES));
results = query("status:new");
assertEquals(2, results.size());
// change1 moved to the top.
assertResultEquals(change1, results.get(0));
assertResultEquals(change2, results.get(1));
}
@Test
public void filterOutMoreThanOnePageOfResults() throws Exception {
TestRepository<InMemoryRepository> repo = createProject("repo");
Change change = newChange(repo, null, null, userId.get(), null).insert();
int user2 = accountManager.authenticate(AuthRequest.forUser("anotheruser"))
.getAccountId().get();
for (int i = 0; i < 5; i++) {
newChange(repo, null, null, user2, null).insert();
}
//assertResultEquals(change, queryOne("status:new ownerin:Administrators"));
assertResultEquals(change,
queryOne("status:new ownerin:Administrators limit:2"));
}
@Test
public void filterOutAllResults() throws Exception {
TestRepository<InMemoryRepository> repo = createProject("repo");
int user2 = accountManager.authenticate(AuthRequest.forUser("anotheruser"))
.getAccountId().get();
for (int i = 0; i < 5; i++) {
newChange(repo, null, null, user2, null).insert();
}
assertTrue(query("status:new ownerin:Administrators").isEmpty());
assertTrue(query("status:new ownerin:Administrators limit:2").isEmpty());
}
@Test
public void byFileExact() throws Exception {
TestRepository<InMemoryRepository> repo = createProject("repo");
RevCommit commit = repo.parseBody(
repo.commit().message("one")
.add("dir/file1", "contents1").add("dir/file2", "contents2")
.create());
Change change = newChange(repo, commit, null, null, null).insert();
assertTrue(query("file:file").isEmpty());
assertResultEquals(change, queryOne("file:dir"));
assertResultEquals(change, queryOne("file:file1"));
assertResultEquals(change, queryOne("file:file2"));
assertResultEquals(change, queryOne("file:dir/file1"));
assertResultEquals(change, queryOne("file:dir/file2"));
}
@Test
public void byFileRegex() throws Exception {
TestRepository<InMemoryRepository> repo = createProject("repo");
RevCommit commit = repo.parseBody(
repo.commit().message("one")
.add("dir/file1", "contents1").add("dir/file2", "contents2")
.create());
Change change = newChange(repo, commit, null, null, null).insert();
assertTrue(query("file:.*file.*").isEmpty());
assertTrue(query("file:^file.*").isEmpty()); // Whole path only.
assertResultEquals(change, queryOne("file:^dir.file.*"));
}
@Test
public void byPathExact() throws Exception {
TestRepository<InMemoryRepository> repo = createProject("repo");
RevCommit commit = repo.parseBody(
repo.commit().message("one")
.add("dir/file1", "contents1").add("dir/file2", "contents2")
.create());
Change change = newChange(repo, commit, null, null, null).insert();
assertTrue(query("path:file").isEmpty());
assertTrue(query("path:dir").isEmpty());
assertTrue(query("path:file1").isEmpty());
assertTrue(query("path:file2").isEmpty());
assertResultEquals(change, queryOne("path:dir/file1"));
assertResultEquals(change, queryOne("path:dir/file2"));
}
@Test
public void byPathRegex() throws Exception {
TestRepository<InMemoryRepository> repo = createProject("repo");
RevCommit commit = repo.parseBody(
repo.commit().message("one")
.add("dir/file1", "contents1").add("dir/file2", "contents2")
.create());
Change change = newChange(repo, commit, null, null, null).insert();
assertTrue(query("path:.*file.*").isEmpty());
assertResultEquals(change, queryOne("path:^dir.file.*"));
}
@Test
public void byComment() throws Exception {
TestRepository<InMemoryRepository> 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));
postReview.apply(new RevisionResource(
changes.parse(change.getId()), ins.getPatchSet()), input);
assertTrue(query("comment:foo").isEmpty());
assertResultEquals(change, queryOne("comment:toplevel"));
assertResultEquals(change, queryOne("comment:inline"));
}
@Test
public void byAge() throws Exception {
long thirtyHours = MILLISECONDS.convert(30, HOURS);
clockStepMs = thirtyHours;
TestRepository<InMemoryRepository> repo = createProject("repo");
Change change1 = newChange(repo, null, null, null, null).insert();
Change change2 = newChange(repo, null, null, null, null).insert();
clockStepMs = 0; // Queried by AgePredicate constructor.
long now = TimeUtil.nowMs();
assertEquals(thirtyHours, lastUpdatedMs(change2) - lastUpdatedMs(change1));
assertEquals(thirtyHours, now - lastUpdatedMs(change2));
assertEquals(now, TimeUtil.nowMs());
assertTrue(query("-age:1d").isEmpty());
assertTrue(query("-age:" + (30*60-1) + "m").isEmpty());
assertResultEquals(change2, queryOne("-age:2d"));
List<ChangeInfo> results;
results = query("-age:3d");
assertEquals(2, results.size());
assertResultEquals(change2, results.get(0));
assertResultEquals(change1, results.get(1));
assertTrue(query("age:3d").isEmpty());
assertResultEquals(change1, queryOne("age:2d"));
results = query("age:1d");
assertEquals(2, results.size());
assertResultEquals(change2, results.get(0));
assertResultEquals(change1, results.get(1));
}
@Test
public void byBefore() throws Exception {
clockStepMs = MILLISECONDS.convert(30, HOURS);
TestRepository<InMemoryRepository> repo = createProject("repo");
Change change1 = newChange(repo, null, null, null, null).insert();
Change change2 = newChange(repo, null, null, null, null).insert();
clockStepMs = 0;
assertTrue(query("before:2009-09-29").isEmpty());
assertTrue(query("before:2009-09-30").isEmpty());
assertTrue(query("before:\"2009-09-30 16:59:00 -0400\"").isEmpty());
assertTrue(query("before:\"2009-09-30 20:59:00 -0000\"").isEmpty());
assertTrue(query("before:\"2009-09-30 20:59:00\"").isEmpty());
assertResultEquals(change1,
queryOne("before:\"2009-09-30 17:02:00 -0400\""));
assertResultEquals(change1,
queryOne("before:\"2009-10-01 21:02:00 -0000\""));
assertResultEquals(change1,
queryOne("before:\"2009-10-01 21:02:00\""));
assertResultEquals(change1, queryOne("before:2009-10-01"));
List<ChangeInfo> results;
results = query("before:2009-10-03");
assertEquals(2, results.size());
assertResultEquals(change2, results.get(0));
assertResultEquals(change1, results.get(1));
}
@Test
public void byAfter() throws Exception {
clockStepMs = MILLISECONDS.convert(30, HOURS);
TestRepository<InMemoryRepository> repo = createProject("repo");
Change change1 = newChange(repo, null, null, null, null).insert();
Change change2 = newChange(repo, null, null, null, null).insert();
clockStepMs = 0;
assertTrue(query("after:2009-10-03").isEmpty());
assertResultEquals(change2,
queryOne("after:\"2009-10-01 20:59:59 -0400\""));
assertResultEquals(change2,
queryOne("after:\"2009-10-01 20:59:59 -0000\""));
assertResultEquals(change2, queryOne("after:2009-10-01"));
List<ChangeInfo> results;
results = query("after:2009-09-30");
assertEquals(2, results.size());
assertResultEquals(change2, results.get(0));
assertResultEquals(change1, results.get(1));
}
@Test
public void bySize() throws Exception {
TestRepository<InMemoryRepository> repo = createProject("repo");
// added = 3, deleted = 0, delta = 3
RevCommit commit1 = repo.parseBody(
repo.commit().add("file1", "foo\n\foo\nfoo").create());
// added = 0, deleted = 2, delta = 2
RevCommit commit2 = repo.parseBody(
repo.commit().parent(commit1).add("file1", "foo").create());
Change change1 = newChange(repo, commit1, null, null, null).insert();
Change change2 = newChange(repo, commit2, null, null, null).insert();
assertTrue(query("added:>4").isEmpty());
assertResultEquals(change1, queryOne("added:3"));
assertResultEquals(change1, queryOne("added:>2"));
assertResultEquals(change1, queryOne("added:>=3"));
assertResultEquals(change2, queryOne("added:<1"));
assertResultEquals(change2, queryOne("added:<=0"));
assertTrue(query("deleted:>3").isEmpty());
assertResultEquals(change2, queryOne("deleted:2"));
assertResultEquals(change2, queryOne("deleted:>1"));
assertResultEquals(change2, queryOne("deleted:>=2"));
assertResultEquals(change1, queryOne("deleted:<1"));
assertResultEquals(change1, queryOne("deleted:<=0"));
for (String str : Lists.newArrayList("delta", "size")) {
assertTrue(query(str + ":<2").isEmpty());
assertResultEquals(change1, queryOne(str + ":3"));
assertResultEquals(change1, queryOne(str + ":>2"));
assertResultEquals(change1, queryOne(str + ":>=3"));
assertResultEquals(change2, queryOne(str + ":<3"));
assertResultEquals(change2, queryOne(str + ":<=2"));
}
}
@Test
public void byDefault() throws Exception {
TestRepository<InMemoryRepository> 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);
postReview.apply(new RevisionResource(
changes.parse(change4.getId()), ins4.getPatchSet()), 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();
assertResultEquals(change1,
queryOne(Integer.toString(change1.getId().get())));
assertResultEquals(change2, queryOne("foosubject"));
assertResultEquals(change3, queryOne("Foo.java"));
assertResultEquals(change4, queryOne("Code-Review+1"));
assertResultEquals(change4, queryOne("toplevel"));
assertResultEquals(change5, queryOne("feature5"));
assertResultEquals(change6, queryOne("branch6"));
assertResultEquals(change6, queryOne("refs/heads/branch6"));
assertEquals(6, query("user@example.com").size());
assertEquals(6, query("repo").size());
}
protected ChangeInserter newChange(
TestRepository<InMemoryRepository> repo,
@Nullable RevCommit commit, @Nullable String key, @Nullable Integer owner,
@Nullable String branch) throws Exception {
if (commit == null) {
commit = repo.parseBody(repo.commit().message("message").create());
}
Account.Id ownerId = owner != null ? new Account.Id(owner) : userId;
branch = Objects.firstNonNull(branch, "refs/heads/master");
if (!branch.startsWith("refs/heads/")) {
branch = "refs/heads/" + branch;
}
Project.NameKey project = new Project.NameKey(
repo.getRepository().getDescription().getRepositoryName());
Change.Id id = new Change.Id(db.nextChangeId());
if (key == null) {
key = "I" + Hashing.sha1().newHasher()
.putInt(id.get())
.putString(project.get(), UTF_8)
.putString(commit.name(), UTF_8)
.putInt(ownerId.get())
.putString(branch, UTF_8)
.hash()
.toString();
}
Change change = new Change(new Change.Key(key), id, ownerId,
new Branch.NameKey(project, branch), TimeUtil.nowTs());
return changeFactory.create(
projectControlFactory.controlFor(project,
userFactory.create(ownerId)).controlFor(change).getRefControl(),
change,
commit);
}
protected void assertResultEquals(Change expected, ChangeInfo actual) {
assertEquals(expected.getId().get(), actual._number);
}
protected void assertResultEquals(String message, Change expected,
ChangeInfo actual) {
assertEquals(message, expected.getId().get(), actual._number);
}
protected TestRepository<InMemoryRepository> createProject(String name)
throws Exception {
CreateProject create = projectFactory.create(name);
create.apply(TLR, new ProjectInput());
return new TestRepository<InMemoryRepository>(
repoManager.openRepository(new Project.NameKey(name)));
}
protected QueryChanges newQuery(Object query) {
QueryChanges q = queryProvider.get();
q.addQuery(query.toString());
return q;
}
@SuppressWarnings({"rawtypes", "unchecked"})
protected List<ChangeInfo> query(QueryChanges q) throws Exception {
Object result = q.apply(TLR);
assertTrue(
String.format("expected List<ChangeInfo>, found %s for [%s]",
result, q.getQuery(0)),
result instanceof List);
List results = (List) result;
if (!results.isEmpty()) {
assertTrue(
String.format("expected ChangeInfo, found %s for [%s]",
result, q.getQuery(0)),
results.get(0) instanceof ChangeInfo);
}
return (List<ChangeInfo>) result;
}
protected List<ChangeInfo> query(Object query) throws Exception {
return query(newQuery(query));
}
protected ChangeInfo queryOne(Object query) throws Exception {
List<ChangeInfo> results = query(query);
assertTrue(
String.format("expected singleton List<ChangeInfo>, found %s for [%s]",
results, query),
results.size() == 1);
return results.get(0);
}
protected static long lastUpdatedMs(Change c) {
return c.getLastUpdatedOn().getTime();
}
}