/*
 * 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 "assembler/assembly-emitter.h"
#include "assembler/assembly-parser.h"

#include "ecmascript/builtins/builtins_ark_tools.h"
#include "ecmascript/containers/containers_bitvector.h"
#include "ecmascript/ecma_vm.h"
#include "ecmascript/js_api/js_api_bitvector.h"
#include "ecmascript/jspandafile/js_pandafile.h"
#include "ecmascript/jspandafile/js_pandafile_manager.h"
#include "ecmascript/jspandafile/program_object.h"
#include "ecmascript/mem/full_gc.h"
#include "ecmascript/object_factory-inl.h"
#include "ecmascript/runtime_lock.h"
#include "ecmascript/mem/concurrent_marker.h"
#include "ecmascript/mem/partial_gc.h"
#include "ecmascript/mem/sparse_space.h"
#include "ecmascript/mem/mem_controller.h"
#include "ecmascript/mem/shared_heap/shared_concurrent_marker.h"
#include "ecmascript/mem/shared_heap/shared_concurrent_sweeper.h"
#include "ecmascript/mem/gc_key_stats.h"
#include "ecmascript/mem/gc_stats.h"
#include "ecmascript/mem/allocation_inspector.h"
#include "ecmascript/dfx/hprof/heap_sampling.h"
#include "ecmascript/tests/ecma_test_common.h"
#if defined(PANDA_TARGET_OHOS) && !defined(STANDALONE_MODE)
#include "parameters.h"
#endif

using namespace panda;

using namespace panda::ecmascript;
using namespace panda::panda_file;
using namespace panda::pandasm;

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", "1");
        OHOS::system::SetParameter("persist.ark.sheap.growstep", "2");
        OHOS::system::SetParameter("persist.ark.sensitive.threshold", "3");
        OHOS::system::SetParameter("persist.ark.native.stepsize", "4");
        OHOS::system::SetParameter("persist.ark.global.alloclimit", "4");
#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);
    }
};

HWTEST_F_L0(GCTest, ArkToolsHintGC)
{
    Heap *heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    heap->GetConcurrentMarker()->EnableConcurrentMarking(EnableConcurrentMarkType::CONFIG_DISABLE);
    auto getSizeAfterCreateAndCallHintGC = [this, heap] (size_t &newSize, size_t &finalSize) -> bool {
        {
            [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
            for (int i = 0; i < 2048; i++) {
                [[maybe_unused]] JSHandle<TaggedArray> obj = thread->GetEcmaVM()->GetFactory()->
                    NewTaggedArray(10 * 1024, JSTaggedValue::Hole(), MemSpaceType::OLD_SPACE);
            }
            newSize = heap->GetCommittedSize();
        }
        std::vector<JSTaggedValue> vals{JSTaggedValue(static_cast<double>(2))};
        auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, vals,
                                                                         6);
        [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
        JSTaggedValue result = builtins::BuiltinsArkTools::HintGC(ecmaRuntimeCallInfo);
        finalSize = heap->GetCommittedSize();
        TestHelper::TearDownFrame(thread, prev);

        return result.ToBoolean();
    };
    {
        // Test HintGC() when sensitive.
        heap->CollectGarbage(TriggerGCType::FULL_GC);
        heap->NotifyHighSensitive(true);
        size_t originSize = heap->GetCommittedSize();
        size_t newSize = 0;
        size_t finalSize = 0;
        bool res = getSizeAfterCreateAndCallHintGC(newSize, finalSize);
        EXPECT_FALSE(res);
        EXPECT_TRUE(newSize > originSize);
        EXPECT_TRUE(finalSize == newSize);
        heap->NotifyHighSensitive(false);
    }
    {
#ifdef NDEBUG
        if constexpr (G_USE_CMS_GC) {
            return;
        }
        size_t newSize = 0;
        size_t finalSize = 0;
        bool res = getSizeAfterCreateAndCallHintGC(newSize, finalSize);
        EXPECT_TRUE(res);
#endif
    }
}

HWTEST_F_L0(GCTest, LargeOverShootSizeTest)
{
    if constexpr (G_USE_CMS_GC) {
        return;
    }
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    heap->CollectGarbage(TriggerGCType::FULL_GC);
    size_t originalYoungSize = heap->GetNewSpace()->GetCommittedSize();

    EXPECT_FALSE(heap->GetNewSpace()->CommittedSizeIsLarge());
    heap->GetConcurrentMarker()->ConfigConcurrentMark(false);
    heap->NotifyHighSensitive(true);
    size_t originalCapacity = heap->GetNewSpace()->GetInitialCapacity();
    size_t originalOverShootSize = heap->GetNewSpace()->GetOvershootSize();
    {
        [[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);
        }
        size_t newYoungSize = heap->GetNewSpace()->GetCommittedSize();
        EXPECT_TRUE(originalYoungSize < newYoungSize);

        heap->NotifyHighSensitive(false);
        heap->CollectGarbage(TriggerGCType::YOUNG_GC);
        newYoungSize = heap->GetNewSpace()->GetCommittedSize();
        size_t newOverShootSize = heap->GetNewSpace()->GetOvershootSize();
        size_t newCapacity = heap->GetNewSpace()->GetInitialCapacity();
        EXPECT_TRUE(originalYoungSize < newYoungSize);
        EXPECT_TRUE(originalOverShootSize < newOverShootSize);
        EXPECT_TRUE(0 < newOverShootSize);
        EXPECT_TRUE(originalCapacity < newCapacity);
        EXPECT_TRUE(heap->GetNewSpace()->GetMaximumCapacity() == newCapacity);
    }
    originalOverShootSize = heap->GetNewSpace()->GetOvershootSize();
    {
        [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
        for (int i = 0; i < 2049; i++) {
            [[maybe_unused]] JSHandle<TaggedArray> array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(
                1024, JSTaggedValue::Hole(), MemSpaceType::SEMI_SPACE);
        }
    }
    size_t newSize = heap->GetNewSpace()->GetCommittedSize();
    EXPECT_TRUE(originalYoungSize <= newSize);
}

HWTEST_F_L0(GCTest, CheckAndTriggerSharedGCTest001)
{
    SharedHeap *heap = SharedHeap::GetInstance();
    ASSERT_EQ(heap->CheckAndTriggerSharedGC(thread), false);
}

HWTEST_F_L0(GCTest, CheckHugeAndTriggerSharedGCTest001)
{
    SharedHeap *heap = SharedHeap::GetInstance();
    ASSERT_EQ(heap->CheckHugeAndTriggerSharedGC(thread, 1374210560), true);
}

HWTEST_F_L0(GCTest, CheckHugeAndTriggerSharedGCTest002)
{
    SharedHeap *heap = SharedHeap::GetInstance();
    ASSERT_EQ(heap->CheckHugeAndTriggerSharedGC(thread, 1), false);
}

HWTEST_F_L0(GCTest, ObjectExceedMaxHeapSizeTest001)
{
    SharedHeap *heap = SharedHeap::GetInstance();
    ASSERT_EQ(heap->ObjectExceedMaxHeapSize(), false);
}

HWTEST_F_L0(GCTest, CheckAndTriggerHintGCTest001)
{
#if !USE_STICKY_CMS_GC && defined(NDEBUG)
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    heap->CollectGarbage(TriggerGCType::OLD_GC);
    ASSERT_EQ(heap->CheckAndTriggerHintGC(MemoryReduceDegree::LOW, GCReason::HINT_GC), false);
    {
        [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
        for (int i = 0; i < 4048; i++) {
            [[maybe_unused]] JSHandle<TaggedArray> array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(
                1024, JSTaggedValue::Hole(), MemSpaceType::OLD_SPACE);
        }
    }
#ifndef PANDA_TARGET_32
    ASSERT_EQ(heap->CheckAndTriggerHintGC(MemoryReduceDegree::LOW, GCReason::HINT_GC), true);
#else
    ASSERT_EQ(heap->CheckAndTriggerHintGC(MemoryReduceDegree::LOW, GCReason::HINT_GC), false);
#endif
#endif
}

HWTEST_F_L0(GCTest, CheckAndTriggerHintGCTest002)
{
#ifdef NDEBUG
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    heap->CollectGarbage(TriggerGCType::FULL_GC);
    ASSERT_EQ(heap->CheckAndTriggerHintGC(MemoryReduceDegree::MIDDLE, GCReason::HINT_GC), false);
    {
        [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
        for (int i = 0; i < 4048; i++) {
            [[maybe_unused]] JSHandle<TaggedArray> array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(
                1024, JSTaggedValue::Hole(), MemSpaceType::OLD_SPACE);
        }
    }
    ASSERT_EQ(heap->CheckAndTriggerHintGC(MemoryReduceDegree::MIDDLE, GCReason::HINT_GC), true);
#endif
}

HWTEST_F_L0(GCTest, CheckAndTriggerHintGCTest003)
{
#ifdef NDEBUG
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    heap->CollectGarbage(TriggerGCType::FULL_GC);
    ASSERT_EQ(heap->CheckAndTriggerHintGC(MemoryReduceDegree::HIGH, GCReason::HINT_GC), false);
    {
        [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
        for (int i = 0; i < 1049; i++) {
            [[maybe_unused]] JSHandle<TaggedArray> array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(
                1024, JSTaggedValue::Hole(), MemSpaceType::OLD_SPACE);
        }
    }
    ASSERT_EQ(heap->CheckAndTriggerHintGC(MemoryReduceDegree::HIGH, GCReason::HINT_GC), true);
#endif
}

HWTEST_F_L0(GCTest, CheckAndTriggerHintGCTest004)
{
#ifdef NDEBUG
    if constexpr (G_USE_CMS_GC) {
        return;
    }
    auto sHeap = SharedHeap::GetInstance();
    sHeap->CollectGarbage<TriggerGCType::SHARED_GC, GCReason::OTHER>(thread);
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    ASSERT_EQ(heap->CheckAndTriggerHintGC(MemoryReduceDegree::LOW, GCReason::HINT_GC), false);
    {
        [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
        for (int i = 0; i < 4048; i++) {
            [[maybe_unused]] JSHandle<TaggedArray> array = thread->GetEcmaVM()->GetFactory()->
                NewSOldSpaceTaggedArray(1024, JSTaggedValue::Undefined());
        }
    }
    ASSERT_EQ(heap->CheckAndTriggerHintGC(MemoryReduceDegree::LOW, GCReason::HINT_GC), true);
#endif
}

HWTEST_F_L0(GCTest, CheckAndTriggerHintGCTest005)
{
#ifdef NDEBUG
    if constexpr (G_USE_CMS_GC) {
        return;
    }
    auto sHeap = SharedHeap::GetInstance();
    sHeap->CollectGarbage<TriggerGCType::SHARED_FULL_GC, GCReason::OTHER>(thread);
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    ASSERT_EQ(heap->CheckAndTriggerHintGC(MemoryReduceDegree::MIDDLE, GCReason::HINT_GC), false);
    {
        [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
        for (int i = 0; i < 4048; i++) {
            [[maybe_unused]] JSHandle<TaggedArray> array = thread->GetEcmaVM()->GetFactory()->
                NewSOldSpaceTaggedArray(1024, JSTaggedValue::Undefined());
        }
    }
    ASSERT_EQ(heap->CheckAndTriggerHintGC(MemoryReduceDegree::MIDDLE, GCReason::HINT_GC), true);
#endif
}

HWTEST_F_L0(GCTest, CheckAndTriggerHintGCTest006)
{
#ifdef NDEBUG
    // fixme: adapt to cms
    if constexpr (G_USE_CMS_GC) {
        return;
    }
    auto sHeap = SharedHeap::GetInstance();
    sHeap->CollectGarbage<TriggerGCType::SHARED_FULL_GC, GCReason::OTHER>(thread);
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    ASSERT_EQ(heap->CheckAndTriggerHintGC(MemoryReduceDegree::HIGH, GCReason::HINT_GC), false);
    {
        [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
        for (int i = 0; i < 2049; i++) {
            [[maybe_unused]] JSHandle<TaggedArray> array = thread->GetEcmaVM()->GetFactory()->
                NewSOldSpaceTaggedArray(1024, JSTaggedValue::Undefined());
        }
    }
    ASSERT_EQ(heap->CheckAndTriggerHintGC(MemoryReduceDegree::HIGH, GCReason::HINT_GC), true);
#endif
}

HWTEST_F_L0(GCTest, TryTriggerFullMarkBySharedLimitTest001)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    ASSERT_EQ(heap->TryTriggerFullMarkBySharedLimit(), false);
}

HWTEST_F_L0(GCTest, TryTriggerFullMarkBySharedLimitTest003)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    for (size_t i = 0; i < 4; i++) {
        ConcurrentMarker::TryIncreaseTaskCounts();
    }
#ifndef PANDA_TARGET_32
    ASSERT_EQ(heap->TryTriggerFullMarkBySharedLimit(), true);
#else
    ASSERT_EQ(heap->TryTriggerFullMarkBySharedLimit(), false);
#endif
}

HWTEST_F_L0(GCTest, CheckAndTriggerTaskFinishedGCTest001)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    heap->CheckAndTriggerTaskFinishedGC();
    ASSERT_EQ(heap->GetRecordObjectSize(), 0);
    ASSERT_EQ(heap->GetRecordNativeSize(), 0);
}

HWTEST_F_L0(GCTest, TryTriggerFullMarkBySharedSizeTest001)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    heap->GetConcurrentMarker()->ConfigConcurrentMark(true);
    heap->TryTriggerFullMarkBySharedSize(81579214);
    ASSERT_TRUE(heap->IsFullMarkRequested());
}

HWTEST_F_L0(GCTest, DecreaseNativeBindingSizeTest001)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    heap->DecreaseNativeBindingSize(0);
    ASSERT_EQ(heap->GetNativeBindingSize(), 0);
}

HWTEST_F_L0(GCTest, TriggerConcurrentMarkingTest001)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    heap->SetMarkType(MarkType::MARK_FULL);
    heap->TriggerConcurrentMarking();
    EXPECT_EQ(heap->GetEcmaGCStats()->GetMarkReason(), MarkReason::OTHER);
}

HWTEST_F_L0(GCTest, NotifyFinishColdStartTest001)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    heap->NotifyFinishColdStart(true);
}

HWTEST_F_L0(GCTest, NotifyFinishColdStartSoonTest001)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    heap->NotifyFinishColdStartSoon();
}

HWTEST_F_L0(GCTest, NeedStopCollectionTest001)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    heap->SetOnSerializeEvent(true);
    ASSERT_EQ(heap->NeedStopCollection(), true);
}

HWTEST_F_L0(GCTest, CollectGarbageTest003)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    heap->GetJSThread()->SetMarkStatus(MarkStatus::READY_TO_MARK);
    heap->CollectGarbage(TriggerGCType::FULL_GC, GCReason::IDLE);
    ASSERT_EQ(heap->GetJSThread()->GetMarkStatus(), MarkStatus::READY_TO_MARK);
}

HWTEST_F_L0(GCTest, NeedStopCollectionTest002)
{
    SharedHeap *heap = SharedHeap::GetInstance();
    heap->SetSensitiveStatus(AppSensitiveStatus::ENTER_HIGH_SENSITIVE);
    ASSERT_EQ(heap->NeedStopCollection(), true);
}

HWTEST_F_L0(GCTest, NeedStopCollectionTest003)
{
    SharedHeap *heap = SharedHeap::GetInstance();
    heap->SetSensitiveStatus(AppSensitiveStatus::ENTER_HIGH_SENSITIVE);
    heap->GetOldSpace()->SetInitialCapacity(1000);
    ASSERT_EQ(heap->NeedStopCollection(), false);
}

HWTEST_F_L0(GCTest, CheckAndTriggerSharedGCTest002)
{
    SharedHeap *heap = SharedHeap::GetInstance();
    heap->GetOldSpace()->SetInitialCapacity(100);
    ASSERT_EQ(heap->CheckAndTriggerSharedGC(thread), true);
}

HWTEST_F_L0(GCTest, CheckAndTriggerSharedGCTest003)
{
    SharedHeap *heap = SharedHeap::GetInstance();
    heap->GetOldSpace()->SetInitialCapacity(100);
    thread->SetSharedMarkStatus(SharedMarkStatus::CONCURRENT_MARKING_OR_FINISHED);
    ASSERT_EQ(heap->CheckAndTriggerSharedGC(thread), true);
}

HWTEST_F_L0(GCTest, CheckAndTriggerSharedGCTest004)
{
    SharedHeap *heap = SharedHeap::GetInstance();
    thread->SetSharedMarkStatus(SharedMarkStatus::CONCURRENT_MARKING_OR_FINISHED);
    ASSERT_EQ(heap->CheckAndTriggerSharedGC(thread), false);
}

HWTEST_F_L0(GCTest, CheckHugeAndTriggerSharedGCTest003)
{
    SharedHeap *heap = SharedHeap::GetInstance();
    heap->GetOldSpace()->SetInitialCapacity(100);
    ASSERT_EQ(heap->CheckHugeAndTriggerSharedGC(thread, 1), false);
}

HWTEST_F_L0(GCTest, CheckHugeAndTriggerSharedGCTest004)
{
    SharedHeap *heap = SharedHeap::GetInstance();
    heap->GetOldSpace()->SetInitialCapacity(100);
    thread->SetSharedMarkStatus(SharedMarkStatus::CONCURRENT_MARKING_OR_FINISHED);
    ASSERT_EQ(heap->CheckHugeAndTriggerSharedGC(thread, 1), false);
}

HWTEST_F_L0(GCTest, CheckHugeAndTriggerSharedGCTest005)
{
    SharedHeap *heap = SharedHeap::GetInstance();
    thread->SetSharedMarkStatus(SharedMarkStatus::CONCURRENT_MARKING_OR_FINISHED);
    ASSERT_EQ(heap->CheckHugeAndTriggerSharedGC(thread, 1), false);
}

HWTEST_F_L0(GCTest, CheckOngoingConcurrentMarkingTest001)
{
    SharedHeap *heap = SharedHeap::GetInstance();
    heap->GetConcurrentMarker()->ConfigConcurrentMark(false);
    ASSERT_EQ(heap->CheckOngoingConcurrentMarking(), false);
}

HWTEST_F_L0(GCTest, CheckOngoingConcurrentMarkingTest002)
{
    SharedHeap *heap = SharedHeap::GetInstance();
    heap->GetConcurrentMarker()->ConfigConcurrentMark(true);
    ASSERT_EQ(heap->CheckOngoingConcurrentMarking(), false);
}

HWTEST_F_L0(GCTest, SelectGCTypeTest001)
{
    if constexpr (G_USE_CMS_GC) {
        return;
    }
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    heap->GetOldSpace()->SetInitialCapacity(100);
    ASSERT_EQ(heap->SelectGCType(), OLD_GC);
}

HWTEST_F_L0(GCTest, SelectGCTypeTest002)
{
    if constexpr (G_USE_CMS_GC) {
        return;
    }
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    heap->GetOldSpace()->SetMaximumCapacity(1000);
    heap->GetOldSpace()->SetOvershootSize(1000);
    heap->GetNewSpace()->IncreaseCommitted(100000);
    ASSERT_EQ(heap->SelectGCType(), OLD_GC);
}

HWTEST_F_L0(GCTest, CollectGarbageTest009)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    heap->GetConcurrentMarker()->EnableConcurrentMarking(EnableConcurrentMarkType::REQUEST_DISABLE);
#ifndef PANDA_TARGET_32
    ASSERT_TRUE(heap->GetConcurrentMarker()->IsRequestDisabled());
#else
    ASSERT_FALSE(heap->GetConcurrentMarker()->IsRequestDisabled());
#endif
    heap->CollectGarbage(TriggerGCType::YOUNG_GC, GCReason::TRIGGER_BY_TASKPOOL);
}

HWTEST_F_L0(GCTest, TryTriggerFullMarkBySharedLimitTest004)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    heap->GetConcurrentMarker()->ConfigConcurrentMark(true);
    ASSERT_TRUE(heap->TryTriggerFullMarkBySharedLimit());
    heap->GetConcurrentMarker()->ConfigConcurrentMark(false);
    ASSERT_FALSE(heap->TryTriggerFullMarkBySharedLimit());
}

HWTEST_F_L0(GCTest, TriggerConcurrentMarkingTest003)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    heap->GetConcurrentMarker()->ConfigConcurrentMark(false);
    heap->TriggerConcurrentMarking(MarkReason::ALLOCATION_LIMIT);
    EXPECT_EQ(heap->GetEcmaGCStats()->GetMarkReason(), MarkReason::OTHER);
    ConcurrentMarker::DecreaseTaskCounts();
    heap->GetConcurrentMarker()->ConfigConcurrentMark(true);
    heap->TriggerConcurrentMarking(MarkReason::ALLOCATION_LIMIT);
    EXPECT_EQ(heap->GetEcmaGCStats()->GetMarkReason(), MarkReason::ALLOCATION_LIMIT);
}

HWTEST_F_L0(GCTest, NotifyFinishColdStartTest002)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    heap->NotifyPostFork();
    ASSERT_EQ(heap->GetStartupStatus(), StartupStatus::ON_STARTUP);
    heap->NotifyFinishColdStart(true);
    ASSERT_EQ(heap->GetStartupStatus(), StartupStatus::JUST_FINISH_STARTUP);
}

HWTEST_F_L0(GCTest, NeedStopCollectionTest004)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    heap->SetOnSerializeEvent(false);
    heap->SetSensitiveStatus(AppSensitiveStatus::ENTER_HIGH_SENSITIVE);
    ASSERT_EQ(heap->NeedStopCollection(), true);
}

HWTEST_F_L0(GCTest, TryToGetSuitableSweptRegionTest001)
{
    if constexpr (G_USE_CMS_GC) {
        return;
    }
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    SparseSpace *space = static_cast<SparseSpace *>(heap->GetSweepableSpaceWithType(MemSpaceType::OLD_SPACE));
    space->FinishFillSweptRegion();
    ASSERT_EQ(space->TryToGetSuitableSweptRegion(100), nullptr);
}

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

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

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

HWTEST_F_L0(GCTest, StartCalculationBeforeGCTest002)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    MemController *memController = new MemController(heap);
    memController->StopCalculationAfterGC(TriggerGCType::YOUNG_GC);
    ASSERT_EQ(memController->GetNewSpaceAllocSizeSinceGC(), 0);
    ASSERT_EQ(memController->GetOldSpaceAllocSizeSinceGC(), 0);
}

HWTEST_F_L0(GCTest, DryTrunkExpandTest001)
{
    auto trunk = thread->GetEcmaVM()->GetChunk();
    DynChunk *dynChunk = new DynChunk(trunk);
    ASSERT_TRUE(dynChunk->GetAllocatedSize() < 1000);
    dynChunk->SetError();
    ASSERT_EQ(dynChunk->Expand(1000), -1);
}

HWTEST_F_L0(GCTest, DryTrunkInsertTest001)
{
    auto trunk = thread->GetEcmaVM()->GetChunk();
    DynChunk *dynChunk = new DynChunk(trunk);
    ASSERT_EQ(dynChunk->Insert(5, 5), -1);
}

HWTEST_F_L0(GCTest, DryTrunkInsertTest002)
{
    auto trunk = thread->GetEcmaVM()->GetChunk();
    DynChunk *dynChunk = new DynChunk(trunk);
    dynChunk->SetError();
    ASSERT_EQ(dynChunk->Insert(0, 5), -1);
}

HWTEST_F_L0(GCTest, AdvanceAllocationInspectorTest001)
{
    auto counter = new AllocationCounter();
    counter->AdvanceAllocationInspector(100);
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    auto profiler = new HeapSampling(thread->GetEcmaVM(), heap, 10, 3);
    auto inspector = new AllocationInspector(heap, 10, profiler);
    counter->AddAllocationInspector(inspector);
    counter->AdvanceAllocationInspector(0);
}

HWTEST_F_L0(GCTest, InvokeAllocationInspectorTest001)
{
    auto counter = new AllocationCounter();
    counter->InvokeAllocationInspector(10000, 100, 100);
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    auto profiler = new HeapSampling(thread->GetEcmaVM(), heap, 10, 3);
    auto inspector = new AllocationInspector(heap, 10, profiler);
    counter->AddAllocationInspector(inspector);
    counter->InvokeAllocationInspector(10000, 100, 100);
}

HWTEST_F_L0(GCTest, OldSpaceValidCheck)
{
    if constexpr (G_USE_CMS_GC) {
        return;
    }
    static constexpr size_t kLength = 10 * 1024;
    static constexpr size_t kCount = 2;
    static constexpr size_t kLimit = 380 * 1024 * 1024;
    instance->GetJSOptions().SetEnableForceGC(false);
    Heap *heap = const_cast<Heap *>(instance->GetHeap());
    ObjectFactory *factory = heap->GetEcmaVM()->GetFactory();
    auto array = factory->NewTaggedArray(kLength, JSTaggedValue::Hole(), MemSpaceType::OLD_SPACE);
    heap->ShouldThrowOOMError(true);
    heap->GetOldSpace()->IncreaseLiveObjectSize(kLimit);
    for (size_t i = 0; i < kCount; i++) {
        array = factory->NewTaggedArray(kLength, JSTaggedValue::Hole(), MemSpaceType::OLD_SPACE);
        Region *objectRegion = Region::ObjectAddressToRange(*array);
        bool inHeap = false;
        heap->GetOldSpace()->EnumerateRegions([objectRegion, &inHeap](Region *each) {
            if (objectRegion == each) {
                inHeap = true;
            }
        });
        EXPECT_TRUE(inHeap);
    }
}

HWTEST_F_L0(GCTest, DisableSharedConcurrentSweep)
{
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    SharedHeap *sHeap = SharedHeap::GetInstance();
    sHeap->GetSweeper()->ConfigConcurrentSweep(false);
    {
        [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
        [[maybe_unused]] JSHandle<EcmaString> key1(factory->NewFromASCII("error1"));
        [[maybe_unused]] JSHandle<EcmaString> key2(factory->NewFromASCII("error2"));
        [[maybe_unused]] JSHandle<EcmaString> msg(factory->NewFromASCII("this is error"));
        [[maybe_unused]] JSHandle<EcmaString> key3(factory->NewFromASCII("error3"));
        [[maybe_unused]] JSHandle<EcmaString> key4(factory->NewFromASCII("error4"));
        [[maybe_unused]] JSHandle<EcmaString> msg2(factory->NewFromASCII("this is error2"));
        auto* newBitSetVector = new std::vector<std::bitset<JSAPIBitVector::BIT_SET_LENGTH>>();
        int32_t capacity = 256;
        std::bitset<JSAPIBitVector::BIT_SET_LENGTH> initBitSet;
        newBitSetVector->resize(capacity, initBitSet);
        auto deleter = []([[maybe_unused]] void *env, void *pointer, [[maybe_unused]] void *data) {
            if (pointer == nullptr) {
                return;
            }
            delete reinterpret_cast<std::vector<std::bitset<JSAPIBitVector::BIT_SET_LENGTH>> *>(pointer);
        };
        [[maybe_unused]] JSHandle<JSNativePointer> pointer = factory->NewSJSNativePointer(newBitSetVector, deleter,
                                                                                          newBitSetVector);
        const char *filename1 = "__JSPandaFileManagerTest1.pa";
        const char *filename2 = "__JSPandaFileManagerTest2.pa";
        const char *data = R"(
            .function void foo() {}
        )";
        JSPandaFileManager *pfManager = JSPandaFileManager::GetInstance();
        Parser parser;
        auto res = parser.Parse(data);
        std::unique_ptr<const File> pfPtr1 = pandasm::AsmEmitter::Emit(res.Value());
        std::unique_ptr<const File> pfPtr2 = pandasm::AsmEmitter::Emit(res.Value());
        std::shared_ptr<JSPandaFile> pf1 = pfManager->NewJSPandaFile(pfPtr1.release(), CString(filename1));
        std::shared_ptr<JSPandaFile> pf2 = pfManager->NewJSPandaFile(pfPtr2.release(), CString(filename2));
        pfManager->AddJSPandaFile(pf1);
        pfManager->AddJSPandaFile(pf2);

        JSHandle<ConstantPool> constpool1 = instance->GetFactory()->NewSConstantPool(1);
        JSHandle<ConstantPool> constpool2 = instance->GetFactory()->NewSConstantPool(2);
        constpool1 = Runtime::GetInstance()->AddOrUpdateConstpool(pf1.get(), constpool1, 0);
        constpool2 = Runtime::GetInstance()->AddOrUpdateConstpool(pf2.get(), constpool2, 0);
    }
    sHeap->CollectGarbage<TriggerGCType::SHARED_GC, GCReason::OTHER>(thread);
    sHeap->WaitGCFinished(thread);
    sHeap->CollectGarbage<TriggerGCType::SHARED_GC, GCReason::OTHER>(thread);
    sHeap->WaitGCFinished(thread);
    sHeap->GetSweeper()->ConfigConcurrentSweep(true);
    EXPECT_FALSE(sHeap->GetSweeper()->IsDisabled());
};

HWTEST_F_L0(GCTest, RawHeapSendSysEventDataSize)
{
    const std::string fileName = "/data/log/faultlog/temp/jsheap.rawheap";
    uint64_t fileSize = 256;
    std::vector<std::string> filePaths;
    std::vector<uint64_t> fileSizes;

    filePaths.emplace_back(fileName);
    fileSizes.emplace_back(fileSize);
    GCKeyStats *keystats = thread->GetEcmaVM()->GetEcmaGCKeyStats();
    int32_t ret = keystats->SendSysEventDataSize(filePaths, fileSizes);
    ASSERT_EQ(ret, 0);
}

HWTEST_F_L0(GCTest, ResetLargeHeapTest)
{
    // fixme: adapt to cms
    if constexpr (G_USE_CMS_GC) {
        return;
    }
    static constexpr size_t heapSize = 512 * 1024 * 1024; // 512 MB
    const Heap *heap = thread->GetEcmaVM()->GetHeap();
    const_cast<Heap *>(heap)->ResetLargeCapacity(heapSize);
    ASSERT_EQ(heap->GetEcmaParamConfiguration().GetMaxHeapSize(), heapSize);
    auto sharedHeap = SharedHeap::GetInstance();
    sharedHeap->ResetLargeCapacity(heapSize);
    ASSERT_EQ(sharedHeap->GetEcmaParamConfiguration().GetMaxHeapSize(), heapSize);
}

HWTEST_F_L0(GCTest, SharedMarkingBarrier)
{
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    SharedHeap *sHeap = SharedHeap::GetInstance();
    JSHandle<TaggedArray> array(factory->NewSTaggedArray(16));
    sHeap->TriggerConcurrentMarking<TriggerGCType::SHARED_GC, MarkReason::OTHER>(thread);
    std::this_thread::sleep_for(std::chrono::seconds(3));
    thread->CheckSafepoint();
    JSHandle<TaggedArray> obj(factory->NewSTaggedArray(16));
    for (int i = 0; i < 10; i++) {
        array->Set(thread, i, obj);
    }
}

HWTEST_F_L0(GCTest, OvershootSizeTest1)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    auto newSpace = heap->GetNewSpace();
    newSpace->SetInitialCapacity(newSpace->GetCommittedSize());
    newSpace->SetOverShootSize(0);
    thread->SetMarkStatus(MarkStatus::READY_TO_MARK);
    thread->SetProcessingLocalToSharedRset(false);
    for (int i = 0; i < 10; i++) {
        newSpace->Allocate(50 * 1024);
    }
    ASSERT_TRUE(newSpace->GetOvershootSize() == 0);
}

HWTEST_F_L0(GCTest, OvershootSizeTest2)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    auto newSpace = heap->GetNewSpace();
    newSpace->SetInitialCapacity(newSpace->GetCommittedSize());
    newSpace->SetOverShootSize(0);
    thread->SetMarkStatus(MarkStatus::READY_TO_MARK);
    thread->SetProcessingLocalToSharedRset(true);
    for (int i = 0; i < 10; i++) {
        newSpace->Allocate(50 * 1024);
    }
    ASSERT_TRUE(newSpace->GetOvershootSize() > 0);
}

HWTEST_F_L0(GCTest, OvershootSizeTest3)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    auto newSpace = heap->GetNewSpace();
    newSpace->SetInitialCapacity(newSpace->GetCommittedSize());
    newSpace->SetOverShootSize(0);
    thread->SetMarkStatus(MarkStatus::MARKING);
    thread->SetProcessingLocalToSharedRset(false);
    for (int i = 0; i < 10; i++) {
        newSpace->Allocate(50 * 1024);
    }
    ASSERT_TRUE(newSpace->GetOvershootSize() > 0);
}

HWTEST_F_L0(GCTest, OvershootSizeTest4)
{
    auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
    auto newSpace = heap->GetNewSpace();
    newSpace->SetInitialCapacity(newSpace->GetCommittedSize());
    newSpace->SetOverShootSize(0);
    thread->SetMarkStatus(MarkStatus::MARKING);
    thread->SetProcessingLocalToSharedRset(true);
    for (int i = 0; i < 10; i++) {
        newSpace->Allocate(50 * 1024);
    }
    ASSERT_TRUE(newSpace->GetOvershootSize() > 0);
}

void TestStringTable(JSThread *thread, EcmaStringTable *stringTable)
{
    EcmaVM *vm = thread->GetEcmaVM();
    ObjectFactory *factory = vm->GetFactory();

    EcmaString *str = EcmaStringAccessor::CreateEmptyString(thread->GetEcmaVM());
    EcmaString *result = stringTable->GetOrInternFlattenString(vm, str);
    ASSERT_STREQ(EcmaStringAccessor(result).ToCString(thread).c_str(), "");
    ASSERT_TRUE(EcmaStringAccessor(result).IsInternString());

    uint8_t utf8Data[] = {0x74, 0x65, 0x73, 0x74}; // "test"
    str = EcmaStringAccessor::CreateFromUtf8(vm, utf8Data, sizeof(utf8Data), true);
    result = stringTable->GetOrInternFlattenStringNoGC(vm, str);
    ASSERT_STREQ(EcmaStringAccessor(result).ToCString(thread).c_str(), "test");
    ASSERT_TRUE(EcmaStringAccessor(result).IsInternString());

    JSHandle<EcmaString> strHandle =
        factory->NewFromASCII("00000x680x650x6c0x6c0x6f0x200x770x6f0x720x6c0x64");  // "hello world"
    uint32_t offset = 4;
    uint32_t utf8Len = EcmaStringAccessor(*strHandle).GetLength() - offset;
    result = stringTable->GetOrInternStringFromCompressedSubString(vm, strHandle, offset, utf8Len);
    ASSERT_STREQ(EcmaStringAccessor(result).ToCString(thread).c_str(), "0x680x650x6c0x6c0x6f0x200x770x6f0x720x6c0x64");
    ASSERT_TRUE(EcmaStringAccessor(result).IsInternString());

    str = EcmaStringAccessor::CreateFromUtf8(vm, utf8Data, sizeof(utf8Data), true);
    result = stringTable->GetOrInternString(vm, str);
    ASSERT_STREQ(EcmaStringAccessor(result).ToCString(thread).c_str(), "test");
    ASSERT_TRUE(EcmaStringAccessor(result).IsInternString());

    JSHandle<EcmaString> first = factory->NewFromASCII("hello");
    JSHandle<EcmaString> second = factory->NewFromASCII("world");
    result = stringTable->GetOrInternString(vm, first, second);
    ASSERT_STREQ(EcmaStringAccessor(result).ToCString(thread).c_str(), "helloworld");
    ASSERT_TRUE(EcmaStringAccessor(result).IsInternString());

    result = stringTable->GetOrInternString(vm, utf8Data, sizeof(utf8Data), true);
    ASSERT_STREQ(EcmaStringAccessor(result).ToCString(thread).c_str(), "test");
    ASSERT_TRUE(EcmaStringAccessor(result).IsInternString());

    uint16_t utf16Data[] = {0x7F16, 0x7801, 0x89E3, 0x7801}; // "编码解码"
    result = stringTable->GetOrInternString(vm, utf16Data, sizeof(utf16Data) / sizeof(uint16_t), false);
    ASSERT_STREQ(EcmaStringAccessor(result).ToCString(thread).c_str(), "编码解码");
    ASSERT_TRUE(EcmaStringAccessor(result).IsInternString());

    strHandle = factory->NewFromASCII("test");
    stringTable->GetOrInternString(vm, *strHandle);
    result = stringTable->TryGetInternString(thread, strHandle);
    ASSERT_STREQ(EcmaStringAccessor(result).ToCString(thread).c_str(), "test");
    ASSERT_TRUE(EcmaStringAccessor(result).IsInternString());

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

HWTEST_F_L0(GCTest, stringTableConcurrentSweepTest1)
{
    EcmaStringTable *stringTable = Runtime::GetInstance()->GetEcmaStringTable();
    typename DisableCMCGCConcurrentSweepTrait::HashTrieMapType *hashTrieMap =
        reinterpret_cast<typename DisableCMCGCConcurrentSweepTrait::HashTrieMapType *>(stringTable->GetHashTrieMap());
    stringTable->GetCleaner()->SetEnableConcurrentSweep(true);

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

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

HWTEST_F_L0(GCTest, stringTableConcurrentSweepTest2)
{
    SharedHeap *sHeap = SharedHeap::GetInstance();
    EcmaStringTable *stringTable = Runtime::GetInstance()->GetEcmaStringTable();
    typename DisableCMCGCConcurrentSweepTrait::HashTrieMapType *hashTrieMap =
        reinterpret_cast<typename DisableCMCGCConcurrentSweepTrait::HashTrieMapType *>(stringTable->GetHashTrieMap());
    stringTable->GetCleaner()->SetEnableConcurrentSweep(true);

    hashTrieMap->IncreaseInuseCount();
    sHeap->CollectGarbage<TriggerGCType::SHARED_GC, GCReason::OTHER>(thread);
    sHeap->CollectGarbage<TriggerGCType::SHARED_FULL_GC, GCReason::OTHER>(thread);

    sHeap->PrepareByJSThread(thread, true);
    sHeap->GetOldSpace()->SelectCSets();
    sHeap->CollectGarbage<TriggerGCType::SHARED_GC, GCReason::OTHER>(thread);
    sHeap->CollectGarbage<TriggerGCType::SHARED_FULL_GC, GCReason::OTHER>(thread);

    hashTrieMap->DecreaseInuseCount();
    sHeap->CollectGarbage<TriggerGCType::SHARED_GC, GCReason::OTHER>(thread);
    sHeap->CollectGarbage<TriggerGCType::SHARED_FULL_GC, GCReason::OTHER>(thread);

    sHeap->PrepareByJSThread(thread, true);
    sHeap->GetOldSpace()->SelectCSets();
    sHeap->CollectGarbage<TriggerGCType::SHARED_GC, GCReason::OTHER>(thread);
    sHeap->CollectGarbage<TriggerGCType::SHARED_FULL_GC, GCReason::OTHER>(thread);

    RuntimeLockHolder(thread, sHeap->GetSuspensionRequestMutex());
    ASSERT_FALSE(hashTrieMap->IsSweeping());
}

#ifndef USE_CMC_GC
HWTEST_F_L0(GCTest, stringTableReadBarrierTest)
{
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();

    JSTaggedType value = reinterpret_cast<JSTaggedType>(nullptr);
    JSTaggedType result = Barriers::ReadBarrierForStringTableSlot(value);
    ASSERT_TRUE(result == reinterpret_cast<JSTaggedType>(nullptr));

    value = thread->GlobalConstants()->GetPrototypeString().GetRawData();
    result = Barriers::ReadBarrierForStringTableSlot(value);
    ASSERT_TRUE(result == value);

    JSHandle<EcmaString> str = factory->NewFromASCIISkippingStringTable("test");
    SharedHeap::GetInstance()->PrepareByJSThread(thread, false);
    value = str.GetTaggedType();
    Region::ObjectAddressToRange(value)->AtomicMark(reinterpret_cast<void *>(value));
    result = Barriers::ReadBarrierForStringTableSlot(value);
    ASSERT_TRUE(result == value);

    Region::ObjectAddressToRange(value)->ClearMark(reinterpret_cast<void *>(value));
    result = Barriers::ReadBarrierForStringTableSlot(value);
    ASSERT_TRUE(result == reinterpret_cast<JSTaggedType>(nullptr));
}
#endif
} // namespace panda::test