1
0
mirror of https://github.com/craigerl/aprsd.git synced 2026-06-07 08:34:40 -04:00

Added unit tests

This commit is contained in:
2025-12-09 17:20:23 -05:00
parent 2b2dbb114b
commit d0dfaa42e6
35 changed files with 5426 additions and 48 deletions
+221
View File
@@ -0,0 +1,221 @@
import datetime
import unittest
from unittest import mock
from aprsd.client.drivers.kiss_common import KISSDriver
from tests import fake
class ConcreteKISSDriver(KISSDriver):
"""Concrete implementation of KISSDriver for testing."""
def __init__(self):
super().__init__()
self.transport = 'test'
self.path = '/dev/test'
def read_frame(self):
"""Implementation of abstract method."""
return None
class TestKISSDriver(unittest.TestCase):
"""Unit tests for the KISSDriver class."""
def setUp(self):
"""Set up test fixtures."""
self.driver = ConcreteKISSDriver()
def tearDown(self):
"""Clean up after tests."""
pass
def test_init(self):
"""Test initialization."""
self.assertFalse(self.driver._connected)
self.assertIsInstance(self.driver.keepalive, datetime.datetime)
self.assertEqual(self.driver.select_timeout, 1)
self.assertEqual(self.driver.packets_received, 0)
self.assertEqual(self.driver.packets_sent, 0)
def test_login_success_not_connected(self):
"""Test login_success() when not connected."""
self.driver._connected = False
self.assertFalse(self.driver.login_success())
def test_login_success_connected(self):
"""Test login_success() when connected."""
self.driver._connected = True
self.assertTrue(self.driver.login_success())
def test_login_failure(self):
"""Test login_failure() method."""
result = self.driver.login_failure()
self.assertEqual(result, 'Login successful')
def test_set_filter(self):
"""Test set_filter() method."""
# Should not raise exception (no-op for KISS)
self.driver.set_filter('test filter')
def test_filter_property(self):
"""Test filter property."""
result = self.driver.filter
self.assertEqual(result, '')
def test_is_alive_not_connected(self):
"""Test is_alive property when not connected."""
self.driver._connected = False
self.assertFalse(self.driver.is_alive)
def test_is_alive_connected(self):
"""Test is_alive property when connected."""
self.driver._connected = True
self.assertTrue(self.driver.is_alive)
def test_handle_fend(self):
"""Test _handle_fend() method."""
from kiss import util as kissutil
buffer = b'\x00test_data'
with mock.patch.object(kissutil, 'recover_special_codes') as mock_recover:
with mock.patch.object(kissutil, 'strip_nmea') as mock_strip:
with mock.patch.object(kissutil, 'strip_df_start') as mock_strip_df:
mock_strip.return_value = buffer
mock_recover.return_value = buffer
mock_strip_df.return_value = b'test_data'
result = self.driver._handle_fend(buffer, strip_df_start=True)
self.assertIsInstance(result, bytes)
def test_fix_raw_frame(self):
"""Test fix_raw_frame() method."""
raw_frame = b'\xc0\x00test_data\xc0'
with mock.patch.object(self.driver, '_handle_fend') as mock_handle:
mock_handle.return_value = b'fixed_frame'
result = self.driver.fix_raw_frame(raw_frame)
self.assertEqual(result, b'fixed_frame')
# Should call _handle_fend with ax25_data (without KISS markers)
mock_handle.assert_called()
def test_decode_packet(self):
"""Test decode_packet() method."""
frame = b'test_frame'
mock_aprs_data = {'from': 'TEST', 'to': 'APRS'}
mock_packet = fake.fake_packet()
with mock.patch('aprsd.client.drivers.kiss_common.aprslib.parse') as mock_parse:
with mock.patch(
'aprsd.client.drivers.kiss_common.core.factory'
) as mock_factory:
mock_parse.return_value = mock_aprs_data
mock_factory.return_value = mock_packet
result = self.driver.decode_packet(frame=frame)
self.assertEqual(result, mock_packet)
mock_parse.assert_called_with(str(frame))
def test_decode_packet_no_frame(self):
"""Test decode_packet() with no frame."""
with mock.patch('aprsd.client.drivers.kiss_common.LOG') as mock_log:
result = self.driver.decode_packet()
self.assertIsNone(result)
mock_log.warning.assert_called()
def test_decode_packet_exception(self):
"""Test decode_packet() with exception."""
frame = b'test_frame'
with mock.patch('aprsd.client.drivers.kiss_common.aprslib.parse') as mock_parse:
mock_parse.side_effect = Exception('Parse error')
with mock.patch('aprsd.client.drivers.kiss_common.LOG') as mock_log:
result = self.driver.decode_packet(frame=frame)
self.assertIsNone(result)
mock_log.error.assert_called()
def test_decode_packet_third_party(self):
"""Test decode_packet() with ThirdPartyPacket."""
from aprsd.packets import core
frame = b'test_frame'
mock_aprs_data = {'from': 'TEST', 'to': 'APRS'}
# Create a ThirdPartyPacket
third_party = core.ThirdPartyPacket(
from_call='TEST', to_call='APRS', subpacket=fake.fake_packet()
)
with mock.patch('aprsd.client.drivers.kiss_common.aprslib.parse') as mock_parse:
with mock.patch(
'aprsd.client.drivers.kiss_common.core.factory'
) as mock_factory:
mock_parse.return_value = mock_aprs_data
mock_factory.return_value = third_party
result = self.driver.decode_packet(frame=frame)
self.assertEqual(result, third_party.subpacket)
def test_consumer_not_connected(self):
"""Test consumer() when not connected."""
self.driver._connected = False
callback = mock.MagicMock()
result = self.driver.consumer(callback)
self.assertIsNone(result)
callback.assert_not_called()
def test_consumer_connected(self):
"""Test consumer() when connected."""
self.driver._connected = True
callback = mock.MagicMock()
mock_frame = b'test_frame'
with mock.patch.object(self.driver, 'read_frame', return_value=mock_frame):
with mock.patch('aprsd.client.drivers.kiss_common.LOG'):
self.driver.consumer(callback)
callback.assert_called()
def test_read_frame_not_implemented(self):
"""Test read_frame() raises NotImplementedError."""
driver = KISSDriver()
with self.assertRaises(NotImplementedError):
driver.read_frame()
def test_stats(self):
"""Test stats() method."""
self.driver._connected = True
self.driver.packets_sent = 5
self.driver.packets_received = 10
self.driver.last_packet_sent = datetime.datetime.now()
self.driver.last_packet_received = datetime.datetime.now()
stats = self.driver.stats()
self.assertIn('client', stats)
self.assertIn('transport', stats)
self.assertIn('connected', stats)
self.assertIn('packets_sent', stats)
self.assertIn('packets_received', stats)
self.assertEqual(stats['packets_sent'], 5)
self.assertEqual(stats['packets_received'], 10)
def test_stats_serializable(self):
"""Test stats() with serializable=True."""
self.driver._connected = True
self.driver.last_packet_sent = datetime.datetime.now()
self.driver.last_packet_received = datetime.datetime.now()
stats = self.driver.stats(serializable=True)
self.assertIsInstance(stats['last_packet_sent'], str)
self.assertIsInstance(stats['last_packet_received'], str)
self.assertIsInstance(stats['connection_keepalive'], str)
def test_stats_none_times(self):
"""Test stats() with None times."""
self.driver.last_packet_sent = None
self.driver.last_packet_received = None
stats = self.driver.stats(serializable=True)
self.assertEqual(stats['last_packet_sent'], 'None')
self.assertEqual(stats['last_packet_received'], 'None')
+386
View File
@@ -0,0 +1,386 @@
import unittest
from unittest import mock
from aprsd.client.client import APRSDClient
from aprsd.client.drivers.registry import DriverRegistry
from aprsd.packets import core
from tests.mock_client_driver import MockClientDriver
class TestAPRSDClient(unittest.TestCase):
"""Unit tests for the APRSDClient class."""
def setUp(self):
"""Set up test fixtures."""
# Reset singleton instances
APRSDClient._instance = None
APRSDClient.driver = None
# Reset DriverRegistry singleton - the singleton decorator stores instance here
DriverRegistry.instance = None
# Mock APRSISDriver to prevent it from being checked
self.aprsis_patcher = mock.patch('aprsd.client.drivers.aprsis.APRSISDriver')
mock_aprsis_class = self.aprsis_patcher.start()
mock_aprsis_class.is_enabled.return_value = False
mock_aprsis_class.is_configured.return_value = False
self.mock_driver = MockClientDriver()
# Create a mock registry instance
mock_registry_instance = mock.MagicMock()
mock_registry_instance.get_driver.return_value = self.mock_driver
# Patch DriverRegistry to return our mock instance
self.registry_patcher = mock.patch(
'aprsd.client.client.DriverRegistry', return_value=mock_registry_instance
)
self.mock_registry = self.registry_patcher.start()
def tearDown(self):
"""Clean up after tests."""
if hasattr(APRSDClient, '_instance'):
if APRSDClient._instance:
APRSDClient._instance.close()
APRSDClient._instance = None
APRSDClient.driver = None
self.registry_patcher.stop()
self.aprsis_patcher.stop()
def test_singleton_pattern(self):
"""Test that APRSDClient is a singleton."""
client1 = APRSDClient(auto_connect=False)
client2 = APRSDClient(auto_connect=False)
self.assertIs(client1, client2)
self.assertEqual(id(client1), id(client2))
def test_init_with_auto_connect(self):
"""Test initialization with auto_connect=True."""
client = APRSDClient(auto_connect=True)
# Should have called setup_connection
self.assertIsNotNone(client.driver)
def test_init_without_auto_connect(self):
"""Test initialization with auto_connect=False."""
client = APRSDClient(auto_connect=False)
self.assertIsNotNone(client.driver)
self.assertFalse(client.connected)
def test_stats(self):
"""Test stats() method."""
client = APRSDClient(auto_connect=False)
stats = client.stats()
self.assertIsInstance(stats, dict)
stats_serializable = client.stats(serializable=True)
self.assertIsInstance(stats_serializable, dict)
def test_stats_no_driver(self):
"""Test stats() when driver is None."""
client = APRSDClient(auto_connect=False)
client.driver = None
stats = client.stats()
self.assertEqual(stats, {})
def test_is_enabled(self):
"""Test is_enabled() static method."""
# Stop the registry patcher temporarily to use real registry
self.registry_patcher.stop()
try:
# Reset singleton
DriverRegistry.instance = None
registry = DriverRegistry()
mock_driver_class = mock.MagicMock()
mock_driver_class.is_enabled.return_value = True
registry.drivers = [mock_driver_class]
result = APRSDClient.is_enabled()
self.assertTrue(result)
finally:
# Restart the patcher
self.registry_patcher.start()
def test_is_enabled_no_drivers(self):
"""Test is_enabled() with no drivers."""
# Stop the registry patcher temporarily to use real registry
self.registry_patcher.stop()
try:
# Reset singleton
DriverRegistry.instance = None
registry = DriverRegistry()
registry.drivers = []
result = APRSDClient.is_enabled()
self.assertFalse(result)
finally:
# Restart the patcher
self.registry_patcher.start()
def test_is_configured(self):
"""Test is_configured() static method."""
# Stop the registry patcher temporarily to use real registry
self.registry_patcher.stop()
try:
# Reset singleton
DriverRegistry.instance = None
registry = DriverRegistry()
mock_driver_class = mock.MagicMock()
mock_driver_class.is_enabled.return_value = True
mock_driver_class.is_configured.return_value = True
registry.drivers = [mock_driver_class]
result = APRSDClient.is_configured()
self.assertTrue(result)
finally:
# Restart the patcher
self.registry_patcher.start()
def test_is_configured_no_drivers(self):
"""Test is_configured() with no drivers."""
# Stop the registry patcher temporarily to use real registry
self.registry_patcher.stop()
try:
# Reset singleton
DriverRegistry.instance = None
registry = DriverRegistry()
registry.drivers = []
result = APRSDClient.is_configured()
self.assertFalse(result)
finally:
# Restart the patcher
self.registry_patcher.start()
def test_login_success_property(self):
"""Test login_success property."""
client = APRSDClient(auto_connect=False)
self.mock_driver.login_status['success'] = True
self.assertTrue(client.login_success)
self.mock_driver.login_status['success'] = False
self.assertFalse(client.login_success)
def test_login_success_no_driver(self):
"""Test login_success property when driver is None."""
client = APRSDClient(auto_connect=False)
client.driver = None
self.assertFalse(client.login_success)
def test_login_failure_property(self):
"""Test login_failure property."""
client = APRSDClient(auto_connect=False)
self.mock_driver.login_status['message'] = 'Test failure'
self.assertEqual(client.login_failure, 'Test failure')
def test_login_failure_no_driver(self):
"""Test login_failure property when driver is None."""
client = APRSDClient(auto_connect=False)
client.driver = None
self.assertIsNone(client.login_failure)
def test_set_filter(self):
"""Test set_filter() method."""
client = APRSDClient(auto_connect=False)
filter_str = 'test filter'
client.set_filter(filter_str)
self.assertEqual(client.filter, filter_str)
self.assertEqual(self.mock_driver.filter, filter_str)
def test_set_filter_no_driver(self):
"""Test set_filter() when driver is None."""
client = APRSDClient(auto_connect=False)
client.driver = None
filter_str = 'test filter'
client.set_filter(filter_str)
self.assertEqual(client.filter, filter_str)
def test_get_filter(self):
"""Test get_filter() method."""
client = APRSDClient(auto_connect=False)
filter_str = 'test filter'
client.set_filter(filter_str)
# get_filter returns driver.filter, not client.filter
self.mock_driver.filter = filter_str
result = client.get_filter()
self.assertEqual(result, filter_str)
def test_get_filter_no_driver(self):
"""Test get_filter() when driver is None."""
client = APRSDClient(auto_connect=False)
client.driver = None
result = client.get_filter()
self.assertIsNone(result)
def test_is_alive(self):
"""Test is_alive() method."""
client = APRSDClient(auto_connect=False)
self.mock_driver._alive = True
self.assertTrue(client.is_alive())
self.mock_driver._alive = False
self.assertFalse(client.is_alive())
def test_connect(self):
"""Test connect() method."""
client = APRSDClient(auto_connect=False)
self.assertFalse(client.connected)
# Make sure driver.is_alive returns True after setup_connection
self.mock_driver._alive = True
client.connect()
self.assertTrue(client.connected)
self.assertTrue(client.running)
def test_connect_already_connected(self):
"""Test connect() when already connected."""
client = APRSDClient(auto_connect=False)
client.connected = True
client.connect()
# Should still be connected
self.assertTrue(client.connected)
def test_connect_no_driver(self):
"""Test connect() when driver is None."""
client = APRSDClient(auto_connect=False)
client.driver = None
client.connect()
# Should get a driver from registry
self.assertIsNotNone(client.driver)
def test_close(self):
"""Test close() method."""
client = APRSDClient(auto_connect=False)
client.connected = True
client.running = True
client.close()
self.assertFalse(client.connected)
self.assertFalse(client.running)
self.mock_driver.close.assert_called()
def test_close_no_driver(self):
"""Test close() when driver is None."""
client = APRSDClient(auto_connect=False)
client.driver = None
client.connected = True
client.close()
self.assertFalse(client.connected)
def test_reset(self):
"""Test reset() method."""
client = APRSDClient(auto_connect=False)
client.connected = True
client.filter = 'test filter'
client.auto_connect = True
client.reset()
self.mock_driver.close.assert_called()
self.mock_driver.setup_connection.assert_called()
self.mock_driver.set_filter.assert_called_with('test filter')
def test_reset_no_driver(self):
"""Test reset() when driver is None."""
client = APRSDClient(auto_connect=False)
client.driver = None
# Should not raise exception
client.reset()
def test_reset_no_auto_connect(self):
"""Test reset() with auto_connect=False."""
client = APRSDClient(auto_connect=False)
client.auto_connect = False
client.reset()
self.mock_driver.close.assert_called()
# Should not call setup_connection
self.mock_driver.setup_connection.assert_not_called()
def test_send(self):
"""Test send() method."""
client = APRSDClient(auto_connect=False)
client.running = True
packet = mock.MagicMock(spec=core.Packet)
result = client.send(packet)
self.assertTrue(result)
self.mock_driver.send.assert_called_with(packet)
def test_send_not_running(self):
"""Test send() when not running."""
client = APRSDClient(auto_connect=False)
client.running = False
packet = mock.MagicMock(spec=core.Packet)
result = client.send(packet)
self.assertFalse(result)
self.mock_driver.send.assert_not_called()
def test_consumer(self):
"""Test consumer() method."""
client = APRSDClient(auto_connect=False)
client.running = True
callback = mock.MagicMock()
client.consumer(callback, raw=True)
self.mock_driver.consumer.assert_called_with(callback=callback, raw=True)
def test_consumer_not_running(self):
"""Test consumer() when not running."""
client = APRSDClient(auto_connect=False)
client.running = False
callback = mock.MagicMock()
result = client.consumer(callback)
self.assertIsNone(result)
self.mock_driver.consumer.assert_not_called()
def test_decode_packet(self):
"""Test decode_packet() method."""
client = APRSDClient(auto_connect=False)
packet = mock.MagicMock(spec=core.Packet)
# Configure the side_effect to return our packet
self.mock_driver.decode_packet.side_effect = lambda *args, **kwargs: packet
result = client.decode_packet(frame='test')
self.assertEqual(result, packet)
self.mock_driver.decode_packet.assert_called_with(frame='test')
def test_decode_packet_exception(self):
"""Test decode_packet() with exception."""
client = APRSDClient(auto_connect=False)
self.mock_driver.decode_packet.side_effect = Exception('Decode error')
result = client.decode_packet(frame='test')
self.assertIsNone(result)
def test_keepalive_check(self):
"""Test keepalive_check() method."""
client = APRSDClient(auto_connect=False)
client._checks = False
self.mock_driver._alive = True
# First check should not reset
with mock.patch.object(client, 'reset') as mock_reset:
client.keepalive_check()
self.assertTrue(client._checks)
mock_reset.assert_not_called()
# Second check with dead driver should reset
self.mock_driver._alive = False
with mock.patch.object(client, 'reset') as mock_reset:
client.keepalive_check()
mock_reset.assert_called()
def test_keepalive_log(self):
"""Test keepalive_log() method."""
import datetime
client = APRSDClient(auto_connect=False)
self.mock_driver._keepalive = datetime.datetime.now()
with mock.patch('aprsd.client.client.LOGU') as mock_logu:
client.keepalive_log()
mock_logu.opt.assert_called()
def test_keepalive_log_no_keepalive(self):
"""Test keepalive_log() when keepalive is None."""
client = APRSDClient(auto_connect=False)
self.mock_driver._keepalive = None
with mock.patch('aprsd.client.client.LOGU') as mock_logu:
client.keepalive_log()
mock_logu.opt.assert_called()
+16 -1
View File
@@ -11,7 +11,7 @@ class TestDriverRegistry(unittest.TestCase):
def setUp(self):
# Reset the singleton instance before each test
DriverRegistry._singleton_instances = {}
DriverRegistry.instance = None
self.registry = DriverRegistry()
self.registry.drivers = []
@@ -32,11 +32,26 @@ class TestDriverRegistry(unittest.TestCase):
mock_conf.aprs_network.password = 'dummy'
mock_conf.aprs_network.login = 'dummy'
# Patch the register method to skip Protocol check for MockClientDriver
self._original_register = self.registry.register
def mock_register(driver):
# Skip Protocol check for MockClientDriver
if hasattr(driver, '__name__') and driver.__name__ == 'MockClientDriver':
self.registry.drivers.append(driver)
else:
self._original_register(driver)
self.registry.register = mock_register
def tearDown(self):
# Reset the singleton instance after each test
DriverRegistry().drivers = []
self.aprsis_patcher.stop()
self.conf_patcher.stop()
# Restore original register method if it was patched
if hasattr(self, '_original_register'):
self.registry.register = self._original_register
def test_get_driver_with_valid_driver(self):
"""Test getting an enabled and configured driver."""