ConsistencyChecker fix to delete patch sets with missing objects

After I0594aef6 it is possible for these to result from the merge
queue; even before that, manual ref deletion by someone with raw
filesystem access was a possibility.

Change-Id: I20dba162bffddf0d994b1ec11dcad3d09016d2c9
This commit is contained in:
Dave Borowitz
2015-01-29 18:03:24 -08:00
parent c3bec5d6b3
commit be49bb08e5
5 changed files with 264 additions and 11 deletions

View File

@@ -32,6 +32,8 @@ 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.InternalUser;
import com.google.gerrit.server.patch.PatchSetInfoFactory;
import com.google.gerrit.testutil.FakeAccountByEmailCache;
import com.google.gerrit.testutil.InMemoryDatabase;
import com.google.gerrit.testutil.InMemoryRepositoryManager;
import com.google.gerrit.testutil.TestChanges;
@@ -62,6 +64,7 @@ public class ConsistencyCheckerTest {
@Before
public void setUp() throws Exception {
FakeAccountByEmailCache accountCache = new FakeAccountByEmailCache();
schemaFactory = InMemoryDatabase.newDatabase();
schemaFactory.create();
db = schemaFactory.open();
@@ -70,10 +73,12 @@ public class ConsistencyCheckerTest {
Providers.<ReviewDb> of(db),
repoManager,
Providers.<CurrentUser> of(new InternalUser(null)),
Providers.of(new PersonIdent("server", "noreply@example.com")));
Providers.of(new PersonIdent("server", "noreply@example.com")),
new PatchSetInfoFactory(repoManager, accountCache));
project = new Project.NameKey("repo");
repo = new TestRepository<>(repoManager.createRepository(project));
userId = new Account.Id(1);
accountCache.putAny(userId);
db.accounts().insert(singleton(new Account(userId, TimeUtil.nowTs())));
tip = repo.branch("master").commit().create();
}
@@ -206,6 +211,109 @@ public class ConsistencyCheckerTest {
.isEqualTo(ps.getRevision().get());
}
@Test
public void patchSetObjectAndRefMissingWithDeletingPatchSet()
throws Exception {
Change c = insertChange();
PatchSet ps1 = insertPatchSet(c);
incrementPatchSet(c);
PatchSet ps2 = insertMissingPatchSet(c,
"deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
FixInput fix = new FixInput();
fix.deletePatchSetIfCommitMissing = true;
List<ProblemInfo> problems = checker.check(c, fix).problems();
assertThat(problems).hasSize(2);
ProblemInfo p = problems.get(0);
assertThat(p.message).isEqualTo("Ref missing: " + ps2.getId().toRefName());
assertThat(p.status).isNull();
p = problems.get(1);
assertThat(p.message).isEqualTo(
"Object missing: patch set 2: deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
assertThat(p.status).isEqualTo(ProblemInfo.Status.FIXED);
assertThat(p.outcome).isEqualTo("Deleted patch set");
c = db.changes().get(c.getId());
assertThat(c.currentPatchSetId().get()).isEqualTo(1);
assertThat(db.patchSets().get(ps1.getId())).isNotNull();
assertThat(db.patchSets().get(ps2.getId())).isNull();
}
@Test
public void patchSetMultipleObjectsMissingWithDeletingPatchSets()
throws Exception {
Change c = insertChange();
PatchSet ps1 = insertPatchSet(c);
incrementPatchSet(c);
PatchSet ps2 = insertMissingPatchSet(c,
"deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
incrementPatchSet(c);
PatchSet ps3 = insertPatchSet(c);
incrementPatchSet(c);
PatchSet ps4 = insertMissingPatchSet(c,
"c0ffeeeec0ffeeeec0ffeeeec0ffeeeec0ffeeee");
FixInput fix = new FixInput();
fix.deletePatchSetIfCommitMissing = true;
List<ProblemInfo> problems = checker.check(c, fix).problems();
assertThat(problems).hasSize(4);
ProblemInfo p = problems.get(0);
assertThat(p.message).isEqualTo("Ref missing: " + ps4.getId().toRefName());
assertThat(p.status).isNull();
p = problems.get(1);
assertThat(p.message).isEqualTo(
"Object missing: patch set 4: c0ffeeeec0ffeeeec0ffeeeec0ffeeeec0ffeeee");
assertThat(p.status).isEqualTo(ProblemInfo.Status.FIXED);
assertThat(p.outcome).isEqualTo("Deleted patch set");
p = problems.get(2);
assertThat(p.message).isEqualTo("Ref missing: " + ps2.getId().toRefName());
assertThat(p.status).isNull();
p = problems.get(3);
assertThat(p.message).isEqualTo(
"Object missing: patch set 2: deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
assertThat(p.status).isEqualTo(ProblemInfo.Status.FIXED);
assertThat(p.outcome).isEqualTo("Deleted patch set");
c = db.changes().get(c.getId());
assertThat(c.currentPatchSetId().get()).isEqualTo(3);
assertThat(db.patchSets().get(ps1.getId())).isNotNull();
assertThat(db.patchSets().get(ps2.getId())).isNull();
assertThat(db.patchSets().get(ps3.getId())).isNotNull();
assertThat(db.patchSets().get(ps4.getId())).isNull();
}
@Test
public void onlyPatchSetObjectMissingWithFix() throws Exception {
Change c = insertChange();
PatchSet ps1 = insertMissingPatchSet(c,
"deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
FixInput fix = new FixInput();
fix.deletePatchSetIfCommitMissing = true;
List<ProblemInfo> problems = checker.check(c, fix).problems();
assertThat(problems).hasSize(2);
ProblemInfo p = problems.get(0);
assertThat(p.message).isEqualTo("Ref missing: " + ps1.getId().toRefName());
assertThat(p.status).isNull();
p = problems.get(1);
assertThat(p.message).isEqualTo(
"Object missing: patch set 1: deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
assertThat(p.status).isEqualTo(ProblemInfo.Status.FIX_FAILED);
assertThat(p.outcome)
.isEqualTo("Cannot delete patch set; no patch sets would remain");
c = db.changes().get(c.getId());
assertThat(c.currentPatchSetId().get()).isEqualTo(1);
assertThat(db.patchSets().get(ps1.getId())).isNotNull();
}
@Test
public void currentPatchSetMissing() throws Exception {
Change c = insertChange();

View File

@@ -0,0 +1,55 @@
// Copyright (C) 2015 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.testutil;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.SetMultimap;
import com.google.common.collect.Sets;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.account.AccountByEmailCache;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/** Fake implementation of {@link AccountByEmailCache} for testing. */
public class FakeAccountByEmailCache implements AccountByEmailCache {
private final SetMultimap<String, Account.Id> byEmail;
private final Set<Account.Id> anyEmail;
public FakeAccountByEmailCache() {
byEmail = HashMultimap.create();
anyEmail = new HashSet<>();
}
@Override
public synchronized Set<Account.Id> get(String email) {
return Collections.unmodifiableSet(
Sets.union(byEmail.get(email), anyEmail));
}
@Override
public synchronized void evict(String email) {
// Do nothing.
}
public synchronized void put(String email, Account.Id id) {
byEmail.put(email, id);
}
public synchronized void putAny(Account.Id id) {
anyEmail.add(id);
}
}