/*
* Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "common_components/heap/ark_collector/ark_collector.h"
#include "common_components/heap/verification.cpp"
#include "common_components/heap/heap_manager.h"
#include "common_components/tests/test_helper.h"
#include "objects/base_object_operator.h"

using namespace common;
namespace common::test {
class TestBaseObjectOperator : public common::BaseObjectOperatorInterfaces {
public:
    bool IsValidObject([[maybe_unused]] const BaseObject *object) const override { return enbaleValidObject_; }
    void ForEachRefField(const BaseObject *object, const common::RefFieldVisitor &visitor) const override {}
    size_t ForEachRefFieldAndGetSize(
        const BaseObject *object,
        const common::RefFieldVisitor &visitor) const override
    {
        return 0;
    }
    size_t GetSize(const BaseObject *object) const override{ return size_; }
    BaseObject *GetForwardingPointer(const BaseObject *object) const override { return nullptr; }
    void SetForwardingPointerAfterExclusive(BaseObject *object, BaseObject *fwdPtr) override {}
    void SetValidObject(bool value) { enbaleValidObject_ = value; }
    void SetSize(size_t size) { size_ = size; }
    void ForEachRefFieldSkipReferent(const BaseObject *object, const RefFieldVisitor &visitor) const override {}
    void IterateXRef(const BaseObject *object, const RefFieldVisitor &visitor) const override {}
private:
    bool enbaleValidObject_ = false;
    size_t size_ = 0;
};
class VerificationTest : public common::test::BaseTestWithScope {
protected:
    static void SetUpTestCase()
    {
        BaseRuntime::GetInstance()->InitFromDynamic();
    }

    static void TearDownTestCase()
    {
        BaseRuntime::GetInstance()->FiniFromDynamic();
    }

    void SetUp() override
    {
        MutatorManager::Instance().CreateRuntimeMutator(ThreadType::GC_THREAD);
    }

    void TearDown() override
    {
        MutatorManager::Instance().DestroyRuntimeMutator(ThreadType::GC_THREAD);
    }
};

HWTEST_F_L0(VerificationTest, GetObjectInfoTest)
{
    BaseObject* obj = nullptr;
    std::string result = GetObjectInfo(obj);

    EXPECT_NE(result.find("address: 0x0"), std::string::npos);
    EXPECT_NE(result.find("Skip: nullptr"), std::string::npos);
    EXPECT_NE(result.find("Skip: Object is not in heap range"), std::string::npos);
}

HWTEST_F_L0(VerificationTest, GetObjectInfoTest2)
{
    BaseObject obj;
    std::string result = GetObjectInfo(&obj);
    EXPECT_NE(result.find("address: 0x"), std::string::npos);
    EXPECT_NE(result.find("Skip: Object is not in heap range"), std::string::npos);
}

HWTEST_F_L0(VerificationTest, GetRefInfoTest)
{
    BaseObject oldObj;
    RefField<false> oldField(&oldObj);
    MAddress oldAddress = oldField.GetFieldValue();
    std::string result = GetRefInfo(oldField);
    EXPECT_NE(result.find("address: 0x"), std::string::npos);
    EXPECT_NE(result.find("Skip: Object is not in heap range"), std::string::npos);
}

HWTEST_F_L0(VerificationTest, VerifyRefImplTest2)
{
    RegionalHeap& theAllocator = reinterpret_cast<RegionalHeap&>(Heap::GetHeap().GetAllocator());
    uintptr_t addr = theAllocator.AllocOldRegion();
    ASSERT_NE(addr, 0U);
    BaseObject* obj = reinterpret_cast<BaseObject*>(addr);
    RegionDesc* region = RegionDesc::GetRegionDescAt(reinterpret_cast<uintptr_t>(obj));
    ASSERT_NE(region, nullptr);
    region->SetRegionType(RegionDesc::RegionType::FROM_REGION);
    RefField<false> field(obj);

    auto refObj = field.GetTargetObject();

    AfterForwardVisitor visitor;
    visitor.VerifyRefImpl(obj, field);
    ASSERT_FALSE(RegionalHeap::IsMarkedObject(refObj));
    ASSERT_FALSE(RegionalHeap::IsResurrectedObject(refObj));
}

HWTEST_F_L0(VerificationTest, VerifyRefImplTest3)
{
    RegionalHeap& theAllocator = reinterpret_cast<RegionalHeap&>(Heap::GetHeap().GetAllocator());
    uintptr_t addr = theAllocator.AllocOldRegion();
    ASSERT_NE(addr, 0U);
    BaseObject* obj = reinterpret_cast<BaseObject*>(addr);
    RegionDesc* region = RegionDesc::GetRegionDescAt(reinterpret_cast<uintptr_t>(obj));
    ASSERT_NE(region, nullptr);
    region->SetRegionType(RegionDesc::RegionType::FULL_POLYSIZE_NONMOVABLE_REGION);
    RefField<false> field(obj);

    auto refObj = field.GetTargetObject();

    ReadBarrierSetter visitor;
    visitor.VerifyRefImpl(nullptr, field);
    visitor.VerifyRefImpl(obj, field);
    EXPECT_EQ(RegionDesc::RegionType::FULL_POLYSIZE_NONMOVABLE_REGION,
        RegionDesc::GetRegionDescAt(reinterpret_cast<MAddress>(field.GetTargetObject()))->GetRegionType());

    region->SetRegionType(RegionDesc::RegionType::RECENT_POLYSIZE_NONMOVABLE_REGION);
    visitor.VerifyRefImpl(obj, field);
    EXPECT_EQ(RegionDesc::RegionType::RECENT_POLYSIZE_NONMOVABLE_REGION,
        RegionDesc::GetRegionDescAt(reinterpret_cast<MAddress>(field.GetTargetObject()))->GetRegionType());

    region->SetRegionType(RegionDesc::RegionType::MONOSIZE_NONMOVABLE_REGION);
    visitor.VerifyRefImpl(obj, field);
    EXPECT_EQ(RegionDesc::RegionType::MONOSIZE_NONMOVABLE_REGION,
        RegionDesc::GetRegionDescAt(reinterpret_cast<MAddress>(field.GetTargetObject()))->GetRegionType());

    region->SetRegionType(RegionDesc::RegionType::FULL_MONOSIZE_NONMOVABLE_REGION);
    visitor.VerifyRefImpl(obj, field);
    EXPECT_EQ(RegionDesc::RegionType::FULL_MONOSIZE_NONMOVABLE_REGION,
        RegionDesc::GetRegionDescAt(reinterpret_cast<MAddress>(field.GetTargetObject()))->GetRegionType());

    region->SetRegionType(RegionDesc::RegionType::READ_ONLY_REGION);
    auto oldRefValue = field.GetFieldValue();
    visitor.VerifyRefImpl(obj, field);
    auto newRefValue = field.GetFieldValue();
    EXPECT_NE(oldRefValue, newRefValue);
}

std::unique_ptr<ArkCollector> GetArkCollector()
{
    CollectorResources &resources = Heap::GetHeap().GetCollectorResources();
    Allocator &allocator = Heap::GetHeap().GetAllocator();

    return std::make_unique<ArkCollector>(allocator, resources);
}

HWTEST_F_L0(VerificationTest, VerifyAfterMarkTest1)
{
    Heap::GetHeap().SetGCPhase(GCPhase::GC_PHASE_POST_MARK);
    std::unique_ptr<ArkCollector> arkCollector = GetArkCollector();
    ASSERT_TRUE(arkCollector != nullptr);
    WVerify verify;
    verify.VerifyAfterMark(*arkCollector);
    ASSERT_FALSE(MutatorManager::Instance().WorldStopped());
}

HWTEST_F_L0(VerificationTest, VerifyAfterForwardTest1)
{
    Heap::GetHeap().SetGCPhase(GCPhase::GC_PHASE_COPY);
    std::unique_ptr<ArkCollector> arkCollector = GetArkCollector();
    ASSERT_TRUE(arkCollector != nullptr);
    WVerify verify;
    verify.VerifyAfterForward(*arkCollector);
    ASSERT_FALSE(MutatorManager::Instance().WorldStopped());
}

HWTEST_F_L0(VerificationTest, VerifyAfterFixTest1)
{
    Heap::GetHeap().SetGCPhase(GCPhase::GC_PHASE_FIX);
    std::unique_ptr<ArkCollector> arkCollector = GetArkCollector();
    ASSERT_TRUE(arkCollector != nullptr);
    WVerify verify;
    verify.VerifyAfterFix(*arkCollector);
    ASSERT_FALSE(MutatorManager::Instance().WorldStopped());
}

HWTEST_F_L0(VerificationTest, EnableReadBarrierDFXTest1)
{
    std::unique_ptr<ArkCollector> arkCollector = GetArkCollector();
    ASSERT_TRUE(arkCollector != nullptr);
    WVerify verify;
    verify.EnableReadBarrierDFX(*arkCollector);
    ASSERT_FALSE(MutatorManager::Instance().WorldStopped());
}

HWTEST_F_L0(VerificationTest, DisableReadBarrierDFXTest1)
{
    std::unique_ptr<ArkCollector> arkCollector = GetArkCollector();
    ASSERT_TRUE(arkCollector != nullptr);
    WVerify verify;
    verify.DisableReadBarrierDFX(*arkCollector);
    ASSERT_FALSE(MutatorManager::Instance().WorldStopped());
}

HWTEST_F_L0(VerificationTest, GetObjectInfoTest3)
{
    HeapAddress addr = HeapManager::Allocate(sizeof(BaseObject), AllocType::NONMOVABLE_OBJECT, true);
    BaseObject *obj = reinterpret_cast<BaseObject *>(addr);
    std::string result = GetObjectInfo(obj);
    EXPECT_NE(result.find("address: 0x"), std::string::npos);
    EXPECT_NE(result.find("Type: 0x"), std::string::npos);
    EXPECT_NE(result.find("Base: 0x"), std::string::npos);
    EXPECT_NE(result.find("Start: 0x"), std::string::npos);
    EXPECT_NE(result.find("End: 0x"), std::string::npos);
    EXPECT_NE(result.find("AllocPtr: 0x"), std::string::npos);
    EXPECT_NE(result.find("MarkingLine: 0x"), std::string::npos);
    EXPECT_NE(result.find("CopyLine: 0x"), std::string::npos);
}

HWTEST_F_L0(VerificationTest, GetRefInfoTest2)
{
    RefField<false> field(nullptr);
    uintptr_t taggedValue = 0x04;
    field.SetFieldValue(static_cast<MAddress>(taggedValue));
    std::string result = GetRefInfo(field);
    EXPECT_NE(result.find("> Raw memory:"), std::string::npos);
    EXPECT_NE(result.find("Skip: primitive"), std::string::npos);
}

HWTEST_F_L0(VerificationTest, VerifyRefImplTest)
{
    HeapAddress addr = HeapManager::Allocate(sizeof(BaseObject), AllocType::NONMOVABLE_OBJECT, true);
    BaseObject *obj = reinterpret_cast<BaseObject *>(addr);
    RefField<false> oldField(obj);
    TestBaseObjectOperator operatorImpl;
    BaseObject::RegisterDynamic(&operatorImpl);
    operatorImpl.SetValidObject(true);
    Heap::GetHeap().SetGCReason(GCReason::GC_REASON_YOUNG);
    operatorImpl.SetSize(BaseObject::BaseObjectSize());
    AfterMarkVisitor visitor;
    visitor.VerifyRefImpl(nullptr, oldField);
    ASSERT_TRUE(Heap::GetHeap().GetGCReason() == GCReason::GC_REASON_YOUNG);
    ASSERT_TRUE(Heap::IsTaggedObject(oldField.GetFieldValue()));

    AfterMarkVisitor<false> visitor1;
    visitor1.VerifyRefImpl(nullptr, oldField);
    ASSERT_TRUE(Heap::IsTaggedObject(oldField.GetFieldValue()));
}

HWTEST_F_L0(VerificationTest, VerifyRefImplTest1)
{
    HeapAddress addr = HeapManager::Allocate(sizeof(BaseObject), AllocType::NONMOVABLE_OBJECT, true);
    BaseObject *obj = reinterpret_cast<BaseObject *>(addr);
    RefField<false> oldField(obj);
    TestBaseObjectOperator operatorImpl;
    BaseObject::RegisterDynamic(&operatorImpl);
    operatorImpl.SetValidObject(true);
    Heap::GetHeap().SetGCReason(GCReason::GC_REASON_YOUNG);
    operatorImpl.SetSize(BaseObject::BaseObjectSize());
    AfterMarkVisitor visitor;
    visitor.VerifyRefImpl(obj, oldField);
    ASSERT_TRUE(Heap::GetHeap().GetGCReason() == GCReason::GC_REASON_YOUNG);
    ASSERT_TRUE(Heap::IsTaggedObject(oldField.GetFieldValue()));
}

static BaseObject* testObj = nullptr;
static void CustomVisitRoot(const RefFieldVisitor& visitorFunc)
{
    RefField<> field(testObj);
    visitorFunc(field);
}
HWTEST_F_L0(VerificationTest, IterateRemarked_VerifyAllRefs)
{
    RegionalHeap regionalHeap;
    VerifyIterator verify(regionalHeap);
    AfterForwardVisitor visitor;
    std::unordered_set<BaseObject*> markSet;
    HeapAddress addr = HeapManager::Allocate(sizeof(BaseObject), AllocType::NONMOVABLE_OBJECT, true);
    testObj = reinterpret_cast<BaseObject*>(addr);
    markSet.insert(testObj);

    verify.IterateRemarked<CustomVisitRoot>(visitor, markSet, true);
    verify.IterateRemarked<CustomVisitRoot>(visitor, markSet, false);
    EXPECT_EQ(markSet.size(), 1);
    EXPECT_TRUE(markSet.find(testObj) != markSet.end());
}
} // namespace common::test