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 + ";drop=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 }