| /* |
| * 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.hadoop.hive.tools; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.HashMap; |
| |
| /** |
| * |
| * GenVectorTestCode. |
| * This class is mutable and maintains a hashmap of TestSuiteClassName to test cases. |
| * The tests cases are added over the course of vectorized expressions class generation, |
| * with test classes being outputted at the end. For each column vector (inputs and/or outputs) |
| * a matrix of pairwise covering Booleans is used to generate test cases across nulls and |
| * repeating dimensions. Based on the input column vector(s) nulls and repeating states |
| * the states of the output column vector (if there is one) is validated, along with the null |
| * vector. For filter operations the selection vector is validated against the generated |
| * data. Each template corresponds to a class representing a test suite. |
| */ |
| public class GenVectorTestCode { |
| |
| public enum TestSuiteClassName{ |
| TestColumnScalarOperationVectorExpressionEvaluation, |
| TestColumnScalarOperationVectorExpressionCheckedEvaluation, |
| TestColumnScalarFilterVectorExpressionEvaluation, |
| TestColumnColumnOperationVectorExpressionEvaluation, |
| TestColumnColumnOperationVectorExpressionCheckedEvaluation, |
| TestColumnColumnFilterVectorExpressionEvaluation, |
| } |
| |
| private final String testOutputDir; |
| private final String testTemplateDirectory; |
| private final HashMap<TestSuiteClassName,StringBuilder> testsuites; |
| |
| public GenVectorTestCode(String testOutputDir, String testTemplateDirectory) { |
| this.testOutputDir = testOutputDir; |
| this.testTemplateDirectory = testTemplateDirectory; |
| testsuites = new HashMap<TestSuiteClassName, StringBuilder>(); |
| |
| for(TestSuiteClassName className : TestSuiteClassName.values()) { |
| testsuites.put(className,new StringBuilder()); |
| } |
| |
| } |
| |
| public void addColumnScalarOperationCheckedTestCases(boolean op1IsCol, String vectorExpClassName, |
| String inputColumnVectorType, String outputColumnVectorType, String scalarType, |
| boolean isReturnTypeLong) |
| throws IOException { |
| |
| TestSuiteClassName template = |
| TestSuiteClassName.TestColumnScalarOperationVectorExpressionCheckedEvaluation; |
| |
| //Read the template into a string; |
| String templateFile = GenVectorCode.joinPath(this.testTemplateDirectory,template.toString()+".txt"); |
| String templateString = removeTemplateComments(GenVectorCode.readFile(templateFile)); |
| String[] outputTypeInfos = null; |
| if (isReturnTypeLong) { |
| outputTypeInfos = new String[] {"tinyint", "smallint", "int", "bigint"}; |
| } else { |
| outputTypeInfos = new String[] {"float", "double"}; |
| } |
| for (String outputTypeInfo : outputTypeInfos) { |
| for (Boolean[] testMatrix : new Boolean[][] { |
| // Pairwise: InitOuputColHasNulls, InitOuputColIsRepeating, ColumnHasNulls, ColumnIsRepeating |
| { false, true, true, true }, { false, false, false, false }, { true, false, true, false }, |
| { true, true, false, false }, { true, false, false, true } }) { |
| String testCase = templateString; |
| testCase = testCase.replaceAll("<TestName>", |
| "test" + vectorExpClassName |
| + createNullRepeatingNameFragment("Out", testMatrix[0], testMatrix[1]) |
| + createNullRepeatingNameFragment("Col", testMatrix[2], testMatrix[3]) |
| + createOutputTypeInfoFragment("Ret", outputTypeInfo)); |
| testCase = testCase.replaceAll("<OutputTypeInfo>", outputTypeInfo); |
| testCase = testCase.replaceAll("<VectorExpClassName>", vectorExpClassName); |
| testCase = testCase.replaceAll("<InputColumnVectorType>", inputColumnVectorType); |
| testCase = testCase.replaceAll("<OutputColumnVectorType>", outputColumnVectorType); |
| testCase = testCase.replaceAll("<ScalarType>", scalarType); |
| testCase = testCase.replaceAll("<CamelCaseScalarType>", GenVectorCode.getCamelCaseType(scalarType)); |
| testCase = testCase.replaceAll("<InitOuputColHasNulls>", testMatrix[0].toString()); |
| testCase = testCase.replaceAll("<InitOuputColIsRepeating>", testMatrix[1].toString()); |
| testCase = testCase.replaceAll("<ColumnHasNulls>", testMatrix[2].toString()); |
| testCase = testCase.replaceAll("<ColumnIsRepeating>", testMatrix[3].toString()); |
| |
| if (op1IsCol) { |
| testCase = testCase.replaceAll("<ConstructorParams>", "0, scalarValue"); |
| } else { |
| testCase = testCase.replaceAll("<ConstructorParams>", "scalarValue, 0"); |
| } |
| |
| testsuites.get(template).append(testCase); |
| } |
| } |
| } |
| |
| public void addColumnScalarOperationTestCases(boolean op1IsCol, String vectorExpClassName, |
| String inputColumnVectorType, String outputColumnVectorType, String scalarType) |
| throws IOException { |
| |
| TestSuiteClassName template = |
| TestSuiteClassName.TestColumnScalarOperationVectorExpressionEvaluation; |
| |
| //Read the template into a string; |
| String templateFile = GenVectorCode.joinPath(this.testTemplateDirectory,template.toString()+".txt"); |
| String templateString = removeTemplateComments(GenVectorCode.readFile(templateFile)); |
| |
| for(Boolean[] testMatrix :new Boolean[][]{ |
| // Pairwise: InitOuputColHasNulls, InitOuputColIsRepeating, ColumnHasNulls, ColumnIsRepeating |
| {false, true, true, true}, |
| {false, false, false, false}, |
| {true, false, true, false}, |
| {true, true, false, false}, |
| {true, false, false, true}}) { |
| String testCase = templateString; |
| testCase = testCase.replaceAll("<TestName>", |
| "test" |
| + vectorExpClassName |
| + createNullRepeatingNameFragment("Out", testMatrix[0], testMatrix[1]) |
| + createNullRepeatingNameFragment("Col", testMatrix[2], testMatrix[3])); |
| testCase = testCase.replaceAll("<VectorExpClassName>", vectorExpClassName); |
| testCase = testCase.replaceAll("<InputColumnVectorType>", inputColumnVectorType); |
| testCase = testCase.replaceAll("<OutputColumnVectorType>", outputColumnVectorType); |
| testCase = testCase.replaceAll("<ScalarType>", scalarType); |
| testCase = testCase.replaceAll("<CamelCaseScalarType>", GenVectorCode.getCamelCaseType(scalarType)); |
| testCase = testCase.replaceAll("<InitOuputColHasNulls>", testMatrix[0].toString()); |
| testCase = testCase.replaceAll("<InitOuputColIsRepeating>", testMatrix[1].toString()); |
| testCase = testCase.replaceAll("<ColumnHasNulls>", testMatrix[2].toString()); |
| testCase = testCase.replaceAll("<ColumnIsRepeating>", testMatrix[3].toString()); |
| |
| if(op1IsCol){ |
| testCase = testCase.replaceAll("<ConstructorParams>","0, scalarValue"); |
| }else{ |
| testCase = testCase.replaceAll("<ConstructorParams>","scalarValue, 0"); |
| } |
| |
| testsuites.get(template).append(testCase); |
| } |
| } |
| |
| public void addColumnScalarFilterTestCases(boolean op1IsCol, String vectorExpClassName, |
| String inputColumnVectorType, String scalarType, String operatorSymbol) |
| throws IOException { |
| |
| TestSuiteClassName template = |
| TestSuiteClassName.TestColumnScalarFilterVectorExpressionEvaluation; |
| |
| //Read the template into a string; |
| String templateFile = GenVectorCode.joinPath(this.testTemplateDirectory,template.toString()+".txt"); |
| String templateString = removeTemplateComments(GenVectorCode.readFile(templateFile)); |
| |
| for(Boolean[] testMatrix : new Boolean[][]{ |
| // Pairwise: ColumnHasNulls, ColumnIsRepeating |
| {true, true}, |
| {true, false}, |
| {false, false}, |
| {false, true}}) { |
| String testCase = templateString; |
| testCase = testCase.replaceAll("<TestName>", |
| "test" |
| + vectorExpClassName |
| + createNullRepeatingNameFragment("Col", testMatrix[0], testMatrix[1])); |
| testCase = testCase.replaceAll("<VectorExpClassName>", vectorExpClassName); |
| testCase = testCase.replaceAll("<InputColumnVectorType>", inputColumnVectorType); |
| testCase = testCase.replaceAll("<ScalarType>", scalarType); |
| testCase = testCase.replaceAll("<CamelCaseScalarType>", GenVectorCode.getCamelCaseType(scalarType)); |
| testCase = testCase.replaceAll("<ColumnHasNulls>", testMatrix[0].toString()); |
| testCase = testCase.replaceAll("<ColumnIsRepeating>", testMatrix[1].toString()); |
| testCase = testCase.replaceAll("<Operator>", operatorSymbol); |
| |
| if(op1IsCol){ |
| testCase = testCase.replaceAll("<Operand1>","inputColumnVector.vector[i]"); |
| testCase = testCase.replaceAll("<Operand2>","scalarValue"); |
| testCase = testCase.replaceAll("<ConstructorParams>","0, scalarValue"); |
| }else{ |
| testCase = testCase.replaceAll("<Operand1>","scalarValue"); |
| testCase = testCase.replaceAll("<Operand2>","inputColumnVector.vector[i]"); |
| testCase = testCase.replaceAll("<ConstructorParams>","scalarValue, 0"); |
| } |
| |
| testsuites.get(template).append(testCase); |
| } |
| } |
| |
| public void addColumnColumnOperationCheckedTestCases(String vectorExpClassName, |
| String inputColumnVectorType1, String inputColumnVectorType2, String outputColumnVectorType, |
| boolean isReturnTypeLong) |
| throws IOException { |
| |
| TestSuiteClassName template= |
| TestSuiteClassName.TestColumnColumnOperationVectorExpressionCheckedEvaluation; |
| |
| //Read the template into a string; |
| String templateFile = GenVectorCode.joinPath(this.testTemplateDirectory,template.toString()+".txt"); |
| String templateString = removeTemplateComments(GenVectorCode.readFile(templateFile)); |
| String[] outputTypeInfos = null; |
| if (isReturnTypeLong) { |
| outputTypeInfos = new String[] {"tinyint", "smallint", "int", "bigint"}; |
| } else { |
| outputTypeInfos = new String[] {"float", "double"}; |
| } |
| for (String outputTypeInfo : outputTypeInfos) { |
| for (Boolean[] testMatrix : new Boolean[][] { |
| // Pairwise: InitOuputColHasNulls, InitOuputColIsRepeating, Column1HasNulls, |
| // Column1IsRepeating, Column2HasNulls, Column2IsRepeating |
| { true, true, false, true, true, true }, |
| { false, false, true, false, false, false }, |
| { true, false, true, false, true, true }, |
| { true, true, true, true, false, false }, |
| { false, false, false, true, true, false }, |
| { false, true, false, false, false, true } }) { |
| String testCase = templateString; |
| testCase = testCase.replaceAll("<TestName>", |
| "test" |
| + vectorExpClassName |
| + createNullRepeatingNameFragment("Out", testMatrix[0], |
| testMatrix[1]) |
| + createNullRepeatingNameFragment("C1", testMatrix[2], testMatrix[3]) |
| + createNullRepeatingNameFragment("C2", testMatrix[4], testMatrix[5]) |
| + createOutputTypeInfoFragment("Ret", outputTypeInfo)); |
| testCase = testCase.replaceAll("<VectorExpClassName>", vectorExpClassName); |
| testCase = testCase.replaceAll("<InputColumnVectorType1>", inputColumnVectorType1); |
| testCase = testCase.replaceAll("<InputColumnVectorType2>", inputColumnVectorType2); |
| testCase = testCase.replaceAll("<OutputColumnVectorType>", outputColumnVectorType); |
| testCase = testCase.replaceAll("<InitOuputColHasNulls>", testMatrix[0].toString()); |
| testCase = testCase.replaceAll("<InitOuputColIsRepeating>", testMatrix[1].toString()); |
| testCase = testCase.replaceAll("<Column1HasNulls>", testMatrix[2].toString()); |
| testCase = testCase.replaceAll("<Column1IsRepeating>", testMatrix[3].toString()); |
| testCase = testCase.replaceAll("<Column2HasNulls>", testMatrix[4].toString()); |
| testCase = testCase.replaceAll("<Column2IsRepeating>", testMatrix[5].toString()); |
| testCase = testCase.replaceAll("<OutputTypeInfo>", outputTypeInfo); |
| |
| testsuites.get(template).append(testCase); |
| } |
| } |
| } |
| |
| public void addColumnColumnOperationTestCases(String vectorExpClassName, |
| String inputColumnVectorType1, String inputColumnVectorType2, String outputColumnVectorType) |
| throws IOException { |
| |
| TestSuiteClassName template= |
| TestSuiteClassName.TestColumnColumnOperationVectorExpressionEvaluation; |
| |
| //Read the template into a string; |
| String templateFile = GenVectorCode.joinPath(this.testTemplateDirectory,template.toString()+".txt"); |
| String templateString = removeTemplateComments(GenVectorCode.readFile(templateFile)); |
| |
| for(Boolean[] testMatrix : new Boolean[][]{ |
| // Pairwise: InitOuputColHasNulls, InitOuputColIsRepeating, Column1HasNulls, |
| // Column1IsRepeating, Column2HasNulls, Column2IsRepeating |
| {true, true, false, true, true, true}, |
| {false, false, true, false, false, false}, |
| {true, false, true, false, true, true}, |
| {true, true, true, true, false, false}, |
| {false, false, false, true, true, false}, |
| {false, true, false, false, false, true}}) { |
| String testCase = templateString; |
| testCase = testCase.replaceAll("<TestName>", |
| "test" |
| + vectorExpClassName |
| + createNullRepeatingNameFragment("Out", testMatrix[0], testMatrix[1]) |
| + createNullRepeatingNameFragment("C1", testMatrix[2], testMatrix[3]) |
| + createNullRepeatingNameFragment("C2", testMatrix[4], testMatrix[5])); |
| testCase = testCase.replaceAll("<VectorExpClassName>", vectorExpClassName); |
| testCase = testCase.replaceAll("<InputColumnVectorType1>", inputColumnVectorType1); |
| testCase = testCase.replaceAll("<InputColumnVectorType2>", inputColumnVectorType2); |
| testCase = testCase.replaceAll("<OutputColumnVectorType>", outputColumnVectorType); |
| testCase = testCase.replaceAll("<InitOuputColHasNulls>", testMatrix[0].toString()); |
| testCase = testCase.replaceAll("<InitOuputColIsRepeating>", testMatrix[1].toString()); |
| testCase = testCase.replaceAll("<Column1HasNulls>", testMatrix[2].toString()); |
| testCase = testCase.replaceAll("<Column1IsRepeating>", testMatrix[3].toString()); |
| testCase = testCase.replaceAll("<Column2HasNulls>", testMatrix[4].toString()); |
| testCase = testCase.replaceAll("<Column2IsRepeating>", testMatrix[5].toString()); |
| |
| testsuites.get(template).append(testCase); |
| } |
| } |
| |
| public void addColumnColumnFilterTestCases(String vectorExpClassName, |
| String inputColumnVectorType1, String inputColumnVectorType2, String operatorSymbol) |
| throws IOException { |
| |
| TestSuiteClassName template= |
| TestSuiteClassName.TestColumnColumnFilterVectorExpressionEvaluation; |
| |
| //Read the template into a string; |
| String templateFile = GenVectorCode.joinPath(this.testTemplateDirectory,template.toString()+".txt"); |
| String templateString = removeTemplateComments(GenVectorCode.readFile(templateFile)); |
| |
| for(Boolean[] testMatrix : new Boolean[][]{ |
| // Pairwise: Column1HasNulls, Column1IsRepeating, Column2HasNulls, Column2IsRepeating |
| {false, true, true, true}, |
| {false, false, false, false}, |
| {true, false, true, false}, |
| {true, true, false, false}, |
| {true, false, false, true}}) { |
| String testCase = templateString; |
| testCase = testCase.replaceAll("<TestName>", |
| "test" |
| + vectorExpClassName |
| + createNullRepeatingNameFragment("C1", testMatrix[0], testMatrix[1]) |
| + createNullRepeatingNameFragment("C2", testMatrix[2], testMatrix[3])); |
| testCase = testCase.replaceAll("<VectorExpClassName>", vectorExpClassName); |
| testCase = testCase.replaceAll("<InputColumnVectorType1>", inputColumnVectorType1); |
| testCase = testCase.replaceAll("<InputColumnVectorType2>", inputColumnVectorType2); |
| testCase = testCase.replaceAll("<Column1HasNulls>", testMatrix[0].toString()); |
| testCase = testCase.replaceAll("<Column1IsRepeating>", testMatrix[1].toString()); |
| testCase = testCase.replaceAll("<Column2HasNulls>", testMatrix[2].toString()); |
| testCase = testCase.replaceAll("<Column2IsRepeating>", testMatrix[3].toString()); |
| testCase = testCase.replaceAll("<Operator>", operatorSymbol); |
| |
| testsuites.get(template).append(testCase); |
| } |
| } |
| |
| public void generateTestSuites() throws IOException { |
| |
| String templateFile = GenVectorCode.joinPath(this.testTemplateDirectory, "TestClass.txt"); |
| for(TestSuiteClassName testClass : testsuites.keySet()) { |
| |
| String templateString = GenVectorCode.readFile(templateFile); |
| templateString = templateString.replaceAll("<ClassName>", testClass.toString()); |
| templateString = templateString.replaceAll("<TestCases>", testsuites.get(testClass).toString()); |
| |
| String outputFile = GenVectorCode.joinPath(this.testOutputDir, testClass + ".java"); |
| |
| GenVectorCode.writeFile(new File(outputFile), templateString); |
| } |
| } |
| |
| private static String createNullRepeatingNameFragment(String identifier, boolean nulls, boolean repeating) |
| { |
| if(nulls || repeating){ |
| if(nulls){ |
| identifier+="Nulls"; |
| } |
| if(repeating){ |
| identifier+="Repeats"; |
| } |
| return identifier; |
| } |
| |
| return ""; |
| } |
| |
| private static String createOutputTypeInfoFragment(String identifier, String outputTypeInfo) { |
| if (identifier == null) { |
| throw new RuntimeException("Received null input for the identifier"); |
| } |
| switch (outputTypeInfo) { |
| case "tinyint": { |
| return identifier + "TinyInt"; |
| } |
| case "smallint": { |
| return identifier + "SmallInt"; |
| } |
| case "int": { |
| return identifier + "Int"; |
| } |
| case "bigint": { |
| return identifier + "BigInt"; |
| } |
| case "float": { |
| return identifier + "Float"; |
| } |
| case "double": { |
| return identifier + "Double"; |
| } |
| default: { |
| throw new RuntimeException("Unsupported input typeInfo " + outputTypeInfo); |
| } |
| } |
| } |
| |
| private static String removeTemplateComments(String templateString){ |
| return templateString.replaceAll("(?s)<!--(.*)-->", ""); |
| } |
| } |