/* This file is part of VoltDB. * Copyright (C) 2008-2017 VoltDB Inc. * * This file contains original code and/or modifications of original code. * Any modifications made by VoltDB Inc. are licensed under the following * terms and conditions: * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ /** * 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. *//* This file is part of VoltDB. * Copyright (C) 2008-2016 VoltDB Inc. * * This file contains original code and/or modifications of original code. * Any modifications made by VoltDB Inc. are licensed under the following * terms and conditions: * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ /** * 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.voltdb.planner.eegentests; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import org.json_voltpatches.JSONException; import org.voltdb.VoltType; import org.voltdb.catalog.Column; import org.voltdb.catalog.Database; import org.voltdb.catalog.Table; import org.voltdb.planner.PlanSelector; import org.voltdb.planner.PlannerTestCase; import org.voltdb.plannodes.AbstractPlanNode; /** * This class generates some C++ unit tests. */ public class EEPlanGenerator extends PlannerTestCase { private static final String TESTFILE_TEMPLATE = "\n" + "/******************************************************************************************\n" + " *\n" + " * NOTA BENE: This file is automagically generated from the source class named\n" + " * @SOURCE_PACKAGE_NAME@.@SOURCE_CLASS_NAME@.\n" + " * Please do not edit it unless you abandon all hope of regenerating it.\n" + " *\n" + " ******************************************************************************************/\n" + "#include \"harness.h\"\n" + "\n" + "#include \"catalog/cluster.h\"\n" + "#include \"catalog/table.h\"\n" + "#include \"plannodes/abstractplannode.h\"\n" + "#include \"storage/persistenttable.h\"\n" + "#include \"storage/temptable.h\"\n" + "#include \"storage/tableutil.h\"\n" + "#include \"test_utils/plan_testing_config.h\"\n" + "#include \"test_utils/LoadTableFrom.hpp\"\n" + "#include \"test_utils/plan_testing_baseclass.h\"\n" + "\n" + "\n" + "namespace {\n" + "extern TestConfig allTests[];\n" + "};\n" + "\n" + "class @TEST_CLASS_NAME@ : public PlanTestingBaseClass<EngineTestTopend> {\n" + "public:\n" + " /*\n" + " * This constructor lets us set the global random seed for the\n" + " * random number generator. It would be better to have a seed\n" + " * just for this test. But that is not easily done.\n" + " */\n" + " @TEST_CLASS_NAME@(uint32_t randomSeed = (unsigned int)time(NULL)) {\n" + " initialize(m_testDB, randomSeed);\n" + " }\n" + "\n" + " ~@TEST_CLASS_NAME@() { }\n" + "protected:\n" + " static DBConfig m_testDB;\n" + "};\n" + "\n" + "/*\n" + " * All the test cases are here.\n" + " */\n" + "@TEST_CASES@\n" + "\n" + "namespace {\n" + "/*\n" + " * These are the names of all the columns.\n" + " */\n" + "@TABLE_COLUMN_NAMES@\n" + "\n" + "/*\n" + " * These are the types of all the columns.\n" + " */\n" + "@TABLE_TYPE_NAMES@\n" + "\n" + "/*\n" + " * These are the sizes of all the column data.\n" + " */\n" + "@TABLE_TYPE_SIZES@\n" + "\n" + "/*\n" + " * These are the strings in each populated columns.\n" + " * The data will either be integers or indices into this table.\n" + " */\n" + "@TABLE_STRINGS@\n" + "\n" + "/*\n" + " * This is the data in all columns.\n" + " */\n" + "@TABLE_DATA@\n" + "\n" + "/*\n" + " * These are the names of all the columns.\n" + " */\n" + "/*\n" + " * These knit together all the bits of data which form a table.\n" + " */\n" + "@TABLE_CONFIGS@\n" + "\n" + "/*\n" + " * This holds all the persistent tables.\n" + " */\n" + "@TABLE_DEFINITIONS@\n" + "\n" + "@ALL_TESTS@\n" + "}\n" + "\n" + "DBConfig @TEST_CLASS_NAME@::m_testDB =\n" + "\n" + "@DATABASE_CONFIG_BODY@\n" + "\n" + "int main() {\n" + " return TestSuite::globalInstance()->runAll();\n" + "}\n"; private String m_sourceDir = "tests/ee"; private boolean m_namesOnly = false; protected String getPlanString(String sqlStmt) throws JSONException { AbstractPlanNode node = compile(sqlStmt); String planString = PlanSelector.outputPlanDebugString(node); return planString; } /** * Pair q column name and a type. This is used for schemae. * * The length is the length of the datatype. For non-strings * this is a property of the datatype. For strings it must be * specified by the schema. */ protected static class ColumnConfig { public ColumnConfig(Column col) { m_name = col.getName(); m_type = VoltType.get((byte)col.getType()); m_length = col.getSize(); } public ColumnConfig(String name, VoltType type, int length) { assert(0 <= length || type != VoltType.STRING); m_name = name; m_type = type; m_length = length; } public ColumnConfig(String name, VoltType type) { this(name, type, type.getLengthInBytesForFixedTypes()); } public final String getName() { return m_name; } public final VoltType getType() { return m_type; } public final int getLength() { return m_length; } public final String getColumnTypeName() { if (m_type == VoltType.STRING) { return "voltdb::VALUE_TYPE_VARCHAR"; } return "voltdb::VALUE_TYPE_" + m_type.getName(); } private String m_name; private VoltType m_type; private int m_length; } protected static class SchemaConfig { public SchemaConfig(ColumnConfig ...columns) { for (ColumnConfig cc : columns) { m_columns.add(cc); } } ColumnConfig getColumn(int idx) { return m_columns.get(idx); } public int getNumColumns() { return m_columns.size(); } public List<ColumnConfig> getColumns() { return m_columns; } private List<ColumnConfig> m_columns = new ArrayList<>(); } /** * Define a schema from the catalog. */ private static SchemaConfig makeSchemaConfig(String tableName, Database db) { Table dbTable = db.getTables().get(tableName); assert(dbTable != null); ColumnConfig[] cols = new ColumnConfig[dbTable.getColumns().size()]; for (Column col : dbTable.getColumns()) { cols[col.getIndex()] = new ColumnConfig(col); } return new SchemaConfig(cols); } /** * Fetch the definition of a Table from the catalog * in a format we can easily use. */ protected static class TableConfig { /** * This constructor is for defining tables when * the data is fixed. * * @param tableName * @param schema * @param data */ public TableConfig(String tableName, Database db, Object[][] data) { tableName = tableName.toUpperCase(); SchemaConfig schema = makeSchemaConfig(tableName, db); m_tableName = tableName; m_schema = schema; if (data != null) { m_rowCount = data.length; m_data = computeData(data); } else { m_rowCount = 0; m_data = null; } ensureTable(); } /** * This constructor is used for defining tables * when the data is randomly generated by the C++ * unit test. This randomly generated data is * generally useful for profiling large tables. * * @param tableName * @param schema * @param nrows */ public TableConfig(String tableName, Database db, int nrows) { this(tableName, db, null); m_rowCount = nrows; } public boolean hasActualData() { return m_data != null; } /* * Strings are stored in the string table. The data * is either the integral data in the table, for integers, * or else an index into the string table for strings. */ private int[][] computeData(Object[][] data) { int[][] answer = new int[data.length][m_schema.getNumColumns()]; for (int ridx = 0; ridx < data.length; ridx += 1) { Object[] row = data[ridx]; for (int cidx = 0; cidx < row.length; cidx += 1) { Object obj = row[cidx]; if (obj instanceof Number) { Number num = (Number)obj; answer[ridx][cidx] = num.intValue(); } else if (obj instanceof String) { answer[ridx][cidx] = addString((String)obj); } } } return answer; } private int addString(String obj) { m_strings.add(obj); return m_strings.size()-1; } public List<String> getStrings() { return m_strings; } public String getTableRowCountName() { return String.format("NUM_TABLE_ROWS_%s", m_tableName.toUpperCase()); } public String getTableColCountName() { return String.format("NUM_TABLE_COLS_%s", m_tableName.toUpperCase()); } public int getRowCount() { return m_rowCount; } public List<ColumnConfig> getColumns() { return m_schema.getColumns(); } public String getColumnNamesName() { return String.format("%s_ColumnNames", m_tableName); } public String getColumnTypesName() { return String.format("%s_Types", m_tableName); } public String getColumnTypesSizesName() { return String.format("%s_Sizes", m_tableName); } public String getTableConfigName() { return String.format("%sConfig", m_tableName); } public final SchemaConfig getSchema() { return m_schema; } private void ensureTable() { // If there is not actual data, all is well. if (m_data == null) { return; } // Ensure there is at least one row, and that // all rows have the same length, and that the // types are all sensible. assert(m_data.length > 0); for (int idx = 1; idx < m_data.length; idx += 1) { assert(m_data[idx].length == m_data[0].length); } } public String getTableDataName() { if (hasActualData()) { return String.format("%sData", m_tableName); } else { return "NULL"; } } public String getStringTableName() { return String.format("%s_Strings", m_tableName); } public String getStringsName() { return "NULL"; } public Object getNumStringsName() { return "num_" + m_tableName + "_strings"; } public Object getNumStrings() { return m_strings.size(); } String m_tableName; SchemaConfig m_schema; int[][] m_data = null; List<String> m_strings = new ArrayList<>(); int m_rowCount; } protected Database getDatabase() { Database db = getCatalog().getClusters().get("cluster").getDatabases().get("database"); return db; } /** * Define a database. */ protected class DBConfig { public DBConfig(Class<? extends PlannerTestCase> klass, URL ddlURL, String catalogString, TableConfig ... tables) { m_class = klass; m_ddlURL = ddlURL; m_catalogString = catalogString; m_tables = Arrays.asList(tables); m_testConfigs = new ArrayList<>(); } /** * Clean up a string used to write a C++ string. Escape double * quotes and newlines. * * @param input * @return */ private String cleanString(String input, String indent) { String quotedInput = input.replace("\"", "\\\""); quotedInput = "\"" + quotedInput.replace("\n", "\\n\"\n" + indent + "\"") + "\""; return quotedInput; } /** * Given a URL, from Class.getResource(), pull in the contents of the DDL file. * * @param ddlURL * @return * @throws Exception */ private String getDDLStringFromURL() throws Exception { InputStream inputStream = null; try { inputStream = m_ddlURL.openStream(); ByteArrayOutputStream result = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int length; while ((length = inputStream.read(buffer)) != -1) { result.write(buffer, 0, length); } return result.toString("UTF-8"); } finally { try { if (inputStream != null) { inputStream.close(); } } catch (Exception ex) { ; } } } /** * Add a test configuration to the database. * * @param testConfig */ public void addTest(TestConfig testConfig) { System.err.printf("Adding test %d: %s\n", m_testConfigs.size(), testConfig.m_testName); System.err.flush(); m_testConfigs.add(testConfig); } private URL m_ddlURL; private String m_catalogString; private List<TableConfig> m_tables; private List<TestConfig> m_testConfigs; private Class<? extends PlannerTestCase> m_class; public String getTestCases(Map<String, String> params) { StringBuffer sb = new StringBuffer(); for (int testIdx = 0; testIdx < m_testConfigs.size(); testIdx += 1) { TestConfig tc = m_testConfigs.get(testIdx); sb.append(String.format("TEST_F(%s, %s) {\n" + " static int testIndex = %d;\n" + " executeTest(allTests[testIndex]);\n" + "}\n", params.get("TEST_CLASS_NAME"), tc.m_testName, testIdx)); } return sb.toString(); } public String getTableColumnTypesString(Map<String, String> params) { StringBuffer sb = new StringBuffer(); for (TableConfig tc : m_tables) { getOneTableColumnTypesString(sb, tc); } return sb.toString(); } private void getOneTableColumnTypesString(StringBuffer sb, TableConfig tc) { sb.append(String.format("const voltdb::ValueType %s[] = {\n", tc.getColumnTypesName())); for (int idx = 0; idx < tc.getColumns().size(); idx += 1) { ColumnConfig cc = tc.getColumns().get(idx); sb.append(" " + cc.getColumnTypeName() + ",\n"); } sb.append("};\n"); } public String getTableColumnSizeString(Map<String, String> params) { StringBuffer sb = new StringBuffer(); for (TableConfig tc : m_tables) { getOneTableColumnSizeString(sb, tc); } return sb.toString(); } private void getOneTableColumnSizeString(StringBuffer sb, TableConfig tc) { sb.append(String.format("const int32_t %s[] = {\n", tc.getColumnTypesSizesName())); for (int idx = 0; idx < tc.getColumns().size(); idx += 1) { ColumnConfig cc = tc.getColumns().get(idx); sb.append(" " + cc.getLength() + ",\n"); } sb.append("};\n"); } public String getTableColumnNames(Map<String, String> params) { StringBuffer sb = new StringBuffer(); for (TableConfig tc : m_tables) { getOneTableColumnNames(sb, tc); } return sb.toString(); } private void getOneTableColumnNames(StringBuffer sb, TableConfig tc) { sb.append(String.format("const char *%s[] = {\n", tc.getColumnNamesName())); for (int idx = 0; idx < tc.getColumns().size(); idx += 1) { ColumnConfig cc = tc.getColumns().get(idx); String colName = cc.getName(); sb.append(" \"" + colName + "\",\n"); } sb.append("};\n"); } private void writeTable(StringBuffer sb, String tableName, String rowCountName, int rowCount, String colCountName, int colCount, int[][] data) { sb.append(String.format("const int %s = %d;\n", rowCountName, rowCount)); sb.append(String.format("const int %s = %d;\n", colCountName, colCount)); // // If there is no data, don't declare it. // We'll fill the table later on. // if (data == null) { sb.append(";\n"); } else { sb.append(String.format("const int %s[%s * %s] = {\n", tableName, rowCountName, colCountName)); for (int ridx = 0; ridx < data.length; ridx += 1) { sb.append(" "); for (int cidx = 0; cidx < data[ridx].length; cidx += 1) { sb.append(String.format("%3d,", data[ridx][cidx])); } sb.append("\n"); } sb.append("};\n\n"); } } public String getTableStrings(Map<String, String> params) { StringBuffer sb = new StringBuffer(); for (TableConfig tc : m_tables) { getOneTableStrings(sb, tc); } return sb.toString(); } private void getOneTableStrings(StringBuffer sb, TableConfig tc) { sb.append(String.format("int32_t %s = %d;\n", tc.getNumStringsName(), tc.getNumStrings())); sb.append(String.format("const char *%s[] = {\n", tc.getStringTableName())); for (String str : tc.getStrings()) { sb.append(String.format(" \"%s\",\n", str)); } sb.append("};\n"); } public String getAllTableData(Map<String, String> params) { StringBuffer sb = new StringBuffer(); for (TableConfig tc : m_tables) { getOneTableData(sb, tc); } return sb.toString(); } private void getOneTableData(StringBuffer sb, TableConfig tc) { String rowCountName = tc.getTableRowCountName(); String colCountName = tc.getTableColCountName(); writeTable(sb, tc.getTableDataName(), rowCountName, tc.getRowCount(), colCountName, tc.getColumns().size(), tc.m_data); } public String getTableConfigs(Map<String, String> params) { StringBuffer sb = new StringBuffer(); for (TableConfig tc : m_tables) { writeOneTableConfig(sb, tc); } return sb.toString(); } private void writeOneTableConfig(StringBuffer sb, TableConfig tc) { sb.append(String.format("const TableConfig %s = {\n", tc.getTableConfigName())) .append(String.format(" \"%s\",\n", tc.m_tableName)) .append(String.format(" %s,\n", tc.getColumnNamesName())) .append(String.format(" %s,\n", tc.getColumnTypesName())) .append(String.format(" %s,\n", tc.getColumnTypesSizesName())) .append(String.format(" %s,\n", tc.getTableRowCountName())) .append(String.format(" %s,\n", tc.getTableColCountName())) .append(String.format(" %s,\n", tc.getTableDataName())) .append(String.format(" %s,\n", tc.getStringTableName())) .append(String.format(" %s\n", tc.getNumStringsName())) .append("};\n"); } public String getTableDefinitions(Map<String, String> params) { StringBuffer sb = new StringBuffer(); sb.append("const TableConfig *allTables[] = {\n"); for (TableConfig tc : m_tables) { sb.append(String.format(" &%s,\n", tc.getTableConfigName())); } sb.append("};\n"); return sb.toString(); } public String getTestResultData(Map<String, String> params) { StringBuffer sb = new StringBuffer(); sb.append("const TableConfig *allResults[] = {\n"); for (TestConfig tstConfig : m_testConfigs) { if (tstConfig.hasExpectedData()) { sb.append(String.format(" &%s,\n", tstConfig.getExpectedOutput().getTableConfigName())); } } sb.append("\n};\n"); return sb.toString(); } public String getAllTests(Map<String, String> params) throws JSONException { StringBuffer sb = new StringBuffer(); sb.append(String.format("TestConfig allTests[%d] = {\n", m_testConfigs.size())); for (TestConfig tc : m_testConfigs) { sb.append(" {\n") .append(" // SQL Statement\n") .append(String.format(" %s,\n", cleanString(tc.m_sqlString, " "))) .append(" // Failure is expected\n") .append(String.format(" %s,\n", tc.isExpectedToFail() ? "true" : "false")) .append(" // Plan String\n") .append(String.format(" %s,\n", cleanString(getPlanString(tc.m_sqlString), " "))) .append(String.format(" %s\n", tc.getOutputTableName())) .append(" },\n"); } sb.append("};\n"); return sb.toString(); } public String getDatabaseConfigBody(Map<String, String> params) throws Exception { StringBuffer sb = new StringBuffer(); sb.append("{\n"); sb.append(" //\n // DDL.\n //\n"); sb.append(String.format(" %s,\n", cleanString(getDDLStringFromURL(), " "))); sb.append(" //\n // Catalog String\n //\n"); sb.append(String.format(" %s,\n", cleanString(m_catalogString, " "))); sb.append(String.format(" %d,\n", m_tables.size())); sb.append(" allTables\n"); sb.append("};\n"); return sb.toString(); } public String getClassPackageName() { return m_class.getPackage().getName(); } public String getClassName() { return m_class.getSimpleName(); } } /** * Define a test. We need the sql string and the expected tabular output. * @author bwhite */ protected static class TestConfig { TestConfig(String testName, String sqlString, boolean expectFail, TableConfig expectedOutput) { m_testName = testName; m_sqlString = sqlString; m_expectedOutput = expectedOutput; m_expectFail = expectFail; } public TestConfig(String testName, String sqlString, boolean expectFail) { this(testName, sqlString, expectFail, null); } /** * Tell whether this test has expected data. * @return */ public boolean hasExpectedData() { return m_expectedOutput != null; } /** * Return the expected output table. This may be null. * @return The expected output table, or null if the output is unspecified. */ public TableConfig getExpectedOutput() { return m_expectedOutput; } /** * Return true if this test is expected to fail. * @return true iff this test is expected to fail. */ public boolean isExpectedToFail() { return m_expectFail; } /** * Return the number of rows in the expected * output. If this is -1, there is no expected * output. * @return */ public int getRowCount() { if (hasExpectedData()) { return m_expectedOutput.getRowCount(); } return -1; } /** * Return the number of columns in the expected * output. If this is -1 there is no expected * output. * @return */ public int getColCount() { if (hasExpectedData()) { return m_expectedOutput.getColumns().size(); } return -1; } public String getColCountName() { return String.format("NUM_OUTPUT_COLS_%s", m_testName.toUpperCase()); } public String getRowCountName() { return String.format("NUM_OUTPUT_ROWS_%s", m_testName.toUpperCase()); } public String getOutputTableName() { if (hasExpectedData()) { return "&" + m_expectedOutput.getTableConfigName(); } else { return "NULL"; } } private String m_testName; private String m_sqlString; private TableConfig m_expectedOutput; private boolean m_expectFail; } /** * Given a foldername and a class name, write a C++ file for the test. * * @param string * @param string2 * @param db * @throws Exception */ protected void generateTests(String testFolder, String testClassName, DBConfig db) throws Exception { System.out.printf("%s/%s\n", testFolder, testClassName); if (m_namesOnly) { return; } Map<String, String> params = new HashMap<>(); params.put("SOURCE_PACKAGE_NAME", db.getClassPackageName()); params.put("SOURCE_CLASS_NAME", db.getClassName()); params.put("TEST_CLASS_NAME", testClassName); params.put("TEST_CASES", db.getTestCases(params)); params.put("TABLE_COLUMN_NAMES", db.getTableColumnNames(params)); params.put("TABLE_TYPE_SIZES", db.getTableColumnSizeString(params)); params.put("TABLE_STRINGS", db.getTableStrings(params)); params.put("TABLE_TYPE_NAMES", db.getTableColumnTypesString(params)); params.put("TABLE_DATA", db.getAllTableData(params)); params.put("TABLE_CONFIGS", db.getTableConfigs(params)); params.put("TABLE_DEFINITIONS", db.getTableDefinitions(params)); params.put("TEST_RESULT_TABLE_DEFINITIONS", db.getTestResultData(params)); params.put("ALL_TESTS", db.getAllTests(params)); params.put("DATABASE_CONFIG_BODY", db.getDatabaseConfigBody(params)); writeTestFile(testFolder, testClassName, params); } public static boolean typeMatch(Object elem, VoltType type, int size) { switch (type) { case TINYINT: case SMALLINT: case INTEGER: case BIGINT: return elem instanceof Number && ! (elem instanceof Float || elem instanceof Double); case FLOAT: // We can't pass floats yet. Sorry. return false; case STRING: if (! (elem instanceof String) ) { return false; } String elemStr = (String)elem; if (0 <= size && elemStr.length() > size) { return false; } return true; default: return false; } } private void writeFile(File path, String contents) throws Exception { PrintWriter out = null; try { out = new PrintWriter(path); out.print(contents); } finally { if (out != null) { try { out.close(); } catch (Exception ex) { ; } } } } protected void processArgs(String args[]) { for (int idx = 0; idx < args.length; idx += 1) { String arg = args[idx]; if ("--generated-dir".equals(arg)) { idx += 1; if (idx < args.length) { m_sourceDir = args[idx]; } else { throw new IllegalArgumentException("No argument for --generated-dir."); } } else if ("--names-only".equals(arg)) { m_namesOnly = true; } } } private void writeTestFile(String testFolder, String testClassName, Map<String, String> params) throws Exception { String template = TESTFILE_TEMPLATE; for (Map.Entry<String, String> entry : params.entrySet()) { String pattern = "@" + entry.getKey() + "@"; String value = params.get(entry.getKey()); template = template.replace(pattern, value); } File outputDir = new File(String.format("%s/%s", m_sourceDir, testFolder)); if (! outputDir.exists() && !outputDir.mkdirs()) { throw new IOException("Cannot make test source folder \"" + outputDir + "\""); } File outputFile = new File(outputDir, testClassName + ".cpp"); writeFile(outputFile, template); } public void setUp(URL ddlURL, String basename, boolean planForSinglePartition) throws Exception { setupSchema(ddlURL, basename, planForSinglePartition); } }