local drive_collection = require('drive.drive_collection')
local c_drive = require('drive.drive_object')
local hook_orm_tasks = require('hook_orm_tasks')
local lu = require('luaunit')
TestDriveCollection = {}
function TestDriveCollection:test_init()
local drive_collection_obj = drive_collection.new()
local ret = pcall(drive_collection_obj.init, drive_collection_obj)
lu.assertEquals(ret, true)
end
local function stub_collection_fold(drives)
local orig = c_drive.collection
c_drive.collection = {
fold = function(_, cb, acc)
for key, obj in pairs(drives) do
acc = cb(acc, obj, key)
end
return acc
end,
}
return orig
end
function TestDriveCollection:test_get_drives_by_controller_id_empty()
local orig = stub_collection_fold({})
local dc = drive_collection.new()
local result = dc:get_drives_by_controller_id(0)
lu.assertEquals(result, {})
c_drive.collection = orig
end
function TestDriveCollection:test_get_drives_by_controller_id_filters_correctly()
local drives = {
{ Name = 'Disk0', RefControllerId = 0, identify_pd = true, presence = 1 },
{ Name = 'Disk1', RefControllerId = 1, identify_pd = true, presence = 1 },
{ Name = 'Disk2', RefControllerId = 0, identify_pd = false, presence = 1 },
{ Name = 'Disk3', RefControllerId = 0, identify_pd = true, presence = 0 },
{ Name = 'Disk4', RefControllerId = 0, identify_pd = true, presence = 1 },
}
local orig = stub_collection_fold(drives)
local dc = drive_collection.new()
local result = dc:get_drives_by_controller_id(0)
table.sort(result)
lu.assertEquals(result, { 'Disk0', 'Disk4' })
c_drive.collection = orig
end
function TestDriveCollection:test_get_drives_by_controller_id_identify_pd_nil_included()
local drives = {
{ Name = 'Disk0', RefControllerId = 0, presence = 1 },
}
local orig = stub_collection_fold(drives)
local dc = drive_collection.new()
local result = dc:get_drives_by_controller_id(0)
lu.assertEquals(result, { 'Disk0' })
c_drive.collection = orig
end
function TestDriveCollection:test_get_drives_by_controller_id_no_match()
local drives = {
{ Name = 'Disk0', RefControllerId = 1, identify_pd = true, presence = 1 },
{ Name = 'Disk1', RefControllerId = 2, identify_pd = true, presence = 1 },
}
local orig = stub_collection_fold(drives)
local dc = drive_collection.new()
local result = dc:get_drives_by_controller_id(0)
lu.assertEquals(result, {})
c_drive.collection = orig
end
function TestDriveCollection:test_get_drives_by_controller_id_returns_independent_tables()
local drives = {
{ Name = 'Disk0', RefControllerId = 0, identify_pd = true, presence = 1 },
}
local orig = stub_collection_fold(drives)
local dc = drive_collection.new()
local r1 = dc:get_drives_by_controller_id(0)
local r2 = dc:get_drives_by_controller_id(0)
lu.assertNotIs(r1, r2)
lu.assertEquals(r1, r2)
c_drive.collection = orig
end
function TestDriveCollection:test_get_drive_objs_by_controller_id_filters_and_returns_objects()
local drives = {
{ Name = 'Disk0', RefControllerId = 0, identify_pd = true, presence = 1 },
{ Name = 'Disk1', RefControllerId = 1, identify_pd = true, presence = 1 },
{ Name = 'Disk2', RefControllerId = 0, identify_pd = false, presence = 1 },
{ Name = 'Disk3', RefControllerId = 0, identify_pd = true, presence = 0 },
{ Name = 'Disk4', RefControllerId = 0, identify_pd = true, presence = 1 },
}
local orig = stub_collection_fold(drives)
local dc = drive_collection.new()
local result = dc:get_drive_objs_by_controller_id(0)
table.sort(result, function(a, b)
return a.Name < b.Name
end)
lu.assertEquals(#result, 2)
lu.assertEquals(result[1].Name, 'Disk0')
lu.assertEquals(result[2].Name, 'Disk4')
c_drive.collection = orig
end
function TestDriveCollection:test_get_drive_objs_by_controller_id_invalid_controller_id_returns_empty()
local drives = {
{ Name = 'Disk0', RefControllerId = 0, identify_pd = true, presence = 1 },
}
local orig = stub_collection_fold(drives)
local dc = drive_collection.new()
lu.assertEquals(dc:get_drive_objs_by_controller_id(nil), {})
lu.assertEquals(dc:get_drive_objs_by_controller_id('0'), {})
c_drive.collection = orig
end
local function mock_fp_w()
local lines = {}
return {
write = function(_, s)
lines[#lines + 1] = s
end,
content = function(self)
return table.concat(lines)
end,
}
end
local function make_physical_drive_stub(attrs)
return {
Id = attrs.Id,
Presence = attrs.Presence,
RefControllerId = attrs.RefControllerId,
dump_physical_drive = function(self, fp_w)
fp_w:write(string.format('DUMP:%d\n', self.Id))
end,
}
end
function TestDriveCollection:test_dump_controller_physical_drive_not_found()
local orig = stub_collection_fold({})
local dc = drive_collection.new()
local fp_w = mock_fp_w()
dc:dump_controller_physical_drive(0, fp_w)
lu.assertEquals(fp_w:content(), 'Not Found Physical Drives.\n')
c_drive.collection = orig
end
function TestDriveCollection:test_dump_controller_physical_drive_none_in_position()
local drives = {
make_physical_drive_stub({ Id = 1, Presence = 0, RefControllerId = 0 }),
make_physical_drive_stub({ Id = 2, Presence = 1, RefControllerId = 1 }),
}
local orig = stub_collection_fold(drives)
local dc = drive_collection.new()
local fp_w = mock_fp_w()
dc:dump_controller_physical_drive(0, fp_w)
lu.assertEquals(fp_w:content(), 'None of physical drives is in position.\n')
c_drive.collection = orig
end
function TestDriveCollection:test_dump_controller_physical_drive_sorted_by_id()
local drives = {
make_physical_drive_stub({ Id = 3, Presence = 1, RefControllerId = 0 }),
make_physical_drive_stub({ Id = 1, Presence = 1, RefControllerId = 0 }),
make_physical_drive_stub({ Id = 2, Presence = 1, RefControllerId = 0 }),
make_physical_drive_stub({ Id = 4, Presence = 1, RefControllerId = 1 }),
make_physical_drive_stub({ Id = 5, Presence = 0, RefControllerId = 0 }),
}
local orig = stub_collection_fold(drives)
local dc = drive_collection.new()
local fp_w = mock_fp_w()
dc:dump_controller_physical_drive(0, fp_w)
lu.assertEquals(fp_w:content(), 'DUMP:1\nDUMP:2\nDUMP:3\n')
c_drive.collection = orig
end
function TestDriveCollection:test_dump_controller_physical_drive_single_drive()
local drives = {
make_physical_drive_stub({ Id = 7, Presence = 1, RefControllerId = 255 }),
}
local orig = stub_collection_fold(drives)
local dc = drive_collection.new()
local fp_w = mock_fp_w()
dc:dump_controller_physical_drive(255, fp_w)
lu.assertEquals(fp_w:content(), 'DUMP:7\n')
c_drive.collection = orig
end
local function with_stub_collection_find(drives, fn)
local orig = c_drive.collection
c_drive.collection = {
find = function(_, filter)
return drives[filter.Id]
end,
fold = function(_, func, acc)
for _, drive in pairs(drives) do
func(acc, drive, nil)
end
return acc
end,
}
local ok, err = pcall(fn)
c_drive.collection = orig
if not ok then
error(err)
end
end
function TestDriveCollection:test_get_manufacturer_found()
local drives = {
[1] = { Id = 1, Name = 'Disk1', Manufacturer = 'SSSTC' },
[2] = { Id = 2, Name = 'Disk2', Manufacturer = 'Intel' },
}
with_stub_collection_find(drives, function()
local dc = drive_collection.new()
lu.assertEquals(dc:get_manufacturer(1), 'SSSTC')
lu.assertEquals(dc:get_manufacturer(2), 'Intel')
end)
end
function TestDriveCollection:test_get_manufacturer_noncontiguous_id_and_fallback()
local drives = {
[1] = { Id = 1, Name = 'Disk1', Manufacturer = 'SSSTC' },
[2] = { Id = 2, Name = 'Disk2', Manufacturer = 'Intel' },
[8] = { Id = 8, Name = 'Disk8', Manufacturer = 'Toshiba' },
[9] = { Id = 9, Name = 'Disk9', Manufacturer = 'WD' },
}
with_stub_collection_find(drives, function()
local dc = drive_collection.new()
lu.assertEquals(dc:get_manufacturer(3), 'Toshiba')
lu.assertEquals(dc:get_manufacturer(9), 'WD')
end)
end
function TestDriveCollection:test_get_manufacturer_drive_not_found()
local drives = {
[2] = { Id = 2, Name = 'Disk2', Manufacturer = 'Intel' },
}
with_stub_collection_find(drives, function()
local dc = drive_collection.new()
lu.assertEquals(dc:get_manufacturer(2), 'Intel')
local mfr, err_code = dc:get_manufacturer(3)
lu.assertEquals(mfr, nil)
lu.assertEquals(err_code, 'not_found')
end)
end
function TestDriveCollection:test_get_manufacturer_manufacturer_nil()
local drives = {
[1] = { Id = 1, Name = 'Disk1' },
}
with_stub_collection_find(drives, function()
local dc = drive_collection.new()
lu.assertEquals(dc:get_manufacturer(1), '')
end)
end
function TestDriveCollection:test_get_manufacturer_drive_absent()
local drives = {
[1] = { Id = 1, Name = 'Disk1', Manufacturer = 'SSSTC', Presence = 0 },
}
with_stub_collection_find(drives, function()
local dc = drive_collection.new()
local mfr, err_code = dc:get_manufacturer(1)
lu.assertEquals(mfr, nil)
lu.assertEquals(err_code, 'absent')
end)
end
local function make_diag_drive(name, presence)
local d = {
Name = name,
Presence = presence or 1,
diagnosed = 0,
}
d.diagnose_continue = function(self)
return self.Presence == 1
end
d.diagnose_task = function(self)
self.diagnosed = self.diagnosed + 1
end
return d
end
local function fresh_dc()
local dc = drive_collection.new()
dc.diag_queue = {}
return dc
end
local function with_hooked_diag_task(dc, fn)
pcall(hook_orm_tasks.unhook)
local my_tasks = hook_orm_tasks.hook()
dc:start_update_pd_info_task()
local ok, err = pcall(fn, my_tasks)
hook_orm_tasks.unhook()
if not ok then
error(err)
end
end
function TestDriveCollection:test_enqueue_fault_drive_fifo_and_dedup()
local dc = fresh_dc()
local d1 = make_diag_drive('Disk1')
dc:enqueue_fault_drive(d1)
dc:enqueue_fault_drive(d1)
dc:enqueue_fault_drive(make_diag_drive('Disk2'))
lu.assertEquals(#dc.diag_queue, 2)
lu.assertEquals(dc.diag_queue[1].Name, 'Disk1')
lu.assertEquals(dc.diag_queue[2].Name, 'Disk2')
end
function TestDriveCollection:test_enqueue_fault_drive_nil_ignored()
local dc = fresh_dc()
dc:enqueue_fault_drive(nil)
dc:enqueue_fault_drive({})
lu.assertEquals(#dc.diag_queue, 0)
end
function TestDriveCollection:test_reset_diag_queue()
local dc = fresh_dc()
dc:enqueue_fault_drive(make_diag_drive('Disk1'))
dc:reset_diag_queue()
lu.assertEquals(#dc.diag_queue, 0)
dc:enqueue_fault_drive(make_diag_drive('Disk2'))
lu.assertEquals(#dc.diag_queue, 1)
dc:reset_diag_queue()
lu.assertEquals(#dc.diag_queue, 0)
end
function TestDriveCollection:test_diag_task_serial_fifo()
local dc = fresh_dc()
local order = {}
local d1 = make_diag_drive('Disk1')
local d2 = make_diag_drive('Disk2')
d1.diagnose_task = function()
table.insert(order, 'Disk1')
end
d2.diagnose_task = function()
table.insert(order, 'Disk2')
end
dc:enqueue_fault_drive(d1)
dc:enqueue_fault_drive(d2)
with_hooked_diag_task(dc, function(my_tasks)
my_tasks:run_task('drive diagnose task')
lu.assertEquals(order, { 'Disk1' })
my_tasks:run_task('drive diagnose task')
lu.assertEquals(order, { 'Disk1', 'Disk2' })
lu.assertEquals(#dc.diag_queue, 0)
end)
end
function TestDriveCollection:test_diag_task_skip_not_present()
local dc = fresh_dc()
local d1 = make_diag_drive('Disk1', 0)
dc:enqueue_fault_drive(d1)
with_hooked_diag_task(dc, function(my_tasks)
my_tasks:run_task('drive diagnose task')
lu.assertEquals(d1.diagnosed, 0)
lu.assertEquals(#dc.diag_queue, 0)
end)
end
function TestDriveCollection:test_diag_task_error_not_blocking_next()
local dc = fresh_dc()
local d1 = make_diag_drive('Disk1')
d1.diagnose_task = function()
error('diag fail')
end
local d2 = make_diag_drive('Disk2')
dc:enqueue_fault_drive(d1)
dc:enqueue_fault_drive(d2)
with_hooked_diag_task(dc, function(my_tasks)
my_tasks:run_task('drive diagnose task')
my_tasks:run_task('drive diagnose task')
lu.assertEquals(d2.diagnosed, 1)
end)
end
function TestDriveCollection:test_smbios_status_changed_clears_diag_queue()
local orig = stub_collection_fold({})
local dc = fresh_dc()
dc:process_smbios_status_changed()
dc:enqueue_fault_drive(make_diag_drive('Disk1'))
dc.on_smbios_status_changed:emit(false)
lu.assertEquals(#dc.diag_queue, 0)
dc:enqueue_fault_drive(make_diag_drive('Disk2'))
dc.on_smbios_status_changed:emit(true)
lu.assertEquals(#dc.diag_queue, 0)
c_drive.collection = orig
end