/*
 * Copyright (c) 2021 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 <chrono>
#include <thread>

#include "ecmascript/builtins/builtins_ark_tools.h"
#include "ecmascript/ecma_vm.h"
#include "ecmascript/mem/full_gc.h"
#include "ecmascript/mem/idle_gc_trigger.h"
#include "ecmascript/object_factory-inl.h"
#include "ecmascript/mem/concurrent_marker.h"
#include "ecmascript/mem/partial_gc.h"
#include "ecmascript/tests/ecma_test_common.h"
#include "ecmascript/napi/include/jsnapi_expo.h"
#include "ecmascript/mem/free_object_list.h"
#include "ecmascript/mem/gc_stats.h"
#include "ecmascript/mem/free_object_set.h"
#include "ecmascript/mem/shared_mem_controller.h"
#include "ecmascript/mem/mem_controller_utils.h"
#include "ecmascript/mem/mem_controller.h"
#if defined(PANDA_TARGET_OHOS) && !defined(STANDALONE_MODE)
#include "parameters.h"
#endif

using namespace panda;

using namespace panda::ecmascript;
using TRIGGER_IDLE_GC_TYPE = panda::JSNApi::TRIGGER_IDLE_GC_TYPE;

namespace panda::test {
class GCTest : public BaseTestWithScope<false> {
public:
    void SetUp() override
    {
#if defined(PANDA_TARGET_OHOS) && !defined(STANDALONE_MODE)
        OHOS::system::SetParameter("persist.ark.sheap.growfactor", "10");
        OHOS::system::SetParameter("persist.ark.sheap.growstep", "640");
        OHOS::system::SetParameter("persist.ark.sensitive.threshold", "640");
        OHOS::system::SetParameter("persist.ark.native.stepsize", "2048");
        OHOS::system::SetParameter("persist.ark.global.alloclimit", "256");
#endif
        JSRuntimeOptions options;
        instance = JSNApi::CreateEcmaVM(options);
        ASSERT_TRUE(instance != nullptr) << "Cannot create EcmaVM";
        thread = instance->GetJSThread();
        thread->ManagedCodeBegin();
        scope = new EcmaHandleScope(thread);
        auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
        heap->GetConcurrentMarker()->EnableConcurrentMarking(EnableConcurrentMarkType::ENABLE);
        heap->GetSweeper()->EnableConcurrentSweep(EnableConcurrentSweepType::ENABLE);
    }
};

class TestableGCStats : public GCStats {
public:
    explicit TestableGCStats(const Heap *heap) : GCStats(heap) {}

    using GCStats::GetGCStatistic;
    using GCStats::GetGCStatisticType;
    using GCStats::MergeGCStatistic;
    using GCStats::RecordGCStatisticEnd;
    using GCStats::RecordGCStatisticStart;
    using GCStats::SetRecordDuration;

    void SetGCTypeForTest(GCType type)
    {
        gcType_ = type;
    }
};

HWTEST_F_L0(GCTest, NativeGCTestConcurrentMarkDisabled)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    // Disable concurrent mark.
    heap->GetConcurrentMarker()->ConfigConcurrentMark(false);
    size_t oldNativeSize = heap->GetNativeBindingSize();
    EcmaTestCommon::GcCommonCase(thread, heap, false);
    const_cast<Heap *>(thread->GetEcmaVM()->GetHeap())->CollectGarbage(TriggerGCType::OLD_GC);
    auto newNativeSize = heap->GetNativeBindingSize();
    EXPECT_EQ(newNativeSize - oldNativeSize, 0UL);
}

HWTEST_F_L0(GCTest, NonNewSpaceNativeGCTestConcurrentMarkDisabled)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    // Disable concurrent mark.
    heap->GetConcurrentMarker()->ConfigConcurrentMark(false);
    size_t oldNativeSize = heap->GetNativeBindingSize();
    EcmaTestCommon::GcCommonCase(thread, heap);
    const_cast<Heap *>(thread->GetEcmaVM()->GetHeap())->CollectGarbage(TriggerGCType::OLD_GC);
    auto newNativeSize = heap->GetNativeBindingSize();
    EXPECT_EQ(newNativeSize - oldNativeSize, 0UL);
}

HWTEST_F_L0(GCTest, CSetTest)
{
    ObjectFactory *factory = instance->GetFactory();
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    constexpr uint32_t length = 300;
    heap->SetSensitiveStatus(AppSensitiveStatus::ENTER_HIGH_SENSITIVE);
    JSHandle<TaggedArray> array = factory->NewTaggedArray(length, JSTaggedValue::Undefined());
    instance->CollectGarbage(TriggerGCType::OLD_GC, GCReason::ALLOCATION_FAILED);
    instance->CollectGarbage(TriggerGCType::OLD_GC);
    Region *region = Region::ObjectAddressToRange(*array);
    heap->SetFullMarkRequestedState(true);
    heap->TryTriggerConcurrentMarking(MarkReason::OTHER);
    EXPECT_TRUE(!region->InCollectSet());
}

HWTEST_F_L0(GCTest, ArkToolsForceFullGC)
{
    const_cast<Heap *>(thread->GetEcmaVM()->GetHeap())->CollectGarbage(TriggerGCType::FULL_GC);
    size_t originalHeapSize = thread->GetEcmaVM()->GetHeap()->GetCommittedSize();
    size_t newSize = originalHeapSize;
    {
        [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);

        for (int i = 0; i < 10; i++) {
            [[maybe_unused]] JSHandle<TaggedArray> obj = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(1024 * 1024);
        }
        newSize = thread->GetEcmaVM()->GetHeap()->GetCommittedSize();
    }
    EXPECT_TRUE(newSize > originalHeapSize);
    auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 0);

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
    [[maybe_unused]] JSTaggedValue result1 = builtins::BuiltinsArkTools::ForceFullGC(ecmaRuntimeCallInfo);

    ASSERT_TRUE(thread->GetEcmaVM()->GetHeap()->GetCommittedSize() < newSize);
}

HWTEST_F_L0(GCTest, ColdStartForceExpand)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    size_t originalHeapSize = heap->GetCommittedSize();
    heap->GetConcurrentMarker()->ConfigConcurrentMark(false);
    heap->NotifyPostFork();
    heap->NotifyFinishColdStartSoon();
    {
        [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
        for (int i = 0; i < 500; i++) {
            [[maybe_unused]] JSHandle<TaggedArray> array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(
                10 * 1024, JSTaggedValue::Hole(), MemSpaceType::SEMI_SPACE);
        }
    }
    size_t expandHeapSize = thread->GetEcmaVM()->GetHeap()->GetCommittedSize();
    usleep(10000000);
    size_t newSize = EcmaTestCommon::GcCommonCase(thread);
    EXPECT_TRUE(originalHeapSize < expandHeapSize);
    if constexpr (G_USE_CMS_GC) {
        return;
    }
    EXPECT_TRUE(expandHeapSize > newSize);
}

HWTEST_F_L0(GCTest, HighSensitiveForceExpand)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    size_t originalHeapSize = heap->GetCommittedSize();
    heap->GetConcurrentMarker()->ConfigConcurrentMark(false);
    heap->NotifyHighSensitive(true);
    {
        [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
        for (int i = 0; i < 500; i++) {
            [[maybe_unused]] JSHandle<TaggedArray> array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(
                10 * 1024, JSTaggedValue::Hole(), MemSpaceType::SEMI_SPACE);
        }
    }
    size_t expandHeapSize = thread->GetEcmaVM()->GetHeap()->GetCommittedSize();
    const_cast<Heap *>(thread->GetEcmaVM()->GetHeap())->NotifyHighSensitive(false);
    size_t newSize = EcmaTestCommon::GcCommonCase(thread);
    EXPECT_TRUE(originalHeapSize < expandHeapSize);
    if constexpr (G_USE_CMS_GC) {
        return;
    }
    EXPECT_TRUE(expandHeapSize > newSize);
}

HWTEST_F_L0(GCTest, HighSensitiveExceedMaxHeapSize)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    heap->NotifyHighSensitive(true);
    // First allocate about 250M TaggedArray, not reach max heap size
    {
        [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
        for (int i = 0; i < 16 * 1000; i++) {
            [[maybe_unused]] JSHandle<TaggedArray> array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(
                1024, JSTaggedValue::Hole(), MemSpaceType::SEMI_SPACE);
        }
    }
    // Continue allocate about 250M TaggedArray, now reach max heap size, must trigger gc to avoid OOM
    {
        [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
        for (int i = 0; i < 10 * 1000; i++) {
            [[maybe_unused]] JSHandle<TaggedArray> array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(
                1024, JSTaggedValue::Hole(), MemSpaceType::SEMI_SPACE);
        }
    }
    size_t commitSize = thread->GetEcmaVM()->GetHeap()->GetCommittedSize();
    const_cast<Heap *>(thread->GetEcmaVM()->GetHeap())->NotifyHighSensitive(false);
    EXPECT_TRUE(commitSize < thread->GetEcmaVM()->GetEcmaParamConfiguration().GetMaxHeapSize());
}

HWTEST_F_L0(GCTest, HighSensitiveExceedMaxHeapSizeVerify)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    heap->Prepare();
    heap->EnableHeapVerication(true);
    heap->NotifyHighSensitive(true);
    // First allocate about 250M TaggedArray, not reach max heap size
    {
        [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
        for (int i = 0; i < 16 * 1000; i++) {
            [[maybe_unused]] JSHandle<TaggedArray> array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(
                1024, JSTaggedValue::Hole(), MemSpaceType::SEMI_SPACE);
        }
    }
    // Continue allocate about 250M TaggedArray, now reach max heap size, must trigger gc to avoid OOM
    {
        [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
        for (int i = 0; i < 10 * 1000; i++) {
            [[maybe_unused]] JSHandle<TaggedArray> array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(
                1024, JSTaggedValue::Hole(), MemSpaceType::SEMI_SPACE);
        }
    }
    size_t commitSize = thread->GetEcmaVM()->GetHeap()->GetCommittedSize();
    const_cast<Heap *>(thread->GetEcmaVM()->GetHeap())->NotifyHighSensitive(false);
    EXPECT_TRUE(commitSize < thread->GetEcmaVM()->GetEcmaParamConfiguration().GetMaxHeapSize());
}

HWTEST_F_L0(GCTest, HighSensitiveExceedMaxHeapSizeSyncSweepVerify)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    heap->Prepare();
    heap->GetSweeper()->EnableConcurrentSweep(EnableConcurrentSweepType::DISABLE);
    heap->EnableHeapVerication(true);
    heap->NotifyHighSensitive(true);
    // First allocate about 250M TaggedArray, not reach max heap size
    {
        [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
        for (int i = 0; i < 16 * 1000; i++) {
            [[maybe_unused]] JSHandle<TaggedArray> array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(
                1024, JSTaggedValue::Hole(), MemSpaceType::SEMI_SPACE);
        }
    }
    // Continue allocate about 250M TaggedArray, now reach max heap size, must trigger gc to avoid OOM
    {
        [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
        for (int i = 0; i < 10 * 1000; i++) {
            [[maybe_unused]] JSHandle<TaggedArray> array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(
                1024, JSTaggedValue::Hole(), MemSpaceType::SEMI_SPACE);
        }
    }
    size_t commitSize = thread->GetEcmaVM()->GetHeap()->GetCommittedSize();
    const_cast<Heap *>(thread->GetEcmaVM()->GetHeap())->NotifyHighSensitive(false);
    EXPECT_TRUE(commitSize < thread->GetEcmaVM()->GetEcmaParamConfiguration().GetMaxHeapSize());
}

HWTEST_F_L0(GCTest, ColdStartNoConcurrentMark)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    heap->NotifyPostFork();
    heap->NotifyHighSensitive(true);
    {
        [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
        for (int i = 0; i < 500; i++) {
            [[maybe_unused]] JSHandle<TaggedArray> array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(
                10 * 1024, JSTaggedValue::Hole(), MemSpaceType::SEMI_SPACE);
        }
    }
    EXPECT_FALSE(heap->HandleExitHighSensitiveEvent());
    heap->NotifyHighSensitive(false);
    EXPECT_FALSE(heap->HandleExitHighSensitiveEvent());
    heap->FinishStartupEvent();

    heap->NotifyHighSensitive(true);
    heap->NotifyHighSensitive(false);
    heap->HandleExitHighSensitiveEvent();
    heap->NotifyHighSensitive(true);
    EXPECT_FALSE(heap->HandleExitHighSensitiveEvent());
}

HWTEST_F_L0(GCTest, ColdStartGCRestrainInternal)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    heap->NotifyPostFork();
    heap->NotifyFinishColdStartSoon();
    std::this_thread::sleep_for(std::chrono::seconds(3));
    if (!heap->OnStartupEvent()) {
        StartupStatus startupStatus = heap->GetStartupStatus();
        EXPECT_TRUE(startupStatus == StartupStatus::JUST_FINISH_STARTUP);
    }
}

HWTEST_F_L0(GCTest, ColdStartGCRestrainExternal)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    heap->NotifyPostFork();
    heap->NotifyFinishColdStartSoon();
    std::this_thread::sleep_for(std::chrono::seconds(1));
    heap->NotifyFinishColdStart(true);
    EXPECT_FALSE(heap->OnStartupEvent());
    StartupStatus startupStatus = heap->GetStartupStatus();
    EXPECT_TRUE(startupStatus == StartupStatus::JUST_FINISH_STARTUP);
}

HWTEST_F_L0(GCTest, ColdStartGCRestrainInGC)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    heap->NotifyPostFork();
    heap->NotifyFinishColdStartSoon();
    EXPECT_FALSE(heap->InGC());
    heap->SetGCState(true);
    std::this_thread::sleep_for(std::chrono::seconds(3));
    heap->SetGCState(false);
}

HWTEST_F_L0(GCTest, CallbackTask)
{
    auto vm = thread->GetEcmaVM();
    Heap *heap = const_cast<Heap *>(vm->GetHeap());
    auto factory = vm->GetFactory();
    {
        [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);

        for (int i = 0; i < 10; i++) {
            // NOLINTNEXTLINE(cppcoreguidelines-no-malloc)
            void *externalPointer = malloc(10);
            [[maybe_unused]] JSHandle<JSNativePointer> nativePointer = factory->NewJSNativePointer(
                externalPointer, []([[maybe_unused]] void *env, void* pointer, [[maybe_unused]] void* data) {
                if (pointer != nullptr) {
                    free(pointer);
                }
            },
            nullptr, false, 10, Concurrent::YES);
        }
    }
    size_t number = heap->concurrentNativePointerList_.size();
    EXPECT_TRUE(number > 0);
    heap->CollectGarbage(TriggerGCType::OLD_GC);
    size_t newNumber = heap->concurrentNativePointerList_.size();
    EXPECT_TRUE(number > newNumber);
}

HWTEST_F_L0(GCTest, RecomputeLimitsTest)
{
    if constexpr (G_USE_CMS_GC) {
        return;
    }
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    auto oldCapacity = heap->GetOldSpace()->GetInitialCapacity();
    heap->CollectGarbage(TriggerGCType::FULL_GC);
    EXPECT_FALSE(heap->IsConcurrentFullMark());
    EXPECT_FALSE(heap->IsFullMarkRequested());
    auto newCapacity = heap->GetOldSpace()->GetInitialCapacity();
    EXPECT_NE(newCapacity, oldCapacity);
    double gcSpeed = heap->GetMemController()->CalculateMarkCompactSpeedPerMS();
    double mutatorSpeed = heap->GetMemController()->GetCurrentOldSpaceAllocationThroughputPerMS();
    size_t oldSpaceSize = heap->GetOldSpace()->GetHeapObjectSize() + heap->GetHugeObjectSpace()->GetHeapObjectSize() +
        heap->GetHugeMachineCodeSpace()->GetHeapObjectSize();
    size_t newSpaceCapacity = heap->GetNewSpace()->GetInitialCapacity();
    double growingFactor =  heap->GetMemController()->CalculateGrowingFactor(gcSpeed, mutatorSpeed);
    size_t maxOldSpaceCapacity = heap->GetOldSpace()->GetMaximumCapacity() - newSpaceCapacity;
    auto newOldSpaceLimit = heap->GetMemController()->CalculateAllocLimit(oldSpaceSize, MIN_OLD_SPACE_LIMIT,
        maxOldSpaceCapacity, newSpaceCapacity, growingFactor);
    EXPECT_EQ(newCapacity, newOldSpaceLimit);
}

HWTEST_F_L0(GCTest, GlobalNativeSizeLargerThanLimitTest)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    auto ret = heap->GlobalNativeSizeLargerThanLimit();
    EXPECT_FALSE(ret);
    heap->GetNativeAreaAllocator()->IncreaseNativeMemoryUsage(300*1000*1000);
    ret = heap->GlobalNativeSizeLargerThanLimit();
    EXPECT_TRUE(ret);
}

#ifdef NDEBUG
HWTEST_F_L0(GCTest, IdleGCTriggerTest)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    auto idleGCTrigger = heap->GetIdleGCTrigger();
    auto sHeap = SharedHeap::GetInstance();
    heap->CollectGarbage(TriggerGCType::FULL_GC);
    int baseLocalGCCount = heap->GetEcmaGCStats()->GetGCCount();
    int baseSharedGCCount = sHeap->GetEcmaGCStats()->GetGCCount();
    heap->GetConcurrentMarker()->ConfigConcurrentMark(false);
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    // Apply for some memory that cannot be released to simulate the actual situation
    for (int i = 0; i < 5120; i++) {
        factory->NewTaggedArray(1024, JSTaggedValue::Hole(), MemSpaceType::OLD_SPACE);
        factory->NewSOldSpaceTaggedArray(1024, JSTaggedValue::Hole());
    }
    for (size_t i = 0; i < 10240; i++)
    {
        factory->NewTaggedArray(512, JSTaggedValue::Hole(), MemSpaceType::OLD_SPACE);
        factory->NewSOldSpaceTaggedArray(512, JSTaggedValue::Hole());
        [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
        [[maybe_unused]] JSHandle<TaggedArray> array = factory->NewTaggedArray(1024, JSTaggedValue::Hole(),
                    MemSpaceType::OLD_SPACE);
        [[maybe_unused]] JSHandle<TaggedArray> sArray = factory->NewSOldSpaceTaggedArray(1024,
                    JSTaggedValue::Hole());
        if (i%340 == 0) {
            idleGCTrigger->NotifyVsyncIdleStart();
        }
    }
    EXPECT_TRUE(idleGCTrigger->GetExpectedMemoryReclamationSize() >= 0);
    idleGCTrigger->TryTriggerIdleGC(TRIGGER_IDLE_GC_TYPE::FULL_GC);
    EXPECT_TRUE(idleGCTrigger->GetExpectedMemoryReclamationSize() >= 0);
    idleGCTrigger->TryTriggerIdleGC(TRIGGER_IDLE_GC_TYPE::SHARED_FULL_GC);
    int afterLocalGCCount = heap->GetEcmaGCStats()->GetGCCount();
    int afterSharedGCCount = sHeap->GetEcmaGCStats()->GetGCCount();
    EXPECT_TRUE(afterLocalGCCount - baseLocalGCCount < 10);
    EXPECT_TRUE(afterSharedGCCount - baseSharedGCCount < 10);
    heap->CollectGarbage(TriggerGCType::FULL_GC);
}
#endif  // #ifndef NDEBUG

HWTEST_F_L0(GCTest, AdjustCapacity)
{
#if defined(PANDA_TARGET_ARM64)
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    SemiSpace * space = heap->GetNewSpace();

    EXPECT_EQ(space->GetSurvivalObjectSize(), 0);
    {
        [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
        for (int i = 0; i < 300; i++) {
            [[maybe_unused]] JSHandle<TaggedArray> array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(
                10 * 1024, JSTaggedValue::Hole(), MemSpaceType::SEMI_SPACE);
        }
    }
    EXPECT_GT(space->GetSurvivalObjectSize(), 0);

    EXPECT_FALSE(space->AdjustCapacity(0, thread));
    size_t size = space->GetInitialCapacity() * GROW_OBJECT_SURVIVAL_RATE / 2;
    EXPECT_FALSE(space->AdjustCapacity(size, thread));

    space->SetInitialCapacity(space->GetSurvivalObjectSize() / GROW_OBJECT_SURVIVAL_RATE - 1);
    size = space->GetSurvivalObjectSize() / GROW_OBJECT_SURVIVAL_RATE - 1;
    size_t oldMaxCapacity = space->GetMaximumCapacity();
    space->SetMaximumCapacity(space->GetInitialCapacity());
    EXPECT_TRUE(space->AdjustCapacity(size, thread));
    space->SetMaximumCapacity(oldMaxCapacity);
    EXPECT_TRUE(space->AdjustCapacity(size, thread));
#endif
}

HWTEST_F_L0(GCTest, NativeMemAllocInSensitive)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    heap->NotifyHighSensitive(true);
    for (size_t i = 0; i < 20; i++) {
        [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
        factory->NewJSArrayBuffer(300 * 1024 * 1024); // 300MB
    }
    EXPECT_TRUE(heap->GetGlobalNativeSize() < 1 * 1024 * 1024* 1024); // 1GB
}

HWTEST_F_L0(GCTest, RecordAllocationForIdleTest001)
{
    SharedHeap *heap = SharedHeap::GetInstance();
    SharedMemController *controller = new SharedMemController(heap);
    controller->RecordAllocationForIdle();
    controller->RecordAllocationForIdle();
}

HWTEST_F_L0(GCTest, RecordAllocationForIdleTest002)
{
    SharedHeap *heap = SharedHeap::GetInstance();
    SharedMemController *controller = new SharedMemController(heap);
    controller->RecordAllocationForIdle();
    size_t before = heap->GetHeapObjectSize();
    heap->ReclaimForAppSpawn();
    size_t after = heap->GetHeapObjectSize();
    ASSERT_NE(before, after);
    controller->RecordAllocationForIdle();
}

HWTEST_F_L0(GCTest, PrintGCStatisticTest001)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    int prop = 1 << 15;
    heap->GetEcmaVM()->GetJSOptions().SetArkProperties(prop);
    ASSERT_EQ(heap->GetEcmaVM()->GetJSOptions().EnableGCTracer(), true);
    GCStats *stats = new GCStats(heap);
    stats->PrintGCStatistic();

    prop = 1 << 14;
    heap->GetEcmaVM()->GetJSOptions().SetArkProperties(prop);
    ASSERT_EQ(heap->GetEcmaVM()->GetJSOptions().EnableGCTracer(), false);
    stats->PrintGCStatistic();

    SharedHeap *sHeap = SharedHeap::GetInstance();
    SharedGCStats *stats1 = new SharedGCStats(sHeap, true);
    stats1->PrintGCStatistic();
}

HWTEST_F_L0(GCTest, GCReasonToStringTest001)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    GCStats *stats = new GCStats(heap);
    ASSERT_EQ(strcmp(stats->GCReasonToString(GCReason::SWITCH_BACKGROUND), "Switch to background"), 0);
    ASSERT_EQ(strcmp(stats->GCReasonToString(GCReason::EXTERNAL_TRIGGER), "Externally triggered"), 0);
    ASSERT_EQ(strcmp(stats->GCReasonToString(GCReason::WORKER_DESTRUCTION), "Worker Destruction"), 0);
    ASSERT_EQ(strcmp(stats->GCReasonToString(GCReason::TRIGGER_BY_ARKUI), "Trigger by ArkUI"), 0);
    ASSERT_EQ(strcmp(stats->GCReasonToString(GCReason::TRIGGER_BY_ABILITY), "Trigger by AbilityRuntime"), 0);
    ASSERT_EQ(strcmp(stats->GCReasonToString(GCReason::TRIGGER_BY_MEM_TOOLS), "Trigger by Mem tools"), 0);
    ASSERT_EQ(strcmp(stats->GCReasonToString(GCReason::TRIGGER_BY_TASKPOOL), "Trigger by taskPool"), 0);
    ASSERT_EQ(strcmp(stats->GCReasonToString(GCReason::NATIVE_LIMIT), "Native reach limit"), 0);
    ASSERT_EQ(strcmp(stats->GCReasonToString(GCReason::SHARED_LIMIT), "Shared reach limit"), 0);
    ASSERT_EQ(strcmp(stats->GCReasonToString(GCReason::IDLE_NATIVE), "Idle time task by native"), 0);
    ASSERT_EQ(strcmp(stats->GCReasonToString(GCReason::HANDLE_MARKING_FINISHED), "ConcurrentMark finished"), 0);

    ASSERT_EQ(strcmp(stats->MarkReasonToString(MarkReason::IDLE), "Idle time task"), 0);
    ASSERT_EQ(strcmp(stats->MarkReasonToString(MarkReason::EXIT_HIGH_SENSITIVE), "Exit high sensitive"), 0);
    ASSERT_EQ(strcmp(stats->MarkReasonToString(MarkReason::EXTERNAL_TRIGGER), "Externally triggered"), 0);
    ASSERT_EQ(strcmp(stats->MarkReasonToString(MarkReason::WORKER_DESTRUCTION), "Worker Destruction"), 0);
    ASSERT_EQ(strcmp(stats->MarkReasonToString(MarkReason::TRIGGER_BY_JS), "Trigger by JS"), 0);
    ASSERT_EQ(strcmp(stats->MarkReasonToString(MarkReason::HINT_GC), "Trigger by hint"), 0);
    ASSERT_EQ(strcmp(stats->MarkReasonToString(MarkReason::NATIVE_LIMIT), "Native reach limit"), 0);
    ASSERT_EQ(strcmp(stats->MarkReasonToString(MarkReason::SHARED_LIMIT), "Shared reach limit"), 0);
    ASSERT_EQ(strcmp(stats->MarkReasonToString(MarkReason::EXIT_SERIALIZE), "Exit serialize"), 0);
}

HWTEST_F_L0(GCTest, PrintGCMemoryStatisticTest002)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    heap->SetMarkType(MarkType::MARK_YOUNG);
    GCStats *stats = new GCStats(heap);
    stats->RecordStatisticBeforeGC(TriggerGCType::YOUNG_GC, GCReason::TRIGGER_BY_ARKUI);
    stats->PrintGCMemoryStatistic();
}

HWTEST_F_L0(GCTest, CheckIfNeedPrintTest001)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    heap->SetMarkType(MarkType::MARK_YOUNG);
    GCStats *stats = new GCStats(heap);
    stats->SetRecordData(RecordData::YOUNG_COUNT, 1);
    stats->PrintStatisticResult();
}

HWTEST_F_L0(GCTest, PrintGCSummaryStatisticTest001)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    heap->SetMarkType(MarkType::MARK_YOUNG);
    GCStats *stats = new GCStats(heap);
    stats->PrintStatisticResult();
}

HWTEST_F_L0(GCTest, CalculateGrowingFactorTest001)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    heap->SetMemGrowingType(MemGrowingType::CONSERVATIVE);
    auto controller = new MemController(heap);
    ASSERT_EQ(controller->CalculateGrowingFactor(1, 1), 2.0);
}

HWTEST_F_L0(GCTest, CalculateGrowingFactorTest002)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    heap->SetMemGrowingType(MemGrowingType::PRESSURE);
    auto controller = new MemController(heap);
    ASSERT_EQ(controller->CalculateGrowingFactor(1, 1), 1.1);
}

HWTEST_F_L0(GCTest, CalculateGrowingFactorTest003)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    heap->SetMemGrowingType(MemGrowingType::CONSERVATIVE);
    auto controller = new MemController(heap);
    ASSERT_EQ(controller->CalculateGrowingFactor(1, 1), 2.0);
}

HWTEST_F_L0(GCTest, StopCalculationAfterGCTest001)
{
    if constexpr (G_USE_CMS_GC) {
        return;
    }
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    auto controller = new MemController(heap);
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
    controller->StartCalculationBeforeGC();
    ASSERT_NE(controller->GetNewSpaceAllocSizeSinceGC(), 0);
    ASSERT_NE(controller->GetOldSpaceAllocSizeSinceGC(), 0);
    controller->StopCalculationAfterGC(TriggerGCType::YOUNG_GC);
    ASSERT_EQ(controller->GetNewSpaceAllocSizeSinceGC(), 0);
    ASSERT_EQ(controller->GetOldSpaceAllocSizeSinceGC(), 0);
}

HWTEST_F_L0(GCTest, RecordAllocationForIdleTest003)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    auto controller = new MemController(heap);
    controller->RecordAllocationForIdle();
}

HWTEST_F_L0(GCTest, WaitAllTasksFinishedTest001)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    heap->GetJSThread()->SetMarkStatus(MarkStatus::MARKING);
    heap->WaitAllTasksFinished();
}

HWTEST_F_L0(GCTest, WaitAllTasksFinishedTest002)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    heap->GetJSThread()->SetMarkStatus(MarkStatus::MARKING);
    heap->GetConcurrentMarker()->Mark();
    heap->WaitAllTasksFinished();
}

HWTEST_F_L0(GCTest, ChangeGCParamsTest002)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    heap->SetMemGrowingType(MemGrowingType::HIGH_THROUGHPUT);
    ASSERT_EQ(heap->GetMemGrowingType(), MemGrowingType::HIGH_THROUGHPUT);
}

HWTEST_F_L0(GCTest, NotifyWarmStartFalse001)
{
#ifndef PANDA_TARGET_32
    auto heap = const_cast<ecmascript::Heap *>(thread->GetEcmaVM()->GetHeap());
    EXPECT_TRUE(heap->AllowWarmStartGcRestrain());
    heap->TriggerConcurrentMarking();
    EXPECT_FALSE(heap->AllowWarmStartGcRestrain());
#endif
}

HWTEST_F_L0(GCTest, NotifyWarmStartFalse002)
{
    auto heap = const_cast<ecmascript::Heap *>(thread->GetEcmaVM()->GetHeap());
    EXPECT_TRUE(heap->AllowWarmStartGcRestrain());
    heap->NotifyPostFork();
    EXPECT_FALSE(heap->AllowWarmStartGcRestrain());
}

HWTEST_F_L0(GCTest, GetGCStatisticTypeTest001)
{
    EXPECT_STREQ(TestableGCStats::GetGCStatisticType(GCType::SHARED_GC), "Shared GC");
    EXPECT_STREQ(TestableGCStats::GetGCStatisticType(GCType::SHARED_PARTIAL_GC), "Shared GC");
    EXPECT_STREQ(TestableGCStats::GetGCStatisticType(GCType::SHARED_FULL_GC), "Shared GC");
    EXPECT_STREQ(TestableGCStats::GetGCStatisticType(GCType::PARTIAL_YOUNG_GC), "Local GC");
    EXPECT_STREQ(TestableGCStats::GetGCStatisticType(GCType::PARTIAL_OLD_GC), "Local GC");
    EXPECT_STREQ(TestableGCStats::GetGCStatisticType(GCType::LOCAL_CC), "Local GC");
    EXPECT_STREQ(TestableGCStats::GetGCStatisticType(GCType::COMPRESS_GC), "Local GC");
    EXPECT_STREQ(TestableGCStats::GetGCStatisticType(GCType::CMS_GC), "Local GC");
    EXPECT_STREQ(TestableGCStats::GetGCStatisticType(GCType::OTHER), "UnknownType");
}

HWTEST_F_L0(GCTest, GetGCStatisticDataTest001)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    TestableGCStats stats(heap);
    stats.SetRecordData(RecordData::YOUNG_COUNT, 2);
    stats.SetRecordDuration(RecordDuration::YOUNG_MIN_PAUSE, 3.5f);
    stats.SetRecordDuration(RecordDuration::YOUNG_MAX_PAUSE, 8.5f);
    stats.SetRecordDuration(RecordDuration::YOUNG_TOTAL_PAUSE, 12.0f);
    stats.SetRecordData(RecordData::OLD_COUNT, 1);
    stats.SetRecordDuration(RecordDuration::OLD_MIN_PAUSE, 5.0f);
    stats.SetRecordDuration(RecordDuration::OLD_MAX_PAUSE, 9.0f);
    stats.SetRecordDuration(RecordDuration::OLD_TOTAL_PAUSE, 5.0f);
    stats.SetRecordData(RecordData::LOCAL_CC_COUNT, 1);
    stats.SetRecordDuration(RecordDuration::LOCAL_CC_MIN_PAUSE, 2.0f);
    stats.SetRecordDuration(RecordDuration::LOCAL_CC_MAX_PAUSE, 4.0f);
    stats.SetRecordDuration(RecordDuration::LOCAL_CC_TOTAL_PAUSE, 2.0f);
    stats.SetGCTypeForTest(GCType::PARTIAL_YOUNG_GC);
    stats.RecordGCStatisticStart();
    stats.RecordGCStatisticEnd();

    GCStatisticData gcStatistic = stats.GetGCStatistic();
    EXPECT_EQ(gcStatistic.count, 4U);
    EXPECT_FLOAT_EQ(gcStatistic.maxPause, 9.0f);
    EXPECT_FLOAT_EQ(gcStatistic.minPause, 2.0f);
    EXPECT_FLOAT_EQ(gcStatistic.totalPause, 19.0f);
    EXPECT_NE(gcStatistic.lastStartTime, 0U);
    EXPECT_NE(gcStatistic.lastEndTime, 0U);
    EXPECT_STREQ(gcStatistic.lastType, "Local GC");
}

HWTEST_F_L0(GCTest, GetGCStatisticTypeTest002)
{
    EXPECT_STREQ(TestableGCStats::GetGCStatisticType(GCType::SHARED_GC), "Shared GC");
    EXPECT_STREQ(TestableGCStats::GetGCStatisticType(GCType::SHARED_PARTIAL_GC), "Shared GC");
    EXPECT_STREQ(TestableGCStats::GetGCStatisticType(GCType::SHARED_FULL_GC), "Shared GC");
    EXPECT_STREQ(TestableGCStats::GetGCStatisticType(GCType::PARTIAL_YOUNG_GC), "Local GC");
    EXPECT_STREQ(TestableGCStats::GetGCStatisticType(GCType::PARTIAL_OLD_GC), "Local GC");
    EXPECT_STREQ(TestableGCStats::GetGCStatisticType(GCType::LOCAL_CC), "Local GC");
    EXPECT_STREQ(TestableGCStats::GetGCStatisticType(GCType::COMPRESS_GC), "Local GC");
    EXPECT_STREQ(TestableGCStats::GetGCStatisticType(GCType::CMS_GC), "Local GC");
    EXPECT_STREQ(TestableGCStats::GetGCStatisticType(GCType::GLOBAL_GC), "Shared GC");
    EXPECT_STREQ(TestableGCStats::GetGCStatisticType(GCType::OTHER), "UnknownType");
}

HWTEST_F_L0(GCTest, GetGCStatisticDataTest002)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    TestableGCStats stats(heap);
    stats.SetGCTypeForTest(GCType::OTHER);
    stats.RecordGCStatisticStart();
    stats.RecordGCStatisticEnd();

    GCStatisticData gcStatistic = stats.GetGCStatistic();
    EXPECT_EQ(gcStatistic.count, 0U);
    EXPECT_FLOAT_EQ(gcStatistic.maxPause, 0.0f);
    EXPECT_FLOAT_EQ(gcStatistic.minPause, 0.0f);
    EXPECT_FLOAT_EQ(gcStatistic.totalPause, 0.0f);
    EXPECT_NE(gcStatistic.lastStartTime, 0U);
    EXPECT_NE(gcStatistic.lastEndTime, 0U);
    EXPECT_STREQ(gcStatistic.lastType, "UnknownType");
}

HWTEST_F_L0(GCTest, GetGCStatisticDataTest003)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    TestableGCStats stats(heap);
    stats.SetRecordData(RecordData::YOUNG_COUNT, 5);
    stats.SetRecordDuration(RecordDuration::YOUNG_MIN_PAUSE, 1.5f);
    stats.SetRecordDuration(RecordDuration::YOUNG_MAX_PAUSE, 9.5f);
    stats.SetRecordDuration(RecordDuration::YOUNG_TOTAL_PAUSE, 20.0f);
    stats.SetGCTypeForTest(GCType::PARTIAL_YOUNG_GC);
    stats.RecordGCStatisticStart();
    stats.RecordGCStatisticEnd();

    GCStatisticData gcStatistic = stats.GetGCStatistic();
    EXPECT_EQ(gcStatistic.count, 5U);
    EXPECT_FLOAT_EQ(gcStatistic.maxPause, 9.5f);
    EXPECT_FLOAT_EQ(gcStatistic.minPause, 1.5f);
    EXPECT_FLOAT_EQ(gcStatistic.totalPause, 20.0f);
    EXPECT_STREQ(gcStatistic.lastType, "Local GC");
}

HWTEST_F_L0(GCTest, GetGCStatisticDataTest004)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    TestableGCStats stats(heap);
    stats.SetRecordData(RecordData::OLD_COUNT, 3);
    stats.SetRecordDuration(RecordDuration::OLD_MIN_PAUSE, 4.0f);
    stats.SetRecordDuration(RecordDuration::OLD_MAX_PAUSE, 12.0f);
    stats.SetRecordDuration(RecordDuration::OLD_TOTAL_PAUSE, 18.5f);
    stats.SetGCTypeForTest(GCType::PARTIAL_OLD_GC);
    stats.RecordGCStatisticStart();
    stats.RecordGCStatisticEnd();

    GCStatisticData gcStatistic = stats.GetGCStatistic();
    EXPECT_EQ(gcStatistic.count, 3U);
    EXPECT_FLOAT_EQ(gcStatistic.maxPause, 12.0f);
    EXPECT_FLOAT_EQ(gcStatistic.minPause, 4.0f);
    EXPECT_FLOAT_EQ(gcStatistic.totalPause, 18.5f);
    EXPECT_STREQ(gcStatistic.lastType, "Local GC");
}

HWTEST_F_L0(GCTest, GetGCStatisticDataTest005)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    TestableGCStats stats(heap);
    stats.SetRecordData(RecordData::COMPRESS_COUNT, 2);
    stats.SetRecordDuration(RecordDuration::COMPRESS_MIN_PAUSE, 6.0f);
    stats.SetRecordDuration(RecordDuration::COMPRESS_MAX_PAUSE, 14.0f);
    stats.SetRecordDuration(RecordDuration::COMPRESS_TOTAL_PAUSE, 21.0f);
    stats.SetGCTypeForTest(GCType::COMPRESS_GC);
    stats.RecordGCStatisticStart();
    stats.RecordGCStatisticEnd();

    GCStatisticData gcStatistic = stats.GetGCStatistic();
    EXPECT_EQ(gcStatistic.count, 2U);
    EXPECT_FLOAT_EQ(gcStatistic.maxPause, 14.0f);
    EXPECT_FLOAT_EQ(gcStatistic.minPause, 6.0f);
    EXPECT_FLOAT_EQ(gcStatistic.totalPause, 21.0f);
    EXPECT_STREQ(gcStatistic.lastType, "Local GC");
}

HWTEST_F_L0(GCTest, GetGCStatisticDataTest006)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    TestableGCStats stats(heap);
    stats.SetRecordData(RecordData::LOCAL_CC_COUNT, 4);
    stats.SetRecordDuration(RecordDuration::LOCAL_CC_MIN_PAUSE, 2.5f);
    stats.SetRecordDuration(RecordDuration::LOCAL_CC_MAX_PAUSE, 6.5f);
    stats.SetRecordDuration(RecordDuration::LOCAL_CC_TOTAL_PAUSE, 16.0f);
    stats.SetGCTypeForTest(GCType::LOCAL_CC);
    stats.RecordGCStatisticStart();
    stats.RecordGCStatisticEnd();

    GCStatisticData gcStatistic = stats.GetGCStatistic();
    EXPECT_EQ(gcStatistic.count, 4U);
    EXPECT_FLOAT_EQ(gcStatistic.maxPause, 6.5f);
    EXPECT_FLOAT_EQ(gcStatistic.minPause, 2.5f);
    EXPECT_FLOAT_EQ(gcStatistic.totalPause, 16.0f);
    EXPECT_STREQ(gcStatistic.lastType, "Local GC");
}

HWTEST_F_L0(GCTest, GetGCStatisticDataTest007)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    TestableGCStats stats(heap);
    stats.SetRecordData(RecordData::SHARED_COUNT, 7);
    stats.SetRecordDuration(RecordDuration::SHARED_MIN_PAUSE, 3.0f);
    stats.SetRecordDuration(RecordDuration::SHARED_MAX_PAUSE, 10.0f);
    stats.SetRecordDuration(RecordDuration::SHARED_TOTAL_PAUSE, 35.0f);
    stats.SetGCTypeForTest(GCType::SHARED_GC);
    stats.RecordGCStatisticStart();
    stats.RecordGCStatisticEnd();

    GCStatisticData gcStatistic = stats.GetGCStatistic();
    EXPECT_EQ(gcStatistic.count, 7U);
    EXPECT_FLOAT_EQ(gcStatistic.maxPause, 10.0f);
    EXPECT_FLOAT_EQ(gcStatistic.minPause, 3.0f);
    EXPECT_FLOAT_EQ(gcStatistic.totalPause, 35.0f);
    EXPECT_STREQ(gcStatistic.lastType, "Shared GC");
}

HWTEST_F_L0(GCTest, GetGCStatisticDataTest008)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    TestableGCStats stats(heap);
    stats.SetRecordData(RecordData::SWEEP_COUNT, 8);
    stats.SetRecordDuration(RecordDuration::SWEEP_MIN_PAUSE, 4.5f);
    stats.SetRecordDuration(RecordDuration::SWEEP_MAX_PAUSE, 11.5f);
    stats.SetRecordDuration(RecordDuration::SWEEP_TOTAL_PAUSE, 40.0f);
    stats.SetGCTypeForTest(GCType::CMS_GC);
    stats.RecordGCStatisticStart();
    stats.RecordGCStatisticEnd();

    GCStatisticData gcStatistic = stats.GetGCStatistic();
    EXPECT_EQ(gcStatistic.count, 8U);
    EXPECT_FLOAT_EQ(gcStatistic.maxPause, 11.5f);
    EXPECT_FLOAT_EQ(gcStatistic.minPause, 4.5f);
    EXPECT_FLOAT_EQ(gcStatistic.totalPause, 40.0f);
    EXPECT_STREQ(gcStatistic.lastType, "Local GC");
}

HWTEST_F_L0(GCTest, GetGCStatisticDataTest009)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    TestableGCStats stats(heap);
    stats.SetRecordData(RecordData::YOUNG_COUNT, 1);
    stats.SetRecordDuration(RecordDuration::YOUNG_MIN_PAUSE, 8.0f);
    stats.SetRecordDuration(RecordDuration::YOUNG_MAX_PAUSE, 11.0f);
    stats.SetRecordDuration(RecordDuration::YOUNG_TOTAL_PAUSE, 11.0f);
    stats.SetRecordData(RecordData::OLD_COUNT, 2);
    stats.SetRecordDuration(RecordDuration::OLD_MIN_PAUSE, 7.0f);
    stats.SetRecordDuration(RecordDuration::OLD_MAX_PAUSE, 12.0f);
    stats.SetRecordDuration(RecordDuration::OLD_TOTAL_PAUSE, 19.0f);
    stats.SetRecordData(RecordData::COMPRESS_COUNT, 3);
    stats.SetRecordDuration(RecordDuration::COMPRESS_MIN_PAUSE, 6.0f);
    stats.SetRecordDuration(RecordDuration::COMPRESS_MAX_PAUSE, 13.0f);
    stats.SetRecordDuration(RecordDuration::COMPRESS_TOTAL_PAUSE, 24.0f);
    stats.SetRecordData(RecordData::LOCAL_CC_COUNT, 4);
    stats.SetRecordDuration(RecordDuration::LOCAL_CC_MIN_PAUSE, 5.0f);
    stats.SetRecordDuration(RecordDuration::LOCAL_CC_MAX_PAUSE, 14.0f);
    stats.SetRecordDuration(RecordDuration::LOCAL_CC_TOTAL_PAUSE, 28.0f);
    stats.SetRecordData(RecordData::SHARED_COUNT, 5);
    stats.SetRecordDuration(RecordDuration::SHARED_MIN_PAUSE, 4.0f);
    stats.SetRecordDuration(RecordDuration::SHARED_MAX_PAUSE, 15.0f);
    stats.SetRecordDuration(RecordDuration::SHARED_TOTAL_PAUSE, 33.0f);
    stats.SetRecordData(RecordData::SWEEP_COUNT, 6);
    stats.SetRecordDuration(RecordDuration::SWEEP_MIN_PAUSE, 3.0f);
    stats.SetRecordDuration(RecordDuration::SWEEP_MAX_PAUSE, 16.0f);
    stats.SetRecordDuration(RecordDuration::SWEEP_TOTAL_PAUSE, 39.0f);
    stats.SetGCTypeForTest(GCType::SHARED_FULL_GC);
    stats.RecordGCStatisticStart();
    stats.RecordGCStatisticEnd();

    GCStatisticData gcStatistic = stats.GetGCStatistic();
    EXPECT_EQ(gcStatistic.count, 21U);
    EXPECT_FLOAT_EQ(gcStatistic.maxPause, 16.0f);
    EXPECT_FLOAT_EQ(gcStatistic.minPause, 3.0f);
    EXPECT_FLOAT_EQ(gcStatistic.totalPause, 154.0f);
    EXPECT_STREQ(gcStatistic.lastType, "Shared GC");
}

HWTEST_F_L0(GCTest, GetGCStatisticDataTest010)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    TestableGCStats stats(heap);
    stats.SetRecordData(RecordData::YOUNG_COUNT, 0);
    stats.SetRecordDuration(RecordDuration::YOUNG_MIN_PAUSE, 1.0f);
    stats.SetRecordDuration(RecordDuration::YOUNG_MAX_PAUSE, 99.0f);
    stats.SetRecordDuration(RecordDuration::YOUNG_TOTAL_PAUSE, 100.0f);
    stats.SetRecordData(RecordData::OLD_COUNT, 2);
    stats.SetRecordDuration(RecordDuration::OLD_MIN_PAUSE, 4.0f);
    stats.SetRecordDuration(RecordDuration::OLD_MAX_PAUSE, 7.0f);
    stats.SetRecordDuration(RecordDuration::OLD_TOTAL_PAUSE, 10.0f);
    stats.SetGCTypeForTest(GCType::PARTIAL_OLD_GC);
    stats.RecordGCStatisticStart();
    stats.RecordGCStatisticEnd();

    GCStatisticData gcStatistic = stats.GetGCStatistic();
    EXPECT_EQ(gcStatistic.count, 2U);
    EXPECT_FLOAT_EQ(gcStatistic.maxPause, 7.0f);
    EXPECT_FLOAT_EQ(gcStatistic.minPause, 4.0f);
    EXPECT_FLOAT_EQ(gcStatistic.totalPause, 10.0f);
    EXPECT_STREQ(gcStatistic.lastType, "Local GC");
}

HWTEST_F_L0(GCTest, GetGCStatisticDataTest011)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    TestableGCStats stats(heap);
    stats.SetGCTypeForTest(GCType::PARTIAL_YOUNG_GC);
    stats.RecordGCStatisticStart();
    stats.RecordGCStatisticEnd();

    GCStatisticData gcStatistic = stats.GetGCStatistic();
    EXPECT_EQ(gcStatistic.count, 0U);
    EXPECT_NE(gcStatistic.lastStartTime, 0U);
    EXPECT_NE(gcStatistic.lastEndTime, 0U);
    EXPECT_LE(gcStatistic.lastStartTime, gcStatistic.lastEndTime);
    EXPECT_STREQ(gcStatistic.lastType, "Local GC");
}

HWTEST_F_L0(GCTest, GetGCStatisticDataTest012)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    TestableGCStats stats(heap);
    stats.SetGCTypeForTest(GCType::SHARED_PARTIAL_GC);
    stats.RecordGCStatisticStart();
    stats.RecordGCStatisticEnd();

    GCStatisticData gcStatistic = stats.GetGCStatistic();
    EXPECT_EQ(gcStatistic.count, 0U);
    EXPECT_NE(gcStatistic.lastStartTime, 0U);
    EXPECT_NE(gcStatistic.lastEndTime, 0U);
    EXPECT_LE(gcStatistic.lastStartTime, gcStatistic.lastEndTime);
    EXPECT_STREQ(gcStatistic.lastType, "Shared GC");
}

HWTEST_F_L0(GCTest, GetGCStatisticDataTest013)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    TestableGCStats stats(heap);
    stats.SetGCTypeForTest(GCType::GLOBAL_GC);
    stats.RecordGCStatisticStart();
    stats.RecordGCStatisticEnd();

    GCStatisticData gcStatistic = stats.GetGCStatistic();
    EXPECT_EQ(gcStatistic.count, 0U);
    EXPECT_NE(gcStatistic.lastStartTime, 0U);
    EXPECT_NE(gcStatistic.lastEndTime, 0U);
    EXPECT_LE(gcStatistic.lastStartTime, gcStatistic.lastEndTime);
    EXPECT_STREQ(gcStatistic.lastType, "Shared GC");
}

HWTEST_F_L0(GCTest, MergeGCStatisticTest001)
{
    GCStatisticData localStats;
    localStats.count = 0;
    localStats.maxPause = 1.0f;
    localStats.minPause = 0.0f;
    localStats.totalPause = 1.0f;
    localStats.lastStartTime = 10;
    localStats.lastEndTime = 20;
    localStats.lastType = "Local GC";

    GCStatisticData sharedStats;
    sharedStats.count = 2;
    sharedStats.maxPause = 5.0f;
    sharedStats.minPause = 2.0f;
    sharedStats.totalPause = 7.0f;
    sharedStats.lastStartTime = 30;
    sharedStats.lastEndTime = 40;
    sharedStats.lastType = "Shared GC";

    GCStatisticData merged = TestableGCStats::MergeGCStatistic(localStats, sharedStats);
    EXPECT_EQ(merged.count, 2U);
    EXPECT_FLOAT_EQ(merged.maxPause, 5.0f);
    EXPECT_FLOAT_EQ(merged.minPause, 2.0f);
    EXPECT_FLOAT_EQ(merged.totalPause, 8.0f);
    EXPECT_EQ(merged.lastStartTime, 30U);
    EXPECT_EQ(merged.lastEndTime, 40U);
    EXPECT_STREQ(merged.lastType, "Shared GC");
}

HWTEST_F_L0(GCTest, MergeGCStatisticTest002)
{
    GCStatisticData localStats;
    localStats.count = 3;
    localStats.maxPause = 6.0f;
    localStats.minPause = 1.5f;
    localStats.totalPause = 9.0f;
    localStats.lastStartTime = 50;
    localStats.lastEndTime = 80;
    localStats.lastType = "Local GC";

    GCStatisticData sharedStats;
    sharedStats.count = 0;
    sharedStats.maxPause = 4.0f;
    sharedStats.minPause = 0.0f;
    sharedStats.totalPause = 2.0f;
    sharedStats.lastStartTime = 40;
    sharedStats.lastEndTime = 70;
    sharedStats.lastType = "Shared GC";

    GCStatisticData merged = TestableGCStats::MergeGCStatistic(localStats, sharedStats);
    EXPECT_EQ(merged.count, 3U);
    EXPECT_FLOAT_EQ(merged.maxPause, 6.0f);
    EXPECT_FLOAT_EQ(merged.minPause, 1.5f);
    EXPECT_FLOAT_EQ(merged.totalPause, 11.0f);
    EXPECT_EQ(merged.lastStartTime, 50U);
    EXPECT_EQ(merged.lastEndTime, 80U);
    EXPECT_STREQ(merged.lastType, "Local GC");
}

HWTEST_F_L0(GCTest, MergeGCStatisticTest003)
{
    GCStatisticData localStats;
    localStats.count = 2;
    localStats.maxPause = 3.0f;
    localStats.minPause = 2.5f;
    localStats.totalPause = 5.0f;
    localStats.lastStartTime = 100;
    localStats.lastEndTime = 120;
    localStats.lastType = "Local GC";

    GCStatisticData sharedStats;
    sharedStats.count = 4;
    sharedStats.maxPause = 7.0f;
    sharedStats.minPause = 1.0f;
    sharedStats.totalPause = 8.0f;
    sharedStats.lastStartTime = 90;
    sharedStats.lastEndTime = 110;
    sharedStats.lastType = "Shared GC";

    GCStatisticData merged = TestableGCStats::MergeGCStatistic(localStats, sharedStats);
    EXPECT_EQ(merged.count, 6U);
    EXPECT_FLOAT_EQ(merged.maxPause, 7.0f);
    EXPECT_FLOAT_EQ(merged.minPause, 1.0f);
    EXPECT_FLOAT_EQ(merged.totalPause, 13.0f);
    EXPECT_EQ(merged.lastStartTime, 100U);
    EXPECT_EQ(merged.lastEndTime, 120U);
    EXPECT_STREQ(merged.lastType, "Local GC");

    sharedStats.lastStartTime = 130;
    sharedStats.lastEndTime = 140;
    merged = TestableGCStats::MergeGCStatistic(localStats, sharedStats);
    EXPECT_EQ(merged.lastStartTime, 130U);
    EXPECT_EQ(merged.lastEndTime, 140U);
    EXPECT_STREQ(merged.lastType, "Shared GC");
}

HWTEST_F_L0(GCTest, MergeGCStatisticTest004)
{
    GCStatisticData localStats;
    localStats.count = 0;
    localStats.maxPause = 2.0f;
    localStats.minPause = 0.0f;
    localStats.totalPause = 1.0f;
    localStats.lastStartTime = 100;
    localStats.lastEndTime = 120;
    localStats.lastType = "Local GC";

    GCStatisticData sharedStats;
    sharedStats.count = 0;
    sharedStats.maxPause = 5.0f;
    sharedStats.minPause = 0.0f;
    sharedStats.totalPause = 3.0f;
    sharedStats.lastStartTime = 50;
    sharedStats.lastEndTime = 150;
    sharedStats.lastType = "Shared GC";

    GCStatisticData merged = TestableGCStats::MergeGCStatistic(localStats, sharedStats);
    EXPECT_EQ(merged.count, 0U);
    EXPECT_FLOAT_EQ(merged.maxPause, 5.0f);
    EXPECT_FLOAT_EQ(merged.minPause, 0.0f);
    EXPECT_FLOAT_EQ(merged.totalPause, 4.0f);
    EXPECT_EQ(merged.lastStartTime, 100U);
    EXPECT_EQ(merged.lastEndTime, 150U);
    EXPECT_STREQ(merged.lastType, "Local GC");
}

HWTEST_F_L0(GCTest, MergeGCStatisticTest005)
{
    GCStatisticData localStats;
    localStats.count = 0;
    localStats.maxPause = 1.0f;
    localStats.minPause = 0.0f;
    localStats.totalPause = 2.0f;
    localStats.lastStartTime = 10;
    localStats.lastEndTime = 80;
    localStats.lastType = "Local GC";

    GCStatisticData sharedStats;
    sharedStats.count = 0;
    sharedStats.maxPause = 2.0f;
    sharedStats.minPause = 0.0f;
    sharedStats.totalPause = 5.0f;
    sharedStats.lastStartTime = 90;
    sharedStats.lastEndTime = 70;
    sharedStats.lastType = "Shared GC";

    GCStatisticData merged = TestableGCStats::MergeGCStatistic(localStats, sharedStats);
    EXPECT_EQ(merged.count, 0U);
    EXPECT_FLOAT_EQ(merged.maxPause, 2.0f);
    EXPECT_FLOAT_EQ(merged.minPause, 0.0f);
    EXPECT_FLOAT_EQ(merged.totalPause, 7.0f);
    EXPECT_EQ(merged.lastStartTime, 90U);
    EXPECT_EQ(merged.lastEndTime, 80U);
    EXPECT_STREQ(merged.lastType, "Shared GC");
}

HWTEST_F_L0(GCTest, MergeGCStatisticTest006)
{
    GCStatisticData localStats;
    localStats.count = 4;
    localStats.maxPause = 6.0f;
    localStats.minPause = 1.0f;
    localStats.totalPause = 11.0f;
    localStats.lastStartTime = 200;
    localStats.lastEndTime = 210;
    localStats.lastType = "Local GC";

    GCStatisticData sharedStats;
    sharedStats.count = 5;
    sharedStats.maxPause = 9.0f;
    sharedStats.minPause = 0.5f;
    sharedStats.totalPause = 15.0f;
    sharedStats.lastStartTime = 200;
    sharedStats.lastEndTime = 260;
    sharedStats.lastType = "Shared GC";

    GCStatisticData merged = TestableGCStats::MergeGCStatistic(localStats, sharedStats);
    EXPECT_EQ(merged.count, 9U);
    EXPECT_FLOAT_EQ(merged.maxPause, 9.0f);
    EXPECT_FLOAT_EQ(merged.minPause, 0.5f);
    EXPECT_FLOAT_EQ(merged.totalPause, 26.0f);
    EXPECT_EQ(merged.lastStartTime, 200U);
    EXPECT_EQ(merged.lastEndTime, 260U);
    EXPECT_STREQ(merged.lastType, "Local GC");
}

HWTEST_F_L0(GCTest, MergeGCStatisticTest007)
{
    GCStatisticData localStats;
    localStats.count = 2;
    localStats.maxPause = 10.0f;
    localStats.minPause = 3.0f;
    localStats.totalPause = 14.0f;
    localStats.lastStartTime = 400;
    localStats.lastEndTime = 405;
    localStats.lastType = "Local GC";

    GCStatisticData sharedStats;
    sharedStats.count = 6;
    sharedStats.maxPause = 12.0f;
    sharedStats.minPause = 2.0f;
    sharedStats.totalPause = 28.0f;
    sharedStats.lastStartTime = 401;
    sharedStats.lastEndTime = 403;
    sharedStats.lastType = "Shared GC";

    GCStatisticData merged = TestableGCStats::MergeGCStatistic(localStats, sharedStats);
    EXPECT_EQ(merged.count, 8U);
    EXPECT_FLOAT_EQ(merged.maxPause, 12.0f);
    EXPECT_FLOAT_EQ(merged.minPause, 2.0f);
    EXPECT_FLOAT_EQ(merged.totalPause, 42.0f);
    EXPECT_EQ(merged.lastStartTime, 401U);
    EXPECT_EQ(merged.lastEndTime, 405U);
    EXPECT_STREQ(merged.lastType, "Shared GC");
}

HWTEST_F_L0(GCTest, MergeGCStatisticTest008)
{
    GCStatisticData localStats;
    localStats.count = 1;
    localStats.maxPause = 0.0f;
    localStats.minPause = 0.0f;
    localStats.totalPause = 0.0f;
    localStats.lastStartTime = 1;
    localStats.lastEndTime = 2;
    localStats.lastType = "Local GC";

    GCStatisticData sharedStats;
    sharedStats.count = 3;
    sharedStats.maxPause = 4.5f;
    sharedStats.minPause = 1.5f;
    sharedStats.totalPause = 6.0f;
    sharedStats.lastStartTime = 3;
    sharedStats.lastEndTime = 4;
    sharedStats.lastType = "Shared GC";

    GCStatisticData merged = TestableGCStats::MergeGCStatistic(localStats, sharedStats);
    EXPECT_EQ(merged.count, 4U);
    EXPECT_FLOAT_EQ(merged.maxPause, 4.5f);
    EXPECT_FLOAT_EQ(merged.minPause, 0.0f);
    EXPECT_FLOAT_EQ(merged.totalPause, 6.0f);
    EXPECT_EQ(merged.lastStartTime, 3U);
    EXPECT_EQ(merged.lastEndTime, 4U);
    EXPECT_STREQ(merged.lastType, "Shared GC");
}

HWTEST_F_L0(GCTest, IsLongGCTest001)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    TestableGCStats stats(heap);

    EXPECT_FALSE(stats.IsLongGC(GCReason::IDLE, false, true,
        static_cast<float>(GCKeyStats::GC_BACKGROUD_IDLE_LONG_TIME)));
    EXPECT_TRUE(stats.IsLongGC(GCReason::IDLE, false, true,
        static_cast<float>(GCKeyStats::GC_BACKGROUD_IDLE_LONG_TIME) + 0.1f));

    EXPECT_FALSE(stats.IsLongGC(GCReason::IDLE, false, false, static_cast<float>(GCKeyStats::GC_IDLE_LONG_TIME)));
    EXPECT_TRUE(stats.IsLongGC(GCReason::IDLE, false, false,
        static_cast<float>(GCKeyStats::GC_IDLE_LONG_TIME) + 0.1f));

    EXPECT_FALSE(stats.IsLongGC(GCReason::OTHER, false, false,
        static_cast<float>(GCKeyStats::GC_NOT_SENSITIVE_LONG_TIME)));
    EXPECT_TRUE(stats.IsLongGC(GCReason::OTHER, false, false,
        static_cast<float>(GCKeyStats::GC_NOT_SENSITIVE_LONG_TIME) + 0.1f));

    EXPECT_FALSE(stats.IsLongGC(GCReason::OTHER, true, false,
        static_cast<float>(GCKeyStats::GC_SENSITIVE_LONG_TIME)));
    EXPECT_TRUE(stats.IsLongGC(GCReason::OTHER, true, false,
        static_cast<float>(GCKeyStats::GC_SENSITIVE_LONG_TIME) + 0.1f));
}

HWTEST_F_L0(GCTest, IsLongGCTest002)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    TestableGCStats stats(heap);

    EXPECT_FALSE(stats.IsLongGC(GCReason::OTHER, false, true,
        static_cast<float>(GCKeyStats::GC_BACKGROUD_LONG_TIME)));
    EXPECT_TRUE(stats.IsLongGC(GCReason::OTHER, false, true,
        static_cast<float>(GCKeyStats::GC_BACKGROUD_LONG_TIME) + 0.1f));

    EXPECT_FALSE(stats.IsLongGC(GCReason::TRIGGER_BY_JS, false, true,
        static_cast<float>(GCKeyStats::GC_BACKGROUD_LONG_TIME)));
    EXPECT_TRUE(stats.IsLongGC(GCReason::TRIGGER_BY_JS, false, true,
        static_cast<float>(GCKeyStats::GC_BACKGROUD_LONG_TIME) + 0.1f));
}

HWTEST_F_L0(GCTest, IsLongGCTest003)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    TestableGCStats stats(heap);

    EXPECT_FALSE(stats.IsLongGC(GCReason::IDLE, true, false,
        static_cast<float>(GCKeyStats::GC_SENSITIVE_LONG_TIME)));
    EXPECT_TRUE(stats.IsLongGC(GCReason::IDLE, true, false,
        static_cast<float>(GCKeyStats::GC_SENSITIVE_LONG_TIME) + 0.1f));

    EXPECT_FALSE(stats.IsLongGC(GCReason::IDLE, true, true,
        static_cast<float>(GCKeyStats::GC_SENSITIVE_LONG_TIME)));
    EXPECT_TRUE(stats.IsLongGC(GCReason::IDLE, true, true,
        static_cast<float>(GCKeyStats::GC_SENSITIVE_LONG_TIME) + 0.1f));
}

HWTEST_F_L0(GCTest, IsLongGCTest004)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    TestableGCStats stats(heap);

    EXPECT_FALSE(stats.IsLongGC(GCReason::IDLE_NATIVE, false, false,
        static_cast<float>(GCKeyStats::GC_IDLE_LONG_TIME)));
    EXPECT_TRUE(stats.IsLongGC(GCReason::IDLE_NATIVE, false, false,
        static_cast<float>(GCKeyStats::GC_IDLE_LONG_TIME) + 0.1f));

    EXPECT_FALSE(stats.IsLongGC(GCReason::IDLE_NATIVE, false, true,
        static_cast<float>(GCKeyStats::GC_BACKGROUD_IDLE_LONG_TIME)));
    EXPECT_TRUE(stats.IsLongGC(GCReason::IDLE_NATIVE, false, true,
        static_cast<float>(GCKeyStats::GC_BACKGROUD_IDLE_LONG_TIME) + 0.1f));
}
} // namespace panda::test