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

eu.drus.jpa.unit.sql.dbunit.DataSetComparator Maven / Gradle / Ivy

The newest version!
package eu.drus.jpa.unit.sql.dbunit;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Function;

import org.dbunit.Assertion;
import org.dbunit.DatabaseUnitException;
import org.dbunit.assertion.DbComparisonFailure;
import org.dbunit.assertion.DiffCollectingFailureHandler;
import org.dbunit.assertion.Difference;
import org.dbunit.dataset.Column;
import org.dbunit.dataset.CompositeTable;
import org.dbunit.dataset.DataSetException;
import org.dbunit.dataset.FilteredDataSet;
import org.dbunit.dataset.FilteredTableMetaData;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.ITable;
import org.dbunit.dataset.NoSuchTableException;
import org.dbunit.dataset.SortedTable;
import org.dbunit.dataset.filter.DefaultColumnFilter;
import org.dbunit.dataset.filter.IColumnFilter;
import org.dbunit.dataset.filter.IncludeTableFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import eu.drus.jpa.unit.api.JpaUnitException;
import eu.drus.jpa.unit.spi.AssertionErrorCollector;
import eu.drus.jpa.unit.spi.ColumnsHolder;

public class DataSetComparator {

    private static final Function ID_MAPPER = (final String name) -> name;
    private static final String DIFF_ERROR = "%s | In row %d: expected value of %s \"%s\" but was \"%s\".";

    private static final Logger LOG = LoggerFactory.getLogger(DataSetComparator.class);

    private final ColumnsHolder toExclude;

    private final ColumnsHolder orderBy;

    private final Set> columnFilters;

    private boolean isStrict;

    public DataSetComparator(final String[] orderBy, final String[] toExclude, final boolean isStrict,
            final Set> columnFilters) {
        this.toExclude = new ColumnsHolder(toExclude, ID_MAPPER);
        this.orderBy = new ColumnsHolder(orderBy, ID_MAPPER);
        this.isStrict = isStrict;
        this.columnFilters = columnFilters;
    }

    public void compare(final IDataSet currentDataSet, final IDataSet expectedDataSet, final AssertionErrorCollector errorCollector)
            throws DatabaseUnitException {
        if (expectedDataSet.getTableNames().length == 0) {
            shouldBeEmpty(currentDataSet, errorCollector);
        } else {
            compareContent(currentDataSet, expectedDataSet, errorCollector);
        }
    }

    private void shouldBeEmpty(final IDataSet dataSet, final AssertionErrorCollector errorCollector) throws DatabaseUnitException {
        for (final String tableName : dataSet.getTableNames()) {
            final int rowCount = dataSet.getTable(tableName).getRowCount();
            if (rowCount != 0) {
                errorCollector.collect(tableName + " was expected to be empty, but has <" + rowCount + "> entries.");
            }
        }
    }

    @SuppressWarnings("unchecked")
    private void compareContent(final IDataSet currentDataSet, final IDataSet expectedDataSet, final AssertionErrorCollector errorCollector)
            throws DatabaseUnitException {
        final String[] expectedTableNames = expectedDataSet.getTableNames();
        final FilteredDataSet filteredCurrentDataSet = new FilteredDataSet(new IncludeTableFilter(expectedTableNames), currentDataSet);

        for (final String tableName : expectedTableNames) {
            try {
                final List columnsForSorting = defineColumnsForSorting(filteredCurrentDataSet, expectedDataSet, tableName);

                final ITable table = filteredCurrentDataSet.getTable(tableName);
                final ITable expectedTable = sort(new TableWrapper(expectedDataSet.getTable(tableName), table.getTableMetaData()),
                        columnsForSorting);
                final ITable currentTable = sort(table, columnsForSorting);

                final List columnsToIgnore = extractColumnsToBeIgnored(expectedDataSet.getTable(tableName), table);

                final String[] toBeIgnored = columnsToIgnore.toArray(new String[columnsToIgnore.size()]);

                final ITable expectedTableWithFilteredColumns = filter(expectedTable, toBeIgnored);
                final ITable actualTableWithFilteredColumns = filter(currentTable, toBeIgnored);

                final DiffCollectingFailureHandler diffCollector = new DiffCollectingFailureHandler();
                Assertion.assertEquals(expectedTableWithFilteredColumns, actualTableWithFilteredColumns, diffCollector);

                collectErrors(errorCollector, diffCollector.getDiffList());
            } catch (final NoSuchTableException e) {
                final int rowCount = expectedDataSet.getTable(tableName).getRowCount();
                errorCollector.collect(tableName + " was expected to be present and to contain <" + rowCount + "> entries, but not found.");
            } catch (final DbComparisonFailure e) {
                errorCollector.collect(e.getMessage());
            }
        }

        if (isStrict) {
            final List currentTableNames = new ArrayList<>(Arrays.asList(currentDataSet.getTableNames()));
            currentTableNames.removeAll(Arrays.asList(expectedTableNames));
            for (final String notExpectedTableName : currentTableNames) {
                try {
                    final int rowCount = currentDataSet.getTable(notExpectedTableName).getRowCount();
                    if (rowCount > 0) {
                        errorCollector.collect(
                                notExpectedTableName + " was not expected, but is present and contains <" + rowCount + "> entries.");
                    }
                } catch (final NoSuchTableException e) {
                    // This should usually not happen. But it looks some persistence provider do not
                    // close the connection pool immediately so that if in-memory DB is used a new
                    // entity context manager is created before the previous active DB instance
                    // could be dropped.
                    // TODO: This needs further investigations
                }
            }
        }
    }

    private List defineColumnsForSorting(final IDataSet currentDataSet, final IDataSet expectedDataSet, final String tableName)
            throws DataSetException {

        final List additionalColumns = additionalColumnsForSorting(expectedDataSet.getTable(tableName),
                currentDataSet.getTable(tableName));

        final List result = new ArrayList<>();
        result.addAll(orderBy.getColumns(tableName));
        result.addAll(additionalColumns);
        return result;
    }

    private ITable sort(final ITable table, final List columnsForSorting) throws DataSetException {
        final SortedTable sortedTable = new SortedTable(table, columnsForSorting.toArray(new String[columnsForSorting.size()]));
        sortedTable.setUseComparable(true);
        return sortedTable;
    }

    private List extractColumnsToBeIgnored(final ITable expectedTableState, final ITable currentTableState)
            throws DataSetException {
        final List columnsToIgnore = extractNotExpectedColumnNames(expectedTableState, currentTableState);
        final String tableName = expectedTableState.getTableMetaData().getTableName();

        columnsToIgnore.addAll(toExclude.getColumns(tableName));

        final List nonExistingColumns = new ArrayList<>(columnsToIgnore);
        nonExistingColumns.removeAll(extractColumnNames(currentTableState.getTableMetaData().getColumns()));

        if (!nonExistingColumns.isEmpty()) {
            LOG.debug("Columns which are specified to be filtered out {} are not existing in the table {}",
                    Arrays.toString(nonExistingColumns.toArray()), tableName);
        }
        return columnsToIgnore;
    }

    private ITable filter(final ITable table, final String[] columnsToFilter) throws DataSetException {
        final ITable filteredTable = DefaultColumnFilter.excludedColumnsTable(table, columnsToFilter);
        return applyCustomFilters(filteredTable);
    }

    private void collectErrors(final AssertionErrorCollector errorCollector, final List diffs) {
        for (final Difference diff : diffs) {
            final String tableName = diff.getActualTable().getTableMetaData().getTableName();
            errorCollector.collect(String.format(DIFF_ERROR, tableName, diff.getRowIndex(), diff.getColumnName(), diff.getExpectedValue(),
                    diff.getActualValue()));
        }
    }

    private List additionalColumnsForSorting(final ITable expectedTableState, final ITable currentTableState)
            throws DataSetException {
        final List columnsForSorting = new ArrayList<>();
        final Set allColumns = new HashSet<>(extractColumnNames(expectedTableState.getTableMetaData().getColumns()));
        final Set columnsToIgnore = new HashSet<>(extractColumnsToBeIgnored(expectedTableState, currentTableState));
        for (final String column : allColumns) {
            if (!columnsToIgnore.contains(column)) {
                columnsForSorting.add(column);
            }
        }

        return columnsForSorting;
    }

    private ITable applyCustomFilters(final ITable table) throws DataSetException {
        ITable compositeTable = table;
        for (final Class columnFilter : columnFilters) {
            IColumnFilter customColumnFilter;
            try {
                customColumnFilter = columnFilter.newInstance();
            } catch (InstantiationException | IllegalAccessException e) {
                throw new JpaUnitException("Could not instanciate custom column filter", e);
            }
            final FilteredTableMetaData metaData = new FilteredTableMetaData(compositeTable.getTableMetaData(), customColumnFilter);
            compositeTable = new CompositeTable(metaData, compositeTable);
        }
        return compositeTable;
    }

    private Collection extractColumnNames(final Column[] columns) {
        final List names = new ArrayList<>(columns.length);
        for (final Column column : columns) {
            names.add(column.getColumnName().toLowerCase());
        }
        return names;
    }

    private List extractNotExpectedColumnNames(final ITable expectedTable, final ITable currentTable) throws DataSetException {
        final Set actualColumnNames = new HashSet<>();
        final Set expectedColumnNames = new HashSet<>();

        if (currentTable != null) {
            actualColumnNames.addAll(extractColumnNames(currentTable.getTableMetaData().getColumns()));
        }

        if (expectedTable != null) {
            expectedColumnNames.addAll(extractColumnNames(expectedTable.getTableMetaData().getColumns()));
        }

        actualColumnNames.removeAll(expectedColumnNames);
        return new ArrayList<>(actualColumnNames);
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy