Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: extend coverage #36

Merged
merged 4 commits into from
Mar 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 1 addition & 14 deletions src/huemon/api/cached_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,14 @@

from huemon.api.api_interface import ApiInterface
from huemon.infrastructure.logger_factory import create_logger
from huemon.util import run_locked
from huemon.util import cache_output_to_temp, run_locked

LOG = create_logger()

DEFAULT_MAX_CACHE_AGE_SECONDS = 10
DEFAULT_CACHE_PATH = tempfile.gettempdir()


def cache_output_to_temp(cache_file_path, fn_call):
tmp_fd, tmp_file_path = tempfile.mkstemp()
with open(tmp_file_path, "w") as f_tmp:
f_tmp.write(json.dumps(fn_call()))

os.close(tmp_fd)

os.rename(tmp_file_path, cache_file_path)

with open(cache_file_path) as f_json:
return json.loads(f_json.read())


class CachedApi(ApiInterface):
def __init__(
self,
Expand Down
10 changes: 8 additions & 2 deletions src/huemon/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,13 @@ def run_locked(lock_file, fn_call):
LOG.debug("Acquired lock successfully (file=%s)", lock_file)

return fn_call()
except: # pylint: disable=bare-except
LOG.debug("Failed to acquire lock, cache hit (file=%s)", lock_file)
except BlockingIOError:
LOG.debug("Failed to acquire lock (file=%s)", lock_file)
except Exception as error: # pylint: disable=broad-except
LOG.debug(
"Something unexpected went wrong while acquiring lock (file=%s, error=%s)",
lock_file,
error,
)

return None
43 changes: 42 additions & 1 deletion src/tests/test_api_configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,16 @@
# This source code is licensed under the MPL-2.0 license found in the
# LICENSE file in the root directory of this source tree.

import os
import tempfile
import threading
import unittest
from time import sleep

from huemon.api.api import Api
from huemon.api.api_factory import create_api, create_hue_hub_url
from huemon.api.cached_api import CachedApi
from huemon.util import run_locked


class TestApiConfiguration(unittest.TestCase):
Expand All @@ -22,10 +27,46 @@ def test_when_cache_enabled_return_cached(self):

self.assertIsInstance(api, CachedApi)

def test_when_cache_enabled_return_regular(self):
def test_when_cache_disabled_return_regular(self):
api = create_api(
{"ip": "IRRELEVANT_IP", "key": "IRRELEVANT_KEY", "cache": {"enable": False}}
)

self.assertNotIsInstance(api, CachedApi)
self.assertIsInstance(api, Api)

@staticmethod
def __wait(timeout_seconds: int, value: dict):
sleep(timeout_seconds)

value["result"] = True

def test_when_locked_return_none(self):
(lfd, lock_file_path) = tempfile.mkstemp()

thread_result = {"result": False}
target = lambda: run_locked(
lock_file_path, lambda: TestApiConfiguration.__wait(0.1, thread_result)
)

thread0 = threading.Thread(target=target)

thread0.start()
maybe_true = run_locked(lock_file_path, lambda: True)
thread0.join()

os.close(lfd)

self.assertIsNone(maybe_true)
self.assertEqual(True, thread_result["result"])

def test_when_locked_raises_exception_return_none(self):
(lfd, lock_file_path) = tempfile.mkstemp()

raise_exception = lambda: 1 / 0

maybe_true = run_locked(lock_file_path, raise_exception)

os.close(lfd)

self.assertIsNone(maybe_true)
3 changes: 0 additions & 3 deletions src/tests/test_command_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@
from huemon.const import EXIT_FAIL
from tests.fixtures import MutableApi, create_system_config, read_result

CACHE_VALIDITY_INFINITE_SECONDS = 1_000_000
CACHE_VALIDITY_ZERO_SECONDS = 0


class TestCachedApi(unittest.TestCase):
def test_when_command_is_loaded_it_should_be_listed_as_available(self):
Expand Down
41 changes: 36 additions & 5 deletions src/tests/test_light_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,15 @@
create_name_to_command_mapping,
)
from huemon.commands_available.light_command import LightCommand
from huemon.const import EXIT_FAIL
from tests.fixtures import MutableApi, read_result

CACHE_VALIDITY_INFINITE_SECONDS = 1_000_000
CACHE_VALIDITY_ZERO_SECONDS = 0


SOME_LIGHT_MAC_0 = "SO:ME:LI:GH:TM:AC:00"
SOME_LIGHT_MAC_1 = "SO:ME:LI:GH:TM:AC:01"


class TestLightCommand(unittest.TestCase):
def test_when_light_doesnt_exist(self):
def test_when_light_doesnt_exist_raise(self):
mutable_api = MutableApi()
mutable_api.set_lights([])

Expand All @@ -33,6 +30,40 @@ def test_when_light_doesnt_exist(self):
with self.assertRaises(Exception):
command_handler.exec("light", [SOME_LIGHT_MAC_0, "status"])

def test_when_not_enough_parameters_raise(self):
mutable_api = MutableApi()
mutable_api.set_lights([])

command_handler = CommandHandler(
create_name_to_command_mapping({}, mutable_api, [LightCommand])
)

with self.assertRaises(SystemExit) as failed_call_context:
command_handler.exec("light", [])

self.assertEqual(
EXIT_FAIL,
failed_call_context.exception.code,
f"Exit code should equal {EXIT_FAIL}",
)

def test_when_unknown_action_raise(self):
mutable_api = MutableApi()
mutable_api.set_lights([])

command_handler = CommandHandler(
create_name_to_command_mapping({}, mutable_api, [LightCommand])
)

with self.assertRaises(SystemExit) as failed_call_context:
command_handler.exec("light", [SOME_LIGHT_MAC_0, "some_unknown_action"])

self.assertEqual(
EXIT_FAIL,
failed_call_context.exception.code,
f"Exit code should equal {EXIT_FAIL}",
)

@patch("builtins.print")
def test_when_light_exists_return_status(self, mock_print: MagicMock):
mutable_api = MutableApi()
Expand Down
4 changes: 0 additions & 4 deletions src/tests/test_sensor_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@
from huemon.commands_available.sensor_command import SensorCommand
from tests.fixtures import MutableApi, read_result

CACHE_VALIDITY_INFINITE_SECONDS = 1_000_000
CACHE_VALIDITY_ZERO_SECONDS = 0


SOME_SENSOR_MAC_0 = "SO:ME:SE:NS:OR:MA:C0"
SOME_SENSOR_MAC_1 = "SO:ME:SE:NS:OR:MA:C1"

Expand Down
3 changes: 0 additions & 3 deletions src/tests/test_system_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@
from huemon.commands_available.system_command import SystemCommand
from tests.fixtures import MutableApi, read_result

CACHE_VALIDITY_INFINITE_SECONDS = 1_000_000
CACHE_VALIDITY_ZERO_SECONDS = 0


class TestCachedApi(unittest.TestCase):
@patch("builtins.print")
Expand Down