/*
 * Copyright (c) 2025 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 <thread>

#include "libpandabase/utils/utf.h"
#include "class_data_accessor-inl.h"

#include "ecmascript/builtins/builtins_arraybuffer.h"
#include "ecmascript/ecma_vm.h"
#include "ecmascript/global_env.h"
#include "ecmascript/js_array.h"
#include "ecmascript/js_arraybuffer.h"
#include "ecmascript/js_hclass.h"
#include "ecmascript/js_regexp.h"
#include "ecmascript/js_set.h"
#include "ecmascript/js_thread.h"
#include "ecmascript/js_typed_array.h"
#include "ecmascript/jspandafile/js_pandafile.h"
#include "ecmascript/jspandafile/js_pandafile_manager.h"
#include "ecmascript/linked_hash_table.h"
#include "ecmascript/mem/c_containers.h"
#include "ecmascript/module/js_module_source_text.h"
#include "ecmascript/object_factory.h"
#include "ecmascript/tests/test_helper.h"

#include "ecmascript/serializer/inter_op_value_deserializer.h"
#include "ecmascript/serializer/inter_op_value_serializer.h"

using namespace panda::ecmascript;
using namespace testing::ext;
using namespace panda::ecmascript::builtins;

namespace panda::test {
using DeserializeFunc = void (*)(SerializeData* data);
using Clock = std::chrono::high_resolution_clock;
using Duration = std::chrono::duration<uint64_t, std::nano>;

constexpr int32_t INITIALIZE_SIZE = 100;

static EcmaVM *g_currentVM = nullptr;

static Local<JSValueRef> NewObjectAttachHook([[maybe_unused]] void *engine, [[maybe_unused]] void *data)
{
    return ObjectRef::New(g_currentVM);
}

static Local<JSValueRef> AttachHook([[maybe_unused]] void *engine, [[maybe_unused]] void *data)
{
    return JSValueRef::Undefined(g_currentVM);
}

static Local<JSValueRef> EmptyAttachHook([[maybe_unused]] void *engine, [[maybe_unused]] void *data)
{
    return Local<JSValueRef>();
}

class InterOpDeserializerTest {
public:
    InterOpDeserializerTest() : ecmaVm(nullptr), scope(nullptr), thread(nullptr) {}
    void Init()
    {
        JSRuntimeOptions options;
        options.SetEnableForceGC(true);
        ecmaVm = JSNApi::CreateEcmaVM(options);
        ecmaVm->SetEnableForceGC(true);
        EXPECT_TRUE(ecmaVm != nullptr) << "Cannot create Runtime";
        thread = ecmaVm->GetJSThread();
        scope = new EcmaHandleScope(thread);
        thread->ManagedCodeBegin();
    }
    void Destroy()
    {
        thread->ManagedCodeEnd();
        delete scope;
        scope = nullptr;
        ecmaVm->SetEnableForceGC(false);
        thread->ClearException();
        JSNApi::DestroyJSVM(ecmaVm);
    }

    void JSSpecialValueTest(SerializeData* data)
    {
        Init();
        JSHandle<JSTaggedValue> jsTrue(thread, JSTaggedValue::True());
        JSHandle<JSTaggedValue> jsFalse(thread, JSTaggedValue::False());
        JSHandle<JSTaggedValue> jsUndefined(thread, JSTaggedValue::Undefined());
        JSHandle<JSTaggedValue> jsNull(thread, JSTaggedValue::Null());
        JSHandle<JSTaggedValue> jsHole(thread, JSTaggedValue::Hole());

        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> retTrue = deserializer.ReadValue();
        EXPECT_TRUE(JSTaggedValue::SameValue(thread, jsTrue, retTrue)) << "Not same value for JS_TRUE";
        JSHandle<JSTaggedValue> retFalse = deserializer.ReadValue();
        EXPECT_TRUE(JSTaggedValue::SameValue(thread, jsFalse, retFalse)) << "Not same value for JS_FALSE";
        JSHandle<JSTaggedValue> retUndefined = deserializer.ReadValue();
        JSHandle<JSTaggedValue> retNull = deserializer.ReadValue();
        JSHandle<JSTaggedValue> retHole = deserializer.ReadValue();

        EXPECT_TRUE(JSTaggedValue::SameValue(thread, jsUndefined, retUndefined)) << "Not same value for JS_UNDEFINED";
        EXPECT_TRUE(JSTaggedValue::SameValue(thread, jsNull, retNull)) << "Not same value for JS_NULL";
        EXPECT_TRUE(JSTaggedValue::SameValue(thread, jsHole, retHole)) << "Not same value for JS_HOLE";
        Destroy();
    }

    void LineStringTest(SerializeData* data)
    {
        Init();
        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> res = deserializer.ReadValue();
        ecmaVm->CollectGarbage(TriggerGCType::YOUNG_GC);
        ecmaVm->CollectGarbage(TriggerGCType::OLD_GC);

        EXPECT_FALSE(res.IsEmpty());
        EXPECT_TRUE(res->IsLineString());

        Destroy();
    }

    void TreeStringTest(SerializeData* data)
    {
        Init();
        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> res = deserializer.ReadValue();
        ecmaVm->CollectGarbage(TriggerGCType::YOUNG_GC);
        ecmaVm->CollectGarbage(TriggerGCType::OLD_GC);

        EXPECT_FALSE(res.IsEmpty());
        EXPECT_TRUE(res->IsLineString());

        Destroy();
    }

    void SlicedStringTest(SerializeData* data)
    {
        Init();
        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> res = deserializer.ReadValue();
        ecmaVm->CollectGarbage(TriggerGCType::YOUNG_GC);
        ecmaVm->CollectGarbage(TriggerGCType::OLD_GC);

        EXPECT_FALSE(res.IsEmpty());
        EXPECT_TRUE(res->IsSlicedString());

        Destroy();
    }

    void JSPlainObjectTest1(SerializeData* data)
    {
        Init();
        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> objValue = deserializer.ReadValue();
        ecmaVm->CollectGarbage(TriggerGCType::YOUNG_GC);
        ecmaVm->CollectGarbage(TriggerGCType::OLD_GC);

        JSHandle<JSObject> retObj = JSHandle<JSObject>::Cast(objValue);
        EXPECT_FALSE(retObj.IsEmpty());

        JSHandle<TaggedArray> array = JSObject::GetOwnPropertyKeys(thread, retObj);
        uint32_t length = array->GetLength();
        EXPECT_EQ(length, 4U); // 4 : test case
        double sum = 0.0;
        for (uint32_t i = 0; i < length; i++) {
            JSHandle<JSTaggedValue> key(thread, array->Get(thread, i));
            double a = JSObject::GetProperty(thread, JSHandle<JSTaggedValue>(retObj), key).GetValue()->GetNumber();
            sum += a;
        }
        EXPECT_EQ(sum, 10); // 10 : test case

        Destroy();
    }

    void JSPlainObjectTest2(SerializeData* data)
    {
        Init();
        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> objValue = deserializer.ReadValue();
        ecmaVm->CollectGarbage(TriggerGCType::YOUNG_GC);
        ecmaVm->CollectGarbage(TriggerGCType::OLD_GC);

        JSHandle<JSObject> retObj = JSHandle<JSObject>::Cast(objValue);
        EXPECT_FALSE(retObj.IsEmpty());

        JSHandle<TaggedArray> array = JSObject::GetOwnPropertyKeys(thread, retObj);
        uint32_t length = array->GetLength();
        EXPECT_EQ(length, 10U);
        for (uint32_t i = 0; i < length; i++) {
            JSHandle<JSTaggedValue> key(thread, array->Get(thread, i));
            JSHandle<JSTaggedValue> value =
                JSObject::GetProperty(thread, JSHandle<JSTaggedValue>(retObj), key).GetValue();
            EXPECT_TRUE(value->GetTaggedObject()->GetClass()->IsJSObject());
        }

        Destroy();
    }

    void JSPlainObjectTest3(SerializeData* data)
    {
        Init();
        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> objValue = deserializer.ReadValue();
        ecmaVm->CollectGarbage(TriggerGCType::YOUNG_GC);
        ecmaVm->CollectGarbage(TriggerGCType::OLD_GC);

        JSHandle<JSObject> retObj = JSHandle<JSObject>::Cast(objValue);
        EXPECT_FALSE(retObj.IsEmpty());
        EXPECT_TRUE(retObj->GetClass()->IsDictionaryMode());

        JSHandle<TaggedArray> array = JSObject::GetOwnPropertyKeys(thread, retObj);
        uint32_t length = array->GetLength();
        EXPECT_EQ(length, 1030U);
        for (uint32_t i = 0; i < length; i++) {
            JSHandle<JSTaggedValue> key(thread, array->Get(thread, i));
            JSHandle<JSTaggedValue> value =
                JSObject::GetProperty(thread, JSHandle<JSTaggedValue>(retObj), key).GetValue();
            EXPECT_TRUE(value->IsInt());
        }

        Destroy();
    }

    void JSPlainObjectTest4(SerializeData* data)
    {
        Init();
        ObjectFactory *factory = ecmaVm->GetFactory();
        JSHandle<JSTaggedValue> key(factory->NewFromASCII("str1"));

        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> objValue = deserializer.ReadValue();
        ecmaVm->CollectGarbage(TriggerGCType::YOUNG_GC);
        ecmaVm->CollectGarbage(TriggerGCType::OLD_GC);

        JSHandle<JSObject> retObj = JSHandle<JSObject>::Cast(objValue);
        EXPECT_FALSE(retObj.IsEmpty());

        JSHandle<JSTaggedValue> value =
            JSObject::GetProperty(thread, JSHandle<JSTaggedValue>(retObj), key).GetValue();
        EXPECT_TRUE(value->IsTaggedArray());
        TaggedArray *array = reinterpret_cast<TaggedArray *>(value->GetTaggedObject());
        size_t length = array->GetLength();
        EXPECT_EQ(length, 102400U); // 102400: array length
        for (uint32_t i = 0; i < length; i++) {
            EXPECT_TRUE(array->Get(thread, i).IsHole());
        }

        Destroy();
    }

    void PrimitiveTest(SerializeData* data)
    {
        Init();

        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> objValue = deserializer.ReadValue();
        ecmaVm->CollectGarbage(TriggerGCType::YOUNG_GC);
        ecmaVm->CollectGarbage(TriggerGCType::OLD_GC);

        EXPECT_FALSE(objValue.IsEmpty());
        EXPECT_TRUE(objValue->IsJSObject());

        Destroy();
    }

    void JSErrorTest1(SerializeData* data)
    {
        Init();

        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> objValue = deserializer.ReadValue();
        ecmaVm->CollectGarbage(TriggerGCType::YOUNG_GC);
        ecmaVm->CollectGarbage(TriggerGCType::OLD_GC);

        EXPECT_FALSE(objValue.IsEmpty());
        EXPECT_TRUE(objValue->IsJSError());

        Destroy();
    }

    void JSErrorTest2(SerializeData* data)
    {
        Init();

        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> objValue = deserializer.ReadValue();
        ecmaVm->CollectGarbage(TriggerGCType::YOUNG_GC);
        ecmaVm->CollectGarbage(TriggerGCType::OLD_GC);

        JSHandle<JSObject> retObj = JSHandle<JSObject>::Cast(objValue);
        EXPECT_FALSE(retObj.IsEmpty());

        JSHandle<TaggedArray> array = JSObject::GetOwnPropertyKeys(thread, retObj);
        uint32_t length = array->GetLength();
        EXPECT_EQ(length, 2U);
        for (uint32_t i = 0; i < length; i++) {
            JSHandle<JSTaggedValue> key(thread, array->Get(thread, i));
            JSHandle<JSTaggedValue> value =
                JSObject::GetProperty(thread, JSHandle<JSTaggedValue>(retObj), key).GetValue();
            EXPECT_TRUE(value->IsJSError());
        }

        Destroy();
    }

    void JSErrorTest3(SerializeData *data)
    {
        Init();

        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> objValue = deserializer.ReadValue();
        ecmaVm->CollectGarbage(TriggerGCType::YOUNG_GC);
        ecmaVm->CollectGarbage(TriggerGCType::OLD_GC);

        JSHandle<JSObject> retObj = JSHandle<JSObject>::Cast(objValue);
        EXPECT_FALSE(retObj.IsEmpty());

        JSHandle<TaggedArray> array = JSObject::GetOwnPropertyKeys(thread, retObj);
        uint32_t length = array->GetLength();
        EXPECT_EQ(length, 7U); // 7 : test case
        for (uint32_t i = 0; i < length; i++) {
            JSHandle<JSTaggedValue> key(thread, array->Get(thread, i));
            JSHandle<JSTaggedValue> value =
                JSObject::GetProperty(thread, JSHandle<JSTaggedValue>(retObj), key).GetValue();
            EXPECT_TRUE(value->IsJSError());
        }

        Destroy();
    }

    void BigIntTest(SerializeData* data)
    {
        Init();
        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> objValue = deserializer.ReadValue();
        ecmaVm->CollectGarbage(TriggerGCType::YOUNG_GC);
        ecmaVm->CollectGarbage(TriggerGCType::OLD_GC);

        JSHandle<JSObject> retObj = JSHandle<JSObject>::Cast(objValue);
        EXPECT_FALSE(retObj.IsEmpty());

        JSHandle<TaggedArray> array = JSObject::GetOwnPropertyKeys(thread, retObj);
        uint32_t length = array->GetLength();
        EXPECT_EQ(length, 2U);
        for (uint32_t i = 0; i < length; i++) {
            JSHandle<JSTaggedValue> key(thread, array->Get(thread, i));
            JSHandle<JSTaggedValue> value =
                JSObject::GetProperty(thread, JSHandle<JSTaggedValue>(retObj), key).GetValue();
            EXPECT_TRUE(value->GetTaggedObject()->GetClass()->IsBigInt());
        }

        Destroy();
    }

    void NativeBindingObjectTest1(SerializeData* data)
    {
        Init();
        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> objValue = deserializer.ReadValue();
        ecmaVm->CollectGarbage(TriggerGCType::YOUNG_GC);
        ecmaVm->CollectGarbage(TriggerGCType::OLD_GC);
        EXPECT_TRUE(objValue->IsUndefined());
        Destroy();
    }

    void NativeBindingObjectTest2(SerializeData* data)
    {
        Init();
        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> objValue = deserializer.ReadValue();
        ecmaVm->CollectGarbage(TriggerGCType::YOUNG_GC);
        ecmaVm->CollectGarbage(TriggerGCType::OLD_GC);
        EXPECT_TRUE(objValue->IsJSObject());

        JSHandle<JSObject> retObj = JSHandle<JSObject>::Cast(objValue);
        JSHandle<TaggedArray> array = JSObject::GetOwnPropertyKeys(thread, retObj);
        uint32_t length = array->GetLength();
        EXPECT_EQ(length, 2U);
        JSHandle<JSTaggedValue> key(thread, array->Get(thread, 0));
        JSHandle<JSTaggedValue> value =
            JSObject::GetProperty(thread, JSHandle<JSTaggedValue>(retObj), key).GetValue();
        EXPECT_TRUE(value->IsUndefined());

        Destroy();
    }

    void JSSetTest(SerializeData* data)
    {
        Init();
        ObjectFactory *factory = ecmaVm->GetFactory();
        JSHandle<JSTaggedValue> value1(thread, JSTaggedValue(7)); // 7 : test case
        JSHandle<JSTaggedValue> value2(thread, JSTaggedValue(9)); // 9 : test case
        JSHandle<JSTaggedValue> value3(factory->NewFromASCII("x"));
        JSHandle<JSTaggedValue> value4(factory->NewFromASCII("y"));

        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> setValue = deserializer.ReadValue();
        EXPECT_TRUE(!setValue.IsEmpty());
        ecmaVm->CollectGarbage(TriggerGCType::YOUNG_GC);
        ecmaVm->CollectGarbage(TriggerGCType::OLD_GC);

        JSHandle<JSSet> retSet = JSHandle<JSSet>::Cast(setValue);
        JSHandle<TaggedArray> array = JSObject::GetOwnPropertyKeys(thread, JSHandle<JSObject>::Cast(retSet));
        uint32_t propertyLength = array->GetLength();
        EXPECT_EQ(propertyLength, 2U); // 2 : test case
        int sum = 0;
        for (uint32_t i = 0; i < propertyLength; i++) {
            JSHandle<JSTaggedValue> key(thread, array->Get(thread, i));
            double a = JSObject::GetProperty(thread, JSHandle<JSTaggedValue>(retSet), key).GetValue()->GetNumber();
            sum += a;
        }
        EXPECT_EQ(sum, 16); // 16 : test case

        EXPECT_EQ(retSet->GetSize(thread), 4);  // 4 : test case
        EXPECT_TRUE(retSet->Has(thread, value1.GetTaggedValue()));
        EXPECT_TRUE(retSet->Has(thread, value2.GetTaggedValue()));
        EXPECT_TRUE(retSet->Has(thread, value3.GetTaggedValue()));
        EXPECT_TRUE(retSet->Has(thread, value4.GetTaggedValue()));
        Destroy();
    }

    void JSArrayTest(SerializeData* data)
    {
        Init();
        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> arrayValue = deserializer.ReadValue();
        EXPECT_TRUE(!arrayValue.IsEmpty());
        ecmaVm->CollectGarbage(TriggerGCType::YOUNG_GC);
        ecmaVm->CollectGarbage(TriggerGCType::OLD_GC);

        JSHandle<JSArray> retArray = JSHandle<JSArray>::Cast(arrayValue);

        JSHandle<TaggedArray> keyArray = JSObject::GetOwnPropertyKeys(thread, JSHandle<JSObject>(retArray));
        uint32_t propertyLength = keyArray->GetLength();
        EXPECT_EQ(propertyLength, 23U);  // 23 : test case
        int sum = 0;
        for (uint32_t i = 0; i < propertyLength; i++) {
            JSHandle<JSTaggedValue> key(thread, keyArray->Get(thread, i));
            double a = JSObject::GetProperty(thread, JSHandle<JSTaggedValue>(retArray), key).GetValue()->GetNumber();
            sum += a;
        }
        EXPECT_EQ(sum, 226);  // 226 : test case

        // test get value from array
        for (int i = 0; i < 20; i++) {  // 20 : test case
            JSHandle<JSTaggedValue> value = JSArray::FastGetPropertyByValue(thread, arrayValue, i);
            EXPECT_EQ(i, value.GetTaggedValue().GetInt());
        }
        Destroy();
    }

    void EcmaStringTest1(SerializeData* data)
    {
        Init();
        const char *rawStr = "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"\
        "sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"\
        "sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"\
        "ssssss";
        JSHandle<EcmaString> ecmaString = thread->GetEcmaVM()->GetFactory()->NewFromASCII(rawStr);

        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> res = deserializer.ReadValue();
        EXPECT_TRUE(!res.IsEmpty()) << "[Empty] Deserialize ecmaString fail";
        EXPECT_TRUE(res->IsString()) << "[NotString] Deserialize ecmaString fail";
        ecmaVm->CollectGarbage(TriggerGCType::YOUNG_GC);
        ecmaVm->CollectGarbage(TriggerGCType::OLD_GC);

        JSHandle<EcmaString> resEcmaString = JSHandle<EcmaString>::Cast(res);
        auto ecmaStringCode = EcmaStringAccessor(ecmaString).GetHashcode(thread);
        auto resEcmaStringCode = EcmaStringAccessor(resEcmaString).GetHashcode(thread);
        EXPECT_TRUE(ecmaStringCode == resEcmaStringCode) << "Not same HashCode";
        EXPECT_TRUE(EcmaStringAccessor::StringsAreEqual(thread, *ecmaString, *resEcmaString)) << "Not same EcmaString";
        Destroy();
    }

    void EcmaStringTest2(SerializeData* data)
    {
        Init();
        JSHandle<EcmaString> ecmaString = thread->GetEcmaVM()->GetFactory()->NewFromStdString("你好,世界");
        JSHandle<EcmaString> ecmaString1 = thread->GetEcmaVM()->GetFactory()->NewFromStdString("你好,世界");
        auto ecmaStringCode1 = EcmaStringAccessor(ecmaString).GetHashcode(thread);
        auto ecmaString1Code = EcmaStringAccessor(ecmaString1).GetHashcode(thread);
        EXPECT_TRUE(ecmaStringCode1 == ecmaString1Code) << "Not same HashCode";
        EXPECT_TRUE(EcmaStringAccessor::StringsAreEqual(thread, *ecmaString, *ecmaString1)) << "Not same EcmaString";

        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> res = deserializer.ReadValue();
        EXPECT_TRUE(!res.IsEmpty()) << "[Empty] Deserialize ecmaString fail";
        EXPECT_TRUE(res->IsString()) << "[NotString] Deserialize ecmaString fail";
        ecmaVm->CollectGarbage(TriggerGCType::YOUNG_GC);
        ecmaVm->CollectGarbage(TriggerGCType::OLD_GC);

        JSHandle<EcmaString> resEcmaString = JSHandle<EcmaString>::Cast(res);
        auto ecmaStringCode2 = EcmaStringAccessor(ecmaString).GetHashcode(thread);
        auto resEcmaStringCode = EcmaStringAccessor(resEcmaString).GetHashcode(thread);
        EXPECT_TRUE(ecmaStringCode2 == resEcmaStringCode) << "Not same HashCode";
        EXPECT_TRUE(EcmaStringAccessor::StringsAreEqual(thread, *ecmaString, *resEcmaString)) << "Not same EcmaString";
        Destroy();
    }

    void Int32Test(SerializeData* data)
    {
        Init();
        int32_t a = 64;
        int32_t min = -2147483648;
        int32_t b = -63;
        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> resA = deserializer.ReadValue();
        JSHandle<JSTaggedValue> resMin = deserializer.ReadValue();
        JSHandle<JSTaggedValue> resB = deserializer.ReadValue();
        EXPECT_TRUE(!resA.IsEmpty() && !resMin.IsEmpty() && !resB.IsEmpty()) << "[Empty] Deserialize Int32 fail";
        EXPECT_TRUE(resA->IsInt() && resMin->IsInt() && resB->IsInt()) << "[NotInt] Deserialize Int32 fail";
        EXPECT_TRUE(JSTaggedValue::ToInt32(thread, resA) == a) << "Not Same Value";
        EXPECT_TRUE(JSTaggedValue::ToInt32(thread, resMin) == min) << "Not Same Value";
        EXPECT_TRUE(JSTaggedValue::ToInt32(thread, resB) == b) << "Not Same Value";
        Destroy();
    }

    void DoubleTest(SerializeData* data)
    {
        Init();
        double a = 3.1415926535;
        double b = -3.1415926535;
        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> resA = deserializer.ReadValue();
        JSHandle<JSTaggedValue> resB = deserializer.ReadValue();
        EXPECT_TRUE(!resA.IsEmpty() && !resB.IsEmpty()) << "[Empty] Deserialize double fail";
        EXPECT_TRUE(resA->IsDouble() && resB->IsDouble()) << "[NotInt] Deserialize double fail";
        EXPECT_TRUE(resA->GetDouble() == a) << "Not Same Value";
        EXPECT_TRUE(resB->GetDouble() == b) << "Not Same Value";
        Destroy();
    }

    void JSDateTest(SerializeData* data)
    {
        Init();
        double tm = 28 * 60 * 60 * 1000;  // 28 * 60 * 60 * 1000 : test case
        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> res = deserializer.ReadValue();
        EXPECT_TRUE(!res.IsEmpty()) << "[Empty] Deserialize JSDate fail";
        EXPECT_TRUE(res->IsDate()) << "[NotJSDate] Deserialize JSDate fail";
        JSHandle<JSDate> resDate = JSHandle<JSDate>(res);
        EXPECT_TRUE(resDate->GetTimeValue(thread) == JSTaggedValue(tm)) << "Not Same Time Value";
        Destroy();
    }

    void JSMapTest(SerializeData* data, const JSHandle<JSMap> &originMap)
    {
        Init();
        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> res = deserializer.ReadValue();
        EXPECT_TRUE(!res.IsEmpty()) << "[Empty] Deserialize JSMap fail";
        EXPECT_TRUE(res->IsJSMap()) << "[NotJSMap] Deserialize JSMap fail";
        JSHandle<JSMap> resMap = JSHandle<JSMap>::Cast(res);
        EXPECT_TRUE(originMap->GetSize(thread) == resMap->GetSize(thread)) << "the map size Not equal";
        uint32_t resSize = static_cast<uint32_t>(resMap->GetSize(thread));
        for (uint32_t i = 0; i < resSize; i++) {
            JSHandle<JSTaggedValue> resKey(thread, resMap->GetKey(thread, i));
            JSHandle<JSTaggedValue> resValue(thread, resMap->GetValue(thread, i));
            JSHandle<JSTaggedValue> key(thread, originMap->GetKey(thread, i));
            JSHandle<JSTaggedValue> value(thread, originMap->GetValue(thread, i));

            JSHandle<EcmaString> resKeyStr = JSHandle<EcmaString>::Cast(resKey);
            JSHandle<EcmaString> keyStr = JSHandle<EcmaString>::Cast(key);
            EXPECT_TRUE(EcmaStringAccessor::StringsAreEqual(thread, *resKeyStr, *keyStr)) << "Not same map key";
            EXPECT_TRUE(JSTaggedValue::ToInt32(thread, resValue) == JSTaggedValue::ToInt32(thread, value))
                << "Not same map value";
        }
        Destroy();
    }

    void JSSharedArrayBufferTest(SerializeData *data, int32_t byteLength, const char *msg)
    {
        Init();
        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> res = deserializer.ReadValue();
        EXPECT_TRUE(!res.IsEmpty()) << "[Empty] Deserialize JSArrayBuffer fail";
        EXPECT_TRUE(res->IsSharedArrayBuffer()) << "[NotJSArrayBuffer] Deserialize JSArrayBuffer fail";
        JSHandle<JSArrayBuffer> resJSArrayBuffer = JSHandle<JSArrayBuffer>::Cast(res);
        int32_t resByteLength = static_cast<int32_t>(resJSArrayBuffer->GetArrayBufferByteLength());
        EXPECT_TRUE(resByteLength == byteLength) << "Not Same ByteLength";
        JSHandle<JSTaggedValue> resBufferData(thread, resJSArrayBuffer->GetArrayBufferData(thread));
        JSHandle<JSNativePointer> resNp = JSHandle<JSNativePointer>::Cast(resBufferData);
        void *resBuffer = resNp->GetExternalPointer();
        ASSERT_NE(resBuffer, nullptr);

        if (msg != nullptr) {
            if (memcpy_s(resBuffer, byteLength, msg, byteLength) != EOK) {
                EXPECT_TRUE(false) << " memcpy error!";
            }
        }
        Destroy();
    }

    void SerializeMultiSharedRegionTest(SerializeData *data)
    {
        Init();
        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> res = deserializer.ReadValue();
        EXPECT_TRUE(!res.IsEmpty());
        EXPECT_TRUE(res->IsJSObject());
        JSTaggedValue elements = JSHandle<JSObject>(res)->GetElements(thread);
        EXPECT_TRUE(elements.IsTaggedArray());
        EXPECT_EQ(JSHandle<TaggedArray>(thread, elements)->GetLength(), 10 * 1024); // 10 * 1024: array length
        JSTaggedValue value = JSHandle<TaggedArray>(thread, elements)->Get(thread, 0);
        EXPECT_TRUE(value.IsTaggedArray());
        uint32_t length = JSHandle<TaggedArray>(thread, value)->GetLength();
        EXPECT_EQ(length, 11 * 1024); // 11 * 1024: array length
        Destroy();
    }

    void SerializeMultiSharedRegionTest1(SerializeData *data)
    {
        Init();
        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> res = deserializer.ReadValue();
        EXPECT_TRUE(!res.IsEmpty());
        EXPECT_TRUE(res->IsJSObject());
        JSTaggedValue elements = JSHandle<JSObject>(res)->GetElements(thread);
        EXPECT_TRUE(elements.IsTaggedArray());
        EXPECT_EQ(JSHandle<TaggedArray>(thread, elements)->GetLength(), 3 * 1024); // 3 * 1024: array length
        for (int i = 0; i < 5; i++) { // 5: array elements
            JSTaggedValue value = JSHandle<TaggedArray>(thread, elements)->Get(thread, i);
            EXPECT_TRUE(value.IsTaggedArray());
            uint32_t length = JSHandle<TaggedArray>(thread, value)->GetLength();
            EXPECT_EQ(length, 3 * 1024); // 3 * 1024: array length
        }
        Destroy();
    }

    void SerializeMultiSharedRegionTest2(SerializeData *data)
    {
        Init();
        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> res = deserializer.ReadValue();
        EXPECT_TRUE(!res.IsEmpty());
        EXPECT_TRUE(res->IsJSObject());
        JSTaggedValue properties = JSHandle<JSObject>(res)->GetProperties(thread);
        EXPECT_TRUE(properties.IsTaggedArray());
        EXPECT_EQ(JSHandle<TaggedArray>(thread, properties)->GetLength(), 3 * 1024); // 3 * 1024: array length
        for (int i = 0; i < 5; i++) { // 5: array elements
            JSTaggedValue value = JSHandle<TaggedArray>(thread, properties)->Get(thread, i);
            EXPECT_TRUE(value.IsTaggedArray());
            uint32_t length = JSHandle<TaggedArray>(thread, value)->GetLength();
            EXPECT_EQ(length, 3 * 1024); // 3 * 1024: array length
        }

        JSTaggedValue elements = JSHandle<JSObject>(res)->GetElements(thread);
        EXPECT_TRUE(elements.IsTaggedArray());
        EXPECT_EQ(JSHandle<TaggedArray>(thread, elements)->GetLength(), 3 * 1024); // 3 * 1024: array length
        for (int i = 0; i < 5; i++) { // 5: array elements
            JSTaggedValue value = JSHandle<TaggedArray>(thread, elements)->Get(thread, i);
            EXPECT_TRUE(value.IsTaggedArray());
            uint32_t length = JSHandle<TaggedArray>(thread, value)->GetLength();
            EXPECT_EQ(length, 3 * 1024); // 3 * 1024: array length
        }
        Destroy();
    }

    void JSSharedSetBasicTest1(SerializeData *data)
    {
        Init();
        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> res = deserializer.ReadValue();
        EXPECT_TRUE(!res.IsEmpty()) << "[Empty] Deserialize JSSharedSet failed";
        EXPECT_TRUE(res->IsJSSharedSet()) << "[NotJSSharedSet] Deserialize JSSharedSet failed";
        JSHandle<JSSharedSet> jsSet = JSHandle<JSSharedSet>::Cast(res);
        auto size = JSSharedSet::GetSize(thread, jsSet);
        EXPECT_TRUE(size == INITIALIZE_SIZE);
        JSSharedSet::Clear(thread, jsSet);
        Destroy();
    }

    void JSSharedSetBasicTest2(SerializeData *data)
    {
        Init();
        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> res = deserializer.ReadValue();
        EXPECT_TRUE(!res.IsEmpty()) << "[Empty] Deserialize JSSharedSet failed";
        EXPECT_TRUE(res->IsJSSharedSet()) << "[NotJSSharedSet] Deserialize JSSharedSet failed";
        JSHandle<JSSharedSet> jsSet = JSHandle<JSSharedSet>::Cast(res);

        auto size = JSSharedSet::GetSize(thread, jsSet);
        EXPECT_TRUE(size == INITIALIZE_SIZE);
        for (int32_t i = 0; i < size; i++) {
            EXPECT_TRUE(JSSharedSet::Has(thread, jsSet, JSTaggedValue(i)));
        }
        JSSharedSet::Add(thread, jsSet, JSHandle<JSTaggedValue>(thread, JSTaggedValue(INITIALIZE_SIZE)));
        bool result = JSSharedSet::Delete(thread, jsSet, JSHandle<JSTaggedValue>(thread, JSTaggedValue(0)));
        EXPECT_TRUE(result) << "Delete failed";
        Destroy();
    }

    void JSSharedSetMultiThreadTest1(SerializeData *data)
    {
        EXPECT_TRUE(data != nullptr);
        Init();
        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> res = deserializer.ReadValue();
        EXPECT_TRUE(!res.IsEmpty()) << "[Empty] Deserialize JSSharedSet fail";
        EXPECT_TRUE(res->IsJSSharedSet()) << "[NotJSSharedSet] Deserialize JSSharedSet fail";
        JSHandle<JSSharedSet> jsSet = JSHandle<JSSharedSet>::Cast(res);
        EXPECT_TRUE(JSSharedSet::GetSize(thread, jsSet) == INITIALIZE_SIZE);
        for (int i = 0; i < INITIALIZE_SIZE; i++) {
            EXPECT_TRUE(JSSharedSet::Has(thread, jsSet, JSTaggedValue(i)));
        }
        Destroy();
    }

    void JSSharedMapBasicTest1(SerializeData *data)
    {
        Init();
        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> res = deserializer.ReadValue();
        EXPECT_TRUE(!res.IsEmpty()) << "[Empty] Deserialize JSSharedMap failed";
        EXPECT_TRUE(res->IsJSSharedMap()) << "[NotJSSharedMap] Deserialize JSSharedMap failed";
        JSHandle<JSSharedMap> jsMap = JSHandle<JSSharedMap>::Cast(res);
        auto size = JSSharedMap::GetSize(thread, jsMap);
        EXPECT_TRUE(size == INITIALIZE_SIZE);
        JSSharedMap::Clear(thread, jsMap);
        Destroy();
    }

    void JSSharedMapBasicTest2(SerializeData *data)
    {
        Init();
        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> res = deserializer.ReadValue();
        EXPECT_TRUE(!res.IsEmpty()) << "[Empty] Deserialize JSSharedMap failed";
        EXPECT_TRUE(res->IsJSSharedMap()) << "[NotJSSharedMap] Deserialize JSSharedMap failed";
        JSHandle<JSSharedMap> jsMap = JSHandle<JSSharedMap>::Cast(res);

        auto size = JSSharedMap::GetSize(thread, jsMap);
        EXPECT_TRUE(size == INITIALIZE_SIZE);
        for (int32_t i = 0; i < size; i++) {
            EXPECT_TRUE(JSSharedMap::Has(thread, jsMap, JSTaggedValue(i)));
        }
        JSSharedMap::Set(thread, jsMap, JSHandle<JSTaggedValue>(thread, JSTaggedValue(INITIALIZE_SIZE)),
            JSHandle<JSTaggedValue>(thread, JSTaggedValue(INITIALIZE_SIZE)));
        bool result = JSSharedMap::Delete(thread, jsMap, JSHandle<JSTaggedValue>(thread, JSTaggedValue(0)));
        EXPECT_TRUE(result) << "Delete failed";
        Destroy();
    }

    void JSRegexpTest(SerializeData *data)
    {
        Init();
        JSHandle<EcmaString> pattern = thread->GetEcmaVM()->GetFactory()->NewFromASCII("key2");
        char buffer[] = "1234567";  // use char buffer to simulate byteCodeBuffer
        uint32_t bufferSize = 7;

        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> res = deserializer.ReadValue();
        EXPECT_TRUE(!res.IsEmpty()) << "[Empty] Deserialize JSRegExp fail";
        EXPECT_TRUE(res->IsJSRegExp()) << "[NotJSRegexp] Deserialize JSRegExp fail";
        JSHandle<JSRegExp> resJSRegexp(res);

        uint32_t resBufferSize = resJSRegexp->GetLength();
        EXPECT_TRUE(resBufferSize == bufferSize) << "Not Same Length";
        JSHandle<JSTaggedValue> originalSource(thread, resJSRegexp->GetOriginalSource(thread));
        EXPECT_TRUE(originalSource->IsString());
        JSHandle<JSTaggedValue> originalFlags(thread, resJSRegexp->GetOriginalFlags(thread));
        EXPECT_TRUE(EcmaStringAccessor::StringsAreEqual(thread, *JSHandle<EcmaString>(originalSource), *pattern));
        EXPECT_TRUE(originalFlags->IsInt());
        EXPECT_TRUE(originalFlags->GetInt() == 0);
        JSHandle<JSTaggedValue> resBufferData(thread, resJSRegexp->GetByteCodeBuffer(thread));
        JSHandle<JSNativePointer> resNp = JSHandle<JSNativePointer>::Cast(resBufferData);
        void *resBuffer = resNp->GetExternalPointer();
        ASSERT_NE(resBuffer, nullptr);

        for (uint32_t i = 0; i < resBufferSize; i++) {
            EXPECT_TRUE(static_cast<char *>(resBuffer)[i] == buffer[i]) << "Not Same ByteCode";
        }

        Destroy();
    }

    void TypedArrayTest1(SerializeData *data)
    {
        Init();
        JSHandle<JSTaggedValue> originTypedArrayName(thread, thread->GlobalConstants()->GetInt8ArrayString());
        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> res = deserializer.ReadValue();
        EXPECT_TRUE(!res.IsEmpty()) << "[Empty] Deserialize TypedArray fail";
        EXPECT_TRUE(res->IsJSInt8Array()) << "[NotJSInt8Array] Deserialize TypedArray fail";
        JSHandle<JSTypedArray> resJSInt8Array = JSHandle<JSTypedArray>::Cast(res);

        uint32_t byteLength = resJSInt8Array->GetByteLength();
        uint32_t byteOffset = resJSInt8Array->GetByteOffset();
        uint32_t arrayLength = resJSInt8Array->GetArrayLength();
        DataViewType contentType = resJSInt8Array->GetContentType();
        JSHandle<JSTaggedValue> viewedArrayBuffer(thread, resJSInt8Array->GetViewedArrayBufferOrByteArray(thread));

        EXPECT_EQ(byteLength, 10) << "Not Same ByteLength"; // 10: bufferLength
        EXPECT_EQ(byteOffset, 0) << "Not Same ByteOffset";
        EXPECT_EQ(arrayLength, 10) << "Not Same ArrayLength"; // 10: arrayLength
        EXPECT_TRUE(contentType == DataViewType::INT8) << "Not Same ContentType";

        // check arrayBuffer
        EXPECT_TRUE(viewedArrayBuffer->IsArrayBuffer());
        JSHandle<JSArrayBuffer> resJSArrayBuffer(viewedArrayBuffer);
        uint32_t resTaggedLength = resJSArrayBuffer->GetArrayBufferByteLength();
        EXPECT_EQ(resTaggedLength, 10) << "Not same viewedBuffer length"; // 10: bufferLength
        JSHandle<JSTaggedValue> resBufferData(thread, resJSArrayBuffer->GetArrayBufferData(thread));
        JSHandle<JSNativePointer> resNp = JSHandle<JSNativePointer>::Cast(resBufferData);
        void *resBuffer = resNp->GetExternalPointer();
        for (uint32_t i = 0; i < resTaggedLength; i++) {
            EXPECT_EQ(static_cast<uint8_t *>(resBuffer)[i], i) << "Not same viewedBuffer";
        }
        Destroy();
    }

    void TypedArrayTest2(SerializeData *data)
    {
        Init();
        JSHandle<JSTaggedValue> originTypedArrayName(thread, thread->GlobalConstants()->GetInt8ArrayString());
        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> res = deserializer.ReadValue();
        EXPECT_TRUE(!res.IsEmpty()) << "[Empty] Deserialize TypedArray fail";
        EXPECT_TRUE(res->IsJSInt8Array()) << "[NotJSInt8Array] Deserialize TypedArray fail";
        JSHandle<JSTypedArray> resJSInt8Array = JSHandle<JSTypedArray>::Cast(res);

        uint32_t byteLength = resJSInt8Array->GetByteLength();
        uint32_t byteOffset = resJSInt8Array->GetByteOffset();
        uint32_t arrayLength = resJSInt8Array->GetArrayLength();
        DataViewType contentType = resJSInt8Array->GetContentType();
        JSHandle<JSTaggedValue> byteArray(thread, resJSInt8Array->GetViewedArrayBufferOrByteArray(thread));

        EXPECT_EQ(byteLength, 10) << "Not Same ByteLength"; // 10: bufferLength
        EXPECT_EQ(byteOffset, 0) << "Not Same ByteOffset";
        EXPECT_EQ(arrayLength, 10) << "Not Same ArrayLength"; // 10: arrayLength
        EXPECT_TRUE(contentType == DataViewType::INT8) << "Not Same ContentType";

        // check byteArray
        EXPECT_TRUE(byteArray->IsByteArray());
        JSHandle<ByteArray> resByteArray(byteArray);
        uint32_t resTaggedLength = resByteArray->GetArrayLength();
        EXPECT_EQ(resTaggedLength, 10) << "Not same viewedBuffer length"; // 10: bufferLength
        uint32_t resElementSize = resByteArray->GetByteLength();
        EXPECT_EQ(resElementSize, 1) << "Not same byteArray size";
        for (uint32_t i = 0; i < resTaggedLength; i++) {
            JSHandle<JSTaggedValue> taggedVal(thread, resByteArray->Get(thread, i, DataViewType::UINT8));
            int32_t byteArrayVal = JSTaggedValue::ToInt32(thread, taggedVal);
            EXPECT_EQ(byteArrayVal, 255) << "Not same byteArray value"; // 255: value in byteArray
        }
        Destroy();
    }

    void SharedObjectTest4(SerializeData* data)
    {
        Init();
        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> res = deserializer.ReadValue();
        EXPECT_FALSE(res.IsEmpty());
        EXPECT_TRUE(res->IsJSSharedObject()) << "[NotJSSharedObject] Deserialize SharedObject fail";

        JSHandle<JSObject> sObj = JSHandle<JSObject>::Cast(res);
        JSHandle<TaggedArray> array = JSObject::GetOwnPropertyKeys(thread, sObj);
        uint32_t length = array->GetLength();
        EXPECT_EQ(length, 512U);
        for (uint32_t i = 0; i < length; i++) {
            JSHandle<JSTaggedValue> key(thread, array->Get(thread, i));
            JSHandle<JSTaggedValue> value =
                JSObject::GetProperty(thread, JSHandle<JSTaggedValue>(sObj), key).GetValue();
            EXPECT_TRUE(value->IsInt());
        }

        Destroy();
    }

    void SerializeSharedFunctionTest(SerializeData *data)
    {
        Init();
        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> res = deserializer.ReadValue();
        EXPECT_TRUE(!res.IsEmpty()) << "[Empty] Deserialize SharedFunction fail";
        EXPECT_TRUE(res->IsJSSharedFunction()) << "[NotJSSharedFunction] Deserialize SharedFunction fail";
        JSHandle<JSSharedFunction> sFunc = JSHandle<JSSharedFunction>::Cast(res);

        EXPECT_TRUE(sFunc->IsCallable());
        EXPECT_FALSE(sFunc->GetProtoOrHClass(thread).IsHole());
        EXPECT_TRUE(sFunc->GetLexicalEnv(thread).IsTaggedArray());
        EXPECT_TRUE(sFunc->GetHomeObject(thread).IsJSSharedObject());
        JSHandle<JSSharedObject> sObj(thread, sFunc->GetHomeObject(thread));
        Destroy();
    }

    void SerializeSharedFunctionTest1(SerializeData *data)
    {
        Init();
        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> res = deserializer.ReadValue();
        EXPECT_TRUE(!res.IsEmpty()) << "[Empty] Deserialize SharedFunction fail";
        EXPECT_TRUE(res->IsJSSharedFunction()) << "[NotJSSharedFunction] Deserialize SharedFunction fail";
        Destroy();
    }

    void ObjectWithConcurrentFunctionTest(SerializeData* data)
    {
        Init();
        ObjectFactory *factory = ecmaVm->GetFactory();
        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> res = deserializer.ReadValue();
        ecmaVm->CollectGarbage(TriggerGCType::YOUNG_GC);
        ecmaVm->CollectGarbage(TriggerGCType::OLD_GC);
        EXPECT_TRUE(!res.IsEmpty()) << "[Empty] Deserialize ObjectWithConcurrentFunction fail";

        JSHandle<JSTaggedValue> key1(factory->NewFromASCII("abc"));
        OperationResult result1 = JSObject::GetProperty(thread, res, key1);
        JSHandle<JSTaggedValue> value1 = result1.GetRawValue();
        EXPECT_TRUE(value1->IsString());

        JSHandle<JSTaggedValue> key2(factory->NewFromASCII("2"));
        OperationResult result2 = JSObject::GetProperty(thread, res, key2);
        JSHandle<JSTaggedValue> value2 = result2.GetRawValue();
        EXPECT_TRUE(value2->IsJSFunction());
        JSHandle<JSTaggedValue> key3(factory->NewFromASCII("key"));
        OperationResult result3 = JSObject::GetProperty(thread, res, key3);
        JSHandle<JSTaggedValue> value3 = result3.GetRawValue();
        EXPECT_TRUE(value3->IsJSFunction());

        Destroy();
    }

    void TransferJSArrayBufferTest1(SerializeData *data, uintptr_t bufferAddrCheck)
    {
        Init();
        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> res = deserializer.ReadValue();
        ecmaVm->CollectGarbage(TriggerGCType::YOUNG_GC);
        ecmaVm->CollectGarbage(TriggerGCType::OLD_GC);

        EXPECT_TRUE(!res.IsEmpty()) << "[Empty] Deserialize TransferJSArrayBuffer1 fail";
        EXPECT_TRUE(res->IsArrayBuffer()) << "[NotJSArrayBuffer] Deserialize TransferJSArrayBuffer1 fail";

        JSHandle<JSArrayBuffer> arrBuf = JSHandle<JSArrayBuffer>::Cast(res);
        EXPECT_EQ(arrBuf->GetArrayBufferByteLength(), 5); // 5: bufferLength
        JSHandle<JSTaggedValue> nativePtr(thread, arrBuf->GetArrayBufferData(thread));
        EXPECT_TRUE(nativePtr->IsJSNativePointer()) << "[NotJSNativePointer] Deserialize TransferJSArrayBuffer1 fail";
        JSHandle<JSNativePointer> np = JSHandle<JSNativePointer>::Cast(nativePtr);
        uintptr_t bufferAddr = reinterpret_cast<uintptr_t>(np->GetExternalPointer());
        // The deserialized C buffer pointer shall be same to the original one
        EXPECT_EQ(static_cast<uint64_t>(bufferAddr), static_cast<uint64_t>(bufferAddrCheck));
        Destroy();
    }

    void TransferJSArrayBufferTest2(SerializeData *data, uintptr_t bufferAddrCheck)
    {
        Init();
        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> res = deserializer.ReadValue();
        ecmaVm->CollectGarbage(TriggerGCType::YOUNG_GC);
        ecmaVm->CollectGarbage(TriggerGCType::OLD_GC);
        EXPECT_TRUE(!res.IsEmpty()) << "[Empty] Deserialize TransferJSArrayBuffer2 fail";
        EXPECT_TRUE(res->IsArrayBuffer()) << "[NotJSArrayBuffer] Deserialize TransferJSArrayBuffer2 fail";

        JSHandle<JSArrayBuffer> arrBuf = JSHandle<JSArrayBuffer>::Cast(res);
        EXPECT_EQ(arrBuf->GetArrayBufferByteLength(), 5); // 5: bufferLength
        JSHandle<JSTaggedValue> nativePtr(thread, arrBuf->GetArrayBufferData(thread));
        EXPECT_TRUE(nativePtr->IsJSNativePointer()) << "[NotJSNativePointer] Deserialize TransferJSArrayBuffer2 fail";
        JSHandle<JSNativePointer> np = JSHandle<JSNativePointer>::Cast(nativePtr);
        uintptr_t bufferAddr = reinterpret_cast<uintptr_t>(np->GetExternalPointer());
        // The deserialized C buffer pointer shall be different to the original one
        EXPECT_NE(static_cast<uint64_t>(bufferAddr), static_cast<uint64_t>(bufferAddrCheck));
        Destroy();
    }

    void TransferJSArrayBufferTest3(SerializeData *data)
    {
        Init();
        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> res = deserializer.ReadValue();
        ecmaVm->CollectGarbage(TriggerGCType::YOUNG_GC);
        ecmaVm->CollectGarbage(TriggerGCType::OLD_GC);

        EXPECT_TRUE(!res.IsEmpty()) << "[Empty] Deserialize TransferJSArrayBuffer3 fail";
        EXPECT_TRUE(res->IsArrayBuffer()) << "[NotJSArrayBuffer] Deserialize TransferJSArrayBuffer3 fail";

        JSHandle<JSArrayBuffer> arrBuf = JSHandle<JSArrayBuffer>::Cast(res);
        EXPECT_EQ(arrBuf->GetArrayBufferByteLength(), 0);
        JSHandle<JSTaggedValue> nativePtr(thread, arrBuf->GetArrayBufferData(thread));
        EXPECT_TRUE(nativePtr->IsUndefined()) << "[NotJSNativePointer] Deserialize TransferJSArrayBuffer3 fail";
        Destroy();
    }

    void TransferJSArrayBufferTest5(SerializeData *data)
    {
        Init();
        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> res = deserializer.ReadValue();
        ecmaVm->CollectGarbage(TriggerGCType::YOUNG_GC);
        ecmaVm->CollectGarbage(TriggerGCType::OLD_GC);

        EXPECT_TRUE(!res.IsEmpty()) << "[Empty] Deserialize TransferJSArrayBuffer5 fail";
        EXPECT_TRUE(res->IsArrayBuffer()) << "[NotJSArrayBuffer] Deserialize TransferJSArrayBuffer5 fail";

        JSHandle<JSArrayBuffer> arrBuf = JSHandle<JSArrayBuffer>::Cast(res);
        EXPECT_EQ(arrBuf->GetArrayBufferByteLength(), 5); // 5: bufferLength
        JSHandle<JSTaggedValue> nativePtr(thread, arrBuf->GetArrayBufferData(thread));
        EXPECT_TRUE(reinterpret_cast<JSNativePointer *>(nativePtr->GetTaggedObject())->GetDeleter());
        Destroy();
    }

    void SerializeCloneListTest1(SerializeData *data)
    {
        Init();
        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> res = deserializer.ReadValue();
        ecmaVm->CollectGarbage(TriggerGCType::YOUNG_GC);
        ecmaVm->CollectGarbage(TriggerGCType::OLD_GC);

        EXPECT_TRUE(!res.IsEmpty()) << "[Empty] Deserialize CloneListTest1 fail";
        EXPECT_TRUE(res->IsJSShared());
        JSType resType = res->GetTaggedObject()->GetClass()->GetObjectType();
        EXPECT_EQ(resType, JSType::JS_SHARED_OBJECT);

        ObjectFactory *factory = ecmaVm->GetFactory();
        JSHandle<JSTaggedValue> key(factory->NewFromASCII("str2str1"));
        JSHandle<JSTaggedValue> shareObj =
            JSObject::GetProperty(thread, JSHandle<JSObject>(res), key).GetValue();
        EXPECT_TRUE(shareObj->IsJSShared());
        Destroy();
    }

    void SerializeCloneListTest2(SerializeData *data)
    {
        Init();
        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> res = deserializer.ReadValue();
        ecmaVm->CollectGarbage(TriggerGCType::YOUNG_GC);
        ecmaVm->CollectGarbage(TriggerGCType::OLD_GC);

        EXPECT_TRUE(!res.IsEmpty()) << "[Empty] Deserialize CloneListTest2 fail";
        ObjectFactory *factory = ecmaVm->GetFactory();
        JSHandle<JSTaggedValue> key(factory->NewFromASCII("shareObj"));
        JSHandle<JSTaggedValue> shareObj =
            JSObject::GetProperty(thread, JSHandle<JSObject>(res), key).GetValue();
        EXPECT_TRUE(shareObj->IsJSShared());
        Destroy();
    }

    void SerializeCloneListTest4(SerializeData *data)
    {
        Init();
        InterOpValueDeserializer deserializer(thread, data);
        JSHandle<JSTaggedValue> res = deserializer.ReadValue();
        ecmaVm->CollectGarbage(TriggerGCType::YOUNG_GC);
        ecmaVm->CollectGarbage(TriggerGCType::OLD_GC);

        EXPECT_TRUE(!res.IsEmpty()) << "[Empty] Deserialize CloneListTest4 fail";
        Destroy();
    }
private:
    EcmaVM *ecmaVm = nullptr;
    EcmaHandleScope *scope = nullptr;
    JSThread *thread = nullptr;
};

class InterOpSerializerTest : public testing::Test {
public:
    static void SetUpTestCase()
    {
        GTEST_LOG_(INFO) << "SetUpTestCase";
    }

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

    void WrapXRefObject(Local<ObjectRef> object, void *attachFunc, bool valid)
    {
        JSNApi::XRefBindingInfo *data = JSNApi::XRefBindingInfo::CreateNewInstance();
        data->attachXRefFunc = attachFunc;
        data->attachXRefData = nullptr;
        object->SetNativePointerFieldCount(ecmaVm, 1);
        void *actualData = valid ? data : nullptr;
        object->SetNativePointerField(ecmaVm, 0, nullptr,
            [](void *env, void *data, void *info) {
                JSNApi::XRefBindingInfo *externalInfo = reinterpret_cast<JSNApi::XRefBindingInfo *>(info);
                delete externalInfo;
            },
            actualData, 0);
    }

    void SetUp() override
    {
        TestHelper::CreateEcmaVMWithScope(ecmaVm, thread, scope);
        g_currentVM = ecmaVm;
    }

    void TearDown() override
    {
        g_currentVM = nullptr;
        TestHelper::DestroyEcmaVMWithScope(ecmaVm, scope);
    }

    void Reset()
    {
        TestHelper::DestroyEcmaVMWithScope(ecmaVm, scope);
        thread = nullptr;
        ecmaVm = nullptr;
        scope = nullptr;
        TestHelper::CreateEcmaVMWithScope(ecmaVm, thread, scope);
        g_currentVM = ecmaVm;
    }

    JSThread *thread {nullptr};
    EcmaVM *ecmaVm {nullptr};
    EcmaHandleScope *scope {nullptr};
};

HWTEST_F_L0(InterOpSerializerTest, SerializeJSSpecialValue)
{
    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    serializer->SerializeJSTaggedValue(JSTaggedValue::True());
    serializer->SerializeJSTaggedValue(JSTaggedValue::False());
    serializer->SerializeJSTaggedValue(JSTaggedValue::Undefined());
    serializer->SerializeJSTaggedValue(JSTaggedValue::Null());
    serializer->SerializeJSTaggedValue(JSTaggedValue::Hole());
    std::unique_ptr<SerializeData> data = serializer->Release();

    InterOpDeserializerTest jsDeserializerTest;
    std::thread t1(&InterOpDeserializerTest::JSSpecialValueTest, jsDeserializerTest, data.release());
    ecmascript::ThreadSuspensionScope scope(thread);
    t1.join();
    delete serializer;
};

HWTEST_F_L0(InterOpSerializerTest, SerializeLineString)
{
    ObjectFactory *factory = ecmaVm->GetFactory();
    JSHandle<EcmaString> str(factory->NewFromASCII("123"));

    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    serializer->WriteValue(thread, JSHandle<JSTaggedValue>(str),
                           JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                           JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    std::unique_ptr<SerializeData> data = serializer->Release();
    InterOpDeserializerTest jsDeserializerTest;
    std::thread t1(&InterOpDeserializerTest::LineStringTest, jsDeserializerTest, data.release());
    {
        ecmascript::ThreadSuspensionScope suspensionScope(thread);
        t1.join();
    }
    delete serializer;
};

HWTEST_F_L0(InterOpSerializerTest, SerializeTreeString)
{
    ObjectFactory *factory = ecmaVm->GetFactory();
    JSHandle<EcmaString> str1(factory->NewFromASCII("123456789"));
    JSHandle<EcmaString> str2(factory->NewFromASCII("abcdefghi"));

    JSHandle<EcmaString> str3 = JSHandle<EcmaString>(thread, EcmaStringAccessor::Concat(ecmaVm, str1, str2));
    EXPECT_TRUE(str3.GetTaggedValue().IsTreeString());

    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    serializer->WriteValue(thread, JSHandle<JSTaggedValue>(str3),
                           JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                           JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    std::unique_ptr<SerializeData> data = serializer->Release();
    InterOpDeserializerTest jsDeserializerTest;
    std::thread t1(&InterOpDeserializerTest::TreeStringTest, jsDeserializerTest, data.release());
    {
        ecmascript::ThreadSuspensionScope suspensionScope(thread);
        t1.join();
    }
    delete serializer;
};

HWTEST_F_L0(InterOpSerializerTest, SerializeSlicedString)
{
    ObjectFactory *factory = ecmaVm->GetFactory();
    JSHandle<EcmaString> str1(factory->NewFromASCII("123456789abcedfghijk"));

    JSHandle<EcmaString> str2 =
        JSHandle<EcmaString>(thread, EcmaStringAccessor::GetSubString(ecmaVm, str1, 2, 13)); // 2: start, 3: len
    EXPECT_TRUE(str2.GetTaggedValue().IsSlicedString());

    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    serializer->WriteValue(thread, JSHandle<JSTaggedValue>(str2),
                           JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                           JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    std::unique_ptr<SerializeData> data = serializer->Release();
    InterOpDeserializerTest jsDeserializerTest;
    std::thread t1(&InterOpDeserializerTest::SlicedStringTest, jsDeserializerTest, data.release());
    {
        ecmascript::ThreadSuspensionScope suspensionScope(thread);
        t1.join();
    }
    delete serializer;
};

HWTEST_F_L0(InterOpSerializerTest, SerializeJSPlainObject1)
{
    ObjectFactory *factory = ecmaVm->GetFactory();
    JSHandle<JSObject> obj = factory->NewEmptyJSObject();

    JSHandle<JSTaggedValue> key1(factory->NewFromASCII("2"));
    JSHandle<JSTaggedValue> key2(factory->NewFromASCII("3"));
    JSHandle<JSTaggedValue> key3(factory->NewFromASCII("x"));
    JSHandle<JSTaggedValue> key4(factory->NewFromASCII("y"));
    JSHandle<JSTaggedValue> value1(thread, JSTaggedValue(1));
    JSHandle<JSTaggedValue> value2(thread, JSTaggedValue(2));
    JSHandle<JSTaggedValue> value3(thread, JSTaggedValue(3));
    JSHandle<JSTaggedValue> value4(thread, JSTaggedValue(4));

    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj), key1, value1);
    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj), key2, value2);
    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj), key3, value3);
    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj), key4, value4);

    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    serializer->WriteValue(thread, JSHandle<JSTaggedValue>(obj),
                           JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                           JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    std::unique_ptr<SerializeData> data = serializer->Release();
    InterOpDeserializerTest jsDeserializerTest;
    std::thread t1(&InterOpDeserializerTest::JSPlainObjectTest1, jsDeserializerTest, data.release());
    ecmascript::ThreadSuspensionScope scope(thread);
    t1.join();
    delete serializer;
};

HWTEST_F_L0(InterOpSerializerTest, SerializeJSPlainObject2)
{
    ObjectFactory *factory = ecmaVm->GetFactory();
    JSHandle<JSObject> obj = factory->NewEmptyJSObject();
    JSHandle<EcmaString> key1(factory->NewFromASCII("str1"));
    JSHandle<EcmaString> key2(factory->NewFromASCII("str2"));
    for (int i = 0; i < 10; i++) {
        JSHandle<JSObject> obj1 = factory->NewEmptyJSObject();
        JSHandle<EcmaString> key3(factory->NewFromASCII("str3"));
        for (int j = 0; j < 10; j++) {
            key3 = JSHandle<EcmaString>(thread, EcmaStringAccessor::Concat(ecmaVm, key3, key1));
            JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj1), JSHandle<JSTaggedValue>(key3),
                                  JSHandle<JSTaggedValue>(factory->NewEmptyJSObject()));
        }
        key2 = JSHandle<EcmaString>(thread, EcmaStringAccessor::Concat(ecmaVm, key2, key1));
        JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj), JSHandle<JSTaggedValue>(key2),
                              JSHandle<JSTaggedValue>(obj1));
    }

    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    serializer->WriteValue(thread, JSHandle<JSTaggedValue>(obj),
                           JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                           JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    std::unique_ptr<SerializeData> data = serializer->Release();
    InterOpDeserializerTest jsDeserializerTest;
    std::thread t1(&InterOpDeserializerTest::JSPlainObjectTest2, jsDeserializerTest, data.release());
    ecmascript::ThreadSuspensionScope scope(thread);
    t1.join();
    delete serializer;
};

// test dictionary mode
HWTEST_F_L0(InterOpSerializerTest, SerializeJSPlainObject3)
{
    ObjectFactory *factory = ecmaVm->GetFactory();
    JSHandle<JSObject> obj = factory->NewEmptyJSObject();
    JSHandle<EcmaString> key1(factory->NewFromASCII("str1"));
    JSHandle<EcmaString> key2(factory->NewFromASCII("str2"));
    JSHandle<JSTaggedValue> value1(thread, JSTaggedValue(1));
    for (int i = 0; i < 1030; i++) {
        key2 = JSHandle<EcmaString>(thread, EcmaStringAccessor::Concat(ecmaVm, key2, key1));
        JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj), JSHandle<JSTaggedValue>(key2), value1);
    }

    EXPECT_TRUE(obj->GetClass()->IsDictionaryMode());

    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    serializer->WriteValue(thread, JSHandle<JSTaggedValue>(obj),
                           JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                           JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    std::unique_ptr<SerializeData> data = serializer->Release();
    InterOpDeserializerTest jsDeserializerTest;
    std::thread t1(&InterOpDeserializerTest::JSPlainObjectTest3, jsDeserializerTest, data.release());
    ecmascript::ThreadSuspensionScope scope(thread);
    t1.join();
    delete serializer;
};

// test huge object serialize
HWTEST_F_L0(InterOpSerializerTest, SerializeJSPlainObject4)
{
    ObjectFactory *factory = ecmaVm->GetFactory();
    JSHandle<JSObject> obj = factory->NewEmptyJSObject();
    JSHandle<EcmaString> key1(factory->NewFromASCII("str1"));
    // new huge tagged array
    JSHandle<TaggedArray> taggedArray =
        factory->NewTaggedArray(1024 * 100, JSTaggedValue::Hole(), MemSpaceType::OLD_SPACE);

    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj), JSHandle<JSTaggedValue>(key1),
                          JSHandle<JSTaggedValue>(taggedArray));

    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    serializer->WriteValue(thread, JSHandle<JSTaggedValue>(obj),
                           JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                           JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    std::unique_ptr<SerializeData> data = serializer->Release();
    InterOpDeserializerTest jsDeserializerTest;
    std::thread t1(&InterOpDeserializerTest::JSPlainObjectTest4, jsDeserializerTest, data.release());
    ecmascript::ThreadSuspensionScope scope(thread);
    t1.join();
    delete serializer;
};

HWTEST_F_L0(InterOpSerializerTest, SerializeJSPlainObject5)
{
    ObjectFactory *factory = ecmaVm->GetFactory();
    JSHandle<JSObject> obj = factory->NewEmptyJSObject();

    JSHandle<JSTaggedValue> key1(factory->NewFromASCII("2"));
    JSHandle<JSTaggedValue> key2(factory->NewFromASCII("3"));
    JSHandle<JSTaggedValue> key3(factory->NewFromASCII("x"));
    JSHandle<JSTaggedValue> key4(factory->NewFromASCII("y"));
    JSHandle<JSTaggedValue> key5(factory->NewFromASCII("func"));
    JSHandle<JSTaggedValue> value1(thread, JSTaggedValue(1));
    JSHandle<JSTaggedValue> value2(thread, JSTaggedValue(2));
    JSHandle<JSTaggedValue> value3(thread, JSTaggedValue(3));
    JSHandle<JSTaggedValue> value4(thread, JSTaggedValue(4));
    JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
    JSHandle<JSFunction> function = factory->NewJSFunction(env, nullptr, FunctionKind::NORMAL_FUNCTION);
    EXPECT_TRUE(function->IsJSFunction());
    JSHandle<JSTaggedValue> value5 = JSHandle<JSTaggedValue>::Cast(function);

    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj), key1, value1);
    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj), key2, value2);
    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj), key3, value3);
    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj), key4, value4);
    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj), key5, value5);

    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    bool success = serializer->WriteValue(thread, JSHandle<JSTaggedValue>(obj),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    EXPECT_FALSE(success);
    std::unique_ptr<SerializeData> data = serializer->Release();
    InterOpValueDeserializer deserializer(thread, data.release());
    JSHandle<JSTaggedValue> res = deserializer.ReadValue();
    EXPECT_TRUE(res.IsEmpty());
    delete serializer;
};

HWTEST_F_L0(InterOpSerializerTest, SerializeJSError1)
{
    ObjectFactory *factory = ecmaVm->GetFactory();
    JSHandle<EcmaString> msg(factory->NewFromASCII("this is error"));
    JSHandle<JSTaggedValue> errorTag =
        JSHandle<JSTaggedValue>::Cast(factory->NewJSError(base::ErrorType::ERROR, msg, StackCheck::NO));

    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    serializer->WriteValue(thread, errorTag, JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                           JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    std::unique_ptr<SerializeData> data = serializer->Release();
    InterOpDeserializerTest jsDeserializerTest;
    std::thread t1(&InterOpDeserializerTest::JSErrorTest1, jsDeserializerTest, data.release());
    ecmascript::ThreadSuspensionScope scope(thread);
    t1.join();
    delete serializer;
};

HWTEST_F_L0(InterOpSerializerTest, SerializeJSError2)
{
    ObjectFactory *factory = ecmaVm->GetFactory();
    JSHandle<JSObject> obj = factory->NewEmptyJSObject();
    JSHandle<EcmaString> key1(factory->NewFromASCII("error1"));
    JSHandle<EcmaString> key2(factory->NewFromASCII("error2"));
    JSHandle<EcmaString> msg(factory->NewFromASCII("this is error"));
    JSHandle<JSTaggedValue> errorTag =
        JSHandle<JSTaggedValue>::Cast(factory->NewJSError(base::ErrorType::ERROR, msg, StackCheck::NO));


    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj), JSHandle<JSTaggedValue>(key1), errorTag);
    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj), JSHandle<JSTaggedValue>(key2), errorTag);

    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    serializer->WriteValue(thread, JSHandle<JSTaggedValue>(obj),
                           JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                           JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    std::unique_ptr<SerializeData> data = serializer->Release();
    InterOpDeserializerTest jsDeserializerTest;
    std::thread t1(&InterOpDeserializerTest::JSErrorTest2, jsDeserializerTest, data.release());
    ecmascript::ThreadSuspensionScope scope(thread);
    t1.join();
    delete serializer;
};

HWTEST_F_L0(InterOpSerializerTest, SerializeJSError3)
{
    ObjectFactory *factory = ecmaVm->GetFactory();
    JSHandle<JSObject> obj = factory->NewEmptyJSObject();
    JSHandle<EcmaString> key1(factory->NewFromASCII("error1"));
    JSHandle<EcmaString> key2(factory->NewFromASCII("error2"));
    JSHandle<EcmaString> key3(factory->NewFromASCII("error3"));
    JSHandle<EcmaString> key4(factory->NewFromASCII("error4"));
    JSHandle<EcmaString> key5(factory->NewFromASCII("error5"));
    JSHandle<EcmaString> key6(factory->NewFromASCII("error6"));
    JSHandle<EcmaString> key7(factory->NewFromASCII("error7"));
    JSHandle<EcmaString> msg(factory->NewFromASCII("this is error"));
    JSHandle<JSTaggedValue> error1 =
        JSHandle<JSTaggedValue>::Cast(factory->NewJSError(base::ErrorType::RANGE_ERROR, msg, StackCheck::NO));
    JSHandle<JSTaggedValue> error2 =
        JSHandle<JSTaggedValue>::Cast(factory->NewJSError(base::ErrorType::REFERENCE_ERROR, msg, StackCheck::NO));
    JSHandle<JSTaggedValue> error3 =
        JSHandle<JSTaggedValue>::Cast(factory->NewJSError(base::ErrorType::TYPE_ERROR, msg, StackCheck::NO));
    JSHandle<JSTaggedValue> error4 =
        JSHandle<JSTaggedValue>::Cast(factory->NewJSError(base::ErrorType::URI_ERROR, msg, StackCheck::NO));
    JSHandle<JSTaggedValue> error5 =
        JSHandle<JSTaggedValue>::Cast(factory->NewJSError(base::ErrorType::SYNTAX_ERROR, msg, StackCheck::NO));
    JSHandle<JSTaggedValue> error6 =
        JSHandle<JSTaggedValue>::Cast(factory->NewJSError(base::ErrorType::OOM_ERROR, msg, StackCheck::NO));
    JSHandle<JSTaggedValue> error7 =
        JSHandle<JSTaggedValue>::Cast(factory->NewJSError(base::ErrorType::TERMINATION_ERROR, msg, StackCheck::NO));

    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj), JSHandle<JSTaggedValue>(key1), error1);
    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj), JSHandle<JSTaggedValue>(key2), error2);
    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj), JSHandle<JSTaggedValue>(key3), error3);
    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj), JSHandle<JSTaggedValue>(key4), error4);
    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj), JSHandle<JSTaggedValue>(key5), error5);
    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj), JSHandle<JSTaggedValue>(key6), error6);
    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj), JSHandle<JSTaggedValue>(key7), error7);

    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    bool success = serializer->WriteValue(thread, JSHandle<JSTaggedValue>(obj),
                           JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                           JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    EXPECT_TRUE(success);
    std::unique_ptr<SerializeData> data = serializer->Release();
    InterOpDeserializerTest jsDeserializerTest;
    std::thread t1(&InterOpDeserializerTest::JSErrorTest3, jsDeserializerTest, data.release());
    ecmascript::ThreadSuspensionScope scope(thread);
    t1.join();
    delete serializer;
}

HWTEST_F_L0(InterOpSerializerTest, SerializeBigInt)
{
    ObjectFactory *factory = ecmaVm->GetFactory();
    JSHandle<JSObject> obj = factory->NewEmptyJSObject();
    JSHandle<EcmaString> key1(factory->NewFromASCII("pss"));
    JSHandle<EcmaString> key2(factory->NewFromASCII("nativeHeap"));
    CString value1 = "365769";
    CString value2 = "139900";
    JSHandle<BigInt> bigInt1 = BigIntHelper::SetBigInt(thread, value1);
    JSHandle<BigInt> bigInt2 = BigIntHelper::SetBigInt(thread, value1);

    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj), JSHandle<JSTaggedValue>(key1),
                          JSHandle<JSTaggedValue>(bigInt1));
    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj), JSHandle<JSTaggedValue>(key2),
                          JSHandle<JSTaggedValue>(bigInt2));

    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    bool success = serializer->WriteValue(thread, JSHandle<JSTaggedValue>(obj),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    EXPECT_TRUE(success) << "Serialize bigInt fail";
    std::unique_ptr<SerializeData> data = serializer->Release();
    InterOpDeserializerTest jsDeserializerTest;
    std::thread t1(&InterOpDeserializerTest::BigIntTest, jsDeserializerTest, data.release());
    ecmascript::ThreadSuspensionScope scope(thread);
    t1.join();
    delete serializer;
};

HWTEST_F_L0(InterOpSerializerTest, SerializePrimitive)
{
    ObjectFactory *factory = ecmaVm->GetFactory();
    JSHandle<JSObject> obj = factory->NewEmptyJSObject();
    JSHandle<EcmaString> keyInt(factory->NewFromASCII("int"));
    JSHandle<EcmaString> keyDouble(factory->NewFromASCII("double"));
    JSHandle<EcmaString> keyBoolean(factory->NewFromASCII("boolean"));
    JSHandle<EcmaString> keyString(factory->NewFromASCII("string"));

    int32_t intValue = 42;
    double doubleValue = 3.14159;
    bool booleanValue = true;
    JSHandle<EcmaString> stringValue(factory->NewFromASCII("Hello World"));

    JSHandle<JSPrimitiveRef> intPrimitive = factory->NewJSPrimitiveRef(
        PrimitiveType::PRIMITIVE_NUMBER, JSHandle<JSTaggedValue>(thread, JSTaggedValue(intValue)));
    JSHandle<JSPrimitiveRef> doublePrimitive = factory->NewJSPrimitiveRef(
        PrimitiveType::PRIMITIVE_NUMBER, JSHandle<JSTaggedValue>(thread, JSTaggedValue(doubleValue)));
    JSHandle<JSPrimitiveRef> booleanPrimitive = factory->NewJSPrimitiveRef(
        PrimitiveType::PRIMITIVE_BOOLEAN, JSHandle<JSTaggedValue>(thread, JSTaggedValue(booleanValue)));
    JSHandle<JSPrimitiveRef> stringPrimitive = factory->NewJSPrimitiveRef(
        PrimitiveType::PRIMITIVE_STRING, JSHandle<JSTaggedValue>(stringValue));

    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj), JSHandle<JSTaggedValue>(keyInt),
                        JSHandle<JSTaggedValue>(intPrimitive));
    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj), JSHandle<JSTaggedValue>(keyDouble),
                        JSHandle<JSTaggedValue>(doublePrimitive));
    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj), JSHandle<JSTaggedValue>(keyBoolean),
                        JSHandle<JSTaggedValue>(booleanPrimitive));
    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj), JSHandle<JSTaggedValue>(keyString),
                        JSHandle<JSTaggedValue>(stringPrimitive));

    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    bool success = serializer->WriteValue(thread, JSHandle<JSTaggedValue>(obj),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    EXPECT_TRUE(success) << "Serialize primitive types failed";
    std::unique_ptr<SerializeData> data = serializer->Release();
    InterOpDeserializerTest jsDeserializerTest;
    std::thread t1(&InterOpDeserializerTest::PrimitiveTest, jsDeserializerTest, data.release());
    ecmascript::ThreadSuspensionScope scope(thread);
    t1.join();
    delete serializer;
}

static void* Detach(void *param1, void *param2, void *hint, void *detachData)
{
    GTEST_LOG_(INFO) << "detach is running";
    if (param1 == nullptr && param2 == nullptr) {
        GTEST_LOG_(INFO) << "detach: two params is nullptr";
    }
    if (hint == nullptr && detachData) {
        GTEST_LOG_(INFO) << "detach: hint is nullptr";
    }
    return nullptr;
}

static void* Attach([[maybe_unused]] void *enginePointer, [[maybe_unused]] void *buffer, [[maybe_unused]] void *hint,
                    [[maybe_unused]] void *attachData)
{
    GTEST_LOG_(INFO) << "attach is running";
    return nullptr;
}

static panda::JSNApi::NativeBindingInfo* CreateNativeBindingInfo(void* attach, void* detach)
{
    GTEST_LOG_(INFO) << "CreateNativeBindingInfo";
    auto info = panda::JSNApi::NativeBindingInfo::CreateNewInstance();
    info->attachFunc = attach;
    info->detachFunc = detach;
    return info;
}

HWTEST_F_L0(InterOpSerializerTest, SerializeNativeBindingObject1)
{
    ObjectFactory *factory = ecmaVm->GetFactory();
    JSHandle<JSObject> obj1 = factory->NewEmptyJSObject();

    JSHandle<JSTaggedValue> key1 = thread->GlobalConstants()->GetHandledNativeBindingSymbol();
    JSHandle<JSTaggedValue> key2(factory->NewFromASCII("x"));
    auto info = CreateNativeBindingInfo(reinterpret_cast<void*>(Attach), reinterpret_cast<void*>(Detach));
    JSHandle<JSTaggedValue> value1(factory->NewJSNativePointer(reinterpret_cast<void*>(info)));
    JSHandle<JSTaggedValue> value2(thread, JSTaggedValue(1));

    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj1), key1, value1);
    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj1), key2, value2);
    obj1->GetClass()->SetIsNativeBindingObject(true);

    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    bool success = serializer->WriteValue(thread, JSHandle<JSTaggedValue>(obj1),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    EXPECT_TRUE(success) << "Serialize fail";
    std::unique_ptr<SerializeData> data = serializer->Release();
    InterOpDeserializerTest jsDeserializerTest;
    std::thread t1(&InterOpDeserializerTest::NativeBindingObjectTest1, jsDeserializerTest, data.release());
    ecmascript::ThreadSuspensionScope scope(thread);
    t1.join();
    delete serializer;
}

HWTEST_F_L0(InterOpSerializerTest, SerializeNativeBindingObject2)
{
    ObjectFactory *factory = ecmaVm->GetFactory();
    JSHandle<JSObject> obj1 = factory->NewEmptyJSObject();
    JSHandle<JSObject> obj2 = factory->NewEmptyJSObject();

    JSHandle<JSTaggedValue> key1 = thread->GlobalConstants()->GetHandledNativeBindingSymbol();
    JSHandle<JSTaggedValue> key2(factory->NewFromASCII("x"));
    JSHandle<JSTaggedValue> key3(factory->NewFromASCII("xx"));
    auto info = CreateNativeBindingInfo(reinterpret_cast<void*>(Attach), reinterpret_cast<void*>(Detach));
    JSHandle<JSTaggedValue> value1(factory->NewJSNativePointer(reinterpret_cast<void*>(info)));
    JSHandle<JSTaggedValue> value2(thread, JSTaggedValue(1));
    JSHandle<JSTaggedValue> value3(thread, JSTaggedValue(2));

    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj1), key1, value1);
    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj1), key2, value2);
    obj1->GetClass()->SetIsNativeBindingObject(true);
    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj2), key2, JSHandle<JSTaggedValue>(obj1));
    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj2), key3, value3);

    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    bool success = serializer->WriteValue(thread, JSHandle<JSTaggedValue>(obj2),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    EXPECT_TRUE(success) << "Serialize fail";
    std::unique_ptr<SerializeData> data = serializer->Release();
    InterOpDeserializerTest jsDeserializerTest;
    std::thread t1(&InterOpDeserializerTest::NativeBindingObjectTest2, jsDeserializerTest, data.release());
    ecmascript::ThreadSuspensionScope scope(thread);
    t1.join();
    delete serializer;
}

HWTEST_F_L0(InterOpSerializerTest, SerializeNativeBindingObject3)
{
    ObjectFactory *factory = ecmaVm->GetFactory();
    JSHandle<JSObject> obj1 = factory->NewEmptyJSObject();

    JSHandle<JSTaggedValue> key1 = thread->GlobalConstants()->GetHandledNativeBindingSymbol();
    JSHandle<JSTaggedValue> key2(factory->NewFromASCII("x"));
    auto info = CreateNativeBindingInfo(reinterpret_cast<void*>(Attach), nullptr);
    JSHandle<JSTaggedValue> value1(factory->NewJSNativePointer(reinterpret_cast<void*>(info)));
    JSHandle<JSTaggedValue> value2(thread, JSTaggedValue(1));

    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj1), key1, value1);
    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj1), key2, value2);
    obj1->GetClass()->SetIsNativeBindingObject(true);

    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    bool success = serializer->WriteValue(thread, JSHandle<JSTaggedValue>(obj1),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    EXPECT_FALSE(success);
    std::unique_ptr<SerializeData> data = serializer->Release();
    InterOpValueDeserializer deserializer(thread, data.release());
    JSHandle<JSTaggedValue> res = deserializer.ReadValue();
    EXPECT_TRUE(res.IsEmpty());
    delete serializer;
}

HWTEST_F_L0(InterOpSerializerTest, TestSerializeJSSet)
{
    ObjectFactory *factory = ecmaVm->GetFactory();
    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();

    JSHandle<JSTaggedValue> constructor = env->GetBuiltinsSetFunction();
    JSHandle<JSSet> set =
        JSHandle<JSSet>::Cast(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), constructor));
    JSHandle<LinkedHashSet> linkedSet = LinkedHashSet::Create(thread);
    set->SetLinkedSet(thread, linkedSet);
    // set property to set
    JSHandle<JSTaggedValue> value1(thread, JSTaggedValue(7));
    JSHandle<JSTaggedValue> value2(thread, JSTaggedValue(9));
    JSHandle<JSTaggedValue> value3(factory->NewFromASCII("x"));
    JSHandle<JSTaggedValue> value4(factory->NewFromASCII("y"));

    JSSet::Add(thread, set, value1);
    JSSet::Add(thread, set, value2);
    JSSet::Add(thread, set, value3);
    JSSet::Add(thread, set, value4);

    // set property to object
    JSHandle<JSTaggedValue> key1(factory->NewFromASCII("5"));
    JSHandle<JSTaggedValue> key2(factory->NewFromASCII("6"));

    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(set), key1, value1);
    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(set), key2, value2);

    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    bool success = serializer->WriteValue(thread, JSHandle<JSTaggedValue>(set),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    EXPECT_TRUE(success) << "Serialize JSSet fail";
    std::unique_ptr<SerializeData> data = serializer->Release();
    InterOpDeserializerTest jsDeserializerTest;
    std::thread t1(&InterOpDeserializerTest::JSSetTest, jsDeserializerTest, data.release());
    ecmascript::ThreadSuspensionScope scope(thread);
    t1.join();
    delete serializer;
};

JSDate *JSDateCreate(EcmaVM *ecmaVM)
{
    ObjectFactory *factory = ecmaVM->GetFactory();
    JSHandle<GlobalEnv> globalEnv = ecmaVM->GetGlobalEnv();
    JSHandle<JSTaggedValue> dateFunction = globalEnv->GetDateFunction();
    JSHandle<JSDate> dateObject =
        JSHandle<JSDate>::Cast(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(dateFunction), dateFunction));
    return *dateObject;
}

HWTEST_F_L0(InterOpSerializerTest, SerializeDate)
{
    double tm = 28 * 60 * 60 * 1000;
    JSHandle<JSDate> jsDate(thread, JSDateCreate(ecmaVm));
    jsDate->SetTimeValue(thread, JSTaggedValue(tm));

    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    bool success = serializer->WriteValue(thread, JSHandle<JSTaggedValue>(jsDate),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    EXPECT_TRUE(success) << "Serialize JSDate fail";
    std::unique_ptr<SerializeData> data = serializer->Release();
    InterOpDeserializerTest jsDeserializerTest;
    std::thread t1(&InterOpDeserializerTest::JSDateTest, jsDeserializerTest, data.release());
    ecmascript::ThreadSuspensionScope scope(thread);
    t1.join();
    delete serializer;
};

JSMap *CreateMap(JSThread *thread)
{
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
    JSHandle<JSTaggedValue> constructor = env->GetBuiltinsMapFunction();
    JSHandle<JSMap> map =
        JSHandle<JSMap>::Cast(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), constructor));
    JSHandle<LinkedHashMap> linkedMap = LinkedHashMap::Create(thread);
    map->SetLinkedMap(thread, linkedMap);
    return *map;
}

HWTEST_F_L0(InterOpSerializerTest, SerializeJSMap)
{
    JSHandle<JSMap> map(thread, CreateMap(thread));
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    JSHandle<JSTaggedValue> key1(factory->NewFromASCII("3"));
    JSHandle<JSTaggedValue> value1(thread, JSTaggedValue(12345));
    JSMap::Set(thread, map, key1, value1);
    JSHandle<JSTaggedValue> key2(factory->NewFromASCII("key1"));
    JSHandle<JSTaggedValue> value2(thread, JSTaggedValue(34567));
    JSMap::Set(thread, map, key2, value2);

    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    bool success = serializer->WriteValue(thread, JSHandle<JSTaggedValue>(map),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    EXPECT_TRUE(success) << "Serialize JSMap fail";
    std::unique_ptr<SerializeData> data = serializer->Release();
    InterOpDeserializerTest jsDeserializerTest;
    std::thread t1(&InterOpDeserializerTest::JSMapTest, jsDeserializerTest, data.release(), map);
    ecmascript::ThreadSuspensionScope scope(thread);
    t1.join();
    delete serializer;
};

HWTEST_F_L0(InterOpSerializerTest, SerializeJSRegExp)
{
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
    JSHandle<JSTaggedValue> target = env->GetRegExpFunction();
    JSHandle<JSRegExp> jsRegexp =
        JSHandle<JSRegExp>::Cast(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(target), target));
    JSHandle<EcmaString> pattern = thread->GetEcmaVM()->GetFactory()->NewFromASCII("key2");
    char buffer[] = "1234567";  // use char to simulate bytecode
    uint32_t bufferSize = 7;
    factory->NewJSRegExpByteCodeData(jsRegexp, static_cast<void *>(buffer), bufferSize);
    jsRegexp->SetOriginalSource(thread, JSHandle<JSTaggedValue>(pattern));
    jsRegexp->SetOriginalFlags(thread, JSTaggedValue(0));

    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    bool success = serializer->WriteValue(thread, JSHandle<JSTaggedValue>(jsRegexp),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    EXPECT_TRUE(success) << "Serialize JSRegExp fail";
    std::unique_ptr<SerializeData> data = serializer->Release();
    InterOpDeserializerTest jsDeserializerTest;
    std::thread t1(&InterOpDeserializerTest::JSRegexpTest, jsDeserializerTest, data.release());
    ecmascript::ThreadSuspensionScope scope(thread);
    t1.join();
    delete serializer;
};

HWTEST_F_L0(InterOpSerializerTest, TestSerializeJSArray)
{
    ObjectFactory *factory = ecmaVm->GetFactory();
    JSHandle<JSArray> array = factory->NewJSArray();

    // set property to object
    JSHandle<JSTaggedValue> key1(factory->NewFromASCII("abasd"));
    JSHandle<JSTaggedValue> key2(factory->NewFromASCII("qweqwedasd"));

    JSHandle<JSTaggedValue> value1(thread, JSTaggedValue(7));
    JSHandle<JSTaggedValue> value2(thread, JSTaggedValue(9));

    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(array), key1, value1);
    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(array), key2, value2);

    // set value to array
    array->SetArrayLength(thread, 20);
    for (int i = 0; i < 20; i++) {
        JSHandle<JSTaggedValue> data(thread, JSTaggedValue(i));
        JSArray::FastSetPropertyByValue(thread, JSHandle<JSTaggedValue>::Cast(array), i, data);
    }

    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    bool success = serializer->WriteValue(thread, JSHandle<JSTaggedValue>(array),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    EXPECT_TRUE(success) << "Serialize JSArray fail";
    std::unique_ptr<SerializeData> data = serializer->Release();
    InterOpDeserializerTest jsDeserializerTest;
    std::thread t1(&InterOpDeserializerTest::JSArrayTest, jsDeserializerTest, data.release());
    ecmascript::ThreadSuspensionScope scope(thread);
    t1.join();
    delete serializer;
};

HWTEST_F_L0(InterOpSerializerTest, SerializeEcmaString1)
{
    const char *rawStr = "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"\
    "sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"\
    "sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"\
    "ssssss";
    JSHandle<EcmaString> ecmaString = thread->GetEcmaVM()->GetFactory()->NewFromASCII(rawStr);

    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    bool success = serializer->WriteValue(thread, JSHandle<JSTaggedValue>(ecmaString),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    EXPECT_TRUE(success) << "Serialize EcmaString fail";
    std::unique_ptr<SerializeData> data = serializer->Release();
    InterOpDeserializerTest jsDeserializerTest;
    std::thread t1(&InterOpDeserializerTest::EcmaStringTest1, jsDeserializerTest, data.release());
    ecmascript::ThreadSuspensionScope scope(thread);
    t1.join();
    delete serializer;
};

// Test EcmaString contains Chinese Text
HWTEST_F_L0(InterOpSerializerTest, SerializeEcmaString2)
{
    std::string rawStr = "你好,世界";
    JSHandle<EcmaString> ecmaString = thread->GetEcmaVM()->GetFactory()->NewFromStdString(rawStr);

    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    bool success = serializer->WriteValue(thread, JSHandle<JSTaggedValue>(ecmaString),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    EXPECT_TRUE(success) << "Serialize EcmaString fail";
    std::unique_ptr<SerializeData> data = serializer->Release();
    InterOpDeserializerTest jsDeserializerTest;
    std::thread t1(&InterOpDeserializerTest::EcmaStringTest2, jsDeserializerTest, data.release());
    ecmascript::ThreadSuspensionScope scope(thread);
    t1.join();
    delete serializer;
};

HWTEST_F_L0(InterOpSerializerTest, SerializeInt32_t)
{
    int32_t a = 64, min = -2147483648, b = -63;
    JSTaggedValue aTag(a), minTag(min), bTag(b);

    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    serializer->SerializeJSTaggedValue(aTag);
    serializer->SerializeJSTaggedValue(minTag);
    serializer->SerializeJSTaggedValue(bTag);
    std::unique_ptr<SerializeData> data = serializer->Release();

    InterOpDeserializerTest jsDeserializerTest;
    std::thread t1(&InterOpDeserializerTest::Int32Test, jsDeserializerTest, data.release());
    ecmascript::ThreadSuspensionScope scope(thread);
    t1.join();
    delete serializer;
};

HWTEST_F_L0(InterOpSerializerTest, SerializeDouble)
{
    double a = 3.1415926535, b = -3.1415926535;
    JSTaggedValue aTag(a), bTag(b);

    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    serializer->SerializeJSTaggedValue(aTag);
    serializer->SerializeJSTaggedValue(bTag);
    std::unique_ptr<SerializeData> data = serializer->Release();

    InterOpDeserializerTest jsDeserializerTest;
    std::thread t1(&InterOpDeserializerTest::DoubleTest, jsDeserializerTest, data.release());
    ecmascript::ThreadSuspensionScope scope(thread);
    t1.join();
    delete serializer;
};

JSArrayBuffer *CreateJSArrayBuffer(JSThread *thread)
{
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
    JSHandle<JSTaggedValue> target = env->GetArrayBufferFunction();
    JSHandle<JSArrayBuffer> jsArrayBuffer =
        JSHandle<JSArrayBuffer>::Cast(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(target), target));
    return *jsArrayBuffer;
}

HWTEST_F_L0(InterOpSerializerTest, SerializeObjectWithConcurrentFunction)
{
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
    JSHandle<JSFunction> concurrentFunction1 = factory->NewJSFunction(env, nullptr, FunctionKind::CONCURRENT_FUNCTION);
    EXPECT_TRUE(concurrentFunction1->IsJSFunction());
    EXPECT_TRUE(concurrentFunction1->GetFunctionKind(thread) == ecmascript::FunctionKind::CONCURRENT_FUNCTION);
    JSHandle<JSFunction> concurrentFunction2 = factory->NewJSFunction(env, nullptr, FunctionKind::CONCURRENT_FUNCTION);
    EXPECT_TRUE(concurrentFunction2->IsJSFunction());
    EXPECT_TRUE(concurrentFunction2->GetFunctionKind(thread) == ecmascript::FunctionKind::CONCURRENT_FUNCTION);
    JSHandle<JSTaggedValue> key1(factory->NewFromASCII("1"));
    JSHandle<JSTaggedValue> key2(factory->NewFromASCII("2"));
    JSHandle<JSTaggedValue> key3(factory->NewFromASCII("abc"));
    JSHandle<JSTaggedValue> key4(factory->NewFromASCII("4"));
    JSHandle<JSTaggedValue> key5(factory->NewFromASCII("key"));
    JSHandle<JSTaggedValue> value1(thread, JSTaggedValue(12345));
    JSHandle<JSTaggedValue> value2(factory->NewFromASCII("def"));
    JSHandle<JSTaggedValue> value3(factory->NewFromASCII("value"));
    JSHandle<JSObject> obj = factory->NewEmptyJSObject();
    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj), key1, value1);
    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj), key2, JSHandle<JSTaggedValue>(concurrentFunction1));
    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj), key3, value2);
    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj), key4, value1);
    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj), key5, JSHandle<JSTaggedValue>(concurrentFunction2));

    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    bool success = serializer->WriteValue(thread, JSHandle<JSTaggedValue>(obj),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    EXPECT_TRUE(success) << "Serialize concurrent function fail";
    std::unique_ptr<SerializeData> data = serializer->Release();
    InterOpDeserializerTest jsDeserializerTest;

    std::thread t1(&InterOpDeserializerTest::ObjectWithConcurrentFunctionTest, jsDeserializerTest, data.release());
    ecmascript::ThreadSuspensionScope scope(thread);
    t1.join();
    delete serializer;
};

// not support most function except concurrent function
HWTEST_F_L0(InterOpSerializerTest, SerializeFunction)
{
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
    JSHandle<JSFunction> function = factory->NewJSFunction(env, nullptr, FunctionKind::NORMAL_FUNCTION);
    EXPECT_TRUE(function->IsJSFunction());

    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    bool success = serializer->WriteValue(thread, JSHandle<JSTaggedValue>(function),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    EXPECT_FALSE(success);
    std::unique_ptr<SerializeData> data = serializer->Release();
    InterOpValueDeserializer deserializer(thread, data.release());
    JSHandle<JSTaggedValue> res = deserializer.ReadValue();
    EXPECT_TRUE(res.IsEmpty());
    delete serializer;
}

HWTEST_F_L0(InterOpSerializerTest, SerializeApiFunction)
{
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
    JSHandle<JSFunction> function = factory->NewNormalJSApiFunction(env, nullptr);
    EXPECT_TRUE(function->IsJSFunction());

    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    bool success = serializer->WriteValue(thread, JSHandle<JSTaggedValue>(function),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    EXPECT_FALSE(success);
    std::unique_ptr<SerializeData> data = serializer->Release();
    InterOpValueDeserializer deserializer(thread, data.release());
    JSHandle<JSTaggedValue> res = deserializer.ReadValue();
    EXPECT_TRUE(res.IsEmpty());
    delete serializer;
}

// Test transfer JSArrayBuffer
HWTEST_F_L0(InterOpSerializerTest, TransferJSArrayBuffer1)
{
    ObjectFactory *factory = ecmaVm->GetFactory();

    // create a JSArrayBuffer
    size_t length = 5;
    uint8_t value = 100;
    void *buffer = ecmaVm->GetNativeAreaAllocator()->AllocateBuffer(length);
    if (memset_s(buffer, length, value, length) != EOK) {
        LOG_ECMA(FATAL) << "this branch is unreachable";
        UNREACHABLE();
    }
    JSHandle<JSArrayBuffer> arrBuf = factory->NewJSArrayBuffer(buffer,
        length, NativeAreaAllocator::FreeBufferFunc, ecmaVm->GetNativeAreaAllocator());
    JSHandle<JSTaggedValue> arrBufTag = JSHandle<JSTaggedValue>(arrBuf);

    JSHandle<JSArray> array = factory->NewJSArray();

    // set value to array
    array->SetArrayLength(thread, 1);
    JSArray::FastSetPropertyByValue(thread, JSHandle<JSTaggedValue>(array), 0, arrBufTag);

    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    bool success = serializer->WriteValue(thread, arrBufTag, JSHandle<JSTaggedValue>(array),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    EXPECT_TRUE(success) << "Serialize transfer JSArrayBuffer fail";
    std::unique_ptr<SerializeData> data = serializer->Release();
    InterOpDeserializerTest jsDeserializerTest;
    std::thread t1(&InterOpDeserializerTest::TransferJSArrayBufferTest1,
                   jsDeserializerTest,
                   data.release(),
                   reinterpret_cast<uintptr_t>(buffer));
    ecmascript::ThreadSuspensionScope scope(thread);
    t1.join();
    delete serializer;
    // test if detached
    EXPECT_TRUE(arrBuf->IsDetach(thread));
};

// Test serialize JSArrayBuffer that not transfer
HWTEST_F_L0(InterOpSerializerTest, TransferJSArrayBuffer2)
{
    ObjectFactory *factory = ecmaVm->GetFactory();

    // create a JSArrayBuffer
    size_t length = 5;
    uint8_t value = 100;
    void *buffer = ecmaVm->GetNativeAreaAllocator()->AllocateBuffer(length);
    if (memset_s(buffer, length, value, length) != EOK) {
        LOG_ECMA(FATAL) << "this branch is unreachable";
        UNREACHABLE();
    }
    JSHandle<JSArrayBuffer> arrBuf = factory->NewJSArrayBuffer(buffer,
        length, NativeAreaAllocator::FreeBufferFunc, ecmaVm->GetNativeAreaAllocator());
    JSHandle<JSTaggedValue> arrBufTag = JSHandle<JSTaggedValue>::Cast(arrBuf);

    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    bool success = serializer->WriteValue(thread, arrBufTag,
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    EXPECT_TRUE(success) << "Serialize not transfer JSArrayBuffer fail";
    std::unique_ptr<SerializeData> data = serializer->Release();
    InterOpDeserializerTest jsDeserializerTest;
    std::thread t1(&InterOpDeserializerTest::TransferJSArrayBufferTest2,
                   jsDeserializerTest,
                   data.release(),
                   reinterpret_cast<uintptr_t>(buffer));
    ecmascript::ThreadSuspensionScope scope(thread);
    t1.join();
    delete serializer;
    // test if detached
    EXPECT_FALSE(arrBuf->IsDetach(thread));
};

// Test serialize an empty JSArrayBuffer
HWTEST_F_L0(InterOpSerializerTest, TransferJSArrayBuffer3)
{
    ObjectFactory *factory = ecmaVm->GetFactory();

    // create a JSArrayBuffer
    JSHandle<JSArrayBuffer> arrBuf = factory->NewJSArrayBuffer(0);
    JSHandle<JSTaggedValue> arrBufTag = JSHandle<JSTaggedValue>::Cast(arrBuf);

    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    bool success = serializer->WriteValue(thread, arrBufTag,
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    EXPECT_TRUE(success) << "Serialize empty JSArrayBuffer fail";
    std::unique_ptr<SerializeData> data = serializer->Release();
    InterOpDeserializerTest jsDeserializerTest;
    std::thread t1(&InterOpDeserializerTest::TransferJSArrayBufferTest3, jsDeserializerTest, data.release());
    ecmascript::ThreadSuspensionScope scope(thread);
    t1.join();
    delete serializer;
    // test if detached
    EXPECT_FALSE(arrBuf->IsDetach(thread));
};

// Test serialize JSArrayBuffer with external native buffer that not transfer
HWTEST_F_L0(InterOpSerializerTest, TransferJSArrayBuffer4)
{
    ObjectFactory *factory = ecmaVm->GetFactory();

    // create a JSArrayBuffer
    size_t length = 5;
    uint8_t value = 100;
    void *buffer = reinterpret_cast<void *>(malloc(length));
    if (memset_s(buffer, length, value, length) != EOK) {
        LOG_ECMA(FATAL) << "this branch is unreachable";
        UNREACHABLE();
    }
    JSHandle<JSArrayBuffer> arrBuf = factory->NewJSArrayBuffer(buffer, length, nullptr, nullptr);
    JSHandle<JSTaggedValue> arrBufTag = JSHandle<JSTaggedValue>::Cast(arrBuf);

    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    bool res = serializer->WriteValue(thread, arrBufTag,
                                      JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                      JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    EXPECT_FALSE(res) << "serialize JSArrayBuffer with external native shall not clone it";
    free(buffer);
};

void ArrayBufferDeleter([[maybe_unused]] void *env, void *buf, [[maybe_unused]] void *data)
{
    free(buf);
}

// Test serialize JSArrayBuffer with external native buffer that transfer
HWTEST_F_L0(InterOpSerializerTest, TransferJSArrayBuffer5)
{
    ObjectFactory *factory = ecmaVm->GetFactory();

    // create a JSArrayBuffer
    size_t length = 5;
    uint8_t value = 100;
    void *buffer = reinterpret_cast<void *>(malloc(length));
    if (memset_s(buffer, length, value, length) != EOK) {
        LOG_ECMA(FATAL) << "this branch is unreachable";
        UNREACHABLE();
    }
    JSHandle<JSArrayBuffer> arrBuf = factory->NewJSArrayBuffer(buffer, length, ArrayBufferDeleter, nullptr);
    JSHandle<JSTaggedValue> arrBufTag = JSHandle<JSTaggedValue>::Cast(arrBuf);

    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread, true);
    bool res = serializer->WriteValue(thread, arrBufTag,
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    EXPECT_TRUE(res) << "serialize JSArrayBuffer with external pointer fail";
    EXPECT_TRUE(arrBuf->IsDetach(thread));
    std::unique_ptr<SerializeData> data = serializer->Release();
    InterOpDeserializerTest jsDeserializerTest;
    std::thread t1(&InterOpDeserializerTest::TransferJSArrayBufferTest5, jsDeserializerTest, data.release());
    ecmascript::ThreadSuspensionScope scope(thread);
    t1.join();
    delete serializer;
};

HWTEST_F_L0(InterOpSerializerTest, TransferJSArrayBuffer6)
{
    ObjectFactory *factory = ecmaVm->GetFactory();
    JSHandle<JSObject> obj = factory->NewEmptyJSObject();
    JSHandle<EcmaString> transfer(factory->NewFromASCII("transfer"));
    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    bool success = serializer->WriteValue(thread, JSHandle<JSTaggedValue>(obj),
                                          JSHandle<JSTaggedValue>(transfer),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    EXPECT_FALSE(success);
    std::unique_ptr<SerializeData> data = serializer->Release();
    InterOpValueDeserializer deserializer(thread, data.release());
    JSHandle<JSTaggedValue> res = deserializer.ReadValue();
    EXPECT_TRUE(res.IsEmpty());
    delete serializer;
};

HWTEST_F_L0(InterOpSerializerTest, TransferJSArrayBuffer7)
{
    ObjectFactory *factory = ecmaVm->GetFactory();
    JSHandle<JSObject> obj = factory->NewEmptyJSObject();
    JSHandle<EcmaString> transfer(factory->NewFromASCII("transfer"));
    JSHandle<JSArray> array = factory->NewJSArray();
    // set value to array
    array->SetArrayLength(thread, 1);
    JSArray::FastSetPropertyByValue(thread, JSHandle<JSTaggedValue>(array), 0, JSHandle<JSTaggedValue>(transfer));
    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    bool success = serializer->WriteValue(thread, JSHandle<JSTaggedValue>(obj),
                                          JSHandle<JSTaggedValue>(array),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    EXPECT_FALSE(success);
    std::unique_ptr<SerializeData> data = serializer->Release();
    InterOpValueDeserializer deserializer(thread, data.release());
    JSHandle<JSTaggedValue> res = deserializer.ReadValue();
    EXPECT_TRUE(res.IsEmpty());
    delete serializer;
};

HWTEST_F_L0(InterOpSerializerTest, SerializeJSArrayBufferShared2)
{
    std::string msg = "hello world";
    int msgBufferLen = static_cast<int>(msg.length()) + 1;
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    JSHandle<JSArrayBuffer> jsArrayBuffer = factory->NewJSSharedArrayBuffer(msgBufferLen);
    JSHandle<JSTaggedValue> BufferData(thread, jsArrayBuffer->GetArrayBufferData(thread));
    JSHandle<JSNativePointer> resNp = JSHandle<JSNativePointer>::Cast(BufferData);
    void *buffer = resNp->GetExternalPointer();
    if (memcpy_s(buffer, msgBufferLen, msg.c_str(), msgBufferLen) != EOK) {
        EXPECT_TRUE(false) << " memcpy error";
    }

    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    bool success = serializer->WriteValue(thread, JSHandle<JSTaggedValue>(jsArrayBuffer),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    EXPECT_TRUE(success) << "Serialize JSSharedArrayBuffer fail";
    std::unique_ptr<SerializeData> data = serializer->Release();
    InterOpDeserializerTest jsDeserializerTest;
    std::string changeStr = "world hello";
    std::thread t1(&InterOpDeserializerTest::JSSharedArrayBufferTest,
                   jsDeserializerTest, data.release(), 12, changeStr.c_str());
    ecmascript::ThreadSuspensionScope scope(thread);
    t1.join();
    EXPECT_TRUE(strcmp((char *)buffer, "world hello") == 0) << "Serialize JSArrayBuffer fail";
    delete serializer;
};

HWTEST_F_L0(InterOpSerializerTest, SerializeJSArrayBufferShared3)
{
    std::string msg = "hello world";
    int msgBufferLen = static_cast<int>(msg.length()) + 1;
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    JSHandle<JSArrayBuffer> jsArrayBuffer = factory->NewJSSharedArrayBuffer(msgBufferLen);
    JSHandle<JSTaggedValue> BufferData(thread, jsArrayBuffer->GetArrayBufferData(thread));
    JSHandle<JSNativePointer> resNp = JSHandle<JSNativePointer>::Cast(BufferData);
    void *buffer = resNp->GetExternalPointer();
    if (memcpy_s(buffer, msgBufferLen, msg.c_str(), msgBufferLen) != EOK) {
        EXPECT_TRUE(false) << " memcpy error";
    }

    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    bool success = serializer->WriteValue(thread, JSHandle<JSTaggedValue>(jsArrayBuffer),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    EXPECT_TRUE(success) << "Serialize JSSharedArrayBuffer fail";
    std::unique_ptr<SerializeData> data = serializer->Release();
    InterOpDeserializerTest jsDeserializerTest;
    std::string changeStr = "world hello";
    std::thread t1(&InterOpDeserializerTest::JSSharedArrayBufferTest,
                   jsDeserializerTest, data.get(), 12, changeStr.c_str());
    {
        ecmascript::ThreadSuspensionScope scope(thread);
        t1.join();
        EXPECT_TRUE(strcmp((char *)buffer, "world hello") == 0) << "Serialize JSArrayBuffer fail";
        changeStr = "world hella";
        InterOpDeserializerTest jsDeserializerTest1;
        std::thread t2(&InterOpDeserializerTest::JSSharedArrayBufferTest,
                    jsDeserializerTest1, data.get(), 12, changeStr.c_str());
        t2.join();
        EXPECT_TRUE(strcmp((char *)buffer, "world hella") == 0) << "Serialize JSArrayBuffer fail";
        changeStr = "world hellb";
        InterOpDeserializerTest jsDeserializerTest2;
        std::thread t3(&InterOpDeserializerTest::JSSharedArrayBufferTest,
                    jsDeserializerTest2, data.get(), 12, changeStr.c_str());
        t3.join();
        EXPECT_TRUE(strcmp((char *)buffer, "world hellb") == 0) << "Serialize JSArrayBuffer fail";
    }
    delete serializer;
    data.reset();
    EXPECT_TRUE(JSHandle<JSTaggedValue>(jsArrayBuffer)->IsSharedArrayBuffer());
};

HWTEST_F_L0(InterOpSerializerTest, SerializeJSNativePointer)
{
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    JSHandle<JSNativePointer> np = factory->NewJSNativePointer(nullptr);
    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    bool success = serializer->WriteValue(thread, JSHandle<JSTaggedValue>(np),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    // Don't support serialize JSNativePointer directly
    EXPECT_TRUE(!success);
    std::unique_ptr<SerializeData> data = serializer->Release();
    EXPECT_TRUE(data->IsIncompleteData());
    delete serializer;
}

JSArrayBuffer *CreateTestJSArrayBuffer(JSThread *thread)
{
    JSHandle<JSArrayBuffer> jsArrayBuffer(thread, CreateJSArrayBuffer(thread));
    int32_t byteLength = 10;
    thread->GetEcmaVM()->GetFactory()->NewJSArrayBufferData(jsArrayBuffer, byteLength);
    jsArrayBuffer->SetArrayBufferByteLength(byteLength);
    JSHandle<JSTaggedValue> obj = JSHandle<JSTaggedValue>(jsArrayBuffer);
    JSMutableHandle<JSTaggedValue> number(thread, JSTaggedValue::Undefined());
    for (int i = 0; i < 10; i++) { // 10: arrayLength
        number.Update(JSTaggedValue(i));
        BuiltinsArrayBuffer::SetValueInBuffer(thread, obj.GetTaggedValue(), i, DataViewType::UINT8,
            number, true);
    }
    return *jsArrayBuffer;
}

HWTEST_F_L0(InterOpSerializerTest, SerializeJSTypedArray1)
{
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
    JSHandle<JSTaggedValue> target = env->GetInt8ArrayFunction();
    JSHandle<JSTypedArray> int8Array =
        JSHandle<JSTypedArray>::Cast(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(target), target));
    JSHandle<JSTaggedValue> viewedArrayBuffer(thread, CreateTestJSArrayBuffer(thread));
    int8Array->SetViewedArrayBufferOrByteArray(thread, viewedArrayBuffer);
    int byteLength = 10;
    int byteOffset = 0;
    int arrayLength = (byteLength - byteOffset) / (sizeof(int8_t));
    int8Array->SetByteLength(byteLength);
    int8Array->SetByteOffset(byteOffset);
    int8Array->SetContentType(DataViewType::INT8);
    int8Array->SetArrayLength(arrayLength);

    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    bool success = serializer->WriteValue(thread, JSHandle<JSTaggedValue>(int8Array),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    EXPECT_TRUE(success) << "Serialize type array fail";
    std::unique_ptr<SerializeData> data = serializer->Release();
    InterOpDeserializerTest jsDeserializerTest;
    std::thread t1(&InterOpDeserializerTest::TypedArrayTest1, jsDeserializerTest, data.release());
    ecmascript::ThreadSuspensionScope scope(thread);
    t1.join();
    delete serializer;
};

HWTEST_F_L0(InterOpSerializerTest, SerializeJSTypedArray2)
{
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
    JSHandle<JSTaggedValue> target = env->GetInt8ArrayFunction();
    JSHandle<JSTypedArray> int8Array =
        JSHandle<JSTypedArray>::Cast(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(target), target));
    uint8_t value = 255; // 255 : test case
    JSTaggedType val = JSTaggedValue(value).GetRawData();
    int byteArrayLength = 10; // 10: arrayLength
    JSHandle<ByteArray> byteArray = factory->NewByteArray(byteArrayLength, sizeof(value));
    for (int i = 0; i < byteArrayLength; i++) {
        byteArray->Set(thread, i, DataViewType::UINT8, val);
    }
    int8Array->SetViewedArrayBufferOrByteArray(thread, byteArray);
    int byteLength = 10;
    int byteOffset = 0;
    int arrayLength = (byteLength - byteOffset) / (sizeof(int8_t));
    int8Array->SetByteLength(byteLength);
    int8Array->SetByteOffset(byteOffset);
    int8Array->SetContentType(DataViewType::INT8);
    int8Array->SetArrayLength(arrayLength);

    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    bool success = serializer->WriteValue(thread, JSHandle<JSTaggedValue>(int8Array),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    EXPECT_TRUE(success) << "Serialize type array fail";
    std::unique_ptr<SerializeData> data = serializer->Release();
    InterOpDeserializerTest jsDeserializerTest;
    std::thread t1(&InterOpDeserializerTest::TypedArrayTest2, jsDeserializerTest, data.release());
    ecmascript::ThreadSuspensionScope scope(thread);
    t1.join();
    delete serializer;
};

JSHandle<JSObject> CreateEmptySObject(JSThread *thread)
{
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    const GlobalEnvConstants *globalConst = thread->GlobalConstants();
    JSHandle<JSTaggedValue> nullHandle = globalConst->GetHandledNull();
    JSHandle<LayoutInfo> emptyLayout = factory->CreateSLayoutInfo(0);
    JSHandle<JSHClass> hclass = factory->NewSEcmaHClass(JSSharedObject::SIZE, 0, JSType::JS_SHARED_OBJECT, nullHandle,
                                                        JSHandle<JSTaggedValue>(emptyLayout));
    return factory->NewSharedOldSpaceJSObject(hclass);
}

JSHandle<JSObject> CreateSObject(JSThread *thread)
{
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    const GlobalEnvConstants *globalConst = thread->GlobalConstants();
    JSHandle<JSTaggedValue> nullHandle = globalConst->GetHandledNull();

    uint32_t index = 0;
    PropertyAttributes attributes = PropertyAttributes::Default(false, false, false);
    attributes.SetIsInlinedProps(true);
    attributes.SetRepresentation(Representation::TAGGED);
    uint32_t length = 4;
    JSHandle<LayoutInfo> layout = factory->CreateSLayoutInfo(length);

    JSHandle<EcmaString> key1(factory->NewFromASCII("str1"));
    JSHandle<EcmaString> key2(factory->NewFromASCII("str2"));

    while (index < length) {
        attributes.SetOffset(index);
        attributes.SetIsAccessor(false);
        key2 = JSHandle<EcmaString>(thread, EcmaStringAccessor::Concat(thread->GetEcmaVM(), key2, key1));
        auto stringTable = thread->GetEcmaVM()->GetEcmaStringTable();
        stringTable->GetOrInternString(thread->GetEcmaVM(), *key2);
        layout->AddKey(thread, index++, key2.GetTaggedValue(), attributes);
    }

    JSHandle<JSHClass> hclass = factory->NewSEcmaHClass(JSSharedObject::SIZE, length, JSType::JS_SHARED_OBJECT,
                                                        nullHandle, JSHandle<JSTaggedValue>(layout));
    JSHandle<JSObject> object = factory->NewSharedOldSpaceJSObject(hclass);
    uint32_t fieldIndex = 0;
    while (fieldIndex < length) {
        JSHandle<JSObject> emptyObject = CreateEmptySObject(thread);
        object->SetPropertyInlinedProps(thread, fieldIndex++, emptyObject.GetTaggedValue());
    }
    return object;
}

JSHandle<JSSharedSet> CreateSSet(JSThread *thread)
{
    auto globalEnv = thread->GetEcmaVM()->GetGlobalEnv();
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    JSHandle<JSTaggedValue> proto = globalEnv->GetSharedSetPrototype();
    auto emptySLayout = thread->GlobalConstants()->GetHandledEmptySLayoutInfo();
    JSHandle<JSHClass> setClass = factory->NewSEcmaHClass(JSSharedSet::SIZE, 0,
        JSType::JS_SHARED_SET, proto, emptySLayout);
    JSHandle<JSSharedSet> jsSet = JSHandle<JSSharedSet>::Cast(factory->NewSharedOldSpaceJSObjectWithInit(setClass));
    JSHandle<LinkedHashSet> linkedSet(
        LinkedHashSet::Create(thread, LinkedHashSet::MIN_CAPACITY, MemSpaceKind::SHARED));
    jsSet->SetLinkedSet(thread, linkedSet);
    jsSet->SetModRecord(0);
    return jsSet;
}

JSHandle<JSSharedMap> CreateSMap(JSThread *thread)
{
    auto globalEnv = thread->GetEcmaVM()->GetGlobalEnv();
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    JSHandle<JSTaggedValue> proto = globalEnv->GetSharedMapPrototype();
    auto emptySLayout = thread->GlobalConstants()->GetHandledEmptySLayoutInfo();
    JSHandle<JSHClass> mapClass = factory->NewSEcmaHClass(JSSharedMap::SIZE, 0,
        JSType::JS_SHARED_MAP, proto, emptySLayout);
    JSHandle<JSSharedMap> jsMap = JSHandle<JSSharedMap>::Cast(factory->NewSharedOldSpaceJSObjectWithInit(mapClass));
    JSHandle<LinkedHashMap> linkedMap(
        LinkedHashMap::Create(thread, LinkedHashSet::MIN_CAPACITY, MemSpaceKind::SHARED));
    jsMap->SetLinkedMap(thread, linkedMap);
    jsMap->SetModRecord(0);
    return jsMap;
}

HWTEST_F_L0(InterOpSerializerTest, SerializeCloneListTest1)
{
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    JSHandle<JSObject> shareObj = CreateSObject(thread);
    EXPECT_TRUE(shareObj->IsJSShared());

    JSHandle<EcmaString> key(factory->NewFromASCII("str2str1"));
    JSHandle<JSTaggedValue> shareObj1 =
        JSObject::GetProperty(thread, JSHandle<JSObject>(shareObj), JSHandle<JSTaggedValue>(key)).GetValue();
    EXPECT_TRUE(shareObj1->IsJSShared());

    JSHandle<JSArray> array = factory->NewJSArray();
    JSArray::FastSetPropertyByValue(thread, JSHandle<JSTaggedValue>(array), 0, JSHandle<JSTaggedValue>(shareObj));

    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    bool success = serializer->WriteValue(thread, JSHandle<JSTaggedValue>(shareObj),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                          JSHandle<JSTaggedValue>(array));
    EXPECT_TRUE(success) << "SerializeCloneListTest1: Serialize shared obj fail";
    std::unique_ptr<SerializeData> data = serializer->Release();
    InterOpDeserializerTest jsDeserializerTest;
    std::thread t1(&InterOpDeserializerTest::SerializeCloneListTest1, jsDeserializerTest, data.release());
    ecmascript::ThreadSuspensionScope scope(thread);
    t1.join();
    delete serializer;
};

HWTEST_F_L0(InterOpSerializerTest, SerializeCloneListTest2)
{
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    JSHandle<JSObject> rootObj = factory->NewEmptyJSObject();
    JSHandle<JSObject> shareObj = CreateSObject(thread);
    JSHandle<JSObject> noShareObj = CreateSObject(thread);

    JSHandle<JSTaggedValue> key1(factory->NewFromASCII("shareObj"));
    JSHandle<JSTaggedValue> key2(factory->NewFromASCII("noShareObj"));

    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(rootObj), key1, JSHandle<JSTaggedValue>(shareObj));
    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(rootObj), key2, JSHandle<JSTaggedValue>(noShareObj));

    JSHandle<JSArray> array = factory->NewJSArray();
    JSArray::FastSetPropertyByValue(thread, JSHandle<JSTaggedValue>(array), 0, JSHandle<JSTaggedValue>(shareObj));

    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    bool success = serializer->WriteValue(thread, JSHandle<JSTaggedValue>(rootObj),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                          JSHandle<JSTaggedValue>(array));
    EXPECT_TRUE(success) << "SerializeCloneListTest2: Serialize shared obj fail";
    std::unique_ptr<SerializeData> data = serializer->Release();
    InterOpDeserializerTest jsDeserializerTest;
    std::thread t1(&InterOpDeserializerTest::SerializeCloneListTest2, jsDeserializerTest, data.release());
    ecmascript::ThreadSuspensionScope scope(thread);
    t1.join();
    delete serializer;
};

HWTEST_F_L0(InterOpSerializerTest, SerializeCloneListTest3)
{
    ObjectFactory *factory = ecmaVm->GetFactory();
    JSHandle<JSObject> obj = factory->NewEmptyJSObject();
    JSHandle<EcmaString> cloneList(factory->NewFromASCII("cloneList"));
    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    bool success = serializer->WriteValue(thread, JSHandle<JSTaggedValue>(obj),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                          JSHandle<JSTaggedValue>(cloneList));
    EXPECT_FALSE(success);
    std::unique_ptr<SerializeData> data = serializer->Release();
    InterOpValueDeserializer deserializer(thread, data.release());
    JSHandle<JSTaggedValue> res = deserializer.ReadValue();
    EXPECT_TRUE(res.IsEmpty());
    delete serializer;
};

HWTEST_F_L0(InterOpSerializerTest, SerializeCloneListTest4)
{
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    JSHandle<JSObject> obj = factory->NewEmptyJSObject();

    JSHandle<JSArrayBuffer> arrBuf = factory->NewJSArrayBuffer(0);
    JSHandle<JSTaggedValue> arrBufTag = JSHandle<JSTaggedValue>::Cast(arrBuf);
    JSHandle<JSArray> array = factory->NewJSArray();
    JSArray::FastSetPropertyByValue(thread, JSHandle<JSTaggedValue>(array), 0, arrBufTag);

    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    bool success = serializer->WriteValue(thread, JSHandle<JSTaggedValue>(obj),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                          JSHandle<JSTaggedValue>(array));
    EXPECT_TRUE(success) << "SerializeCloneListTest4: Serialize shared obj fail";
    std::unique_ptr<SerializeData> data = serializer->Release();
    InterOpDeserializerTest jsDeserializerTest;
    std::thread t1(&InterOpDeserializerTest::SerializeCloneListTest4, jsDeserializerTest, data.release());
    ecmascript::ThreadSuspensionScope scope(thread);
    t1.join();
    delete serializer;
};

HWTEST_F_L0(InterOpSerializerTest, SerializeCloneListTest5)
{
    ObjectFactory *factory = ecmaVm->GetFactory();
    JSHandle<JSObject> obj = factory->NewEmptyJSObject();
    JSHandle<JSObject> obj1 = factory->NewEmptyJSObject();
    JSHandle<JSArray> array = factory->NewJSArray();
    // set value to array
    array->SetArrayLength(thread, 1);
    JSArray::FastSetPropertyByValue(thread, JSHandle<JSTaggedValue>(array), 0, JSHandle<JSTaggedValue>(obj1));
    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    bool success = serializer->WriteValue(thread, JSHandle<JSTaggedValue>(obj),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                          JSHandle<JSTaggedValue>(array));
    EXPECT_FALSE(success);
    std::unique_ptr<SerializeData> data = serializer->Release();
    InterOpValueDeserializer deserializer(thread, data.release());
    JSHandle<JSTaggedValue> res = deserializer.ReadValue();
    EXPECT_TRUE(res.IsEmpty());
    delete serializer;
};

HWTEST_F_L0(InterOpSerializerTest, SerializeJSSharedSetBasic1)
{
    JSHandle<JSSharedSet> jsSet = CreateSSet(thread);
    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    bool success = serializer->WriteValue(thread, JSHandle<JSTaggedValue>(jsSet),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    EXPECT_TRUE(success) << "Serialize JSSharedSet failed";
    std::unique_ptr<SerializeData> data = serializer->Release();
    {
        for (int i = 0; i < INITIALIZE_SIZE; i++) {
            JSSharedSet::Add(thread, jsSet, JSHandle<JSTaggedValue>(thread, JSTaggedValue(i)));
        }
        InterOpDeserializerTest jsDeserializerTest;
        // The Deserializer thread will clear the shared set
        std::thread t1(&InterOpDeserializerTest::JSSharedSetBasicTest1,
                       jsDeserializerTest, data.get());
        ecmascript::ThreadSuspensionScope scope(thread);
        t1.join();
        EXPECT_TRUE(JSSharedSet::GetSize(thread, jsSet) == 0);
    }
    {
        for (int i = 0; i < INITIALIZE_SIZE; i++) {
            JSSharedSet::Add(thread, jsSet, JSHandle<JSTaggedValue>(thread, JSTaggedValue(i)));
        }
        EXPECT_TRUE(!JSSharedSet::Has(thread, jsSet, JSTaggedValue(INITIALIZE_SIZE)));
        InterOpDeserializerTest jsDeserializerTest;
        // The Deserializer thread will add and delete a element
        std::thread t1(&InterOpDeserializerTest::JSSharedSetBasicTest2,
                       jsDeserializerTest, data.get());
        ecmascript::ThreadSuspensionScope scope(thread);
        t1.join();
        EXPECT_TRUE(!JSSharedSet::Has(thread, jsSet, JSTaggedValue(0)));
        EXPECT_TRUE(JSSharedSet::Has(thread, jsSet, JSTaggedValue(INITIALIZE_SIZE)));
    }
    delete serializer;
};

HWTEST_F_L0(InterOpSerializerTest, SerializeMultiThreadJSSharedSet)
{
    Reset();
    JSHandle<JSSharedSet> jsSet = CreateSSet(thread);
    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    bool success = serializer->WriteValue(thread, JSHandle<JSTaggedValue>(jsSet),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    EXPECT_TRUE(success) << "Serialize JSSharedSet fail";
    std::unique_ptr<SerializeData> data = serializer->Release();
    for (int i = 0; i < INITIALIZE_SIZE; i++) {
        JSSharedSet::Add(thread, jsSet, JSHandle<JSTaggedValue>(thread, JSTaggedValue(i)));
    }
    constexpr uint32_t maxNumDeserializers = 10;
    InterOpDeserializerTest jsDeserializerTests[maxNumDeserializers];
    std::thread threads[maxNumDeserializers];
    for (int32_t i = 0; i < maxNumDeserializers; i++) {
        threads[i] = std::thread(&InterOpDeserializerTest::JSSharedSetMultiThreadTest1,
            jsDeserializerTests[i], data.get());
    }
    ecmascript::ThreadSuspensionScope scope(thread);
    for (int i = 0; i < maxNumDeserializers; i++) {
        threads[i].join();
    }
    EXPECT_TRUE(jsSet->GetModRecord() == 0);
    delete serializer;
};

HWTEST_F_L0(InterOpSerializerTest, SerializeJSSharedMapBasic)
{
    JSHandle<JSSharedMap> jsMap = CreateSMap(thread);
    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread);
    bool success = serializer->WriteValue(thread, JSHandle<JSTaggedValue>(jsMap),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    EXPECT_TRUE(success) << "Serialize JSSharedMap failed";
    std::unique_ptr<SerializeData> data = serializer->Release();
    {
        for (int i = 0; i < INITIALIZE_SIZE; i++) {
            JSSharedMap::Set(thread, jsMap, JSHandle<JSTaggedValue>(thread, JSTaggedValue(i)),
                JSHandle<JSTaggedValue>(thread, JSTaggedValue(i)));
        }
        InterOpDeserializerTest jsDeserializerTest;
        // The Deserializer thread will clear the shared map
        std::thread t1(&InterOpDeserializerTest::JSSharedMapBasicTest1,
                       jsDeserializerTest, data.get());
        ecmascript::ThreadSuspensionScope scope(thread);
        t1.join();
        EXPECT_TRUE(JSSharedMap::GetSize(thread, jsMap) == 0);
    }
    {
        for (int i = 0; i < INITIALIZE_SIZE; i++) {
            JSSharedMap::Set(thread, jsMap, JSHandle<JSTaggedValue>(thread, JSTaggedValue(i)),
                JSHandle<JSTaggedValue>(thread, JSTaggedValue(i)));
        }
        EXPECT_TRUE(!JSSharedMap::Has(thread, jsMap, JSTaggedValue(INITIALIZE_SIZE)));
        InterOpDeserializerTest jsDeserializerTest;
        // The Deserializer thread will add and delete a element
        std::thread t1(&InterOpDeserializerTest::JSSharedMapBasicTest2,
                       jsDeserializerTest, data.get());
        ecmascript::ThreadSuspensionScope scope(thread);
        t1.join();
        EXPECT_TRUE(!JSSharedMap::Has(thread, jsMap, JSTaggedValue(0)));
        EXPECT_TRUE(JSSharedMap::Has(thread, jsMap, JSTaggedValue(INITIALIZE_SIZE)));
    }
    delete serializer;
};

HWTEST_F_L0(InterOpSerializerTest, SerializeMultiSharedRegion)
{
    ObjectFactory *factory = ecmaVm->GetFactory();
    JSHandle<TaggedArray> array = factory->NewSTaggedArray(10 * 1024, JSTaggedValue::Hole()); // 10 * 1024: array length
    for (int i = 0; i < 5; i++) {
        JSHandle<TaggedArray> element = factory->NewSTaggedArray((11 + i) * 1024, JSTaggedValue::Hole());
        array->Set(thread, i, element);
    }
    JSHandle<JSObject> sobj = CreateEmptySObject(thread);
    sobj->SetElements(thread, array);
    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread, false, true);
    bool success = serializer->WriteValue(thread, JSHandle<JSTaggedValue>(sobj),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    EXPECT_TRUE(success);
    std::unique_ptr<SerializeData> data = serializer->Release();
    InterOpDeserializerTest jsDeserializerTest;
    std::thread t1(&InterOpDeserializerTest::SerializeMultiSharedRegionTest, jsDeserializerTest, data.release());
    ecmascript::ThreadSuspensionScope scope(thread);
    t1.join();
    delete serializer;
};

HWTEST_F_L0(InterOpSerializerTest, SerializeMultiSharedRegion1)
{
    ObjectFactory *factory = ecmaVm->GetFactory();
    JSHandle<TaggedArray> array = factory->NewTaggedArray(3 * 1024, JSTaggedValue::Hole()); // 3 * 1024: array length
    for (int i = 0; i < 5; i++) {
        JSHandle<TaggedArray> element = factory->NewTaggedArray(3 * 1024, JSTaggedValue::Hole());
        array->Set(thread, i, element);
    }
    JSHandle<JSObject> obj = factory->NewEmptyJSObject();
    obj->SetElements(thread, array);
    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread, false, true);
    bool success = serializer->WriteValue(thread, JSHandle<JSTaggedValue>(obj),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    EXPECT_TRUE(success);
    std::unique_ptr<SerializeData> data = serializer->Release();
    InterOpDeserializerTest jsDeserializerTest;
    std::thread t1(&InterOpDeserializerTest::SerializeMultiSharedRegionTest1, jsDeserializerTest, data.release());
    ecmascript::ThreadSuspensionScope scope(thread);
    t1.join();
    delete serializer;
};

HWTEST_F_L0(InterOpSerializerTest, SerializeMultiSharedRegion2)
{
    ObjectFactory *factory = ecmaVm->GetFactory();
    JSHandle<TaggedArray> array = factory->NewTaggedArray(3 * 1024, // 10 * 1024: array length
        JSTaggedValue::Hole());
    for (int i = 0; i < 5; i++) {
        JSHandle<TaggedArray> element = factory->NewTaggedArray(3 * 1024, JSTaggedValue::Hole());
        array->Set(thread, i, element);
    }
    JSHandle<TaggedArray> array1 = factory->NewTaggedArray(3 * 1024, // 10 * 1024: array length
        JSTaggedValue::Hole(), true);
    for (int i = 0; i < 5; i++) {
        JSHandle<TaggedArray> element = factory->NewTaggedArray(3 * 1024, JSTaggedValue::Hole(), true);
        array1->Set(thread, i, element);
    }
    JSHandle<JSObject> obj = factory->NewEmptyJSObject();
    obj->SetProperties(thread, array);
    obj->SetElements(thread, array1);
    InterOpValueSerializer *serializer = new InterOpValueSerializer(thread, false, true);
    bool success = serializer->WriteValue(thread, JSHandle<JSTaggedValue>(obj),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                          JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    EXPECT_TRUE(success);
    std::unique_ptr<SerializeData> data = serializer->Release();
    InterOpDeserializerTest jsDeserializerTest;
    std::thread t1(&InterOpDeserializerTest::SerializeMultiSharedRegionTest2, jsDeserializerTest, data.release());
    ecmascript::ThreadSuspensionScope scope(thread);
    t1.join();
    delete serializer;
};

HWTEST_F_L0(InterOpSerializerTest, SerializeInterOpObject1)
{
    auto vm = thread->GetEcmaVM();
    JSNApi::InitHybridVMEnv(vm);
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<JSObject> xrefObject = factory->NewJSXRefObject();
    WrapXRefObject(JSNApiHelper::ToLocal<ObjectRef>(JSHandle<JSTaggedValue>(xrefObject)),
                   reinterpret_cast<void *>(NewObjectAttachHook), true);

    JSHandle<JSHClass> hClassHandle(thread->GlobalConstants()->GetHandledObjectClass());
    JSHandle<JSObject> object = factory->NewJSObject(hClassHandle);

    JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>(object),
                               JSHandle<JSTaggedValue>(thread->GlobalConstants()->GetHandledProxyNapiWrapperString()),
                               JSHandle<JSTaggedValue>(xrefObject));

    InterOpValueSerializer serializer(thread);
    bool success = serializer.WriteValue(thread, JSHandle<JSTaggedValue>(object),
                                         JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                         JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    ASSERT_TRUE(success);
    std::unique_ptr<SerializeData> data = serializer.Release();
    InterOpValueDeserializer deserializer(thread, data.release());
    JSHandle<JSTaggedValue> res = deserializer.ReadValue();
    ASSERT_TRUE(res->IsJSObject());
}

HWTEST_F_L0(InterOpSerializerTest, SerializeInterOpObject2)
{
    auto vm = thread->GetEcmaVM();
    JSNApi::InitHybridVMEnv(vm);
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<JSObject> xrefObject = factory->NewJSXRefObject();
    WrapXRefObject(JSNApiHelper::ToLocal<ObjectRef>(JSHandle<JSTaggedValue>(xrefObject)),
                   reinterpret_cast<void *>(NewObjectAttachHook), true);

    JSHandle<JSHClass> hClassHandle(thread->GlobalConstants()->GetHandledObjectClass());
    JSHandle<JSObject> object = factory->NewJSObject(hClassHandle);

    JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>(object),
                               JSHandle<JSTaggedValue>(thread->GlobalConstants()->GetHandledProxyNapiWrapperString()),
                               JSHandle<JSTaggedValue>(xrefObject));

    InterOpValueSerializer serializer(thread);
    bool success = serializer.WriteValue(thread, JSHandle<JSTaggedValue>(object),
                                         JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                         JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    ASSERT_TRUE(success);
    std::unique_ptr<SerializeData> data = serializer.Release();
    InterOpValueDeserializer deserializer(thread, data.release());
    JSHandle<JSTaggedValue> res = deserializer.ReadValue();
    ASSERT_TRUE(res->IsJSObject());
}

HWTEST_F_L0(InterOpSerializerTest, SerializeInterOpObjectWithEmptyAttachFunc1)
{
    auto vm = thread->GetEcmaVM();
    JSNApi::InitHybridVMEnv(vm);
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<JSObject> xrefObject = factory->NewJSXRefObject();
    WrapXRefObject(JSNApiHelper::ToLocal<ObjectRef>(JSHandle<JSTaggedValue>(xrefObject)),
                   reinterpret_cast<void *>(EmptyAttachHook), true);

    JSHandle<JSHClass> hClassHandle(thread->GlobalConstants()->GetHandledObjectClass());
    JSHandle<JSObject> object = factory->NewJSObject(hClassHandle);

    JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>(object),
                               JSHandle<JSTaggedValue>(thread->GlobalConstants()->GetHandledProxyNapiWrapperString()),
                               JSHandle<JSTaggedValue>(xrefObject));

    InterOpValueSerializer serializer(thread);
    bool success = serializer.WriteValue(thread, JSHandle<JSTaggedValue>(object),
                                         JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                         JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    ASSERT_TRUE(success);
    std::unique_ptr<SerializeData> data = serializer.Release();
    InterOpValueDeserializer deserializer(thread, data.release());
    JSHandle<JSTaggedValue> res = deserializer.ReadValue();
    ASSERT_TRUE(res->IsUndefined());
}

HWTEST_F_L0(InterOpSerializerTest, SerializeInterOpObjectWithEmptyAttachFunc2)
{
    auto vm = thread->GetEcmaVM();
    JSNApi::InitHybridVMEnv(vm);
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<JSObject> xrefObject = factory->NewJSXRefObject();
    WrapXRefObject(JSNApiHelper::ToLocal<ObjectRef>(JSHandle<JSTaggedValue>(xrefObject)),
                   reinterpret_cast<void *>(EmptyAttachHook), true);

    JSHandle<JSHClass> hClassHandle(thread->GlobalConstants()->GetHandledObjectClass());
    JSHandle<JSObject> object = factory->NewJSObject(hClassHandle);

    JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>(object),
                               JSHandle<JSTaggedValue>(thread->GlobalConstants()->GetHandledProxyNapiWrapperString()),
                               JSHandle<JSTaggedValue>(xrefObject));

    InterOpValueSerializer serializer(thread);
    bool success = serializer.WriteValue(thread, JSHandle<JSTaggedValue>(object),
                                         JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                         JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    ASSERT_TRUE(success);
    std::unique_ptr<SerializeData> data = serializer.Release();
    InterOpValueDeserializer deserializer(thread, data.release());
    JSHandle<JSTaggedValue> res = deserializer.ReadValue();
    ASSERT_TRUE(res->IsUndefined());
}

HWTEST_F_L0(InterOpSerializerTest, SerializeWeakInterOpObject)
{
    auto vm = thread->GetEcmaVM();
    JSNApi::InitHybridVMEnv(vm);
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<JSObject> xrefObject = factory->NewJSXRefObject();
    WrapXRefObject(JSNApiHelper::ToLocal<ObjectRef>(JSHandle<JSTaggedValue>(xrefObject)),
                   reinterpret_cast<void *>(AttachHook), true);

    JSHandle<JSHClass> hClassHandle(thread->GlobalConstants()->GetHandledObjectClass());
    JSHandle<JSObject> object = factory->NewJSObject(hClassHandle);

    JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>(object),
                               JSHandle<JSTaggedValue>(thread->GlobalConstants()->GetHandledProxyNapiWrapperString()),
                               JSHandle<JSTaggedValue>(xrefObject));

    JSHandle<TaggedArray> array = factory->NewTaggedArray(1, JSTaggedValue::Hole());
    array->Set(thread, 0, object.GetTaggedValue().CreateAndGetWeakRef());

    InterOpValueSerializer serializer(thread);
    bool success = serializer.WriteValue(thread, JSHandle<JSTaggedValue>(array),
                                         JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                         JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    ASSERT_TRUE(success);
    std::unique_ptr<SerializeData> data = serializer.Release();
    InterOpValueDeserializer deserializer(thread, data.release());
    JSHandle<JSTaggedValue> res = deserializer.ReadValue();
    ASSERT_TRUE(res->IsTaggedArray());
    ASSERT_TRUE(JSHandle<TaggedArray>(res)->Get(thread, 0).IsUndefined());
}

HWTEST_F_L0(InterOpSerializerTest, SerializeInterOpProxyObject)
{
    auto vm = thread->GetEcmaVM();
    JSNApi::InitHybridVMEnv(vm);
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<JSObject> xrefObject = factory->NewJSXRefObject();
    WrapXRefObject(JSNApiHelper::ToLocal<ObjectRef>(JSHandle<JSTaggedValue>(xrefObject)),
                   reinterpret_cast<void *>(AttachHook), true);

    JSHandle<JSHClass> hClassHandle(thread->GlobalConstants()->GetHandledObjectClass());
    JSHandle<JSObject> object = factory->NewJSObject(hClassHandle);

    JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>(object),
                               JSHandle<JSTaggedValue>(thread->GlobalConstants()->GetHandledProxyNapiWrapperString()),
                               JSHandle<JSTaggedValue>(xrefObject));

    JSHandle<JSProxy> jsProxy = factory->NewJSProxy(JSHandle<JSTaggedValue>(object), JSHandle<JSTaggedValue>(object));

    InterOpValueSerializer serializer(thread);
    bool success = serializer.WriteValue(thread, JSHandle<JSTaggedValue>(jsProxy),
                                         JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                         JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    ASSERT_TRUE(success);
    std::unique_ptr<SerializeData> data = serializer.Release();
    InterOpValueDeserializer deserializer(thread, data.release());
    JSHandle<JSTaggedValue> res = deserializer.ReadValue();
    ASSERT_TRUE(res->IsUndefined());
}

HWTEST_F_L0(InterOpSerializerTest, SerializeInvalidInterOpObject1)
{
    auto vm = thread->GetEcmaVM();
    JSNApi::InitHybridVMEnv(vm);
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<JSHClass> hClassHandle(thread->GlobalConstants()->GetHandledObjectClass());
    JSHandle<JSObject> object = factory->NewJSObject(hClassHandle);

    InterOpValueSerializer serializer(thread);
    bool success = serializer.WriteValue(thread, JSHandle<JSTaggedValue>(object),
                                         JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                         JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    ASSERT_TRUE(success);
}

HWTEST_F_L0(InterOpSerializerTest, SerializeInvalidInterOpObject2)
{
    auto vm = thread->GetEcmaVM();
    JSNApi::InitHybridVMEnv(vm);
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<JSObject> xrefObject = factory->NewJSXRefObject();
    WrapXRefObject(JSNApiHelper::ToLocal<ObjectRef>(JSHandle<JSTaggedValue>(xrefObject)),
                   reinterpret_cast<void *>(AttachHook), false);

    JSHandle<JSHClass> hClassHandle(thread->GlobalConstants()->GetHandledObjectClass());
    JSHandle<JSObject> object = factory->NewJSObject(hClassHandle);

    JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>(object),
                               JSHandle<JSTaggedValue>(thread->GlobalConstants()->GetHandledProxyNapiWrapperString()),
                               JSHandle<JSTaggedValue>(xrefObject));

    InterOpValueSerializer serializer(thread);
    bool success = serializer.WriteValue(thread, JSHandle<JSTaggedValue>(object),
                                         JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                         JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    ASSERT_FALSE(success);
}

HWTEST_F_L0(InterOpSerializerTest, SerializeInvalidInterOpObject3)
{
    auto vm = thread->GetEcmaVM();
    JSNApi::InitHybridVMEnv(vm);
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<JSObject> xrefObject = factory->NewJSXRefObject();

    JSHandle<JSHClass> hClassHandle(thread->GlobalConstants()->GetHandledObjectClass());
    JSHandle<JSObject> object = factory->NewJSObject(hClassHandle);

    JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>(object),
                               JSHandle<JSTaggedValue>(thread->GlobalConstants()->GetHandledProxyNapiWrapperString()),
                               JSHandle<JSTaggedValue>(xrefObject));

    InterOpValueSerializer serializer(thread);
    bool success = serializer.WriteValue(thread, JSHandle<JSTaggedValue>(object),
                                         JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
                                         JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    ASSERT_FALSE(success);
}
}  // namespace panda::test