amd-debug-tools 0.2.1__py3-none-any.whl → 0.2.3__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.
Potentially problematic release.
This version of amd-debug-tools might be problematic. Click here for more details.
- amd_debug/common.py +24 -0
- amd_debug/failures.py +14 -2
- amd_debug/installer.py +34 -2
- amd_debug/kernel.py +8 -0
- amd_debug/prerequisites.py +95 -45
- amd_debug/s2idle.py +45 -30
- amd_debug/sleep_report.py +128 -85
- amd_debug/templates/html +4 -2
- amd_debug/templates/md +0 -7
- amd_debug/validator.py +20 -15
- {amd_debug_tools-0.2.1.dist-info → amd_debug_tools-0.2.3.dist-info}/METADATA +4 -3
- {amd_debug_tools-0.2.1.dist-info → amd_debug_tools-0.2.3.dist-info}/RECORD +22 -22
- {amd_debug_tools-0.2.1.dist-info → amd_debug_tools-0.2.3.dist-info}/WHEEL +1 -1
- test_common.py +112 -0
- test_installer.py +8 -6
- test_prerequisites.py +204 -45
- test_s2idle.py +93 -28
- test_sleep_report.py +29 -0
- test_validator.py +119 -12
- {amd_debug_tools-0.2.1.dist-info → amd_debug_tools-0.2.3.dist-info}/entry_points.txt +0 -0
- {amd_debug_tools-0.2.1.dist-info → amd_debug_tools-0.2.3.dist-info}/licenses/LICENSE +0 -0
- {amd_debug_tools-0.2.1.dist-info → amd_debug_tools-0.2.3.dist-info}/top_level.txt +0 -0
test_common.py
CHANGED
|
@@ -16,9 +16,11 @@ from platform import uname_result
|
|
|
16
16
|
from amd_debug.common import (
|
|
17
17
|
apply_prefix_wrapper,
|
|
18
18
|
Colors,
|
|
19
|
+
convert_string_to_bool,
|
|
19
20
|
colorize_choices,
|
|
20
21
|
check_lockdown,
|
|
21
22
|
compare_file,
|
|
23
|
+
find_ip_version,
|
|
22
24
|
fatal_error,
|
|
23
25
|
get_distro,
|
|
24
26
|
get_log_priority,
|
|
@@ -330,3 +332,113 @@ class TestCommon(unittest.TestCase):
|
|
|
330
332
|
default = "option1"
|
|
331
333
|
expected_output = f"{Colors.OK}{default}{Colors.ENDC}"
|
|
332
334
|
self.assertEqual(colorize_choices(choices, default), expected_output)
|
|
335
|
+
|
|
336
|
+
@patch("amd_debug.common.read_file")
|
|
337
|
+
@patch("os.path.exists")
|
|
338
|
+
def test_find_ip_version_found(self, mock_exists, mock_read_file):
|
|
339
|
+
"""Test find_ip_version returns True when expected value is found"""
|
|
340
|
+
base_path = "/foo"
|
|
341
|
+
kind = "bar"
|
|
342
|
+
hw_ver = {"baz": 42}
|
|
343
|
+
|
|
344
|
+
# Simulate file exists and value matches
|
|
345
|
+
def exists_side_effect(path):
|
|
346
|
+
return True
|
|
347
|
+
|
|
348
|
+
mock_exists.side_effect = exists_side_effect
|
|
349
|
+
mock_read_file.return_value = "42"
|
|
350
|
+
result = find_ip_version(base_path, kind, hw_ver)
|
|
351
|
+
self.assertTrue(result)
|
|
352
|
+
b = os.path.join(base_path, "ip_discovery", "die", "0", kind, "0")
|
|
353
|
+
expected_path = os.path.join(b, "baz")
|
|
354
|
+
mock_exists.assert_any_call(expected_path)
|
|
355
|
+
mock_read_file.assert_any_call(expected_path)
|
|
356
|
+
|
|
357
|
+
@patch("amd_debug.common.read_file")
|
|
358
|
+
@patch("os.path.exists")
|
|
359
|
+
def test_find_ip_version_not_found_due_to_missing_file(
|
|
360
|
+
self, mock_exists, mock_read_file
|
|
361
|
+
):
|
|
362
|
+
"""Test find_ip_version returns False if file does not exist"""
|
|
363
|
+
base_path = "/foo"
|
|
364
|
+
kind = "bar"
|
|
365
|
+
hw_ver = {"baz": 42}
|
|
366
|
+
# Simulate file does not exist
|
|
367
|
+
mock_exists.return_value = False
|
|
368
|
+
result = find_ip_version(base_path, kind, hw_ver)
|
|
369
|
+
self.assertFalse(result)
|
|
370
|
+
b = os.path.join(base_path, "ip_discovery", "die", "0", kind, "0")
|
|
371
|
+
expected_path = os.path.join(b, "baz")
|
|
372
|
+
mock_exists.assert_any_call(expected_path)
|
|
373
|
+
mock_read_file.assert_not_called()
|
|
374
|
+
|
|
375
|
+
@patch("amd_debug.common.read_file")
|
|
376
|
+
@patch("os.path.exists")
|
|
377
|
+
def test_find_ip_version_not_found_due_to_value_mismatch(
|
|
378
|
+
self, mock_exists, mock_read_file
|
|
379
|
+
):
|
|
380
|
+
"""Test find_ip_version returns False if value does not match"""
|
|
381
|
+
base_path = "/foo"
|
|
382
|
+
kind = "bar"
|
|
383
|
+
hw_ver = {"baz": 42}
|
|
384
|
+
# Simulate file exists but value does not match
|
|
385
|
+
mock_exists.return_value = True
|
|
386
|
+
mock_read_file.return_value = "99"
|
|
387
|
+
result = find_ip_version(base_path, kind, hw_ver)
|
|
388
|
+
self.assertFalse(result)
|
|
389
|
+
b = os.path.join(base_path, "ip_discovery", "die", "0", kind, "0")
|
|
390
|
+
expected_path = os.path.join(b, "baz")
|
|
391
|
+
mock_exists.assert_any_call(expected_path)
|
|
392
|
+
mock_read_file.assert_any_call(expected_path)
|
|
393
|
+
|
|
394
|
+
@patch("amd_debug.common.read_file")
|
|
395
|
+
@patch("os.path.exists")
|
|
396
|
+
def test_find_ip_version_multiple_keys(self, mock_exists, mock_read_file):
|
|
397
|
+
"""Test find_ip_version with multiple keys in hw_ver"""
|
|
398
|
+
base_path = "/foo"
|
|
399
|
+
kind = "bar"
|
|
400
|
+
hw_ver = {"baz": 42, "qux": 99}
|
|
401
|
+
|
|
402
|
+
# First key: file exists, value does not match
|
|
403
|
+
# Second key: file exists, value matches
|
|
404
|
+
def exists_side_effect(path):
|
|
405
|
+
return True
|
|
406
|
+
|
|
407
|
+
def read_file_side_effect(path):
|
|
408
|
+
if path.endswith("baz"):
|
|
409
|
+
return "0"
|
|
410
|
+
if path.endswith("qux"):
|
|
411
|
+
return "99"
|
|
412
|
+
return "0"
|
|
413
|
+
|
|
414
|
+
mock_exists.side_effect = exists_side_effect
|
|
415
|
+
mock_read_file.side_effect = read_file_side_effect
|
|
416
|
+
result = find_ip_version(base_path, kind, hw_ver)
|
|
417
|
+
self.assertFalse(result)
|
|
418
|
+
|
|
419
|
+
def test_convert_string_to_bool_true_values(self):
|
|
420
|
+
"""Test convert_string_to_bool returns True for truthy string values"""
|
|
421
|
+
self.assertTrue(convert_string_to_bool("True"))
|
|
422
|
+
self.assertTrue(convert_string_to_bool("1"))
|
|
423
|
+
self.assertTrue(convert_string_to_bool("'nonempty'"))
|
|
424
|
+
self.assertTrue(convert_string_to_bool('"nonempty"'))
|
|
425
|
+
|
|
426
|
+
def test_convert_string_to_bool_false_values(self):
|
|
427
|
+
"""Test convert_string_to_bool returns False for falsy string values"""
|
|
428
|
+
self.assertFalse(convert_string_to_bool("False"))
|
|
429
|
+
self.assertFalse(convert_string_to_bool("0"))
|
|
430
|
+
self.assertFalse(convert_string_to_bool("''"))
|
|
431
|
+
self.assertFalse(convert_string_to_bool('""'))
|
|
432
|
+
self.assertFalse(convert_string_to_bool("None"))
|
|
433
|
+
|
|
434
|
+
def test_convert_string_to_bool_invalid_syntax(self):
|
|
435
|
+
"""Test convert_string_to_bool exits on invalid syntax"""
|
|
436
|
+
with patch("sys.exit") as mock_exit:
|
|
437
|
+
convert_string_to_bool("not_a_bool")
|
|
438
|
+
mock_exit.assert_called_once_with("Invalid entry: not_a_bool")
|
|
439
|
+
|
|
440
|
+
def test_convert_string_to_bool_invalid_value(self):
|
|
441
|
+
"""Test convert_string_to_bool exits on invalid value"""
|
|
442
|
+
with patch("sys.exit") as mock_exit:
|
|
443
|
+
convert_string_to_bool("[unclosed_list")
|
|
444
|
+
mock_exit.assert_called_once_with("Invalid entry: [unclosed_list")
|
test_installer.py
CHANGED
|
@@ -192,7 +192,7 @@ class TestInstaller(unittest.TestCase):
|
|
|
192
192
|
"""Test install requirements function"""
|
|
193
193
|
self.installer.set_requirements("iasl", "ethtool")
|
|
194
194
|
ret = self.installer.install_dependencies()
|
|
195
|
-
self.
|
|
195
|
+
self.assertTrue(ret)
|
|
196
196
|
|
|
197
197
|
@patch("builtins.print")
|
|
198
198
|
@patch("amd_debug.installer.get_distro", return_value="ubuntu")
|
|
@@ -205,7 +205,9 @@ class TestInstaller(unittest.TestCase):
|
|
|
205
205
|
"""Test install requirements function for edid-decode on Ubuntu"""
|
|
206
206
|
self.installer.set_requirements("edid-decode")
|
|
207
207
|
ret = self.installer.install_dependencies()
|
|
208
|
-
_mock_check_call.assert_called_once_with(
|
|
208
|
+
_mock_check_call.assert_called_once_with(
|
|
209
|
+
["apt", "install", "libdisplay-info-bin"]
|
|
210
|
+
)
|
|
209
211
|
self.assertTrue(ret)
|
|
210
212
|
|
|
211
213
|
@patch("builtins.print")
|
|
@@ -229,7 +231,7 @@ class TestInstaller(unittest.TestCase):
|
|
|
229
231
|
self.installer.set_requirements("edid-decode")
|
|
230
232
|
ret = self.installer.install_dependencies()
|
|
231
233
|
_mock_check_call.assert_called_once_with(
|
|
232
|
-
["dnf", "install", "-y", "
|
|
234
|
+
["dnf", "install", "-y", "libdisplay-info"]
|
|
233
235
|
)
|
|
234
236
|
self.assertTrue(ret)
|
|
235
237
|
|
|
@@ -249,8 +251,8 @@ class TestInstaller(unittest.TestCase):
|
|
|
249
251
|
"""Test install requirements function for edid-decode on Arch"""
|
|
250
252
|
self.installer.set_requirements("edid-decode")
|
|
251
253
|
ret = self.installer.install_dependencies()
|
|
252
|
-
_mock_check_call.
|
|
253
|
-
self.
|
|
254
|
+
_mock_check_call.assert_called_once_with(["pacman", "-Sy", "libdisplay-info"])
|
|
255
|
+
self.assertTrue(ret)
|
|
254
256
|
|
|
255
257
|
@patch("builtins.print")
|
|
256
258
|
@patch("os.path.exists", return_value=False)
|
|
@@ -263,7 +265,7 @@ class TestInstaller(unittest.TestCase):
|
|
|
263
265
|
"""Test install requirements function for edid-decode on unsupported distro"""
|
|
264
266
|
self.installer.set_requirements("edid-decode")
|
|
265
267
|
ret = self.installer.install_dependencies()
|
|
266
|
-
self.
|
|
268
|
+
self.assertTrue(ret)
|
|
267
269
|
|
|
268
270
|
@patch("builtins.print")
|
|
269
271
|
@patch("os.path.exists", return_value=False)
|
test_prerequisites.py
CHANGED
|
@@ -8,7 +8,7 @@ This module contains unit tests for the prerequisite functions in the amd-debug-
|
|
|
8
8
|
import logging
|
|
9
9
|
import unittest
|
|
10
10
|
import subprocess
|
|
11
|
-
from unittest.mock import patch, MagicMock
|
|
11
|
+
from unittest.mock import patch, MagicMock, mock_open
|
|
12
12
|
|
|
13
13
|
from amd_debug.prerequisites import PrerequisiteValidator
|
|
14
14
|
from amd_debug.failures import *
|
|
@@ -180,20 +180,22 @@ class TestPrerequisiteValidator(unittest.TestCase):
|
|
|
180
180
|
self.assertTrue(result)
|
|
181
181
|
self.mock_db.record_prereq.assert_called_with("IOMMU disabled", "✅")
|
|
182
182
|
|
|
183
|
-
|
|
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):
|
|
184
189
|
"""Test check_iommu when DMA protection is not enabled"""
|
|
185
190
|
self.validator.cpu_family = 0x1A
|
|
186
191
|
self.validator.cpu_model = 0x20
|
|
187
192
|
iommu_device = MagicMock(sys_path="/sys/devices/iommu")
|
|
188
|
-
thunderbolt_device = MagicMock(sys_path="/sys/devices/thunderbolt")
|
|
189
193
|
self.mock_pyudev.list_devices.side_effect = [
|
|
190
194
|
[iommu_device],
|
|
191
|
-
[thunderbolt_device],
|
|
192
195
|
[],
|
|
193
196
|
[],
|
|
194
197
|
]
|
|
195
|
-
|
|
196
|
-
result = self.validator.check_iommu()
|
|
198
|
+
result = self.validator.check_iommu()
|
|
197
199
|
self.assertFalse(result)
|
|
198
200
|
self.assertTrue(
|
|
199
201
|
any(isinstance(f, DMArNotEnabled) for f in self.validator.failures)
|
|
@@ -202,20 +204,22 @@ class TestPrerequisiteValidator(unittest.TestCase):
|
|
|
202
204
|
"IOMMU is misconfigured: Pre-boot DMA protection not enabled", "❌"
|
|
203
205
|
)
|
|
204
206
|
|
|
205
|
-
|
|
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):
|
|
206
213
|
"""Test check_iommu when MSFT0201 ACPI device is missing"""
|
|
207
214
|
self.validator.cpu_family = 0x1A
|
|
208
215
|
self.validator.cpu_model = 0x20
|
|
209
216
|
iommu_device = MagicMock(sys_path="/sys/devices/iommu")
|
|
210
|
-
thunderbolt_device = MagicMock(sys_path="/sys/devices/thunderbolt")
|
|
211
217
|
self.mock_pyudev.list_devices.side_effect = [
|
|
212
218
|
[iommu_device],
|
|
213
|
-
[thunderbolt_device],
|
|
214
219
|
[],
|
|
215
220
|
[],
|
|
216
221
|
]
|
|
217
|
-
|
|
218
|
-
result = self.validator.check_iommu()
|
|
222
|
+
result = self.validator.check_iommu()
|
|
219
223
|
self.assertFalse(result)
|
|
220
224
|
self.assertTrue(
|
|
221
225
|
any(isinstance(f, MissingIommuACPI) for f in self.validator.failures)
|
|
@@ -224,47 +228,48 @@ class TestPrerequisiteValidator(unittest.TestCase):
|
|
|
224
228
|
"IOMMU is misconfigured: missing MSFT0201 ACPI device", "❌"
|
|
225
229
|
)
|
|
226
230
|
|
|
227
|
-
|
|
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):
|
|
228
237
|
"""Test check_iommu when policy is not bound to MSFT0201"""
|
|
229
238
|
self.validator.cpu_family = 0x1A
|
|
230
239
|
self.validator.cpu_model = 0x20
|
|
231
240
|
iommu_device = MagicMock(sys_path="/sys/devices/iommu")
|
|
232
|
-
thunderbolt_device = MagicMock(sys_path="/sys/devices/thunderbolt")
|
|
233
241
|
acpi_device = MagicMock(sys_path="/sys/devices/acpi/MSFT0201")
|
|
234
242
|
platform_device = MagicMock(sys_path="/sys/devices/platform/MSFT0201")
|
|
235
243
|
self.mock_pyudev.list_devices.side_effect = [
|
|
236
244
|
[iommu_device],
|
|
237
|
-
[thunderbolt_device],
|
|
238
245
|
[acpi_device],
|
|
239
246
|
[platform_device],
|
|
240
247
|
]
|
|
241
|
-
|
|
242
|
-
"os.path.exists", return_value=False
|
|
243
|
-
):
|
|
244
|
-
result = self.validator.check_iommu()
|
|
248
|
+
result = self.validator.check_iommu()
|
|
245
249
|
self.assertFalse(result)
|
|
246
250
|
self.assertTrue(
|
|
247
251
|
any(isinstance(f, MissingIommuPolicy) for f in self.validator.failures)
|
|
248
252
|
)
|
|
249
253
|
|
|
250
|
-
|
|
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):
|
|
251
261
|
"""Test check_iommu when IOMMU is properly configured"""
|
|
252
262
|
self.validator.cpu_family = 0x1A
|
|
253
263
|
self.validator.cpu_model = 0x20
|
|
254
264
|
iommu_device = MagicMock(sys_path="/sys/devices/iommu")
|
|
255
|
-
thunderbolt_device = MagicMock(sys_path="/sys/devices/thunderbolt")
|
|
256
265
|
acpi_device = MagicMock(sys_path="/sys/devices/acpi/MSFT0201")
|
|
257
266
|
platform_device = MagicMock(sys_path="/sys/devices/platform/MSFT0201")
|
|
258
267
|
self.mock_pyudev.list_devices.side_effect = [
|
|
259
268
|
[iommu_device],
|
|
260
|
-
[thunderbolt_device],
|
|
261
269
|
[acpi_device],
|
|
262
270
|
[platform_device],
|
|
263
271
|
]
|
|
264
|
-
|
|
265
|
-
"os.path.exists", return_value=True
|
|
266
|
-
):
|
|
267
|
-
result = self.validator.check_iommu()
|
|
272
|
+
result = self.validator.check_iommu()
|
|
268
273
|
self.assertTrue(result)
|
|
269
274
|
self.mock_db.record_prereq.assert_called_with("IOMMU properly configured", "✅")
|
|
270
275
|
|
|
@@ -495,9 +500,7 @@ class TestPrerequisiteValidator(unittest.TestCase):
|
|
|
495
500
|
result = self.validator.map_acpi_path()
|
|
496
501
|
self.assertTrue(result)
|
|
497
502
|
self.mock_db.record_debug.assert_called_with(
|
|
498
|
-
"ACPI name
|
|
499
|
-
"│ device1: mocked_path [driver]\n"
|
|
500
|
-
"└─device2: mocked_path [driver]\n"
|
|
503
|
+
"ACPI name | ACPI path | Kernel driver\ndevice1 | mocked_path | driver\ndevice2 | mocked_path | driver\n"
|
|
501
504
|
)
|
|
502
505
|
|
|
503
506
|
@patch("amd_debug.prerequisites.os.path.exists")
|
|
@@ -540,7 +543,7 @@ class TestPrerequisiteValidator(unittest.TestCase):
|
|
|
540
543
|
result = self.validator.map_acpi_path()
|
|
541
544
|
self.assertTrue(result)
|
|
542
545
|
self.mock_db.record_debug.assert_called_with(
|
|
543
|
-
"ACPI name
|
|
546
|
+
"ACPI name | ACPI path | Kernel driver\ndevice1 | mocked_path | None\n"
|
|
544
547
|
)
|
|
545
548
|
|
|
546
549
|
@patch("amd_debug.prerequisites.read_file")
|
|
@@ -564,8 +567,7 @@ class TestPrerequisiteValidator(unittest.TestCase):
|
|
|
564
567
|
|
|
565
568
|
self.validator.capture_pci_acpi()
|
|
566
569
|
self.mock_db.record_debug.assert_called_with(
|
|
567
|
-
"PCI
|
|
568
|
-
"└─0000:00:1f.0 : Intel Corporation ISA bridge [1234abcd] : mocked_acpi_path\n"
|
|
570
|
+
"PCI Slot | Vendor | Class | ID | ACPI path\n└─0000:00:1f.0 | Intel Corporation | ISA bridge | 1234abcd | mocked_acpi_path\n"
|
|
569
571
|
)
|
|
570
572
|
|
|
571
573
|
@patch("amd_debug.prerequisites.read_file")
|
|
@@ -588,8 +590,7 @@ class TestPrerequisiteValidator(unittest.TestCase):
|
|
|
588
590
|
|
|
589
591
|
self.validator.capture_pci_acpi()
|
|
590
592
|
self.mock_db.record_debug.assert_called_with(
|
|
591
|
-
"PCI
|
|
592
|
-
"└─0000:01:00.0 : NVIDIA Corporation VGA compatible controller [5678efgh]\n"
|
|
593
|
+
"PCI Slot | Vendor | Class | ID | ACPI path\n└─0000:01:00.0 | NVIDIA Corporation | VGA compatible controller | 5678efgh | \n"
|
|
593
594
|
)
|
|
594
595
|
|
|
595
596
|
@patch("amd_debug.prerequisites.read_file")
|
|
@@ -623,9 +624,7 @@ class TestPrerequisiteValidator(unittest.TestCase):
|
|
|
623
624
|
|
|
624
625
|
self.validator.capture_pci_acpi()
|
|
625
626
|
self.mock_db.record_debug.assert_called_with(
|
|
626
|
-
"PCI
|
|
627
|
-
"│ 0000:00:1f.0 : Intel Corporation ISA bridge [1234abcd] : mocked_acpi_path\n"
|
|
628
|
-
"└─0000:01:00.0 : NVIDIA Corporation VGA compatible controller [5678efgh] : mocked_acpi_path\n"
|
|
627
|
+
"PCI Slot | Vendor | Class | ID | ACPI path\n│ 0000:00:1f.0 | Intel Corporation | ISA bridge | 1234abcd | mocked_acpi_path\n└─0000:01:00.0 | NVIDIA Corporation | VGA compatible controller | 5678efgh | mocked_acpi_path\n"
|
|
629
628
|
)
|
|
630
629
|
|
|
631
630
|
def test_capture_pci_acpi_no_devices(self):
|
|
@@ -633,7 +632,9 @@ class TestPrerequisiteValidator(unittest.TestCase):
|
|
|
633
632
|
self.mock_pyudev.list_devices.return_value = []
|
|
634
633
|
|
|
635
634
|
self.validator.capture_pci_acpi()
|
|
636
|
-
self.mock_db.record_debug.assert_called_with(
|
|
635
|
+
self.mock_db.record_debug.assert_called_with(
|
|
636
|
+
"PCI Slot | Vendor | Class | ID | ACPI path\n"
|
|
637
|
+
)
|
|
637
638
|
|
|
638
639
|
@patch("amd_debug.prerequisites.read_file")
|
|
639
640
|
def test_check_aspm_default_policy(self, mock_read_file):
|
|
@@ -1001,7 +1002,7 @@ class TestPrerequisiteValidator(unittest.TestCase):
|
|
|
1001
1002
|
"MockVendor MockProduct (MockFamily)", "💻"
|
|
1002
1003
|
)
|
|
1003
1004
|
self.mock_db.record_debug.assert_called_with(
|
|
1004
|
-
"DMI
|
|
1005
|
+
"DMI|value\nchassis_type| Desktop\n"
|
|
1005
1006
|
)
|
|
1006
1007
|
|
|
1007
1008
|
@patch("amd_debug.prerequisites.os.walk")
|
|
@@ -1029,7 +1030,7 @@ class TestPrerequisiteValidator(unittest.TestCase):
|
|
|
1029
1030
|
self.mock_db.record_prereq.assert_called_with(
|
|
1030
1031
|
"MockVendor MockProduct (MockFamily)", "💻"
|
|
1031
1032
|
)
|
|
1032
|
-
self.mock_db.record_debug.assert_called_with("DMI
|
|
1033
|
+
self.mock_db.record_debug.assert_called_with("DMI|value\n")
|
|
1033
1034
|
|
|
1034
1035
|
@patch("amd_debug.prerequisites.os.walk")
|
|
1035
1036
|
@patch("amd_debug.prerequisites.read_file")
|
|
@@ -1705,9 +1706,6 @@ class TestPrerequisiteValidator(unittest.TestCase):
|
|
|
1705
1706
|
]
|
|
1706
1707
|
result = self.validator.check_storage()
|
|
1707
1708
|
self.assertTrue(result)
|
|
1708
|
-
self.mock_db.record_debug.assert_called_with(
|
|
1709
|
-
"New enough kernel to avoid NVME check"
|
|
1710
|
-
)
|
|
1711
1709
|
|
|
1712
1710
|
def test_check_storage_no_kernel_log(self):
|
|
1713
1711
|
"""Test check_storage when kernel log is unavailable"""
|
|
@@ -1737,7 +1735,7 @@ class TestPrerequisiteValidator(unittest.TestCase):
|
|
|
1737
1735
|
result = self.validator.capture_edid()
|
|
1738
1736
|
self.assertTrue(result)
|
|
1739
1737
|
self.mock_db.record_prereq.assert_called_with(
|
|
1740
|
-
"
|
|
1738
|
+
"Failed to capture EDID table", "👀"
|
|
1741
1739
|
)
|
|
1742
1740
|
|
|
1743
1741
|
@patch("amd_debug.prerequisites.subprocess.check_output")
|
|
@@ -1750,9 +1748,9 @@ class TestPrerequisiteValidator(unittest.TestCase):
|
|
|
1750
1748
|
returncode=1, cmd="edid-decode", output=b"Error decoding EDID"
|
|
1751
1749
|
)
|
|
1752
1750
|
result = self.validator.capture_edid()
|
|
1753
|
-
self.
|
|
1751
|
+
self.assertTrue(result)
|
|
1754
1752
|
self.mock_db.record_prereq.assert_called_with(
|
|
1755
|
-
"Failed to capture EDID table
|
|
1753
|
+
"Failed to capture EDID table", "👀"
|
|
1756
1754
|
)
|
|
1757
1755
|
|
|
1758
1756
|
@patch("amd_debug.prerequisites.subprocess.check_output")
|
|
@@ -1767,3 +1765,164 @@ class TestPrerequisiteValidator(unittest.TestCase):
|
|
|
1767
1765
|
self.mock_db.record_debug.assert_called_with(
|
|
1768
1766
|
apply_prefix_wrapper("EDID for Monitor1:", "Decoded EDID data")
|
|
1769
1767
|
)
|
|
1768
|
+
|
|
1769
|
+
@patch("amd_debug.prerequisites.find_ip_version", return_value=True)
|
|
1770
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
1771
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
1772
|
+
def test_check_dpia_pg_dmcub_usb4_found(
|
|
1773
|
+
self, mock_read_file, mock_path_exists, mock_find_ip_version
|
|
1774
|
+
):
|
|
1775
|
+
"""Test check_dpia_pg_dmcub when USB4 routers are found"""
|
|
1776
|
+
usb4_device = MagicMock()
|
|
1777
|
+
self.mock_pyudev.list_devices.side_effect = [
|
|
1778
|
+
[usb4_device], # First call: USB4 present
|
|
1779
|
+
]
|
|
1780
|
+
result = self.validator.check_dpia_pg_dmcub()
|
|
1781
|
+
self.assertTrue(result)
|
|
1782
|
+
self.mock_db.record_debug.assert_called_with(
|
|
1783
|
+
"USB4 routers found, no need to check DMCUB version"
|
|
1784
|
+
)
|
|
1785
|
+
|
|
1786
|
+
@patch("amd_debug.prerequisites.find_ip_version", return_value=True)
|
|
1787
|
+
@patch("amd_debug.prerequisites.os.path.exists", return_value=True)
|
|
1788
|
+
@patch("amd_debug.prerequisites.read_file", return_value="0x90001B01")
|
|
1789
|
+
def test_check_dpia_pg_dmcub_dmcub_fw_version_new_enough(
|
|
1790
|
+
self, mock_read_file, mock_path_exists, mock_find_ip_version
|
|
1791
|
+
):
|
|
1792
|
+
"""Test check_dpia_pg_dmcub when DMCUB firmware version is new enough"""
|
|
1793
|
+
self.mock_pyudev.list_devices.side_effect = [
|
|
1794
|
+
[], # First call: no USB4
|
|
1795
|
+
[
|
|
1796
|
+
MagicMock(
|
|
1797
|
+
properties={
|
|
1798
|
+
"PCI_CLASS": "30000",
|
|
1799
|
+
"PCI_ID": "1002abcd",
|
|
1800
|
+
"PCI_SLOT_NAME": "0000:01:00.0",
|
|
1801
|
+
},
|
|
1802
|
+
sys_path="/sys/devices/pci0000:01/0000:01:00.0",
|
|
1803
|
+
)
|
|
1804
|
+
],
|
|
1805
|
+
]
|
|
1806
|
+
with patch("builtins.open", new_callable=mock_open, read_data="3") as mock_file:
|
|
1807
|
+
handlers = (
|
|
1808
|
+
mock_file.return_value,
|
|
1809
|
+
mock_open(read_data="5").return_value,
|
|
1810
|
+
mock_open(read_data="0").return_value,
|
|
1811
|
+
)
|
|
1812
|
+
mock_open.side_effect = handlers
|
|
1813
|
+
result = self.validator.check_dpia_pg_dmcub()
|
|
1814
|
+
self.assertTrue(result)
|
|
1815
|
+
self.mock_db.record_prereq.assert_not_called()
|
|
1816
|
+
|
|
1817
|
+
@patch("amd_debug.prerequisites.find_ip_version", return_value=True)
|
|
1818
|
+
@patch("amd_debug.prerequisites.os.path.exists", return_value=True)
|
|
1819
|
+
@patch("amd_debug.prerequisites.read_file", return_value="0x8001B00")
|
|
1820
|
+
def test_check_dpia_pg_dmcub_dmcub_fw_version_too_old(
|
|
1821
|
+
self, mock_read_file, mock_path_exists, mock_find_ip_version
|
|
1822
|
+
):
|
|
1823
|
+
"""Test check_dpia_pg_dmcub when DMCUB firmware version is too old"""
|
|
1824
|
+
self.mock_pyudev.list_devices.side_effect = [
|
|
1825
|
+
[], # First call: no USB4
|
|
1826
|
+
[
|
|
1827
|
+
MagicMock(
|
|
1828
|
+
properties={
|
|
1829
|
+
"PCI_CLASS": "30000",
|
|
1830
|
+
"PCI_ID": "1002abcd",
|
|
1831
|
+
"PCI_SLOT_NAME": "0000:01:00.0",
|
|
1832
|
+
},
|
|
1833
|
+
sys_path="/sys/devices/pci0000:01/0000:01:00.0",
|
|
1834
|
+
)
|
|
1835
|
+
],
|
|
1836
|
+
]
|
|
1837
|
+
result = self.validator.check_dpia_pg_dmcub()
|
|
1838
|
+
self.assertFalse(result)
|
|
1839
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
1840
|
+
"DMCUB Firmware is outdated", "❌"
|
|
1841
|
+
)
|
|
1842
|
+
self.assertTrue(
|
|
1843
|
+
any(isinstance(f, DmcubTooOld) for f in self.validator.failures)
|
|
1844
|
+
)
|
|
1845
|
+
|
|
1846
|
+
@patch("amd_debug.prerequisites.find_ip_version", return_value=True)
|
|
1847
|
+
@patch("amd_debug.prerequisites.os.path.exists", return_value=False)
|
|
1848
|
+
@patch(
|
|
1849
|
+
"amd_debug.prerequisites.read_file",
|
|
1850
|
+
side_effect=[
|
|
1851
|
+
"", # sysfs read returns empty, so fallback to debugfs
|
|
1852
|
+
"DMCUB fw: 09001B00\nOther line\n", # debugfs read
|
|
1853
|
+
],
|
|
1854
|
+
)
|
|
1855
|
+
def test_check_dpia_pg_dmcub_debugfs_version_new_enough(
|
|
1856
|
+
self, mock_read_file, mock_path_exists, mock_find_ip_version
|
|
1857
|
+
):
|
|
1858
|
+
"""Test check_dpia_pg_dmcub when DMCUB version is found in debugfs and is new enough"""
|
|
1859
|
+
self.mock_pyudev.list_devices.side_effect = [
|
|
1860
|
+
[], # First call: no USB4
|
|
1861
|
+
[
|
|
1862
|
+
MagicMock(
|
|
1863
|
+
properties={
|
|
1864
|
+
"PCI_CLASS": "30000",
|
|
1865
|
+
"PCI_ID": "1002abcd",
|
|
1866
|
+
"PCI_SLOT_NAME": "0",
|
|
1867
|
+
},
|
|
1868
|
+
sys_path="/sys/devices/pci0000:01/0000:01:00.0",
|
|
1869
|
+
)
|
|
1870
|
+
],
|
|
1871
|
+
]
|
|
1872
|
+
result = self.validator.check_dpia_pg_dmcub()
|
|
1873
|
+
self.assertTrue(result)
|
|
1874
|
+
self.mock_db.record_prereq.assert_not_called()
|
|
1875
|
+
|
|
1876
|
+
@patch("amd_debug.prerequisites.find_ip_version", return_value=True)
|
|
1877
|
+
@patch("amd_debug.prerequisites.os.path.exists", return_value=False)
|
|
1878
|
+
@patch(
|
|
1879
|
+
"amd_debug.prerequisites.read_file",
|
|
1880
|
+
side_effect=[
|
|
1881
|
+
"DMCUB fw: 0x08001B00\nOther line\n", # debugfs read
|
|
1882
|
+
],
|
|
1883
|
+
)
|
|
1884
|
+
def test_check_dpia_pg_dmcub_debugfs_version_too_old(
|
|
1885
|
+
self, mock_read_file, mock_path_exists, mock_find_ip_version
|
|
1886
|
+
):
|
|
1887
|
+
"""Test check_dpia_pg_dmcub when DMCUB version is found in debugfs and is too old"""
|
|
1888
|
+
self.mock_pyudev.list_devices.side_effect = [
|
|
1889
|
+
[], # First call: no USB4
|
|
1890
|
+
[
|
|
1891
|
+
MagicMock(
|
|
1892
|
+
properties={
|
|
1893
|
+
"PCI_CLASS": "30000",
|
|
1894
|
+
"PCI_ID": "1002abcd",
|
|
1895
|
+
"PCI_SLOT_NAME": "0",
|
|
1896
|
+
},
|
|
1897
|
+
sys_path="/sys/devices/pci0000:01/0000:01:00.0",
|
|
1898
|
+
)
|
|
1899
|
+
],
|
|
1900
|
+
]
|
|
1901
|
+
result = self.validator.check_dpia_pg_dmcub()
|
|
1902
|
+
self.assertFalse(result)
|
|
1903
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
1904
|
+
"DMCUB Firmware is outdated", "❌"
|
|
1905
|
+
)
|
|
1906
|
+
self.assertTrue(
|
|
1907
|
+
any(isinstance(f, DmcubTooOld) for f in self.validator.failures)
|
|
1908
|
+
)
|
|
1909
|
+
|
|
1910
|
+
@patch("amd_debug.prerequisites.find_ip_version", return_value=False)
|
|
1911
|
+
def test_check_dpia_pg_dmcub_no_matching_dcn(self, mock_find_ip_version):
|
|
1912
|
+
"""Test check_dpia_pg_dmcub when no matching DCN is found"""
|
|
1913
|
+
self.mock_pyudev.list_devices.side_effect = [
|
|
1914
|
+
[], # First call: no USB4
|
|
1915
|
+
[
|
|
1916
|
+
MagicMock(
|
|
1917
|
+
properties={
|
|
1918
|
+
"PCI_CLASS": "30000",
|
|
1919
|
+
"PCI_ID": "1002abcd",
|
|
1920
|
+
"PCI_SLOT_NAME": "0",
|
|
1921
|
+
},
|
|
1922
|
+
sys_path="/sys/devices/pci0000:01/0000:01:00.0",
|
|
1923
|
+
)
|
|
1924
|
+
],
|
|
1925
|
+
]
|
|
1926
|
+
result = self.validator.check_dpia_pg_dmcub()
|
|
1927
|
+
self.assertTrue(result)
|
|
1928
|
+
self.mock_db.record_prereq.assert_not_called()
|