Hussain Towaileb has uploaded this change for review. ( https://asterix-gerrit.ics.uci.edu/3393
Change subject: [NO ISSUE][TEST] Convert type computer tests to parameterized ...................................................................... [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 https://asterix-gerrit.ics.uci.edu/3393 To unsubscribe, visit https://asterix-gerrit.ics.uci.edu/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 <[email protected]> Gerrit-Reviewer: Hussain Towaileb <[email protected]>
