
org.apache.calcite.sql.test.ResultCheckers Maven / Gradle / Ivy
/*
* 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.
*/
package org.apache.calcite.sql.test;
import org.apache.calcite.avatica.ColumnMetaData;
import org.apache.calcite.test.Matchers;
import org.apache.calcite.util.ImmutableNullableSet;
import org.apache.calcite.util.JdbcType;
import com.google.common.collect.ImmutableSet;
import org.hamcrest.Matcher;
import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.Types;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Pattern;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
import static java.util.Objects.requireNonNull;
/** Utilities for {@link SqlTester.ResultChecker}. */
public class ResultCheckers {
private ResultCheckers() {
}
public static SqlTester.ResultChecker isExactly(double value) {
return new MatcherResultChecker<>(is(value),
JdbcType.DOUBLE);
}
public static SqlTester.ResultChecker isExactly(String value) {
return new MatcherResultChecker<>(is(new BigDecimal(value)),
JdbcType.BIG_DECIMAL);
}
public static SqlTester.ResultChecker isWithin(double value, double delta) {
return new MatcherResultChecker<>(Matchers.within(value, delta),
JdbcType.DOUBLE);
}
public static SqlTester.ResultChecker isSingle(double delta, String value) {
assert delta == 0d; // if not zero, call a different method
return isSingle(value);
}
public static SqlTester.ResultChecker isSingle(String value) {
return new MatcherResultChecker<>(is(value),
JdbcType.STRING_NULLABLE);
}
public static SqlTester.ResultChecker isSingle(boolean value) {
return new MatcherResultChecker<>(is(value),
JdbcType.BOOLEAN);
}
public static SqlTester.ResultChecker isSingle(int value) {
return new MatcherResultChecker<>(is(value),
JdbcType.INTEGER);
}
public static SqlTester.ResultChecker isDecimal(String value) {
return new MatcherResultChecker<>(is(new BigDecimal(value)),
JdbcType.BIG_DECIMAL);
}
public static SqlTester.ResultChecker isSet(String... values) {
return new RefSetResultChecker(ImmutableSet.copyOf(values));
}
public static SqlTester.ResultChecker isNullValue() {
return new RefSetResultChecker(Collections.singleton(null));
}
/**
* Compares the first column of a result set against a String-valued
* reference set, disregarding order entirely.
*
* @param resultSet Result set
* @param refSet Expected results
* @throws Exception .
*/
static void compareResultSet(ResultSet resultSet,
Set refSet) throws Exception {
Set actualSet = new HashSet<>();
final int columnType = resultSet.getMetaData().getColumnType(1);
final ColumnMetaData.Rep rep = rep(columnType);
while (resultSet.next()) {
final String s = resultSet.getString(1);
final String s0 = s == null ? "0" : s;
final boolean wasNull0 = resultSet.wasNull();
actualSet.add(s);
switch (rep) {
case BOOLEAN:
case PRIMITIVE_BOOLEAN:
assertThat(resultSet.getBoolean(1), equalTo(Boolean.valueOf(s)));
break;
case BYTE:
case PRIMITIVE_BYTE:
case SHORT:
case PRIMITIVE_SHORT:
case INTEGER:
case PRIMITIVE_INT:
case LONG:
case PRIMITIVE_LONG:
long l;
try {
l = Long.parseLong(s0);
} catch (NumberFormatException e) {
// Large integers come out in scientific format, say "5E+06"
l = (long) Double.parseDouble(s0);
}
assertThat(resultSet.getByte(1), equalTo((byte) l));
assertThat(resultSet.getShort(1), equalTo((short) l));
assertThat(resultSet.getInt(1), equalTo((int) l));
assertThat(resultSet.getLong(1), equalTo(l));
break;
case FLOAT:
case PRIMITIVE_FLOAT:
case DOUBLE:
case PRIMITIVE_DOUBLE:
final double d = Double.parseDouble(s0);
assertThat(resultSet.getFloat(1), equalTo((float) d));
assertThat(resultSet.getDouble(1), equalTo(d));
break;
default:
// fall through; no type-specific validation is necessary
}
final boolean wasNull1 = resultSet.wasNull();
final Object object = resultSet.getObject(1);
final boolean wasNull2 = resultSet.wasNull();
assertThat(object == null, equalTo(wasNull0));
assertThat(wasNull1, equalTo(wasNull0));
assertThat(wasNull2, equalTo(wasNull0));
}
resultSet.close();
assertEquals(refSet, actualSet);
}
private static ColumnMetaData.Rep rep(int columnType) {
switch (columnType) {
case Types.BOOLEAN:
return ColumnMetaData.Rep.BOOLEAN;
case Types.TINYINT:
return ColumnMetaData.Rep.BYTE;
case Types.SMALLINT:
return ColumnMetaData.Rep.SHORT;
case Types.INTEGER:
return ColumnMetaData.Rep.INTEGER;
case Types.BIGINT:
return ColumnMetaData.Rep.LONG;
case Types.REAL:
return ColumnMetaData.Rep.FLOAT;
case Types.DOUBLE:
return ColumnMetaData.Rep.DOUBLE;
case Types.TIME:
return ColumnMetaData.Rep.JAVA_SQL_TIME;
case Types.TIMESTAMP:
return ColumnMetaData.Rep.JAVA_SQL_TIMESTAMP;
case Types.DATE:
return ColumnMetaData.Rep.JAVA_SQL_DATE;
default:
return ColumnMetaData.Rep.OBJECT;
}
}
/**
* Compares the first column of a result set against a pattern. The result
* set must return exactly one row.
*
* @param resultSet Result set
* @param pattern Expected pattern
*/
static void compareResultSetWithPattern(ResultSet resultSet,
Pattern pattern) throws Exception {
if (!resultSet.next()) {
fail("Query returned 0 rows, expected 1");
}
String actual = resultSet.getString(1);
if (resultSet.next()) {
fail("Query returned 2 or more rows, expected 1");
}
if (!pattern.matcher(actual).matches()) {
fail("Query returned '"
+ actual
+ "', expected '"
+ pattern.pattern()
+ "'");
}
}
/**
* Compares the first column of a result set against a {@link Matcher}.
* The result set must return exactly one row.
*
* @param resultSet Result set
* @param matcher Matcher
*
* @param Value type
*/
static void compareResultSetWithMatcher(ResultSet resultSet,
JdbcType jdbcType, Matcher matcher) throws Exception {
if (!resultSet.next()) {
fail("Query returned 0 rows, expected 1");
}
T actual = jdbcType.get(1, resultSet);
if (resultSet.next()) {
fail("Query returned 2 or more rows, expected 1");
}
assertThat(actual, matcher);
}
/** Creates a ResultChecker that accesses a column of a given type
* and then uses a Hamcrest matcher to check the value. */
public static SqlTester.ResultChecker createChecker(Matcher matcher,
JdbcType jdbcType) {
return new MatcherResultChecker<>(matcher, jdbcType);
}
/** Creates a ResultChecker from an expected result.
*
* The result may be a {@link SqlTester.ResultChecker},
* a regular expression ({@link Pattern}),
* a Hamcrest {@link Matcher},
* a {@link Collection} of strings (representing the values of one column).
*
*
If none of the above, the value is converted to a string and compared
* with the value of a single column, single row result set that is converted
* to a string.
*/
public static SqlTester.ResultChecker createChecker(Object result) {
requireNonNull(result, "to check for a null result, use isNullValue()");
if (result instanceof Pattern) {
return new PatternResultChecker((Pattern) result);
} else if (result instanceof SqlTester.ResultChecker) {
return (SqlTester.ResultChecker) result;
} else if (result instanceof Matcher) {
//noinspection unchecked,rawtypes
return createChecker((Matcher) result, JdbcType.DOUBLE);
} else if (result instanceof Collection) {
//noinspection unchecked
final Collection collection = (Collection) result;
return new RefSetResultChecker(ImmutableNullableSet.copyOf(collection));
} else {
return isSingle(result.toString());
}
}
/**
* Result checker that checks a result against a regular expression.
*/
static class PatternResultChecker implements SqlTester.ResultChecker {
final Pattern pattern;
PatternResultChecker(Pattern pattern) {
this.pattern = requireNonNull(pattern, "pattern");
}
@Override public void checkResult(ResultSet resultSet) throws Exception {
compareResultSetWithPattern(resultSet, pattern);
}
}
/**
* Result checker that checks a result using a {@link org.hamcrest.Matcher}.
*
* @param Result type
*/
static class MatcherResultChecker implements SqlTester.ResultChecker {
private final Matcher matcher;
private final JdbcType jdbcType;
MatcherResultChecker(Matcher matcher, JdbcType jdbcType) {
this.matcher = requireNonNull(matcher, "matcher");
this.jdbcType = requireNonNull(jdbcType, "jdbcType");
}
@Override public void checkResult(ResultSet resultSet) throws Exception {
compareResultSetWithMatcher(resultSet, jdbcType, matcher);
}
}
/**
* Result checker that checks a result against a list of expected strings.
*/
static class RefSetResultChecker implements SqlTester.ResultChecker {
private final Set expected;
RefSetResultChecker(Set expected) {
this.expected = ImmutableNullableSet.copyOf(expected);
}
@Override public void checkResult(ResultSet resultSet) throws Exception {
compareResultSet(resultSet, expected);
}
}
}