package net.sourceforge.mayfly.acceptance; import junit.framework.TestCase; import net.sourceforge.mayfly.util.L; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.BitSet; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.List; public abstract class SqlTestCase extends TestCase { protected Dialect dialect = new MayflyDialect() //new H2Dialect() //new HypersonicDialect() //new MySqlDialect() //new PostgresDialect() //new DerbyDialect() //new SmallSqlDialect() //new MySql4Dialect() ; // Turn this on to see a comparison of mayfly exception messages with // the current database's messages. static final boolean SHOW_MESSAGES = false; /** * @internal * Like {@link #SHOW_MESSAGES} but the whole stack trace (in case a database * includes vital information other than in the message, which doesn't * usually seem to be the case). * Enable this or {@link #SHOW_MESSAGES} but not both. */ static final boolean SHOW_STACK_TRACES = false; protected Connection connection; private Statement statement; @Override public void setUp() throws Exception { connection = dialect.openConnection(); } @Override public void tearDown() throws Exception { dialect.shutdown(connection); if (statement != null) { statement.close(); } connection.close(); } protected int execute(String sql) throws SQLException { return execute(sql, connection); } static int execute(String sql, Connection connection) throws SQLException { Statement statement = connection.createStatement(); int rowsAffected = statement.executeUpdate(sql); statement.close(); return rowsAffected; } protected ResultSet query(String sql) throws SQLException { if (statement != null) { statement.close(); } statement = connection.createStatement(); return statement.executeQuery(sql); } protected void assertTableCount(int expected) { dialect.assertTableCount(expected); } protected void assertMessage(String expectedMessage, SQLException exception) { dialect.assertMessage(expectedMessage, exception); } /** * Like {@link #assertResultList(String[], ResultSet)} but using a set instead * of a list. This has two big consequences: (1) order is not important * (this matters when testing ORDER BY and similar features), and (2) duplicates * are removed (this matters when testing DISTINCT and similar features). */ public static void assertResultSet(String[] rowsAsStrings, ResultSet results) throws SQLException { Collection expected = new HashSet(); HashSet actual = new HashSet(); assertResults(rowsAsStrings, results, expected, actual); } public static void assertResultList(String[] rowsAsStrings, ResultSet results) throws SQLException { Collection expected = new ArrayList(); ArrayList actual = new ArrayList(); assertResults(rowsAsStrings, results, expected, actual); } private static void assertResults(String[] rowsAsStrings, ResultSet results, Collection expected, Collection actual) throws SQLException { BitSet strings = buildExpected(rowsAsStrings, expected); int columnsToFetch = countColumnsOfFirstRow(expected); buildActual(results, columnsToFetch, strings, actual); assertEquals(expected, actual); } private static int countColumnsOfFirstRow(Collection expected) { Iterator iterator = expected.iterator(); if (iterator.hasNext()) { return ((List) iterator.next()).size(); } else { // We don't expect to fetch any rows in this case. // So if the actual has a row, getting zero columns is fine. return 0; } } private static void buildActual(ResultSet results, int columnsToFetch, BitSet strings, Collection actual) throws SQLException { while (results.next()) { L row = new L(); for (int column = 1; column <= columnsToFetch; ++column) { Object value; if (strings.get(column - 1)) { value = results.getString(column); } else { value = new Long(results.getLong(column)); } if (results.wasNull()) { row.append(null); } else { row.append(value); } } actual.add(row); } results.close(); } private static BitSet buildExpected(String[] rowsAsStrings, Collection expected) { BitSet strings = null; for (int i = 0; i < rowsAsStrings.length; i++) { String rowString = rowsAsStrings[i]; String[] cells = rowString.split(","); L row = new L(); if (strings == null) { strings = new BitSet(cells.length); } for (int j = 0; j < cells.length; j++) { String cell = cells[j].trim(); if (cell.startsWith("'")) { strings.set(j); row.append(cell.substring(1, cell.length() - 1)); } else if (cell.equals("null")) { row.append(null); } else { strings.clear(j); row.append(new Long(cell)); } } expected.add(row); } return strings; } protected void expectQueryFailure(String sql, String expectedMessage) { try { query(sql); failForMissingException(sql, expectedMessage); } catch (SQLException e) { assertMessage(expectedMessage, e); } } protected void expectQueryFailure(String sql, String expectedMessage, int expectedStartLine, int expectedStartColumn, int expectedEndLine, int expectedEndColumn) { try { query(sql); failForMissingException(sql, expectedMessage); } catch (SQLException expected) { dialect.assertMessage(expectedMessage, expected, expectedStartLine, expectedStartColumn, expectedEndLine, expectedEndColumn); } } protected void expectExecuteFailure(String sql, String expectedMessage) { try { execute(sql); failForMissingException(sql, expectedMessage); } catch (SQLException expected) { assertMessage(expectedMessage, expected); } } protected void expectExecuteFailure(String sql, String expectedMessage, int expectedStartLine, int expectedStartColumn, int expectedEndLine, int expectedEndColumn) { try { execute(sql); failForMissingException(sql, expectedMessage); } catch (SQLException expected) { dialect.assertMessage(expectedMessage, expected, expectedStartLine, expectedStartColumn, expectedEndLine, expectedEndColumn); } } public static void failForMissingException(String sql, String expectedMessage) { fail("Did not find expected exception.\n" + "expected message: " + expectedMessage + "\n" + "command: " + sql + "\n" ); } public void createEmptySchema(String name) throws SQLException { assertEquals(0, execute(dialect.createEmptySchemaCommand(name))); assertEquals(0, execute("set schema " + name)); } protected void dropIndex(String name, String table) throws SQLException { execute(dropIndexCommand(name, table)); } protected String dropIndexCommand(String name, String table) { if (dialect.indexNamesArePerTable()) { return "drop index " + name + " on " + table; } else { return "drop index " + name; } } }