* Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved.
*
* Licensed 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.
*/
#if __has_include(<filesystem>)
#include <filesystem>
#else
#include <experimental/filesystem>
#endif
#include <stdexcept>
#include <string>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <msgpack.hpp>
#define private public
#include "api/cpp/src/executor/function_executor.h"
#include "api/cpp/src/executor/executor_holder.h"
#include "api/cpp/src/executor/posix_executor.h"
#include "src/utility/timer_worker.h"
#include "yr/api/function_manager.h"
namespace YR {
namespace test {
using namespace testing;
using namespace YR::internal;
using YR::Libruntime::ErrorCode;
using YR::Libruntime::ModuleCode;
using YR::utility::CloseGlobalTimer;
using YR::utility::InitGlobalTimer;
namespace fs = std::filesystem;
class FunctionExecutorTest : public testing::Test {
public:
FunctionExecutorTest(){};
~FunctionExecutorTest(){};
static void SetUpTestCase()
{
ClearFunctions();
}
void SetUp() override
{
exec_ = std::make_shared<FunctionExecutor>();
}
void TearDown() override
{
exec_.reset();
ClearFunctions();
}
static void ClearFunctions()
{
FunctionManager::Singleton().funcIdToName_.clear();
FunctionManager::Singleton().memberFuncIdToName_.clear();
FunctionManager::Singleton().clsMap_.clear();
FunctionManager::Singleton().funcMap_.clear();
FunctionManager::Singleton().memberFuncMap_.clear();
FunctionManager::Singleton().shutdownCallerMap_.clear();
FunctionManager::Singleton().ckptFuncMap_.clear();
FunctionManager::Singleton().recoverFuncMap_.clear();
FunctionManager::Singleton().recoverCallbackFuncMap_.clear();
}
private:
std::shared_ptr<FunctionExecutor> exec_;
};
TEST_F(FunctionExecutorTest, DISABLED_LoadFunctionsSuccessfullyTest)
{
fs::path currentPath = fs::current_path();
auto path = currentPath.string();
auto idx = path.find("yuanrong/");
std::string subPath = path.substr(0, idx);
auto libPath = subPath + "yuanrong/metrics/lib";
InitGlobalTimer();
YR::Libruntime::ErrorInfo err;
err = LoadFunctions({libPath});
ASSERT_EQ(err.Code(), YR::Libruntime::ErrorCode::ERR_OK) << err.Msg();
err = exec_->LoadFunctions({libPath + "/libz.so"});
ASSERT_EQ(err.Code(), YR::Libruntime::ErrorCode::ERR_OK) << err.Msg();
CloseGlobalTimer();
}
TEST_F(FunctionExecutorTest, LoadFunctionsFailedTest)
{
YR::Libruntime::ErrorInfo err;
err = exec_->LoadFunctions({});
ASSERT_EQ(err.Code(), YR::Libruntime::ErrorCode::ERR_USER_CODE_LOAD);
err = exec_->LoadFunctions({"/ddd"});
ASSERT_EQ(err.Code(), YR::Libruntime::ErrorCode::ERR_USER_CODE_LOAD);
fs::path currentPath = fs::current_path();
err = exec_->LoadFunctions({currentPath.string()});
ASSERT_EQ(err.Code(), YR::Libruntime::ErrorCode::ERR_USER_CODE_LOAD);
err = exec_->LoadFunctions({(currentPath / "code_manager_test.cpp").string()});
ASSERT_EQ(err.Code(), YR::Libruntime::ErrorCode::ERR_USER_CODE_LOAD);
}
TEST_F(FunctionExecutorTest, ExecuteFunctionTest)
{
YR::Libruntime::ErrorInfo err;
YR::Libruntime::FunctionMeta function;
function.funcName = "funcName";
libruntime::InvokeType type = libruntime::InvokeType::CreateInstance;
std::vector<std::shared_ptr<YR::Libruntime::DataObject>> rawArgs;
std::vector<std::shared_ptr<YR::Libruntime::DataObject>> returnObjects;
returnObjects.push_back(std::make_shared<YR::Libruntime::DataObject>());
err = ExecuteFunction(function, type, rawArgs, returnObjects);
ASSERT_EQ(err.Code(), ErrorCode::ERR_USER_FUNCTION_EXCEPTION);
FunctionManager::Singleton().funcMap_["funcName"] =
[](const std::string &returnObjId,
const std::vector<msgpack::sbuffer> &rawBuffers) -> std::pair<std::shared_ptr<msgpack::sbuffer>, bool> {
throw std::exception();
};
err = exec_->ExecuteFunction(function, type, rawArgs, returnObjects);
ASSERT_EQ(err.Code(), ErrorCode::ERR_USER_FUNCTION_EXCEPTION);
FunctionManager::Singleton().funcMap_["funcName"] =
[](const std::string &returnObjId,
const std::vector<msgpack::sbuffer> &rawBuffers) -> std::pair<std::shared_ptr<msgpack::sbuffer>, bool> {
throw YR::Exception(ErrorCode::ERR_INCORRECT_INVOKE_USAGE, ModuleCode::RUNTIME_INVOKE,
"YR_INVOKE function is duplicated");
};
err = exec_->ExecuteFunction(function, type, rawArgs, returnObjects);
ASSERT_EQ(err.Code(), ErrorCode::ERR_USER_FUNCTION_EXCEPTION);
FunctionManager::Singleton().funcMap_["funcName"] =
[](const std::string &returnObjId,
const std::vector<msgpack::sbuffer> &rawBuffers) -> std::pair<std::shared_ptr<msgpack::sbuffer>, bool> {
std::string val = "hello";
std::shared_ptr<msgpack::sbuffer> data = std::make_shared<msgpack::sbuffer>(YR::internal::Serialize(val));
return std::make_pair(data, true);
};
err = exec_->ExecuteFunction(function, type, rawArgs, returnObjects);
ASSERT_EQ(err.Code(), ErrorCode::ERR_OK);
type = libruntime::InvokeType::InvokeFunctionStateless;
err = exec_->ExecuteFunction(function, type, rawArgs, returnObjects);
ASSERT_EQ(err.Code(), ErrorCode::ERR_OK);
type = libruntime::InvokeType::CreateInstanceStateless;
err = exec_->ExecuteFunction(function, type, rawArgs, returnObjects);
ASSERT_EQ(err.Code(), ErrorCode::ERR_OK);
type = libruntime::InvokeType::InvokeFunction;
err = exec_->ExecuteFunction(function, type, rawArgs, returnObjects);
ASSERT_EQ(err.Code(), ErrorCode::ERR_USER_FUNCTION_EXCEPTION);
FunctionManager::Singleton().memberFuncMap_["funcName"] =
[](const std::string &returnObjId, const msgpack::sbuffer &sbuffer,
const std::vector<msgpack::sbuffer> &rawBuffers) -> std::pair<std::shared_ptr<msgpack::sbuffer>, bool> {
throw std::exception();
};
err = exec_->ExecuteFunction(function, type, rawArgs, returnObjects);
ASSERT_EQ(err.Code(), ErrorCode::ERR_USER_FUNCTION_EXCEPTION);
FunctionManager::Singleton().memberFuncMap_["funcName"] =
[](const std::string &returnObjId, const msgpack::sbuffer &sbuffer,
const std::vector<msgpack::sbuffer> &rawBuffers) -> std::pair<std::shared_ptr<msgpack::sbuffer>, bool> {
throw YR::Exception(ErrorCode::ERR_INCORRECT_INVOKE_USAGE, ModuleCode::RUNTIME_INVOKE,
"YR_INVOKE function is duplicated");
};
err = exec_->ExecuteFunction(function, type, rawArgs, returnObjects);
ASSERT_EQ(err.Code(), ErrorCode::ERR_USER_FUNCTION_EXCEPTION);
FunctionManager::Singleton().memberFuncMap_["funcName"] =
[](const std::string &returnObjId, const msgpack::sbuffer &sbuffer,
const std::vector<msgpack::sbuffer> &rawBuffers) -> std::pair<std::shared_ptr<msgpack::sbuffer>, bool> {
std::string val = "hello";
std::shared_ptr<msgpack::sbuffer> data = std::make_shared<msgpack::sbuffer>(YR::internal::Serialize(val));
return std::make_pair(data, true);
};
err = exec_->ExecuteFunction(function, type, rawArgs, returnObjects);
ASSERT_EQ(err.Code(), ErrorCode::ERR_OK);
}
class TestCounter {
public:
static TestCounter *FactoryCreate()
{
return new TestCounter();
}
int Get()
{
return count;
}
void ShutDownThrow(uint64_t gracePeriodSecond)
{
throw std::exception();
}
void ShutDownThrow2(uint64_t gracePeriodSecond)
{
throw YR::Exception("msg");
}
void ShutDown(uint64_t gracePeriodSecond)
{
return;
}
int count = 0;
MSGPACK_DEFINE(count);
};
TEST_F(FunctionExecutorTest, ExecuteShutdownFunctionTest)
{
YR::Libruntime::ErrorInfo err;
YR::Libruntime::FunctionMeta function;
err = ExecuteShutdownFunction(1);
ASSERT_EQ(err.Code(), ErrorCode::ERR_USER_FUNCTION_EXCEPTION) << err.Msg();
function.funcName = "&TestCounter::FactoryCreate";
function.className = "TestCounter";
libruntime::InvokeType type = libruntime::InvokeType::CreateInstance;
std::vector<std::shared_ptr<YR::Libruntime::DataObject>> rawArgs;
std::vector<std::shared_ptr<YR::Libruntime::DataObject>> returnObjects;
returnObjects.push_back(std::make_shared<YR::Libruntime::DataObject>());
FunctionManager::Singleton().RegisterInvokeFunction("&TestCounter::FactoryCreate", &TestCounter::FactoryCreate);
err = exec_->ExecuteFunction(function, type, rawArgs, returnObjects);
ASSERT_EQ(err.Code(), ErrorCode::ERR_OK) << err.Msg();
err = exec_->ExecuteShutdownFunction(1);
ASSERT_EQ(err.Code(), ErrorCode::ERR_OK) << err.Msg();
FunctionManager::Singleton().RegisterShutdownFunctions("&TestCounter::ShutDownThrow", &TestCounter::ShutDownThrow);
err = exec_->ExecuteShutdownFunction(1);
ASSERT_EQ(err.Code(), ErrorCode::ERR_USER_FUNCTION_EXCEPTION) << err.Msg();
FunctionManager::Singleton().RegisterShutdownFunctions("&TestCounter::ShutDownThrow2",
&TestCounter::ShutDownThrow2);
err = exec_->ExecuteShutdownFunction(1);
ASSERT_EQ(err.Code(), ErrorCode::ERR_USER_FUNCTION_EXCEPTION) << err.Msg();
FunctionManager::Singleton().shutdownCallerMap_.clear();
FunctionManager::Singleton().RegisterShutdownFunctions("&TestCounter::ShutDown", &TestCounter::ShutDown);
err = exec_->ExecuteShutdownFunction(1);
ASSERT_EQ(err.Code(), ErrorCode::ERR_OK) << err.Msg();
}
TEST_F(FunctionExecutorTest, CheckpointRecoverTest)
{
YR::Libruntime::ErrorInfo err;
YR::Libruntime::FunctionMeta function;
function.funcName = "&TestCounter::FactoryCreate";
function.className = "TestCounter";
libruntime::InvokeType type = libruntime::InvokeType::CreateInstance;
std::vector<std::shared_ptr<YR::Libruntime::DataObject>> rawArgs;
std::vector<std::shared_ptr<YR::Libruntime::DataObject>> returnObjects;
returnObjects.push_back(std::make_shared<YR::Libruntime::DataObject>());
FunctionManager::Singleton().RegisterInvokeFunction("&TestCounter::FactoryCreate", &TestCounter::FactoryCreate);
FunctionManager::Singleton().RegisterInvokeFunction("&TestCounter::Get", &TestCounter::Get);
err = exec_->ExecuteFunction(function, type, rawArgs, returnObjects);
ASSERT_EQ(err.Code(), ErrorCode::ERR_OK) << err.Msg();
std::shared_ptr<YR::Libruntime::Buffer> emptyData = std::make_shared<YR::Libruntime::NativeBuffer>(0);
err = Recover(emptyData);
ASSERT_EQ(err.Code(), ErrorCode::ERR_OK) << err.Msg();
std::shared_ptr<YR::Libruntime::Buffer> data;
err = exec_->Checkpoint("instanceid", data);
ASSERT_EQ(err.Code(), ErrorCode::ERR_OK) << err.Msg();
err = exec_->Recover(data);
ASSERT_EQ(err.Code(), ErrorCode::ERR_OK) << err.Msg();
err = Signal(10, nullptr);
ASSERT_EQ(err.Code(), ErrorCode::ERR_OK) << err.Msg();
}
TEST_F(FunctionExecutorTest, PosixExecutorTest)
{
std::shared_ptr<Executor> posixExecutor = std::make_shared<PosixExecutor>();
auto err = posixExecutor->LoadFunctions({""});
ASSERT_EQ(err.Code(), ErrorCode::ERR_PARAM_INVALID) << err.Msg();
YR::Libruntime::FunctionMeta function;
function.funcName = "funcName";
libruntime::InvokeType type = libruntime::InvokeType::CreateInstance;
std::vector<std::shared_ptr<YR::Libruntime::DataObject>> rawArgs;
std::vector<std::shared_ptr<YR::Libruntime::DataObject>> returnObjects;
returnObjects.push_back(std::make_shared<YR::Libruntime::DataObject>());
err = posixExecutor->ExecuteFunction(function, type, rawArgs, returnObjects);
ASSERT_EQ(err.Code(), ErrorCode::ERR_PARAM_INVALID) << err.Msg();
std::shared_ptr<YR::Libruntime::Buffer> data;
err = posixExecutor->Checkpoint("instanceId", data);
ASSERT_EQ(err.Code(), ErrorCode::ERR_PARAM_INVALID) << err.Msg();
err = posixExecutor->Recover(data);
ASSERT_EQ(err.Code(), ErrorCode::ERR_PARAM_INVALID) << err.Msg();
err = posixExecutor->ExecuteShutdownFunction(100);
ASSERT_EQ(err.Code(), ErrorCode::ERR_PARAM_INVALID) << err.Msg();
err = posixExecutor->Signal(1, data);
ASSERT_EQ(err.Code(), ErrorCode::ERR_PARAM_INVALID) << err.Msg();
}
}
}