blob: 4fb588f532b7a607d1a8937f75d4fac8d0ef7999 [file] [log] [blame]
#!/usr/bin/python3
# SPDX-License-Identifier: MIT
"""
This module contains unit tests for the validator functions in the amd-debug-tools package.
"""
from unittest.mock import patch, mock_open, Mock
import os
import logging
import unittest
import math
from datetime import datetime
from amd_debug.validator import pm_debugging, soc_needs_irq1_wa, SleepValidator
class TestValidatorHelpers(unittest.TestCase):
"""Test validator Helper functions"""
@classmethod
def setUpClass(cls):
logging.basicConfig(filename="/dev/null", level=logging.DEBUG)
def test_soc_needs_irq1_wa(self):
"""Test if the SOC should apply an IRQ1 workaround"""
ret = soc_needs_irq1_wa(0x17, 0x68, "1.2.3")
self.assertTrue(ret)
ret = soc_needs_irq1_wa(0x17, 0x69, "1.2.3")
self.assertFalse(ret)
ret = soc_needs_irq1_wa(0x19, 0x51, "64.65.0")
self.assertFalse(ret)
ret = soc_needs_irq1_wa(0x19, 0x50, "64.65.0")
self.assertTrue(ret)
ret = soc_needs_irq1_wa(0x19, 0x50, "64.66.0")
self.assertFalse(ret)
def test_pm_debugging(self):
"""Test pm_debugging decorator"""
@pm_debugging
def test_function():
return "Test function executed"
# Mock /sys/power/pm_debug_messages existing and all ACPI existing
with patch("builtins.open", new_callable=mock_open, read_data="0") as mock_file:
handlers = (
mock_file.return_value,
mock_open(read_data="0").return_value,
mock_open(read_data="0").return_value,
mock_open(read_data="0").return_value,
)
mock_open.side_effect = handlers
result = test_function()
self.assertEqual(result, "Test function executed")
# Mock /sys/power/pm_debug_messages missing
with patch(
"builtins.open", side_effect=FileNotFoundError("not found")
) as mock_file:
with self.assertRaises(FileNotFoundError):
result = test_function()
class TestValidator(unittest.TestCase):
"""Test validator functions"""
@classmethod
def setUpClass(cls):
logging.basicConfig(filename="/dev/null", level=logging.DEBUG)
@patch("amd_debug.validator.SleepDatabase")
def setUp(self, _db_mock):
"""Set up a mock context for testing"""
self.validator = SleepValidator(tool_debug=True, bios_debug=False)
def test_capture_running_compositors(self):
"""Test capture_running_compositors method"""
with patch("glob.glob", return_value=["/proc/1234", "/proc/5678"]), patch(
"os.path.exists", return_value=True
), patch(
"os.readlink", side_effect=["/usr/bin/kwin_wayland", "/usr/bin/gnome-shell"]
), patch.object(
self.validator.db, "record_debug"
) as mock_record_debug:
self.validator.capture_running_compositors()
mock_record_debug.assert_any_call("kwin_wayland compositor is running")
mock_record_debug.assert_any_call("gnome-shell compositor is running")
def test_capture_power_profile(self):
"""Test capture_power_profile method"""
with patch("os.path.exists", return_value=True), patch(
"subprocess.check_output",
return_value=b"Performance\nBalanced\nPower Saver",
), patch.object(self.validator.db, "record_debug") as mock_record_debug:
self.validator.capture_power_profile()
mock_record_debug.assert_any_call("Power Profiles:")
mock_record_debug.assert_any_call("│ Performance")
mock_record_debug.assert_any_call("│ Balanced")
mock_record_debug.assert_any_call("└─Power Saver")
def test_capture_battery(self):
"""Test capture_battery method"""
with patch.object(
self.validator.batteries, "get_batteries", return_value=["BAT0"]
), patch.object(
self.validator.batteries, "get_energy_unit", return_value="µWh"
), patch.object(
self.validator.batteries, "get_energy", return_value=50000
), patch.object(
self.validator.batteries, "get_energy_full", return_value=60000
), patch.object(
self.validator.db, "record_debug"
) as mock_record_debug, patch.object(
self.validator.db, "record_battery_energy"
) as mock_record_battery_energy:
self.validator.capture_battery()
mock_record_debug.assert_called_with("BAT0 energy level is 50000 µWh")
mock_record_battery_energy.assert_called_with("BAT0", 50000, 60000, "W")
def test_check_rtc_cmos(self):
"""Test check_rtc_cmos method"""
with patch(
"os.path.join",
return_value="/sys/module/rtc_cmos/parameters/use_acpi_alarm",
), patch("builtins.open", mock_open(read_data="N")), patch.object(
self.validator.db, "record_cycle_data"
) as mock_record_cycle_data:
self.validator.check_rtc_cmos()
mock_record_cycle_data.assert_called_with(
"`rtc_cmos` not configured to use ACPI alarm", "🚦"
)
def test_capture_wake_sources(self):
"""Test capture_wake_sources method"""
mock_pyudev = patch.object(self.validator, "pyudev").start()
mock_record_debug = patch.object(self.validator.db, "record_debug").start()
mock_read_file = patch("amd_debug.validator.read_file").start()
mock_os_path_exists = patch("os.path.exists").start()
# Mock wakeup devices
mock_wakeup_device = mock_pyudev.list_devices.return_value = [
unittest.mock.Mock(
sys_path="/sys/devices/pci0000:00/0000:00:14.0",
find_parent=lambda subsystem, **kwargs: None,
)
]
# Mock wakeup file existence and content
mock_os_path_exists.return_value = True
mock_read_file.return_value = "enabled"
# Mock device properties
mock_wakeup_device[0].properties = {"PCI_CLASS": "0x0c0330"}
mock_wakeup_device[0].sys_path = "/sys/devices/pci0000:00/0000:00:14.0"
self.validator.capture_wake_sources()
# Validate debug messages
mock_record_debug.assert_any_call(
"Wakeup Source|Linux Device|Status\n|/sys/devices/pci0000:00/0000:00:14.0|enabled\n"
)
# Stop patches
patch.stopall()
def test_capture_lid(self):
"""Test capture_lid method"""
with patch("os.walk", return_value=[("/", [], ["lid0", "lid1"])]), patch(
"os.path.join", side_effect=lambda *args: "/".join(args)
), patch(
"amd_debug.validator.read_file",
side_effect=["state: open", "state: closed"],
), patch.object(
self.validator.db, "record_debug"
) as mock_record_debug:
self.validator.capture_lid()
mock_record_debug.assert_any_call("ACPI Lid (//lid0): open")
mock_record_debug.assert_any_call("ACPI Lid (//lid1): closed")
def test_capture_wakeup_irq_data(self):
"""Test capture_wakeup_irq_data method"""
with patch("os.path.join", side_effect=lambda *args: "/".join(args)), patch(
"amd_debug.validator.read_file",
side_effect=[
"123", # IRQ number
"chip_name_mock", # Chip name
"irq_name_mock", # IRQ name
"hw_mock", # Hardware IRQ
"actions_mock", # Actions
],
), patch.object(self.validator.db, "record_debug") as mock_record_debug:
result = self.validator.capture_wakeup_irq_data()
self.assertTrue(result)
mock_record_debug.assert_called_once_with(
"Woke up from IRQ 123 (chip_name_mock hw_mock-irq_name_mock actions_mock)"
)
def test_capture_thermal(self):
"""Test capture_thermal method"""
# Mock pyudev devices
mock_pyudev = patch.object(self.validator, "pyudev").start()
mock_record_debug = patch.object(self.validator.db, "record_debug").start()
mock_record_prereq = patch.object(self.validator.db, "record_prereq").start()
mock_read_file = patch("amd_debug.validator.read_file").start()
mock_os_listdir = patch("os.listdir").start()
# Mock thermal devices
mock_device = unittest.mock.Mock()
mock_device.device_path = "/devices/LNXTHERM:00"
mock_device.sys_path = "/sys/devices/LNXTHERM:00"
mock_pyudev.list_devices.return_value = [mock_device]
# Mock thermal zone files
mock_read_file.side_effect = [
"45000", # Current temperature in millidegrees
"critical", # Trip point 0 type
"50000", # Trip point 0 temperature in millidegrees
]
mock_os_listdir.return_value = ["trip_point_0_type", "trip_point_0_temp"]
# Call the method
result = self.validator.capture_thermal()
# Validate debug messages
mock_record_debug.assert_any_call("Thermal zones")
mock_record_debug.assert_any_call("└─LNXTHERM:00")
mock_record_debug.assert_any_call(" \t temp: 45.0°C")
mock_record_debug.assert_any_call(" \t critical trip: 50.0°C")
# Ensure no prereq was recorded since temp < trip
mock_record_prereq.assert_not_called()
# Stop patches
patch.stopall()
def test_capture_input_wakeup_count(self):
"""Test capture_input_wakeup_count method"""
# Mock pyudev devices
mock_pyudev = patch.object(self.validator, "pyudev").start()
mock_record_debug = patch.object(self.validator.db, "record_debug").start()
mock_read_file = patch("amd_debug.validator.read_file").start()
mock_os_path_exists = patch("os.path.exists").start()
# Mock input devices
mock_device = unittest.mock.Mock()
mock_device.sys_path = "/sys/devices/input0"
mock_device.parent = None
mock_pyudev.list_devices.return_value = [mock_device]
# Mock wakeup file existence and content
mock_os_path_exists.side_effect = (
lambda path: "wakeup" in path or "wakeup_count" in path
)
mock_read_file.side_effect = ["5"] # Wakeup count
# Set initial wakeup count
self.validator.wakeup_count = {"/sys/devices/input0": "3"}
# Call the method
self.validator.capture_input_wakeup_count()
# Validate debug messages
mock_record_debug.assert_called_once_with(
"Woke up from input source /sys/devices/input0 (3->5)", "💤"
)
# Stop patches
patch.stopall()
def test_capture_hw_sleep_suspend_stats(self):
"""Test capture_hw_sleep stats method"""
# Case 1: Suspend stats file exists and contains valid data
with patch(
"os.path.exists", side_effect=lambda path: "suspend_stats" in path
), patch("amd_debug.validator.read_file", return_value="1000000"), patch.object(
self.validator.db, "record_cycle_data"
) as mock_record_cycle_data:
result = self.validator.capture_hw_sleep()
self.assertTrue(result)
self.assertEqual(self.validator.hw_sleep_duration, 1.0)
mock_record_cycle_data.assert_not_called()
def test_capture_hw_sleep_smu_fw_info(self):
"""Test capture_hw_sleep smu_fw_info method"""
# Case 2: Suspend stats file does not exist, fallback to smu_fw_info
with patch(
"os.path.exists", side_effect=lambda path: "suspend_stats" not in path
), patch(
"amd_debug.validator.read_file",
side_effect=[
"Last S0i3 Status: Success\nTime (in us) in S0i3: 2000000", # smu_fw_info content
],
), patch.object(
self.validator.db, "record_cycle_data"
) as mock_record_cycle_data:
result = self.validator.capture_hw_sleep()
self.assertTrue(result)
self.assertEqual(self.validator.hw_sleep_duration, 2.0)
mock_record_cycle_data.assert_not_called()
def test_capture_hw_sleep_smu_fw_info_lockdown(self):
"""Test capture_hw_sleep smu_fw_info method while locked down"""
# Case 3: PermissionError while reading smu_fw_info with lockdown enabled
self.validator.lockdown = True
with patch("os.path.exists", return_value=False), patch(
"amd_debug.validator.read_file", side_effect=PermissionError
), patch.object(
self.validator.db, "record_cycle_data"
) as mock_record_cycle_data:
result = self.validator.capture_hw_sleep()
self.assertFalse(result)
mock_record_cycle_data.assert_called_once_with(
"Unable to gather hardware sleep data with lockdown engaged", "🚦"
)
def test_capture_hw_sleep_smu_fw_info_missing(self):
"""Test capture_hw_sleep smu_fw_info missing method"""
# Case 4: FileNotFoundError while reading smu_fw_info
self.validator.lockdown = False
with patch("os.path.exists", return_value=False), patch(
"amd_debug.validator.read_file", side_effect=FileNotFoundError
), patch.object(self.validator.db, "record_debug") as mock_record:
result = self.validator.capture_hw_sleep()
self.assertFalse(result)
mock_record.assert_called_once_with(
"HW sleep statistics file /sys/kernel/debug/amd_pmc/smu_fw_info is missing"
)
def test_capture_amdgpu_ips_status(self):
"""Test capture_amdgpu_ips_status method"""
# Mock pyudev devices
mock_pyudev = patch.object(self.validator, "pyudev").start()
mock_record_debug = patch.object(self.validator.db, "record_debug").start()
mock_read_file = patch("amd_debug.validator.read_file").start()
mock_os_path_exists = patch("os.path.exists").start()
# Mock PCI devices
mock_device = unittest.mock.Mock()
mock_device.properties = {
"PCI_ID": "1002:abcd",
"PCI_SLOT_NAME": "0000:01:00.0",
}
mock_pyudev.list_devices.return_value = [mock_device]
# Case 1: IPS status file exists and is readable
mock_os_path_exists.return_value = True
mock_read_file.return_value = "IPS Enabled\nIPS Level: 2"
self.validator.capture_amdgpu_ips_status()
# Validate debug messages
mock_record_debug.assert_any_call("IPS status")
mock_record_debug.assert_any_call("│ IPS Enabled")
mock_record_debug.assert_any_call("└─IPS Level: 2")
# Case 2: IPS status file does not exist
mock_os_path_exists.return_value = False
self.validator.capture_amdgpu_ips_status()
# Case 3: PermissionError while reading IPS status file
mock_os_path_exists.return_value = True
mock_read_file.side_effect = PermissionError
self.validator.lockdown = True
self.validator.capture_amdgpu_ips_status()
mock_record_debug.assert_any_call(
"Unable to gather IPS state data due to kernel lockdown."
)
# Case 4: PermissionError without lockdown
self.validator.lockdown = False
self.validator.capture_amdgpu_ips_status()
mock_record_debug.assert_any_call("Failed to read IPS state data")
# Stop patches
patch.stopall()
def test_analyze_kernel_log(self):
"""Test analyze_kernel_log method"""
# Mock kernel log lines
mock_kernel_log_lines = [
"Timekeeping suspended for 123456 us",
"Successfully transitioned to state lps0 ms entry",
"Triggering wakeup from IRQ 5",
"ACPI BIOS Error (bug): Something went wrong",
"Event logged [IO_PAGE_FAULT device=0000:00:0c.0 domain=0x0000 address=0x7e800000 flags=0x0050]",
"Dispatching Notify on [UBTC] (Device) Value 0x80 (Status Change)",
]
# Mock kernel log processing
mock_process_callback = patch.object(
self.validator.kernel_log, "process_callback"
).start()
mock_process_callback.side_effect = lambda callback: [
callback(line, 7) for line in mock_kernel_log_lines
]
# Mock database recording
mock_record_cycle_data = patch.object(
self.validator.db, "record_cycle_data"
).start()
mock_record_debug = patch.object(self.validator.db, "record_debug").start()
# Call the method
self.validator.analyze_kernel_log()
# Validate recorded cycle data
mock_record_cycle_data.assert_any_call("Hardware sleep cycle count: 1", "💤")
mock_record_cycle_data.assert_any_call("ACPI BIOS errors found", "❌")
mock_record_cycle_data.assert_any_call("Page faults found", "❌")
mock_record_cycle_data.assert_any_call(
"Notify devices ['UBTC'] found during suspend", "💤"
)
# Validate recorded debug messages
mock_record_debug.assert_any_call("Used Microsoft uPEP GUID in LPS0 _DSM")
mock_record_debug.assert_any_call("Triggering wakeup from IRQ 5", 7)
# Stop patches
patch.stopall()
def test_prep(self):
"""Test prep method"""
with patch("amd_debug.validator.datetime") as mock_datetime, patch.object(
self.validator.kernel_log, "seek_tail"
) as mock_seek_tail, patch.object(
self.validator.db, "start_cycle"
) as mock_start_cycle, patch.object(
self.validator, "capture_battery"
) as mock_capture_battery, patch.object(
self.validator, "check_gpes"
) as mock_check_gpes, patch.object(
self.validator, "capture_lid"
) as mock_capture_lid, patch.object(
self.validator, "capture_command_line"
) as mock_capture_command_line, patch.object(
self.validator, "capture_wake_sources"
) as mock_capture_wake_sources, patch.object(
self.validator, "capture_running_compositors"
) as mock_capture_running_compositors, patch.object(
self.validator, "capture_power_profile"
) as mock_capture_power_profile, patch.object(
self.validator, "capture_amdgpu_ips_status"
) as mock_capture_amdgpu_ips_status, patch.object(
self.validator, "capture_thermal"
) as mock_capture_thermal, patch.object(
self.validator, "capture_input_wakeup_count"
) as mock_capture_input_wakeup_count, patch.object(
self.validator.acpica, "trace_bios"
) as mock_trace_bios, patch.object(
self.validator.acpica, "trace_notify"
) as mock_trace_notify, patch.object(
self.validator.db, "record_cycle"
) as mock_record_cycle:
# Mock datetime
mock_datetime.now.return_value = "mocked_datetime"
# Set bios_debug to True and test
self.validator.bios_debug = True
self.validator.prep()
mock_seek_tail.assert_called_once()
mock_start_cycle.assert_called_once_with("mocked_datetime")
mock_capture_battery.assert_called_once()
mock_check_gpes.assert_called_once()
mock_capture_lid.assert_called_once()
mock_capture_command_line.assert_called_once()
mock_capture_wake_sources.assert_called_once()
mock_capture_running_compositors.assert_called_once()
mock_capture_power_profile.assert_called_once()
mock_capture_amdgpu_ips_status.assert_called_once()
mock_capture_thermal.assert_called_once()
mock_capture_input_wakeup_count.assert_called_once()
mock_trace_bios.assert_called_once()
mock_trace_notify.assert_not_called()
mock_record_cycle.assert_called_once()
# Reset mocks
mock_seek_tail.reset_mock()
mock_start_cycle.reset_mock()
mock_capture_battery.reset_mock()
mock_check_gpes.reset_mock()
mock_capture_lid.reset_mock()
mock_capture_command_line.reset_mock()
mock_capture_wake_sources.reset_mock()
mock_capture_running_compositors.reset_mock()
mock_capture_power_profile.reset_mock()
mock_capture_amdgpu_ips_status.reset_mock()
mock_capture_thermal.reset_mock()
mock_capture_input_wakeup_count.reset_mock()
mock_trace_bios.reset_mock()
mock_trace_notify.reset_mock()
mock_record_cycle.reset_mock()
# Set bios_debug to False and test
self.validator.bios_debug = False
self.validator.prep()
mock_seek_tail.assert_called_once()
mock_start_cycle.assert_called_once_with("mocked_datetime")
mock_capture_battery.assert_called_once()
mock_check_gpes.assert_called_once()
mock_capture_lid.assert_called_once()
mock_capture_command_line.assert_called_once()
mock_capture_wake_sources.assert_called_once()
mock_capture_running_compositors.assert_called_once()
mock_capture_power_profile.assert_called_once()
mock_capture_amdgpu_ips_status.assert_called_once()
mock_capture_thermal.assert_called_once()
mock_capture_input_wakeup_count.assert_called_once()
mock_trace_bios.assert_not_called()
mock_trace_notify.assert_called_once()
mock_record_cycle.assert_called_once()
def test_post(self):
"""Test post method"""
with patch.object(
self.validator, "analyze_kernel_log"
) as mock_analyze_kernel_log, patch.object(
self.validator, "capture_wakeup_irq_data"
) as mock_capture_wakeup_irq_data, patch.object(
self.validator, "check_gpes"
) as mock_check_gpes, patch.object(
self.validator, "capture_lid"
) as mock_capture_lid, patch.object(
self.validator, "check_rtc_cmos"
) as mock_check_rtc_cmos, patch.object(
self.validator, "capture_hw_sleep"
) as mock_capture_hw_sleep, patch.object(
self.validator, "capture_battery"
) as mock_capture_battery, patch.object(
self.validator, "capture_amdgpu_ips_status"
) as mock_capture_amdgpu_ips_status, patch.object(
self.validator, "capture_thermal"
) as mock_capture_thermal, patch.object(
self.validator, "capture_input_wakeup_count"
) as mock_capture_input_wakeup_count, patch.object(
self.validator.acpica, "restore"
) as mock_acpica_restore, patch.object(
self.validator.db, "record_cycle"
) as mock_record_cycle:
# Set mock return values
mock_analyze_kernel_log.return_value = None
mock_capture_wakeup_irq_data.return_value = None
mock_check_gpes.return_value = None
mock_capture_lid.return_value = None
mock_check_rtc_cmos.return_value = None
mock_capture_hw_sleep.return_value = None
mock_capture_battery.return_value = None
mock_capture_amdgpu_ips_status.return_value = None
mock_capture_thermal.return_value = None
mock_capture_input_wakeup_count.return_value = None
mock_acpica_restore.return_value = None
# Set attributes for record_cycle
self.validator.requested_duration = 60
self.validator.active_gpios = ["GPIO1"]
self.validator.wakeup_irqs = [5]
self.validator.kernel_duration = 1.5
self.validator.hw_sleep_duration = 1.0
# Call the method
self.validator.post()
# Assert all checks were called
mock_analyze_kernel_log.assert_called_once()
mock_capture_wakeup_irq_data.assert_called_once()
mock_check_gpes.assert_called_once()
mock_capture_lid.assert_called_once()
mock_check_rtc_cmos.assert_called_once()
mock_capture_hw_sleep.assert_called_once()
mock_capture_battery.assert_called_once()
mock_capture_amdgpu_ips_status.assert_called_once()
mock_capture_thermal.assert_called_once()
mock_capture_input_wakeup_count.assert_called_once()
mock_acpica_restore.assert_called_once()
# Assert record_cycle was called with correct arguments
mock_record_cycle.assert_called_once_with(
self.validator.requested_duration,
self.validator.active_gpios,
self.validator.wakeup_irqs,
self.validator.kernel_duration,
self.validator.hw_sleep_duration,
)
def test_program_wakealarm(self):
"""Test program_wakealarm method"""
# Mock pyudev devices
mock_pyudev = patch.object(self.validator, "pyudev").start()
_mock_record_debug = patch.object(self.validator.db, "record_debug").start()
mock_print_color = patch("amd_debug.validator.print_color").start()
mock_open_file = patch("builtins.open", mock_open()).start()
# Case 1: RTC device exists
mock_device = unittest.mock.Mock()
mock_device.sys_path = "/sys/class/rtc/rtc0"
mock_pyudev.list_devices.return_value = [mock_device]
self.validator.requested_duration = 60
self.validator.program_wakealarm()
# Validate file writes
mock_open_file.assert_any_call(
"/sys/class/rtc/rtc0/wakealarm", "w", encoding="utf-8"
)
mock_open_file().write.assert_any_call("0")
mock_open_file().write.assert_any_call("+60\n")
# Case 2: No RTC device found
mock_pyudev.list_devices.return_value = []
self.validator.program_wakealarm()
# Validate print_color call
mock_print_color.assert_called_once_with(
"No RTC device found, please manually wake system", "🚦"
)
# Stop patches
patch.stopall()
@patch("amd_debug.validator.SleepReport")
@patch("amd_debug.validator.print_color")
def test_report_cycle(self, mock_print_color, mock_sleep_report):
"""Test report_cycle method"""
# Mock SleepReport instance
mock_report_instance = mock_sleep_report.return_value
mock_report_instance.run.return_value = None
# Set attributes for the test
self.validator.last_suspend = "mocked_last_suspend"
self.validator.display_debug = True
# Call the method
self.validator.report_cycle()
# Assert print_color was called with correct arguments
mock_print_color.assert_called_once_with("Results from last s2idle cycle", "🗣️")
# Assert SleepReport was instantiated with correct arguments
mock_sleep_report.assert_called_once_with(
since="mocked_last_suspend",
until="mocked_last_suspend",
fname=None,
fmt="stdout",
tool_debug=True,
report_debug=False,
)
# Assert run method of SleepReport was called
mock_report_instance.run.assert_called_once_with(inc_prereq=False)
@patch("amd_debug.validator.run_countdown")
@patch("amd_debug.validator.random.randint")
@patch("amd_debug.validator.datetime")
@patch.object(SleepValidator, "prep")
@patch.object(SleepValidator, "program_wakealarm")
@patch.object(SleepValidator, "suspend_system")
@patch.object(SleepValidator, "post")
@patch.object(SleepValidator, "unlock_session")
@patch.object(SleepValidator, "report_cycle")
@patch("amd_debug.validator.print_color")
def test_run(
self,
_mock_print_color,
mock_report_cycle,
mock_unlock_session,
mock_post,
mock_suspend_system,
mock_program_wakealarm,
mock_prep,
mock_datetime,
mock_randint,
mock_run_countdown,
):
"""Test the run method"""
# Mock datetime
mock_datetime.now.return_value = datetime(2023, 1, 1, 12, 0, 0)
# Mock suspend_system to return True
mock_suspend_system.return_value = True
# Test case 1: count is 0
result = self.validator.run(
duration=10, count=0, wait=5, rand=False, logind=False
)
self.assertTrue(result)
# Test case 2: logind is True
self.validator.run(duration=10, count=1, wait=5, rand=False, logind=True)
self.assertTrue(self.validator.logind)
# Test case 3: Randomized test
mock_randint.side_effect = [7, 3] # Random duration and wait
self.validator.run(duration=10, count=1, wait=5, rand=True, logind=False)
mock_randint.assert_any_call(4, 10)
mock_randint.assert_any_call(1, 5)
mock_run_countdown.assert_any_call("Suspending system", math.ceil(3 / 2))
mock_run_countdown.assert_any_call("Collecting data", math.ceil(3 / 2))
mock_prep.assert_called()
mock_program_wakealarm.assert_called()
mock_suspend_system.assert_called()
mock_post.assert_called()
mock_report_cycle.assert_called()
mock_unlock_session.assert_called()
# Test case 4: Randomized test, but too short of a duration
result = self.validator.run(
duration=4, count=1, wait=5, rand=True, logind=False
)
self.assertFalse(result)
mock_report_cycle.assert_called()
# Test case 5: Multiple cycles
self.validator.run(duration=10, count=2, wait=5, rand=False, logind=False)
self.assertEqual(mock_prep.call_count, 4) # Includes previous calls
self.assertEqual(mock_program_wakealarm.call_count, 4)
self.assertEqual(mock_suspend_system.call_count, 4)
self.assertEqual(mock_post.call_count, 4)
self.assertEqual(mock_report_cycle.call_count, 5)
self.assertEqual(mock_unlock_session.call_count, 3)
# Test case 6: suspend_system fails
mock_suspend_system.return_value = False
result = self.validator.run(
duration=10, count=1, wait=5, rand=False, logind=False
)
self.assertFalse(result)
mock_report_cycle.assert_called()
@patch("os.path.exists")
@patch("builtins.open", new_callable=mock_open, read_data="3")
@patch("os.write")
@patch("os.open")
@patch("os.close")
def test_suspend_system_sysfs_success(
self,
mock_os_close,
mock_os_open,
mock_os_write,
_mock_open_file,
mock_path_exists,
):
"""Test suspend_system method using sysfs interface with success"""
# Mock wakeup_count file existence
mock_path_exists.side_effect = lambda path: "wakeup_count" in path
# Mock os.open and os.write
mock_os_open.return_value = 3
mock_os_write.return_value = None
# Call the method
result = self.validator.suspend_system()
# Assert the method returned True
self.assertTrue(result)
# Assert os.open and os.write were called
mock_os_open.assert_called_once_with(
"/sys/power/state", os.O_WRONLY | os.O_SYNC
)
mock_os_write.assert_called_once_with(3, b"mem")
mock_os_close.assert_called_once_with(3)
@patch("os.path.exists")
@patch("builtins.open", new_callable=mock_open, read_data="3")
@patch("os.write")
@patch("os.open")
@patch("os.close")
def test_suspend_system_sysfs_failure(
self,
mock_os_close,
mock_os_open,
mock_os_write,
_mock_open_file,
mock_path_exists,
):
"""Test suspend_system method using sysfs interface with failure"""
# Mock wakeup_count file existence
mock_path_exists.side_effect = lambda path: "wakeup_count" in path
# Mock os.open to raise OSError
mock_os_open.return_value = 3
mock_os_write.side_effect = OSError("Failed to write to state")
# Call the method
result = self.validator.suspend_system()
# Assert the method returned False
self.assertFalse(result)
# Assert os.open and os.write were called
mock_os_open.assert_called_once_with(
"/sys/power/state", os.O_WRONLY | os.O_SYNC
)
mock_os_write.assert_called_once_with(3, b"mem")
mock_os_close.assert_called_once_with(3)
@patch("os.path.exists")
@patch("builtins.open", new_callable=mock_open)
@patch("os.write")
@patch("os.open")
@patch("os.close")
def test_suspend_system_sysfs_no_wakeup_count(
self,
mock_os_close,
mock_os_open,
mock_os_write,
_mock_open_file,
mock_path_exists,
):
"""Test suspend_system method using sysfs interface with no wakeup_count file"""
# Mock wakeup_count file does not exist
mock_path_exists.return_value = False
# Mock os.open and os.write
mock_os_open.return_value = 3
mock_os_write.return_value = None
# Call the method
result = self.validator.suspend_system()
# Assert the method returned True
self.assertTrue(result)
# Assert os.open and os.write were called
mock_os_open.assert_called_once_with(
"/sys/power/state", os.O_WRONLY | os.O_SYNC
)
mock_os_write.assert_called_once_with(3, b"mem")
mock_os_close.assert_called_once_with(3)