]> source.dussan.org Git - sonarqube.git/commitdiff
use testFixtures instead of test configuration of db-core
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Wed, 28 Aug 2019 15:33:17 +0000 (17:33 +0200)
committerSonarTech <sonartech@sonarsource.com>
Mon, 2 Sep 2019 18:21:04 +0000 (20:21 +0200)
18 files changed:
server/sonar-ce-task-projectanalysis/build.gradle
server/sonar-ce/build.gradle
server/sonar-db-core/build.gradle
server/sonar-db-core/src/test/java/org/sonar/db/AbstractDbTester.java [deleted file]
server/sonar-db-core/src/test/java/org/sonar/db/CoreDbTester.java [deleted file]
server/sonar-db-core/src/test/java/org/sonar/db/CoreH2Database.java [deleted file]
server/sonar-db-core/src/test/java/org/sonar/db/CoreTestDb.java [deleted file]
server/sonar-db-core/src/test/java/org/sonar/db/DefaultOrganizationTesting.java [deleted file]
server/sonar-db-core/src/test/java/org/sonar/db/TestDb.java [deleted file]
server/sonar-db-core/src/testFixtures/java/org/sonar/db/AbstractDbTester.java [new file with mode: 0644]
server/sonar-db-core/src/testFixtures/java/org/sonar/db/CoreDbTester.java [new file with mode: 0644]
server/sonar-db-core/src/testFixtures/java/org/sonar/db/CoreH2Database.java [new file with mode: 0644]
server/sonar-db-core/src/testFixtures/java/org/sonar/db/CoreTestDb.java [new file with mode: 0644]
server/sonar-db-core/src/testFixtures/java/org/sonar/db/DefaultOrganizationTesting.java [new file with mode: 0644]
server/sonar-db-core/src/testFixtures/java/org/sonar/db/TestDb.java [new file with mode: 0644]
server/sonar-db-dao/build.gradle
server/sonar-db-migration/build.gradle
server/sonar-db-testing/build.gradle

index 4dc7020300803b94bd918329fd1892a88fe36b0d..15226d641341bed9da95a2bac20d84e079dbf5e7 100644 (file)
@@ -38,7 +38,6 @@ dependencies {
   
   compile project(':sonar-core')
   compile project(':server:sonar-ce-task')
-  compile project(':server:sonar-db-core')
   compile project(':server:sonar-db-dao')
   compile project(':server:sonar-db-migration')
   compile project(':server:sonar-process')
index 731dbaeee7bc4ac2fc1c6930b352434ddc469474..c828f41e7678fb007089879c0d9034791b36fb74 100644 (file)
@@ -19,7 +19,6 @@ dependencies {
   compile project(':server:sonar-ce-task')
   compile project(':server:sonar-ce-task-projectanalysis')
   compile project(':server:sonar-db-dao')
-  compile project(':server:sonar-db-core')
   compile project(':server:sonar-process')
   compile project(':server:sonar-ce-task')
   compile project(':server:sonar-server-common')
index e7e7485f05284c33ba10ad62a0e249e54b4298c6..f8bb54ca37371c52a6689587eb4bb69fe553bba6 100644 (file)
@@ -28,8 +28,6 @@ dependencies {
   testCompile 'com.microsoft.sqlserver:mssql-jdbc'
   testCompile 'com.oracle.jdbc:ojdbc8'
   testCompile 'com.tngtech.java:junit-dataprovider'
-  testCompile 'junit:junit'
-  testCompile 'org.assertj:assertj-core'
   testCompile 'org.mockito:mockito-core'
   testCompile 'org.postgresql:postgresql'
   testCompile project(':sonar-testing-harness')
@@ -38,6 +36,11 @@ dependencies {
   testRuntime 'com.microsoft.sqlserver:mssql-jdbc'
   testRuntime 'com.oracle.jdbc:ojdbc8'
   testRuntime 'org.postgresql:postgresql'
+
+  testFixturesApi 'junit:junit'
+  testFixturesApi 'org.assertj:assertj-core'
+
+  testFixturesCompileOnly 'com.google.code.findbugs:jsr305'
 }
 
 test {
@@ -45,17 +48,6 @@ test {
     systemProperty 'orchestrator.configUrl', System.getProperty('orchestrator.configUrl')
 }
 
-task testJar(type: Jar) {
-  classifier = 'tests'
-  from sourceSets.test.output
-}
-
-configurations { tests { extendsFrom testRuntime } }
-
-artifacts {
-   tests testJar
-}
-
 artifactoryPublish.skip = false
 
 // Used by core plugins
@@ -66,7 +58,6 @@ publishing {
       if (release) {
         artifact sourcesJar
         artifact javadocJar
-        artifact testJar
       }
     }
   }
diff --git a/server/sonar-db-core/src/test/java/org/sonar/db/AbstractDbTester.java b/server/sonar-db-core/src/test/java/org/sonar/db/AbstractDbTester.java
deleted file mode 100644 (file)
index 0b9bb16..0000000
+++ /dev/null
@@ -1,512 +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 com.google.common.base.Joiner;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Ordering;
-import java.math.BigDecimal;
-import java.sql.Clob;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.ResultSetMetaData;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.sql.Timestamp;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-import javax.annotation.CheckForNull;
-import javax.annotation.Nullable;
-import org.apache.commons.dbutils.QueryRunner;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang.StringUtils;
-import org.junit.rules.ExternalResource;
-import org.sonar.api.utils.log.Loggers;
-import org.sonar.core.util.stream.MoreCollectors;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.collect.Lists.asList;
-import static com.google.common.collect.Lists.newArrayList;
-import static com.google.common.collect.Maps.newHashMap;
-import static java.sql.ResultSetMetaData.columnNoNulls;
-import static java.sql.ResultSetMetaData.columnNullable;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.Assert.fail;
-
-public class AbstractDbTester<T extends TestDb> extends ExternalResource {
-  private static final Joiner COMMA_JOINER = Joiner.on(", ");
-  protected final T db;
-
-  public AbstractDbTester(T db) {
-    this.db = db;
-  }
-
-  public T getDb() {
-    return db;
-  }
-
-  public void executeUpdateSql(String sql, Object... params) {
-    try (Connection connection = getConnection()) {
-      new QueryRunner().update(connection, sql, params);
-      if (!connection.getAutoCommit()) {
-        connection.commit();
-      }
-    } catch (SQLException e) {
-      SQLException nextException = e.getNextException();
-      if (nextException != null) {
-        throw new IllegalStateException("Fail to execute sql: " + sql,
-          new SQLException(e.getMessage(), nextException.getSQLState(), nextException.getErrorCode(), nextException));
-      }
-      throw new IllegalStateException("Fail to execute sql: " + sql, e);
-    } catch (Exception e) {
-      throw new IllegalStateException("Fail to execute sql: " + sql, e);
-    }
-  }
-
-  public void executeDdl(String ddl) {
-    try (Connection connection = getConnection();
-      Statement stmt = connection.createStatement()) {
-      stmt.execute(ddl);
-    } catch (SQLException e) {
-      throw new IllegalStateException("Failed to execute DDL: " + ddl, e);
-    }
-  }
-
-  /**
-   * Very simple helper method to insert some data into a table.
-   * It's the responsibility of the caller to convert column values to string.
-   */
-  public void executeInsert(String table, String firstColumn, Object... others) {
-    executeInsert(table, mapOf(firstColumn, others));
-  }
-
-  private static Map<String, Object> mapOf(String firstColumn, Object... values) {
-    ImmutableMap.Builder<String, Object> builder = ImmutableMap.builder();
-    List<Object> args = asList(firstColumn, values);
-    for (int i = 0; i < args.size(); i++) {
-      String key = args.get(i).toString();
-      Object value = args.get(i + 1);
-      if (value != null) {
-        builder.put(key, value);
-      }
-      i++;
-    }
-    return builder.build();
-  }
-
-  /**
-   * Very simple helper method to insert some data into a table.
-   * It's the responsibility of the caller to convert column values to string.
-   */
-  public void executeInsert(String table, Map<String, Object> valuesByColumn) {
-    if (valuesByColumn.isEmpty()) {
-      throw new IllegalArgumentException("Values cannot be empty");
-    }
-
-    String sql = "insert into " + table.toLowerCase(Locale.ENGLISH) + " (" +
-      COMMA_JOINER.join(valuesByColumn.keySet().stream().map(t -> t.toLowerCase(Locale.ENGLISH)).toArray(String[]::new)) +
-      ") values (" +
-      COMMA_JOINER.join(Collections.nCopies(valuesByColumn.size(), '?')) +
-      ")";
-    executeUpdateSql(sql, valuesByColumn.values().toArray(new Object[valuesByColumn.size()]));
-  }
-
-  /**
-   * Returns the number of rows in the table. Example:
-   * <pre>int issues = countRowsOfTable("issues")</pre>
-   */
-  public int countRowsOfTable(String tableName) {
-    return countRowsOfTable(tableName, new NewConnectionSupplier());
-  }
-
-  protected int countRowsOfTable(String tableName, ConnectionSupplier connectionSupplier) {
-    checkArgument(StringUtils.containsNone(tableName, " "), "Parameter must be the name of a table. Got " + tableName);
-    return countSql("select count(1) from " + tableName.toLowerCase(Locale.ENGLISH), connectionSupplier);
-  }
-
-  /**
-   * Executes a SQL request starting with "SELECT COUNT(something) FROM", for example:
-   * <pre>int OpenIssues = countSql("select count('id') from issues where status is not null")</pre>
-   */
-  public int countSql(String sql) {
-    return countSql(sql, new NewConnectionSupplier());
-  }
-
-  protected int countSql(String sql, ConnectionSupplier connectionSupplier) {
-    checkArgument(StringUtils.contains(sql, "count("),
-      "Parameter must be a SQL request containing 'count(x)' function. Got " + sql);
-    try (
-      ConnectionSupplier supplier = connectionSupplier;
-      PreparedStatement stmt = supplier.get().prepareStatement(sql);
-      ResultSet rs = stmt.executeQuery()) {
-      if (rs.next()) {
-        return rs.getInt(1);
-      }
-      throw new IllegalStateException("No results for " + sql);
-
-    } catch (Exception e) {
-      throw new IllegalStateException("Fail to execute sql: " + sql, e);
-    }
-  }
-
-  public List<Map<String, Object>> select(String selectSql) {
-    return select(selectSql, new NewConnectionSupplier());
-  }
-
-  protected List<Map<String, Object>> select(String selectSql, ConnectionSupplier connectionSupplier) {
-    try (
-      ConnectionSupplier supplier = connectionSupplier;
-      PreparedStatement stmt = supplier.get().prepareStatement(selectSql);
-      ResultSet rs = stmt.executeQuery()) {
-      return getHashMap(rs);
-    } catch (Exception e) {
-      throw new IllegalStateException("Fail to execute sql: " + selectSql, e);
-    }
-  }
-
-  public Map<String, Object> selectFirst(String selectSql) {
-    return selectFirst(selectSql, new NewConnectionSupplier());
-  }
-
-  protected Map<String, Object> selectFirst(String selectSql, ConnectionSupplier connectionSupplier) {
-    List<Map<String, Object>> rows = select(selectSql, connectionSupplier);
-    if (rows.isEmpty()) {
-      throw new IllegalStateException("No results for " + selectSql);
-    } else if (rows.size() > 1) {
-      throw new IllegalStateException("Too many results for " + selectSql);
-    }
-    return rows.get(0);
-  }
-
-  private static List<Map<String, Object>> getHashMap(ResultSet resultSet) throws Exception {
-    ResultSetMetaData metaData = resultSet.getMetaData();
-    int colCount = metaData.getColumnCount();
-    List<Map<String, Object>> rows = newArrayList();
-    while (resultSet.next()) {
-      Map<String, Object> columns = newHashMap();
-      for (int i = 1; i <= colCount; i++) {
-        Object value = resultSet.getObject(i);
-        if (value instanceof Clob) {
-          Clob clob = (Clob) value;
-          value = IOUtils.toString((clob.getAsciiStream()));
-          doClobFree(clob);
-        } else if (value instanceof BigDecimal) {
-          // In Oracle, INTEGER types are mapped as BigDecimal
-          BigDecimal bgValue = ((BigDecimal) value);
-          if (bgValue.scale() == 0) {
-            value = bgValue.longValue();
-          } else {
-            value = bgValue.doubleValue();
-          }
-        } else if (value instanceof Integer) {
-          // To be consistent, all INTEGER types are mapped as Long
-          value = ((Integer) value).longValue();
-        } else if (value instanceof Byte) {
-          Byte byteValue = (Byte) value;
-          value = byteValue.intValue();
-        } else if (value instanceof Timestamp) {
-          value = new Date(((Timestamp) value).getTime());
-        }
-        columns.put(metaData.getColumnLabel(i), value);
-      }
-      rows.add(columns);
-    }
-    return rows;
-  }
-
-  public void assertColumnDefinition(String table, String column, int expectedType, @Nullable Integer expectedSize, @Nullable Boolean isNullable) {
-    try (Connection connection = getConnection();
-      PreparedStatement stmt = connection.prepareStatement("select * from " + table);
-      ResultSet res = stmt.executeQuery()) {
-      Integer columnIndex = getColumnIndex(res, column);
-      if (columnIndex == null) {
-        fail("The column '" + column + "' does not exist");
-      }
-
-      assertThat(res.getMetaData().getColumnType(columnIndex)).isEqualTo(expectedType);
-      if (expectedSize != null) {
-        assertThat(res.getMetaData().getColumnDisplaySize(columnIndex)).isEqualTo(expectedSize);
-      }
-      if (isNullable != null) {
-        assertThat(res.getMetaData().isNullable(columnIndex)).isEqualTo(isNullable ? columnNullable : columnNoNulls);
-      }
-    } catch (Exception e) {
-      throw new IllegalStateException("Fail to check column", e);
-    }
-  }
-
-  public void assertColumnDoesNotExist(String table, String column) throws SQLException {
-    try (Connection connection = getConnection();
-      PreparedStatement stmt = connection.prepareStatement("select * from " + table);
-      ResultSet res = stmt.executeQuery()) {
-      assertThat(getColumnNames(res)).doesNotContain(column);
-    }
-  }
-
-  public void assertTableDoesNotExist(String table) {
-    assertTableExists(table, false);
-  }
-
-  public void assertTableExists(String table) {
-    assertTableExists(table, true);
-  }
-
-  private void assertTableExists(String table, boolean expected) {
-    try (Connection connection = getConnection()) {
-      boolean tableExists = DatabaseUtils.tableExists(table, connection);
-      assertThat(tableExists).isEqualTo(expected);
-    } catch (Exception e) {
-      throw new IllegalStateException("Fail to check if table exists", e);
-    }
-  }
-
-  /**
-   * Verify that non-unique index exists on columns
-   */
-  public void assertIndex(String tableName, String indexName, String expectedColumn, String... expectedSecondaryColumns) {
-    assertIndexImpl(tableName, indexName, false, expectedColumn, expectedSecondaryColumns);
-  }
-
-  /**
-   * Verify that unique index exists on columns
-   */
-  public void assertUniqueIndex(String tableName, String indexName, String expectedColumn, String... expectedSecondaryColumns) {
-    assertIndexImpl(tableName, indexName, true, expectedColumn, expectedSecondaryColumns);
-  }
-
-  private void assertIndexImpl(String tableName, String indexName, boolean expectedUnique, String expectedColumn, String... expectedSecondaryColumns) {
-    try (Connection connection = getConnection();
-      ResultSet rs = connection.getMetaData().getIndexInfo(null, null, tableName.toUpperCase(Locale.ENGLISH), false, false)) {
-      List<String> onColumns = new ArrayList<>();
-      while (rs.next()) {
-        if (indexName.equalsIgnoreCase(rs.getString("INDEX_NAME"))) {
-          assertThat(rs.getBoolean("NON_UNIQUE")).isEqualTo(!expectedUnique);
-          int position = rs.getInt("ORDINAL_POSITION");
-          onColumns.add(position - 1, rs.getString("COLUMN_NAME").toLowerCase(Locale.ENGLISH));
-        }
-      }
-      assertThat(asList(expectedColumn, expectedSecondaryColumns)).isEqualTo(onColumns);
-    } catch (SQLException e) {
-      throw new IllegalStateException("Fail to check index", e);
-    }
-  }
-
-  /**
-   * Verify that index with name {@code indexName} does not exist on the table {@code tableName}
-   */
-  public void assertIndexDoesNotExist(String tableName, String indexName) {
-    try (Connection connection = getConnection();
-      ResultSet rs = connection.getMetaData().getIndexInfo(null, null, tableName.toUpperCase(Locale.ENGLISH), false, false)) {
-      List<String> indices = new ArrayList<>();
-      while (rs.next()) {
-        indices.add(rs.getString("INDEX_NAME").toLowerCase(Locale.ENGLISH));
-      }
-      assertThat(indices).doesNotContain(indexName);
-    } catch (SQLException e) {
-      throw new IllegalStateException("Fail to check existence of index", e);
-    }
-  }
-
-  public void assertPrimaryKey(String tableName, @Nullable String expectedPkName, String columnName, String... otherColumnNames) {
-    try (Connection connection = getConnection()) {
-      PK pk = pkOf(connection, tableName.toUpperCase(Locale.ENGLISH));
-      if (pk == null) {
-        pkOf(connection, tableName.toLowerCase(Locale.ENGLISH));
-      }
-      assertThat(pk).as("No primary key is defined on table %s", tableName).isNotNull();
-      if (expectedPkName != null) {
-        assertThat(pk.getName()).isEqualToIgnoringCase(expectedPkName);
-      }
-      List<String> expectedColumns = ImmutableList.copyOf(Iterables.concat(Collections.singletonList(columnName), Arrays.asList(otherColumnNames)));
-      assertThat(pk.getColumns()).as("Primary key does not have the '%s' expected columns", expectedColumns.size()).hasSize(expectedColumns.size());
-
-      Iterator<String> expectedColumnsIt = expectedColumns.iterator();
-      Iterator<String> actualColumnsIt = pk.getColumns().iterator();
-      while (expectedColumnsIt.hasNext() && actualColumnsIt.hasNext()) {
-        assertThat(actualColumnsIt.next()).isEqualToIgnoringCase(expectedColumnsIt.next());
-      }
-    } catch (SQLException e) {
-      throw new IllegalStateException("Fail to check primary key", e);
-    }
-  }
-
-  @CheckForNull
-  private PK pkOf(Connection connection, String tableName) throws SQLException {
-    try (ResultSet resultSet = connection.getMetaData().getPrimaryKeys(null, null, tableName)) {
-      String pkName = null;
-      List<PkColumn> columnNames = null;
-      while (resultSet.next()) {
-        if (columnNames == null) {
-          pkName = resultSet.getString("PK_NAME");
-          columnNames = new ArrayList<>(1);
-        } else {
-          assertThat(pkName).as("Multiple primary keys found").isEqualTo(resultSet.getString("PK_NAME"));
-        }
-        columnNames.add(new PkColumn(resultSet.getInt("KEY_SEQ") - 1, resultSet.getString("COLUMN_NAME")));
-      }
-      if (columnNames == null) {
-        return null;
-      }
-      return new PK(
-        pkName,
-        columnNames.stream()
-          .sorted(PkColumn.ORDERING_BY_INDEX)
-          .map(PkColumn::getName)
-          .collect(MoreCollectors.toList()));
-    }
-  }
-
-  private static final class PkColumn {
-    private static final Ordering<PkColumn> ORDERING_BY_INDEX = Ordering.natural().onResultOf(PkColumn::getIndex);
-
-    /** 0-based */
-    private final int index;
-    private final String name;
-
-    private PkColumn(int index, String name) {
-      this.index = index;
-      this.name = name;
-    }
-
-    public int getIndex() {
-      return index;
-    }
-
-    public String getName() {
-      return name;
-    }
-  }
-
-  @CheckForNull
-  private Integer getColumnIndex(ResultSet res, String column) {
-    try {
-      ResultSetMetaData meta = res.getMetaData();
-      int numCol = meta.getColumnCount();
-      for (int i = 1; i < numCol + 1; i++) {
-        if (meta.getColumnLabel(i).toLowerCase().equals(column.toLowerCase())) {
-          return i;
-        }
-      }
-      return null;
-
-    } catch (Exception e) {
-      throw new IllegalStateException("Fail to get column index");
-    }
-  }
-
-  private Set<String> getColumnNames(ResultSet res) {
-    try {
-      Set<String> columnNames = new HashSet<>();
-      ResultSetMetaData meta = res.getMetaData();
-      int numCol = meta.getColumnCount();
-      for (int i = 1; i < numCol + 1; i++) {
-        columnNames.add(meta.getColumnLabel(i).toLowerCase());
-      }
-      return columnNames;
-    } catch (Exception e) {
-      throw new IllegalStateException("Fail to get column names");
-    }
-  }
-
-  private static void doClobFree(Clob clob) throws SQLException {
-    try {
-      clob.free();
-    } catch (AbstractMethodError e) {
-      // JTS driver do not implement free() as it's using JDBC 3.0
-    }
-  }
-
-  public Connection openConnection() throws SQLException {
-    return getConnection();
-  }
-
-  private Connection getConnection() throws SQLException {
-    return db.getDatabase().getDataSource().getConnection();
-  }
-
-  public Database database() {
-    return db.getDatabase();
-  }
-
-  /**
-   * An {@link AutoCloseable} supplier of {@link Connection}.
-   */
-  protected interface ConnectionSupplier extends AutoCloseable {
-    Connection get() throws SQLException;
-
-    @Override
-    void close();
-  }
-
-  private static class PK {
-    @CheckForNull
-    private final String name;
-    private final List<String> columns;
-
-    private PK(@Nullable String name, List<String> columns) {
-      this.name = name;
-      this.columns = ImmutableList.copyOf(columns);
-    }
-
-    @CheckForNull
-    public String getName() {
-      return name;
-    }
-
-    public List<String> getColumns() {
-      return columns;
-    }
-  }
-
-  private class NewConnectionSupplier implements ConnectionSupplier {
-    private Connection connection;
-
-    @Override
-    public Connection get() throws SQLException {
-      if (this.connection == null) {
-        this.connection = getConnection();
-      }
-      return this.connection;
-    }
-
-    @Override
-    public void close() {
-      if (this.connection != null) {
-        try {
-          this.connection.close();
-        } catch (SQLException e) {
-          Loggers.get(CoreDbTester.class).warn("Fail to close connection", e);
-          // do not re-throw the exception
-        }
-      }
-    }
-  }
-}
diff --git a/server/sonar-db-core/src/test/java/org/sonar/db/CoreDbTester.java b/server/sonar-db-core/src/test/java/org/sonar/db/CoreDbTester.java
deleted file mode 100644 (file)
index ed85f85..0000000
+++ /dev/null
@@ -1,60 +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 org.apache.commons.lang.StringUtils;
-
-/**
- * This class should be called using @Rule.
- * Data is truncated between each tests. The schema is created between each test.
- */
-public class CoreDbTester extends AbstractDbTester<CoreTestDb> {
-  private final DefaultOrganizationTesting defaultOrganizationTesting;
-
-  private CoreDbTester(CoreTestDb testDb) {
-    super(testDb);
-    this.defaultOrganizationTesting = new DefaultOrganizationTesting(this);
-  }
-
-  public static CoreDbTester createForSchema(Class testClass, String filename) {
-    String path = StringUtils.replaceChars(testClass.getCanonicalName(), '.', '/');
-    String schemaPath = path + "/" + filename;
-    return new CoreDbTester(CoreTestDb.create(schemaPath));
-  }
-
-  public static CoreDbTester createEmpty() {
-    return new CoreDbTester(CoreTestDb.createEmpty());
-  }
-
-  @Override
-  protected void before() {
-    db.start();
-    db.truncateTables();
-  }
-
-  @Override
-  protected void after() {
-    db.stop();
-  }
-
-  public DefaultOrganizationTesting defaultOrganization() {
-    return defaultOrganizationTesting;
-  }
-}
diff --git a/server/sonar-db-core/src/test/java/org/sonar/db/CoreH2Database.java b/server/sonar-db-core/src/test/java/org/sonar/db/CoreH2Database.java
deleted file mode 100644 (file)
index f4a78ee..0000000
+++ /dev/null
@@ -1,119 +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.io.PrintWriter;
-import java.sql.Connection;
-import java.sql.SQLException;
-import javax.sql.DataSource;
-import org.apache.commons.dbcp2.BasicDataSource;
-import org.apache.commons.io.output.NullWriter;
-import org.apache.ibatis.io.Resources;
-import org.apache.ibatis.jdbc.ScriptRunner;
-import org.sonar.db.dialect.Dialect;
-import org.sonar.db.dialect.H2;
-
-import static java.lang.String.format;
-
-/**
- * H2 in-memory database, used for unit tests only against an empty DB or a provided H2 SQL script.
- */
-public class CoreH2Database implements Database {
-  private final String name;
-  private BasicDataSource datasource;
-
-  /**
-   * IMPORTANT: change DB name in order to not conflict with {@link DefaultDatabaseTest}
-   */
-  public CoreH2Database(String name) {
-    this.name = name;
-  }
-
-  @Override
-  public void start() {
-    startDatabase();
-  }
-
-  private void startDatabase() {
-    try {
-      datasource = new BasicDataSource();
-      datasource.setDriverClassName("org.h2.Driver");
-      datasource.setUsername("sonar");
-      datasource.setPassword("sonar");
-      datasource.setUrl("jdbc:h2:mem:" + name);
-    } catch (Exception e) {
-      throw new IllegalStateException("Fail to start H2", e);
-    }
-  }
-
-  public void executeScript(String classloaderPath) {
-    try (Connection connection = datasource.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;
-  }
-
-  @Override
-  public void stop() {
-    try {
-      datasource.close();
-    } catch (SQLException e) {
-      // Ignore error
-    }
-  }
-
-  public DataSource getDataSource() {
-    return datasource;
-  }
-
-  public Dialect getDialect() {
-    return new H2();
-  }
-
-  @Override
-  public void enableSqlLogging(boolean enable) {
-    throw new UnsupportedOperationException();
-  }
-
-  @Override
-  public String toString() {
-    return format("H2 Database[%s]", name);
-  }
-}
diff --git a/server/sonar-db-core/src/test/java/org/sonar/db/CoreTestDb.java b/server/sonar-db-core/src/test/java/org/sonar/db/CoreTestDb.java
deleted file mode 100644 (file)
index 4479031..0000000
+++ /dev/null
@@ -1,190 +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 java.sql.Statement;
-import java.util.function.BiConsumer;
-import java.util.function.Consumer;
-import java.util.function.Function;
-import javax.annotation.Nullable;
-import javax.sql.DataSource;
-import org.apache.commons.codec.digest.DigestUtils;
-import org.junit.AssumptionViolatedException;
-import org.sonar.api.config.Settings;
-import org.sonar.api.config.internal.MapSettings;
-import org.sonar.api.utils.log.Logger;
-import org.sonar.api.utils.log.Loggers;
-import org.sonar.db.version.SqTables;
-
-import static java.util.Objects.requireNonNull;
-import static org.sonar.process.ProcessProperties.Property.JDBC_USERNAME;
-
-/**
- * This class should be call using @ClassRule in order to create the schema once (if @Rule is used
- * the schema will be recreated before each test).
- * <p>
- * <strong>Tests which rely on this class can only be run on H2</strong> because:
- * <ul>
- *   <li>resetting the schema for each test on non-H2 database is assumed to expensive and slow</li>
- *   <li>when a specific schema is provided, this schema can't provide a syntax supported by all SGBDs and therefor only
- *       H2 is targeted</li>
- * </ul>
- */
-class CoreTestDb implements TestDb {
-
-  private Database db;
-
-  protected CoreTestDb() {
-    // use static factory method
-  }
-
-  protected CoreTestDb(Database db) {
-    this.db = db;
-  }
-
-  static CoreTestDb create(String schemaPath) {
-    requireNonNull(schemaPath, "schemaPath can't be null");
-
-    return new CoreTestDb().init(schemaPath);
-  }
-
-  static CoreTestDb createEmpty() {
-    return new CoreTestDb().init(null);
-  }
-
-  private CoreTestDb init(@Nullable String schemaPath) {
-    Consumer<Settings> noExtraSettingsLoaded = settings -> {
-    };
-    Function<Settings, Database> databaseCreator = settings -> {
-      String dialect = settings.getString("sonar.jdbc.dialect");
-
-      // test relying on CoreTestDb can only run on H2
-      if (dialect != null && !"h2".equals(dialect)) {
-        throw new AssumptionViolatedException("This test is intended to be run on H2 only");
-      }
-
-      return new CoreH2Database("h2Tests-" + (schemaPath == null ? "empty" : DigestUtils.md5Hex(schemaPath)));
-    };
-    Consumer<Database> databaseInitializer = database -> {
-      if (schemaPath == null) {
-        return;
-      }
-
-      ((CoreH2Database) database).executeScript(schemaPath);
-    };
-    BiConsumer<Database, Boolean> noPostStartAction = (db, created) -> {
-    };
-
-    init(noExtraSettingsLoaded, databaseCreator, databaseInitializer, noPostStartAction);
-    return this;
-  }
-
-  protected void init(Consumer<Settings> settingsLoader,
-    Function<Settings, Database> databaseCreator,
-    Consumer<Database> databaseInitializer,
-    BiConsumer<Database, Boolean> extendedStart) {
-    if (db == null) {
-      Settings settings = new MapSettings().addProperties(System.getProperties());
-      settingsLoader.accept(settings);
-      logJdbcSettings(settings);
-      db = databaseCreator.apply(settings);
-      db.start();
-
-      databaseInitializer.accept(db);
-      Loggers.get(getClass()).debug("Test Database: " + db);
-
-      String login = settings.getString(JDBC_USERNAME.getKey());
-
-      extendedStart.accept(db, true);
-    } else {
-      extendedStart.accept(db, false);
-    }
-  }
-
-  public void truncateTables() {
-    try {
-      truncateDatabase(getDatabase().getDataSource());
-    } catch (SQLException e) {
-      throw new IllegalStateException("Fail to truncate db tables", e);
-    }
-  }
-
-  private void truncateDatabase(DataSource dataSource) throws SQLException {
-    try (Connection connection = dataSource.getConnection()) {
-      connection.setAutoCommit(false);
-      try (Statement statement = connection.createStatement()) {
-        for (String table : SqTables.TABLES) {
-          try {
-            if (shouldTruncate(connection, table)) {
-              statement.executeUpdate(truncateSql(table));
-              connection.commit();
-            }
-          } catch (Exception e) {
-            connection.rollback();
-            throw new IllegalStateException("Fail to truncate table " + table, e);
-          }
-        }
-      }
-    }
-  }
-
-  private static boolean shouldTruncate(Connection connection, String table) {
-    try (Statement stmt = connection.createStatement();
-      ResultSet rs = stmt.executeQuery("select count(1) from " + table)) {
-      if (rs.next()) {
-        return rs.getInt(1) > 0;
-      }
-
-    } catch (SQLException ignored) {
-      // probably because table does not exist. That's the case with H2 tests.
-    }
-    return false;
-  }
-
-  private static String truncateSql(String table) {
-    return "TRUNCATE TABLE " + table;
-  }
-
-  @Override
-  public Database getDatabase() {
-    return db;
-  }
-
-  @Override
-  public void start() {
-    // everything is done in init
-  }
-
-  @Override
-  public void stop() {
-    db.stop();
-  }
-
-  private void logJdbcSettings(Settings settings) {
-    Logger logger = Loggers.get(getClass());
-    for (String key : settings.getKeysStartingWith("sonar.jdbc")) {
-      logger.info(key + ": " + settings.getString(key));
-    }
-  }
-
-}
diff --git a/server/sonar-db-core/src/test/java/org/sonar/db/DefaultOrganizationTesting.java b/server/sonar-db-core/src/test/java/org/sonar/db/DefaultOrganizationTesting.java
deleted file mode 100644 (file)
index f2bb100..0000000
+++ /dev/null
@@ -1,66 +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;
-
-public class DefaultOrganizationTesting {
-  private static final String TABLE_ORGANIZATIONS = "organizations";
-  private static final String DEFAULT_ORGANIZATION_UUID = "def-org";
-
-  private final CoreDbTester db;
-
-  public DefaultOrganizationTesting(CoreDbTester db) {
-    this.db = db;
-  }
-
-  public String setupDefaultOrganization() {
-    insertInternalProperty(DEFAULT_ORGANIZATION_UUID);
-    insertOrganization(DEFAULT_ORGANIZATION_UUID);
-    return DEFAULT_ORGANIZATION_UUID;
-  }
-
-  public String insertOrganization() {
-    insertOrganization(DEFAULT_ORGANIZATION_UUID);
-    return DEFAULT_ORGANIZATION_UUID;
-  }
-
-  public void insertOrganization(String uuid) {
-    db.executeInsert(
-      TABLE_ORGANIZATIONS,
-      "UUID", uuid,
-      "KEE", uuid,
-      "NAME", uuid,
-      "CREATED_AT", "1000",
-      "UPDATED_AT", "1000");
-  }
-
-  public String insertInternalProperty() {
-    insertInternalProperty(DEFAULT_ORGANIZATION_UUID);
-    return DEFAULT_ORGANIZATION_UUID;
-  }
-
-  public void insertInternalProperty(String defaultOrganizationUuid) {
-    db.executeInsert(
-      "INTERNAL_PROPERTIES",
-      "KEE", "organization.default",
-      "IS_EMPTY", "false",
-      "TEXT_VALUE", defaultOrganizationUuid);
-  }
-
-}
diff --git a/server/sonar-db-core/src/test/java/org/sonar/db/TestDb.java b/server/sonar-db-core/src/test/java/org/sonar/db/TestDb.java
deleted file mode 100644 (file)
index c87e371..0000000
+++ /dev/null
@@ -1,29 +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;
-
-public interface TestDb {
-  void start();
-
-  void stop();
-
-  Database getDatabase();
-
-}
diff --git a/server/sonar-db-core/src/testFixtures/java/org/sonar/db/AbstractDbTester.java b/server/sonar-db-core/src/testFixtures/java/org/sonar/db/AbstractDbTester.java
new file mode 100644 (file)
index 0000000..0b9bb16
--- /dev/null
@@ -0,0 +1,512 @@
+/*
+ * 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 com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Ordering;
+import java.math.BigDecimal;
+import java.sql.Clob;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import org.apache.commons.dbutils.QueryRunner;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.StringUtils;
+import org.junit.rules.ExternalResource;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.core.util.stream.MoreCollectors;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.Lists.asList;
+import static com.google.common.collect.Lists.newArrayList;
+import static com.google.common.collect.Maps.newHashMap;
+import static java.sql.ResultSetMetaData.columnNoNulls;
+import static java.sql.ResultSetMetaData.columnNullable;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.fail;
+
+public class AbstractDbTester<T extends TestDb> extends ExternalResource {
+  private static final Joiner COMMA_JOINER = Joiner.on(", ");
+  protected final T db;
+
+  public AbstractDbTester(T db) {
+    this.db = db;
+  }
+
+  public T getDb() {
+    return db;
+  }
+
+  public void executeUpdateSql(String sql, Object... params) {
+    try (Connection connection = getConnection()) {
+      new QueryRunner().update(connection, sql, params);
+      if (!connection.getAutoCommit()) {
+        connection.commit();
+      }
+    } catch (SQLException e) {
+      SQLException nextException = e.getNextException();
+      if (nextException != null) {
+        throw new IllegalStateException("Fail to execute sql: " + sql,
+          new SQLException(e.getMessage(), nextException.getSQLState(), nextException.getErrorCode(), nextException));
+      }
+      throw new IllegalStateException("Fail to execute sql: " + sql, e);
+    } catch (Exception e) {
+      throw new IllegalStateException("Fail to execute sql: " + sql, e);
+    }
+  }
+
+  public void executeDdl(String ddl) {
+    try (Connection connection = getConnection();
+      Statement stmt = connection.createStatement()) {
+      stmt.execute(ddl);
+    } catch (SQLException e) {
+      throw new IllegalStateException("Failed to execute DDL: " + ddl, e);
+    }
+  }
+
+  /**
+   * Very simple helper method to insert some data into a table.
+   * It's the responsibility of the caller to convert column values to string.
+   */
+  public void executeInsert(String table, String firstColumn, Object... others) {
+    executeInsert(table, mapOf(firstColumn, others));
+  }
+
+  private static Map<String, Object> mapOf(String firstColumn, Object... values) {
+    ImmutableMap.Builder<String, Object> builder = ImmutableMap.builder();
+    List<Object> args = asList(firstColumn, values);
+    for (int i = 0; i < args.size(); i++) {
+      String key = args.get(i).toString();
+      Object value = args.get(i + 1);
+      if (value != null) {
+        builder.put(key, value);
+      }
+      i++;
+    }
+    return builder.build();
+  }
+
+  /**
+   * Very simple helper method to insert some data into a table.
+   * It's the responsibility of the caller to convert column values to string.
+   */
+  public void executeInsert(String table, Map<String, Object> valuesByColumn) {
+    if (valuesByColumn.isEmpty()) {
+      throw new IllegalArgumentException("Values cannot be empty");
+    }
+
+    String sql = "insert into " + table.toLowerCase(Locale.ENGLISH) + " (" +
+      COMMA_JOINER.join(valuesByColumn.keySet().stream().map(t -> t.toLowerCase(Locale.ENGLISH)).toArray(String[]::new)) +
+      ") values (" +
+      COMMA_JOINER.join(Collections.nCopies(valuesByColumn.size(), '?')) +
+      ")";
+    executeUpdateSql(sql, valuesByColumn.values().toArray(new Object[valuesByColumn.size()]));
+  }
+
+  /**
+   * Returns the number of rows in the table. Example:
+   * <pre>int issues = countRowsOfTable("issues")</pre>
+   */
+  public int countRowsOfTable(String tableName) {
+    return countRowsOfTable(tableName, new NewConnectionSupplier());
+  }
+
+  protected int countRowsOfTable(String tableName, ConnectionSupplier connectionSupplier) {
+    checkArgument(StringUtils.containsNone(tableName, " "), "Parameter must be the name of a table. Got " + tableName);
+    return countSql("select count(1) from " + tableName.toLowerCase(Locale.ENGLISH), connectionSupplier);
+  }
+
+  /**
+   * Executes a SQL request starting with "SELECT COUNT(something) FROM", for example:
+   * <pre>int OpenIssues = countSql("select count('id') from issues where status is not null")</pre>
+   */
+  public int countSql(String sql) {
+    return countSql(sql, new NewConnectionSupplier());
+  }
+
+  protected int countSql(String sql, ConnectionSupplier connectionSupplier) {
+    checkArgument(StringUtils.contains(sql, "count("),
+      "Parameter must be a SQL request containing 'count(x)' function. Got " + sql);
+    try (
+      ConnectionSupplier supplier = connectionSupplier;
+      PreparedStatement stmt = supplier.get().prepareStatement(sql);
+      ResultSet rs = stmt.executeQuery()) {
+      if (rs.next()) {
+        return rs.getInt(1);
+      }
+      throw new IllegalStateException("No results for " + sql);
+
+    } catch (Exception e) {
+      throw new IllegalStateException("Fail to execute sql: " + sql, e);
+    }
+  }
+
+  public List<Map<String, Object>> select(String selectSql) {
+    return select(selectSql, new NewConnectionSupplier());
+  }
+
+  protected List<Map<String, Object>> select(String selectSql, ConnectionSupplier connectionSupplier) {
+    try (
+      ConnectionSupplier supplier = connectionSupplier;
+      PreparedStatement stmt = supplier.get().prepareStatement(selectSql);
+      ResultSet rs = stmt.executeQuery()) {
+      return getHashMap(rs);
+    } catch (Exception e) {
+      throw new IllegalStateException("Fail to execute sql: " + selectSql, e);
+    }
+  }
+
+  public Map<String, Object> selectFirst(String selectSql) {
+    return selectFirst(selectSql, new NewConnectionSupplier());
+  }
+
+  protected Map<String, Object> selectFirst(String selectSql, ConnectionSupplier connectionSupplier) {
+    List<Map<String, Object>> rows = select(selectSql, connectionSupplier);
+    if (rows.isEmpty()) {
+      throw new IllegalStateException("No results for " + selectSql);
+    } else if (rows.size() > 1) {
+      throw new IllegalStateException("Too many results for " + selectSql);
+    }
+    return rows.get(0);
+  }
+
+  private static List<Map<String, Object>> getHashMap(ResultSet resultSet) throws Exception {
+    ResultSetMetaData metaData = resultSet.getMetaData();
+    int colCount = metaData.getColumnCount();
+    List<Map<String, Object>> rows = newArrayList();
+    while (resultSet.next()) {
+      Map<String, Object> columns = newHashMap();
+      for (int i = 1; i <= colCount; i++) {
+        Object value = resultSet.getObject(i);
+        if (value instanceof Clob) {
+          Clob clob = (Clob) value;
+          value = IOUtils.toString((clob.getAsciiStream()));
+          doClobFree(clob);
+        } else if (value instanceof BigDecimal) {
+          // In Oracle, INTEGER types are mapped as BigDecimal
+          BigDecimal bgValue = ((BigDecimal) value);
+          if (bgValue.scale() == 0) {
+            value = bgValue.longValue();
+          } else {
+            value = bgValue.doubleValue();
+          }
+        } else if (value instanceof Integer) {
+          // To be consistent, all INTEGER types are mapped as Long
+          value = ((Integer) value).longValue();
+        } else if (value instanceof Byte) {
+          Byte byteValue = (Byte) value;
+          value = byteValue.intValue();
+        } else if (value instanceof Timestamp) {
+          value = new Date(((Timestamp) value).getTime());
+        }
+        columns.put(metaData.getColumnLabel(i), value);
+      }
+      rows.add(columns);
+    }
+    return rows;
+  }
+
+  public void assertColumnDefinition(String table, String column, int expectedType, @Nullable Integer expectedSize, @Nullable Boolean isNullable) {
+    try (Connection connection = getConnection();
+      PreparedStatement stmt = connection.prepareStatement("select * from " + table);
+      ResultSet res = stmt.executeQuery()) {
+      Integer columnIndex = getColumnIndex(res, column);
+      if (columnIndex == null) {
+        fail("The column '" + column + "' does not exist");
+      }
+
+      assertThat(res.getMetaData().getColumnType(columnIndex)).isEqualTo(expectedType);
+      if (expectedSize != null) {
+        assertThat(res.getMetaData().getColumnDisplaySize(columnIndex)).isEqualTo(expectedSize);
+      }
+      if (isNullable != null) {
+        assertThat(res.getMetaData().isNullable(columnIndex)).isEqualTo(isNullable ? columnNullable : columnNoNulls);
+      }
+    } catch (Exception e) {
+      throw new IllegalStateException("Fail to check column", e);
+    }
+  }
+
+  public void assertColumnDoesNotExist(String table, String column) throws SQLException {
+    try (Connection connection = getConnection();
+      PreparedStatement stmt = connection.prepareStatement("select * from " + table);
+      ResultSet res = stmt.executeQuery()) {
+      assertThat(getColumnNames(res)).doesNotContain(column);
+    }
+  }
+
+  public void assertTableDoesNotExist(String table) {
+    assertTableExists(table, false);
+  }
+
+  public void assertTableExists(String table) {
+    assertTableExists(table, true);
+  }
+
+  private void assertTableExists(String table, boolean expected) {
+    try (Connection connection = getConnection()) {
+      boolean tableExists = DatabaseUtils.tableExists(table, connection);
+      assertThat(tableExists).isEqualTo(expected);
+    } catch (Exception e) {
+      throw new IllegalStateException("Fail to check if table exists", e);
+    }
+  }
+
+  /**
+   * Verify that non-unique index exists on columns
+   */
+  public void assertIndex(String tableName, String indexName, String expectedColumn, String... expectedSecondaryColumns) {
+    assertIndexImpl(tableName, indexName, false, expectedColumn, expectedSecondaryColumns);
+  }
+
+  /**
+   * Verify that unique index exists on columns
+   */
+  public void assertUniqueIndex(String tableName, String indexName, String expectedColumn, String... expectedSecondaryColumns) {
+    assertIndexImpl(tableName, indexName, true, expectedColumn, expectedSecondaryColumns);
+  }
+
+  private void assertIndexImpl(String tableName, String indexName, boolean expectedUnique, String expectedColumn, String... expectedSecondaryColumns) {
+    try (Connection connection = getConnection();
+      ResultSet rs = connection.getMetaData().getIndexInfo(null, null, tableName.toUpperCase(Locale.ENGLISH), false, false)) {
+      List<String> onColumns = new ArrayList<>();
+      while (rs.next()) {
+        if (indexName.equalsIgnoreCase(rs.getString("INDEX_NAME"))) {
+          assertThat(rs.getBoolean("NON_UNIQUE")).isEqualTo(!expectedUnique);
+          int position = rs.getInt("ORDINAL_POSITION");
+          onColumns.add(position - 1, rs.getString("COLUMN_NAME").toLowerCase(Locale.ENGLISH));
+        }
+      }
+      assertThat(asList(expectedColumn, expectedSecondaryColumns)).isEqualTo(onColumns);
+    } catch (SQLException e) {
+      throw new IllegalStateException("Fail to check index", e);
+    }
+  }
+
+  /**
+   * Verify that index with name {@code indexName} does not exist on the table {@code tableName}
+   */
+  public void assertIndexDoesNotExist(String tableName, String indexName) {
+    try (Connection connection = getConnection();
+      ResultSet rs = connection.getMetaData().getIndexInfo(null, null, tableName.toUpperCase(Locale.ENGLISH), false, false)) {
+      List<String> indices = new ArrayList<>();
+      while (rs.next()) {
+        indices.add(rs.getString("INDEX_NAME").toLowerCase(Locale.ENGLISH));
+      }
+      assertThat(indices).doesNotContain(indexName);
+    } catch (SQLException e) {
+      throw new IllegalStateException("Fail to check existence of index", e);
+    }
+  }
+
+  public void assertPrimaryKey(String tableName, @Nullable String expectedPkName, String columnName, String... otherColumnNames) {
+    try (Connection connection = getConnection()) {
+      PK pk = pkOf(connection, tableName.toUpperCase(Locale.ENGLISH));
+      if (pk == null) {
+        pkOf(connection, tableName.toLowerCase(Locale.ENGLISH));
+      }
+      assertThat(pk).as("No primary key is defined on table %s", tableName).isNotNull();
+      if (expectedPkName != null) {
+        assertThat(pk.getName()).isEqualToIgnoringCase(expectedPkName);
+      }
+      List<String> expectedColumns = ImmutableList.copyOf(Iterables.concat(Collections.singletonList(columnName), Arrays.asList(otherColumnNames)));
+      assertThat(pk.getColumns()).as("Primary key does not have the '%s' expected columns", expectedColumns.size()).hasSize(expectedColumns.size());
+
+      Iterator<String> expectedColumnsIt = expectedColumns.iterator();
+      Iterator<String> actualColumnsIt = pk.getColumns().iterator();
+      while (expectedColumnsIt.hasNext() && actualColumnsIt.hasNext()) {
+        assertThat(actualColumnsIt.next()).isEqualToIgnoringCase(expectedColumnsIt.next());
+      }
+    } catch (SQLException e) {
+      throw new IllegalStateException("Fail to check primary key", e);
+    }
+  }
+
+  @CheckForNull
+  private PK pkOf(Connection connection, String tableName) throws SQLException {
+    try (ResultSet resultSet = connection.getMetaData().getPrimaryKeys(null, null, tableName)) {
+      String pkName = null;
+      List<PkColumn> columnNames = null;
+      while (resultSet.next()) {
+        if (columnNames == null) {
+          pkName = resultSet.getString("PK_NAME");
+          columnNames = new ArrayList<>(1);
+        } else {
+          assertThat(pkName).as("Multiple primary keys found").isEqualTo(resultSet.getString("PK_NAME"));
+        }
+        columnNames.add(new PkColumn(resultSet.getInt("KEY_SEQ") - 1, resultSet.getString("COLUMN_NAME")));
+      }
+      if (columnNames == null) {
+        return null;
+      }
+      return new PK(
+        pkName,
+        columnNames.stream()
+          .sorted(PkColumn.ORDERING_BY_INDEX)
+          .map(PkColumn::getName)
+          .collect(MoreCollectors.toList()));
+    }
+  }
+
+  private static final class PkColumn {
+    private static final Ordering<PkColumn> ORDERING_BY_INDEX = Ordering.natural().onResultOf(PkColumn::getIndex);
+
+    /** 0-based */
+    private final int index;
+    private final String name;
+
+    private PkColumn(int index, String name) {
+      this.index = index;
+      this.name = name;
+    }
+
+    public int getIndex() {
+      return index;
+    }
+
+    public String getName() {
+      return name;
+    }
+  }
+
+  @CheckForNull
+  private Integer getColumnIndex(ResultSet res, String column) {
+    try {
+      ResultSetMetaData meta = res.getMetaData();
+      int numCol = meta.getColumnCount();
+      for (int i = 1; i < numCol + 1; i++) {
+        if (meta.getColumnLabel(i).toLowerCase().equals(column.toLowerCase())) {
+          return i;
+        }
+      }
+      return null;
+
+    } catch (Exception e) {
+      throw new IllegalStateException("Fail to get column index");
+    }
+  }
+
+  private Set<String> getColumnNames(ResultSet res) {
+    try {
+      Set<String> columnNames = new HashSet<>();
+      ResultSetMetaData meta = res.getMetaData();
+      int numCol = meta.getColumnCount();
+      for (int i = 1; i < numCol + 1; i++) {
+        columnNames.add(meta.getColumnLabel(i).toLowerCase());
+      }
+      return columnNames;
+    } catch (Exception e) {
+      throw new IllegalStateException("Fail to get column names");
+    }
+  }
+
+  private static void doClobFree(Clob clob) throws SQLException {
+    try {
+      clob.free();
+    } catch (AbstractMethodError e) {
+      // JTS driver do not implement free() as it's using JDBC 3.0
+    }
+  }
+
+  public Connection openConnection() throws SQLException {
+    return getConnection();
+  }
+
+  private Connection getConnection() throws SQLException {
+    return db.getDatabase().getDataSource().getConnection();
+  }
+
+  public Database database() {
+    return db.getDatabase();
+  }
+
+  /**
+   * An {@link AutoCloseable} supplier of {@link Connection}.
+   */
+  protected interface ConnectionSupplier extends AutoCloseable {
+    Connection get() throws SQLException;
+
+    @Override
+    void close();
+  }
+
+  private static class PK {
+    @CheckForNull
+    private final String name;
+    private final List<String> columns;
+
+    private PK(@Nullable String name, List<String> columns) {
+      this.name = name;
+      this.columns = ImmutableList.copyOf(columns);
+    }
+
+    @CheckForNull
+    public String getName() {
+      return name;
+    }
+
+    public List<String> getColumns() {
+      return columns;
+    }
+  }
+
+  private class NewConnectionSupplier implements ConnectionSupplier {
+    private Connection connection;
+
+    @Override
+    public Connection get() throws SQLException {
+      if (this.connection == null) {
+        this.connection = getConnection();
+      }
+      return this.connection;
+    }
+
+    @Override
+    public void close() {
+      if (this.connection != null) {
+        try {
+          this.connection.close();
+        } catch (SQLException e) {
+          Loggers.get(CoreDbTester.class).warn("Fail to close connection", e);
+          // do not re-throw the exception
+        }
+      }
+    }
+  }
+}
diff --git a/server/sonar-db-core/src/testFixtures/java/org/sonar/db/CoreDbTester.java b/server/sonar-db-core/src/testFixtures/java/org/sonar/db/CoreDbTester.java
new file mode 100644 (file)
index 0000000..ed85f85
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * 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 org.apache.commons.lang.StringUtils;
+
+/**
+ * This class should be called using @Rule.
+ * Data is truncated between each tests. The schema is created between each test.
+ */
+public class CoreDbTester extends AbstractDbTester<CoreTestDb> {
+  private final DefaultOrganizationTesting defaultOrganizationTesting;
+
+  private CoreDbTester(CoreTestDb testDb) {
+    super(testDb);
+    this.defaultOrganizationTesting = new DefaultOrganizationTesting(this);
+  }
+
+  public static CoreDbTester createForSchema(Class testClass, String filename) {
+    String path = StringUtils.replaceChars(testClass.getCanonicalName(), '.', '/');
+    String schemaPath = path + "/" + filename;
+    return new CoreDbTester(CoreTestDb.create(schemaPath));
+  }
+
+  public static CoreDbTester createEmpty() {
+    return new CoreDbTester(CoreTestDb.createEmpty());
+  }
+
+  @Override
+  protected void before() {
+    db.start();
+    db.truncateTables();
+  }
+
+  @Override
+  protected void after() {
+    db.stop();
+  }
+
+  public DefaultOrganizationTesting defaultOrganization() {
+    return defaultOrganizationTesting;
+  }
+}
diff --git a/server/sonar-db-core/src/testFixtures/java/org/sonar/db/CoreH2Database.java b/server/sonar-db-core/src/testFixtures/java/org/sonar/db/CoreH2Database.java
new file mode 100644 (file)
index 0000000..f4a78ee
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * 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 javax.sql.DataSource;
+import org.apache.commons.dbcp2.BasicDataSource;
+import org.apache.commons.io.output.NullWriter;
+import org.apache.ibatis.io.Resources;
+import org.apache.ibatis.jdbc.ScriptRunner;
+import org.sonar.db.dialect.Dialect;
+import org.sonar.db.dialect.H2;
+
+import static java.lang.String.format;
+
+/**
+ * H2 in-memory database, used for unit tests only against an empty DB or a provided H2 SQL script.
+ */
+public class CoreH2Database implements Database {
+  private final String name;
+  private BasicDataSource datasource;
+
+  /**
+   * IMPORTANT: change DB name in order to not conflict with {@link DefaultDatabaseTest}
+   */
+  public CoreH2Database(String name) {
+    this.name = name;
+  }
+
+  @Override
+  public void start() {
+    startDatabase();
+  }
+
+  private void startDatabase() {
+    try {
+      datasource = new BasicDataSource();
+      datasource.setDriverClassName("org.h2.Driver");
+      datasource.setUsername("sonar");
+      datasource.setPassword("sonar");
+      datasource.setUrl("jdbc:h2:mem:" + name);
+    } catch (Exception e) {
+      throw new IllegalStateException("Fail to start H2", e);
+    }
+  }
+
+  public void executeScript(String classloaderPath) {
+    try (Connection connection = datasource.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;
+  }
+
+  @Override
+  public void stop() {
+    try {
+      datasource.close();
+    } catch (SQLException e) {
+      // Ignore error
+    }
+  }
+
+  public DataSource getDataSource() {
+    return datasource;
+  }
+
+  public Dialect getDialect() {
+    return new H2();
+  }
+
+  @Override
+  public void enableSqlLogging(boolean enable) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public String toString() {
+    return format("H2 Database[%s]", name);
+  }
+}
diff --git a/server/sonar-db-core/src/testFixtures/java/org/sonar/db/CoreTestDb.java b/server/sonar-db-core/src/testFixtures/java/org/sonar/db/CoreTestDb.java
new file mode 100644 (file)
index 0000000..4479031
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * 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 java.sql.Statement;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import javax.annotation.Nullable;
+import javax.sql.DataSource;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.junit.AssumptionViolatedException;
+import org.sonar.api.config.Settings;
+import org.sonar.api.config.internal.MapSettings;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.db.version.SqTables;
+
+import static java.util.Objects.requireNonNull;
+import static org.sonar.process.ProcessProperties.Property.JDBC_USERNAME;
+
+/**
+ * This class should be call using @ClassRule in order to create the schema once (if @Rule is used
+ * the schema will be recreated before each test).
+ * <p>
+ * <strong>Tests which rely on this class can only be run on H2</strong> because:
+ * <ul>
+ *   <li>resetting the schema for each test on non-H2 database is assumed to expensive and slow</li>
+ *   <li>when a specific schema is provided, this schema can't provide a syntax supported by all SGBDs and therefor only
+ *       H2 is targeted</li>
+ * </ul>
+ */
+class CoreTestDb implements TestDb {
+
+  private Database db;
+
+  protected CoreTestDb() {
+    // use static factory method
+  }
+
+  protected CoreTestDb(Database db) {
+    this.db = db;
+  }
+
+  static CoreTestDb create(String schemaPath) {
+    requireNonNull(schemaPath, "schemaPath can't be null");
+
+    return new CoreTestDb().init(schemaPath);
+  }
+
+  static CoreTestDb createEmpty() {
+    return new CoreTestDb().init(null);
+  }
+
+  private CoreTestDb init(@Nullable String schemaPath) {
+    Consumer<Settings> noExtraSettingsLoaded = settings -> {
+    };
+    Function<Settings, Database> databaseCreator = settings -> {
+      String dialect = settings.getString("sonar.jdbc.dialect");
+
+      // test relying on CoreTestDb can only run on H2
+      if (dialect != null && !"h2".equals(dialect)) {
+        throw new AssumptionViolatedException("This test is intended to be run on H2 only");
+      }
+
+      return new CoreH2Database("h2Tests-" + (schemaPath == null ? "empty" : DigestUtils.md5Hex(schemaPath)));
+    };
+    Consumer<Database> databaseInitializer = database -> {
+      if (schemaPath == null) {
+        return;
+      }
+
+      ((CoreH2Database) database).executeScript(schemaPath);
+    };
+    BiConsumer<Database, Boolean> noPostStartAction = (db, created) -> {
+    };
+
+    init(noExtraSettingsLoaded, databaseCreator, databaseInitializer, noPostStartAction);
+    return this;
+  }
+
+  protected void init(Consumer<Settings> settingsLoader,
+    Function<Settings, Database> databaseCreator,
+    Consumer<Database> databaseInitializer,
+    BiConsumer<Database, Boolean> extendedStart) {
+    if (db == null) {
+      Settings settings = new MapSettings().addProperties(System.getProperties());
+      settingsLoader.accept(settings);
+      logJdbcSettings(settings);
+      db = databaseCreator.apply(settings);
+      db.start();
+
+      databaseInitializer.accept(db);
+      Loggers.get(getClass()).debug("Test Database: " + db);
+
+      String login = settings.getString(JDBC_USERNAME.getKey());
+
+      extendedStart.accept(db, true);
+    } else {
+      extendedStart.accept(db, false);
+    }
+  }
+
+  public void truncateTables() {
+    try {
+      truncateDatabase(getDatabase().getDataSource());
+    } catch (SQLException e) {
+      throw new IllegalStateException("Fail to truncate db tables", e);
+    }
+  }
+
+  private void truncateDatabase(DataSource dataSource) throws SQLException {
+    try (Connection connection = dataSource.getConnection()) {
+      connection.setAutoCommit(false);
+      try (Statement statement = connection.createStatement()) {
+        for (String table : SqTables.TABLES) {
+          try {
+            if (shouldTruncate(connection, table)) {
+              statement.executeUpdate(truncateSql(table));
+              connection.commit();
+            }
+          } catch (Exception e) {
+            connection.rollback();
+            throw new IllegalStateException("Fail to truncate table " + table, e);
+          }
+        }
+      }
+    }
+  }
+
+  private static boolean shouldTruncate(Connection connection, String table) {
+    try (Statement stmt = connection.createStatement();
+      ResultSet rs = stmt.executeQuery("select count(1) from " + table)) {
+      if (rs.next()) {
+        return rs.getInt(1) > 0;
+      }
+
+    } catch (SQLException ignored) {
+      // probably because table does not exist. That's the case with H2 tests.
+    }
+    return false;
+  }
+
+  private static String truncateSql(String table) {
+    return "TRUNCATE TABLE " + table;
+  }
+
+  @Override
+  public Database getDatabase() {
+    return db;
+  }
+
+  @Override
+  public void start() {
+    // everything is done in init
+  }
+
+  @Override
+  public void stop() {
+    db.stop();
+  }
+
+  private void logJdbcSettings(Settings settings) {
+    Logger logger = Loggers.get(getClass());
+    for (String key : settings.getKeysStartingWith("sonar.jdbc")) {
+      logger.info(key + ": " + settings.getString(key));
+    }
+  }
+
+}
diff --git a/server/sonar-db-core/src/testFixtures/java/org/sonar/db/DefaultOrganizationTesting.java b/server/sonar-db-core/src/testFixtures/java/org/sonar/db/DefaultOrganizationTesting.java
new file mode 100644 (file)
index 0000000..f2bb100
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * 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;
+
+public class DefaultOrganizationTesting {
+  private static final String TABLE_ORGANIZATIONS = "organizations";
+  private static final String DEFAULT_ORGANIZATION_UUID = "def-org";
+
+  private final CoreDbTester db;
+
+  public DefaultOrganizationTesting(CoreDbTester db) {
+    this.db = db;
+  }
+
+  public String setupDefaultOrganization() {
+    insertInternalProperty(DEFAULT_ORGANIZATION_UUID);
+    insertOrganization(DEFAULT_ORGANIZATION_UUID);
+    return DEFAULT_ORGANIZATION_UUID;
+  }
+
+  public String insertOrganization() {
+    insertOrganization(DEFAULT_ORGANIZATION_UUID);
+    return DEFAULT_ORGANIZATION_UUID;
+  }
+
+  public void insertOrganization(String uuid) {
+    db.executeInsert(
+      TABLE_ORGANIZATIONS,
+      "UUID", uuid,
+      "KEE", uuid,
+      "NAME", uuid,
+      "CREATED_AT", "1000",
+      "UPDATED_AT", "1000");
+  }
+
+  public String insertInternalProperty() {
+    insertInternalProperty(DEFAULT_ORGANIZATION_UUID);
+    return DEFAULT_ORGANIZATION_UUID;
+  }
+
+  public void insertInternalProperty(String defaultOrganizationUuid) {
+    db.executeInsert(
+      "INTERNAL_PROPERTIES",
+      "KEE", "organization.default",
+      "IS_EMPTY", "false",
+      "TEXT_VALUE", defaultOrganizationUuid);
+  }
+
+}
diff --git a/server/sonar-db-core/src/testFixtures/java/org/sonar/db/TestDb.java b/server/sonar-db-core/src/testFixtures/java/org/sonar/db/TestDb.java
new file mode 100644 (file)
index 0000000..c87e371
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * 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;
+
+public interface TestDb {
+  void start();
+
+  void stop();
+
+  Database getDatabase();
+
+}
index b6f989e601f686e785f796c702fc6c6c24aaf37c..ac0c3db3aa8b388105f1584b423be907f3849e3b 100644 (file)
@@ -30,7 +30,7 @@ dependencies {
   testCompile 'org.mockito:mockito-core'
   testCompile 'org.sonarsource.orchestrator:sonar-orchestrator'
   testCompile project(':sonar-testing-harness')
-  testCompile project(':server:sonar-db-core').sourceSets.test.output
+  testCompile testFixtures(project(':server:sonar-db-core'))
   testCompile project(':sonar-plugin-api-impl')
 
   testCompileOnly 'com.google.code.findbugs:jsr305'
index 4c36d880a94c93976e1d04ade09d823a738c1a94..4dd9a0d0b5808f9839328ed3b65f28d7e62017e3 100644 (file)
@@ -27,7 +27,7 @@ dependencies {
   testCompile 'org.mockito:mockito-core'
   testCompile project(':sonar-scanner-protocol')
   testCompile project(':sonar-testing-harness')
-  testCompile project(':server:sonar-db-core').sourceSets.test.output
+  testCompile testFixtures(project(':server:sonar-db-core'))
 
   testRuntime 'com.h2database:h2'
   testRuntime 'com.microsoft.sqlserver:mssql-jdbc'
index d3ad8365f54ae94507fb05139e549157465dee5c..0b76a66038ddc059a2a0550d697015b331c4a75f 100644 (file)
@@ -8,6 +8,6 @@ dependencies {
   // please keep the list grouped by configuration and ordered by name
 
   compile 'com.h2database:h2'
-  compile project(':server:sonar-db-core').sourceSets.test.output
+  compile testFixtures(project(':server:sonar-db-core'))
   compile project(':server:sonar-db-dao').sourceSets.test.output
 }