org.apache.ddlutils.io.RoundtripTestBase.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.ddlutils.io.RoundtripTestBase.java

Source

package org.apache.ddlutils.io;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import java.io.StringWriter;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.sql.Types;
import java.util.List;

import junit.framework.TestSuite;

import org.apache.commons.beanutils.DynaBean;
import org.apache.ddlutils.DdlUtilsException;
import org.apache.ddlutils.PlatformFactory;
import org.apache.ddlutils.PlatformInfo;
import org.apache.ddlutils.TestDatabaseWriterBase;
import org.apache.ddlutils.dynabean.SqlDynaBean;
import org.apache.ddlutils.dynabean.SqlDynaClass;
import org.apache.ddlutils.dynabean.SqlDynaProperty;
import org.apache.ddlutils.model.Column;
import org.apache.ddlutils.model.Database;
import org.apache.ddlutils.model.ForeignKey;
import org.apache.ddlutils.model.Index;
import org.apache.ddlutils.model.IndexColumn;
import org.apache.ddlutils.model.Reference;
import org.apache.ddlutils.model.Table;
import org.apache.ddlutils.model.TypeMap;
import org.apache.ddlutils.platform.DefaultValueHelper;

/**
 * Base class for database roundtrip (creation & reconstruction from the database).
 * 
 * @version $Revision: 289996 $
 */
public abstract class RoundtripTestBase extends TestDatabaseWriterBase {
    /**
     * Creates the test suite for the given test class which must be a sub class of
     * {@link RoundtripTestBase}. If the platform supports it, it will be tested
     * with both delimited and undelimited identifiers.
     * 
     * @param testedClass The tested class
     * @return The tests
     */
    protected static TestSuite getTests(Class testedClass) {
        if (!RoundtripTestBase.class.isAssignableFrom(testedClass)
                || Modifier.isAbstract(testedClass.getModifiers())) {
            throw new DdlUtilsException("Cannot create parameterized tests for class " + testedClass.getName());
        }

        TestSuite suite = new TestSuite();

        try {
            Method[] methods = testedClass.getMethods();
            PlatformInfo info = null;
            RoundtripTestBase newTest;

            for (int idx = 0; (methods != null) && (idx < methods.length); idx++) {
                if (methods[idx].getName().startsWith("test") && ((methods[idx].getParameterTypes() == null)
                        || (methods[idx].getParameterTypes().length == 0))) {
                    newTest = (RoundtripTestBase) testedClass.newInstance();
                    newTest.setName(methods[idx].getName());
                    newTest.setUseDelimitedIdentifiers(false);
                    suite.addTest(newTest);

                    if (info == null) {
                        info = PlatformFactory.createNewPlatformInstance(newTest.getDatabaseName())
                                .getPlatformInfo();
                    }
                    if (info.isDelimitedIdentifiersSupported()) {
                        newTest = (RoundtripTestBase) testedClass.newInstance();
                        newTest.setName(methods[idx].getName());
                        newTest.setUseDelimitedIdentifiers(true);
                        suite.addTest(newTest);
                    }
                }
            }
        } catch (Exception ex) {
            throw new DdlUtilsException(ex);
        }

        return suite;
    }

    /** Whether to use delimited identifiers for the test. */
    private boolean _useDelimitedIdentifiers;

    /**
     * Specifies whether the test shall use delimited identifiers.
     * 
     * @param useDelimitedIdentifiers Whether to use delimited identifiers
     */
    protected void setUseDelimitedIdentifiers(boolean useDelimitedIdentifiers) {
        _useDelimitedIdentifiers = useDelimitedIdentifiers;
    }

    /**
     * {@inheritDoc}
     */
    protected void setUp() throws Exception {
        super.setUp();
        getPlatform().setDelimitedIdentifierModeOn(_useDelimitedIdentifiers);
    }

    /**
     * Inserts a row into the designated table.
     * 
     * @param tableName    The name of the table (case insensitive)
     * @param columnValues The values for the columns in order of definition
     */
    protected void insertRow(String tableName, Object[] columnValues) {
        Table table = getModel().findTable(tableName);
        DynaBean bean = getModel().createDynaBeanFor(table);

        for (int idx = 0; (idx < table.getColumnCount()) && (idx < columnValues.length); idx++) {
            Column column = table.getColumn(idx);

            bean.set(column.getName(), columnValues[idx]);
        }
        getPlatform().insert(getModel(), bean);
    }

    /**
     * Returns a "SELECT * FROM [table name]" statement. It also takes
     * delimited identifier mode into account if enabled.
     *  
     * @param table       The table
     * @param orderColumn The column to order the rows by (can be <code>null</code>)
     * @return The statement
     */
    protected String getSelectQueryForAllString(Table table, String orderColumn) {
        StringBuffer query = new StringBuffer();

        query.append("SELECT * FROM ");
        if (getPlatform().isDelimitedIdentifierModeOn()) {
            query.append(getPlatformInfo().getDelimiterToken());
        }
        query.append(table.getName());
        if (getPlatform().isDelimitedIdentifierModeOn()) {
            query.append(getPlatformInfo().getDelimiterToken());
        }
        if (orderColumn != null) {
            query.append(" ORDER BY ");
            if (getPlatform().isDelimitedIdentifierModeOn()) {
                query.append(getPlatformInfo().getDelimiterToken());
            }
            query.append(orderColumn);
            if (getPlatform().isDelimitedIdentifierModeOn()) {
                query.append(getPlatformInfo().getDelimiterToken());
            }
        }
        return query.toString();
    }

    /**
     * Retrieves all rows from the given table.
     * 
     * @param tableName The table
     * @return The rows
     */
    protected List getRows(String tableName) {
        Table table = getModel().findTable(tableName, getPlatform().isDelimitedIdentifierModeOn());

        return getPlatform().fetch(getModel(), getSelectQueryForAllString(table, null), new Table[] { table });
    }

    /**
     * Retrieves all rows from the given table.
     * 
     * @param tableName   The table
     * @param orderColumn The column to order the rows by
     * @return The rows
     */
    protected List getRows(String tableName, String orderColumn) {
        Table table = getModel().findTable(tableName, getPlatform().isDelimitedIdentifierModeOn());

        return getPlatform().fetch(getModel(), getSelectQueryForAllString(table, orderColumn),
                new Table[] { table });
    }

    /**
     * Returns the original model adjusted for type changes because of the native type mappings
     * which when read back from the database will map to different types.
     * 
     * @return The adjusted model
     */
    protected Database getAdjustedModel() {
        try {
            Database model = (Database) getModel().clone();

            for (int tableIdx = 0; tableIdx < model.getTableCount(); tableIdx++) {
                Table table = model.getTable(tableIdx);

                for (int columnIdx = 0; columnIdx < table.getColumnCount(); columnIdx++) {
                    Column column = table.getColumn(columnIdx);
                    int origType = column.getTypeCode();
                    int targetType = getPlatformInfo().getTargetJdbcType(origType);

                    // we adjust the column types if the native type would back-map to a
                    // different jdbc type
                    if (targetType != origType) {
                        column.setTypeCode(targetType);
                        // we should also adapt the default value
                        if (column.getDefaultValue() != null) {
                            DefaultValueHelper helper = getPlatform().getSqlBuilder().getDefaultValueHelper();

                            column.setDefaultValue(helper.convert(column.getDefaultValue(), origType, targetType));
                        }
                    }
                    // we also promote the default size if the column has no size
                    // spec of its own
                    if ((column.getSize() == null) && getPlatformInfo().hasSize(targetType)) {
                        Integer defaultSize = getPlatformInfo().getDefaultSize(targetType);

                        if (defaultSize != null) {
                            column.setSize(defaultSize.toString());
                        }
                    }
                    // finally the platform might return a synthetic default value if the column
                    // is a primary key column
                    if (getPlatformInfo().isSyntheticDefaultValueForRequiredReturned()
                            && (column.getDefaultValue() == null) && column.isRequired()
                            && !column.isAutoIncrement()) {
                        switch (column.getTypeCode()) {
                        case Types.TINYINT:
                        case Types.SMALLINT:
                        case Types.INTEGER:
                        case Types.BIGINT:
                            column.setDefaultValue("0");
                            break;
                        case Types.REAL:
                        case Types.FLOAT:
                        case Types.DOUBLE:
                            column.setDefaultValue("0.0");
                            break;
                        case Types.BIT:
                            column.setDefaultValue("false");
                            break;
                        default:
                            column.setDefaultValue("");
                            break;
                        }
                    }
                }
                // we also add the default names to foreign keys that are initially unnamed
                for (int fkIdx = 0; fkIdx < table.getForeignKeyCount(); fkIdx++) {
                    ForeignKey fk = table.getForeignKey(fkIdx);

                    if (fk.getName() == null) {
                        fk.setName(getPlatform().getSqlBuilder().getForeignKeyName(table, fk));
                    }
                }
            }
            return model;
        } catch (CloneNotSupportedException ex) {
            throw new RuntimeException(ex);
        }
    }

    /**
     * Compares the specified attribute value of the given bean with the expected object.
     * 
     * @param expected The expected object
     * @param bean     The bean
     * @param attrName The attribute name
     */
    protected void assertEquals(Object expected, Object bean, String attrName) {
        DynaBean dynaBean = (DynaBean) bean;
        Object value = dynaBean.get(attrName);

        if ((value instanceof byte[]) && !(expected instanceof byte[]) && (dynaBean instanceof SqlDynaBean)) {
            SqlDynaClass dynaClass = (SqlDynaClass) ((SqlDynaBean) dynaBean).getDynaClass();
            Column column = ((SqlDynaProperty) dynaClass.getDynaProperty(attrName)).getColumn();

            if (TypeMap.isBinaryType(column.getTypeCode())) {
                value = new BinaryObjectsHelper().deserialize((byte[]) value);
            }
        }
        if (expected == null) {
            assertNull(value);
        } else {
            assertEquals(expected, value);
        }
    }

    /**
     * Asserts that the two given database models are equal, and if not, writes both of them
     * in XML form to <code>stderr</code>.
     * 
     * @param expected The expected model
     * @param actual   The actual model
     */
    protected void assertEquals(Database expected, Database actual) {
        try {
            assertEquals("Model names do not match.", expected.getName(), actual.getName());
            assertEquals("Not the same number of tables.", expected.getTableCount(), actual.getTableCount());
            for (int tableIdx = 0; tableIdx < actual.getTableCount(); tableIdx++) {
                assertEquals(expected.getTable(tableIdx), actual.getTable(tableIdx));
            }
        } catch (Throwable ex) {
            StringWriter writer = new StringWriter();
            DatabaseIO dbIo = new DatabaseIO();

            dbIo.write(expected, writer);

            getLog().error("Expected model:\n" + writer.toString());

            writer = new StringWriter();
            dbIo.write(actual, writer);

            getLog().error("Actual model:\n" + writer.toString());

            if (ex instanceof Error) {
                throw (Error) ex;
            } else {
                throw new DdlUtilsException(ex);
            }
        }
    }

    /**
     * Asserts that the two given database tables are equal.
     * 
     * @param expected The expected table
     * @param actual   The actual table
     */
    protected void assertEquals(Table expected, Table actual) {
        if (_useDelimitedIdentifiers) {
            assertEquals("Table names do not match.",
                    getPlatform().getSqlBuilder().shortenName(expected.getName(),
                            getSqlBuilder().getMaxTableNameLength()),
                    getPlatform().getSqlBuilder().shortenName(actual.getName(),
                            getSqlBuilder().getMaxTableNameLength()));
        } else {
            assertEquals("Table names do not match (ignoring case).",
                    getPlatform().getSqlBuilder().shortenName(expected.getName().toUpperCase(),
                            getSqlBuilder().getMaxTableNameLength()),
                    getPlatform().getSqlBuilder().shortenName(actual.getName().toUpperCase(),
                            getSqlBuilder().getMaxTableNameLength()));
        }
        assertEquals("Not the same number of columns in table " + actual.getName() + ".", expected.getColumnCount(),
                actual.getColumnCount());
        for (int columnIdx = 0; columnIdx < actual.getColumnCount(); columnIdx++) {
            assertEquals(expected.getColumn(columnIdx), actual.getColumn(columnIdx));
        }
        assertEquals("Not the same number of foreign keys in table " + actual.getName() + ".",
                expected.getForeignKeyCount(), actual.getForeignKeyCount());
        // order is not assumed with the way foreignkeys are returned.
        for (int expectedFkIdx = 0; expectedFkIdx < expected.getForeignKeyCount(); expectedFkIdx++) {
            ForeignKey expectedFk = expected.getForeignKey(expectedFkIdx);
            String expectedName = getPlatform().getSqlBuilder().shortenName(expectedFk.getName(),
                    getSqlBuilder().getMaxForeignKeyNameLength());

            for (int actualFkIdx = 0; actualFkIdx < actual.getForeignKeyCount(); actualFkIdx++) {
                ForeignKey actualFk = actual.getForeignKey(actualFkIdx);
                String actualName = getPlatform().getSqlBuilder().shortenName(actualFk.getName(),
                        getSqlBuilder().getMaxForeignKeyNameLength());

                if ((_useDelimitedIdentifiers && expectedName.equals(actualName))
                        || (!_useDelimitedIdentifiers && expectedName.equalsIgnoreCase(actualName))) {
                    assertEquals(expectedFk, actualFk);
                }
            }
        }
        assertEquals("Not the same number of indices in table " + actual.getName() + ".", expected.getIndexCount(),
                actual.getIndexCount());
        for (int indexIdx = 0; indexIdx < actual.getIndexCount(); indexIdx++) {
            assertEquals(expected.getIndex(indexIdx), actual.getIndex(indexIdx));
        }
    }

    /**
     * Asserts that the two given columns are equal.
     * 
     * @param expected The expected column
     * @param actual   The actual column
     */
    protected void assertEquals(Column expected, Column actual) {
        if (_useDelimitedIdentifiers) {
            assertEquals("Column names do not match.",
                    getPlatform().getSqlBuilder().shortenName(expected.getName(),
                            getSqlBuilder().getMaxColumnNameLength()),
                    getPlatform().getSqlBuilder().shortenName(actual.getName(),
                            getSqlBuilder().getMaxColumnNameLength()));
        } else {
            assertEquals("Column names do not match (ignoring case).",
                    getPlatform().getSqlBuilder().shortenName(expected.getName().toUpperCase(),
                            getSqlBuilder().getMaxColumnNameLength()),
                    getPlatform().getSqlBuilder().shortenName(actual.getName().toUpperCase(),
                            getSqlBuilder().getMaxColumnNameLength()));
        }
        assertEquals("Primary key status not the same for column " + actual.getName() + ".",
                expected.isPrimaryKey(), actual.isPrimaryKey());
        assertEquals("Required status not the same for column " + actual.getName() + ".", expected.isRequired(),
                actual.isRequired());
        if (getPlatformInfo().getIdentityStatusReadingSupported()) {
            // we're only comparing this if the platform can actually read the
            // auto-increment status back from an existing database
            assertEquals("Auto-increment status not the same for column " + actual.getName() + ".",
                    expected.isAutoIncrement(), actual.isAutoIncrement());
        }
        assertEquals("Type not the same for column " + actual.getName() + ".", expected.getType(),
                actual.getType());
        assertEquals("Type code not the same for column " + actual.getName() + ".", expected.getTypeCode(),
                actual.getTypeCode());
        assertEquals("Parsed default values do not match for column " + actual.getName() + ".",
                expected.getParsedDefaultValue(), actual.getParsedDefaultValue());

        // comparing the size makes only sense for types where it is relevant
        if ((expected.getTypeCode() == Types.NUMERIC) || (expected.getTypeCode() == Types.DECIMAL)) {
            assertEquals("Precision not the same for column " + actual.getName() + ".", expected.getSizeAsInt(),
                    actual.getSizeAsInt());
            assertEquals("Scale not the same for column " + actual.getName() + ".", expected.getScale(),
                    actual.getScale());
        } else if ((expected.getTypeCode() == Types.CHAR) || (expected.getTypeCode() == Types.VARCHAR)
                || (expected.getTypeCode() == Types.BINARY) || (expected.getTypeCode() == Types.VARBINARY)) {
            assertEquals("Size not the same for column " + actual.getName() + ".", expected.getSize(),
                    actual.getSize());
        }
    }

    /**
     * Asserts that the two given foreign keys are equal.
     * 
     * @param expected The expected foreign key
     * @param actual   The actual foreign key
     */
    protected void assertEquals(ForeignKey expected, ForeignKey actual) {
        if (_useDelimitedIdentifiers) {
            assertEquals("Foreign key names do not match.",
                    getPlatform().getSqlBuilder().shortenName(expected.getName(),
                            getSqlBuilder().getMaxForeignKeyNameLength()),
                    getPlatform().getSqlBuilder().shortenName(actual.getName(),
                            getSqlBuilder().getMaxForeignKeyNameLength()));
            assertEquals("Referenced table names do not match.",
                    getPlatform().getSqlBuilder().shortenName(expected.getForeignTableName(),
                            getSqlBuilder().getMaxTableNameLength()),
                    getPlatform().getSqlBuilder().shortenName(actual.getForeignTableName(),
                            getSqlBuilder().getMaxTableNameLength()));
        } else {
            assertEquals("Foreign key names do not match (ignoring case).",
                    getPlatform().getSqlBuilder().shortenName(expected.getName().toUpperCase(),
                            getSqlBuilder().getMaxForeignKeyNameLength()),
                    getPlatform().getSqlBuilder().shortenName(actual.getName().toUpperCase(),
                            getSqlBuilder().getMaxForeignKeyNameLength()));
            assertEquals("Referenced table names do not match (ignoring case).",
                    getPlatform().getSqlBuilder().shortenName(expected.getForeignTableName().toUpperCase(),
                            getSqlBuilder().getMaxTableNameLength()),
                    getPlatform().getSqlBuilder().shortenName(actual.getForeignTableName().toUpperCase(),
                            getSqlBuilder().getMaxTableNameLength()));
        }
        assertEquals("Not the same number of references in foreign key " + actual.getName() + ".",
                expected.getReferenceCount(), actual.getReferenceCount());
        for (int refIdx = 0; refIdx < actual.getReferenceCount(); refIdx++) {
            assertEquals(expected.getReference(refIdx), actual.getReference(refIdx));
        }
    }

    /**
     * Asserts that the two given references are equal.
     * 
     * @param expected The expected reference
     * @param actual   The actual reference
     */
    protected void assertEquals(Reference expected, Reference actual) {
        if (_useDelimitedIdentifiers) {
            assertEquals("Local column names do not match.",
                    getPlatform().getSqlBuilder().shortenName(expected.getLocalColumnName(),
                            getSqlBuilder().getMaxColumnNameLength()),
                    getPlatform().getSqlBuilder().shortenName(actual.getLocalColumnName(),
                            getSqlBuilder().getMaxColumnNameLength()));
            assertEquals("Foreign column names do not match.",
                    getPlatform().getSqlBuilder().shortenName(expected.getForeignColumnName(),
                            getSqlBuilder().getMaxColumnNameLength()),
                    getPlatform().getSqlBuilder().shortenName(actual.getForeignColumnName(),
                            getSqlBuilder().getMaxColumnNameLength()));
        } else {
            assertEquals("Local column names do not match (ignoring case).",
                    getPlatform().getSqlBuilder().shortenName(expected.getLocalColumnName().toUpperCase(),
                            getSqlBuilder().getMaxColumnNameLength()),
                    getPlatform().getSqlBuilder().shortenName(actual.getLocalColumnName().toUpperCase(),
                            getSqlBuilder().getMaxColumnNameLength()));
            assertEquals("Foreign column names do not match (ignoring case).",
                    getPlatform().getSqlBuilder().shortenName(expected.getForeignColumnName().toUpperCase(),
                            getSqlBuilder().getMaxColumnNameLength()),
                    getPlatform().getSqlBuilder().shortenName(actual.getForeignColumnName().toUpperCase(),
                            getSqlBuilder().getMaxColumnNameLength()));
        }
    }

    /**
     * Asserts that the two given indices are equal.
     * 
     * @param expected The expected index
     * @param actual   The actual index
     */
    protected void assertEquals(Index expected, Index actual) {
        if (_useDelimitedIdentifiers) {
            assertEquals("Index names do not match.",
                    getPlatform().getSqlBuilder().shortenName(expected.getName(),
                            getSqlBuilder().getMaxConstraintNameLength()),
                    getPlatform().getSqlBuilder().shortenName(actual.getName(),
                            getSqlBuilder().getMaxConstraintNameLength()));
        } else {
            assertEquals("Index names do not match (ignoring case).",
                    getPlatform().getSqlBuilder().shortenName(expected.getName().toUpperCase(),
                            getSqlBuilder().getMaxConstraintNameLength()),
                    getPlatform().getSqlBuilder().shortenName(actual.getName().toUpperCase(),
                            getSqlBuilder().getMaxConstraintNameLength()));
        }
        assertEquals("Unique status not the same for index " + actual.getName() + ".", expected.isUnique(),
                actual.isUnique());
        assertEquals("Not the same number of columns in index " + actual.getName() + ".", expected.getColumnCount(),
                actual.getColumnCount());
        for (int columnIdx = 0; columnIdx < actual.getColumnCount(); columnIdx++) {
            assertEquals(expected.getColumn(columnIdx), actual.getColumn(columnIdx));
        }
    }

    /**
     * Asserts that the two given index columns are equal.
     * 
     * @param expected The expected index column
     * @param actual   The actual index column
     */
    protected void assertEquals(IndexColumn expected, IndexColumn actual) {
        if (_useDelimitedIdentifiers) {
            assertEquals("Index column names do not match.",
                    getPlatform().getSqlBuilder().shortenName(expected.getName(),
                            getSqlBuilder().getMaxColumnNameLength()),
                    getPlatform().getSqlBuilder().shortenName(actual.getName(),
                            getSqlBuilder().getMaxColumnNameLength()));
        } else {
            assertEquals("Index column names do not match (ignoring case).",
                    getPlatform().getSqlBuilder().shortenName(expected.getName().toUpperCase(),
                            getSqlBuilder().getMaxColumnNameLength()),
                    getPlatform().getSqlBuilder().shortenName(actual.getName().toUpperCase(),
                            getSqlBuilder().getMaxColumnNameLength()));
        }
        assertEquals("Size not the same for index column " + actual.getName() + ".", expected.getSize(),
                actual.getSize());
    }
}