Merge changes from topic 'submit-whole-topic-mergeability-check'

* changes:
  AbstractSubmit: Check submit button is there before clicking it
  ChangeNotes: Enforce project passed to factory matches change
  AbstractDaemonTest: Extract methods for parsing resources
  AbstractSubmit: Use testing time
  TestTimeUtil: Also stub out JGit's SystemReader
  ChangeNotesParser: Don't always bump lastUpdatedOn
This commit is contained in:
Edwin Kempin
2016-02-22 12:03:42 +00:00
committed by Gerrit Code Review
15 changed files with 271 additions and 83 deletions

View File

@@ -38,6 +38,7 @@ import com.google.gerrit.extensions.client.ListChangesOption;
import com.google.gerrit.extensions.common.ActionInfo; import com.google.gerrit.extensions.common.ActionInfo;
import com.google.gerrit.extensions.common.ChangeInfo; import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.EditInfo; import com.google.gerrit.extensions.common.EditInfo;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.RestApiException; import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.reviewdb.client.AccountGroup; import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.reviewdb.client.PatchSet;
@@ -51,6 +52,9 @@ import com.google.gerrit.server.OutputFormat;
import com.google.gerrit.server.PatchSetUtil; import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.account.AccountCache; import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.GroupCache; import com.google.gerrit.server.account.GroupCache;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.RevisionResource;
import com.google.gerrit.server.change.Revisions;
import com.google.gerrit.server.config.AllProjectsName; import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.CanonicalWebUrl; import com.google.gerrit.server.config.CanonicalWebUrl;
import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.config.GerritServerConfig;
@@ -59,6 +63,7 @@ import com.google.gerrit.server.git.MetaDataUpdate;
import com.google.gerrit.server.git.ProjectConfig; import com.google.gerrit.server.git.ProjectConfig;
import com.google.gerrit.server.index.ChangeIndexer; import com.google.gerrit.server.index.ChangeIndexer;
import com.google.gerrit.server.notedb.ChangeNotes; import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.ProjectCache; import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.Util; import com.google.gerrit.server.project.Util;
import com.google.gerrit.server.query.change.ChangeData; import com.google.gerrit.server.query.change.ChangeData;
@@ -179,6 +184,9 @@ public abstract class AbstractDaemonTest {
@Inject @Inject
protected ChangeFinder changeFinder; protected ChangeFinder changeFinder;
@Inject
protected Revisions revisions;
protected TestRepository<InMemoryRepository> testRepo; protected TestRepository<InMemoryRepository> testRepo;
protected GerritServer server; protected GerritServer server;
protected TestAccount admin; protected TestAccount admin;
@@ -653,4 +661,32 @@ public abstract class AbstractDaemonTest {
protected IdentifiedUser user(TestAccount testAccount) { protected IdentifiedUser user(TestAccount testAccount) {
return identifiedUserFactory.create(Providers.of(db), testAccount.getId()); return identifiedUserFactory.create(Providers.of(db), testAccount.getId());
} }
protected RevisionResource parseCurrentRevisionResource(String changeId)
throws Exception {
ChangeResource cr = parseChangeResource(changeId);
int psId = cr.getChange().currentPatchSetId().get();
return revisions.parse(cr,
IdString.fromDecoded(Integer.toString(psId)));
}
protected RevisionResource parseRevisionResource(String changeId, int n)
throws Exception {
return revisions.parse(parseChangeResource(changeId),
IdString.fromDecoded(Integer.toString(n)));
}
protected RevisionResource parseRevisionResource(PushOneCommit.Result r)
throws Exception {
PatchSet.Id psId = r.getPatchSetId();
return parseRevisionResource(psId.getParentKey().toString(), psId.get());
}
protected ChangeResource parseChangeResource(String changeId)
throws Exception {
List<ChangeControl> ctls = changeFinder.find(
changeId, atrScope.get().getUser());
assertThat(ctls).hasSize(1);
return new ChangeResource(ctls.get(0));
}
} }

View File

@@ -48,15 +48,10 @@ import com.google.gerrit.extensions.common.RevisionInfo;
import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BinaryResult; import com.google.gerrit.extensions.restapi.BinaryResult;
import com.google.gerrit.extensions.restapi.ETagView; import com.google.gerrit.extensions.restapi.ETagView;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceConflictException; import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.reviewdb.client.Patch; import com.google.gerrit.reviewdb.client.Patch;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.GetRevisionActions; import com.google.gerrit.server.change.GetRevisionActions;
import com.google.gerrit.server.change.RevisionResource; import com.google.gerrit.server.change.RevisionResource;
import com.google.gerrit.server.change.Revisions;
import com.google.gerrit.server.project.ChangeControl;
import com.google.inject.Inject; import com.google.inject.Inject;
import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectId;
@@ -81,9 +76,6 @@ public class RevisionIT extends AbstractDaemonTest {
@Inject @Inject
private GetRevisionActions getRevisionActions; private GetRevisionActions getRevisionActions;
@Inject
private Revisions revisions;
private TestAccount admin2; private TestAccount admin2;
@Before @Before
@@ -686,17 +678,6 @@ public class RevisionIT extends AbstractDaemonTest {
return gApi.changes().id(r.getChangeId()).current(); return gApi.changes().id(r.getChangeId()).current();
} }
private RevisionResource parseRevisionResource(PushOneCommit.Result r)
throws Exception {
PatchSet.Id psId = r.getPatchSetId();
List<ChangeControl> ctls = changeFinder.find(
Integer.toString(psId.getParentKey().get()), atrScope.get().getUser());
assertThat(ctls).hasSize(1);
return revisions.parse(
new ChangeResource(ctls.get(0)),
IdString.fromDecoded(Integer.toString(psId.get())));
}
private String checkETag(ETagView<RevisionResource> view, private String checkETag(ETagView<RevisionResource> view,
PushOneCommit.Result r, String oldETag) throws Exception { PushOneCommit.Result r, String oldETag) throws Exception {
String eTag = view.getETag(parseRevisionResource(r)); String eTag = view.getETag(parseRevisionResource(r));

View File

@@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assert_;
import static com.google.common.truth.TruthJUnit.assume; import static com.google.common.truth.TruthJUnit.assume;
import static com.google.gerrit.extensions.client.ListChangesOption.CURRENT_REVISION; import static com.google.gerrit.extensions.client.ListChangesOption.CURRENT_REVISION;
import static com.google.gerrit.extensions.client.ListChangesOption.DETAILED_LABELS; import static com.google.gerrit.extensions.client.ListChangesOption.DETAILED_LABELS;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import com.google.common.base.Function; import com.google.common.base.Function;
@@ -44,6 +45,7 @@ import com.google.gerrit.extensions.common.ChangeMessageInfo;
import com.google.gerrit.extensions.common.LabelInfo; import com.google.gerrit.extensions.common.LabelInfo;
import com.google.gerrit.extensions.restapi.ResourceConflictException; import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestApiException; import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.webui.UiAction;
import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.reviewdb.client.PatchSet;
@@ -52,6 +54,8 @@ import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.ApprovalsUtil; import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.change.RevisionResource;
import com.google.gerrit.server.change.Submit;
import com.google.gerrit.server.data.ChangeAttribute; import com.google.gerrit.server.data.ChangeAttribute;
import com.google.gerrit.server.data.PatchSetAttribute; import com.google.gerrit.server.data.PatchSetAttribute;
import com.google.gerrit.server.events.ChangeMergedEvent; import com.google.gerrit.server.events.ChangeMergedEvent;
@@ -59,6 +63,7 @@ import com.google.gerrit.server.events.Event;
import com.google.gerrit.server.notedb.ChangeNotes; import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.project.NoSuchChangeException; import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.testutil.ConfigSuite; import com.google.gerrit.testutil.ConfigSuite;
import com.google.gerrit.testutil.TestTimeUtil;
import com.google.gwtorm.server.OrmException; import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject; import com.google.inject.Inject;
@@ -100,11 +105,28 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
@Inject @Inject
private IdentifiedUser.GenericFactory factory; private IdentifiedUser.GenericFactory factory;
@Inject
private Submit submitHandler;
@Inject @Inject
EventSource source; EventSource source;
private EventListener eventListener; private EventListener eventListener;
private String systemTimeZone;
@Before
public void setTimeForTesting() {
systemTimeZone = System.setProperty("user.timezone", "US/Eastern");
TestTimeUtil.resetWithClockStep(1, SECONDS);
}
@After
public void resetTime() {
TestTimeUtil.useSystemTime();
System.setProperty("user.timezone", systemTimeZone);
}
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
mergeResults = Maps.newHashMap(); mergeResults = Maps.newHashMap();
@@ -234,6 +256,9 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
Class<? extends RestApiException> expectedExceptionType, Class<? extends RestApiException> expectedExceptionType,
String expectedExceptionMsg, boolean checkMergeResult) throws Exception { String expectedExceptionMsg, boolean checkMergeResult) throws Exception {
approve(changeId); approve(changeId);
if (expectedExceptionType == null) {
assertSubmittable(changeId);
}
try { try {
gApi.changes().id(changeId).current().submit(input); gApi.changes().id(changeId).current().submit(input);
if (expectedExceptionType != null) { if (expectedExceptionType != null) {
@@ -263,6 +288,16 @@ public abstract class AbstractSubmit extends AbstractDaemonTest {
} }
} }
protected void assertSubmittable(String changeId) throws Exception {
assertThat(gApi.changes().id(changeId).info().submittable)
.named("submit bit on ChangeInfo")
.isEqualTo(true);
RevisionResource rsrc = parseCurrentRevisionResource(changeId);
UiAction.Description desc = submitHandler.getDescription(rsrc);
assertThat(desc.isVisible()).named("visible bit on submit action").isTrue();
assertThat(desc.isEnabled()).named("enabled bit on submit action").isTrue();
}
private void checkMergeResult(ChangeInfo change) throws Exception { private void checkMergeResult(ChangeInfo change) throws Exception {
// Get the revision of the branch after the submit to compare with the // Get the revision of the branch after the submit to compare with the
// newRev of the ChangeMergedEvent. // newRev of the ChangeMergedEvent.

View File

@@ -39,7 +39,6 @@ import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.ChangesCollection; import com.google.gerrit.server.change.ChangesCollection;
import com.google.gerrit.server.change.PostReview; import com.google.gerrit.server.change.PostReview;
import com.google.gerrit.server.change.RevisionResource; import com.google.gerrit.server.change.RevisionResource;
import com.google.gerrit.server.change.Revisions;
import com.google.gerrit.testutil.FakeEmailSender; import com.google.gerrit.testutil.FakeEmailSender;
import com.google.gerrit.testutil.FakeEmailSender.Message; import com.google.gerrit.testutil.FakeEmailSender.Message;
import com.google.inject.Inject; import com.google.inject.Inject;
@@ -59,9 +58,6 @@ public class CommentsIT extends AbstractDaemonTest {
@Inject @Inject
private Provider<ChangesCollection> changes; private Provider<ChangesCollection> changes;
@Inject
private Provider<Revisions> revisions;
@Inject @Inject
private Provider<PostReview> postReview; private Provider<PostReview> postReview;
@@ -243,7 +239,7 @@ public class CommentsIT extends AbstractDaemonTest {
changes.get().parse(TopLevelResource.INSTANCE, changes.get().parse(TopLevelResource.INSTANCE,
IdString.fromDecoded(changeId)); IdString.fromDecoded(changeId));
RevisionResource revRsrc = RevisionResource revRsrc =
revisions.get().parse(changeRsrc, IdString.fromDecoded(revId)); revisions.parse(changeRsrc, IdString.fromDecoded(revId));
postReview.get().apply(revRsrc, input, timestamp); postReview.get().apply(revRsrc, input, timestamp);
Map<String, List<CommentInfo>> result = getPublishedComments(changeId, revId); Map<String, List<CommentInfo>> result = getPublishedComments(changeId, revId);
assertThat(result).isNotEmpty(); assertThat(result).isNotEmpty();

View File

@@ -68,7 +68,8 @@ public class GetRevisionActions implements ETagView<RevisionResource> {
rsrc.getChangeResource().prepareETag(h, user); rsrc.getChangeResource().prepareETag(h, user);
h.putBoolean(Submit.wholeTopicEnabled(config)); h.putBoolean(Submit.wholeTopicEnabled(config));
ReviewDb db = dbProvider.get(); ReviewDb db = dbProvider.get();
ChangeSet cs = mergeSuperSet.completeChangeSet(db, rsrc.getChange()); ChangeSet cs =
mergeSuperSet.completeChangeSet(db, rsrc.getChange(), user);
for (ChangeData cd : cs.changes()) { for (ChangeData cd : cs.changes()) {
new ChangeResource(cd.changeControl()).prepareETag(h, user); new ChangeResource(cd.changeControl()).prepareETag(h, user);
} }

View File

@@ -32,7 +32,6 @@ import com.google.gerrit.extensions.webui.UiAction;
import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.ChangeMessage; import com.google.gerrit.reviewdb.client.ChangeMessage;
import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.RevId; import com.google.gerrit.reviewdb.client.RevId;
import com.google.gerrit.reviewdb.server.ReviewDb; import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ChangeMessagesUtil; import com.google.gerrit.server.ChangeMessagesUtil;
@@ -47,7 +46,6 @@ import com.google.gerrit.server.git.MergeOp;
import com.google.gerrit.server.git.MergeSuperSet; import com.google.gerrit.server.git.MergeSuperSet;
import com.google.gerrit.server.notedb.ChangeNotes; import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.project.ChangeControl; import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.query.change.ChangeData; import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.InternalChangeQuery; import com.google.gerrit.server.query.change.InternalChangeQuery;
import com.google.gwtorm.server.OrmException; import com.google.gwtorm.server.OrmException;
@@ -117,7 +115,6 @@ public class Submit implements RestModifyView<RevisionResource, SubmitInput>,
private final GitRepositoryManager repoManager; private final GitRepositoryManager repoManager;
private final ChangeData.Factory changeDataFactory; private final ChangeData.Factory changeDataFactory;
private final ChangeMessagesUtil cmUtil; private final ChangeMessagesUtil cmUtil;
private final ChangeControl.GenericFactory changeControlFactory;
private final ChangeNotes.Factory changeNotesFactory; private final ChangeNotes.Factory changeNotesFactory;
private final Provider<MergeOp> mergeOpProvider; private final Provider<MergeOp> mergeOpProvider;
private final MergeSuperSet mergeSuperSet; private final MergeSuperSet mergeSuperSet;
@@ -137,7 +134,6 @@ public class Submit implements RestModifyView<RevisionResource, SubmitInput>,
GitRepositoryManager repoManager, GitRepositoryManager repoManager,
ChangeData.Factory changeDataFactory, ChangeData.Factory changeDataFactory,
ChangeMessagesUtil cmUtil, ChangeMessagesUtil cmUtil,
ChangeControl.GenericFactory changeControlFactory,
ChangeNotes.Factory changeNotesFactory, ChangeNotes.Factory changeNotesFactory,
Provider<MergeOp> mergeOpProvider, Provider<MergeOp> mergeOpProvider,
MergeSuperSet mergeSuperSet, MergeSuperSet mergeSuperSet,
@@ -149,7 +145,6 @@ public class Submit implements RestModifyView<RevisionResource, SubmitInput>,
this.repoManager = repoManager; this.repoManager = repoManager;
this.changeDataFactory = changeDataFactory; this.changeDataFactory = changeDataFactory;
this.cmUtil = cmUtil; this.cmUtil = cmUtil;
this.changeControlFactory = changeControlFactory;
this.changeNotesFactory = changeNotesFactory; this.changeNotesFactory = changeNotesFactory;
this.mergeOpProvider = mergeOpProvider; this.mergeOpProvider = mergeOpProvider;
this.mergeSuperSet = mergeSuperSet; this.mergeSuperSet = mergeSuperSet;
@@ -233,19 +228,16 @@ public class Submit implements RestModifyView<RevisionResource, SubmitInput>,
/** /**
* @param cs set of changes to be submitted at once * @param cs set of changes to be submitted at once
* @param project the name of the project
* @param identifiedUser the user who is checking to submit * @param identifiedUser the user who is checking to submit
* @return a reason why any of the changes is not submittable or null * @return a reason why any of the changes is not submittable or null
*/ */
private String problemsForSubmittingChangeset(ChangeSet cs, private String problemsForSubmittingChangeset(ChangeSet cs,
Project.NameKey project, IdentifiedUser identifiedUser) { IdentifiedUser identifiedUser) {
try { try {
@SuppressWarnings("resource") @SuppressWarnings("resource")
ReviewDb db = dbProvider.get(); ReviewDb db = dbProvider.get();
for (PatchSet.Id psId : cs.patchIds()) { for (ChangeData c : cs.changes()) {
ChangeControl changeControl = changeControlFactory ChangeControl changeControl = c.changeControl(identifiedUser);
.controlFor(db, project, psId.getParentKey(), identifiedUser);
ChangeData c = changeDataFactory.create(db, changeControl);
if (!changeControl.isVisible(db)) { if (!changeControl.isVisible(db)) {
return BLOCKED_HIDDEN_SUBMIT_TOOLTIP; return BLOCKED_HIDDEN_SUBMIT_TOOLTIP;
@@ -270,7 +262,7 @@ public class Submit implements RestModifyView<RevisionResource, SubmitInput>,
} }
} catch (ResourceConflictException e) { } catch (ResourceConflictException e) {
return BLOCKED_SUBMIT_TOOLTIP; return BLOCKED_SUBMIT_TOOLTIP;
} catch (NoSuchChangeException | OrmException e) { } catch (OrmException e) {
log.error("Error checking if change is submittable", e); log.error("Error checking if change is submittable", e);
throw new OrmRuntimeException("Could not determine problems for the change", e); throw new OrmRuntimeException("Could not determine problems for the change", e);
} }
@@ -329,7 +321,8 @@ public class Submit implements RestModifyView<RevisionResource, SubmitInput>,
ChangeSet cs; ChangeSet cs;
try { try {
cs = mergeSuperSet.completeChangeSet(db, cd.change()); cs = mergeSuperSet.completeChangeSet(
db, cd.change(), resource.getControl().getUser());
} catch (OrmException | IOException e) { } catch (OrmException | IOException e) {
throw new OrmRuntimeException("Could not determine complete set of " + throw new OrmRuntimeException("Could not determine complete set of " +
"changes to be submitted", e); "changes to be submitted", e);
@@ -343,8 +336,8 @@ public class Submit implements RestModifyView<RevisionResource, SubmitInput>,
&& !Strings.isNullOrEmpty(topic) && !Strings.isNullOrEmpty(topic)
&& topicSize > 1; && topicSize > 1;
String submitProblems = problemsForSubmittingChangeset(cs, String submitProblems =
resource.getProject(), resource.getUser()); problemsForSubmittingChangeset(cs, resource.getUser());
if (submitProblems != null) { if (submitProblems != null) {
return new UiAction.Description() return new UiAction.Description()
.setLabel(treatWithTopic .setLabel(treatWithTopic

View File

@@ -23,6 +23,7 @@ import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestReadView; import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.server.ReviewDb; import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.change.WalkSorter.PatchSetData; import com.google.gerrit.server.change.WalkSorter.PatchSetData;
import com.google.gerrit.server.git.ChangeSet; import com.google.gerrit.server.git.ChangeSet;
import com.google.gerrit.server.git.MergeSuperSet; import com.google.gerrit.server.git.MergeSuperSet;
@@ -74,7 +75,7 @@ public class SubmittedTogether implements RestReadView<ChangeResource> {
Change c = resource.getChange(); Change c = resource.getChange();
List<ChangeData> cds; List<ChangeData> cds;
if (c.getStatus().isOpen()) { if (c.getStatus().isOpen()) {
cds = getForOpenChange(c); cds = getForOpenChange(c, resource.getControl().getUser());
} else if (c.getStatus().asChangeStatus() == ChangeStatus.MERGED) { } else if (c.getStatus().asChangeStatus() == ChangeStatus.MERGED) {
cds = getForMergedChange(c); cds = getForMergedChange(c);
} else { } else {
@@ -99,9 +100,9 @@ public class SubmittedTogether implements RestReadView<ChangeResource> {
} }
} }
private List<ChangeData> getForOpenChange(Change c) private List<ChangeData> getForOpenChange(Change c, CurrentUser user)
throws OrmException, IOException { throws OrmException, IOException {
ChangeSet cs = mergeSuperSet.completeChangeSet(dbProvider.get(), c); ChangeSet cs = mergeSuperSet.completeChangeSet(dbProvider.get(), c, user);
return cs.changes().asList(); return cs.changes().asList();
} }

View File

@@ -547,7 +547,7 @@ public class MergeOp implements AutoCloseable {
this.db = db; this.db = db;
logDebug("Beginning integration of {}", change); logDebug("Beginning integration of {}", change);
try { try {
ChangeSet cs = mergeSuperSet.completeChangeSet(db, change); ChangeSet cs = mergeSuperSet.completeChangeSet(db, change, caller);
checkState(cs.ids().contains(change.getId()), checkState(cs.ids().contains(change.getId()),
"change %s missing from %s", change.getId(), cs); "change %s missing from %s", change.getId(), cs);
this.commits = new CommitStatus(cs); this.commits = new CommitStatus(cs);

View File

@@ -24,6 +24,7 @@ import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.server.ReviewDb; import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.change.Submit; import com.google.gerrit.server.change.Submit;
import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.index.ChangeField; import com.google.gerrit.server.index.ChangeField;
@@ -90,21 +91,22 @@ public class MergeSuperSet {
this.repoManager = repoManager; this.repoManager = repoManager;
} }
public ChangeSet completeChangeSet(ReviewDb db, Change change) public ChangeSet completeChangeSet(ReviewDb db, Change change, CurrentUser user)
throws MissingObjectException, IncorrectObjectTypeException, IOException, throws MissingObjectException, IncorrectObjectTypeException, IOException,
OrmException { OrmException {
ChangeData cd = ChangeData cd =
changeDataFactory.create(db, change.getProject(), change.getId()); changeDataFactory.create(db, change.getProject(), change.getId());
cd.changeControl(user);
if (Submit.wholeTopicEnabled(cfg)) { if (Submit.wholeTopicEnabled(cfg)) {
return completeChangeSetIncludingTopics(db, new ChangeSet(cd)); return completeChangeSetIncludingTopics(db, new ChangeSet(cd), user);
} else { } else {
return completeChangeSetWithoutTopic(db, new ChangeSet(cd)); return completeChangeSetWithoutTopic(db, new ChangeSet(cd), user);
} }
} }
private ChangeSet completeChangeSetWithoutTopic(ReviewDb db, ChangeSet changes) private ChangeSet completeChangeSetWithoutTopic(ReviewDb db, ChangeSet changes,
throws MissingObjectException, IncorrectObjectTypeException, IOException, CurrentUser user) throws MissingObjectException,
OrmException { IncorrectObjectTypeException, IOException, OrmException {
List<ChangeData> ret = new ArrayList<>(); List<ChangeData> ret = new ArrayList<>();
Multimap<Project.NameKey, Change.Id> pc = changes.changesByProject(); Multimap<Project.NameKey, Change.Id> pc = changes.changesByProject();
@@ -113,6 +115,7 @@ public class MergeSuperSet {
RevWalk rw = CodeReviewCommit.newRevWalk(repo)) { RevWalk rw = CodeReviewCommit.newRevWalk(repo)) {
for (Change.Id cId : pc.get(project)) { for (Change.Id cId : pc.get(project)) {
ChangeData cd = changeDataFactory.create(db, project, cId); ChangeData cd = changeDataFactory.create(db, project, cId);
cd.changeControl(user);
SubmitTypeRecord str = cd.submitTypeRecord(); SubmitTypeRecord str = cd.submitTypeRecord();
if (!str.isOk()) { if (!str.isOk()) {
@@ -169,11 +172,12 @@ public class MergeSuperSet {
} }
private ChangeSet completeChangeSetIncludingTopics( private ChangeSet completeChangeSetIncludingTopics(
ReviewDb db, ChangeSet changes) throws MissingObjectException, ReviewDb db, ChangeSet changes, CurrentUser user)
IncorrectObjectTypeException, IOException, OrmException { throws MissingObjectException, IncorrectObjectTypeException, IOException,
OrmException {
Set<String> topicsTraversed = new HashSet<>(); Set<String> topicsTraversed = new HashSet<>();
boolean done = false; boolean done = false;
ChangeSet newCs = completeChangeSetWithoutTopic(db, changes); ChangeSet newCs = completeChangeSetWithoutTopic(db, changes, user);
while (!done) { while (!done) {
List<ChangeData> chgs = new ArrayList<>(); List<ChangeData> chgs = new ArrayList<>();
done = true; done = true;
@@ -187,7 +191,7 @@ public class MergeSuperSet {
} }
} }
changes = new ChangeSet(chgs); changes = new ChangeSet(chgs);
newCs = completeChangeSetWithoutTopic(db, changes); newCs = completeChangeSetWithoutTopic(db, changes, user);
} }
return newCs; return newCs;
} }

View File

@@ -14,6 +14,7 @@
package com.google.gerrit.server.notedb; package com.google.gerrit.server.notedb;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Preconditions.checkState;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.GERRIT_PLACEHOLDER_HOST; import static com.google.gerrit.server.notedb.ChangeNoteUtil.GERRIT_PLACEHOLDER_HOST;
@@ -174,6 +175,10 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
public ChangeNotes create(ReviewDb db, Project.NameKey project, public ChangeNotes create(ReviewDb db, Project.NameKey project,
Change.Id changeId) throws OrmException { Change.Id changeId) throws OrmException {
Change change = db.changes().get(changeId); Change change = db.changes().get(changeId);
checkArgument(change.getProject().equals(project),
"passed project %s when creating ChangeNotes for %s, but actual"
+ " project is %s",
project, changeId, change.getProject());
// TODO: Throw NoSuchChangeException when the change is not found in the // TODO: Throw NoSuchChangeException when the change is not found in the
// database // database
return new ChangeNotes(repoManager, migration, allUsersProvider, project, return new ChangeNotes(repoManager, migration, allUsersProvider, project,

View File

@@ -195,15 +195,15 @@ class ChangeNotesParser implements AutoCloseable {
Timestamp ts = Timestamp ts =
new Timestamp(commit.getCommitterIdent().getWhen().getTime()); new Timestamp(commit.getCommitterIdent().getWhen().getTime());
boolean updateTs = commit.getParentCount() == 0;
createdOn = ts; createdOn = ts;
if (lastUpdatedOn == null) {
lastUpdatedOn = ts;
}
if (branch == null) { if (branch == null) {
branch = parseBranch(commit); branch = parseBranch(commit);
updateTs |= branch != null;
} }
if (status == null) { if (status == null) {
status = parseStatus(commit); status = parseStatus(commit);
updateTs |= status != null;
} }
PatchSet.Id psId = parsePatchSetId(commit); PatchSet.Id psId = parsePatchSetId(commit);
@@ -221,6 +221,7 @@ class ChangeNotesParser implements AutoCloseable {
if (changeId == null) { if (changeId == null) {
changeId = parseChangeId(commit); changeId = parseChangeId(commit);
updateTs |= changeId != null;
} }
String currSubject = parseSubject(commit); String currSubject = parseSubject(commit);
@@ -229,21 +230,28 @@ class ChangeNotesParser implements AutoCloseable {
subject = currSubject; subject = currSubject;
} }
originalSubject = currSubject; originalSubject = currSubject;
updateTs = true;
} }
parseChangeMessage(psId, accountId, commit, ts); updateTs |= parseChangeMessage(psId, accountId, commit, ts) != null;
if (topic == null) { if (topic == null) {
topic = parseTopic(commit); topic = parseTopic(commit);
updateTs |= topic != null;
} }
Set<String> oldHashtags = hashtags;
parseHashtags(commit); parseHashtags(commit);
updateTs |= hashtags != oldHashtags;
if (submissionId == null) { if (submissionId == null) {
submissionId = parseSubmissionId(commit); submissionId = parseSubmissionId(commit);
updateTs |= submissionId != null;
} }
ObjectId currRev = parseRevision(commit); ObjectId currRev = parseRevision(commit);
if (currRev != null) { if (currRev != null) {
parsePatchSet(psId, currRev, accountId, ts); parsePatchSet(psId, currRev, accountId, ts);
updateTs = true;
} }
parseGroups(psId, commit); parseGroups(psId, commit);
@@ -251,16 +259,24 @@ class ChangeNotesParser implements AutoCloseable {
// Only parse the most recent set of submit records; any older ones are // Only parse the most recent set of submit records; any older ones are
// still there, but not currently used. // still there, but not currently used.
parseSubmitRecords(commit.getFooterLines(FOOTER_SUBMITTED_WITH)); parseSubmitRecords(commit.getFooterLines(FOOTER_SUBMITTED_WITH));
updateTs |= !submitRecords.isEmpty();
} }
for (String line : commit.getFooterLines(FOOTER_LABEL)) { for (String line : commit.getFooterLines(FOOTER_LABEL)) {
parseApproval(psId, accountId, ts, line); parseApproval(psId, accountId, ts, line);
updateTs = true;
} }
for (ReviewerStateInternal state : ReviewerStateInternal.values()) { for (ReviewerStateInternal state : ReviewerStateInternal.values()) {
for (String line : commit.getFooterLines(state.getFooterKey())) { for (String line : commit.getFooterLines(state.getFooterKey())) {
parseReviewer(state, line); parseReviewer(state, line);
} }
// Don't update timestamp when a reviewer was added, matching RevewDb
// behavior.
}
if (lastUpdatedOn == null && updateTs) {
lastUpdatedOn = ts;
} }
} }
@@ -418,20 +434,20 @@ class ChangeNotesParser implements AutoCloseable {
throw invalidFooter(FOOTER_PATCH_SET, psIdLine); throw invalidFooter(FOOTER_PATCH_SET, psIdLine);
} }
private void parseChangeMessage(PatchSet.Id psId, Account.Id accountId, private ChangeMessage parseChangeMessage(PatchSet.Id psId,
RevCommit commit, Timestamp ts) { Account.Id accountId, RevCommit commit, Timestamp ts) {
byte[] raw = commit.getRawBuffer(); byte[] raw = commit.getRawBuffer();
int size = raw.length; int size = raw.length;
Charset enc = RawParseUtils.parseEncoding(raw); Charset enc = RawParseUtils.parseEncoding(raw);
int subjectStart = RawParseUtils.commitMessage(raw, 0); int subjectStart = RawParseUtils.commitMessage(raw, 0);
if (subjectStart < 0 || subjectStart >= size) { if (subjectStart < 0 || subjectStart >= size) {
return; return null;
} }
int subjectEnd = RawParseUtils.endOfParagraph(raw, subjectStart); int subjectEnd = RawParseUtils.endOfParagraph(raw, subjectStart);
if (subjectEnd == size) { if (subjectEnd == size) {
return; return null;
} }
int changeMessageStart; int changeMessageStart;
@@ -441,7 +457,7 @@ class ChangeNotesParser implements AutoCloseable {
} else if (raw[subjectEnd] == '\r') { } else if (raw[subjectEnd] == '\r') {
changeMessageStart = subjectEnd + 4; //\r\n\r\n ends paragraph changeMessageStart = subjectEnd + 4; //\r\n\r\n ends paragraph
} else { } else {
return; return null;
} }
int ptr = size - 1; int ptr = size - 1;
@@ -461,7 +477,7 @@ class ChangeNotesParser implements AutoCloseable {
} }
if (ptr <= changeMessageStart) { if (ptr <= changeMessageStart) {
return; return null;
} }
String changeMsgString = RawParseUtils.decode(enc, raw, String changeMsgString = RawParseUtils.decode(enc, raw,
@@ -474,6 +490,7 @@ class ChangeNotesParser implements AutoCloseable {
changeMessage.setMessage(changeMsgString); changeMessage.setMessage(changeMsgString);
changeMessagesByPatchSet.put(psId, changeMessage); changeMessagesByPatchSet.put(psId, changeMessage);
allChangeMessages.add(changeMessage); allChangeMessages.add(changeMessage);
return changeMessage;
} }
private void parseNotes() private void parseNotes()

View File

@@ -618,6 +618,13 @@ public class ChangeData {
public ChangeControl changeControl(CurrentUser user) throws OrmException { public ChangeControl changeControl(CurrentUser user) throws OrmException {
if (changeControl != null) { if (changeControl != null) {
CurrentUser oldUser = user;
// TODO(dborowitz): This is a hack; general CurrentUser equality would be
// better.
if (user.isIdentifiedUser() && oldUser.isIdentifiedUser()
&& user.getAccountId().equals(oldUser.getAccountId())) {
return changeControl;
}
throw new IllegalStateException( throw new IllegalStateException(
"user already specified: " + changeControl.getUser()); "user already specified: " + changeControl.getUser());
} }

View File

@@ -26,6 +26,7 @@ import com.google.common.base.Function;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap; import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.ListMultimap; import com.google.common.collect.ListMultimap;
@@ -571,16 +572,70 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
Change c = newChange(); Change c = newChange();
ChangeNotes notes = newNotes(c); ChangeNotes notes = newNotes(c);
Timestamp lastUpdatedOn = notes.getChange().getLastUpdatedOn(); Timestamp ts1 = notes.getChange().getLastUpdatedOn();
assertThat(lastUpdatedOn).isNotNull(); assertThat(ts1).isEqualTo(notes.getChange().getCreatedOn());
assertThat(lastUpdatedOn).isEqualTo(notes.getChange().getCreatedOn());
// An update creates a new lastUpdatedOn timestamp. // Various kinds of updates that update the timestamp.
ChangeUpdate update = newUpdate(c, changeOwner); ChangeUpdate update = newUpdate(c, changeOwner);
update.setTopic("topic"); // Change something to get a new commit. update.setTopic("topic"); // Change something to get a new commit.
update.commit(); update.commit();
assertThat(newNotes(c).getChange().getLastUpdatedOn()) Timestamp ts2 = newNotes(c).getChange().getLastUpdatedOn();
.isGreaterThan(lastUpdatedOn); assertThat(ts2).isGreaterThan(ts1);
update = newUpdate(c, changeOwner);
update.setChangeMessage("Some message");
update.commit();
Timestamp ts3 = newNotes(c).getChange().getLastUpdatedOn();
assertThat(ts3).isGreaterThan(ts2);
update = newUpdate(c, changeOwner);
update.setHashtags(ImmutableSet.of("foo"));
update.commit();
Timestamp ts4 = newNotes(c).getChange().getLastUpdatedOn();
assertThat(ts4).isGreaterThan(ts3);
incrementPatchSet(c);
RevCommit commit = tr.commit().message("PS2").create();
update = newUpdate(c, changeOwner);
update.setCommit(rw, commit);
update.commit();
Timestamp ts5 = newNotes(c).getChange().getLastUpdatedOn();
assertThat(ts5).isGreaterThan(ts4);
update = newUpdate(c, changeOwner);
update.putApproval("Code-Review", (short) 1);
update.commit();
Timestamp ts6 = newNotes(c).getChange().getLastUpdatedOn();
assertThat(ts6).isGreaterThan(ts5);
update = newUpdate(c, changeOwner);
update.setStatus(Change.Status.ABANDONED);
update.commit();
Timestamp ts7 = newNotes(c).getChange().getLastUpdatedOn();
assertThat(ts7).isGreaterThan(ts6);
// Updates that should not touch the timestamp.
update = newUpdate(c, changeOwner);
update.putReviewer(otherUser.getAccountId(), ReviewerStateInternal.REVIEWER);
update.commit();
Timestamp ts8 = newNotes(c).getChange().getLastUpdatedOn();
assertThat(ts8).isEqualTo(ts7);
update = newUpdate(c, changeOwner);
update.setGroups(ImmutableList.of("a", "b"));
update.commit();
Timestamp ts9 = newNotes(c).getChange().getLastUpdatedOn();
assertThat(ts9).isEqualTo(ts8);
// Finish off by merging the change.
update = newUpdate(c, changeOwner);
update.merge("1-1453387607626-96fabc25", ImmutableList.of(
submitRecord("NOT_READY", null,
submitLabel("Verified", "OK", changeOwner.getAccountId()),
submitLabel("Alternative-Code-Review", "NEED", null))));
update.commit();
Timestamp ts10 = newNotes(c).getChange().getLastUpdatedOn();
assertThat(ts10).isGreaterThan(ts9);
} }
@Test @Test

View File

@@ -84,6 +84,7 @@ import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ObjectInserter; import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.util.SystemReader;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Ignore; import org.junit.Ignore;
@@ -93,6 +94,7 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit;
@Ignore @Ignore
public abstract class AbstractQueryChangesTest extends GerritServerTests { public abstract class AbstractQueryChangesTest extends GerritServerTests {
@@ -179,8 +181,15 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
@Before @Before
public void setTimeForTesting() { public void setTimeForTesting() {
resetTimeWithClockStep(1, MILLISECONDS);
}
private void resetTimeWithClockStep(long clockStep, TimeUnit clockStepUnit) {
systemTimeZone = System.setProperty("user.timezone", "US/Eastern"); systemTimeZone = System.setProperty("user.timezone", "US/Eastern");
TestTimeUtil.resetWithClockStep(1, MILLISECONDS); // TODO(dborowitz): Figure out why tests fail when stubbing out
// SystemReader.
TestTimeUtil.resetWithClockStep(clockStep, clockStepUnit);
SystemReader.setInstance(null);
} }
@After @After
@@ -676,7 +685,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
@Test @Test
public void updateOrder() throws Exception { public void updateOrder() throws Exception {
TestTimeUtil.resetWithClockStep(2, MINUTES); resetTimeWithClockStep(2, MINUTES);
TestRepository<Repo> repo = createProject("repo"); TestRepository<Repo> repo = createProject("repo");
List<ChangeInserter> inserters = Lists.newArrayList(); List<ChangeInserter> inserters = Lists.newArrayList();
List<Change> changes = Lists.newArrayList(); List<Change> changes = Lists.newArrayList();
@@ -701,7 +710,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
@Test @Test
public void updatedOrderWithMinuteResolution() throws Exception { public void updatedOrderWithMinuteResolution() throws Exception {
TestTimeUtil.resetWithClockStep(2, MINUTES); resetTimeWithClockStep(2, MINUTES);
TestRepository<Repo> repo = createProject("repo"); TestRepository<Repo> repo = createProject("repo");
ChangeInserter ins1 = newChange(repo); ChangeInserter ins1 = newChange(repo);
Change change1 = insert(repo, ins1); Change change1 = insert(repo, ins1);
@@ -710,8 +719,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
assertThat(lastUpdatedMs(change1)).isLessThan(lastUpdatedMs(change2)); assertThat(lastUpdatedMs(change1)).isLessThan(lastUpdatedMs(change2));
assertQuery("status:new", change2, change1); assertQuery("status:new", change2, change1);
gApi.changes().id(change1.getId().get()).current() gApi.changes().id(change1.getId().get()).topic("new-topic");
.review(new ReviewInput());
change1 = notesFactory.create(db, change1.getProject(), change1.getId()) change1 = notesFactory.create(db, change1.getProject(), change1.getId())
.getChange(); .getChange();
@@ -725,7 +733,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
@Test @Test
public void updatedOrderWithSubMinuteResolution() throws Exception { public void updatedOrderWithSubMinuteResolution() throws Exception {
TestTimeUtil.resetWithClockStep(1, SECONDS); resetTimeWithClockStep(1, SECONDS);
TestRepository<Repo> repo = createProject("repo"); TestRepository<Repo> repo = createProject("repo");
ChangeInserter ins1 = newChange(repo); ChangeInserter ins1 = newChange(repo);
@@ -736,8 +744,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
assertQuery("status:new", change2, change1); assertQuery("status:new", change2, change1);
gApi.changes().id(change1.getId().get()).current() gApi.changes().id(change1.getId().get()).topic("new-topic");
.review(new ReviewInput());
change1 = notesFactory.create(db, change1.getProject(), change1.getId()) change1 = notesFactory.create(db, change1.getProject(), change1.getId())
.getChange(); .getChange();
@@ -860,7 +867,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
@Test @Test
public void byAge() throws Exception { public void byAge() throws Exception {
long thirtyHoursInMs = MILLISECONDS.convert(30, HOURS); long thirtyHoursInMs = MILLISECONDS.convert(30, HOURS);
TestTimeUtil.resetWithClockStep(thirtyHoursInMs, MILLISECONDS); resetTimeWithClockStep(thirtyHoursInMs, MILLISECONDS);
TestRepository<Repo> repo = createProject("repo"); TestRepository<Repo> repo = createProject("repo");
Change change1 = insert(repo, newChange(repo)); Change change1 = insert(repo, newChange(repo));
Change change2 = insert(repo, newChange(repo)); Change change2 = insert(repo, newChange(repo));
@@ -883,7 +890,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
@Test @Test
public void byBefore() throws Exception { public void byBefore() throws Exception {
TestTimeUtil.resetWithClockStep(30, HOURS); resetTimeWithClockStep(30, HOURS);
TestRepository<Repo> repo = createProject("repo"); TestRepository<Repo> repo = createProject("repo");
Change change1 = insert(repo, newChange(repo)); Change change1 = insert(repo, newChange(repo));
Change change2 = insert(repo, newChange(repo)); Change change2 = insert(repo, newChange(repo));
@@ -903,7 +910,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
@Test @Test
public void byAfter() throws Exception { public void byAfter() throws Exception {
TestTimeUtil.resetWithClockStep(30, HOURS); resetTimeWithClockStep(30, HOURS);
TestRepository<Repo> repo = createProject("repo"); TestRepository<Repo> repo = createProject("repo");
Change change1 = insert(repo, newChange(repo)); Change change1 = insert(repo, newChange(repo));
Change change2 = insert(repo, newChange(repo)); Change change2 = insert(repo, newChange(repo));
@@ -1200,7 +1207,7 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
@Test @Test
public void reviewedBy() throws Exception { public void reviewedBy() throws Exception {
TestTimeUtil.resetWithClockStep(2, MINUTES); resetTimeWithClockStep(2, MINUTES);
TestRepository<Repo> repo = createProject("repo"); TestRepository<Repo> repo = createProject("repo");
Change change1 = insert(repo, newChange(repo)); Change change1 = insert(repo, newChange(repo));
Change change2 = insert(repo, newChange(repo)); Change change2 = insert(repo, newChange(repo));

View File

@@ -17,6 +17,10 @@ package com.google.gerrit.testutil;
import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Preconditions.checkState;
import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.MILLISECONDS;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.storage.file.FileBasedConfig;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.SystemReader;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.joda.time.DateTimeUtils; import org.joda.time.DateTimeUtils;
import org.joda.time.DateTimeUtils.MillisProvider; import org.joda.time.DateTimeUtils.MillisProvider;
@@ -63,11 +67,57 @@ public class TestTimeUtil {
return clockMs.getAndAdd(clockStepMs); return clockMs.getAndAdd(clockStepMs);
} }
}); });
SystemReader.setInstance(null);
final SystemReader defaultReader = SystemReader.getInstance();
SystemReader r = new SystemReader() {
@Override
public String getHostname() {
return defaultReader.getHostname();
}
@Override
public String getenv(String variable) {
return defaultReader.getenv(variable);
}
@Override
public String getProperty(String key) {
return defaultReader.getProperty(key);
}
@Override
public FileBasedConfig openUserConfig(Config parent, FS fs) {
return defaultReader.openUserConfig(parent, fs);
}
@Override
public FileBasedConfig openSystemConfig(Config parent, FS fs) {
return defaultReader.openSystemConfig(parent, fs);
}
@Override
public long getCurrentTime() {
return clockMs.getAndAdd(clockStepMs);
}
@Override
public int getTimezone(long when) {
return defaultReader.getTimezone(when);
}
};
SystemReader.setInstance(r);
} }
/** Reset the clock to use the actual system clock. */ /**
* Reset the clock to use the actual system clock.
* <p>
* As a side effect, resets the {@link SystemReader} to the original default
* instance.
*/
public static synchronized void useSystemTime() { public static synchronized void useSystemTime() {
DateTimeUtils.setCurrentMillisSystem(); DateTimeUtils.setCurrentMillisSystem();
SystemReader.setInstance(null);
} }
private TestTimeUtil() { private TestTimeUtil() {