From b83c58503a60c532379a7797c3a5f630be77a1ff Mon Sep 17 00:00:00 2001 From: Sébastien Lesaint Date: Fri, 26 Jul 2019 11:19:42 +0200 Subject: SONAR-8520 remove H2 specific way of creating database in Unit tests --- .../src/test/java/org/sonar/db/H2Database.java | 187 ---------------- .../src/test/java/org/sonar/db/H2DatabaseTest.java | 62 ------ .../src/test/java/org/sonar/db/MyBatisTest.java | 4 +- .../src/test/java/org/sonar/db/SQDatabase.java | 236 +++++++++++++++++++++ .../java/org/sonar/db/SQDatabaseForH2Test.java | 62 ++++++ .../src/test/java/org/sonar/db/TestDbImpl.java | 4 +- .../java/org/sonar/db/dump/SQSchemaDumper.java | 4 +- 7 files changed, 304 insertions(+), 255 deletions(-) delete mode 100644 server/sonar-db-dao/src/test/java/org/sonar/db/H2Database.java delete mode 100644 server/sonar-db-dao/src/test/java/org/sonar/db/H2DatabaseTest.java create mode 100644 server/sonar-db-dao/src/test/java/org/sonar/db/SQDatabase.java create mode 100644 server/sonar-db-dao/src/test/java/org/sonar/db/SQDatabaseForH2Test.java (limited to 'server/sonar-db-dao') diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/H2Database.java b/server/sonar-db-dao/src/test/java/org/sonar/db/H2Database.java deleted file mode 100644 index c102ff0b0f6..00000000000 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/H2Database.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2019 SonarSource SA - * mailto:info 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.db; - -import java.sql.Connection; -import java.sql.SQLException; -import java.util.List; -import javax.sql.DataSource; -import org.apache.commons.dbutils.DbUtils; -import org.sonar.api.SonarEdition; -import org.sonar.api.SonarQubeSide; -import org.sonar.api.internal.SonarRuntimeImpl; -import org.sonar.api.utils.System2; -import org.sonar.api.utils.Version; -import org.sonar.api.utils.log.Loggers; -import org.sonar.core.platform.ComponentContainer; -import org.sonar.core.util.UuidFactoryFast; -import org.sonar.core.util.logs.Profiler; -import org.sonar.db.dialect.Dialect; -import org.sonar.db.dialect.H2; -import org.sonar.server.platform.db.migration.MigrationConfigurationModule; -import org.sonar.server.platform.db.migration.engine.MigrationContainer; -import org.sonar.server.platform.db.migration.engine.MigrationContainerImpl; -import org.sonar.server.platform.db.migration.engine.MigrationContainerPopulator; -import org.sonar.server.platform.db.migration.engine.MigrationContainerPopulatorImpl; -import org.sonar.server.platform.db.migration.history.MigrationHistoryTableImpl; -import org.sonar.server.platform.db.migration.step.MigrationStep; -import org.sonar.server.platform.db.migration.step.MigrationStepExecutionException; -import org.sonar.server.platform.db.migration.step.MigrationSteps; -import org.sonar.server.platform.db.migration.step.MigrationStepsExecutor; -import org.sonar.server.platform.db.migration.step.RegisteredMigrationStep; -import org.sonar.server.platform.db.migration.version.DbVersion; - -import static com.google.common.base.Preconditions.checkState; - -/** - * H2 in-memory database, used for unit tests against an empty DB, a specific script or against SQ schema. - */ -public class H2Database extends CoreH2Database { - private final boolean createSchema; - - /** - * IMPORTANT: change DB name in order to not conflict with {@link DefaultDatabaseTest} - */ - public H2Database(String name, boolean createSchema) { - super(name); - this.createSchema = createSchema; - } - - @Override - public void start() { - super.start(); - if (createSchema) { - createSchema(); - } - } - - private void createSchema() { - Connection connection = null; - try { - connection = getDataSource().getConnection(); - NoopH2Database noopH2Database = new NoopH2Database(); - // create and populate schema - createMigrationHistoryTable(noopH2Database); - executeDbMigrations(noopH2Database); - } catch (SQLException e) { - throw new IllegalStateException("Fail to create schema", e); - } finally { - DbUtils.closeQuietly(connection); - } - } - - public static final class H2MigrationContainerPopulator extends MigrationContainerPopulatorImpl { - public H2MigrationContainerPopulator(DbVersion... dbVersions) { - super(H2StepExecutor.class, dbVersions); - } - } - - public static final class H2StepExecutor implements MigrationStepsExecutor { - private static final String STEP_START_PATTERN = "{}..."; - private static final String STEP_STOP_PATTERN = "{}: {}"; - - private final ComponentContainer componentContainer; - - public H2StepExecutor(ComponentContainer componentContainer) { - this.componentContainer = componentContainer; - } - - @Override - public void execute(List steps) { - steps.forEach(step -> execute(step, componentContainer)); - } - - private void execute(RegisteredMigrationStep step, ComponentContainer componentContainer) { - MigrationStep migrationStep = componentContainer.getComponentByType(step.getStepClass()); - checkState(migrationStep != null, "Can not find instance of " + step.getStepClass()); - - execute(step, migrationStep); - } - - private void execute(RegisteredMigrationStep step, MigrationStep migrationStep) { - Profiler stepProfiler = Profiler.create(Loggers.get(H2Database.class)); - stepProfiler.startInfo(STEP_START_PATTERN, step); - boolean done = false; - try { - migrationStep.execute(); - done = true; - } catch (Exception e) { - throw new MigrationStepExecutionException(step, e); - } finally { - if (done) { - stepProfiler.stopInfo(STEP_STOP_PATTERN, step, "success"); - } else { - stepProfiler.stopError(STEP_STOP_PATTERN, step, "failure"); - } - } - } - } - - private void executeDbMigrations(NoopH2Database noopH2Database) { - ComponentContainer parentContainer = new ComponentContainer(); - parentContainer.add(noopH2Database); - parentContainer.add(H2MigrationContainerPopulator.class); - MigrationConfigurationModule migrationConfigurationModule = new MigrationConfigurationModule(); - migrationConfigurationModule.configure(parentContainer); - - // dependencies required by DB migrations - parentContainer.add(SonarRuntimeImpl.forSonarQube(Version.create(8, 0), SonarQubeSide.SERVER, SonarEdition.COMMUNITY)); - parentContainer.add(UuidFactoryFast.getInstance()); - parentContainer.add(System2.INSTANCE); - - parentContainer.startComponents(); - - MigrationContainer migrationContainer = new MigrationContainerImpl(parentContainer, parentContainer.getComponentByType(MigrationContainerPopulator.class)); - MigrationSteps migrationSteps = migrationContainer.getComponentByType(MigrationSteps.class); - migrationContainer.getComponentByType(MigrationStepsExecutor.class) - .execute(migrationSteps.readAll()); - } - - private void createMigrationHistoryTable(NoopH2Database noopH2Database) { - new MigrationHistoryTableImpl(noopH2Database).start(); - } - - private class NoopH2Database implements Database { - @Override - public DataSource getDataSource() { - return H2Database.this.getDataSource(); - } - - @Override - public Dialect getDialect() { - return new H2(); - } - - @Override - public void enableSqlLogging(boolean enable) { - - } - - @Override - public void start() { - // do nothing - } - - @Override - public void stop() { - // do nothing - } - } -} diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/H2DatabaseTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/H2DatabaseTest.java deleted file mode 100644 index 98a64beb4f7..00000000000 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/H2DatabaseTest.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2019 SonarSource SA - * mailto:info 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.db; - -import java.sql.Connection; -import java.sql.ResultSet; -import java.sql.SQLException; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -public class H2DatabaseTest { - H2Database db = new H2Database("sonar2", true); - - @Before - public void startDb() { - db.start(); - } - - @After - public void stopDb() { - db.stop(); - } - - @Test - public void shouldExecuteDdlAtStartup() throws SQLException { - Connection connection = db.getDataSource().getConnection(); - int tableCount = countTables(connection); - connection.close(); - - assertThat(tableCount).isGreaterThan(30); - } - - private static int countTables(Connection connection) throws SQLException { - int count = 0; - ResultSet resultSet = connection.getMetaData().getTables("", null, null, new String[] {"TABLE"}); - while (resultSet.next()) { - count++; - } - resultSet.close(); - return count; - } -} diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/MyBatisTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/MyBatisTest.java index 62e0e7dffce..e5a4f94946c 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/MyBatisTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/MyBatisTest.java @@ -31,11 +31,11 @@ import static org.hamcrest.CoreMatchers.notNullValue; import static org.junit.Assert.assertThat; public class MyBatisTest { - private static H2Database database; + private static SQDatabase database; @BeforeClass public static void start() { - database = new H2Database("sonar2", true); + database = SQDatabase.newH2Database("sonar2", true); database.start(); } diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/SQDatabase.java b/server/sonar-db-dao/src/test/java/org/sonar/db/SQDatabase.java new file mode 100644 index 00000000000..4a4fd2eae79 --- /dev/null +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/SQDatabase.java @@ -0,0 +1,236 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 SonarSource SA + * mailto:info 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.db; + +import java.io.PrintWriter; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.List; +import javax.sql.DataSource; +import org.apache.commons.dbutils.DbUtils; +import org.apache.commons.io.output.NullWriter; +import org.apache.ibatis.io.Resources; +import org.apache.ibatis.jdbc.ScriptRunner; +import org.sonar.api.SonarEdition; +import org.sonar.api.SonarQubeSide; +import org.sonar.api.config.Settings; +import org.sonar.api.config.internal.MapSettings; +import org.sonar.api.internal.SonarRuntimeImpl; +import org.sonar.api.utils.System2; +import org.sonar.api.utils.Version; +import org.sonar.api.utils.log.Loggers; +import org.sonar.core.platform.ComponentContainer; +import org.sonar.core.util.UuidFactoryFast; +import org.sonar.core.util.logs.Profiler; +import org.sonar.db.dialect.Dialect; +import org.sonar.process.logging.LogbackHelper; +import org.sonar.server.platform.db.migration.MigrationConfigurationModule; +import org.sonar.server.platform.db.migration.engine.MigrationContainer; +import org.sonar.server.platform.db.migration.engine.MigrationContainerImpl; +import org.sonar.server.platform.db.migration.engine.MigrationContainerPopulator; +import org.sonar.server.platform.db.migration.engine.MigrationContainerPopulatorImpl; +import org.sonar.server.platform.db.migration.history.MigrationHistoryTableImpl; +import org.sonar.server.platform.db.migration.step.MigrationStep; +import org.sonar.server.platform.db.migration.step.MigrationStepExecutionException; +import org.sonar.server.platform.db.migration.step.MigrationSteps; +import org.sonar.server.platform.db.migration.step.MigrationStepsExecutor; +import org.sonar.server.platform.db.migration.step.RegisteredMigrationStep; +import org.sonar.server.platform.db.migration.version.DbVersion; + +import static com.google.common.base.Preconditions.checkState; + +public class SQDatabase extends DefaultDatabase { + private final boolean createSchema; + + private SQDatabase(Settings settings, boolean createSchema) { + super(new LogbackHelper(), settings); + this.createSchema = createSchema; + } + + public static SQDatabase newDatabase(Settings settings, boolean createSchema) { + return new SQDatabase(settings, createSchema); + } + + public static SQDatabase newH2Database(String name, boolean createSchema) { + MapSettings settings = new MapSettings() + .setProperty("sonar.jdbc.dialect", "h2") + .setProperty("sonar.jdbc.driverClassName", "org.h2.Driver") + .setProperty("sonar.jdbc.url", "jdbc:h2:mem:" + name) + .setProperty("sonar.jdbc.username", "sonar") + .setProperty("sonar.jdbc.password", "sonar"); + return new SQDatabase(settings, createSchema); + } + + @Override + public void start() { + super.start(); + if (createSchema) { + createSchema(); + } + } + + private void createSchema() { + Connection connection = null; + try { + connection = getDataSource().getConnection(); + NoopDatabase noopDatabase = new NoopDatabase(getDialect(), getDataSource()); + // create and populate schema + createMigrationHistoryTable(noopDatabase); + executeDbMigrations(noopDatabase); + } catch (SQLException e) { + throw new IllegalStateException("Fail to create schema", e); + } finally { + DbUtils.closeQuietly(connection); + } + } + + public static final class H2MigrationContainerPopulator extends MigrationContainerPopulatorImpl { + public H2MigrationContainerPopulator(DbVersion... dbVersions) { + super(H2StepExecutor.class, dbVersions); + } + } + + public static final class H2StepExecutor implements MigrationStepsExecutor { + private static final String STEP_START_PATTERN = "{}..."; + private static final String STEP_STOP_PATTERN = "{}: {}"; + + private final ComponentContainer componentContainer; + + public H2StepExecutor(ComponentContainer componentContainer) { + this.componentContainer = componentContainer; + } + + @Override + public void execute(List steps) { + steps.forEach(step -> execute(step, componentContainer)); + } + + private void execute(RegisteredMigrationStep step, ComponentContainer componentContainer) { + MigrationStep migrationStep = componentContainer.getComponentByType(step.getStepClass()); + checkState(migrationStep != null, "Can not find instance of " + step.getStepClass()); + + execute(step, migrationStep); + } + + private void execute(RegisteredMigrationStep step, MigrationStep migrationStep) { + Profiler stepProfiler = Profiler.create(Loggers.get(SQDatabase.class)); + stepProfiler.startInfo(STEP_START_PATTERN, step); + boolean done = false; + try { + migrationStep.execute(); + done = true; + } catch (Exception e) { + throw new MigrationStepExecutionException(step, e); + } finally { + if (done) { + stepProfiler.stopInfo(STEP_STOP_PATTERN, step, "success"); + } else { + stepProfiler.stopError(STEP_STOP_PATTERN, step, "failure"); + } + } + } + } + + private void executeDbMigrations(NoopDatabase noopDatabase) { + ComponentContainer parentContainer = new ComponentContainer(); + parentContainer.add(noopDatabase); + parentContainer.add(H2MigrationContainerPopulator.class); + MigrationConfigurationModule migrationConfigurationModule = new MigrationConfigurationModule(); + migrationConfigurationModule.configure(parentContainer); + + // dependencies required by DB migrations + parentContainer.add(SonarRuntimeImpl.forSonarQube(Version.create(8, 0), SonarQubeSide.SERVER, SonarEdition.COMMUNITY)); + parentContainer.add(UuidFactoryFast.getInstance()); + parentContainer.add(System2.INSTANCE); + + parentContainer.startComponents(); + + MigrationContainer migrationContainer = new MigrationContainerImpl(parentContainer, parentContainer.getComponentByType(MigrationContainerPopulator.class)); + MigrationSteps migrationSteps = migrationContainer.getComponentByType(MigrationSteps.class); + migrationContainer.getComponentByType(MigrationStepsExecutor.class) + .execute(migrationSteps.readAll()); + } + + private void createMigrationHistoryTable(NoopDatabase noopDatabase) { + new MigrationHistoryTableImpl(noopDatabase).start(); + } + + private class NoopDatabase implements Database { + private final Dialect dialect; + private final DataSource dataSource; + + private NoopDatabase(Dialect dialect, DataSource dataSource) { + this.dialect = dialect; + this.dataSource = dataSource; + } + + @Override + public DataSource getDataSource() { + return dataSource; + } + + @Override + public Dialect getDialect() { + return dialect; + } + + @Override + public void enableSqlLogging(boolean enable) { + + } + + @Override + public void start() { + // do nothing + } + + @Override + public void stop() { + // do nothing + } + } + + public void executeScript(String classloaderPath) { + try (Connection connection = getDataSource().getConnection()) { + executeScript(connection, classloaderPath); + } catch (SQLException e) { + throw new IllegalStateException("Fail to execute script: " + classloaderPath, e); + } + } + + private static void executeScript(Connection connection, String path) { + ScriptRunner scriptRunner = newScriptRunner(connection); + try { + scriptRunner.runScript(Resources.getResourceAsReader(path)); + connection.commit(); + + } catch (Exception e) { + throw new IllegalStateException("Fail to restore: " + path, e); + } + } + + private static ScriptRunner newScriptRunner(Connection connection) { + ScriptRunner scriptRunner = new ScriptRunner(connection); + scriptRunner.setDelimiter(";"); + scriptRunner.setStopOnError(true); + scriptRunner.setLogWriter(new PrintWriter(new NullWriter())); + return scriptRunner; + } +} diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/SQDatabaseForH2Test.java b/server/sonar-db-dao/src/test/java/org/sonar/db/SQDatabaseForH2Test.java new file mode 100644 index 00000000000..0c339676dff --- /dev/null +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/SQDatabaseForH2Test.java @@ -0,0 +1,62 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 SonarSource SA + * mailto:info 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.db; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class SQDatabaseForH2Test { + SQDatabase db = SQDatabase.newH2Database("sonar2", true); + + @Before + public void startDb() { + db.start(); + } + + @After + public void stopDb() { + db.stop(); + } + + @Test + public void shouldExecuteDdlAtStartup() throws SQLException { + Connection connection = db.getDataSource().getConnection(); + int tableCount = countTables(connection); + connection.close(); + + assertThat(tableCount).isGreaterThan(30); + } + + private static int countTables(Connection connection) throws SQLException { + int count = 0; + ResultSet resultSet = connection.getMetaData().getTables("", null, null, new String[] {"TABLE"}); + while (resultSet.next()) { + count++; + } + resultSet.close(); + return count; + } +} diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/TestDbImpl.java b/server/sonar-db-dao/src/test/java/org/sonar/db/TestDbImpl.java index d3a80308f91..b54eafd45a9 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/TestDbImpl.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/TestDbImpl.java @@ -58,7 +58,7 @@ class TestDbImpl extends CoreTestDb { if (dialect != null && !"h2".equals(dialect)) { return new DefaultDatabase(new LogbackHelper(), settings); } - return new H2Database("h2Tests" + DigestUtils.md5Hex(StringUtils.defaultString(schemaPath)), schemaPath == null); + return SQDatabase.newH2Database("h2Tests" + DigestUtils.md5Hex(StringUtils.defaultString(schemaPath)), schemaPath == null); }; Consumer schemaPathExecutor = database -> { if (schemaPath == null) { @@ -70,7 +70,7 @@ class TestDbImpl extends CoreTestDb { database.stop(); throw new AssumptionViolatedException("This test is intended to be run on H2 only"); } - ((H2Database) database).executeScript(schemaPath); + ((SQDatabase) database).executeScript(schemaPath); }; BiConsumer createMyBatis = (db, created) -> myBatis = newMyBatis(db, confExtensions); init(databaseCreator, schemaPathExecutor, createMyBatis); diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/dump/SQSchemaDumper.java b/server/sonar-db-dao/src/test/java/org/sonar/db/dump/SQSchemaDumper.java index 3dca3902210..601fa7ed490 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/dump/SQSchemaDumper.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/dump/SQSchemaDumper.java @@ -26,7 +26,7 @@ import java.sql.Statement; import java.util.ArrayList; import java.util.Comparator; import java.util.List; -import org.sonar.db.H2Database; +import org.sonar.db.SQDatabase; import static com.google.common.base.Preconditions.checkState; @@ -55,7 +55,7 @@ class SQSchemaDumper { }).thenComparing(String.CASE_INSENSITIVE_ORDER); String dumpToText() throws SQLException { - H2Database database = new H2Database("SQSchemaDumper", true); + SQDatabase database = SQDatabase.newH2Database("SQSchemaDumper", true); database.start(); try (Connection connection = database.getDataSource().getConnection(); -- cgit v1.2.3