amd-debug-tools 0.2.7__py3-none-any.whl → 0.2.9__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/__init__.py +0 -1
- amd_debug/acpi.py +0 -1
- amd_debug/battery.py +0 -1
- amd_debug/bios.py +0 -1
- amd_debug/common.py +41 -13
- amd_debug/database.py +22 -5
- amd_debug/display.py +2 -3
- amd_debug/failures.py +14 -2
- amd_debug/installer.py +1 -2
- amd_debug/kernel.py +10 -7
- amd_debug/prerequisites.py +36 -10
- amd_debug/pstate.py +0 -1
- amd_debug/s2idle.py +5 -4
- amd_debug/sleep_report.py +1 -2
- amd_debug/ttm.py +1 -1
- amd_debug/validator.py +5 -8
- amd_debug/wake.py +0 -1
- amd_debug_tools-0.2.9.dist-info/METADATA +75 -0
- amd_debug_tools-0.2.9.dist-info/RECORD +47 -0
- test_common.py +76 -51
- test_database.py +1 -1
- test_display.py +6 -6
- test_installer.py +1 -1
- test_prerequisites.py +177 -0
- test_validator.py +6 -6
- amd_debug_tools-0.2.7.dist-info/METADATA +0 -184
- amd_debug_tools-0.2.7.dist-info/RECORD +0 -47
- {amd_debug_tools-0.2.7.dist-info → amd_debug_tools-0.2.9.dist-info}/WHEEL +0 -0
- {amd_debug_tools-0.2.7.dist-info → amd_debug_tools-0.2.9.dist-info}/entry_points.txt +0 -0
- {amd_debug_tools-0.2.7.dist-info → amd_debug_tools-0.2.9.dist-info}/licenses/LICENSE +0 -0
- {amd_debug_tools-0.2.7.dist-info → amd_debug_tools-0.2.9.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: amd-debug-tools
|
|
3
|
+
Version: 0.2.9
|
|
4
|
+
Summary: debug tools for AMD systems
|
|
5
|
+
Author-email: Mario Limonciello <superm1@kernel.org>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://web.git.kernel.org/pub/scm/linux/kernel/git/superm1/amd-debug-tools.git/
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
10
|
+
Requires-Python: >=3.7
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Requires-Dist: dbus-fast
|
|
14
|
+
Requires-Dist: pyudev
|
|
15
|
+
Requires-Dist: packaging
|
|
16
|
+
Requires-Dist: pandas
|
|
17
|
+
Requires-Dist: jinja2
|
|
18
|
+
Requires-Dist: tabulate
|
|
19
|
+
Requires-Dist: seaborn
|
|
20
|
+
Requires-Dist: cysystemd
|
|
21
|
+
Requires-Dist: Jinja2
|
|
22
|
+
Requires-Dist: matplotlib
|
|
23
|
+
Requires-Dist: seaborn
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
# Helpful tools for debugging AMD Zen systems
|
|
27
|
+
[](https://codecov.io/github/superm1/amd-debug-tools)
|
|
28
|
+
[](https://pypi.org/project/amd-debug-tools/)
|
|
29
|
+
|
|
30
|
+
This repository hosts open tools that are useful for debugging issues on AMD systems.
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
### Distro (Arch)
|
|
34
|
+
`amd-debug-tools` has been [packaged for Arch Linux](https://archlinux.org/packages/extra/any/amd-debug-tools/) (and derivatives). You can install it using:
|
|
35
|
+
|
|
36
|
+
pacman -Sy amd-debug-tools
|
|
37
|
+
|
|
38
|
+
### Using a python wheel (Generic)
|
|
39
|
+
It is suggested to install tools in a virtual environment either using
|
|
40
|
+
`pipx` or `python3 -m venv`.
|
|
41
|
+
|
|
42
|
+
#### From PyPI
|
|
43
|
+
`amd-debug-tools` is distributed as a python wheel, which is a
|
|
44
|
+
binary package format for Python. To install from PyPI, run the following
|
|
45
|
+
command:
|
|
46
|
+
|
|
47
|
+
pipx install amd-debug-tools
|
|
48
|
+
|
|
49
|
+
### From source
|
|
50
|
+
To build the package from source, you will need to the `python3-build`
|
|
51
|
+
package natively installed by your distribution package manager. Then you
|
|
52
|
+
can generate and install a wheel by running the following commands:
|
|
53
|
+
|
|
54
|
+
python3 -m build
|
|
55
|
+
pipx install dist/amd-debug-tools-*.whl
|
|
56
|
+
|
|
57
|
+
### Ensuring path
|
|
58
|
+
If you have not used a `pipx` environment before, you may need to run the following command
|
|
59
|
+
to set up the environment:
|
|
60
|
+
|
|
61
|
+
pipx ensurepath
|
|
62
|
+
|
|
63
|
+
This will add the `pipx` environment to your path.
|
|
64
|
+
|
|
65
|
+
## Running in-tree
|
|
66
|
+
Documentation about running directly from a git checkout is available [here](https://github.com/superm1/amd-debug-tools/blob/master/docs/in-tree.md).
|
|
67
|
+
|
|
68
|
+
## Tools
|
|
69
|
+
|
|
70
|
+
Each tool has its own individual documentation page:
|
|
71
|
+
* [amd-s2idle](https://github.com/superm1/amd-debug-tools/blob/master/docs/amd-s2idle.md)
|
|
72
|
+
* [amd-bios](https://github.com/superm1/amd-debug-tools/blob/master/docs/amd-bios.md)
|
|
73
|
+
* [amd-pstate](https://github.com/superm1/amd-debug-tools/blob/master/docs/amd-pstate.md)
|
|
74
|
+
* [amd-ttm](https://github.com/superm1/amd-debug-tools/blob/master/docs/amd-ttm.md)
|
|
75
|
+
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
launcher.py,sha256=M8kT9DtyZoQgZaKWDbSBu4jsS6tZF1gWko3sovNVyag,947
|
|
2
|
+
test_acpi.py,sha256=wtS43Rz95h7YEEJBeFa6Mswaeo4syBZrw4hY8i0YbJY,3117
|
|
3
|
+
test_batteries.py,sha256=nN5pfP5El7Whypq3HHEpW8bufdf5EWSTVGbayfNQYP4,3360
|
|
4
|
+
test_bios.py,sha256=x_KLmQqGEbQhTugyWCHGXjGp2H1dCdhRz0kgw2Big8w,9276
|
|
5
|
+
test_common.py,sha256=qOCouXyO-dhY_x_L8kyNyuP_c0bhHhlqPoc084_F6Xg,20757
|
|
6
|
+
test_database.py,sha256=HAC4M4dyZBxskNFMfZn2kro6uT2-j0exjnto8-0OOnk,10042
|
|
7
|
+
test_display.py,sha256=awC1-OEPG1aV34BH-MzrOWtAHHPfhCmnKRetas4AGNE,5813
|
|
8
|
+
test_failures.py,sha256=H1UxXeVjhJs9-j9yas4vwAha676GX1Es7Kz8RN2B590,6845
|
|
9
|
+
test_installer.py,sha256=tZpGUOY4ACgXU-cs1Z5RHSPY5INa3HhLmmuB2H4QrOc,10222
|
|
10
|
+
test_kernel.py,sha256=2EXrLht5ZWdT4N5pb_F3zqZl9NEghjnDpcMGCMw3obI,7917
|
|
11
|
+
test_launcher.py,sha256=8g8CBTvLX64Us4RmHtRPSdpV5E2kQFaudBl7VIsxLhE,1733
|
|
12
|
+
test_prerequisites.py,sha256=SJk9y5zXdBmsNbtPMbBOHzkFsnuQF0j3BS6tdZ3ccTo,95944
|
|
13
|
+
test_pstate.py,sha256=a9oAJ9-LANX32XNQhplz6Y75VNYc__QqoSBKIrwvANg,6058
|
|
14
|
+
test_s2idle.py,sha256=FxsyujgX9Px3m56VzHNeA8yMTHmJiRLWxYt-fh1m5gw,33585
|
|
15
|
+
test_sleep_report.py,sha256=ANuxYi_C1oSKAi4xUU2wBu4SwJtcZA7VPpazBe3_WUQ,6922
|
|
16
|
+
test_ttm.py,sha256=QrCdRodQ_CD3nyDqKodb6yR158mgE0iIM5f1fV1Axu8,10515
|
|
17
|
+
test_validator.py,sha256=bfFFB2V3hrZtxTruwrvcUyZjoh-dcDa2TPt-l6pAfTA,36476
|
|
18
|
+
test_wake.py,sha256=6zi5GVFHQKU1sTWw3O5-aGriB9uu5713QLn4l2wjhpM,7152
|
|
19
|
+
amd_debug/__init__.py,sha256=0VsbaXBas5gcwZ6vBbJg20-8qHNDo_BKFYzdb1Agfq4,1252
|
|
20
|
+
amd_debug/acpi.py,sha256=_lnnAwTnAb4g8AW2BXwF45UY-WUmXIDpthhFLE5ENGo,3142
|
|
21
|
+
amd_debug/battery.py,sha256=qd6bo1ssAPnEfPY2HCmJfFcwgYADrfoJkxFQWQyxKlU,3103
|
|
22
|
+
amd_debug/bios.py,sha256=3dfE5yVoW0aZUN5osGKOtYF-RLOzUcH3ryA4yHZZoJA,4022
|
|
23
|
+
amd_debug/common.py,sha256=OR0rCFkWWoGlRIdsPdJ0hBr9iQ_vXcjIAL3poZ9BTj8,12606
|
|
24
|
+
amd_debug/database.py,sha256=kkl1iKIvekpVMc8M0xzo0iX3rc4yDaREyjCuLSnGuCk,11052
|
|
25
|
+
amd_debug/display.py,sha256=XATsKPh2QDoJAbm5mAE1YNFdVuFEq3yS3dffEasEq8c,922
|
|
26
|
+
amd_debug/failures.py,sha256=Sa2GEwhn8PImcIbZkHEPA2oCbuayTx9JheLaXBscz4I,23280
|
|
27
|
+
amd_debug/installer.py,sha256=JNlqGWNgF5pYxzy2VJiphmP4gXg99F0f4TBy2xk1Tpg,14275
|
|
28
|
+
amd_debug/kernel.py,sha256=w2y4syIMPd4OYXhPYrEmCDYnMBg9K16tK6iNwfzRVWU,11718
|
|
29
|
+
amd_debug/prerequisites.py,sha256=f55y-DUCMQHPWSQRjJcP-J8qvGaNKbXUIBbyhOzUB1U,52632
|
|
30
|
+
amd_debug/pstate.py,sha256=fGA-pKS1mzIrOa9fIqd_q3Y9DvMhuWmInq5GPAGc21k,9564
|
|
31
|
+
amd_debug/s2idle-hook,sha256=LLiaqPtGd0qetu9n6EYxKHZaIdHpVQDONdOuSc0pfFg,1695
|
|
32
|
+
amd_debug/s2idle.py,sha256=tj1_5g2adTScWfgaFAlH4NVjAse2jSCgOhpaOeWBYzk,13381
|
|
33
|
+
amd_debug/sleep_report.py,sha256=JQDtd_y-78gXQN5o7O7h-hKosEOWZeTmnZBSepdeCEE,17298
|
|
34
|
+
amd_debug/ttm.py,sha256=KoCY35jnU7Q2ZB0SO1nQmC_RsiCFwTWUzVmRbyUg0Lw,4660
|
|
35
|
+
amd_debug/validator.py,sha256=OEpaIAnkZg_GF_MeU48YNj8Wcj45vEgT7edqgflsChM,34151
|
|
36
|
+
amd_debug/wake.py,sha256=x33z60sOaukTlZTABKpvLJZVRk-kIe_o8XljtV3Q38Q,3917
|
|
37
|
+
amd_debug/bash/amd-s2idle,sha256=g_cle1ElCJpwE4wcLezL6y-BdasDKTnNMhrtzKLE9ks,1142
|
|
38
|
+
amd_debug/templates/html,sha256=JfGhpmHIB2C2GItdGI1kuC8uayqEVgrpQvAWAj35eZ4,14580
|
|
39
|
+
amd_debug/templates/md,sha256=r8X2aehnH2gzj0WHYTZ5K9wAqC5y39i_3nkDORSC0uM,787
|
|
40
|
+
amd_debug/templates/stdout,sha256=hyoOJ96K2dJfnWRWhyCuariLKbEHXvs9mstV_g5aMdI,469
|
|
41
|
+
amd_debug/templates/txt,sha256=nNdsvbPFOhGdL7VA-_4k5aN3nB-6ouGQt6AsWst7T3w,649
|
|
42
|
+
amd_debug_tools-0.2.9.dist-info/licenses/LICENSE,sha256=RBlZI6r3MRGzymI2VDX2iW__D2APDbMhu_Xg5t6BWeo,1066
|
|
43
|
+
amd_debug_tools-0.2.9.dist-info/METADATA,sha256=4rIp2I7Oipo1o3hCRtw4CoCf3IliJjyJcUV6SnYhkrE,2772
|
|
44
|
+
amd_debug_tools-0.2.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
45
|
+
amd_debug_tools-0.2.9.dist-info/entry_points.txt,sha256=hIskDz6k0_6q1qpqWCpVFsca_djxAqkLrUAwzAyEGuE,144
|
|
46
|
+
amd_debug_tools-0.2.9.dist-info/top_level.txt,sha256=VvGkkY5I7O3HoLNrc2VfgjCA-to3PUjnnKd7juONaFw,243
|
|
47
|
+
amd_debug_tools-0.2.9.dist-info/RECORD,,
|
test_common.py
CHANGED
|
@@ -4,13 +4,16 @@
|
|
|
4
4
|
"""
|
|
5
5
|
This module contains unit tests for the common functions in the amd-debug-tools package.
|
|
6
6
|
"""
|
|
7
|
-
from unittest.mock import patch, mock_open, call
|
|
7
|
+
from unittest.mock import patch, mock_open, call, Mock
|
|
8
8
|
|
|
9
|
+
import asyncio
|
|
10
|
+
import builtins
|
|
9
11
|
import logging
|
|
10
12
|
import tempfile
|
|
11
13
|
import unittest
|
|
12
14
|
import os
|
|
13
15
|
from platform import uname_result
|
|
16
|
+
import sys
|
|
14
17
|
|
|
15
18
|
|
|
16
19
|
from amd_debug.common import (
|
|
@@ -49,6 +52,7 @@ color_dict = {
|
|
|
49
52
|
"○": Colors.OK,
|
|
50
53
|
"💤": Colors.OK,
|
|
51
54
|
"💯": Colors.UNDERLINE,
|
|
55
|
+
"🚫": Colors.UNDERLINE,
|
|
52
56
|
"🗣️": Colors.HEADER,
|
|
53
57
|
}
|
|
54
58
|
|
|
@@ -473,63 +477,84 @@ class TestCommon(unittest.TestCase):
|
|
|
473
477
|
self.assertAlmostEqual(get_system_mem(), expected_gb)
|
|
474
478
|
mock_file.assert_called_once_with("/proc/meminfo", "r", encoding="utf-8")
|
|
475
479
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
def test_get_system_mem_missing(self, _mock_join, _mock_file):
|
|
479
|
-
"""Test get_system_mem raises ValueError if MemTotal is missing"""
|
|
480
|
-
with self.assertRaises(ValueError):
|
|
481
|
-
get_system_mem()
|
|
482
|
-
|
|
483
|
-
@patch("amd_debug.common.fatal_error")
|
|
484
|
-
def test_reboot_importerror(self, mock_fatal_error):
|
|
485
|
-
"""Test reboot handles ImportError"""
|
|
486
|
-
with patch.dict("sys.modules", {"dbus": None}):
|
|
487
|
-
reboot()
|
|
488
|
-
mock_fatal_error.assert_called_once_with("Missing dbus")
|
|
489
|
-
|
|
490
|
-
@patch("amd_debug.common.fatal_error")
|
|
491
|
-
def test_reboot_dbus_exception(self, mock_fatal_error):
|
|
492
|
-
"""Test reboot handles dbus.exceptions.DBusException"""
|
|
493
|
-
|
|
494
|
-
class DummyDBusException(Exception):
|
|
495
|
-
"""Dummy exception"""
|
|
496
|
-
|
|
497
|
-
class DummyIntf:
|
|
498
|
-
"""Dummy interface"""
|
|
480
|
+
def test_reboot_dbus_fast_success(self):
|
|
481
|
+
"""Test reboot returns True when reboot_dbus_fast succeeds"""
|
|
499
482
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
def __init__(self):
|
|
483
|
+
# Create a mock loop that properly handles coroutines
|
|
484
|
+
def mock_run_until_complete(coro):
|
|
485
|
+
# Consume the coroutine to prevent the warning
|
|
486
|
+
try:
|
|
487
|
+
# Close the coroutine to prevent the warning
|
|
488
|
+
coro.close()
|
|
489
|
+
except (AttributeError, RuntimeError):
|
|
508
490
|
pass
|
|
491
|
+
return True
|
|
509
492
|
|
|
510
|
-
|
|
511
|
-
|
|
493
|
+
mock_loop = Mock()
|
|
494
|
+
mock_loop.run_until_complete.side_effect = mock_run_until_complete
|
|
512
495
|
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
496
|
+
with patch("amd_debug.common.asyncio.get_event_loop", return_value=mock_loop):
|
|
497
|
+
result = reboot()
|
|
498
|
+
self.assertTrue(result)
|
|
499
|
+
mock_loop.run_until_complete.assert_called_once()
|
|
516
500
|
|
|
517
|
-
|
|
518
|
-
|
|
501
|
+
@patch("asyncio.get_event_loop")
|
|
502
|
+
def test_reboot_dbus_fast_failure_and_dbus_success(self, mock_get_event_loop):
|
|
503
|
+
"""Test reboot falls back to reboot_dbus when reboot_dbus_fast fails"""
|
|
519
504
|
|
|
520
|
-
|
|
521
|
-
|
|
505
|
+
# Create a mock loop that properly handles coroutines
|
|
506
|
+
def mock_run_until_complete(coro):
|
|
507
|
+
# Consume the coroutine to prevent the warning
|
|
508
|
+
try:
|
|
509
|
+
coro.close()
|
|
510
|
+
except (AttributeError, RuntimeError):
|
|
511
|
+
pass
|
|
512
|
+
return False
|
|
513
|
+
|
|
514
|
+
mock_loop = Mock()
|
|
515
|
+
mock_loop.run_until_complete.side_effect = mock_run_until_complete
|
|
516
|
+
mock_get_event_loop.return_value = mock_loop
|
|
517
|
+
|
|
518
|
+
# Mock the dbus module to avoid ImportError in CI
|
|
519
|
+
mock_dbus = Mock()
|
|
520
|
+
mock_bus = Mock()
|
|
521
|
+
mock_obj = Mock()
|
|
522
|
+
mock_intf = Mock()
|
|
523
|
+
|
|
524
|
+
mock_dbus.SystemBus.return_value = mock_bus
|
|
525
|
+
mock_bus.get_object.return_value = mock_obj
|
|
526
|
+
mock_obj.get_interface.return_value = mock_intf
|
|
527
|
+
mock_dbus.Interface = Mock(return_value=mock_intf)
|
|
528
|
+
|
|
529
|
+
with patch.dict("sys.modules", {"dbus": mock_dbus}):
|
|
530
|
+
result = reboot()
|
|
531
|
+
self.assertTrue(result)
|
|
532
|
+
|
|
533
|
+
@patch("asyncio.get_event_loop")
|
|
534
|
+
def test_reboot_dbus_fast_failure_and_dbus_failure(self, mock_get_event_loop):
|
|
535
|
+
"""Test reboot returns False when both reboot_dbus_fast and reboot_dbus fail"""
|
|
536
|
+
|
|
537
|
+
# Create a mock loop that properly handles coroutines
|
|
538
|
+
def mock_run_until_complete(coro):
|
|
539
|
+
# Consume the coroutine to prevent the warning
|
|
540
|
+
try:
|
|
541
|
+
coro.close()
|
|
542
|
+
except (AttributeError, RuntimeError):
|
|
543
|
+
pass
|
|
544
|
+
return False
|
|
522
545
|
|
|
523
|
-
|
|
546
|
+
mock_loop = Mock()
|
|
547
|
+
mock_loop.run_until_complete.side_effect = mock_run_until_complete
|
|
548
|
+
mock_get_event_loop.return_value = mock_loop
|
|
524
549
|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
return DummyBus()
|
|
550
|
+
# Mock the import to raise ImportError when dbus is imported
|
|
551
|
+
original_import = builtins.__import__
|
|
528
552
|
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
553
|
+
def mock_import(name, *args, **kwargs):
|
|
554
|
+
if name == "dbus":
|
|
555
|
+
raise ImportError("No module named 'dbus'")
|
|
556
|
+
return original_import(name, *args, **kwargs)
|
|
532
557
|
|
|
533
|
-
with patch
|
|
534
|
-
reboot()
|
|
535
|
-
self.
|
|
558
|
+
with patch("builtins.__import__", side_effect=mock_import):
|
|
559
|
+
result = reboot()
|
|
560
|
+
self.assertFalse(result)
|
test_database.py
CHANGED
|
@@ -255,7 +255,7 @@ class TestSleepDatabase(unittest.TestCase):
|
|
|
255
255
|
def test_get_last_prereq_ts_no_data(self):
|
|
256
256
|
"""Test getting the last prereq timestamp when no data exists"""
|
|
257
257
|
result = self.db.get_last_prereq_ts()
|
|
258
|
-
self.
|
|
258
|
+
self.assertEqual(result, 0)
|
|
259
259
|
|
|
260
260
|
def test_report_cycle_data(self):
|
|
261
261
|
"""Test reporting cycle data"""
|
test_display.py
CHANGED
|
@@ -40,7 +40,7 @@ class TestDisplay(unittest.TestCase):
|
|
|
40
40
|
display = Display()
|
|
41
41
|
|
|
42
42
|
# Verify the EDID paths are correctly set
|
|
43
|
-
expected_edid =
|
|
43
|
+
expected_edid = ["/sys/devices/card0/edid"]
|
|
44
44
|
self.assertEqual(display.get_edid(), expected_edid)
|
|
45
45
|
mock_context.assert_called_once()
|
|
46
46
|
|
|
@@ -54,7 +54,7 @@ class TestDisplay(unittest.TestCase):
|
|
|
54
54
|
display = Display()
|
|
55
55
|
|
|
56
56
|
# Verify the EDID dictionary is empty
|
|
57
|
-
self.assertEqual(display.get_edid(),
|
|
57
|
+
self.assertEqual(display.get_edid(), [])
|
|
58
58
|
|
|
59
59
|
@patch("amd_debug.display.Context")
|
|
60
60
|
def test_device_without_card(self, mock_context):
|
|
@@ -70,7 +70,7 @@ class TestDisplay(unittest.TestCase):
|
|
|
70
70
|
display = Display()
|
|
71
71
|
|
|
72
72
|
# Verify the EDID dictionary is empty
|
|
73
|
-
self.assertEqual(display.get_edid(),
|
|
73
|
+
self.assertEqual(display.get_edid(), [])
|
|
74
74
|
|
|
75
75
|
@patch("amd_debug.display.Context")
|
|
76
76
|
@patch("amd_debug.display.read_file")
|
|
@@ -94,7 +94,7 @@ class TestDisplay(unittest.TestCase):
|
|
|
94
94
|
display = Display()
|
|
95
95
|
|
|
96
96
|
# Verify the EDID dictionary is empty
|
|
97
|
-
self.assertEqual(display.get_edid(),
|
|
97
|
+
self.assertEqual(display.get_edid(), [])
|
|
98
98
|
|
|
99
99
|
@patch("amd_debug.display.Context")
|
|
100
100
|
@patch("amd_debug.display.read_file")
|
|
@@ -116,7 +116,7 @@ class TestDisplay(unittest.TestCase):
|
|
|
116
116
|
display = Display()
|
|
117
117
|
|
|
118
118
|
# Verify the EDID dictionary is empty
|
|
119
|
-
self.assertEqual(display.get_edid(),
|
|
119
|
+
self.assertEqual(display.get_edid(), [])
|
|
120
120
|
|
|
121
121
|
@patch("amd_debug.display.Context")
|
|
122
122
|
@patch("amd_debug.display.read_file")
|
|
@@ -140,4 +140,4 @@ class TestDisplay(unittest.TestCase):
|
|
|
140
140
|
display = Display()
|
|
141
141
|
|
|
142
142
|
# Verify the EDID dictionary is empty
|
|
143
|
-
self.assertEqual(display.get_edid(),
|
|
143
|
+
self.assertEqual(display.get_edid(), [])
|
test_installer.py
CHANGED
|
@@ -231,7 +231,7 @@ class TestInstaller(unittest.TestCase):
|
|
|
231
231
|
self.installer.set_requirements("edid-decode")
|
|
232
232
|
ret = self.installer.install_dependencies()
|
|
233
233
|
_mock_check_call.assert_called_once_with(
|
|
234
|
-
["dnf", "install", "-y", "libdisplay-info"]
|
|
234
|
+
["dnf", "install", "-y", "libdisplay-info-tools"]
|
|
235
235
|
)
|
|
236
236
|
self.assertTrue(ret)
|
|
237
237
|
|
test_prerequisites.py
CHANGED
|
@@ -2035,3 +2035,180 @@ class TestPrerequisiteValidator(unittest.TestCase):
|
|
|
2035
2035
|
self.mock_db.record_prereq.assert_called_with(
|
|
2036
2036
|
"NVIDIA GPU {f} not readable", "👀"
|
|
2037
2037
|
)
|
|
2038
|
+
|
|
2039
|
+
@patch("amd_debug.prerequisites.os.walk")
|
|
2040
|
+
@patch(
|
|
2041
|
+
"builtins.open",
|
|
2042
|
+
new_callable=unittest.mock.mock_open,
|
|
2043
|
+
read_data=b"C1 state info",
|
|
2044
|
+
)
|
|
2045
|
+
def test_capture_cstates_single_file(self, mock_open, mock_walk):
|
|
2046
|
+
"""Test capture_cstates with a single cpuidle file"""
|
|
2047
|
+
mock_walk.return_value = [
|
|
2048
|
+
("/sys/bus/cpu/devices/cpu0/cpuidle", [], ["state1"]),
|
|
2049
|
+
]
|
|
2050
|
+
self.validator.capture_cstates()
|
|
2051
|
+
self.mock_db.record_debug.assert_called_with(
|
|
2052
|
+
"ACPI C-state information\n└─/sys/bus/cpu/devices/cpu0/cpuidle/state1: C1 state info"
|
|
2053
|
+
)
|
|
2054
|
+
|
|
2055
|
+
@patch("amd_debug.prerequisites.os.walk")
|
|
2056
|
+
@patch("builtins.open", new_callable=mock_open)
|
|
2057
|
+
def test_capture_cstates_multiple_files(self, mock_open_func, mock_walk):
|
|
2058
|
+
"""Test capture_cstates with multiple cpuidle files"""
|
|
2059
|
+
# Setup mock file reads for two files
|
|
2060
|
+
file_contents = {
|
|
2061
|
+
"/sys/bus/cpu/devices/cpu0/cpuidle/state1": b"C1 info",
|
|
2062
|
+
"/sys/bus/cpu/devices/cpu0/cpuidle/state2": b"C2 info",
|
|
2063
|
+
}
|
|
2064
|
+
|
|
2065
|
+
def side_effect(path, mode="rb"):
|
|
2066
|
+
mock_file = mock_open(read_data=file_contents[path])()
|
|
2067
|
+
return mock_file
|
|
2068
|
+
|
|
2069
|
+
mock_open_func.side_effect = side_effect
|
|
2070
|
+
mock_walk.return_value = [
|
|
2071
|
+
("/sys/bus/cpu/devices/cpu0/cpuidle", [], ["state1", "state2"]),
|
|
2072
|
+
]
|
|
2073
|
+
self.validator.capture_cstates()
|
|
2074
|
+
# The prefix logic is based on order, so check for both lines
|
|
2075
|
+
debug_call = self.mock_db.record_debug.call_args[0][0]
|
|
2076
|
+
self.assertIn("/sys/bus/cpu/devices/cpu0/cpuidle/state1: C1 info", debug_call)
|
|
2077
|
+
self.assertIn("/sys/bus/cpu/devices/cpu0/cpuidle/state2: C2 info", debug_call)
|
|
2078
|
+
self.assertTrue(debug_call.startswith("ACPI C-state information\n"))
|
|
2079
|
+
|
|
2080
|
+
@patch("amd_debug.prerequisites.os.walk")
|
|
2081
|
+
@patch("builtins.open", new_callable=mock_open, read_data=b"")
|
|
2082
|
+
def test_capture_cstates_empty_files(self, _mock_open, mock_walk):
|
|
2083
|
+
"""Test capture_cstates with empty cpuidle files"""
|
|
2084
|
+
mock_walk.return_value = [
|
|
2085
|
+
("/sys/bus/cpu/devices/cpu0/cpuidle", [], ["state1"]),
|
|
2086
|
+
]
|
|
2087
|
+
self.validator.capture_cstates()
|
|
2088
|
+
self.mock_db.record_debug.assert_called_with(
|
|
2089
|
+
"ACPI C-state information\n└─/sys/bus/cpu/devices/cpu0/cpuidle/state1: "
|
|
2090
|
+
)
|
|
2091
|
+
|
|
2092
|
+
@patch("amd_debug.prerequisites.os.walk")
|
|
2093
|
+
@patch("builtins.open", side_effect=PermissionError)
|
|
2094
|
+
def test_capture_cstates_permission_error(self, _mock_open, mock_walk):
|
|
2095
|
+
"""Test capture_cstates when reading cpuidle files raises PermissionError"""
|
|
2096
|
+
mock_walk.return_value = [
|
|
2097
|
+
("/sys/bus/cpu/devices/cpu0/cpuidle", [], ["state1"]),
|
|
2098
|
+
]
|
|
2099
|
+
with self.assertRaises(PermissionError):
|
|
2100
|
+
self.validator.capture_cstates()
|
|
2101
|
+
self.mock_db.record_debug.assert_not_called()
|
|
2102
|
+
|
|
2103
|
+
@patch("amd_debug.prerequisites.os.walk")
|
|
2104
|
+
def test_capture_cstates_no_files(self, mock_walk):
|
|
2105
|
+
"""Test capture_cstates when no cpuidle files are present"""
|
|
2106
|
+
mock_walk.return_value = [
|
|
2107
|
+
("/sys/bus/cpu/devices/cpu0/cpuidle", [], []),
|
|
2108
|
+
]
|
|
2109
|
+
self.validator.capture_cstates()
|
|
2110
|
+
self.mock_db.record_debug.assert_called_with("ACPI C-state information\n")
|
|
2111
|
+
|
|
2112
|
+
@patch("amd_debug.prerequisites.read_file")
|
|
2113
|
+
def test_check_pinctrl_amd_driver_loaded_with_missing_file_error(
|
|
2114
|
+
self, mock_read_file
|
|
2115
|
+
):
|
|
2116
|
+
"""Test check_pinctrl_amd when the driver is loaded but debug file is missing"""
|
|
2117
|
+
mock_read_file.side_effect = FileNotFoundError
|
|
2118
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
2119
|
+
MagicMock(properties={"DRIVER": "amd_gpio"})
|
|
2120
|
+
]
|
|
2121
|
+
|
|
2122
|
+
result = self.validator.check_pinctrl_amd()
|
|
2123
|
+
self.assertTrue(result)
|
|
2124
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
2125
|
+
"GPIO debugfs not available", "👀"
|
|
2126
|
+
)
|
|
2127
|
+
|
|
2128
|
+
def test_check_amdgpu_no_devices(self):
|
|
2129
|
+
"""Test check_amdgpu when no PCI devices are found"""
|
|
2130
|
+
self.mock_pyudev.list_devices.return_value = []
|
|
2131
|
+
result = self.validator.check_amdgpu()
|
|
2132
|
+
self.assertFalse(result)
|
|
2133
|
+
self.mock_db.record_prereq.assert_called_with("Integrated GPU not found", "❌")
|
|
2134
|
+
self.assertTrue(any(isinstance(f, MissingGpu) for f in self.validator.failures))
|
|
2135
|
+
|
|
2136
|
+
def test_check_amdgpu_non_amd_devices(self):
|
|
2137
|
+
"""Test check_amdgpu when PCI devices are present but not AMD GPUs"""
|
|
2138
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
2139
|
+
MagicMock(
|
|
2140
|
+
properties={
|
|
2141
|
+
"PCI_CLASS": "30000",
|
|
2142
|
+
"PCI_ID": "8086abcd",
|
|
2143
|
+
"DRIVER": "i915",
|
|
2144
|
+
}
|
|
2145
|
+
),
|
|
2146
|
+
]
|
|
2147
|
+
result = self.validator.check_amdgpu()
|
|
2148
|
+
self.assertFalse(result)
|
|
2149
|
+
self.mock_db.record_prereq.assert_called_with("Integrated GPU not found", "❌")
|
|
2150
|
+
self.assertTrue(any(isinstance(f, MissingGpu) for f in self.validator.failures))
|
|
2151
|
+
|
|
2152
|
+
def test_check_amdgpu_driver_not_loaded(self):
|
|
2153
|
+
"""Test check_amdgpu when AMD GPU is present but driver is not loaded"""
|
|
2154
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
2155
|
+
MagicMock(
|
|
2156
|
+
properties={"PCI_CLASS": "20000", "PCI_ID": "1111abcd", "DRIVER": None}
|
|
2157
|
+
),
|
|
2158
|
+
MagicMock(
|
|
2159
|
+
properties={"PCI_CLASS": "30000", "PCI_ID": "1002abcd", "DRIVER": None}
|
|
2160
|
+
),
|
|
2161
|
+
]
|
|
2162
|
+
result = self.validator.check_amdgpu()
|
|
2163
|
+
self.assertFalse(result)
|
|
2164
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
2165
|
+
"GPU driver `amdgpu` not loaded", "❌"
|
|
2166
|
+
)
|
|
2167
|
+
self.assertTrue(
|
|
2168
|
+
any(isinstance(f, MissingAmdgpu) for f in self.validator.failures)
|
|
2169
|
+
)
|
|
2170
|
+
|
|
2171
|
+
def test_check_amdgpu_driver_loaded(self):
|
|
2172
|
+
"""Test check_amdgpu when AMD GPU is present and driver is loaded"""
|
|
2173
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
2174
|
+
MagicMock(
|
|
2175
|
+
properties={
|
|
2176
|
+
"PCI_CLASS": "30000",
|
|
2177
|
+
"PCI_ID": "1002abcd",
|
|
2178
|
+
"DRIVER": "amdgpu",
|
|
2179
|
+
"PCI_SLOT_NAME": "0000:01:00.0",
|
|
2180
|
+
}
|
|
2181
|
+
),
|
|
2182
|
+
]
|
|
2183
|
+
result = self.validator.check_amdgpu()
|
|
2184
|
+
self.assertTrue(result)
|
|
2185
|
+
self.mock_db.record_prereq.assert_called_with(
|
|
2186
|
+
"GPU driver `amdgpu` bound to 0000:01:00.0", "✅"
|
|
2187
|
+
)
|
|
2188
|
+
|
|
2189
|
+
def test_check_amdgpu_multiple_devices_mixed(self):
|
|
2190
|
+
"""Test check_amdgpu with multiple devices, one with driver loaded, one without"""
|
|
2191
|
+
self.mock_pyudev.list_devices.return_value = [
|
|
2192
|
+
MagicMock(
|
|
2193
|
+
properties={
|
|
2194
|
+
"PCI_CLASS": "30000",
|
|
2195
|
+
"PCI_ID": "1002abcd",
|
|
2196
|
+
"DRIVER": "amdgpu",
|
|
2197
|
+
"PCI_SLOT_NAME": "0000:01:00.0",
|
|
2198
|
+
}
|
|
2199
|
+
),
|
|
2200
|
+
MagicMock(
|
|
2201
|
+
properties={"PCI_CLASS": "30000", "PCI_ID": "1002abcd", "DRIVER": None}
|
|
2202
|
+
),
|
|
2203
|
+
]
|
|
2204
|
+
result = self.validator.check_amdgpu()
|
|
2205
|
+
self.assertFalse(result)
|
|
2206
|
+
self.mock_db.record_prereq.assert_any_call(
|
|
2207
|
+
"GPU driver `amdgpu` bound to 0000:01:00.0", "✅"
|
|
2208
|
+
)
|
|
2209
|
+
self.mock_db.record_prereq.assert_any_call(
|
|
2210
|
+
"GPU driver `amdgpu` not loaded", "❌"
|
|
2211
|
+
)
|
|
2212
|
+
self.assertTrue(
|
|
2213
|
+
any(isinstance(f, MissingAmdgpu) for f in self.validator.failures)
|
|
2214
|
+
)
|
test_validator.py
CHANGED
|
@@ -264,7 +264,7 @@ class TestValidator(unittest.TestCase):
|
|
|
264
264
|
|
|
265
265
|
# Validate debug messages
|
|
266
266
|
mock_record_debug.assert_called_once_with(
|
|
267
|
-
"Woke up from input source /sys/devices/input0 (3->5)"
|
|
267
|
+
"Woke up from input source /sys/devices/input0 (3->5)"
|
|
268
268
|
)
|
|
269
269
|
|
|
270
270
|
# Stop patches
|
|
@@ -557,7 +557,7 @@ class TestValidator(unittest.TestCase):
|
|
|
557
557
|
# Set attributes for record_cycle
|
|
558
558
|
self.validator.requested_duration = 60
|
|
559
559
|
self.validator.active_gpios = ["GPIO1"]
|
|
560
|
-
self.validator.wakeup_irqs = [5]
|
|
560
|
+
self.validator.wakeup_irqs = ["5"]
|
|
561
561
|
self.validator.kernel_duration = 1.5
|
|
562
562
|
self.validator.hw_sleep_duration = 1.0
|
|
563
563
|
|
|
@@ -580,10 +580,10 @@ class TestValidator(unittest.TestCase):
|
|
|
580
580
|
# Assert record_cycle was called with correct arguments
|
|
581
581
|
mock_record_cycle.assert_called_once_with(
|
|
582
582
|
self.validator.requested_duration,
|
|
583
|
-
self.validator.active_gpios,
|
|
584
|
-
self.validator.wakeup_irqs,
|
|
585
|
-
self.validator.kernel_duration,
|
|
586
|
-
self.validator.hw_sleep_duration,
|
|
583
|
+
",".join(str(gpio) for gpio in self.validator.active_gpios),
|
|
584
|
+
",".join(str(irq) for irq in self.validator.wakeup_irqs),
|
|
585
|
+
int(self.validator.kernel_duration),
|
|
586
|
+
int(self.validator.hw_sleep_duration),
|
|
587
587
|
)
|
|
588
588
|
|
|
589
589
|
def test_program_wakealarm(self):
|