/*
 * Copyright (c) 2021-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 "test.h"

#include <chrono>
#include <thread>
#include <uv.h>

#include "event_runner.h"
#include "napi/native_api.h"
#include "napi/native_node_api.h"
#include "native_safe_async_work.h"
#include "securec.h"
#include "utils/log.h"

struct CallJsCbData_t {
    int32_t id = 0;
    bool secondaryThread = false;
    napi_threadsafe_function_call_mode blockOnFull = napi_tsfn_nonblocking;
};

struct FinalCbData_t {
    int32_t id = 0;
};

struct CallbackData {
    napi_threadsafe_function tsfn;
    napi_async_work work;
    napi_task_priority priority;
};

static constexpr int32_t SEND_DATA_TEST = 11;
static constexpr int32_t CALL_JS_CB_DATA_TEST_ID = 101;
static constexpr int32_t FINAL_CB_DATA_TEST_ID = 1001;
static constexpr int32_t SEND_DATAS_LENGTH = 10;
static constexpr int32_t THREAD_COUNT = 2;
static constexpr int32_t THREAD_COUNT_FOUR = 4;
static constexpr int32_t MAX_QUEUE_SIZE = 3;
static constexpr int32_t SUCCESS_COUNT_JS_FOUR = 4;
static constexpr int32_t INVALID_NAPI_THREAD_SAFE_PRIORITY = -1;
static constexpr int32_t FIRST_RECEIVER = 1;
static constexpr int32_t SECOND_RECEIVER = 2;
static constexpr int32_t THIRD_RECEIVER = 3;
static constexpr int32_t FOURTH_RECEIVER = 4;
static constexpr int32_t FIFTH_RECEIVER = 5;
static constexpr int32_t DATA_LENGTH = 40;
static constexpr int32_t THREAD_SIZE = 5;
static constexpr int32_t SECOND_THREAD_INDEX = 1;
static constexpr int32_t THIRD_THREAD_INDEX = 2;
static constexpr int32_t FOURTH_THREAD_INDEX = 3;
static constexpr int32_t FIFTH_THREAD_INDEX = 4;

static pid_t g_mainTid = 0;
static CallJsCbData_t g_jsData;
static CallJsCbData_t g_jsDataInternal;
static FinalCbData_t g_finalData;
static int32_t g_sendData = 0;
static uv_thread_t g_uvThread;
static uv_thread_t g_uvThreadTest5;
static uv_thread_t g_uvThreadTest6;
static uv_thread_t g_uvThreadTest7;
static uv_thread_t g_uvThreadSecondary;
static uv_thread_t g_uvTheads2;
static uv_thread_t g_uvTheads3;
static int32_t g_sendDatas[SEND_DATAS_LENGTH];
static int32_t  g_callSuccessCount = 0;
static int32_t  g_callSuccessCountJS = 0;
static int32_t  g_callSuccessCountJSFour = 0;
static int32_t  g_callDepth = 4;
bool acquireFlag = false;
static int32_t g_receiveCnt = 0;
static bool g_isTailA = false;
static bool g_isTailB = false;
static bool g_isCalled = false;
static std::vector<std::string> gTaskExecOrder;
constexpr const char* kTaskNameHighPrio = "HighPrioTask";
constexpr const char* kTaskNameNull = "Null";

static constexpr int INT_ONE = 1;
static constexpr int INT_TWO = 2;

static void TsFuncCallJs(napi_env env, napi_value tsfn_cb, void* context, void* data)
{
    HILOG_INFO("TsFuncCallJs called");

    EXPECT_EQ(gettid(), g_mainTid);

    // expect context equal
    EXPECT_EQ(((CallJsCbData_t*)context)->id, CALL_JS_CB_DATA_TEST_ID);

    // expect data equal
    int* pData = (int32_t*)data;
    EXPECT_EQ((*pData), SEND_DATA_TEST);
}

static void TsFuncCallJsWithNewCall(napi_env env, napi_value tsfn_cb, void* context, void* data)
{
    HILOG_INFO("TsFuncCallJsWithNewCall called");
    EXPECT_EQ(gettid(), g_mainTid);

    // expect context equal
    EXPECT_EQ(((CallJsCbData_t*)context)->id, CALL_JS_CB_DATA_TEST_ID);

    napi_threadsafe_function tsFunc = (napi_threadsafe_function)data;

    if (g_callDepth > 0) {
        g_callDepth--;
        auto status = napi_call_threadsafe_function(tsFunc, data, napi_tsfn_nonblocking);
        EXPECT_EQ(status, napi_ok);
        return;
    }
    if (g_callDepth == 0) {
        auto status = napi_release_threadsafe_function(tsFunc, napi_tsfn_release);
        EXPECT_EQ(status, napi_ok);
    }
}

static void TsFuncCallJsTwo(napi_env env, napi_value tsfn_cb, void* context, void* data)
{
    HILOG_INFO("TsFuncCallJsTwo called");
    TsFuncCallJs(env, tsfn_cb, context, data);
    g_callSuccessCountJS++;
}
static void TsFuncCallJsFour(napi_env env, napi_value tsfn_cb, void* context, void* data)
{
    HILOG_INFO("TsFuncCallJsFour called");

    TsFuncCallJs(env, tsfn_cb, context, data);
    g_callSuccessCountJSFour++;
}
static void TsFuncCallJsMulti(napi_env env,
                              napi_value tsfn_cb,
                              void* context,
                              void* data)
{
    HILOG_INFO("TsFuncCallJsMulti called");

    EXPECT_EQ(gettid(), g_mainTid);

    // expect context equal
    EXPECT_EQ(((CallJsCbData_t*)context)->id, CALL_JS_CB_DATA_TEST_ID);

    int* pData = ((int32_t*)data);

    HILOG_INFO("TsFuncCallJsMulti data %d", (*pData));
}

static void TsFuncFinal(napi_env env, void* finalizeData, void* hint)
{
    HILOG_INFO("TsFuncFinal called");

    // expect thread id equal
    EXPECT_EQ(gettid(), g_mainTid);

    // wait data source thread
    uv_thread_join(&g_uvThread);

    // expect context equal
    EXPECT_EQ(((CallJsCbData_t*)hint)->id, CALL_JS_CB_DATA_TEST_ID);

    // expect finalize data equal
    EXPECT_EQ(((FinalCbData_t*)finalizeData)->id, FINAL_CB_DATA_TEST_ID);
}
static void TsFuncFinalTest5(napi_env env, void* finalizeData, void* hint)
{
    HILOG_INFO("TsFuncFinalTest5 called");

    // expect thread id equal
    EXPECT_EQ(gettid(), g_mainTid);

    // wait data source thread
    uv_thread_join(&g_uvThreadTest5);

    // expect context equal
    EXPECT_EQ(((CallJsCbData_t*)hint)->id, CALL_JS_CB_DATA_TEST_ID);

    // expect finalize data equal
    EXPECT_EQ(((FinalCbData_t*)finalizeData)->id, FINAL_CB_DATA_TEST_ID);
}
static void TsFuncFinalTotal(napi_env env, void* finalizeData, void* hint)
{
    HILOG_INFO("TsFuncFinalTotal called");
    uv_thread_join(&g_uvThreadTest6);
    // when add thread,repair  g_callSuccessCountJS eq  SUCCESS_COUNT_JS_TWO
    EXPECT_EQ(g_callSuccessCountJS, SUCCESS_COUNT_JS_FOUR);
    HILOG_INFO("TsFuncFinalTotal end");
}
static void TsFuncFinalTotalFour(napi_env env, void* finalizeData, void* hint)
{
    HILOG_INFO("TsFuncFinalTotalFour called");
    uv_thread_join(&g_uvThreadTest7);
    EXPECT_EQ(g_callSuccessCountJSFour, SUCCESS_COUNT_JS_FOUR);
    HILOG_INFO("TsFuncFinalTotalFour end");
}
static void TsFuncFinalCallback(napi_env env, void* finalizeData, void* hint)
{
    HILOG_INFO("TsFuncFinalCallback called");
    EXPECT_EQ(g_callSuccessCountJSFour, SUCCESS_COUNT_JS_FOUR);
    HILOG_INFO("TsFuncFinalCallback end");
}

static void TsFuncFinalJoinThread(napi_env env, void* data, void* hint)
{
    HILOG_INFO("TsFuncFinalJoinThread called");

    uv_thread_t *uvThread = reinterpret_cast<uv_thread_t*>(data);
    CallJsCbData_t *jsData = reinterpret_cast<CallJsCbData_t*>(hint);

    uv_thread_join(uvThread);

    if (jsData->secondaryThread) {
        uv_thread_join(&g_uvThreadSecondary);
    }
}

static void TsFuncSecondaryThread(void* data)
{
    HILOG_INFO("TsFuncSecondaryThread called");

    // expect thread id not equal
    EXPECT_NE(gettid(), g_mainTid);

    napi_threadsafe_function func = (napi_threadsafe_function)data;

    auto status = napi_release_threadsafe_function(func, napi_tsfn_release);
    EXPECT_EQ(status, napi_ok);
}

static void TsFuncDataSourceThread(void* data)
{
    HILOG_INFO("TsFuncDataSourceThread called");

    // expect thread id not equal
    EXPECT_NE(gettid(), g_mainTid);

    napi_threadsafe_function func = (napi_threadsafe_function)data;
    napi_threadsafe_function_call_mode blockMode = napi_tsfn_nonblocking;
    void* context = nullptr;

    auto status = napi_get_threadsafe_function_context(func, &context);
    EXPECT_EQ(status, napi_ok);

    // expect context equal
    EXPECT_EQ(((CallJsCbData_t*)context)->id, CALL_JS_CB_DATA_TEST_ID);

    // set send data
    g_sendData = SEND_DATA_TEST;

    // As main thread has set initial_thread_count to 1 and only this one secondary thread,
    // so no need to call `napi_acquire_threadsafe_function()`.
    status = napi_call_threadsafe_function(func, &g_sendData, blockMode);
    EXPECT_EQ(status, napi_ok);

    status = napi_release_threadsafe_function(func, napi_tsfn_release);
    EXPECT_EQ(status, napi_ok);
}
static void TsFuncDataSourceThreadAbort(void* data)
{
    HILOG_INFO("TsFuncDataSourceThreadAbort called");

    // expect thread id not equal
    EXPECT_NE(gettid(), g_mainTid);

    napi_threadsafe_function func = (napi_threadsafe_function)data;
    napi_threadsafe_function_call_mode blockMode = napi_tsfn_nonblocking;
    void* context = nullptr;

    auto status = napi_get_threadsafe_function_context(func, &context);
    EXPECT_EQ(status, napi_ok);

    // expect context equal
    EXPECT_EQ(((CallJsCbData_t*)context)->id, CALL_JS_CB_DATA_TEST_ID);

    // set send data
    g_sendData = SEND_DATA_TEST;

    status = napi_call_threadsafe_function(func, &g_sendData, blockMode);
    EXPECT_EQ(status, napi_closing);
}

static void TsFuncDataSourceThreadCountTotal(void* data)
{
    HILOG_INFO("TsFuncDataSourceThreadCountTotal called");

    // expect thread id not equal
    EXPECT_NE(gettid(), g_mainTid);

    napi_threadsafe_function func = (napi_threadsafe_function)data;
    napi_threadsafe_function_call_mode blockMode = napi_tsfn_nonblocking;
    void* context = nullptr;

    auto status = napi_get_threadsafe_function_context(func, &context);
    EXPECT_EQ(status, napi_ok);

    // expect context equal
    EXPECT_EQ(((CallJsCbData_t*)context)->id, CALL_JS_CB_DATA_TEST_ID);
    // set send data
    g_sendData = SEND_DATA_TEST;
    if (acquireFlag) {
        std::cout<<"acquireFlag  is true"<<std::endl;
        status = napi_acquire_threadsafe_function(func);
        EXPECT_EQ(status, napi_ok);
    }
    status = napi_call_threadsafe_function(func, &g_sendData, blockMode);
    if (status == napi_ok) {
        g_callSuccessCount++;
    }
    status = napi_release_threadsafe_function(func, napi_tsfn_release);
}

static void TsFuncDataSourceThreadMulti(void* data)
{
    HILOG_INFO("TsFuncDataSourceThreadMulti called");

    // expect thread id not equal
    EXPECT_NE(gettid(), g_mainTid);

    napi_threadsafe_function func =  (napi_threadsafe_function)data;
    void* context = nullptr;

    auto status = napi_get_threadsafe_function_context(func, &context);
    EXPECT_EQ(status, napi_ok);
    CallJsCbData_t* jsData = nullptr;
    jsData = (CallJsCbData_t*)context;

    if (jsData->secondaryThread) {
        status = napi_acquire_threadsafe_function(func);
        EXPECT_EQ(status, napi_ok);

        if (uv_thread_create(&g_uvThreadSecondary, TsFuncSecondaryThread, func) != 0) {
            HILOG_ERROR("Failed to create uv thread!");
        }
    }

    bool queueClosing = false;
    bool queueFull = false;
    int32_t index = 0;
    for (index = SEND_DATAS_LENGTH - 1; index > -1 && !queueClosing; index--) {
        g_sendDatas[index] = index;
        status = napi_call_threadsafe_function(func, &g_sendDatas[index], jsData->blockOnFull);
        HILOG_INFO("napi_call_threadsafe_function index %d status %d", index, status);

        switch (status) {
            case napi_queue_full:
                queueFull = true;
                index++;
                [[fallthrough]];
            case napi_ok:
                continue;
            case napi_closing:
                queueClosing = true;
                break;
            default:
                HILOG_ERROR("Failed to call napi_call_threadsafe_function!");
        }
    }

    if (!queueClosing && (napi_release_threadsafe_function(func, napi_tsfn_release) != napi_ok)) {
        HILOG_ERROR("Failed to call napi_release_threadsafe_function!");
    }
}

static void TsFuncThreadInternal(napi_env env,
                                 napi_threadsafe_function_call_js cb,
                                 uv_thread_t& uvThread,
                                 bool secondary,
                                 bool block)
{
    HILOG_INFO("TsFuncThreadInternal start secondary %d block %d", secondary, block);

    napi_threadsafe_function tsFunc = nullptr;
    napi_value resourceName = 0;

    napi_create_string_latin1(env, __func__, NAPI_AUTO_LENGTH, &resourceName);
    g_mainTid = gettid();

    g_jsDataInternal.id = CALL_JS_CB_DATA_TEST_ID;
    g_jsDataInternal.secondaryThread = (secondary ? true : false);
    g_jsDataInternal.blockOnFull = (block ? napi_tsfn_blocking : napi_tsfn_nonblocking);

    auto status = napi_create_threadsafe_function(env,
                                                  nullptr,
                                                  nullptr,
                                                  resourceName,
                                                  MAX_QUEUE_SIZE,
                                                  THREAD_COUNT,
                                                  &uvThread,
                                                  TsFuncFinalJoinThread,
                                                  &g_jsDataInternal,
                                                  cb,
                                                  &tsFunc);
    EXPECT_EQ(status, napi_ok);

    if (uv_thread_create(&uvThread, TsFuncDataSourceThreadMulti, tsFunc) != 0) {
        HILOG_ERROR("Failed to create uv thread!");
    }

    HILOG_INFO("TsFuncThreadInternal end");
}

static void StopCurrentRunner()
{
    auto runner = OHOS::AppExecFwk::EventRunner::Current();
    if (runner != nullptr) {
        HILOG_INFO("Stop the current runner!");
        runner->Stop();
    }
}

void ThreadFanalize(napi_env env, void* context, void*)
{
    StopCurrentRunner();
    delete reinterpret_cast<CallbackData *>(context);
}

static void CallJs(napi_env env, napi_value jsCb, void *context, void *data)
{
    napi_release_threadsafe_function(reinterpret_cast<CallbackData*>(context)->tsfn, napi_tsfn_release);

    g_receiveCnt++;
    if (g_receiveCnt == FIRST_RECEIVER) {
        return;
    }
    // check data string
    char *testData = reinterpret_cast<char *>(data);
    EXPECT_NE(testData, nullptr) << "testData is nullptr";
    int32_t ret = 0;
    if (g_receiveCnt == SECOND_RECEIVER) {
        if (!g_isTailB) {
            ret = strcmp(testData, "hello world from B");
        } else {
            ret = strcmp(testData, "hello world from A");
        }
    }
    if (g_receiveCnt == THIRD_RECEIVER) {
        if (!g_isTailB) {
            ret = strcmp(testData, "hello world from A");
        } else {
            ret = strcmp(testData, "hello world from B");
        }
    }
    EXPECT_EQ(ret, 0);
}

static void CallJsWithDiffPriority(napi_env env, napi_value jsCb, void *context, void *data)
{
    EXPECT_NE(env, nullptr);
    auto ctx = reinterpret_cast<CallbackData *>(context);
    g_receiveCnt++;
    napi_release_threadsafe_function(ctx->tsfn, napi_tsfn_release);
    if (g_receiveCnt == FIRST_RECEIVER) {
        return;
    }
    // check data string
    char *testData = reinterpret_cast<char *>(data);
    EXPECT_NE(testData, nullptr) << "testData is nullptr";
    int32_t ret = 0;
    if (g_receiveCnt == SECOND_RECEIVER) {
        ret = strcmp(testData, "hello world from A");
    } else if (g_receiveCnt == THIRD_RECEIVER) {
        ret = strcmp(testData, "hello world from B");
    } else if (g_receiveCnt == FOURTH_RECEIVER) {
        ret = strcmp(testData, "hello world from C");
    } else if (g_receiveCnt == FIFTH_RECEIVER) {
        ret = strcmp(testData, "hello world from D");
    }
    EXPECT_EQ(ret, 0) << "data is: " << testData << ", execute id: " << g_receiveCnt;
}

class UnitLoopHandler : public OHOS::AppExecFwk::FileDescriptorListener,
    public std::enable_shared_from_this<UnitLoopHandler> {
public:
    explicit UnitLoopHandler(uv_loop_t* uvLoop) : uvLoop_(uvLoop) {}
    void OnReadable(int32_t) override { uv_run(uvLoop_, UV_RUN_NOWAIT); }
    void OnWritable(int32_t) override { uv_run(uvLoop_, UV_RUN_NOWAIT); }

private:
    uv_loop_t* uvLoop_ = nullptr;
};

class NapiThreadsafeTest : public NativeEngineTest {
public:
    static void SetUpTestCase()
    {
        GTEST_LOG_(INFO) << "NapiThreadsafeTest SetUpTestCase";
    }

    static void TearDownTestCase()
    {
        GTEST_LOG_(INFO) << "NapiThreadsafeTest TearDownTestCase";
    }

    void SetUp() override { AttachEventHandler(); }
    void TearDown() override {}

    void CallThreadSafeWithSamePriorityTest(napi_task_priority priority);
    void CallThreadSafeWithDiffPriorityTest();
    void CallThreadSafeWithDiffPriorityMultipleThreadTest();
    void AttachEventHandler();
};

void NapiThreadsafeTest::AttachEventHandler()
{
    if (eventHandler_ != nullptr) {
        return;
    }
    auto uvLoop = engine_->GetUVLoop();
    auto fd = uvLoop != nullptr ? uv_backend_fd(uvLoop) : -1;
    EXPECT_GE(fd, 0);
    uv_run(uvLoop, UV_RUN_NOWAIT);
    auto runner = OHOS::AppExecFwk::EventRunner::Create(false);
    EXPECT_NE(runner, nullptr);
    eventHandler_ = std::make_shared<OHOS::AppExecFwk::EventHandler>(runner);
    EXPECT_NE(eventHandler_, nullptr);
    uint32_t events = OHOS::AppExecFwk::FILE_DESCRIPTOR_INPUT_EVENT | OHOS::AppExecFwk::FILE_DESCRIPTOR_OUTPUT_EVENT;
    eventHandler_->AddFileDescriptorListener(fd, events, std::make_shared<UnitLoopHandler>(uvLoop), "uvLoopTask");
    HILOG_INFO("AttachEventHandler is completed!");
}

class AutoTsfn {
public:
    explicit AutoTsfn(napi_threadsafe_function tsfn) : tsfn_(tsfn) {}
    ~AutoTsfn()
    {
        if (autoRelease_) {
            Release();
        }
    }

    void Acquire()
    {
        ASSERT_EQ(napi_acquire_threadsafe_function(tsfn_), napi_ok);
    }

    void Release()
    {
        ASSERT_EQ(napi_release_threadsafe_function(tsfn_, napi_tsfn_release), napi_ok);
    }

    void CancelRelease()
    {
        autoRelease_ = false;
    }

private:
    napi_threadsafe_function tsfn_ {nullptr};
    bool autoRelease_ {true};
};

static void CallThreadSafeFunc(napi_threadsafe_function tsfn, AutoTsfn& autoTsfn, napi_task_priority priority)
{
    char *testData = (char *)malloc(DATA_LENGTH);
    if (testData == nullptr) {
        return;
    }
    memset_s(testData, DATA_LENGTH, 0, DATA_LENGTH);
    if (priority == napi_priority_immediate) {
        strcpy_s(testData, DATA_LENGTH, "hello world from A");
    } else if (priority == napi_priority_high) {
        strcpy_s(testData, DATA_LENGTH, "hello world from B");
    } else if (priority == napi_priority_low) {
        strcpy_s(testData, DATA_LENGTH, "hello world from C");
    } else if (priority == napi_priority_idle) {
        strcpy_s(testData, DATA_LENGTH, "hello world from D");
    }
    autoTsfn.Acquire();
    auto status = napi_call_threadsafe_function_with_priority(tsfn, testData, priority, true);
    if (status != napi_ok) {
        autoTsfn.Release();
    }
    EXPECT_EQ(status, napi_ok);
}

static void CallThreadSafeFunc(
    napi_threadsafe_function tsfn, AutoTsfn &autoTsfn, char *testData, napi_task_priority priority, bool isTail)
{
    autoTsfn.Acquire();
    auto status = napi_call_threadsafe_function_with_priority(tsfn, testData, priority, isTail);
    if (status != napi_ok) {
        autoTsfn.Release();
    }
    EXPECT_EQ(status, napi_ok);
}

void NapiThreadsafeTest::CallThreadSafeWithSamePriorityTest(napi_task_priority priority)
{
    napi_env env = (napi_env)engine_;
    napi_value resourceName = 0;
    napi_create_string_latin1(env, __func__, NAPI_AUTO_LENGTH, &resourceName);
    CallbackData *callbackData = new CallbackData();
    callbackData->priority = priority;

    auto status = napi_create_threadsafe_function(env, nullptr, nullptr, resourceName,
        0, 1, callbackData, ThreadFanalize, callbackData, CallJs, &callbackData->tsfn);
    EXPECT_EQ(status, napi_ok);
    std::thread([](napi_env env, void* data) {
        CallbackData* callbackData = (CallbackData*)data;
        g_receiveCnt = 1;

        char *testDataA = (char *)malloc(DATA_LENGTH);
        memset_s(testDataA, DATA_LENGTH, 0, DATA_LENGTH);
        strcpy_s(testDataA, DATA_LENGTH, "hello world from A");

        char *testDataB = (char *)malloc(DATA_LENGTH);
        memset_s(testDataB, DATA_LENGTH, 0, DATA_LENGTH);
        strcpy_s(testDataB, DATA_LENGTH, "hello world from B");

        AutoTsfn tsfn(callbackData->tsfn);
        CallThreadSafeFunc(callbackData->tsfn, tsfn, testDataA, callbackData->priority, g_isTailA);
        CallThreadSafeFunc(callbackData->tsfn, tsfn, testDataB, callbackData->priority, g_isTailB);
    }, env, callbackData).join();
}

void NapiThreadsafeTest::CallThreadSafeWithDiffPriorityTest()
{
    napi_env env = (napi_env)engine_;
    napi_value resourceName = 0;
    napi_create_string_latin1(env, __func__, NAPI_AUTO_LENGTH, &resourceName);
    CallbackData *callbackData = new CallbackData();

    auto status = napi_create_threadsafe_function(env, nullptr, nullptr, resourceName,
        0, 1, callbackData, ThreadFanalize, callbackData, CallJsWithDiffPriority, &callbackData->tsfn);
    EXPECT_EQ(status, napi_ok);
    AutoTsfn tsfn(callbackData->tsfn);
    tsfn.Acquire();
    g_receiveCnt = 0;

    status =
        napi_call_threadsafe_function_with_priority(callbackData->tsfn, nullptr, napi_priority_immediate,
            true);
    EXPECT_EQ(status, napi_ok);

    std::thread([](napi_env env, void* data) {
        CallbackData* callbackData = (CallbackData*)data;
        AutoTsfn tsfn(callbackData->tsfn);
        CallThreadSafeFunc(callbackData->tsfn, tsfn, napi_priority_immediate);
        CallThreadSafeFunc(callbackData->tsfn, tsfn, napi_priority_high);
        CallThreadSafeFunc(callbackData->tsfn, tsfn, napi_priority_low);
        CallThreadSafeFunc(callbackData->tsfn, tsfn, napi_priority_idle);
        tsfn.CancelRelease();
    }, env, callbackData).join();
}

void NapiThreadsafeTest::CallThreadSafeWithDiffPriorityMultipleThreadTest()
{
    napi_env env = (napi_env)engine_;
    napi_value resourceName = 0;
    napi_create_string_latin1(env, __func__, NAPI_AUTO_LENGTH, &resourceName);
    CallbackData *callbackData = new CallbackData();

    auto status = napi_create_threadsafe_function(env, nullptr, nullptr, resourceName,
        0, 1, callbackData, ThreadFanalize, callbackData, CallJsWithDiffPriority, &callbackData->tsfn);
    EXPECT_EQ(status, napi_ok);

    g_receiveCnt = 1;

    uint32_t* queuedTasks = new uint32_t;
    uint32_t* executedTasks = new uint32_t;
    *queuedTasks = 0;
    *executedTasks = 0;

    std::mutex taskMutex;
    std::condition_variable taskExecute;

    auto runFunc = [callbackData, &executedTasks, &taskMutex, &taskExecute](const napi_env &env, int32_t threadIndex) {
        AutoTsfn tsfn(callbackData->tsfn);

        if (threadIndex == SECOND_THREAD_INDEX) {
            CallThreadSafeFunc(callbackData->tsfn, tsfn, napi_priority_immediate);
        } else if (threadIndex == THIRD_THREAD_INDEX) {
            CallThreadSafeFunc(callbackData->tsfn, tsfn, napi_priority_high);
        } else if (threadIndex == FOURTH_THREAD_INDEX) {
            CallThreadSafeFunc(callbackData->tsfn, tsfn, napi_priority_low);
        } else if (threadIndex == FIFTH_THREAD_INDEX) {
            CallThreadSafeFunc(callbackData->tsfn, tsfn, napi_priority_idle);
        }
        {
            std::lock_guard<std::mutex> lock(taskMutex);
            ++(*executedTasks);
            taskExecute.notify_one();
        }
        tsfn.CancelRelease();
    };

    for (int32_t index = 1; index < THREAD_SIZE; ++index) {
        std::thread runThread = std::thread(runFunc, std::ref(env), index);
        ++(*queuedTasks);
        runThread.detach();
    }

    std::unique_lock<std::mutex> lock(taskMutex);
    while (*queuedTasks != *executedTasks) {
        taskExecute.wait(lock, [queuedTasks, executedTasks] {
            return *queuedTasks == *executedTasks;
        });
    }
    std::cout << "post task finish, posted: " << *queuedTasks << ", executed: " << g_receiveCnt << std::endl;
    AutoTsfn tsfn(callbackData->tsfn);
}

/**
 * @tc.name: ThreadsafeTest001
 * @tc.desc: Test LoadModule Func.
 * @tc.type: FUNC
 * @tc.require: I5K6KF
 */
HWTEST_F(NapiThreadsafeTest, ThreadsafeTest001, testing::ext::TestSize.Level1)
{
    HILOG_INFO("Threadsafe_Test_0100 start");
    napi_env env = (napi_env)engine_;
    napi_threadsafe_function tsFunc = nullptr;
    napi_value resourceName = 0;

    napi_create_string_latin1(env, __func__, NAPI_AUTO_LENGTH, &resourceName);
    g_mainTid = gettid();
    g_jsData.id = CALL_JS_CB_DATA_TEST_ID;
    g_finalData.id = FINAL_CB_DATA_TEST_ID;

    auto status = napi_create_threadsafe_function(env,
                                                  nullptr,
                                                  nullptr,
                                                  resourceName,
                                                  0,
                                                  1,
                                                  &g_finalData,
                                                  TsFuncFinal,
                                                  &g_jsData,
                                                  TsFuncCallJs,
                                                  &tsFunc);
    EXPECT_EQ(status, napi_ok);

    if (uv_thread_create(&g_uvThread, TsFuncDataSourceThread, tsFunc) != 0) {
        HILOG_ERROR("Failed to create uv thread!");
    }

    HILOG_INFO("Threadsafe_Test_0100 end");
}

/**
 * @tc.name: ThreadsafeTest002
 * @tc.desc: Test LoadModule Func.
 * @tc.type: FUNC
 * @tc.require: I5K6KF
 */
HWTEST_F(NapiThreadsafeTest, ThreadsafeTest002, testing::ext::TestSize.Level1)
{
    HILOG_INFO("Threadsafe_Test_0200 start");

    // start secondary thread, block on full
    TsFuncThreadInternal((napi_env)engine_, TsFuncCallJsMulti, g_uvTheads2, true, true);

    HILOG_INFO("Threadsafe_Test_0200 end");
}

/**
 * @tc.name: ThreadsafeTest003
 * @tc.desc: Test threadsafe Func, no js.
 * @tc.type: FUNC
 * @tc.require: I5K6KF
 */
HWTEST_F(NapiThreadsafeTest, ThreadsafeTest003, testing::ext::TestSize.Level1)
{
    HILOG_INFO("Threadsafe_Test_0300 start");

    // secondary thread, not block
    TsFuncThreadInternal((napi_env)engine_, TsFuncCallJsMulti, g_uvTheads3, false, false);

    HILOG_INFO("Threadsafe_Test_0300 end");
}

/**
 * @tc.name: ThreadsafeTest004
 * @tc.desc: Test napi_release_threadsafe_function, napi_tsfn_abort.
 * @tc.type: FUNC
 * @tc.require: I5K6KF
 */
HWTEST_F(NapiThreadsafeTest, ThreadsafeTest004, testing::ext::TestSize.Level1)
{
    HILOG_INFO("Threadsafe_Test_0400 start");
    napi_env env = (napi_env)engine_;
    napi_threadsafe_function tsFunc = nullptr;
    napi_value resourceName = 0;

    napi_create_string_latin1(env, __func__, NAPI_AUTO_LENGTH, &resourceName);
    g_mainTid = gettid();
    g_jsData.id = CALL_JS_CB_DATA_TEST_ID;
    g_finalData.id = FINAL_CB_DATA_TEST_ID;

    auto status = napi_create_threadsafe_function(env,
                                                  nullptr,
                                                  nullptr,
                                                  resourceName,
                                                  0,
                                                  10,
                                                  &g_finalData,
                                                  TsFuncFinalTest5,
                                                  &g_jsData,
                                                  TsFuncCallJs,
                                                  &tsFunc);
    EXPECT_EQ(status, napi_ok);
    status = napi_release_threadsafe_function(tsFunc, napi_tsfn_abort);
    EXPECT_EQ(status, napi_ok);
    if (uv_thread_create(&g_uvThreadTest5, TsFuncDataSourceThreadAbort, tsFunc) != 0) {
        HILOG_ERROR("Failed to create uv thread!");
    }

    HILOG_INFO("Threadsafe_Test_0400 end");
}

/**
 * @tc.name: ThreadsafeTest005
 * @tc.desc: Test initial_thread_count not enough.
 * @tc.type: FUNC
 * @tc.require: I5K6KF
 */
HWTEST_F(NapiThreadsafeTest, ThreadsafeTest005, testing::ext::TestSize.Level1)
{
    HILOG_INFO("Threadsafe_Test_0500 start");
    napi_env env = (napi_env)engine_;
    napi_threadsafe_function tsFunc = nullptr;
    napi_value resourceName = 0;
    g_callSuccessCountJS = 0;
    g_callSuccessCount = 0;
    napi_create_string_latin1(env, __func__, NAPI_AUTO_LENGTH, &resourceName);
    g_mainTid = gettid();
    g_jsData.id = CALL_JS_CB_DATA_TEST_ID;
    g_finalData.id = FINAL_CB_DATA_TEST_ID;

    auto status = napi_create_threadsafe_function(env,
                                                  nullptr,
                                                  nullptr,
                                                  resourceName,
                                                  0,
                                                  2,
                                                  &g_finalData,
                                                  TsFuncFinalTotal,
                                                  &g_jsData,
                                                  TsFuncCallJsTwo,
                                                  &tsFunc);
    EXPECT_EQ(status, napi_ok);
    int threadCount = THREAD_COUNT_FOUR;
    acquireFlag = false;

    for (int i = 0; i < threadCount; i++) {
        if (uv_thread_create(&g_uvThreadTest6, TsFuncDataSourceThreadCountTotal, tsFunc) != 0) {
            HILOG_ERROR("Failed to create uv thread!");
        }
    }

    usleep(200 * 1000);
    EXPECT_EQ(g_callSuccessCount, SUCCESS_COUNT_JS_FOUR);
    HILOG_INFO("Threadsafe_Test_0500 end");
}

/**
 * @tc.name: ThreadsafeTest006
 * @tc.desc: Test initial_thread_count not enough but acquire.
 * @tc.type: FUNC
 * @tc.require: I5K6KF
 */
HWTEST_F(NapiThreadsafeTest, ThreadsafeTest006, testing::ext::TestSize.Level1)
{
    HILOG_INFO("Threadsafe_Test_0600 start");
    napi_env env = (napi_env)engine_;
    napi_threadsafe_function tsFunc = nullptr;
    napi_value resourceName = 0;
    g_callSuccessCount = 0;
    napi_create_string_latin1(env, __func__, NAPI_AUTO_LENGTH, &resourceName);
    g_mainTid = gettid();
    g_jsData.id = CALL_JS_CB_DATA_TEST_ID;
    g_finalData.id = FINAL_CB_DATA_TEST_ID;

    auto status = napi_create_threadsafe_function(env,
                                                  nullptr,
                                                  nullptr,
                                                  resourceName,
                                                  0,
                                                  1,
                                                  &g_finalData,
                                                  TsFuncFinalTotalFour,
                                                  &g_jsData,
                                                  TsFuncCallJsFour,
                                                  &tsFunc);
    EXPECT_EQ(status, napi_ok);
    int threadCount = THREAD_COUNT_FOUR;
    acquireFlag = true;

    for (int i = 0; i < threadCount; i++) {
        if (uv_thread_create(&g_uvThreadTest7, TsFuncDataSourceThreadCountTotal, tsFunc) != 0) {
            HILOG_ERROR("Failed to create uv thread!");
        }
    }

    usleep(200 * 1000);
    EXPECT_EQ(g_callSuccessCount, SUCCESS_COUNT_JS_FOUR);
    HILOG_INFO("Threadsafe_Test_0600 end");
}

/**
 * @tc.name: ThreadsafeTest007
 * @tc.desc: Test napi_ref_threadsafe_function.
 * @tc.type: FUNC
 * @tc.require: I5K6KF
 */
HWTEST_F(NapiThreadsafeTest, ThreadsafeTest007, testing::ext::TestSize.Level1)
{
    HILOG_INFO("Threadsafe_Test_0700 start");

    napi_env env = (napi_env)engine_;
    napi_threadsafe_function tsFunc = nullptr;
    napi_value resourceName = 0;

    napi_create_string_latin1(env, __func__, NAPI_AUTO_LENGTH, &resourceName);
    g_jsData.id = CALL_JS_CB_DATA_TEST_ID;
    g_finalData.id = FINAL_CB_DATA_TEST_ID;

    auto status = napi_create_threadsafe_function(env, nullptr, nullptr, resourceName,
        0, 1, &g_finalData, TsFuncFinalCallback, &g_jsData, TsFuncCallJsFour, &tsFunc);
    EXPECT_EQ(status, napi_ok);

    status = napi_ref_threadsafe_function(env, tsFunc);
    EXPECT_EQ(status, napi_ok);

    HILOG_INFO("Threadsafe_Test_0700 end");
}

/**
 * @tc.name: ThreadsafeTest008
 * @tc.desc: Test napi_unref_threadsafe_function.
 * @tc.type: FUNC
 * @tc.require: I5K6KF
 */
HWTEST_F(NapiThreadsafeTest, ThreadsafeTest008, testing::ext::TestSize.Level1)
{
    HILOG_INFO("Threadsafe_Test_0800 start");
    napi_env env = (napi_env)engine_;
    napi_threadsafe_function tsFunc = nullptr;
    napi_value resourceName = 0;

    napi_create_string_latin1(env, __func__, NAPI_AUTO_LENGTH, &resourceName);
    g_jsData.id = CALL_JS_CB_DATA_TEST_ID;
    g_finalData.id = FINAL_CB_DATA_TEST_ID;

    auto status = napi_create_threadsafe_function(env, nullptr, nullptr, resourceName,
        0, 1, &g_finalData, TsFuncFinalCallback, &g_jsData, TsFuncCallJsFour, &tsFunc);
    EXPECT_EQ(status, napi_ok);

    status = napi_unref_threadsafe_function(env, tsFunc);
    EXPECT_EQ(status, napi_ok);

    HILOG_INFO("Threadsafe_Test_0800 end");
}

/**
 * @tc.name: ThreadsafeTest009
 * @tc.desc: Test napi_ref_threadsafe_function and napi_unref_threadsafe_function.
 * @tc.type: FUNC
 * @tc.require: I5K6KF
 */
HWTEST_F(NapiThreadsafeTest, ThreadsafeTest009, testing::ext::TestSize.Level1)
{
    HILOG_INFO("Threadsafe_Test_0900 start");
    napi_env env = (napi_env)engine_;
    napi_threadsafe_function tsFunc = nullptr;
    napi_value resourceName = 0;

    napi_create_string_latin1(env, __func__, NAPI_AUTO_LENGTH, &resourceName);
    g_jsData.id = CALL_JS_CB_DATA_TEST_ID;
    g_finalData.id = FINAL_CB_DATA_TEST_ID;

    auto status = napi_create_threadsafe_function(env, nullptr, nullptr, resourceName,
        0, 1, &g_finalData, TsFuncFinalCallback, &g_jsData, TsFuncCallJsFour, &tsFunc);
    EXPECT_EQ(status, napi_ok);

    status = napi_unref_threadsafe_function(env, tsFunc);
    EXPECT_EQ(status, napi_ok);

    status = napi_unref_threadsafe_function(env, tsFunc);
    EXPECT_EQ(status, napi_ok);

    HILOG_INFO("Threadsafe_Test_0900 end");
}

/**
 * @tc.name: ThreadsafeTest010
 * @tc.desc: Test napi_unref_threadsafe_function and napi_release_threadsafe_function.
 * @tc.type: FUNC
 * @tc.require: I5K6KF
 */
HWTEST_F(NapiThreadsafeTest, ThreadsafeTest010, testing::ext::TestSize.Level1)
{
    HILOG_INFO("Threadsafe_Test_1000 start");
    napi_env env = (napi_env)engine_;
    napi_threadsafe_function tsFunc = nullptr;
    napi_value resourceName = 0;
    napi_threadsafe_function_release_mode abort = napi_tsfn_abort;

    napi_create_string_latin1(env, __func__, NAPI_AUTO_LENGTH, &resourceName);
    g_jsData.id = CALL_JS_CB_DATA_TEST_ID;
    g_finalData.id = FINAL_CB_DATA_TEST_ID;

    auto status = napi_create_threadsafe_function(env, nullptr, nullptr, resourceName,
        0, 1, &g_finalData, TsFuncFinalCallback, &g_jsData, TsFuncCallJsFour, &tsFunc);
    EXPECT_EQ(status, napi_ok);

    status = napi_unref_threadsafe_function(env, tsFunc);
    EXPECT_EQ(status, napi_ok);

    status = napi_release_threadsafe_function(tsFunc, abort);
    EXPECT_EQ(status, napi_ok);

    HILOG_INFO("Threadsafe_Test_1000 end");
}

/**
 * @tc.name: ThreadsafeTest011
 * @tc.desc: Test napi_ref_threadsafe_function and napi_release_threadsafe_function.
 * @tc.type: FUNC
 * @tc.require: I5K6KF
 */
HWTEST_F(NapiThreadsafeTest, ThreadsafeTest011, testing::ext::TestSize.Level1)
{
    HILOG_INFO("Threadsafe_Test_1100 start");

    napi_env env = (napi_env)engine_;
    napi_threadsafe_function tsFunc = nullptr;
    napi_value resourceName = 0;
    napi_threadsafe_function_release_mode abort = napi_tsfn_abort;

    napi_create_string_latin1(env, __func__, NAPI_AUTO_LENGTH, &resourceName);
    g_jsData.id = CALL_JS_CB_DATA_TEST_ID;
    g_finalData.id = FINAL_CB_DATA_TEST_ID;

    auto status = napi_create_threadsafe_function(env, nullptr, nullptr, resourceName,
        0, 1, &g_finalData, TsFuncFinalCallback, &g_jsData, TsFuncCallJsFour, &tsFunc);
    EXPECT_EQ(status, napi_ok);

    status = napi_ref_threadsafe_function(env, tsFunc);
    EXPECT_EQ(status, napi_ok);

    status = napi_release_threadsafe_function(tsFunc, abort);
    EXPECT_EQ(status, napi_ok);

    HILOG_INFO("Threadsafe_Test_1100 end");
}

/**
 * @tc.name: ThreadsafeWithPriorityArgsCheckTest001
 * @tc.desc: Test napi_call_threadsafe_function_with_priority.
 * @tc.type: FUNC
 * @tc.require: I99QUH
 */
HWTEST_F(NapiThreadsafeTest, ThreadsafeWithPriorityArgsCheckTest001, testing::ext::TestSize.Level1)
{
    HILOG_INFO("ThreadsafeWithPriorityArgsCheckTest001 start");
    auto status =
        napi_call_threadsafe_function_with_priority(nullptr, nullptr, napi_priority_immediate, true);
    EXPECT_EQ(status, napi_invalid_arg);
    HILOG_INFO("ThreadsafeWithPriorityArgsCheckTest001 end");
}

/**
 * @tc.name: ThreadsafeWithPriorityArgsCheckTest002
 * @tc.desc: Test napi_call_threadsafe_function_with_priority.
 * @tc.type: FUNC
 * @tc.require: I99QUH
 */
HWTEST_F(NapiThreadsafeTest, ThreadsafeWithPriorityArgsCheckTest002, testing::ext::TestSize.Level1)
{
    HILOG_INFO("ThreadsafeWithPriorityArgsCheckTest002 start");
    napi_env env = (napi_env)engine_;
    napi_value resourceName = 0;
    napi_create_string_latin1(env, __func__, NAPI_AUTO_LENGTH, &resourceName);
    CallbackData *callbackData = new CallbackData();
    auto status = napi_create_threadsafe_function(env, nullptr, nullptr, resourceName,
        0, 1, callbackData, nullptr, callbackData, CallJs, &callbackData->tsfn);
    EXPECT_EQ(status, napi_ok);
    status =
        napi_call_threadsafe_function_with_priority(callbackData->tsfn, nullptr,
            static_cast<napi_task_priority>(INVALID_NAPI_THREAD_SAFE_PRIORITY), true);
    EXPECT_NE(status, napi_ok);
    delete callbackData;
    HILOG_INFO("ThreadsafeWithPriorityArgsCheckTest002 end");
}

/**
 * @tc.name: ThreadsafeWithPriorityTest001
 * @tc.desc: Test napi_call_threadsafe_function_with_priority.
 * @tc.type: FUNC
 * @tc.require: I99QUH
 */
HWTEST_F(NapiThreadsafeTest, ThreadsafeWithPriorityTest001, testing::ext::TestSize.Level1)
{
    HILOG_INFO("ThreadsafeWithPriorityTest001 start");
    g_isTailA = true;
    g_isTailB = true;
    auto task = [test = this]() {
        test->CallThreadSafeWithSamePriorityTest(napi_priority_immediate);
    };
    EXPECT_NE(eventHandler_, nullptr);
    eventHandler_->PostTask(task);
    auto runner = eventHandler_->GetEventRunner();
    EXPECT_NE(runner, nullptr);
    runner->Run();
    HILOG_INFO("ThreadsafeWithPriorityTest001 end");
}

/**
 * @tc.name: ThreadsafeWithPriorityTest002
 * @tc.desc: Test napi_call_threadsafe_function_with_priority.
 * @tc.type: FUNC
 * @tc.require: I99QUH
 */
HWTEST_F(NapiThreadsafeTest, ThreadsafeWithPriorityTest002, testing::ext::TestSize.Level1)
{
    HILOG_INFO("ThreadsafeWithPriorityTest002 start");
    g_isTailA = false;
    g_isTailB = false;
    auto task = [test = this]() {
        test->CallThreadSafeWithSamePriorityTest(napi_priority_immediate);
    };
    EXPECT_NE(eventHandler_, nullptr);
    eventHandler_->PostTask(task);
    auto runner = eventHandler_->GetEventRunner();
    EXPECT_NE(runner, nullptr);
    runner->Run();
    HILOG_INFO("ThreadsafeWithPriorityTest002 end");
}

/**
 * @tc.name: ThreadsafeWithPriorityTest003
 * @tc.desc: Test napi_call_threadsafe_function_with_priority.
 * @tc.type: FUNC
 * @tc.require: I99QUH
 */
HWTEST_F(NapiThreadsafeTest, ThreadsafeWithPriorityTest003, testing::ext::TestSize.Level1)
{
    HILOG_INFO("ThreadsafeWithPriorityTest003 start");
    g_isTailA = true;
    g_isTailB = false;
    auto task = [test = this]() {
        test->CallThreadSafeWithSamePriorityTest(napi_priority_immediate);
    };
    EXPECT_NE(eventHandler_, nullptr);
    eventHandler_->PostTask(task);
    auto runner = eventHandler_->GetEventRunner();
    EXPECT_NE(runner, nullptr);
    runner->Run();
    HILOG_INFO("ThreadsafeWithPriorityTest003 end");
}

/**
 * @tc.name: ThreadsafeWithPriorityTest004
 * @tc.desc: Test napi_call_threadsafe_function_with_priority.
 * @tc.type: FUNC
 * @tc.require: I99QUH
 */
HWTEST_F(NapiThreadsafeTest, ThreadsafeWithPriorityTest004, testing::ext::TestSize.Level1)
{
    HILOG_INFO("ThreadsafeWithPriorityTest004 start");
    g_isTailA = true;
    g_isTailB = true;
    auto task = [test = this]() {
        test->CallThreadSafeWithSamePriorityTest(napi_priority_high);
    };
    EXPECT_NE(eventHandler_, nullptr);
    eventHandler_->PostTask(task);
    auto runner = eventHandler_->GetEventRunner();
    EXPECT_NE(runner, nullptr);
    runner->Run();
    HILOG_INFO("ThreadsafeWithPriorityTest004 end");
}

/**
 * @tc.name: ThreadsafeWithPriorityTest005
 * @tc.desc: Test napi_call_threadsafe_function_with_priority.
 * @tc.type: FUNC
 * @tc.require: I99QUH
 */
HWTEST_F(NapiThreadsafeTest, ThreadsafeWithPriorityTest005, testing::ext::TestSize.Level1)
{
    HILOG_INFO("ThreadsafeWithPriorityTest005 start");
    g_isTailA = false;
    g_isTailB = false;
    auto task = [test = this]() {
        test->CallThreadSafeWithSamePriorityTest(napi_priority_high);
    };
    EXPECT_NE(eventHandler_, nullptr);
    eventHandler_->PostTask(task);
    auto runner = eventHandler_->GetEventRunner();
    EXPECT_NE(runner, nullptr);
    runner->Run();
    HILOG_INFO("ThreadsafeWithPriorityTest005 end");
}

/**
 * @tc.name: ThreadsafeWithPriorityTest006
 * @tc.desc: Test napi_call_threadsafe_function_with_priority.
 * @tc.type: FUNC
 * @tc.require: I99QUH
 */
HWTEST_F(NapiThreadsafeTest, ThreadsafeWithPriorityTest006, testing::ext::TestSize.Level1)
{
    HILOG_INFO("ThreadsafeWithPriorityTest006 start");
    g_isTailA = true;
    g_isTailB = false;
    auto task = [test = this]() {
        test->CallThreadSafeWithSamePriorityTest(napi_priority_high);
    };
    EXPECT_NE(eventHandler_, nullptr);
    eventHandler_->PostTask(task);
    auto runner = eventHandler_->GetEventRunner();
    EXPECT_NE(runner, nullptr);
    runner->Run();
    HILOG_INFO("ThreadsafeWithPriorityTest006 end");
}

/**
 * @tc.name: ThreadsafeWithPriorityTest007
 * @tc.desc: Test napi_call_threadsafe_function_with_priority.
 * @tc.type: FUNC
 * @tc.require: I99QUH
 */
HWTEST_F(NapiThreadsafeTest, ThreadsafeWithPriorityTest007, testing::ext::TestSize.Level1)
{
    HILOG_INFO("ThreadsafeWithPriorityTest007 start");
    g_isTailA = true;
    g_isTailB = true;
    auto task = [test = this]() {
        test->CallThreadSafeWithSamePriorityTest(napi_priority_low);
    };
    EXPECT_NE(eventHandler_, nullptr);
    eventHandler_->PostTask(task);
    auto runner = eventHandler_->GetEventRunner();
    EXPECT_NE(runner, nullptr);
    runner->Run();
    HILOG_INFO("ThreadsafeWithPriorityTest007 end");
}

/**
 * @tc.name: ThreadsafeWithPriorityTest008
 * @tc.desc: Test napi_call_threadsafe_function_with_priority.
 * @tc.type: FUNC
 * @tc.require: I99QUH
 */
HWTEST_F(NapiThreadsafeTest, ThreadsafeWithPriorityTest008, testing::ext::TestSize.Level1)
{
    HILOG_INFO("ThreadsafeWithPriorityTest008 start");
    g_isTailA = false;
    g_isTailB = false;
    auto task = [test = this]() {
        test->CallThreadSafeWithSamePriorityTest(napi_priority_low);
    };
    EXPECT_NE(eventHandler_, nullptr);
    eventHandler_->PostTask(task);
    auto runner = eventHandler_->GetEventRunner();
    EXPECT_NE(runner, nullptr);
    runner->Run();
    HILOG_INFO("ThreadsafeWithPriorityTest008 end");
}

/**
 * @tc.name: ThreadsafeWithPriorityTest009
 * @tc.desc: Test napi_call_threadsafe_function_with_priority.
 * @tc.type: FUNC
 * @tc.require: I99QUH
 */
HWTEST_F(NapiThreadsafeTest, ThreadsafeWithPriorityTest009, testing::ext::TestSize.Level1)
{
    HILOG_INFO("ThreadsafeWithPriorityTest009 start");
    g_isTailA = true;
    g_isTailB = false;
    auto task = [test = this]() {
        test->CallThreadSafeWithSamePriorityTest(napi_priority_low);
    };
    EXPECT_NE(eventHandler_, nullptr);
    eventHandler_->PostTask(task);
    auto runner = eventHandler_->GetEventRunner();
    EXPECT_NE(runner, nullptr);
    runner->Run();
    HILOG_INFO("ThreadsafeWithPriorityTest009 end");
}


/**
 * @tc.name: ThreadsafeWithPriorityTest010
 * @tc.desc: Test napi_call_threadsafe_function_with_priority.
 * @tc.type: FUNC
 * @tc.require: I99QUH
 */
HWTEST_F(NapiThreadsafeTest, ThreadsafeWithPriorityTest010, testing::ext::TestSize.Level1)
{
    HILOG_INFO("ThreadsafeWithPriorityTest010 start");
    g_isTailA = true;
    g_isTailB = true;
    auto task = [test = this]() {
        test->CallThreadSafeWithSamePriorityTest(napi_priority_idle);
    };
    EXPECT_NE(eventHandler_, nullptr);
    eventHandler_->PostTask(task);
    auto runner = eventHandler_->GetEventRunner();
    EXPECT_NE(runner, nullptr);
    runner->Run();
    HILOG_INFO("ThreadsafeWithPriorityTest010 end");
}

/**
 * @tc.name: ThreadsafeWithPriorityTest011
 * @tc.desc: Test napi_call_threadsafe_function_with_priority.
 * @tc.type: FUNC
 * @tc.require: I99QUH
 */
HWTEST_F(NapiThreadsafeTest, ThreadsafeWithPriorityTest011, testing::ext::TestSize.Level1)
{
    HILOG_INFO("ThreadsafeWithPriorityTest005 start");
    g_isTailA = false;
    g_isTailB = false;
    auto task = [test = this]() {
        test->CallThreadSafeWithSamePriorityTest(napi_priority_idle);
    };
    EXPECT_NE(eventHandler_, nullptr);
    eventHandler_->PostTask(task);
    auto runner = eventHandler_->GetEventRunner();
    EXPECT_NE(runner, nullptr);
    runner->Run();
    HILOG_INFO("ThreadsafeWithPriorityTest011 end");
}

/**
 * @tc.name: ThreadsafeWithPriorityTest012
 * @tc.desc: Test napi_call_threadsafe_function_with_priority.
 * @tc.type: FUNC
 * @tc.require: I99QUH
 */
HWTEST_F(NapiThreadsafeTest, ThreadsafeWithPriorityTest012, testing::ext::TestSize.Level1)
{
    HILOG_INFO("ThreadsafeWithPriorityTest012 start");
    g_isTailA = true;
    g_isTailB = false;
    auto task = [test = this]() {
        test->CallThreadSafeWithSamePriorityTest(napi_priority_idle);
    };
    EXPECT_NE(eventHandler_, nullptr);
    eventHandler_->PostTask(task);
    auto runner = eventHandler_->GetEventRunner();
    EXPECT_NE(runner, nullptr);
    runner->Run();
    HILOG_INFO("ThreadsafeWithPriorityTest012 end");
}

/**
 * @tc.name: ThreadsafeWithPriorityTest013
 * @tc.desc: Test napi_call_threadsafe_function_with_priority.
 * @tc.type: FUNC
 * @tc.require: I99QUH
 */
HWTEST_F(NapiThreadsafeTest, ThreadsafeWithPriorityTest013, testing::ext::TestSize.Level1)
{
    HILOG_INFO("ThreadsafeWithPriorityTest013 start");
    auto task = [test = this]() {
        test->CallThreadSafeWithDiffPriorityTest();
    };
    EXPECT_NE(eventHandler_, nullptr);
    eventHandler_->PostTask(task);
    auto runner = eventHandler_->GetEventRunner();
    EXPECT_NE(runner, nullptr);
    runner->Run();
    HILOG_INFO("ThreadsafeWithPriorityTest013 end");
}

/**
 * @tc.name: ThreadsafeWithPriorityTest014
 * @tc.desc: Test napi_call_threadsafe_function_with_priority.
 * @tc.type: FUNC
 * @tc.require: I99QUH
 */
HWTEST_F(NapiThreadsafeTest, ThreadsafeWithPriorityTest014, testing::ext::TestSize.Level1)
{
    HILOG_INFO("ThreadsafeWithPriorityTest014 start");
    auto task = [test = this]() {
        test->CallThreadSafeWithDiffPriorityMultipleThreadTest();
    };
    EXPECT_NE(eventHandler_, nullptr);
    eventHandler_->PostTask(task);
    auto runner = eventHandler_->GetEventRunner();
    EXPECT_NE(runner, nullptr);
    runner->Run();
    HILOG_INFO("ThreadsafeWithPriorityTest014 end");
}

/**
 * @tc.name: ThreadsafeTest012
 * @tc.desc: Test LoadModule Func, call napi_call_threadsafe_function in callback.
 * @tc.type: FUNC
 * @tc.require: I5K6KF
 */
HWTEST_F(NapiThreadsafeTest, ThreadsafeTest012, testing::ext::TestSize.Level1)
{
    HILOG_INFO("Threadsafe_Test_1200 start");
    napi_env env = (napi_env)engine_;
    napi_threadsafe_function tsFunc = nullptr;
    napi_value resourceName = 0;

    napi_create_string_latin1(env, __func__, NAPI_AUTO_LENGTH, &resourceName);
    g_mainTid = gettid();
    g_jsData.id = CALL_JS_CB_DATA_TEST_ID;
    g_finalData.id = FINAL_CB_DATA_TEST_ID;

    auto status = napi_create_threadsafe_function(env,
                                                  nullptr,
                                                  nullptr,
                                                  resourceName,
                                                  0,
                                                  1,
                                                  &g_finalData,
                                                  TsFuncFinal,
                                                  &g_jsData,
                                                  TsFuncCallJsWithNewCall,
                                                  &tsFunc);
    EXPECT_EQ(status, napi_ok);

    if (uv_thread_create(
            &g_uvThread,
            [](void *data) {
                napi_threadsafe_function func = (napi_threadsafe_function)data;
                auto status = napi_call_threadsafe_function(func, data, napi_tsfn_nonblocking);
                EXPECT_EQ(status, napi_ok);
            },
            tsFunc) != 0) {
        HILOG_ERROR("Failed to create uv thread!");
    }

    HILOG_INFO("Threadsafe_Test_1200 end");
}


struct CallbackCountData {
    napi_threadsafe_function tsfn;
    napi_async_work work;
    int callThreadsafeCount;
    int callbackCount;
};

static void callJSCallBack(napi_env env, napi_value tsfn_cb, void* context, void* data)
{
    CallbackCountData *callbackData = reinterpret_cast<CallbackCountData *>(data);
    if (callbackData->callThreadsafeCount == INT_ONE) {
        callbackData->callThreadsafeCount++;
        napi_call_threadsafe_function(callbackData->tsfn, (void *)callbackData, napi_tsfn_nonblocking);
        napi_release_threadsafe_function(callbackData->tsfn, napi_tsfn_release);
    }
    callbackData->callbackCount++;
}

static void finalizeCallBack(napi_env env, void* finalizeData, void* hint)
{
    CallbackCountData *callbackData = reinterpret_cast<CallbackCountData *>(finalizeData);
    EXPECT_EQ(callbackData->callbackCount, INT_TWO);
    if (callbackData->work == nullptr) {
        delete callbackData;
    }
}

static void executeWork(napi_env env, void *data)
{
    CallbackCountData *callbackData = reinterpret_cast<CallbackCountData *>(data);
    callbackData->callThreadsafeCount++;
    napi_call_threadsafe_function(callbackData->tsfn, (void *)callbackData, napi_tsfn_nonblocking);
}

HWTEST_F(NapiThreadsafeTest, ThreadsafeTest013, testing::ext::TestSize.Level1)
{
    HILOG_INFO("ThreadsafeTest013 start");
    napi_env env = (napi_env)engine_;
    CallbackCountData *callbackData = new CallbackCountData();
    callbackData->callThreadsafeCount = 0;
    callbackData->callbackCount = 0;
    napi_value resourceName = 0;
    napi_create_string_latin1(env, __func__, NAPI_AUTO_LENGTH, &resourceName);

    auto status = napi_create_threadsafe_function(env, nullptr, nullptr, resourceName,
        0, 1, callbackData, finalizeCallBack, callbackData, callJSCallBack, &callbackData->tsfn);
    EXPECT_EQ(status, napi_ok);

    napi_create_async_work(env, nullptr, resourceName, executeWork,
        [](napi_env env, napi_status status, void* data) {
            CallbackCountData *callbackData = reinterpret_cast<CallbackCountData *>(data);
            napi_delete_async_work(env, callbackData->work);
            callbackData->work = nullptr;
            if (callbackData->tsfn == nullptr) {
                delete callbackData;
            }
        },
        callbackData, &callbackData->work);
    napi_queue_async_work(env, callbackData->work);
    HILOG_INFO("ThreadsafeTest013 end");
}

struct CallbackCountDataThreadsafe014 {
    napi_threadsafe_function tsfn;
    napi_async_work work;
};

static void callJSCallBackThreadsafe014(napi_env env, napi_value tsfn_cb, void* context, void* data)
{
    CallbackCountDataThreadsafe014 *callbackData = reinterpret_cast<CallbackCountDataThreadsafe014 *>(data);
    napi_release_threadsafe_function(callbackData->tsfn, napi_tsfn_release);
    return;
}

static void finalizeCallBackThreadsafe014(napi_env env, void* finalizeData, void* hint)
{
    CallbackCountDataThreadsafe014 *callbackData = reinterpret_cast<CallbackCountDataThreadsafe014 *>(finalizeData);
    callbackData->tsfn = nullptr;
}

static void executeWorkThreadsafe014(napi_env env, void *data)
{
    CallbackCountDataThreadsafe014 *callbackData = reinterpret_cast<CallbackCountDataThreadsafe014 *>(data);
    napi_call_threadsafe_function(callbackData->tsfn, (void *)callbackData, napi_tsfn_nonblocking);
    std::this_thread::sleep_for(std::chrono::seconds(INT_TWO));
}

HWTEST_F(NapiThreadsafeTest, ThreadsafeTest014, testing::ext::TestSize.Level1)
{
    HILOG_INFO("ThreadsafeTest014 start");
    napi_env env = (napi_env)engine_;
    CallbackCountDataThreadsafe014 *callbackData = new CallbackCountDataThreadsafe014();
    napi_value resourceName = 0;
    napi_create_string_latin1(env, __func__, NAPI_AUTO_LENGTH, &resourceName);

    auto status = napi_create_threadsafe_function(env, nullptr, nullptr, resourceName, 0, 1,
        callbackData, finalizeCallBackThreadsafe014, callbackData, callJSCallBackThreadsafe014, &callbackData->tsfn);
    EXPECT_EQ(status, napi_ok);

    napi_create_async_work(env, nullptr, resourceName, executeWorkThreadsafe014,
        [](napi_env env, napi_status status, void* data) {
            CallbackCountDataThreadsafe014 *callbackData = reinterpret_cast<CallbackCountDataThreadsafe014 *>(data);
            EXPECT_EQ(callbackData->tsfn, nullptr);
            napi_delete_async_work(env, callbackData->work);
            callbackData->work = nullptr;
            delete callbackData;
        },
        callbackData, &callbackData->work);
    napi_queue_async_work(env, callbackData->work);
    HILOG_INFO("ThreadsafeTest014 end");
}


static void HighPrioTask(uv_loop_t* loop)
{
    g_isCalled = true;
    gTaskExecOrder.push_back(kTaskNameHighPrio);
    uv_unregister_task_to_event(loop);
}

static void HighPriorityTaskTest(napi_env env, napi_value tsfn_cb, void* context, void* data)
{
    ASSERT_TRUE(g_isCalled);
    if (data) {
        std::string strData = static_cast<const char*>(data);
        gTaskExecOrder.push_back(strData);
    } else {
        gTaskExecOrder.push_back(kTaskNameNull);
    }
}

HWTEST_F(NapiThreadsafeTest, ThreadsafeTest015, testing::ext::TestSize.Level1)
{
    HILOG_INFO("ThreadsafeTest015 start");
    g_isCalled = false;
    napi_env env = (napi_env)engine_;
    uv_loop_t* loop = nullptr;
    napi_get_uv_event_loop(env, &loop);
    uv_register_task_to_worker(loop, HighPrioTask);

    napi_threadsafe_function tsFunc = nullptr;
    napi_value name = nullptr;
    ASSERT_EQ(napi_create_string_latin1(env, "HighPriorityTaskTest", NAPI_AUTO_LENGTH, &name), napi_ok);
    ASSERT_EQ(napi_create_threadsafe_function(env, nullptr, nullptr, name,
        0, 1, nullptr, nullptr, nullptr, HighPriorityTaskTest, &tsFunc), napi_ok);

    uv_thread_t uvThread;
    int rc = uv_thread_create(&uvThread, [](void* data)
    {
            napi_threadsafe_function func = (napi_threadsafe_function)data;
            napi_status s = napi_call_threadsafe_function(func, nullptr, napi_tsfn_nonblocking);
            EXPECT_EQ(s, napi_ok);
    }, tsFunc);
    ASSERT_EQ(rc, 0);
    uv_thread_join(&uvThread);
    napi_release_threadsafe_function(tsFunc, napi_tsfn_release);
    HILOG_INFO("ThreadsafeTest015 end");
}

HWTEST_F(NapiThreadsafeTest, ThreadsafeTest016, testing::ext::TestSize.Level1)
{
    HILOG_INFO("ThreadsafeTest016 start");
    g_isCalled = false;
    gTaskExecOrder.clear();
    napi_env env = (napi_env)engine_;
    uv_loop_t* loop = nullptr;
    napi_get_uv_event_loop(env, &loop);
    uv_register_task_to_worker(loop, HighPrioTask);

    napi_threadsafe_function tsFunc = nullptr;
    napi_value name = nullptr;
    ASSERT_EQ(napi_create_string_latin1(env, "HighPriorityTaskTest", NAPI_AUTO_LENGTH, &name), napi_ok);
    ASSERT_EQ(napi_create_threadsafe_function(env, nullptr, nullptr, name,
        0, 1, nullptr, nullptr, nullptr, HighPriorityTaskTest, &tsFunc), napi_ok);
    
    const int numTasks = 5;
    const char* taskNames[numTasks] = { "A", "B", "C", "D", "E" };
    uv_thread_t uvThread;

    struct ThreadArg { napi_threadsafe_function func; const char** msgs; int num; };
    int rc = uv_thread_create(&uvThread, [](void* data) {
        ThreadArg* arg = (ThreadArg*)data;
        for (int i = 0; i < arg->num; i++) {
            napi_status s = napi_call_threadsafe_function(arg->func, (void*)arg->msgs[i], napi_tsfn_nonblocking);
            EXPECT_EQ(s, napi_ok);
        }
        delete arg;
    }, new ThreadArg{ tsFunc, taskNames, numTasks });
    ASSERT_EQ(rc, 0);
    uv_thread_join(&uvThread);
    napi_release_threadsafe_function(tsFunc, napi_tsfn_release);
    HILOG_INFO("ThreadsafeTest016 end");
}

HWTEST_F(NapiThreadsafeTest, ThreadsafeTest017, testing::ext::TestSize.Level1)
{
    HILOG_INFO("ThreadsafeTest017 start");
    g_isCalled = false;
    gTaskExecOrder.clear();
    napi_env env = (napi_env)engine_;
    uv_loop_t* loop = nullptr;
    napi_get_uv_event_loop(env, &loop);
    uv_register_task_to_worker(loop, HighPrioTask);

    napi_threadsafe_function tsFunc = nullptr;
    napi_value name = nullptr;
    ASSERT_EQ(napi_create_string_latin1(env, "HighPriorityTaskTest", NAPI_AUTO_LENGTH, &name), napi_ok);
    ASSERT_EQ(napi_create_threadsafe_function(env, nullptr, nullptr, name,
        0, 1, nullptr, nullptr, nullptr, HighPriorityTaskTest, &tsFunc), napi_ok);
    
    const int numThreads = 5;
    const char* msgs[numThreads] = { "A", "B", "C", "D", "E" };
    uv_thread_t threads[numThreads];

    struct ThreadArg { napi_threadsafe_function func; const char* msg; };

    for (int i = 0; i < numThreads; i++) {
        int rc = uv_thread_create(&threads[i], [](void* data) {
            ThreadArg* arg = (ThreadArg*)data;
            napi_status s = napi_call_threadsafe_function(arg->func, (void*)arg->msg, napi_tsfn_nonblocking);
            EXPECT_EQ(s, napi_ok);
            delete arg;
        }, new ThreadArg{ tsFunc, msgs[i] });
        ASSERT_EQ(rc, 0);
    }
    for (int i = 0; i < numThreads; i++) {
        uv_thread_join(&threads[i]);
    }

    napi_release_threadsafe_function(tsFunc, napi_tsfn_release);
    HILOG_INFO("ThreadsafeTest017 end");
}

HWTEST_F(NapiThreadsafeTest, ThreadsafeTest018, testing::ext::TestSize.Level1)
{
    HILOG_INFO("ThreadsafeTest018 start");
    g_isCalled = false;
    gTaskExecOrder.clear();
    napi_env env = (napi_env)engine_;
    uv_loop_t* loop = nullptr;
    napi_get_uv_event_loop(env, &loop);
    
    napi_threadsafe_function tsFunc = nullptr;
    napi_value name = nullptr;
    ASSERT_EQ(napi_create_string_latin1(env, "HighPriorityTaskTest", NAPI_AUTO_LENGTH, &name), napi_ok);
    ASSERT_EQ(napi_create_threadsafe_function(env, nullptr, nullptr, name,
        0, 1, nullptr, nullptr, nullptr, HighPriorityTaskTest, &tsFunc), napi_ok);

    const int numThreads = 3;
    const int numTasksPerThread = 5;
    const char* msgs[numThreads][numTasksPerThread] = {
        { "A1", "A2", "A3", "A4", "A5" },
        { "B1", "B2", "B3", "B4", "B5" },
        { "C1", "C2", "C3", "C4", "C5" }
    };
    
    uv_thread_t threads[numThreads];
    struct ThreadArg { napi_threadsafe_function func; const char** msgs; int num; };
    for (int t = 0; t < numThreads; t++) {
        int rc = uv_thread_create(&threads[t], [](void* data) {
            ThreadArg* arg = (ThreadArg*)data;
            for (int i = 0; i < arg->num; i++) {
                napi_status s = napi_call_threadsafe_function(arg->func, (void*)arg->msgs[i], napi_tsfn_nonblocking);
                EXPECT_EQ(s, napi_ok);
            }
            delete arg;
        }, new ThreadArg{ tsFunc, msgs[t], numTasksPerThread });
        ASSERT_EQ(rc, 0);
    }
    
    for (int i = 0; i < numThreads; i++) {
        uv_thread_join(&threads[i]);
    }
    uv_register_task_to_worker(loop, HighPrioTask);
    napi_release_threadsafe_function(tsFunc, napi_tsfn_release);
    HILOG_INFO("ThreadsafeTest018 end");
}