001: /*
002: *
003: * Derby - Class BaseJDBCTestCase
004: *
005: * Licensed to the Apache Software Foundation (ASF) under one or more
006: * contributor license agreements. See the NOTICE file distributed with
007: * this work for additional information regarding copyright ownership.
008: * The ASF licenses this file to You under the Apache License, Version 2.0
009: * (the "License"); you may not use this file except in compliance with
010: * the License. You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing,
015: * software distributed under the License is distributed on an
016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
017: * either express or implied. See the License for the specific
018: * language governing permissions and limitations under the License.
019: */
020: package org.apache.derbyTesting.junit;
021:
022: import java.io.ByteArrayInputStream;
023: import java.io.InputStream;
024: import java.io.IOException;
025: import java.io.OutputStream;
026: import java.io.PrintStream;
027: import java.io.Reader;
028: import java.io.UnsupportedEncodingException;
029: import java.sql.*;
030:
031: import junit.framework.AssertionFailedError;
032:
033: import org.apache.derby.tools.ij;
034:
035: /**
036: * Base class for JDBC JUnit tests.
037: * A method for getting a default connection is provided, along with methods
038: * for telling if a specific JDBC client is used.
039: */
040: public abstract class BaseJDBCTestCase extends BaseTestCase {
041:
042: /**
043: * Maintain a single connection to the default
044: * database, opened at the first call to getConnection.
045: * Typical setup will just require a single connection.
046: * @see BaseJDBCTestCase#getConnection()
047: */
048: private Connection conn;
049:
050: /**
051: * Create a test case with the given name.
052: *
053: * @param name of the test case.
054: */
055: public BaseJDBCTestCase(String name) {
056: super (name);
057: }
058:
059: /**
060: * Obtain the connection to the default database.
061: * This class maintains a single connection returned
062: * by this class, it is opened on the first call to
063: * this method. Subsequent calls will return the same
064: * connection object unless it has been closed. In that
065: * case a new connection object will be returned.
066: * <P>
067: * The tearDown method will close the connection if
068: * it is open.
069: * @see TestConfiguration#openDefaultConnection()
070: */
071: public Connection getConnection() throws SQLException {
072: if (conn != null) {
073: if (!conn.isClosed())
074: return conn;
075: conn = null;
076: }
077: return conn = openDefaultConnection();
078: }
079:
080: /**
081: * Allow a sub-class to initialize a connection to provide
082: * consistent connection state for its tests. Called once
083: * for each time these method calls open a connection:
084: * <UL>
085: * <LI> getConnection()
086: * <LI> openDefaultConnection()
087: * <LI> openConnection(database)
088: * <LI> getDefaultConnection(String connAttrs)
089: * <LI> getConnection(String databaseName, String connAttrs)
090: * </UL>
091: * when getConnection() opens a new connection. Default
092: * action is to not modify the connection's state from
093: * the initialization provided by the data source.
094: * @param conn Connection to be intialized
095: * @throws SQLException Error setting the initial state.
096: */
097: protected void initializeConnection(Connection conn)
098: throws SQLException {
099: }
100:
101: /**
102: * Utility method to create a Statement using the connection
103: * returned by getConnection.
104: * @return Statement object from getConnection.createStatement()
105: * @throws SQLException
106: */
107: public Statement createStatement() throws SQLException {
108: return getConnection().createStatement();
109: }
110:
111: /**
112: * Utility method to create a Statement using the connection
113: * returned by getConnection.
114: * @return Statement object from
115: * getConnection.createStatement(resultSetType, resultSetConcurrency)
116: * @throws SQLException
117: */
118: public Statement createStatement(int resultSetType,
119: int resultSetConcurrency) throws SQLException {
120: return getConnection().createStatement(resultSetType,
121: resultSetConcurrency);
122: }
123:
124: /**
125: * Utility method to create a PreparedStatement using the connection
126: * returned by getConnection.
127: * @return Statement object from
128: * getConnection.prepareStatement(sql)
129: * @throws SQLException
130: */
131: public PreparedStatement prepareStatement(String sql)
132: throws SQLException {
133: return getConnection().prepareStatement(sql);
134: }
135:
136: /**
137: * Utility method to create a CallableStatement using the connection
138: * returned by getConnection.
139: * @return Statement object from
140: * getConnection().prepareCall(sql)
141: * @throws SQLException
142: */
143: public CallableStatement prepareCall(String sql)
144: throws SQLException {
145: return getConnection().prepareCall(sql);
146: }
147:
148: /**
149: * Utility method to commit using the connection
150: * returned by getConnection.
151: * @throws SQLException
152: */
153: public void commit() throws SQLException {
154: getConnection().commit();
155: }
156:
157: /**
158: * Utility method to rollback using the connection
159: * returned by getConnection.
160: * @throws SQLException
161: */
162: public void rollback() throws SQLException {
163: getConnection().rollback();
164: }
165:
166: /**
167: * Tear down this fixture, sub-classes should call
168: * super.tearDown(). This cleanups & closes the connection
169: * if it is open.
170: */
171: protected void tearDown() throws java.lang.Exception {
172: JDBC.cleanup(conn);
173: conn = null;
174: }
175:
176: /**
177: * Open a connection to the default database.
178: * If the database does not exist, it will be created.
179: * A default username and password will be used for the connection.
180: *
181: * @return connection to default database.
182: * @see TestConfiguration#openDefaultConnection()
183: */
184: public Connection openDefaultConnection() throws SQLException {
185: Connection conn = getTestConfiguration()
186: .openDefaultConnection();
187: initializeConnection(conn);
188: return conn;
189: }
190:
191: public Connection openConnection(String databaseName)
192: throws SQLException {
193: Connection conn = getTestConfiguration().openConnection(
194: databaseName);
195: initializeConnection(conn);
196: return conn;
197: }
198:
199: /**
200: * Get a connection to the default database using the specified connection
201: * attributes.
202: *
203: * @param connAttrs connection attributes
204: * @return connection to default database.
205: * @throws SQLException
206: */
207: public Connection getDefaultConnection(String connAttrs)
208: throws SQLException {
209: Connection conn = getTestConfiguration().getDefaultConnection(
210: connAttrs);
211: initializeConnection(conn);
212: return conn;
213: }
214:
215: /**
216: * Get a connection to a database using the specified connection
217: * attributes.
218: *
219: * @param databaseName database to connect to
220: * @param connAttrs connection attributes
221: * @return connection to database
222: * @throws SQLException
223: */
224: public Connection getConnection(String databaseName,
225: String connAttrs) throws SQLException {
226: Connection conn = getTestConfiguration().getConnection(
227: databaseName, connAttrs);
228: initializeConnection(conn);
229: return conn;
230: }
231:
232: /**
233: * Run a SQL script through ij discarding the output
234: * using this object's default connection. Intended for
235: * setup scripts.
236: * @throws UnsupportedEncodingException
237: * @throws SQLException
238: */
239: public int runScript(InputStream script, String encoding)
240: throws UnsupportedEncodingException, SQLException {
241: // Sink output.
242: OutputStream sink = new OutputStream() {
243: public void write(byte[] b, int off, int len) {
244: }
245:
246: public void write(int b) {
247: }
248: };
249:
250: // Use the same encoding as the input for the output.
251: return ij.runScript(getConnection(), script, encoding, sink,
252: encoding);
253: }
254:
255: /**
256: * Run a set of SQL commands from a String discarding the output.
257: * Commands are separated by a semi-colon. Connection used
258: * is this objects default connection.
259: * @param sqlCommands
260: * @return Number of errors executing the script.
261: * @throws UnsupportedEncodingException
262: * @throws SQLException
263: */
264: public int runSQLCommands(String sqlCommands)
265: throws UnsupportedEncodingException, SQLException {
266: byte[] raw = sqlCommands.getBytes("UTF-8");
267: ByteArrayInputStream in = new ByteArrayInputStream(raw);
268:
269: return runScript(in, "UTF-8");
270: }
271:
272: /**
273: * Tell if the client is embedded.
274: *
275: * @return <code>true</code> if using the embedded client
276: * <code>false</code> otherwise.
277: */
278: public static boolean usingEmbedded() {
279: return TestConfiguration.getCurrent().getJDBCClient()
280: .isEmbedded();
281: }
282:
283: /**
284: * Tell if the client is DerbyNetClient.
285: *
286: * @return <code>true</code> if using the DerbyNetClient client
287: * <code>false</code> otherwise.
288: */
289: public static boolean usingDerbyNetClient() {
290: return TestConfiguration.getCurrent().getJDBCClient()
291: .isDerbyNetClient();
292: }
293:
294: /**
295: * Tell if the client is DerbyNet.
296: *
297: * @return <code>true</code> if using the DerbyNet client
298: * <code>false</code> otherwise.
299: */
300: public static boolean usingDerbyNet() {
301: return TestConfiguration.getCurrent().getJDBCClient()
302: .isDB2Client();
303: }
304:
305: /**
306: * Assert equality between two <code>Blob</code> objects.
307: * If both input references are <code>null</code>, they are considered
308: * equal. The same is true if both blobs have <code>null</code>-streams.
309: *
310: * @param b1 first <code>Blob</code>.
311: * @param b2 second <code>Blob</code>.
312: * @throws AssertionFailedError if blobs are not equal.
313: * @throws IOException if reading or closing a stream fails
314: * @throws SQLException if obtaining a stream fails
315: */
316: public static void assertEquals(Blob b1, Blob b2)
317: throws IOException, SQLException {
318: if (b1 == null || b2 == null) {
319: assertNull("Blob b2 is null, b1 is not", b1);
320: assertNull("Blob b1 is null, b2 is not", b2);
321: return;
322: }
323: assertEquals("Blobs have different lengths", b1.length(), b2
324: .length());
325: InputStream is1 = b1.getBinaryStream();
326: InputStream is2 = b2.getBinaryStream();
327: if (is1 == null || is2 == null) {
328: assertNull("Blob b2 has null-stream, blob b1 doesn't", is1);
329: assertNull("Blob b1 has null-stream, blob b2 doesn't", is2);
330: return;
331: }
332: long index = 1;
333: int by1 = is1.read();
334: int by2 = is2.read();
335: do {
336: // Avoid string concatenation for every byte in the stream.
337: if (by1 != by2) {
338: assertEquals("Blobs differ at index " + index, by1, by2);
339: }
340: index++;
341: by1 = is1.read();
342: by2 = is2.read();
343: } while (by1 != -1 || by2 != -1);
344: is1.close();
345: is2.close();
346: }
347:
348: /**
349: * Assert equality between two <code>Clob</code> objects.
350: * If both input references are <code>null</code>, they are considered
351: * equal. The same is true if both clobs have <code>null</code>-streams.
352: *
353: * @param c1 first <code>Clob</code>.
354: * @param c2 second <code>Clob</code>.
355: * @throws AssertionFailedError if clobs are not equal.
356: * @throws IOException if reading or closing a stream fails
357: * @throws SQLException if obtaining a stream fails
358: */
359: public static void assertEquals(Clob c1, Clob c2)
360: throws IOException, SQLException {
361: if (c1 == null || c2 == null) {
362: assertNull("Clob c2 is null, c1 is not", c1);
363: assertNull("Clob c1 is null, c2 is not", c2);
364: return;
365: }
366: assertEquals("Clobs have different lengths", c1.length(), c2
367: .length());
368: Reader r1 = c1.getCharacterStream();
369: Reader r2 = c2.getCharacterStream();
370: if (r1 == null || r2 == null) {
371: assertNull("Clob c2 has null-stream, clob c1 doesn't", r1);
372: assertNull("Clob c1 has null-stream, clob c2 doesn't", r2);
373: return;
374: }
375: long index = 1;
376: int ch1 = r1.read();
377: int ch2 = r2.read();
378: do {
379: // Avoid string concatenation for every char in the stream.
380: if (ch1 != ch2) {
381: assertEquals("Clobs differ at index " + index, ch1, ch2);
382: }
383: index++;
384: ch1 = r1.read();
385: ch2 = r2.read();
386: } while (ch1 != -1 || ch2 != -1);
387: r1.close();
388: r2.close();
389: }
390:
391: /**
392: * Assert that SQLState is as expected.
393: *
394: * @param message message to print on failure.
395: * @param expected the expected SQLState.
396: * @param exception the exception to check the SQLState of.
397: */
398: public static void assertSQLState(String message, String expected,
399: SQLException exception) {
400: // Make sure exception is not null. We want to separate between a
401: // null-exception object, and a null-SQLState.
402: assertNotNull(
403: "Exception cannot be null when asserting on SQLState",
404: exception);
405:
406: try {
407: String state = exception.getSQLState();
408:
409: if (state != null)
410: assertTrue(
411: "The exception's SQL state must be five characters long",
412: state.length() == 5);
413:
414: if (expected != null)
415: assertTrue(
416: "The expected SQL state must be five characters long",
417: expected.length() == 5);
418:
419: assertEquals(message, expected, state);
420: } catch (AssertionFailedError e) {
421:
422: // Save the SQLException
423: // e.initCause(exception);
424:
425: throw e;
426: }
427: }
428:
429: /**
430: * Assert that SQLState is as expected.
431: *
432: * @param expected the expected SQLState.
433: * @param exception the exception to check the SQLState of.
434: */
435: public static void assertSQLState(String expected,
436: SQLException exception) {
437: assertSQLState("Unexpected SQL state.", expected, exception);
438: }
439:
440: /**
441: * Assert that the query does not compile and throws
442: * a SQLException with the expected state.
443: *
444: * @param sqlstate expected sql state.
445: * @param query the query to compile.
446: */
447: public void assertCompileError(String sqlState, String query) {
448:
449: try {
450: prepareStatement(query).close();
451: fail("expected compile error: " + sqlState);
452: } catch (SQLException se) {
453: assertSQLState(sqlState, se);
454: }
455: }
456:
457: } // End class BaseJDBCTestCase
|