Skip to content

Commit

Permalink
Merge pull request #347 from TWilkin/237.common-tests
Browse files Browse the repository at this point in the history
#237 Upgrade tests in python common library to fixtures
  • Loading branch information
TWilkin committed Jun 11, 2023
2 parents 73e2837 + 5a2800d commit d487134
Show file tree
Hide file tree
Showing 27 changed files with 537 additions and 488 deletions.
7 changes: 6 additions & 1 deletion common/pytest/powerpi_common_test/fixture/common.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import pytest
from unittest.mock import PropertyMock

from pytest_mock import MockerFixture

import pytest


@pytest.fixture
def powerpi_config(mocker: MockerFixture):
config = mocker.MagicMock()

type(config).message_age_cutoff = PropertyMock(return_value=120)

return config


Expand Down
2 changes: 1 addition & 1 deletion common/pytest/powerpi_common_test/sensor/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
from .sensor import SensorTestBase
from .sensor import SensorTestBase, SensorTestBaseNew
10 changes: 7 additions & 3 deletions common/pytest/powerpi_common_test/sensor/sensor.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
from abc import abstractmethod
from typing import Callable

import pytest

from pytest_mock import MockerFixture

from powerpi_common_test.device.base import BaseDeviceTestBase
import pytest
from powerpi_common_test.device.base import (BaseDeviceTestBase,
BaseDeviceTestBaseNew)


class SensorTestBase(BaseDeviceTestBase):
Expand All @@ -25,3 +25,7 @@ def create_subject(self, mocker: MockerFixture, func: Callable[[], None] = None)
@abstractmethod
def get_subject(self, mocker: MockerFixture):
raise NotImplementedError


class SensorTestBaseNew(BaseDeviceTestBaseNew):
pass
2 changes: 1 addition & 1 deletion common/pytest/powerpi_common_test/variable/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
from .variable import VariableTestBase
from .variable import VariableTestBase, VariableTestBaseNew
18 changes: 18 additions & 0 deletions common/pytest/powerpi_common_test/variable/variable.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,21 @@ def test_str(self, mocker: MockerFixture):
r'^var\.(device|sensor)\..*=\{.*\}$',
str(subject)
)) is True


class VariableTestBaseNew:
def test_name(self, subject):
assert subject.name is not None

def test_variable_type(self, subject):
assert subject.variable_type is not None

def test_json(self, subject):
assert subject.json is not None
assert isinstance(subject.json, dict)

def test_str(self, subject):
assert bool(re.match(
r'^var\.(device|sensor)\..*=\{.*\}$',
str(subject)
)) is True
41 changes: 11 additions & 30 deletions common/python/poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

105 changes: 48 additions & 57 deletions common/python/tests/condition/test_parser.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
from typing import List
from typing import Any, Callable, Dict, List, Union

import pytest

from powerpi_common.condition import (ConditionParser,
InvalidArgumentException,
InvalidIdentifierException,
UnexpectedTokenException)
from powerpi_common.variable import SensorValue
from pytest_mock import MockerFixture


class DeviceVariableImpl:
Expand All @@ -20,15 +20,10 @@ def __init__(self, name: str, action: str):
self.value = SensorValue(f'{name}/{action}', f'{action}/{name}')


class TestConditionParser:
def create_subject(self, mocker: MockerFixture, message=None):
# pylint: disable=attribute-defined-outside-init
self.variable_manager = mocker.Mock()
SubjectBuilder = Callable[[Union[Dict[str, Any], None]], ConditionParser]

self.variable_manager.get_device = DeviceVariableImpl
self.variable_manager.get_sensor = SensorVariableImpl

return ConditionParser(self.variable_manager, message)
class TestConditionParser:

@pytest.mark.parametrize('constant', [
'strING',
Expand All @@ -37,16 +32,12 @@ def create_subject(self, mocker: MockerFixture, message=None):
True,
None
])
def test_constant_success(self, mocker: MockerFixture, constant: str):
subject = self.create_subject(mocker)

def test_constant_success(self, subject: ConditionParser, constant: str):
result = subject.constant(constant)

assert result == constant

def test_constant_invalid(self, mocker: MockerFixture):
subject = self.create_subject(mocker)

def test_constant_invalid(self, subject: ConditionParser):
with pytest.raises(UnexpectedTokenException):
subject.constant({'too-complex': True})

Expand All @@ -58,10 +49,15 @@ def test_constant_invalid(self, mocker: MockerFixture):
('message.timestamp', 1337),
('message.whatever', None),
])
def test_identifier_success(self, mocker: MockerFixture, identifier: str, expected: str):
def test_identifier_success(
self,
subject_builder: SubjectBuilder,
identifier: str,
expected: str
):
message = {'timestamp': 1337}

subject = self.create_subject(mocker, message)
subject = subject_builder(message)

result = subject.identifier({'var': identifier})

Expand All @@ -79,9 +75,7 @@ def test_identifier_success(self, mocker: MockerFixture, identifier: str, expect
'message',
'message.timestamp'
])
def test_identifier_invalid(self, mocker: MockerFixture, identifier: str):
subject = self.create_subject(mocker)

def test_identifier_invalid(self, subject: ConditionParser, identifier: str):
with pytest.raises(InvalidIdentifierException):
subject.identifier({'var': identifier})

Expand All @@ -90,20 +84,21 @@ def test_identifier_invalid(self, mocker: MockerFixture, identifier: str):
'sensor.office.temperature.value',
'sensor.office.temperature.unit'
])
def test_sensor_identifier_invalid(self, mocker: MockerFixture, identifier: str):
subject = self.create_subject(mocker)

def test_sensor_identifier_invalid(
self,
subject: ConditionParser,
powerpi_variable_manager,
identifier: str
):
class NoValueSensor:
pass

self.variable_manager.get_sensor = lambda _, __: NoValueSensor
powerpi_variable_manager.get_sensor = lambda _, __: NoValueSensor

with pytest.raises(InvalidIdentifierException):
subject.identifier({'var': identifier})

def test_identifier_fail(self, mocker: MockerFixture):
subject = self.create_subject(mocker)

def test_identifier_fail(self, subject: ConditionParser):
with pytest.raises(InvalidArgumentException):
subject.identifier({'var': ['message.timestamp', 'message.state']})

Expand All @@ -114,16 +109,12 @@ def test_identifier_fail(self, mocker: MockerFixture):
(0, True),
({'!': True}, True)
])
def test_unary_expression_success(self, mocker: MockerFixture, operand, expected: bool):
subject = self.create_subject(mocker)

def test_unary_expression_success(self, subject: ConditionParser, operand, expected: bool):
result = subject.unary_expression({'not': operand})

assert result is expected

def test_unary_expression_fail(self, mocker: MockerFixture):
subject = self.create_subject(mocker)

def test_unary_expression_fail(self, subject: ConditionParser):
with pytest.raises(InvalidArgumentException):
subject.unary_expression({'not': [1, 2]})

Expand All @@ -138,10 +129,8 @@ def test_unary_expression_fail(self, mocker: MockerFixture):
('less_than_equal', [3, 2], False),
])
def test_relational_expression_success(
self, mocker: MockerFixture, operator: str, values: List, expected: bool
self, subject: ConditionParser, operator: str, values: List, expected: bool
):
subject = self.create_subject(mocker)

result = subject.relational_expression({operator: values})

assert result is expected
Expand All @@ -151,9 +140,9 @@ def test_relational_expression_success(
'not a list',
[1, 2, 3]
])
def test_relational_expression_fail(self, mocker: MockerFixture, operator: str, values: List):
subject = self.create_subject(mocker)

def test_relational_expression_fail(
self, subject: ConditionParser, operator: str, values: List
):
with pytest.raises(InvalidArgumentException):
subject.relational_expression({operator: values})

Expand All @@ -168,16 +157,12 @@ def test_relational_expression_fail(self, mocker: MockerFixture, operator: str,
([{'not': False}, True], True),
([{'=': [1, 1.0]}, 1], True)
])
def test_equality_expression_success(self, mocker: MockerFixture, values: List, expected: bool):
subject = self.create_subject(mocker)

def test_equality_expression_success(self, subject: ConditionParser, values: List, expected: bool):
result = subject.equality_expression({'equals': values})

assert result is expected

def test_equality_expression_fail(self, mocker: MockerFixture):
subject = self.create_subject(mocker)

def test_equality_expression_fail(self, subject: ConditionParser,):
with pytest.raises(InvalidArgumentException):
subject.equality_expression({'equals': 'not a list'})

Expand All @@ -190,17 +175,13 @@ def test_equality_expression_fail(self, mocker: MockerFixture):
([True, {'either': [False, True]}], True)
])
def test_logical_and_expression_success(
self, mocker: MockerFixture, values: List, expected: bool
self, subject: ConditionParser, values: List, expected: bool
):
subject = self.create_subject(mocker)

result = subject.logical_and_expression({'and': values})

assert result is expected

def test_logical_and_expression_fail(self, mocker: MockerFixture):
subject = self.create_subject(mocker)

def test_logical_and_expression_fail(self, subject: ConditionParser):
with pytest.raises(InvalidArgumentException):
subject.logical_and_expression({'and': 'not a list'})

Expand All @@ -213,16 +194,26 @@ def test_logical_and_expression_fail(self, mocker: MockerFixture):
([{'not': True}, False], False)
])
def test_logical_or_expression_success(
self, mocker: MockerFixture, values: List, expected: bool
self, subject: ConditionParser, values: List, expected: bool
):
subject = self.create_subject(mocker)

result = subject.logical_or_expression({'or': values})

assert result is expected

def test_logical_or_expression_fail(self, mocker: MockerFixture):
subject = self.create_subject(mocker)

def test_logical_or_expression_fail(self, subject: ConditionParser):
with pytest.raises(InvalidArgumentException):
subject.logical_or_expression({'or': 'not a list'})

@pytest.fixture
def subject_builder(self, powerpi_variable_manager):
def build(message: Union[Dict[str, Any], None] = None):
powerpi_variable_manager.get_device = DeviceVariableImpl
powerpi_variable_manager.get_sensor = SensorVariableImpl

return ConditionParser(powerpi_variable_manager, message)

return build

@pytest.fixture
def subject(self, subject_builder: SubjectBuilder):
return subject_builder()
Loading

0 comments on commit d487134

Please sign in to comment.