diff options
Diffstat (limited to 'gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/notedb/OnlineNoteDbMigrationIT.java')
-rw-r--r-- | gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/notedb/OnlineNoteDbMigrationIT.java | 675 |
1 files changed, 0 insertions, 675 deletions
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/notedb/OnlineNoteDbMigrationIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/notedb/OnlineNoteDbMigrationIT.java deleted file mode 100644 index 135809082f..0000000000 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/notedb/OnlineNoteDbMigrationIT.java +++ /dev/null @@ -1,675 +0,0 @@ -// Copyright (C) 2017 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.acceptance.server.notedb; - -import static com.google.common.truth.Truth.assertThat; -import static com.google.common.truth.Truth.assert_; -import static com.google.common.truth.Truth8.assertThat; -import static com.google.common.truth.TruthJUnit.assume; -import static com.google.gerrit.server.notedb.NoteDbChangeState.NOTE_DB_PRIMARY_STATE; -import static com.google.gerrit.server.notedb.NotesMigrationState.NOTE_DB; -import static com.google.gerrit.server.notedb.NotesMigrationState.READ_WRITE_NO_SEQUENCE; -import static com.google.gerrit.server.notedb.NotesMigrationState.READ_WRITE_WITH_SEQUENCE_NOTE_DB_PRIMARY; -import static com.google.gerrit.server.notedb.NotesMigrationState.READ_WRITE_WITH_SEQUENCE_REVIEW_DB_PRIMARY; -import static com.google.gerrit.server.notedb.NotesMigrationState.REVIEW_DB; -import static com.google.gerrit.server.notedb.NotesMigrationState.WRITE; -import static java.nio.charset.StandardCharsets.UTF_8; -import static org.easymock.EasyMock.createStrictMock; -import static org.easymock.EasyMock.expectLastCall; -import static org.easymock.EasyMock.replay; -import static org.easymock.EasyMock.verify; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.gerrit.acceptance.AbstractDaemonTest; -import com.google.gerrit.acceptance.GerritConfig; -import com.google.gerrit.acceptance.NoHttpd; -import com.google.gerrit.acceptance.PushOneCommit; -import com.google.gerrit.acceptance.Sandboxed; -import com.google.gerrit.acceptance.UseLocalDisk; -import com.google.gerrit.extensions.registration.DynamicSet; -import com.google.gerrit.extensions.registration.RegistrationHandle; -import com.google.gerrit.reviewdb.client.Change; -import com.google.gerrit.reviewdb.client.PatchSet; -import com.google.gerrit.reviewdb.client.Project; -import com.google.gerrit.reviewdb.client.RefNames; -import com.google.gerrit.reviewdb.server.ReviewDb; -import com.google.gerrit.server.CommentsUtil; -import com.google.gerrit.server.Sequences; -import com.google.gerrit.server.config.SitePaths; -import com.google.gerrit.server.notedb.ChangeBundle; -import com.google.gerrit.server.notedb.ChangeBundleReader; -import com.google.gerrit.server.notedb.NoteDbChangeState; -import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage; -import com.google.gerrit.server.notedb.NoteDbChangeState.RefState; -import com.google.gerrit.server.notedb.NotesMigrationState; -import com.google.gerrit.server.notedb.rebuild.MigrationException; -import com.google.gerrit.server.notedb.rebuild.NoteDbMigrator; -import com.google.gerrit.server.notedb.rebuild.NotesMigrationStateListener; -import com.google.gerrit.server.schema.ReviewDbFactory; -import com.google.gerrit.testutil.ConfigSuite; -import com.google.gerrit.testutil.NoteDbMode; -import com.google.gwtorm.server.OrmException; -import com.google.gwtorm.server.SchemaFactory; -import com.google.inject.Inject; -import com.google.inject.Provider; -import java.io.IOException; -import java.nio.file.FileVisitResult; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.SimpleFileVisitor; -import java.nio.file.attribute.BasicFileAttributes; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.SortedSet; -import java.util.TreeSet; -import org.eclipse.jgit.internal.storage.file.FileRepository; -import org.eclipse.jgit.junit.TestRepository; -import org.eclipse.jgit.lib.Config; -import org.eclipse.jgit.lib.Constants; -import org.eclipse.jgit.lib.ObjectId; -import org.eclipse.jgit.lib.ObjectLoader; -import org.eclipse.jgit.lib.ObjectReader; -import org.eclipse.jgit.lib.Ref; -import org.eclipse.jgit.lib.RefUpdate; -import org.eclipse.jgit.lib.Repository; -import org.eclipse.jgit.storage.file.FileBasedConfig; -import org.eclipse.jgit.util.FS; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -@Sandboxed -@UseLocalDisk -@NoHttpd -public class OnlineNoteDbMigrationIT extends AbstractDaemonTest { - private static final String INVALID_STATE = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"; - - @ConfigSuite.Default - public static Config defaultConfig() { - Config cfg = new Config(); - cfg.setInt("noteDb", "changes", "sequenceBatchSize", 10); - cfg.setInt("noteDb", "changes", "initialSequenceGap", 500); - return cfg; - } - - // Tests in this class are generally interested in the actual ReviewDb contents, but the shifting - // migration state may result in various kinds of wrappers showing up unexpectedly. - @Inject @ReviewDbFactory private SchemaFactory<ReviewDb> schemaFactory; - - @Inject private ChangeBundleReader changeBundleReader; - @Inject private CommentsUtil commentsUtil; - @Inject private DynamicSet<NotesMigrationStateListener> listeners; - @Inject private Provider<NoteDbMigrator.Builder> migratorBuilderProvider; - @Inject private Sequences sequences; - @Inject private SitePaths sitePaths; - - private FileBasedConfig noteDbConfig; - private List<RegistrationHandle> addedListeners; - - @Before - public void setUp() throws Exception { - assume().that(NoteDbMode.get()).isEqualTo(NoteDbMode.OFF); - // Unlike in the running server, for tests, we don't stack notedb.config on gerrit.config. - noteDbConfig = new FileBasedConfig(sitePaths.notedb_config.toFile(), FS.detect()); - assertNotesMigrationState(REVIEW_DB, false, false); - addedListeners = new ArrayList<>(); - } - - @After - public void tearDown() throws Exception { - if (addedListeners != null) { - addedListeners.forEach(RegistrationHandle::remove); - addedListeners = null; - } - } - - @Test - public void preconditionsFail() throws Exception { - List<Change.Id> cs = ImmutableList.of(new Change.Id(1)); - List<Project.NameKey> ps = ImmutableList.of(new Project.NameKey("p")); - assertMigrationException( - "Cannot rebuild without noteDb.changes.write=true", b -> b, NoteDbMigrator::rebuild); - assertMigrationException( - "Cannot combine changes, projects and skipProjects", - b -> b.setChanges(cs).setProjects(ps), - m -> {}); - assertMigrationException( - "Cannot combine changes, projects and skipProjects", - b -> b.setChanges(cs).setSkipProjects(ps), - m -> {}); - assertMigrationException( - "Cannot combine changes, projects and skipProjects", - b -> b.setProjects(ps).setSkipProjects(ps), - m -> {}); - assertMigrationException( - "Cannot set changes or projects or skipProjects during full migration", - b -> b.setChanges(cs), - NoteDbMigrator::migrate); - assertMigrationException( - "Cannot set changes or projects or skipProjects during full migration", - b -> b.setProjects(ps), - NoteDbMigrator::migrate); - assertMigrationException( - "Cannot set changes or projects or skipProjects during full migration", - b -> b.setSkipProjects(ps), - NoteDbMigrator::migrate); - - setNotesMigrationState(READ_WRITE_WITH_SEQUENCE_REVIEW_DB_PRIMARY); - assertMigrationException( - "Migration has already progressed past the endpoint of the \"trial mode\" state", - b -> b.setTrialMode(true), - NoteDbMigrator::migrate); - - setNotesMigrationState(READ_WRITE_WITH_SEQUENCE_NOTE_DB_PRIMARY); - assertMigrationException( - "Cannot force rebuild changes; NoteDb is already the primary storage for some changes", - b -> b.setForceRebuild(true), - NoteDbMigrator::migrate); - } - - @Test - @GerritConfig(name = "noteDb.changes.initialSequenceGap", value = "-7") - public void initialSequenceGapMustBeNonNegative() throws Exception { - setNotesMigrationState(READ_WRITE_NO_SEQUENCE); - assertMigrationException("Sequence gap must be non-negative: -7", b -> b, m -> {}); - } - - @Test - public void rebuildOneChangeTrialModeAndForceRebuild() throws Exception { - PushOneCommit.Result r = createChange(); - Change.Id id = r.getChange().getId(); - - migrate(b -> b.setTrialMode(true)); - assertNotesMigrationState(READ_WRITE_NO_SEQUENCE, false, true); - - ObjectId oldMetaId; - try (Repository repo = repoManager.openRepository(project); - ReviewDb db = schemaFactory.open()) { - Ref ref = repo.exactRef(RefNames.changeMetaRef(id)); - assertThat(ref).isNotNull(); - oldMetaId = ref.getObjectId(); - - Change c = db.changes().get(id); - assertThat(c).isNotNull(); - NoteDbChangeState state = NoteDbChangeState.parse(c); - assertThat(state).isNotNull(); - assertThat(state.getPrimaryStorage()).isEqualTo(PrimaryStorage.REVIEW_DB); - assertThat(state.getRefState()).hasValue(RefState.create(oldMetaId, ImmutableMap.of())); - - // Force change to be out of date, and change topic so it will get rebuilt as something other - // than oldMetaId. - c.setNoteDbState(INVALID_STATE); - c.setTopic(name("a-new-topic")); - db.changes().update(ImmutableList.of(c)); - } - - migrate(b -> b.setTrialMode(true)); - assertNotesMigrationState(READ_WRITE_NO_SEQUENCE, false, true); - - try (Repository repo = repoManager.openRepository(project); - ReviewDb db = schemaFactory.open()) { - // Change is out of date, but was not rebuilt without forceRebuild. - assertThat(repo.exactRef(RefNames.changeMetaRef(id)).getObjectId()).isEqualTo(oldMetaId); - Change c = db.changes().get(id); - assertThat(c.getNoteDbState()).isEqualTo(INVALID_STATE); - } - - migrate(b -> b.setTrialMode(true).setForceRebuild(true)); - assertNotesMigrationState(READ_WRITE_NO_SEQUENCE, false, true); - - try (Repository repo = repoManager.openRepository(project); - ReviewDb db = schemaFactory.open()) { - Ref ref = repo.exactRef(RefNames.changeMetaRef(id)); - assertThat(ref).isNotNull(); - ObjectId newMetaId = ref.getObjectId(); - assertThat(newMetaId).isNotEqualTo(oldMetaId); - - NoteDbChangeState state = NoteDbChangeState.parse(db.changes().get(id)); - assertThat(state).isNotNull(); - assertThat(state.getPrimaryStorage()).isEqualTo(PrimaryStorage.REVIEW_DB); - assertThat(state.getRefState()).hasValue(RefState.create(newMetaId, ImmutableMap.of())); - } - } - - @Test - public void autoMigrateTrialMode() throws Exception { - PushOneCommit.Result r = createChange(); - Change.Id id = r.getChange().getId(); - - migrate(b -> b.setAutoMigrate(true).setTrialMode(true).setStopAtStateForTesting(WRITE)); - assertNotesMigrationState(WRITE, true, true); - - migrate(b -> b); - // autoMigrate is still enabled so that we can continue the migration by only unsetting trial. - assertNotesMigrationState(READ_WRITE_NO_SEQUENCE, true, true); - - ObjectId metaId; - try (Repository repo = repoManager.openRepository(project); - ReviewDb db = schemaFactory.open()) { - Ref ref = repo.exactRef(RefNames.changeMetaRef(id)); - assertThat(ref).isNotNull(); - metaId = ref.getObjectId(); - NoteDbChangeState state = NoteDbChangeState.parse(db.changes().get(id)); - assertThat(state).isNotNull(); - assertThat(state.getPrimaryStorage()).isEqualTo(PrimaryStorage.REVIEW_DB); - assertThat(state.getRefState()).hasValue(RefState.create(metaId, ImmutableMap.of())); - } - - // Unset trial mode and the next migration runs to completion. - noteDbConfig.load(); - NoteDbMigrator.setTrialMode(noteDbConfig, false); - noteDbConfig.save(); - - migrate(b -> b); - assertNotesMigrationState(NOTE_DB, false, false); - - try (Repository repo = repoManager.openRepository(project); - ReviewDb db = schemaFactory.open()) { - Ref ref = repo.exactRef(RefNames.changeMetaRef(id)); - assertThat(ref).isNotNull(); - assertThat(ref.getObjectId()).isEqualTo(metaId); - NoteDbChangeState state = NoteDbChangeState.parse(db.changes().get(id)); - assertThat(state).isNotNull(); - assertThat(state.getPrimaryStorage()).isEqualTo(PrimaryStorage.NOTE_DB); - } - } - - @Test - public void rebuildSubsetOfChanges() throws Exception { - setNotesMigrationState(WRITE); - - PushOneCommit.Result r1 = createChange(); - PushOneCommit.Result r2 = createChange(); - Change.Id id1 = r1.getChange().getId(); - Change.Id id2 = r2.getChange().getId(); - - invalidateNoteDbState(id1, id2); - migrate(b -> b.setChanges(ImmutableList.of(id2)), NoteDbMigrator::rebuild); - assertNotRebuilt(id1); - assertRebuilt(id2); - } - - @Test - public void rebuildSubsetOfProjects() throws Exception { - setNotesMigrationState(WRITE); - - Project.NameKey p2 = createProject("project2"); - TestRepository<?> tr2 = cloneProject(p2, admin); - - PushOneCommit.Result r1 = createChange(); - PushOneCommit.Result r2 = pushFactory.create(db, admin.getIdent(), tr2).to("refs/for/master"); - Change.Id id1 = r1.getChange().getId(); - Change.Id id2 = r2.getChange().getId(); - - invalidateNoteDbState(id1, id2); - migrate(b -> b.setProjects(ImmutableList.of(p2)), NoteDbMigrator::rebuild); - assertNotRebuilt(id1); - assertRebuilt(id2); - } - - @Test - public void rebuildNonSkippedProjects() throws Exception { - setNotesMigrationState(WRITE); - - Project.NameKey p2 = createProject("project2"); - TestRepository<?> tr2 = cloneProject(p2, admin); - Project.NameKey p3 = createProject("project3"); - TestRepository<?> tr3 = cloneProject(p3, admin); - - PushOneCommit.Result r1 = createChange(); - PushOneCommit.Result r2 = pushFactory.create(db, admin.getIdent(), tr2).to("refs/for/master"); - PushOneCommit.Result r3 = pushFactory.create(db, admin.getIdent(), tr3).to("refs/for/master"); - Change.Id id1 = r1.getChange().getId(); - Change.Id id2 = r2.getChange().getId(); - Change.Id id3 = r3.getChange().getId(); - - invalidateNoteDbState(id1, id2, id3); - migrate(b -> b.setSkipProjects(ImmutableList.of(p3)), NoteDbMigrator::rebuild); - assertRebuilt(id1, id2); - assertNotRebuilt(id3); - } - - private void invalidateNoteDbState(Change.Id... ids) throws OrmException { - List<Change> list = new ArrayList<>(ids.length); - try (ReviewDb db = schemaFactory.open()) { - for (Change.Id id : ids) { - Change c = db.changes().get(id); - c.setNoteDbState(INVALID_STATE); - list.add(c); - } - db.changes().update(list); - } - } - - private void assertRebuilt(Change.Id... ids) throws OrmException { - try (ReviewDb db = schemaFactory.open()) { - for (Change.Id id : ids) { - NoteDbChangeState s = NoteDbChangeState.parse(db.changes().get(id)); - assertThat(s.getChangeMetaId().name()).isNotEqualTo(INVALID_STATE); - } - } - } - - private void assertNotRebuilt(Change.Id... ids) throws OrmException { - try (ReviewDb db = schemaFactory.open()) { - for (Change.Id id : ids) { - NoteDbChangeState s = NoteDbChangeState.parse(db.changes().get(id)); - assertThat(s.getChangeMetaId().name()).isEqualTo(INVALID_STATE); - } - } - } - - @Test - public void enableSequencesNoGap() throws Exception { - testEnableSequences(0, 3, "13"); - } - - @Test - public void enableSequencesWithGap() throws Exception { - testEnableSequences(-1, 502, "512"); - } - - private void testEnableSequences(int builderOption, int expectedFirstId, String expectedRefValue) - throws Exception { - PushOneCommit.Result r = createChange(); - Change.Id id = r.getChange().getId(); - assertThat(id.get()).isEqualTo(1); - - migrate( - b -> - b.setSequenceGap(builderOption) - .setStopAtStateForTesting(READ_WRITE_WITH_SEQUENCE_REVIEW_DB_PRIMARY)); - - assertThat(sequences.nextChangeId()).isEqualTo(expectedFirstId); - assertThat(sequences.nextChangeId()).isEqualTo(expectedFirstId + 1); - - try (Repository repo = repoManager.openRepository(allProjects); - ObjectReader reader = repo.newObjectReader()) { - Ref ref = repo.exactRef("refs/sequences/changes"); - assertThat(ref).isNotNull(); - ObjectLoader loader = reader.open(ref.getObjectId()); - assertThat(loader.getType()).isEqualTo(Constants.OBJ_BLOB); - // Acquired a block of 10 to serve the first nextChangeId call after migration. - assertThat(new String(loader.getCachedBytes(), UTF_8)).isEqualTo(expectedRefValue); - } - - try (ReviewDb db = schemaFactory.open()) { - // Underlying, unused ReviewDb is still on its own sequence. - @SuppressWarnings("deprecation") - int nextFromReviewDb = db.nextChangeId(); - assertThat(nextFromReviewDb).isEqualTo(3); - } - } - - @Test - public void fullMigrationSameThread() throws Exception { - testFullMigration(1); - } - - @Test - public void fullMigrationMultipleThreads() throws Exception { - testFullMigration(2); - } - - private void testFullMigration(int threads) throws Exception { - PushOneCommit.Result r1 = createChange(); - PushOneCommit.Result r2 = createChange(); - Change.Id id1 = r1.getChange().getId(); - Change.Id id2 = r2.getChange().getId(); - - Set<String> objectFiles = getObjectFiles(project); - assertThat(objectFiles).isNotEmpty(); - - migrate(b -> b.setThreads(threads)); - - assertNotesMigrationState(NOTE_DB, false, false); - assertThat(sequences.nextChangeId()).isEqualTo(503); - assertThat(getObjectFiles(project)).containsExactlyElementsIn(objectFiles); - - ObjectId oldMetaId = null; - int rowVersion = 0; - try (ReviewDb db = schemaFactory.open(); - Repository repo = repoManager.openRepository(project)) { - for (Change.Id id : ImmutableList.of(id1, id2)) { - String refName = RefNames.changeMetaRef(id); - Ref ref = repo.exactRef(refName); - assertThat(ref).named(refName).isNotNull(); - - Change c = db.changes().get(id); - assertThat(c.getTopic()).named("topic of change %s", id).isNull(); - NoteDbChangeState s = NoteDbChangeState.parse(c); - assertThat(s.getPrimaryStorage()) - .named("primary storage of change %s", id) - .isEqualTo(PrimaryStorage.NOTE_DB); - assertThat(s.getRefState()).named("ref state of change %s").isEmpty(); - - if (id.equals(id1)) { - oldMetaId = ref.getObjectId(); - rowVersion = c.getRowVersion(); - } - } - } - - // Do not open a new context, to simulate races with other threads that opened a context earlier - // in the migration process; this needs to work. - gApi.changes().id(id1.get()).topic(name("a-topic")); - - // Of course, it should also work with a new context. - resetCurrentApiUser(); - gApi.changes().id(id1.get()).topic(name("another-topic")); - - try (ReviewDb db = schemaFactory.open(); - Repository repo = repoManager.openRepository(project)) { - assertThat(repo.exactRef(RefNames.changeMetaRef(id1)).getObjectId()).isNotEqualTo(oldMetaId); - - Change c = db.changes().get(id1); - assertThat(c.getTopic()).isNull(); - assertThat(c.getRowVersion()).isEqualTo(rowVersion); - } - } - - @Test - public void fullMigrationOneChangeWithNoPatchSets() throws Exception { - PushOneCommit.Result r1 = createChange(); - PushOneCommit.Result r2 = createChange(); - Change.Id id1 = r1.getChange().getId(); - Change.Id id2 = r2.getChange().getId(); - - db.changes().beginTransaction(id2); - try { - db.patchSets().delete(db.patchSets().byChange(id2)); - db.commit(); - } finally { - db.rollback(); - } - - migrate(b -> b); - assertNotesMigrationState(NOTE_DB, false, false); - - try (ReviewDb db = schemaFactory.open(); - Repository repo = repoManager.openRepository(project)) { - assertThat(repo.exactRef(RefNames.changeMetaRef(id1))).isNotNull(); - assertThat(db.changes().get(id1).getNoteDbState()).isEqualTo(NOTE_DB_PRIMARY_STATE); - - // A change with no patch sets is so corrupt that it is completely skipped by the migration - // process. - assertThat(repo.exactRef(RefNames.changeMetaRef(id2))).isNull(); - assertThat(db.changes().get(id2).getNoteDbState()).isNull(); - } - } - - @Test - public void fullMigrationMissingPatchSetRefs() throws Exception { - PushOneCommit.Result r = createChange(); - Change.Id id = r.getChange().getId(); - - try (Repository repo = repoManager.openRepository(project)) { - RefUpdate u = repo.updateRef(new PatchSet.Id(id, 1).toRefName()); - u.setForceUpdate(true); - assertThat(u.delete()).isEqualTo(RefUpdate.Result.FORCED); - } - - ChangeBundle reviewDbBundle; - try (ReviewDb db = schemaFactory.open()) { - reviewDbBundle = changeBundleReader.fromReviewDb(db, id); - } - - migrate(b -> b); - assertNotesMigrationState(NOTE_DB, false, false); - - try (ReviewDb db = schemaFactory.open(); - Repository repo = repoManager.openRepository(project)) { - // Change migrated successfully even though it was missing patch set refs. - assertThat(repo.exactRef(RefNames.changeMetaRef(id))).isNotNull(); - assertThat(db.changes().get(id).getNoteDbState()).isEqualTo(NOTE_DB_PRIMARY_STATE); - - ChangeBundle noteDbBundle = - ChangeBundle.fromNotes(commentsUtil, notesFactory.createChecked(db, project, id)); - assertThat(noteDbBundle.differencesFrom(reviewDbBundle)).isEmpty(); - } - } - - @Test - public void autoMigrationConfig() throws Exception { - createChange(); - - migrate(b -> b.setStopAtStateForTesting(WRITE)); - assertNotesMigrationState(WRITE, false, false); - - migrate(b -> b.setAutoMigrate(true).setStopAtStateForTesting(READ_WRITE_NO_SEQUENCE)); - assertNotesMigrationState(READ_WRITE_NO_SEQUENCE, true, false); - - migrate(b -> b); - assertNotesMigrationState(NOTE_DB, false, false); - } - - @Test - public void notesMigrationStateListener() throws Exception { - NotesMigrationStateListener listener = createStrictMock(NotesMigrationStateListener.class); - listener.preStateChange(REVIEW_DB, WRITE); - expectLastCall(); - listener.preStateChange(WRITE, READ_WRITE_NO_SEQUENCE); - expectLastCall(); - listener.preStateChange(READ_WRITE_NO_SEQUENCE, READ_WRITE_WITH_SEQUENCE_REVIEW_DB_PRIMARY); - expectLastCall(); - listener.preStateChange( - READ_WRITE_WITH_SEQUENCE_REVIEW_DB_PRIMARY, READ_WRITE_WITH_SEQUENCE_NOTE_DB_PRIMARY); - listener.preStateChange(READ_WRITE_WITH_SEQUENCE_NOTE_DB_PRIMARY, NOTE_DB); - expectLastCall(); - replay(listener); - addListener(listener); - - createChange(); - migrate(b -> b); - assertNotesMigrationState(NOTE_DB, false, false); - verify(listener); - } - - @Test - public void notesMigrationStateListenerFails() throws Exception { - NotesMigrationStateListener listener = createStrictMock(NotesMigrationStateListener.class); - listener.preStateChange(REVIEW_DB, WRITE); - expectLastCall(); - listener.preStateChange(WRITE, READ_WRITE_NO_SEQUENCE); - IOException listenerException = new IOException("Listener failed"); - expectLastCall().andThrow(listenerException); - replay(listener); - addListener(listener); - - createChange(); - try { - migrate(b -> b); - assert_().fail("expected IOException"); - } catch (IOException e) { - assertThat(e).isSameAs(listenerException); - } - assertNotesMigrationState(WRITE, false, false); - verify(listener); - } - - private void assertNotesMigrationState( - NotesMigrationState expected, boolean autoMigrate, boolean trialMode) throws Exception { - assertThat(NotesMigrationState.forNotesMigration(notesMigration)).hasValue(expected); - noteDbConfig.load(); - assertThat(NotesMigrationState.forConfig(noteDbConfig)).hasValue(expected); - assertThat(NoteDbMigrator.getAutoMigrate(noteDbConfig)) - .named("noteDb.changes.autoMigrate") - .isEqualTo(autoMigrate); - assertThat(NoteDbMigrator.getTrialMode(noteDbConfig)) - .named("noteDb.changes.trial") - .isEqualTo(trialMode); - } - - private void setNotesMigrationState(NotesMigrationState state) throws Exception { - noteDbConfig.load(); - state.setConfigValues(noteDbConfig); - noteDbConfig.save(); - notesMigration.setFrom(state); - } - - @FunctionalInterface - interface PrepareBuilder { - NoteDbMigrator.Builder prepare(NoteDbMigrator.Builder b) throws Exception; - } - - @FunctionalInterface - interface RunMigration { - void run(NoteDbMigrator m) throws Exception; - } - - private void migrate(PrepareBuilder b) throws Exception { - migrate(b, NoteDbMigrator::migrate); - } - - private void migrate(PrepareBuilder b, RunMigration m) throws Exception { - try (NoteDbMigrator migrator = b.prepare(migratorBuilderProvider.get()).build()) { - m.run(migrator); - } - } - - private void assertMigrationException( - String expectMessageContains, PrepareBuilder b, RunMigration m) throws Exception { - try { - migrate(b, m); - } catch (MigrationException e) { - assertThat(e).hasMessageThat().contains(expectMessageContains); - } - } - - private void addListener(NotesMigrationStateListener listener) { - addedListeners.add(listeners.add(listener)); - } - - private SortedSet<String> getObjectFiles(Project.NameKey project) throws Exception { - SortedSet<String> files = new TreeSet<>(); - try (Repository repo = repoManager.openRepository(project)) { - Files.walkFileTree( - ((FileRepository) repo).getObjectDatabase().getDirectory().toPath(), - new SimpleFileVisitor<Path>() { - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { - String name = file.getFileName().toString(); - if (!attrs.isDirectory() && !name.endsWith(".pack") && !name.endsWith(".idx")) { - files.add(name); - } - return FileVisitResult.CONTINUE; - } - }); - } - return files; - } -} |