-- Copyright (c) 2026 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.

local lu = require('luaunit')

local common_def = require('common_def')
local array_collection = require('array.array_collection')
local volume_dump = require('volume.volume_dump')

TestVolumeDump = {}

local function make_fp()
    return {
        buf = {},
        write = function(self, s)
            table.insert(self.buf, s)
        end,
    }
end

local function buf_to_string(fp)
    return table.concat(fp.buf)
end

function TestVolumeDump:setUp()
    self._orig_get_array = array_collection.get_instance().get_ctrl_array_by_id
end

function TestVolumeDump:tearDown()
    if self._orig_get_array then
        array_collection.get_instance().get_ctrl_array_by_id = self._orig_get_array
    end
end

function TestVolumeDump:test_dump_info_basic_and_policy()
    local fp = make_fp()
    local vol = {
        VolumeId = 0,
        VolumeName = 'LD0',
        RAIDType = common_def.RAID_LEVEL_0,
        State = 0,
        DefaultReadPolicy = 'ReadAhead',
        DefaultWritePolicy = 'WriteBack',
        DefaultCachePolicy = 'CachedIO',
        CurrentReadPolicy = 'NoReadAhead',
        CurrentWritePolicy = 'WriteThrough',
        CurrentCachePolicy = 'DirectIO',
        AccessPolicy = 'ReadWrite',
        SpanCount = 1,
        NumDrivePerSpan = 2,
        OptimumIOSizeBytes = 64 * 1024,
        CapacityBytes = 10 * 1024 * 1024 * 1024,
        DriveCachePolicy = 0,
        InitializationMode = 0,
        BGIEnable = 1,
        CurrentForegroundInitState = 0,
        ForegroundInitProgress = 50,
        ConsistencyCheck = 1,
        BootEnable = 1,
        BootPriority = 0,
        SSDCachingEnable = 0,
        RefDriveList = { 'Disk0', 'Disk1' },
        HotSpareDriveList = {},
        AccelerationMethod = 'ControllerCache',
        CacheLineSizeKiB = 1,
        RebuildState = 1,
        RebuildProgress = 30,
    }

    volume_dump.get_instance():dump_info(fp, vol)
    local out = buf_to_string(fp)
    lu.assertStrContains(out, 'LD0')
    lu.assertStrContains(out, 'Read Ahead')
    lu.assertStrContains(out, 'Write Back')
    lu.assertStrContains(out, 'Cached IO')
    lu.assertStrContains(out, 'Strip Size')
    lu.assertStrContains(out, 'Rebuild in Progress')
end

function TestVolumeDump:test_dump_multi_span_and_spare_and_cacheline()
    local fp = make_fp()
    array_collection.get_instance().get_ctrl_array_by_id = function(_, ctrl_id, array_id)
        if array_id == 0x8001 then
            return { RefDrives = { 'Disk1', 'Disk3' } }
        else
            return { RefDrives = { 'Disk2' } }
        end
    end

    local vol = {
        VolumeId = 1,
        VolumeName = 'LD1',
        RAIDType = common_def.RAID_LEVEL_10,
        State = 0,
        DefaultReadPolicy = 'ReadAheadAdaptive',
        DefaultWritePolicy = 'WriteBackWithBBU',
        DefaultCachePolicy = 'CachedIO',
        CurrentReadPolicy = 'ReadAhead',
        CurrentWritePolicy = 'WriteBack',
        CurrentCachePolicy = 'CachedIO',
        AccessPolicy = 'ReadOnly',
        SpanCount = 2,
        NumDrivePerSpan = 2,
        OptimumIOSizeBytes = 0,
        CapacityBytes = common_def.INVALID_U32,
        DriveCachePolicy = 0,
        InitializationMode = 0,
        BGIEnable = 0,
        CurrentForegroundInitState = 255,
        ForegroundInitProgress = 255,
        ConsistencyCheck = 0,
        BootEnable = 0,
        BootPriority = 0,
        SSDCachingEnable = 1,
        RefDriveList = {},
        RefDiskArrayList = { 0x8001 },
        HotSpareDriveList = { 'Disk4' },
        AccelerationMethod = 'UnknownType',
        CacheLineSizeKiB = 4,
        RebuildState = 255,
        RebuildProgress = 255,
    }

    volume_dump.get_instance():dump_info(fp, vol)
    local out = buf_to_string(fp)
    lu.assertStrContains(out, 'Dedicated Hot Spare PD')
    lu.assertStrContains(out, 'Cache Line Size')
    lu.assertStrContains(out, '256 KB')
    lu.assertStrContains(out, 'Acceleration Method')
end

function TestVolumeDump:test_trans_stripsize_all_branches()
    local fp = make_fp()
    local vol = {
        VolumeId = 0,
        VolumeName = 'LD',
        RAIDType = common_def.RAID_LEVEL_0,
        State = 0,
        DefaultReadPolicy = 'NoReadAhead',
        DefaultWritePolicy = 'WriteThrough',
        DefaultCachePolicy = 'CachedIO',
        CurrentReadPolicy = 'NoReadAhead',
        CurrentWritePolicy = 'WriteThrough',
        CurrentCachePolicy = 'CachedIO',
        AccessPolicy = 'ReadWrite',
        SpanCount = 1,
        NumDrivePerSpan = common_def.INVALID_U8,
        OptimumIOSizeBytes = 512,
        CapacityBytes = 100 * 1024 * 1024,
        DriveCachePolicy = 0,
        InitializationMode = 0,
        BGIEnable = 1,
        CurrentForegroundInitState = 1,
        ForegroundInitProgress = 50,
        ConsistencyCheck = 1,
        BootEnable = 1,
        BootPriority = 0,
        SSDCachingEnable = 1,
        RefDriveList = {},
        HotSpareDriveList = {},
        AccelerationMethod = 'None',
        CacheLineSizeKiB = 0,
        RebuildState = 0,
        RebuildProgress = 0,
    }
    volume_dump.get_instance():dump_info(fp, vol)
    local out = buf_to_string(fp)
    lu.assertStrContains(out, '512 B')
    lu.assertStrContains(out, 'N/A')
    lu.assertStrContains(out, 'Span depth')
    lu.assertStrContains(out, 'Rebuild in Progress')
end

function TestVolumeDump:test_trans_stripsize_mb_format()
    local fp = make_fp()
    local vol = {
        VolumeId = 0,
        VolumeName = 'LD',
        RAIDType = common_def.RAID_LEVEL_0,
        State = 0,
        DefaultReadPolicy = 'NoReadAhead',
        DefaultWritePolicy = 'WriteThrough',
        DefaultCachePolicy = 'CachedIO',
        CurrentReadPolicy = 'NoReadAhead',
        CurrentWritePolicy = 'WriteThrough',
        CurrentCachePolicy = 'CachedIO',
        AccessPolicy = 'ReadWrite',
        SpanCount = 1,
        NumDrivePerSpan = 2,
        OptimumIOSizeBytes = 1048576,
        CapacityBytes = 2048 * 1024 * 1024,
        DriveCachePolicy = 0,
        InitializationMode = 0,
        BGIEnable = 1,
        CurrentForegroundInitState = 1,
        ForegroundInitProgress = 50,
        ConsistencyCheck = 1,
        BootEnable = 1,
        BootPriority = 0,
        SSDCachingEnable = 0,
        RefDriveList = {},
        HotSpareDriveList = {},
        AccelerationMethod = 'IOBypass',
        CacheLineSizeKiB = 99,
        RebuildState = 2,
        RebuildProgress = 0,
    }
    volume_dump.get_instance():dump_info(fp, vol)
    local out = buf_to_string(fp)
    lu.assertStrContains(out, 'MB')
    lu.assertStrContains(out, '2.000 GB')
    lu.assertStrContains(out, 'Unknown')
    lu.assertStrContains(out, 'Rebuild in Progress')
    lu.assertStrContains(out, 'Unknown')
end

function TestVolumeDump:test_trans_capacity_tb_and_pb()
    local fp = make_fp()
    -- TB range: capacity_size_mb >= 1024*1024 and < 1024^3
    local vol = {
        VolumeId = 0,
        VolumeName = 'LD',
        RAIDType = common_def.RAID_LEVEL_0,
        State = 0,
        DefaultReadPolicy = 'NoReadAhead',
        DefaultWritePolicy = 'WriteThrough',
        DefaultCachePolicy = 'CachedIO',
        CurrentReadPolicy = 'NoReadAhead',
        CurrentWritePolicy = 'WriteThrough',
        CurrentCachePolicy = 'CachedIO',
        AccessPolicy = 'ReadWrite',
        SpanCount = 1,
        NumDrivePerSpan = 2,
        OptimumIOSizeBytes = 65536,
        CapacityBytes = 2 * 1024 * 1024 * 1024 * 1024,
        DriveCachePolicy = 0,
        InitializationMode = 0,
        BGIEnable = 1,
        CurrentForegroundInitState = 1,
        ForegroundInitProgress = 50,
        ConsistencyCheck = 1,
        BootEnable = 1,
        BootPriority = 0,
        SSDCachingEnable = 0,
        RefDriveList = {},
        HotSpareDriveList = {},
        AccelerationMethod = 'maxCache',
        CacheLineSizeKiB = 4,
        RebuildState = 1,
        RebuildProgress = 255,
    }
    volume_dump.get_instance():dump_info(fp, vol)
    local out = buf_to_string(fp)
    lu.assertStrContains(out, 'TB')
    lu.assertStrContains(out, 'Yes (Unknown Progress)')
end

function TestVolumeDump:test_trans_capacity_pb_range()
    local fp = make_fp()
    -- PB range: capacity_size_mb >= 1024^3
    local vol = {
        VolumeId = 0,
        VolumeName = 'LD',
        RAIDType = common_def.RAID_LEVEL_0,
        State = 0,
        DefaultReadPolicy = 'NoReadAhead',
        DefaultWritePolicy = 'WriteThrough',
        DefaultCachePolicy = 'CachedIO',
        CurrentReadPolicy = 'NoReadAhead',
        CurrentWritePolicy = 'WriteThrough',
        CurrentCachePolicy = 'CachedIO',
        AccessPolicy = 'ReadWrite',
        SpanCount = 1,
        NumDrivePerSpan = 2,
        OptimumIOSizeBytes = 65536,
        CapacityBytes = 2 * 1024 * 1024 * 1024 * 1024 * 1024,
        DriveCachePolicy = 0,
        InitializationMode = 0,
        BGIEnable = 1,
        CurrentForegroundInitState = 1,
        ForegroundInitProgress = 50,
        ConsistencyCheck = 1,
        BootEnable = 1,
        BootPriority = 0,
        SSDCachingEnable = 0,
        RefDriveList = {},
        HotSpareDriveList = {},
        AccelerationMethod = 'None',
        CacheLineSizeKiB = 4,
        RebuildState = 0,
        RebuildProgress = 0,
    }
    volume_dump.get_instance():dump_info(fp, vol)
    local out = buf_to_string(fp)
    lu.assertStrContains(out, 'PB')
end

function TestVolumeDump:test_trans_capacity_invalid_u32()
    local fp = make_fp()
    -- capacity_size_byte / 1024 / 1024 == INVALID_U32
    local vol = {
        VolumeId = 0,
        VolumeName = 'LD',
        RAIDType = common_def.RAID_LEVEL_0,
        State = 0,
        DefaultReadPolicy = 'NoReadAhead',
        DefaultWritePolicy = 'WriteThrough',
        DefaultCachePolicy = 'CachedIO',
        CurrentReadPolicy = 'NoReadAhead',
        CurrentWritePolicy = 'WriteThrough',
        CurrentCachePolicy = 'CachedIO',
        AccessPolicy = 'ReadWrite',
        SpanCount = 1,
        NumDrivePerSpan = 2,
        OptimumIOSizeBytes = 65536,
        CapacityBytes = common_def.INVALID_U32 * 1024 * 1024,
        DriveCachePolicy = 0,
        InitializationMode = 0,
        BGIEnable = 1,
        CurrentForegroundInitState = 1,
        ForegroundInitProgress = 50,
        ConsistencyCheck = 1,
        BootEnable = 1,
        BootPriority = 0,
        SSDCachingEnable = 0,
        RefDriveList = {},
        HotSpareDriveList = {},
        AccelerationMethod = 'None',
        CacheLineSizeKiB = 4,
        RebuildState = 0,
        RebuildProgress = 0,
    }
    volume_dump.get_instance():dump_info(fp, vol)
    local out = buf_to_string(fp)
    lu.assertStrContains(out, 'Total Size')
end

function TestVolumeDump:test_dump_multi_span_empty_refdiskarray()
    local fp = make_fp()
    local vol = {
        VolumeId = 0,
        VolumeName = 'LD',
        RAIDType = common_def.RAID_LEVEL_10,
        State = 0,
        DefaultReadPolicy = 'NoReadAhead',
        DefaultWritePolicy = 'WriteThrough',
        DefaultCachePolicy = 'CachedIO',
        CurrentReadPolicy = 'NoReadAhead',
        CurrentWritePolicy = 'WriteThrough',
        CurrentCachePolicy = 'CachedIO',
        AccessPolicy = 'ReadWrite',
        SpanCount = 2,
        NumDrivePerSpan = 2,
        OptimumIOSizeBytes = 0,
        CapacityBytes = common_def.INVALID_U32,
        DriveCachePolicy = 0,
        InitializationMode = 0,
        BGIEnable = 0,
        CurrentForegroundInitState = 0,
        ForegroundInitProgress = 50,
        ConsistencyCheck = 0,
        BootEnable = 0,
        BootPriority = 0,
        SSDCachingEnable = 0,
        RefDriveList = {},
        RefDiskArrayList = {},
        HotSpareDriveList = {},
        AccelerationMethod = 'None',
        CacheLineSizeKiB = 4,
        RebuildState = 255,
        RebuildProgress = 255,
    }
    volume_dump.get_instance():dump_info(fp, vol)
    local out = buf_to_string(fp)
    lu.assertStrContains(out, 'PD participating in LD (ID#)')
    lu.assertStrContains(out, 'N/A')
end