archey4/archey/test/entries/test_archey_uptime.py
Michael Bromilow 641dcd21ad
Entries: Use __iter__ and __next__ magic methods.
Incl. changed tests where appropriate.
2024-04-08 22:23:10 +01:00

246 lines
11 KiB
Python

"""Test module for Archey's uptime detection module"""
import unittest
from datetime import timedelta
from itertools import product
from unittest.mock import mock_open, patch
from archey.entries.uptime import Uptime
from archey.exceptions import ArcheyException
from archey.test.entries import HelperMethods
class TestUptimeEntry(unittest.TestCase):
"""Test cases for `Uptime` entry module"""
@patch(
"archey.entries.uptime.open",
mock_open(read_data="90120.75 XXXX.XX\n"),
)
def test_proc_file_uptime(self):
"""Test `_proc_file_uptime` static method"""
self.assertEqual(
Uptime._proc_file_uptime(), # pylint: disable=protected-access
timedelta(seconds=90120.75),
)
@patch(
"archey.entries.uptime.time.CLOCK_BOOTTIME", # Ensure first `getattr` call succeeds anyhow.
create=True, # Required for Python < 3.7.
)
@patch(
"archey.entries.uptime.time.clock_gettime", # Ensure `clock_gettime` call succeeds anyhow.
return_value=1000,
create=True,
)
def test_clock_uptime(self, _, __):
"""
Test `_clock_uptime` static method`.
We only test one clock as all clocks rely on the same built-in `time.clock_gettime` method.
"""
self.assertEqual(
Uptime._clock_uptime(), # pylint: disable=protected-access
timedelta(seconds=1000),
)
@patch("archey.entries.uptime.run")
def test_parse_uptime_cmd(self, run_mock):
"""Test `_parse_uptime_cmd` static method"""
# Create an uptime instance to perform testing.
# It doesn't matter that its `__init__` will be called.
uptime_instance_mock = HelperMethods.entry_mock(Uptime)
# Keys: `uptime` outputs; values: expected `timedelta` instances.
# We will test these with various time formats (and various numbers of users).
# pylint: disable=line-too-long
test_uptime_cases = {
# Recently booted:
"{time} up 0 sec, {user_loadavg}": timedelta(seconds=0), # BSD, just booted
"{time} up 1 sec, {user_loadavg}": timedelta(seconds=1), # BSD, < 1 min uptime
"{time} up 12 secs, {user_loadavg}": timedelta(seconds=12), # BSD, < 1 min uptime
"{time} up 0 min, {user_loadavg}": timedelta(minutes=0), # Linux, < 1 min uptime
"{time} up 1 min, {user_loadavg}": timedelta(minutes=1),
# 1 min to 1 day
"{time} up 12 mins, {user_loadavg}": timedelta(minutes=12),
## Variation without plural minutes
"{time} up 12 min, {user_loadavg}": timedelta(minutes=12),
"{time} up 1:00, {user_loadavg}": timedelta(hours=1),
"{time} up 1:01, {user_loadavg}": timedelta(hours=1, minutes=1),
"{time} up 1:23, {user_loadavg}": timedelta(hours=1, minutes=23),
"{time} up 12:34, {user_loadavg}": timedelta(hours=12, minutes=34),
# 1 day to 2 days
"{time} up 1 day, 0 sec, {user_loadavg}": timedelta(days=1), # BSD
"{time} up 1 day, 1 sec, {user_loadavg}": timedelta(days=1, seconds=1), # BSD
"{time} up 1 day, 12 secs, {user_loadavg}": timedelta(days=1, seconds=12), # BSD
"{time} up 1 day, 0 min, {user_loadavg}": timedelta(days=1), # Linux
"{time} up 1 day, 1 min, {user_loadavg}": timedelta(days=1, minutes=1),
"{time} up 1 day, 12 mins, {user_loadavg}": timedelta(days=1, minutes=12),
## Variation without plural minutes
"{time} up 1 day, 12 min, {user_loadavg}": timedelta(days=1, minutes=12),
"{time} up 1 day, 1:00, {user_loadavg}": timedelta(days=1, hours=1),
"{time} up 1 day, 1:01, {user_loadavg}": timedelta(days=1, hours=1, minutes=1),
"{time} up 1 day, 1:23, {user_loadavg}": timedelta(days=1, hours=1, minutes=23),
"{time} up 1 day, 12:34, {user_loadavg}": timedelta(days=1, hours=12, minutes=34),
# 2 days onwards
"{time} up 12 days, 0 sec, {user_loadavg}": timedelta(days=12), # BSD
"{time} up 12 days, 1 sec, {user_loadavg}": timedelta(days=12, seconds=1), # BSD
"{time} up 12 days, 12 secs, {user_loadavg}": timedelta(days=12, seconds=12), # BSD
"{time} up 12 days, 0 min, {user_loadavg}": timedelta(days=12), # Linux
"{time} up 12 days, 1 min, {user_loadavg}": timedelta(days=12, minutes=1),
"{time} up 12 days, 12 mins, {user_loadavg}": timedelta(days=12, minutes=12),
## Variation without plural minutes
"{time} up 12 day, 12 min, {user_loadavg}": timedelta(days=12, minutes=12),
"{time} up 12 days, 1:00, {user_loadavg}": timedelta(days=12, hours=1),
"{time} up 12 days, 1:01, {user_loadavg}": timedelta(days=12, hours=1, minutes=1),
"{time} up 12 days, 1:23, {user_loadavg}": timedelta(days=12, hours=1, minutes=23),
"{time} up 12 days, 12:34, {user_loadavg}": timedelta(days=12, hours=12, minutes=34),
# Very long uptimes - sanity check :)
"{time} up 500 days, 0 sec, {user_loadavg}": timedelta(days=500), # BSD
"{time} up 500 days, 1 sec, {user_loadavg}": timedelta(days=500, seconds=1), # BSD
"{time} up 500 days, 12 secs, {user_loadavg}": timedelta(days=500, seconds=12), # BSD
"{time} up 500 days, 0 min, {user_loadavg}": timedelta(days=500), # Linux
"{time} up 500 days, 1 min, {user_loadavg}": timedelta(days=500, minutes=1),
"{time} up 500 days, 12 mins, {user_loadavg}": timedelta(days=500, minutes=12),
## Variation without plural minutes
"{time} up 500 day, 12 min, {user_loadavg}": timedelta(days=500, minutes=12),
"{time} up 500 days, 1:00, {user_loadavg}": timedelta(days=500, hours=1),
"{time} up 500 days, 1:01, {user_loadavg}": timedelta(days=500, hours=1, minutes=1),
"{time} up 500 days, 1:23, {user_loadavg}": timedelta(days=500, hours=1, minutes=23),
"{time} up 500 days, 12:34, {user_loadavg}": timedelta(days=500, hours=12, minutes=34),
}
# pylint: enable=line-too-long
# Variations of the time in the `{time}` section.
# These _should_ be avoided when we set the locale in `run`,
# however let's check we can handle them anyway, just in case.
time_variations = (
"0:00",
"9:43 ",
"11:37 ",
"19:21",
"23:59",
"12:00am",
"1:10am",
"1:10pm ",
"6:43pm ",
"8:26am ",
"03:14:15",
"09:26:12 ",
"23:19:20 ",
"nonsense_time ",
"hopefully_works_anyway ",
" even with strange spacing!",
)
# Variations of the user count and load average section.
# For this, we'll just combine user variations with a few load average variations.
user_variations = (
"1 user ",
"1 user ",
" 1 user, ",
" 1 user, ",
"2 users ",
"2 users ",
" 2 users, ",
" 2 users, ",
"15 users ",
"15 users ",
" 15 users, ",
" 15 users, ",
"150 users ",
"150 users ",
"150 users, ",
"150 users, ",
)
loadavg_variations = (
"load averages: 1.95 1.28 2.10",
"load average: 0.13, 0.17, 0.13",
"we never match this part so the content here",
" should not affect our parsing",
)
user_loadavg_variations = [
user + loadavg for user in user_variations for loadavg in loadavg_variations
]
for uptime_output, expected_delta in test_uptime_cases.items():
# We use `itertools.product` to get the permutations of our variations
# since there are a lot of them! (a list comprehension would be slower)
for variations in product(time_variations, user_loadavg_variations):
run_mock.return_value.stdout = uptime_output.format(
time=variations[0], user_loadavg=variations[1]
).encode()
self.assertEqual(
Uptime._parse_uptime_cmd( # pylint: disable=protected-access
uptime_instance_mock
),
expected_delta,
msg=(
"`uptime` output: "
+ uptime_output.format(time=variations[0], user_loadavg=variations[1])
),
)
# Check that our internal exception is correctly raised when `uptime` is not available.
run_mock.side_effect = FileNotFoundError()
self.assertRaises(
ArcheyException,
Uptime._parse_uptime_cmd, # pylint: disable=protected-access
uptime_instance_mock,
)
def test_various_output_cases(self):
"""Test when the device has just been started..."""
uptime_instance_mock = HelperMethods.entry_mock(Uptime)
with self.subTest("Output in case of hours and minutes."):
uptime_instance_mock.value = {
"days": 0,
"hours": 2,
"minutes": 1,
"seconds": 0,
}
self.assertEqual(
str(uptime_instance_mock),
"2 hours and 1 minute",
)
with self.subTest("Output in case of days, hours and minutes."):
uptime_instance_mock.value = {
"days": 1,
"hours": 1,
"minutes": 2,
"seconds": 0,
}
self.assertEqual(
str(uptime_instance_mock),
"1 day, 1 hour and 2 minutes",
)
with self.subTest("Output in case of days and minutes."):
uptime_instance_mock.value = {
"days": 3,
"hours": 0,
"minutes": 3,
"seconds": 0,
}
self.assertEqual(
str(uptime_instance_mock),
"3 days and 3 minutes",
)
with self.subTest("Output in case of very early execution."):
uptime_instance_mock.value = {
"days": 0,
"hours": 0,
"minutes": 0,
"seconds": 0,
}
self.assertEqual(
str(uptime_instance_mock),
"< 1 minute",
)
if __name__ == "__main__":
unittest.main()