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_common.py
ADDED
|
@@ -0,0 +1,444 @@
|
|
|
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
|
+
convert_string_to_bool,
|
|
20
|
+
colorize_choices,
|
|
21
|
+
check_lockdown,
|
|
22
|
+
compare_file,
|
|
23
|
+
find_ip_version,
|
|
24
|
+
fatal_error,
|
|
25
|
+
get_distro,
|
|
26
|
+
get_log_priority,
|
|
27
|
+
get_pretty_distro,
|
|
28
|
+
is_root,
|
|
29
|
+
minimum_kernel,
|
|
30
|
+
print_color,
|
|
31
|
+
run_countdown,
|
|
32
|
+
systemd_in_use,
|
|
33
|
+
running_ssh,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
color_dict = {
|
|
37
|
+
"🚦": Colors.WARNING,
|
|
38
|
+
"🦟": Colors.DEBUG,
|
|
39
|
+
"❌": Colors.FAIL,
|
|
40
|
+
"👀": Colors.FAIL,
|
|
41
|
+
"✅": Colors.OK,
|
|
42
|
+
"🔋": Colors.OK,
|
|
43
|
+
"🐧": Colors.OK,
|
|
44
|
+
"💻": Colors.OK,
|
|
45
|
+
"○": Colors.OK,
|
|
46
|
+
"💤": Colors.OK,
|
|
47
|
+
"💯": Colors.UNDERLINE,
|
|
48
|
+
"🗣️": Colors.HEADER,
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class TestCommon(unittest.TestCase):
|
|
53
|
+
"""Test common functions"""
|
|
54
|
+
|
|
55
|
+
@classmethod
|
|
56
|
+
def setUpClass(cls):
|
|
57
|
+
logging.basicConfig(filename="/dev/null", level=logging.DEBUG)
|
|
58
|
+
|
|
59
|
+
def test_read_compare_file(self):
|
|
60
|
+
"""Test read_file and compare_file strip files correctly"""
|
|
61
|
+
|
|
62
|
+
f = tempfile.NamedTemporaryFile()
|
|
63
|
+
f.write("foo bar baz\n ".encode("utf-8"))
|
|
64
|
+
f.seek(0)
|
|
65
|
+
self.assertTrue(compare_file(f.name, "foo bar baz"))
|
|
66
|
+
|
|
67
|
+
def test_countdown(self):
|
|
68
|
+
"""Test countdown function"""
|
|
69
|
+
|
|
70
|
+
result = run_countdown("Full foo", 1)
|
|
71
|
+
self.assertTrue(result)
|
|
72
|
+
result = run_countdown("Half foo", 0.5)
|
|
73
|
+
self.assertTrue(result)
|
|
74
|
+
result = run_countdown("No foo", 0)
|
|
75
|
+
self.assertTrue(result)
|
|
76
|
+
result = run_countdown("Negative foo", -1)
|
|
77
|
+
self.assertFalse(result)
|
|
78
|
+
|
|
79
|
+
@patch("os.path.exists", return_value=True)
|
|
80
|
+
@patch("builtins.open", new_callable=mock_open, read_data="ID=foo\nVERSION_ID=bar")
|
|
81
|
+
def test_get_distro_known(self, mock_exists, _mock_open):
|
|
82
|
+
"""Test get_distro function"""
|
|
83
|
+
distro = get_distro()
|
|
84
|
+
mock_exists.assert_has_calls(
|
|
85
|
+
[
|
|
86
|
+
call("/etc/os-release", "r", encoding="utf-8"),
|
|
87
|
+
call().__enter__(),
|
|
88
|
+
call().__iter__(),
|
|
89
|
+
call().__exit__(None, None, None),
|
|
90
|
+
]
|
|
91
|
+
)
|
|
92
|
+
self.assertEqual(distro, "foo")
|
|
93
|
+
|
|
94
|
+
@patch("os.path.exists", return_value=False)
|
|
95
|
+
def test_get_distro_unknown(self, mock_exists):
|
|
96
|
+
"""Test get_distro function"""
|
|
97
|
+
distro = get_distro()
|
|
98
|
+
mock_exists.assert_has_calls(
|
|
99
|
+
[
|
|
100
|
+
call("/etc/os-release"),
|
|
101
|
+
call("/etc/arch-release"),
|
|
102
|
+
call("/etc/fedora-release"),
|
|
103
|
+
call("/etc/debian_version"),
|
|
104
|
+
]
|
|
105
|
+
)
|
|
106
|
+
self.assertEqual(distro, "unknown")
|
|
107
|
+
|
|
108
|
+
@patch("os.path.exists", return_value=True)
|
|
109
|
+
@patch("builtins.open", new_callable=mock_open, read_data="PRETTY_NAME=Foo")
|
|
110
|
+
def test_get_pretty_distro_known(self, mock_exists, _mock_open):
|
|
111
|
+
"""Test get_distro function"""
|
|
112
|
+
distro = get_pretty_distro()
|
|
113
|
+
self.assertEqual(distro, "Foo")
|
|
114
|
+
mock_exists.assert_has_calls(
|
|
115
|
+
[
|
|
116
|
+
call("/etc/os-release", "r", encoding="utf-8"),
|
|
117
|
+
call().__enter__(),
|
|
118
|
+
call().__iter__(),
|
|
119
|
+
call().__exit__(None, None, None),
|
|
120
|
+
]
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
@patch("os.path.exists", return_value=False)
|
|
124
|
+
def test_get_pretty_distro_unknown(self, mock_exists):
|
|
125
|
+
"""Test get_distro function"""
|
|
126
|
+
distro = get_pretty_distro()
|
|
127
|
+
self.assertEqual(distro, "Unknown")
|
|
128
|
+
mock_exists.assert_has_calls(
|
|
129
|
+
[
|
|
130
|
+
call("/etc/os-release"),
|
|
131
|
+
]
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
@patch("os.path.exists", return_value=True)
|
|
135
|
+
@patch(
|
|
136
|
+
"builtins.open",
|
|
137
|
+
new_callable=mock_open,
|
|
138
|
+
read_data="[none] integrity confidentiality",
|
|
139
|
+
)
|
|
140
|
+
def test_lockdown_pass(self, mock_exists, _mock_file):
|
|
141
|
+
"""Test lockdown function"""
|
|
142
|
+
lockdown = check_lockdown()
|
|
143
|
+
mock_exists.assert_called_once_with(
|
|
144
|
+
"/sys/kernel/security/lockdown", "r", encoding="utf-8"
|
|
145
|
+
)
|
|
146
|
+
self.assertFalse(lockdown)
|
|
147
|
+
|
|
148
|
+
@patch("os.path.exists", return_value=True)
|
|
149
|
+
@patch(
|
|
150
|
+
"builtins.open",
|
|
151
|
+
new_callable=mock_open,
|
|
152
|
+
read_data="none [integrity] confidentiality",
|
|
153
|
+
)
|
|
154
|
+
def test_lockdown_fail_integrity(self, mock_exists, _mock_file):
|
|
155
|
+
"""Test lockdown function"""
|
|
156
|
+
lockdown = check_lockdown()
|
|
157
|
+
mock_exists.assert_called_once_with(
|
|
158
|
+
"/sys/kernel/security/lockdown", "r", encoding="utf-8"
|
|
159
|
+
)
|
|
160
|
+
self.assertTrue(lockdown)
|
|
161
|
+
|
|
162
|
+
@patch("os.path.exists", return_value=True)
|
|
163
|
+
@patch(
|
|
164
|
+
"builtins.open",
|
|
165
|
+
new_callable=mock_open,
|
|
166
|
+
read_data="none integrity [confidentiality]",
|
|
167
|
+
)
|
|
168
|
+
def test_lockdown_fail_confidentiality(self, mock_exists, _mock_file):
|
|
169
|
+
"""Test lockdown function"""
|
|
170
|
+
lockdown = check_lockdown()
|
|
171
|
+
mock_exists.assert_called_once_with(
|
|
172
|
+
"/sys/kernel/security/lockdown", "r", encoding="utf-8"
|
|
173
|
+
)
|
|
174
|
+
self.assertTrue(lockdown)
|
|
175
|
+
|
|
176
|
+
@patch("os.path.exists", return_value=False)
|
|
177
|
+
def test_lockdown_missing(self, mock_exists):
|
|
178
|
+
"""Test lockdown function"""
|
|
179
|
+
lockdown = check_lockdown()
|
|
180
|
+
mock_exists.assert_called_once_with("/sys/kernel/security/lockdown")
|
|
181
|
+
self.assertFalse(lockdown)
|
|
182
|
+
|
|
183
|
+
@patch("builtins.print")
|
|
184
|
+
def test_print_color(self, mocked_print):
|
|
185
|
+
"""Test print_color function for all expected levels"""
|
|
186
|
+
message = "foo"
|
|
187
|
+
# test all color groups
|
|
188
|
+
for group, color in color_dict.items():
|
|
189
|
+
prefix = f"{group} "
|
|
190
|
+
print_color(message, group)
|
|
191
|
+
mocked_print.assert_called_once_with(
|
|
192
|
+
f"{prefix}{color}{message}{Colors.ENDC}"
|
|
193
|
+
)
|
|
194
|
+
mocked_print.reset_mock()
|
|
195
|
+
|
|
196
|
+
# call without a group
|
|
197
|
+
print_color(message, Colors.WARNING)
|
|
198
|
+
mocked_print.assert_called_once_with(f"{Colors.WARNING}{message}{Colors.ENDC}")
|
|
199
|
+
mocked_print.reset_mock()
|
|
200
|
+
|
|
201
|
+
# test dumb terminal
|
|
202
|
+
os.environ["TERM"] = "dumb"
|
|
203
|
+
print_color(message, Colors.WARNING)
|
|
204
|
+
mocked_print.assert_called_once_with(f"{message}")
|
|
205
|
+
|
|
206
|
+
@patch("builtins.print")
|
|
207
|
+
def test_fatal_error(self, mocked_print):
|
|
208
|
+
"""Test fatal_error function"""
|
|
209
|
+
with patch("sys.exit") as mock_exit:
|
|
210
|
+
fatal_error("foo")
|
|
211
|
+
mocked_print.assert_called_once_with(f"👀 {Colors.FAIL}foo{Colors.ENDC}")
|
|
212
|
+
mock_exit.assert_called_once_with(1)
|
|
213
|
+
|
|
214
|
+
@patch("os.geteuid", return_value=0)
|
|
215
|
+
def test_is_root_true(self, mock_geteuid):
|
|
216
|
+
"""Test is_root function when user is root"""
|
|
217
|
+
self.assertTrue(is_root())
|
|
218
|
+
mock_geteuid.assert_called_once()
|
|
219
|
+
self.assertEqual(mock_geteuid.call_count, 1)
|
|
220
|
+
|
|
221
|
+
@patch("os.geteuid", return_value=1000)
|
|
222
|
+
def test_is_root_false(self, mock_geteuid):
|
|
223
|
+
"""Test is_root function when user is not root"""
|
|
224
|
+
self.assertFalse(is_root())
|
|
225
|
+
mock_geteuid.assert_called_once()
|
|
226
|
+
self.assertEqual(mock_geteuid.call_count, 1)
|
|
227
|
+
|
|
228
|
+
def test_get_log_priority(self):
|
|
229
|
+
"""Test get_log_priority works for expected values"""
|
|
230
|
+
ret = get_log_priority(None)
|
|
231
|
+
self.assertEqual(ret, "○")
|
|
232
|
+
ret = get_log_priority("foo")
|
|
233
|
+
self.assertEqual(ret, "foo")
|
|
234
|
+
ret = get_log_priority("3")
|
|
235
|
+
self.assertEqual(ret, "❌")
|
|
236
|
+
ret = get_log_priority(4)
|
|
237
|
+
self.assertEqual(ret, "🚦")
|
|
238
|
+
ret = get_log_priority(7)
|
|
239
|
+
self.assertEqual(ret, "🦟")
|
|
240
|
+
|
|
241
|
+
def test_minimum_kernel(self):
|
|
242
|
+
"""Test minimum_kernel function"""
|
|
243
|
+
with patch("platform.uname") as mock_uname:
|
|
244
|
+
mock_uname.return_value = uname_result(
|
|
245
|
+
system="Linux",
|
|
246
|
+
node="foo",
|
|
247
|
+
release="6.12.0-rc5",
|
|
248
|
+
version="baz",
|
|
249
|
+
machine="x86_64",
|
|
250
|
+
)
|
|
251
|
+
self.assertTrue(minimum_kernel("6", "12"))
|
|
252
|
+
self.assertFalse(minimum_kernel("6", "13"))
|
|
253
|
+
self.assertTrue(minimum_kernel(5, 1))
|
|
254
|
+
self.assertFalse(minimum_kernel(7, 1))
|
|
255
|
+
with self.assertRaises(ValueError):
|
|
256
|
+
minimum_kernel("foo", "bar")
|
|
257
|
+
with self.assertRaises(TypeError):
|
|
258
|
+
minimum_kernel(None, None)
|
|
259
|
+
|
|
260
|
+
def test_systemd_in_use(self):
|
|
261
|
+
"""Test systemd_in_use function"""
|
|
262
|
+
with patch(
|
|
263
|
+
"builtins.open", new_callable=mock_open, read_data="systemd"
|
|
264
|
+
) as mock_file:
|
|
265
|
+
self.assertTrue(systemd_in_use())
|
|
266
|
+
mock_file.assert_called_once_with("/proc/1/comm", "r", encoding="utf-8")
|
|
267
|
+
with patch(
|
|
268
|
+
"builtins.open", new_callable=mock_open, read_data="upstart"
|
|
269
|
+
) as mock_file:
|
|
270
|
+
self.assertFalse(systemd_in_use())
|
|
271
|
+
mock_file.assert_called_once_with("/proc/1/comm", "r", encoding="utf-8")
|
|
272
|
+
|
|
273
|
+
def test_running_in_ssh(self):
|
|
274
|
+
"""Test running_in_ssh function"""
|
|
275
|
+
with patch("os.environ", {"SSH_TTY": "/dev/pts/0"}):
|
|
276
|
+
self.assertTrue(running_ssh())
|
|
277
|
+
with patch("os.environ", {}):
|
|
278
|
+
self.assertFalse(running_ssh())
|
|
279
|
+
|
|
280
|
+
def test_apply_prefix_wrapper(self):
|
|
281
|
+
"""Test apply_prefix_wrapper function"""
|
|
282
|
+
header = "Header:"
|
|
283
|
+
message = "Line 1\nLine 2\nLine 3"
|
|
284
|
+
expected_output = "Header:\n" "│ Line 1\n" "│ Line 2\n" "└─ Line 3\n"
|
|
285
|
+
self.assertEqual(apply_prefix_wrapper(header, message), expected_output)
|
|
286
|
+
|
|
287
|
+
# Test with a single line message
|
|
288
|
+
message = "Single Line"
|
|
289
|
+
expected_output = "Header:\n└─ Single Line\n"
|
|
290
|
+
self.assertEqual(apply_prefix_wrapper(header, message), expected_output)
|
|
291
|
+
|
|
292
|
+
# Test with an empty message
|
|
293
|
+
message = ""
|
|
294
|
+
expected_output = "Header:\n"
|
|
295
|
+
self.assertEqual(apply_prefix_wrapper(header, message), expected_output)
|
|
296
|
+
|
|
297
|
+
# Test with leading/trailing whitespace in the message
|
|
298
|
+
message = " Line 1\nLine 2 \n Line 3 "
|
|
299
|
+
expected_output = "Header:\n" "│ Line 1\n" "│ Line 2\n" "└─ Line 3\n"
|
|
300
|
+
self.assertEqual(apply_prefix_wrapper(header, message), expected_output)
|
|
301
|
+
|
|
302
|
+
def test_colorize_choices_with_default(self):
|
|
303
|
+
"""Test colorize_choices function with a default value"""
|
|
304
|
+
choices = ["option1", "option2", "option3"]
|
|
305
|
+
default = "option2"
|
|
306
|
+
expected_output = f"{Colors.OK}{default}{Colors.ENDC}, option1, option3"
|
|
307
|
+
self.assertEqual(colorize_choices(choices, default), expected_output)
|
|
308
|
+
|
|
309
|
+
def test_colorize_choices_without_default(self):
|
|
310
|
+
"""Test colorize_choices function when default is not in choices"""
|
|
311
|
+
choices = ["option1", "option2", "option3"]
|
|
312
|
+
default = "option4"
|
|
313
|
+
with self.assertRaises(ValueError) as context:
|
|
314
|
+
colorize_choices(choices, default)
|
|
315
|
+
self.assertEqual(
|
|
316
|
+
str(context.exception), "Default choice 'option4' not in choices"
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
def test_colorize_choices_empty_list(self):
|
|
320
|
+
"""Test colorize_choices function with an empty list"""
|
|
321
|
+
choices = []
|
|
322
|
+
default = "option1"
|
|
323
|
+
with self.assertRaises(ValueError) as context:
|
|
324
|
+
colorize_choices(choices, default)
|
|
325
|
+
self.assertEqual(
|
|
326
|
+
str(context.exception), "Default choice 'option1' not in choices"
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
def test_colorize_choices_single_choice(self):
|
|
330
|
+
"""Test colorize_choices function with a single choice"""
|
|
331
|
+
choices = ["option1"]
|
|
332
|
+
default = "option1"
|
|
333
|
+
expected_output = f"{Colors.OK}{default}{Colors.ENDC}"
|
|
334
|
+
self.assertEqual(colorize_choices(choices, default), expected_output)
|
|
335
|
+
|
|
336
|
+
@patch("amd_debug.common.read_file")
|
|
337
|
+
@patch("os.path.exists")
|
|
338
|
+
def test_find_ip_version_found(self, mock_exists, mock_read_file):
|
|
339
|
+
"""Test find_ip_version returns True when expected value is found"""
|
|
340
|
+
base_path = "/foo"
|
|
341
|
+
kind = "bar"
|
|
342
|
+
hw_ver = {"baz": 42}
|
|
343
|
+
|
|
344
|
+
# Simulate file exists and value matches
|
|
345
|
+
def exists_side_effect(path):
|
|
346
|
+
return True
|
|
347
|
+
|
|
348
|
+
mock_exists.side_effect = exists_side_effect
|
|
349
|
+
mock_read_file.return_value = "42"
|
|
350
|
+
result = find_ip_version(base_path, kind, hw_ver)
|
|
351
|
+
self.assertTrue(result)
|
|
352
|
+
b = os.path.join(base_path, "ip_discovery", "die", "0", kind, "0")
|
|
353
|
+
expected_path = os.path.join(b, "baz")
|
|
354
|
+
mock_exists.assert_any_call(expected_path)
|
|
355
|
+
mock_read_file.assert_any_call(expected_path)
|
|
356
|
+
|
|
357
|
+
@patch("amd_debug.common.read_file")
|
|
358
|
+
@patch("os.path.exists")
|
|
359
|
+
def test_find_ip_version_not_found_due_to_missing_file(
|
|
360
|
+
self, mock_exists, mock_read_file
|
|
361
|
+
):
|
|
362
|
+
"""Test find_ip_version returns False if file does not exist"""
|
|
363
|
+
base_path = "/foo"
|
|
364
|
+
kind = "bar"
|
|
365
|
+
hw_ver = {"baz": 42}
|
|
366
|
+
# Simulate file does not exist
|
|
367
|
+
mock_exists.return_value = False
|
|
368
|
+
result = find_ip_version(base_path, kind, hw_ver)
|
|
369
|
+
self.assertFalse(result)
|
|
370
|
+
b = os.path.join(base_path, "ip_discovery", "die", "0", kind, "0")
|
|
371
|
+
expected_path = os.path.join(b, "baz")
|
|
372
|
+
mock_exists.assert_any_call(expected_path)
|
|
373
|
+
mock_read_file.assert_not_called()
|
|
374
|
+
|
|
375
|
+
@patch("amd_debug.common.read_file")
|
|
376
|
+
@patch("os.path.exists")
|
|
377
|
+
def test_find_ip_version_not_found_due_to_value_mismatch(
|
|
378
|
+
self, mock_exists, mock_read_file
|
|
379
|
+
):
|
|
380
|
+
"""Test find_ip_version returns False if value does not match"""
|
|
381
|
+
base_path = "/foo"
|
|
382
|
+
kind = "bar"
|
|
383
|
+
hw_ver = {"baz": 42}
|
|
384
|
+
# Simulate file exists but value does not match
|
|
385
|
+
mock_exists.return_value = True
|
|
386
|
+
mock_read_file.return_value = "99"
|
|
387
|
+
result = find_ip_version(base_path, kind, hw_ver)
|
|
388
|
+
self.assertFalse(result)
|
|
389
|
+
b = os.path.join(base_path, "ip_discovery", "die", "0", kind, "0")
|
|
390
|
+
expected_path = os.path.join(b, "baz")
|
|
391
|
+
mock_exists.assert_any_call(expected_path)
|
|
392
|
+
mock_read_file.assert_any_call(expected_path)
|
|
393
|
+
|
|
394
|
+
@patch("amd_debug.common.read_file")
|
|
395
|
+
@patch("os.path.exists")
|
|
396
|
+
def test_find_ip_version_multiple_keys(self, mock_exists, mock_read_file):
|
|
397
|
+
"""Test find_ip_version with multiple keys in hw_ver"""
|
|
398
|
+
base_path = "/foo"
|
|
399
|
+
kind = "bar"
|
|
400
|
+
hw_ver = {"baz": 42, "qux": 99}
|
|
401
|
+
|
|
402
|
+
# First key: file exists, value does not match
|
|
403
|
+
# Second key: file exists, value matches
|
|
404
|
+
def exists_side_effect(path):
|
|
405
|
+
return True
|
|
406
|
+
|
|
407
|
+
def read_file_side_effect(path):
|
|
408
|
+
if path.endswith("baz"):
|
|
409
|
+
return "0"
|
|
410
|
+
if path.endswith("qux"):
|
|
411
|
+
return "99"
|
|
412
|
+
return "0"
|
|
413
|
+
|
|
414
|
+
mock_exists.side_effect = exists_side_effect
|
|
415
|
+
mock_read_file.side_effect = read_file_side_effect
|
|
416
|
+
result = find_ip_version(base_path, kind, hw_ver)
|
|
417
|
+
self.assertFalse(result)
|
|
418
|
+
|
|
419
|
+
def test_convert_string_to_bool_true_values(self):
|
|
420
|
+
"""Test convert_string_to_bool returns True for truthy string values"""
|
|
421
|
+
self.assertTrue(convert_string_to_bool("True"))
|
|
422
|
+
self.assertTrue(convert_string_to_bool("1"))
|
|
423
|
+
self.assertTrue(convert_string_to_bool("'nonempty'"))
|
|
424
|
+
self.assertTrue(convert_string_to_bool('"nonempty"'))
|
|
425
|
+
|
|
426
|
+
def test_convert_string_to_bool_false_values(self):
|
|
427
|
+
"""Test convert_string_to_bool returns False for falsy string values"""
|
|
428
|
+
self.assertFalse(convert_string_to_bool("False"))
|
|
429
|
+
self.assertFalse(convert_string_to_bool("0"))
|
|
430
|
+
self.assertFalse(convert_string_to_bool("''"))
|
|
431
|
+
self.assertFalse(convert_string_to_bool('""'))
|
|
432
|
+
self.assertFalse(convert_string_to_bool("None"))
|
|
433
|
+
|
|
434
|
+
def test_convert_string_to_bool_invalid_syntax(self):
|
|
435
|
+
"""Test convert_string_to_bool exits on invalid syntax"""
|
|
436
|
+
with patch("sys.exit") as mock_exit:
|
|
437
|
+
convert_string_to_bool("not_a_bool")
|
|
438
|
+
mock_exit.assert_called_once_with("Invalid entry: not_a_bool")
|
|
439
|
+
|
|
440
|
+
def test_convert_string_to_bool_invalid_value(self):
|
|
441
|
+
"""Test convert_string_to_bool exits on invalid value"""
|
|
442
|
+
with patch("sys.exit") as mock_exit:
|
|
443
|
+
convert_string_to_bool("[unclosed_list")
|
|
444
|
+
mock_exit.assert_called_once_with("Invalid entry: [unclosed_list")
|