| // 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. |
| |
| #include <gtest/gtest.h> |
| |
| #include <boost/date_time/posix_time/posix_time.hpp> |
| #include <iostream> |
| |
| #include "common/logging.h" |
| #include "udf/udf_test_harness.hpp" |
| #include "util/logging.h" |
| |
| namespace doris_udf { |
| |
| DoubleVal zero_udf(FunctionContext* context) { |
| return DoubleVal(0); |
| } |
| |
| StringVal log_udf(FunctionContext* context, const StringVal& arg1) { |
| std::cerr << (arg1.is_null ? "NULL" : std::string((char*)arg1.ptr, arg1.len)) << std::endl; |
| return arg1; |
| } |
| |
| StringVal upper_udf(FunctionContext* context, const StringVal& input) { |
| if (input.is_null) { |
| return StringVal::null(); |
| } |
| |
| // Create a new StringVal object that's the same length as the input |
| StringVal result = StringVal(context, input.len); |
| |
| for (int i = 0; i < input.len; ++i) { |
| result.ptr[i] = toupper(input.ptr[i]); |
| } |
| |
| return result; |
| } |
| |
| FloatVal min3(FunctionContext* context, const FloatVal& f1, const FloatVal& f2, |
| const FloatVal& f3) { |
| bool is_null = true; |
| float v = 0.0; |
| |
| if (!f1.is_null) { |
| if (is_null) { |
| v = f1.val; |
| is_null = false; |
| } else { |
| v = std::min(v, f1.val); |
| } |
| } |
| |
| if (!f2.is_null) { |
| if (is_null) { |
| v = f2.val; |
| is_null = false; |
| } else { |
| v = std::min(v, f2.val); |
| } |
| } |
| |
| if (!f3.is_null) { |
| if (is_null) { |
| v = f3.val; |
| is_null = false; |
| } else { |
| v = std::min(v, f3.val); |
| } |
| } |
| |
| return is_null ? FloatVal::null() : FloatVal(v); |
| } |
| |
| StringVal concat(FunctionContext* context, int n, const StringVal* args) { |
| int size = 0; |
| bool all_null = true; |
| |
| for (int i = 0; i < n; ++i) { |
| if (args[i].is_null) { |
| continue; |
| } |
| |
| size += args[i].len; |
| all_null = false; |
| } |
| |
| if (all_null) { |
| return StringVal::null(); |
| } |
| |
| int offset = 0; |
| StringVal result(context, size); |
| |
| for (int i = 0; i < n; ++i) { |
| if (args[i].is_null) { |
| continue; |
| } |
| |
| memcpy(result.ptr + offset, args[i].ptr, args[i].len); |
| offset += args[i].len; |
| } |
| |
| return result; |
| } |
| |
| IntVal num_var_args(FunctionContext*, const BigIntVal& dummy, int n, const IntVal* args) { |
| return IntVal(n); |
| } |
| |
| IntVal validat_udf(FunctionContext* context) { |
| EXPECT_EQ(context->version(), FunctionContext::V2_0); |
| EXPECT_FALSE(context->has_error()); |
| EXPECT_TRUE(context->error_msg() == NULL); |
| return IntVal::null(); |
| } |
| |
| IntVal validate_fail(FunctionContext* context) { |
| EXPECT_FALSE(context->has_error()); |
| EXPECT_TRUE(context->error_msg() == NULL); |
| context->set_error("Fail"); |
| EXPECT_TRUE(context->has_error()); |
| EXPECT_TRUE(strcmp(context->error_msg(), "Fail") == 0); |
| return IntVal::null(); |
| } |
| |
| IntVal validate_mem(FunctionContext* context) { |
| EXPECT_TRUE(context->allocate(0) == NULL); |
| uint8_t* buffer = context->allocate(10); |
| EXPECT_TRUE(buffer != NULL); |
| memset(buffer, 0, 10); |
| context->free(buffer); |
| return IntVal::null(); |
| } |
| |
| StringVal time_to_string(FunctionContext* context, const TimestampVal& time) { |
| boost::posix_time::ptime t(*(boost::gregorian::date*)&time.date); |
| t += boost::posix_time::nanoseconds(time.time_of_day); |
| std::stringstream ss; |
| ss << boost::posix_time::to_iso_extended_string(t) << " " |
| << boost::posix_time::to_simple_string(t.time_of_day()); |
| std::string s = ss.str(); |
| StringVal result(context, s.size()); |
| memcpy(result.ptr, s.data(), result.len); |
| return result; |
| } |
| |
| TEST(UdfTest, TestFunctionContext) { |
| EXPECT_TRUE(UdfTestHarness::validat_udf<IntVal>(validat_udf, IntVal::null())); |
| EXPECT_FALSE(UdfTestHarness::validat_udf<IntVal>(validate_fail, IntVal::null())); |
| EXPECT_TRUE(UdfTestHarness::validat_udf<IntVal>(validate_mem, IntVal::null())); |
| } |
| |
| TEST(UdfTest, TestValidate) { |
| EXPECT_TRUE(UdfTestHarness::validat_udf<DoubleVal>(zero_udf, DoubleVal(0))); |
| EXPECT_FALSE(UdfTestHarness::validat_udf<DoubleVal>(zero_udf, DoubleVal(10))); |
| |
| EXPECT_TRUE((UdfTestHarness::validat_udf<StringVal, StringVal>(log_udf, StringVal("abcd"), |
| StringVal("abcd")))); |
| |
| EXPECT_TRUE((UdfTestHarness::validat_udf<StringVal, StringVal>(upper_udf, StringVal("abcd"), |
| StringVal("ABCD")))); |
| |
| EXPECT_TRUE((UdfTestHarness::validat_udf<FloatVal, FloatVal, FloatVal, FloatVal>( |
| min3, FloatVal(1), FloatVal(2), FloatVal(3), FloatVal(1)))); |
| EXPECT_TRUE((UdfTestHarness::validat_udf<FloatVal, FloatVal, FloatVal, FloatVal>( |
| min3, FloatVal(1), FloatVal::null(), FloatVal(3), FloatVal(1)))); |
| EXPECT_TRUE((UdfTestHarness::validat_udf<FloatVal, FloatVal, FloatVal, FloatVal>( |
| min3, FloatVal::null(), FloatVal::null(), FloatVal::null(), FloatVal::null()))); |
| } |
| |
| TEST(UdfTest, TestTimestampVal) { |
| boost::gregorian::date d(2003, 3, 15); |
| TimestampVal t1(*(int32_t*)&d); |
| EXPECT_TRUE((UdfTestHarness::validat_udf<StringVal, TimestampVal>(time_to_string, t1, |
| "2003-03-15 00:00:00"))); |
| |
| TimestampVal t2(*(int32_t*)&d, 1000L * 1000L * 5000L); |
| EXPECT_TRUE((UdfTestHarness::validat_udf<StringVal, TimestampVal>(time_to_string, t2, |
| "2003-03-15 00:00:05"))); |
| } |
| |
| TEST(UdfTest, TestVarArgs) { |
| std::vector<StringVal> input; |
| input.push_back(StringVal("Hello")); |
| input.push_back(StringVal("World")); |
| |
| EXPECT_TRUE((UdfTestHarness::validat_udf<StringVal, StringVal>(concat, input, |
| StringVal("HelloWorld")))); |
| |
| input.push_back(StringVal("More")); |
| EXPECT_TRUE((UdfTestHarness::validat_udf<StringVal, StringVal>(concat, input, |
| StringVal("HelloWorldMore")))); |
| |
| std::vector<IntVal> args; |
| args.resize(10); |
| EXPECT_TRUE((UdfTestHarness::validat_udf<IntVal, BigIntVal, IntVal>( |
| num_var_args, BigIntVal(0), args, IntVal(args.size())))); |
| } |
| } // namespace doris_udf |
| |
| int main(int argc, char** argv) { |
| std::string conffile = std::string(getenv("DORIS_HOME")) + "/conf/be.conf"; |
| if (!doris::config::init(conffile.c_str(), false)) { |
| fprintf(stderr, "error read config file. \n"); |
| return -1; |
| } |
| init_glog("be-test"); |
| ::testing::InitGoogleTest(&argc, argv); |
| return RUN_ALL_TESTS(); |
| } |