This is an automated email from the ASF dual-hosted git repository. zabetak pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/calcite-avatica.git
commit 89e0deb510311b85b8c8bacde6d2ff70c309930e Author: Stamatis Zampetakis <[email protected]> AuthorDate: Fri Feb 26 15:32:42 2021 +0100 Replace AssertTestUtils with custom Hamcrest matcher for accessors content Assertions regarding: * verification of the number of created accessors; * verification of the metadata of the accessor's result set; are removed since they are part of the test setup. --- .../apache/calcite/avatica/AvaticaMatchers.java | 57 ++++++++++++++ .../avatica/IsArrayAccessorResultSetEqual.java | 91 ++++++++++++++++++++++ .../calcite/avatica/util/ArrayAccessorTest.java | 67 ++++++++++------ .../apache/calcite/avatica/util/ArrayImplTest.java | 58 +++++++++----- .../calcite/avatica/util/AssertTestUtils.java | 76 ------------------ 5 files changed, 227 insertions(+), 122 deletions(-) diff --git a/core/src/test/java/org/apache/calcite/avatica/AvaticaMatchers.java b/core/src/test/java/org/apache/calcite/avatica/AvaticaMatchers.java new file mode 100644 index 0000000..d259e3a --- /dev/null +++ b/core/src/test/java/org/apache/calcite/avatica/AvaticaMatchers.java @@ -0,0 +1,57 @@ +/* + * 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.avatica; + +import org.apache.calcite.avatica.util.Cursor; + +import org.hamcrest.Matcher; + +import java.util.List; +import java.util.function.BiPredicate; + +/** + * Custom Hamcrest matchers for the Avatica project. + */ +public class AvaticaMatchers { + + private static final double DELTA = 1e-15; + + private AvaticaMatchers() { + // private constructor + } + + /** + * Creates a matcher that matches if the result set of the examined accessor + * ({@link org.apache.calcite.avatica.util.AbstractCursor.ArrayAccessor}) is logically equal to + * the specified values. + * + * Equality between individual elements is usually determined by {@link Object#equals(Object)} but + * it can be more permissive when comparing approximate numbers (e.g., Float, Double) to account + * for some margin of error. + */ + public static Matcher<Cursor.Accessor> isArrayAccessorResult(List<Object> value, Class<?> type) { + BiPredicate<Object, Object> comparisonPredicate = Object::equals; + if (Float.class.equals(type)) { + comparisonPredicate = (f1, f2) -> Math.abs((float) f1 - (float) f2) <= DELTA; + } else if (Double.class.equals(type)) { + comparisonPredicate = (d1, d2) -> Math.abs((double) d1 - (double) d2) <= DELTA; + } + return new IsArrayAccessorResultSetEqual(value, comparisonPredicate); + } +} + +// End AvaticaMatchers.java diff --git a/core/src/test/java/org/apache/calcite/avatica/IsArrayAccessorResultSetEqual.java b/core/src/test/java/org/apache/calcite/avatica/IsArrayAccessorResultSetEqual.java new file mode 100644 index 0000000..723b5f2 --- /dev/null +++ b/core/src/test/java/org/apache/calcite/avatica/IsArrayAccessorResultSetEqual.java @@ -0,0 +1,91 @@ +/* + * 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.avatica; + +import org.apache.calcite.avatica.util.Cursor; + +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiPredicate; + +/** + * Is the result set of an {@link org.apache.calcite.avatica.util.AbstractCursor.ArrayAccessor} + * equal to the provided + * elements. + * + * Two elements are considered equal if they satisfy the provided predicate. + */ +class IsArrayAccessorResultSetEqual extends BaseMatcher<Cursor.Accessor> { + final List<Object> expectedElements; + final BiPredicate<Object, Object> p; + + IsArrayAccessorResultSetEqual(List<Object> expected, BiPredicate<Object, Object> p) { + this.expectedElements = expected; + this.p = p; + } + + @Override public boolean matches(Object item) { + Cursor.Accessor accessor = (Cursor.Accessor) item; + try { + ResultSet rs = accessor.getArray().getResultSet(); + // Result set is not closed on purpose (attempt to close it throws exception) + int size = 0; + while (rs.next()) { + // Array's result set has one row per array element. + // Each row has two columns: + // * column 1 is the array offset (1-based); + // * column 2 is the array value. + int index = rs.getInt(1); + Object actual = rs.getObject(2); + Object expected = expectedElements.get(index - 1); + if (!p.test(actual, expected)) { + return false; + } + size++; + } + return expectedElements.size() == size; + } catch (SQLException e) { + throw new RuntimeException("Failed to extract value from accessor.", e); + } + } + + @Override public void describeTo(Description description) { + description.appendText("ArrayAccessor result set should match " + expectedElements); + } + + @Override public void describeMismatch(Object item, Description description) { + Cursor.Accessor accessor = (Cursor.Accessor) item; + try { + ResultSet rs = accessor.getArray().getResultSet(); + List<Object> rsvalues = new ArrayList<>(); + while (rs.next()) { + rsvalues.add(rs.getObject(2)); + } + description.appendText("was ").appendValue(rsvalues); + } catch (SQLException e) { + throw new RuntimeException("Failed to create matcher description.", e); + } + } + +} + +// End IsArrayAccessorResultSetEqual.java diff --git a/core/src/test/java/org/apache/calcite/avatica/util/ArrayAccessorTest.java b/core/src/test/java/org/apache/calcite/avatica/util/ArrayAccessorTest.java index a9efc93..2b77ac0 100644 --- a/core/src/test/java/org/apache/calcite/avatica/util/ArrayAccessorTest.java +++ b/core/src/test/java/org/apache/calcite/avatica/util/ArrayAccessorTest.java @@ -18,8 +18,8 @@ package org.apache.calcite.avatica.util; import org.apache.calcite.avatica.ColumnMetaData; import org.apache.calcite.avatica.ColumnMetaData.Rep; - import org.apache.calcite.avatica.MetaImpl; + import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -31,7 +31,9 @@ import java.util.List; import java.util.function.Function; import java.util.stream.Collectors; -import static org.junit.Assert.assertEquals; +import static org.apache.calcite.avatica.AvaticaMatchers.isArrayAccessorResult; + +import static org.junit.Assert.assertThat; /** * Test class for verifying functionality in array accessor from abstract cursor. @@ -39,7 +41,6 @@ import static org.junit.Assert.assertEquals; @RunWith(Parameterized.class) public class ArrayAccessorTest { - private static final double DELTA = 1e-15; private static final ArrayImpl.Factory ARRAY_FACTORY = new ArrayFactoryImpl(Unsafe.localCalendar().getTimeZone()); @@ -62,10 +63,8 @@ public class ArrayAccessorTest { } @Test public void listIteratorFromIntegerArray() throws Exception { - AssertTestUtils.Validator validator = - (Object o1, Object o2) -> assertEquals((int) o1, (int) o2); - - ColumnMetaData.ScalarType intType = ColumnMetaData.scalar(Types.INTEGER, "INTEGER", Rep.INTEGER); + ColumnMetaData.ScalarType intType = + ColumnMetaData.scalar(Types.INTEGER, "INTEGER", Rep.INTEGER); ColumnMetaData arrayMetadata = createArrayMetaData(intType); @@ -73,15 +72,17 @@ public class ArrayAccessorTest { Collections.singletonList(3), Arrays.asList(4, 5, 6)); try (Cursor cursor = cursorBuilder.apply(rowsValues)) { - AssertTestUtils.assertRowsValuesMatchCursorContentViaArrayAccessor( - rowsValues, intType, cursor, arrayMetadata, ARRAY_FACTORY, validator); + Cursor.Accessor accessor = createArrayAccessor(cursor, arrayMetadata); + int rowid = 0; + while (cursor.next()) { + List<Object> expectedArray = rowsValues.get(rowid); + assertThat(accessor, isArrayAccessorResult(expectedArray, Integer.class)); + rowid++; + } } } @Test public void resultSetFromRealArray() throws Exception { - AssertTestUtils.Validator validator = - (Object o1, Object o2) -> assertEquals((float) o1, (float) o2, DELTA); - ColumnMetaData.ScalarType realType = ColumnMetaData.scalar(Types.REAL, "REAL", Rep.FLOAT); ColumnMetaData arrayMetadata = createArrayMetaData(realType); @@ -92,16 +93,19 @@ public class ArrayAccessorTest { ); try (Cursor cursor = cursorBuilder.apply(rowsValues)) { - AssertTestUtils.assertRowsValuesMatchCursorContentViaArrayAccessor( - rowsValues, realType, cursor, arrayMetadata, ARRAY_FACTORY, validator); + Cursor.Accessor accessor = createArrayAccessor(cursor, arrayMetadata); + int rowid = 0; + while (cursor.next()) { + List<Object> expectedArray = rowsValues.get(rowid); + assertThat(accessor, isArrayAccessorResult(expectedArray, Float.class)); + rowid++; + } } } @Test public void resultSetFromDoubleArray() throws Exception { - AssertTestUtils.Validator validator = - (Object o1, Object o2) -> assertEquals((double) o1, (double) o2, DELTA); - - ColumnMetaData.ScalarType doubleType = ColumnMetaData.scalar(Types.DOUBLE, "DOUBLE", Rep.DOUBLE); + ColumnMetaData.ScalarType doubleType = + ColumnMetaData.scalar(Types.DOUBLE, "DOUBLE", Rep.DOUBLE); ColumnMetaData arrayMetadata = createArrayMetaData(doubleType); @@ -111,15 +115,17 @@ public class ArrayAccessorTest { ); try (Cursor cursor = cursorBuilder.apply(rowsValues)) { - AssertTestUtils.assertRowsValuesMatchCursorContentViaArrayAccessor( - rowsValues, doubleType, cursor, arrayMetadata, ARRAY_FACTORY, validator); + Cursor.Accessor accessor = createArrayAccessor(cursor, arrayMetadata); + int rowid = 0; + while (cursor.next()) { + List<Object> expectedArray = rowsValues.get(rowid); + assertThat(accessor, isArrayAccessorResult(expectedArray, Double.class)); + rowid++; + } } } @Test public void resultSetFromFloatArray() throws Exception { - AssertTestUtils.Validator validator = - (Object o1, Object o2) -> assertEquals((double) o1, (double) o2, DELTA); - ColumnMetaData.ScalarType floatType = ColumnMetaData.scalar(Types.FLOAT, "FLOAT", Rep.DOUBLE); ColumnMetaData arrayMetadata = createArrayMetaData(floatType); @@ -130,8 +136,13 @@ public class ArrayAccessorTest { ); try (Cursor cursor = cursorBuilder.apply(rowsValues)) { - AssertTestUtils.assertRowsValuesMatchCursorContentViaArrayAccessor( - rowsValues, floatType, cursor, arrayMetadata, ARRAY_FACTORY, validator); + Cursor.Accessor accessor = createArrayAccessor(cursor, arrayMetadata); + int rowid = 0; + while (cursor.next()) { + List<Object> expectedArray = rowsValues.get(rowid); + assertThat(accessor, isArrayAccessorResult(expectedArray, Double.class)); + rowid++; + } } } @@ -140,6 +151,12 @@ public class ArrayAccessorTest { ColumnMetaData.array(componentType, componentType.name, componentType.rep); return MetaImpl.columnMetaData("MY_ARRAY", 1, arrayType, false); } + + private static Cursor.Accessor createArrayAccessor(Cursor c, ColumnMetaData meta) { + List<Cursor.Accessor> accessors = + c.createAccessors(Collections.singletonList(meta), Unsafe.localCalendar(), ARRAY_FACTORY); + return accessors.get(0); + } } // End ArrayAccessorTest.java diff --git a/core/src/test/java/org/apache/calcite/avatica/util/ArrayImplTest.java b/core/src/test/java/org/apache/calcite/avatica/util/ArrayImplTest.java index e2e4d9e..7e44e36 100644 --- a/core/src/test/java/org/apache/calcite/avatica/util/ArrayImplTest.java +++ b/core/src/test/java/org/apache/calcite/avatica/util/ArrayImplTest.java @@ -24,6 +24,7 @@ import org.apache.calcite.avatica.ColumnMetaData.StructType; import org.apache.calcite.avatica.MetaImpl; import org.apache.calcite.avatica.util.Cursor.Accessor; +import org.junit.Assert; import org.junit.Test; import java.sql.Array; @@ -34,6 +35,8 @@ import java.util.Collections; import java.util.List; import java.util.Objects; +import static org.apache.calcite.avatica.AvaticaMatchers.isArrayAccessorResult; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -42,14 +45,10 @@ import static org.junit.Assert.assertTrue; */ public class ArrayImplTest { - private static final double DELTA = 1e-15; private static final ArrayImpl.Factory ARRAY_FACTORY = new ArrayFactoryImpl(Unsafe.localCalendar().getTimeZone()); @Test public void resultSetFromIntegerArray() throws Exception { - AssertTestUtils.Validator validator = - (Object o1, Object o2) -> assertEquals((int) o1, (int) o2); - ScalarType intType = ColumnMetaData.scalar(Types.INTEGER, "INTEGER", Rep.INTEGER); ColumnMetaData arrayMetadata = createArrayMetaData(intType); @@ -58,15 +57,17 @@ public class ArrayImplTest { try (Cursor cursor = CursorTestUtils.createArrayImplBasedCursor(rowsValues, intType, ARRAY_FACTORY)) { - AssertTestUtils.assertRowsValuesMatchCursorContentViaArrayAccessor( - rowsValues, intType, cursor, arrayMetadata, ARRAY_FACTORY, validator); + Cursor.Accessor accessor = createArrayAccessor(cursor, arrayMetadata); + int rowid = 0; + while (cursor.next()) { + List<Object> expectedArray = rowsValues.get(rowid); + Assert.assertThat(accessor, isArrayAccessorResult(expectedArray, Integer.class)); + rowid++; + } } } @Test public void resultSetFromRealArray() throws Exception { - AssertTestUtils.Validator validator = - (Object o1, Object o2) -> assertEquals((float) o1, (float) o2, DELTA); - ScalarType realType = ColumnMetaData.scalar(Types.REAL, "REAL", Rep.FLOAT); ColumnMetaData arrayMetadata = createArrayMetaData(realType); @@ -77,15 +78,17 @@ public class ArrayImplTest { try (Cursor cursor = CursorTestUtils.createArrayImplBasedCursor(rowsValues, realType, ARRAY_FACTORY)) { - AssertTestUtils.assertRowsValuesMatchCursorContentViaArrayAccessor( - rowsValues, realType, cursor, arrayMetadata, ARRAY_FACTORY, validator); + Cursor.Accessor accessor = createArrayAccessor(cursor, arrayMetadata); + int rowid = 0; + while (cursor.next()) { + List<Object> expectedArray = rowsValues.get(rowid); + Assert.assertThat(accessor, isArrayAccessorResult(expectedArray, Float.class)); + rowid++; + } } } @Test public void resultSetFromDoubleArray() throws Exception { - AssertTestUtils.Validator validator = - (Object o1, Object o2) -> assertEquals((double) o1, (double) o2, DELTA); - ScalarType doubleType = ColumnMetaData.scalar(Types.DOUBLE, "DOUBLE", Rep.PRIMITIVE_DOUBLE); ColumnMetaData arrayMetadata = createArrayMetaData(doubleType); @@ -96,15 +99,17 @@ public class ArrayImplTest { try (Cursor cursor = CursorTestUtils.createArrayImplBasedCursor(rowsValues, doubleType, ARRAY_FACTORY)) { - AssertTestUtils.assertRowsValuesMatchCursorContentViaArrayAccessor( - rowsValues, doubleType, cursor, arrayMetadata, ARRAY_FACTORY, validator); + Cursor.Accessor accessor = createArrayAccessor(cursor, arrayMetadata); + int rowid = 0; + while (cursor.next()) { + List<Object> expectedArray = rowsValues.get(rowid); + Assert.assertThat(accessor, isArrayAccessorResult(expectedArray, Double.class)); + rowid++; + } } } @Test public void resultSetFromFloatArray() throws Exception { - AssertTestUtils.Validator validator = - (Object o1, Object o2) -> assertEquals((double) o1, (double) o2, DELTA); - ScalarType floatType = ColumnMetaData.scalar(Types.FLOAT, "FLOAT", Rep.PRIMITIVE_DOUBLE); ColumnMetaData arrayMetadata = createArrayMetaData(floatType); @@ -115,8 +120,13 @@ public class ArrayImplTest { try (Cursor cursor = CursorTestUtils.createArrayImplBasedCursor(rowsValues, floatType, ARRAY_FACTORY)) { - AssertTestUtils.assertRowsValuesMatchCursorContentViaArrayAccessor( - rowsValues, floatType, cursor, arrayMetadata, ARRAY_FACTORY, validator); + Cursor.Accessor accessor = createArrayAccessor(cursor, arrayMetadata); + int rowid = 0; + while (cursor.next()) { + List<Object> expectedArray = rowsValues.get(rowid); + Assert.assertThat(accessor, isArrayAccessorResult(expectedArray, Double.class)); + rowid++; + } } } @@ -417,6 +427,12 @@ public class ArrayImplTest { assertEquals(6, data[2]); } + private static Cursor.Accessor createArrayAccessor(Cursor c, ColumnMetaData meta) { + List<Cursor.Accessor> accessors = + c.createAccessors(Collections.singletonList(meta), Unsafe.localCalendar(), ARRAY_FACTORY); + return accessors.get(0); + } + private static ColumnMetaData createArrayMetaData(ColumnMetaData.ScalarType componentType) { ColumnMetaData.ArrayType arrayType = ColumnMetaData.array(componentType, componentType.name, componentType.rep); diff --git a/core/src/test/java/org/apache/calcite/avatica/util/AssertTestUtils.java b/core/src/test/java/org/apache/calcite/avatica/util/AssertTestUtils.java deleted file mode 100644 index ac2ba60..0000000 --- a/core/src/test/java/org/apache/calcite/avatica/util/AssertTestUtils.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * 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.avatica.util; - -import org.apache.calcite.avatica.ColumnMetaData; - -import java.sql.Array; -import java.sql.ResultSet; -import java.util.Collections; -import java.util.List; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -/** - * Utilities for assertions in tests of the util package. - */ -public class AssertTestUtils { - - private AssertTestUtils() { - // private constructor - } - - /** - * A simple interface to validate expected and actual values. - */ - interface Validator { - void validate(Object expected, Object actual); - } - - static void assertRowsValuesMatchCursorContentViaArrayAccessor( - List<List<Object>> rowsValues, ColumnMetaData.ScalarType arrayContentMetadata, - Cursor cursorOverArray, ColumnMetaData arrayMetaData, ArrayImpl.Factory factory, - Validator validator) throws Exception { - List<Cursor.Accessor> accessors = cursorOverArray.createAccessors( - Collections.singletonList(arrayMetaData), Unsafe.localCalendar(), factory); - assertEquals(1, accessors.size()); - Cursor.Accessor accessor = accessors.get(0); - - for (List<Object> rowValue : rowsValues) { - assertTrue(cursorOverArray.next()); - Array actualArray = accessor.getArray(); - // An Array's result set has one row per array element. - // Each row has two columns. Column 1 is the array offset (1-based), Column 2 is the value. - ResultSet actualArrayResultSet = actualArray.getResultSet(); - assertEquals(2, actualArrayResultSet.getMetaData().getColumnCount()); - assertEquals(arrayContentMetadata.id, actualArrayResultSet.getMetaData().getColumnType(2)); - assertTrue(actualArrayResultSet.next()); - - for (int j = 0; j < rowValue.size(); ++j) { - assertEquals(j + 1, actualArrayResultSet.getInt(1)); - // ResultSet.getObject() uses the column type internally, we can rely on that - validator.validate(rowValue.get(j), actualArrayResultSet.getObject(2)); - assertEquals(j < rowValue.size() - 1, actualArrayResultSet.next()); - } - } - assertFalse(cursorOverArray.next()); - } -} - -// End AssertTestUtils.java
