From cd6ef631ba58ee382fb67852c464c8864eca0631 Mon Sep 17 00:00:00 2001 From: Lewis Chambers Date: Tue, 25 Jun 2024 16:22:26 +0100 Subject: [PATCH] Implemented session object --- src/iotswarm/session.py | 68 +++++++++++++++++++++++++ src/tests/test_sessions.py | 100 +++++++++++++++++++++++++++++++++++++ 2 files changed, 168 insertions(+) create mode 100644 src/iotswarm/session.py create mode 100644 src/tests/test_sessions.py diff --git a/src/iotswarm/session.py b/src/iotswarm/session.py new file mode 100644 index 0000000..545bb96 --- /dev/null +++ b/src/iotswarm/session.py @@ -0,0 +1,68 @@ +"""This package is for holding classes relevant to managing swarm sessions. +It should allow the state of a swarm to be restored from the point of failure""" + +from iotswarm.swarm import Swarm +import uuid + + +class Session: + """Represents the current session. Holds configuration necessary to the + SessionWriter.""" + + swarm: Swarm + """The swarm object in use.""" + + session_id: str + """Unique ID for the session""" + + def __init__(self, swarm: Swarm, session_id: str | None = None): + """Initialises the class. + + Args: + swarm: A Swarm object to track the state of. + session_id: A unique identifier of the session. Automatically assigned if not provided. + """ + + if not isinstance(swarm, Swarm): + raise TypeError(f'"swarm" must be a Swarm, not "{type(swarm)}".') + + self.swarm = swarm + + if session_id is not None: + self.session_id = str(session_id) + else: + self.session_id = self._build_session_id(swarm.name) + + def __repr__(self): + + return f'{self.__class__.__name__}({self.swarm}, "{self.session_id}")' + + def __str__(self): + + return f'{self.__class__.__name__}: "{self.session_id}"' + + @staticmethod + def _build_session_id(prefix: str | None = None): + """Builds a session ID with a prefix if requested. + + Args: + prefix: Adds a prefix to the ID for readability. + + Returns: + str: A session ID string. + """ + + session_id = str(uuid.uuid4()) + + if prefix is not None: + session_id = f"{prefix}-{session_id}" + + return session_id + + +class SessionWriter: + """Handles writing of the session state to file.""" + + +class SessionLoader: + """Loads a session, instantiates the swarm and devices.""" diff --git a/src/tests/test_sessions.py b/src/tests/test_sessions.py new file mode 100644 index 0000000..65be337 --- /dev/null +++ b/src/tests/test_sessions.py @@ -0,0 +1,100 @@ +import unittest +import pytest +from parameterized import parameterized +from iotswarm.session import Session +from iotswarm.swarm import Swarm +from iotswarm.devices import BaseDevice +from iotswarm.messaging.core import MockMessageConnection +from iotswarm.db import LoopingSQLite3, MockDB + + +class TestSession(unittest.TestCase): + """Tests the Session class""" + + @classmethod + def setUpClass(cls) -> None: + cls.devices = [ + BaseDevice(x, MockDB(), MockMessageConnection()) + for x in ["MORLY", "ALIC1", "SPENF", "RISEH"] + ] + + cls.swarm = Swarm(cls.devices, "test-swarm") + cls.maxDiff = None + + def test_instantiation(self): + """Tests that the class can be instantiated under normal conditions.""" + + session = Session(self.swarm) + + print(session) + + self.assertIsInstance(session.swarm, Swarm) + self.assertIsInstance(session.session_id, str) + + def test_error_if_bad_swarm_type(self): + + with self.assertRaises(TypeError): + Session("swarm?") + + def test_session_id_set_if_given(self): + """Tests that the session_id attribute is set when the argument is supplied.""" + session_id = "this-is-my-session" + session = Session(self.swarm, session_id=session_id) + + self.assertEqual(session.session_id, session_id) + + def test_session_id_assigned_if_not_given(self): + """Tests that an id is generated if not given.""" + + session = Session(self.swarm) + + self.assertTrue(session.session_id.startswith(self.swarm.name)) + + def test_session_id_method(self): + """Tests the _build_session_id method returns an ID""" + + value = Session._build_session_id() + + self.assertIsInstance(value, str) + self.assertEqual(len(value), 36) + + # string prefix + prefix = "my-swarm" + value = Session._build_session_id(prefix) + + self.assertTrue(value.startswith(f"my-swarm-")) + self.assertEqual(len(value), 36 + len(prefix) + 1) + + # number prefix + prefix = 12345 + value = Session._build_session_id(prefix) + + self.assertTrue(value.startswith(f"12345-")) + self.assertEqual(len(value), 36 + 6) + + @parameterized.expand([None, "my-swarm"]) + def test_repr(self, session_id): + + session = Session(self.swarm, session_id=session_id) + + if session_id is not None: + expected_start = ( + f'{session.__class__.__name__}({self.swarm}, "{session_id}"' + ) + else: + expected_start = expected_start = ( + f'{session.__class__.__name__}({self.swarm}, "{self.swarm.name}-' + ) + self.assertTrue(session.__repr__().startswith(expected_start)) + + @parameterized.expand([None, "my-swarm"]) + def test_str(self, session_id): + + session = Session(self.swarm, session_id=session_id) + + if session_id is not None: + expected_start = f'Session: "{session_id}"' + else: + expected_start = expected_start = f'Session: "{self.swarm.name}-' + + self.assertTrue(str(session).startswith(expected_start))