mirror of
https://github.com/craigerl/aprsd.git
synced 2026-01-13 09:07:35 -05:00
272 lines
9.6 KiB
Python
272 lines
9.6 KiB
Python
import os
|
|
import shutil
|
|
import tempfile
|
|
import unittest
|
|
from unittest import mock
|
|
|
|
from aprsd import utils
|
|
|
|
|
|
class TestUtils(unittest.TestCase):
|
|
"""Unit tests for utility functions in aprsd.utils."""
|
|
|
|
def test_singleton_decorator(self):
|
|
"""Test singleton() decorator."""
|
|
|
|
@utils.singleton
|
|
class TestClass:
|
|
def __init__(self):
|
|
self.value = 42
|
|
|
|
instance1 = TestClass()
|
|
instance2 = TestClass()
|
|
|
|
self.assertIs(instance1, instance2)
|
|
self.assertEqual(instance1.value, 42)
|
|
|
|
def test_env(self):
|
|
"""Test env() function."""
|
|
# Test with existing environment variable
|
|
os.environ['TEST_VAR'] = 'test_value'
|
|
result = utils.env('TEST_VAR')
|
|
self.assertEqual(result, 'test_value')
|
|
|
|
# Test with non-existent variable
|
|
result = utils.env('NON_EXISTENT_VAR')
|
|
self.assertEqual(result, '')
|
|
|
|
# Test with default
|
|
result = utils.env('NON_EXISTENT_VAR2', default='default_value')
|
|
self.assertEqual(result, 'default_value')
|
|
|
|
# Cleanup
|
|
del os.environ['TEST_VAR']
|
|
|
|
def test_env_multiple_vars(self):
|
|
"""Test env() with multiple variables."""
|
|
os.environ['VAR1'] = 'value1'
|
|
result = utils.env('VAR1', 'VAR2', 'VAR3')
|
|
self.assertEqual(result, 'value1')
|
|
|
|
del os.environ['VAR1']
|
|
|
|
def test_mkdir_p(self):
|
|
"""Test mkdir_p() function."""
|
|
temp_dir = tempfile.mkdtemp()
|
|
test_path = os.path.join(temp_dir, 'test', 'nested', 'dir')
|
|
|
|
try:
|
|
utils.mkdir_p(test_path)
|
|
self.assertTrue(os.path.isdir(test_path))
|
|
|
|
# Should not raise exception if directory exists
|
|
utils.mkdir_p(test_path)
|
|
self.assertTrue(os.path.isdir(test_path))
|
|
finally:
|
|
shutil.rmtree(temp_dir)
|
|
|
|
def test_insert_str(self):
|
|
"""Test insert_str() function."""
|
|
result = utils.insert_str('hello', ' world', 5)
|
|
self.assertEqual(result, 'hello world')
|
|
|
|
result = utils.insert_str('test', 'X', 0)
|
|
self.assertEqual(result, 'Xtest')
|
|
|
|
result = utils.insert_str('test', 'X', 4)
|
|
self.assertEqual(result, 'testX')
|
|
|
|
def test_end_substr(self):
|
|
"""Test end_substr() function."""
|
|
result = utils.end_substr('hello world', 'hello')
|
|
self.assertEqual(result, 5)
|
|
|
|
result = utils.end_substr('test', 'notfound')
|
|
self.assertEqual(result, -1)
|
|
|
|
result = utils.end_substr('abc', 'abc')
|
|
self.assertEqual(result, 3)
|
|
|
|
def test_rgb_from_name(self):
|
|
"""Test rgb_from_name() function."""
|
|
rgb = utils.rgb_from_name('test')
|
|
self.assertIsInstance(rgb, tuple)
|
|
self.assertEqual(len(rgb), 3)
|
|
self.assertGreaterEqual(rgb[0], 0)
|
|
self.assertLessEqual(rgb[0], 255)
|
|
self.assertGreaterEqual(rgb[1], 0)
|
|
self.assertLessEqual(rgb[1], 255)
|
|
self.assertGreaterEqual(rgb[2], 0)
|
|
self.assertLessEqual(rgb[2], 255)
|
|
|
|
# Same name should produce same RGB
|
|
rgb1 = utils.rgb_from_name('test')
|
|
rgb2 = utils.rgb_from_name('test')
|
|
self.assertEqual(rgb1, rgb2)
|
|
|
|
def test_hextriplet(self):
|
|
"""Test hextriplet() function."""
|
|
result = utils.hextriplet((255, 0, 128))
|
|
self.assertEqual(result, '#FF0080')
|
|
|
|
result = utils.hextriplet((0, 0, 0))
|
|
self.assertEqual(result, '#000000')
|
|
|
|
result = utils.hextriplet((255, 255, 255))
|
|
self.assertEqual(result, '#FFFFFF')
|
|
|
|
def test_hex_from_name(self):
|
|
"""Test hex_from_name() function."""
|
|
hex_color = utils.hex_from_name('test')
|
|
self.assertIsInstance(hex_color, str)
|
|
self.assertTrue(hex_color.startswith('#'))
|
|
self.assertEqual(len(hex_color), 7)
|
|
|
|
# Same name should produce same hex
|
|
hex1 = utils.hex_from_name('test')
|
|
hex2 = utils.hex_from_name('test')
|
|
self.assertEqual(hex1, hex2)
|
|
|
|
def test_human_size(self):
|
|
"""Test human_size() function."""
|
|
result = utils.human_size(1024)
|
|
self.assertIn('KB', result)
|
|
|
|
result = utils.human_size(512)
|
|
self.assertIn('bytes', result)
|
|
|
|
result = utils.human_size(1024 * 1024)
|
|
self.assertIn('MB', result)
|
|
|
|
def test_strfdelta(self):
|
|
"""Test strfdelta() function."""
|
|
import datetime
|
|
|
|
delta = datetime.timedelta(hours=1, minutes=30, seconds=45)
|
|
result = utils.strfdelta(delta)
|
|
self.assertIn('01', result)
|
|
self.assertIn('30', result)
|
|
self.assertIn('45', result)
|
|
|
|
delta = datetime.timedelta(days=1, hours=2, minutes=30, seconds=15)
|
|
result = utils.strfdelta(delta)
|
|
self.assertIn('1 days', result)
|
|
|
|
def test_flatten_dict(self):
|
|
"""Test flatten_dict() function."""
|
|
nested = {'a': 1, 'b': {'c': 2, 'd': {'e': 3}}}
|
|
result = utils.flatten_dict(nested)
|
|
self.assertIn('a', result)
|
|
self.assertIn('b.c', result)
|
|
self.assertIn('b.d.e', result)
|
|
self.assertEqual(result['a'], 1)
|
|
self.assertEqual(result['b.c'], 2)
|
|
self.assertEqual(result['b.d.e'], 3)
|
|
|
|
def test_flatten_dict_custom_sep(self):
|
|
"""Test flatten_dict() with custom separator."""
|
|
nested = {'a': {'b': 1}}
|
|
result = utils.flatten_dict(nested, sep='_')
|
|
self.assertIn('a_b', result)
|
|
|
|
def test_parse_delta_str(self):
|
|
"""Test parse_delta_str() function."""
|
|
result = utils.parse_delta_str('1:30:45')
|
|
self.assertIn('hours', result)
|
|
self.assertIn('minutes', result)
|
|
self.assertIn('seconds', result)
|
|
self.assertEqual(result['hours'], 1.0)
|
|
self.assertEqual(result['minutes'], 30.0)
|
|
self.assertEqual(result['seconds'], 45.0)
|
|
|
|
result = utils.parse_delta_str('1 day, 2:30:15')
|
|
self.assertIn('days', result)
|
|
self.assertEqual(result['days'], 1.0)
|
|
|
|
def test_parse_delta_str_invalid(self):
|
|
"""Test parse_delta_str() with invalid input."""
|
|
result = utils.parse_delta_str('invalid')
|
|
self.assertEqual(result, {})
|
|
|
|
def test_calculate_initial_compass_bearing(self):
|
|
"""Test calculate_initial_compass_bearing() function."""
|
|
point_a = (40.7128, -74.0060) # New York
|
|
point_b = (34.0522, -118.2437) # Los Angeles
|
|
|
|
bearing = utils.calculate_initial_compass_bearing(point_a, point_b)
|
|
self.assertGreaterEqual(bearing, 0)
|
|
self.assertLessEqual(bearing, 360)
|
|
|
|
# Same point should have undefined bearing, but function should handle it
|
|
bearing = utils.calculate_initial_compass_bearing(point_a, point_a)
|
|
self.assertIsInstance(bearing, float)
|
|
|
|
def test_calculate_initial_compass_bearing_invalid(self):
|
|
"""Test calculate_initial_compass_bearing() with invalid input."""
|
|
with self.assertRaises(TypeError):
|
|
utils.calculate_initial_compass_bearing([1, 2], (3, 4))
|
|
|
|
def test_degrees_to_cardinal(self):
|
|
"""Test degrees_to_cardinal() function."""
|
|
self.assertEqual(utils.degrees_to_cardinal(0), 'N')
|
|
self.assertEqual(utils.degrees_to_cardinal(90), 'E')
|
|
self.assertEqual(utils.degrees_to_cardinal(180), 'S')
|
|
self.assertEqual(utils.degrees_to_cardinal(270), 'W')
|
|
self.assertEqual(utils.degrees_to_cardinal(45), 'NE')
|
|
|
|
def test_degrees_to_cardinal_full_string(self):
|
|
"""Test degrees_to_cardinal() with full_string=True."""
|
|
self.assertEqual(utils.degrees_to_cardinal(0, full_string=True), 'North')
|
|
self.assertEqual(utils.degrees_to_cardinal(90, full_string=True), 'East')
|
|
self.assertEqual(utils.degrees_to_cardinal(180, full_string=True), 'South')
|
|
self.assertEqual(utils.degrees_to_cardinal(270, full_string=True), 'West')
|
|
|
|
def test_aprs_passcode(self):
|
|
"""Test aprs_passcode() function."""
|
|
passcode = utils.aprs_passcode('N0CALL')
|
|
self.assertIsInstance(passcode, int)
|
|
self.assertGreaterEqual(passcode, 0)
|
|
self.assertLessEqual(passcode, 0x7FFF)
|
|
|
|
# Same callsign should produce same passcode
|
|
passcode1 = utils.aprs_passcode('N0CALL')
|
|
passcode2 = utils.aprs_passcode('N0CALL')
|
|
self.assertEqual(passcode1, passcode2)
|
|
|
|
# Different callsigns should produce different passcodes
|
|
passcode3 = utils.aprs_passcode('K1ABC')
|
|
self.assertNotEqual(passcode1, passcode3)
|
|
|
|
def test_aprs_passcode_with_ssid(self):
|
|
"""Test aprs_passcode() with SSID."""
|
|
passcode1 = utils.aprs_passcode('N0CALL-1')
|
|
passcode2 = utils.aprs_passcode('N0CALL')
|
|
self.assertEqual(passcode1, passcode2)
|
|
|
|
def test_load_entry_points(self):
|
|
"""Test load_entry_points() function."""
|
|
# Should not raise exception even with non-existent group
|
|
utils.load_entry_points('nonexistent.group')
|
|
|
|
@mock.patch('aprsd.utils.update_checker.UpdateChecker')
|
|
def test_check_version(self, mock_checker):
|
|
"""Test _check_version() function."""
|
|
mock_instance = mock.MagicMock()
|
|
mock_instance.check.return_value = None
|
|
mock_checker.return_value = mock_instance
|
|
|
|
level, msg = utils._check_version()
|
|
self.assertEqual(level, 0)
|
|
self.assertIn('up to date', msg)
|
|
|
|
@mock.patch('aprsd.utils.update_checker.UpdateChecker')
|
|
def test_check_version_update_available(self, mock_checker):
|
|
"""Test _check_version() when update is available."""
|
|
mock_instance = mock.MagicMock()
|
|
mock_instance.check.return_value = 'New version available'
|
|
mock_checker.return_value = mock_instance
|
|
|
|
level, msg = utils._check_version()
|
|
self.assertEqual(level, 1)
|
|
self.assertEqual(msg, 'New version available')
|