001: /*
002: Copyright (C) 2002-2007 MySQL AB
003:
004: This program is free software; you can redistribute it and/or modify
005: it under the terms of version 2 of the GNU General Public License as
006: published by the Free Software Foundation.
007:
008: There are special exceptions to the terms and conditions of the GPL
009: as it is applied to this software. View the full text of the
010: exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
011: software distribution.
012:
013: This program is distributed in the hope that it will be useful,
014: but WITHOUT ANY WARRANTY; without even the implied warranty of
015: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016: GNU General Public License for more details.
017:
018: You should have received a copy of the GNU General Public License
019: along with this program; if not, write to the Free Software
020: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
021:
022:
023:
024: */
025: package testsuite;
026:
027: import java.io.BufferedOutputStream;
028: import java.io.FileOutputStream;
029: import java.io.File;
030: import java.io.FilenameFilter;
031: import java.io.IOException;
032: import java.sql.Blob;
033: import java.sql.Connection;
034: import java.sql.DriverManager;
035: import java.sql.PreparedStatement;
036: import java.sql.ResultSet;
037: import java.sql.SQLException;
038: import java.sql.Statement;
039: import java.util.ArrayList;
040: import java.util.Iterator;
041: import java.util.List;
042: import java.util.Locale;
043: import java.util.Properties;
044:
045: import com.mysql.jdbc.NonRegisteringDriver;
046: import com.mysql.jdbc.StringUtils;
047:
048: import junit.framework.TestCase;
049:
050: /**
051: * Base class for all test cases. Creates connections, statements, etc. and
052: * closes them.
053: *
054: * @author Mark Matthews
055: * @version $Id: BaseTestCase.java 5440 2006-06-27 17:00:53 +0000 (Tue, 27 Jun
056: * 2006) mmatthews $
057: */
058: public abstract class BaseTestCase extends TestCase {
059: private final static String ADMIN_CONNECTION_PROPERTY_NAME = "com.mysql.jdbc.testsuite.admin-url";
060:
061: private final static String NO_MULTI_HOST_PROPERTY_NAME = "com.mysql.jdbc.testsuite.no-multi-hosts-tests";
062:
063: /**
064: * JDBC URL, initialized from com.mysql.jdbc.testsuite.url system property,
065: * or defaults to jdbc:mysql:///test
066: */
067: protected static String dbUrl = "jdbc:mysql:///test";
068:
069: /** Instance counter */
070: private static int instanceCount = 1;
071:
072: /** Connection to server, initialized in setUp() Cleaned up in tearDown(). */
073: protected Connection conn = null;
074:
075: /** list of schema objects to be dropped in tearDown */
076: private List createdObjects;
077:
078: /** The driver to use */
079: protected String dbClass = "com.mysql.jdbc.Driver";
080:
081: /** My instance number */
082: private int myInstanceNumber = 0;
083:
084: /**
085: * PreparedStatement to be used in tests, not initialized. Cleaned up in
086: * tearDown().
087: */
088: protected PreparedStatement pstmt = null;
089:
090: /**
091: * ResultSet to be used in tests, not initialized. Cleaned up in tearDown().
092: */
093: protected ResultSet rs = null;
094:
095: /**
096: * Statement to be used in tests, initialized in setUp(). Cleaned up in
097: * tearDown().
098: */
099: protected Statement stmt = null;
100:
101: private boolean runningOnJdk131 = false;
102:
103: /**
104: * Creates a new BaseTestCase object.
105: *
106: * @param name
107: * The name of the JUnit test case
108: */
109: public BaseTestCase(String name) {
110: super (name);
111: this .myInstanceNumber = instanceCount++;
112:
113: String newDbUrl = System
114: .getProperty("com.mysql.jdbc.testsuite.url");
115:
116: if ((newDbUrl != null) && (newDbUrl.trim().length() != 0)) {
117: dbUrl = newDbUrl;
118: } else {
119: String defaultDbUrl = System
120: .getProperty("com.mysql.jdbc.testsuite.url.default");
121:
122: if ((defaultDbUrl != null)
123: && (defaultDbUrl.trim().length() != 0)) {
124: dbUrl = defaultDbUrl;
125: }
126: }
127:
128: String newDriver = System
129: .getProperty("com.mysql.jdbc.testsuite.driver");
130:
131: if ((newDriver != null) && (newDriver.trim().length() != 0)) {
132: this .dbClass = newDriver;
133: }
134:
135: try {
136: Blob.class.getMethod("truncate", new Class[] { Long.TYPE });
137: this .runningOnJdk131 = false;
138: } catch (NoSuchMethodException nsme) {
139: this .runningOnJdk131 = true;
140: }
141: }
142:
143: protected void createSchemaObject(String objectType,
144: String objectName, String columnsAndOtherStuff)
145: throws SQLException {
146: this .createdObjects
147: .add(new String[] { objectType, objectName });
148: dropSchemaObject(objectType, objectName);
149:
150: StringBuffer createSql = new StringBuffer(objectName.length()
151: + objectType.length() + columnsAndOtherStuff.length()
152: + 10);
153: createSql.append("CREATE ");
154: createSql.append(objectType);
155: createSql.append(" ");
156: createSql.append(objectName);
157: createSql.append(" ");
158: createSql.append(columnsAndOtherStuff);
159: this .stmt.executeUpdate(createSql.toString());
160: }
161:
162: protected void createFunction(String functionName,
163: String functionDefn) throws SQLException {
164: createSchemaObject("FUNCTION", functionName, functionDefn);
165: }
166:
167: protected void dropFunction(String functionName)
168: throws SQLException {
169: dropSchemaObject("FUNCTION", functionName);
170: }
171:
172: protected void createProcedure(String procedureName,
173: String procedureDefn) throws SQLException {
174: createSchemaObject("PROCEDURE", procedureName, procedureDefn);
175: }
176:
177: protected void dropProcedure(String procedureName)
178: throws SQLException {
179: dropSchemaObject("PROCEDURE", procedureName);
180: }
181:
182: protected void createTable(String tableName,
183: String columnsAndOtherStuff) throws SQLException {
184: createSchemaObject("TABLE", tableName, columnsAndOtherStuff);
185: }
186:
187: protected void dropTable(String tableName) throws SQLException {
188: dropSchemaObject("TABLE", tableName);
189: }
190:
191: protected void dropSchemaObject(String objectType, String objectName)
192: throws SQLException {
193: this .stmt.executeUpdate("DROP " + objectType + " IF EXISTS "
194: + objectName);
195: }
196:
197: protected Connection getAdminConnection() throws SQLException {
198: return getAdminConnectionWithProps(new Properties());
199: }
200:
201: protected Connection getAdminConnectionWithProps(Properties props)
202: throws SQLException {
203: String adminUrl = System
204: .getProperty(ADMIN_CONNECTION_PROPERTY_NAME);
205:
206: if (adminUrl != null) {
207: return DriverManager.getConnection(adminUrl, props);
208: } else {
209: return null;
210: }
211: }
212:
213: protected Connection getConnectionWithProps(String propsList)
214: throws SQLException {
215: return getConnectionWithProps(dbUrl, propsList);
216: }
217:
218: protected Connection getConnectionWithProps(String url,
219: String propsList) throws SQLException {
220: Properties props = new Properties();
221:
222: if (propsList != null) {
223: List keyValuePairs = StringUtils.split(propsList, ",",
224: false);
225:
226: Iterator iter = keyValuePairs.iterator();
227:
228: while (iter.hasNext()) {
229: String kvp = (String) iter.next();
230: List splitUp = StringUtils.split(kvp, "=", false);
231: props.setProperty(splitUp.get(0).toString().trim(),
232: splitUp.get(1).toString());
233: }
234: }
235:
236: return getConnectionWithProps(url, props);
237: }
238:
239: /**
240: * Returns a new connection with the given properties
241: *
242: * @param props
243: * the properties to use (the URL will come from the standard for
244: * this testcase).
245: *
246: * @return a new connection using the given properties.
247: *
248: * @throws SQLException
249: * DOCUMENT ME!
250: */
251: protected Connection getConnectionWithProps(Properties props)
252: throws SQLException {
253: return DriverManager.getConnection(dbUrl, props);
254: }
255:
256: protected Connection getConnectionWithProps(String url,
257: Properties props) throws SQLException {
258: return DriverManager.getConnection(url, props);
259: }
260:
261: /**
262: * Returns the per-instance counter (for messages when multi-threading
263: * stress tests)
264: *
265: * @return int the instance number
266: */
267: protected int getInstanceNumber() {
268: return this .myInstanceNumber;
269: }
270:
271: protected String getMysqlVariable(Connection c, String variableName)
272: throws SQLException {
273: Object value = getSingleIndexedValueWithQuery(c, 2,
274: "SHOW VARIABLES LIKE '" + variableName + "'");
275:
276: if (value != null) {
277: if (value instanceof byte[]) {
278: // workaround for bad 4.1.x bugfix
279: return new String((byte[]) value);
280: }
281:
282: return value.toString();
283: }
284:
285: return null;
286:
287: }
288:
289: /**
290: * Returns the named MySQL variable from the currently connected server.
291: *
292: * @param variableName
293: * the name of the variable to return
294: *
295: * @return the value of the given variable, or NULL if it doesn't exist
296: *
297: * @throws SQLException
298: * if an error occurs
299: */
300: protected String getMysqlVariable(String variableName)
301: throws SQLException {
302: return getMysqlVariable(this .conn, variableName);
303: }
304:
305: /**
306: * Returns the properties that represent the default URL used for
307: * connections for all testcases.
308: *
309: * @return properties parsed from com.mysql.jdbc.testsuite.url
310: *
311: * @throws SQLException
312: * if parsing fails
313: */
314: protected Properties getPropertiesFromTestsuiteUrl()
315: throws SQLException {
316: Properties props = new NonRegisteringDriver().parseURL(dbUrl,
317: null);
318:
319: String hostname = props
320: .getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY);
321:
322: if (hostname == null) {
323: props.setProperty(NonRegisteringDriver.HOST_PROPERTY_KEY,
324: "localhost");
325: } else if (hostname.startsWith(":")) {
326: props.setProperty(NonRegisteringDriver.HOST_PROPERTY_KEY,
327: "localhost");
328: props.setProperty(NonRegisteringDriver.PORT_PROPERTY_KEY,
329: hostname.substring(1));
330: }
331:
332: String portNumber = props
333: .getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY);
334:
335: if (portNumber == null) {
336: props.setProperty(NonRegisteringDriver.PORT_PROPERTY_KEY,
337: "3306");
338: }
339:
340: return props;
341: }
342:
343: protected int getRowCount(String tableName) throws SQLException {
344: ResultSet countRs = null;
345:
346: try {
347: countRs = this .stmt.executeQuery("SELECT COUNT(*) FROM "
348: + tableName);
349:
350: countRs.next();
351:
352: return countRs.getInt(1);
353: } finally {
354: if (countRs != null) {
355: countRs.close();
356: }
357: }
358: }
359:
360: protected Object getSingleIndexedValueWithQuery(Connection c,
361: int columnIndex, String query) throws SQLException {
362: ResultSet valueRs = null;
363:
364: Statement svStmt = null;
365:
366: try {
367: svStmt = c.createStatement();
368:
369: valueRs = svStmt.executeQuery(query);
370:
371: if (!valueRs.next()) {
372: return null;
373: }
374:
375: return valueRs.getObject(columnIndex);
376: } finally {
377: if (valueRs != null) {
378: valueRs.close();
379: }
380:
381: if (svStmt != null) {
382: svStmt.close();
383: }
384: }
385: }
386:
387: protected Object getSingleIndexedValueWithQuery(int columnIndex,
388: String query) throws SQLException {
389: return getSingleIndexedValueWithQuery(this .conn, columnIndex,
390: query);
391: }
392:
393: protected Object getSingleValue(String tableName,
394: String columnName, String whereClause) throws SQLException {
395: return getSingleValueWithQuery("SELECT " + columnName
396: + " FROM " + tableName
397: + ((whereClause == null) ? "" : " " + whereClause));
398: }
399:
400: protected Object getSingleValueWithQuery(String query)
401: throws SQLException {
402: return getSingleIndexedValueWithQuery(1, query);
403: }
404:
405: protected boolean isAdminConnectionConfigured() {
406: return System.getProperty(ADMIN_CONNECTION_PROPERTY_NAME) != null;
407: }
408:
409: protected boolean isServerRunningOnWindows() throws SQLException {
410: return (getMysqlVariable("datadir").indexOf('\\') != -1);
411: }
412:
413: public void logDebug(String message) {
414: if (System
415: .getProperty("com.mysql.jdbc.testsuite.noDebugOutput") == null) {
416: System.err.println(message);
417: }
418: }
419:
420: protected File newTempBinaryFile(String name, long size)
421: throws IOException {
422: File tempFile = File.createTempFile(name, "tmp");
423: tempFile.deleteOnExit();
424:
425: cleanupTempFiles(tempFile, name);
426:
427: FileOutputStream fos = new FileOutputStream(tempFile);
428: BufferedOutputStream bos = new BufferedOutputStream(fos);
429: for (long i = 0; i < size; i++) {
430: bos.write((byte) i);
431: }
432: bos.close();
433: assertTrue(tempFile.exists());
434: assertEquals(size, tempFile.length());
435: return tempFile;
436: }
437:
438: protected final boolean runLongTests() {
439: return runTestIfSysPropDefined("com.mysql.jdbc.testsuite.runLongTests");
440: }
441:
442: /**
443: * Checks whether a certain system property is defined, in order to
444: * run/not-run certain tests
445: *
446: * @param propName
447: * the property name to check for
448: *
449: * @return true if the property is defined.
450: */
451: protected boolean runTestIfSysPropDefined(String propName) {
452: String prop = System.getProperty(propName);
453:
454: return (prop != null) && (prop.length() > 0);
455: }
456:
457: protected boolean runMultiHostTests() {
458: return !runTestIfSysPropDefined(NO_MULTI_HOST_PROPERTY_NAME);
459: }
460:
461: /**
462: * Creates resources used by all tests.
463: *
464: * @throws Exception
465: * if an error occurs.
466: */
467: public void setUp() throws Exception {
468: System.out
469: .println("Loading JDBC driver '" + this .dbClass + "'");
470: Class.forName(this .dbClass).newInstance();
471: System.out.println("Done.\n");
472: this .createdObjects = new ArrayList();
473:
474: if (this .dbClass.equals("gwe.sql.gweMysqlDriver")) {
475: try {
476: this .conn = DriverManager.getConnection(dbUrl, "", "");
477: } catch (Exception ex) {
478: ex.printStackTrace();
479: fail();
480: }
481: } else {
482: try {
483: this .conn = DriverManager.getConnection(dbUrl);
484: } catch (Exception ex) {
485: ex.printStackTrace();
486: fail();
487: }
488: }
489:
490: System.out.println("Done.\n");
491: this .stmt = this .conn.createStatement();
492:
493: try {
494: if (dbUrl.indexOf("mysql") != -1) {
495: this .rs = this .stmt.executeQuery("SELECT VERSION()");
496: this .rs.next();
497: logDebug("Connected to " + this .rs.getString(1));
498: this .rs.close();
499: this .rs = null;
500: } else {
501: logDebug("Connected to "
502: + this .conn.getMetaData()
503: .getDatabaseProductName()
504: + " / "
505: + this .conn.getMetaData()
506: .getDatabaseProductVersion());
507: }
508: } finally {
509: if (this .rs != null) {
510: this .rs.close();
511: }
512: }
513: }
514:
515: /**
516: * Destroys resources created during the test case.
517: *
518: * @throws Exception
519: * DOCUMENT ME!
520: */
521: public void tearDown() throws Exception {
522: if (this .rs != null) {
523: try {
524: this .rs.close();
525: } catch (SQLException SQLE) {
526: ;
527: }
528: }
529:
530: for (int i = 0; i < this .createdObjects.size(); i++) {
531: try {
532: String[] objectInfo = (String[]) this .createdObjects
533: .get(i);
534:
535: dropSchemaObject(objectInfo[0], objectInfo[1]);
536: } catch (SQLException SQLE) {
537: ;
538: }
539: }
540:
541: if (this .stmt != null) {
542: try {
543: this .stmt.close();
544: } catch (SQLException SQLE) {
545: ;
546: }
547: }
548:
549: if (this .pstmt != null) {
550: try {
551: this .pstmt.close();
552: } catch (SQLException SQLE) {
553: ;
554: }
555: }
556:
557: if (this .conn != null) {
558: try {
559: this .conn.close();
560: } catch (SQLException SQLE) {
561: ;
562: }
563: }
564: }
565:
566: /**
567: * Checks whether the database we're connected to meets the given version
568: * minimum
569: *
570: * @param major
571: * the major version to meet
572: * @param minor
573: * the minor version to meet
574: *
575: * @return boolean if the major/minor is met
576: *
577: * @throws SQLException
578: * if an error occurs.
579: */
580: protected boolean versionMeetsMinimum(int major, int minor)
581: throws SQLException {
582: return versionMeetsMinimum(major, minor, 0);
583: }
584:
585: /**
586: * Checks whether the database we're connected to meets the given version
587: * minimum
588: *
589: * @param major
590: * the major version to meet
591: * @param minor
592: * the minor version to meet
593: *
594: * @return boolean if the major/minor is met
595: *
596: * @throws SQLException
597: * if an error occurs.
598: */
599: protected boolean versionMeetsMinimum(int major, int minor,
600: int subminor) throws SQLException {
601: return (((com.mysql.jdbc.Connection) this .conn)
602: .versionMeetsMinimum(major, minor, subminor));
603: }
604:
605: protected boolean isRunningOnJdk131() {
606: return this .runningOnJdk131;
607: }
608:
609: protected boolean isClassAvailable(String classname) {
610: try {
611: Class.forName(classname);
612: return true;
613: } catch (ClassNotFoundException e) {
614: return false;
615: }
616: }
617:
618: protected void closeMemberJDBCResources() {
619: if (this .rs != null) {
620: ResultSet toClose = this .rs;
621: this .rs = null;
622:
623: try {
624: toClose.close();
625: } catch (SQLException sqlEx) {
626: // ignore
627: }
628: }
629:
630: if (this .pstmt != null) {
631: PreparedStatement toClose = this .pstmt;
632: this .pstmt = null;
633:
634: try {
635: toClose.close();
636: } catch (SQLException sqlEx) {
637: // ignore
638: }
639: }
640: }
641:
642: protected boolean isRunningOnJRockit() {
643: String vmVendor = System.getProperty("java.vm.vendor");
644:
645: return (vmVendor != null && vmVendor.toUpperCase(Locale.US)
646: .startsWith("BEA"));
647: }
648:
649: protected String randomString() {
650: int length = (int) (Math.random() * 32);
651:
652: StringBuffer buf = new StringBuffer(length);
653:
654: for (int i = 0; i < length; i++) {
655: buf.append((char) ((Math.random() * 26) + 'a'));
656: }
657:
658: return buf.toString();
659: }
660:
661: protected void cleanupTempFiles(final File exampleTempFile,
662: final String tempfilePrefix) {
663:
664: File tempfilePath = exampleTempFile.getParentFile();
665:
666: File[] possibleFiles = tempfilePath
667: .listFiles(new FilenameFilter() {
668:
669: public boolean accept(File dir, String name) {
670: return (name.indexOf(tempfilePrefix) != -1 && !exampleTempFile
671: .getName().equals(name));
672: }
673: });
674:
675: for (int i = 0; i < possibleFiles.length; i++) {
676: try {
677: possibleFiles[i].delete();
678: } catch (Throwable t) {
679: // ignore, we're only making a best effort cleanup attempt here
680: }
681: }
682: }
683: }
|