instead of package migrations, so that it is located as if it was in sonar-db-migration
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.platform.db.migration;
+
+import java.util.concurrent.ExecutorService;
+
+/**
+ * Flag interface for the ExecutorService to be used by the {@link DatabaseMigrationImpl}
+ * component.
+ */
+public interface DatabaseMigrationExecutorService extends ExecutorService {
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.platform.db.migration;
+
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import org.sonar.server.util.AbstractStoppableExecutorService;
+
+import java.util.concurrent.Executors;
+
+/**
+ * Since only one DB migration can run at a time, this implementation of DatabaseMigrationExecutorService
+ * wraps a single thread executor from the JDK.
+ */
+public class DatabaseMigrationExecutorServiceImpl
+ extends AbstractStoppableExecutorService
+ implements DatabaseMigrationExecutorService {
+
+ public DatabaseMigrationExecutorServiceImpl() {
+ super(
+ Executors.newSingleThreadExecutor(
+ new ThreadFactoryBuilder()
+ .setDaemon(false)
+ .setNameFormat("DB_migration-%d")
+ .build()
+ ));
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.platform.db.migration;
+
+import java.util.Date;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.ReentrantLock;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.api.utils.log.Profiler;
+import org.sonar.server.platform.Platform;
+import org.sonar.server.ruby.RubyBridge;
+
+import static org.sonar.server.platform.db.migration.DatabaseMigrationState.Status;
+
+/**
+ * Handles concurrency to make sure only one DB migration can run at a time.
+ */
+public class DatabaseMigrationImpl implements DatabaseMigration {
+
+ private static final Logger LOGGER = Loggers.get(DatabaseMigrationImpl.class);
+
+ private final RubyBridge rubyBridge;
+ /**
+ * ExecutorService implements threads management.
+ */
+ private final DatabaseMigrationExecutorService executorService;
+ private final Platform platform;
+ private final MutableDatabaseMigrationState migrationState;
+ /**
+ * This lock implements thread safety from concurrent calls of method {@link #startIt()}
+ */
+ private final ReentrantLock lock = new ReentrantLock();
+
+ /**
+ * This property acts as a semaphore to make sure at most one db migration task is created at a time.
+ * <p>
+ * It is set to {@code true} by the first thread to execute the {@link #startIt()} method and set to {@code false}
+ * by the thread executing the db migration.
+ * </p>
+ */
+ private final AtomicBoolean running = new AtomicBoolean(false);
+
+ public DatabaseMigrationImpl(RubyBridge rubyBridge, DatabaseMigrationExecutorService executorService, Platform platform,
+ MutableDatabaseMigrationState migrationState) {
+ this.rubyBridge = rubyBridge;
+ this.executorService = executorService;
+ this.platform = platform;
+ this.migrationState = migrationState;
+ }
+
+ @Override
+ public void startIt() {
+ if (lock.isLocked() || this.running.get()) {
+ LOGGER.trace("{}: lock is already taken or process is already running", Thread.currentThread().getName());
+ return;
+ }
+
+ if (lock.tryLock()) {
+ try {
+ startAsynchronousDBMigration();
+ } finally {
+ lock.unlock();
+ }
+ }
+ }
+
+ /**
+ * This method is not thread safe and must be externally protected from concurrent executions.
+ */
+ private void startAsynchronousDBMigration() {
+ if (this.running.get()) {
+ return;
+ }
+
+ running.set(true);
+ executorService.execute(this::doDatabaseMigration);
+ }
+
+ private void doDatabaseMigration() {
+ migrationState.setStatus(Status.RUNNING);
+ migrationState.setStartedAt(new Date());
+ migrationState.setError(null);
+ Profiler profiler = Profiler.create(LOGGER);
+ try {
+ profiler.startInfo("Starting DB Migration");
+ doUpgradeDb();
+ doRestartContainer();
+ doRecreateWebRoutes();
+ migrationState.setStatus(Status.SUCCEEDED);
+ profiler.stopInfo("DB Migration ended successfully");
+ } catch (Throwable t) {
+ profiler.stopInfo("DB migration failed");
+ LOGGER.error("DB Migration or container restart failed. Process ended with an exception", t);
+ migrationState.setStatus(Status.FAILED);
+ migrationState.setError(t);
+ } finally {
+ running.getAndSet(false);
+ }
+ }
+
+ private void doUpgradeDb() {
+ Profiler profiler = Profiler.createIfTrace(LOGGER);
+ profiler.startTrace("Starting DB Migration");
+ rubyBridge.databaseMigration().trigger();
+ profiler.stopTrace("DB Migration ended");
+ }
+
+ private void doRestartContainer() {
+ Profiler profiler = Profiler.createIfTrace(LOGGER);
+ profiler.startTrace("Restarting container");
+ platform.doStart();
+ profiler.stopTrace("Container restarted successfully");
+ }
+
+ private void doRecreateWebRoutes() {
+ Profiler profiler = Profiler.createIfTrace(LOGGER);
+ profiler.startTrace("Recreating web routes");
+ rubyBridge.railsRoutes().recreate();
+ profiler.startTrace("Routes recreated successfully");
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.server.platform.db.migration;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.platform.db.migrations;
-
-import java.util.Date;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.locks.ReentrantLock;
-import org.sonar.api.utils.log.Logger;
-import org.sonar.api.utils.log.Loggers;
-import org.sonar.api.utils.log.Profiler;
-import org.sonar.server.platform.Platform;
-import org.sonar.server.platform.db.migration.DatabaseMigration;
-import org.sonar.server.platform.db.migration.MutableDatabaseMigrationState;
-import org.sonar.server.ruby.RubyBridge;
-
-import static org.sonar.server.platform.db.migration.DatabaseMigrationState.Status;
-
-/**
- * Handles concurrency to make sure only one DB migration can run at a time.
- */
-public class PlatformDatabaseMigration implements DatabaseMigration {
-
- private static final Logger LOGGER = Loggers.get(PlatformDatabaseMigration.class);
-
- private final RubyBridge rubyBridge;
- /**
- * ExecutorService implements threads management.
- */
- private final PlatformDatabaseMigrationExecutorService executorService;
- private final Platform platform;
- private final MutableDatabaseMigrationState migrationState;
- /**
- * This lock implements thread safety from concurrent calls of method {@link #startIt()}
- */
- private final ReentrantLock lock = new ReentrantLock();
-
- /**
- * This property acts as a semaphore to make sure at most one db migration task is created at a time.
- * <p>
- * It is set to {@code true} by the first thread to execute the {@link #startIt()} method and set to {@code false}
- * by the thread executing the db migration.
- * </p>
- */
- private final AtomicBoolean running = new AtomicBoolean(false);
-
- public PlatformDatabaseMigration(RubyBridge rubyBridge, PlatformDatabaseMigrationExecutorService executorService, Platform platform,
- MutableDatabaseMigrationState migrationState) {
- this.rubyBridge = rubyBridge;
- this.executorService = executorService;
- this.platform = platform;
- this.migrationState = migrationState;
- }
-
- @Override
- public void startIt() {
- if (lock.isLocked() || this.running.get()) {
- LOGGER.trace("{}: lock is already taken or process is already running", Thread.currentThread().getName());
- return;
- }
-
- if (lock.tryLock()) {
- try {
- startAsynchronousDBMigration();
- } finally {
- lock.unlock();
- }
- }
- }
-
- /**
- * This method is not thread safe and must be externally protected from concurrent executions.
- */
- private void startAsynchronousDBMigration() {
- if (this.running.get()) {
- return;
- }
-
- running.set(true);
- executorService.execute(this::doDatabaseMigration);
- }
-
- private void doDatabaseMigration() {
- migrationState.setStatus(Status.RUNNING);
- migrationState.setStartedAt(new Date());
- migrationState.setError(null);
- Profiler profiler = Profiler.create(LOGGER);
- try {
- profiler.startInfo("Starting DB Migration");
- doUpgradeDb();
- doRestartContainer();
- doRecreateWebRoutes();
- migrationState.setStatus(Status.SUCCEEDED);
- profiler.stopInfo("DB Migration ended successfully");
- } catch (Throwable t) {
- profiler.stopInfo("DB migration failed");
- LOGGER.error("DB Migration or container restart failed. Process ended with an exception", t);
- migrationState.setStatus(Status.FAILED);
- migrationState.setError(t);
- } finally {
- running.getAndSet(false);
- }
- }
-
- private void doUpgradeDb() {
- Profiler profiler = Profiler.createIfTrace(LOGGER);
- profiler.startTrace("Starting DB Migration");
- rubyBridge.databaseMigration().trigger();
- profiler.stopTrace("DB Migration ended");
- }
-
- private void doRestartContainer() {
- Profiler profiler = Profiler.createIfTrace(LOGGER);
- profiler.startTrace("Restarting container");
- platform.doStart();
- profiler.stopTrace("Container restarted successfully");
- }
-
- private void doRecreateWebRoutes() {
- Profiler profiler = Profiler.createIfTrace(LOGGER);
- profiler.startTrace("Recreating web routes");
- rubyBridge.railsRoutes().recreate();
- profiler.startTrace("Routes recreated successfully");
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.platform.db.migrations;
-
-import java.util.concurrent.ExecutorService;
-
-/**
- * Flag interface for the ExecutorService to be used by the {@link PlatformDatabaseMigration}
- * component.
- */
-public interface PlatformDatabaseMigrationExecutorService extends ExecutorService {
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.platform.db.migrations;
-
-import com.google.common.util.concurrent.ThreadFactoryBuilder;
-import org.sonar.server.util.AbstractStoppableExecutorService;
-
-import java.util.concurrent.Executors;
-
-/**
- * Since only one DB migration can run at a time, this implementation of PlatformDatabaseMigrationExecutorService
- * wraps a single thread executor from the JDK.
- */
-public class PlatformDatabaseMigrationExecutorServiceImpl
- extends AbstractStoppableExecutorService
- implements PlatformDatabaseMigrationExecutorService {
-
- public PlatformDatabaseMigrationExecutorServiceImpl() {
- super(
- Executors.newSingleThreadExecutor(
- new ThreadFactoryBuilder()
- .setDaemon(false)
- .setNameFormat("DB_migration-%d")
- .build()
- ));
- }
-}
import org.sonar.server.platform.DefaultServerUpgradeStatus;
import org.sonar.server.platform.StartupMetadataProvider;
import org.sonar.server.platform.db.CheckDatabaseCharsetAtStartup;
+import org.sonar.server.platform.db.migration.DatabaseMigrationExecutorServiceImpl;
import org.sonar.server.platform.db.migration.DatabaseMigrationStateImpl;
import org.sonar.server.platform.db.migration.history.MigrationHistoryTableImpl;
import org.sonar.server.platform.db.migrations.DatabaseMigrator;
-import org.sonar.server.platform.db.migrations.PlatformDatabaseMigrationExecutorServiceImpl;
import org.sonar.server.platform.web.RailsAppsDeployer;
import org.sonar.server.plugins.InstalledPluginReferentialFactory;
import org.sonar.server.plugins.ServerPluginJarExploder;
// Full Java DB Migration framework and configuration
addIfStartupLeader(MigrationHistoryTableImpl.class);
add(DatabaseMigrationStateImpl.class,
- PlatformDatabaseMigrationExecutorServiceImpl.class);
+ DatabaseMigrationExecutorServiceImpl.class);
// Ruby DB Migration
add(
DatabaseMigrator.class,
import org.sonar.server.organization.NoopDefaultOrganizationCache;
import org.sonar.server.platform.ServerImpl;
-import org.sonar.server.platform.db.migrations.PlatformDatabaseMigration;
+import org.sonar.server.platform.db.migration.DatabaseMigrationImpl;
import org.sonar.server.platform.web.WebPagesFilter;
import org.sonar.server.platform.ws.DbMigrationStatusAction;
import org.sonar.server.platform.ws.IndexAction;
WebServiceFilter.class,
NoopDefaultOrganizationCache.class);
- add(PlatformDatabaseMigration.class);
+ add(DatabaseMigrationImpl.class);
}
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.platform.db.migration;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Adaptor for the DatabaseMigrationExecutorService interface which implementation of methods all throw
+ * UnsupportedOperationException.
+ */
+class DatabaseMigrationExecutorServiceAdaptor implements DatabaseMigrationExecutorService {
+
+ @Override
+ public void execute(Runnable command) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void shutdown() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List<Runnable> shutdownNow() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isShutdown() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isTerminated() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <T> Future<T> submit(Callable<T> task) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <T> Future<T> submit(Runnable task, T result) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Future<?> submit(Runnable task) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
+ throw new UnsupportedOperationException();
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.platform.db.migration;
+
+import org.junit.Test;
+import org.sonar.server.platform.Platform;
+import org.sonar.server.ruby.RubyBridge;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+public class DatabaseMigrationImplAsynchronousTest {
+
+ private boolean taskSuppliedForAsyncProcess = false;
+ /**
+ * Implementation of execute wraps specified Runnable to add a delay of 200 ms before passing it
+ * to a SingleThread executor to execute asynchronously.
+ */
+ private DatabaseMigrationExecutorService executorService = new DatabaseMigrationExecutorServiceAdaptor() {
+ @Override
+ public void execute(final Runnable command) {
+ taskSuppliedForAsyncProcess = true;
+ }
+ };
+ private RubyBridge rubyBridge = mock(RubyBridge.class);
+ private Platform platform = mock(Platform.class);
+ private MutableDatabaseMigrationState migrationState = mock(MutableDatabaseMigrationState.class);
+ private DatabaseMigrationImpl underTest = new DatabaseMigrationImpl(rubyBridge, executorService, platform, migrationState);
+
+ @Test
+ public void testName() throws Exception {
+ underTest.startIt();
+
+ assertThat(taskSuppliedForAsyncProcess).isTrue();
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.platform.db.migration;
+
+import com.google.common.base.Throwables;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.junit.After;
+import org.junit.Test;
+import org.sonar.server.platform.Platform;
+import org.sonar.server.ruby.RubyBridge;
+import org.sonar.server.ruby.RubyDatabaseMigration;
+import org.sonar.server.ruby.RubyRailsRoutes;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class DatabaseMigrationImplConcurrentAccessTest {
+
+ private ExecutorService pool = Executors.newFixedThreadPool(2);
+ /**
+ * Latch is used to make sure both testing threads try and call {@link DatabaseMigrationImpl#startIt()} at the
+ * same time
+ */
+ private CountDownLatch latch = new CountDownLatch(2);
+
+ /**
+ * Implementation of execute runs Runnable synchronously
+ */
+ private DatabaseMigrationExecutorService executorService = new DatabaseMigrationExecutorServiceAdaptor() {
+ @Override
+ public void execute(Runnable command) {
+ command.run();
+ }
+ };
+ /**
+ * thread-safe counter of calls to the trigger method of {@link #rubyDatabaseMigration}
+ */
+ private AtomicInteger triggerCount = new AtomicInteger();
+ /**
+ * Implementation of RubyDatabaseMigration which trigger method increments a thread-safe counter and add a delay of 200ms
+ */
+ private RubyDatabaseMigration rubyDatabaseMigration = new RubyDatabaseMigration() {
+ @Override
+ public void trigger() {
+ triggerCount.incrementAndGet();
+ try {
+ Thread.currentThread().sleep(1000);
+ } catch (InterruptedException e) {
+ Throwables.propagate(e);
+ }
+ }
+ };
+ private RubyBridge rubyBridge = mock(RubyBridge.class);
+ private Platform platform = mock(Platform.class);
+ private RubyRailsRoutes railsRoutes = mock(RubyRailsRoutes.class);
+ private MutableDatabaseMigrationState migrationState = mock(MutableDatabaseMigrationState.class);
+ private DatabaseMigrationImpl underTest = new DatabaseMigrationImpl(rubyBridge, executorService, platform, migrationState);
+
+ @After
+ public void tearDown() {
+ pool.shutdownNow();
+ }
+
+ @Test
+ public void two_concurrent_calls_to_startit_call_trigger_only_once() throws Exception {
+ when(rubyBridge.databaseMigration()).thenReturn(rubyDatabaseMigration);
+ when(rubyBridge.railsRoutes()).thenReturn(railsRoutes);
+
+ pool.submit(new CallStartit());
+ pool.submit(new CallStartit());
+
+ pool.awaitTermination(2, TimeUnit.SECONDS);
+
+ assertThat(triggerCount.get()).isEqualTo(1);
+ }
+
+ private class CallStartit implements Runnable {
+ @Override
+ public void run() {
+ latch.countDown();
+ try {
+ latch.await();
+ } catch (InterruptedException e) {
+ // propagate interruption
+ Thread.currentThread().interrupt();
+ }
+ underTest.startIt();
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.platform.db.migration;
+
+import java.util.Date;
+import org.junit.Test;
+import org.mockito.InOrder;
+import org.sonar.server.platform.Platform;
+import org.sonar.server.ruby.RubyBridge;
+import org.sonar.server.ruby.RubyDatabaseMigration;
+import org.sonar.server.ruby.RubyRailsRoutes;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * Unit test for DatabaseMigrationImpl which does not test any of its concurrency management and asynchronous execution code.
+ */
+public class DatabaseMigrationImplTest {
+ private static final Throwable AN_ERROR = new RuntimeException("runtime exception created on purpose");
+
+ /**
+ * Implementation of execute runs Runnable synchronously.
+ */
+ private DatabaseMigrationExecutorService executorService = new DatabaseMigrationExecutorServiceAdaptor() {
+ @Override
+ public void execute(Runnable command) {
+ command.run();
+ }
+ };
+ private RubyBridge rubyBridge = mock(RubyBridge.class);
+ private RubyDatabaseMigration rubyDatabaseMigration = mock(RubyDatabaseMigration.class);
+ private RubyRailsRoutes rubyRailsRoutes = mock(RubyRailsRoutes.class);
+ private Platform platform = mock(Platform.class);
+ private MutableDatabaseMigrationState migrationState = new DatabaseMigrationStateImpl();
+ private InOrder inOrder = inOrder(rubyDatabaseMigration, rubyBridge, rubyRailsRoutes, platform);
+
+ private DatabaseMigrationImpl underTest = new DatabaseMigrationImpl(rubyBridge, executorService, platform, migrationState);
+
+ @Test
+ public void startit_calls_databasemigration_trigger_in_a_separate_thread() {
+ when(rubyBridge.databaseMigration()).thenReturn(rubyDatabaseMigration);
+ when(rubyBridge.railsRoutes()).thenReturn(rubyRailsRoutes);
+
+ underTest.startIt();
+
+ inOrder.verify(rubyBridge).databaseMigration();
+ inOrder.verify(rubyDatabaseMigration).trigger();
+ inOrder.verify(platform).doStart();
+ inOrder.verify(rubyBridge).railsRoutes();
+ inOrder.verify(rubyRailsRoutes).recreate();
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void status_is_SUCCEEDED_and_failure_is_null_when_trigger_runs_without_an_exception() {
+ when(rubyBridge.databaseMigration()).thenReturn(rubyDatabaseMigration);
+ when(rubyBridge.railsRoutes()).thenReturn(rubyRailsRoutes);
+
+ underTest.startIt();
+
+ assertThat(migrationState.getStatus()).isEqualTo(DatabaseMigrationState.Status.SUCCEEDED);
+ assertThat(migrationState.getError()).isNull();
+ assertThat(migrationState.getStartedAt()).isNotNull();
+ }
+
+ @Test
+ public void status_is_FAILED_and_failure_stores_the_exception_when_trigger_throws_an_exception() {
+ mockTriggerThrowsError();
+
+ underTest.startIt();
+
+ assertThat(migrationState.getStatus()).isEqualTo(DatabaseMigrationState.Status.FAILED);
+ assertThat(migrationState.getError()).isSameAs(AN_ERROR);
+ assertThat(migrationState.getStartedAt()).isNotNull();
+ }
+
+ @Test
+ public void successive_calls_to_startIt_reset_status_startedAt_and_failureError() {
+ mockTriggerThrowsError();
+
+ underTest.startIt();
+
+ assertThat(migrationState.getStatus()).isEqualTo(DatabaseMigrationState.Status.FAILED);
+ assertThat(migrationState.getError()).isSameAs(AN_ERROR);
+ Date firstStartDate = migrationState.getStartedAt();
+ assertThat(firstStartDate).isNotNull();
+
+ mockTriggerDoesNothing();
+
+ underTest.startIt();
+
+ assertThat(migrationState.getStatus()).isEqualTo(DatabaseMigrationState.Status.SUCCEEDED);
+ assertThat(migrationState.getError()).isNull();
+ assertThat(migrationState.getStartedAt()).isNotSameAs(firstStartDate);
+ }
+
+ private void mockTriggerThrowsError() {
+ when(rubyBridge.databaseMigration()).thenReturn(rubyDatabaseMigration);
+ doThrow(AN_ERROR).when(rubyDatabaseMigration).trigger();
+ when(rubyBridge.railsRoutes()).thenReturn(rubyRailsRoutes);
+ }
+
+ private void mockTriggerDoesNothing() {
+ when(rubyBridge.databaseMigration()).thenReturn(rubyDatabaseMigration);
+ doNothing().when(rubyDatabaseMigration).trigger();
+ when(rubyBridge.railsRoutes()).thenReturn(rubyRailsRoutes);
+ }
+}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.platform.db.migrations;
-
-import org.junit.Test;
-import org.sonar.server.platform.Platform;
-import org.sonar.server.platform.db.migration.MutableDatabaseMigrationState;
-import org.sonar.server.ruby.RubyBridge;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-
-public class PlatformDatabaseMigrationAsynchronousTest {
-
- private boolean taskSuppliedForAsyncProcess = false;
- /**
- * Implementation of execute wraps specified Runnable to add a delay of 200 ms before passing it
- * to a SingleThread executor to execute asynchronously.
- */
- private PlatformDatabaseMigrationExecutorService executorService = new PlatformDatabaseMigrationExecutorServiceAdaptor() {
- @Override
- public void execute(final Runnable command) {
- taskSuppliedForAsyncProcess = true;
- }
- };
- private RubyBridge rubyBridge = mock(RubyBridge.class);
- private Platform platform = mock(Platform.class);
- private MutableDatabaseMigrationState migrationState = mock(MutableDatabaseMigrationState.class);
- private PlatformDatabaseMigration underTest = new PlatformDatabaseMigration(rubyBridge, executorService, platform, migrationState);
-
- @Test
- public void testName() throws Exception {
- underTest.startIt();
-
- assertThat(taskSuppliedForAsyncProcess).isTrue();
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.platform.db.migrations;
-
-import com.google.common.base.Throwables;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-import org.junit.After;
-import org.junit.Test;
-import org.sonar.server.platform.Platform;
-import org.sonar.server.platform.db.migration.MutableDatabaseMigrationState;
-import org.sonar.server.ruby.RubyBridge;
-import org.sonar.server.ruby.RubyDatabaseMigration;
-import org.sonar.server.ruby.RubyRailsRoutes;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class PlatformDatabaseMigrationConcurrentAccessTest {
-
- private ExecutorService pool = Executors.newFixedThreadPool(2);
- /**
- * Latch is used to make sure both testing threads try and call {@link PlatformDatabaseMigration#startIt()} at the
- * same time
- */
- private CountDownLatch latch = new CountDownLatch(2);
-
- /**
- * Implementation of execute runs Runnable synchronously
- */
- private PlatformDatabaseMigrationExecutorService executorService = new PlatformDatabaseMigrationExecutorServiceAdaptor() {
- @Override
- public void execute(Runnable command) {
- command.run();
- }
- };
- /**
- * thread-safe counter of calls to the trigger method of {@link #rubyDatabaseMigration}
- */
- private AtomicInteger triggerCount = new AtomicInteger();
- /**
- * Implementation of RubyDatabaseMigration which trigger method increments a thread-safe counter and add a delay of 200ms
- */
- private RubyDatabaseMigration rubyDatabaseMigration = new RubyDatabaseMigration() {
- @Override
- public void trigger() {
- triggerCount.incrementAndGet();
- try {
- Thread.currentThread().sleep(1000);
- } catch (InterruptedException e) {
- Throwables.propagate(e);
- }
- }
- };
- private RubyBridge rubyBridge = mock(RubyBridge.class);
- private Platform platform = mock(Platform.class);
- private RubyRailsRoutes railsRoutes = mock(RubyRailsRoutes.class);
- private MutableDatabaseMigrationState migrationState = mock(MutableDatabaseMigrationState.class);
- private PlatformDatabaseMigration underTest = new PlatformDatabaseMigration(rubyBridge, executorService, platform, migrationState);
-
- @After
- public void tearDown() {
- pool.shutdownNow();
- }
-
- @Test
- public void two_concurrent_calls_to_startit_call_trigger_only_once() throws Exception {
- when(rubyBridge.databaseMigration()).thenReturn(rubyDatabaseMigration);
- when(rubyBridge.railsRoutes()).thenReturn(railsRoutes);
-
- pool.submit(new CallStartit());
- pool.submit(new CallStartit());
-
- pool.awaitTermination(2, TimeUnit.SECONDS);
-
- assertThat(triggerCount.get()).isEqualTo(1);
- }
-
- private class CallStartit implements Runnable {
- @Override
- public void run() {
- latch.countDown();
- try {
- latch.await();
- } catch (InterruptedException e) {
- // propagate interruption
- Thread.currentThread().interrupt();
- }
- underTest.startIt();
- }
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.platform.db.migrations;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-/**
- * Adaptor for the PlatformDatabaseMigrationExecutorService interface which implementation of methods all throw
- * UnsupportedOperationException.
- */
-class PlatformDatabaseMigrationExecutorServiceAdaptor implements PlatformDatabaseMigrationExecutorService {
-
- @Override
- public void execute(Runnable command) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void shutdown() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public List<Runnable> shutdownNow() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean isShutdown() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean isTerminated() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public <T> Future<T> submit(Callable<T> task) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public <T> Future<T> submit(Runnable task, T result) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public Future<?> submit(Runnable task) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
- throw new UnsupportedOperationException();
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.platform.db.migrations;
-
-import java.util.Date;
-import org.junit.Test;
-import org.mockito.InOrder;
-import org.sonar.server.platform.Platform;
-import org.sonar.server.platform.db.migration.DatabaseMigrationState;
-import org.sonar.server.platform.db.migration.DatabaseMigrationStateImpl;
-import org.sonar.server.platform.db.migration.MutableDatabaseMigrationState;
-import org.sonar.server.ruby.RubyBridge;
-import org.sonar.server.ruby.RubyDatabaseMigration;
-import org.sonar.server.ruby.RubyRailsRoutes;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-/**
- * Unit test for PlatformDatabaseMigration which does not test any of its concurrency management and asynchronous execution code.
- */
-public class PlatformDatabaseMigrationTest {
- private static final Throwable AN_ERROR = new RuntimeException("runtime exception created on purpose");
-
- /**
- * Implementation of execute runs Runnable synchronously.
- */
- private PlatformDatabaseMigrationExecutorService executorService = new PlatformDatabaseMigrationExecutorServiceAdaptor() {
- @Override
- public void execute(Runnable command) {
- command.run();
- }
- };
- private RubyBridge rubyBridge = mock(RubyBridge.class);
- private RubyDatabaseMigration rubyDatabaseMigration = mock(RubyDatabaseMigration.class);
- private RubyRailsRoutes rubyRailsRoutes = mock(RubyRailsRoutes.class);
- private Platform platform = mock(Platform.class);
- private MutableDatabaseMigrationState migrationState = new DatabaseMigrationStateImpl();
- private InOrder inOrder = inOrder(rubyDatabaseMigration, rubyBridge, rubyRailsRoutes, platform);
-
- private PlatformDatabaseMigration underTest = new PlatformDatabaseMigration(rubyBridge, executorService, platform, migrationState);
-
- @Test
- public void startit_calls_databasemigration_trigger_in_a_separate_thread() {
- when(rubyBridge.databaseMigration()).thenReturn(rubyDatabaseMigration);
- when(rubyBridge.railsRoutes()).thenReturn(rubyRailsRoutes);
-
- underTest.startIt();
-
- inOrder.verify(rubyBridge).databaseMigration();
- inOrder.verify(rubyDatabaseMigration).trigger();
- inOrder.verify(platform).doStart();
- inOrder.verify(rubyBridge).railsRoutes();
- inOrder.verify(rubyRailsRoutes).recreate();
- inOrder.verifyNoMoreInteractions();
- }
-
- @Test
- public void status_is_SUCCEEDED_and_failure_is_null_when_trigger_runs_without_an_exception() {
- when(rubyBridge.databaseMigration()).thenReturn(rubyDatabaseMigration);
- when(rubyBridge.railsRoutes()).thenReturn(rubyRailsRoutes);
-
- underTest.startIt();
-
- assertThat(migrationState.getStatus()).isEqualTo(DatabaseMigrationState.Status.SUCCEEDED);
- assertThat(migrationState.getError()).isNull();
- assertThat(migrationState.getStartedAt()).isNotNull();
- }
-
- @Test
- public void status_is_FAILED_and_failure_stores_the_exception_when_trigger_throws_an_exception() {
- mockTriggerThrowsError();
-
- underTest.startIt();
-
- assertThat(migrationState.getStatus()).isEqualTo(DatabaseMigrationState.Status.FAILED);
- assertThat(migrationState.getError()).isSameAs(AN_ERROR);
- assertThat(migrationState.getStartedAt()).isNotNull();
- }
-
- @Test
- public void successive_calls_to_startIt_reset_status_startedAt_and_failureError() {
- mockTriggerThrowsError();
-
- underTest.startIt();
-
- assertThat(migrationState.getStatus()).isEqualTo(DatabaseMigrationState.Status.FAILED);
- assertThat(migrationState.getError()).isSameAs(AN_ERROR);
- Date firstStartDate = migrationState.getStartedAt();
- assertThat(firstStartDate).isNotNull();
-
- mockTriggerDoesNothing();
-
- underTest.startIt();
-
- assertThat(migrationState.getStatus()).isEqualTo(DatabaseMigrationState.Status.SUCCEEDED);
- assertThat(migrationState.getError()).isNull();
- assertThat(migrationState.getStartedAt()).isNotSameAs(firstStartDate);
- }
-
- private void mockTriggerThrowsError() {
- when(rubyBridge.databaseMigration()).thenReturn(rubyDatabaseMigration);
- doThrow(AN_ERROR).when(rubyDatabaseMigration).trigger();
- when(rubyBridge.railsRoutes()).thenReturn(rubyRailsRoutes);
- }
-
- private void mockTriggerDoesNothing() {
- when(rubyBridge.databaseMigration()).thenReturn(rubyDatabaseMigration);
- doNothing().when(rubyDatabaseMigration).trigger();
- when(rubyBridge.railsRoutes()).thenReturn(rubyRailsRoutes);
- }
-}