/**
 * Copyright (c) 2025-2026 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.
 */

/*!
 * \file test_device_exception_dump.cpp
 * \brief UT for device_exception_dump.cpp - 入口函数 ExceptionDumpCallBack
 */

#include <gtest/gtest.h>
#include <cstring>
#include <vector>
#include <memory>
#include "securec.h"
#include "interface/machine/device/tilefwk/aikernel_data.h"
#include "adapter/api/runtime_define.h"
#include "adapter/api/adump_define.h"
#include "adapter/api/runtime_api.h"
#include "adapter/api/adump_api.h"
#include "machine/runtime/runner/device_exception_dump.h"
#include "tilefwk/data_type.h"
#define private public
#define protected public
#include "interface/function/function.h"
#include "interface/program/program.h"
#undef private
#undef protected

using namespace npu::tile_fwk;
using namespace npu::tile_fwk::dynamic;

constexpr int32_t MAX_AICPU_ARG_NUM = 7;
constexpr int32_t KERNEL_NAME_IDX = 0;
constexpr int32_t INPUT_OUTPUT_IDX = 4;
constexpr int32_t TENSOR_DATA_IDX = 6;

class DeviceExceptionDumpTest : public testing::Test {
public:
    static void SetUpTestCase() {}
    static void TearDownTestCase() {}
    void SetUp() override
    {
        Program::GetInstance().Reset();
        auto dynAttr = std::make_shared<DyndevFunctionAttribute>();
        dynAttr->disableL2List.clear();
        auto func = std::make_shared<Function>(Program::GetInstance(), "TestFunc", "TENSOR_TestFunc", nullptr);
        func->dyndevAttr_ = dynAttr;
        Program::GetInstance().SetLastFunction(func.get());
        testFunc_ = func;
    }
    void TearDown() override
    {
        Program::GetInstance().Reset();
        Program::GetInstance().SetLastFunction(nullptr);
        testFunc_.reset();
    }
    std::shared_ptr<Function> testFunc_;
};

TEST_F(DeviceExceptionDumpTest, TestExceptionDumpInfoIsNullptr)
{
    RtExceptionInfo exceptionInfo = {};
    uint32_t dumpSize = 1;
    uint32_t realSize = 0;
    AdxExceptionDumpMode mode = AdxExceptionDumpMode::ADX_DUMP_MODE_NONE;
    auto ret = ExceptionDumpCallBack(&exceptionInfo, nullptr, dumpSize, &realSize, &mode);
    EXPECT_EQ(ret, 1);
}

TEST_F(DeviceExceptionDumpTest, TestAdumpRegExeptionDump)
{
    auto ret = AdumpRegExceptionDump();
    EXPECT_EQ(ret, 0);
}

TEST_F(DeviceExceptionDumpTest, TestBothPtrIsNullptr)
{
    uint32_t dumpSize = 1;
    uint32_t realSize = 0;
    AdxExceptionDumpMode mode = AdxExceptionDumpMode::ADX_DUMP_MODE_NONE;
    auto ret = ExceptionDumpCallBack(nullptr, nullptr, dumpSize, &realSize, &mode);
    EXPECT_EQ(ret, 1);
}

TEST_F(DeviceExceptionDumpTest, TestAicoreExceptionWithNullArgAddr)
{
    RtExceptionInfo exceptionInfo = {};
    AdxExceptionDumpInfo dumpInfo = {};
    uint32_t dumpSize = 1;
    uint32_t realSize = 0;
    AdxExceptionDumpMode mode = AdxExceptionDumpMode::ADX_DUMP_MODE_NONE;
    exceptionInfo.expandInfo.type = RtExceptionExpandType::AICORE;
    exceptionInfo.expandInfo.u.aicoreInfo.exceptionArgs.argAddr = nullptr;
    exceptionInfo.expandInfo.u.aicoreInfo.exceptionArgs.argsize = sizeof(void*) * MAX_AICPU_ARG_NUM;
    auto ret = ExceptionDumpCallBack(&exceptionInfo, &dumpInfo, dumpSize, &realSize, &mode);
    EXPECT_EQ(ret, 1);
}

TEST_F(DeviceExceptionDumpTest, TestAicoreExceptionWithMismatchedArgsize)
{
    char kernelName[] = "PyPTO_TestKernel";
    std::vector<void*> kernelArg(MAX_AICPU_ARG_NUM, nullptr);
    kernelArg[KERNEL_NAME_IDX] = kernelName;
    RtExceptionInfo exceptionInfo = {};
    AdxExceptionDumpInfo dumpInfo = {};
    uint32_t dumpSize = 1;
    uint32_t realSize = 0;
    AdxExceptionDumpMode mode = AdxExceptionDumpMode::ADX_DUMP_MODE_NONE;
    exceptionInfo.expandInfo.type = RtExceptionExpandType::AICORE;
    exceptionInfo.expandInfo.u.aicoreInfo.exceptionArgs.argAddr = kernelArg.data();
    exceptionInfo.expandInfo.u.aicoreInfo.exceptionArgs.argsize = sizeof(void*) * 4;
    auto ret = ExceptionDumpCallBack(&exceptionInfo, &dumpInfo, dumpSize, &realSize, &mode);
    EXPECT_EQ(ret, 0);
    EXPECT_EQ(mode, AdxExceptionDumpMode::ADX_DUMP_MODE_OVERWRITE);
    EXPECT_EQ(realSize, 1);
}

TEST_F(DeviceExceptionDumpTest, TestAicoreExceptionWithNonPyptoKernel)
{
    char kernelName[] = "OtherKernelName";
    std::vector<void*> kernelArg(MAX_AICPU_ARG_NUM, nullptr);
    kernelArg[KERNEL_NAME_IDX] = kernelName;
    RtExceptionInfo exceptionInfo = {};
    AdxExceptionDumpInfo dumpInfo = {};
    uint32_t dumpSize = 1;
    uint32_t realSize = 0;
    AdxExceptionDumpMode mode = AdxExceptionDumpMode::ADX_DUMP_MODE_NONE;
    exceptionInfo.expandInfo.type = RtExceptionExpandType::AICORE;
    exceptionInfo.expandInfo.u.aicoreInfo.exceptionArgs.argAddr = kernelArg.data();
    exceptionInfo.expandInfo.u.aicoreInfo.exceptionArgs.argsize = sizeof(void*) * MAX_AICPU_ARG_NUM;
    auto ret = ExceptionDumpCallBack(&exceptionInfo, &dumpInfo, dumpSize, &realSize, &mode);
    EXPECT_EQ(ret, 0);
    EXPECT_EQ(mode, AdxExceptionDumpMode::ADX_DUMP_MODE_OVERWRITE);
    EXPECT_EQ(realSize, 1);
}

TEST_F(DeviceExceptionDumpTest, TestAicoreExceptionWithValidTensors)
{
    char kernelName[] = "PyPTO_ValidTensorKernel";
    int64_t inputOutputInfo[2] = {2, 0};
    std::vector<uint8_t> tensorBuf0(32, 0xAB);
    std::vector<uint8_t> tensorBuf1(16, 0xCD);
    DevTensorData tensorData[2] = {};
    tensorData[0].address = reinterpret_cast<uint64_t>(tensorBuf0.data());
    tensorData[0].dataType = static_cast<int32_t>(DataType::DT_FP32);
    tensorData[0].shape.dim[0] = 4;
    tensorData[0].shape.dim[1] = 8;
    tensorData[0].shape.dimSize = 2;
    tensorData[1].address = reinterpret_cast<uint64_t>(tensorBuf1.data());
    tensorData[1].dataType = static_cast<int32_t>(DataType::DT_FP32);
    tensorData[1].shape.dim[0] = 4;
    tensorData[1].shape.dim[1] = 4;
    tensorData[1].shape.dimSize = 2;
    std::vector<void*> kernelArg(MAX_AICPU_ARG_NUM, nullptr);
    kernelArg[KERNEL_NAME_IDX] = kernelName;
    kernelArg[INPUT_OUTPUT_IDX] = inputOutputInfo;
    kernelArg[TENSOR_DATA_IDX] = tensorData;
    RtExceptionInfo exceptionInfo = {};
    AdxExceptionDumpInfo dumpInfo = {};
    uint32_t dumpSize = 1;
    uint32_t realSize = 0;
    AdxExceptionDumpMode mode = AdxExceptionDumpMode::ADX_DUMP_MODE_NONE;
    exceptionInfo.expandInfo.type = RtExceptionExpandType::AICORE;
    exceptionInfo.expandInfo.u.aicoreInfo.exceptionArgs.argAddr = kernelArg.data();
    exceptionInfo.expandInfo.u.aicoreInfo.exceptionArgs.argsize = sizeof(void*) * MAX_AICPU_ARG_NUM;
    auto ret = ExceptionDumpCallBack(&exceptionInfo, &dumpInfo, dumpSize, &realSize, &mode);
    EXPECT_EQ(ret, 0);
    EXPECT_EQ(mode, AdxExceptionDumpMode::ADX_DUMP_MODE_OVERWRITE);
    EXPECT_EQ(realSize, 1);
    EXPECT_EQ(dumpInfo.extraTensorNum, 2);
}

TEST_F(DeviceExceptionDumpTest, TestAicoreExceptionWithZeroAddressTensor)
{
    char kernelName[] = "PyPTO_ZeroAddrKernel";
    int64_t inputOutputInfo[2] = {1, 0};
    DevTensorData tensorData[1] = {};
    tensorData[0].address = 0;
    tensorData[0].dataType = static_cast<int32_t>(DataType::DT_FP32);
    tensorData[0].shape.dim[0] = 16;
    tensorData[0].shape.dimSize = 1;
    std::vector<void*> kernelArg(MAX_AICPU_ARG_NUM, nullptr);
    kernelArg[KERNEL_NAME_IDX] = kernelName;
    kernelArg[INPUT_OUTPUT_IDX] = inputOutputInfo;
    kernelArg[TENSOR_DATA_IDX] = tensorData;
    RtExceptionInfo exceptionInfo = {};
    AdxExceptionDumpInfo dumpInfo = {};
    uint32_t dumpSize = 1;
    uint32_t realSize = 0;
    AdxExceptionDumpMode mode = AdxExceptionDumpMode::ADX_DUMP_MODE_NONE;
    exceptionInfo.expandInfo.type = RtExceptionExpandType::AICORE;
    exceptionInfo.expandInfo.u.aicoreInfo.exceptionArgs.argAddr = kernelArg.data();
    exceptionInfo.expandInfo.u.aicoreInfo.exceptionArgs.argsize = sizeof(void*) * MAX_AICPU_ARG_NUM;
    auto ret = ExceptionDumpCallBack(&exceptionInfo, &dumpInfo, dumpSize, &realSize, &mode);
    EXPECT_EQ(ret, 0);
    EXPECT_EQ(mode, AdxExceptionDumpMode::ADX_DUMP_MODE_OVERWRITE);
    EXPECT_EQ(realSize, 1);
    EXPECT_EQ(dumpInfo.extraTensorNum, 1);
}

TEST_F(DeviceExceptionDumpTest, TestAicoreExceptionWithMultipleTensors)
{
    char kernelName[] = "PyPTO_MultiTensorKernel";
    int64_t inputOutputInfo[2] = {3, 0};
    std::vector<uint8_t> tensorBuf0(64, 0xAB);
    std::vector<uint8_t> tensorBuf1(32, 0xCD);
    std::vector<uint8_t> tensorBuf2(16, 0xEF);
    DevTensorData tensorData[3] = {};
    tensorData[0].address = reinterpret_cast<uint64_t>(tensorBuf0.data());
    tensorData[0].dataType = static_cast<int32_t>(DataType::DT_FP32);
    tensorData[0].shape.dim[0] = 8;
    tensorData[0].shape.dim[1] = 8;
    tensorData[0].shape.dimSize = 2;
    tensorData[1].address = reinterpret_cast<uint64_t>(tensorBuf1.data());
    tensorData[1].dataType = static_cast<int32_t>(DataType::DT_FP32);
    tensorData[1].shape.dim[0] = 4;
    tensorData[1].shape.dim[1] = 8;
    tensorData[1].shape.dimSize = 2;
    tensorData[2].address = reinterpret_cast<uint64_t>(tensorBuf2.data());
    tensorData[2].dataType = static_cast<int32_t>(DataType::DT_FP32);
    tensorData[2].shape.dim[0] = 4;
    tensorData[2].shape.dim[1] = 4;
    tensorData[2].shape.dimSize = 2;
    std::vector<void*> kernelArg(MAX_AICPU_ARG_NUM, nullptr);
    kernelArg[KERNEL_NAME_IDX] = kernelName;
    kernelArg[INPUT_OUTPUT_IDX] = inputOutputInfo;
    kernelArg[TENSOR_DATA_IDX] = tensorData;
    RtExceptionInfo exceptionInfo = {};
    AdxExceptionDumpInfo dumpInfo = {};
    uint32_t dumpSize = 1;
    uint32_t realSize = 0;
    AdxExceptionDumpMode mode = AdxExceptionDumpMode::ADX_DUMP_MODE_NONE;
    exceptionInfo.expandInfo.type = RtExceptionExpandType::AICORE;
    exceptionInfo.expandInfo.u.aicoreInfo.exceptionArgs.argAddr = kernelArg.data();
    exceptionInfo.expandInfo.u.aicoreInfo.exceptionArgs.argsize = sizeof(void*) * MAX_AICPU_ARG_NUM;
    auto ret = ExceptionDumpCallBack(&exceptionInfo, &dumpInfo, dumpSize, &realSize, &mode);
    EXPECT_EQ(ret, 0);
    EXPECT_EQ(mode, AdxExceptionDumpMode::ADX_DUMP_MODE_OVERWRITE);
    EXPECT_EQ(realSize, 1);
    EXPECT_EQ(dumpInfo.extraTensorNum, 3);
}

TEST_F(DeviceExceptionDumpTest, TestAicoreExceptionWithOutputTensors)
{
    char kernelName[] = "PyPTO_OutputTensorKernel";
    int64_t inputOutputInfo[2] = {2, 1};
    std::vector<uint8_t> tensorBuf0(32, 0xAB);
    std::vector<uint8_t> tensorBuf1(16, 0xCD);
    DevTensorData tensorData[2] = {};
    tensorData[0].address = reinterpret_cast<uint64_t>(tensorBuf0.data());
    tensorData[0].dataType = static_cast<int32_t>(DataType::DT_FP32);
    tensorData[0].shape.dim[0] = 4;
    tensorData[0].shape.dim[1] = 8;
    tensorData[0].shape.dimSize = 2;
    tensorData[1].address = reinterpret_cast<uint64_t>(tensorBuf1.data());
    tensorData[1].dataType = static_cast<int32_t>(DataType::DT_FP32);
    tensorData[1].shape.dim[0] = 4;
    tensorData[1].shape.dim[1] = 4;
    tensorData[1].shape.dimSize = 2;
    std::vector<void*> kernelArg(MAX_AICPU_ARG_NUM, nullptr);
    kernelArg[KERNEL_NAME_IDX] = kernelName;
    kernelArg[INPUT_OUTPUT_IDX] = inputOutputInfo;
    kernelArg[TENSOR_DATA_IDX] = tensorData;
    RtExceptionInfo exceptionInfo = {};
    AdxExceptionDumpInfo dumpInfo = {};
    uint32_t dumpSize = 1;
    uint32_t realSize = 0;
    AdxExceptionDumpMode mode = AdxExceptionDumpMode::ADX_DUMP_MODE_NONE;
    exceptionInfo.expandInfo.type = RtExceptionExpandType::AICORE;
    exceptionInfo.expandInfo.u.aicoreInfo.exceptionArgs.argAddr = kernelArg.data();
    exceptionInfo.expandInfo.u.aicoreInfo.exceptionArgs.argsize = sizeof(void*) * MAX_AICPU_ARG_NUM;
    auto ret = ExceptionDumpCallBack(&exceptionInfo, &dumpInfo, dumpSize, &realSize, &mode);
    EXPECT_EQ(ret, 0);
    EXPECT_EQ(mode, AdxExceptionDumpMode::ADX_DUMP_MODE_OVERWRITE);
    EXPECT_EQ(realSize, 1);
    EXPECT_EQ(dumpInfo.extraTensorNum, 1);
}

TEST_F(DeviceExceptionDumpTest, TestNonAicoreExceptionTypeFFTSPlus)
{
    RtExceptionInfo exceptionInfo = {};
    AdxExceptionDumpInfo dumpInfo = {};
    uint32_t dumpSize = 1;
    uint32_t realSize = 0;
    AdxExceptionDumpMode mode = AdxExceptionDumpMode::ADX_DUMP_MODE_NONE;
    exceptionInfo.expandInfo.type = RtExceptionExpandType::FFTS_PLUS;
    auto ret = ExceptionDumpCallBack(&exceptionInfo, &dumpInfo, dumpSize, &realSize, &mode);
    EXPECT_EQ(ret, 0);
    EXPECT_EQ(mode, AdxExceptionDumpMode::ADX_DUMP_MODE_OVERWRITE);
    EXPECT_EQ(realSize, 1);
}