blob: b1974d952e5fc645d37c14846281b01aa9979be3 [file] [log] [blame]
/*
* 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.
*/
#ifndef DBCOMMON_SRC_DBCOMMON_TESTUTIL_FUNCTION_UTILS_H_
#define DBCOMMON_SRC_DBCOMMON_TESTUTIL_FUNCTION_UTILS_H_
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "dbcommon/common/vector.h"
#include "dbcommon/function/func-kind.cg.h"
#include "dbcommon/function/func.h"
#include "dbcommon/log/debug-logger.h"
#include "dbcommon/nodes/datum.h"
#include "dbcommon/nodes/select-list.h"
#include "dbcommon/testutil/scalar-utils.h"
#include "dbcommon/testutil/vector-utils.h"
#include "gtest/gtest.h"
namespace dbcommon {
// FunctionUtility intends to setup a testing framework for dbcommon::func_type.
//
// To complete a new dbcommon::func_type, whether input param is Scalar or
// Vector, whether Vector param is with SelectList and whether Vector param
// contains null value should be taken into account, which is really a tedious
// work.
//
// Therefore we introduce FunctionUtility, which checks the common control
// logic of dbcommon::func_type, to free your hand from preparing Scalar input
// and applying SelectList/null values to Vector input.
// The common user case is to generate parameters and expect using ScalarUtility
// and VectorUtiliy. And then call FunctionUtility::test to check the function.
// Refer test-string-function.cc as a good example.
struct FunctionUtilityMonitor;
class FunctionUtility {
explicit FunctionUtility(FuncKind funcid);
~FunctionUtility();
// Generate parameter for FunctionUtility::test
//
// @param parameterIdx stands for the exact parameter position in
// dbcommon::Function' params, as a result of which, 0 stands for the return
// value of the SQL output.
// @param Type
template <int parameterIdx, typename Type>
Datum generatePara(const std::string &str, char delimiter = ' ') {
assert(parameterIdx >= 0 && parameterIdx <= funcEnt_->argTypes.size());
if (std::is_same<Type, dbcommon::Vector>::value) {
if (parameterIdx == 0)
paraVus_.emplace_back(funcEnt_->retType);
else
paraVus_.emplace_back(funcEnt_->argTypes[parameterIdx - 1]);
paraVectors_.emplace_back(paraVus_.back().generateVector(str, delimiter));
if (paraVus_.back().getTypeKind() == TypeKind::BOOLEANID &&
parameterIdx == 0) {
std::unique_ptr<SelectList> sel(new SelectList);
sel->fromVector(paraVectors_.back().get());
paraSelectList_.push_back(std::move(sel));
return CreateDatum(paraSelectList_.back().get());
}
return CreateDatum(paraVectors_.back().get());
}
if (std::is_same<Type, dbcommon::Scalar>::value) {
if (parameterIdx == 0)
paraSus_.emplace_back(funcEnt_->retType);
else
paraSus_.emplace_back(funcEnt_->argTypes[parameterIdx - 1]);
paraScalars_.emplace_back(
new Scalar(paraSus_.back().generateScalar(str)));
return CreateDatum(paraScalars_.back().get());
}
assert("May not generate parameter other than Vector or Scalar" && false);
return CreateDatum(0);
}
template <typename Type>
Datum generatePara(int parameterIdx, const std::string &str,
char delimiter = ' ') {
assert(parameterIdx < 5);
switch (parameterIdx) {
case 0:
return generatePara<0, Type>(str, delimiter);
case 1:
return generatePara<1, Type>(str, delimiter);
case 2:
return generatePara<2, Type>(str, delimiter);
case 3:
return generatePara<3, Type>(str, delimiter);
}
return CreateDatum(0);
}
// FunctionUtility::test automatically generate several kinds of test case:
// 1. Decompose Vector input into Scalar input
// 2. Convert Vector input to
// 2.1 Vector input without both SelectList and NullValue
// 2.2 Vector input without SelectList but with NullValue
// 2.3 Vector input with SelectList but without NullValue
// 2.4 Vector input with both SelectList and NullValue
//
// When testing TransactionAbortException, all the test cases should be
// exception cases.
//
// @param[in] params
// @param[in] paramsSize
// @param[in] expect
// @param[in] expectErrcode
void test(Datum *params, uint64_t paramsSize, Datum expect,
int expectErrcode = ERRCODE_SUCCESSFUL_COMPLETION);
private:
void cloneInput(Datum *params, uint64_t paramsSize, Datum expect);
// Select a number of elements
void applyFilterSelectList(size_t selCount);
// Select a specified element
void applyPointLookupSelectList(size_t selIdx);
void stripNulls();
void testVectorInput(Datum *params, uint64_t paramsSize, Datum expect,
int expectErrcode);
void testScalarInput(Datum *params, uint64_t paramsSize, Datum expect,
int expectErrcode);
const dbcommon::FuncEntry *funcEnt_;
dbcommon::func_type funcToTest_;
bool hasVectorInput_ = false;
bool hasVectorInputWithNull_ = false;
bool hasNullScalarInput_ = false;
bool hasScalarInput_ = false;
// buffer for clone input
size_t numOfCase_ = 0; // number of function test case
std::vector<SelectList> selsBackup_;
std::vector<std::unique_ptr<Vector>> vecs_;
std::vector<SelectList> sels_;
std::vector<Scalar> scalars_;
SelectList applySel_;
std::vector<Datum> params_;
Datum expect_;
// buffer for generated parameter
std::vector<std::unique_ptr<Scalar>> paraScalars_;
std::vector<std::unique_ptr<Vector>> paraVectors_;
std::vector<std::unique_ptr<SelectList>> paraSelectList_;
std::vector<ScalarUtility> paraSus_;
std::vector<VectorUtility> paraVus_;
static FunctionUtilityMonitor functionUtilityMonitor_;
};
struct FunctionUtilityMonitorUnit {
bool hasVectorInput = false;
bool hasVectorInputWithNull = false;
bool hasNullScalarInput = false;
bool hasScalarInput = false;
};
struct FunctionUtilityMonitor {
~FunctionUtilityMonitor();
std::map<FuncKind, FunctionUtilityMonitorUnit> fus;
};
// TestFunctionEntry works as a test case constructor for dbcommon::Function.
//
// It has 3 attributes(i.e. funcid, expectRet and args) corresponding to
// dbcommon::FuncEntry's .funcId, .retType and .argTypes. Its .expectErrcode
// field somehow serves as a extra specification of expected return values.
//
// When run as a test case, TestFunctionEntry converts its std::string
// representation into real raw data according to the type info that retrieved
// from dbcommon::FuncEntry. It classified the std::string representation input
// into three categories.
// 1. Vector
// "Vector: val1 val2", using space character as default value separator
// "Vector{delimiter=X}: val1Xval2", using character 'X' as value separator
// 2. Scalar
// "Scalar: val", scalar value
// 3. Error
// "Error", used in .expectRet only when testing exception case, which comes
// along with extra .expectErrcode
//
struct TestFunctionEntry {
TestFunctionEntry(FuncKind funcid, std::string expectRet,
std::vector<std::string> args,
int expectErrcode = ERRCODE_SUCCESSFUL_COMPLETION)
: funcid(funcid),
expectRet(expectRet),
args(args),
expectErrcode(expectErrcode) {}
FuncKind funcid = FuncKind::FUNCINVALID;
std::string expectRet;
std::vector<std::string> args;
int expectErrcode = ERRCODE_SUCCESSFUL_COMPLETION;
};
class TestFunction : public ::testing::TestWithParam<TestFunctionEntry> {};
} // namespace dbcommon
#endif // DBCOMMON_SRC_DBCOMMON_TESTUTIL_FUNCTION_UTILS_H_