amd-debug-tools 0.2.4__py3-none-any.whl → 0.2.6__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/ttm.py ADDED
@@ -0,0 +1,161 @@
1
+ #!/usr/bin/python3
2
+ # SPDX-License-Identifier: MIT
3
+ """TTM configuration tool"""
4
+
5
+ import os
6
+ import argparse
7
+ from amd_debug.common import (
8
+ AmdTool,
9
+ bytes_to_gb,
10
+ gb_to_pages,
11
+ get_system_mem,
12
+ is_root,
13
+ print_color,
14
+ reboot,
15
+ version,
16
+ )
17
+
18
+ TTM_PARAM_PATH = "/sys/module/ttm/parameters/pages_limit"
19
+ MODPROBE_CONF_PATH = "/etc/modprobe.d/ttm.conf"
20
+ # Maximum percentage of total system memory to allow for TTM
21
+ MAX_MEMORY_PERCENTAGE = 90
22
+
23
+
24
+ def maybe_reboot() -> bool:
25
+ """Prompt to reboot system"""
26
+ response = input("Would you like to reboot the system now? (y/n): ").strip().lower()
27
+ if response in ("y", "yes"):
28
+ return reboot()
29
+ return True
30
+
31
+
32
+ class AmdTtmTool(AmdTool):
33
+ """Class for handling TTM page configuration"""
34
+
35
+ def __init__(self, logging):
36
+ log_prefix = "ttm" if logging else None
37
+ super().__init__(log_prefix)
38
+
39
+ def get(self) -> bool:
40
+ """Read current page limit"""
41
+ try:
42
+ with open(TTM_PARAM_PATH, "r", encoding="utf-8") as f:
43
+ pages = int(f.read().strip())
44
+ gb_value = bytes_to_gb(pages)
45
+ print_color(
46
+ f"Current TTM pages limit: {pages} pages ({gb_value:.2f} GB)", "💻"
47
+ )
48
+ except FileNotFoundError:
49
+ print_color(f"Error: Could not find {TTM_PARAM_PATH}", "❌")
50
+ return False
51
+
52
+ total = get_system_mem()
53
+ if total > 0:
54
+ print_color(f"Total system memory: {total:.2f} GB", "💻")
55
+
56
+ return True
57
+
58
+ def set(self, gb_value) -> bool:
59
+ """Set a new page limit"""
60
+ if not is_root():
61
+ print_color("Root privileges required", "❌")
62
+ return False
63
+
64
+ # Check against system memory
65
+ total = get_system_mem()
66
+ if total > 0:
67
+ max_recommended_gb = total * MAX_MEMORY_PERCENTAGE / 100
68
+
69
+ if gb_value > total:
70
+ print_color(
71
+ f"{gb_value:.2f} GB is greater than total system memory ({total:.2f} GB)",
72
+ "❌",
73
+ )
74
+ return False
75
+
76
+ if gb_value > max_recommended_gb:
77
+ print_color(
78
+ f"Warning: The requested value ({gb_value:.2f} GB) exceeds {MAX_MEMORY_PERCENTAGE}% of your system memory ({max_recommended_gb:.2f} GB).",
79
+ "🚦",
80
+ )
81
+ response = (
82
+ input(
83
+ "This could cause system instability. Continue anyway? (y/n): "
84
+ )
85
+ .strip()
86
+ .lower()
87
+ )
88
+ if response not in ("y", "yes"):
89
+ print_color("Operation cancelled.", "🚦")
90
+ return False
91
+
92
+ pages = gb_to_pages(gb_value)
93
+
94
+ with open(MODPROBE_CONF_PATH, "w", encoding="utf-8") as f:
95
+ f.write(f"options ttm pages_limit={pages}\n")
96
+ print_color(
97
+ f"Successfully set TTM pages limit to {pages} pages ({gb_value:.2f} GB)",
98
+ "🐧",
99
+ )
100
+ print_color(f"Configuration written to {MODPROBE_CONF_PATH}", "🐧")
101
+ print_color("NOTE: You need to reboot for changes to take effect.", "○")
102
+
103
+ return maybe_reboot()
104
+
105
+ def clear(self) -> bool:
106
+ """Clears the page limit"""
107
+ if not os.path.exists(MODPROBE_CONF_PATH):
108
+ print_color(f"{MODPROBE_CONF_PATH} doesn't exist", "❌")
109
+ return False
110
+
111
+ if not is_root():
112
+ print_color("Root privileges required", "❌")
113
+ return False
114
+
115
+ os.remove(MODPROBE_CONF_PATH)
116
+ print_color(f"Configuration {MODPROBE_CONF_PATH} removed", "🐧")
117
+
118
+ return maybe_reboot()
119
+
120
+
121
+ def parse_args():
122
+ """Parse command line arguments."""
123
+ parser = argparse.ArgumentParser(description="Manage TTM pages limit")
124
+ parser.add_argument("--set", type=float, help="Set pages limit in GB")
125
+ parser.add_argument(
126
+ "--clear", action="store_true", help="Clear a previously set page limit"
127
+ )
128
+ parser.add_argument(
129
+ "--version", action="store_true", help="Show version information"
130
+ )
131
+ parser.add_argument(
132
+ "--tool-debug",
133
+ action="store_true",
134
+ help="Enable tool debug logging",
135
+ )
136
+
137
+ return parser.parse_args()
138
+
139
+
140
+ def main() -> None | int:
141
+ """Main function"""
142
+
143
+ args = parse_args()
144
+ tool = AmdTtmTool(args.tool_debug)
145
+ ret = False
146
+
147
+ if args.version:
148
+ print(version())
149
+ return
150
+ elif args.set is not None:
151
+ if args.set <= 0:
152
+ print("Error: GB value must be greater than 0")
153
+ return 1
154
+ ret = tool.set(args.set)
155
+ elif args.clear:
156
+ ret = tool.clear()
157
+ else:
158
+ ret = tool.get()
159
+ if ret is False:
160
+ return 1
161
+ return
amd_debug/validator.py CHANGED
@@ -703,6 +703,22 @@ class SleepValidator(AmdTool):
703
703
  else:
704
704
  print_color("No RTC device found, please manually wake system", "🚦")
705
705
 
706
+ def toggle_nvidia(self, value):
707
+ """Write to the NVIDIA suspend interface"""
708
+ p = os.path.join("/", "proc", "driver", "nvidia", "suspend")
709
+ if not os.path.exists(p):
710
+ return True
711
+ fd = os.open(p, os.O_WRONLY | os.O_SYNC)
712
+ try:
713
+ os.write(fd, value)
714
+ except OSError as e:
715
+ self.db.record_cycle_data(f"Failed to set {value} in NVIDIA {e}", "❌")
716
+ return False
717
+ finally:
718
+ os.close(fd)
719
+ self.db.record_debug(f"Wrote {value} to NVIDIA driver")
720
+ return True
721
+
706
722
  @pm_debugging
707
723
  def suspend_system(self):
708
724
  """Suspend the system using the dbus or sysfs interface"""
@@ -744,6 +760,8 @@ class SleepValidator(AmdTool):
744
760
  self.db.record_cycle_data("Missing dbus", "❌")
745
761
  return False
746
762
  else:
763
+ if not self.toggle_nvidia(b"suspend"):
764
+ return False
747
765
  old = get_wakeup_count()
748
766
  p = os.path.join("/", "sys", "power", "state")
749
767
  fd = os.open(p, os.O_WRONLY | os.O_SYNC)
@@ -757,6 +775,8 @@ class SleepValidator(AmdTool):
757
775
  return False
758
776
  finally:
759
777
  os.close(fd)
778
+ if not self.toggle_nvidia(b"resume"):
779
+ return False
760
780
  return True
761
781
 
762
782
  def unlock_session(self):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: amd-debug-tools
3
- Version: 0.2.4
3
+ Version: 0.2.6
4
4
  Summary: debug tools for AMD systems
5
5
  Author-email: Mario Limonciello <superm1@kernel.org>
6
6
  License-Expression: MIT
@@ -131,7 +131,7 @@ The following optional arguments are supported for this command:
131
131
  If the tool is launched with an environment that can call `xdg-open`, the report
132
132
  will be opened in a browser.
133
133
 
134
- ### `amd-s2idle version`
134
+ ### `amd-s2idle --version`
135
135
  This will print the version of the tool and exit.
136
136
 
137
137
  ### Debug output
@@ -164,7 +164,7 @@ The following optional arguments are supported for this command:
164
164
  --input INPUT Optional input file to parse
165
165
  --tool-debug Enable tool debug logging
166
166
 
167
- ### `amd-bios version`
167
+ ### `amd-bios --version`
168
168
  This will print the version of the tool and exit.
169
169
 
170
170
  ## amd-pstate
@@ -172,6 +172,9 @@ This will print the version of the tool and exit.
172
172
  It will capture some state from the system as well as from the machine specific registers that
173
173
  amd-pstate uses.
174
174
 
175
+ ## amd-ttm
176
+ `amd-ttm` is a tool used for managing the TTM memory settings on AMD systems.
177
+
175
178
  ## Compatibility scripts
176
179
 
177
180
  Compatibility scripts are provided for the previous names the tools went by:
@@ -1,45 +1,47 @@
1
1
  launcher.py,sha256=M8kT9DtyZoQgZaKWDbSBu4jsS6tZF1gWko3sovNVyag,947
2
2
  test_acpi.py,sha256=wtS43Rz95h7YEEJBeFa6Mswaeo4syBZrw4hY8i0YbJY,3117
3
3
  test_batteries.py,sha256=nN5pfP5El7Whypq3HHEpW8bufdf5EWSTVGbayfNQYP4,3360
4
- test_bios.py,sha256=ZPqI5X0QpEJBNJP-i5gNZzlbOlVSpznH4uv34esSqD8,8984
5
- test_common.py,sha256=fb16Oilh5ga6VgF-UgBj6azoYzZnPrS7KpECQ3nCwlg,16335
4
+ test_bios.py,sha256=x_KLmQqGEbQhTugyWCHGXjGp2H1dCdhRz0kgw2Big8w,9276
5
+ test_common.py,sha256=EYbyObC9vIXquT3EbgQ_98V4Zw2ebUCY9cfS9VOoywE,19722
6
6
  test_database.py,sha256=q5ZjI5u20f7ki6iCY5o1iPi0YOvPz1_W0LTDraU8mN4,10040
7
7
  test_display.py,sha256=hHggv-zBthF1BlwWWSjzAm7BBw1DWcElwil5xAuz87g,5822
8
8
  test_failures.py,sha256=H1UxXeVjhJs9-j9yas4vwAha676GX1Es7Kz8RN2B590,6845
9
9
  test_installer.py,sha256=oDMCvaKqqAWjTggltacnasQ-s1gyUvXPDcNrCUGnux4,10216
10
- test_kernel.py,sha256=RW-eLbae02Bhwfu1cegqA1pTj6AS5IqD5lLe-6T0Rjo,7871
11
- test_launcher.py,sha256=80xVbidrbx8ixMt_x5Uvfn7nFnB637nX69yIZTifyuk,1511
12
- test_prerequisites.py,sha256=DiB2SkZJf7OFEta1SifoEHyszuvKOfjs967CsHE6rg4,83244
10
+ test_kernel.py,sha256=2EXrLht5ZWdT4N5pb_F3zqZl9NEghjnDpcMGCMw3obI,7917
11
+ test_launcher.py,sha256=8g8CBTvLX64Us4RmHtRPSdpV5E2kQFaudBl7VIsxLhE,1733
12
+ test_prerequisites.py,sha256=-q6v80QXDMB_Mdek2KZTmKfKcRVZnHf8SBEhcT0RyIY,88498
13
13
  test_pstate.py,sha256=a9oAJ9-LANX32XNQhplz6Y75VNYc__QqoSBKIrwvANg,6058
14
- test_s2idle.py,sha256=YpFGpH84xvjI9mY6uBSKapa74hZnG8ZwBShXsJXpmyQ,33540
14
+ test_s2idle.py,sha256=FxsyujgX9Px3m56VzHNeA8yMTHmJiRLWxYt-fh1m5gw,33585
15
15
  test_sleep_report.py,sha256=ANuxYi_C1oSKAi4xUU2wBu4SwJtcZA7VPpazBe3_WUQ,6922
16
- test_validator.py,sha256=-MfrWfhwef_aRqOSD_dJGhH0shsghhtOBgzeijzyLW4,33975
16
+ test_validator.py,sha256=RpjyzxDpExhLcSJfQ0UDuonr4sTFAfa7sTtY5g7tc_Q,36410
17
17
  test_wake.py,sha256=6zi5GVFHQKU1sTWw3O5-aGriB9uu5713QLn4l2wjhpM,7152
18
- amd_debug/__init__.py,sha256=3wZxCDY3KPpfIxMz4vGmp6jUAB2GF4VTK4Xb86vy8c4,1101
18
+ amd_debug/__init__.py,sha256=66Ya61av8RCws6bEY_vdujGmjBIZ6_UqfuWHgMNNOJY,1271
19
19
  amd_debug/acpi.py,sha256=fkD3Sov8cRT5ryPlakRlT7Z9jiCLT9x_MPWxt3xU_tc,3161
20
20
  amd_debug/battery.py,sha256=WN-6ys9PHCZIwg7PdwyBOa62GjBp8WKG0v1YZt5_W5s,3122
21
- amd_debug/bios.py,sha256=nVIDYqyyKiIO21nyS8lV-qB3ypBJOSIKIuVYFOVoBuw,4017
22
- amd_debug/common.py,sha256=H9tIRlRFOMwe0d3f2-vXQeK2rJl5Z1WJzkpQM9ivpOc,10347
21
+ amd_debug/bios.py,sha256=y1iwDqX-mXCkoUtHSi-XO9pN-oLfaqbAMzANGI12zHs,4041
22
+ amd_debug/common.py,sha256=fHrmSEVerVAE7KXjspf60eOAetUO7K9s1yhNyE2xi94,11598
23
23
  amd_debug/database.py,sha256=GkRg3cmaNceyQ2_hy0MBAlMbnTDPHo2co2o4ObWpnQg,10621
24
24
  amd_debug/display.py,sha256=5L9x9tI_UoulHpIvuxuVASRtdXta7UCW_JjTb5StEB0,953
25
25
  amd_debug/failures.py,sha256=z4O4Q-akv3xYGssSZFCqE0cDE4P9F_aw1hxil3McoD4,22910
26
- amd_debug/installer.py,sha256=tNWhlfxQEA30guk-fzMvcc237hf_PARVQuHaH3sTp4A,14287
27
- amd_debug/kernel.py,sha256=UAlxlXNuZxtHVtrfCmTp12YombVaUs4mizOxwuXTX2M,12038
28
- amd_debug/prerequisites.py,sha256=_SlRzYvWQpZt8VdioOAA2bMt1G9TbNs75swr60Yu1V4,50005
29
- amd_debug/pstate.py,sha256=lLRsayKi7KOXZCQ6Zjm2pNaobpjLXcgLHXZ9Zt40Fd4,9559
26
+ amd_debug/installer.py,sha256=6_Y0oHypW-oh_P8N9JW7fzbqidpsi5jphw9_8s5Qvso,14288
27
+ amd_debug/kernel.py,sha256=HpX-QRh8tgkvqKnExfo2JrYqfcbMY8GNgDrC2VVV0Oc,11638
28
+ amd_debug/prerequisites.py,sha256=zK-IXL52p_jomA1SsS-6btIHuaapNKW4sD-Fy0wBr68,51399
29
+ amd_debug/pstate.py,sha256=AOKCvUb0ngwHU2C59uSKrFwdLzEyn8r1w2DgWhZAMKM,9583
30
30
  amd_debug/s2idle-hook,sha256=LLiaqPtGd0qetu9n6EYxKHZaIdHpVQDONdOuSc0pfFg,1695
31
- amd_debug/s2idle.py,sha256=lr1wcuJcpvI5pL2gNHqrc7n5E7EYCztvaAYYFPMlGYk,13259
31
+ amd_debug/s2idle.py,sha256=4cxHNfmvq11BE-AtkEthIqR-xrWmxW2LTa6oVDtGztY,13284
32
32
  amd_debug/sleep_report.py,sha256=hhqu711AKtjeYF2xmGcejyCyyPtmq4-gC_hROUCrC0g,17317
33
- amd_debug/validator.py,sha256=-rPqPnYAM1Vevw7vxIbGNPKo1bCRo48IpCBi3Y72-Cw,33419
33
+ amd_debug/test_ttm.py,sha256=McNdEJZ14AeFw8fIxzul9ff8kr67dz1dPvK2fqlM4IA,11219
34
+ amd_debug/ttm.py,sha256=U7beRffgoXIPgUXet4ZMabEGo7b0qqKMluhBVMD7O04,4830
35
+ amd_debug/validator.py,sha256=X-cNFVvHWKzFgT4aR0Td3I2kwQRBOK4vQUk6L276VCQ,34153
34
36
  amd_debug/wake.py,sha256=xT8WrFrN6voCmXWo5dsn4mQ7iR2QJxHrrYBd3EREG-Q,3936
35
37
  amd_debug/bash/amd-s2idle,sha256=g_cle1ElCJpwE4wcLezL6y-BdasDKTnNMhrtzKLE9ks,1142
36
38
  amd_debug/templates/html,sha256=JfGhpmHIB2C2GItdGI1kuC8uayqEVgrpQvAWAj35eZ4,14580
37
39
  amd_debug/templates/md,sha256=r8X2aehnH2gzj0WHYTZ5K9wAqC5y39i_3nkDORSC0uM,787
38
40
  amd_debug/templates/stdout,sha256=hyoOJ96K2dJfnWRWhyCuariLKbEHXvs9mstV_g5aMdI,469
39
41
  amd_debug/templates/txt,sha256=nNdsvbPFOhGdL7VA-_4k5aN3nB-6ouGQt6AsWst7T3w,649
40
- amd_debug_tools-0.2.4.dist-info/licenses/LICENSE,sha256=RBlZI6r3MRGzymI2VDX2iW__D2APDbMhu_Xg5t6BWeo,1066
41
- amd_debug_tools-0.2.4.dist-info/METADATA,sha256=jzff0ncunf8ZwS6z90_U577R4OEOlZyf3tVtGA8RHsg,6877
42
- amd_debug_tools-0.2.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
43
- amd_debug_tools-0.2.4.dist-info/entry_points.txt,sha256=HC11T2up0pPfroAn6Pg5M2jOZXhkWIipToJ1YPTKqu8,116
44
- amd_debug_tools-0.2.4.dist-info/top_level.txt,sha256=XYjxExbUTEtiIlag_5iQvZSVOC1EIxhKM4NLklReQ0k,234
45
- amd_debug_tools-0.2.4.dist-info/RECORD,,
42
+ amd_debug_tools-0.2.6.dist-info/licenses/LICENSE,sha256=RBlZI6r3MRGzymI2VDX2iW__D2APDbMhu_Xg5t6BWeo,1066
43
+ amd_debug_tools-0.2.6.dist-info/METADATA,sha256=pMVZTIUIqoKEksnbkS2UCFJPnDRJ1k6sf3Jwj74mNxk,6971
44
+ amd_debug_tools-0.2.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
45
+ amd_debug_tools-0.2.6.dist-info/entry_points.txt,sha256=hIskDz6k0_6q1qpqWCpVFsca_djxAqkLrUAwzAyEGuE,144
46
+ amd_debug_tools-0.2.6.dist-info/top_level.txt,sha256=XYjxExbUTEtiIlag_5iQvZSVOC1EIxhKM4NLklReQ0k,234
47
+ amd_debug_tools-0.2.6.dist-info/RECORD,,
@@ -2,3 +2,4 @@
2
2
  amd-bios = amd_debug:amd_bios
3
3
  amd-pstate = amd_debug:amd_pstate
4
4
  amd-s2idle = amd_debug:amd_s2idle
5
+ amd-ttm = amd_debug:amd_ttm
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, _mock_print, mock_acpica_tracer, mock_minimum_kernel, mock_relaunch_sudo
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, _mock_print, mock_acpica_tracer, mock_minimum_kernel, mock_relaunch_sudo
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
- def test_analyze_kernel_log_line(self, mock_print_color, mock_sscanf_bios_args):
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.assertEqual(args.command, "version")
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="version")
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(command="invalid")
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
@@ -15,6 +15,7 @@ from platform import uname_result
15
15
 
16
16
  from amd_debug.common import (
17
17
  apply_prefix_wrapper,
18
+ bytes_to_gb,
18
19
  Colors,
19
20
  convert_string_to_bool,
20
21
  colorize_choices,
@@ -22,12 +23,15 @@ from amd_debug.common import (
22
23
  compare_file,
23
24
  find_ip_version,
24
25
  fatal_error,
26
+ gb_to_pages,
25
27
  get_distro,
26
28
  get_log_priority,
27
29
  get_pretty_distro,
30
+ get_system_mem,
28
31
  is_root,
29
32
  minimum_kernel,
30
33
  print_color,
34
+ reboot,
31
35
  run_countdown,
32
36
  systemd_in_use,
33
37
  running_ssh,
@@ -442,3 +446,90 @@ class TestCommon(unittest.TestCase):
442
446
  with patch("sys.exit") as mock_exit:
443
447
  convert_string_to_bool("[unclosed_list")
444
448
  mock_exit.assert_called_once_with("Invalid entry: [unclosed_list")
449
+
450
+ def test_bytes_to_gb(self):
451
+ """Test bytes_to_gb conversion"""
452
+ # 4096 bytes should be 4096*4096/(1024*1024*1024) GB
453
+ self.assertAlmostEqual(bytes_to_gb(1), 4096 / (1024 * 1024 * 1024))
454
+ self.assertAlmostEqual(bytes_to_gb(0), 0)
455
+ self.assertAlmostEqual(bytes_to_gb(1024), 1024 * 4096 / (1024 * 1024 * 1024))
456
+
457
+ def test_gb_to_pages(self):
458
+ """Test gb_to_pages conversion"""
459
+ # 1 GB should be int(1 * (1024*1024*1024) / 4096)
460
+ self.assertEqual(gb_to_pages(1), int((1024 * 1024 * 1024) / 4096))
461
+ self.assertEqual(gb_to_pages(0), 0)
462
+ self.assertEqual(gb_to_pages(2), int(2 * (1024 * 1024 * 1024) / 4096))
463
+
464
+ @patch(
465
+ "builtins.open",
466
+ new_callable=mock_open,
467
+ read_data="MemTotal: 16384516 kB\n",
468
+ )
469
+ @patch("os.path.join", return_value="/proc/meminfo")
470
+ def test_get_system_mem_valid(self, _mock_join, mock_file):
471
+ """Test get_system_mem returns correct value"""
472
+ expected_gb = 16384516 / (1024 * 1024)
473
+ self.assertAlmostEqual(get_system_mem(), expected_gb)
474
+ mock_file.assert_called_once_with("/proc/meminfo", "r", encoding="utf-8")
475
+
476
+ @patch("builtins.open", new_callable=mock_open, read_data="NoMemHere: 1234\n")
477
+ @patch("os.path.join", return_value="/proc/meminfo")
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"""
499
+
500
+ def Reboot(self, _arg): # pylint: disable=invalid-name
501
+ """Dummy Reboot method"""
502
+ raise DummyDBusException("fail")
503
+
504
+ class DummyObj: # pylint: disable=too-few-public-methods
505
+ """Dummy object"""
506
+
507
+ def __init__(self):
508
+ pass
509
+
510
+ class DummyBus: # pylint: disable=too-few-public-methods
511
+ """Dummy bus"""
512
+
513
+ def get_object(self, *args, **kwargs):
514
+ """Dummy get_object method"""
515
+ return DummyObj()
516
+
517
+ class DummyDBus:
518
+ """Dummy dbus"""
519
+
520
+ class exceptions: # pylint: disable=invalid-name
521
+ """Dummy exceptions"""
522
+
523
+ DBusException = DummyDBusException
524
+
525
+ def SystemBus(self): # pylint: disable=invalid-name
526
+ """Dummy SystemBus method"""
527
+ return DummyBus()
528
+
529
+ def Interface(self, _obj, _name): # pylint: disable=invalid-name
530
+ """Dummy Interface method"""
531
+ return DummyIntf()
532
+
533
+ with patch.dict("sys.modules", {"dbus": DummyDBus()}):
534
+ reboot()
535
+ self.assertTrue(mock_fatal_error.called)
test_kernel.py CHANGED
@@ -85,7 +85,7 @@ class TestKernelLog(unittest.TestCase):
85
85
 
86
86
  # test a real post code line
87
87
  line = 'ex_trace_args: " POST CODE: %X ACPI TIMER: %X TIME: %d.%d ms\\n", b0003f33, 83528798, 0, 77, 0, 0'
88
- expected_output = "POST CODE: B0003F33 ACPI TIMER: 83528798 TIME: 0.77 ms"
88
+ expected_output = "POST CODE: B0003F33 ACPI TIMER: 83528798 TIME: 0.119 ms"
89
89
  result = sscanf_bios_args(line)
90
90
  self.assertEqual(result, expected_output)
91
91
 
@@ -114,7 +114,8 @@ class TestDmesgLogger(unittest.TestCase):
114
114
  """Test Dmesg logger functions"""
115
115
 
116
116
  @classmethod
117
- def setUpClass(cls):
117
+ @patch("subprocess.run")
118
+ def setUpClass(cls, _mock_run=None):
118
119
  logging.basicConfig(filename="/dev/null", level=logging.DEBUG)
119
120
 
120
121
  def test_dmesg_logger_initialization(self):
test_launcher.py CHANGED
@@ -52,3 +52,10 @@ class TestLauncher(unittest.TestCase):
52
52
  with patch("amd_debug.pstate.main") as mock_main:
53
53
  amd_debug.launch_tool("amd_pstate.py")
54
54
  mock_main.assert_called_once()
55
+
56
+ def test_launcher_amd_ttm(self):
57
+ """Test launching amd_ttm"""
58
+
59
+ with patch("amd_debug.ttm.main") as mock_main:
60
+ amd_debug.launch_tool("amd_ttm.py")
61
+ mock_main.assert_called_once()