// 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 "runtime/user_function_cache.h" #include #include #include #include "common/logging.h" #include "http/ev_http_server.h" #include "http/http_channel.h" #include "http/http_handler.h" #include "http/http_request.h" #include "util/md5.h" #include "util/file_utils.h" int main(int argc, char* argv[]); namespace doris { bool k_is_downloaded = false; class UserFunctionTestHandler : public HttpHandler { public: void handle(HttpRequest* req) override { auto& file_name = req->param("FILE"); std::string lib_dir = "./be/test/runtime/test_data/user_function_cache/lib"; auto lib_file = lib_dir + "/" + file_name; FILE* fp = fopen(lib_file.c_str(), "r"); if (fp == nullptr) { HttpChannel::send_error(req, INTERNAL_SERVER_ERROR); return; } std::string response; char buf[1024]; while (true) { auto size = fread(buf, 1, 1024, fp); response.append(buf, size); if (size < 1024) { break; } } HttpChannel::send_reply(req, response); k_is_downloaded = true; fclose(fp); } }; static UserFunctionTestHandler s_test_handler = UserFunctionTestHandler(); static EvHttpServer* s_server = nullptr; static int real_port = 0; static std::string hostname = ""; std::string my_add_md5sum; static std::string compute_md5(const std::string& file) { FILE* fp = fopen(file.c_str(), "r"); Md5Digest md5; char buf[1024]; while (true) { auto size = fread(buf, 1, 1024, fp); md5.update(buf, size); if (size < 1024) { break; } } fclose(fp); md5.digest(); return md5.hex(); } class UserFunctionCacheTest : public testing::Test { public: UserFunctionCacheTest() { } virtual ~UserFunctionCacheTest() { } static void SetUpTestCase() { s_server = new EvHttpServer(0); s_server->register_handler(GET, "/{FILE}", &s_test_handler); s_server->start(); real_port = s_server->get_real_port(); ASSERT_NE(0, real_port); hostname = "http://127.0.0.1:" + std::to_string(real_port); // compile code to so system("g++ -shared ./be/test/runtime/test_data/user_function_cache/lib/my_add.cc -o ./be/test/runtime/test_data/user_function_cache/lib/my_add.so"); my_add_md5sum = compute_md5("./be/test/runtime/test_data/user_function_cache/lib/my_add.so"); } static void TearDownTestCase() { delete s_server; system("rm -rf ./be/test/runtime/test_data/user_function_cache/lib/my_add.so"); } void SetUp() override { k_is_downloaded = false; } }; TEST_F(UserFunctionCacheTest, process_symbol) { UserFunctionCache cache; std::string lib_dir = "./be/test/runtime/test_data/user_function_cache/normal"; auto st = cache.init(lib_dir); ASSERT_TRUE(st.ok()); void* fn_ptr = nullptr; UserFunctionCacheEntry* entry = nullptr; st = cache.get_function_ptr(0, "main", "", "", &fn_ptr, &entry); ASSERT_TRUE(st.ok()); ASSERT_EQ(&main, fn_ptr); ASSERT_EQ(nullptr, entry); cache.release_entry(entry); } TEST_F(UserFunctionCacheTest, download_normal) { UserFunctionCache cache; std::string lib_dir = "./be/test/runtime/test_data/user_function_cache/download"; FileUtils::remove_all(lib_dir); auto st = cache.init(lib_dir); ASSERT_TRUE(st.ok()); void* fn_ptr = nullptr; UserFunctionCacheEntry* entry = nullptr; // get my_add st = cache.get_function_ptr(1, "_Z6my_addv", hostname + "/my_add.so", my_add_md5sum, &fn_ptr, &entry); ASSERT_TRUE(st.ok()); ASSERT_TRUE(k_is_downloaded); ASSERT_NE(nullptr, fn_ptr); ASSERT_NE(nullptr, entry); // get my_del st = cache.get_function_ptr(1, "_Z6my_delv", hostname + "/my_add.so", my_add_md5sum, &fn_ptr, &entry); ASSERT_TRUE(st.ok()); ASSERT_NE(nullptr, fn_ptr); ASSERT_NE(nullptr, entry); // get my_mul st = cache.get_function_ptr(1, "_Z6my_mulv", hostname + "/my_add.so", my_add_md5sum, &fn_ptr, &entry); ASSERT_FALSE(st.ok()); cache.release_entry(entry); } TEST_F(UserFunctionCacheTest, load_normal) { UserFunctionCache cache; std::string lib_dir = "./be/test/runtime/test_data/user_function_cache/download"; auto st = cache.init(lib_dir); ASSERT_TRUE(st.ok()); void* fn_ptr = nullptr; UserFunctionCacheEntry* entry = nullptr; st = cache.get_function_ptr(1, "_Z6my_addv", hostname + "/my_add.so", my_add_md5sum, &fn_ptr, &entry); ASSERT_TRUE(st.ok()); ASSERT_FALSE(k_is_downloaded); ASSERT_NE(nullptr, fn_ptr); ASSERT_NE(nullptr, entry); cache.release_entry(entry); } TEST_F(UserFunctionCacheTest, download_fail) { UserFunctionCache cache; std::string lib_dir = "./be/test/runtime/test_data/user_function_cache/download"; auto st = cache.init(lib_dir); ASSERT_TRUE(st.ok()); void* fn_ptr = nullptr; UserFunctionCacheEntry* entry = nullptr; st = cache.get_function_ptr(2, "_Z6my_delv", hostname + "/my_del.so", my_add_md5sum, &fn_ptr, &entry); ASSERT_FALSE(st.ok()); } TEST_F(UserFunctionCacheTest, md5_fail) { UserFunctionCache cache; std::string lib_dir = "./be/test/runtime/test_data/user_function_cache/download"; FileUtils::remove_all(lib_dir); auto st = cache.init(lib_dir); ASSERT_TRUE(st.ok()); void* fn_ptr = nullptr; UserFunctionCacheEntry* entry = nullptr; st = cache.get_function_ptr(1, "_Z6my_addv", hostname + "/my_add.so", "1234", &fn_ptr, &entry); ASSERT_FALSE(st.ok()); } TEST_F(UserFunctionCacheTest, bad_so) { UserFunctionCache cache; std::string lib_dir = "./be/test/runtime/test_data/user_function_cache/bad"; FileUtils::create_dir(lib_dir + "/2"); auto so_file = lib_dir + "/2/2.abc.so"; FILE* fp = fopen(so_file.c_str(), "w"); fwrite(&fp, sizeof(FILE*), 1, fp); fclose(fp); auto st = cache.init(lib_dir); ASSERT_TRUE(st.ok()); void* fn_ptr = nullptr; UserFunctionCacheEntry* entry = nullptr; st = cache.get_function_ptr(2, "_Z6my_addv", hostname + "/my_add.so", "abc", &fn_ptr, &entry); ASSERT_FALSE(st.ok()); } } int main(int argc, char* argv[]) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }