/*
 * Copyright (c) 2026 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 "ecmascript/ecma_string-inl.h"
#include "ecmascript/ecma_string_table.h"
#include "ecmascript/ecma_string_table_optimization-inl.h"
#include "ecmascript/dfx/hprof/heap_profiler_interface.h"
#include "ecmascript/dfx/hprof/file_stream.h"
#include "ecmascript/dfx/hprof/progress.h"
#include "ecmascript/mem/barriers.h"
#include "ecmascript/mem/heap.h"
#include "ecmascript/mem/region.h"
#include "ecmascript/mem/shared_heap/shared_gc.h"
#include "ecmascript/mem/shared_heap/shared_concurrent_sweeper.h"
#include "ecmascript/object_factory-inl.h"
#include "ecmascript/runtime_lock.h"
#include "ecmascript/string/hashtriemap.h"
#include "ecmascript/string/hashtriemap-inl.h"
#include "ecmascript/tests/test_helper.h"
#include "common_components/taskpool/taskpool.h"

using namespace panda::ecmascript;

namespace panda::test {

using HashTrieMapType = DisableCMCGCConcurrentSweepTrait::HashTrieMapType;
using HashTrieMapTypeNormal = DisableCMCGCNormalTrait::HashTrieMapType;
using HashTrieMapInUseScopeType = DisableCMCGCConcurrentSweepTrait::HashTrieMapInUseScopeType;
using HashTrieMapInUseScopeTypeNormal = DisableCMCGCNormalTrait::HashTrieMapInUseScopeType;
using HashTrieMapOperationNormalType = DisableCMCGCNormalTrait::HashTrieMapOperationType;
using HashTrieMapOperationConcurrentSweepType = DisableCMCGCConcurrentSweepTrait::HashTrieMapOperationType;

class EcmaStringTableSweepingTest : public BaseTestWithScope<false> {
public:
    static void SetUpTestCase()
    {
        GTEST_LOG_(INFO) << "EcmaStringTableSweepingTest SetUpTestCase";
    }

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

protected:
    EcmaString *CreateUtf8String(const char *name)
    {
        return EcmaStringAccessor::CreateFromUtf8(thread->GetEcmaVM(),
            reinterpret_cast<const uint8_t*>(name), strlen(name), true);
    }

    EcmaString *CreateAndInternUtf8String(EcmaStringTable *stringTable, const char *name)
    {
        EcmaString *str = CreateUtf8String(name);
        return stringTable->GetOrInternString(thread->GetEcmaVM(), str);
    }

    void InternMultipleUtf8Strings(EcmaStringTable *stringTable, int count, const char *prefix, int round = -1)
    {
        char nameBuf[64];
        for (int i = 0; i < count; ++i) {
            int ret;
            if (round >= 0) {
                ret = snprintf_s(nameBuf, sizeof(nameBuf), sizeof(nameBuf) - 1,
                                 "%s_%d_%d", prefix, round, i);
            } else {
                ret = snprintf_s(nameBuf, sizeof(nameBuf), sizeof(nameBuf) - 1,
                                 "%s_%d", prefix, i);
            }
            if (ret < 0) {
                LOG_ECMA(ERROR) << "snprintf_s failed in InternMultipleUtf8Strings";
                continue;
            }
            ASSERT_NE(CreateAndInternUtf8String(stringTable, nameBuf), nullptr);
        }
    }

    void InternMultipleUtf8Strings(EcmaStringTable *stringTable, int count, const char *prefix,
                                   int phase, int round)
    {
        char nameBuf[64];
        for (int i = 0; i < count; ++i) {
            int ret = snprintf_s(nameBuf, sizeof(nameBuf), sizeof(nameBuf) - 1,
                                 "%s_%d_%d_%d", prefix, phase, round, i);
            if (ret < 0) {
                LOG_ECMA(ERROR) << "snprintf_s failed in InternMultipleUtf8Strings";
                continue;
            }
            ASSERT_NE(CreateAndInternUtf8String(stringTable, nameBuf), nullptr);
        }
    }

    template<typename OperationType, bool needIntern = true>
    BaseString *LoadOrStoreUtf8String(OperationType &operation, const uint8_t *utf8Data, uint32_t utf8Len)
    {
        uint32_t hashcode = EcmaStringAccessor::ComputeHashcodeUtf8(utf8Data, utf8Len, true);

        auto loaderCallback = [this, utf8Data, utf8Len, hashcode]() {
            EcmaString* value = EcmaStringAccessor::CreateFromUtf8(thread->GetEcmaVM(),
                utf8Data, utf8Len, true, MemSpaceType::SHARED_OLD_SPACE);
            value->SetMixHashcode(hashcode);
            JSHandle<EcmaString> stringHandle(thread, value);
            return stringHandle;
        };

        auto equalsCallback = [this, utf8Data, utf8Len](BaseString *foundString) {
            JSThread *jsThread = thread;
            return EcmaStringAccessor::StringIsEqualUint8Data(jsThread,
                EcmaString::FromBaseString(foundString), utf8Data, utf8Len, true);
        };

        return operation.template LoadOrStore<needIntern>(thread, hashcode, loaderCallback, equalsCallback);
    }

    template<typename OperationType, bool needIntern = true>
    BaseString *LoadOrStoreUtf16String(OperationType &operation, const uint16_t *utf16Data, uint32_t utf16Len)
    {
        uint32_t hashcode = EcmaStringAccessor::ComputeHashcodeUtf16(utf16Data, utf16Len);

        auto loaderCallback = [this, utf16Data, utf16Len, hashcode]() {
            EcmaString* value = EcmaStringAccessor::CreateFromUtf16(thread->GetEcmaVM(),
                utf16Data, utf16Len, true, MemSpaceType::SHARED_OLD_SPACE);
            value->SetMixHashcode(hashcode);
            JSHandle<EcmaString> stringHandle(thread, value);
            return stringHandle;
        };

        auto equalsCallback = [this, utf16Data, utf16Len](BaseString *foundString) {
            JSThread *jsThread = thread;
            return EcmaStringAccessor::StringsAreEqualUtf16(jsThread,
                EcmaString::FromBaseString(foundString), utf16Data, utf16Len);
        };

        return operation.template LoadOrStore<needIntern>(thread, hashcode, loaderCallback, equalsCallback);
    }

    template<typename OperationType, bool needIntern = true>
    BaseString *LoadOrStoreUtf16StringWithAtomicMark(OperationType &operation,
                                                      const uint16_t *utf16Data, uint32_t utf16Len)
    {
        uint32_t hashcode = EcmaStringAccessor::ComputeHashcodeUtf16(utf16Data, utf16Len);

        auto loaderCallback = [this, utf16Data, utf16Len, hashcode]() {
            EcmaString* value = EcmaStringAccessor::CreateFromUtf16(thread->GetEcmaVM(),
                utf16Data, utf16Len, true, MemSpaceType::SHARED_OLD_SPACE);
            value->SetMixHashcode(hashcode);
            Region *region = Region::ObjectAddressToRange(reinterpret_cast<JSTaggedType>(value));
            if (region != nullptr) {
                region->AtomicMark(value);
            }
            JSHandle<EcmaString> stringHandle(thread, value);
            return stringHandle;
        };

        auto equalsCallback = [this, utf16Data, utf16Len](BaseString *foundString) {
            JSThread *jsThread = thread;
            return EcmaStringAccessor::StringsAreEqualUtf16(jsThread,
                EcmaString::FromBaseString(foundString), utf16Data, utf16Len);
        };

        return operation.template LoadOrStore<needIntern>(thread, hashcode, loaderCallback, equalsCallback);
    }

    template<typename OperationType, bool needIntern = true>
    BaseString *LoadOrStoreUtf8StringWithAtomicMark(OperationType &operation,
                                                     const uint8_t *utf8Data, uint32_t utf8Len)
    {
        uint32_t hashcode = EcmaStringAccessor::ComputeHashcodeUtf8(utf8Data, utf8Len, true);

        auto loaderCallback = [this, utf8Data, utf8Len, hashcode]() {
            EcmaString* value = EcmaStringAccessor::CreateFromUtf8(thread->GetEcmaVM(),
                utf8Data, utf8Len, true, MemSpaceType::SHARED_OLD_SPACE);
            value->SetMixHashcode(hashcode);
            Region *region = Region::ObjectAddressToRange(reinterpret_cast<JSTaggedType>(value));
            if (region != nullptr) {
                region->AtomicMark(value);
            }
            JSHandle<EcmaString> stringHandle(thread, value);
            return stringHandle;
        };

        auto equalsCallback = [this, utf8Data, utf8Len](BaseString *foundString) {
            JSThread *jsThread = thread;
            return EcmaStringAccessor::StringIsEqualUint8Data(jsThread,
                EcmaString::FromBaseString(foundString), utf8Data, utf8Len, true);
        };

        return operation.template LoadOrStore<needIntern>(thread, hashcode, loaderCallback, equalsCallback);
    }

    uint32_t MakeUtf16DataFromAsciiWithNumber(uint16_t *buffer, const char *prefix, int number)
    {
        uint32_t len = 0;
        while (*prefix != '\0') {
            buffer[len++] = static_cast<uint16_t>(*prefix);
            prefix++;
        }
        buffer[len++] = static_cast<uint16_t>('0' + (number / 10));  // 10: decimal base for digit extraction
        buffer[len++] = static_cast<uint16_t>('0' + (number % 10));  // 10: decimal base for digit extraction
        return len;
    }

    uint32_t MakeUtf16DataFromAscii(uint16_t *buffer, const char *str)
    {
        uint32_t len = 0;
        while (*str != '\0') {
            buffer[len++] = static_cast<uint16_t>(*str);
            str++;
        }
        return len;
    }

    template<typename VisitorFunc>
    void DoSweepingWithClearNode(HashTrieMapType &hashTrieMap, VisitorFunc visitor)
    {
        RuntimeLockHolder locker(thread, SharedHeap::GetInstance()->GetSuspensionRequestMutex());
        hashTrieMap.StartSweeping();

        HashTrieMapOperationConcurrentSweepType concurrentSweepOp(&hashTrieMap);

        std::vector<HashTrieMapEntry*> waitDeleteEntries;
        std::vector<HashTrieMapSlotCheckInfo> waitCheckAndFreeHeadEntries;

        for (uint32_t i = 0; i < TrieMapConfig::ROOT_SIZE; ++i) {
            auto root = hashTrieMap.GetRoot(i).load(std::memory_order_acquire);
            if (root != nullptr) {
                for (uint32_t j = 0; j < TrieMapConfig::INDIRECT_SIZE; ++j) {
                    concurrentSweepOp.ClearNodeFromGC(root, j, visitor, waitDeleteEntries,
                                                      waitCheckAndFreeHeadEntries);
                }
            }
        }

        for (HashTrieMapEntry* entry : waitDeleteEntries) {
            delete entry;
        }
        waitDeleteEntries.clear();

        hashTrieMap.CheckAndFreeHeadEntries(waitCheckAndFreeHeadEntries);

        hashTrieMap.FinishSweeping();
        hashTrieMap.ClearToSpaceTagForFreshEntries();
    }

    void DoSweepingWithClearNodeKeepAll(HashTrieMapType &hashTrieMap)
    {
        auto visitorKeep = [](TaggedObject *header) -> TaggedObject* {
            return header;
        };
        DoSweepingWithClearNode(hashTrieMap, visitorKeep);
    }

    void DoSweepingWithClearNodeClearAll(HashTrieMapType &hashTrieMap)
    {
        auto visitorClear = [](TaggedObject *header) -> TaggedObject* {
            return nullptr;
        };
        DoSweepingWithClearNode(hashTrieMap, visitorClear);
    }
};

/**
 * @tc.name: StringTable_EnableDisableConcurrentSweepWithGCTest
 * @tc.desc: Test enabling and disabling concurrent sweep during GC cycles. Verify that
 *           the concurrent sweep flag state is correctly maintained after each GC cycle.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, StringTable_EnableDisableConcurrentSweepWithGCTest)
{
    EcmaStringTable *stringTable = thread->GetEcmaVM()->GetEcmaStringTable();
    ASSERT_NE(stringTable, nullptr);

    EcmaStringTableCleaner *cleaner = stringTable->GetCleaner();
    ASSERT_NE(cleaner, nullptr);

    SharedHeap *sharedHeap = SharedHeap::GetInstance();
    ASSERT_NE(sharedHeap, nullptr);

    for (int round = 0; round < 10; ++round) {
        bool enable = (round % 2 == 0);
        cleaner->SetEnableConcurrentSweep(enable);

        InternMultipleUtf8Strings(stringTable, 100, "enable_gc", round);

        sharedHeap->CollectGarbage<TriggerGCType::SHARED_GC, GCReason::OTHER>(thread);

        ASSERT_EQ(cleaner->IsEnableConcurrentSweep(), enable);
    }

    cleaner->SetEnableConcurrentSweep(false);
}

/**
 * @tc.name: SharedHeap_CollectGarbageWithStringsTest
 * @tc.desc: Test shared heap garbage collection with strings. Verify that strings
           interned before and after GC cycles are correctly handled.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, SharedHeap_CollectGarbageWithStringsTest)
{
    SharedHeap *sharedHeap = SharedHeap::GetInstance();
    ASSERT_NE(sharedHeap, nullptr);

    EcmaStringTable *stringTable = thread->GetEcmaVM()->GetEcmaStringTable();
    ASSERT_NE(stringTable, nullptr);

    for (int round = 0; round < 10; ++round) {
        InternMultipleUtf8Strings(stringTable, 100, "collect_gc", round);

        sharedHeap->CollectGarbage<TriggerGCType::SHARED_GC, GCReason::OTHER>(thread);

        InternMultipleUtf8Strings(stringTable, 100, "collect_after", round);
    }
}

/**
 * @tc.name: SharedHeap_SharedPartialGCWithManyStringsTest
 * @tc.desc: Test shared partial GC with many strings interned. Verify that string table
 *           handles partial GC cycles correctly while interning strings.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, SharedHeap_SharedPartialGCWithManyStringsTest)
{
    SharedHeap *sharedHeap = SharedHeap::GetInstance();
    ASSERT_NE(sharedHeap, nullptr);

    EcmaStringTable *stringTable = thread->GetEcmaVM()->GetEcmaStringTable();
    ASSERT_NE(stringTable, nullptr);

    for (int gcCycle = 0; gcCycle < 10; ++gcCycle) {
        InternMultipleUtf8Strings(stringTable, 100, "partial_gc_str", gcCycle);

        sharedHeap->CollectGarbage<TriggerGCType::SHARED_PARTIAL_GC, GCReason::OTHER>(thread);

        InternMultipleUtf8Strings(stringTable, 100, "partial_concurrent", gcCycle);
    }
}

/**
 * @tc.name: SharedHeap_AlternatingGCtypesWithStringTableTest
 * @tc.desc: Test alternating GC types (SHARED_GC and SHARED_PARTIAL_GC) with string table.
 * Verify that string table handles both GC types correctly while interning strings.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, SharedHeap_AlternatingGCtypesWithStringTableTest)
{
    SharedHeap *sharedHeap = SharedHeap::GetInstance();
    ASSERT_NE(sharedHeap, nullptr);

    EcmaStringTable *stringTable = thread->GetEcmaVM()->GetEcmaStringTable();
    ASSERT_NE(stringTable, nullptr);

    for (int round = 0; round < 10; ++round) {
        InternMultipleUtf8Strings(stringTable, 100, "alt_gc", round);

        if (round % 2 == 0) {
            sharedHeap->CollectGarbage<TriggerGCType::SHARED_GC, GCReason::OTHER>(thread);
        } else {
            sharedHeap->CollectGarbage<TriggerGCType::SHARED_PARTIAL_GC, GCReason::OTHER>(thread);
        }

        InternMultipleUtf8Strings(stringTable, 100, "alt_concurrent", round);
    }
}

/**
 * @tc.name: HashTrieMap_SweepingFlagConsistencyTest
 * @tc.desc: Test HashTrieMap sweeping flag consistency. Verify that IsSweeping() returns
           correct state before, during, and after sweeping operations.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, HashTrieMap_SweepingFlagConsistencyTest)
{
    HashTrieMapType hashTrieMap;

    for (int round = 0; round < 100; ++round) {
        ASSERT_FALSE(hashTrieMap.IsSweeping());

        hashTrieMap.StartSweeping();
        ASSERT_TRUE(hashTrieMap.IsSweeping());

        hashTrieMap.FinishSweeping();
        ASSERT_FALSE(hashTrieMap.IsSweeping());
    }
}

/**
 * @tc.name: HashTrieMapInUseScope_StackUnwindingTest
 * @tc.desc: Test HashTrieMapInUseScope stack unwinding behavior. Verify that in-use scope
           correctly manages the in-use flag during normal execution and exception scenarios.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, HashTrieMapInUseScope_StackUnwindingTest)
{
    HashTrieMapType hashTrieMap;

    for (int round = 0; round < 100; ++round) {
        uint32_t outerCount = hashTrieMap.GetInuseCount();

        HashTrieMapInUseScopeType outerScope(&hashTrieMap);
        ASSERT_EQ(hashTrieMap.GetInuseCount(), outerCount + 1);

        {
            HashTrieMapInUseScopeType innerScope1(&hashTrieMap);
            ASSERT_EQ(hashTrieMap.GetInuseCount(), outerCount + 2);

            {
                HashTrieMapInUseScopeType innerScope2(&hashTrieMap);
                ASSERT_EQ(hashTrieMap.GetInuseCount(), outerCount + 3);
            }

            ASSERT_EQ(hashTrieMap.GetInuseCount(), outerCount + 2);
        }

        ASSERT_EQ(hashTrieMap.GetInuseCount(), outerCount + 1);
    }

    ASSERT_EQ(hashTrieMap.GetInuseCount(), 0U);
}

/**
 * @tc.name: StringTable_ReadBarrierMultipleObjectsTest
 * @tc.desc: Test read barrier with multiple objects. Verify that read barrier
           correctly handles multiple string objects during GC.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, StringTable_ReadBarrierMultipleObjectsTest)
{
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();

    for (int round = 0; round < 100; ++round) {
        JSHandle<EcmaString> str = factory->NewFromASCIISkippingStringTable("multi_rb_test");
        JSTaggedType value = str.GetTaggedType();

        Region *region = Region::ObjectAddressToRange(value);
        ASSERT_NE(region, nullptr);

        region->AtomicMark(reinterpret_cast<void *>(value));
        JSTaggedType result1 = Barriers::ReadBarrierForStringTableSlot(value);
        ASSERT_EQ(result1, value);

        region->ClearMark(reinterpret_cast<void *>(value));
        JSTaggedType result2 = Barriers::ReadBarrierForStringTableSlot(value);
        bool isNullOrToObj = (result2 == 0) || Region::ObjectAddressToRange(result2)->IsToRegion();
        ASSERT_TRUE(isNullOrToObj);

        region->AtomicMark(reinterpret_cast<void *>(value));
        JSTaggedType result3 = Barriers::ReadBarrierForStringTableSlot(value);
        ASSERT_EQ(result3, value);
    }
}

/**
 * @tc.name: HashTrieMapEntry_ToSpaceTagBasicTest
 * @tc.desc: Test HashTrieMapEntry ToSpace tag basic operations. Verify that
           ToSpace tag can be set, cleared, and checked correctly.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, HashTrieMapEntry_ToSpaceTagBasicTest)
{
    char nameBuf[32];

    for (int round = 0; round < 100; ++round) {
        int ret = snprintf_s(nameBuf, sizeof(nameBuf), sizeof(nameBuf) - 1, "tospace_%d", round);
        if (ret < 0) {
            LOG_ECMA(ERROR) << "snprintf_s failed in HashTrieMapEntry_ToSpaceTagBasicTest";
            continue;
        }
        EcmaString *str = EcmaStringAccessor::CreateFromUtf8(thread->GetEcmaVM(),
            reinterpret_cast<const uint8_t*>(nameBuf), strlen(nameBuf), true);

        HashTrieMapEntry entry(str->ToBaseString(), true);
        ASSERT_TRUE(entry.IsToSpaceObject());

        entry.ClearToSpaceTag();
        ASSERT_FALSE(entry.IsToSpaceObject());

        HashTrieMapEntry entry2(str->ToBaseString(), false);
        ASSERT_FALSE(entry2.IsToSpaceObject());
    }
}

/**
 * @tc.name: StringTableConcurrentSweep_InternUtf8WithSharedGCTest
 * @tc.desc: Test interning UTF8 strings with concurrent sweep triggered by SharedGC. Verify that
 *           strings can be interned correctly after SharedGC triggers concurrent sweep.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, StringTableConcurrentSweep_InternUtf8WithSharedGCTest)
{
    SharedHeap *sharedHeap = SharedHeap::GetInstance();
    ASSERT_NE(sharedHeap, nullptr);

    EcmaStringTable *stringTable = thread->GetEcmaVM()->GetEcmaStringTable();
    ASSERT_NE(stringTable, nullptr);

    EcmaStringTableCleaner *cleaner = stringTable->GetCleaner();
    ASSERT_NE(cleaner, nullptr);
    cleaner->SetEnableConcurrentSweep(true);

    for (int round = 0; round < 10; ++round) {
        sharedHeap->CollectGarbage<TriggerGCType::SHARED_GC, GCReason::OTHER>(thread);

        InternMultipleUtf8Strings(stringTable, 100, "utf8_sweep", round);
    }

    cleaner->SetEnableConcurrentSweep(false);
}

/**
 * @tc.name: StringTableConcurrentSweep_CheckValidityWithSharedGCTest
 * @tc.desc: Test string table validity check with concurrent sweep triggered by SharedGC. Verify that
 *           string table remains valid while strings are interned after SharedGC.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, StringTableConcurrentSweep_CheckValidityWithSharedGCTest)
{
    SharedHeap *sharedHeap = SharedHeap::GetInstance();
    ASSERT_NE(sharedHeap, nullptr);

    EcmaStringTable *stringTable = thread->GetEcmaVM()->GetEcmaStringTable();
    ASSERT_NE(stringTable, nullptr);

    EcmaStringTableCleaner *cleaner = stringTable->GetCleaner();
    ASSERT_NE(cleaner, nullptr);
    cleaner->SetEnableConcurrentSweep(true);

    for (int round = 0; round < 10; ++round) {
        InternMultipleUtf8Strings(stringTable, 100, "validity", round);

        sharedHeap->CollectGarbage<TriggerGCType::SHARED_GC, GCReason::OTHER>(thread);

        InternMultipleUtf8Strings(stringTable, 100, "validity_sweep", round);

        ASSERT_TRUE(stringTable->CheckStringTableValidity(thread));
    }

    cleaner->SetEnableConcurrentSweep(false);
}

/**
 * @tc.name: StringTable_InternAfterMultipleGCRoundsTest
 * @tc.desc: Test interning strings after multiple GC rounds. Verify that
           existing strings remain valid and new strings can be interned after multiple GC cycles.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, StringTable_InternAfterMultipleGCRoundsTest)
{
    SharedHeap *sharedHeap = SharedHeap::GetInstance();
    ASSERT_NE(sharedHeap, nullptr);

    EcmaStringTable *stringTable = thread->GetEcmaVM()->GetEcmaStringTable();
    ASSERT_NE(stringTable, nullptr);

    std::vector<EcmaString *> existingStrings;

    for (int i = 0; i < 100; ++i) {
        char nameBuf[64];
        int ret = snprintf_s(nameBuf, sizeof(nameBuf), sizeof(nameBuf) - 1, "existing_%d", i);
        if (ret < 0) {
            LOG_ECMA(ERROR) << "snprintf_s failed in InternAfterMultipleGCRoundsTest";
            continue;
        }
        EcmaString *str = EcmaStringAccessor::CreateFromUtf8(thread->GetEcmaVM(),
            reinterpret_cast<const uint8_t*>(nameBuf), strlen(nameBuf), true);
        existingStrings.push_back(stringTable->GetOrInternString(thread->GetEcmaVM(), str));
    }

    for (int gcRound = 0; gcRound < 10; ++gcRound) {
        sharedHeap->CollectGarbage<TriggerGCType::SHARED_GC, GCReason::OTHER>(thread);
        InternMultipleUtf8Strings(stringTable, 100, "after_gc", gcRound);
    }
}

/**
 * @tc.name: StringTable_InternThenLookupUtf8WithGCTest
 * @tc.desc: Test interning UTF8 strings then looking them up with GC. Verify that
 *           UTF8 strings interned before GC can still be found after GC.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, StringTable_InternThenLookupUtf8WithGCTest)
{
    SharedHeap *sharedHeap = SharedHeap::GetInstance();
    ASSERT_NE(sharedHeap, nullptr);

    EcmaStringTable *stringTable = thread->GetEcmaVM()->GetEcmaStringTable();
    ASSERT_NE(stringTable, nullptr);

    for (int round = 0; round < 10; ++round) {
        InternMultipleUtf8Strings(stringTable, 100, "lookup_utf8", round);
        sharedHeap->CollectGarbage<TriggerGCType::SHARED_GC, GCReason::OTHER>(thread);
        InternMultipleUtf8Strings(stringTable, 100, "lookup_utf8", round);
    }
}

/**
 * @tc.name: StringTable_InternThenLookupUtf16WithGCTest
 * @tc.desc: Test interning UTF16 strings then looking them up with GC. Verify that
 *           UTF16 strings interned before GC can still be found after GC.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, StringTable_InternThenLookupUtf16WithGCTest)
{
    SharedHeap *sharedHeap = SharedHeap::GetInstance();
    ASSERT_NE(sharedHeap, nullptr);

    EcmaStringTable *stringTable = thread->GetEcmaVM()->GetEcmaStringTable();
    ASSERT_NE(stringTable, nullptr);

    uint16_t utf16Data[] = {0x4E2D, 0x6587, 0x6D4B, 0x8BD5, 0x5B57, 0x7B26};

    for (int round = 0; round < 10; ++round) {
        for (int i = 0; i < 100; ++i) {
            EcmaString *str = stringTable->GetOrInternString(thread->GetEcmaVM(),
                utf16Data, sizeof(utf16Data) / sizeof(uint16_t), false);
            ASSERT_NE(str, nullptr);
        }
        sharedHeap->CollectGarbage<TriggerGCType::SHARED_GC, GCReason::OTHER>(thread);
        for (int i = 0; i < 100; ++i) {
            EcmaString *str = stringTable->GetOrInternString(thread->GetEcmaVM(),
                utf16Data, sizeof(utf16Data) / sizeof(uint16_t), false);
            ASSERT_NE(str, nullptr);
        }
    }
}

/**
 * @tc.name: StringTable_InternThenLookupFlattenWithGCTest
 * @tc.desc: Test interning flatten strings then looking them up with GC. Verify that
 *           flatten strings interned before GC can still be found after GC.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, StringTable_InternThenLookupFlattenWithGCTest)
{
    SharedHeap *sharedHeap = SharedHeap::GetInstance();
    ASSERT_NE(sharedHeap, nullptr);

    EcmaStringTable *stringTable = thread->GetEcmaVM()->GetEcmaStringTable();
    ASSERT_NE(stringTable, nullptr);

    for (int round = 0; round < 10; ++round) {
        for (int i = 0; i < 100; ++i) {
            char nameBuf[64];
            int ret = snprintf_s(nameBuf, sizeof(nameBuf), sizeof(nameBuf) - 1,
                                 "flatten_lookup_%d_%d", round, i);
            if (ret < 0) {
                LOG_ECMA(ERROR) << "snprintf_s failed in FlattenWithGCTest";
                continue;
            }
            EcmaString *str = CreateUtf8String(nameBuf);
            EcmaString *flattened = EcmaStringAccessor::Flatten(thread->GetEcmaVM(),
                JSHandle<EcmaString>(thread, str));
            EcmaString *interned = stringTable->GetOrInternFlattenString(thread->GetEcmaVM(), flattened);
            ASSERT_NE(interned, nullptr);
        }
        sharedHeap->CollectGarbage<TriggerGCType::SHARED_GC, GCReason::OTHER>(thread);
        for (int i = 0; i < 100; ++i) {
            char nameBuf[64];
            int ret = snprintf_s(nameBuf, sizeof(nameBuf), sizeof(nameBuf) - 1,
                                 "flatten_lookup_%d_%d", round, i);
            if (ret < 0) {
                LOG_ECMA(ERROR) << "snprintf_s failed in FlattenWithGCTest";
                continue;
            }
            EcmaString *str = CreateUtf8String(nameBuf);
            EcmaString *flattened = EcmaStringAccessor::Flatten(thread->GetEcmaVM(),
                JSHandle<EcmaString>(thread, str));
            EcmaString *interned = stringTable->GetOrInternFlattenString(thread->GetEcmaVM(), flattened);
            ASSERT_NE(interned, nullptr);
        }
    }
}

/**
 * @tc.name: StringTable_InternThenLookupConcatWithGCTest
 * @tc.desc: Test interning concat strings then looking them up with GC. Verify that
 *           concat strings interned before GC can still be found after GC.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, StringTable_InternThenLookupConcatWithGCTest)
{
    SharedHeap *sharedHeap = SharedHeap::GetInstance();
    ASSERT_NE(sharedHeap, nullptr);

    EcmaStringTable *stringTable = thread->GetEcmaVM()->GetEcmaStringTable();
    ASSERT_NE(stringTable, nullptr);

    char nameBuf[64];

    for (int round = 0; round < 10; ++round) {
        int ret = snprintf_s(nameBuf, sizeof(nameBuf), sizeof(nameBuf) - 1,
                             "concat_lookup_%d_", round);
        if (ret < 0) {
            LOG_ECMA(ERROR) << "snprintf_s failed in ConcatWithGCTest";
            continue;
        }
        JSHandle<EcmaString> baseFirst = thread->GetEcmaVM()->GetFactory()->NewFromASCII(nameBuf);
        JSHandle<EcmaString> baseSecond = thread->GetEcmaVM()->GetFactory()->NewFromASCII(nameBuf);

        for (int i = 0; i < 100; ++i) {
            EcmaString *concatenated = stringTable->GetOrInternString(thread->GetEcmaVM(), baseFirst, baseSecond);
            ASSERT_NE(concatenated, nullptr);
        }
        sharedHeap->CollectGarbage<TriggerGCType::SHARED_GC, GCReason::OTHER>(thread);
        for (int i = 0; i < 100; ++i) {
            EcmaString *concatenated = stringTable->GetOrInternString(thread->GetEcmaVM(), baseFirst, baseSecond);
            ASSERT_NE(concatenated, nullptr);
        }
    }
}

/**
 * @tc.name: ConcurrentSweep_InternUtf8StringWithSharedGCTest
 * @tc.desc: Test interning UTF8 strings with concurrent sweep triggered by SharedGC. Verify that
 *           UTF8 strings can be correctly interned after SharedGC triggers concurrent sweep.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, ConcurrentSweep_InternUtf8StringWithSharedGCTest)
{
    SharedHeap *sharedHeap = SharedHeap::GetInstance();
    ASSERT_NE(sharedHeap, nullptr);

    EcmaStringTable *stringTable = thread->GetEcmaVM()->GetEcmaStringTable();
    ASSERT_NE(stringTable, nullptr);

    EcmaStringTableCleaner *cleaner = stringTable->GetCleaner();
    ASSERT_NE(cleaner, nullptr);
    cleaner->SetEnableConcurrentSweep(true);

    const char *utf8Data = "utf8_sweep_test";

    for (int round = 0; round < 10; ++round) {
        sharedHeap->CollectGarbage<TriggerGCType::SHARED_GC, GCReason::OTHER>(thread);

        EcmaString *firstIntern = nullptr;
        for (int i = 0; i < 100; ++i) {
            EcmaString *str = stringTable->GetOrInternString(thread->GetEcmaVM(),
                reinterpret_cast<const uint8_t*>(utf8Data), strlen(utf8Data), true);
            if (firstIntern == nullptr) {
                firstIntern = str;
            }
            ASSERT_EQ(str, firstIntern);
        }
    }

    cleaner->SetEnableConcurrentSweep(false);
}

/**
 * @tc.name: ConcurrentSweep_InternUtf16StringWithSharedGCTest
 * @tc.desc: Test interning UTF16 strings with concurrent sweep triggered by SharedGC. Verify that
 *           UTF16 strings can be correctly interned after SharedGC triggers concurrent sweep.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, ConcurrentSweep_InternUtf16StringWithSharedGCTest)
{
    SharedHeap *sharedHeap = SharedHeap::GetInstance();
    ASSERT_NE(sharedHeap, nullptr);

    EcmaStringTable *stringTable = thread->GetEcmaVM()->GetEcmaStringTable();
    ASSERT_NE(stringTable, nullptr);

    EcmaStringTableCleaner *cleaner = stringTable->GetCleaner();
    ASSERT_NE(cleaner, nullptr);
    cleaner->SetEnableConcurrentSweep(true);

    uint16_t utf16Data[] = {0x4E2D, 0x6587, 0x6D4B, 0x8BD5, 0x5B57, 0x7B26};

    for (int round = 0; round < 10; ++round) {
        sharedHeap->CollectGarbage<TriggerGCType::SHARED_GC, GCReason::OTHER>(thread);

        EcmaString *firstIntern = nullptr;
        for (int i = 0; i < 100; ++i) {
            EcmaString *str = stringTable->GetOrInternString(thread->GetEcmaVM(),
                utf16Data, sizeof(utf16Data) / sizeof(uint16_t), false);
            if (firstIntern == nullptr) {
                firstIntern = str;
            }
            ASSERT_EQ(str, firstIntern);
        }
    }

    cleaner->SetEnableConcurrentSweep(false);
}

/**
 * @tc.name: ConcurrentSweep_InternFlattenStringWithSharedGCTest
 * @tc.desc: Test interning flatten strings with concurrent sweep triggered by SharedGC. Verify that
 *           flattened strings can be correctly interned after SharedGC triggers concurrent sweep.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, ConcurrentSweep_InternFlattenStringWithSharedGCTest)
{
    SharedHeap *sharedHeap = SharedHeap::GetInstance();
    ASSERT_NE(sharedHeap, nullptr);

    EcmaStringTable *stringTable = thread->GetEcmaVM()->GetEcmaStringTable();
    ASSERT_NE(stringTable, nullptr);

    EcmaStringTableCleaner *cleaner = stringTable->GetCleaner();
    ASSERT_NE(cleaner, nullptr);
    cleaner->SetEnableConcurrentSweep(true);

    char nameBuf[64];

    for (int round = 0; round < 10; ++round) {
        sharedHeap->CollectGarbage<TriggerGCType::SHARED_GC, GCReason::OTHER>(thread);

        for (int i = 0; i < 100; ++i) {
            int ret = snprintf_s(nameBuf, sizeof(nameBuf), sizeof(nameBuf) - 1,
                                 "flatten_sweep_%d_%d", round, i);
            if (ret < 0) {
                LOG_ECMA(ERROR) << "snprintf_s failed in InternFlattenStringWithSharedGCTest";
                continue;
            }
            EcmaString *str = CreateUtf8String(nameBuf);
            EcmaString *flattened = EcmaStringAccessor::Flatten(thread->GetEcmaVM(),
                JSHandle<EcmaString>(thread, str));
            EcmaString *interned = stringTable->GetOrInternFlattenString(thread->GetEcmaVM(), flattened);
            ASSERT_NE(interned, nullptr);
        }
    }

    cleaner->SetEnableConcurrentSweep(false);
}

/**
 * @tc.name: ConcurrentSweep_InternConcatStringWithSharedGCTest
 * @tc.desc: Test interning concat strings with concurrent sweep triggered by SharedGC. Verify that
 *           concatenated strings can be correctly interned after SharedGC triggers concurrent sweep.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, ConcurrentSweep_InternConcatStringWithSharedGCTest)
{
    SharedHeap *sharedHeap = SharedHeap::GetInstance();
    ASSERT_NE(sharedHeap, nullptr);

    EcmaStringTable *stringTable = thread->GetEcmaVM()->GetEcmaStringTable();
    ASSERT_NE(stringTable, nullptr);

    EcmaStringTableCleaner *cleaner = stringTable->GetCleaner();
    ASSERT_NE(cleaner, nullptr);
    cleaner->SetEnableConcurrentSweep(true);

    char nameBuf[64];

    for (int round = 0; round < 10; ++round) {
        int ret = snprintf_s(nameBuf, sizeof(nameBuf), sizeof(nameBuf) - 1, "concat_base_%d_", round);
        if (ret < 0) {
            LOG_ECMA(ERROR) << "snprintf_s failed in InternConcatStringWithSharedGCTest";
            continue;
        }
        JSHandle<EcmaString> baseFirst = thread->GetEcmaVM()->GetFactory()->NewFromASCII(nameBuf);
        JSHandle<EcmaString> baseSecond = thread->GetEcmaVM()->GetFactory()->NewFromASCII(nameBuf);

        sharedHeap->CollectGarbage<TriggerGCType::SHARED_GC, GCReason::OTHER>(thread);

        for (int i = 0; i < 100; ++i) {
            EcmaString *concatenated = stringTable->GetOrInternString(thread->GetEcmaVM(), baseFirst, baseSecond);
            ASSERT_NE(concatenated, nullptr);
        }
    }

    cleaner->SetEnableConcurrentSweep(false);
}

/**
 * @tc.name: ConcurrentSweep_ReadBarrierWithSharedGCTest
 * @tc.desc: Test read barrier with concurrent sweep triggered by SharedGC. Verify that
 *           read barrier correctly handles string access after SharedGC triggers concurrent sweep.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, ConcurrentSweep_ReadBarrierWithSharedGCTest)
{
    SharedHeap *sharedHeap = SharedHeap::GetInstance();
    ASSERT_NE(sharedHeap, nullptr);

    EcmaStringTable *stringTable = thread->GetEcmaVM()->GetEcmaStringTable();
    ASSERT_NE(stringTable, nullptr);

    EcmaStringTableCleaner *cleaner = stringTable->GetCleaner();
    ASSERT_NE(cleaner, nullptr);
    cleaner->SetEnableConcurrentSweep(true);

    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();

    for (int round = 0; round < 10; ++round) {
        sharedHeap->CollectGarbage<TriggerGCType::SHARED_GC, GCReason::OTHER>(thread);

        for (int i = 0; i < 100; ++i) {
            JSHandle<EcmaString> strHandle = factory->NewFromASCIISkippingStringTable("rb_sweep_test");
            JSTaggedType value = strHandle.GetTaggedType();
            Region *region = Region::ObjectAddressToRange(value);
            if (region != nullptr) {
                region->AtomicMark(reinterpret_cast<void *>(value));
                JSTaggedType result = Barriers::ReadBarrierForStringTableSlot(value);
                ASSERT_TRUE(result == value || result == reinterpret_cast<JSTaggedType>(nullptr));
            }
        }
    }

    cleaner->SetEnableConcurrentSweep(false);
}

/**
 * @tc.name: ConcurrentSweep_InternEmptyStringWithSharedGCTest
 * @tc.desc: Test interning empty strings with concurrent sweep triggered by SharedGC. Verify that
 *           empty strings are correctly handled after SharedGC triggers concurrent sweep.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, ConcurrentSweep_InternEmptyStringWithSharedGCTest)
{
    SharedHeap *sharedHeap = SharedHeap::GetInstance();
    ASSERT_NE(sharedHeap, nullptr);

    EcmaStringTable *stringTable = thread->GetEcmaVM()->GetEcmaStringTable();
    ASSERT_NE(stringTable, nullptr);

    EcmaStringTableCleaner *cleaner = stringTable->GetCleaner();
    ASSERT_NE(cleaner, nullptr);
    cleaner->SetEnableConcurrentSweep(true);

    for (int round = 0; round < 10; ++round) {
        sharedHeap->CollectGarbage<TriggerGCType::SHARED_GC, GCReason::OTHER>(thread);

        EcmaString *emptyStr = EcmaStringAccessor::CreateEmptyString(thread->GetEcmaVM());
        EcmaString *firstIntern = stringTable->GetOrInternString(thread->GetEcmaVM(), emptyStr);
        ASSERT_NE(firstIntern, nullptr);

        for (int i = 0; i < 100; ++i) {
            EcmaString *emptyStr2 = EcmaStringAccessor::CreateEmptyString(thread->GetEcmaVM());
            EcmaString *interned = stringTable->GetOrInternString(thread->GetEcmaVM(), emptyStr2);
            ASSERT_EQ(interned, firstIntern);
        }
    }

    cleaner->SetEnableConcurrentSweep(false);
}

/**
 * @tc.name: SharedGC_ParallelGCEnableDisableTest
 * @tc.desc: Test shared GC with parallel GC enable/disable. Verify that
           string table handles parallel GC state changes correctly.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, SharedGC_ParallelGCEnableDisableTest)
{
    SharedHeap *sharedHeap = SharedHeap::GetInstance();
    ASSERT_NE(sharedHeap, nullptr);

    Heap *heap = thread->GetEcmaVM()->GetHeap();
    ASSERT_NE(heap, nullptr);

    EcmaStringTable *stringTable = thread->GetEcmaVM()->GetEcmaStringTable();
    ASSERT_NE(stringTable, nullptr);

    EcmaStringTableCleaner *cleaner = stringTable->GetCleaner();
    ASSERT_NE(cleaner, nullptr);

    bool originalParallelGC = heap->IsParallelGCEnabled();

    for (int phase = 0; phase < 10; ++phase) {
        bool enableParallel = (phase % 2 == 0);
        heap->SetParallelGCEnabled(enableParallel);

        for (int round = 0; round < 10; ++round) {
            InternMultipleUtf8Strings(stringTable, 10, "parallel_test", phase, round);

            cleaner->SetEnableConcurrentSweep(true);
            sharedHeap->CollectGarbage<TriggerGCType::SHARED_GC, GCReason::OTHER>(thread);

            InternMultipleUtf8Strings(stringTable, 10, "parallel_concurrent", phase, round);

            cleaner->SetEnableConcurrentSweep(false);
        }
    }

    heap->SetParallelGCEnabled(originalParallelGC);
}

/**
 * @tc.name: StringTable_IsSweepingTest
 * @tc.desc: Test StringTable IsSweeping functionality. Verify that IsSweeping() returns
           correct state during concurrent sweep operations.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, StringTable_IsSweepingTest)
{
    EcmaStringTable *stringTable = thread->GetEcmaVM()->GetEcmaStringTable();
    ASSERT_NE(stringTable, nullptr);

    EcmaStringTableCleaner *cleaner = stringTable->GetCleaner();
    ASSERT_NE(cleaner, nullptr);
    cleaner->SetEnableConcurrentSweep(true);

    HashTrieMapType *hashTrieMap = reinterpret_cast<HashTrieMapType *>(stringTable->GetHashTrieMap());

    for (int round = 0; round < 10; ++round) {
        ASSERT_FALSE(stringTable->IsSweeping());

        {
            RuntimeLockHolder locker(thread, SharedHeap::GetInstance()->GetSuspensionRequestMutex());
            hashTrieMap->StartSweeping();
        }
        ASSERT_TRUE(stringTable->IsSweeping());

        InternMultipleUtf8Strings(stringTable, 100, "sweeping_test", round);

        {
            RuntimeLockHolder locker(thread, SharedHeap::GetInstance()->GetSuspensionRequestMutex());
            hashTrieMap->FinishSweeping();
        }
        ASSERT_FALSE(stringTable->IsSweeping());

        hashTrieMap->ClearToSpaceTagForFreshEntries();
    }

    cleaner->SetEnableConcurrentSweep(false);
}

/**
 * @tc.name: StringTable_IsInUseTest
 * @tc.desc: Test StringTable IsInUse functionality. Verify that IsInUse() correctly
           reflects the in-use state during HashTrieMapInUseScope.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, StringTable_IsInUseTest)
{
    EcmaStringTable *stringTable = thread->GetEcmaVM()->GetEcmaStringTable();
    ASSERT_NE(stringTable, nullptr);

    HashTrieMapType *hashTrieMap = reinterpret_cast<HashTrieMapType *>(stringTable->GetHashTrieMap());

    for (int round = 0; round < 10; ++round) {
        ASSERT_FALSE(stringTable->IsInUse());

        {
            HashTrieMapInUseScopeType scope(hashTrieMap);
            ASSERT_TRUE(stringTable->IsInUse());

            InternMultipleUtf8Strings(stringTable, 100, "inuse_test", round);

            ASSERT_TRUE(stringTable->IsInUse());
        }

        ASSERT_FALSE(stringTable->IsInUse());
    }
}

/**
 * @tc.name: StringTable_EnableStringTableConcurrentSweepPropertyTest
 * @tc.desc: Test StringTable concurrent sweep property configuration. Verify that
           the concurrent sweep property can be correctly enabled and disabled via ArkProperties.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, StringTable_EnableStringTableConcurrentSweepPropertyTest)
{
    SharedHeap *sharedHeap = SharedHeap::GetInstance();
    ASSERT_NE(sharedHeap, nullptr);

    EcmaStringTable *stringTable = thread->GetEcmaVM()->GetEcmaStringTable();
    ASSERT_NE(stringTable, nullptr);

    int64_t originalArkProperties = thread->GetEcmaVM()->GetJSOptions().GetArkProperties();

    for (int phase = 0; phase < 10; ++phase) {
        bool enableConcurrentSweep = (phase % 2 == 0);
        int64_t newProperties = originalArkProperties;
        if (enableConcurrentSweep) {
            newProperties &= ~ArkProperties::DISABLE_STRING_TABLE_CONCURRENT_SWEEP;
        } else {
            newProperties |= ArkProperties::DISABLE_STRING_TABLE_CONCURRENT_SWEEP;
        }
        thread->GetEcmaVM()->GetJSOptions().SetArkProperties(newProperties);

        bool actualValue = thread->GetEcmaVM()->GetJSOptions().EnableStringTableConcurrentSweep();
        ASSERT_EQ(actualValue, enableConcurrentSweep);

        InternMultipleUtf8Strings(stringTable, 100, "property_test", phase);

        sharedHeap->CollectGarbage<TriggerGCType::SHARED_GC, GCReason::OTHER>(thread);

        InternMultipleUtf8Strings(stringTable, 100, "property_after_gc", phase);
    }

    thread->GetEcmaVM()->GetJSOptions().SetArkProperties(originalArkProperties);
}

/**
 * @tc.name: HeapProfiler_DumpHeapSnapshotWithSharedGCTest
 * @tc.desc: Test heap profiler dump snapshot with shared GC. Verify that
           heap snapshot can be dumped correctly while interning strings and performing GC.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, HeapProfiler_DumpHeapSnapshotWithSharedGCTest)
{
    SharedHeap *sharedHeap = SharedHeap::GetInstance();
    ASSERT_NE(sharedHeap, nullptr);

    EcmaStringTable *stringTable = thread->GetEcmaVM()->GetEcmaStringTable();
    ASSERT_NE(stringTable, nullptr);

    HeapProfilerInterface *heapProfiler = HeapProfilerInterface::GetInstance(thread->GetEcmaVM());
    ASSERT_NE(heapProfiler, nullptr);

    for (int round = 0; round < 10; ++round) {
        InternMultipleUtf8Strings(stringTable, 100, "dump_gc", round);

        DumpSnapShotOption dumpOption;
        dumpOption.isFullGC = false;
        dumpOption.isSync = true;
        dumpOption.dumpFormat = DumpFormat::JSON;

        std::string fileName = "test_dump_gc_" + std::to_string(round) + ".heapsnapshot";
        FileStream stream(fileName.c_str());
        heapProfiler->DumpHeapSnapshot(&stream, dumpOption);

        sharedHeap->CollectGarbage<TriggerGCType::SHARED_GC, GCReason::OTHER>(thread);

        InternMultipleUtf8Strings(stringTable, 100, "dump_after_gc", round);
    }

    HeapProfilerInterface::Destroy(thread->GetEcmaVM());
}

/**
 * @tc.name: HeapProfiler_StartHeapTrackingWithSharedGCTest
 * @tc.desc: Test heap profiler start tracking with shared GC. Verify that
           heap tracking can be started correctly during string interning and GC operations.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, HeapProfiler_StartHeapTrackingWithSharedGCTest)
{
    SharedHeap *sharedHeap = SharedHeap::GetInstance();
    ASSERT_NE(sharedHeap, nullptr);

    EcmaStringTable *stringTable = thread->GetEcmaVM()->GetEcmaStringTable();
    ASSERT_NE(stringTable, nullptr);

    HeapProfilerInterface *heapProfiler = HeapProfilerInterface::GetInstance(thread->GetEcmaVM());
    ASSERT_NE(heapProfiler, nullptr);

    for (int round = 0; round < 10; ++round) {
        InternMultipleUtf8Strings(stringTable, 100, "tracking_gc", round);

        sharedHeap->CollectGarbage<TriggerGCType::SHARED_GC, GCReason::OTHER>(thread);

        std::string fileName = "test_tracking_gc_" + std::to_string(round) + ".heaptimeline";
        FileStream stream(fileName.c_str());
        heapProfiler->StartHeapTracking(50.0, true, &stream, false, false);

        InternMultipleUtf8Strings(stringTable, 100, "tracking_after", round);

        heapProfiler->StopHeapTracking(&stream, nullptr, false);
    }

    HeapProfilerInterface::Destroy(thread->GetEcmaVM());
}

/**
 * @tc.name: HeapProfiler_UpdateHeapTrackingWithSharedGCTest
 * @tc.desc: Test heap profiler update tracking with shared GC. Verify that
           heap tracking updates correctly during string interning and GC cycles.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, HeapProfiler_UpdateHeapTrackingWithSharedGCTest)
{
    SharedHeap *sharedHeap = SharedHeap::GetInstance();
    ASSERT_NE(sharedHeap, nullptr);

    EcmaStringTable *stringTable = thread->GetEcmaVM()->GetEcmaStringTable();
    ASSERT_NE(stringTable, nullptr);

    HeapProfilerInterface *heapProfiler = HeapProfilerInterface::GetInstance(thread->GetEcmaVM());
    ASSERT_NE(heapProfiler, nullptr);

    std::string fileName = "test_update_tracking.heaptimeline";
    FileStream stream(fileName.c_str());
    heapProfiler->StartHeapTracking(50.0, true, &stream, false, false);

    for (int round = 0; round < 10; ++round) {
        InternMultipleUtf8Strings(stringTable, 100, "update_tracking", round);

        sharedHeap->CollectGarbage<TriggerGCType::SHARED_GC, GCReason::OTHER>(thread);

        heapProfiler->UpdateHeapTracking(&stream);

        InternMultipleUtf8Strings(stringTable, 100, "update_after", round);
    }

    heapProfiler->StopHeapTracking(&stream, nullptr, false);
    HeapProfilerInterface::Destroy(thread->GetEcmaVM());
}

/**
 * @tc.name: HeapProfiler_StopHeapTrackingWithSharedGCTest
 * @tc.desc: Test heap profiler stop tracking with shared GC. Verify that
           heap tracking can be stopped correctly after string interning and GC operations.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, HeapProfiler_StopHeapTrackingWithSharedGCTest)
{
    SharedHeap *sharedHeap = SharedHeap::GetInstance();
    ASSERT_NE(sharedHeap, nullptr);

    EcmaStringTable *stringTable = thread->GetEcmaVM()->GetEcmaStringTable();
    ASSERT_NE(stringTable, nullptr);

    HeapProfilerInterface *heapProfiler = HeapProfilerInterface::GetInstance(thread->GetEcmaVM());
    ASSERT_NE(heapProfiler, nullptr);

    for (int round = 0; round < 10; ++round) {
        std::string fileName = "test_stop_tracking_" + std::to_string(round) + ".heaptimeline";
        FileStream stream(fileName.c_str());
        heapProfiler->StartHeapTracking(50.0, true, &stream, false, false);

        InternMultipleUtf8Strings(stringTable, 100, "stop_tracking", round);

        sharedHeap->CollectGarbage<TriggerGCType::SHARED_GC, GCReason::OTHER>(thread);

        InternMultipleUtf8Strings(stringTable, 100, "stop_before", round);

        heapProfiler->StopHeapTracking(&stream, nullptr, false);
    }

    HeapProfilerInterface::Destroy(thread->GetEcmaVM());
}

/**
 * @tc.name: SharedHeap_WaitAllTasksFinishedTest
 * @tc.desc: Test SharedHeap WaitAllTasksFinished functionality. Verify that
           all shared heap tasks complete correctly after GC and string interning.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, SharedHeap_WaitAllTasksFinishedTest)
{
    SharedHeap *sharedHeap = SharedHeap::GetInstance();
    ASSERT_NE(sharedHeap, nullptr);

    EcmaStringTable *stringTable = thread->GetEcmaVM()->GetEcmaStringTable();
    ASSERT_NE(stringTable, nullptr);

    EcmaStringTableCleaner *cleaner = stringTable->GetCleaner();
    ASSERT_NE(cleaner, nullptr);

    for (int round = 0; round < 10; ++round) {
        cleaner->SetEnableConcurrentSweep(true);

        InternMultipleUtf8Strings(stringTable, 100, "wait_all", round);

        sharedHeap->CollectGarbage<TriggerGCType::SHARED_GC, GCReason::OTHER>(thread);

        sharedHeap->WaitAllTasksFinished(thread);

        ASSERT_FALSE(stringTable->IsSweeping());

        cleaner->SetEnableConcurrentSweep(false);
    }
}

/**
 * @tc.name: SharedHeap_PrepareTest
 * @tc.desc: Test SharedHeap Prepare functionality. Verify that
           shared heap preparation works correctly with string table operations.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, SharedHeap_PrepareTest)
{
    SharedHeap *sharedHeap = SharedHeap::GetInstance();
    ASSERT_NE(sharedHeap, nullptr);

    EcmaStringTable *stringTable = thread->GetEcmaVM()->GetEcmaStringTable();
    ASSERT_NE(stringTable, nullptr);

    EcmaStringTableCleaner *cleaner = stringTable->GetCleaner();
    ASSERT_NE(cleaner, nullptr);

    for (int round = 0; round < 10; ++round) {
        cleaner->SetEnableConcurrentSweep(true);

        InternMultipleUtf8Strings(stringTable, 100, "prepare_test", round);

        sharedHeap->CollectGarbage<TriggerGCType::SHARED_GC, GCReason::OTHER>(thread);

        sharedHeap->PrepareByJSThread(thread, false);

        ASSERT_FALSE(stringTable->IsSweeping());

        cleaner->SetEnableConcurrentSweep(false);
    }
}

/**
 * @tc.name: SharedHeap_PrepareByJSThreadTest
 * @tc.desc: Test SharedHeap PrepareByJSThread functionality. Verify that
           shared heap preparation via JSThread works correctly with string table.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, SharedHeap_PrepareByJSThreadTest)
{
    SharedHeap *sharedHeap = SharedHeap::GetInstance();
    ASSERT_NE(sharedHeap, nullptr);

    EcmaStringTable *stringTable = thread->GetEcmaVM()->GetEcmaStringTable();
    ASSERT_NE(stringTable, nullptr);

    EcmaStringTableCleaner *cleaner = stringTable->GetCleaner();
    ASSERT_NE(cleaner, nullptr);

    for (int round = 0; round < 10; ++round) {
        cleaner->SetEnableConcurrentSweep(true);

        InternMultipleUtf8Strings(stringTable, 100, "prepare_js", round);

        sharedHeap->CollectGarbage<TriggerGCType::SHARED_GC, GCReason::OTHER>(thread);

        sharedHeap->PrepareByJSThread(thread, false);

        ASSERT_FALSE(stringTable->IsSweeping());

        cleaner->SetEnableConcurrentSweep(false);
    }
}

/**
 * @tc.name: HashTrieMap_ClearTest
 * @tc.desc: Test HashTrieMap Clear functionality. Verify that
           HashTrieMap can be cleared correctly after inserting entries.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, HashTrieMap_ClearTest)
{
    HashTrieMapTypeNormal hashTrieMap;
    HashTrieMapOperationNormalType operation(&hashTrieMap);
    HashTrieMapInUseScopeTypeNormal inUseScope(&hashTrieMap);

    for (int i = 0; i < 100; ++i) {
        char nameBuf[32];
        int ret = snprintf_s(nameBuf, sizeof(nameBuf), sizeof(nameBuf) - 1, "clear_test_%d", i);
        if (ret < 0) {
            LOG_ECMA(ERROR) << "snprintf_s failed in HashTrieMap_ClearTest";
            continue;
        }
        uint8_t utf8Data[32];
        errno_t memcpyRet = memcpy_s(utf8Data, sizeof(utf8Data), nameBuf, strlen(nameBuf));
        if (memcpyRet != EOK) {
            LOG_ECMA(ERROR) << "memcpy_s failed in HashTrieMap_ClearTest";
            continue;
        }
        uint32_t utf8Len = strlen(nameBuf);

        BaseString *result = LoadOrStoreUtf8String(operation, utf8Data, utf8Len);
        ASSERT_NE(result, nullptr);
    }

    bool hasNonEmptyRoot = false;
    for (uint32_t i = 0; i < TrieMapConfig::ROOT_SIZE; ++i) {
        auto root = hashTrieMap.GetRoot(i).load(std::memory_order_acquire);
        if (root != nullptr) {
            hasNonEmptyRoot = true;
            break;
        }
    }
    ASSERT_TRUE(hasNonEmptyRoot);

    hashTrieMap.Clear();

    for (uint32_t i = 0; i < TrieMapConfig::ROOT_SIZE; ++i) {
        auto root = hashTrieMap.GetRoot(i).load(std::memory_order_acquire);
        ASSERT_EQ(root, nullptr);
    }
}

/**
 * @tc.name: HashTrieMap_GetOrCreateRootTest
 * @tc.desc: Test HashTrieMap GetOrCreateRoot functionality. Verify that
           roots can be created and retrieved correctly.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, HashTrieMap_GetOrCreateRootTest)
{
    HashTrieMapType hashTrieMap;

    for (int round = 0; round < 100; ++round) {
        for (uint32_t i = 0; i < TrieMapConfig::ROOT_SIZE; ++i) {
            auto root = hashTrieMap.GetOrCreateRoot(i);
            ASSERT_NE(root, nullptr);
            ASSERT_EQ(root, hashTrieMap.GetRoot(i).load(std::memory_order_acquire));
        }

        hashTrieMap.Clear();
    }
}

/**
 * @tc.name: HashTrieMap_ClearNodeFromGC_NormalInsertUtf8
 * @tc.desc: Test ClearNodeFromGC with Normal mode insertion for UTF8 strings. Use Normal mode
 *           (NoSlotBarrierDynamic) HashTrieMapOperation to insert UTF8 strings via LoadOrStore,
 *           these entries have no ToSpace tag. Then use ConcurrentSweep mode (NeedSlotBarrier)
 *           HashTrieMapOperation to call ClearNodeFromGC.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, HashTrieMap_ClearNodeFromGC_NormalInsertUtf8Test)
{
    HashTrieMapType hashTrieMap;
    HashTrieMapInUseScopeType inUseScope(&hashTrieMap);

    HashTrieMapOperationNormalType normalOp(&hashTrieMap);

    for (int i = 0; i < 100; ++i) {
        char nameBuf[32];
        int ret = snprintf_s(nameBuf, sizeof(nameBuf), sizeof(nameBuf) - 1, "normal_insert_%d", i);
        if (ret < 0) {
            LOG_ECMA(ERROR) << "snprintf_s failed in NormalInsertTest";
            continue;
        }
        uint8_t utf8Data[32];
        errno_t memcpyRet = memcpy_s(utf8Data, sizeof(utf8Data), nameBuf, strlen(nameBuf));
        if (memcpyRet != EOK) {
            LOG_ECMA(ERROR) << "memcpy_s failed in NormalInsertTest";
            continue;
        }
        uint32_t utf8Len = strlen(nameBuf);

        BaseString *result = LoadOrStoreUtf8String(normalOp, utf8Data, utf8Len);
        ASSERT_NE(result, nullptr);
    }

    DoSweepingWithClearNodeKeepAll(hashTrieMap);

    hashTrieMap.Clear();
}

/**
 * @tc.name: HashTrieMap_ClearNodeFromGC_ConcurrentSweepInsertUtf8
 * @tc.desc: Test ClearNodeFromGC with ConcurrentSweep mode insertion for UTF8 strings. Start sweeping first,
 *           then use ConcurrentSweep mode (NeedSlotBarrier) HashTrieMapOperation to insert UTF8 strings
 *           via LoadOrStore. These entries are created with ToSpace tag during sweeping.
 *           Then call ClearNodeFromGC to clear these entries.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, HashTrieMap_ClearNodeFromGC_ConcurrentSweepInsertUtf8Test)
{
    HashTrieMapType hashTrieMap;
    HashTrieMapInUseScopeType inUseScope(&hashTrieMap);

    HashTrieMapOperationConcurrentSweepType concurrentSweepOp(&hashTrieMap);

    for (int i = 0; i < 100; ++i) {
        char nameBuf[32];
        int ret = snprintf_s(nameBuf, sizeof(nameBuf), sizeof(nameBuf) - 1, "concurrent_insert_%d", i);
        if (ret < 0) {
            LOG_ECMA(ERROR) << "snprintf_s failed in ConcurrentSweepInsertTest";
            continue;
        }
        uint8_t utf8Data[32];
        errno_t memcpyRet = memcpy_s(utf8Data, sizeof(utf8Data), nameBuf, strlen(nameBuf));
        if (memcpyRet != EOK) {
            LOG_ECMA(ERROR) << "memcpy_s failed in ConcurrentSweepInsertTest";
            continue;
        }
        uint32_t utf8Len = strlen(nameBuf);

        BaseString *result = LoadOrStoreUtf8String(concurrentSweepOp, utf8Data, utf8Len);
        ASSERT_NE(result, nullptr);
    }

    DoSweepingWithClearNodeKeepAll(hashTrieMap);

    hashTrieMap.Clear();
}

/**
 * @tc.name: HashTrieMap_ClearNodeFromGC_MixedInsertUtf8
 * @tc.desc: Test ClearNodeFromGC with mixed mode insertion for UTF8 strings. First use Normal mode
 *           (NoSlotBarrierDynamic) to insert UTF8 entries without ToSpace tag, then start sweeping and
 *           use ConcurrentSweep mode (NeedSlotBarrier) to insert UTF8 entries with ToSpace tag.
 *           Finally call ClearNodeFromGC.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, HashTrieMap_ClearNodeFromGC_MixedInsertUtf8Test)
{
    HashTrieMapType hashTrieMap;
    HashTrieMapInUseScopeType inUseScope(&hashTrieMap);

    HashTrieMapOperationNormalType normalOp(&hashTrieMap);

    for (int i = 0; i < 50; ++i) {
        char nameBuf[32];
        int ret = snprintf_s(nameBuf, sizeof(nameBuf), sizeof(nameBuf) - 1, "normal_mixed_%d", i);
        if (ret < 0) {
            LOG_ECMA(ERROR) << "snprintf_s failed in MixedInsertTest";
            continue;
        }
        uint8_t utf8Data[32];
        errno_t memcpyRet = memcpy_s(utf8Data, sizeof(utf8Data), nameBuf, strlen(nameBuf));
        if (memcpyRet != EOK) {
            LOG_ECMA(ERROR) << "memcpy_s failed in MixedInsertTest";
            continue;
        }
        uint32_t utf8Len = strlen(nameBuf);

        BaseString *result = LoadOrStoreUtf8String(normalOp, utf8Data, utf8Len);
        ASSERT_NE(result, nullptr);
    }

    HashTrieMapOperationConcurrentSweepType concurrentSweepOp(&hashTrieMap);

    for (int i = 50; i < 100; ++i) {
        char nameBuf[32];
        int ret = snprintf_s(nameBuf, sizeof(nameBuf), sizeof(nameBuf) - 1, "concurrent_mixed_%d", i);
        if (ret < 0) {
            LOG_ECMA(ERROR) << "snprintf_s failed in MixedInsertTest";
            continue;
        }
        uint8_t utf8Data[32];
        errno_t memcpyRet = memcpy_s(utf8Data, sizeof(utf8Data), nameBuf, strlen(nameBuf));
        if (memcpyRet != EOK) {
            LOG_ECMA(ERROR) << "memcpy_s failed in MixedInsertTest";
            continue;
        }
        uint32_t utf8Len = strlen(nameBuf);

        BaseString *result = LoadOrStoreUtf8String(concurrentSweepOp, utf8Data, utf8Len);
        ASSERT_NE(result, nullptr);
    }

    DoSweepingWithClearNodeKeepAll(hashTrieMap);

    hashTrieMap.Clear();
}

/**
 * @tc.name: HashTrieMap_ClearNodeFromGC_NormalInsertUtf16
 * @tc.desc: Test ClearNodeFromGC with Normal mode insertion for UTF16 strings. Use Normal mode
 *           (NoSlotBarrierDynamic) HashTrieMapOperation to insert UTF16 strings via LoadOrStore,
 *           these entries have no ToSpace tag. Then use ConcurrentSweep mode (NeedSlotBarrier)
 *           HashTrieMapOperation to call ClearNodeFromGC.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, HashTrieMap_ClearNodeFromGC_NormalInsertUtf16Test)
{
    HashTrieMapType hashTrieMap;
    HashTrieMapInUseScopeType inUseScope(&hashTrieMap);

    HashTrieMapOperationNormalType normalOp(&hashTrieMap);

    for (int i = 0; i < 100; ++i) {
        uint16_t utf16Data[16];
        uint32_t utf16Len = MakeUtf16DataFromAsciiWithNumber(utf16Data, "normal_utf16_", i);

        BaseString *result = LoadOrStoreUtf16String(normalOp, utf16Data, utf16Len);
        ASSERT_NE(result, nullptr);
    }

    DoSweepingWithClearNodeKeepAll(hashTrieMap);

    hashTrieMap.Clear();
}

/**
 * @tc.name: HashTrieMap_ClearNodeFromGC_ConcurrentSweepInsertUtf16
 * @tc.desc: Test ClearNodeFromGC with ConcurrentSweep mode insertion for UTF16 strings. Start sweeping first,
 *           then use ConcurrentSweep mode (NeedSlotBarrier) HashTrieMapOperation to insert UTF16 strings
 *           via LoadOrStore. These entries are created with ToSpace tag during sweeping.
 *           Then call ClearNodeFromGC to clear these entries.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, HashTrieMap_ClearNodeFromGC_ConcurrentSweepInsertUtf16Test)
{
    HashTrieMapType hashTrieMap;
    HashTrieMapInUseScopeType inUseScope(&hashTrieMap);

    HashTrieMapOperationConcurrentSweepType concurrentSweepOp(&hashTrieMap);

    for (int i = 0; i < 100; ++i) {
        uint16_t utf16Data[20];
        uint32_t utf16Len = MakeUtf16DataFromAsciiWithNumber(utf16Data, "concurrent_utf16_", i);

        BaseString *result = LoadOrStoreUtf16String(concurrentSweepOp, utf16Data, utf16Len);
        ASSERT_NE(result, nullptr);
    }

    DoSweepingWithClearNodeKeepAll(hashTrieMap);

    hashTrieMap.Clear();
}

/**
 * @tc.name: HashTrieMap_ClearNodeFromGC_MixedInsertUtf16
 * @tc.desc: Test ClearNodeFromGC with mixed mode insertion for UTF16 strings. First use Normal mode
 *           (NoSlotBarrierDynamic) to insert UTF16 entries without ToSpace tag, then start sweeping and
 *           use ConcurrentSweep mode (NeedSlotBarrier) to insert UTF16 entries with ToSpace tag.
 *           Finally call ClearNodeFromGC.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, HashTrieMap_ClearNodeFromGC_MixedInsertUtf16Test)
{
    HashTrieMapType hashTrieMap;
    HashTrieMapInUseScopeType inUseScope(&hashTrieMap);

    HashTrieMapOperationNormalType normalOp(&hashTrieMap);

    for (int i = 0; i < 50; ++i) {
        uint16_t utf16Data[16];
        uint32_t utf16Len = MakeUtf16DataFromAsciiWithNumber(utf16Data, "normal_mix_", i);

        BaseString *result = LoadOrStoreUtf16String(normalOp, utf16Data, utf16Len);
        ASSERT_NE(result, nullptr);
    }

    HashTrieMapOperationConcurrentSweepType concurrentSweepOp(&hashTrieMap);

    for (int i = 50; i < 100; ++i) {
        uint16_t utf16Data[20];
        uint32_t utf16Len = MakeUtf16DataFromAsciiWithNumber(utf16Data, "concurrent_mix_", i);

        BaseString *result = LoadOrStoreUtf16String(concurrentSweepOp, utf16Data, utf16Len);
        ASSERT_NE(result, nullptr);
    }

    DoSweepingWithClearNodeKeepAll(hashTrieMap);

    hashTrieMap.Clear();
}

/**
 * @tc.name: HashTrieMap_ClearNodeFromGC_NoAtomicMarkUtf8
 * @tc.desc: Test ClearNodeFromGC without AtomicMark for UTF8 strings. Use Normal mode (NoSlotBarrierDynamic)
 *           HashTrieMapOperation to insert UTF8 strings via LoadOrStore without calling AtomicMark
 *           on the string objects. The visitor returns nullptr to simulate unmarked objects
 *           being cleared during GC.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, HashTrieMap_ClearNodeFromGC_NoAtomicMarkUtf8Test)
{
    HashTrieMapType hashTrieMap;
    HashTrieMapInUseScopeType inUseScope(&hashTrieMap);

    HashTrieMapOperationNormalType normalOp(&hashTrieMap);

    for (int i = 0; i < 100; ++i) {
        char nameBuf[32];
        int ret = snprintf_s(nameBuf, sizeof(nameBuf), sizeof(nameBuf) - 1, "no_mark_utf8_%d", i);
        if (ret < 0) {
            LOG_ECMA(ERROR) << "snprintf_s failed in NoAtomicMarkUtf8Test";
            continue;
        }
        uint8_t utf8Data[32];
        errno_t memcpyRet = memcpy_s(utf8Data, sizeof(utf8Data), nameBuf, strlen(nameBuf));
        if (memcpyRet != EOK) {
            LOG_ECMA(ERROR) << "memcpy_s failed in NoAtomicMarkUtf8Test";
            continue;
        }
        uint32_t utf8Len = strlen(nameBuf);

        BaseString *result = LoadOrStoreUtf8String(normalOp, utf8Data, utf8Len);
        ASSERT_NE(result, nullptr);
    }

    DoSweepingWithClearNodeClearAll(hashTrieMap);

    hashTrieMap.Clear();
}

/**
 * @tc.name: HashTrieMap_ClearNodeFromGC_AllAtomicMarkUtf8
 * @tc.desc: Test ClearNodeFromGC with all entries AtomicMarked for UTF8 strings. Use Normal mode
 *           (NoSlotBarrierDynamic) HashTrieMapOperation to insert UTF8 strings via LoadOrStore,
 *           and call AtomicMark on all string objects. The visitor checks the mark bit and returns
 *           the object if marked, simulating all objects surviving during GC.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, HashTrieMap_ClearNodeFromGC_AllAtomicMarkUtf8Test)
{
    HashTrieMapType hashTrieMap;
    HashTrieMapInUseScopeType inUseScope(&hashTrieMap);

    HashTrieMapOperationNormalType normalOp(&hashTrieMap);

    hashTrieMap.GetMutex().Lock();
    for (int i = 0; i < 100; ++i) {
        char nameBuf[32];
        int ret = snprintf_s(nameBuf, sizeof(nameBuf), sizeof(nameBuf) - 1, "all_mark_utf8_%d", i);
        if (ret < 0) {
            LOG_ECMA(ERROR) << "snprintf_s failed in AllAtomicMarkUtf8Test";
            continue;
        }
        uint8_t utf8Data[32];
        errno_t memcpyRet = memcpy_s(utf8Data, sizeof(utf8Data), nameBuf, strlen(nameBuf));
        if (memcpyRet != EOK) {
            LOG_ECMA(ERROR) << "memcpy_s failed in AllAtomicMarkUtf8Test";
            continue;
        }
        uint32_t utf8Len = strlen(nameBuf);

        BaseString *result = LoadOrStoreUtf8StringWithAtomicMark<
            HashTrieMapOperationNormalType, false>(normalOp, utf8Data, utf8Len);
        ASSERT_NE(result, nullptr);
    }
    hashTrieMap.GetMutex().Unlock();

    DoSweepingWithClearNodeKeepAll(hashTrieMap);

    hashTrieMap.Clear();
}

/**
 * @tc.name: HashTrieMap_ClearNodeFromGC_PartialAtomicMarkUtf8
 * @tc.desc: Test ClearNodeFromGC with partial entries AtomicMarked for UTF8 strings. Use Normal mode
 *           (NoSlotBarrierDynamic) HashTrieMapOperation to insert UTF8 strings via LoadOrStore.
 *           Half of the entries are AtomicMarked and half are not. The visitor checks the mark bit,
 *           simulating partial objects surviving during GC while others are cleared.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, HashTrieMap_ClearNodeFromGC_PartialAtomicMarkUtf8Test)
{
    HashTrieMapType hashTrieMap;
    HashTrieMapInUseScopeType inUseScope(&hashTrieMap);

    HashTrieMapOperationNormalType normalOp(&hashTrieMap);

    hashTrieMap.GetMutex().Lock();
    for (int i = 0; i < 50; ++i) {
        char nameBuf[32];
        int ret = snprintf_s(nameBuf, sizeof(nameBuf), sizeof(nameBuf) - 1, "partial_yes_utf8_%d", i);
        if (ret < 0) {
            LOG_ECMA(ERROR) << "snprintf_s failed in PartialAtomicMarkUtf8Test";
            continue;
        }
        uint8_t utf8Data[32];
        errno_t memcpyRet = memcpy_s(utf8Data, sizeof(utf8Data), nameBuf, strlen(nameBuf));
        if (memcpyRet != EOK) {
            LOG_ECMA(ERROR) << "memcpy_s failed in PartialAtomicMarkUtf8Test";
            continue;
        }
        uint32_t utf8Len = strlen(nameBuf);

        BaseString *result = LoadOrStoreUtf8StringWithAtomicMark<
            HashTrieMapOperationNormalType, false>(normalOp, utf8Data, utf8Len);
        ASSERT_NE(result, nullptr);
    }

    for (int i = 50; i < 100; ++i) {
        char nameBuf[32];
        int ret = snprintf_s(nameBuf, sizeof(nameBuf), sizeof(nameBuf) - 1, "partial_no_utf8_%d", i);
        if (ret < 0) {
            LOG_ECMA(ERROR) << "snprintf_s failed in PartialAtomicMarkUtf8Test";
            continue;
        }
        uint8_t utf8Data[32];
        errno_t memcpyRet = memcpy_s(utf8Data, sizeof(utf8Data), nameBuf, strlen(nameBuf));
        if (memcpyRet != EOK) {
            LOG_ECMA(ERROR) << "memcpy_s failed in PartialAtomicMarkUtf8Test";
            continue;
        }
        uint32_t utf8Len = strlen(nameBuf);

        BaseString *result = LoadOrStoreUtf8String<
            HashTrieMapOperationNormalType, false>(normalOp, utf8Data, utf8Len);
        ASSERT_NE(result, nullptr);
    }
    hashTrieMap.GetMutex().Unlock();

    DoSweepingWithClearNodeKeepAll(hashTrieMap);

    hashTrieMap.Clear();
}

/**
 * @tc.name: HashTrieMap_ClearNodeFromGC_NoAtomicMarkUtf16
 * @tc.desc: Test ClearNodeFromGC without AtomicMark for UTF16 strings. Use Normal mode (NoSlotBarrierDynamic)
 *           HashTrieMapOperation to insert UTF16 strings via LoadOrStore without calling AtomicMark
 *           on the string objects. The visitor returns nullptr to simulate unmarked objects
 *           being cleared during GC.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, HashTrieMap_ClearNodeFromGC_NoAtomicMarkUtf16Test)
{
    HashTrieMapType hashTrieMap;
    HashTrieMapInUseScopeType inUseScope(&hashTrieMap);

    HashTrieMapOperationNormalType normalOp(&hashTrieMap);

    for (int i = 0; i < 100; ++i) {
        uint16_t utf16Data[8];
        uint32_t utf16Len = MakeUtf16DataFromAsciiWithNumber(utf16Data, "n", i);

        BaseString *result = LoadOrStoreUtf16String(normalOp, utf16Data, utf16Len);
        ASSERT_NE(result, nullptr);
    }

    DoSweepingWithClearNodeClearAll(hashTrieMap);

    hashTrieMap.Clear();
}

/**
 * @tc.name: HashTrieMap_ClearNodeFromGC_AllAtomicMarkUtf16
 * @tc.desc: Test ClearNodeFromGC with all entries AtomicMarked for UTF16 strings. Use Normal mode
 *           (NoSlotBarrierDynamic) HashTrieMapOperation to insert UTF16 strings via LoadOrStore,
 *           and call AtomicMark on all string objects. The visitor checks the mark bit and returns
 *           the object if marked, simulating all objects surviving during GC.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, HashTrieMap_ClearNodeFromGC_AllAtomicMarkUtf16Test)
{
    HashTrieMapType hashTrieMap;
    HashTrieMapInUseScopeType inUseScope(&hashTrieMap);

    HashTrieMapOperationNormalType normalOp(&hashTrieMap);

    hashTrieMap.GetMutex().Lock();
    for (int i = 0; i < 100; ++i) {
        uint16_t utf16Data[16];
        uint32_t utf16Len = MakeUtf16DataFromAsciiWithNumber(utf16Data, "all_", i);

        BaseString *result = LoadOrStoreUtf16StringWithAtomicMark<
            HashTrieMapOperationNormalType, false>(normalOp, utf16Data, utf16Len);
        ASSERT_NE(result, nullptr);
    }
    hashTrieMap.GetMutex().Unlock();

    DoSweepingWithClearNodeKeepAll(hashTrieMap);

    hashTrieMap.Clear();
}

/**
 * @tc.name: HashTrieMap_ClearNodeFromGC_PartialAtomicMarkUtf16
 * @tc.desc: Test ClearNodeFromGC with partial entries AtomicMarked for UTF16 strings. Use Normal mode
 *           (NoSlotBarrierDynamic) HashTrieMapOperation to insert UTF16 strings via LoadOrStore.
 *           Half of the entries are AtomicMarked and half are not. The visitor checks the mark bit,
 *           simulating partial objects surviving during GC while others are cleared.
 * @tc.type: FUNC
 * @tc.require:
 */
HWTEST_F_L0(EcmaStringTableSweepingTest, HashTrieMap_ClearNodeFromGC_PartialAtomicMarkUtf16Test)
{
    HashTrieMapType hashTrieMap;
    HashTrieMapInUseScopeType inUseScope(&hashTrieMap);

    HashTrieMapOperationNormalType normalOp(&hashTrieMap);

    hashTrieMap.GetMutex().Lock();
    for (int i = 0; i < 50; ++i) {
        uint16_t utf16Data[24];
        uint32_t utf16Len = MakeUtf16DataFromAsciiWithNumber(utf16Data, "partial_yes_", i);

        BaseString *result = LoadOrStoreUtf16StringWithAtomicMark<
            HashTrieMapOperationNormalType, false>(normalOp, utf16Data, utf16Len);
        ASSERT_NE(result, nullptr);
    }

    for (int i = 50; i < 100; ++i) {
        uint16_t utf16Data[24];
        uint32_t utf16Len = MakeUtf16DataFromAsciiWithNumber(utf16Data, "partial_no_", i);

        BaseString *result = LoadOrStoreUtf16String<
            HashTrieMapOperationNormalType, false>(normalOp, utf16Data, utf16Len);
        ASSERT_NE(result, nullptr);
    }
    hashTrieMap.GetMutex().Unlock();

    DoSweepingWithClearNodeKeepAll(hashTrieMap);

    hashTrieMap.Clear();
}

}  // namespace panda::test