amd-debug-tools 0.2.0__py3-none-any.whl → 0.2.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- amd_debug/common.py +48 -0
- amd_debug/display.py +34 -0
- amd_debug/failures.py +13 -1
- amd_debug/installer.py +69 -5
- amd_debug/prerequisites.py +157 -56
- amd_debug/s2idle.py +46 -17
- amd_debug/sleep_report.py +2 -2
- amd_debug/templates/md +0 -7
- amd_debug/validator.py +3 -5
- {amd_debug_tools-0.2.0.dist-info → amd_debug_tools-0.2.2.dist-info}/METADATA +4 -3
- amd_debug_tools-0.2.2.dist-info/RECORD +45 -0
- {amd_debug_tools-0.2.0.dist-info → amd_debug_tools-0.2.2.dist-info}/WHEEL +1 -1
- amd_debug_tools-0.2.2.dist-info/top_level.txt +18 -0
- launcher.py +35 -0
- test_acpi.py +90 -0
- test_batteries.py +92 -0
- test_bios.py +250 -0
- test_common.py +444 -0
- test_database.py +284 -0
- test_display.py +143 -0
- test_failures.py +146 -0
- test_installer.py +281 -0
- test_kernel.py +205 -0
- test_launcher.py +53 -0
- test_prerequisites.py +1935 -0
- test_pstate.py +164 -0
- test_s2idle.py +868 -0
- test_sleep_report.py +167 -0
- test_validator.py +723 -0
- test_wake.py +216 -0
- amd_debug_tools-0.2.0.dist-info/RECORD +0 -27
- amd_debug_tools-0.2.0.dist-info/top_level.txt +0 -1
- {amd_debug_tools-0.2.0.dist-info → amd_debug_tools-0.2.2.dist-info}/entry_points.txt +0 -0
- {amd_debug_tools-0.2.0.dist-info → amd_debug_tools-0.2.2.dist-info}/licenses/LICENSE +0 -0
test_prerequisites.py
ADDED
|
@@ -0,0 +1,1935 @@
|
|
|
1
|
+
#!/usr/bin/python3
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
This module contains unit tests for the prerequisite functions in the amd-debug-tools package.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
import unittest
|
|
10
|
+
import subprocess
|
|
11
|
+
from unittest.mock import patch, MagicMock, mock_open
|
|
12
|
+
|
|
13
|
+
from amd_debug.prerequisites import PrerequisiteValidator
|
|
14
|
+
from amd_debug.failures import *
|
|
15
|
+
from amd_debug.common import apply_prefix_wrapper, BIT
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class TestPrerequisiteValidator(unittest.TestCase):
|
|
19
|
+
|
|
20
|
+
@classmethod
|
|
21
|
+
def setUpClass(cls):
|
|
22
|
+
logging.basicConfig(filename="/dev/null", level=logging.DEBUG)
|
|
23
|
+
|
|
24
|
+
@patch("amd_debug.prerequisites.is_root", return_value=True)
|
|
25
|
+
@patch("amd_debug.prerequisites.get_kernel_log")
|
|
26
|
+
@patch("amd_debug.prerequisites.get_distro", return_value="Ubuntu")
|
|
27
|
+
@patch("amd_debug.prerequisites.read_file", return_value="mocked_cmdline")
|
|
28
|
+
@patch("amd_debug.prerequisites.pyudev.Context")
|
|
29
|
+
@patch("amd_debug.prerequisites.SleepDatabase")
|
|
30
|
+
def setUp(
|
|
31
|
+
self,
|
|
32
|
+
MockSleepDatabase,
|
|
33
|
+
MockPyudev,
|
|
34
|
+
_mock_read_file,
|
|
35
|
+
_mock_get_distro,
|
|
36
|
+
mock_get_kernel_log,
|
|
37
|
+
_mock_is_root,
|
|
38
|
+
):
|
|
39
|
+
self.mock_db = MockSleepDatabase.return_value
|
|
40
|
+
self.mock_pyudev = MockPyudev.return_value
|
|
41
|
+
self.mock_kernel_log = mock_get_kernel_log.return_value
|
|
42
|
+
self.validator = PrerequisiteValidator(tool_debug=True)
|
|
43
|
+
|
|
44
|
+
def test_check_amdgpu_no_driver(self):
|
|
45
|
+
"""Test check_amdgpu with no driver present"""
|
|
46
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
47
|
+
MagicMock(
|
|
48
|
+
properties={"PCI_CLASS": "30000", "PCI_ID": "1002abcd", "DRIVER": None}
|
|
49
|
+
)
|
|
50
|
+
]
|
|
51
|
+
result = self.validator.check_amdgpu()
|
|
52
|
+
self.assertFalse(result)
|
|
53
|
+
self.assertTrue(
|
|
54
|
+
any(isinstance(f, MissingAmdgpu) for f in self.validator.failures)
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
def test_check_amdgpu_with_driver(self):
|
|
58
|
+
"""Test check_amdgpu with driver present"""
|
|
59
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
60
|
+
MagicMock(
|
|
61
|
+
properties={
|
|
62
|
+
"PCI_CLASS": "30000",
|
|
63
|
+
"PCI_ID": "1002abcd",
|
|
64
|
+
"DRIVER": "amdgpu",
|
|
65
|
+
}
|
|
66
|
+
)
|
|
67
|
+
]
|
|
68
|
+
result = self.validator.check_amdgpu()
|
|
69
|
+
self.assertTrue(result)
|
|
70
|
+
|
|
71
|
+
def test_check_wcn6855_bug_no_bug(self):
|
|
72
|
+
"""Test check_wcn6855_bug with no bug present"""
|
|
73
|
+
self.mock_kernel_log.match_pattern.side_effect = lambda pattern: None
|
|
74
|
+
result = self.validator.check_wcn6855_bug()
|
|
75
|
+
self.assertTrue(result)
|
|
76
|
+
|
|
77
|
+
def test_check_storage_no_nvme(self):
|
|
78
|
+
"""Test check_storage with no NVMe devices"""
|
|
79
|
+
self.mock_pyudev.list_devices.return_value = []
|
|
80
|
+
result = self.validator.check_storage()
|
|
81
|
+
self.assertTrue(result)
|
|
82
|
+
|
|
83
|
+
@patch("amd_debug.prerequisites.minimum_kernel", return_value=True)
|
|
84
|
+
def test_check_amd_hsmp_new_kernel(self, _mock_minimum_kernel):
|
|
85
|
+
"""Test check_amd_hsmp with CONFIG_AMD_HSMP=y and kernel version >= 6.10"""
|
|
86
|
+
result = self.validator.check_amd_hsmp()
|
|
87
|
+
self.assertTrue(result)
|
|
88
|
+
|
|
89
|
+
@patch("amd_debug.prerequisites.read_file", return_value="CONFIG_AMD_HSMP=y")
|
|
90
|
+
@patch("os.path.exists", return_value=True)
|
|
91
|
+
@patch("amd_debug.prerequisites.minimum_kernel", return_value=False)
|
|
92
|
+
def test_check_amd_hsmp_conflict(
|
|
93
|
+
self, _mock_min_kernel, _mock_exists, _mock_read_file
|
|
94
|
+
):
|
|
95
|
+
"""Test check_amd_hsmp with CONFIG_AMD_HSMP=y and kernel version < 6.10"""
|
|
96
|
+
result = self.validator.check_amd_hsmp()
|
|
97
|
+
self.assertFalse(result)
|
|
98
|
+
self.assertTrue(any(isinstance(f, AmdHsmpBug) for f in self.validator.failures))
|
|
99
|
+
|
|
100
|
+
def test_check_amd_pmc_no_driver(self):
|
|
101
|
+
"""Test check_amd_pmc with no driver"""
|
|
102
|
+
self.mock_pyudev.list_devices.return_value = []
|
|
103
|
+
result = self.validator.check_amd_pmc()
|
|
104
|
+
self.assertFalse(result)
|
|
105
|
+
self.assertTrue(
|
|
106
|
+
any(isinstance(f, MissingAmdPmc) for f in self.validator.failures)
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
def test_check_sleep_mode_not_supported(self):
|
|
110
|
+
"""Test check_sleep_mode with no sleep mode support"""
|
|
111
|
+
with patch("os.path.exists", return_value=False):
|
|
112
|
+
result = self.validator.check_sleep_mode()
|
|
113
|
+
self.assertFalse(result)
|
|
114
|
+
|
|
115
|
+
def test_check_sleep_mode_s2idle(self):
|
|
116
|
+
"""Test check_sleep_mode with s2idle mode"""
|
|
117
|
+
with patch("os.path.exists", return_value=True), patch(
|
|
118
|
+
"amd_debug.prerequisites.read_file", return_value="[s2idle]"
|
|
119
|
+
):
|
|
120
|
+
result = self.validator.check_sleep_mode()
|
|
121
|
+
self.assertTrue(result)
|
|
122
|
+
|
|
123
|
+
def test_check_port_pm_override_non_family_19(self):
|
|
124
|
+
"""Test check_port_pm_override with non-family 0x19 CPU"""
|
|
125
|
+
self.validator.cpu_family = 0x18
|
|
126
|
+
result = self.validator.check_port_pm_override()
|
|
127
|
+
self.assertTrue(result)
|
|
128
|
+
|
|
129
|
+
def test_check_port_pm_override_non_matching_model(self):
|
|
130
|
+
"""Test check_port_pm_override with non-matching CPU model"""
|
|
131
|
+
self.validator.cpu_family = 0x19
|
|
132
|
+
self.validator.cpu_model = 0x72
|
|
133
|
+
result = self.validator.check_port_pm_override()
|
|
134
|
+
self.assertTrue(result)
|
|
135
|
+
|
|
136
|
+
@patch("amd_debug.prerequisites.version.parse")
|
|
137
|
+
def test_check_port_pm_override_smu_version_too_high(self, mock_version_parse):
|
|
138
|
+
"""Test check_port_pm_override with SMU version > 76.60.0"""
|
|
139
|
+
self.validator.cpu_family = 0x19
|
|
140
|
+
self.validator.cpu_model = 0x74
|
|
141
|
+
mock_version_parse.side_effect = lambda v: v if isinstance(v, str) else None
|
|
142
|
+
self.validator.smu_version = "76.61.0"
|
|
143
|
+
result = self.validator.check_port_pm_override()
|
|
144
|
+
self.assertTrue(result)
|
|
145
|
+
|
|
146
|
+
@patch("amd_debug.prerequisites.version.parse")
|
|
147
|
+
def test_check_port_pm_override_smu_version_too_low(self, mock_version_parse):
|
|
148
|
+
"""Test check_port_pm_override with SMU version < 76.18.0"""
|
|
149
|
+
self.validator.cpu_family = 0x19
|
|
150
|
+
self.validator.cpu_model = 0x74
|
|
151
|
+
mock_version_parse.side_effect = lambda v: v if isinstance(v, str) else None
|
|
152
|
+
self.validator.smu_version = "76.17.0"
|
|
153
|
+
result = self.validator.check_port_pm_override()
|
|
154
|
+
self.assertTrue(result)
|
|
155
|
+
|
|
156
|
+
@patch("amd_debug.prerequisites.read_file", return_value="pcie_port_pm=off")
|
|
157
|
+
def test_check_port_pm_override_cmdline_override(self, mock_read_file):
|
|
158
|
+
"""Test check_port_pm_override with pcie_port_pm=off in cmdline"""
|
|
159
|
+
self.validator.cpu_family = 0x19
|
|
160
|
+
self.validator.cpu_model = 0x74
|
|
161
|
+
self.validator.smu_version = "76.50.0"
|
|
162
|
+
result = self.validator.check_port_pm_override()
|
|
163
|
+
self.assertTrue(result)
|
|
164
|
+
|
|
165
|
+
@patch("amd_debug.prerequisites.read_file", return_value="mocked_cmdline")
|
|
166
|
+
def test_check_port_pm_override_no_override(self, mock_read_file):
|
|
167
|
+
"""Test check_port_pm_override without pcie_port_pm=off in cmdline"""
|
|
168
|
+
self.validator.cpu_family = 0x19
|
|
169
|
+
self.validator.cpu_model = 0x74
|
|
170
|
+
self.validator.smu_version = "76.50.0"
|
|
171
|
+
result = self.validator.check_port_pm_override()
|
|
172
|
+
self.assertFalse(result)
|
|
173
|
+
|
|
174
|
+
def test_check_iommu_disabled(self):
|
|
175
|
+
"""Test check_iommu when IOMMU is disabled"""
|
|
176
|
+
self.validator.cpu_family = 0x1A
|
|
177
|
+
self.validator.cpu_model = 0x20
|
|
178
|
+
self.mock_pyudev.list_devices.return_value = []
|
|
179
|
+
result = self.validator.check_iommu()
|
|
180
|
+
self.assertTrue(result)
|
|
181
|
+
self.mock_db.record_prereq.assert_called_with("IOMMU disabled", "✅")
|
|
182
|
+
|
|
183
|
+
@patch(
|
|
184
|
+
"amd_debug.prerequisites.open",
|
|
185
|
+
new_callable=unittest.mock.mock_open,
|
|
186
|
+
read_data=b"\x00" * 45,
|
|
187
|
+
)
|
|
188
|
+
def test_check_iommu_no_dma_protection(self, _mock_open):
|
|
189
|
+
"""Test check_iommu when DMA protection is not enabled"""
|
|
190
|
+
self.validator.cpu_family = 0x1A
|
|
191
|
+
self.validator.cpu_model = 0x20
|
|
192
|
+
iommu_device = MagicMock(sys_path="/sys/devices/iommu")
|
|
193
|
+
self.mock_pyudev.list_devices.side_effect = [
|
|
194
|
+
[iommu_device],
|
|
195
|
+
[],
|
|
196
|
+
[],
|
|
197
|
+
]
|
|
198
|
+
result = self.validator.check_iommu()
|
|
199
|
+
self.assertFalse(result)
|
|
200
|
+
self.assertTrue(
|
|
201
|
+
any(isinstance(f, DMArNotEnabled) for f in self.validator.failures)
|
|
202
|
+
)
|
|
203
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
204
|
+
"IOMMU is misconfigured: Pre-boot DMA protection not enabled", "❌"
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
@patch(
|
|
208
|
+
"amd_debug.prerequisites.open",
|
|
209
|
+
new_callable=unittest.mock.mock_open,
|
|
210
|
+
read_data=b"\x00" * 36 + b"\xff" * 4,
|
|
211
|
+
)
|
|
212
|
+
def test_check_iommu_missing_acpi_device(self, _mock_open):
|
|
213
|
+
"""Test check_iommu when MSFT0201 ACPI device is missing"""
|
|
214
|
+
self.validator.cpu_family = 0x1A
|
|
215
|
+
self.validator.cpu_model = 0x20
|
|
216
|
+
iommu_device = MagicMock(sys_path="/sys/devices/iommu")
|
|
217
|
+
self.mock_pyudev.list_devices.side_effect = [
|
|
218
|
+
[iommu_device],
|
|
219
|
+
[],
|
|
220
|
+
[],
|
|
221
|
+
]
|
|
222
|
+
result = self.validator.check_iommu()
|
|
223
|
+
self.assertFalse(result)
|
|
224
|
+
self.assertTrue(
|
|
225
|
+
any(isinstance(f, MissingIommuACPI) for f in self.validator.failures)
|
|
226
|
+
)
|
|
227
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
228
|
+
"IOMMU is misconfigured: missing MSFT0201 ACPI device", "❌"
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
@patch(
|
|
232
|
+
"amd_debug.prerequisites.open",
|
|
233
|
+
new_callable=unittest.mock.mock_open,
|
|
234
|
+
read_data=b"\x00" * 36 + b"\xff" * 4,
|
|
235
|
+
)
|
|
236
|
+
def test_check_iommu_missing_policy(self, _mock_open):
|
|
237
|
+
"""Test check_iommu when policy is not bound to MSFT0201"""
|
|
238
|
+
self.validator.cpu_family = 0x1A
|
|
239
|
+
self.validator.cpu_model = 0x20
|
|
240
|
+
iommu_device = MagicMock(sys_path="/sys/devices/iommu")
|
|
241
|
+
acpi_device = MagicMock(sys_path="/sys/devices/acpi/MSFT0201")
|
|
242
|
+
platform_device = MagicMock(sys_path="/sys/devices/platform/MSFT0201")
|
|
243
|
+
self.mock_pyudev.list_devices.side_effect = [
|
|
244
|
+
[iommu_device],
|
|
245
|
+
[acpi_device],
|
|
246
|
+
[platform_device],
|
|
247
|
+
]
|
|
248
|
+
result = self.validator.check_iommu()
|
|
249
|
+
self.assertFalse(result)
|
|
250
|
+
self.assertTrue(
|
|
251
|
+
any(isinstance(f, MissingIommuPolicy) for f in self.validator.failures)
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
@patch(
|
|
255
|
+
"amd_debug.prerequisites.open",
|
|
256
|
+
new_callable=unittest.mock.mock_open,
|
|
257
|
+
read_data=b"\x00" * 36 + b"\xff" * 4,
|
|
258
|
+
)
|
|
259
|
+
@patch("amd_debug.prerequisites.os.path.exists", return_value=True)
|
|
260
|
+
def test_check_iommu_properly_configured(self, _mock_open, _mock_exists):
|
|
261
|
+
"""Test check_iommu when IOMMU is properly configured"""
|
|
262
|
+
self.validator.cpu_family = 0x1A
|
|
263
|
+
self.validator.cpu_model = 0x20
|
|
264
|
+
iommu_device = MagicMock(sys_path="/sys/devices/iommu")
|
|
265
|
+
acpi_device = MagicMock(sys_path="/sys/devices/acpi/MSFT0201")
|
|
266
|
+
platform_device = MagicMock(sys_path="/sys/devices/platform/MSFT0201")
|
|
267
|
+
self.mock_pyudev.list_devices.side_effect = [
|
|
268
|
+
[iommu_device],
|
|
269
|
+
[acpi_device],
|
|
270
|
+
[platform_device],
|
|
271
|
+
]
|
|
272
|
+
result = self.validator.check_iommu()
|
|
273
|
+
self.assertTrue(result)
|
|
274
|
+
self.mock_db.record_prereq.assert_called_with("IOMMU properly configured", "✅")
|
|
275
|
+
|
|
276
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
277
|
+
@patch("amd_debug.validator.print_color")
|
|
278
|
+
def test_check_taint_not_tainted(self, _mock_print_color, mock_read_file):
|
|
279
|
+
"""Test check_taint when the kernel is not tainted"""
|
|
280
|
+
mock_read_file.return_value = "0"
|
|
281
|
+
result = self.validator.check_taint()
|
|
282
|
+
self.assertTrue(result)
|
|
283
|
+
self.assertFalse(
|
|
284
|
+
any(isinstance(f, TaintedKernel) for f in self.validator.failures)
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
288
|
+
@patch("amd_debug.validator.print_color")
|
|
289
|
+
def test_check_taint_tainted(self, _mock_print_color, mock_read_file):
|
|
290
|
+
"""Test check_taint when the kernel is tainted"""
|
|
291
|
+
mock_read_file.return_value = str(
|
|
292
|
+
BIT(9) | 1
|
|
293
|
+
) # Kernel warnings ignored, other taint present
|
|
294
|
+
result = self.validator.check_taint()
|
|
295
|
+
self.assertFalse(result)
|
|
296
|
+
self.assertTrue(
|
|
297
|
+
any(isinstance(f, TaintedKernel) for f in self.validator.failures)
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
301
|
+
@patch("amd_debug.validator.print_color")
|
|
302
|
+
def test_check_taint_file_not_found(self, _mock_print_color, mock_read_file):
|
|
303
|
+
"""Test check_taint when the tainted file is not found"""
|
|
304
|
+
mock_read_file.side_effect = FileNotFoundError
|
|
305
|
+
with self.assertRaises(FileNotFoundError):
|
|
306
|
+
self.validator.check_taint()
|
|
307
|
+
|
|
308
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
309
|
+
@patch("amd_debug.validator.print_color")
|
|
310
|
+
def test_check_taint_invalid_value(self, _mock_print_color, mock_read_file):
|
|
311
|
+
"""Test check_taint when the tainted file contains invalid data"""
|
|
312
|
+
mock_read_file.return_value = "invalid"
|
|
313
|
+
with self.assertRaises(ValueError):
|
|
314
|
+
self.validator.check_taint()
|
|
315
|
+
|
|
316
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
317
|
+
def test_check_smt_not_supported(self, mock_read_file):
|
|
318
|
+
"""Test check_smt when SMT is not supported"""
|
|
319
|
+
mock_read_file.side_effect = ["notsupported"]
|
|
320
|
+
result = self.validator.check_smt()
|
|
321
|
+
self.assertTrue(result)
|
|
322
|
+
self.mock_db.record_debug.assert_called_with("SMT control: notsupported")
|
|
323
|
+
|
|
324
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
325
|
+
def test_check_smt_disabled(self, mock_read_file):
|
|
326
|
+
"""Test check_smt when SMT is disabled"""
|
|
327
|
+
mock_read_file.side_effect = ["on", "0"]
|
|
328
|
+
result = self.validator.check_smt()
|
|
329
|
+
self.assertFalse(result)
|
|
330
|
+
self.assertTrue(
|
|
331
|
+
any(isinstance(f, SMTNotEnabled) for f in self.validator.failures)
|
|
332
|
+
)
|
|
333
|
+
self.mock_db.record_prereq.assert_called_with("SMT is not enabled", "❌")
|
|
334
|
+
|
|
335
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
336
|
+
def test_check_smt_enabled(self, mock_read_file):
|
|
337
|
+
"""Test check_smt when SMT is enabled"""
|
|
338
|
+
mock_read_file.side_effect = ["on", "1"]
|
|
339
|
+
result = self.validator.check_smt()
|
|
340
|
+
self.assertTrue(result)
|
|
341
|
+
self.mock_db.record_prereq.assert_called_with("SMT enabled", "✅")
|
|
342
|
+
|
|
343
|
+
@patch("amd_debug.prerequisites.read_msr")
|
|
344
|
+
def test_check_msr_pc6_disabled(self, mock_read_msr):
|
|
345
|
+
"""Test check_msr when PC6 is disabled"""
|
|
346
|
+
mock_read_msr.side_effect = lambda reg, _: (
|
|
347
|
+
0 if reg == 0xC0010292 else BIT(22) | BIT(14) | BIT(6)
|
|
348
|
+
)
|
|
349
|
+
result = self.validator.check_msr()
|
|
350
|
+
self.assertFalse(result)
|
|
351
|
+
self.assertTrue(any(isinstance(f, MSRFailure) for f in self.validator.failures))
|
|
352
|
+
|
|
353
|
+
@patch("amd_debug.prerequisites.read_msr")
|
|
354
|
+
def test_check_msr_cc6_disabled(self, mock_read_msr):
|
|
355
|
+
"""Test check_msr when CC6 is disabled"""
|
|
356
|
+
mock_read_msr.side_effect = lambda reg, _: BIT(32) if reg == 0xC0010292 else 0
|
|
357
|
+
result = self.validator.check_msr()
|
|
358
|
+
self.assertFalse(result)
|
|
359
|
+
self.assertTrue(any(isinstance(f, MSRFailure) for f in self.validator.failures))
|
|
360
|
+
|
|
361
|
+
@patch("amd_debug.prerequisites.read_msr")
|
|
362
|
+
def test_check_msr_enabled(self, mock_read_msr):
|
|
363
|
+
"""Test check_msr when PC6 and CC6 are enabled"""
|
|
364
|
+
mock_read_msr.side_effect = lambda reg, _: (
|
|
365
|
+
BIT(32) if reg == 0xC0010292 else (BIT(22) | BIT(14) | BIT(6))
|
|
366
|
+
)
|
|
367
|
+
result = self.validator.check_msr()
|
|
368
|
+
self.assertTrue(result)
|
|
369
|
+
self.mock_db.record_prereq.assert_called_with("PC6 and CC6 enabled", "✅")
|
|
370
|
+
|
|
371
|
+
@patch("amd_debug.prerequisites.read_msr")
|
|
372
|
+
def test_check_msr_file_not_found(self, mock_read_msr):
|
|
373
|
+
"""Test check_msr when MSR file is not found"""
|
|
374
|
+
mock_read_msr.side_effect = FileNotFoundError
|
|
375
|
+
result = self.validator.check_msr()
|
|
376
|
+
self.assertFalse(result)
|
|
377
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
378
|
+
"Unable to check MSRs: MSR kernel module not loaded", "❌"
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
@patch("amd_debug.prerequisites.read_msr")
|
|
382
|
+
def test_check_msr_permission_error(self, mock_read_msr):
|
|
383
|
+
"""Test check_msr when there is a permission error"""
|
|
384
|
+
mock_read_msr.side_effect = PermissionError
|
|
385
|
+
result = self.validator.check_msr()
|
|
386
|
+
self.assertTrue(result)
|
|
387
|
+
self.mock_db.record_prereq.assert_called_with("MSR checks unavailable", "🚦")
|
|
388
|
+
|
|
389
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
390
|
+
@patch("amd_debug.prerequisites.os.path.exists", return_value=True)
|
|
391
|
+
def test_check_cpu_unsupported_model(self, mock_path_exists, mock_read_file):
|
|
392
|
+
"""Test check_cpu with an unsupported CPU model"""
|
|
393
|
+
self.validator.cpu_family = 0x19
|
|
394
|
+
self.validator.cpu_model = 0x08
|
|
395
|
+
mock_read_file.return_value = "7"
|
|
396
|
+
result = self.validator.check_cpu()
|
|
397
|
+
self.assertFalse(result)
|
|
398
|
+
self.assertTrue(
|
|
399
|
+
any(isinstance(f, UnsupportedModel) for f in self.validator.failures)
|
|
400
|
+
)
|
|
401
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
402
|
+
"This CPU model does not support hardware sleep over s2idle", "❌"
|
|
403
|
+
)
|
|
404
|
+
|
|
405
|
+
@patch("amd_debug.prerequisites.os.walk")
|
|
406
|
+
@patch(
|
|
407
|
+
"amd_debug.prerequisites.open",
|
|
408
|
+
new_callable=unittest.mock.mock_open,
|
|
409
|
+
read_data=b"mocked_data",
|
|
410
|
+
)
|
|
411
|
+
@patch("amd_debug.prerequisites.tempfile.mkdtemp", return_value="/mocked/tempdir")
|
|
412
|
+
@patch("amd_debug.prerequisites.subprocess.check_call")
|
|
413
|
+
@patch("amd_debug.prerequisites.shutil.rmtree")
|
|
414
|
+
def test_capture_acpi_success(
|
|
415
|
+
self, mock_rmtree, mock_check_call, mock_mkdtemp, mock_open, mock_walk
|
|
416
|
+
):
|
|
417
|
+
"""Test capture_acpi when ACPI tables are successfully captured"""
|
|
418
|
+
mock_walk.return_value = [
|
|
419
|
+
("/sys/firmware/acpi/tables", [], ["SSDT1", "IVRS", "OTHER"]),
|
|
420
|
+
]
|
|
421
|
+
result = self.validator.capture_acpi()
|
|
422
|
+
self.assertTrue(result)
|
|
423
|
+
mock_check_call.assert_called_with(
|
|
424
|
+
[
|
|
425
|
+
"iasl",
|
|
426
|
+
"-p",
|
|
427
|
+
"/mocked/tempdir/acpi",
|
|
428
|
+
"-d",
|
|
429
|
+
"/sys/firmware/acpi/tables/IVRS",
|
|
430
|
+
],
|
|
431
|
+
stdout=subprocess.DEVNULL,
|
|
432
|
+
stderr=subprocess.DEVNULL,
|
|
433
|
+
)
|
|
434
|
+
self.mock_db.record_debug_file.assert_called_with("/mocked/tempdir/acpi.dsl")
|
|
435
|
+
mock_rmtree.assert_called_with("/mocked/tempdir")
|
|
436
|
+
|
|
437
|
+
@patch("amd_debug.prerequisites.os.walk")
|
|
438
|
+
@patch(
|
|
439
|
+
"amd_debug.prerequisites.open",
|
|
440
|
+
new_callable=unittest.mock.mock_open,
|
|
441
|
+
read_data=b"mocked_data",
|
|
442
|
+
)
|
|
443
|
+
@patch("amd_debug.prerequisites.tempfile.mkdtemp", return_value="/mocked/tempdir")
|
|
444
|
+
@patch(
|
|
445
|
+
"amd_debug.prerequisites.subprocess.check_call",
|
|
446
|
+
side_effect=subprocess.CalledProcessError(1, "iasl"),
|
|
447
|
+
)
|
|
448
|
+
@patch("amd_debug.prerequisites.shutil.rmtree")
|
|
449
|
+
def test_capture_acpi_subprocess_error(
|
|
450
|
+
self, mock_rmtree, mock_check_call, mock_mkdtemp, mock_open, mock_walk
|
|
451
|
+
):
|
|
452
|
+
"""Test capture_acpi when subprocess.check_call raises an error"""
|
|
453
|
+
mock_walk.return_value = [
|
|
454
|
+
("/sys/firmware/acpi/tables", [], ["SSDT1", "IVRS", "OTHER"]),
|
|
455
|
+
]
|
|
456
|
+
result = self.validator.capture_acpi()
|
|
457
|
+
self.assertTrue(result)
|
|
458
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
459
|
+
"Failed to capture ACPI table: None", "👀"
|
|
460
|
+
)
|
|
461
|
+
mock_rmtree.assert_called_with("/mocked/tempdir")
|
|
462
|
+
|
|
463
|
+
@patch("amd_debug.prerequisites.os.walk")
|
|
464
|
+
@patch(
|
|
465
|
+
"amd_debug.prerequisites.open",
|
|
466
|
+
new_callable=unittest.mock.mock_open,
|
|
467
|
+
read_data=b"mocked_data",
|
|
468
|
+
)
|
|
469
|
+
@patch("amd_debug.prerequisites.tempfile.mkdtemp", return_value="/mocked/tempdir")
|
|
470
|
+
@patch("amd_debug.prerequisites.subprocess.check_call")
|
|
471
|
+
@patch("amd_debug.prerequisites.shutil.rmtree")
|
|
472
|
+
def test_capture_acpi_no_matching_files(
|
|
473
|
+
self, mock_rmtree, mock_check_call, mock_mkdtemp, mock_open, mock_walk
|
|
474
|
+
):
|
|
475
|
+
"""Test capture_acpi when no matching ACPI tables are found"""
|
|
476
|
+
mock_walk.return_value = [
|
|
477
|
+
("/sys/firmware/acpi/tables", [], ["OTHER"]),
|
|
478
|
+
]
|
|
479
|
+
result = self.validator.capture_acpi()
|
|
480
|
+
self.assertTrue(result)
|
|
481
|
+
mock_check_call.assert_not_called()
|
|
482
|
+
self.mock_db.record_debug_file.assert_not_called()
|
|
483
|
+
mock_rmtree.assert_not_called()
|
|
484
|
+
|
|
485
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
486
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
487
|
+
@patch("amd_debug.prerequisites.os.readlink")
|
|
488
|
+
def test_map_acpi_path_with_devices(
|
|
489
|
+
self, mock_readlink, mock_read_file, mock_path_exists
|
|
490
|
+
):
|
|
491
|
+
"""Test map_acpi_path with valid ACPI devices"""
|
|
492
|
+
mock_path_exists.side_effect = lambda p: "path" in p or "driver" in p
|
|
493
|
+
mock_read_file.side_effect = lambda p: "mocked_path" if "path" in p else "1"
|
|
494
|
+
mock_readlink.return_value = "/mocked/driver"
|
|
495
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
496
|
+
MagicMock(sys_path="/sys/devices/acpi/device1", sys_name="device1"),
|
|
497
|
+
MagicMock(sys_path="/sys/devices/acpi/device2", sys_name="device2"),
|
|
498
|
+
]
|
|
499
|
+
|
|
500
|
+
result = self.validator.map_acpi_path()
|
|
501
|
+
self.assertTrue(result)
|
|
502
|
+
self.mock_db.record_debug.assert_called_with(
|
|
503
|
+
"ACPI name: ACPI path [driver]\n"
|
|
504
|
+
"│ device1: mocked_path [driver]\n"
|
|
505
|
+
"└─device2: mocked_path [driver]\n"
|
|
506
|
+
)
|
|
507
|
+
|
|
508
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
509
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
510
|
+
def test_map_acpi_path_no_devices(self, mock_read_file, mock_path_exists):
|
|
511
|
+
"""Test map_acpi_path with no valid ACPI devices"""
|
|
512
|
+
mock_path_exists.return_value = False
|
|
513
|
+
self.mock_pyudev.list_devices.return_value = []
|
|
514
|
+
|
|
515
|
+
result = self.validator.map_acpi_path()
|
|
516
|
+
self.assertTrue(result)
|
|
517
|
+
|
|
518
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
519
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
520
|
+
def test_map_acpi_path_device_with_status_zero(
|
|
521
|
+
self, mock_read_file, mock_path_exists
|
|
522
|
+
):
|
|
523
|
+
"""Test map_acpi_path when a device has status 0"""
|
|
524
|
+
mock_path_exists.side_effect = lambda p: "path" in p or "status" in p
|
|
525
|
+
mock_read_file.side_effect = lambda p: "mocked_path" if "path" in p else "0"
|
|
526
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
527
|
+
MagicMock(sys_path="/sys/devices/acpi/device1", sys_name="device1")
|
|
528
|
+
]
|
|
529
|
+
|
|
530
|
+
result = self.validator.map_acpi_path()
|
|
531
|
+
self.assertTrue(result)
|
|
532
|
+
|
|
533
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
534
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
535
|
+
def test_map_acpi_path_device_without_driver(
|
|
536
|
+
self, mock_read_file, mock_path_exists
|
|
537
|
+
):
|
|
538
|
+
"""Test map_acpi_path when a device does not have a driver"""
|
|
539
|
+
mock_path_exists.side_effect = lambda p: "path" in p
|
|
540
|
+
mock_read_file.side_effect = lambda p: "mocked_path"
|
|
541
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
542
|
+
MagicMock(sys_path="/sys/devices/acpi/device1", sys_name="device1")
|
|
543
|
+
]
|
|
544
|
+
|
|
545
|
+
result = self.validator.map_acpi_path()
|
|
546
|
+
self.assertTrue(result)
|
|
547
|
+
self.mock_db.record_debug.assert_called_with(
|
|
548
|
+
"ACPI name: ACPI path [driver]\n└─device1: mocked_path [None]\n"
|
|
549
|
+
)
|
|
550
|
+
|
|
551
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
552
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
553
|
+
def test_capture_pci_acpi_with_acpi_path(self, mock_path_exists, mock_read_file):
|
|
554
|
+
"""Test capture_pci_acpi when ACPI paths exist for devices"""
|
|
555
|
+
mock_path_exists.side_effect = lambda p: "firmware_node/path" in p
|
|
556
|
+
mock_read_file.side_effect = lambda p: "mocked_acpi_path"
|
|
557
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
558
|
+
MagicMock(
|
|
559
|
+
properties={
|
|
560
|
+
"PCI_ID": "1234abcd",
|
|
561
|
+
"PCI_SLOT_NAME": "0000:00:1f.0",
|
|
562
|
+
"ID_PCI_SUBCLASS_FROM_DATABASE": "ISA bridge",
|
|
563
|
+
"ID_VENDOR_FROM_DATABASE": "Intel Corporation",
|
|
564
|
+
},
|
|
565
|
+
parent=MagicMock(subsystem="platform"),
|
|
566
|
+
sys_path="/sys/devices/pci0000:00/0000:00:1f.0",
|
|
567
|
+
)
|
|
568
|
+
]
|
|
569
|
+
|
|
570
|
+
self.validator.capture_pci_acpi()
|
|
571
|
+
self.mock_db.record_debug.assert_called_with(
|
|
572
|
+
"PCI devices\n"
|
|
573
|
+
"└─0000:00:1f.0 : Intel Corporation ISA bridge [1234abcd] : mocked_acpi_path\n"
|
|
574
|
+
)
|
|
575
|
+
|
|
576
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
577
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
578
|
+
def test_capture_pci_acpi_without_acpi_path(self, mock_path_exists, mock_read_file):
|
|
579
|
+
"""Test capture_pci_acpi when ACPI paths do not exist for devices"""
|
|
580
|
+
mock_path_exists.return_value = False
|
|
581
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
582
|
+
MagicMock(
|
|
583
|
+
properties={
|
|
584
|
+
"PCI_ID": "5678efgh",
|
|
585
|
+
"PCI_SLOT_NAME": "0000:01:00.0",
|
|
586
|
+
"ID_PCI_SUBCLASS_FROM_DATABASE": "VGA compatible controller",
|
|
587
|
+
"ID_VENDOR_FROM_DATABASE": "NVIDIA Corporation",
|
|
588
|
+
},
|
|
589
|
+
parent=MagicMock(subsystem="pci"),
|
|
590
|
+
sys_path="/sys/devices/pci0000:01/0000:01:00.0",
|
|
591
|
+
)
|
|
592
|
+
]
|
|
593
|
+
|
|
594
|
+
self.validator.capture_pci_acpi()
|
|
595
|
+
self.mock_db.record_debug.assert_called_with(
|
|
596
|
+
"PCI devices\n"
|
|
597
|
+
"└─0000:01:00.0 : NVIDIA Corporation VGA compatible controller [5678efgh]\n"
|
|
598
|
+
)
|
|
599
|
+
|
|
600
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
601
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
602
|
+
def test_capture_pci_acpi_multiple_devices(self, mock_path_exists, mock_read_file):
|
|
603
|
+
"""Test capture_pci_acpi with multiple devices"""
|
|
604
|
+
mock_path_exists.side_effect = lambda p: "firmware_node/path" in p
|
|
605
|
+
mock_read_file.side_effect = lambda p: "mocked_acpi_path" if "path" in p else ""
|
|
606
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
607
|
+
MagicMock(
|
|
608
|
+
properties={
|
|
609
|
+
"PCI_ID": "1234abcd",
|
|
610
|
+
"PCI_SLOT_NAME": "0000:00:1f.0",
|
|
611
|
+
"ID_PCI_SUBCLASS_FROM_DATABASE": "ISA bridge",
|
|
612
|
+
"ID_VENDOR_FROM_DATABASE": "Intel Corporation",
|
|
613
|
+
},
|
|
614
|
+
parent=MagicMock(subsystem="platform"),
|
|
615
|
+
sys_path="/sys/devices/pci0000:00/0000:00:1f.0",
|
|
616
|
+
),
|
|
617
|
+
MagicMock(
|
|
618
|
+
properties={
|
|
619
|
+
"PCI_ID": "5678efgh",
|
|
620
|
+
"PCI_SLOT_NAME": "0000:01:00.0",
|
|
621
|
+
"ID_PCI_SUBCLASS_FROM_DATABASE": "VGA compatible controller",
|
|
622
|
+
"ID_VENDOR_FROM_DATABASE": "NVIDIA Corporation",
|
|
623
|
+
},
|
|
624
|
+
parent=MagicMock(subsystem="pci"),
|
|
625
|
+
sys_path="/sys/devices/pci0000:01/0000:01:00.0",
|
|
626
|
+
),
|
|
627
|
+
]
|
|
628
|
+
|
|
629
|
+
self.validator.capture_pci_acpi()
|
|
630
|
+
self.mock_db.record_debug.assert_called_with(
|
|
631
|
+
"PCI devices\n"
|
|
632
|
+
"│ 0000:00:1f.0 : Intel Corporation ISA bridge [1234abcd] : mocked_acpi_path\n"
|
|
633
|
+
"└─0000:01:00.0 : NVIDIA Corporation VGA compatible controller [5678efgh] : mocked_acpi_path\n"
|
|
634
|
+
)
|
|
635
|
+
|
|
636
|
+
def test_capture_pci_acpi_no_devices(self):
|
|
637
|
+
"""Test capture_pci_acpi when no PCI devices are found"""
|
|
638
|
+
self.mock_pyudev.list_devices.return_value = []
|
|
639
|
+
|
|
640
|
+
self.validator.capture_pci_acpi()
|
|
641
|
+
self.mock_db.record_debug.assert_called_with("PCI devices\n")
|
|
642
|
+
|
|
643
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
644
|
+
def test_check_aspm_default_policy(self, mock_read_file):
|
|
645
|
+
"""Test check_aspm when the policy is set to default"""
|
|
646
|
+
mock_read_file.return_value = "[default]"
|
|
647
|
+
result = self.validator.check_aspm()
|
|
648
|
+
self.assertTrue(result)
|
|
649
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
650
|
+
"ASPM policy set to 'default'", "✅"
|
|
651
|
+
)
|
|
652
|
+
|
|
653
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
654
|
+
def test_check_aspm_non_default_policy(self, mock_read_file):
|
|
655
|
+
"""Test check_aspm when the policy is not set to default"""
|
|
656
|
+
mock_read_file.return_value = "[performance]"
|
|
657
|
+
result = self.validator.check_aspm()
|
|
658
|
+
self.assertFalse(result)
|
|
659
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
660
|
+
"ASPM policy set to [performance]", "❌"
|
|
661
|
+
)
|
|
662
|
+
self.assertTrue(any(isinstance(f, ASpmWrong) for f in self.validator.failures))
|
|
663
|
+
|
|
664
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
665
|
+
def test_check_aspm_empty_policy(self, mock_read_file):
|
|
666
|
+
"""Test check_aspm when the policy file is empty"""
|
|
667
|
+
mock_read_file.return_value = ""
|
|
668
|
+
result = self.validator.check_aspm()
|
|
669
|
+
self.assertFalse(result)
|
|
670
|
+
self.mock_db.record_prereq.assert_called_with("ASPM policy set to ", "❌")
|
|
671
|
+
self.assertTrue(any(isinstance(f, ASpmWrong) for f in self.validator.failures))
|
|
672
|
+
|
|
673
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
674
|
+
def test_check_aspm_file_not_found(self, mock_read_file):
|
|
675
|
+
"""Test check_aspm when the policy file is not found"""
|
|
676
|
+
mock_read_file.side_effect = FileNotFoundError
|
|
677
|
+
with self.assertRaises(FileNotFoundError):
|
|
678
|
+
self.validator.check_aspm()
|
|
679
|
+
|
|
680
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
681
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
682
|
+
def test_check_i2c_hid_no_devices(self, mock_read_file, mock_path_exists):
|
|
683
|
+
"""Test check_i2c_hid when no I2C HID devices are found"""
|
|
684
|
+
self.mock_pyudev.list_devices.return_value = []
|
|
685
|
+
result = self.validator.check_i2c_hid()
|
|
686
|
+
self.assertTrue(result)
|
|
687
|
+
|
|
688
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
689
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
690
|
+
def test_check_i2c_hid_with_devices(self, mock_read_file, mock_path_exists):
|
|
691
|
+
"""Test check_i2c_hid when I2C HID devices are found"""
|
|
692
|
+
mock_path_exists.side_effect = (
|
|
693
|
+
lambda p: "firmware_node/path" in p or "firmware_node/hid" in p
|
|
694
|
+
)
|
|
695
|
+
mock_read_file.side_effect = lambda p: (
|
|
696
|
+
"mocked_path" if "path" in p else "mocked_hid"
|
|
697
|
+
)
|
|
698
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
699
|
+
MagicMock(
|
|
700
|
+
properties={"NAME": "I2C Device 1"},
|
|
701
|
+
find_parent=MagicMock(
|
|
702
|
+
return_value=MagicMock(sys_path="/sys/devices/i2c-1")
|
|
703
|
+
),
|
|
704
|
+
),
|
|
705
|
+
MagicMock(
|
|
706
|
+
properties={"NAME": "I2C Device 2"},
|
|
707
|
+
find_parent=MagicMock(
|
|
708
|
+
return_value=MagicMock(sys_path="/sys/devices/i2c-2")
|
|
709
|
+
),
|
|
710
|
+
),
|
|
711
|
+
]
|
|
712
|
+
|
|
713
|
+
result = self.validator.check_i2c_hid()
|
|
714
|
+
self.assertTrue(result)
|
|
715
|
+
self.mock_db.record_debug.assert_called_with(
|
|
716
|
+
"I2C HID devices:\n"
|
|
717
|
+
"│ I2C Device 1 [mocked_hid] : mocked_path\n"
|
|
718
|
+
"└─I2C Device 2 [mocked_hid] : mocked_path\n"
|
|
719
|
+
)
|
|
720
|
+
|
|
721
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
722
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
723
|
+
def test_check_i2c_hid_with_buggy_device(self, mock_read_file, mock_path_exists):
|
|
724
|
+
"""Test check_i2c_hid when a buggy I2C HID device is found"""
|
|
725
|
+
mock_path_exists.side_effect = (
|
|
726
|
+
lambda p: "firmware_node/path" in p or "firmware_node/hid" in p
|
|
727
|
+
)
|
|
728
|
+
mock_read_file.side_effect = lambda p: (
|
|
729
|
+
"mocked_path" if "path" in p else "mocked_hid"
|
|
730
|
+
)
|
|
731
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
732
|
+
MagicMock(
|
|
733
|
+
properties={"NAME": "IDEA5002"},
|
|
734
|
+
find_parent=MagicMock(
|
|
735
|
+
return_value=MagicMock(
|
|
736
|
+
sys_path="/sys/devices/i2c-1", driver="mock_driver"
|
|
737
|
+
)
|
|
738
|
+
),
|
|
739
|
+
)
|
|
740
|
+
]
|
|
741
|
+
|
|
742
|
+
result = self.validator.check_i2c_hid()
|
|
743
|
+
self.assertFalse(result)
|
|
744
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
745
|
+
"IDEA5002 may cause spurious wakeups", "❌"
|
|
746
|
+
)
|
|
747
|
+
self.assertTrue(any(isinstance(f, I2CHidBug) for f in self.validator.failures))
|
|
748
|
+
|
|
749
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
750
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
751
|
+
def test_check_i2c_hid_missing_firmware_node(
|
|
752
|
+
self, mock_read_file, mock_path_exists
|
|
753
|
+
):
|
|
754
|
+
"""Test check_i2c_hid when firmware_node paths are missing"""
|
|
755
|
+
mock_path_exists.return_value = False
|
|
756
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
757
|
+
MagicMock(
|
|
758
|
+
properties={"NAME": "I2C Device 1"},
|
|
759
|
+
find_parent=MagicMock(
|
|
760
|
+
return_value=MagicMock(sys_path="/sys/devices/i2c-1")
|
|
761
|
+
),
|
|
762
|
+
)
|
|
763
|
+
]
|
|
764
|
+
|
|
765
|
+
result = self.validator.check_i2c_hid()
|
|
766
|
+
self.assertTrue(result)
|
|
767
|
+
self.mock_db.record_debug.assert_called_with(
|
|
768
|
+
"I2C HID devices:\n└─I2C Device 1 [] : \n"
|
|
769
|
+
)
|
|
770
|
+
|
|
771
|
+
@patch("amd_debug.prerequisites.os.path.exists", return_value=True)
|
|
772
|
+
@patch(
|
|
773
|
+
"amd_debug.prerequisites.open",
|
|
774
|
+
new_callable=unittest.mock.mock_open,
|
|
775
|
+
read_data=b"\x00" * 0x70 + b"\x20\x00\x00\x00",
|
|
776
|
+
)
|
|
777
|
+
@patch("amd_debug.prerequisites.struct.unpack", return_value=(0x00200000,))
|
|
778
|
+
def test_check_fadt_supports_low_power_idle(
|
|
779
|
+
self, mock_unpack, mock_open, mock_path_exists
|
|
780
|
+
):
|
|
781
|
+
"""Test check_fadt when ACPI FADT supports Low-power S0 idle"""
|
|
782
|
+
self.mock_kernel_log.match_line.return_value = False
|
|
783
|
+
result = self.validator.check_fadt()
|
|
784
|
+
self.assertTrue(result)
|
|
785
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
786
|
+
"ACPI FADT supports Low-power S0 idle", "✅"
|
|
787
|
+
)
|
|
788
|
+
|
|
789
|
+
@patch("amd_debug.prerequisites.os.path.exists", return_value=True)
|
|
790
|
+
@patch(
|
|
791
|
+
"amd_debug.prerequisites.open",
|
|
792
|
+
new_callable=unittest.mock.mock_open,
|
|
793
|
+
read_data=b"\x00" * 0x70 + b"\x00\x00\x00\x00",
|
|
794
|
+
)
|
|
795
|
+
@patch("amd_debug.prerequisites.struct.unpack", return_value=(0x00000000,))
|
|
796
|
+
def test_check_fadt_does_not_support_low_power_idle(
|
|
797
|
+
self, mock_unpack, mock_open, mock_path_exists
|
|
798
|
+
):
|
|
799
|
+
"""Test check_fadt when ACPI FADT does not support Low-power S0 idle"""
|
|
800
|
+
self.mock_kernel_log.match_line.return_value = False
|
|
801
|
+
result = self.validator.check_fadt()
|
|
802
|
+
self.assertFalse(result)
|
|
803
|
+
self.assertTrue(any(isinstance(f, FadtWrong) for f in self.validator.failures))
|
|
804
|
+
|
|
805
|
+
@patch("amd_debug.prerequisites.os.path.exists", return_value=False)
|
|
806
|
+
def test_check_fadt_file_not_found(self, mock_path_exists):
|
|
807
|
+
"""Test check_fadt when FADT file is not found"""
|
|
808
|
+
self.mock_kernel_log.match_line.return_value = False
|
|
809
|
+
result = self.validator.check_fadt()
|
|
810
|
+
self.assertTrue(result)
|
|
811
|
+
self.mock_db.record_prereq.assert_called_with("FADT check unavailable", "🚦")
|
|
812
|
+
|
|
813
|
+
@patch("amd_debug.prerequisites.os.path.exists", return_value=True)
|
|
814
|
+
@patch("amd_debug.prerequisites.open", side_effect=PermissionError)
|
|
815
|
+
def test_check_fadt_permission_error(self, mock_open, mock_path_exists):
|
|
816
|
+
"""Test check_fadt when there is a permission error accessing the FADT file"""
|
|
817
|
+
self.mock_kernel_log.match_line.return_value = False
|
|
818
|
+
result = self.validator.check_fadt()
|
|
819
|
+
self.assertTrue(result)
|
|
820
|
+
self.mock_db.record_prereq.assert_called_with("FADT check unavailable", "🚦")
|
|
821
|
+
|
|
822
|
+
def test_check_fadt_kernel_log_match(self):
|
|
823
|
+
"""Test check_fadt when kernel log contains the required message"""
|
|
824
|
+
self.mock_kernel_log.match_line.return_value = True
|
|
825
|
+
result = self.validator.check_fadt()
|
|
826
|
+
self.assertTrue(result)
|
|
827
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
828
|
+
"ACPI FADT supports Low-power S0 idle", "✅"
|
|
829
|
+
)
|
|
830
|
+
|
|
831
|
+
@patch(
|
|
832
|
+
"amd_debug.prerequisites.open",
|
|
833
|
+
new_callable=unittest.mock.mock_open,
|
|
834
|
+
read_data=b"\x00" * 0x70 + b"\x00\x00\x00\x00",
|
|
835
|
+
)
|
|
836
|
+
def test_check_fadt_no_kernel_log(self, _mock_open):
|
|
837
|
+
"""Test check_fadt when kernel log is not available"""
|
|
838
|
+
self.validator.kernel_log = None
|
|
839
|
+
result = self.validator.check_fadt()
|
|
840
|
+
self.assertFalse(result)
|
|
841
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
842
|
+
"ACPI FADT doesn't support Low-power S0 idle", "❌"
|
|
843
|
+
)
|
|
844
|
+
|
|
845
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
846
|
+
def test_get_cpu_vendor_all_fields_present(self, mock_read_file):
|
|
847
|
+
"""Test get_cpu_vendor when all fields are present in /proc/cpuinfo"""
|
|
848
|
+
mock_read_file.return_value = (
|
|
849
|
+
"vendor_id\t: AuthenticAMD\n"
|
|
850
|
+
"cpu family\t: 23\n"
|
|
851
|
+
"model\t\t: 1\n"
|
|
852
|
+
"model name\t: AMD Ryzen 7 3700X\n"
|
|
853
|
+
)
|
|
854
|
+
vendor = self.validator.get_cpu_vendor()
|
|
855
|
+
self.assertEqual(vendor, "AuthenticAMD")
|
|
856
|
+
self.assertEqual(self.validator.cpu_family, 23)
|
|
857
|
+
self.assertEqual(self.validator.cpu_model, 1)
|
|
858
|
+
self.assertEqual(self.validator.cpu_model_string, "AMD Ryzen 7 3700X")
|
|
859
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
860
|
+
"AMD Ryzen 7 3700X (family 17 model 1)", "💻"
|
|
861
|
+
)
|
|
862
|
+
|
|
863
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
864
|
+
def test_get_cpu_vendor_missing_model_name(self, mock_read_file):
|
|
865
|
+
"""Test get_cpu_vendor when model name is missing in /proc/cpuinfo"""
|
|
866
|
+
mock_read_file.return_value = (
|
|
867
|
+
"vendor_id\t: AuthenticAMD\n" "cpu family\t: 23\n" "model\t\t: 1\n"
|
|
868
|
+
)
|
|
869
|
+
vendor = self.validator.get_cpu_vendor()
|
|
870
|
+
self.assertEqual(vendor, "AuthenticAMD")
|
|
871
|
+
self.assertEqual(self.validator.cpu_family, 23)
|
|
872
|
+
self.assertEqual(self.validator.cpu_model, 1)
|
|
873
|
+
self.assertIsNone(self.validator.cpu_model_string)
|
|
874
|
+
self.mock_db.record_prereq.assert_not_called()
|
|
875
|
+
|
|
876
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
877
|
+
def test_get_cpu_vendor_missing_vendor_id(self, mock_read_file):
|
|
878
|
+
"""Test get_cpu_vendor when vendor_id is missing in /proc/cpuinfo"""
|
|
879
|
+
mock_read_file.return_value = (
|
|
880
|
+
"cpu family\t: 23\n" "model\t\t: 1\n" "model name\t: AMD Ryzen 7 3700X\n"
|
|
881
|
+
)
|
|
882
|
+
vendor = self.validator.get_cpu_vendor()
|
|
883
|
+
self.assertEqual(vendor, "")
|
|
884
|
+
self.assertEqual(self.validator.cpu_family, 23)
|
|
885
|
+
self.assertEqual(self.validator.cpu_model, 1)
|
|
886
|
+
self.assertEqual(self.validator.cpu_model_string, "AMD Ryzen 7 3700X")
|
|
887
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
888
|
+
"AMD Ryzen 7 3700X (family 17 model 1)", "💻"
|
|
889
|
+
)
|
|
890
|
+
|
|
891
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
892
|
+
def test_get_cpu_vendor_missing_cpu_family(self, mock_read_file):
|
|
893
|
+
"""Test get_cpu_vendor when cpu family is missing in /proc/cpuinfo"""
|
|
894
|
+
mock_read_file.return_value = (
|
|
895
|
+
"vendor_id\t: AuthenticAMD\n"
|
|
896
|
+
"model\t\t: 1\n"
|
|
897
|
+
"model name\t: AMD Ryzen 7 3700X\n"
|
|
898
|
+
)
|
|
899
|
+
vendor = self.validator.get_cpu_vendor()
|
|
900
|
+
self.assertEqual(vendor, "AuthenticAMD")
|
|
901
|
+
self.assertIsNone(self.validator.cpu_family)
|
|
902
|
+
self.assertEqual(self.validator.cpu_model, 1)
|
|
903
|
+
self.assertEqual(self.validator.cpu_model_string, "AMD Ryzen 7 3700X")
|
|
904
|
+
self.mock_db.record_prereq.assert_not_called()
|
|
905
|
+
|
|
906
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
907
|
+
def test_get_cpu_vendor_missing_model(self, mock_read_file):
|
|
908
|
+
"""Test get_cpu_vendor when model is missing in /proc/cpuinfo"""
|
|
909
|
+
mock_read_file.return_value = (
|
|
910
|
+
"vendor_id\t: AuthenticAMD\n"
|
|
911
|
+
"cpu family\t: 23\n"
|
|
912
|
+
"model name\t: AMD Ryzen 7 3700X\n"
|
|
913
|
+
)
|
|
914
|
+
vendor = self.validator.get_cpu_vendor()
|
|
915
|
+
self.assertEqual(vendor, "AuthenticAMD")
|
|
916
|
+
self.assertEqual(self.validator.cpu_family, 23)
|
|
917
|
+
self.assertIsNone(self.validator.cpu_model)
|
|
918
|
+
self.assertEqual(self.validator.cpu_model_string, "AMD Ryzen 7 3700X")
|
|
919
|
+
self.mock_db.record_prereq.assert_not_called()
|
|
920
|
+
|
|
921
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
922
|
+
def test_get_cpu_vendor_empty_cpuinfo(self, mock_read_file):
|
|
923
|
+
"""Test get_cpu_vendor when /proc/cpuinfo is empty"""
|
|
924
|
+
mock_read_file.return_value = ""
|
|
925
|
+
vendor = self.validator.get_cpu_vendor()
|
|
926
|
+
self.assertEqual(vendor, "")
|
|
927
|
+
self.assertIsNone(self.validator.cpu_family)
|
|
928
|
+
self.assertIsNone(self.validator.cpu_model)
|
|
929
|
+
self.assertIsNone(self.validator.cpu_model_string)
|
|
930
|
+
self.mock_db.record_prereq.assert_not_called()
|
|
931
|
+
|
|
932
|
+
def test_check_usb4_driver_missing(self):
|
|
933
|
+
"""Test check_usb4 when the thunderbolt driver is missing"""
|
|
934
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
935
|
+
MagicMock(
|
|
936
|
+
properties={
|
|
937
|
+
"PCI_SLOT_NAME": "0000:00:1d.0",
|
|
938
|
+
"DRIVER": None,
|
|
939
|
+
}
|
|
940
|
+
)
|
|
941
|
+
]
|
|
942
|
+
result = self.validator.check_usb4()
|
|
943
|
+
self.assertFalse(result)
|
|
944
|
+
self.assertTrue(
|
|
945
|
+
any(isinstance(f, MissingThunderbolt) for f in self.validator.failures)
|
|
946
|
+
)
|
|
947
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
948
|
+
"USB4 driver `thunderbolt` missing", "❌"
|
|
949
|
+
)
|
|
950
|
+
|
|
951
|
+
def test_check_usb4_driver_present(self):
|
|
952
|
+
"""Test check_usb4 when the thunderbolt driver is present"""
|
|
953
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
954
|
+
MagicMock(
|
|
955
|
+
properties={
|
|
956
|
+
"PCI_SLOT_NAME": "0000:00:1d.0",
|
|
957
|
+
"DRIVER": "thunderbolt",
|
|
958
|
+
}
|
|
959
|
+
)
|
|
960
|
+
]
|
|
961
|
+
result = self.validator.check_usb4()
|
|
962
|
+
self.assertTrue(result)
|
|
963
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
964
|
+
"USB4 driver `thunderbolt` bound to 0000:00:1d.0", "✅"
|
|
965
|
+
)
|
|
966
|
+
|
|
967
|
+
def test_check_usb4_no_devices(self):
|
|
968
|
+
"""Test check_usb4 when no USB4 devices are found"""
|
|
969
|
+
self.mock_pyudev.list_devices.return_value = []
|
|
970
|
+
result = self.validator.check_usb4()
|
|
971
|
+
self.assertTrue(result)
|
|
972
|
+
self.mock_db.record_prereq.assert_not_called()
|
|
973
|
+
|
|
974
|
+
@patch("amd_debug.prerequisites.os.path.exists", return_value=False)
|
|
975
|
+
def test_capture_smbios_not_setup(self, mock_path_exists):
|
|
976
|
+
"""Test capture_smbios when DMI data is not set up"""
|
|
977
|
+
self.validator.capture_smbios()
|
|
978
|
+
self.mock_db.record_prereq.assert_called_with("DMI data was not setup", "🚦")
|
|
979
|
+
self.assertTrue(
|
|
980
|
+
any(isinstance(f, DmiNotSetup) for f in self.validator.failures)
|
|
981
|
+
)
|
|
982
|
+
|
|
983
|
+
@patch("amd_debug.prerequisites.os.walk")
|
|
984
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
985
|
+
@patch("amd_debug.prerequisites.os.path.exists", return_value=True)
|
|
986
|
+
def test_capture_smbios_success(
|
|
987
|
+
self, mock_path_exists, mock_read_file, mock_os_walk
|
|
988
|
+
):
|
|
989
|
+
"""Test capture_smbios when DMI data is successfully captured"""
|
|
990
|
+
mock_os_walk.return_value = [
|
|
991
|
+
(
|
|
992
|
+
"/sys/class/dmi/id",
|
|
993
|
+
[],
|
|
994
|
+
["sys_vendor", "product_name", "product_family", "chassis_type"],
|
|
995
|
+
)
|
|
996
|
+
]
|
|
997
|
+
mock_read_file.side_effect = lambda path: {
|
|
998
|
+
"/sys/class/dmi/id/sys_vendor": "MockVendor",
|
|
999
|
+
"/sys/class/dmi/id/product_name": "MockProduct",
|
|
1000
|
+
"/sys/class/dmi/id/product_family": "MockFamily",
|
|
1001
|
+
"/sys/class/dmi/id/chassis_type": "Desktop",
|
|
1002
|
+
}.get(path, "")
|
|
1003
|
+
result = self.validator.capture_smbios()
|
|
1004
|
+
self.assertTrue(result)
|
|
1005
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
1006
|
+
"MockVendor MockProduct (MockFamily)", "💻"
|
|
1007
|
+
)
|
|
1008
|
+
self.mock_db.record_debug.assert_called_with(
|
|
1009
|
+
"DMI data:\nchassis_type: Desktop\n"
|
|
1010
|
+
)
|
|
1011
|
+
|
|
1012
|
+
@patch("amd_debug.prerequisites.os.walk")
|
|
1013
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
1014
|
+
@patch("amd_debug.prerequisites.os.path.exists", return_value=True)
|
|
1015
|
+
def test_capture_smbios_filtered_keys(
|
|
1016
|
+
self, _mock_path_exists, mock_read_file, mock_os_walk
|
|
1017
|
+
):
|
|
1018
|
+
"""Test capture_smbios when filtered keys are present"""
|
|
1019
|
+
mock_os_walk.return_value = [
|
|
1020
|
+
(
|
|
1021
|
+
"/sys/class/dmi/id",
|
|
1022
|
+
[],
|
|
1023
|
+
["sys_vendor", "product_name", "product_family", "product_serial"],
|
|
1024
|
+
)
|
|
1025
|
+
]
|
|
1026
|
+
mock_read_file.side_effect = lambda path: {
|
|
1027
|
+
"/sys/class/dmi/id/sys_vendor": "MockVendor",
|
|
1028
|
+
"/sys/class/dmi/id/product_name": "MockProduct",
|
|
1029
|
+
"/sys/class/dmi/id/product_family": "MockFamily",
|
|
1030
|
+
"/sys/class/dmi/id/product_serial": "12345",
|
|
1031
|
+
}.get(path, "")
|
|
1032
|
+
result = self.validator.capture_smbios()
|
|
1033
|
+
self.assertTrue(result)
|
|
1034
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
1035
|
+
"MockVendor MockProduct (MockFamily)", "💻"
|
|
1036
|
+
)
|
|
1037
|
+
self.mock_db.record_debug.assert_called_with("DMI data:\n")
|
|
1038
|
+
|
|
1039
|
+
@patch("amd_debug.prerequisites.os.walk")
|
|
1040
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
1041
|
+
@patch("amd_debug.prerequisites.os.path.exists", return_value=True)
|
|
1042
|
+
def test_capture_smbios_missing_keys(
|
|
1043
|
+
self, _mock_path_exists, mock_read_file, mock_os_walk
|
|
1044
|
+
):
|
|
1045
|
+
"""Test capture_smbios when required keys are missing"""
|
|
1046
|
+
mock_os_walk.return_value = [("/sys/class/dmi/id", [], ["chassis_type"])]
|
|
1047
|
+
mock_read_file.side_effect = lambda path: {
|
|
1048
|
+
"/sys/class/dmi/id/chassis_type": "Desktop",
|
|
1049
|
+
}.get(path, "")
|
|
1050
|
+
result = self.validator.capture_smbios()
|
|
1051
|
+
self.assertTrue(
|
|
1052
|
+
any(isinstance(f, DmiNotSetup) for f in self.validator.failures)
|
|
1053
|
+
)
|
|
1054
|
+
self.assertFalse(result)
|
|
1055
|
+
|
|
1056
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
1057
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
1058
|
+
def test_check_lps0_enabled(self, mock_read_file, mock_path_exists):
|
|
1059
|
+
"""Test check_lps0 when LPS0 is enabled"""
|
|
1060
|
+
mock_path_exists.return_value = True
|
|
1061
|
+
mock_read_file.return_value = "N"
|
|
1062
|
+
result = self.validator.check_lps0()
|
|
1063
|
+
self.assertTrue(result)
|
|
1064
|
+
self.mock_db.record_prereq.assert_called_with("LPS0 _DSM enabled", "✅")
|
|
1065
|
+
|
|
1066
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
1067
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
1068
|
+
def test_check_lps0_disabled(self, mock_read_file, mock_path_exists):
|
|
1069
|
+
"""Test check_lps0 when LPS0 is disabled"""
|
|
1070
|
+
mock_path_exists.return_value = True
|
|
1071
|
+
mock_read_file.return_value = "Y"
|
|
1072
|
+
result = self.validator.check_lps0()
|
|
1073
|
+
self.assertFalse(result)
|
|
1074
|
+
self.mock_db.record_prereq.assert_called_with("LPS0 _DSM disabled", "❌")
|
|
1075
|
+
|
|
1076
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
1077
|
+
def test_check_lps0_not_found(self, mock_path_exists):
|
|
1078
|
+
"""Test check_lps0 when LPS0 parameter is not found"""
|
|
1079
|
+
mock_path_exists.return_value = False
|
|
1080
|
+
result = self.validator.check_lps0()
|
|
1081
|
+
self.assertFalse(result)
|
|
1082
|
+
self.mock_db.record_prereq.assert_called_with("LPS0 _DSM not found", "👀")
|
|
1083
|
+
|
|
1084
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
1085
|
+
@patch(
|
|
1086
|
+
"builtins.open",
|
|
1087
|
+
new_callable=unittest.mock.mock_open,
|
|
1088
|
+
read_data="ignore_wake_value",
|
|
1089
|
+
)
|
|
1090
|
+
def test_capture_disabled_pins_with_parameters(self, _mock_open, mock_path_exists):
|
|
1091
|
+
"""Test capture_disabled_pins when parameters are present and configured"""
|
|
1092
|
+
mock_path_exists.side_effect = (
|
|
1093
|
+
lambda path: "ignore_wake" in path or "ignore_interrupt" in path
|
|
1094
|
+
)
|
|
1095
|
+
self.validator.capture_disabled_pins()
|
|
1096
|
+
self.mock_db.record_debug.assert_called_with(
|
|
1097
|
+
"Disabled pins:\n/sys/module/gpiolib_acpi/parameters/ignore_wake is configured to ignore_wake_value\n/sys/module/gpiolib_acpi/parameters/ignore_interrupt is configured to ignore_wake_value\n"
|
|
1098
|
+
)
|
|
1099
|
+
|
|
1100
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
1101
|
+
@patch("builtins.open", new_callable=unittest.mock.mock_open, read_data="(null)")
|
|
1102
|
+
def test_capture_disabled_pins_with_null_values(self, _mock_open, mock_path_exists):
|
|
1103
|
+
mock_path_exists.side_effect = (
|
|
1104
|
+
lambda path: "ignore_wake" in path or "ignore_interrupt" in path
|
|
1105
|
+
)
|
|
1106
|
+
self.validator.capture_disabled_pins()
|
|
1107
|
+
self.mock_db.record_debug.assert_not_called()
|
|
1108
|
+
|
|
1109
|
+
@patch("amd_debug.prerequisites.os.path.exists", return_value=False)
|
|
1110
|
+
def test_capture_disabled_pins_no_parameters(self, _mock_path_exists):
|
|
1111
|
+
"""Test capture_disabled_pins when parameters are not present"""
|
|
1112
|
+
self.validator.capture_disabled_pins()
|
|
1113
|
+
self.mock_db.record_debug.assert_not_called()
|
|
1114
|
+
|
|
1115
|
+
@patch("amd_debug.prerequisites.os.listdir")
|
|
1116
|
+
@patch("amd_debug.prerequisites.os.path.isdir")
|
|
1117
|
+
@patch("amd_debug.prerequisites.WakeIRQ")
|
|
1118
|
+
def test_capture_irq_with_irqs(self, MockWakeIRQ, mock_isdir, mock_listdir):
|
|
1119
|
+
"""Test capture_irq when IRQ directories are present"""
|
|
1120
|
+
mock_listdir.return_value = ["1", "2", "3"]
|
|
1121
|
+
mock_isdir.side_effect = lambda path: path.endswith(("1", "2", "3"))
|
|
1122
|
+
MockWakeIRQ.side_effect = lambda irq, pyudev: f"WakeIRQ-{irq}"
|
|
1123
|
+
|
|
1124
|
+
result = self.validator.capture_irq()
|
|
1125
|
+
|
|
1126
|
+
self.assertTrue(result)
|
|
1127
|
+
self.assertEqual(
|
|
1128
|
+
self.validator.irqs,
|
|
1129
|
+
[[1, "WakeIRQ-1"], [2, "WakeIRQ-2"], [3, "WakeIRQ-3"]],
|
|
1130
|
+
)
|
|
1131
|
+
self.mock_db.record_debug.assert_any_call("Interrupts")
|
|
1132
|
+
self.mock_db.record_debug.assert_any_call("│ 1: WakeIRQ-1")
|
|
1133
|
+
self.mock_db.record_debug.assert_any_call("│ 2: WakeIRQ-2")
|
|
1134
|
+
self.mock_db.record_debug.assert_any_call("└─3: WakeIRQ-3")
|
|
1135
|
+
|
|
1136
|
+
@patch("amd_debug.prerequisites.os.listdir")
|
|
1137
|
+
@patch("amd_debug.prerequisites.os.path.isdir")
|
|
1138
|
+
def test_capture_irq_no_irqs(self, mock_isdir, mock_listdir):
|
|
1139
|
+
"""Test capture_irq when no IRQ directories are present"""
|
|
1140
|
+
mock_listdir.return_value = []
|
|
1141
|
+
mock_isdir.return_value = False
|
|
1142
|
+
|
|
1143
|
+
result = self.validator.capture_irq()
|
|
1144
|
+
|
|
1145
|
+
self.assertTrue(result)
|
|
1146
|
+
self.assertEqual(self.validator.irqs, [])
|
|
1147
|
+
self.mock_db.record_debug.assert_called_with("Interrupts")
|
|
1148
|
+
|
|
1149
|
+
@patch("amd_debug.prerequisites.os.listdir")
|
|
1150
|
+
@patch("amd_debug.prerequisites.os.path.isdir")
|
|
1151
|
+
@patch("amd_debug.prerequisites.WakeIRQ")
|
|
1152
|
+
def test_capture_irq_mixed_entries(self, mock_wake_irq, mock_isdir, mock_listdir):
|
|
1153
|
+
"""Test capture_irq with mixed valid and invalid IRQ directories"""
|
|
1154
|
+
mock_listdir.return_value = ["1", "invalid", "2"]
|
|
1155
|
+
mock_isdir.side_effect = lambda path: path.endswith(("1", "2"))
|
|
1156
|
+
mock_wake_irq.side_effect = lambda irq, pyudev: f"WakeIRQ-{irq}"
|
|
1157
|
+
|
|
1158
|
+
result = self.validator.capture_irq()
|
|
1159
|
+
|
|
1160
|
+
self.assertTrue(result)
|
|
1161
|
+
self.assertEqual(
|
|
1162
|
+
self.validator.irqs,
|
|
1163
|
+
[[1, "WakeIRQ-1"], [2, "WakeIRQ-2"]],
|
|
1164
|
+
)
|
|
1165
|
+
self.mock_db.record_debug.assert_any_call("Interrupts")
|
|
1166
|
+
self.mock_db.record_debug.assert_any_call("│ 1: WakeIRQ-1")
|
|
1167
|
+
self.mock_db.record_debug.assert_any_call("└─2: WakeIRQ-2")
|
|
1168
|
+
|
|
1169
|
+
@patch("amd_debug.prerequisites.os.path.exists", return_value=True)
|
|
1170
|
+
@patch("builtins.open", new_callable=unittest.mock.mock_open)
|
|
1171
|
+
def test_check_permissions_success(self, _mock_open, _mock_path_exists):
|
|
1172
|
+
"""Test check_permissions when the user has write permissions"""
|
|
1173
|
+
result = self.validator.check_permissions()
|
|
1174
|
+
self.assertTrue(result)
|
|
1175
|
+
self.mock_db.record_prereq.assert_not_called()
|
|
1176
|
+
|
|
1177
|
+
@patch("amd_debug.prerequisites.os.path.exists", return_value=True)
|
|
1178
|
+
@patch("builtins.open", side_effect=PermissionError)
|
|
1179
|
+
def test_check_permissions_permission_error(self, _mock_open, _mock_path_exists):
|
|
1180
|
+
"""Test check_permissions when the user lacks write permissions"""
|
|
1181
|
+
result = self.validator.check_permissions()
|
|
1182
|
+
self.assertFalse(result)
|
|
1183
|
+
|
|
1184
|
+
@patch("amd_debug.prerequisites.os.path.exists", return_value=False)
|
|
1185
|
+
@patch("builtins.open", side_effect=FileNotFoundError)
|
|
1186
|
+
def test_check_permissions_file_not_found(self, _mock_open, _mock_path_exists):
|
|
1187
|
+
"""Test check_permissions when the /sys/power/state file is not found"""
|
|
1188
|
+
result = self.validator.check_permissions()
|
|
1189
|
+
self.assertFalse(result)
|
|
1190
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
1191
|
+
"Kernel doesn't support power management", "❌"
|
|
1192
|
+
)
|
|
1193
|
+
|
|
1194
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
1195
|
+
@patch("amd_debug.prerequisites.os.path.exists", return_value=True)
|
|
1196
|
+
def test_check_pinctrl_amd_driver_loaded(self, mock_path_exists, mock_read_file):
|
|
1197
|
+
"""Test check_pinctrl_amd when the driver is loaded and debug information is available"""
|
|
1198
|
+
mock_read_file.return_value = (
|
|
1199
|
+
"trigger\n" "edge\n" "level\n" "WAKE_INT_MASTER_REG: 8000\n"
|
|
1200
|
+
)
|
|
1201
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
1202
|
+
MagicMock(properties={"DRIVER": "amd_gpio"})
|
|
1203
|
+
]
|
|
1204
|
+
|
|
1205
|
+
result = self.validator.check_pinctrl_amd()
|
|
1206
|
+
self.assertTrue(result)
|
|
1207
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
1208
|
+
"GPIO driver `pinctrl_amd` available", "✅"
|
|
1209
|
+
)
|
|
1210
|
+
self.mock_db.record_debug.assert_called_with("trigger\nedge\nlevel\n")
|
|
1211
|
+
|
|
1212
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
1213
|
+
@patch("amd_debug.prerequisites.os.path.exists", return_value=True)
|
|
1214
|
+
def test_check_pinctrl_amd_driver_loaded_with_permission_error(
|
|
1215
|
+
self, mock_path_exists, mock_read_file
|
|
1216
|
+
):
|
|
1217
|
+
"""Test check_pinctrl_amd when the driver is loaded but debug file cannot be read due to permission error"""
|
|
1218
|
+
mock_read_file.side_effect = PermissionError
|
|
1219
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
1220
|
+
MagicMock(properties={"DRIVER": "amd_gpio"})
|
|
1221
|
+
]
|
|
1222
|
+
|
|
1223
|
+
result = self.validator.check_pinctrl_amd()
|
|
1224
|
+
self.assertTrue(result)
|
|
1225
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
1226
|
+
"GPIO driver `pinctrl_amd` available", "✅"
|
|
1227
|
+
)
|
|
1228
|
+
self.mock_db.record_debug.assert_called_with(
|
|
1229
|
+
"Unable to capture /sys/kernel/debug/gpio"
|
|
1230
|
+
)
|
|
1231
|
+
|
|
1232
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
1233
|
+
@patch("amd_debug.prerequisites.os.path.exists", return_value=True)
|
|
1234
|
+
def test_check_pinctrl_amd_unserviced_gpio(self, _mock_path_exists, mock_read_file):
|
|
1235
|
+
"""Test check_pinctrl_amd when unserviced GPIO is detected"""
|
|
1236
|
+
mock_read_file.return_value = "🔥"
|
|
1237
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
1238
|
+
MagicMock(properties={"DRIVER": "amd_gpio"})
|
|
1239
|
+
]
|
|
1240
|
+
|
|
1241
|
+
result = self.validator.check_pinctrl_amd()
|
|
1242
|
+
self.assertFalse(result)
|
|
1243
|
+
self.assertTrue(
|
|
1244
|
+
any(isinstance(f, UnservicedGpio) for f in self.validator.failures)
|
|
1245
|
+
)
|
|
1246
|
+
|
|
1247
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
1248
|
+
@patch("amd_debug.prerequisites.os.path.exists", return_value=True)
|
|
1249
|
+
def test_check_pinctrl_amd_no_debug_info(self, mock_path_exists, mock_read_file):
|
|
1250
|
+
"""Test check_pinctrl_amd when the driver is loaded but no debug information is available"""
|
|
1251
|
+
mock_read_file.return_value = ""
|
|
1252
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
1253
|
+
MagicMock(properties={"DRIVER": "amd_gpio"})
|
|
1254
|
+
]
|
|
1255
|
+
|
|
1256
|
+
result = self.validator.check_pinctrl_amd()
|
|
1257
|
+
self.assertTrue(result)
|
|
1258
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
1259
|
+
"GPIO driver `pinctrl_amd` available", "✅"
|
|
1260
|
+
)
|
|
1261
|
+
self.mock_db.record_debug.assert_not_called()
|
|
1262
|
+
|
|
1263
|
+
def test_check_pinctrl_amd_driver_not_loaded(self):
|
|
1264
|
+
"""Test check_pinctrl_amd when the driver is not loaded"""
|
|
1265
|
+
self.mock_pyudev.list_devices.return_value = []
|
|
1266
|
+
|
|
1267
|
+
result = self.validator.check_pinctrl_amd()
|
|
1268
|
+
self.assertFalse(result)
|
|
1269
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
1270
|
+
"GPIO driver `pinctrl_amd` not loaded", "❌"
|
|
1271
|
+
)
|
|
1272
|
+
|
|
1273
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
1274
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
1275
|
+
def test_check_asus_rog_ally_mcu_version_too_old(
|
|
1276
|
+
self, mock_read_file, mock_path_exists
|
|
1277
|
+
):
|
|
1278
|
+
"""Test check_asus_rog_ally when MCU version is too old"""
|
|
1279
|
+
mock_path_exists.side_effect = lambda p: "mcu_version" in p
|
|
1280
|
+
mock_read_file.side_effect = lambda p: "318" if "mcu_version" in p else ""
|
|
1281
|
+
self.mock_pyudev.list_devices.side_effect = [
|
|
1282
|
+
[MagicMock(sys_path="/sys/devices/hid1", properties={"HID_ID": "1ABE"})],
|
|
1283
|
+
[],
|
|
1284
|
+
]
|
|
1285
|
+
|
|
1286
|
+
result = self.validator.check_asus_rog_ally()
|
|
1287
|
+
self.assertFalse(result)
|
|
1288
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
1289
|
+
"ROG Ally MCU firmware too old", "❌"
|
|
1290
|
+
)
|
|
1291
|
+
self.assertTrue(
|
|
1292
|
+
any(isinstance(f, RogAllyOldMcu) for f in self.validator.failures)
|
|
1293
|
+
)
|
|
1294
|
+
|
|
1295
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
1296
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
1297
|
+
def test_check_asus_rog_ally_mcu_version_valid(
|
|
1298
|
+
self, mock_read_file, mock_path_exists
|
|
1299
|
+
):
|
|
1300
|
+
"""Test check_asus_rog_ally when MCU version is valid"""
|
|
1301
|
+
mock_path_exists.side_effect = lambda p: "mcu_version" in p
|
|
1302
|
+
mock_read_file.side_effect = lambda p: "320" if "mcu_version" in p else ""
|
|
1303
|
+
self.mock_pyudev.list_devices.side_effect = [
|
|
1304
|
+
[MagicMock(sys_path="/sys/devices/hid1", properties={"HID_ID": "1ABE"})],
|
|
1305
|
+
[],
|
|
1306
|
+
]
|
|
1307
|
+
|
|
1308
|
+
result = self.validator.check_asus_rog_ally()
|
|
1309
|
+
self.assertTrue(result)
|
|
1310
|
+
|
|
1311
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
1312
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
1313
|
+
def test_check_asus_rog_ally_mcu_powersave_disabled(
|
|
1314
|
+
self, mock_read_file, mock_path_exists
|
|
1315
|
+
):
|
|
1316
|
+
"""Test check_asus_rog_ally when MCU powersave is disabled"""
|
|
1317
|
+
mock_path_exists.side_effect = lambda p: "current_value" in p
|
|
1318
|
+
mock_read_file.side_effect = lambda p: "0" if "current_value" in p else ""
|
|
1319
|
+
self.mock_pyudev.list_devices.side_effect = [
|
|
1320
|
+
[],
|
|
1321
|
+
[MagicMock(sys_path="/sys/devices/firmware1")],
|
|
1322
|
+
]
|
|
1323
|
+
|
|
1324
|
+
result = self.validator.check_asus_rog_ally()
|
|
1325
|
+
self.assertFalse(result)
|
|
1326
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
1327
|
+
"Rog Ally doesn't have MCU powersave enabled", "❌"
|
|
1328
|
+
)
|
|
1329
|
+
self.assertTrue(
|
|
1330
|
+
any(isinstance(f, RogAllyMcuPowerSave) for f in self.validator.failures)
|
|
1331
|
+
)
|
|
1332
|
+
|
|
1333
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
1334
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
1335
|
+
def test_check_asus_rog_ally_mcu_powersave_enabled(
|
|
1336
|
+
self, mock_read_file, mock_path_exists
|
|
1337
|
+
):
|
|
1338
|
+
"""Test check_asus_rog_ally when MCU powersave is enabled"""
|
|
1339
|
+
mock_path_exists.side_effect = lambda p: "current_value" in p
|
|
1340
|
+
mock_read_file.side_effect = lambda p: "1" if "current_value" in p else ""
|
|
1341
|
+
self.mock_pyudev.list_devices.side_effect = [
|
|
1342
|
+
[],
|
|
1343
|
+
[MagicMock(sys_path="/sys/devices/firmware1")],
|
|
1344
|
+
]
|
|
1345
|
+
|
|
1346
|
+
result = self.validator.check_asus_rog_ally()
|
|
1347
|
+
self.assertTrue(result)
|
|
1348
|
+
|
|
1349
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
1350
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
1351
|
+
def test_check_asus_rog_ally_no_devices(self, _mock_read_file, mock_path_exists):
|
|
1352
|
+
"""Test check_asus_rog_ally when no devices are found"""
|
|
1353
|
+
mock_path_exists.return_value = False
|
|
1354
|
+
self.mock_pyudev.list_devices.side_effect = [[], []]
|
|
1355
|
+
|
|
1356
|
+
result = self.validator.check_asus_rog_ally()
|
|
1357
|
+
self.assertTrue(result)
|
|
1358
|
+
|
|
1359
|
+
@patch("amd_debug.prerequisites.subprocess.check_output")
|
|
1360
|
+
def test_check_network_wol_supported_and_enabled(self, mock_check_output):
|
|
1361
|
+
"""Test check_network when WoL is supported and enabled"""
|
|
1362
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
1363
|
+
MagicMock(properties={"INTERFACE": "eth0"})
|
|
1364
|
+
]
|
|
1365
|
+
mock_check_output.return_value = (
|
|
1366
|
+
"Supports Wake-on: g\n" "Wake-on: g\n"
|
|
1367
|
+
).encode("utf-8")
|
|
1368
|
+
|
|
1369
|
+
result = self.validator.check_network()
|
|
1370
|
+
self.assertTrue(result)
|
|
1371
|
+
self.mock_db.record_debug.assert_called_with("eth0 supports WoL")
|
|
1372
|
+
self.mock_db.record_prereq.assert_called_with("eth0 has WoL enabled", "✅")
|
|
1373
|
+
|
|
1374
|
+
@patch("amd_debug.prerequisites.subprocess.check_output")
|
|
1375
|
+
def test_check_network_wol_supported_but_disabled(self, mock_check_output):
|
|
1376
|
+
"""Test check_network when WoL is supported but disabled"""
|
|
1377
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
1378
|
+
MagicMock(properties={"INTERFACE": "eth0"})
|
|
1379
|
+
]
|
|
1380
|
+
mock_check_output.return_value = (
|
|
1381
|
+
"Supports Wake-on: g\n" "Wake-on: d\n"
|
|
1382
|
+
).encode("utf-8")
|
|
1383
|
+
|
|
1384
|
+
result = self.validator.check_network()
|
|
1385
|
+
self.assertTrue(result)
|
|
1386
|
+
self.mock_db.record_debug.assert_called_with("eth0 supports WoL")
|
|
1387
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
1388
|
+
"Platform may have low hardware sleep residency with Wake-on-lan disabled. Run `ethtool -s eth0 wol g` to enable it if necessary.",
|
|
1389
|
+
"🚦",
|
|
1390
|
+
)
|
|
1391
|
+
|
|
1392
|
+
@patch("amd_debug.prerequisites.subprocess.check_output")
|
|
1393
|
+
def test_check_network_wol_not_supported(self, mock_check_output):
|
|
1394
|
+
"""Test check_network when WoL is not supported"""
|
|
1395
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
1396
|
+
MagicMock(properties={"INTERFACE": "eth0"})
|
|
1397
|
+
]
|
|
1398
|
+
mock_check_output.return_value = ("Supports Wake-on: d\n").encode("utf-8")
|
|
1399
|
+
|
|
1400
|
+
result = self.validator.check_network()
|
|
1401
|
+
self.assertTrue(result)
|
|
1402
|
+
self.mock_db.record_debug.assert_called_with("eth0 doesn't support WoL (d)")
|
|
1403
|
+
|
|
1404
|
+
@patch("amd_debug.prerequisites.subprocess.check_output")
|
|
1405
|
+
def test_check_network_no_devices(self, mock_check_output):
|
|
1406
|
+
"""Test check_network when no network devices are found"""
|
|
1407
|
+
self.mock_pyudev.list_devices.return_value = []
|
|
1408
|
+
|
|
1409
|
+
result = self.validator.check_network()
|
|
1410
|
+
self.assertTrue(result)
|
|
1411
|
+
self.mock_db.record_debug.assert_not_called()
|
|
1412
|
+
self.mock_db.record_prereq.assert_not_called()
|
|
1413
|
+
|
|
1414
|
+
@patch(
|
|
1415
|
+
"amd_debug.prerequisites.subprocess.check_output",
|
|
1416
|
+
side_effect=subprocess.CalledProcessError(1, "ethtool"),
|
|
1417
|
+
)
|
|
1418
|
+
def test_check_network_ethtool_error(self, mock_check_output):
|
|
1419
|
+
"""Test check_network when ethtool command fails"""
|
|
1420
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
1421
|
+
MagicMock(properties={"INTERFACE": "eth0"})
|
|
1422
|
+
]
|
|
1423
|
+
|
|
1424
|
+
with self.assertRaises(subprocess.CalledProcessError):
|
|
1425
|
+
self.validator.check_network()
|
|
1426
|
+
self.mock_db.record_debug.assert_not_called()
|
|
1427
|
+
self.mock_db.record_prereq.assert_not_called()
|
|
1428
|
+
|
|
1429
|
+
@patch("amd_debug.prerequisites.version.parse")
|
|
1430
|
+
def test_check_amd_cpu_hpet_wa_family_17_model_68(self, mock_version_parse):
|
|
1431
|
+
"""Test check_amd_cpu_hpet_wa for family 0x17, model 0x68"""
|
|
1432
|
+
self.validator.cpu_family = 0x17
|
|
1433
|
+
self.validator.cpu_model = 0x68
|
|
1434
|
+
result = self.validator.check_amd_cpu_hpet_wa()
|
|
1435
|
+
self.assertTrue(result)
|
|
1436
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
1437
|
+
"Timer based wakeup doesn't work properly for your ASIC/firmware, please manually wake the system",
|
|
1438
|
+
"🚦",
|
|
1439
|
+
)
|
|
1440
|
+
|
|
1441
|
+
@patch("amd_debug.prerequisites.version.parse")
|
|
1442
|
+
def test_check_amd_cpu_hpet_wa_family_17_model_60(self, mock_version_parse):
|
|
1443
|
+
"""Test check_amd_cpu_hpet_wa for family 0x17, model 0x60"""
|
|
1444
|
+
self.validator.cpu_family = 0x17
|
|
1445
|
+
self.validator.cpu_model = 0x60
|
|
1446
|
+
result = self.validator.check_amd_cpu_hpet_wa()
|
|
1447
|
+
self.assertTrue(result)
|
|
1448
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
1449
|
+
"Timer based wakeup doesn't work properly for your ASIC/firmware, please manually wake the system",
|
|
1450
|
+
"🚦",
|
|
1451
|
+
)
|
|
1452
|
+
|
|
1453
|
+
@patch("amd_debug.prerequisites.version.parse")
|
|
1454
|
+
def test_check_amd_cpu_hpet_wa_family_19_model_50_smu_version_low(
|
|
1455
|
+
self, mock_version_parse
|
|
1456
|
+
):
|
|
1457
|
+
"""Test check_amd_cpu_hpet_wa for family 0x19, model 0x50 with SMU version < 64.53.0"""
|
|
1458
|
+
self.validator.cpu_family = 0x19
|
|
1459
|
+
self.validator.cpu_model = 0x50
|
|
1460
|
+
self.validator.smu_version = "64.52.0"
|
|
1461
|
+
mock_version_parse.side_effect = lambda v: v if isinstance(v, str) else None
|
|
1462
|
+
result = self.validator.check_amd_cpu_hpet_wa()
|
|
1463
|
+
self.assertTrue(result)
|
|
1464
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
1465
|
+
"Timer based wakeup doesn't work properly for your ASIC/firmware, please manually wake the system",
|
|
1466
|
+
"🚦",
|
|
1467
|
+
)
|
|
1468
|
+
|
|
1469
|
+
@patch("amd_debug.prerequisites.version.parse")
|
|
1470
|
+
def test_check_amd_cpu_hpet_wa_family_19_model_50_smu_version_high(
|
|
1471
|
+
self, mock_version_parse
|
|
1472
|
+
):
|
|
1473
|
+
"""Test check_amd_cpu_hpet_wa for family 0x19, model 0x50 with SMU version >= 64.53.0"""
|
|
1474
|
+
self.validator.cpu_family = 0x19
|
|
1475
|
+
self.validator.cpu_model = 0x50
|
|
1476
|
+
self.validator.smu_version = "64.53.0"
|
|
1477
|
+
mock_version_parse.side_effect = lambda v: v if isinstance(v, str) else None
|
|
1478
|
+
result = self.validator.check_amd_cpu_hpet_wa()
|
|
1479
|
+
self.assertTrue(result)
|
|
1480
|
+
self.mock_db.record_prereq.assert_not_called()
|
|
1481
|
+
|
|
1482
|
+
def test_check_amd_cpu_hpet_wa_family_19_non_matching_model(self):
|
|
1483
|
+
"""Test check_amd_cpu_hpet_wa for family 0x19 with non-matching model"""
|
|
1484
|
+
self.validator.cpu_family = 0x19
|
|
1485
|
+
self.validator.cpu_model = 0x51
|
|
1486
|
+
result = self.validator.check_amd_cpu_hpet_wa()
|
|
1487
|
+
self.assertTrue(result)
|
|
1488
|
+
self.mock_db.record_prereq.assert_not_called()
|
|
1489
|
+
|
|
1490
|
+
def test_check_amd_cpu_hpet_wa_non_matching_family(self):
|
|
1491
|
+
"""Test check_amd_cpu_hpet_wa for non-matching CPU family"""
|
|
1492
|
+
self.validator.cpu_family = 0x18
|
|
1493
|
+
self.validator.cpu_model = 0x68
|
|
1494
|
+
result = self.validator.check_amd_cpu_hpet_wa()
|
|
1495
|
+
self.assertTrue(result)
|
|
1496
|
+
self.mock_db.record_prereq.assert_not_called()
|
|
1497
|
+
|
|
1498
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
1499
|
+
def test_capture_linux_firmware_debug_files_exist(self, mock_path_exists):
|
|
1500
|
+
"""Test capture_linux_firmware when debug files exist"""
|
|
1501
|
+
mock_path_exists.side_effect = lambda path: "amdgpu_firmware_info" in path
|
|
1502
|
+
|
|
1503
|
+
self.validator.distro = "ubuntu"
|
|
1504
|
+
self.validator.capture_linux_firmware()
|
|
1505
|
+
|
|
1506
|
+
self.mock_db.record_debug_file.assert_any_call(
|
|
1507
|
+
"/sys/kernel/debug/dri/0/amdgpu_firmware_info"
|
|
1508
|
+
)
|
|
1509
|
+
self.mock_db.record_debug_file.assert_any_call(
|
|
1510
|
+
"/sys/kernel/debug/dri/1/amdgpu_firmware_info"
|
|
1511
|
+
)
|
|
1512
|
+
|
|
1513
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
1514
|
+
def test_capture_linux_firmware_debug_files_missing(self, mock_path_exists):
|
|
1515
|
+
"""Test capture_linux_firmware when debug files are missing"""
|
|
1516
|
+
mock_path_exists.return_value = False
|
|
1517
|
+
|
|
1518
|
+
self.validator.distro = "ubuntu"
|
|
1519
|
+
self.validator.capture_linux_firmware()
|
|
1520
|
+
|
|
1521
|
+
self.mock_db.record_debug_file.assert_not_called()
|
|
1522
|
+
|
|
1523
|
+
def test_check_wlan_no_devices(self):
|
|
1524
|
+
"""Test check_wlan when no WLAN devices are found"""
|
|
1525
|
+
self.mock_pyudev.list_devices.return_value = []
|
|
1526
|
+
result = self.validator.check_wlan()
|
|
1527
|
+
self.assertTrue(result)
|
|
1528
|
+
self.mock_db.record_prereq.assert_not_called()
|
|
1529
|
+
|
|
1530
|
+
def test_check_wlan_missing_driver(self):
|
|
1531
|
+
"""Test check_wlan when a WLAN device is missing a driver"""
|
|
1532
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
1533
|
+
MagicMock(properties={"PCI_SLOT_NAME": "0000:00:1f.0", "DRIVER": None})
|
|
1534
|
+
]
|
|
1535
|
+
result = self.validator.check_wlan()
|
|
1536
|
+
self.assertFalse(result)
|
|
1537
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
1538
|
+
"WLAN device in 0000:00:1f.0 missing driver", "🚦"
|
|
1539
|
+
)
|
|
1540
|
+
self.assertTrue(
|
|
1541
|
+
any(isinstance(f, MissingDriver) for f in self.validator.failures)
|
|
1542
|
+
)
|
|
1543
|
+
|
|
1544
|
+
def test_check_wlan_with_driver(self):
|
|
1545
|
+
"""Test check_wlan when a WLAN device has a driver"""
|
|
1546
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
1547
|
+
MagicMock(properties={"PCI_SLOT_NAME": "0000:00:1f.0", "DRIVER": "iwlwifi"})
|
|
1548
|
+
]
|
|
1549
|
+
result = self.validator.check_wlan()
|
|
1550
|
+
self.assertTrue(result)
|
|
1551
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
1552
|
+
"WLAN driver `iwlwifi` bound to 0000:00:1f.0", "✅"
|
|
1553
|
+
)
|
|
1554
|
+
|
|
1555
|
+
def test_check_wlan_multiple_devices(self):
|
|
1556
|
+
"""Test check_wlan with multiple WLAN devices"""
|
|
1557
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
1558
|
+
MagicMock(
|
|
1559
|
+
properties={"PCI_SLOT_NAME": "0000:00:1f.0", "DRIVER": "iwlwifi"}
|
|
1560
|
+
),
|
|
1561
|
+
MagicMock(properties={"PCI_SLOT_NAME": "0000:00:1f.1", "DRIVER": None}),
|
|
1562
|
+
]
|
|
1563
|
+
result = self.validator.check_wlan()
|
|
1564
|
+
self.assertFalse(result)
|
|
1565
|
+
self.mock_db.record_prereq.assert_any_call(
|
|
1566
|
+
"WLAN driver `iwlwifi` bound to 0000:00:1f.0", "✅"
|
|
1567
|
+
)
|
|
1568
|
+
self.mock_db.record_prereq.assert_any_call(
|
|
1569
|
+
"WLAN device in 0000:00:1f.1 missing driver", "🚦"
|
|
1570
|
+
)
|
|
1571
|
+
self.assertTrue(
|
|
1572
|
+
any(isinstance(f, MissingDriver) for f in self.validator.failures)
|
|
1573
|
+
)
|
|
1574
|
+
|
|
1575
|
+
def test_check_usb3_no_devices(self):
|
|
1576
|
+
"""Test check_usb3 when no USB3 devices are found"""
|
|
1577
|
+
self.mock_pyudev.list_devices.return_value = []
|
|
1578
|
+
result = self.validator.check_usb3()
|
|
1579
|
+
self.assertTrue(result)
|
|
1580
|
+
self.mock_db.record_prereq.assert_not_called()
|
|
1581
|
+
|
|
1582
|
+
def test_check_usb3_driver_missing(self):
|
|
1583
|
+
"""Test check_usb3 when the xhci_hcd driver is missing"""
|
|
1584
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
1585
|
+
MagicMock(
|
|
1586
|
+
properties={
|
|
1587
|
+
"PCI_SLOT_NAME": "0000:00:1d.0",
|
|
1588
|
+
"DRIVER": None,
|
|
1589
|
+
}
|
|
1590
|
+
)
|
|
1591
|
+
]
|
|
1592
|
+
result = self.validator.check_usb3()
|
|
1593
|
+
self.assertFalse(result)
|
|
1594
|
+
self.assertTrue(
|
|
1595
|
+
any(isinstance(f, MissingXhciHcd) for f in self.validator.failures)
|
|
1596
|
+
)
|
|
1597
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
1598
|
+
"USB3 controller for 0000:00:1d.0 not using `xhci_hcd` driver", "❌"
|
|
1599
|
+
)
|
|
1600
|
+
|
|
1601
|
+
def test_check_usb3_driver_present(self):
|
|
1602
|
+
"""Test check_usb3 when the xhci_hcd driver is present"""
|
|
1603
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
1604
|
+
MagicMock(
|
|
1605
|
+
properties={
|
|
1606
|
+
"PCI_SLOT_NAME": "0000:00:1d.0",
|
|
1607
|
+
"DRIVER": "xhci_hcd",
|
|
1608
|
+
}
|
|
1609
|
+
)
|
|
1610
|
+
]
|
|
1611
|
+
result = self.validator.check_usb3()
|
|
1612
|
+
self.assertTrue(result)
|
|
1613
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
1614
|
+
"USB3 driver `xhci_hcd` bound to 0000:00:1d.0", "✅"
|
|
1615
|
+
)
|
|
1616
|
+
|
|
1617
|
+
def test_check_usb3_multiple_devices(self):
|
|
1618
|
+
"""Test check_usb3 with multiple USB3 devices"""
|
|
1619
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
1620
|
+
MagicMock(
|
|
1621
|
+
properties={
|
|
1622
|
+
"PCI_SLOT_NAME": "0000:00:1d.0",
|
|
1623
|
+
"DRIVER": "xhci_hcd",
|
|
1624
|
+
}
|
|
1625
|
+
),
|
|
1626
|
+
MagicMock(
|
|
1627
|
+
properties={
|
|
1628
|
+
"PCI_SLOT_NAME": "0000:00:1d.1",
|
|
1629
|
+
"DRIVER": None,
|
|
1630
|
+
}
|
|
1631
|
+
),
|
|
1632
|
+
]
|
|
1633
|
+
result = self.validator.check_usb3()
|
|
1634
|
+
self.assertFalse(result)
|
|
1635
|
+
self.mock_db.record_prereq.assert_any_call(
|
|
1636
|
+
"USB3 controller for 0000:00:1d.1 not using `xhci_hcd` driver", "❌"
|
|
1637
|
+
)
|
|
1638
|
+
self.assertTrue(
|
|
1639
|
+
any(isinstance(f, MissingXhciHcd) for f in self.validator.failures)
|
|
1640
|
+
)
|
|
1641
|
+
|
|
1642
|
+
def test_check_amd_pmc_driver_loaded(self):
|
|
1643
|
+
"""Test check_amd_pmc when the driver is loaded"""
|
|
1644
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
1645
|
+
MagicMock(
|
|
1646
|
+
sys_path="/sys/devices/platform/amd_pmc",
|
|
1647
|
+
properties={"DRIVER": "amd_pmc"},
|
|
1648
|
+
)
|
|
1649
|
+
]
|
|
1650
|
+
with patch("amd_debug.prerequisites.os.path.exists", return_value=True), patch(
|
|
1651
|
+
"amd_debug.prerequisites.read_file",
|
|
1652
|
+
side_effect=["mock_version", "mock_program"],
|
|
1653
|
+
):
|
|
1654
|
+
result = self.validator.check_amd_pmc()
|
|
1655
|
+
self.assertTrue(result)
|
|
1656
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
1657
|
+
"PMC driver `amd_pmc` loaded (Program mock_program Firmware mock_version)",
|
|
1658
|
+
"✅",
|
|
1659
|
+
)
|
|
1660
|
+
|
|
1661
|
+
def test_check_amd_pmc_driver_loaded_timeout_error(self):
|
|
1662
|
+
"""Test check_amd_pmc when a TimeoutError occurs while reading files"""
|
|
1663
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
1664
|
+
MagicMock(
|
|
1665
|
+
sys_path="/sys/devices/platform/amd_pmc",
|
|
1666
|
+
properties={"DRIVER": "amd_pmc"},
|
|
1667
|
+
)
|
|
1668
|
+
]
|
|
1669
|
+
with patch("amd_debug.prerequisites.os.path.exists", return_value=True), patch(
|
|
1670
|
+
"amd_debug.prerequisites.read_file", side_effect=TimeoutError
|
|
1671
|
+
):
|
|
1672
|
+
result = self.validator.check_amd_pmc()
|
|
1673
|
+
self.assertFalse(result)
|
|
1674
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
1675
|
+
"failed to communicate using `amd_pmc` driver", "❌"
|
|
1676
|
+
)
|
|
1677
|
+
|
|
1678
|
+
def test_check_amd_pmc_driver_not_loaded(self):
|
|
1679
|
+
"""Test check_amd_pmc when the driver is not loaded"""
|
|
1680
|
+
self.mock_pyudev.list_devices.return_value = []
|
|
1681
|
+
result = self.validator.check_amd_pmc()
|
|
1682
|
+
self.assertFalse(result)
|
|
1683
|
+
self.assertTrue(
|
|
1684
|
+
any(isinstance(f, MissingAmdPmc) for f in self.validator.failures)
|
|
1685
|
+
)
|
|
1686
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
1687
|
+
"PMC driver `amd_pmc` did not bind to any ACPI device", "❌"
|
|
1688
|
+
)
|
|
1689
|
+
|
|
1690
|
+
def test_check_amd_pmc_driver_loaded_no_version_info(self):
|
|
1691
|
+
"""Test check_amd_pmc when the driver is loaded but version info is missing"""
|
|
1692
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
1693
|
+
MagicMock(
|
|
1694
|
+
sys_path="/sys/devices/platform/amd_pmc",
|
|
1695
|
+
properties={"DRIVER": "amd_pmc"},
|
|
1696
|
+
)
|
|
1697
|
+
]
|
|
1698
|
+
with patch("amd_debug.prerequisites.os.path.exists", return_value=False):
|
|
1699
|
+
result = self.validator.check_amd_pmc()
|
|
1700
|
+
self.assertTrue(result)
|
|
1701
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
1702
|
+
"PMC driver `amd_pmc` loaded", "✅"
|
|
1703
|
+
)
|
|
1704
|
+
|
|
1705
|
+
@patch("amd_debug.prerequisites.minimum_kernel", return_value=True)
|
|
1706
|
+
def test_check_storage_new_kernel(self, _mock_minimum_kernel):
|
|
1707
|
+
"""Test check_storage when kernel version >= 6.10"""
|
|
1708
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
1709
|
+
MagicMock(properties={"PCI_SLOT_NAME": "0000:00:1f.0", "DRIVER": "nvme"})
|
|
1710
|
+
]
|
|
1711
|
+
result = self.validator.check_storage()
|
|
1712
|
+
self.assertTrue(result)
|
|
1713
|
+
self.mock_db.record_debug.assert_called_with(
|
|
1714
|
+
"New enough kernel to avoid NVME check"
|
|
1715
|
+
)
|
|
1716
|
+
|
|
1717
|
+
def test_check_storage_no_kernel_log(self):
|
|
1718
|
+
"""Test check_storage when kernel log is unavailable"""
|
|
1719
|
+
self.validator.kernel_log = None
|
|
1720
|
+
result = self.validator.check_storage()
|
|
1721
|
+
self.assertTrue(result)
|
|
1722
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
1723
|
+
"Unable to test storage from kernel log", "🚦"
|
|
1724
|
+
)
|
|
1725
|
+
|
|
1726
|
+
@patch("amd_debug.prerequisites.subprocess.check_output")
|
|
1727
|
+
def test_capture_edid_no_edid_data(self, mock_check_output):
|
|
1728
|
+
"""Test capture_edid when no EDID data is found"""
|
|
1729
|
+
self.validator.display.get_edid = MagicMock(return_value={})
|
|
1730
|
+
result = self.validator.capture_edid()
|
|
1731
|
+
self.assertTrue(result)
|
|
1732
|
+
self.mock_db.record_debug.assert_called_with("No EDID data found")
|
|
1733
|
+
mock_check_output.assert_not_called()
|
|
1734
|
+
|
|
1735
|
+
@patch("amd_debug.prerequisites.subprocess.check_output")
|
|
1736
|
+
def test_capture_edid_file_not_found(self, mock_check_output):
|
|
1737
|
+
"""Test capture_edid when edid-decode is not installed"""
|
|
1738
|
+
self.validator.display.get_edid = MagicMock(
|
|
1739
|
+
return_value={"Monitor1": "/path/to/edid"}
|
|
1740
|
+
)
|
|
1741
|
+
mock_check_output.side_effect = FileNotFoundError
|
|
1742
|
+
result = self.validator.capture_edid()
|
|
1743
|
+
self.assertTrue(result)
|
|
1744
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
1745
|
+
"Failed to capture EDID table", "👀"
|
|
1746
|
+
)
|
|
1747
|
+
|
|
1748
|
+
@patch("amd_debug.prerequisites.subprocess.check_output")
|
|
1749
|
+
def test_capture_edid_subprocess_error(self, mock_check_output):
|
|
1750
|
+
"""Test capture_edid when subprocess.check_output raises an error"""
|
|
1751
|
+
self.validator.display.get_edid = MagicMock(
|
|
1752
|
+
return_value={"Monitor1": "/path/to/edid"}
|
|
1753
|
+
)
|
|
1754
|
+
mock_check_output.side_effect = subprocess.CalledProcessError(
|
|
1755
|
+
returncode=1, cmd="edid-decode", output=b"Error decoding EDID"
|
|
1756
|
+
)
|
|
1757
|
+
result = self.validator.capture_edid()
|
|
1758
|
+
self.assertTrue(result)
|
|
1759
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
1760
|
+
"Failed to capture EDID table", "👀"
|
|
1761
|
+
)
|
|
1762
|
+
|
|
1763
|
+
@patch("amd_debug.prerequisites.subprocess.check_output")
|
|
1764
|
+
def test_capture_edid_success(self, mock_check_output):
|
|
1765
|
+
"""Test capture_edid when EDID data is successfully decoded"""
|
|
1766
|
+
self.validator.display.get_edid = MagicMock(
|
|
1767
|
+
return_value={"Monitor1": "/path/to/edid"}
|
|
1768
|
+
)
|
|
1769
|
+
mock_check_output.return_value = b"Decoded EDID data"
|
|
1770
|
+
result = self.validator.capture_edid()
|
|
1771
|
+
self.assertTrue(result)
|
|
1772
|
+
self.mock_db.record_debug.assert_called_with(
|
|
1773
|
+
apply_prefix_wrapper("EDID for Monitor1:", "Decoded EDID data")
|
|
1774
|
+
)
|
|
1775
|
+
|
|
1776
|
+
@patch("amd_debug.prerequisites.find_ip_version", return_value=True)
|
|
1777
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
1778
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
1779
|
+
def test_check_dpia_pg_dmcub_usb4_found(
|
|
1780
|
+
self, mock_read_file, mock_path_exists, mock_find_ip_version
|
|
1781
|
+
):
|
|
1782
|
+
"""Test check_dpia_pg_dmcub when USB4 routers are found"""
|
|
1783
|
+
usb4_device = MagicMock()
|
|
1784
|
+
self.mock_pyudev.list_devices.side_effect = [
|
|
1785
|
+
[usb4_device], # First call: USB4 present
|
|
1786
|
+
]
|
|
1787
|
+
result = self.validator.check_dpia_pg_dmcub()
|
|
1788
|
+
self.assertTrue(result)
|
|
1789
|
+
self.mock_db.record_debug.assert_called_with(
|
|
1790
|
+
"USB4 routers found, no need to check DMCUB version"
|
|
1791
|
+
)
|
|
1792
|
+
|
|
1793
|
+
@patch("amd_debug.prerequisites.find_ip_version", return_value=True)
|
|
1794
|
+
@patch("amd_debug.prerequisites.os.path.exists", return_value=True)
|
|
1795
|
+
@patch("amd_debug.prerequisites.read_file", return_value="0x90001B01")
|
|
1796
|
+
def test_check_dpia_pg_dmcub_dmcub_fw_version_new_enough(
|
|
1797
|
+
self, mock_read_file, mock_path_exists, mock_find_ip_version
|
|
1798
|
+
):
|
|
1799
|
+
"""Test check_dpia_pg_dmcub when DMCUB firmware version is new enough"""
|
|
1800
|
+
self.mock_pyudev.list_devices.side_effect = [
|
|
1801
|
+
[], # First call: no USB4
|
|
1802
|
+
[
|
|
1803
|
+
MagicMock(
|
|
1804
|
+
properties={
|
|
1805
|
+
"PCI_CLASS": "30000",
|
|
1806
|
+
"PCI_ID": "1002abcd",
|
|
1807
|
+
"PCI_SLOT_NAME": "0000:01:00.0",
|
|
1808
|
+
},
|
|
1809
|
+
sys_path="/sys/devices/pci0000:01/0000:01:00.0",
|
|
1810
|
+
)
|
|
1811
|
+
],
|
|
1812
|
+
]
|
|
1813
|
+
with patch("builtins.open", new_callable=mock_open, read_data="3") as mock_file:
|
|
1814
|
+
handlers = (
|
|
1815
|
+
mock_file.return_value,
|
|
1816
|
+
mock_open(read_data="5").return_value,
|
|
1817
|
+
mock_open(read_data="0").return_value,
|
|
1818
|
+
)
|
|
1819
|
+
mock_open.side_effect = handlers
|
|
1820
|
+
result = self.validator.check_dpia_pg_dmcub()
|
|
1821
|
+
self.assertTrue(result)
|
|
1822
|
+
self.mock_db.record_prereq.assert_not_called()
|
|
1823
|
+
|
|
1824
|
+
@patch("amd_debug.prerequisites.find_ip_version", return_value=True)
|
|
1825
|
+
@patch("amd_debug.prerequisites.os.path.exists", return_value=True)
|
|
1826
|
+
@patch("amd_debug.prerequisites.read_file", return_value="0x8001B00")
|
|
1827
|
+
def test_check_dpia_pg_dmcub_dmcub_fw_version_too_old(
|
|
1828
|
+
self, mock_read_file, mock_path_exists, mock_find_ip_version
|
|
1829
|
+
):
|
|
1830
|
+
"""Test check_dpia_pg_dmcub when DMCUB firmware version is too old"""
|
|
1831
|
+
self.mock_pyudev.list_devices.side_effect = [
|
|
1832
|
+
[], # First call: no USB4
|
|
1833
|
+
[
|
|
1834
|
+
MagicMock(
|
|
1835
|
+
properties={
|
|
1836
|
+
"PCI_CLASS": "30000",
|
|
1837
|
+
"PCI_ID": "1002abcd",
|
|
1838
|
+
"PCI_SLOT_NAME": "0000:01:00.0",
|
|
1839
|
+
},
|
|
1840
|
+
sys_path="/sys/devices/pci0000:01/0000:01:00.0",
|
|
1841
|
+
)
|
|
1842
|
+
],
|
|
1843
|
+
]
|
|
1844
|
+
result = self.validator.check_dpia_pg_dmcub()
|
|
1845
|
+
self.assertFalse(result)
|
|
1846
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
1847
|
+
"DMCUB Firmware is outdated", "❌"
|
|
1848
|
+
)
|
|
1849
|
+
self.assertTrue(
|
|
1850
|
+
any(isinstance(f, DmcubTooOld) for f in self.validator.failures)
|
|
1851
|
+
)
|
|
1852
|
+
|
|
1853
|
+
@patch("amd_debug.prerequisites.find_ip_version", return_value=True)
|
|
1854
|
+
@patch("amd_debug.prerequisites.os.path.exists", return_value=False)
|
|
1855
|
+
@patch(
|
|
1856
|
+
"amd_debug.prerequisites.read_file",
|
|
1857
|
+
side_effect=[
|
|
1858
|
+
"", # sysfs read returns empty, so fallback to debugfs
|
|
1859
|
+
"DMCUB fw: 09001B00\nOther line\n", # debugfs read
|
|
1860
|
+
],
|
|
1861
|
+
)
|
|
1862
|
+
def test_check_dpia_pg_dmcub_debugfs_version_new_enough(
|
|
1863
|
+
self, mock_read_file, mock_path_exists, mock_find_ip_version
|
|
1864
|
+
):
|
|
1865
|
+
"""Test check_dpia_pg_dmcub when DMCUB version is found in debugfs and is new enough"""
|
|
1866
|
+
self.mock_pyudev.list_devices.side_effect = [
|
|
1867
|
+
[], # First call: no USB4
|
|
1868
|
+
[
|
|
1869
|
+
MagicMock(
|
|
1870
|
+
properties={
|
|
1871
|
+
"PCI_CLASS": "30000",
|
|
1872
|
+
"PCI_ID": "1002abcd",
|
|
1873
|
+
"PCI_SLOT_NAME": "0",
|
|
1874
|
+
},
|
|
1875
|
+
sys_path="/sys/devices/pci0000:01/0000:01:00.0",
|
|
1876
|
+
)
|
|
1877
|
+
],
|
|
1878
|
+
]
|
|
1879
|
+
result = self.validator.check_dpia_pg_dmcub()
|
|
1880
|
+
self.assertTrue(result)
|
|
1881
|
+
self.mock_db.record_prereq.assert_not_called()
|
|
1882
|
+
|
|
1883
|
+
@patch("amd_debug.prerequisites.find_ip_version", return_value=True)
|
|
1884
|
+
@patch("amd_debug.prerequisites.os.path.exists", return_value=False)
|
|
1885
|
+
@patch(
|
|
1886
|
+
"amd_debug.prerequisites.read_file",
|
|
1887
|
+
side_effect=[
|
|
1888
|
+
"DMCUB fw: 0x08001B00\nOther line\n", # debugfs read
|
|
1889
|
+
],
|
|
1890
|
+
)
|
|
1891
|
+
def test_check_dpia_pg_dmcub_debugfs_version_too_old(
|
|
1892
|
+
self, mock_read_file, mock_path_exists, mock_find_ip_version
|
|
1893
|
+
):
|
|
1894
|
+
"""Test check_dpia_pg_dmcub when DMCUB version is found in debugfs and is too old"""
|
|
1895
|
+
self.mock_pyudev.list_devices.side_effect = [
|
|
1896
|
+
[], # First call: no USB4
|
|
1897
|
+
[
|
|
1898
|
+
MagicMock(
|
|
1899
|
+
properties={
|
|
1900
|
+
"PCI_CLASS": "30000",
|
|
1901
|
+
"PCI_ID": "1002abcd",
|
|
1902
|
+
"PCI_SLOT_NAME": "0",
|
|
1903
|
+
},
|
|
1904
|
+
sys_path="/sys/devices/pci0000:01/0000:01:00.0",
|
|
1905
|
+
)
|
|
1906
|
+
],
|
|
1907
|
+
]
|
|
1908
|
+
result = self.validator.check_dpia_pg_dmcub()
|
|
1909
|
+
self.assertFalse(result)
|
|
1910
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
1911
|
+
"DMCUB Firmware is outdated", "❌"
|
|
1912
|
+
)
|
|
1913
|
+
self.assertTrue(
|
|
1914
|
+
any(isinstance(f, DmcubTooOld) for f in self.validator.failures)
|
|
1915
|
+
)
|
|
1916
|
+
|
|
1917
|
+
@patch("amd_debug.prerequisites.find_ip_version", return_value=False)
|
|
1918
|
+
def test_check_dpia_pg_dmcub_no_matching_dcn(self, mock_find_ip_version):
|
|
1919
|
+
"""Test check_dpia_pg_dmcub when no matching DCN is found"""
|
|
1920
|
+
self.mock_pyudev.list_devices.side_effect = [
|
|
1921
|
+
[], # First call: no USB4
|
|
1922
|
+
[
|
|
1923
|
+
MagicMock(
|
|
1924
|
+
properties={
|
|
1925
|
+
"PCI_CLASS": "30000",
|
|
1926
|
+
"PCI_ID": "1002abcd",
|
|
1927
|
+
"PCI_SLOT_NAME": "0",
|
|
1928
|
+
},
|
|
1929
|
+
sys_path="/sys/devices/pci0000:01/0000:01:00.0",
|
|
1930
|
+
)
|
|
1931
|
+
],
|
|
1932
|
+
]
|
|
1933
|
+
result = self.validator.check_dpia_pg_dmcub()
|
|
1934
|
+
self.assertTrue(result)
|
|
1935
|
+
self.mock_db.record_prereq.assert_not_called()
|