amd-debug-tools 0.2.2__py3-none-any.whl → 0.2.12__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/__init__.py +10 -3
- amd_debug/acpi.py +0 -1
- amd_debug/battery.py +0 -1
- amd_debug/bios.py +8 -5
- amd_debug/common.py +70 -3
- amd_debug/database.py +22 -5
- amd_debug/display.py +2 -3
- amd_debug/failures.py +44 -3
- amd_debug/installer.py +7 -6
- amd_debug/kernel.py +20 -17
- amd_debug/prerequisites.py +166 -61
- amd_debug/pstate.py +9 -6
- amd_debug/s2idle.py +53 -44
- amd_debug/sleep_report.py +129 -87
- amd_debug/templates/html +4 -2
- amd_debug/ttm.py +157 -0
- amd_debug/validator.py +61 -34
- amd_debug/wake.py +0 -1
- amd_debug_tools-0.2.12.dist-info/METADATA +75 -0
- amd_debug_tools-0.2.12.dist-info/RECORD +47 -0
- {amd_debug_tools-0.2.2.dist-info → amd_debug_tools-0.2.12.dist-info}/entry_points.txt +1 -0
- {amd_debug_tools-0.2.2.dist-info → amd_debug_tools-0.2.12.dist-info}/top_level.txt +1 -0
- launcher.py +0 -1
- test_acpi.py +1 -1
- test_bios.py +30 -12
- test_common.py +117 -1
- test_database.py +1 -1
- test_display.py +6 -6
- test_installer.py +68 -1
- test_kernel.py +7 -6
- test_launcher.py +9 -1
- test_prerequisites.py +629 -26
- test_s2idle.py +66 -19
- test_sleep_report.py +29 -0
- test_ttm.py +276 -0
- test_validator.py +187 -16
- amd_debug_tools-0.2.2.dist-info/METADATA +0 -181
- amd_debug_tools-0.2.2.dist-info/RECORD +0 -45
- {amd_debug_tools-0.2.2.dist-info → amd_debug_tools-0.2.12.dist-info}/WHEEL +0 -0
- {amd_debug_tools-0.2.2.dist-info → amd_debug_tools-0.2.12.dist-info}/licenses/LICENSE +0 -0
test_prerequisites.py
CHANGED
|
@@ -8,6 +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
|
+
import struct
|
|
11
12
|
from unittest.mock import patch, MagicMock, mock_open
|
|
12
13
|
|
|
13
14
|
from amd_debug.prerequisites import PrerequisiteValidator
|
|
@@ -143,6 +144,16 @@ class TestPrerequisiteValidator(unittest.TestCase):
|
|
|
143
144
|
result = self.validator.check_port_pm_override()
|
|
144
145
|
self.assertTrue(result)
|
|
145
146
|
|
|
147
|
+
@patch("amd_debug.prerequisites.version.parse")
|
|
148
|
+
def test_check_port_pm_override_smu_version_missing(self, mock_version_parse):
|
|
149
|
+
"""Test check_port_pm_override with SMU version undefined"""
|
|
150
|
+
self.validator.cpu_family = 0x19
|
|
151
|
+
self.validator.cpu_model = 0x74
|
|
152
|
+
mock_version_parse.side_effect = lambda v: v if isinstance(v, str) else None
|
|
153
|
+
self.validator.smu_version = ""
|
|
154
|
+
result = self.validator.check_port_pm_override()
|
|
155
|
+
self.assertTrue(result)
|
|
156
|
+
|
|
146
157
|
@patch("amd_debug.prerequisites.version.parse")
|
|
147
158
|
def test_check_port_pm_override_smu_version_too_low(self, mock_version_parse):
|
|
148
159
|
"""Test check_port_pm_override with SMU version < 76.18.0"""
|
|
@@ -185,15 +196,18 @@ class TestPrerequisiteValidator(unittest.TestCase):
|
|
|
185
196
|
new_callable=unittest.mock.mock_open,
|
|
186
197
|
read_data=b"\x00" * 45,
|
|
187
198
|
)
|
|
188
|
-
|
|
189
|
-
|
|
199
|
+
@patch("amd_debug.prerequisites.os.path.exists", return_value=True)
|
|
200
|
+
def test_check_iommu_no_dma_protection_no_msft0201(self, _mock_open, _mock_exists):
|
|
201
|
+
"""Test check_iommu when DMA protection is not enabled and no MSFT0201 in IVRS"""
|
|
190
202
|
self.validator.cpu_family = 0x1A
|
|
191
203
|
self.validator.cpu_model = 0x20
|
|
192
204
|
iommu_device = MagicMock(sys_path="/sys/devices/iommu")
|
|
205
|
+
acpi_device = MagicMock(sys_path="/sys/devices/acpi/MSFT0201")
|
|
206
|
+
platform_device = MagicMock(sys_path="/sys/devices/platform/MSFT0201")
|
|
193
207
|
self.mock_pyudev.list_devices.side_effect = [
|
|
194
208
|
[iommu_device],
|
|
195
|
-
[],
|
|
196
|
-
[],
|
|
209
|
+
[acpi_device],
|
|
210
|
+
[platform_device],
|
|
197
211
|
]
|
|
198
212
|
result = self.validator.check_iommu()
|
|
199
213
|
self.assertFalse(result)
|
|
@@ -204,6 +218,27 @@ class TestPrerequisiteValidator(unittest.TestCase):
|
|
|
204
218
|
"IOMMU is misconfigured: Pre-boot DMA protection not enabled", "❌"
|
|
205
219
|
)
|
|
206
220
|
|
|
221
|
+
@patch(
|
|
222
|
+
"amd_debug.prerequisites.open",
|
|
223
|
+
new_callable=unittest.mock.mock_open,
|
|
224
|
+
read_data=b"\x00" * 45 + "MSFT0201".encode("utf-8"),
|
|
225
|
+
)
|
|
226
|
+
@patch("amd_debug.prerequisites.os.path.exists", return_value=True)
|
|
227
|
+
def test_check_iommu_no_dma_protection_BUT_msft0201(self, _mock_open, _mock_exists):
|
|
228
|
+
"""Test check_iommu when DMA protection is not enabled BUT MSFT0201 is in IVRS"""
|
|
229
|
+
self.validator.cpu_family = 0x1A
|
|
230
|
+
self.validator.cpu_model = 0x20
|
|
231
|
+
iommu_device = MagicMock(sys_path="/sys/devices/iommu")
|
|
232
|
+
acpi_device = MagicMock(sys_path="/sys/devices/acpi/MSFT0201")
|
|
233
|
+
platform_device = MagicMock(sys_path="/sys/devices/platform/MSFT0201")
|
|
234
|
+
self.mock_pyudev.list_devices.side_effect = [
|
|
235
|
+
[iommu_device],
|
|
236
|
+
[acpi_device],
|
|
237
|
+
[platform_device],
|
|
238
|
+
]
|
|
239
|
+
result = self.validator.check_iommu()
|
|
240
|
+
self.assertTrue(result)
|
|
241
|
+
|
|
207
242
|
@patch(
|
|
208
243
|
"amd_debug.prerequisites.open",
|
|
209
244
|
new_callable=unittest.mock.mock_open,
|
|
@@ -292,7 +327,7 @@ class TestPrerequisiteValidator(unittest.TestCase):
|
|
|
292
327
|
BIT(9) | 1
|
|
293
328
|
) # Kernel warnings ignored, other taint present
|
|
294
329
|
result = self.validator.check_taint()
|
|
295
|
-
self.
|
|
330
|
+
self.assertTrue(result)
|
|
296
331
|
self.assertTrue(
|
|
297
332
|
any(isinstance(f, TaintedKernel) for f in self.validator.failures)
|
|
298
333
|
)
|
|
@@ -402,6 +437,176 @@ class TestPrerequisiteValidator(unittest.TestCase):
|
|
|
402
437
|
"This CPU model does not support hardware sleep over s2idle", "❌"
|
|
403
438
|
)
|
|
404
439
|
|
|
440
|
+
@patch("builtins.open", new_callable=mock_open)
|
|
441
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
442
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
443
|
+
def test_check_cpu_limited_cores_single_ccd(
|
|
444
|
+
self, mock_path_exists, mock_read_file, mock_file_open
|
|
445
|
+
):
|
|
446
|
+
"""Test check_cpu with artificially limited CPUs on single-CCD system"""
|
|
447
|
+
self.validator.cpu_family = 0x19
|
|
448
|
+
self.validator.cpu_model = 0x74
|
|
449
|
+
mock_read_file.return_value = "7" # kernel_max = 7 (8 cores)
|
|
450
|
+
mock_path_exists.return_value = True
|
|
451
|
+
# Simulate finding socket level at subleaf 1
|
|
452
|
+
# First call: subleaf 0, level_type = 1 (not socket)
|
|
453
|
+
# Second call: subleaf 1, level_type = 4 (socket level)
|
|
454
|
+
# Third call: read cpu_count from subleaf 1
|
|
455
|
+
mock_file_open.return_value.read.side_effect = [
|
|
456
|
+
struct.pack("4I", 0, 0, 0x100, 0), # subleaf 0: level_type = 1 (thread)
|
|
457
|
+
struct.pack(
|
|
458
|
+
"4I", 0, 0, 0x400, 0
|
|
459
|
+
), # subleaf 1: level_type = 4 (socket) - FOUND
|
|
460
|
+
struct.pack("4I", 0, 16, 0, 0), # subleaf 1: cpu_count = 16
|
|
461
|
+
]
|
|
462
|
+
result = self.validator.check_cpu()
|
|
463
|
+
self.assertFalse(result)
|
|
464
|
+
self.assertTrue(
|
|
465
|
+
any(isinstance(f, LimitedCores) for f in self.validator.failures)
|
|
466
|
+
)
|
|
467
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
468
|
+
"The kernel has been limited to 8 CPU cores, but the system has 16 cores",
|
|
469
|
+
"❌",
|
|
470
|
+
)
|
|
471
|
+
|
|
472
|
+
@patch("builtins.open", new_callable=mock_open)
|
|
473
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
474
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
475
|
+
def test_check_cpu_limited_cores_multi_ccd(
|
|
476
|
+
self, mock_path_exists, mock_read_file, mock_file_open
|
|
477
|
+
):
|
|
478
|
+
"""Test check_cpu with multi-CCD system (tests socket-level counting)"""
|
|
479
|
+
self.validator.cpu_family = 0x19
|
|
480
|
+
self.validator.cpu_model = 0x74
|
|
481
|
+
mock_read_file.return_value = "15" # kernel_max = 15 (16 cores)
|
|
482
|
+
mock_path_exists.return_value = True
|
|
483
|
+
# Simulate multi-CCD: iterate through levels to find socket
|
|
484
|
+
# subleaf 0: level_type = 1 (thread)
|
|
485
|
+
# subleaf 1: level_type = 2 (core)
|
|
486
|
+
# subleaf 2: level_type = 3 (complex/CCD)
|
|
487
|
+
# subleaf 3: level_type = 4 (socket) - FOUND
|
|
488
|
+
mock_file_open.return_value.read.side_effect = [
|
|
489
|
+
struct.pack("4I", 0, 0, 0x100, 0), # subleaf 0: level_type = 1 (thread)
|
|
490
|
+
struct.pack("4I", 0, 0, 0x200, 0), # subleaf 1: level_type = 2 (core)
|
|
491
|
+
struct.pack(
|
|
492
|
+
"4I", 0, 0, 0x300, 0
|
|
493
|
+
), # subleaf 2: level_type = 3 (complex/CCD)
|
|
494
|
+
struct.pack(
|
|
495
|
+
"4I", 0, 0, 0x400, 0
|
|
496
|
+
), # subleaf 3: level_type = 4 (socket) - FOUND
|
|
497
|
+
struct.pack("4I", 0, 32, 0, 0), # subleaf 3: cpu_count = 32
|
|
498
|
+
]
|
|
499
|
+
result = self.validator.check_cpu()
|
|
500
|
+
self.assertFalse(result)
|
|
501
|
+
self.assertTrue(
|
|
502
|
+
any(isinstance(f, LimitedCores) for f in self.validator.failures)
|
|
503
|
+
)
|
|
504
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
505
|
+
"The kernel has been limited to 16 CPU cores, but the system has 32 cores",
|
|
506
|
+
"❌",
|
|
507
|
+
)
|
|
508
|
+
|
|
509
|
+
@patch("builtins.open", new_callable=mock_open)
|
|
510
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
511
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
512
|
+
def test_check_cpu_not_limited(
|
|
513
|
+
self, mock_path_exists, mock_read_file, mock_file_open
|
|
514
|
+
):
|
|
515
|
+
"""Test check_cpu when CPUs are not artificially limited"""
|
|
516
|
+
self.validator.cpu_family = 0x19
|
|
517
|
+
self.validator.cpu_model = 0x74
|
|
518
|
+
mock_read_file.return_value = "31" # kernel_max = 31 (32 cores)
|
|
519
|
+
mock_path_exists.return_value = True
|
|
520
|
+
# Simulate finding socket level
|
|
521
|
+
mock_file_open.return_value.read.side_effect = [
|
|
522
|
+
struct.pack("4I", 0, 0, 0x100, 0), # subleaf 0: level_type = 1
|
|
523
|
+
struct.pack("4I", 0, 0, 0x400, 0), # subleaf 1: level_type = 4 (socket)
|
|
524
|
+
struct.pack("4I", 0, 16, 0, 0), # subleaf 1: cpu_count = 16
|
|
525
|
+
]
|
|
526
|
+
result = self.validator.check_cpu()
|
|
527
|
+
self.assertTrue(result)
|
|
528
|
+
self.mock_db.record_debug.assert_called_with("CPU core count: 16 max: 32")
|
|
529
|
+
|
|
530
|
+
@patch("builtins.open", new_callable=mock_open)
|
|
531
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
532
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
533
|
+
def test_check_cpu_socket_level_at_boundary(
|
|
534
|
+
self, mock_path_exists, mock_read_file, mock_file_open
|
|
535
|
+
):
|
|
536
|
+
"""Test check_cpu when socket level is found at the last checked subleaf"""
|
|
537
|
+
self.validator.cpu_family = 0x19
|
|
538
|
+
self.validator.cpu_model = 0x74
|
|
539
|
+
mock_read_file.return_value = "15" # kernel_max = 15 (16 cores)
|
|
540
|
+
mock_path_exists.return_value = True
|
|
541
|
+
# Socket level found at subleaf 4 (last iteration)
|
|
542
|
+
mock_file_open.return_value.read.side_effect = [
|
|
543
|
+
struct.pack("4I", 0, 0, 0x100, 0), # subleaf 0: level_type = 1
|
|
544
|
+
struct.pack("4I", 0, 0, 0x200, 0), # subleaf 1: level_type = 2
|
|
545
|
+
struct.pack("4I", 0, 0, 0x300, 0), # subleaf 2: level_type = 3
|
|
546
|
+
struct.pack("4I", 0, 0, 0x000, 0), # subleaf 3: level_type = 0
|
|
547
|
+
struct.pack("4I", 0, 0, 0x400, 0), # subleaf 4: level_type = 4 (socket)
|
|
548
|
+
struct.pack("4I", 0, 16, 0, 0), # subleaf 4: cpu_count = 16
|
|
549
|
+
]
|
|
550
|
+
result = self.validator.check_cpu()
|
|
551
|
+
self.assertTrue(result)
|
|
552
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
553
|
+
"Unable to discover CPU topology, didn't find socket level", "🚦"
|
|
554
|
+
)
|
|
555
|
+
|
|
556
|
+
@patch("builtins.open", new_callable=mock_open)
|
|
557
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
558
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
559
|
+
def test_check_cpu_socket_level_first_subleaf(
|
|
560
|
+
self, mock_path_exists, mock_read_file, mock_file_open
|
|
561
|
+
):
|
|
562
|
+
"""Test check_cpu when socket level is found at first subleaf"""
|
|
563
|
+
self.validator.cpu_family = 0x19
|
|
564
|
+
self.validator.cpu_model = 0x74
|
|
565
|
+
mock_read_file.return_value = "7" # kernel_max = 7 (8 cores)
|
|
566
|
+
mock_path_exists.return_value = True
|
|
567
|
+
# Socket level found immediately at subleaf 0
|
|
568
|
+
mock_file_open.return_value.read.side_effect = [
|
|
569
|
+
struct.pack(
|
|
570
|
+
"4I", 0, 0, 0x400, 0
|
|
571
|
+
), # subleaf 0: level_type = 4 (socket) - FOUND
|
|
572
|
+
struct.pack("4I", 0, 8, 0, 0), # subleaf 0: cpu_count = 8
|
|
573
|
+
]
|
|
574
|
+
result = self.validator.check_cpu()
|
|
575
|
+
self.assertTrue(result)
|
|
576
|
+
self.mock_db.record_debug.assert_called_with("CPU core count: 8 max: 8")
|
|
577
|
+
|
|
578
|
+
@patch("builtins.open", side_effect=FileNotFoundError)
|
|
579
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
580
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
581
|
+
def test_check_cpu_cpuid_file_not_found(
|
|
582
|
+
self, mock_path_exists, mock_read_file, mock_file_open
|
|
583
|
+
):
|
|
584
|
+
"""Test check_cpu when cpuid kernel module is not loaded"""
|
|
585
|
+
self.validator.cpu_family = 0x19
|
|
586
|
+
self.validator.cpu_model = 0x74
|
|
587
|
+
mock_read_file.return_value = "7"
|
|
588
|
+
mock_path_exists.return_value = False
|
|
589
|
+
result = self.validator.check_cpu()
|
|
590
|
+
self.assertFalse(result)
|
|
591
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
592
|
+
"Unable to check CPU topology: cpuid kernel module not loaded", "❌"
|
|
593
|
+
)
|
|
594
|
+
|
|
595
|
+
@patch("builtins.open", side_effect=PermissionError)
|
|
596
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
597
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
598
|
+
def test_check_cpu_cpuid_permission_error(
|
|
599
|
+
self, mock_path_exists, mock_read_file, mock_file_open
|
|
600
|
+
):
|
|
601
|
+
"""Test check_cpu when there is a permission error accessing cpuid"""
|
|
602
|
+
self.validator.cpu_family = 0x19
|
|
603
|
+
self.validator.cpu_model = 0x74
|
|
604
|
+
mock_read_file.return_value = "7"
|
|
605
|
+
mock_path_exists.return_value = True
|
|
606
|
+
result = self.validator.check_cpu()
|
|
607
|
+
self.assertTrue(result)
|
|
608
|
+
self.mock_db.record_prereq.assert_called_with("CPUID checks unavailable", "🚦")
|
|
609
|
+
|
|
405
610
|
@patch("amd_debug.prerequisites.os.walk")
|
|
406
611
|
@patch(
|
|
407
612
|
"amd_debug.prerequisites.open",
|
|
@@ -500,9 +705,7 @@ class TestPrerequisiteValidator(unittest.TestCase):
|
|
|
500
705
|
result = self.validator.map_acpi_path()
|
|
501
706
|
self.assertTrue(result)
|
|
502
707
|
self.mock_db.record_debug.assert_called_with(
|
|
503
|
-
"ACPI name
|
|
504
|
-
"│ device1: mocked_path [driver]\n"
|
|
505
|
-
"└─device2: mocked_path [driver]\n"
|
|
708
|
+
"ACPI name | ACPI path | Kernel driver\ndevice1 | mocked_path | driver\ndevice2 | mocked_path | driver\n"
|
|
506
709
|
)
|
|
507
710
|
|
|
508
711
|
@patch("amd_debug.prerequisites.os.path.exists")
|
|
@@ -545,7 +748,7 @@ class TestPrerequisiteValidator(unittest.TestCase):
|
|
|
545
748
|
result = self.validator.map_acpi_path()
|
|
546
749
|
self.assertTrue(result)
|
|
547
750
|
self.mock_db.record_debug.assert_called_with(
|
|
548
|
-
"ACPI name
|
|
751
|
+
"ACPI name | ACPI path | Kernel driver\ndevice1 | mocked_path | None\n"
|
|
549
752
|
)
|
|
550
753
|
|
|
551
754
|
@patch("amd_debug.prerequisites.read_file")
|
|
@@ -569,8 +772,7 @@ class TestPrerequisiteValidator(unittest.TestCase):
|
|
|
569
772
|
|
|
570
773
|
self.validator.capture_pci_acpi()
|
|
571
774
|
self.mock_db.record_debug.assert_called_with(
|
|
572
|
-
"PCI
|
|
573
|
-
"└─0000:00:1f.0 : Intel Corporation ISA bridge [1234abcd] : mocked_acpi_path\n"
|
|
775
|
+
"PCI Slot | Vendor | Class | ID | ACPI path\n└─0000:00:1f.0 | Intel Corporation | ISA bridge | 1234abcd | mocked_acpi_path\n"
|
|
574
776
|
)
|
|
575
777
|
|
|
576
778
|
@patch("amd_debug.prerequisites.read_file")
|
|
@@ -593,8 +795,7 @@ class TestPrerequisiteValidator(unittest.TestCase):
|
|
|
593
795
|
|
|
594
796
|
self.validator.capture_pci_acpi()
|
|
595
797
|
self.mock_db.record_debug.assert_called_with(
|
|
596
|
-
"PCI
|
|
597
|
-
"└─0000:01:00.0 : NVIDIA Corporation VGA compatible controller [5678efgh]\n"
|
|
798
|
+
"PCI Slot | Vendor | Class | ID | ACPI path\n└─0000:01:00.0 | NVIDIA Corporation | VGA compatible controller | 5678efgh | \n"
|
|
598
799
|
)
|
|
599
800
|
|
|
600
801
|
@patch("amd_debug.prerequisites.read_file")
|
|
@@ -628,9 +829,7 @@ class TestPrerequisiteValidator(unittest.TestCase):
|
|
|
628
829
|
|
|
629
830
|
self.validator.capture_pci_acpi()
|
|
630
831
|
self.mock_db.record_debug.assert_called_with(
|
|
631
|
-
"PCI
|
|
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"
|
|
832
|
+
"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"
|
|
634
833
|
)
|
|
635
834
|
|
|
636
835
|
def test_capture_pci_acpi_no_devices(self):
|
|
@@ -638,7 +837,9 @@ class TestPrerequisiteValidator(unittest.TestCase):
|
|
|
638
837
|
self.mock_pyudev.list_devices.return_value = []
|
|
639
838
|
|
|
640
839
|
self.validator.capture_pci_acpi()
|
|
641
|
-
self.mock_db.record_debug.assert_called_with(
|
|
840
|
+
self.mock_db.record_debug.assert_called_with(
|
|
841
|
+
"PCI Slot | Vendor | Class | ID | ACPI path\n"
|
|
842
|
+
)
|
|
642
843
|
|
|
643
844
|
@patch("amd_debug.prerequisites.read_file")
|
|
644
845
|
def test_check_aspm_default_policy(self, mock_read_file):
|
|
@@ -802,8 +1003,7 @@ class TestPrerequisiteValidator(unittest.TestCase):
|
|
|
802
1003
|
self.assertFalse(result)
|
|
803
1004
|
self.assertTrue(any(isinstance(f, FadtWrong) for f in self.validator.failures))
|
|
804
1005
|
|
|
805
|
-
|
|
806
|
-
def test_check_fadt_file_not_found(self, mock_path_exists):
|
|
1006
|
+
def test_check_fadt_file_not_found(self):
|
|
807
1007
|
"""Test check_fadt when FADT file is not found"""
|
|
808
1008
|
self.mock_kernel_log.match_line.return_value = False
|
|
809
1009
|
result = self.validator.check_fadt()
|
|
@@ -1006,7 +1206,7 @@ class TestPrerequisiteValidator(unittest.TestCase):
|
|
|
1006
1206
|
"MockVendor MockProduct (MockFamily)", "💻"
|
|
1007
1207
|
)
|
|
1008
1208
|
self.mock_db.record_debug.assert_called_with(
|
|
1009
|
-
"DMI
|
|
1209
|
+
"DMI|value\nchassis_type| Desktop\n"
|
|
1010
1210
|
)
|
|
1011
1211
|
|
|
1012
1212
|
@patch("amd_debug.prerequisites.os.walk")
|
|
@@ -1034,7 +1234,7 @@ class TestPrerequisiteValidator(unittest.TestCase):
|
|
|
1034
1234
|
self.mock_db.record_prereq.assert_called_with(
|
|
1035
1235
|
"MockVendor MockProduct (MockFamily)", "💻"
|
|
1036
1236
|
)
|
|
1037
|
-
self.mock_db.record_debug.assert_called_with("DMI
|
|
1237
|
+
self.mock_db.record_debug.assert_called_with("DMI|value\n")
|
|
1038
1238
|
|
|
1039
1239
|
@patch("amd_debug.prerequisites.os.walk")
|
|
1040
1240
|
@patch("amd_debug.prerequisites.read_file")
|
|
@@ -1083,7 +1283,7 @@ class TestPrerequisiteValidator(unittest.TestCase):
|
|
|
1083
1283
|
|
|
1084
1284
|
@patch("amd_debug.prerequisites.os.path.exists")
|
|
1085
1285
|
@patch(
|
|
1086
|
-
"
|
|
1286
|
+
"amd_debug.prerequisites.open",
|
|
1087
1287
|
new_callable=unittest.mock.mock_open,
|
|
1088
1288
|
read_data="ignore_wake_value",
|
|
1089
1289
|
)
|
|
@@ -1098,7 +1298,11 @@ class TestPrerequisiteValidator(unittest.TestCase):
|
|
|
1098
1298
|
)
|
|
1099
1299
|
|
|
1100
1300
|
@patch("amd_debug.prerequisites.os.path.exists")
|
|
1101
|
-
@patch(
|
|
1301
|
+
@patch(
|
|
1302
|
+
"amd_debug.prerequisites.open",
|
|
1303
|
+
new_callable=unittest.mock.mock_open,
|
|
1304
|
+
read_data="(null)",
|
|
1305
|
+
)
|
|
1102
1306
|
def test_capture_disabled_pins_with_null_values(self, _mock_open, mock_path_exists):
|
|
1103
1307
|
mock_path_exists.side_effect = (
|
|
1104
1308
|
lambda path: "ignore_wake" in path or "ignore_interrupt" in path
|
|
@@ -1710,9 +1914,6 @@ class TestPrerequisiteValidator(unittest.TestCase):
|
|
|
1710
1914
|
]
|
|
1711
1915
|
result = self.validator.check_storage()
|
|
1712
1916
|
self.assertTrue(result)
|
|
1713
|
-
self.mock_db.record_debug.assert_called_with(
|
|
1714
|
-
"New enough kernel to avoid NVME check"
|
|
1715
|
-
)
|
|
1716
1917
|
|
|
1717
1918
|
def test_check_storage_no_kernel_log(self):
|
|
1718
1919
|
"""Test check_storage when kernel log is unavailable"""
|
|
@@ -1933,3 +2134,405 @@ class TestPrerequisiteValidator(unittest.TestCase):
|
|
|
1933
2134
|
result = self.validator.check_dpia_pg_dmcub()
|
|
1934
2135
|
self.assertTrue(result)
|
|
1935
2136
|
self.mock_db.record_prereq.assert_not_called()
|
|
2137
|
+
|
|
2138
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
2139
|
+
def test_capture_nvidia_version_file_missing(self, mock_exists):
|
|
2140
|
+
"""Test capture_nvidia when /proc/driver/nvidia/version does not exist"""
|
|
2141
|
+
mock_exists.side_effect = lambda p: False if "version" in p else True
|
|
2142
|
+
result = self.validator.capture_nvidia()
|
|
2143
|
+
self.assertTrue(result)
|
|
2144
|
+
self.mock_db.record_debug_file.assert_not_called()
|
|
2145
|
+
self.mock_db.record_prereq.assert_not_called()
|
|
2146
|
+
|
|
2147
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
2148
|
+
def test_capture_nvidia_gpus_dir_missing(self, mock_exists):
|
|
2149
|
+
"""Test capture_nvidia when /proc/driver/nvidia/gpus does not exist"""
|
|
2150
|
+
|
|
2151
|
+
def exists_side_effect(path):
|
|
2152
|
+
if "version" in path:
|
|
2153
|
+
return True
|
|
2154
|
+
if "gpus" in path:
|
|
2155
|
+
return False
|
|
2156
|
+
return True
|
|
2157
|
+
|
|
2158
|
+
mock_exists.side_effect = exists_side_effect
|
|
2159
|
+
result = self.validator.capture_nvidia()
|
|
2160
|
+
self.assertTrue(result)
|
|
2161
|
+
self.mock_db.record_debug_file.assert_called_once_with(
|
|
2162
|
+
"/proc/driver/nvidia/version"
|
|
2163
|
+
)
|
|
2164
|
+
self.mock_db.record_prereq.assert_not_called()
|
|
2165
|
+
|
|
2166
|
+
@patch("amd_debug.prerequisites.os.walk")
|
|
2167
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
2168
|
+
def test_capture_nvidia_success(self, mock_exists, mock_walk):
|
|
2169
|
+
"""Test capture_nvidia when NVIDIA GPU files are present and readable"""
|
|
2170
|
+
mock_exists.side_effect = lambda p: True
|
|
2171
|
+
mock_walk.return_value = [
|
|
2172
|
+
("/proc/driver/nvidia/gpus/0000:01:00.0", [], ["info", "power"])
|
|
2173
|
+
]
|
|
2174
|
+
result = self.validator.capture_nvidia()
|
|
2175
|
+
self.assertTrue(result)
|
|
2176
|
+
self.mock_db.record_debug_file.assert_any_call("/proc/driver/nvidia/version")
|
|
2177
|
+
self.mock_db.record_debug.assert_any_call("NVIDIA info")
|
|
2178
|
+
self.mock_db.record_debug_file.assert_any_call(
|
|
2179
|
+
"/proc/driver/nvidia/gpus/0000:01:00.0/info"
|
|
2180
|
+
)
|
|
2181
|
+
self.mock_db.record_debug.assert_any_call("NVIDIA power")
|
|
2182
|
+
self.mock_db.record_debug_file.assert_any_call(
|
|
2183
|
+
"/proc/driver/nvidia/gpus/0000:01:00.0/power"
|
|
2184
|
+
)
|
|
2185
|
+
|
|
2186
|
+
@patch("amd_debug.prerequisites.os.walk")
|
|
2187
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
2188
|
+
def test_capture_nvidia_permission_error_on_version(self, mock_exists, mock_walk):
|
|
2189
|
+
"""Test capture_nvidia when PermissionError occurs reading version file"""
|
|
2190
|
+
mock_exists.side_effect = lambda p: True if "version" in p else False
|
|
2191
|
+
self.mock_db.record_debug_file.side_effect = PermissionError
|
|
2192
|
+
result = self.validator.capture_nvidia()
|
|
2193
|
+
self.assertTrue(result)
|
|
2194
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
2195
|
+
"NVIDIA GPU version not readable", "👀"
|
|
2196
|
+
)
|
|
2197
|
+
|
|
2198
|
+
@patch("amd_debug.prerequisites.os.walk")
|
|
2199
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
2200
|
+
def test_capture_nvidia_permission_error_on_gpu_file(self, mock_exists, mock_walk):
|
|
2201
|
+
"""Test capture_nvidia when PermissionError occurs reading a GPU file"""
|
|
2202
|
+
mock_exists.side_effect = lambda p: True
|
|
2203
|
+
mock_walk.return_value = [
|
|
2204
|
+
("/proc/driver/nvidia/gpus/0000:01:00.0", [], ["info"])
|
|
2205
|
+
]
|
|
2206
|
+
self.mock_db.record_debug_file.side_effect = [None, PermissionError]
|
|
2207
|
+
result = self.validator.capture_nvidia()
|
|
2208
|
+
self.assertTrue(result)
|
|
2209
|
+
self.mock_db.record_debug.assert_any_call("NVIDIA info")
|
|
2210
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
2211
|
+
"NVIDIA GPU {f} not readable", "👀"
|
|
2212
|
+
)
|
|
2213
|
+
|
|
2214
|
+
@patch("amd_debug.prerequisites.os.walk")
|
|
2215
|
+
@patch(
|
|
2216
|
+
"builtins.open",
|
|
2217
|
+
new_callable=unittest.mock.mock_open,
|
|
2218
|
+
read_data=b"C1 state info",
|
|
2219
|
+
)
|
|
2220
|
+
def test_capture_cstates_single_file(self, mock_open, mock_walk):
|
|
2221
|
+
"""Test capture_cstates with a single cpuidle file"""
|
|
2222
|
+
mock_walk.return_value = [
|
|
2223
|
+
("/sys/bus/cpu/devices/cpu0/cpuidle", [], ["state1"]),
|
|
2224
|
+
]
|
|
2225
|
+
self.validator.capture_cstates()
|
|
2226
|
+
self.mock_db.record_debug.assert_called_with(
|
|
2227
|
+
"ACPI C-state information\n└─/sys/bus/cpu/devices/cpu0/cpuidle/state1: C1 state info"
|
|
2228
|
+
)
|
|
2229
|
+
|
|
2230
|
+
@patch("amd_debug.prerequisites.os.walk")
|
|
2231
|
+
@patch("builtins.open", new_callable=mock_open)
|
|
2232
|
+
def test_capture_cstates_multiple_files(self, mock_open_func, mock_walk):
|
|
2233
|
+
"""Test capture_cstates with multiple cpuidle files"""
|
|
2234
|
+
# Setup mock file reads for two files
|
|
2235
|
+
file_contents = {
|
|
2236
|
+
"/sys/bus/cpu/devices/cpu0/cpuidle/state1": b"C1 info",
|
|
2237
|
+
"/sys/bus/cpu/devices/cpu0/cpuidle/state2": b"C2 info",
|
|
2238
|
+
}
|
|
2239
|
+
|
|
2240
|
+
def side_effect(path, mode="rb"):
|
|
2241
|
+
mock_file = mock_open(read_data=file_contents[path])()
|
|
2242
|
+
return mock_file
|
|
2243
|
+
|
|
2244
|
+
mock_open_func.side_effect = side_effect
|
|
2245
|
+
mock_walk.return_value = [
|
|
2246
|
+
("/sys/bus/cpu/devices/cpu0/cpuidle", [], ["state1", "state2"]),
|
|
2247
|
+
]
|
|
2248
|
+
self.validator.capture_cstates()
|
|
2249
|
+
# The prefix logic is based on order, so check for both lines
|
|
2250
|
+
debug_call = self.mock_db.record_debug.call_args[0][0]
|
|
2251
|
+
self.assertIn("/sys/bus/cpu/devices/cpu0/cpuidle/state1: C1 info", debug_call)
|
|
2252
|
+
self.assertIn("/sys/bus/cpu/devices/cpu0/cpuidle/state2: C2 info", debug_call)
|
|
2253
|
+
self.assertTrue(debug_call.startswith("ACPI C-state information\n"))
|
|
2254
|
+
|
|
2255
|
+
@patch("amd_debug.prerequisites.os.walk")
|
|
2256
|
+
@patch("builtins.open", new_callable=mock_open, read_data=b"")
|
|
2257
|
+
def test_capture_cstates_empty_files(self, _mock_open, mock_walk):
|
|
2258
|
+
"""Test capture_cstates with empty cpuidle files"""
|
|
2259
|
+
mock_walk.return_value = [
|
|
2260
|
+
("/sys/bus/cpu/devices/cpu0/cpuidle", [], ["state1"]),
|
|
2261
|
+
]
|
|
2262
|
+
self.validator.capture_cstates()
|
|
2263
|
+
self.mock_db.record_debug.assert_called_with(
|
|
2264
|
+
"ACPI C-state information\n└─/sys/bus/cpu/devices/cpu0/cpuidle/state1: "
|
|
2265
|
+
)
|
|
2266
|
+
|
|
2267
|
+
@patch("amd_debug.prerequisites.os.walk")
|
|
2268
|
+
@patch("amd_debug.prerequisites.open", side_effect=PermissionError)
|
|
2269
|
+
def test_capture_cstates_permission_error(self, _mock_open, mock_walk):
|
|
2270
|
+
"""Test capture_cstates when reading cpuidle files raises PermissionError"""
|
|
2271
|
+
mock_walk.return_value = [
|
|
2272
|
+
("/sys/bus/cpu/devices/cpu0/cpuidle", [], ["state1"]),
|
|
2273
|
+
]
|
|
2274
|
+
with self.assertRaises(PermissionError):
|
|
2275
|
+
self.validator.capture_cstates()
|
|
2276
|
+
self.mock_db.record_debug.assert_not_called()
|
|
2277
|
+
|
|
2278
|
+
@patch("amd_debug.prerequisites.os.walk")
|
|
2279
|
+
def test_capture_cstates_no_files(self, mock_walk):
|
|
2280
|
+
"""Test capture_cstates when no cpuidle files are present"""
|
|
2281
|
+
mock_walk.return_value = [
|
|
2282
|
+
("/sys/bus/cpu/devices/cpu0/cpuidle", [], []),
|
|
2283
|
+
]
|
|
2284
|
+
self.validator.capture_cstates()
|
|
2285
|
+
self.mock_db.record_debug.assert_called_with("ACPI C-state information\n")
|
|
2286
|
+
|
|
2287
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
2288
|
+
def test_check_pinctrl_amd_driver_loaded_with_missing_file_error(
|
|
2289
|
+
self, mock_read_file
|
|
2290
|
+
):
|
|
2291
|
+
"""Test check_pinctrl_amd when the driver is loaded but debug file is missing"""
|
|
2292
|
+
mock_read_file.side_effect = FileNotFoundError
|
|
2293
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
2294
|
+
MagicMock(properties={"DRIVER": "amd_gpio"})
|
|
2295
|
+
]
|
|
2296
|
+
|
|
2297
|
+
result = self.validator.check_pinctrl_amd()
|
|
2298
|
+
self.assertTrue(result)
|
|
2299
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
2300
|
+
"GPIO debugfs not available", "👀"
|
|
2301
|
+
)
|
|
2302
|
+
|
|
2303
|
+
def test_check_amdgpu_no_devices(self):
|
|
2304
|
+
"""Test check_amdgpu when no PCI devices are found"""
|
|
2305
|
+
self.mock_pyudev.list_devices.return_value = []
|
|
2306
|
+
result = self.validator.check_amdgpu()
|
|
2307
|
+
self.assertFalse(result)
|
|
2308
|
+
self.mock_db.record_prereq.assert_called_with("Integrated GPU not found", "❌")
|
|
2309
|
+
self.assertTrue(any(isinstance(f, MissingGpu) for f in self.validator.failures))
|
|
2310
|
+
|
|
2311
|
+
def test_check_amdgpu_non_amd_devices(self):
|
|
2312
|
+
"""Test check_amdgpu when PCI devices are present but not AMD GPUs"""
|
|
2313
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
2314
|
+
MagicMock(
|
|
2315
|
+
properties={
|
|
2316
|
+
"PCI_CLASS": "30000",
|
|
2317
|
+
"PCI_ID": "8086abcd",
|
|
2318
|
+
"DRIVER": "i915",
|
|
2319
|
+
}
|
|
2320
|
+
),
|
|
2321
|
+
]
|
|
2322
|
+
result = self.validator.check_amdgpu()
|
|
2323
|
+
self.assertFalse(result)
|
|
2324
|
+
self.mock_db.record_prereq.assert_called_with("Integrated GPU not found", "❌")
|
|
2325
|
+
self.assertTrue(any(isinstance(f, MissingGpu) for f in self.validator.failures))
|
|
2326
|
+
|
|
2327
|
+
def test_check_amdgpu_driver_not_loaded(self):
|
|
2328
|
+
"""Test check_amdgpu when AMD GPU is present but driver is not loaded"""
|
|
2329
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
2330
|
+
MagicMock(
|
|
2331
|
+
properties={"PCI_CLASS": "20000", "PCI_ID": "1111abcd", "DRIVER": None}
|
|
2332
|
+
),
|
|
2333
|
+
MagicMock(
|
|
2334
|
+
properties={"PCI_CLASS": "30000", "PCI_ID": "1002abcd", "DRIVER": None}
|
|
2335
|
+
),
|
|
2336
|
+
]
|
|
2337
|
+
result = self.validator.check_amdgpu()
|
|
2338
|
+
self.assertFalse(result)
|
|
2339
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
2340
|
+
"GPU driver `amdgpu` not loaded", "❌"
|
|
2341
|
+
)
|
|
2342
|
+
self.assertTrue(
|
|
2343
|
+
any(isinstance(f, MissingAmdgpu) for f in self.validator.failures)
|
|
2344
|
+
)
|
|
2345
|
+
|
|
2346
|
+
def test_check_amdgpu_driver_loaded(self):
|
|
2347
|
+
"""Test check_amdgpu when AMD GPU is present and driver is loaded"""
|
|
2348
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
2349
|
+
MagicMock(
|
|
2350
|
+
properties={
|
|
2351
|
+
"PCI_CLASS": "30000",
|
|
2352
|
+
"PCI_ID": "1002abcd",
|
|
2353
|
+
"DRIVER": "amdgpu",
|
|
2354
|
+
"PCI_SLOT_NAME": "0000:01:00.0",
|
|
2355
|
+
}
|
|
2356
|
+
),
|
|
2357
|
+
]
|
|
2358
|
+
result = self.validator.check_amdgpu()
|
|
2359
|
+
self.assertTrue(result)
|
|
2360
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
2361
|
+
"GPU driver `amdgpu` bound to 0000:01:00.0", "✅"
|
|
2362
|
+
)
|
|
2363
|
+
|
|
2364
|
+
def test_check_amdgpu_multiple_devices_mixed(self):
|
|
2365
|
+
"""Test check_amdgpu with multiple devices, one with driver loaded, one without"""
|
|
2366
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
2367
|
+
MagicMock(
|
|
2368
|
+
properties={
|
|
2369
|
+
"PCI_CLASS": "30000",
|
|
2370
|
+
"PCI_ID": "1002abcd",
|
|
2371
|
+
"DRIVER": "amdgpu",
|
|
2372
|
+
"PCI_SLOT_NAME": "0000:01:00.0",
|
|
2373
|
+
}
|
|
2374
|
+
),
|
|
2375
|
+
MagicMock(
|
|
2376
|
+
properties={"PCI_CLASS": "30000", "PCI_ID": "1002abcd", "DRIVER": None}
|
|
2377
|
+
),
|
|
2378
|
+
]
|
|
2379
|
+
result = self.validator.check_amdgpu()
|
|
2380
|
+
self.assertFalse(result)
|
|
2381
|
+
self.mock_db.record_prereq.assert_any_call(
|
|
2382
|
+
"GPU driver `amdgpu` bound to 0000:01:00.0", "✅"
|
|
2383
|
+
)
|
|
2384
|
+
self.mock_db.record_prereq.assert_any_call(
|
|
2385
|
+
"GPU driver `amdgpu` not loaded", "❌"
|
|
2386
|
+
)
|
|
2387
|
+
self.assertTrue(
|
|
2388
|
+
any(isinstance(f, MissingAmdgpu) for f in self.validator.failures)
|
|
2389
|
+
)
|
|
2390
|
+
|
|
2391
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
2392
|
+
@patch("amd_debug.prerequisites.os.readlink")
|
|
2393
|
+
def test_check_isp4_no_devices(self, _mock_readlink, _mock_path_exists):
|
|
2394
|
+
"""Test check_isp4 when no ISP4 camera devices are found"""
|
|
2395
|
+
self.mock_pyudev.list_devices.return_value = []
|
|
2396
|
+
result = self.validator.check_isp4()
|
|
2397
|
+
self.assertTrue(result)
|
|
2398
|
+
self.mock_db.record_prereq.assert_not_called()
|
|
2399
|
+
|
|
2400
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
2401
|
+
@patch("amd_debug.prerequisites.os.readlink")
|
|
2402
|
+
def test_check_isp4_device_without_path(self, _mock_readlink, mock_path_exists):
|
|
2403
|
+
"""Test check_isp4 when ACPI device exists but path file is missing"""
|
|
2404
|
+
mock_path_exists.return_value = False
|
|
2405
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
2406
|
+
MagicMock(sys_path="/sys/devices/acpi/OMNI5C10:00", sys_name="OMNI5C10:00")
|
|
2407
|
+
]
|
|
2408
|
+
result = self.validator.check_isp4()
|
|
2409
|
+
self.assertTrue(result)
|
|
2410
|
+
self.mock_db.record_prereq.assert_not_called()
|
|
2411
|
+
|
|
2412
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
2413
|
+
@patch("amd_debug.prerequisites.os.readlink")
|
|
2414
|
+
def test_check_isp4_driver_not_bound(self, _mock_readlink, mock_path_exists):
|
|
2415
|
+
"""Test check_isp4 when ISP4 driver is not bound to the device"""
|
|
2416
|
+
mock_path_exists.side_effect = lambda p: "path" in p
|
|
2417
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
2418
|
+
MagicMock(sys_path="/sys/devices/acpi/OMNI5C10:00", sys_name="OMNI5C10:00")
|
|
2419
|
+
]
|
|
2420
|
+
result = self.validator.check_isp4()
|
|
2421
|
+
self.assertFalse(result)
|
|
2422
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
2423
|
+
"ISP4 platform camera driver 'amd-isp4' not bound to OMNI5C10:00", "❌"
|
|
2424
|
+
)
|
|
2425
|
+
self.assertTrue(
|
|
2426
|
+
any(
|
|
2427
|
+
isinstance(f, MissingIsp4PlatformDriver)
|
|
2428
|
+
for f in self.validator.failures
|
|
2429
|
+
)
|
|
2430
|
+
)
|
|
2431
|
+
|
|
2432
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
2433
|
+
@patch(
|
|
2434
|
+
"amd_debug.prerequisites.os.readlink",
|
|
2435
|
+
return_value="/sys/devices/platform/drivers/amd-isp4",
|
|
2436
|
+
)
|
|
2437
|
+
def test_check_isp4_driver_bound_but_module_not_loaded(
|
|
2438
|
+
self, _mock_readlink, mock_path_exists
|
|
2439
|
+
):
|
|
2440
|
+
"""Test check_isp4 when driver is bound but amd_capture module is not loaded"""
|
|
2441
|
+
|
|
2442
|
+
def exists_side_effect(path):
|
|
2443
|
+
if "path" in path or "driver" in path:
|
|
2444
|
+
return True
|
|
2445
|
+
if "amd_capture" in path:
|
|
2446
|
+
return False
|
|
2447
|
+
return True
|
|
2448
|
+
|
|
2449
|
+
mock_path_exists.side_effect = exists_side_effect
|
|
2450
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
2451
|
+
MagicMock(sys_path="/sys/devices/acpi/OMNI5C10:00", sys_name="OMNI5C10:00")
|
|
2452
|
+
]
|
|
2453
|
+
result = self.validator.check_isp4()
|
|
2454
|
+
self.assertFalse(result)
|
|
2455
|
+
self.mock_db.record_prereq.assert_any_call(
|
|
2456
|
+
"ISP4 platform camera driver 'amd-isp4' bound to OMNI5C10:00", "✅"
|
|
2457
|
+
)
|
|
2458
|
+
self.mock_db.record_prereq.assert_any_call(
|
|
2459
|
+
"Camera driver module 'amd_capture' not loaded", "❌"
|
|
2460
|
+
)
|
|
2461
|
+
self.assertTrue(
|
|
2462
|
+
any(isinstance(f, MissingAmdCaptureModule) for f in self.validator.failures)
|
|
2463
|
+
)
|
|
2464
|
+
|
|
2465
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
2466
|
+
@patch(
|
|
2467
|
+
"amd_debug.prerequisites.os.readlink",
|
|
2468
|
+
return_value="/sys/devices/platform/drivers/amd-isp4",
|
|
2469
|
+
)
|
|
2470
|
+
def test_check_isp4_fully_configured(self, _mock_readlink, mock_path_exists):
|
|
2471
|
+
"""Test check_isp4 when ISP4 is fully configured with driver and module"""
|
|
2472
|
+
mock_path_exists.return_value = True
|
|
2473
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
2474
|
+
MagicMock(sys_path="/sys/devices/acpi/OMNI5C10:00", sys_name="OMNI5C10:00")
|
|
2475
|
+
]
|
|
2476
|
+
result = self.validator.check_isp4()
|
|
2477
|
+
self.assertTrue(result)
|
|
2478
|
+
self.mock_db.record_prereq.assert_any_call(
|
|
2479
|
+
"ISP4 platform camera driver 'amd-isp4' bound to OMNI5C10:00", "✅"
|
|
2480
|
+
)
|
|
2481
|
+
self.mock_db.record_prereq.assert_any_call(
|
|
2482
|
+
"Camera driver module 'amd_capture' loaded", "✅"
|
|
2483
|
+
)
|
|
2484
|
+
|
|
2485
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
2486
|
+
@patch(
|
|
2487
|
+
"amd_debug.prerequisites.os.readlink",
|
|
2488
|
+
return_value="/sys/devices/platform/drivers/other-driver",
|
|
2489
|
+
)
|
|
2490
|
+
def test_check_isp4_wrong_driver(self, _mock_readlink, mock_path_exists):
|
|
2491
|
+
"""Test check_isp4 when wrong driver is bound to the device"""
|
|
2492
|
+
mock_path_exists.side_effect = lambda p: "path" in p or "driver" in p
|
|
2493
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
2494
|
+
MagicMock(sys_path="/sys/devices/acpi/OMNI5C10:00", sys_name="OMNI5C10:00")
|
|
2495
|
+
]
|
|
2496
|
+
result = self.validator.check_isp4()
|
|
2497
|
+
self.assertFalse(result)
|
|
2498
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
2499
|
+
"ISP4 platform camera driver 'amd-isp4' not bound to OMNI5C10:00", "❌"
|
|
2500
|
+
)
|
|
2501
|
+
self.assertTrue(
|
|
2502
|
+
any(
|
|
2503
|
+
isinstance(f, MissingIsp4PlatformDriver)
|
|
2504
|
+
for f in self.validator.failures
|
|
2505
|
+
)
|
|
2506
|
+
)
|
|
2507
|
+
|
|
2508
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
2509
|
+
@patch(
|
|
2510
|
+
"amd_debug.prerequisites.os.readlink",
|
|
2511
|
+
return_value="/sys/devices/platform/drivers/amd-isp4",
|
|
2512
|
+
)
|
|
2513
|
+
def test_check_isp4_multiple_devices(self, _mock_readlink, mock_path_exists):
|
|
2514
|
+
"""Test check_isp4 with multiple ISP4 camera devices"""
|
|
2515
|
+
mock_path_exists.return_value = True
|
|
2516
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
2517
|
+
MagicMock(sys_path="/sys/devices/acpi/OMNI5C10:00", sys_name="OMNI5C10:00"),
|
|
2518
|
+
MagicMock(sys_path="/sys/devices/acpi/OMNI5C10:01", sys_name="OMNI5C10:01"),
|
|
2519
|
+
]
|
|
2520
|
+
result = self.validator.check_isp4()
|
|
2521
|
+
self.assertTrue(result)
|
|
2522
|
+
self.mock_db.record_prereq.assert_any_call(
|
|
2523
|
+
"ISP4 platform camera driver 'amd-isp4' bound to OMNI5C10:00", "✅"
|
|
2524
|
+
)
|
|
2525
|
+
self.mock_db.record_prereq.assert_any_call(
|
|
2526
|
+
"ISP4 platform camera driver 'amd-isp4' bound to OMNI5C10:01", "✅"
|
|
2527
|
+
)
|
|
2528
|
+
|
|
2529
|
+
@patch("amd_debug.prerequisites.os.path.exists")
|
|
2530
|
+
def test_check_isp4_device_not_starting_with_omni5c10(self, mock_path_exists):
|
|
2531
|
+
"""Test check_isp4 when ACPI devices don't match OMNI5C10 pattern"""
|
|
2532
|
+
mock_path_exists.return_value = True
|
|
2533
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
2534
|
+
MagicMock(sys_path="/sys/devices/acpi/OTHER:00", sys_name="OTHER:00")
|
|
2535
|
+
]
|
|
2536
|
+
result = self.validator.check_isp4()
|
|
2537
|
+
self.assertTrue(result)
|
|
2538
|
+
self.mock_db.record_prereq.assert_not_called()
|