| // 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(); |
| } |
| } |