2 Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; version 2 of the License.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 package testsuite.clusterj;
20 import com.mysql.clusterj.ClusterJException;
21 import com.mysql.clusterj.ClusterJHelper;
22 import com.mysql.clusterj.Constants;
23 import com.mysql.clusterj.Session;
24 import com.mysql.clusterj.SessionFactory;
25 import com.mysql.clusterj.Transaction;
27 import java.io.BufferedReader;
29 import java.io.FileInputStream;
30 import java.io.FileNotFoundException;
31 import java.io.IOException;
32 import java.io.InputStream;
33 import java.io.InputStreamReader;
34 import java.lang.Thread.UncaughtExceptionHandler;
36 import java.sql.Connection;
37 import java.sql.DriverManager;
38 import java.sql.PreparedStatement;
39 import java.sql.SQLException;
40 import java.sql.Statement;
42 import java.util.ArrayList;
43 import java.util.Arrays;
44 import java.util.Collection;
45 import java.util.Collections;
46 import java.util.Comparator;
47 import java.util.Iterator;
48 import java.util.LinkedList;
49 import java.util.List;
50 import java.util.Map.Entry;
51 import java.util.Properties;
53 import junit.framework.TestCase;
58 public abstract class AbstractClusterJTest extends TestCase {
59 protected static final String JDBC_DRIVER_NAME = "jdbc.driverName";
60 protected static final String JDBC_URL = "jdbc.url";
61 protected static Connection connection;
62 protected static String jdbcDriverName;
63 protected static String jdbcPassword;
64 protected static String jdbcURL;
65 protected static String jdbcUsername;
66 protected static Properties props;
67 protected static List<String> schemaDefinition = new ArrayList<String>();
68 /** Has the schema been initialized */
69 protected static boolean schemaInitialized = false;
70 String PROPS_FILE_NAME = System.getProperty("clusterj.properties", "clusterj.properties");
71 protected Session session;
72 protected SessionFactory sessionFactory;
73 protected Transaction tx;
76 * Error messages collected during a test.
78 private StringBuffer errorMessages;
81 * A list of registered pc classes.
82 * The extents of these classes are deleted in <code>tearDown</code>.
84 private Collection<Class<?>> tearDownClasses = new LinkedList<Class<?>>();
87 * A list of registered oid instances.
88 * Corresponding pc instances are deleted in <code>localTearDown</code>.
90 private Collection<Object> tearDownInstances = new LinkedList<Object>();
94 * Indicates an exception thrown in method <code>tearDown</code>.
95 * At the end of method <code>tearDown</code> this field is nullified.
96 * TODO support this feature
98 // private Throwable tearDownThrowable;
99 private String NL = "\n";
101 protected boolean debug;
103 /** Subclasses can override this method to get debugging info printed to System.out */
104 protected boolean getDebug() {
108 public AbstractClusterJTest() {
112 protected void addTearDownClasses(Class<?>... classes) {
113 for (Class<?> cls : classes) {
114 tearDownClasses.add(cls);
118 protected void createSessionFactory() {
119 if (sessionFactory == null) {
121 Properties modifiedProperties = modifyProperties();
122 if (debug) System.out.println("createSessionFactory props: " + modifiedProperties);
123 sessionFactory = ClusterJHelper.getSessionFactory(modifiedProperties);
128 protected Properties modifyProperties() {
129 // doesn't do anything but can be overridden by a subclass
133 public void createSession() {
134 if (session != null && !session.isClosed()) {
135 tx = session.currentTransaction();
141 session = sessionFactory.getSession();
142 tx = session.currentTransaction();
145 protected void dumpSystemProperties() {
146 Properties sysprops = System.getProperties();
147 List<Entry<Object, Object>> entries = new ArrayList<Entry<Object, Object>>(sysprops.entrySet());
148 Collections.sort(entries, new Comparator<Entry<Object, Object>>() {
150 public int compare(Entry<Object, Object> o1, Entry<Object, Object> o2) {
151 return ((String) o1.getKey()).compareToIgnoreCase((String) o2.getKey());
154 for (Iterator<Entry<Object, Object>> iterator = entries.iterator(); iterator.hasNext();) {
155 Entry<Object, Object> entry = iterator.next();
156 System.out.println("key: " + entry.getKey() + "; value: " + entry.getValue());
160 protected void error(String message) {
161 initializeErrorMessages();
162 errorMessages.append(message + NL);
165 protected void error(String context, Exception ex) {
166 String message = context + " " + ex.getClass().getName() + ":" + ex.getMessage();
169 ex.printStackTrace();
173 protected void errorIfNotEqual(String message, Object expected, Object actual) {
174 if (expected == null && actual == null) {
177 if (expected != null && expected.equals(actual)) {
180 initializeErrorMessages();
181 errorMessages.append(message + NL);
182 errorMessages.append(
183 "Expected: " + ((expected==null)?"null":expected.toString())
184 + " actual: " + ((actual==null)?"null":actual.toString()) + NL);
188 protected void errorIfNotEqual(String message, int[] expected, int[] actual) {
189 if (expected == null && actual == null) {
193 if (expected.length == actual.length) {
194 for (i = 0; i < expected.length; ++i) {
195 if (expected[i] != actual[i]) {
199 if (i == expected.length) {
203 initializeErrorMessages();
204 errorMessages.append(message + NL);
205 errorMessages.append(
206 "Expected: " + ((expected==null)?"null":Arrays.toString(expected))
207 + " actual: " + ((actual==null)?"null":Arrays.toString(actual)) + NL);
210 protected void errorIfEqual(String message, Object expected, Object actual) {
211 if (expected == null && actual != null) {
214 if (expected != null && !expected.equals(actual)) {
217 initializeErrorMessages();
218 errorMessages.append(message + NL);
219 errorMessages.append(
220 "Error value: " + ((expected==null)?"null":expected.toString()));
224 protected void failOnError() {
225 if (errorMessages != null) {
226 fail(errorMessages.toString());
230 /** Close the connection and reset the connection variable.
233 protected void closeConnection() {
235 if (connection != null) {
238 } catch (SQLException e) {
239 throw new RuntimeException("Caught SQLException during close.", e);
245 /** Get a connection with special properties. If the connection is open,
246 * close it and get a new one.
249 protected void getConnection(Properties extraProperties) {
250 // characterEncoding = utf8 property is especially useful
251 Properties properties = new Properties();
252 properties.put("user", jdbcUsername);
253 properties.put("password", jdbcPassword);
254 properties.putAll(extraProperties);
256 if (connection != null && !connection.isClosed()) {
260 if (debug) System.out.println("Getting new connection with properties " + properties);
261 connection = DriverManager.getConnection(jdbcURL, properties);
262 } catch (SQLException ex) {
263 ex.printStackTrace();
264 throw new ClusterJException("Exception getting connection to " + jdbcURL + "; username " + jdbcUsername, ex);
265 // TODO Auto-generated catch block
269 /** Get a connection with properties from the Properties instance.
272 protected Connection getConnection() {
273 if (connection == null) {
275 Class.forName(jdbcDriverName, true, Thread.currentThread().getContextClassLoader());
276 connection = DriverManager.getConnection(jdbcURL, jdbcUsername, jdbcPassword);
277 } catch (SQLException ex) {
278 throw new ClusterJException("Exception getting connection to " + jdbcURL + "; username " + jdbcUsername, ex);
279 } catch (ClassNotFoundException ex) {
280 throw new ClusterJException("Exception loading JDBC driver." + jdbcDriverName, ex);
286 /** Get a connection with properties from a file.
288 * @param propertiesFileName the name of the properties file
290 protected void getConnection(String propertiesFileName) {
291 loadProperties(propertiesFileName);
293 String url = props.getProperty(JDBC_URL);
295 connection = DriverManager.getConnection(url);
296 setAutoCommit(connection, false);
297 } catch (SQLException e) {
298 throw new RuntimeException("Could not get Connection: " + url, e);
303 * @throws ClassNotFoundException
305 protected void loadDriver() {
306 String driverName = props.getProperty(JDBC_DRIVER_NAME);
308 Class.forName(driverName);
309 } catch (ClassNotFoundException e) {
310 throw new RuntimeException("Class not found: " + driverName, e);
314 protected void setAutoCommit(Connection connection, boolean b) {
316 connection.setAutoCommit(false);
317 } catch (SQLException e) {
318 throw new RuntimeException("setAutoCommit failed", e);
322 Properties getProperties(String fileName) {
323 Properties result = null;
325 InputStream stream = new FileInputStream(new File(fileName));
326 result = new Properties();
329 } catch (FileNotFoundException ex) {
330 // ignore and try getResourceAsStream
331 } catch (IOException ex) {
332 // ignore and try getResourceAsStream
334 if (result == null) {
336 // try to load the resource from the class loader
337 ClassLoader cl = this.getClass().getClassLoader();
338 InputStream stream = cl.getResourceAsStream(fileName);
339 result = new Properties();
342 } catch (IOException ex) {
343 fail("Could not create ConnectionFactory " + ex);
344 } catch (NullPointerException ex) {
345 fail("Missing properties file " + fileName);
351 protected void initializeErrorMessages() {
352 if (errorMessages == null) {
353 errorMessages = new StringBuffer();
354 errorMessages.append(NL);
358 /** Initialize the JDBC driver */
359 protected void initializeJDBC() {
364 protected void initializeSchema() {
366 Iterator<String> it = schemaDefinition.iterator();
367 // skip past drop table
369 // skip past test table
371 String statement = null;
373 while (it.hasNext()) {
374 statement = it.next();
375 if (debug) System.out.println("Executing statement " + statement + ";");
376 PreparedStatement s = connection.prepareStatement(statement);
380 schemaInitialized = true;
381 // connection.close();
382 System.out.println("Successfully initialized schema.");
383 } catch (SQLException ex) {
384 // on failure, drop the test table so we try again
386 throw new ClusterJException("initializeSchema threw exception on " + statement, ex);
390 /** Load properties from clusterj.properties */
391 protected void loadProperties() {
392 loadProperties(PROPS_FILE_NAME);
395 /** Load properties from an arbitrary file name */
396 protected void loadProperties(String propsFileName) {
397 // if (props == null) {
398 props = getProperties(propsFileName);
400 jdbcDriverName = props.getProperty(Constants.PROPERTY_JDBC_DRIVER_NAME);
401 jdbcURL = props.getProperty(Constants.PROPERTY_JDBC_URL);
402 jdbcUsername = props.getProperty(Constants.PROPERTY_JDBC_USERNAME);
403 jdbcPassword = props.getProperty(Constants.PROPERTY_JDBC_PASSWORD);
404 if (jdbcPassword == null) {
409 /** Load the schema for tests */
410 protected void loadSchema() {
412 if (!schemaInitialized) {
413 loadSchemaDefinition();
420 protected void loadSchemaDefinition() {
421 InputStream inputStream = null;
422 StringBuffer buffer = new StringBuffer();
425 inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("schema.sql");
426 BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
427 while (reader.ready()) {
428 line = reader.readLine();
429 if (line.contains("#")) {
430 // comment line; ignore
433 int semi = line.indexOf(";");
435 buffer.append(line.substring(0, semi));
436 schemaDefinition.add(buffer.toString());
437 buffer = new StringBuffer();
442 } catch (IOException ex) {
443 throw new ClusterJException("Exception reading schema.sql.", ex);
446 if (inputStream != null) {
449 } catch (IOException ex) {
455 * Subclasses may override this method to allocate any data and resources
456 * that they need in order to successfully execute this testcase.
457 * Adding teardown classes and instances is done in the overridden method.
459 protected void localSetUp() {
463 * Subclasses may override this method to deallocate any data and resources
464 * that they needed in order to successfully execute this testcase.
466 protected void localTearDown() {
470 protected final void setUp() throws Exception {
475 protected final void tearDown() throws Exception {
477 // if session is null or closed, test class has already cleaned up
478 if (session != null && !(session.isClosed())) {
479 // if tx is null, get it again
481 tx = session.currentTransaction();
483 // if transaction is active (leftover), roll it back
487 // if any work to do, start a transaction and clean up
488 if (!tearDownClasses.isEmpty() | !tearDownInstances.isEmpty()) {
490 for (Class<?> cls : tearDownClasses) {
491 session.deletePersistentAll(cls);
493 for (Object o : tearDownInstances) {
494 session.deletePersistent(o);
502 sessionFactory = null;
505 protected void removeAll(Class<?> cls) {
506 sessionFactory.getSession();
507 session.currentTransaction().begin();
508 session.deletePersistentAll(cls);
509 session.currentTransaction().commit();
512 protected boolean testSchema() {
514 Statement statement = connection.createStatement();
515 statement.execute(schemaDefinition.get(1));
518 } catch (SQLException ex) {
520 ex.printStackTrace();
522 System.out.println("Test schema failed (normal) " + schemaDefinition.get(1));
527 protected boolean resetSchema() {
529 Statement statement = connection.createStatement();
530 statement.execute(schemaDefinition.get(0));
533 } catch (SQLException ex) {
534 System.out.println("Test schema failed (normal) " + schemaDefinition.get(0));
539 protected static String dump(String string) {
540 StringBuffer buffer = new StringBuffer("[");
541 for (int i = 0; i < string.length(); ++i) {
542 int theCharacter = string.charAt(i);
543 buffer.append(theCharacter);
547 return buffer.toString();
550 protected String dump(List<String> list) {
551 StringBuffer result = new StringBuffer();
552 for (String string: list) {
553 result.append(dump(string));
556 return result.toString();
559 /** Catch otherwise uncaught exceptions and maintain a list of them.
560 * When needed, they can be obtained via the getUncaughtExceptions method.
562 public static class MyUncaughtExceptionHandler implements UncaughtExceptionHandler {
563 private static List<Throwable> uncaughtExceptions = new ArrayList<Throwable>();
564 public List<Throwable> getUncaughtExceptions() {
565 return uncaughtExceptions;
567 public synchronized void uncaughtException(Thread t, Throwable e) {
569 uncaughtExceptions.add(e);