• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

database-rider / database-rider / #917

24 Jan 2026 11:44AM UTC coverage: 84.22% (-0.04%) from 84.262%
#917

push

web-flow
add detailed error message for Operation.CONTAINS (#628)

Co-authored-by: Rafael Pestano <rmpestano@gmail.com>

66 of 80 new or added lines in 4 files covered. (82.5%)

1 existing line in 1 file now uncovered.

3197 of 3796 relevant lines covered (84.22%)

0.84 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.32
/rider-core/src/main/java/com/github/database/rider/core/util/ContainsFilterTable.java
1
package com.github.database.rider.core.util;
2

3
import org.dbunit.dataset.*;
4
import org.dbunit.dataset.datatype.DataType;
5
import org.slf4j.Logger;
6
import org.slf4j.LoggerFactory;
7

8
import java.util.ArrayList;
9
import java.util.List;
10
import java.util.regex.Pattern;
11

12
public class ContainsFilterTable implements ITable {
13

14
    /**
15
     * reference to the original table being wrapped
16
     */
17
    private final ITable originalTable;
18
    /** mapping of filtered rows, i.e, each entry on this list has the value of
19
     the index on the original table corresponding to the desired index.
20
     For instance, if the original table is:
21
     row Value
22
     0    v1
23
     1    v2
24
     2    v3
25
     3    v4
26
     And the expected values are:
27
     row Value
28
     0   v2
29
     1   v4
30
     The new table should be:
31
     row Value
32
     0    v2
33
     1    v4
34
     Consequently, the mapping will be {1, 3}
35
     */
36
    private final List<Integer> filteredRowIndexes;
37
    /**
38
     * logger
39
     */
40
    private final Logger logger = LoggerFactory.getLogger(RowFilterTable.class);
1✔
41

42
    /**
43
     * Creates a new {@link ITable} where some rows can be filtered out from the original table
44
     * @param actualTable The table to be wrapped
45
     * @param expectedTable actualTable will be filtered by this table
46
     * @param ignoredCols columns to ignore in comparison
47
     * @throws DataSetException throws DataSetException
48
     */
49
    public ContainsFilterTable(ITable actualTable, ITable expectedTable, List<String> ignoredCols) throws DataSetException {
1✔
50
        if ( expectedTable == null || actualTable == null ) {
1✔
51
            throw new IllegalArgumentException( "Constructor cannot receive null arguments" );
×
52
        }
53
        this.originalTable = actualTable;
1✔
54
        // sets the rows for the new table
55
        // NOTE: this conversion might be an issue for long tables, as it iterates for
56
        // all values of the original table and that might take time and memory leaks.
57
        this.filteredRowIndexes = setRows(expectedTable, toUpper(ignoredCols));
1✔
58
    }
1✔
59

60
    private List<String> toUpper(List<String> ignoredCols) {
61
        List<String> upperCaseColumns = new ArrayList<>();
1✔
62
        for (String ignoredCol : ignoredCols) {
1✔
63
            upperCaseColumns.add(ignoredCol.toUpperCase());
1✔
64
        }
1✔
65
        return upperCaseColumns;
1✔
66

67
    }
68

69
    private List<Integer> setRows(ITable expectedTable, List<String> ignoredCols) throws DataSetException {
70

71
        ITableMetaData tableMetadata = this.originalTable.getTableMetaData();
1✔
72
        this.logger.debug("Setting rows for table {}",  tableMetadata.getTableName() );
1✔
73

74
        int fullSize = expectedTable.getRowCount();
1✔
75
        List<String> columns = new ArrayList<>();
1✔
76
        if (fullSize > 0) {
1✔
77
            for (Column column : expectedTable.getTableMetaData().getColumns()) {
1✔
78
                columns.add(column.getColumnName());
1✔
79
            }
80
        }
81
        List<Integer> filteredRowIndexes = new ArrayList<>();
1✔
82
        final ContainsErrorMessage message = ContainsErrorMessage.create(columns, this.originalTable);
1✔
83
        for ( int row=0; row<fullSize; row++ ) {
1✔
84
            List<Object> values = new ArrayList<>();
1✔
85
            for (String column : columns) {
1✔
86
                values.add(expectedTable.getValue(row, column));
1✔
87
            }
1✔
88
            message.initWithValues(values);
1✔
89
            Integer actualRowIndex = tableContains(columns, values, filteredRowIndexes, ignoredCols, message);
1✔
90
            if (actualRowIndex == null) {
1✔
91
                this.logger.debug("Discarding row {}", row);
×
NEW
92
                message.print();
×
UNCOV
93
                continue;
×
94
            }
95

96
            this.logger.debug("Adding row {}", row);
1✔
97
            filteredRowIndexes.add(actualRowIndex);
1✔
98
        }
99
        return filteredRowIndexes;
1✔
100
    }
101

102
    /**
103
     * Searches for full match in original table by values from expected table
104
     * @param columns column names
105
     * @param values column values
106
     * @param filteredRowIndexes list of row indexes already found by previous runs
107
     * @return row index of original table containing all requested values
108
     * @throws DataSetException throws DataSetException
109
     */
110
    private Integer tableContains(List<String> columns, List<Object> values, List<Integer> filteredRowIndexes, List<String> ignoredCols, ContainsErrorMessage message) throws DataSetException {
111
        int fullSize = this.originalTable.getRowCount();
1✔
112
        message.addTableHeader();
1✔
113
        for ( int row=0; row<fullSize; row++ ) {
1✔
114
            message.addRow(row);
1✔
115
            boolean match = true;
1✔
116
            for (int column = 0; column < columns.size(); column++) {
1✔
117
                if(ignoredCols != null && ignoredCols.contains(columns.get(column).toUpperCase())) {
1✔
118
                    continue;
1✔
119
                }
120
                if (values.get(column) != null && values.get(column).toString().startsWith("regex:")) {
1✔
121
                    if (!regexMatches(values.get(column).toString(), this.originalTable.getValue(row, columns.get(column)).toString())) {
1✔
122
                        match = false;
×
123
                        break;
×
124
                    }
125
                    continue;
126
                }
127

128
                int columnIndex = this.originalTable.getTableMetaData().getColumnIndex(columns.get(column));
1✔
129
                DataType dataType = this.originalTable.getTableMetaData().getColumns()[columnIndex].getDataType();
1✔
130
                if (dataType.compare(values.get(column), this.originalTable.getValue(row, columns.get(column))) != 0) {
1✔
131
                    match = false;
1✔
132
                    message.addFail(column, row);
1✔
133
                    break;
1✔
134
                }
135
            }
136

137
            if (match && !filteredRowIndexes.contains(row)) {
1✔
138
                message.setMatch();
1✔
139
                return row;
1✔
140
            }
141
            message.nextLine();
1✔
142
        }
143
        return null;
×
144
    }
145

146
    private boolean regexMatches(String expectedValue, String actualValue) {
147
        Pattern pattern = Pattern.compile(expectedValue.substring(expectedValue.indexOf(':')+1).trim());
1✔
148
        return pattern.matcher(actualValue).matches();
1✔
149
    }
150

151
    @Override
152
    public ITableMetaData getTableMetaData() {
153
        logger.debug("getTableMetaData() - start");
1✔
154

155
        return this.originalTable.getTableMetaData();
1✔
156
    }
157

158
    @Override
159
    public int getRowCount() {
160
        logger.debug("getRowCount() - start");
1✔
161

162
        return this.filteredRowIndexes.size();
1✔
163
    }
164

165
    @Override
166
    public Object getValue(int row, String column) throws DataSetException
167
    {
168
        if(logger.isDebugEnabled())
1✔
169
            logger.debug("getValue(row={}, columnName={}) - start", Integer.toString(row), column);
×
170

171
        int max = this.filteredRowIndexes.size();
1✔
172
        if ( row < max ) {
1✔
173
            int realRow = this.filteredRowIndexes.get(row);
1✔
174
            return this.originalTable.getValue(realRow, column);
1✔
175
        } else {
176
            throw new RowOutOfBoundsException( "tried to access row " + row +
×
177
                    " but rowCount is " + max );
178
        }
179
    }
180
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc