Hussain Towaileb has uploaded this change for review.

View Change

[NO ISSUE][TEST] Convert type computer tests to parameterized

Change-Id: I4426efcc9e825ebb00e04e3783d18bb1cbb63a90
---
D asterixdb/asterix-om/src/test/java/org/apache/asterix/om/typecomputer/TypeComputerTest.java
R asterixdb/asterix-om/src/test/java/org/apache/asterix/test/om/typecomputer/ExceptionTest.java
A asterixdb/asterix-om/src/test/java/org/apache/asterix/test/om/typecomputer/TypeComputerTest.java
R asterixdb/asterix-om/src/test/java/org/apache/asterix/test/om/util/JSONDeserializerForTypesTest.java
4 files changed, 293 insertions(+), 168 deletions(-)

git pull ssh://asterix-gerrit.ics.uci.edu:29418/asterixdb refs/changes/93/3393/1
diff --git a/asterixdb/asterix-om/src/test/java/org/apache/asterix/om/typecomputer/TypeComputerTest.java b/asterixdb/asterix-om/src/test/java/org/apache/asterix/om/typecomputer/TypeComputerTest.java
deleted file mode 100644
index bd77580..0000000
--- a/asterixdb/asterix-om/src/test/java/org/apache/asterix/om/typecomputer/TypeComputerTest.java
+++ /dev/null
@@ -1,166 +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.asterix.om.typecomputer;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import org.apache.asterix.om.typecomputer.base.IResultTypeComputer;
-import org.apache.asterix.om.types.ATypeTag;
-import org.apache.asterix.om.types.AUnionType;
-import org.apache.asterix.om.types.BuiltinType;
-import org.apache.asterix.om.types.IAType;
-import org.apache.commons.lang3.mutable.Mutable;
-import org.apache.commons.lang3.mutable.MutableObject;
-import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression;
-import org.apache.hyracks.algebricks.core.algebra.expressions.AbstractFunctionCallExpression;
-import org.apache.hyracks.algebricks.core.algebra.expressions.IVariableTypeEnvironment;
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.algebricks.core.algebra.metadata.IMetadataProvider;
-import org.junit.Assert;
-import org.junit.Test;
-import org.reflections.Reflections;
-import org.reflections.scanners.SubTypesScanner;
-
-// Tests if all type computers can handle input type ANY properly.
-// TODO: this test should be fixed/updated/modified/enhanced
-public class TypeComputerTest {
-
- @Test
- public void test() throws Exception {
- // Mocks the type environment.
- IVariableTypeEnvironment mockTypeEnv = mock(IVariableTypeEnvironment.class);
-
- // Mocks the metadata provider.
- IMetadataProvider<?, ?> mockMetadataProvider = mock(IMetadataProvider.class);
-
- // Mocks function expression.
- AbstractFunctionCallExpression mockExpr = mock(AbstractFunctionCallExpression.class);
- FunctionIdentifier fid = mock(FunctionIdentifier.class);
- when(mockExpr.getFunctionIdentifier()).thenReturn(fid);
- when(fid.getName()).thenReturn("testFunction");
-
- // A function at most has six argument.
- List<Mutable<ILogicalExpression>> sixArgs = createArgs(6, mockTypeEnv);
-
- // Sets up arguments for the mocked expression.
- when(mockExpr.getArguments()).thenReturn(sixArgs);
-
- // Sets up required/actual types of the mocked expression.
- Object[] opaqueParameters = new Object[2];
- opaqueParameters[0] = BuiltinType.ANY;
- opaqueParameters[1] = BuiltinType.ANY;
- when(mockExpr.getOpaqueParameters()).thenReturn(opaqueParameters);
-
- // functions that check the number of args inside the type computer
- List<Mutable<ILogicalExpression>> replaceArgs = createArgs(4, mockTypeEnv);
- List<Mutable<ILogicalExpression>> sliceArgs = createArgs(3, mockTypeEnv);
- List<Mutable<ILogicalExpression>> rangeArgs = createArgs(3, mockTypeEnv);
- HashMap<String, List<Mutable<ILogicalExpression>>> map = new HashMap<>();
- map.put("INSTANCE_REPLACE", replaceArgs);
- map.put("INSTANCE_SLICE", sliceArgs);
- map.put("ArrayRangeTypeComputer", rangeArgs);
-
- // Several exceptional type computers.
- Set<String> exceptionalTypeComputers = new HashSet<>();
- exceptionalTypeComputers.add("InjectFailureTypeComputer");
- exceptionalTypeComputers.add("RecordAddFieldsTypeComputer");
- exceptionalTypeComputers.add("OpenRecordConstructorResultType");
- exceptionalTypeComputers.add("RecordRemoveFieldsTypeComputer");
- exceptionalTypeComputers.add("ClosedRecordConstructorResultType");
- exceptionalTypeComputers.add("LocalAvgTypeComputer");
- exceptionalTypeComputers.add("BooleanOnlyTypeComputer");
- exceptionalTypeComputers.add("AMissingTypeComputer");
- exceptionalTypeComputers.add("NullableDoubleTypeComputer");
- exceptionalTypeComputers.add("RecordMergeTypeComputer");
- exceptionalTypeComputers.add("BooleanOrMissingTypeComputer");
- exceptionalTypeComputers.add("LocalSingleVarStatisticsTypeComputer");
-
- // Tests all usual type computers.
- Reflections reflections = new Reflections("org.apache.asterix.om.typecomputer", new SubTypesScanner(false));
- Set<Class<? extends IResultTypeComputer>> classes = reflections.getSubTypesOf(IResultTypeComputer.class);
- for (Class<? extends IResultTypeComputer> c : classes) {
- if (exceptionalTypeComputers.contains(c.getSimpleName()) || Modifier.isAbstract(c.getModifiers())) {
- continue;
- }
- System.out.println("Test type computer: " + c.getName());
- Assert.assertTrue(testTypeComputer(c, mockTypeEnv, mockMetadataProvider, mockExpr, map, sixArgs));
- }
- }
-
- private boolean testTypeComputer(Class<? extends IResultTypeComputer> c, IVariableTypeEnvironment mockTypeEnv,
- IMetadataProvider<?, ?> mockMetadataProvider, AbstractFunctionCallExpression mockExpr,
- HashMap<String, List<Mutable<ILogicalExpression>>> map, List<Mutable<ILogicalExpression>> sixArgs)
- throws Exception {
- // Tests the return type. It should be either ANY or NULLABLE/MISSABLE.
- IResultTypeComputer instance;
- IAType resultType;
- Field[] fields = c.getFields();
- List<Mutable<ILogicalExpression>> args;
- for (Field field : fields) {
- if (field.getName().startsWith("INSTANCE")) {
- System.out.println("Test type computer INSTANCE: " + field.getName());
- args = getArgs(field.getName(), c, map);
- if (args != null) {
- when(mockExpr.getArguments()).thenReturn(args);
- } else {
- when(mockExpr.getArguments()).thenReturn(sixArgs);
- }
- instance = (IResultTypeComputer) field.get(null);
- resultType = instance.computeType(mockExpr, mockTypeEnv, mockMetadataProvider);
- ATypeTag typeTag = resultType.getTypeTag();
- if (typeTag != ATypeTag.ANY && !(typeTag == ATypeTag.UNION && ((AUnionType) resultType).isNullableType()
- && ((AUnionType) resultType).isMissableType())) {
- return false;
- }
- }
- }
- return true;
- }
-
- private List<Mutable<ILogicalExpression>> createArgs(int numArgs, IVariableTypeEnvironment mockTypeEnv)
- throws Exception {
- List<Mutable<ILogicalExpression>> argRefs = new ArrayList<>();
- for (int argIndex = 0; argIndex < numArgs; ++argIndex) {
- ILogicalExpression mockArg = mock(ILogicalExpression.class);
- argRefs.add(new MutableObject<>(mockArg));
- when(mockTypeEnv.getType(mockArg)).thenReturn(BuiltinType.ANY);
- }
-
- return argRefs;
- }
-
- private List<Mutable<ILogicalExpression>> getArgs(String instanceName, Class<? extends IResultTypeComputer> c,
- HashMap<String, List<Mutable<ILogicalExpression>>> map) {
- if (instanceName.equals("INSTANCE")) {
- return map.get(c.getSimpleName());
- } else {
- return map.get(instanceName);
- }
- }
-}
diff --git a/asterixdb/asterix-om/src/test/java/org/apache/asterix/om/typecomputer/ExceptionTest.java b/asterixdb/asterix-om/src/test/java/org/apache/asterix/test/om/typecomputer/ExceptionTest.java
similarity index 98%
rename from asterixdb/asterix-om/src/test/java/org/apache/asterix/om/typecomputer/ExceptionTest.java
rename to asterixdb/asterix-om/src/test/java/org/apache/asterix/test/om/typecomputer/ExceptionTest.java
index 29cb9a7..3d8c690 100644
--- a/asterixdb/asterix-om/src/test/java/org/apache/asterix/om/typecomputer/ExceptionTest.java
+++ b/asterixdb/asterix-om/src/test/java/org/apache/asterix/test/om/typecomputer/ExceptionTest.java
@@ -17,7 +17,7 @@
* under the License.
*/

-package org.apache.asterix.om.typecomputer;
+package org.apache.asterix.test.om.typecomputer;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
diff --git a/asterixdb/asterix-om/src/test/java/org/apache/asterix/test/om/typecomputer/TypeComputerTest.java b/asterixdb/asterix-om/src/test/java/org/apache/asterix/test/om/typecomputer/TypeComputerTest.java
new file mode 100644
index 0000000..b65c15e
--- /dev/null
+++ b/asterixdb/asterix-om/src/test/java/org/apache/asterix/test/om/typecomputer/TypeComputerTest.java
@@ -0,0 +1,291 @@
+/*
+ * 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.asterix.test.om.typecomputer;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.asterix.om.typecomputer.base.IResultTypeComputer;
+import org.apache.asterix.om.types.ATypeTag;
+import org.apache.asterix.om.types.AUnionType;
+import org.apache.asterix.om.types.BuiltinType;
+import org.apache.asterix.om.types.IAType;
+import org.apache.commons.lang3.mutable.Mutable;
+import org.apache.commons.lang3.mutable.MutableObject;
+import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression;
+import org.apache.hyracks.algebricks.core.algebra.expressions.AbstractFunctionCallExpression;
+import org.apache.hyracks.algebricks.core.algebra.expressions.IVariableTypeEnvironment;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+import org.apache.hyracks.algebricks.core.algebra.metadata.IMetadataProvider;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+import org.reflections.Reflections;
+import org.reflections.scanners.SubTypesScanner;
+
+/**
+ * This test passes a number of arguments to the type computers, all arguments of type "any", and ensures that
+ * each computer handles the "any" type properly. The expected behavior for each type computer is to return "any",
+ * "nullable" or "missable" type result.
+ *
+ * Things to note:
+ * - The function passes 6 "any" arguments because, as of now, no function that we have has more than 6 arguments.
+ * two other lists are made (3 args and 4 args), those are needed by specific type computers for now, those should be
+ * changed and then the lists can be removed from the test.
+ * - Some functions have a different behavior with "any" value, those will be added to an exception list.
+ * - Some functions check their arguments count, this will make passing 6 arguments fail,
+ * those are added to exception list.
+ */
+
+@RunWith(Parameterized.class)
+public class TypeComputerTest {
+
+ private static final Logger LOGGER = LogManager.getLogger();
+
+ // type computers that have a different behavior when handling "any" type
+ private static Set<String> differentBehaviorFunctions = new HashSet<>();
+
+ // type computers that check the number of arguments
+ private static HashMap<Field, List<Mutable<ILogicalExpression>>> checkArgsCountFunctions = new HashMap<>();
+
+ // Members
+ private static IVariableTypeEnvironment env;
+ private static IMetadataProvider metadataProvider;
+ private static AbstractFunctionCallExpression functionCallExpression;
+
+ // Arguments all of type "any", different combination is provided because it's needed by other functions (for now)
+ private static List<Mutable<ILogicalExpression>> threeArgs;
+ private static List<Mutable<ILogicalExpression>> fourArgs;
+ private static List<Mutable<ILogicalExpression>> sixArgs;
+
+ @Parameter
+ public String testName;
+
+ @Parameter(1)
+ public Class<? extends IResultTypeComputer> clazz;
+
+ @Parameters(name = "TypeComputerTest {index}: {0}")
+ public static Collection<Object[]> tests() throws Exception {
+
+ System.out.println("\n--------------------------------------------------------------------------------------");
+
+ // Prepare the test environment first
+ setup();
+
+ // Prepare tests
+ List<Object[]> tests = new ArrayList<>();
+
+ // Tests all usual type computers.
+ Reflections reflections = new Reflections("org.apache.asterix.om.typecomputer", new SubTypesScanner(false));
+ Set<Class<? extends IResultTypeComputer>> classes = reflections.getSubTypesOf(IResultTypeComputer.class);
+
+ for (Class<? extends IResultTypeComputer> clazz : classes) {
+ if (Modifier.isAbstract(clazz.getModifiers())) {
+ LOGGER.log(Level.INFO, "Excluding " + clazz.getSimpleName() + " (Abstract class)");
+ continue;
+ }
+
+ if (differentBehaviorFunctions.contains(clazz.getSimpleName())) {
+ LOGGER.log(Level.INFO, "Excluding " + clazz.getSimpleName() + " (Special behavior)");
+ continue;
+ }
+
+ tests.add(new Object[] { clazz.getSimpleName(), clazz });
+ }
+
+ System.out.println("--------------------------------------------------------------------------------------\n");
+
+ return tests;
+ }
+
+ /**
+ * Test return types should be either "any", "missable" or "nullable"
+ *
+ * @throws Exception Exception
+ */
+ @Test
+ public void test() throws Exception {
+
+ // Tests the return type. It should be either ANY or NULLABLE/MISSABLE.
+ IResultTypeComputer instance;
+ IAType resultType;
+ List<Mutable<ILogicalExpression>> args;
+ Field[] fields = clazz.getFields();
+
+ // The type computer fields are only the ones that start with "INSTANCE"
+ for (Field field : fields) {
+
+ if (field.getName().startsWith("INSTANCE")) {
+ LOGGER.log(Level.INFO, "Testing " + clazz.getSimpleName() + ": " + field.getName());
+
+ // Need to check if this is a special type computer that counts number of arguments
+ args = checkArgsCountFunctions.get(field);
+
+ // Yes, pass its specified arguments in the map
+ if (args != null) {
+ when(functionCallExpression.getArguments()).thenReturn(args);
+ }
+ // No, pass six arguments
+ else {
+ when(functionCallExpression.getArguments()).thenReturn(sixArgs);
+ }
+
+ instance = (IResultTypeComputer) field.get(null);
+ resultType = instance.computeType(functionCallExpression, env, metadataProvider);
+ ATypeTag typeTag = resultType.getTypeTag();
+
+ // Result should be ANY, Missable or Nullable
+ Assert.assertTrue(typeTag == ATypeTag.ANY
+ || (typeTag == ATypeTag.UNION && ((AUnionType) resultType).isNullableType()
+ || ((AUnionType) resultType).isMissableType()));
+ }
+ }
+ }
+
+ public static void setup() throws Exception {
+
+ // Mocks the type environment.
+ env = mock(IVariableTypeEnvironment.class);
+
+ // Mocks the metadata provider.
+ metadataProvider = mock(IMetadataProvider.class);
+
+ // Prepare the function arguments
+ threeArgs = createArgs(3);
+ fourArgs = createArgs(4);
+ sixArgs = createArgs(6);
+
+ // Mocks function identifier
+ FunctionIdentifier functionIdentifier = mock(FunctionIdentifier.class);
+ when(functionIdentifier.getName()).thenReturn("testFunction");
+
+ // Mocks function expression.
+ functionCallExpression = mock(AbstractFunctionCallExpression.class);
+ when(functionCallExpression.getFunctionIdentifier()).thenReturn(functionIdentifier);
+ when(functionCallExpression.getArguments()).thenReturn(sixArgs);
+
+ // Sets up required/actual types of the mocked expression.
+ Object[] opaqueParameters = new Object[2];
+ opaqueParameters[0] = BuiltinType.ANY;
+ opaqueParameters[1] = BuiltinType.ANY;
+ when(functionCallExpression.getOpaqueParameters()).thenReturn(opaqueParameters);
+
+ // Add to exception list for computers checking their arguments count
+ addComputersCheckingArgsCount();
+
+ // Add to exception list for computers having a different behavior for "any" type
+ addComputersBehavingDifferently();
+ }
+
+ // TODO This is not a good practice, if the class name is changed, the test will fail and this needs to be updated
+ // Consider using annotation to avoid modifying the test and have a generic behavior
+ /**
+ * Adds the type computers that have a different behavior for "any" type.
+ */
+ private static void addComputersBehavingDifferently() {
+ differentBehaviorFunctions.add("InjectFailureTypeComputer");
+ differentBehaviorFunctions.add("RecordAddFieldsTypeComputer");
+ differentBehaviorFunctions.add("OpenRecordConstructorResultType");
+ differentBehaviorFunctions.add("RecordRemoveFieldsTypeComputer");
+ differentBehaviorFunctions.add("ClosedRecordConstructorResultType");
+ differentBehaviorFunctions.add("LocalAvgTypeComputer");
+ differentBehaviorFunctions.add("BooleanOnlyTypeComputer");
+ differentBehaviorFunctions.add("AMissingTypeComputer");
+ differentBehaviorFunctions.add("NullableDoubleTypeComputer");
+ differentBehaviorFunctions.add("RecordMergeTypeComputer");
+ differentBehaviorFunctions.add("BooleanOrMissingTypeComputer");
+ differentBehaviorFunctions.add("LocalSingleVarStatisticsTypeComputer");
+ }
+
+ // TODO Type computers should not be checking the arguments count, if the argument count is variable but knowable,
+ // then multiple INSTANCES need to be created, each indicating the number of incoming arguments, and based on that
+ // applying the appropriate behavior in the compute method body
+ /**
+ * Adds the type computers that check the args count in their method body. If 6 arguments are passed to those
+ * computers, they're gonna throw an exception, so we manually specify how many arguments they should get.
+ *
+ * @throws Exception Exception
+ */
+ private static void addComputersCheckingArgsCount() throws Exception {
+
+ // AListTypeComputer
+ Class<?> clazz = Class.forName("org.apache.asterix.om.typecomputer.impl.AListTypeComputer");
+ Field[] fields = clazz.getFields();
+
+ for (Field field : fields) {
+ if (field.getName().equalsIgnoreCase("INSTANCE_SLICE")) {
+ LOGGER.log(Level.INFO, field.getName() + " will use only 3 arguments");
+ checkArgsCountFunctions.put(field, threeArgs);
+ }
+
+ if (field.getName().equalsIgnoreCase("INSTANCE_REPLACE")) {
+ LOGGER.log(Level.INFO, field.getName() + " will use only 4 arguments");
+ checkArgsCountFunctions.put(field, fourArgs);
+ }
+ }
+
+ // ArrayRangeTypeComputer
+ clazz = Class.forName("org.apache.asterix.om.typecomputer.impl.ArrayRangeTypeComputer");
+ fields = clazz.getFields();
+
+ for (Field field : fields) {
+ if (field.getName().equalsIgnoreCase("INSTANCE")) {
+ LOGGER.log(Level.INFO, field.getName() + " will use only 3 arguments");
+ checkArgsCountFunctions.put(field, threeArgs);
+ }
+ }
+ }
+
+ /**
+ * Creates expressions matching the number passed as an argument. Variable type environment is set for all those
+ * expressions to be of type "any".
+ *
+ * @param numArgs number of arguments to create
+ * @return a list holding the created expressions
+ * @throws Exception Exception
+ */
+ private static List<Mutable<ILogicalExpression>> createArgs(int numArgs) throws Exception {
+
+ List<Mutable<ILogicalExpression>> arguments = new ArrayList<>();
+
+ for (int i = 0; i < numArgs; ++i) {
+ ILogicalExpression argument = mock(ILogicalExpression.class);
+ arguments.add(new MutableObject<>(argument));
+ when(env.getType(argument)).thenReturn(BuiltinType.ANY);
+ }
+
+ return arguments;
+ }
+}
diff --git a/asterixdb/asterix-om/src/test/java/org/apache/asterix/om/util/JSONDeserializerForTypesTest.java b/asterixdb/asterix-om/src/test/java/org/apache/asterix/test/om/util/JSONDeserializerForTypesTest.java
similarity index 98%
rename from asterixdb/asterix-om/src/test/java/org/apache/asterix/om/util/JSONDeserializerForTypesTest.java
rename to asterixdb/asterix-om/src/test/java/org/apache/asterix/test/om/util/JSONDeserializerForTypesTest.java
index 32ec75a..35e4d35 100644
--- a/asterixdb/asterix-om/src/test/java/org/apache/asterix/om/util/JSONDeserializerForTypesTest.java
+++ b/asterixdb/asterix-om/src/test/java/org/apache/asterix/test/om/util/JSONDeserializerForTypesTest.java
@@ -17,7 +17,7 @@
* under the License.
*/

-package org.apache.asterix.om.util;
+package org.apache.asterix.test.om.util;

import java.util.ArrayList;
import java.util.List;

To view, visit change 3393. To unsubscribe, visit settings.

Gerrit-Project: asterixdb
Gerrit-Branch: master
Gerrit-MessageType: newchange
Gerrit-Change-Id: I4426efcc9e825ebb00e04e3783d18bb1cbb63a90
Gerrit-Change-Number: 3393
Gerrit-PatchSet: 1
Gerrit-Owner: Hussain Towaileb <hussainht@gmail.com>
Gerrit-Reviewer: Hussain Towaileb <HussainHT@Gmail.com>