/**
 * 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 "acl/acl.h"
#include "securec.h"
#include "aicpu_api/aicpu_api.h"
#include "aicpu_rt.h"
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <cstring>
#include <cstdint>
#include <mockcpp/mockcpp.hpp>

class TEST_AICPU_RT : public testing::Test {
protected:
    void SetUp() {}
    void TearDown() { GlobalMockObject::verify(); }
};

extern "C" {
int32_t ElfGetSymbolOffset(uint8_t* elf, size_t elfSize, const char* symbolName, size_t* offset, size_t* size);
}

int AicpuGetDumpConfigStub(void** addr, size_t* size)
{
    char buffer[16] = {0};
    *addr = buffer;
    *size = 1048576;
    return 0;
}

// Stubs for ElfGetSymbolOffset with different validation scenarios
int32_t ElfGetSymbolOffsetStubInvalidSize(
    uint8_t* elf, size_t elfSize, const char* symbolName, size_t* offset, size_t* size)
{
    *offset = 16;
    *size = 8; // Less than sizeof(size_t) * 2 = 16
    return 0;
}

int32_t ElfGetSymbolOffsetStubInvalidAlignment(
    uint8_t* elf, size_t elfSize, const char* symbolName, size_t* offset, size_t* size)
{
    *offset = 10; // Not 8-byte aligned
    *size = 16;
    return 0;
}

int32_t ElfGetSymbolOffsetStubOutOfBounds(
    uint8_t* elf, size_t elfSize, const char* symbolName, size_t* offset, size_t* size)
{
    *offset = elfSize; // Will cause out-of-bounds
    *size = 16;
    return 0;
}

int32_t ElfGetSymbolOffsetStubSuccess(
    uint8_t* elf, size_t elfSize, const char* symbolName, size_t* offset, size_t* size)
{
    *offset = 16; // Valid aligned offset
    *size = 16;   // Valid size (>= sizeof(size_t) * 2)
    return 0;
}

TEST_F(TEST_AICPU_RT, AicpuSetDumpConfigReturnNullptr)
{
    const size_t fileSize = 1024;
    unsigned long* aicpuFileBuf = new unsigned long[fileSize / sizeof(unsigned long)];
    memset(aicpuFileBuf, 0, fileSize);
    MOCKER(ElfGetSymbolOffset).stubs().will(returnValue(1));
    MOCKER(AicpuGetDumpConfig).stubs().will(invoke(AicpuGetDumpConfigStub));
    EXPECT_EQ(AicpuSetDumpConfig(aicpuFileBuf, fileSize), nullptr);
    delete[] aicpuFileBuf;
}

TEST_F(TEST_AICPU_RT, AicpuSetDumpConfigTest)
{
    const size_t fileSize = 1024;
    unsigned long* aicpuFileBuf = new unsigned long[fileSize / sizeof(unsigned long)];
    memset(aicpuFileBuf, 0, fileSize);
    MOCKER(ElfGetSymbolOffset).stubs().will(invoke(ElfGetSymbolOffsetStubSuccess));
    MOCKER(AicpuGetDumpConfig).stubs().will(invoke(AicpuGetDumpConfigStub));
    size_t* result = AicpuSetDumpConfig(aicpuFileBuf, fileSize);
    EXPECT_NE(result, nullptr);
    if (result != nullptr) {
        free(result);
    }
    delete[] aicpuFileBuf;
}

int aclrtGetDeviceStub(int32_t* devicdId)
{
    *devicdId = 0;
    return 0;
}

int aclrtMallocStub(void** devPtr, size_t size, aclrtMemMallocPolicy policy)
{
    *devPtr = malloc(size);
    return 0;
}

TEST_F(TEST_AICPU_RT, AicpuGetDumpConfigTest)
{
    char buffer[16] = {0};
    void* addr = buffer;
    size_t size = 1048576;
    MOCKER(aclrtGetDevice).stubs().will(invoke(aclrtGetDeviceStub));
    MOCKER(aclrtMalloc).stubs().will(invoke(aclrtMallocStub));
    EXPECT_EQ(AicpuGetDumpConfig(&addr, &size), 0);
}

TEST_F(TEST_AICPU_RT, AicpuDumpPrintBufferReturn)
{
    char buffer[16] = {0};
    void* addr = buffer;
    size_t size = 0;
    EXPECT_NO_THROW(AicpuDumpPrintBuffer(addr, size));
}

TEST_F(TEST_AICPU_RT, AicpuDumpPrintBufferTest)
{
    char buffer[16] = {0};
    void* addr = buffer;
    size_t size = 1048576;
    EXPECT_NO_THROW(AicpuDumpPrintBuffer(addr, size));
}

TEST_F(TEST_AICPU_RT, AicpuSetDumpConfigInvalidSymbolSize)
{
    const size_t fileSize = 1024;
    unsigned long* aicpuFileBuf = new unsigned long[fileSize / sizeof(unsigned long)];
    memset(aicpuFileBuf, 0, fileSize);

    MOCKER(ElfGetSymbolOffset).stubs().will(invoke(ElfGetSymbolOffsetStubInvalidSize));
    MOCKER(AicpuGetDumpConfig).stubs().will(invoke(AicpuGetDumpConfigStub));

    size_t* result = AicpuSetDumpConfig(aicpuFileBuf, fileSize);
    EXPECT_NE(result, nullptr);
    free(result);
    delete[] aicpuFileBuf;
}

TEST_F(TEST_AICPU_RT, AicpuSetDumpConfigInvalidOffsetAlignment)
{
    const size_t fileSize = 1024;
    unsigned long* aicpuFileBuf = new unsigned long[fileSize / sizeof(unsigned long)];
    memset(aicpuFileBuf, 0, fileSize);

    MOCKER(ElfGetSymbolOffset).stubs().will(invoke(ElfGetSymbolOffsetStubInvalidAlignment));
    MOCKER(AicpuGetDumpConfig).stubs().will(invoke(AicpuGetDumpConfigStub));

    size_t* result = AicpuSetDumpConfig(aicpuFileBuf, fileSize);
    EXPECT_NE(result, nullptr);
    free(result);
    delete[] aicpuFileBuf;
}

TEST_F(TEST_AICPU_RT, AicpuSetDumpConfigOffsetOutOfBounds)
{
    const size_t fileSize = 1024;
    unsigned long* aicpuFileBuf = new unsigned long[fileSize / sizeof(unsigned long)];
    memset(aicpuFileBuf, 0, fileSize);

    MOCKER(ElfGetSymbolOffset).stubs().will(invoke(ElfGetSymbolOffsetStubOutOfBounds));
    MOCKER(AicpuGetDumpConfig).stubs().will(invoke(AicpuGetDumpConfigStub));

    size_t* result = AicpuSetDumpConfig(aicpuFileBuf, fileSize);
    EXPECT_NE(result, nullptr);
    free(result);
    delete[] aicpuFileBuf;
}