amd-debug-tools 0.2.5__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 +9 -2
- amd_debug/acpi.py +0 -1
- amd_debug/battery.py +0 -1
- amd_debug/bios.py +5 -4
- amd_debug/common.py +70 -3
- amd_debug/database.py +22 -5
- amd_debug/display.py +2 -3
- amd_debug/failures.py +43 -2
- amd_debug/installer.py +4 -5
- amd_debug/kernel.py +10 -7
- amd_debug/prerequisites.py +116 -16
- amd_debug/pstate.py +5 -4
- amd_debug/s2idle.py +22 -16
- amd_debug/sleep_report.py +1 -2
- amd_debug/ttm.py +157 -0
- amd_debug/validator.py +40 -19
- 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.5.dist-info → amd_debug_tools-0.2.12.dist-info}/entry_points.txt +1 -0
- {amd_debug_tools-0.2.5.dist-info → amd_debug_tools-0.2.12.dist-info}/top_level.txt +1 -0
- test_acpi.py +1 -1
- test_bios.py +26 -8
- test_common.py +117 -1
- test_database.py +1 -1
- test_display.py +6 -6
- test_installer.py +68 -1
- test_kernel.py +6 -5
- test_launcher.py +7 -0
- test_prerequisites.py +580 -3
- test_s2idle.py +25 -8
- test_ttm.py +276 -0
- test_validator.py +71 -9
- amd_debug_tools-0.2.5.dist-info/METADATA +0 -181
- amd_debug_tools-0.2.5.dist-info/RECORD +0 -45
- {amd_debug_tools-0.2.5.dist-info → amd_debug_tools-0.2.12.dist-info}/WHEEL +0 -0
- {amd_debug_tools-0.2.5.dist-info → amd_debug_tools-0.2.12.dist-info}/licenses/LICENSE +0 -0
amd_debug/validator.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
#!/usr/bin/python3
|
|
2
1
|
# SPDX-License-Identifier: MIT
|
|
3
2
|
|
|
4
3
|
import glob
|
|
@@ -318,6 +317,9 @@ class SleepValidator(AmdTool):
|
|
|
318
317
|
actions = read_file(os.path.join(p, "actions"))
|
|
319
318
|
message = f"{Headers.WokeFromIrq} {n} ({chip_name} {hw}-{name} {actions})"
|
|
320
319
|
self.db.record_debug(message)
|
|
320
|
+
irq = int(n)
|
|
321
|
+
if irq and irq not in self.wakeup_irqs:
|
|
322
|
+
self.wakeup_irqs += [irq]
|
|
321
323
|
except OSError:
|
|
322
324
|
pass
|
|
323
325
|
return True
|
|
@@ -435,7 +437,6 @@ class SleepValidator(AmdTool):
|
|
|
435
437
|
continue
|
|
436
438
|
self.db.record_debug(
|
|
437
439
|
f"Woke up from input source {device} ({self.wakeup_count[device]}->{count})",
|
|
438
|
-
"💤",
|
|
439
440
|
)
|
|
440
441
|
self.wakeup_count = wakeup_count
|
|
441
442
|
|
|
@@ -470,7 +471,7 @@ class SleepValidator(AmdTool):
|
|
|
470
471
|
self.db.record_debug(f"HW sleep statistics file {p} is missing")
|
|
471
472
|
if not self.hw_sleep_duration:
|
|
472
473
|
self.db.record_cycle_data("Did not reach hardware sleep state", "❌")
|
|
473
|
-
return self.hw_sleep_duration
|
|
474
|
+
return self.hw_sleep_duration > 0
|
|
474
475
|
|
|
475
476
|
def capture_command_line(self):
|
|
476
477
|
"""Capture the kernel command line to debug"""
|
|
@@ -544,16 +545,6 @@ class SleepValidator(AmdTool):
|
|
|
544
545
|
|
|
545
546
|
def analyze_kernel_log(self):
|
|
546
547
|
"""Analyze one of the lines from the kernel log"""
|
|
547
|
-
self.cycle_count = 0
|
|
548
|
-
self.upep = False
|
|
549
|
-
self.upep_microsoft = False
|
|
550
|
-
self.wakeup_irqs = []
|
|
551
|
-
self.idle_masks = []
|
|
552
|
-
self.acpi_errors = []
|
|
553
|
-
self.active_gpios = []
|
|
554
|
-
self.notify_devices = []
|
|
555
|
-
self.page_faults = []
|
|
556
|
-
self.irq1_workaround = False
|
|
557
548
|
self.kernel_log.process_callback(self._analyze_kernel_log_line)
|
|
558
549
|
|
|
559
550
|
if self.cycle_count:
|
|
@@ -583,7 +574,6 @@ class SleepValidator(AmdTool):
|
|
|
583
574
|
if bit_changed & BIT(bit):
|
|
584
575
|
self.db.record_debug(
|
|
585
576
|
f"Idle mask bit {bit} (0x{BIT(bit):x}) changed during suspend",
|
|
586
|
-
"○",
|
|
587
577
|
)
|
|
588
578
|
if self.upep:
|
|
589
579
|
if self.upep_microsoft:
|
|
@@ -644,9 +634,20 @@ class SleepValidator(AmdTool):
|
|
|
644
634
|
|
|
645
635
|
def post(self):
|
|
646
636
|
"""Post-process the suspend test results"""
|
|
637
|
+
self.cycle_count = 0
|
|
638
|
+
self.upep = False
|
|
639
|
+
self.upep_microsoft = False
|
|
640
|
+
self.wakeup_irqs = []
|
|
641
|
+
self.idle_masks = []
|
|
642
|
+
self.acpi_errors = []
|
|
643
|
+
self.active_gpios = []
|
|
644
|
+
self.notify_devices = []
|
|
645
|
+
self.page_faults = []
|
|
646
|
+
self.irq1_workaround = False
|
|
647
|
+
|
|
647
648
|
checks = [
|
|
648
|
-
self.analyze_kernel_log,
|
|
649
649
|
self.capture_wakeup_irq_data,
|
|
650
|
+
self.analyze_kernel_log,
|
|
650
651
|
self.check_gpes,
|
|
651
652
|
self.capture_lid,
|
|
652
653
|
self.check_rtc_cmos,
|
|
@@ -661,10 +662,10 @@ class SleepValidator(AmdTool):
|
|
|
661
662
|
check()
|
|
662
663
|
self.db.record_cycle(
|
|
663
664
|
self.requested_duration,
|
|
664
|
-
self.active_gpios,
|
|
665
|
-
self.wakeup_irqs,
|
|
666
|
-
self.kernel_duration,
|
|
667
|
-
self.hw_sleep_duration,
|
|
665
|
+
",".join(str(gpio) for gpio in self.active_gpios),
|
|
666
|
+
",".join(str(irq) for irq in self.wakeup_irqs),
|
|
667
|
+
int(self.kernel_duration),
|
|
668
|
+
int(self.hw_sleep_duration),
|
|
668
669
|
)
|
|
669
670
|
|
|
670
671
|
def prep(self):
|
|
@@ -703,6 +704,22 @@ class SleepValidator(AmdTool):
|
|
|
703
704
|
else:
|
|
704
705
|
print_color("No RTC device found, please manually wake system", "🚦")
|
|
705
706
|
|
|
707
|
+
def toggle_nvidia(self, value):
|
|
708
|
+
"""Write to the NVIDIA suspend interface"""
|
|
709
|
+
p = os.path.join("/", "proc", "driver", "nvidia", "suspend")
|
|
710
|
+
if not os.path.exists(p):
|
|
711
|
+
return True
|
|
712
|
+
fd = os.open(p, os.O_WRONLY | os.O_SYNC)
|
|
713
|
+
try:
|
|
714
|
+
os.write(fd, value)
|
|
715
|
+
except OSError as e:
|
|
716
|
+
self.db.record_cycle_data(f"Failed to set {value} in NVIDIA {e}", "❌")
|
|
717
|
+
return False
|
|
718
|
+
finally:
|
|
719
|
+
os.close(fd)
|
|
720
|
+
self.db.record_debug(f"Wrote {value} to NVIDIA driver")
|
|
721
|
+
return True
|
|
722
|
+
|
|
706
723
|
@pm_debugging
|
|
707
724
|
def suspend_system(self):
|
|
708
725
|
"""Suspend the system using the dbus or sysfs interface"""
|
|
@@ -744,6 +761,8 @@ class SleepValidator(AmdTool):
|
|
|
744
761
|
self.db.record_cycle_data("Missing dbus", "❌")
|
|
745
762
|
return False
|
|
746
763
|
else:
|
|
764
|
+
if not self.toggle_nvidia(b"suspend"):
|
|
765
|
+
return False
|
|
747
766
|
old = get_wakeup_count()
|
|
748
767
|
p = os.path.join("/", "sys", "power", "state")
|
|
749
768
|
fd = os.open(p, os.O_WRONLY | os.O_SYNC)
|
|
@@ -757,6 +776,8 @@ class SleepValidator(AmdTool):
|
|
|
757
776
|
return False
|
|
758
777
|
finally:
|
|
759
778
|
os.close(fd)
|
|
779
|
+
if not self.toggle_nvidia(b"resume"):
|
|
780
|
+
return False
|
|
760
781
|
return True
|
|
761
782
|
|
|
762
783
|
def unlock_session(self):
|
amd_debug/wake.py
CHANGED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: amd-debug-tools
|
|
3
|
+
Version: 0.2.12
|
|
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=1w5WjuTimgd6ru8FUi-dhOoX-7FsiVsAC42E6RZBcls,3125
|
|
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=QP0lXVPCOG-UOGwEWFPIV9-fnfkglsF8NEjXP7P6HEs,12691
|
|
10
|
+
test_kernel.py,sha256=QsggZOLEt78ID_l6JsHSffH4xzQaBSzSS0uCLaOxpAc,7949
|
|
11
|
+
test_launcher.py,sha256=8g8CBTvLX64Us4RmHtRPSdpV5E2kQFaudBl7VIsxLhE,1733
|
|
12
|
+
test_prerequisites.py,sha256=9OzUI3vZZmAu0yKBLjHRkjeJZufsi7AR8plDtN2ZBco,110642
|
|
13
|
+
test_pstate.py,sha256=a9oAJ9-LANX32XNQhplz6Y75VNYc__QqoSBKIrwvANg,6058
|
|
14
|
+
test_s2idle.py,sha256=BrNj4Bxov4LxpheKAeajCH3tJA_iW-Muy0vW2PRkoEk,34018
|
|
15
|
+
test_sleep_report.py,sha256=ANuxYi_C1oSKAi4xUU2wBu4SwJtcZA7VPpazBe3_WUQ,6922
|
|
16
|
+
test_ttm.py,sha256=QrCdRodQ_CD3nyDqKodb6yR158mgE0iIM5f1fV1Axu8,10515
|
|
17
|
+
test_validator.py,sha256=8uhhP_Piwe4sp0HE-ZthDJWG-QdNxWmE2ICWtcGsA1w,36498
|
|
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=sewb8L4D-p8HmbTIZVSlwbHdHJ3nO2zAG9otcwHpsqs,24382
|
|
27
|
+
amd_debug/installer.py,sha256=BEsjl_sh8cUsbPsnGfoq3BntkDwJdMgsKmR54dzxkKA,14288
|
|
28
|
+
amd_debug/kernel.py,sha256=w2y4syIMPd4OYXhPYrEmCDYnMBg9K16tK6iNwfzRVWU,11718
|
|
29
|
+
amd_debug/prerequisites.py,sha256=tI_D_iyqNBGb3OmdvNfY5X36zen9Q6klYSm-Oi-ZHIY,54842
|
|
30
|
+
amd_debug/pstate.py,sha256=fGA-pKS1mzIrOa9fIqd_q3Y9DvMhuWmInq5GPAGc21k,9564
|
|
31
|
+
amd_debug/s2idle-hook,sha256=LLiaqPtGd0qetu9n6EYxKHZaIdHpVQDONdOuSc0pfFg,1695
|
|
32
|
+
amd_debug/s2idle.py,sha256=73BngSl14f8MWiD8Rp4-QKSzu5-a3XxotDArJ_Y7c4E,13279
|
|
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=VygKoP30psFkLIrZUUCdvauD_xB43867h2BnI-o8ODE,34271
|
|
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.12.dist-info/licenses/LICENSE,sha256=RBlZI6r3MRGzymI2VDX2iW__D2APDbMhu_Xg5t6BWeo,1066
|
|
43
|
+
amd_debug_tools-0.2.12.dist-info/METADATA,sha256=-HT90JLmuyiEcusEKIvBj1rTQouT28VMGAJOYMHsujg,2773
|
|
44
|
+
amd_debug_tools-0.2.12.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
45
|
+
amd_debug_tools-0.2.12.dist-info/entry_points.txt,sha256=hIskDz6k0_6q1qpqWCpVFsca_djxAqkLrUAwzAyEGuE,144
|
|
46
|
+
amd_debug_tools-0.2.12.dist-info/top_level.txt,sha256=VvGkkY5I7O3HoLNrc2VfgjCA-to3PUjnnKd7juONaFw,243
|
|
47
|
+
amd_debug_tools-0.2.12.dist-info/RECORD,,
|
test_acpi.py
CHANGED
|
@@ -79,7 +79,7 @@ class TestAcpi(unittest.TestCase):
|
|
|
79
79
|
def test_acpica_trace_no_acpi_debug(self):
|
|
80
80
|
"""Test AcpicaTracer class when ACPI tracing is not supported"""
|
|
81
81
|
with patch("os.path.exists", return_value=False), patch(
|
|
82
|
-
"
|
|
82
|
+
"amd_debug.common.open", mock_open(read_data="foo")
|
|
83
83
|
):
|
|
84
84
|
tracer = AcpicaTracer()
|
|
85
85
|
self.assertFalse(tracer.supported)
|
test_bios.py
CHANGED
|
@@ -33,8 +33,14 @@ class TestAmdBios(unittest.TestCase):
|
|
|
33
33
|
@patch("amd_debug.bios.minimum_kernel")
|
|
34
34
|
@patch("amd_debug.bios.AcpicaTracer")
|
|
35
35
|
@patch("amd_debug.bios.print_color")
|
|
36
|
+
@patch("subprocess.run")
|
|
36
37
|
def test_set_tracing_enable(
|
|
37
|
-
self,
|
|
38
|
+
self,
|
|
39
|
+
_mock_run,
|
|
40
|
+
_mock_print,
|
|
41
|
+
mock_acpica_tracer,
|
|
42
|
+
mock_minimum_kernel,
|
|
43
|
+
mock_relaunch_sudo,
|
|
38
44
|
):
|
|
39
45
|
"""Test enabling tracing"""
|
|
40
46
|
mock_minimum_kernel.return_value = True
|
|
@@ -53,8 +59,14 @@ class TestAmdBios(unittest.TestCase):
|
|
|
53
59
|
@patch("amd_debug.bios.minimum_kernel")
|
|
54
60
|
@patch("amd_debug.bios.AcpicaTracer")
|
|
55
61
|
@patch("amd_debug.bios.print_color")
|
|
62
|
+
@patch("subprocess.run")
|
|
56
63
|
def test_set_tracing_disable(
|
|
57
|
-
self,
|
|
64
|
+
self,
|
|
65
|
+
_mock_run,
|
|
66
|
+
_mock_print,
|
|
67
|
+
mock_acpica_tracer,
|
|
68
|
+
mock_minimum_kernel,
|
|
69
|
+
mock_relaunch_sudo,
|
|
58
70
|
):
|
|
59
71
|
"""Test disabling tracing"""
|
|
60
72
|
mock_minimum_kernel.return_value = True
|
|
@@ -71,7 +83,10 @@ class TestAmdBios(unittest.TestCase):
|
|
|
71
83
|
|
|
72
84
|
@patch("amd_debug.bios.sscanf_bios_args")
|
|
73
85
|
@patch("amd_debug.bios.print_color")
|
|
74
|
-
|
|
86
|
+
@patch("subprocess.run")
|
|
87
|
+
def test_analyze_kernel_log_line(
|
|
88
|
+
self, _mock_run, mock_print_color, mock_sscanf_bios_args
|
|
89
|
+
):
|
|
75
90
|
"""Test analyzing kernel log line"""
|
|
76
91
|
mock_sscanf_bios_args.return_value = "BIOS argument found"
|
|
77
92
|
|
|
@@ -85,8 +100,9 @@ class TestAmdBios(unittest.TestCase):
|
|
|
85
100
|
|
|
86
101
|
@patch("amd_debug.bios.sscanf_bios_args")
|
|
87
102
|
@patch("amd_debug.bios.print_color")
|
|
103
|
+
@patch("subprocess.run")
|
|
88
104
|
def test_analyze_kernel_log_line_no_bios_args(
|
|
89
|
-
self, mock_print_color, mock_sscanf_bios_args
|
|
105
|
+
self, _mock_run, mock_print_color, mock_sscanf_bios_args
|
|
90
106
|
):
|
|
91
107
|
"""Test analyzing kernel log line with no BIOS arguments"""
|
|
92
108
|
mock_sscanf_bios_args.return_value = None
|
|
@@ -140,12 +156,12 @@ class TestAmdBios(unittest.TestCase):
|
|
|
140
156
|
self.assertFalse(args.enable)
|
|
141
157
|
self.assertTrue(args.disable)
|
|
142
158
|
|
|
143
|
-
@patch("sys.argv", ["bios.py", "version"])
|
|
159
|
+
@patch("sys.argv", ["bios.py", "--version"])
|
|
144
160
|
def test_parse_args_version_command(self):
|
|
145
161
|
"""Test parse_args with version command"""
|
|
146
162
|
|
|
147
163
|
args = parse_args()
|
|
148
|
-
self.
|
|
164
|
+
self.assertTrue(args.version)
|
|
149
165
|
|
|
150
166
|
@patch("sys.argv", ["bios.py"])
|
|
151
167
|
@patch("argparse.ArgumentParser.print_help")
|
|
@@ -227,7 +243,7 @@ class TestAmdBios(unittest.TestCase):
|
|
|
227
243
|
self, _mock_print, mock_show_log_info, mock_version, mock_parse_args
|
|
228
244
|
):
|
|
229
245
|
"""Test main function with version command"""
|
|
230
|
-
mock_parse_args.return_value = argparse.Namespace(command=
|
|
246
|
+
mock_parse_args.return_value = argparse.Namespace(version=True, command=None)
|
|
231
247
|
mock_version.return_value = "1.0.0"
|
|
232
248
|
|
|
233
249
|
result = main()
|
|
@@ -241,7 +257,9 @@ class TestAmdBios(unittest.TestCase):
|
|
|
241
257
|
@patch("amd_debug.bios.show_log_info")
|
|
242
258
|
def test_main_invalid_command(self, mock_show_log_info, mock_parse_args):
|
|
243
259
|
"""Test main function with an invalid command"""
|
|
244
|
-
mock_parse_args.return_value = argparse.Namespace(
|
|
260
|
+
mock_parse_args.return_value = argparse.Namespace(
|
|
261
|
+
version=False, command="invalid"
|
|
262
|
+
)
|
|
245
263
|
|
|
246
264
|
result = main()
|
|
247
265
|
|
test_common.py
CHANGED
|
@@ -4,17 +4,21 @@
|
|
|
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 (
|
|
17
20
|
apply_prefix_wrapper,
|
|
21
|
+
bytes_to_gb,
|
|
18
22
|
Colors,
|
|
19
23
|
convert_string_to_bool,
|
|
20
24
|
colorize_choices,
|
|
@@ -22,12 +26,15 @@ from amd_debug.common import (
|
|
|
22
26
|
compare_file,
|
|
23
27
|
find_ip_version,
|
|
24
28
|
fatal_error,
|
|
29
|
+
gb_to_pages,
|
|
25
30
|
get_distro,
|
|
26
31
|
get_log_priority,
|
|
27
32
|
get_pretty_distro,
|
|
33
|
+
get_system_mem,
|
|
28
34
|
is_root,
|
|
29
35
|
minimum_kernel,
|
|
30
36
|
print_color,
|
|
37
|
+
reboot,
|
|
31
38
|
run_countdown,
|
|
32
39
|
systemd_in_use,
|
|
33
40
|
running_ssh,
|
|
@@ -45,6 +52,7 @@ color_dict = {
|
|
|
45
52
|
"○": Colors.OK,
|
|
46
53
|
"💤": Colors.OK,
|
|
47
54
|
"💯": Colors.UNDERLINE,
|
|
55
|
+
"🚫": Colors.UNDERLINE,
|
|
48
56
|
"🗣️": Colors.HEADER,
|
|
49
57
|
}
|
|
50
58
|
|
|
@@ -442,3 +450,111 @@ class TestCommon(unittest.TestCase):
|
|
|
442
450
|
with patch("sys.exit") as mock_exit:
|
|
443
451
|
convert_string_to_bool("[unclosed_list")
|
|
444
452
|
mock_exit.assert_called_once_with("Invalid entry: [unclosed_list")
|
|
453
|
+
|
|
454
|
+
def test_bytes_to_gb(self):
|
|
455
|
+
"""Test bytes_to_gb conversion"""
|
|
456
|
+
# 4096 bytes should be 4096*4096/(1024*1024*1024) GB
|
|
457
|
+
self.assertAlmostEqual(bytes_to_gb(1), 4096 / (1024 * 1024 * 1024))
|
|
458
|
+
self.assertAlmostEqual(bytes_to_gb(0), 0)
|
|
459
|
+
self.assertAlmostEqual(bytes_to_gb(1024), 1024 * 4096 / (1024 * 1024 * 1024))
|
|
460
|
+
|
|
461
|
+
def test_gb_to_pages(self):
|
|
462
|
+
"""Test gb_to_pages conversion"""
|
|
463
|
+
# 1 GB should be int(1 * (1024*1024*1024) / 4096)
|
|
464
|
+
self.assertEqual(gb_to_pages(1), int((1024 * 1024 * 1024) / 4096))
|
|
465
|
+
self.assertEqual(gb_to_pages(0), 0)
|
|
466
|
+
self.assertEqual(gb_to_pages(2), int(2 * (1024 * 1024 * 1024) / 4096))
|
|
467
|
+
|
|
468
|
+
@patch(
|
|
469
|
+
"builtins.open",
|
|
470
|
+
new_callable=mock_open,
|
|
471
|
+
read_data="MemTotal: 16384516 kB\n",
|
|
472
|
+
)
|
|
473
|
+
@patch("os.path.join", return_value="/proc/meminfo")
|
|
474
|
+
def test_get_system_mem_valid(self, _mock_join, mock_file):
|
|
475
|
+
"""Test get_system_mem returns correct value"""
|
|
476
|
+
expected_gb = 16384516 / (1024 * 1024)
|
|
477
|
+
self.assertAlmostEqual(get_system_mem(), expected_gb)
|
|
478
|
+
mock_file.assert_called_once_with("/proc/meminfo", "r", encoding="utf-8")
|
|
479
|
+
|
|
480
|
+
def test_reboot_dbus_fast_success(self):
|
|
481
|
+
"""Test reboot returns True when reboot_dbus_fast succeeds"""
|
|
482
|
+
|
|
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):
|
|
490
|
+
pass
|
|
491
|
+
return True
|
|
492
|
+
|
|
493
|
+
mock_loop = Mock()
|
|
494
|
+
mock_loop.run_until_complete.side_effect = mock_run_until_complete
|
|
495
|
+
|
|
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()
|
|
500
|
+
|
|
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"""
|
|
504
|
+
|
|
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
|
|
545
|
+
|
|
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
|
|
549
|
+
|
|
550
|
+
# Mock the import to raise ImportError when dbus is imported
|
|
551
|
+
original_import = builtins.__import__
|
|
552
|
+
|
|
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)
|
|
557
|
+
|
|
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
|
@@ -125,6 +125,29 @@ class TestInstaller(unittest.TestCase):
|
|
|
125
125
|
)
|
|
126
126
|
self.assertTrue(ret)
|
|
127
127
|
|
|
128
|
+
@patch("builtins.print")
|
|
129
|
+
@patch("amd_debug.installer.get_distro", return_value="fedora")
|
|
130
|
+
@patch("builtins.open", new_callable=mock_open, read_data="VARIANT_ID=kde\n")
|
|
131
|
+
@patch("os.execvp", return_value=None)
|
|
132
|
+
@patch("subprocess.check_call", return_value=0)
|
|
133
|
+
@patch("subprocess.call", return_value=1)
|
|
134
|
+
def test_install_iasl_fedora_kde(
|
|
135
|
+
self,
|
|
136
|
+
_mock_call,
|
|
137
|
+
_mock_check_call,
|
|
138
|
+
_mock_variant,
|
|
139
|
+
_mock_distro,
|
|
140
|
+
_fake_sudo,
|
|
141
|
+
_mock_print,
|
|
142
|
+
):
|
|
143
|
+
"""Test install requirements function on Fedora KDE"""
|
|
144
|
+
self.installer.set_requirements("iasl")
|
|
145
|
+
ret = self.installer.install_dependencies()
|
|
146
|
+
_mock_check_call.assert_called_once_with(
|
|
147
|
+
["dnf", "install", "-y", "acpica-tools"]
|
|
148
|
+
)
|
|
149
|
+
self.assertTrue(ret)
|
|
150
|
+
|
|
128
151
|
@patch("builtins.print")
|
|
129
152
|
@patch("amd_debug.installer.get_distro", return_value="ubuntu")
|
|
130
153
|
@patch("os.execvp", return_value=None)
|
|
@@ -162,6 +185,27 @@ class TestInstaller(unittest.TestCase):
|
|
|
162
185
|
_mock_check_call.assert_called_once_with(["dnf", "install", "-y", "ethtool"])
|
|
163
186
|
self.assertTrue(ret)
|
|
164
187
|
|
|
188
|
+
@patch("builtins.print")
|
|
189
|
+
@patch("amd_debug.installer.get_distro", return_value="fedora")
|
|
190
|
+
@patch("builtins.open", new_callable=mock_open, read_data="VARIANT_ID=kde\n")
|
|
191
|
+
@patch("os.execvp", return_value=None)
|
|
192
|
+
@patch("subprocess.check_call", return_value=0)
|
|
193
|
+
@patch("subprocess.call", return_value=1)
|
|
194
|
+
def test_install_ethtool_fedora_kde(
|
|
195
|
+
self,
|
|
196
|
+
_mock_call,
|
|
197
|
+
_mock_check_call,
|
|
198
|
+
_mock_variant,
|
|
199
|
+
_mock_distro,
|
|
200
|
+
_fake_sudo,
|
|
201
|
+
_mock_print,
|
|
202
|
+
):
|
|
203
|
+
"""Test install requirements function on Fedora KDE"""
|
|
204
|
+
self.installer.set_requirements("ethtool")
|
|
205
|
+
ret = self.installer.install_dependencies()
|
|
206
|
+
_mock_check_call.assert_called_once_with(["dnf", "install", "-y", "ethtool"])
|
|
207
|
+
self.assertTrue(ret)
|
|
208
|
+
|
|
165
209
|
@patch("builtins.print")
|
|
166
210
|
@patch("amd_debug.installer.get_distro", return_value="arch")
|
|
167
211
|
@patch("os.execvp", return_value=None)
|
|
@@ -231,7 +275,30 @@ class TestInstaller(unittest.TestCase):
|
|
|
231
275
|
self.installer.set_requirements("edid-decode")
|
|
232
276
|
ret = self.installer.install_dependencies()
|
|
233
277
|
_mock_check_call.assert_called_once_with(
|
|
234
|
-
["dnf", "install", "-y", "libdisplay-info"]
|
|
278
|
+
["dnf", "install", "-y", "libdisplay-info-tools"]
|
|
279
|
+
)
|
|
280
|
+
self.assertTrue(ret)
|
|
281
|
+
|
|
282
|
+
@patch("builtins.print")
|
|
283
|
+
@patch("amd_debug.installer.get_distro", return_value="fedora")
|
|
284
|
+
@patch("builtins.open", new_callable=mock_open, read_data="VARIANT_ID=kde\n")
|
|
285
|
+
@patch("os.execvp", return_value=None)
|
|
286
|
+
@patch("subprocess.check_call", return_value=0)
|
|
287
|
+
@patch("subprocess.call", return_value=1)
|
|
288
|
+
def test_install_edid_decode_fedora_kde(
|
|
289
|
+
self,
|
|
290
|
+
_mock_call,
|
|
291
|
+
_mock_check_call,
|
|
292
|
+
_mock_variant,
|
|
293
|
+
_mock_distro,
|
|
294
|
+
_fake_sudo,
|
|
295
|
+
_mock_print,
|
|
296
|
+
):
|
|
297
|
+
"""Test install requirements function for edid-decode on Fedora KDE"""
|
|
298
|
+
self.installer.set_requirements("edid-decode")
|
|
299
|
+
ret = self.installer.install_dependencies()
|
|
300
|
+
_mock_check_call.assert_called_once_with(
|
|
301
|
+
["dnf", "install", "-y", "libdisplay-info-tools"]
|
|
235
302
|
)
|
|
236
303
|
self.assertTrue(ret)
|
|
237
304
|
|