/**
* Copyright (c) 2025 Huawei Technologies Co., Ltd.
* This program is free software, you can redistribute it and/or modify it under the terms and conditions of
* CANN Open Software License Agreement Version 2.0 (the "License").
* Please refer to the License for details. You may not use this file except in compliance with the License.
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
* See LICENSE in the root of the software repository for the full text of the License.
*/

#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <cstring>
#include <memory>
#include "acl/acl.h"
#include "acl/acl_prof.h"
#include "kernel_elf_parser.h"

#define ASCENDC_CPU_DEBUG

class TEST_ACL_STUB : public ::testing::Test {
protected:
    void SetUp() {}
    void TearDown() {}
};

TEST_F(TEST_ACL_STUB, AclProfInitSuccess)
{
    EXPECT_EQ(aclprofInit(nullptr, 0), ACL_SUCCESS);
}

TEST_F(TEST_ACL_STUB, AclProfSetConfigSuccess)
{
    EXPECT_EQ(aclprofSetConfig(ACL_PROF_STORAGE_LIMIT, nullptr, 0), ACL_SUCCESS);
}

TEST_F(TEST_ACL_STUB, AclProfStartStopFinalizeSuccess)
{
    EXPECT_EQ(aclprofStart(nullptr), ACL_SUCCESS);
    EXPECT_EQ(aclprofStop(nullptr), ACL_SUCCESS);
    EXPECT_EQ(aclprofFinalize(), ACL_SUCCESS);
}

TEST_F(TEST_ACL_STUB, AclInitFinalizeTest)
{
    EXPECT_EQ(aclInit(nullptr), ACL_SUCCESS);
    EXPECT_EQ(aclFinalize(), ACL_SUCCESS);
}

TEST_F(TEST_ACL_STUB, AclrtGetVersionSuccess)
{
    EXPECT_EQ(aclrtGetVersion(nullptr, nullptr, nullptr), ACL_SUCCESS);
}

TEST_F(TEST_ACL_STUB, AclDataTypeSizeCorrect)
{
    EXPECT_EQ(aclDataTypeSize(ACL_FLOAT), 4);
    EXPECT_EQ(aclDataTypeSize(ACL_FLOAT16), 2);
    EXPECT_EQ(aclDataTypeSize(ACL_INT8), 1);
    EXPECT_EQ(aclDataTypeSize(ACL_INT16), 2);
    EXPECT_EQ(aclDataTypeSize(ACL_INT32), 4);
    EXPECT_EQ(aclDataTypeSize(ACL_INT64), 8);
    EXPECT_EQ(aclDataTypeSize(ACL_UINT8), 1);
    EXPECT_EQ(aclDataTypeSize(ACL_UINT16), 2);
    EXPECT_EQ(aclDataTypeSize(ACL_UINT32), 4);
    EXPECT_EQ(aclDataTypeSize(ACL_UINT64), 8);
    EXPECT_EQ(aclDataTypeSize(ACL_DOUBLE), 8);
    EXPECT_EQ(aclDataTypeSize(ACL_BOOL), 1);
    EXPECT_EQ(aclDataTypeSize(static_cast<aclDataType>(999)), 0);
}

TEST_F(TEST_ACL_STUB, AclFloat16AndFloatConvert)
{
    float f_val = 123.0f;
    aclFloat16 f16_val = aclFloatToFloat16(f_val);
    float f_convert = aclFloat16ToFloat(f16_val);
    EXPECT_NEAR(f_convert, f_val, 0.1f);

    EXPECT_EQ(aclFloat16ToFloat(aclFloatToFloat16(0.0f)), 0.0f);
    EXPECT_NEAR(aclFloat16ToFloat(aclFloatToFloat16(100.0f)), 100.0f, 0.1f);
}

TEST_F(TEST_ACL_STUB, AclrtDeviceStreamContextSuccess)
{
    EXPECT_EQ(aclrtSetDevice(0), ACL_SUCCESS);
    EXPECT_EQ(aclrtSetDevice(-1), ACL_SUCCESS);
    EXPECT_EQ(aclrtResetDevice(0), ACL_SUCCESS);

    aclrtStream stream = nullptr;
    EXPECT_EQ(aclrtCreateStream(&stream), ACL_SUCCESS);
    EXPECT_EQ(aclrtCreateStreamWithConfig(&stream, 10, 1), ACL_SUCCESS);
    EXPECT_EQ(aclrtDestroyStream(stream), ACL_SUCCESS);
    EXPECT_EQ(aclrtDestroyStreamForce(stream), ACL_SUCCESS);
    EXPECT_EQ(aclrtSynchronizeStream(stream), ACL_SUCCESS);

    aclrtContext ctx = nullptr;
    EXPECT_EQ(aclrtCreateContext(&ctx, 0), ACL_SUCCESS);
    EXPECT_EQ(aclrtDestroyContext(ctx), ACL_SUCCESS);
}

TEST_F(TEST_ACL_STUB, AclrtSynchronizeDeviceSuccess)
{
    EXPECT_EQ(aclrtSynchronizeDevice(), ACL_SUCCESS);
}

TEST_F(TEST_ACL_STUB, AclrtMallocFreeSuccess)
{
    void* dev_ptr = nullptr;
    EXPECT_EQ(aclrtMalloc(&dev_ptr, 1024, ACL_MEM_MALLOC_HUGE_FIRST), ACL_SUCCESS);
    EXPECT_NE(dev_ptr, nullptr);
    EXPECT_EQ(aclrtFree(dev_ptr), ACL_SUCCESS);

    EXPECT_EQ(aclrtMalloc(nullptr, 1024, ACL_MEM_MALLOC_HUGE_FIRST), ACL_ERROR_INVALID_PARAM);
    EXPECT_EQ(aclrtMalloc(&dev_ptr, 0, ACL_MEM_MALLOC_HUGE_FIRST), ACL_ERROR_INVALID_PARAM);
    EXPECT_EQ(aclrtFree(nullptr), ACL_ERROR_INVALID_PARAM);
}

TEST_F(TEST_ACL_STUB, AclrtMallocHostFreeHostSuccess)
{
    void* host_ptr = nullptr;
    EXPECT_EQ(aclrtMallocHost(&host_ptr, 2048), ACL_SUCCESS);
    EXPECT_NE(host_ptr, nullptr);
    EXPECT_EQ(aclrtFreeHost(host_ptr), ACL_SUCCESS);

    EXPECT_EQ(aclrtMallocHost(nullptr, 2048), ACL_ERROR_INVALID_PARAM);
    EXPECT_EQ(aclrtMallocHost(&host_ptr, 0), ACL_ERROR_INVALID_PARAM);
    EXPECT_EQ(aclrtFreeHost(nullptr), ACL_ERROR_INVALID_PARAM);
}

TEST_F(TEST_ACL_STUB, AclrtMemsetSuccess)
{
    char buf[1024] = {0};
    EXPECT_EQ(aclrtMemset(buf, sizeof(buf), 0x55, sizeof(buf) / 2), ACL_SUCCESS);
    for (int i = 0; i < static_cast<int>(sizeof(buf) / 2); ++i) {
        EXPECT_EQ(buf[i], 0x55);
    }

    EXPECT_EQ(aclrtMemset(nullptr, 1024, 0, 512), ACL_ERROR_INVALID_PARAM);
    EXPECT_EQ(aclrtMemset(buf, 1024, 0, 2048), ACL_ERROR_BAD_ALLOC);
}

TEST_F(TEST_ACL_STUB, AclrtMemsetAsyncSuccess)
{
    char buf[1024] = {0};
    aclrtStream stream = nullptr;
    EXPECT_EQ(aclrtMemsetAsync(buf, sizeof(buf), 0x66, sizeof(buf), stream), ACL_SUCCESS);
    for (int i = 0; i < static_cast<int>(sizeof(buf)); ++i) {
        EXPECT_EQ(buf[i], 0x66);
    }

    EXPECT_EQ(aclrtMemsetAsync(nullptr, 1024, 0, 512, stream), ACL_ERROR_INVALID_PARAM);
    EXPECT_EQ(aclrtMemsetAsync(buf, 1024, 0, 2048, stream), ACL_ERROR_BAD_ALLOC);
}

TEST_F(TEST_ACL_STUB, AclrtMemcpySuccess)
{
    char src[1024] = "test_memcpy";
    char dst[1024] = {0};
    EXPECT_EQ(aclrtMemcpy(dst, sizeof(dst), src, strlen(src) + 1, ACL_MEMCPY_HOST_TO_HOST), ACL_SUCCESS);
    EXPECT_STREQ(dst, src);

    EXPECT_EQ(aclrtMemcpy(dst, 5, src, 10, ACL_MEMCPY_HOST_TO_HOST), ACL_ERROR_BAD_ALLOC);
}

TEST_F(TEST_ACL_STUB, AclrtMemcpyAsyncSuccess)
{
    char src[1024] = "test_memcpy_async";
    char dst[1024] = {0};
    aclrtStream stream = nullptr;
    EXPECT_EQ(aclrtMemcpyAsync(dst, sizeof(dst), src, strlen(src) + 1, ACL_MEMCPY_HOST_TO_HOST, stream), ACL_SUCCESS);
    EXPECT_STREQ(dst, src);

    EXPECT_EQ(aclrtMemcpyAsync(dst, 5, src, 10, ACL_MEMCPY_HOST_TO_HOST, stream), ACL_ERROR_BAD_ALLOC);
}

TEST_F(TEST_ACL_STUB, AclrtMemcpy2dSuccess)
{
    char src[1024] = "test_memcpy2d";
    char dst[1024] = {0};
    EXPECT_EQ(aclrtMemcpy2d(dst, sizeof(dst), src, sizeof(src), strlen(src) + 1, 1, ACL_MEMCPY_HOST_TO_HOST), ACL_SUCCESS);
    EXPECT_STREQ(dst, src);

    EXPECT_EQ(aclrtMemcpy2d(dst, sizeof(dst), nullptr, sizeof(src), 10, 1, ACL_MEMCPY_HOST_TO_HOST), ACL_ERROR_INVALID_PARAM);
    EXPECT_EQ(aclrtMemcpy2d(dst, 5, src, sizeof(src), 10, 1, ACL_MEMCPY_HOST_TO_HOST), ACL_ERROR_BAD_ALLOC);
}

TEST_F(TEST_ACL_STUB, AclrtMemcpy2dAsyncSuccess)
{
    char src[1024] = "test_memcpy2d_async";
    char dst[1024] = {0};
    aclrtStream stream = nullptr;
    EXPECT_EQ(aclrtMemcpy2dAsync(dst, sizeof(dst), src, sizeof(src), strlen(src) + 1, 1, ACL_MEMCPY_HOST_TO_HOST, stream), ACL_SUCCESS);
    EXPECT_STREQ(dst, src);

    EXPECT_EQ(aclrtMemcpy2dAsync(dst, sizeof(dst), nullptr, sizeof(src), 10, 1, ACL_MEMCPY_HOST_TO_HOST, stream), ACL_ERROR_INVALID_PARAM);
    EXPECT_EQ(aclrtMemcpy2dAsync(dst, 5, src, sizeof(src), 10, 1, ACL_MEMCPY_HOST_TO_HOST, stream), ACL_ERROR_BAD_ALLOC);
}

TEST_F(TEST_ACL_STUB, AclrtMemcpy2dMultiRowPacked)
{
    constexpr size_t rowBytes = 4 * sizeof(float);
    constexpr size_t height = 3;
    float src[height * 4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
    float dst[height * 4] = {0};

    EXPECT_EQ(aclrtMemcpy2d(dst, rowBytes, src, rowBytes, rowBytes, height, ACL_MEMCPY_HOST_TO_HOST), ACL_SUCCESS);
    for (size_t i = 0; i < height * 4; ++i) {
        EXPECT_EQ(dst[i], src[i]);
    }
}

TEST_F(TEST_ACL_STUB, AclrtMemcpy2dStridedRows)
{
    constexpr size_t width = 3 * sizeof(float);
    constexpr size_t spitch = 5 * sizeof(float);
    constexpr size_t dpitch = 4 * sizeof(float);
    constexpr size_t height = 3;
    float src[5 * height] = {1, 2, 3, 0, 0, 4, 5, 6, 0, 0, 7, 8, 9, 0, 0};
    float dst[4 * height] = {0};

    EXPECT_EQ(aclrtMemcpy2d(dst, dpitch, src, spitch, width, height, ACL_MEMCPY_HOST_TO_HOST), ACL_SUCCESS);
    for (size_t row = 0; row < height; ++row) {
        EXPECT_EQ(dst[row * 4 + 0], src[row * 5 + 0]);
        EXPECT_EQ(dst[row * 4 + 1], src[row * 5 + 1]);
        EXPECT_EQ(dst[row * 4 + 2], src[row * 5 + 2]);
    }
}

TEST_F(TEST_ACL_STUB, AclrtMemcpy2dAsyncMultiRow)
{
    constexpr size_t rowBytes = 4 * sizeof(float);
    constexpr size_t height = 3;
    float src[height * 4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
    float dst[height * 4] = {0};
    aclrtStream stream = nullptr;

    EXPECT_EQ(aclrtMemcpy2dAsync(dst, rowBytes, src, rowBytes, rowBytes, height, ACL_MEMCPY_HOST_TO_HOST, stream),
              ACL_SUCCESS);
    for (size_t i = 0; i < height * 4; ++i) {
        EXPECT_EQ(dst[i], src[i]);
    }
}

TEST_F(TEST_ACL_STUB, AclrtBinaryLoadFromDataSuccess)
{
    uint8_t elfData[512] = {0};
    
    elfData[EI_MAG0] = 0x7f;
    elfData[EI_MAG1] = 'E';
    elfData[EI_MAG2] = 'L';
    elfData[EI_MAG3] = 'F';
    elfData[EI_CLASS] = ELFCLASS64;
    elfData[EI_DATA] = ELFDATA2LSB;
    
    elfData[40] = 64; // e_shoff
    elfData[58] = 64; // e_shentsize
    elfData[60] = 2; // e_shnum
    elfData[62] = 1; // e_shstrndx
    
    Elf64_Shdr* shdr0 = reinterpret_cast<Elf64_Shdr*>(elfData + 64);
    shdr0->sh_name = 0;
    shdr0->sh_type = 0;
    shdr0->sh_offset = 192;
    shdr0->sh_size = 64;
    
    Elf64_Shdr* shdr1 = reinterpret_cast<Elf64_Shdr*>(elfData + 128);
    shdr1->sh_name = 1;
    shdr1->sh_type = 0;
    shdr1->sh_offset = 256;
    shdr1->sh_size = 64;
    
    const char* strTab = ".ascend.meta._Z11test_kernelv\0.shstrtab\n";
    memcpy(elfData + 256, strTab, strlen(strTab) + 1);
    
    uint8_t* metaSection = elfData + 192;
    AscendC::ElfTlvHead* head = reinterpret_cast<AscendC::ElfTlvHead*>(metaSection);
    head->type = AscendC::FUNC_META_TYPE_KERNEL_TYPE;
    head->length = 4;
    uint32_t kernelType = AscendC::K_TYPE_AIC;
    memcpy(metaSection + sizeof(AscendC::ElfTlvHead), &kernelType, sizeof(uint32_t));
    
    shdr0->sh_size = sizeof(AscendC::ElfTlvHead) + 4;
    
    AscendC::KernelModeRegister& reg = AscendC::KernelModeRegister::GetInstance();
    reg.Clear();
    
    EXPECT_EQ(aclrtBinaryLoadFromData(elfData, sizeof(elfData), nullptr, nullptr), ACL_SUCCESS);
    
    KernelMode mode = reg.GetKenelMode("_Z11test_kernelv");
    EXPECT_EQ(mode, KernelMode::AIC_MODE);
}

TEST_F(TEST_ACL_STUB, AclrtBinaryLoadFromDataInvalidElf)
{
    uint8_t elfData[32] = {0};
    elfData[EI_MAG0] = 0x7f;
    elfData[EI_MAG1] = 'E';
    elfData[EI_MAG2] = 'L';
    elfData[EI_MAG3] = 'X';
    
    aclrtBinHandle binHandle = nullptr;
    EXPECT_EQ(aclrtBinaryLoadFromData(elfData, sizeof(elfData), nullptr, &binHandle), ACL_ERROR_INVALID_PARAM);
}

TEST_F(TEST_ACL_STUB, AclrtBinaryLoadFromDataNullPointer)
{
    aclrtBinHandle binHandle = nullptr;
    EXPECT_EQ(aclrtBinaryLoadFromData(nullptr, 512, nullptr, &binHandle), ACL_ERROR_INVALID_PARAM);
}