All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hsqldb.test.TestUtil Maven / Gradle / Ivy

There is a newer version: 2.7.2
Show newest version
/* Copyright (c) 2001-2019, The HSQL Development Group
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the HSQL Development Group nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


package org.hsqldb.test;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
import java.io.Reader;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.SimpleDateFormat;

import org.hsqldb.lib.ArraySort;
import org.hsqldb.lib.ArrayUtil;
import org.hsqldb.lib.FileUtil;
import org.hsqldb.lib.HsqlArrayList;
import org.hsqldb.lib.LineGroupReader;
import org.hsqldb.lib.StopWatch;
import org.hsqldb.lib.StringComparator;
import org.hsqldb.lib.StringUtil;

/**
 * Utility class providing methodes for submitting test statements or
 * scripts to the database, comparing the results returned with
 * the expected results. The test script format is compatible with existing
 * scripts.
 *
 * Script writers be aware that you can't use stderr to distinguish error
 * messages.  This class writes error messages to stdout.
 *
 * @author Ewan Slater (ewanslater@users dot sourceforge.net)
 * @author Fred Toussi (fredt@users dot sourceforge.net)
 */
public class TestUtil {

    /*
     * The executing scripts do have state.  This class should be
     * redesigned with OOD.
     */
    static private final SimpleDateFormat sdfYMDHMS =
        new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    static private boolean      abortOnErr        = false;
    static final private String TIMESTAMP_VAR_STR = "${timestamp}";
    static final String LS = System.getProperty("line.separator", "\n");
    static final boolean        oneSessionOnly    = false;

    public static void main(String[] argv) {

        StopWatch sw = new StopWatch(true);

        TestUtil.testScripts("testrun/hsqldb", sw);
        System.out.println(sw.currentElapsedTimeToMessage("Total time :"));
    }

    public static void deleteDatabase(String path) {
        FileUtil.deleteOrRenameDatabaseFiles(path);
    }

    public static boolean delete(String file) {
        return new File(file).delete();
    }

    public static void checkDatabaseFilesDeleted(String path) {

        File[] list = FileUtil.getDatabaseFileList(path);

        if (list.length != 0) {
            System.out.println("database files not deleted");
        }
    }

    /**
     * Expand occurrences of "${timestamp}" in input to time stamps.
     */
    static protected void expandStamps(StringBuilder sb) {

        int i = sb.indexOf(TIMESTAMP_VAR_STR);

        if (i < 1) {
            return;
        }

        String timestamp;

        synchronized (sdfYMDHMS) {
            timestamp = sdfYMDHMS.format(new java.util.Date());
        }

        while (i > -1) {
            sb.replace(i, i + TIMESTAMP_VAR_STR.length(), timestamp);

            i = sb.indexOf(TIMESTAMP_VAR_STR);
        }
    }

    static void testScripts(String directory, StopWatch sw) {

        TestUtil.deleteDatabase("test1");

        try {
            Class.forName("org.hsqldb.jdbc.JDBCDriver");

            String     url = "jdbc:hsqldb:test1;sql.enforce_strict_size=true";
            String     user        = "sa";
            String     password    = "";
            Connection cConnection = null;
            String[]   filelist;
            String     absolute = new File(directory).getAbsolutePath();

            filelist = new File(absolute).list();

            ArraySort.sort((Object[]) filelist, filelist.length,
                           new StringComparator());

            for (int i = 0; i < filelist.length; i++) {
                String fname = filelist[i];

                if (fname.startsWith("TestSelf") && fname.endsWith(".txt")) {
                    long elapsed = sw.elapsedTime();

                    if (!oneSessionOnly || cConnection == null) {
                        cConnection = DriverManager.getConnection(url, user,
                                password);
                    }

                    print("Opened DB in "
                          + (double) (sw.elapsedTime() - elapsed) / 1000
                          + " s");
                    testScript(cConnection, absolute + File.separator + fname);

                    if (!oneSessionOnly) {
                        cConnection.close();
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            print("TestUtil init error: " + e.toString());
        }
    }

    static void testScript(Connection aConnection, String aPath) {

        /*
         * This is a legacy wrapper method which purposefully inherits the sins
         * of the original.
         * No indication is given to the invoker of even RuntimeExceptions.
         */
        File file = new File(aPath);

        try {
            TestUtil.testScript(aConnection, file.getAbsolutePath(),
                                new FileReader(file));
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("test script file error: " + e.toString());
        }
    }

    /**
     * Runs a preformatted script.

* * Where a result set is required, each line in the script will * be interpreted as a seperate expected row in the ResultSet * returned by the query. Within each row, fields should be delimited * using either comma (the default), or a user defined delimiter * which should be specified in the System property TestUtilFieldDelimiter * @param aConnection Connection object for the database * @param sourceName Identifies the script which failed * @param inReader Source of commands to be tested */ public static void testScript(Connection aConnection, String sourceName, Reader inReader) throws SQLException, IOException { Statement statement = aConnection.createStatement(); LineNumberReader reader = new LineNumberReader(inReader); LineGroupReader sqlReader = new LineGroupReader(reader); int startLine = 0; System.out.println("Opened test script file: " + sourceName); /** * we read the lines from the start of one section of the script "/*" * until the start of the next section, collecting the lines in the * list. * When a new section starts, we pass the list of lines * to the test method to be processed. */ try { while (true) { HsqlArrayList section = sqlReader.getSection(); startLine = sqlReader.getStartLineNumber(); if (section.size() == 0) { break; } testSection(statement, section, sourceName, startLine); } statement.close(); // The following catch blocks are just to report the source location // of the failure. } catch (SQLException se) { System.out.println("Error encountered at command beginning at " + sourceName + ':' + startLine); throw se; } catch (RuntimeException re) { System.out.println("Error encountered at command beginning at " + sourceName + ':' + startLine); throw re; } System.out.println("Processed " + reader.getLineNumber() + " lines from " + sourceName); } /** Legacy wrapper */ static void test(Statement stat, String s, int line) { TestUtil.test(stat, s, null, line); } /** * Performs a preformatted statement or group of statements and throws * if the result does not match the expected one. * @param line start line in the script file for this test * @param stat Statement object used to access the database * @param sourceName Identifies the script which failed * @param s Contains the type, expected result and SQL for the test */ static void test(Statement stat, String s, String sourceName, int line) { //maintain the interface for this method HsqlArrayList section = new HsqlArrayList(new String[8], 0); section.add(s); testSection(stat, section, sourceName, line); } /** * Method to save typing ;-) * This method does not distinguish between normal and error output. * * @param s String to be printed */ static void print(String s) { System.out.println(s); } /** * Takes a discrete section of the test script, contained in the * section vector, splits this into the expected result(s) and * submits the statement to the database, comparing the results * returned with the expected results. * If the actual result differs from that expected, or an * exception is thrown, then the appropriate message is printed. * @param stat Statement object used to access the database * @param section Vector of script lines containing a discrete * section of script (i.e. test type, expected results, * SQL for the statement). * @param line line of the script file where this section started */ private static void testSection(Statement stat, HsqlArrayList section, String scriptName, int line) { //create an appropriate instance of ParsedSection ParsedSection pSection = parsedSectionFactory(section); if (pSection == null) { //it was not possible to sucessfully parse the section System.out.println( "The section starting at " + scriptName + ':' + line + " could not be parsed, and so was not processed." + LS); return; } if (pSection instanceof IgnoreParsedSection) { System.out.println("At " + scriptName + ':' + line + ": " + pSection.getResultString()); return; } if (pSection instanceof DisplaySection || pSection instanceof WaitSection || pSection instanceof ProceedSection) { String s = pSection.getResultString(); if (s != null) { // May or may not want to report line number for these sections? System.out.println(pSection.getResultString()); } } if (pSection instanceof DisplaySection) { return; // Do not run test method for DisplaySections. } if (!pSection.test(stat)) { System.out.println("Section starting at " + scriptName + ':' + line + " returned an unexpected result: " + pSection.getTestResultString()); if (TestUtil.abortOnErr) { throw new TestRuntimeException(scriptName + ": " + line + "pSection"); } } } /** * Factory method to create appropriate parsed section class for the section * @param sectionLines Vector containing the section of script * @return a ParesedSection object */ private static ParsedSection parsedSectionFactory( HsqlArrayList sectionLines) { //type of the section char type = ' '; //read the first line of the Vector... String topLine = (String) sectionLines.get(0); //...and check it for the type... if (topLine.startsWith("/*")) { type = topLine.charAt(2); //if the type code is UPPERCASE and system property IgnoreCodeCase //has been set to true, make the type code lowercase if ((Character.isUpperCase(type)) && (Boolean.getBoolean("IgnoreCodeCase"))) { type = Character.toLowerCase(type); } //if the type code is invalid return null if (!ParsedSection.isValidCode(type)) { return null; } } //then pass this to the constructor for the ParsedSection class that //corresponds to the value of type switch (type) { case 'u' : { ParsedSection section = new UpdateParsedSection(sectionLines); if (TestUtil.oneSessionOnly) { if (section.getSql().toUpperCase().contains("SHUTDOWN")) { section = new IgnoreParsedSection(sectionLines, type); } } return section; } case 's' : return new SilentParsedSection(sectionLines); case 'w' : return new WaitSection(sectionLines); case 'p' : return new ProceedSection(sectionLines); case 'r' : return new ResultSetParsedSection(sectionLines); case 'o' : return new ResultSetOutputParsedSection(sectionLines); case 'c' : return new CountParsedSection(sectionLines); case 'd' : return new DisplaySection(sectionLines); case 'e' : return new ExceptionParsedSection(sectionLines); case ' ' : { ParsedSection section = new BlankParsedSection(sectionLines); if (TestUtil.oneSessionOnly) { if (section.getSql().toUpperCase().contains("SHUTDOWN")) { section = new IgnoreParsedSection(sectionLines, type); } } return section; } default : //if we arrive here, then we should have a valid code, //since we validated it earlier, so return an //IgnoreParsedSection object return new IgnoreParsedSection(sectionLines, type); } } /** * This method should certainly be an instance method. * * Can't do that until make this entire class OO. */ public static void setAbortOnErr(boolean aoe) { abortOnErr = aoe; } static class TestRuntimeException extends RuntimeException { public TestRuntimeException(String s) { super(s); } public TestRuntimeException(Throwable t) { super(t); } public TestRuntimeException(String s, Throwable t) { super(s, t); } } } /** * Abstract inner class representing a parsed section of script. * The specific ParsedSections for each type of test should inherit from this. */ abstract class ParsedSection { static final String LS = System.getProperty("line.separator", "\n"); /** * Type of this test. * @see #isValidCode(char) for allowed values */ protected char type = ' '; /** error message for this section */ String message = null; /** contents of the section as an array of Strings, one for each line in the section. */ protected String[] lines = null; /** number of the last row containing results in sectionLines */ protected int resEndRow = 0; /** SQL query to be submitted to the database. */ protected String sqlString = null; /** * Constructor when the section's input lines do not need to be parsed * into SQL. */ protected ParsedSection() {} /** * Common constructor functions for this family. * @param linesArray Array of the script lines containing the section of script. * database */ protected ParsedSection(HsqlArrayList linesArray) { //read the lines array backwards to get out the SQL String //using a StringBuilder for efficency until we've got the whole String StringBuilder sqlBuff = new StringBuilder(); int endIndex = 0; int k; String s = (String) linesArray.get(0); if (s.startsWith("/*")) { //if, after stripping out the declaration from topLine, the length of topLine //is greater than 0, then keep the rest of the line, as the first row. //Otherwise it will be discarded, and the offset (between the array and the vector) //set to 1. if (s.length() == 3) { lines = (String[]) linesArray.toArray(1, linesArray.size()); } else { lines = (String[]) linesArray.toArray(); lines[0] = lines[0].substring(3); } k = lines.length - 1; do { //check to see if the row contains the end of the result set if ((endIndex = lines[k].indexOf("*/")) != -1) { //then this is the end of the result set sqlBuff.insert(0, lines[k].substring(endIndex + 2)); lines[k] = lines[k].substring(0, endIndex); if (lines[k].length() == 0) { resEndRow = k - 1; } else { resEndRow = k; } break; } else { sqlBuff.insert(0, lines[k]); } k--; } while (k >= 0); } else { lines = (String[]) linesArray.toArray(); for (k = 0; k < lines.length; k++) { sqlBuff.append(lines[k]); sqlBuff.append(LS); } } //set sqlString value sqlString = sqlBuff.toString(); } /** * String representation of this ParsedSection * @return String representation of this ParsedSection */ protected String getTestResultString() { StringBuilder b = new StringBuilder(); b.append(LS + "******" + LS); b.append("Type: "); b.append(getType()).append(LS); b.append("SQL: ").append(getSql()).append(LS); b.append("expected results:").append(LS); b.append(getResultString()).append(LS); //check to see if the message field has been populated if (getMessage() != null) { b.append(LS + "message:").append(LS); b.append(getMessage()).append(LS); } b.append("actual results:").append(LS); b.append(getActualResultString()); b.append(LS + "******" + LS); return b.toString(); } /** * returns a String representation of the expected result for the test * @return The expected result(s) for the test */ protected abstract String getResultString(); /** * returns a String representation of the actual result for the test * @return The expected result(s) for the test */ protected String getActualResultString() { return ""; } /** * returns the error message for the section * * @return message */ protected String getMessage() { return message; } /** * returns the type of this section * @return type of this section */ protected char getType() { return type; } /** * returns the SQL statement for this section * @return SQL statement for this section */ protected String getSql() { return sqlString; } /** * performs the test contained in the section against the database. * @param aStatement Statement object * @return true if the result(s) are as expected, otherwise false */ protected boolean test(Statement aStatement) { try { String sql = getSql(); aStatement.execute(sql); } catch (Exception x) { message = x.toString(); return false; } return true; } /** * Checks that the type code letter is valid * @param aCode Lower-cased type code to validate. * @return true if the type code is valid, otherwise false. */ protected static boolean isValidCode(char aCode) { /* Allowed values for test codes are: * (note that UPPERCASE codes, while valid are only processed if the * system property IgnoreCodeCase has been set to true) * * 'u' - update * 'c' - count * 'e' - exception * 'r' - results * 'w' - wait * 'p' - proceed * 's' - silent * 'd' - display (No reason to use upper-case). * ' ' - not a test */ switch (aCode) { case ' ' : case 'r' : case 'o' : case 'e' : case 'c' : case 'u' : case 's' : case 'd' : case 'w' : case 'p' : return true; } return false; } } /** Represents a ParsedSection for a ResultSet test */ class ResultSetParsedSection extends ParsedSection { private String delim = System.getProperty("TestUtilFieldDelimiter", ","); private String[] expectedRows = null; private String[] actualRows = null; /** * constructs a new instance of ResultSetParsedSection, interpreting * the supplied results as one or more lines of delimited field values */ protected ResultSetParsedSection(HsqlArrayList linesArray) { super(linesArray); type = 'r'; //now we'll populate the expectedResults array expectedRows = new String[(resEndRow + 1)]; for (int i = 0; i <= resEndRow; i++) { int skip = StringUtil.skipSpaces(lines[i], 0); expectedRows[i] = lines[i].substring(skip); } } protected String getResultString() { StringBuilder printVal = new StringBuilder(); String[] expectedRows = getExpectedRows(); for (int i = 0; i < expectedRows.length; i++) { printVal.append(expectedRows[i]).append(LS); } return printVal.toString(); } protected String getActualResultString() { StringBuilder printVal = new StringBuilder(); String[] actualRows = getActualRows(); if (actualRows == null) { return "no result"; } for (int i = 0; i < actualRows.length; i++) { printVal.append(actualRows[i]).append(LS); } return printVal.toString(); } protected boolean test(Statement aStatement) { try { try { //execute the SQL aStatement.execute(getSql()); } catch (SQLException s) { throw new Exception("Expected a ResultSet, but got the error: " + s.getMessage()); } //check that update count != -1 if (aStatement.getUpdateCount() != -1) { throw new Exception( "Expected a ResultSet, but got an update count of " + aStatement.getUpdateCount()); } //iterate over the ResultSet HsqlArrayList list = new HsqlArrayList(new String[1][], 0); ResultSet results = aStatement.getResultSet(); int colCount = results.getMetaData().getColumnCount(); while (results.next()) { String[] row = new String[colCount]; for (int i = 0; i < colCount; i++) { row[i] = results.getString(i + 1); } list.add(row); } results.close(); actualRows = new String[list.size()]; for (int i = 0; i < list.size(); i++) { String[] row = (String[]) list.get(i); StringBuilder sb = new StringBuilder(); for (int j = 0; j < row.length; j++) { if (j > 0) { sb.append(','); } sb.append(row[j]); } actualRows[i] = sb.toString(); } String[] expectedRows = getExpectedRows(); int count = 0; for (; count < list.size(); count++) { if (count < expectedRows.length) { String[] expectedFields = StringUtil.split(expectedRows[count], delim); // handle ARRAY[val,val, val] commas for (int i = 0; i < expectedFields.length; i++) { if (expectedFields[i] == null) { expectedFields = (String[]) ArrayUtil.resizeArray( expectedFields, i); break; } if (expectedFields[i].startsWith("ARRAY[")) { if (expectedFields[i].endsWith("]")) { continue; } for (int j = i + 1; j < expectedFields.length; j++) { String part = expectedFields[j]; expectedFields[i] += delim + part; if (part.endsWith("]")) { ArrayUtil.adjustArray( ArrayUtil.CLASS_CODE_OBJECT, expectedFields, expectedFields.length, i + 1, i - j); break; } } } } //check that we have the number of columns expected... if (colCount == expectedFields.length) { //...and if so, check that the column values are as expected... int j = 0; for (int i = 0; i < expectedFields.length; i++) { j = i + 1; String actual = ((String[]) list.get(count))[i]; //...including null values... if (actual == null) { //..then we have a null //...check to see if we were expecting it... if (!expectedFields[i].equalsIgnoreCase( "NULL")) { message = "Expected row " + (count + 1) + " of the ResultSet to contain:" + LS + expectedRows[count] + LS + "but field " + j + " contained NULL"; break; } } else if (!actual.equals(expectedFields[i])) { //then the results are different message = "Expected row " + (count + 1) + " of the ResultSet to contain:" + LS + expectedRows[count] + LS + "but field " + j + " contained " + actual; break; } } } else { //we have the wrong number of columns message = "Expected the ResultSet to contain " + expectedFields.length + " fields, but it contained " + colCount + " fields."; } } if (message != null) { break; } } //check that we got as many rows as expected if (count != expectedRows.length) { if (message == null) { //we don't have the expected number of rows message = "Expected the ResultSet to contain " + expectedRows.length + " rows, but it contained " + count + " rows."; } } } catch (Exception x) { message = x.toString(); return false; } return message == null; } private String[] getExpectedRows() { return expectedRows; } private String[] getActualRows() { return actualRows; } } /** Represents a ParsedSection for a ResultSet dump */ class ResultSetOutputParsedSection extends ParsedSection { private String delim = System.getProperty("TestUtilFieldDelimiter", ","); private String[] expectedRows = null; /** * constructs a new instance of ResultSetParsedSection, interpreting * the supplied results as one or more lines of delimited field values */ protected ResultSetOutputParsedSection(HsqlArrayList linesArray) { super(linesArray); type = 'o'; } protected String getResultString() { return ""; } protected boolean test(Statement aStatement) { try { try { //execute the SQL aStatement.execute(getSql()); } catch (SQLException s) { throw new Exception("Expected a ResultSet, but got the error: " + s.getMessage()); } //check that update count != -1 if (aStatement.getUpdateCount() != -1) { throw new Exception( "Expected a ResultSet, but got an update count of " + aStatement.getUpdateCount()); } //iterate over the ResultSet ResultSet results = aStatement.getResultSet(); StringBuilder printVal = new StringBuilder(); while (results.next()) { for (int j = 0; j < results.getMetaData().getColumnCount(); j++) { if (j != 0) { printVal.append(','); } printVal.append(results.getString(j + 1)); } printVal.append(LS); } throw new Exception(printVal.toString()); } catch (Exception x) { message = x.toString(); return false; } } private String[] getExpectedRows() { return expectedRows; } } /** Represents a ParsedSection for an update test */ class UpdateParsedSection extends ParsedSection { //expected update count int countWeWant; protected UpdateParsedSection(HsqlArrayList linesArray) { super(linesArray); type = 'u'; countWeWant = Integer.parseInt(lines[0]); } protected String getResultString() { return Integer.toString(getCountWeWant()); } private int getCountWeWant() { return countWeWant; } protected boolean test(Statement aStatement) { try { try { //execute the SQL aStatement.execute(getSql()); } catch (SQLException s) { throw new Exception("Expected an update count of " + getCountWeWant() + ", but got the error: " + s.getMessage()); } if (aStatement.getUpdateCount() != getCountWeWant()) { throw new Exception("Expected an update count of " + getCountWeWant() + ", but got an update count of " + aStatement.getUpdateCount() + "."); } } catch (Exception x) { message = x.toString(); return false; } return true; } } class WaitSection extends ParsedSection { /* Would love to have a setting to say whether multi-thread mode, * but the static design of TestUtil prevents that. * a W command will cause a non-threaded execution to wait forever. */ static private String W_SYNTAX_MSG = "Syntax of Wait commands:" + LS + " /*w 123*/ To Wait 123 milliseconds" + LS + " /*w false x*/ Wait until /*p*/ command in another script has executed" + LS + " /*w true x*/ Same, but the /*p*/ must not have executed yet"; /** Represents a ParsedSection for wait execution */ long sleepTime = -1; Waiter waiter = null; boolean enforceSequence = false; protected WaitSection(HsqlArrayList linesArray) { /* Can't user the super constructor, since it does funny things when * constructing the SQL Buffer, which we don't need. */ lines = (String[]) linesArray.toArray(); int closeCmd = lines[0].indexOf("*/"); String cmd = lines[0].substring(0, closeCmd); lines[0] = lines[0].substring(closeCmd + 2).trim(); String trimmed = cmd.trim(); if (trimmed.indexOf('e') < 0 && trimmed.indexOf('E') < 0) { // Does not contain "true" or "false" sleepTime = Long.parseLong(trimmed); } else { try { // Would like to use String.split(), but don't know if Java 4 // is allowed here. // Until we can use Java 4, prohibit tabs as white space. int index = trimmed.indexOf(' '); if (index < 0) { throw new IllegalArgumentException(); } enforceSequence = Boolean.valueOf(trimmed.substring(0, index)).booleanValue(); waiter = Waiter.getWaiter(trimmed.substring(index).trim()); } catch (IllegalArgumentException ie) { throw new IllegalArgumentException(W_SYNTAX_MSG); } } type = 'w'; } protected String getResultString() { StringBuilder sb = new StringBuilder(); if (lines.length == 1 && lines[0].trim().length() < 1) { return null; } for (int i = 0; i < lines.length; i++) { if (i > 0) { sb.append(LS); } sb.append("+ " + lines[i]); } TestUtil.expandStamps(sb); return sb.toString().trim(); } protected boolean test(Statement aStatement) { if (waiter == null) { try { //System.err.println("Sleeping for " + sleepTime + " ms."); Thread.sleep(sleepTime); } catch (InterruptedException ie) { throw new RuntimeException("Test sleep interrupted", ie); } } else { waiter.waitFor(enforceSequence); } return true; } } class ProceedSection extends ParsedSection { /* See comment above for WaitSection */ static private String P_SYNTAX_MSG = "Syntax of Proceed commands:" + LS + " /*p false x*/ /*p*/ command in another script may Proceed" + LS + " /*p true x*/ Same, but the /*w*/ must be waiting when we execute /*p*/" ; /** Represents a ParsedSection for wait execution */ Waiter waiter = null; boolean enforceSequence = false; protected ProceedSection(HsqlArrayList linesArray) { /* Can't use the super constructor, since it does funny things when * constructing the SQL Buffer, which we don't need. */ lines = (String[]) linesArray.toArray(); int closeCmd = lines[0].indexOf("*/"); String cmd = lines[0].substring(0, closeCmd); lines[0] = lines[0].substring(closeCmd + 2).trim(); String trimmed = cmd.trim(); try { // Would like to use String.split(), but don't know if Java 4 // is allowed here. // Until we can use Java 4, prohibit tabs as white space. int index = trimmed.indexOf(' '); if (index < 0) { throw new IllegalArgumentException(); } enforceSequence = Boolean.valueOf(trimmed.substring(0, index)).booleanValue(); waiter = Waiter.getWaiter(trimmed.substring(index).trim()); } catch (IllegalArgumentException ie) { throw new IllegalArgumentException(P_SYNTAX_MSG); } type = 'p'; } protected String getResultString() { StringBuilder sb = new StringBuilder(); if (lines.length == 1 && lines[0].trim().length() < 1) { return ""; } for (int i = 0; i < lines.length; i++) { if (i > 0) { sb.append(LS); } sb.append("+ " + lines[i]); } TestUtil.expandStamps(sb); return sb.toString().trim(); } protected boolean test(Statement aStatement) { waiter.resume(enforceSequence); return true; } } /** Represents a ParsedSection for silent execution */ class SilentParsedSection extends ParsedSection { protected SilentParsedSection(HsqlArrayList linesArray) { super(linesArray); type = 's'; } protected String getResultString() { return ""; } protected boolean test(Statement aStatement) { try { aStatement.execute(getSql()); } catch (Exception x) {} return true; } } /** Represents a ParsedSection for a count test */ class CountParsedSection extends ParsedSection { //expected row count private int countWeWant; protected CountParsedSection(HsqlArrayList linesArray) { super(linesArray); type = 'c'; countWeWant = Integer.parseInt(lines[0]); } protected String getResultString() { return Integer.toString(getCountWeWant()); } private int getCountWeWant() { return countWeWant; } protected boolean test(Statement aStatement) { try { //execute the SQL try { aStatement.execute(getSql()); } catch (SQLException s) { throw new Exception("Expected a ResultSet containing " + getCountWeWant() + " rows, but got the error: " + s.getMessage()); } //check that update count != -1 if (aStatement.getUpdateCount() != -1) { throw new Exception( "Expected a ResultSet, but got an update count of " + aStatement.getUpdateCount()); } //iterate over the ResultSet ResultSet results = aStatement.getResultSet(); int count = 0; while (results.next()) { count++; } //check that we got as many rows as expected if (count != getCountWeWant()) { //we don't have the expected number of rows throw new Exception("Expected the ResultSet to contain " + getCountWeWant() + " rows, but it contained " + count + " rows."); } } catch (Exception x) { message = x.toString(); return false; } return true; } } /** Represents a ParsedSection for an Exception test */ class ExceptionParsedSection extends ParsedSection { private String expectedState = null; private Throwable caught = null; protected ExceptionParsedSection(HsqlArrayList linesArray) { super(linesArray); expectedState = lines[0].trim(); if (expectedState.length() < 1) { expectedState = null; } type = 'e'; } protected String getResultString() { return (caught == null) ? "Nothing thrown" : caught.toString(); } protected boolean test(Statement aStatement) { try { aStatement.execute(getSql()); } catch (SQLException sqlX) { caught = sqlX; if (expectedState == null || expectedState.equalsIgnoreCase(sqlX.getSQLState())) { return true; } message = "SQLState '" + sqlX.getSQLState() + "' : " + sqlX.toString() + " instead of '" + expectedState + "'"; } catch (Exception x) { caught = x; message = x.toString(); } return false; } } /** Represents a ParsedSection for a section with blank type */ class BlankParsedSection extends ParsedSection { protected BlankParsedSection(HsqlArrayList linesArray) { super(linesArray); type = ' '; } protected String getResultString() { return ""; } } /** Represents a ParsedSection that is to be ignored */ class IgnoreParsedSection extends ParsedSection { protected IgnoreParsedSection(HsqlArrayList sectionLines, char aType) { /* Extremely ambiguous to use input parameter of same exact * variable name as the superclass member "lines". * Therefore, renaming to inLines. */ // Inefficient to parse this into SQL when we aren't going to use // it as SQL. Should probably just be removed to use the // super() constructor. super(sectionLines); type = aType; } protected String getResultString() { return "This section, of type '" + getType() + "' was ignored"; } } /** Represents a Section to be Displayed, not executed */ class DisplaySection extends ParsedSection { protected DisplaySection(HsqlArrayList sectionLines) { /* Can't user the super constructor, since it does funny things when * constructing the SQL Buffer, which we don't need. */ lines = (String[]) sectionLines.toArray(); int firstSlash = lines[0].indexOf('/'); lines[0] = lines[0].substring(firstSlash + 1).trim(); } protected String getResultString() { StringBuilder sb = new StringBuilder(); if (lines.length == 1 && lines[0].trim().length() < 1) { return null; } for (int i = 0; i < lines.length; i++) { if (i > 0) { sb.append(LS); } sb.append("+ " + lines[i]); } TestUtil.expandStamps(sb); return sb.toString().trim(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy