Hussain Towaileb has submitted this change and it was merged. ( https://asterix-gerrit.ics.uci.edu/3393 )
Change subject: [ASTERIXDB-2569][TEST] Refactor type computer tests ...................................................................... [ASTERIXDB-2569][TEST] Refactor type computer tests - user model changes: no - storage format changes: no - interface changes: no Details: - Converted type computer tests to be parameterized. - Moved the tests to appropriate packages to use the log4j logger. - Type computer instances check is based on the field type instead of field name. Change-Id: I4426efcc9e825ebb00e04e3783d18bb1cbb63a90 Reviewed-on: https://asterix-gerrit.ics.uci.edu/3393 Contrib: Jenkins <[email protected]> Sonar-Qube: Jenkins <[email protected]> Tested-by: Jenkins <[email protected]> Integration-Tests: Jenkins <[email protected]> Reviewed-by: Dmitry Lychagin <[email protected]> --- M asterixdb/asterix-om/pom.xml D asterixdb/asterix-om/src/test/java/org/apache/asterix/om/typecomputer/ExceptionTest.java D asterixdb/asterix-om/src/test/java/org/apache/asterix/om/typecomputer/TypeComputerTest.java A 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 6 files changed, 464 insertions(+), 301 deletions(-) Approvals: Jenkins: Verified; No violations found; ; Verified Anon. E. Moose (1000171): Dmitry Lychagin: Looks good to me, approved diff --git a/asterixdb/asterix-om/pom.xml b/asterixdb/asterix-om/pom.xml index b2beb41..f536be1 100644 --- a/asterixdb/asterix-om/pom.xml +++ b/asterixdb/asterix-om/pom.xml @@ -129,5 +129,10 @@ <artifactId>jaxb-api</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-api</artifactId> + <scope>test</scope> + </dependency> </dependencies> </project> 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/om/typecomputer/ExceptionTest.java deleted file mode 100644 index 29cb9a7..0000000 --- a/asterixdb/asterix-om/src/test/java/org/apache/asterix/om/typecomputer/ExceptionTest.java +++ /dev/null @@ -1,133 +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.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.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.common.exceptions.AlgebricksException; -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; - -public class ExceptionTest { - - @Test - public void test() throws Exception { - // 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); - int numTypeComputers = 0; - for (Class<? extends IResultTypeComputer> c : classes) { - if (Modifier.isAbstract(c.getModifiers())) { - continue; - } - testTypeComputer(c); - ++numTypeComputers; - } - // Currently, there are 83 type computers. - Assert.assertTrue(numTypeComputers >= 83); - } - - private void testTypeComputer(Class<? extends IResultTypeComputer> c) 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"); - - int numCombination = (int) Math.pow(ATypeTag.values().length, 2); - // Sets two arguments for the mocked function expression. - for (int index = 0; index < numCombination; ++index) { - try { - List<Mutable<ILogicalExpression>> argRefs = new ArrayList<>(); - for (int argIndex = 0; argIndex < 2; ++argIndex) { - int base = (int) Math.pow(ATypeTag.values().length, argIndex); - ILogicalExpression mockArg = mock(ILogicalExpression.class); - argRefs.add(new MutableObject<>(mockArg)); - IAType mockType = mock(IAType.class); - when(mockTypeEnv.getType(mockArg)).thenReturn(mockType); - int serializedTypeTag = (index / base) % ATypeTag.values().length + 1; - ATypeTag typeTag = ATypeTag.VALUE_TYPE_MAPPING[serializedTypeTag]; - if (typeTag == null) { - // For some reason, type tag 39 does not exist. - typeTag = ATypeTag.ANY; - } - when(mockType.getTypeTag()).thenReturn(typeTag); - } - - // Sets up arguments for the mocked expression. - when(mockExpr.getArguments()).thenReturn(argRefs); - - // 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); - - // Invokes a type computer. - IResultTypeComputer instance; - Field[] fields = c.getFields(); - for (Field field : fields) { - if (field.getName().startsWith("INSTANCE")) { - instance = (IResultTypeComputer) field.get(null); - instance.computeType(mockExpr, mockTypeEnv, mockMetadataProvider); - } - } - } catch (AlgebricksException ae) { - String msg = ae.getMessage(); - if (msg.startsWith("ASX")) { - // Verifies the error code. - int errorCode = Integer.parseInt(msg.substring(3, 7)); - Assert.assertTrue(errorCode >= 1000 && errorCode < 2000); - continue; - } else { - // Any root-level compilation exceptions thrown from type computers should have an error code. - Assert.assertTrue(!(ae instanceof AlgebricksException) || (ae.getCause() != null)); - } - } catch (ClassCastException e) { - continue; - } - } - } -} 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 5f5af65..0000000 --- a/asterixdb/asterix-om/src/test/java/org/apache/asterix/om/typecomputer/TypeComputerTest.java +++ /dev/null @@ -1,167 +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"); - exceptionalTypeComputers.add("ScalarArrayAggTypeComputer"); - - // 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/test/om/typecomputer/ExceptionTest.java b/asterixdb/asterix-om/src/test/java/org/apache/asterix/test/om/typecomputer/ExceptionTest.java new file mode 100644 index 0000000..d0ffdb9 --- /dev/null +++ b/asterixdb/asterix-om/src/test/java/org/apache/asterix/test/om/typecomputer/ExceptionTest.java @@ -0,0 +1,183 @@ +/* + * 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.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.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.common.exceptions.AlgebricksException; +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 all possible combinations of types to each type computer, to ensure each type computer handles + * them properly, and in case of an exception, the exception falls within the defined exception codes. + * + * Care needs to be taken when deciding the number of arguments, below is how the tests are calculated: + * The number of combinations is = n ^ m + * n = number of arguments + * m = number of types available + * + * Example: + * 2 arguments, 40 types = 1600 combinations per type computer + * 3 arguments, 40 types = 64000 combinations per type computer + */ + +@RunWith(Parameterized.class) +public class ExceptionTest { + + private static final Logger LOGGER = LogManager.getLogger(); + + // Number of arguments to be passed. Number of combinations depends on number of arguments + private static int numberOfArguments = 2; + private static int numberOfCombinations = (int) Math.pow(ATypeTag.values().length, numberOfArguments); + + // Test parameters + @Parameter + public String testName; + + @Parameter(1) + public Class<? extends IResultTypeComputer> clazz; + + @Parameters(name = "TypeComputerTest {index}: {0}") + public static Collection<Object[]> tests() { + + // 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; + } + + tests.add(new Object[] { clazz.getSimpleName(), clazz }); + } + + return tests; + } + + @Test + public void test() throws Exception { + + // Arguments list + Object[] opaqueParameters = new Object[] { BuiltinType.ANY, BuiltinType.ANY }; + List<Mutable<ILogicalExpression>> argumentsList = new ArrayList<>(numberOfArguments); + + // Sets arguments for the mocked function expression. + for (int index = 0; index < numberOfCombinations; ++index) { + + argumentsList.clear(); + + // Type environment and metadata provider + IVariableTypeEnvironment typeEnv = mock(IVariableTypeEnvironment.class); + IMetadataProvider metadataProvider = mock(IMetadataProvider.class); + + // Function identifier + FunctionIdentifier functionIdentifier = mock(FunctionIdentifier.class); + when(functionIdentifier.getName()).thenReturn("testFunction"); + + // Function call expression + AbstractFunctionCallExpression functionCallExpression = mock(AbstractFunctionCallExpression.class); + when(functionCallExpression.getFunctionIdentifier()).thenReturn(functionIdentifier); + + try { + for (int argIndex = 0; argIndex < numberOfArguments; ++argIndex) { + int base = (int) Math.pow(ATypeTag.values().length, argIndex); + int serializedTypeTag = (index / base) % ATypeTag.values().length + 1; + + ATypeTag typeTag = ATypeTag.VALUE_TYPE_MAPPING[serializedTypeTag]; + if (typeTag == null) { + // If any tags have been removed or are missing, replace them with any + typeTag = ATypeTag.ANY; + } + + // Set the types + IAType iaType = mock(IAType.class); + when(iaType.getTypeTag()).thenReturn(typeTag); + + ILogicalExpression argument = mock(ILogicalExpression.class); + when(typeEnv.getType(argument)).thenReturn(iaType); + + // Add argument to the arguments list + argumentsList.add(new MutableObject<>(argument)); + } + + // Sets up arguments for the mocked expression. + when(functionCallExpression.getArguments()).thenReturn(argumentsList); + when(functionCallExpression.getOpaqueParameters()).thenReturn(opaqueParameters); + + // Invokes a type computer. + IResultTypeComputer instance; + Field[] fields = clazz.getFields(); + + for (Field field : fields) { + if (field.getType().equals(clazz)) { + instance = (IResultTypeComputer) field.get(null); + instance.computeType(functionCallExpression, typeEnv, metadataProvider); + } + } + } catch (AlgebricksException ae) { + String msg = ae.getMessage(); + if (msg.startsWith("ASX")) { + // Verifies the error code. + int errorCode = Integer.parseInt(msg.substring(3, 7)); + Assert.assertTrue(errorCode >= 1000 && errorCode < 2000); + } else { + // Any root-level compilation exceptions thrown from type computers should have an error code. + Assert.assertTrue((ae.getCause() != null)); + } + } catch (ClassCastException e) { + // Do nothing + } + } + } +} 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..9f6724e --- /dev/null +++ b/asterixdb/asterix-om/src/test/java/org/apache/asterix/test/om/typecomputer/TypeComputerTest.java @@ -0,0 +1,275 @@ +/* + * 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<>(); + + // TODO(Hussain) Remove this after the type computers have been updated + // type computers that check the number of arguments + private static HashMap<Field, List<Mutable<ILogicalExpression>>> checkArgsCountFunctions = new HashMap<>(); + + // Test parameters + @Parameter + public String testName; + + @Parameter(1) + public Class<? extends IResultTypeComputer> clazz; + + @Parameters(name = "TypeComputerTest {index}: {0}") + public static Collection<Object[]> tests() { + + // Do any needed preparations once + prepare(); + + // 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 }); + } + + return tests; + } + + /** + * Test return types should be either "any", "missable" or "nullable" + * + * @throws Exception Exception + */ + @Test + public void test() throws Exception { + + // Type environment and metadata provider + IVariableTypeEnvironment typeEnv = mock(IVariableTypeEnvironment.class); + IMetadataProvider metadataProvider = mock(IMetadataProvider.class); + + // Arguments all of type "any" + List<Mutable<ILogicalExpression>> threeArgs = createArgs(3, typeEnv); + List<Mutable<ILogicalExpression>> fourArgs = createArgs(4, typeEnv); + List<Mutable<ILogicalExpression>> sixArgs = createArgs(6, typeEnv); + + // TODO(Hussain) Remove this after the type computers are updated + // Add to exception list for computers checking their arguments count + addComputersCheckingArgsCount(threeArgs, fourArgs); + + // Mocks function identifier + FunctionIdentifier functionIdentifier = mock(FunctionIdentifier.class); + when(functionIdentifier.getName()).thenReturn("testFunction"); + + // Mocks function expression. + AbstractFunctionCallExpression functionCallExpression = mock(AbstractFunctionCallExpression.class); + when(functionCallExpression.getFunctionIdentifier()).thenReturn(functionIdentifier); + + // Sets up required/actual types of the mocked expression. + Object[] opaqueParameters = new Object[] { BuiltinType.ANY, BuiltinType.ANY }; + when(functionCallExpression.getOpaqueParameters()).thenReturn(opaqueParameters); + + // Tests the return type. It should be either ANY or NULLABLE/MISSABLE. + IResultTypeComputer instance; + IAType resultType; + List<Mutable<ILogicalExpression>> args; + Field[] fields = clazz.getFields(); + + for (Field field : fields) { + + // Ensure the field is one of the instances of the class + if (field.getType().equals(clazz)) { + 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, typeEnv, 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 prepare() { + + // 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(Hussain) Remove this after the type computers are updated + /** + * 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(List<Mutable<ILogicalExpression>> threeArgs, + List<Mutable<ILogicalExpression>> fourArgs) 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, IVariableTypeEnvironment typeEnv) + 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(typeEnv.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: merged Gerrit-Change-Id: I4426efcc9e825ebb00e04e3783d18bb1cbb63a90 Gerrit-Change-Number: 3393 Gerrit-PatchSet: 6 Gerrit-Owner: Hussain Towaileb <[email protected]> Gerrit-Reviewer: Ali Alsuliman <[email protected]> Gerrit-Reviewer: Anon. E. Moose (1000171) Gerrit-Reviewer: Dmitry Lychagin <[email protected]> Gerrit-Reviewer: Hussain Towaileb <[email protected]> Gerrit-Reviewer: Hussain Towaileb <[email protected]> Gerrit-Reviewer: Jenkins <[email protected]>
