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.
- amd_debug/common.py +24 -0
- amd_debug/display.py +34 -0
- amd_debug/installer.py +36 -4
- amd_debug/prerequisites.py +84 -46
- amd_debug/s2idle.py +25 -10
- {amd_debug_tools-0.2.0.dist-info → amd_debug_tools-0.2.1.dist-info}/METADATA +1 -1
- amd_debug_tools-0.2.1.dist-info/RECORD +45 -0
- {amd_debug_tools-0.2.0.dist-info → amd_debug_tools-0.2.1.dist-info}/WHEEL +1 -1
- amd_debug_tools-0.2.1.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 +332 -0
- test_database.py +284 -0
- test_display.py +143 -0
- test_failures.py +146 -0
- test_installer.py +279 -0
- test_kernel.py +205 -0
- test_launcher.py +53 -0
- test_prerequisites.py +1769 -0
- test_pstate.py +164 -0
- test_s2idle.py +833 -0
- test_sleep_report.py +167 -0
- test_validator.py +725 -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.1.dist-info}/entry_points.txt +0 -0
- {amd_debug_tools-0.2.0.dist-info → amd_debug_tools-0.2.1.dist-info}/licenses/LICENSE +0 -0
test_common.py
ADDED
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
#!/usr/bin/python3
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
This module contains unit tests for the common functions in the amd-debug-tools package.
|
|
6
|
+
"""
|
|
7
|
+
from unittest.mock import patch, mock_open, call
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
import tempfile
|
|
11
|
+
import unittest
|
|
12
|
+
import os
|
|
13
|
+
from platform import uname_result
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
from amd_debug.common import (
|
|
17
|
+
apply_prefix_wrapper,
|
|
18
|
+
Colors,
|
|
19
|
+
colorize_choices,
|
|
20
|
+
check_lockdown,
|
|
21
|
+
compare_file,
|
|
22
|
+
fatal_error,
|
|
23
|
+
get_distro,
|
|
24
|
+
get_log_priority,
|
|
25
|
+
get_pretty_distro,
|
|
26
|
+
is_root,
|
|
27
|
+
minimum_kernel,
|
|
28
|
+
print_color,
|
|
29
|
+
run_countdown,
|
|
30
|
+
systemd_in_use,
|
|
31
|
+
running_ssh,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
color_dict = {
|
|
35
|
+
"🚦": Colors.WARNING,
|
|
36
|
+
"🦟": Colors.DEBUG,
|
|
37
|
+
"❌": Colors.FAIL,
|
|
38
|
+
"👀": Colors.FAIL,
|
|
39
|
+
"✅": Colors.OK,
|
|
40
|
+
"🔋": Colors.OK,
|
|
41
|
+
"🐧": Colors.OK,
|
|
42
|
+
"💻": Colors.OK,
|
|
43
|
+
"○": Colors.OK,
|
|
44
|
+
"💤": Colors.OK,
|
|
45
|
+
"💯": Colors.UNDERLINE,
|
|
46
|
+
"🗣️": Colors.HEADER,
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class TestCommon(unittest.TestCase):
|
|
51
|
+
"""Test common functions"""
|
|
52
|
+
|
|
53
|
+
@classmethod
|
|
54
|
+
def setUpClass(cls):
|
|
55
|
+
logging.basicConfig(filename="/dev/null", level=logging.DEBUG)
|
|
56
|
+
|
|
57
|
+
def test_read_compare_file(self):
|
|
58
|
+
"""Test read_file and compare_file strip files correctly"""
|
|
59
|
+
|
|
60
|
+
f = tempfile.NamedTemporaryFile()
|
|
61
|
+
f.write("foo bar baz\n ".encode("utf-8"))
|
|
62
|
+
f.seek(0)
|
|
63
|
+
self.assertTrue(compare_file(f.name, "foo bar baz"))
|
|
64
|
+
|
|
65
|
+
def test_countdown(self):
|
|
66
|
+
"""Test countdown function"""
|
|
67
|
+
|
|
68
|
+
result = run_countdown("Full foo", 1)
|
|
69
|
+
self.assertTrue(result)
|
|
70
|
+
result = run_countdown("Half foo", 0.5)
|
|
71
|
+
self.assertTrue(result)
|
|
72
|
+
result = run_countdown("No foo", 0)
|
|
73
|
+
self.assertTrue(result)
|
|
74
|
+
result = run_countdown("Negative foo", -1)
|
|
75
|
+
self.assertFalse(result)
|
|
76
|
+
|
|
77
|
+
@patch("os.path.exists", return_value=True)
|
|
78
|
+
@patch("builtins.open", new_callable=mock_open, read_data="ID=foo\nVERSION_ID=bar")
|
|
79
|
+
def test_get_distro_known(self, mock_exists, _mock_open):
|
|
80
|
+
"""Test get_distro function"""
|
|
81
|
+
distro = get_distro()
|
|
82
|
+
mock_exists.assert_has_calls(
|
|
83
|
+
[
|
|
84
|
+
call("/etc/os-release", "r", encoding="utf-8"),
|
|
85
|
+
call().__enter__(),
|
|
86
|
+
call().__iter__(),
|
|
87
|
+
call().__exit__(None, None, None),
|
|
88
|
+
]
|
|
89
|
+
)
|
|
90
|
+
self.assertEqual(distro, "foo")
|
|
91
|
+
|
|
92
|
+
@patch("os.path.exists", return_value=False)
|
|
93
|
+
def test_get_distro_unknown(self, mock_exists):
|
|
94
|
+
"""Test get_distro function"""
|
|
95
|
+
distro = get_distro()
|
|
96
|
+
mock_exists.assert_has_calls(
|
|
97
|
+
[
|
|
98
|
+
call("/etc/os-release"),
|
|
99
|
+
call("/etc/arch-release"),
|
|
100
|
+
call("/etc/fedora-release"),
|
|
101
|
+
call("/etc/debian_version"),
|
|
102
|
+
]
|
|
103
|
+
)
|
|
104
|
+
self.assertEqual(distro, "unknown")
|
|
105
|
+
|
|
106
|
+
@patch("os.path.exists", return_value=True)
|
|
107
|
+
@patch("builtins.open", new_callable=mock_open, read_data="PRETTY_NAME=Foo")
|
|
108
|
+
def test_get_pretty_distro_known(self, mock_exists, _mock_open):
|
|
109
|
+
"""Test get_distro function"""
|
|
110
|
+
distro = get_pretty_distro()
|
|
111
|
+
self.assertEqual(distro, "Foo")
|
|
112
|
+
mock_exists.assert_has_calls(
|
|
113
|
+
[
|
|
114
|
+
call("/etc/os-release", "r", encoding="utf-8"),
|
|
115
|
+
call().__enter__(),
|
|
116
|
+
call().__iter__(),
|
|
117
|
+
call().__exit__(None, None, None),
|
|
118
|
+
]
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
@patch("os.path.exists", return_value=False)
|
|
122
|
+
def test_get_pretty_distro_unknown(self, mock_exists):
|
|
123
|
+
"""Test get_distro function"""
|
|
124
|
+
distro = get_pretty_distro()
|
|
125
|
+
self.assertEqual(distro, "Unknown")
|
|
126
|
+
mock_exists.assert_has_calls(
|
|
127
|
+
[
|
|
128
|
+
call("/etc/os-release"),
|
|
129
|
+
]
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
@patch("os.path.exists", return_value=True)
|
|
133
|
+
@patch(
|
|
134
|
+
"builtins.open",
|
|
135
|
+
new_callable=mock_open,
|
|
136
|
+
read_data="[none] integrity confidentiality",
|
|
137
|
+
)
|
|
138
|
+
def test_lockdown_pass(self, mock_exists, _mock_file):
|
|
139
|
+
"""Test lockdown function"""
|
|
140
|
+
lockdown = check_lockdown()
|
|
141
|
+
mock_exists.assert_called_once_with(
|
|
142
|
+
"/sys/kernel/security/lockdown", "r", encoding="utf-8"
|
|
143
|
+
)
|
|
144
|
+
self.assertFalse(lockdown)
|
|
145
|
+
|
|
146
|
+
@patch("os.path.exists", return_value=True)
|
|
147
|
+
@patch(
|
|
148
|
+
"builtins.open",
|
|
149
|
+
new_callable=mock_open,
|
|
150
|
+
read_data="none [integrity] confidentiality",
|
|
151
|
+
)
|
|
152
|
+
def test_lockdown_fail_integrity(self, mock_exists, _mock_file):
|
|
153
|
+
"""Test lockdown function"""
|
|
154
|
+
lockdown = check_lockdown()
|
|
155
|
+
mock_exists.assert_called_once_with(
|
|
156
|
+
"/sys/kernel/security/lockdown", "r", encoding="utf-8"
|
|
157
|
+
)
|
|
158
|
+
self.assertTrue(lockdown)
|
|
159
|
+
|
|
160
|
+
@patch("os.path.exists", return_value=True)
|
|
161
|
+
@patch(
|
|
162
|
+
"builtins.open",
|
|
163
|
+
new_callable=mock_open,
|
|
164
|
+
read_data="none integrity [confidentiality]",
|
|
165
|
+
)
|
|
166
|
+
def test_lockdown_fail_confidentiality(self, mock_exists, _mock_file):
|
|
167
|
+
"""Test lockdown function"""
|
|
168
|
+
lockdown = check_lockdown()
|
|
169
|
+
mock_exists.assert_called_once_with(
|
|
170
|
+
"/sys/kernel/security/lockdown", "r", encoding="utf-8"
|
|
171
|
+
)
|
|
172
|
+
self.assertTrue(lockdown)
|
|
173
|
+
|
|
174
|
+
@patch("os.path.exists", return_value=False)
|
|
175
|
+
def test_lockdown_missing(self, mock_exists):
|
|
176
|
+
"""Test lockdown function"""
|
|
177
|
+
lockdown = check_lockdown()
|
|
178
|
+
mock_exists.assert_called_once_with("/sys/kernel/security/lockdown")
|
|
179
|
+
self.assertFalse(lockdown)
|
|
180
|
+
|
|
181
|
+
@patch("builtins.print")
|
|
182
|
+
def test_print_color(self, mocked_print):
|
|
183
|
+
"""Test print_color function for all expected levels"""
|
|
184
|
+
message = "foo"
|
|
185
|
+
# test all color groups
|
|
186
|
+
for group, color in color_dict.items():
|
|
187
|
+
prefix = f"{group} "
|
|
188
|
+
print_color(message, group)
|
|
189
|
+
mocked_print.assert_called_once_with(
|
|
190
|
+
f"{prefix}{color}{message}{Colors.ENDC}"
|
|
191
|
+
)
|
|
192
|
+
mocked_print.reset_mock()
|
|
193
|
+
|
|
194
|
+
# call without a group
|
|
195
|
+
print_color(message, Colors.WARNING)
|
|
196
|
+
mocked_print.assert_called_once_with(f"{Colors.WARNING}{message}{Colors.ENDC}")
|
|
197
|
+
mocked_print.reset_mock()
|
|
198
|
+
|
|
199
|
+
# test dumb terminal
|
|
200
|
+
os.environ["TERM"] = "dumb"
|
|
201
|
+
print_color(message, Colors.WARNING)
|
|
202
|
+
mocked_print.assert_called_once_with(f"{message}")
|
|
203
|
+
|
|
204
|
+
@patch("builtins.print")
|
|
205
|
+
def test_fatal_error(self, mocked_print):
|
|
206
|
+
"""Test fatal_error function"""
|
|
207
|
+
with patch("sys.exit") as mock_exit:
|
|
208
|
+
fatal_error("foo")
|
|
209
|
+
mocked_print.assert_called_once_with(f"👀 {Colors.FAIL}foo{Colors.ENDC}")
|
|
210
|
+
mock_exit.assert_called_once_with(1)
|
|
211
|
+
|
|
212
|
+
@patch("os.geteuid", return_value=0)
|
|
213
|
+
def test_is_root_true(self, mock_geteuid):
|
|
214
|
+
"""Test is_root function when user is root"""
|
|
215
|
+
self.assertTrue(is_root())
|
|
216
|
+
mock_geteuid.assert_called_once()
|
|
217
|
+
self.assertEqual(mock_geteuid.call_count, 1)
|
|
218
|
+
|
|
219
|
+
@patch("os.geteuid", return_value=1000)
|
|
220
|
+
def test_is_root_false(self, mock_geteuid):
|
|
221
|
+
"""Test is_root function when user is not root"""
|
|
222
|
+
self.assertFalse(is_root())
|
|
223
|
+
mock_geteuid.assert_called_once()
|
|
224
|
+
self.assertEqual(mock_geteuid.call_count, 1)
|
|
225
|
+
|
|
226
|
+
def test_get_log_priority(self):
|
|
227
|
+
"""Test get_log_priority works for expected values"""
|
|
228
|
+
ret = get_log_priority(None)
|
|
229
|
+
self.assertEqual(ret, "○")
|
|
230
|
+
ret = get_log_priority("foo")
|
|
231
|
+
self.assertEqual(ret, "foo")
|
|
232
|
+
ret = get_log_priority("3")
|
|
233
|
+
self.assertEqual(ret, "❌")
|
|
234
|
+
ret = get_log_priority(4)
|
|
235
|
+
self.assertEqual(ret, "🚦")
|
|
236
|
+
ret = get_log_priority(7)
|
|
237
|
+
self.assertEqual(ret, "🦟")
|
|
238
|
+
|
|
239
|
+
def test_minimum_kernel(self):
|
|
240
|
+
"""Test minimum_kernel function"""
|
|
241
|
+
with patch("platform.uname") as mock_uname:
|
|
242
|
+
mock_uname.return_value = uname_result(
|
|
243
|
+
system="Linux",
|
|
244
|
+
node="foo",
|
|
245
|
+
release="6.12.0-rc5",
|
|
246
|
+
version="baz",
|
|
247
|
+
machine="x86_64",
|
|
248
|
+
)
|
|
249
|
+
self.assertTrue(minimum_kernel("6", "12"))
|
|
250
|
+
self.assertFalse(minimum_kernel("6", "13"))
|
|
251
|
+
self.assertTrue(minimum_kernel(5, 1))
|
|
252
|
+
self.assertFalse(minimum_kernel(7, 1))
|
|
253
|
+
with self.assertRaises(ValueError):
|
|
254
|
+
minimum_kernel("foo", "bar")
|
|
255
|
+
with self.assertRaises(TypeError):
|
|
256
|
+
minimum_kernel(None, None)
|
|
257
|
+
|
|
258
|
+
def test_systemd_in_use(self):
|
|
259
|
+
"""Test systemd_in_use function"""
|
|
260
|
+
with patch(
|
|
261
|
+
"builtins.open", new_callable=mock_open, read_data="systemd"
|
|
262
|
+
) as mock_file:
|
|
263
|
+
self.assertTrue(systemd_in_use())
|
|
264
|
+
mock_file.assert_called_once_with("/proc/1/comm", "r", encoding="utf-8")
|
|
265
|
+
with patch(
|
|
266
|
+
"builtins.open", new_callable=mock_open, read_data="upstart"
|
|
267
|
+
) as mock_file:
|
|
268
|
+
self.assertFalse(systemd_in_use())
|
|
269
|
+
mock_file.assert_called_once_with("/proc/1/comm", "r", encoding="utf-8")
|
|
270
|
+
|
|
271
|
+
def test_running_in_ssh(self):
|
|
272
|
+
"""Test running_in_ssh function"""
|
|
273
|
+
with patch("os.environ", {"SSH_TTY": "/dev/pts/0"}):
|
|
274
|
+
self.assertTrue(running_ssh())
|
|
275
|
+
with patch("os.environ", {}):
|
|
276
|
+
self.assertFalse(running_ssh())
|
|
277
|
+
|
|
278
|
+
def test_apply_prefix_wrapper(self):
|
|
279
|
+
"""Test apply_prefix_wrapper function"""
|
|
280
|
+
header = "Header:"
|
|
281
|
+
message = "Line 1\nLine 2\nLine 3"
|
|
282
|
+
expected_output = "Header:\n" "│ Line 1\n" "│ Line 2\n" "└─ Line 3\n"
|
|
283
|
+
self.assertEqual(apply_prefix_wrapper(header, message), expected_output)
|
|
284
|
+
|
|
285
|
+
# Test with a single line message
|
|
286
|
+
message = "Single Line"
|
|
287
|
+
expected_output = "Header:\n└─ Single Line\n"
|
|
288
|
+
self.assertEqual(apply_prefix_wrapper(header, message), expected_output)
|
|
289
|
+
|
|
290
|
+
# Test with an empty message
|
|
291
|
+
message = ""
|
|
292
|
+
expected_output = "Header:\n"
|
|
293
|
+
self.assertEqual(apply_prefix_wrapper(header, message), expected_output)
|
|
294
|
+
|
|
295
|
+
# Test with leading/trailing whitespace in the message
|
|
296
|
+
message = " Line 1\nLine 2 \n Line 3 "
|
|
297
|
+
expected_output = "Header:\n" "│ Line 1\n" "│ Line 2\n" "└─ Line 3\n"
|
|
298
|
+
self.assertEqual(apply_prefix_wrapper(header, message), expected_output)
|
|
299
|
+
|
|
300
|
+
def test_colorize_choices_with_default(self):
|
|
301
|
+
"""Test colorize_choices function with a default value"""
|
|
302
|
+
choices = ["option1", "option2", "option3"]
|
|
303
|
+
default = "option2"
|
|
304
|
+
expected_output = f"{Colors.OK}{default}{Colors.ENDC}, option1, option3"
|
|
305
|
+
self.assertEqual(colorize_choices(choices, default), expected_output)
|
|
306
|
+
|
|
307
|
+
def test_colorize_choices_without_default(self):
|
|
308
|
+
"""Test colorize_choices function when default is not in choices"""
|
|
309
|
+
choices = ["option1", "option2", "option3"]
|
|
310
|
+
default = "option4"
|
|
311
|
+
with self.assertRaises(ValueError) as context:
|
|
312
|
+
colorize_choices(choices, default)
|
|
313
|
+
self.assertEqual(
|
|
314
|
+
str(context.exception), "Default choice 'option4' not in choices"
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
def test_colorize_choices_empty_list(self):
|
|
318
|
+
"""Test colorize_choices function with an empty list"""
|
|
319
|
+
choices = []
|
|
320
|
+
default = "option1"
|
|
321
|
+
with self.assertRaises(ValueError) as context:
|
|
322
|
+
colorize_choices(choices, default)
|
|
323
|
+
self.assertEqual(
|
|
324
|
+
str(context.exception), "Default choice 'option1' not in choices"
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
def test_colorize_choices_single_choice(self):
|
|
328
|
+
"""Test colorize_choices function with a single choice"""
|
|
329
|
+
choices = ["option1"]
|
|
330
|
+
default = "option1"
|
|
331
|
+
expected_output = f"{Colors.OK}{default}{Colors.ENDC}"
|
|
332
|
+
self.assertEqual(colorize_choices(choices, default), expected_output)
|
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)
|