amd-debug-tools 0.2.0__py3-none-any.whl → 0.2.1__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.

launcher.py ADDED
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/python3
2
+ # SPDX-License-Identifier: MIT
3
+ """
4
+ This module is a launcher for the AMD Debug Tools package. It is meant for
5
+ launching various tools within the package without installation.
6
+ """
7
+
8
+ import sys
9
+ import os
10
+
11
+ URL = "git://git.kernel.org/pub/scm/linux/kernel/git/superm1/amd-debug-tools.git"
12
+ try:
13
+ import amd_debug
14
+ from amd_debug.common import fatal_error
15
+ except ModuleNotFoundError:
16
+ sys.exit(
17
+ f"\033[91m{sys.argv[0]} can not be run standalone.\n"
18
+ f"\033[0m\033[94mCheck out the full branch from {URL}\033[0m"
19
+ )
20
+
21
+
22
+ def main():
23
+ """Main function to launch the appropriate tool based on the script name."""
24
+ try:
25
+ return amd_debug.launch_tool(os.path.basename(sys.argv[0]))
26
+ except ModuleNotFoundError as e:
27
+ fatal_error(
28
+ f"Missing dependency: {e}\n"
29
+ f"Run ./install_deps.py to install dependencies."
30
+ )
31
+ return False
32
+
33
+
34
+ if __name__ == "__main__":
35
+ sys.exit(main())
test_acpi.py ADDED
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/python3
2
+ # SPDX-License-Identifier: MIT
3
+
4
+ """
5
+ This module contains unit tests for the acpi functions in the amd-debug-tools package.
6
+ """
7
+ from unittest.mock import patch, mock_open, call
8
+
9
+ import logging
10
+ import unittest
11
+
12
+ from amd_debug.acpi import search_acpi_tables, AcpicaTracer, ACPI_METHOD
13
+
14
+
15
+ class TestAcpi(unittest.TestCase):
16
+ """Test acpi functions"""
17
+
18
+ @classmethod
19
+ def setUpClass(cls):
20
+ logging.basicConfig(filename="/dev/null", level=logging.DEBUG)
21
+
22
+ def test_search_acpi_tables(self):
23
+ """Test search_acpi_tables function"""
24
+ pattern = "test_pattern"
25
+ bad_pattern = "bad_pattern"
26
+ mock_listdir = ["ABA", "SSDT1", "DSDT2", "SSDT3"]
27
+ mock_file_content = b"test_pattern"
28
+
29
+ with patch("os.listdir", return_value=mock_listdir), patch(
30
+ "builtins.open", mock_open(read_data=mock_file_content)
31
+ ):
32
+ result = search_acpi_tables(pattern)
33
+ self.assertTrue(result)
34
+
35
+ with patch("os.listdir", return_value=mock_listdir), patch(
36
+ "builtins.open", mock_open(read_data=mock_file_content)
37
+ ):
38
+ result = search_acpi_tables(bad_pattern)
39
+ self.assertFalse(result)
40
+
41
+ with patch("os.listdir", return_value=["OTHER1", "OTHER2"]), patch(
42
+ "builtins.open", mock_open(read_data=b"no_match")
43
+ ):
44
+ result = search_acpi_tables(pattern)
45
+ self.assertFalse(result)
46
+
47
+ def test_acpica_tracer_missing_bios(self):
48
+ """Test AcpicaTracer class when ACPI tracing is not supported"""
49
+
50
+ mock_listdir = ["SSDT1", "DSDT2", "SSDT3"]
51
+
52
+ with patch("os.listdir", return_value=mock_listdir), patch(
53
+ "builtins.open", mock_open(read_data=b"foo")
54
+ ), patch("os.path.exists", return_value=True):
55
+
56
+ tracer = AcpicaTracer()
57
+ self.assertTrue(tracer.supported)
58
+
59
+ self.assertFalse(tracer.trace_bios())
60
+
61
+ def test_acpica_tracer(self):
62
+ """Test AcpicaTracer class"""
63
+
64
+ mock_listdir = ["SSDT1", "DSDT2", "SSDT3"]
65
+ mock_file_content = bytes(ACPI_METHOD, "utf-8")
66
+
67
+ with patch("os.listdir", return_value=mock_listdir), patch(
68
+ "builtins.open", mock_open(read_data=mock_file_content)
69
+ ), patch("os.path.exists", return_value=True):
70
+
71
+ tracer = AcpicaTracer()
72
+ self.assertTrue(tracer.supported)
73
+
74
+ self.assertTrue(tracer.trace_notify())
75
+ self.assertTrue(tracer.trace_bios())
76
+ self.assertTrue(tracer.disable())
77
+ self.assertTrue(tracer.restore())
78
+
79
+ def test_acpica_trace_no_acpi_debug(self):
80
+ """Test AcpicaTracer class when ACPI tracing is not supported"""
81
+ with patch("os.path.exists", return_value=False), patch(
82
+ "builtins.open", mock_open(read_data="foo")
83
+ ):
84
+ tracer = AcpicaTracer()
85
+ self.assertFalse(tracer.supported)
86
+
87
+ self.assertFalse(tracer.trace_notify())
88
+ self.assertFalse(tracer.trace_bios())
89
+ self.assertFalse(tracer.disable())
90
+ self.assertFalse(tracer.restore())
test_batteries.py ADDED
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/python3
2
+ # SPDX-License-Identifier: MIT
3
+
4
+ """
5
+ This module contains unit tests for the battery functions in the amd-debug-tools package.
6
+ """
7
+ import unittest
8
+ import logging
9
+ from unittest.mock import MagicMock, patch
10
+
11
+
12
+ from amd_debug.battery import Batteries
13
+
14
+
15
+ class TestBatteries(unittest.TestCase):
16
+ """Test battery functions"""
17
+
18
+ @classmethod
19
+ def setUpClass(cls):
20
+ logging.basicConfig(filename="/dev/null", level=logging.DEBUG)
21
+
22
+ @patch("amd_debug.battery.Context")
23
+ def setUp(self, mock_context):
24
+ """Set up a mock context for testing"""
25
+ self.mock_context = mock_context.return_value
26
+ self.batteries = Batteries()
27
+
28
+ def test_get_batteries(self):
29
+ """Test getting battery names"""
30
+ mock_device = MagicMock()
31
+ mock_device.device_path = "/devices/LNXSYSTM:00/device:00/PNP0C0A:00"
32
+ mock_device.properties = {"POWER_SUPPLY_NAME": "BAT0"}
33
+ self.mock_context.list_devices.return_value = [mock_device]
34
+
35
+ result = self.batteries.get_batteries()
36
+ self.assertEqual(result, ["BAT0"])
37
+
38
+ def test_get_energy_unit(self):
39
+ """Test getting energy unit for a battery"""
40
+ mock_device = MagicMock()
41
+ mock_device.device_path = "/devices/LNXSYSTM:00/device:00/PNP0C0A:00"
42
+ mock_device.properties = {
43
+ "POWER_SUPPLY_NAME": "BAT0",
44
+ "POWER_SUPPLY_ENERGY_NOW": "50000",
45
+ }
46
+ self.mock_context.list_devices.return_value = [mock_device]
47
+ result = self.batteries.get_energy_unit("BAT0")
48
+ self.assertEqual(result, "µWh")
49
+
50
+ def test_get_energy(self):
51
+ """Test getting current energy for a battery"""
52
+ mock_device = MagicMock()
53
+ mock_device.device_path = "/devices/LNXSYSTM:00/device:00/PNP0C0A:00"
54
+ mock_device.properties = {
55
+ "POWER_SUPPLY_NAME": "BAT0",
56
+ "POWER_SUPPLY_ENERGY_NOW": "50000",
57
+ }
58
+ self.mock_context.list_devices.return_value = [mock_device]
59
+ result = self.batteries.get_energy("BAT0")
60
+ self.assertEqual(result, "50000")
61
+
62
+ def test_get_energy_full(self):
63
+ """Test getting full energy for a battery"""
64
+ mock_device = MagicMock()
65
+ mock_device.device_path = "/devices/LNXSYSTM:00/device:00/PNP0C0A:00"
66
+ mock_device.properties = {
67
+ "POWER_SUPPLY_NAME": "BAT0",
68
+ "POWER_SUPPLY_ENERGY_FULL": "60000",
69
+ }
70
+ self.mock_context.list_devices.return_value = [mock_device]
71
+
72
+ result = self.batteries.get_energy_full("BAT0")
73
+ self.assertEqual(result, "60000")
74
+
75
+ def test_get_description_string(self):
76
+ """Test getting description string for a battery"""
77
+ mock_device = MagicMock()
78
+ mock_device.device_path = "/devices/LNXSYSTM:00/device:00/PNP0C0A:00"
79
+ mock_device.properties = {
80
+ "POWER_SUPPLY_NAME": "BAT0",
81
+ "POWER_SUPPLY_MANUFACTURER": "ACME",
82
+ "POWER_SUPPLY_MODEL_NAME": "SuperBattery",
83
+ "POWER_SUPPLY_ENERGY_FULL": "60000",
84
+ "POWER_SUPPLY_ENERGY_FULL_DESIGN": "80000",
85
+ }
86
+ self.mock_context.list_devices.return_value = [mock_device]
87
+
88
+ result = self.batteries.get_description_string("BAT0")
89
+ self.assertEqual(
90
+ result,
91
+ "Battery BAT0 (ACME SuperBattery) is operating at 75.00% of design",
92
+ )
test_bios.py ADDED
@@ -0,0 +1,250 @@
1
+ #!/usr/bin/python3
2
+ # SPDX-License-Identifier: MIT
3
+
4
+ """
5
+ This module contains unit tests for the bios tool in the amd-debug-tools package.
6
+ """
7
+ import argparse
8
+ import logging
9
+ import unittest
10
+ from unittest.mock import patch, MagicMock
11
+
12
+ from amd_debug.bios import AmdBios, parse_args, main
13
+
14
+
15
+ class TestAmdBios(unittest.TestCase):
16
+ """Test AmdBios class"""
17
+
18
+ @classmethod
19
+ def setUpClass(cls):
20
+ logging.basicConfig(filename="/dev/null", level=logging.DEBUG)
21
+
22
+ @patch("amd_debug.bios.get_kernel_log")
23
+ def test_init(self, mock_get_kernel_log):
24
+ """Test initialization of AmdBios class"""
25
+ mock_kernel_log = MagicMock()
26
+ mock_get_kernel_log.return_value = mock_kernel_log
27
+
28
+ app = AmdBios("test_input", debug=True)
29
+
30
+ self.assertEqual(app.kernel_log, mock_kernel_log)
31
+
32
+ @patch("amd_debug.bios.relaunch_sudo")
33
+ @patch("amd_debug.bios.minimum_kernel")
34
+ @patch("amd_debug.bios.AcpicaTracer")
35
+ @patch("amd_debug.bios.print_color")
36
+ def test_set_tracing_enable(
37
+ self, _mock_print, mock_acpica_tracer, mock_minimum_kernel, mock_relaunch_sudo
38
+ ):
39
+ """Test enabling tracing"""
40
+ mock_minimum_kernel.return_value = True
41
+ mock_tracer = MagicMock()
42
+ mock_acpica_tracer.return_value = mock_tracer
43
+ mock_tracer.trace_bios.return_value = True
44
+
45
+ app = AmdBios(None, debug=False)
46
+ result = app.set_tracing(enable=True)
47
+
48
+ mock_relaunch_sudo.assert_called_once()
49
+ mock_tracer.trace_bios.assert_called_once()
50
+ self.assertTrue(result)
51
+
52
+ @patch("amd_debug.bios.relaunch_sudo")
53
+ @patch("amd_debug.bios.minimum_kernel")
54
+ @patch("amd_debug.bios.AcpicaTracer")
55
+ @patch("amd_debug.bios.print_color")
56
+ def test_set_tracing_disable(
57
+ self, _mock_print, mock_acpica_tracer, mock_minimum_kernel, mock_relaunch_sudo
58
+ ):
59
+ """Test disabling tracing"""
60
+ mock_minimum_kernel.return_value = True
61
+ mock_tracer = MagicMock()
62
+ mock_acpica_tracer.return_value = mock_tracer
63
+ mock_tracer.disable.return_value = True
64
+
65
+ app = AmdBios(None, debug=False)
66
+ result = app.set_tracing(enable=False)
67
+
68
+ mock_relaunch_sudo.assert_called_once()
69
+ mock_tracer.disable.assert_called_once()
70
+ self.assertTrue(result)
71
+
72
+ @patch("amd_debug.bios.sscanf_bios_args")
73
+ @patch("amd_debug.bios.print_color")
74
+ def test_analyze_kernel_log_line(self, mock_print_color, mock_sscanf_bios_args):
75
+ """Test analyzing kernel log line"""
76
+ mock_sscanf_bios_args.return_value = "BIOS argument found"
77
+
78
+ app = AmdBios(None, debug=False)
79
+ app._analyze_kernel_log_line( # pylint: disable=protected-access
80
+ "test log line", priority="INFO"
81
+ )
82
+
83
+ mock_sscanf_bios_args.assert_called_once_with("test log line")
84
+ mock_print_color.assert_called_once_with("BIOS argument found", "🖴")
85
+
86
+ @patch("amd_debug.bios.sscanf_bios_args")
87
+ @patch("amd_debug.bios.print_color")
88
+ def test_analyze_kernel_log_line_no_bios_args(
89
+ self, mock_print_color, mock_sscanf_bios_args
90
+ ):
91
+ """Test analyzing kernel log line with no BIOS arguments"""
92
+ mock_sscanf_bios_args.return_value = None
93
+
94
+ app = AmdBios(None, debug=False)
95
+ app._analyze_kernel_log_line( # pylint: disable=protected-access
96
+ "[123.456] test log line", priority="INFO"
97
+ )
98
+
99
+ mock_sscanf_bios_args.assert_called_once_with("[123.456] test log line")
100
+ mock_print_color.assert_called_once_with("test log line", "INFO")
101
+
102
+ @patch("amd_debug.bios.get_kernel_log")
103
+ def test_run(self, _mock_run):
104
+ """Test run method"""
105
+ mock_kernel_log = MagicMock()
106
+ app = AmdBios(None, debug=False)
107
+ app.kernel_log = mock_kernel_log
108
+
109
+ app.run()
110
+
111
+ mock_kernel_log.process_callback.assert_called_once_with(
112
+ app._analyze_kernel_log_line # pylint: disable=protected-access
113
+ )
114
+
115
+ @patch("sys.argv", ["bios.py", "parse", "--input", "test.log", "--tool-debug"])
116
+ def test_parse_args_parse_command(self):
117
+ """Test parse_args with parse command"""
118
+
119
+ args = parse_args()
120
+ self.assertEqual(args.command, "parse")
121
+ self.assertEqual(args.input, "test.log")
122
+ self.assertTrue(args.tool_debug)
123
+
124
+ @patch("sys.argv", ["bios.py", "trace", "--enable", "--tool-debug"])
125
+ def test_parse_args_trace_enable(self):
126
+ """Test parse_args with trace enable command"""
127
+
128
+ args = parse_args()
129
+ self.assertEqual(args.command, "trace")
130
+ self.assertTrue(args.enable)
131
+ self.assertFalse(args.disable)
132
+ self.assertTrue(args.tool_debug)
133
+
134
+ @patch("sys.argv", ["bios.py", "trace", "--disable"])
135
+ def test_parse_args_trace_disable(self):
136
+ """Test parse_args with trace disable command"""
137
+
138
+ args = parse_args()
139
+ self.assertEqual(args.command, "trace")
140
+ self.assertFalse(args.enable)
141
+ self.assertTrue(args.disable)
142
+
143
+ @patch("sys.argv", ["bios.py", "version"])
144
+ def test_parse_args_version_command(self):
145
+ """Test parse_args with version command"""
146
+
147
+ args = parse_args()
148
+ self.assertEqual(args.command, "version")
149
+
150
+ @patch("sys.argv", ["bios.py"])
151
+ @patch("argparse.ArgumentParser.print_help")
152
+ @patch("sys.exit")
153
+ def test_parse_args_no_arguments(self, mock_exit, mock_print_help):
154
+ """Test parse_args with no arguments"""
155
+
156
+ parse_args()
157
+ mock_print_help.assert_called_once()
158
+ mock_exit.assert_called_once_with(1)
159
+
160
+ @patch("sys.argv", ["bios.py", "trace", "--enable", "--disable"])
161
+ @patch("sys.exit")
162
+ def test_parse_args_conflicting_trace_arguments(self, mock_exit):
163
+ """Test parse_args with conflicting trace arguments"""
164
+
165
+ parse_args()
166
+ mock_exit.assert_called_once_with("can't set both enable and disable")
167
+
168
+ @patch("sys.argv", ["bios.py", "trace"])
169
+ @patch("sys.exit")
170
+ def test_parse_args_missing_trace_arguments(self, mock_exit):
171
+ """Test parse_args with missing trace arguments"""
172
+
173
+ parse_args()
174
+ mock_exit.assert_called_once_with("must set either enable or disable")
175
+
176
+ @patch("amd_debug.bios.AmdBios")
177
+ @patch("amd_debug.bios.parse_args")
178
+ @patch("amd_debug.bios.version")
179
+ @patch("amd_debug.bios.show_log_info")
180
+ def test_main_trace_command(
181
+ self, mock_show_log_info, _mock_version, mock_parse_args, mock_amd_bios
182
+ ):
183
+ """Test main function with trace command"""
184
+ mock_app = MagicMock()
185
+ mock_amd_bios.return_value = mock_app
186
+ mock_parse_args.return_value = argparse.Namespace(
187
+ command="trace", enable=True, disable=False, tool_debug=True
188
+ )
189
+ mock_app.set_tracing.return_value = True
190
+
191
+ result = main()
192
+
193
+ mock_parse_args.assert_called_once()
194
+ mock_amd_bios.assert_called_once_with(None, True)
195
+ mock_app.set_tracing.assert_called_once_with(True)
196
+ mock_show_log_info.assert_called_once()
197
+ self.assertTrue(result)
198
+
199
+ @patch("amd_debug.bios.AmdBios")
200
+ @patch("amd_debug.bios.parse_args")
201
+ @patch("amd_debug.bios.version")
202
+ @patch("amd_debug.bios.show_log_info")
203
+ def test_main_parse_command(
204
+ self, mock_show_log_info, _mock_version, mock_parse_args, mock_amd_bios
205
+ ):
206
+ """Test main function with parse command"""
207
+ mock_app = MagicMock()
208
+ mock_amd_bios.return_value = mock_app
209
+ mock_parse_args.return_value = argparse.Namespace(
210
+ command="parse", input="test.log", tool_debug=True
211
+ )
212
+ mock_app.run.return_value = True
213
+
214
+ result = main()
215
+
216
+ mock_parse_args.assert_called_once()
217
+ mock_amd_bios.assert_called_once_with("test.log", True)
218
+ mock_app.run.assert_called_once()
219
+ mock_show_log_info.assert_called_once()
220
+ self.assertTrue(result)
221
+
222
+ @patch("amd_debug.bios.parse_args")
223
+ @patch("amd_debug.bios.version")
224
+ @patch("amd_debug.bios.show_log_info")
225
+ @patch("amd_debug.bios.print")
226
+ def test_main_version_command(
227
+ self, _mock_print, mock_show_log_info, mock_version, mock_parse_args
228
+ ):
229
+ """Test main function with version command"""
230
+ mock_parse_args.return_value = argparse.Namespace(command="version")
231
+ mock_version.return_value = "1.0.0"
232
+
233
+ result = main()
234
+
235
+ mock_parse_args.assert_called_once()
236
+ mock_version.assert_called_once()
237
+ mock_show_log_info.assert_called_once()
238
+ self.assertEqual(result, False)
239
+
240
+ @patch("amd_debug.bios.parse_args")
241
+ @patch("amd_debug.bios.show_log_info")
242
+ def test_main_invalid_command(self, mock_show_log_info, mock_parse_args):
243
+ """Test main function with an invalid command"""
244
+ mock_parse_args.return_value = argparse.Namespace(command="invalid")
245
+
246
+ result = main()
247
+
248
+ mock_parse_args.assert_called_once()
249
+ mock_show_log_info.assert_called_once()
250
+ self.assertFalse(result)