/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * 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.
 */

#include "appspawn_test_cmder.h"

#include <cerrno>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <fcntl.h>
#include <string>
#include <termios.h>
#include <unistd.h>

#include <sys/stat.h>

#include "appspawn.h"
#include "appspawn_msg.h"
#include "appspawn_utils.h"
#include "cJSON.h"
#include "command_lexer.h"
#include "json_utils.h"
#include "securec.h"
#include "thread_manager.h"

#define MAX_THREAD 10
#define MAX_SEND 200
#define PTY_PATH_SIZE 128

namespace OHOS {
namespace AppSpawnModuleTest {
static const std::string g_defaultAppInfo = "{ \
    \"msg-type\": \"MSG_APP_SPAWN\", \
    \"msg-flags\": [1, 2 ], \
    \"process-name\" : \"com.example.myapplication\", \
    \"dac-info\" : { \
            \"uid\" : 20010043, \
            \"gid\" : 20010043,\
            \"gid-table\" : [],\
            \"user-name\" : \"\" \
    },\
    \"access-token\" : {\
            \"accessTokenIdEx\" : 537854093\
    },\
    \"permission\" : [\
            \"ohos.permission.MANAGE_PRIVATE_PHOTOS\",\
            \"ohos.permission.ACTIVATE_THEME_PACKAGE\"\
    ],\
    \"internet-permission\" : {\
            \"set-allow-internet\" : 0,\
            \"allow-internet\" : 0\
    },\
    \"bundle-info\" : {\
            \"bundle-index\" : 0,\
            \"bundle-name\" : \"com.example.myapplication\" \
    },\
    \"owner-id\" : \"\",\
    \"render-cmd\" : \"1234567890\",\
    \"domain-info\" : {\
            \"hap-flags\" : 0,\
            \"apl\" : \"system_core\"\
    },\
    \"ext-info\" : [\
            {\
                    \"name\" : \"test\",\
                    \"value\" : \"4444444444444444444\" \
            } \
    ]\
}";

static const char *APPSPAWN_TEST_USAGE = "usage: AppSpawnTest <options> \n"
    "options list:\n"
    "  --help                   list available commands\n"
    "  --file xx                file path with app info\n"
    "  --thread xx              use multi-thread to send message\n"
    "  --type xx                send msg type \n"
    "  --pid xx                 render terminate pid\n"
    "  --mode nwebspawn         send message to nwebspawn service\n"
    "  --mode nativespawn       send message to nativespawn service\n";

int AppSpawnTestCommander::ProcessArgs(int argc, char *const argv[])
{
    int sendMsg = 0;
    msgType_ = MAX_TYPE_INVALID;
    for (int32_t i = 0; i < argc; i++) {
        if (argv[i] == nullptr) {
            continue;
        }
        if (strcmp(argv[i], "--file") == 0 && ((i + 1) < argc)) {  // test file
            i++;
            testFileName_ = argv[i];
            sendMsg = 1;
        } else if (strcmp(argv[i], "--thread") == 0 && ((i + 1) < argc)) {  // use thread
            i++;
            threadCount_ = atoi(argv[i]);
            if (threadCount_ > MAX_THREAD) {
                threadCount_ = MAX_THREAD;
            }
            sendMsg = 1;
        } else if (strcmp(argv[i], "--mode") == 0 && ((i + 1) < argc)) {
            i++;
            if (strcmp(argv[i], "nwebspawn") == 0) {
                appSpawn_ = 0;
            } else if (strcmp(argv[i], "nativespawn") == 0) {
                appSpawn_ = 2; // 2 is nwebspawn
            } else {
                appSpawn_ = 1;
            }
            sendMsg = 1;
        } else if (strcmp(argv[i], "--type") == 0 && ((i + 1) < argc)) {
            i++;
            msgType_ = atoi(argv[i]);
            sendMsg = 1;
        } else if (strcmp(argv[i], "--pid") == 0 && ((i + 1) < argc)) {
            i++;
            msgType_ = MSG_GET_RENDER_TERMINATION_STATUS;
            terminatePid_ = atoi(argv[i]);
            sendMsg = 1;
        } else if (strcmp(argv[i], "--help") == 0) {
            printf("%s\n", APPSPAWN_TEST_USAGE);
            return 1;
        } else if (strcmp(argv[i], "--send") == 0 || strcmp(argv[i], "send") == 0) {
            sendMsg = 1;
        }
    }
    if (sendMsg == 0) {
        printf("%s\n", APPSPAWN_TEST_USAGE);
        return 1;
    }
    return 0;
}

uint32_t AppSpawnTestCommander::GetUint32ArrayFromJson(const cJSON *json,
    const char *name, uint32_t dataArray[], uint32_t maxCount)
{
    APPSPAWN_CHECK(json != NULL, return 0, "Invalid json");
    APPSPAWN_CHECK(name != NULL, return 0, "Invalid name");
    APPSPAWN_CHECK(dataArray != NULL, return 0, "Invalid dataArray");
    APPSPAWN_CHECK(cJSON_IsObject(json), return 0, "json is not object.");
    cJSON *array = cJSON_GetObjectItemCaseSensitive(json, name);
    APPSPAWN_CHECK_ONLY_EXPER(array != NULL, return 0);
    APPSPAWN_CHECK(cJSON_IsArray(array), return 0, "json is not object.");

    uint32_t count = 0;
    uint32_t arrayLen = cJSON_GetArraySize(array);
    for (int i = 0; i < arrayLen; i++) {
        cJSON *item = cJSON_GetArrayItem(array, i);
        uint32_t value = (uint32_t)cJSON_GetNumberValue(item);
        if (count < maxCount) {
            dataArray[count++] = value;
        }
    }
    return count;
}

int AppSpawnTestCommander::AddBundleInfoFromJson(const cJSON *appInfoConfig, AppSpawnReqMsgHandle reqHandle)
{
    cJSON *config = cJSON_GetObjectItemCaseSensitive(appInfoConfig, "bundle-info");
    APPSPAWN_CHECK_ONLY_EXPER(config, return 0);

    uint32_t bundleIndex = GetIntValueFromJsonObj(config, "bundle-index", 0);
    char *bundleName = GetStringFromJsonObj(config, "bundle-name");
    int ret = AppSpawnReqMsgSetBundleInfo(reqHandle, bundleIndex, bundleName);
    APPSPAWN_CHECK(ret == 0, return ret, "Failed to add bundle info req %{public}s", bundleName);
    return 0;
}

int AppSpawnTestCommander::AddDacInfoFromJson(const cJSON *appInfoConfig, AppSpawnReqMsgHandle reqHandle)
{
    cJSON *config = cJSON_GetObjectItemCaseSensitive(appInfoConfig, "dac-info");
    APPSPAWN_CHECK_ONLY_EXPER(config, return 0);

    AppDacInfo info = {};
    info.uid = GetIntValueFromJsonObj(config, "uid", 0);
    info.gid = GetIntValueFromJsonObj(config, "gid", 0);
    info.gidCount = GetUint32ArrayFromJson(config, "gid-table", info.gidTable, APP_MAX_GIDS);
    char *userName = GetStringFromJsonObj(config, "user-name");
    if (userName != nullptr) {
        int ret = strcpy_s(info.userName, sizeof(info.userName), userName);
        APPSPAWN_CHECK(ret == 0, return ret, "Failed to add userName info req %{public}s", userName);
    }
    return AppSpawnReqMsgSetAppDacInfo(reqHandle, &info);
}

int AppSpawnTestCommander::AddInternetPermissionInfoFromJson(
    const cJSON *appInfoConfig, AppSpawnReqMsgHandle reqHandle)
{
    cJSON *config = cJSON_GetObjectItemCaseSensitive(appInfoConfig, "internet-permission");
    APPSPAWN_CHECK_ONLY_EXPER(config, return 0);

    uint8_t setAllowInternet = GetIntValueFromJsonObj(config, "set-allow-internet", 0);
    uint8_t allowInternet = GetIntValueFromJsonObj(config, "allow-internet", 0);
    return AppSpawnReqMsgSetAppInternetPermissionInfo(reqHandle, allowInternet, setAllowInternet);
}

int AppSpawnTestCommander::AddAccessTokenFromJson(const cJSON *appInfoConfig, AppSpawnReqMsgHandle reqHandle)
{
    cJSON *config = cJSON_GetObjectItemCaseSensitive(appInfoConfig, "access-token");
    APPSPAWN_CHECK_ONLY_EXPER(config, return 0);

    uint64_t accessTokenIdEx = GetIntValueFromJsonObj(config, "accessTokenIdEx", 0);
    return AppSpawnReqMsgSetAppAccessToken(reqHandle, accessTokenIdEx);
}

int AppSpawnTestCommander::AddDomainInfoFromJson(const cJSON *appInfoConfig, AppSpawnReqMsgHandle reqHandle)
{
    cJSON *config = cJSON_GetObjectItemCaseSensitive(appInfoConfig, "domain-info");
    APPSPAWN_CHECK_ONLY_EXPER(config, return 0);

    uint32_t hapFlags = GetIntValueFromJsonObj(config, "hap-flags", 0);
    char *apl = GetStringFromJsonObj(config, "apl");
    int ret = AppSpawnReqMsgSetAppDomainInfo(reqHandle, hapFlags, apl);
    APPSPAWN_CHECK(ret == 0, return ret, "Failed to domain info");
    return 0;
}

int AppSpawnTestCommander::AddCheckPointInfoFromJson(const cJSON *appInfoConfig, AppSpawnReqMsgHandle reqHandle)
{
    cJSON *config = cJSON_GetObjectItemCaseSensitive(appInfoConfig, "checkpoint-info");
    APPSPAWN_CHECK_ONLY_EXPER(config, return 0);

    pid_t imgPid = static_cast<pid_t>(GetIntValueFromJsonObj(config, "img-pid", 0));
    uint64_t checkPointId = static_cast<uint64_t>(GetIntValueFromJsonObj(config, "checkpoint-id", 0));
    char *imgName = GetStringFromJsonObj(config, "img-name");
    int ret = AppSpawnReqMsgSetCheckpointInfo(reqHandle, imgPid, checkPointId, imgName);
    APPSPAWN_CHECK(ret == 0, return ret, "Failed to set checkpoint info");
    return 0;
}

int AppSpawnTestCommander::AddExtTlv(const cJSON *appInfoConfig, AppSpawnReqMsgHandle reqHandle)
{
    cJSON *configs = cJSON_GetObjectItemCaseSensitive(appInfoConfig, "ext-info");
    APPSPAWN_CHECK_ONLY_EXPER(configs != nullptr, return 0);

    int ret = 0;
    uint32_t count = cJSON_GetArraySize(configs);
    for (unsigned int j = 0; j < count; j++) {
        cJSON *config = cJSON_GetArrayItem(configs, j);

        char *name = GetStringFromJsonObj(config, "name");
        char *value = GetStringFromJsonObj(config, "value");
        APPSPAWN_LOGV("ext-info %{public}s %{public}s", name, value);
        ret = AppSpawnReqMsgAddStringInfo(reqHandle, name, value);
        APPSPAWN_CHECK(ret == 0, return ret, "Failed to add ext name %{public}s", name);
    }

    // 添加一个二进制的扩展元素
    AppDacInfo dacInfo{};
    dacInfo.uid = 101;          // 101 test data
    dacInfo.gid = 101;          // 101 test data
    dacInfo.gidTable[0] = 101;  // 101 test data
    dacInfo.gidCount = 1;
    (void)strcpy_s(dacInfo.userName, sizeof(dacInfo.userName), processName_.c_str());
    ret = AppSpawnReqMsgAddExtInfo(reqHandle,
        "app-dac-info", reinterpret_cast<uint8_t *>(&dacInfo), sizeof(dacInfo));
    APPSPAWN_CHECK(ret == 0, return ret, "Failed to add ext name app-info");
    return ret;
}

int AppSpawnTestCommander::BuildMsgFromJson(const cJSON *appInfoConfig, AppSpawnReqMsgHandle reqHandle)
{
    int ret = AddBundleInfoFromJson(appInfoConfig, reqHandle);
    APPSPAWN_CHECK(ret == 0, return ret, "Failed to add dac %{public}s", processName_.c_str());

    ret = AddDomainInfoFromJson(appInfoConfig, reqHandle);
    APPSPAWN_CHECK(ret == 0, return ret, "Failed to add dac %{public}s", processName_.c_str());

    ret = AddDacInfoFromJson(appInfoConfig, reqHandle);
    APPSPAWN_CHECK(ret == 0, return ret, "Failed to add dac %{public}s", processName_.c_str());

    ret = AddAccessTokenFromJson(appInfoConfig, reqHandle);
    APPSPAWN_CHECK(ret == 0, return ret, "Failed to add access token %{public}s", processName_.c_str());

    cJSON *obj = cJSON_GetObjectItemCaseSensitive(appInfoConfig, "permission");
    if (obj != nullptr && cJSON_IsArray(obj)) {
        int count = cJSON_GetArraySize(obj);
        for (int i = 0; i < count; i++) {
            char *value = cJSON_GetStringValue(cJSON_GetArrayItem(obj, i));
            APPSPAWN_LOGV("permission %{public}s ", value);
            ret = AppSpawnReqMsgAddPermission(reqHandle, value);
            APPSPAWN_CHECK(ret == 0, return ret, "Failed to permission %{public}s", value);
        }
    }

    ret = AddInternetPermissionInfoFromJson(appInfoConfig, reqHandle);
    APPSPAWN_CHECK(ret == 0, return ret, "Failed to internet info %{public}s", processName_.c_str());

    ret = AddCheckPointInfoFromJson(appInfoConfig, reqHandle);
    APPSPAWN_CHECK(ret == 0, return ret, "Failed to checkpoint info %{public}s", processName_.c_str());

    std::string ownerId = GetStringFromJsonObj(appInfoConfig, "owner-id");
    if (!ownerId.empty()) {
        ret = AppSpawnReqMsgSetAppOwnerId(reqHandle, ownerId.c_str());
        APPSPAWN_CHECK(ret == 0, return ret, "Failed to ownerid %{public}s", processName_.c_str());
    }

    std::string renderCmd = GetStringFromJsonObj(appInfoConfig, "render-cmd");
    if (!renderCmd.empty()) {
        ret = AppSpawnReqMsgAddStringInfo(reqHandle, MSG_EXT_NAME_RENDER_CMD, renderCmd.c_str());
        APPSPAWN_CHECK(ret == 0, return -1, "Failed to add renderCmd %{public}s", renderCmd.c_str());
    }
    return AddExtTlv(appInfoConfig, reqHandle);
}

int AppSpawnTestCommander::CreateOtherMsg(AppSpawnReqMsgHandle &reqHandle, pid_t pid)
{
    if (msgType_ == MSG_GET_RENDER_TERMINATION_STATUS) {
        int ret = AppSpawnTerminateMsgCreate(pid, &reqHandle);
        APPSPAWN_CHECK(ret == 0, return ret, "Failed to termination message req %{public}s", processName_.c_str());
    }
    if (msgType_ == MSG_DUMP) {
        int ret = AppSpawnReqMsgCreate(static_cast<AppSpawnMsgType>(msgType_), processName_.c_str(), &reqHandle);
        APPSPAWN_CHECK(ret == 0, return ret, "Failed to dump req %{public}s", processName_.c_str());
        ret = AppSpawnReqMsgAddStringInfo(reqHandle, "pty-name", ptyName_.c_str());
        APPSPAWN_CHECK(ret == 0, return -1, "Failed to add ptyName_ %{public}s", ptyName_.c_str());
    }
    return 0;
}

static uint32_t GetMsgTypeFromJson(const cJSON *json)
{
    const char *msgType = GetStringFromJsonObj(json, "msg-type");
    if (msgType == nullptr) {
        return MSG_APP_SPAWN;
    }
    if (strcmp(msgType, "MSG_SPAWN_NATIVE_PROCESS") == 0) {
        return MSG_SPAWN_NATIVE_PROCESS;
    }
    if (strcmp(msgType, "MSG_SPAWN_IMAGE_PROCESS") == 0) {
        return MSG_SPAWN_IMAGE_PROCESS;
    }
    if (strcmp(msgType, "MSG_SPAWN_WORKER_PROCESS") == 0) {
        return MSG_SPAWN_WORKER_PROCESS;
    }
    if (strcmp(msgType, "MSG_GET_RENDER_TERMINATION_STATUS") == 0) {
        return MSG_GET_RENDER_TERMINATION_STATUS;
    }
    if (strcmp(msgType, "MSG_DUMP") == 0) {
        return MSG_DUMP;
    }
    return MSG_APP_SPAWN;
}

int AppSpawnTestCommander::CreateMsg(AppSpawnReqMsgHandle &reqHandle,
    const char *defaultConfig, uint32_t defMsgType)
{
    int ret = APPSPAWN_SYSTEM_ERROR;
    if (clientHandle_ == NULL) {
        ret = AppSpawnClientInit(appSpawn_ == 1 ? APPSPAWN_SERVER_NAME : (appSpawn_  == 2 ? NATIVESPAWN_SERVER_NAME :
            (appSpawn_ == 3 ? HYBRIDSPAWN_SERVER_NAME : NWEBSPAWN_SERVER_NAME)), &clientHandle_);
    }
    reqHandle = INVALID_REQ_HANDLE;
    if (appInfoConfig_) {
        cJSON_Delete(appInfoConfig_);
        appInfoConfig_ = nullptr;
    }
    if (!testFileName_.empty()) {
        appInfoConfig_ = GetJsonObjFromFile(testFileName_.c_str());
        if (appInfoConfig_ == nullptr) {
            printf("Failed to load file %s, so use default info \n", testFileName_.c_str());
        }
    }
    if (appInfoConfig_ == nullptr) {
        appInfoConfig_ = cJSON_Parse(defaultConfig);
    }
    if (appInfoConfig_ == nullptr) {
        printf("Invalid app info \n");
        return APPSPAWN_SYSTEM_ERROR;
    }
    processName_ = GetStringFromJsonObj(appInfoConfig_, "process-name");
    if (processName_.empty()) {
        processName_ = "com.example.myapplication";
    }
    msgType_ = (msgType_ == MAX_TYPE_INVALID) ? GetMsgTypeFromJson(appInfoConfig_) : msgType_;
    msgType_ = (defMsgType != MAX_TYPE_INVALID) ? defMsgType : msgType_;
    if (msgType_ == MSG_DUMP) {
        return CreateOtherMsg(reqHandle, 0);
    } else if (msgType_ == MSG_GET_RENDER_TERMINATION_STATUS) {
        pid_t pid = GetIntValueFromJsonObj(appInfoConfig_, "pid", 0);
        return CreateOtherMsg(reqHandle, pid);
    }
    ret = AppSpawnReqMsgCreate(static_cast<AppSpawnMsgType>(msgType_), processName_.c_str(), &reqHandle);
    APPSPAWN_CHECK(ret == 0, return ret, "Failed to create req %{public}s", processName_.c_str());

    uint32_t msgFlags[64] = {};  // 64
    uint32_t count = GetUint32ArrayFromJson(appInfoConfig_, "msg-flags", msgFlags, ARRAY_LENGTH(msgFlags));
    for (uint32_t j = 0; j < count; j++) {
        (void)AppSpawnReqMsgSetAppFlag(reqHandle, static_cast<AppFlagsIndex>(msgFlags[j]));
    }
    (void)AppSpawnReqMsgSetAppFlag(reqHandle, APP_FLAGS_IGNORE_SANDBOX);
    ret = BuildMsgFromJson(appInfoConfig_, reqHandle);
    APPSPAWN_CHECK(ret == 0, AppSpawnReqMsgFree(reqHandle);
        return ret, "Failed to build req %{public}s", processName_.c_str());
    return ret;
}

static void PrintSpawnResult(uint32_t msgType, const std::string &processName,
    const char *server, int ret, const AppSpawnResult &result)
{
    struct SpawnLabel {
        uint32_t msgType;
        const char *label;
    };
    static const SpawnLabel labels[] = {
        {MSG_APP_SPAWN,             "app"},
        {MSG_SPAWN_NATIVE_PROCESS,  "native app"},
        {MSG_SPAWN_IMAGE_PROCESS,   "image process"},
        {MSG_SPAWN_WORKER_PROCESS,  "worker process"},
    };
    for (const auto &item : labels) {
        if (msgType == item.msgType) {
            if (result.result == 0) {
                printf("Spawn %s %s success, pid %d \n", item.label, processName.c_str(), result.pid);
            } else {
                printf("Spawn %s %s fail, result 0x%x \n", item.label, processName.c_str(), result.result);
            }
            return;
        }
    }
    if (msgType == MSG_GET_RENDER_TERMINATION_STATUS) {
        printf("Terminate app %s success, pid %d status 0x%x \n",
            processName.c_str(), result.pid, result.result);
    } else {
        printf("Dump server %s result %d \n", server, ret);
    }
}

int AppSpawnTestCommander::SendMsg()
{
    const char *server = appSpawn_ == 1 ? APPSPAWN_SERVER_NAME : (appSpawn_ == 2 ? NATIVESPAWN_SERVER_NAME :
        (appSpawn_ == 3 ? HYBRIDSPAWN_SERVER_NAME : NWEBSPAWN_SERVER_NAME));
    printf("Send msg to server '%s' \n", server);
    AppSpawnReqMsgHandle reqHandle = INVALID_REQ_HANDLE;
    int ret = 0;
    if (msgType_ == MSG_DUMP) {
        while (!dumpFlags) {
            usleep(20000);  // 20000
        }
        ret = CreateOtherMsg(reqHandle, 0);
    } else if (msgType_ == MSG_GET_RENDER_TERMINATION_STATUS) {
        ret = CreateOtherMsg(reqHandle, terminatePid_);
    } else {
        ret = CreateMsg(reqHandle, g_defaultAppInfo.c_str());
    }
    AppSpawnResult result = {ret, 0};
    if (ret == 0) {
        ret = AppSpawnClientSendMsg(clientHandle_, reqHandle, &result);
    }
    PrintSpawnResult(msgType_, processName_, server, ret, result);
    msgType_ = MAX_TYPE_INVALID;
    terminatePid_ = 0;
    printf("Please input cmd: \n");
    return 0;
}

int AppSpawnTestCommander::StartSendMsg()
{
    int ret = 0;
    printf("Start send msg thread count %d file name %s \n", threadCount_, testFileName_.c_str());
    if (threadCount_ == 1) {
        SendMsg();
    } else {
        ThreadTaskHandle taskHandle = 0;
        ret = ThreadMgrAddTask(threadMgr_, &taskHandle);
        APPSPAWN_CHECK(ret == 0, return 0, "Failed to add task ");
        for (uint32_t index = 0; index < threadCount_; index++) {
            ThreadMgrAddExecutor(threadMgr_, taskHandle, TaskExecutorProc, reinterpret_cast<ThreadContext *>(this));
        }
        TaskSyncExecute(threadMgr_, taskHandle);
    }
    return 0;
}

void AppSpawnTestCommander::TaskExecutorProc(ThreadTaskHandle handle, const ThreadContext *context)
{
    AppSpawnTestCommander *testCmder = AppSpawnTestCommander::ConvertTo(context);
    testCmder->SendMsg();
}

void AppSpawnTestCommander::SendTaskFinish(ThreadTaskHandle handle, const ThreadContext *context)
{
    APPSPAWN_LOGV("SendTaskFinish %{public}u \n", handle);
}

static std::vector<std::string> g_args;
static int HandleSplitString(const char *str, void *context)
{
    APPSPAWN_LOGV("HandleSplitString %{public}s ", str);
    std::string value = str;
    g_args.push_back(value);
    return 0;
}

int AppSpawnTestCommander::ProcessInputCmd(std::string &cmd)
{
    g_args.clear();
    int ret = StringSplit(cmd.c_str(), " ", nullptr, HandleSplitString);
    std::vector<char *> options;
    for (const auto &arg : g_args) {
        if (!arg.empty()) {
            options.push_back(const_cast<char *>(arg.c_str()));
        }
    }
    (void)ProcessArgs(options.size(), options.data());
    StartSendMsg();
    return ret;
}

void AppSpawnTestCommander::InputThread(ThreadTaskHandle handle, const ThreadContext *context)
{
    AppSpawnTestCommander *testCmder = AppSpawnTestCommander::ConvertTo(context);
    char buffer[1024] = {0};  // 1024 test buffer max len
    fd_set fds;
    printf("Please input cmd: \n");
    while (1) {
        FD_ZERO(&fds);
        FD_SET(STDIN_FILENO, &fds);
        int ret = select(STDIN_FILENO + 1, &fds, nullptr, nullptr, nullptr);
        if (ret <= 0) {
            if (testCmder->exit_) {
                break;
            }
            continue;
        }
        ssize_t rlen = read(STDIN_FILENO, buffer, sizeof(buffer) - 1);
        if (rlen <= 1) {
            continue;
        }
        buffer[rlen - 1] = 0;
        printf("Recv command: '%s' \n", buffer);
        if (strncmp("quit", buffer, strlen("quit")) == 0) {
            testCmder->exit_ = 1;
            break;
        }
        if (strncmp("send", buffer, 4) == 0) {  // 4 strlen("send")
            std::string cmd(buffer);
            testCmder->ProcessInputCmd(cmd);
            printf("Please input cmd: \n");
        }
    }
}

void AppSpawnTestCommander::DumpThread(ThreadTaskHandle handle, const ThreadContext *context)
{
    AppSpawnTestCommander *testCmder = AppSpawnTestCommander::ConvertTo(context);
    printf("Start dump thread \n");
    char buffer[10240] = {0};  // 1024 test buffer max len
    fd_set fds;
    while (1) {
        testCmder->dumpFlags = 1;
        FD_ZERO(&fds);
        FD_SET(testCmder->ptyFd_, &fds);
        int ret = select(testCmder->ptyFd_ + 1, &fds, nullptr, nullptr, nullptr);
        if (ret <= 0) {
            if (testCmder->exit_) {
                break;
            }
            continue;
        }
        if (!FD_ISSET(testCmder->ptyFd_, &fds)) {
            continue;
        }
        ssize_t rlen = read(testCmder->ptyFd_, buffer, sizeof(buffer) - 1);
        while (rlen > 0) {
            buffer[rlen] = '\0';
            printf("%s", buffer);
            fflush(stdout);
            rlen = read(testCmder->ptyFd_, buffer, sizeof(buffer) - 1);
        }
    }
}

int AppSpawnTestCommander::Run()
{
    int ret = 0;
    const char *name = appSpawn_ == 1 ? APPSPAWN_SERVER_NAME : (appSpawn_ == 2 ? NATIVESPAWN_SERVER_NAME :
        (appSpawn_ == 3 ? HYBRIDSPAWN_SERVER_NAME : NWEBSPAWN_SERVER_NAME));
    if (clientHandle_ == NULL) {
        ret = AppSpawnClientInit(name, &clientHandle_);
        APPSPAWN_CHECK(ret == 0, return -1, "Failed to create client %{public}s", name);
    }

    InitPtyInterface();

    ret = CreateThreadMgr(5, &threadMgr_);  // 5 max thread
    APPSPAWN_CHECK(ret == 0, return -1, "Failed to create thread manager");

    ret = ThreadMgrAddTask(threadMgr_, &inputHandle_);
    APPSPAWN_CHECK(ret == 0, return 0, "Failed to add task for thread ");
    ThreadMgrAddExecutor(threadMgr_, inputHandle_, InputThread, this);
    TaskExecute(threadMgr_, inputHandle_, SendTaskFinish, this);

    ret = ThreadMgrAddTask(threadMgr_, &dumpHandle_);
    APPSPAWN_CHECK(ret == 0, return 0, "Failed to add task for thread ");
    ThreadMgrAddExecutor(threadMgr_, dumpHandle_, DumpThread, this);
    TaskExecute(threadMgr_, dumpHandle_, SendTaskFinish, this);

    StartSendMsg();

    APPSPAWN_LOGV("Finish send msg \n");
    while (!exit_) {
        usleep(200000);  // 200000 200ms
    }
    ThreadMgrCancelTask(threadMgr_, inputHandle_);
    ThreadMgrCancelTask(threadMgr_, dumpHandle_);
    DestroyThreadMgr(threadMgr_);
    threadMgr_ = nullptr;
    inputHandle_ = 0;
    dumpHandle_ = 0;
    AppSpawnClientDestroy(clientHandle_);
    clientHandle_ = nullptr;
    return 0;
}

int AppSpawnTestCommander::InitPtyInterface()
{
    // open master pty and get slave pty
    int pfd = open("/dev/ptmx", O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK);
    APPSPAWN_CHECK(pfd >= 0, return -1, "Failed open pty err=%{public}d", errno);
    APPSPAWN_CHECK(grantpt(pfd) >= 0, close(pfd); return -1, "Failed to call grantpt");
    APPSPAWN_CHECK(unlockpt(pfd) >= 0, close(pfd); return -1, "Failed to call unlockpt");
    char ptsbuffer[PTY_PATH_SIZE] = {0};
    int ret = ptsname_r(pfd, ptsbuffer, sizeof(ptsbuffer));
    APPSPAWN_CHECK(ret >= 0, close(pfd);
        return -1, "Failed to get pts name err=%{public}d", errno);
    APPSPAWN_LOGI("ptsbuffer is %{public}s", ptsbuffer);
    APPSPAWN_CHECK(chmod(ptsbuffer, S_IRWXU | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) == 0, close(pfd);
        return -1, "Failed to chmod %{public}s, err=%{public}d", ptsbuffer, errno);
    ptyFd_ = pfd;
    ptyName_ = std::string(ptsbuffer);
    return 0;
}
}  // namespace AppSpawnModuleTest
}  // namespace OHOS