001 /* 002 * Sonar, open source software quality management tool. 003 * Copyright (C) 2008-2011 SonarSource 004 * mailto:contact AT sonarsource DOT com 005 * 006 * Sonar is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 3 of the License, or (at your option) any later version. 010 * 011 * Sonar is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public 017 * License along with Sonar; if not, write to the Free Software 018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 019 */ 020 package org.sonar.test.persistence; 021 022 import org.apache.commons.io.IOUtils; 023 import org.apache.commons.lang.StringUtils; 024 import org.apache.derby.jdbc.EmbeddedDriver; 025 import org.dbunit.Assertion; 026 import org.dbunit.DatabaseUnitException; 027 import org.dbunit.IDatabaseTester; 028 import org.dbunit.JdbcDatabaseTester; 029 import org.dbunit.dataset.CompositeDataSet; 030 import org.dbunit.dataset.DataSetException; 031 import org.dbunit.dataset.IDataSet; 032 import org.dbunit.dataset.ReplacementDataSet; 033 import org.dbunit.dataset.xml.FlatXmlDataSet; 034 import org.dbunit.operation.DatabaseOperation; 035 import org.junit.After; 036 import org.junit.AfterClass; 037 import org.junit.Assert; 038 import org.junit.BeforeClass; 039 040 import java.io.InputStream; 041 import java.sql.*; 042 import java.util.List; 043 044 import static org.junit.Assert.fail; 045 046 public abstract class DatabaseTestCase { 047 048 private static IDatabaseTester databaseTester = null; 049 private static final String JDBC_URL = "jdbc:derby:memory:sonar"; 050 private Connection connection = null; 051 052 053 @BeforeClass 054 public static void startDatabase() throws Exception { 055 System.setProperty("derby.stream.error.file", "target/derby.log"); 056 057 /* 058 Note: we could use a datasource instead of a direct JDBC connection. 059 See org.apache.derby.jdbc.ClientDataSource (http://db.apache.org/derby/papers/DerbyClientSpec.html#Connection+URL+Format) 060 and org.dbunit.DataSourceDatabaseTester 061 */ 062 EmbeddedDriver driver = new EmbeddedDriver(); 063 DriverManager.registerDriver(driver); 064 databaseTester = new JdbcDatabaseTester(driver.getClass().getName(), JDBC_URL + ";create=true"); 065 createDatabase(); 066 } 067 068 private static void createDatabase() throws Exception { 069 Connection c = databaseTester.getConnection().getConnection(); 070 Statement st = c.createStatement(); 071 for (String ddl : loadDdlStatements()) { 072 st.executeUpdate(ddl); 073 c.commit(); 074 } 075 st.close(); 076 c.close(); 077 } 078 079 private static String[] loadDdlStatements() throws Exception { 080 InputStream in = DatabaseTestCase.class.getResourceAsStream("/org/sonar/test/persistence/sonar-test.ddl"); 081 List<String> lines = IOUtils.readLines(in); 082 StringBuilder ddl = new StringBuilder(); 083 for (String line : lines) { 084 if (StringUtils.isNotBlank(line) && !StringUtils.startsWith(StringUtils.trimToEmpty(line), "#")) { 085 ddl.append(line).append(" "); 086 } 087 } 088 089 in.close(); 090 return StringUtils.split(StringUtils.trim(ddl.toString()), ";"); 091 } 092 093 @AfterClass 094 public static void stopDatabase() throws Exception { 095 try { 096 DriverManager.getConnection(JDBC_URL + ";shutdown=true"); 097 databaseTester.onTearDown(); 098 } catch (Exception e) { 099 // silently fail 100 } 101 } 102 103 public static IDatabaseTester getDatabaseTester() { 104 return databaseTester; 105 } 106 107 protected final Connection getConnection() { 108 try { 109 if (connection == null) { 110 connection = getDatabaseTester().getConnection().getConnection(); 111 } 112 113 } catch (Exception e) { 114 throw new RuntimeException(e); 115 } 116 return connection; 117 } 118 119 @After 120 public final void truncateTables() throws SQLException { 121 ResultSet rs = getConnection().getMetaData().getTables(null, "APP", null, null); 122 Statement st = getConnection().createStatement(); 123 while (rs.next()) { 124 String tableName = rs.getString(3); 125 // truncate command is implemented since derby 10.7 126 st.executeUpdate("TRUNCATE TABLE " + tableName); 127 } 128 st.close(); 129 rs.close(); 130 getConnection().commit(); 131 } 132 133 @After 134 public final void closeConnection() { 135 if (connection != null) { 136 try { 137 connection.close(); 138 connection = null; 139 } catch (SQLException e) { 140 throw new RuntimeException(e); 141 } 142 } 143 } 144 145 146 protected final void setupData(String... testNames) { 147 InputStream[] streams = new InputStream[testNames.length]; 148 try { 149 for (int i = 0; i < testNames.length; i++) { 150 String className = getClass().getName(); 151 className = String.format("/%s/%s.xml", className.replace(".", "/"), testNames[i]); 152 streams[i] = getClass().getResourceAsStream(className); 153 if (streams[i] == null) { 154 throw new RuntimeException("Test not found :" + className); 155 } 156 } 157 158 setupData(streams); 159 160 } finally { 161 for (InputStream stream : streams) { 162 IOUtils.closeQuietly(stream); 163 } 164 } 165 } 166 167 private void setupData(InputStream... dataSetStream) { 168 try { 169 IDataSet[] dataSets = new IDataSet[dataSetStream.length]; 170 for (int i = 0; i < dataSetStream.length; i++) { 171 ReplacementDataSet dataSet = new ReplacementDataSet(new FlatXmlDataSet(dataSetStream[i])); 172 dataSet.addReplacementObject("[null]", null); 173 dataSet.addReplacementObject("true", 1); 174 dataSet.addReplacementObject("false", 0); 175 dataSets[i] = dataSet; 176 } 177 CompositeDataSet compositeDataSet = new CompositeDataSet(dataSets); 178 DatabaseOperation.CLEAN_INSERT.execute(getDatabaseTester().getConnection(), compositeDataSet); 179 180 } catch (Exception e) { 181 throw new RuntimeException("Could not setup DBUnit data", e); 182 } 183 } 184 185 protected final void assertTables(String testName, String... tables) { 186 try { 187 IDataSet dataSet = getCurrentDataSet(); 188 IDataSet expectedDataSet = getExpectedData(testName); 189 for (String table : tables) { 190 Assertion.assertEquals(expectedDataSet.getTable(table), dataSet.getTable(table)); 191 } 192 } catch (DataSetException e) { 193 throw translateException("Error while checking results", e); 194 } catch (DatabaseUnitException e) { 195 fail(e.getMessage()); 196 } 197 } 198 199 protected final void assertTables(String testName, String[] tables, String[] ignoreCols) { 200 try { 201 IDataSet dataSet = getCurrentDataSet(); 202 IDataSet expectedDataSet = getExpectedData(testName); 203 for (String table : tables) { 204 Assertion.assertEqualsIgnoreCols(expectedDataSet.getTable(table), dataSet.getTable(table), ignoreCols); 205 } 206 } catch (DataSetException e) { 207 throw translateException("Error while checking results", e); 208 } catch (DatabaseUnitException e) { 209 fail(e.getMessage()); 210 } 211 } 212 213 protected final void assertEmptyTables(String... emptyTables) { 214 for (String table : emptyTables) { 215 try { 216 Assert.assertEquals(0, getCurrentDataSet().getTable(table).getRowCount()); 217 } catch (DataSetException e) { 218 throw translateException("Error while checking results", e); 219 } 220 } 221 } 222 223 private IDataSet getExpectedData(String testName) { 224 String className = getClass().getName(); 225 className = String.format("/%s/%s-result.xml", className.replace(".", "/"), testName); 226 227 InputStream in = getClass().getResourceAsStream(className); 228 try { 229 return getData(in); 230 } finally { 231 IOUtils.closeQuietly(in); 232 } 233 } 234 235 private IDataSet getData(InputStream stream) { 236 try { 237 ReplacementDataSet dataSet = new ReplacementDataSet(new FlatXmlDataSet(stream)); 238 dataSet.addReplacementObject("[null]", null); 239 return dataSet; 240 } catch (Exception e) { 241 throw translateException("Could not read the dataset stream", e); 242 } 243 } 244 245 private IDataSet getCurrentDataSet() { 246 try { 247 return databaseTester.getConnection().createDataSet(); 248 } catch (Exception e) { 249 throw translateException("Could not create the current dataset", e); 250 } 251 } 252 253 private static RuntimeException translateException(String msg, Exception cause) { 254 RuntimeException runtimeException = new RuntimeException(String.format("%s: [%s] %s", msg, cause.getClass().getName(), cause.getMessage())); 255 runtimeException.setStackTrace(cause.getStackTrace()); 256 return runtimeException; 257 } 258 259 }