amd-debug-tools 0.2.0__py3-none-any.whl → 0.2.2__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/common.py +48 -0
- amd_debug/display.py +34 -0
- amd_debug/failures.py +13 -1
- amd_debug/installer.py +69 -5
- amd_debug/prerequisites.py +157 -56
- amd_debug/s2idle.py +46 -17
- amd_debug/sleep_report.py +2 -2
- amd_debug/templates/md +0 -7
- amd_debug/validator.py +3 -5
- {amd_debug_tools-0.2.0.dist-info → amd_debug_tools-0.2.2.dist-info}/METADATA +4 -3
- amd_debug_tools-0.2.2.dist-info/RECORD +45 -0
- {amd_debug_tools-0.2.0.dist-info → amd_debug_tools-0.2.2.dist-info}/WHEEL +1 -1
- amd_debug_tools-0.2.2.dist-info/top_level.txt +18 -0
- launcher.py +35 -0
- test_acpi.py +90 -0
- test_batteries.py +92 -0
- test_bios.py +250 -0
- test_common.py +444 -0
- test_database.py +284 -0
- test_display.py +143 -0
- test_failures.py +146 -0
- test_installer.py +281 -0
- test_kernel.py +205 -0
- test_launcher.py +53 -0
- test_prerequisites.py +1935 -0
- test_pstate.py +164 -0
- test_s2idle.py +868 -0
- test_sleep_report.py +167 -0
- test_validator.py +723 -0
- test_wake.py +216 -0
- amd_debug_tools-0.2.0.dist-info/RECORD +0 -27
- amd_debug_tools-0.2.0.dist-info/top_level.txt +0 -1
- {amd_debug_tools-0.2.0.dist-info → amd_debug_tools-0.2.2.dist-info}/entry_points.txt +0 -0
- {amd_debug_tools-0.2.0.dist-info → amd_debug_tools-0.2.2.dist-info}/licenses/LICENSE +0 -0
test_database.py
ADDED
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
#!/usr/bin/python3
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
This module contains unit tests for the datbase functions in the amd-debug-tools package.
|
|
6
|
+
"""
|
|
7
|
+
import unittest
|
|
8
|
+
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
|
|
11
|
+
from amd_debug.database import SleepDatabase
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class TestSleepDatabase(unittest.TestCase):
|
|
15
|
+
"""Test SleepDatabase class"""
|
|
16
|
+
|
|
17
|
+
def setUp(self):
|
|
18
|
+
"""Set up mocks and an in-memory database for testing"""
|
|
19
|
+
# Initialize SleepDatabase after mocks are set up
|
|
20
|
+
self.db = SleepDatabase(dbf=":memory:")
|
|
21
|
+
|
|
22
|
+
def test_start_cycle(self):
|
|
23
|
+
"""Test starting a new sleep cycle"""
|
|
24
|
+
timestamp = datetime.now()
|
|
25
|
+
self.db.start_cycle(timestamp)
|
|
26
|
+
self.assertEqual(self.db.last_suspend, timestamp)
|
|
27
|
+
self.assertEqual(self.db.cycle_data_cnt, 0)
|
|
28
|
+
self.assertEqual(self.db.debug_cnt, 0)
|
|
29
|
+
|
|
30
|
+
def test_record_debug(self):
|
|
31
|
+
"""Test recording a debug message"""
|
|
32
|
+
timestamp = datetime.now()
|
|
33
|
+
self.db.start_cycle(timestamp)
|
|
34
|
+
self.db.record_debug("Test debug message", level=5)
|
|
35
|
+
cur = self.db.db.cursor()
|
|
36
|
+
cur.execute(
|
|
37
|
+
"SELECT message, priority FROM debug WHERE t0=?",
|
|
38
|
+
(int(timestamp.strftime("%Y%m%d%H%M%S")),),
|
|
39
|
+
)
|
|
40
|
+
result = cur.fetchone()
|
|
41
|
+
self.assertEqual(result, ("Test debug message", 5))
|
|
42
|
+
|
|
43
|
+
def test_record_battery_energy(self):
|
|
44
|
+
"""Test recording battery energy"""
|
|
45
|
+
timestamp = datetime.now()
|
|
46
|
+
self.db.start_cycle(timestamp)
|
|
47
|
+
self.db.record_battery_energy("Battery1", 50, 100, "mWh")
|
|
48
|
+
cur = self.db.db.cursor()
|
|
49
|
+
cur.execute(
|
|
50
|
+
"SELECT name, b0, b1, full, unit FROM battery WHERE t0=?",
|
|
51
|
+
(int(timestamp.strftime("%Y%m%d%H%M%S")),),
|
|
52
|
+
)
|
|
53
|
+
result = cur.fetchone()
|
|
54
|
+
self.assertEqual(result, ("Battery1", 50, None, 100, "mWh"))
|
|
55
|
+
|
|
56
|
+
def test_record_cycle_data(self):
|
|
57
|
+
"""Test recording cycle data"""
|
|
58
|
+
timestamp = datetime.now()
|
|
59
|
+
self.db.start_cycle(timestamp)
|
|
60
|
+
self.db.record_cycle_data("Test cycle data", "symbol1")
|
|
61
|
+
cur = self.db.db.cursor()
|
|
62
|
+
cur.execute(
|
|
63
|
+
"SELECT message, symbol FROM cycle_data WHERE t0=?",
|
|
64
|
+
(int(timestamp.strftime("%Y%m%d%H%M%S")),),
|
|
65
|
+
)
|
|
66
|
+
result = cur.fetchone()
|
|
67
|
+
self.assertEqual(result, ("Test cycle data", "symbol1"))
|
|
68
|
+
|
|
69
|
+
def test_record_cycle(self):
|
|
70
|
+
"""Test recording a sleep cycle"""
|
|
71
|
+
timestamp = datetime.now()
|
|
72
|
+
self.db.start_cycle(timestamp)
|
|
73
|
+
self.db.record_cycle(
|
|
74
|
+
requested_duration=100,
|
|
75
|
+
active_gpios="GPIO1",
|
|
76
|
+
wakeup_irqs="IRQ1",
|
|
77
|
+
kernel_duration=1.5,
|
|
78
|
+
hw_sleep_duration=2.5,
|
|
79
|
+
)
|
|
80
|
+
cur = self.db.db.cursor()
|
|
81
|
+
cur.execute(
|
|
82
|
+
"SELECT requested, gpio, wake_irq, kernel, hw FROM cycle WHERE t0=?",
|
|
83
|
+
(int(timestamp.strftime("%Y%m%d%H%M%S")),),
|
|
84
|
+
)
|
|
85
|
+
result = cur.fetchone()
|
|
86
|
+
self.assertEqual(result, (100, "GPIO1", "IRQ1", 1.5, 2.5))
|
|
87
|
+
|
|
88
|
+
def test_report_debug(self):
|
|
89
|
+
"""Test reporting debug messages"""
|
|
90
|
+
timestamp = datetime.now()
|
|
91
|
+
self.db.start_cycle(timestamp)
|
|
92
|
+
self.db.record_debug("Test debug message", level=5)
|
|
93
|
+
result = self.db.report_debug(timestamp)
|
|
94
|
+
self.assertEqual(result, [("Test debug message", 5)])
|
|
95
|
+
|
|
96
|
+
def test_report_battery(self):
|
|
97
|
+
"""Test reporting battery data"""
|
|
98
|
+
timestamp = datetime.now()
|
|
99
|
+
self.db.start_cycle(timestamp)
|
|
100
|
+
self.db.record_battery_energy("Battery1", 50, 100, "mWh")
|
|
101
|
+
result = self.db.report_battery(timestamp)
|
|
102
|
+
self.assertEqual(
|
|
103
|
+
result,
|
|
104
|
+
[
|
|
105
|
+
(
|
|
106
|
+
int(timestamp.strftime("%Y%m%d%H%M%S")),
|
|
107
|
+
"Battery1",
|
|
108
|
+
50,
|
|
109
|
+
None,
|
|
110
|
+
100,
|
|
111
|
+
"mWh",
|
|
112
|
+
)
|
|
113
|
+
],
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
def test_record_prereq(self):
|
|
117
|
+
"""Test recording a prereq message"""
|
|
118
|
+
timestamp = datetime.now()
|
|
119
|
+
self.db.start_cycle(timestamp)
|
|
120
|
+
self.db.record_prereq("Test prereq message", "symbol1")
|
|
121
|
+
cur = self.db.db.cursor()
|
|
122
|
+
cur.execute(
|
|
123
|
+
"SELECT message, symbol FROM prereq_data WHERE t0=?",
|
|
124
|
+
(int(timestamp.strftime("%Y%m%d%H%M%S")),),
|
|
125
|
+
)
|
|
126
|
+
result = cur.fetchone()
|
|
127
|
+
self.assertEqual(result, ("Test prereq message", "symbol1"))
|
|
128
|
+
|
|
129
|
+
def test_report_prereq(self):
|
|
130
|
+
"""Test reporting prereq messages"""
|
|
131
|
+
timestamp = datetime.now()
|
|
132
|
+
self.db.start_cycle(timestamp)
|
|
133
|
+
self.db.record_prereq("Test prereq message 1", "symbol1")
|
|
134
|
+
self.db.record_prereq("Test prereq message 2", "symbol2")
|
|
135
|
+
result = self.db.report_prereq(timestamp)
|
|
136
|
+
self.assertEqual(
|
|
137
|
+
result,
|
|
138
|
+
[
|
|
139
|
+
(
|
|
140
|
+
int(timestamp.strftime("%Y%m%d%H%M%S")),
|
|
141
|
+
0,
|
|
142
|
+
"Test prereq message 1",
|
|
143
|
+
"symbol1",
|
|
144
|
+
),
|
|
145
|
+
(
|
|
146
|
+
int(timestamp.strftime("%Y%m%d%H%M%S")),
|
|
147
|
+
1,
|
|
148
|
+
"Test prereq message 2",
|
|
149
|
+
"symbol2",
|
|
150
|
+
),
|
|
151
|
+
],
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
def test_report_prereq_no_data(self):
|
|
155
|
+
"""Test reporting prereq messages when no data exists"""
|
|
156
|
+
timestamp = datetime.now()
|
|
157
|
+
self.db.start_cycle(timestamp)
|
|
158
|
+
result = self.db.report_prereq(timestamp)
|
|
159
|
+
self.assertEqual(result, [])
|
|
160
|
+
|
|
161
|
+
def test_report_prereq_none_timestamp(self):
|
|
162
|
+
"""Test reporting prereq messages with None timestamp"""
|
|
163
|
+
result = self.db.report_prereq(None)
|
|
164
|
+
self.assertEqual(result, [])
|
|
165
|
+
|
|
166
|
+
def test_report_cycle(self):
|
|
167
|
+
"""Test reporting a cycle from the database"""
|
|
168
|
+
timestamp = datetime.now()
|
|
169
|
+
self.db.start_cycle(timestamp)
|
|
170
|
+
self.db.record_cycle(
|
|
171
|
+
requested_duration=100,
|
|
172
|
+
active_gpios="GPIO1",
|
|
173
|
+
wakeup_irqs="IRQ1",
|
|
174
|
+
kernel_duration=1.5,
|
|
175
|
+
hw_sleep_duration=2.5,
|
|
176
|
+
)
|
|
177
|
+
result = self.db.report_cycle(timestamp)
|
|
178
|
+
self.assertEqual(
|
|
179
|
+
result,
|
|
180
|
+
[
|
|
181
|
+
(
|
|
182
|
+
int(timestamp.strftime("%Y%m%d%H%M%S")),
|
|
183
|
+
int(datetime.now().strftime("%Y%m%d%H%M%S")),
|
|
184
|
+
100,
|
|
185
|
+
"GPIO1",
|
|
186
|
+
"IRQ1",
|
|
187
|
+
1.5,
|
|
188
|
+
2.5,
|
|
189
|
+
)
|
|
190
|
+
],
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
def test_report_cycle_no_data(self):
|
|
194
|
+
"""Test reporting a cycle when no data exists"""
|
|
195
|
+
timestamp = datetime.now()
|
|
196
|
+
self.db.start_cycle(timestamp)
|
|
197
|
+
result = self.db.report_cycle(timestamp)
|
|
198
|
+
self.assertEqual(result, [])
|
|
199
|
+
|
|
200
|
+
def test_report_cycle_none_timestamp(self):
|
|
201
|
+
"""Test reporting a cycle with None timestamp"""
|
|
202
|
+
timestamp = datetime.now()
|
|
203
|
+
self.db.start_cycle(timestamp)
|
|
204
|
+
self.db.record_cycle(
|
|
205
|
+
requested_duration=100,
|
|
206
|
+
active_gpios="GPIO1",
|
|
207
|
+
wakeup_irqs="IRQ1",
|
|
208
|
+
kernel_duration=1.5,
|
|
209
|
+
hw_sleep_duration=2.5,
|
|
210
|
+
)
|
|
211
|
+
result = self.db.report_cycle(None)
|
|
212
|
+
self.assertEqual(
|
|
213
|
+
result,
|
|
214
|
+
[
|
|
215
|
+
(
|
|
216
|
+
int(timestamp.strftime("%Y%m%d%H%M%S")),
|
|
217
|
+
int(datetime.now().strftime("%Y%m%d%H%M%S")),
|
|
218
|
+
100,
|
|
219
|
+
"GPIO1",
|
|
220
|
+
"IRQ1",
|
|
221
|
+
1.5,
|
|
222
|
+
2.5,
|
|
223
|
+
)
|
|
224
|
+
],
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
def test_get_last_cycle(self):
|
|
228
|
+
"""Test getting the last cycle from the database"""
|
|
229
|
+
timestamp = datetime.now()
|
|
230
|
+
self.db.start_cycle(timestamp)
|
|
231
|
+
self.db.record_cycle(
|
|
232
|
+
requested_duration=100,
|
|
233
|
+
active_gpios="GPIO1",
|
|
234
|
+
wakeup_irqs="IRQ1",
|
|
235
|
+
kernel_duration=1.5,
|
|
236
|
+
hw_sleep_duration=2.5,
|
|
237
|
+
)
|
|
238
|
+
result = self.db.get_last_cycle()
|
|
239
|
+
self.assertEqual(result, (int(timestamp.strftime("%Y%m%d%H%M%S")),))
|
|
240
|
+
|
|
241
|
+
def test_get_last_cycle_no_data(self):
|
|
242
|
+
"""Test getting the last cycle when no data exists"""
|
|
243
|
+
result = self.db.get_last_cycle()
|
|
244
|
+
self.assertIsNone(result)
|
|
245
|
+
|
|
246
|
+
def test_get_last_prereq_ts(self):
|
|
247
|
+
"""Test getting the last prereq timestamp from the database"""
|
|
248
|
+
timestamp = datetime.now()
|
|
249
|
+
self.db.start_cycle(timestamp)
|
|
250
|
+
self.db.record_prereq("Test prereq message 1", "symbol1")
|
|
251
|
+
self.db.record_prereq("Test prereq message 2", "symbol2")
|
|
252
|
+
result = self.db.get_last_prereq_ts()
|
|
253
|
+
self.assertEqual(result, int(timestamp.strftime("%Y%m%d%H%M%S")))
|
|
254
|
+
|
|
255
|
+
def test_get_last_prereq_ts_no_data(self):
|
|
256
|
+
"""Test getting the last prereq timestamp when no data exists"""
|
|
257
|
+
result = self.db.get_last_prereq_ts()
|
|
258
|
+
self.assertIsNone(result)
|
|
259
|
+
|
|
260
|
+
def test_report_cycle_data(self):
|
|
261
|
+
"""Test reporting cycle data"""
|
|
262
|
+
timestamp = datetime.now()
|
|
263
|
+
self.db.start_cycle(timestamp)
|
|
264
|
+
self.db.record_cycle_data("Test cycle data 1", "symbol1")
|
|
265
|
+
self.db.record_cycle_data("Test cycle data 2", "symbol2")
|
|
266
|
+
result = self.db.report_cycle_data(timestamp)
|
|
267
|
+
expected_result = "symbol1 Test cycle data 1\nsymbol2 Test cycle data 2\n"
|
|
268
|
+
self.assertEqual(result, expected_result)
|
|
269
|
+
|
|
270
|
+
def test_report_cycle_data_no_data(self):
|
|
271
|
+
"""Test reporting cycle data when no data exists"""
|
|
272
|
+
timestamp = datetime.now()
|
|
273
|
+
self.db.start_cycle(timestamp)
|
|
274
|
+
result = self.db.report_cycle_data(timestamp)
|
|
275
|
+
self.assertEqual(result, "")
|
|
276
|
+
|
|
277
|
+
def test_report_cycle_data_none_timestamp(self):
|
|
278
|
+
"""Test reporting cycle data with None timestamp"""
|
|
279
|
+
timestamp = datetime.now()
|
|
280
|
+
self.db.start_cycle(timestamp)
|
|
281
|
+
self.db.record_cycle_data("Test cycle data 1", "symbol1")
|
|
282
|
+
result = self.db.report_cycle_data(None)
|
|
283
|
+
expected_result = "symbol1 Test cycle data 1\n"
|
|
284
|
+
self.assertEqual(result, expected_result)
|
test_display.py
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
#!/usr/bin/python3
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
This module contains unit tests for the display functions in the amd-debug-tools package.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
import unittest
|
|
10
|
+
from unittest.mock import patch, MagicMock
|
|
11
|
+
from amd_debug.display import Display
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class TestDisplay(unittest.TestCase):
|
|
15
|
+
"""Unit tests for the Display class in amd-debug-tools"""
|
|
16
|
+
|
|
17
|
+
@classmethod
|
|
18
|
+
def setUpClass(cls):
|
|
19
|
+
logging.basicConfig(filename="/dev/null", level=logging.DEBUG)
|
|
20
|
+
|
|
21
|
+
@patch("amd_debug.display.Context")
|
|
22
|
+
@patch("amd_debug.display.read_file")
|
|
23
|
+
@patch("os.path.exists")
|
|
24
|
+
def test_display_initialization(self, mock_exists, mock_read_file, mock_context):
|
|
25
|
+
"""Test the Display class initialization and EDID retrieval"""
|
|
26
|
+
# Mock the pyudev Context and its list_devices method
|
|
27
|
+
mock_device = MagicMock()
|
|
28
|
+
mock_device.device_path = "/devices/card0"
|
|
29
|
+
mock_device.sys_path = "/sys/devices/card0"
|
|
30
|
+
mock_device.sys_name = "card0"
|
|
31
|
+
mock_context.return_value.list_devices.return_value = [mock_device]
|
|
32
|
+
|
|
33
|
+
# Mock os.path.exists and read_file behavior
|
|
34
|
+
mock_exists.side_effect = lambda path: "status" in path or "enabled" in path
|
|
35
|
+
mock_read_file.side_effect = lambda path: (
|
|
36
|
+
"connected" if "status" in path else "enabled"
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
# Initialize the Display class
|
|
40
|
+
display = Display()
|
|
41
|
+
|
|
42
|
+
# Verify the EDID paths are correctly set
|
|
43
|
+
expected_edid = {"card0": "/sys/devices/card0/edid"}
|
|
44
|
+
self.assertEqual(display.get_edid(), expected_edid)
|
|
45
|
+
mock_context.assert_called_once()
|
|
46
|
+
|
|
47
|
+
@patch("amd_debug.display.Context")
|
|
48
|
+
def test_no_connected_displays(self, mock_context):
|
|
49
|
+
"""Test the Display class when no connected displays are found"""
|
|
50
|
+
# Mock the pyudev Context to return no devices
|
|
51
|
+
mock_context.return_value.list_devices.return_value = []
|
|
52
|
+
|
|
53
|
+
# Initialize the Display class
|
|
54
|
+
display = Display()
|
|
55
|
+
|
|
56
|
+
# Verify the EDID dictionary is empty
|
|
57
|
+
self.assertEqual(display.get_edid(), {})
|
|
58
|
+
|
|
59
|
+
@patch("amd_debug.display.Context")
|
|
60
|
+
def test_device_without_card(self, mock_context):
|
|
61
|
+
"""Test the Display class with a device that does not have 'card' in the name"""
|
|
62
|
+
# Mock the pyudev Context to return a device without 'card' in the name
|
|
63
|
+
mock_device = MagicMock()
|
|
64
|
+
mock_device.device_path = "/devices/other_device"
|
|
65
|
+
mock_device.sys_path = "/sys/devices/other_device"
|
|
66
|
+
mock_device.sys_name = "other_device"
|
|
67
|
+
mock_context.return_value.list_devices.return_value = [mock_device]
|
|
68
|
+
|
|
69
|
+
# Initialize the Display class
|
|
70
|
+
display = Display()
|
|
71
|
+
|
|
72
|
+
# Verify the EDID dictionary is empty
|
|
73
|
+
self.assertEqual(display.get_edid(), {})
|
|
74
|
+
|
|
75
|
+
@patch("amd_debug.display.Context")
|
|
76
|
+
@patch("amd_debug.display.read_file")
|
|
77
|
+
@patch("os.path.exists")
|
|
78
|
+
def test_device_not_enabled(self, mock_exists, mock_read_file, mock_context):
|
|
79
|
+
"""Test the Display class with a device that is not enabled"""
|
|
80
|
+
# Mock the pyudev Context to return a device that is not enabled
|
|
81
|
+
mock_device = MagicMock()
|
|
82
|
+
mock_device.device_path = "/devices/card0"
|
|
83
|
+
mock_device.sys_path = "/sys/devices/card0"
|
|
84
|
+
mock_device.sys_name = "card0"
|
|
85
|
+
mock_context.return_value.list_devices.return_value = [mock_device]
|
|
86
|
+
|
|
87
|
+
# Mock os.path.exists and read_file behavior
|
|
88
|
+
mock_exists.side_effect = lambda path: "status" in path or "enabled" in path
|
|
89
|
+
mock_read_file.side_effect = lambda path: (
|
|
90
|
+
"connected" if "status" in path else "disabled"
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
# Initialize the Display class
|
|
94
|
+
display = Display()
|
|
95
|
+
|
|
96
|
+
# Verify the EDID dictionary is empty
|
|
97
|
+
self.assertEqual(display.get_edid(), {})
|
|
98
|
+
|
|
99
|
+
@patch("amd_debug.display.Context")
|
|
100
|
+
@patch("amd_debug.display.read_file")
|
|
101
|
+
@patch("os.path.exists")
|
|
102
|
+
def test_missing_status_file(self, mock_exists, mock_read_file, mock_context):
|
|
103
|
+
"""Test the Display class when the status file is missing"""
|
|
104
|
+
# Mock the pyudev Context to return a device with a missing status file
|
|
105
|
+
mock_device = MagicMock()
|
|
106
|
+
mock_device.device_path = "/devices/card0"
|
|
107
|
+
mock_device.sys_path = "/sys/devices/card0"
|
|
108
|
+
mock_device.sys_name = "card0"
|
|
109
|
+
mock_context.return_value.list_devices.return_value = [mock_device]
|
|
110
|
+
|
|
111
|
+
# Mock os.path.exists to return False for the status file
|
|
112
|
+
mock_exists.side_effect = lambda path: "enabled" in path
|
|
113
|
+
mock_read_file.side_effect = lambda path: "enabled" if "enabled" in path else ""
|
|
114
|
+
|
|
115
|
+
# Initialize the Display class
|
|
116
|
+
display = Display()
|
|
117
|
+
|
|
118
|
+
# Verify the EDID dictionary is empty
|
|
119
|
+
self.assertEqual(display.get_edid(), {})
|
|
120
|
+
|
|
121
|
+
@patch("amd_debug.display.Context")
|
|
122
|
+
@patch("amd_debug.display.read_file")
|
|
123
|
+
@patch("os.path.exists")
|
|
124
|
+
def test_status_not_connected(self, mock_exists, mock_read_file, mock_context):
|
|
125
|
+
"""Test the Display class when the status file does not indicate connected"""
|
|
126
|
+
# Mock the pyudev Context to return a device with a status file that does not indicate connected
|
|
127
|
+
mock_device = MagicMock()
|
|
128
|
+
mock_device.device_path = "/devices/card0"
|
|
129
|
+
mock_device.sys_path = "/sys/devices/card0"
|
|
130
|
+
mock_device.sys_name = "card0"
|
|
131
|
+
mock_context.return_value.list_devices.return_value = [mock_device]
|
|
132
|
+
|
|
133
|
+
# Mock os.path.exists and read_file behavior
|
|
134
|
+
mock_exists.side_effect = lambda path: "status" in path or "enabled" in path
|
|
135
|
+
mock_read_file.side_effect = lambda path: (
|
|
136
|
+
"not_connected" if "status" in path else "enabled"
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
# Initialize the Display class
|
|
140
|
+
display = Display()
|
|
141
|
+
|
|
142
|
+
# Verify the EDID dictionary is empty
|
|
143
|
+
self.assertEqual(display.get_edid(), {})
|
test_failures.py
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
#!/usr/bin/python3
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
This module contains unit tests for the failure functions in the amd-debug-tools package.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from unittest.mock import patch, call
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
import unittest
|
|
12
|
+
import os
|
|
13
|
+
|
|
14
|
+
import amd_debug.failures
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class TestFailures(unittest.TestCase):
|
|
18
|
+
"""Test failure functions"""
|
|
19
|
+
|
|
20
|
+
@classmethod
|
|
21
|
+
def setUpClass(cls):
|
|
22
|
+
logging.basicConfig(filename="/dev/null", level=logging.DEBUG)
|
|
23
|
+
|
|
24
|
+
@patch("builtins.print")
|
|
25
|
+
def test_failures(self, mocked_print):
|
|
26
|
+
"""Test failure functions"""
|
|
27
|
+
|
|
28
|
+
cls = amd_debug.failures.RtcAlarmWrong()
|
|
29
|
+
self.assertEqual(
|
|
30
|
+
cls.get_description(), "rtc_cmos is not configured to use ACPI alarm"
|
|
31
|
+
)
|
|
32
|
+
self.assertEqual(
|
|
33
|
+
str(cls),
|
|
34
|
+
"Some problems can occur during wakeup cycles if the HPET RTC "
|
|
35
|
+
"emulation is used to wake systems. This can manifest in unexpected "
|
|
36
|
+
"wakeups or high power consumption.For more information on this failure "
|
|
37
|
+
"see:https://github.com/systemd/systemd/issues/24279",
|
|
38
|
+
)
|
|
39
|
+
cls = amd_debug.failures.MissingAmdgpu()
|
|
40
|
+
self.assertEqual(cls.get_description(), "AMDGPU driver is missing")
|
|
41
|
+
cls = amd_debug.failures.MissingAmdgpuFirmware(["foo", "bar"])
|
|
42
|
+
self.assertEqual(cls.get_description(), "AMDGPU firmware is missing")
|
|
43
|
+
cls = amd_debug.failures.AmdgpuPpFeatureMask()
|
|
44
|
+
self.assertEqual(cls.get_description(), "AMDGPU ppfeaturemask changed")
|
|
45
|
+
cls = amd_debug.failures.MissingAmdPmc()
|
|
46
|
+
self.assertEqual(cls.get_description(), "AMD-PMC driver is missing")
|
|
47
|
+
cls = amd_debug.failures.MissingThunderbolt()
|
|
48
|
+
self.assertEqual(cls.get_description(), "thunderbolt driver is missing")
|
|
49
|
+
cls = amd_debug.failures.MissingXhciHcd()
|
|
50
|
+
self.assertEqual(cls.get_description(), "xhci_hcd driver is missing")
|
|
51
|
+
cls = amd_debug.failures.MissingDriver("4")
|
|
52
|
+
self.assertEqual(cls.get_description(), "4 driver is missing")
|
|
53
|
+
cls = amd_debug.failures.AcpiBiosError("5")
|
|
54
|
+
self.assertEqual(cls.get_description(), "ACPI BIOS Errors detected")
|
|
55
|
+
cls = amd_debug.failures.UnsupportedModel()
|
|
56
|
+
self.assertEqual(cls.get_description(), "Unsupported CPU model")
|
|
57
|
+
cls = amd_debug.failures.UserNvmeConfiguration()
|
|
58
|
+
self.assertEqual(cls.get_description(), "NVME ACPI support is disabled")
|
|
59
|
+
cls = amd_debug.failures.AcpiNvmeStorageD3Enable("foo", 2)
|
|
60
|
+
self.assertEqual(cls.get_description(), "foo missing ACPI attributes")
|
|
61
|
+
cls = amd_debug.failures.DevSlpHostIssue()
|
|
62
|
+
self.assertEqual(
|
|
63
|
+
cls.get_description(), "AHCI controller doesn't support DevSlp"
|
|
64
|
+
)
|
|
65
|
+
cls = amd_debug.failures.DevSlpDiskIssue()
|
|
66
|
+
self.assertEqual(cls.get_description(), "SATA disk doesn't support DevSlp")
|
|
67
|
+
cls = amd_debug.failures.SleepModeWrong()
|
|
68
|
+
self.assertEqual(
|
|
69
|
+
cls.get_description(),
|
|
70
|
+
"The system hasn't been configured for Modern Standby in BIOS setup",
|
|
71
|
+
)
|
|
72
|
+
cls = amd_debug.failures.DeepSleep()
|
|
73
|
+
self.assertEqual(
|
|
74
|
+
cls.get_description(),
|
|
75
|
+
"The kernel command line is asserting the system to use deep sleep",
|
|
76
|
+
)
|
|
77
|
+
cls = amd_debug.failures.FadtWrong()
|
|
78
|
+
self.assertEqual(
|
|
79
|
+
cls.get_description(),
|
|
80
|
+
"The kernel didn't emit a message that low power idle was supported",
|
|
81
|
+
)
|
|
82
|
+
cls = amd_debug.failures.Irq1Workaround()
|
|
83
|
+
self.assertEqual(
|
|
84
|
+
cls.get_description(),
|
|
85
|
+
"The wakeup showed an IRQ1 wakeup source, which might be a platform firmware bug",
|
|
86
|
+
)
|
|
87
|
+
cls = amd_debug.failures.KernelRingBufferWrapped()
|
|
88
|
+
self.assertEqual(cls.get_description(), "Kernel ringbuffer has wrapped")
|
|
89
|
+
cls = amd_debug.failures.AmdHsmpBug()
|
|
90
|
+
self.assertEqual(cls.get_description(), "amd-hsmp built in to kernel")
|
|
91
|
+
cls = amd_debug.failures.WCN6855Bug()
|
|
92
|
+
self.assertEqual(
|
|
93
|
+
cls.get_description(),
|
|
94
|
+
"The firmware loaded for the WCN6855 causes spurious wakeups",
|
|
95
|
+
)
|
|
96
|
+
cls = amd_debug.failures.I2CHidBug("touchpad", "block")
|
|
97
|
+
self.assertEqual(
|
|
98
|
+
cls.get_description(),
|
|
99
|
+
"The touchpad device has been reported to cause high power consumption and spurious wakeups",
|
|
100
|
+
)
|
|
101
|
+
cls = amd_debug.failures.SpuriousWakeup(1, 0)
|
|
102
|
+
self.assertEqual(
|
|
103
|
+
cls.get_description(), "Userspace wasn't asleep at least 0:00:01"
|
|
104
|
+
)
|
|
105
|
+
cls = amd_debug.failures.LowHardwareSleepResidency(5, 30)
|
|
106
|
+
self.assertEqual(
|
|
107
|
+
cls.get_description(), "System had low hardware sleep residency"
|
|
108
|
+
)
|
|
109
|
+
cls = amd_debug.failures.MSRFailure()
|
|
110
|
+
self.assertEqual(cls.get_description(), "PC6 or CC6 state disabled")
|
|
111
|
+
cls = amd_debug.failures.TaintedKernel()
|
|
112
|
+
self.assertEqual(cls.get_description(), "Kernel is tainted")
|
|
113
|
+
cls = amd_debug.failures.DMArNotEnabled()
|
|
114
|
+
self.assertEqual(cls.get_description(), "Pre-boot DMA protection disabled")
|
|
115
|
+
cls = amd_debug.failures.MissingIommuACPI("foo")
|
|
116
|
+
self.assertEqual(cls.get_description(), "Device foo missing from ACPI tables")
|
|
117
|
+
cls = amd_debug.failures.MissingIommuPolicy("foo")
|
|
118
|
+
self.assertEqual(
|
|
119
|
+
cls.get_description(), "Device foo does not have IOMMU policy applied"
|
|
120
|
+
)
|
|
121
|
+
cls = amd_debug.failures.IommuPageFault("foo")
|
|
122
|
+
self.assertEqual(cls.get_description(), "Page fault reported for foo")
|
|
123
|
+
cls = amd_debug.failures.SMTNotEnabled()
|
|
124
|
+
self.assertEqual(cls.get_description(), "SMT is not enabled")
|
|
125
|
+
cls = amd_debug.failures.ASpmWrong()
|
|
126
|
+
self.assertEqual(cls.get_description(), "ASPM is overridden")
|
|
127
|
+
cls = amd_debug.failures.UnservicedGpio()
|
|
128
|
+
self.assertEqual(cls.get_description(), "GPIO interrupt is not serviced")
|
|
129
|
+
cls = amd_debug.failures.DmiNotSetup()
|
|
130
|
+
self.assertEqual(cls.get_description(), "DMI data was not scanned")
|
|
131
|
+
cls = amd_debug.failures.LimitedCores(10, 7)
|
|
132
|
+
self.assertEqual(cls.get_description(), "CPU cores have been limited")
|
|
133
|
+
cls = amd_debug.failures.RogAllyOldMcu(1, 2)
|
|
134
|
+
self.assertEqual(cls.get_description(), "Rog Ally MCU firmware is too old")
|
|
135
|
+
os.environ["TERM"] = "dumb"
|
|
136
|
+
cls = amd_debug.failures.RogAllyMcuPowerSave()
|
|
137
|
+
self.assertEqual(cls.get_description(), "Rog Ally MCU power save is disabled")
|
|
138
|
+
failure = "The MCU powersave feature is disabled which will cause problems with the controller after suspend/resume."
|
|
139
|
+
self.assertEqual(str(cls), failure)
|
|
140
|
+
cls.get_failure()
|
|
141
|
+
mocked_print.assert_has_calls(
|
|
142
|
+
[
|
|
143
|
+
call("🚦 Rog Ally MCU power save is disabled"),
|
|
144
|
+
call(failure),
|
|
145
|
+
]
|
|
146
|
+
)
|