From ca02e5d620f95a4ec7011ba49c881983b17d21fb Mon Sep 17 00:00:00 2001 From: Chris Pastl <chris@crispybits.app> Date: Tue, 30 Apr 2024 16:41:10 +0200 Subject: [PATCH] refactor: use enum for core mock functions --- tests/core_client_mock.py | 196 +++++++++++++++++++++++++++----------- 1 file changed, 141 insertions(+), 55 deletions(-) diff --git a/tests/core_client_mock.py b/tests/core_client_mock.py index 391ab90a..4f6122a3 100644 --- a/tests/core_client_mock.py +++ b/tests/core_client_mock.py @@ -18,6 +18,7 @@ import logging +from enum import StrEnum, auto from time import sleep from urllib.parse import urlparse @@ -30,9 +31,54 @@ from aura_engine.core.mixer import Mixer from aura_engine.events import EngineEventDispatcher +# enums reflecting namespace, action and reponses +class Namespace(StrEnum): + MIXER = auto() + CHANNEL = auto() + + @classmethod + def _missing_(cls, value): + # map 'mixer' to .MIXER and values of ChannelName to .CHANNEL + if isinstance(value, Namespace): + return cls(value) + elif value in ChannelName._value2member_map_.values(): + return cls.CHANNEL + else: + raise TypeError + + +class MixerAction(StrEnum): + INPUTS = auto() + STATUS = auto() + VOLUME = auto() + SELECT = auto() + ACTIVATE = auto() + + +class ChannelAction(StrEnum): + PUSH = auto() + LOAD = auto() + URL = auto() + STATUS = auto() + START = auto() + STOP = auto() + CLEAR = auto() + ROLL = auto() + SET_TRACK_METADATA = auto() + + +class Response(StrEnum): + """ + Using custom reponse type since PlayoutStatusResponse.SUCCESS is list instead of single string. + """ + + SUCCESS = "OK" # PlayoutStatusResponse.SUCCESS + INVALID_URL = "Invalid URL" # some arbitrary string which is not "OK" signaling failure + + class CoreClientMock(CoreClient): """ - Suppress unwanted behavior by subclassing and overriding. + Suppress unwanted behavior by subclassing and overriding necessary functions. """ instance = None @@ -41,6 +87,7 @@ class CoreClientMock(CoreClient): event_dispatcher = None conn = None stream_url = None + resource_id = -1 volumes = [0, 0, 0, 0] selections = [0, 0, 0, 0] @@ -61,70 +108,109 @@ class CoreClientMock(CoreClient): CoreClientMock.instance = CoreClientMock(event_dispatcher) return CoreClientMock.instance + def _validate_url(self, url: str) -> bool: + """ + Check url validity. + """ + parse = urlparse(url) + return all([parse.scheme, parse.netloc]) + + def _get_resource_id(self) -> int: + """ + Increment and return index starting with 0. + """ + self.resource_id += 1 + return self.resource_id + @synchronized def connect(self): + sleep(0.5) pass @synchronized def disconnect(self): + sleep(0.5) pass @synchronized def exec(self, namespace: str, action: str, args: str = "") -> str: - self.logger.debug( - f"Core mock namespace: '{namespace}', action: '{action}', args: '{args}'" - ) - if namespace == "mixer": - if action == "inputs": - return f"{ChannelName.QUEUE_A} {ChannelName.QUEUE_B}" - elif action == "status": - chn = int(args) - vol = self.volumes[chn] - return f"ready=true selected=true single=false volume={vol}% remaining=0" - elif action == "volume": - argv = args.split(" ") - chn = int(argv[0]) - vol = self.volumes[chn] - self.volumes[chn] = int(float(argv[1]) / 100.0) - resp = f"volume={vol}% remaining=0" - # print(resp) - return resp - elif action == "select": - argv = args.split(" ") - chn = int(argv[0]) - vol = self.volumes[chn] - sel = 1 if argv[1] == "true" else 0 - self.selections[chn] = sel - return f"volume={vol}% remaining=0 selected={sel}" - elif action == "activate": - return "OK" - elif namespace in ChannelName._value2member_map_.values(): - if action == "push": - sleep(5) # simulate some loading time - return 1 # res id - elif action == "load": - sleep(3) # simulate some loading time - return 2 # res id - elif action == "url": - parse = urlparse(args) - if all([parse.scheme, parse.netloc]): - self.stream_url = args - return "OK" - else: - return "Invalid URL" - elif action == "status": - return PlayoutStatusResponse.STREAM_STATUS_CONNECTED.value + " " + self.stream_url - elif action == "start": - sleep(1) # simulate small start delay - return "OK" - elif action == "stop": - return "OK" - elif action == "clear": - return "OK" - elif action == "roll": - return "OK" - elif action == "set_track_metadata": - return "OK" + log = self.logger.debug + log(f"Core mock request 'ns: {namespace}', action: '{action}', args: '{args}'") + + response: str = None + + ns = Namespace(namespace) + if ns is None: + raise TypeError + match ns: + case Namespace.MIXER: + act = MixerAction(action) + if act is None: + raise TypeError + match act: + case MixerAction.INPUTS: + response = f"{ChannelName.QUEUE_A} {ChannelName.QUEUE_B}" + case MixerAction.STATUS: + chn = int(args) + vol = self.volumes[chn] + response = ( + f"ready=true selected=true single=false volume={vol}% remaining=0" + ) + case MixerAction.VOLUME: + argv = args.split(" ") + chn = int(argv[0]) + vol = self.volumes[chn] + self.volumes[chn] = int(float(argv[1]) / 100.0) + response = f"volume={vol}% remaining=0" + case MixerAction.SELECT: + argv = args.split(" ") + chn = int(argv[0]) + vol = self.volumes[chn] + sel = 1 if argv[1] == "true" else 0 + self.selections[chn] = sel + response = f"volume={vol}% remaining=0 selected={sel}" + case MixerAction.ACTIVATE: + response = Response.SUCCESS + case Namespace.CHANNEL: + act = ChannelAction(action) + if act is None: + raise TypeError + match act: + case ChannelAction.PUSH: + sleep(5) + resid = self._get_resource_id() + response = str(resid) + case ChannelAction.LOAD: + sleep(3) + resid = self._get_resource_id() + response = str(resid) + case ChannelAction.URL: + if self._validate_url(args): + self.stream_url = args + response = Response.SUCCESS + else: + response = Response.INVALID_URL + case ChannelAction.STATUS: + response = ( + PlayoutStatusResponse.STREAM_STATUS_CONNECTED.value + + " " + + self.stream_url + ) + case ChannelAction.START: + sleep(1) + response = Response.SUCCESS + case ChannelAction.STOP: + response = Response.SUCCESS + case ChannelAction.CLEAR: + response = Response.SUCCESS + case ChannelAction.ROLL: + response = Response.SUCCESS + case ChannelAction.SET_TRACK_METADATA: + response = Response.SUCCESS + + if response is not None: + log(f"Core mock reponse '{response}'") + return response raise Exception(f"Unhandled namespace: '{namespace}', action: '{action}', args: '{args}'") -- GitLab