From afbccccb1229429b49b5934a560531e92e3a4bad Mon Sep 17 00:00:00 2001 From: Igor Tkachenko Date: Fri, 8 Jul 2022 16:44:32 +0300 Subject: [PATCH 01/11] test: refactore tests --- tests/unit/test_tion.py | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/tests/unit/test_tion.py b/tests/unit/test_tion.py index 4acc515..7a97518 100644 --- a/tests/unit/test_tion.py +++ b/tests/unit/test_tion.py @@ -1,4 +1,5 @@ import time +import pytest import unittest import unittest.mock as mock import bluepy @@ -142,18 +143,22 @@ def e(): e() -class TionTests(unittest.TestCase): - def setUp(self): - self.instances = [tion, LiteFamily, Lite, S3, S4] - - def test_DecodeTemperature(self): - self.assertEqual(tion.decode_temperature(0x09), 9, "Should be 9") - self.assertEqual(tion.decode_temperature(0xFF), -1, "Should be -1") - - def test_mac(self): - """mac property should be same as in init""" - target = 'foo' - for s in self.instances: - with self.subTest(test_instance=s): - t_tion = s(target) - self.assertEqual(t_tion.mac, target) +@pytest.mark.parametrize( + "raw_temperature, result", + [ + [0x09, 9], + [0xFF, -1] + ] +) +def test_decode_temperature(raw_temperature, result): + assert tion.decode_temperature(raw_temperature) == result + + +@pytest.mark.parametrize( + "instance", + [tion, LiteFamily, Lite, S3, S4] +) +def test_mac(instance): + target = 'foo' + t_tion = instance(target) + assert t_tion.mac == target From e07bdf69093c0a9a0f453d8f6f3b45fcc9889b1a Mon Sep 17 00:00:00 2001 From: Igor Tkachenko Date: Fri, 8 Jul 2022 17:21:51 +0300 Subject: [PATCH 02/11] test: refactor retry tests --- tests/unit/test_tion.py | 104 ++++++++++++++++------------------------ 1 file changed, 40 insertions(+), 64 deletions(-) diff --git a/tests/unit/test_tion.py b/tests/unit/test_tion.py index 7a97518..53889d9 100644 --- a/tests/unit/test_tion.py +++ b/tests/unit/test_tion.py @@ -2,7 +2,7 @@ import pytest import unittest import unittest.mock as mock -import bluepy +from bleak import exc import tion_btle.tion from tion_btle.tion import tion @@ -13,7 +13,45 @@ from tion_btle.tion import retry, MaxTriesExceededError -class retryTests(unittest.TestCase): +@pytest.mark.parametrize( + "retries, repeats, succeed_run, t_delay", + [ + pytest.param(0, 1, 0, 0, id="Succeed after first attempt with no retry"), + pytest.param(1, 1, 0, 0, id="Succeed after first attempt with retry"), + pytest.param(5, 4, 3, 0, id="Succeed after first 3rd attempt with 5 retry"), + pytest.param(1, 2, 3, 0, id="Fail after one retry"), + pytest.param(2, 2, 1, 2, id="Delay between retries"), + ] +) +def test_retry(retries: int, repeats: int, succeed_run: int, t_delay: int): + class TestRetry: + count = 0 + + @retry(retries=retries, delay=t_delay) + def a(self, _succeed_run: int = 0): + if self.count <= _succeed_run: + self.count += 1 + if self.count - 1 == _succeed_run: + return "expected_result" + + raise exc.BleakError + + i = TestRetry() + start = time.time() + + if succeed_run < repeats: + assert i.a(_succeed_run=succeed_run) == "expected_result" + else: + with pytest.raises(MaxTriesExceededError) as c: + i.a(_succeed_run=succeed_run) + + end = time.time() + + assert i.count == repeats + assert end - start >= t_delay + + +class TestLogLevels: def setUp(self): self.count = 0 tion_btle.tion._LOGGER.debug = mock.MagicMock(name='method') @@ -21,60 +59,6 @@ def setUp(self): tion_btle.tion._LOGGER.warning = mock.MagicMock(name='method') tion_btle.tion._LOGGER.critical = mock.MagicMock(name='method') - def test_success_single_try(self): - @retry(retries=0) - def a(): - self.count += 1 - return "expected_result" - self.assertEqual(a(), "expected_result") - self.assertEqual(self.count, 1) - - def test_success_two_tries(self): - @retry(retries=1) - def a(): - self.count += 1 - return "expected_result" - self.assertEqual(a(), "expected_result") - self.assertEqual(self.count, 1) - - def test_failure_two_tries(self): - @retry(retries=1) - def a(): - self.count += 1 - raise Exception() - try: - a() - self.fail() - except MaxTriesExceededError: - self.assertEqual(self.count, 2) - - def test_success_after_third_try(self): - @retry(retries=5) - def a(): - self.count += 1 - if self.count == 3: - return "expected_result" - else: - raise Exception() - self.assertEqual(a(), "expected_result") - self.assertEqual(self.count, 3) - - def test_delay(self): - t_delay = 2 - - @retry(retries=2, delay=t_delay) - def a(): - self.count += 1 - if self.count == 2: - return "expected_result" - else: - raise Exception() - - start = time.time() - a() - end = time.time() - self.assertGreaterEqual(end-start, t_delay) - def test_debug_log_level(self): @retry(retries=0) def debug(): @@ -134,14 +118,6 @@ def critical(): pass log_mock.assert_called() - def test_MaxTriesExceededError(self): - @retry(retries=0) - def e(): - raise Exception - - with self.assertRaises(MaxTriesExceededError) as c: - e() - @pytest.mark.parametrize( "raw_temperature, result", From 0ee2fae87d1d75169606b60cf4c8d8f3bfab0424 Mon Sep 17 00:00:00 2001 From: IATkachenko Date: Fri, 8 Jul 2022 18:49:59 +0300 Subject: [PATCH 03/11] test: refactor decode tests --- tests/unit/test_decode.py | 59 +++++++++++++++++++++++++++++++++ tests/unit/test_light_decode.py | 50 ---------------------------- tests/unit/test_s3_decode.py | 42 ----------------------- tion_btle/lite.py | 9 ----- tion_btle/s3.py | 5 --- 5 files changed, 59 insertions(+), 106 deletions(-) create mode 100644 tests/unit/test_decode.py delete mode 100644 tests/unit/test_light_decode.py delete mode 100644 tests/unit/test_s3_decode.py diff --git a/tests/unit/test_decode.py b/tests/unit/test_decode.py new file mode 100644 index 0000000..41f6852 --- /dev/null +++ b/tests/unit/test_decode.py @@ -0,0 +1,59 @@ +import pytest +from typing import ClassVar + +import tion_btle + + +@pytest.mark.parametrize( + "tion_instance, data, result", + [ + [ + tion_btle.s3.S3, + [0xb3, 0x10, 0x24, 0x14, 0x03, 0x00, 0x15, 0x14, 0x14, 0x8f, + 0x00, 0x0c, 0x0a, 0x00, 0x4b, 0x0a, 0x00, 0x33, 0x00, 0x5a], + { + 'state': 'on', + 'heater': 'on', + 'sound': 'off', + 'mode': 'outside', + 'out_temp': 20, + 'in_temp': 20, + 'target_temp': 20, + 'fan_speed': 4, + '_filter_remain': 143, + 'model': 'S3', + } + ], + [ + tion_btle.lite.Lite, + [0xcf, 0xd8, 0x02, 0x0f, 0x04, 0x09, 0x0f, 0x1a, 0x80, 0x8e, 0x05, 0x00, 0xe9, 0x8b, 0x05, 0x00, 0x17, 0xc2, + 0xe7, 0x00, 0x26, 0x1b, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x14, 0x19, 0x02, 0x04, 0x06, + 0x06, 0x18, 0x00], + { + 'state': 'on', + 'heater': 'on', + 'sound': 'on', + 'mode': 'outside', + 'out_temp': 15, + 'in_temp': 9, + 'target_temp': 15, + 'fan_speed': 4, + '_filter_remain': 175.7928587962963, + 'model': 'Lite', + '_device_work_time': 18.284884259259258, + '_electronic_work_time': 4.214814814814815, + '_electronic_temp': 26, + '_co2_auto_control': 0, + '_filter_change_required': 0, + 'light': 'on', + } + ] + ] +) +def test_decode(tion_instance: ClassVar[tion_btle.tion.tion], data: bytearray, result: dict): + tion = tion_instance(mac="") + tion._decode_response(data) + for parameter in result.keys(): + assert getattr(tion, parameter) == result[parameter] + diff --git a/tests/unit/test_light_decode.py b/tests/unit/test_light_decode.py deleted file mode 100644 index 8c4f607..0000000 --- a/tests/unit/test_light_decode.py +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/python -import sys -import os -import unittest - -PACKAGE_PARENT = '..' -SCRIPT_DIR = os.path.dirname(os.path.realpath(os.path.join(os.getcwd(), os.path.expanduser(__file__)))) -sys.path.append(os.path.normpath(os.path.join(SCRIPT_DIR, PACKAGE_PARENT))) - -from tion_btle.lite import Lite - - -class LiteTests(unittest.TestCase): - data = bytearray() - - def test_decode(self): - self.lite = Lite("dummy") - self.lite._decode_response(self.data) - name_value = [ - ('state', 'on'), - ('heater', 'on'), - ('sound', 'on'), - ('mode', 'outside'), - ('out_temp', 15), - ('in_temp', 9), - ('target_temp', 15), - ('fan_speed', 4), - ('_filter_remain', 175.7928587962963), - ('model', 'Lite'), - ('_device_work_time', 18.284884259259258), - ('_electronic_work_time', 4.214814814814815), - ('_electronic_temp', 26), - ('_co2_auto_control', 0), - ('_filter_change_required', 0), - ('light', 'on') - ] - for s in name_value: - with self.subTest(s[0]): - self.assertEqual(getattr(self.lite, s[0]), s[1], "%s should be '%s'" % (s[0], s[1]) ) - - -LiteTests.data = bytearray([ - 0xcf, 0xd8, 0x02, 0x0f, 0x04, 0x09, 0x0f, 0x1a, 0x80, 0x8e, 0x05, 0x00, 0xe9, 0x8b, 0x05, 0x00, 0x17, 0xc2, - 0xe7, 0x00, 0x26, 0x1b, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, - 0x00, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x14, 0x19, 0x02, 0x04, 0x06, - 0x06, 0x18, 0x00 -]) - -if __name__ == '__main__': - unittest.main() diff --git a/tests/unit/test_s3_decode.py b/tests/unit/test_s3_decode.py deleted file mode 100644 index a9bd977..0000000 --- a/tests/unit/test_s3_decode.py +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/python -import sys -import os -import unittest - -PACKAGE_PARENT = '..' -SCRIPT_DIR = os.path.dirname(os.path.realpath(os.path.join(os.getcwd(), os.path.expanduser(__file__)))) -sys.path.append(os.path.normpath(os.path.join(SCRIPT_DIR, PACKAGE_PARENT))) - -from tion_btle.s3 import S3 - - -class TestLite(unittest.TestCase): - data = bytearray() - - def test_decode(self): - self.lite = S3("dummy") - self.lite._decode_response(self.data) - name_value = [ - ('state', 'on'), - ('heater', 'on'), - ('sound', 'off'), - ('mode', 'outside'), - ('out_temp', 20), - ('in_temp', 20), - ('target_temp', 20), - ('fan_speed', 4), - ('_filter_remain', 143), - ('model', 'S3'), - ] - for s in name_value: - with self.subTest(s[0]): - self.assertEqual(getattr(self.lite, s[0]), s[1], "%s should be '%s'" % (s[0], s[1]) ) - - -TestLite.data = bytearray([ - 0xb3, 0x10, 0x24, 0x14, 0x03, 0x00, 0x15, 0x14, 0x14, 0x8f, 0x00, 0x0c, 0x0a, 0x00, 0x4b, 0x0a, 0x00, 0x33, 0x00, - 0x5a -]) - -if __name__ == '__main__': - unittest.main() diff --git a/tion_btle/lite.py b/tion_btle/lite.py index 3fee785..1106088 100644 --- a/tion_btle/lite.py +++ b/tion_btle/lite.py @@ -140,12 +140,3 @@ def _packages(self) -> list: bytearray([0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00]), bytearray([0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x14, 0x19, 0x02, 0x04, 0x06, 0x06, 0x18, 0x00, 0xb5, 0xad]) ] - - @property - def _dummy_data(self) -> bytearray: - return bytearray([ - 0xcf, 0xd8, 0x02, 0x0f, 0x04, 0x09, 0x0f, 0x1a, 0x80, 0x8e, 0x05, 0x00, 0xe9, 0x8b, 0x05, 0x00, 0x17, - 0xc2, 0xe7, 0x00, 0x26, 0x1b, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x00, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x14, 0x19, - 0x02, 0x04, 0x06, 0x06, 0x18, 0x00 - ]) diff --git a/tion_btle/s3.py b/tion_btle/s3.py index 2fbf6c9..9739ab2 100644 --- a/tion_btle/s3.py +++ b/tion_btle/s3.py @@ -132,8 +132,3 @@ def _encode_request(self, request: dict) -> bytearray: def _send_request(self, request: bytearray): self._try_write(request) - - @property - def _dummy_data(self) -> bytearray: - return bytearray([0xb3, 0x10, 0x24, 0x14, 0x03, 0x00, 0x15, 0x14, 0x14, 0x8f, 0x00, 0x0c, 0x0a, 0x00, 0x4b, - 0x0a, 0x00, 0x33, 0x00, 0x5a]) From ea6200313475344c6e7f6450820cd9926b1c0be5 Mon Sep 17 00:00:00 2001 From: IATkachenko Date: Fri, 8 Jul 2022 19:01:52 +0300 Subject: [PATCH 04/11] ci: install test requirements --- .github/workflows/tests.yml | 2 +- requirements_test.txt | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 requirements_test.txt diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 38522e8..04a469e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -34,7 +34,7 @@ jobs: pip install setuptools wheel twine - name: Install requirements - run: pip install -r requirements.txt + run: pip install -r requirements_test.txt - name: Run ${{ matrix.type }} tests run: python -m unittest discover -s tests/${{ matrix.type }} -v diff --git a/requirements_test.txt b/requirements_test.txt new file mode 100644 index 0000000..39f5fcc --- /dev/null +++ b/requirements_test.txt @@ -0,0 +1,2 @@ +-r requirements.txt +bluepy \ No newline at end of file From 68bc489c4de8fd9a1c1d17cb5eda7e64779611d5 Mon Sep 17 00:00:00 2001 From: IATkachenko Date: Fri, 8 Jul 2022 19:02:15 +0300 Subject: [PATCH 05/11] test: remove unnecesarry imports --- tests/__init__.py | 1 - tests/unit/__init__.py | 2 -- 2 files changed, 3 deletions(-) diff --git a/tests/__init__.py b/tests/__init__.py index 8d5befa..e69de29 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1 +0,0 @@ -from .unit import * diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py index e863797..e69de29 100644 --- a/tests/unit/__init__.py +++ b/tests/unit/__init__.py @@ -1,2 +0,0 @@ -from .test_tion import retryTests -from .test_tion import TionTests From 943b246914ec6c6cc1b56be9698dd3f99eccf0c8 Mon Sep 17 00:00:00 2001 From: IATkachenko Date: Fri, 8 Jul 2022 20:46:29 +0300 Subject: [PATCH 06/11] test: add testing of S4 decoding --- tests/unit/test_decode.py | 129 +++++++++++++++++++++++--------------- tion_btle/__init__.py | 2 + tion_btle/s4.py | 8 --- 3 files changed, 81 insertions(+), 58 deletions(-) diff --git a/tests/unit/test_decode.py b/tests/unit/test_decode.py index 41f6852..56aea75 100644 --- a/tests/unit/test_decode.py +++ b/tests/unit/test_decode.py @@ -1,59 +1,88 @@ import pytest -from typing import ClassVar +from typing import List, Type, Union import tion_btle -@pytest.mark.parametrize( - "tion_instance, data, result", - [ - [ - tion_btle.s3.S3, - [0xb3, 0x10, 0x24, 0x14, 0x03, 0x00, 0x15, 0x14, 0x14, 0x8f, - 0x00, 0x0c, 0x0a, 0x00, 0x4b, 0x0a, 0x00, 0x33, 0x00, 0x5a], - { - 'state': 'on', - 'heater': 'on', - 'sound': 'off', - 'mode': 'outside', - 'out_temp': 20, - 'in_temp': 20, - 'target_temp': 20, - 'fan_speed': 4, - '_filter_remain': 143, - 'model': 'S3', - } - ], - [ - tion_btle.lite.Lite, - [0xcf, 0xd8, 0x02, 0x0f, 0x04, 0x09, 0x0f, 0x1a, 0x80, 0x8e, 0x05, 0x00, 0xe9, 0x8b, 0x05, 0x00, 0x17, 0xc2, +scenarios: List[dict[str, Union[Type[tion_btle.Tion], bytearray, dict]]] = [ + { + "instance": tion_btle.s3.S3, + "data": [0xb3, 0x10, 0x24, 0x14, 0x03, 0x00, 0x15, 0x14, 0x14, 0x8f, 0x00, 0x0c, 0x0a, 0x00, 0x4b, 0x0a, 0x00, + 0x33, 0x00, 0x5a], + "results": { + 'state': 'on', + 'heater': 'on', + 'sound': 'off', + 'mode': 'outside', + 'out_temp': 20, + 'in_temp': 20, + 'target_temp': 20, + 'fan_speed': 4, + '_filter_remain': 143, + 'model': 'S3', + } + }, + { + "instance": tion_btle.lite.Lite, + "data": [0xcf, 0xd8, 0x02, 0x0f, 0x04, 0x09, 0x0f, 0x1a, 0x80, 0x8e, 0x05, 0x00, 0xe9, 0x8b, 0x05, 0x00, 0x17, 0xc2, 0xe7, 0x00, 0x26, 0x1b, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x14, 0x19, 0x02, 0x04, 0x06, 0x06, 0x18, 0x00], - { - 'state': 'on', - 'heater': 'on', - 'sound': 'on', - 'mode': 'outside', - 'out_temp': 15, - 'in_temp': 9, - 'target_temp': 15, - 'fan_speed': 4, - '_filter_remain': 175.7928587962963, - 'model': 'Lite', - '_device_work_time': 18.284884259259258, - '_electronic_work_time': 4.214814814814815, - '_electronic_temp': 26, - '_co2_auto_control': 0, - '_filter_change_required': 0, - 'light': 'on', - } - ] - ] -) -def test_decode(tion_instance: ClassVar[tion_btle.tion.tion], data: bytearray, result: dict): - tion = tion_instance(mac="") - tion._decode_response(data) - for parameter in result.keys(): - assert getattr(tion, parameter) == result[parameter] + "results": { + 'state': 'on', + 'heater': 'on', + 'sound': 'on', + 'mode': 'outside', + 'out_temp': 15, + 'in_temp': 9, + 'target_temp': 15, + 'fan_speed': 4, + '_filter_remain': 175.7928587962963, + 'model': 'Lite', + '_device_work_time': 18.284884259259258, + '_electronic_work_time': 4.214814814814815, + '_electronic_temp': 26, + '_co2_auto_control': 0, + '_filter_change_required': 0, + 'light': 'on', + } + }, + { + "instance": tion_btle.S4, + "data": [0x2f, 0x51, 0x00, 0x19, 0x04, 0x0e, 0x10, 0x1b, 0x26, 0x3b, 0x6e, 0x07, 0x00, 0xfa, 0x4e, 0x07, 0x00, + 0x06, 0xff, 0xe5, 0x00, 0xa6, 0xe9, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00], + "results": { + 'state': 'on', + 'heater': 'on', + 'sound': 'on', + 'mode': 'outside', + 'out_temp': 16, + 'in_temp': 14, + 'target_temp': 25, + 'fan_speed': 4, + '_filter_remain': 174.45636574074075, + 'model': 'S4', + } + } +] + +def pytest_generate_tests(metafunc): + global scenarios + tests = [] + + for scenario in scenarios: + tion_type: Type[tion_btle.Tion] = scenario["instance"] + tion: tion_btle.Tion = tion_type(mac="") + tion._decode_response(response=scenario["data"]) + + for k in scenario["results"].keys(): + v = scenario["results"][k] + _id = f"{tion.__class__.__name__}-{k}" + tests.append(pytest.param(_id, getattr(tion, k), v, id=_id)) + + metafunc.parametrize("parameter,decoded_value,expected_value", tests) + + +def test_param(parameter, decoded_value, expected_value): + assert decoded_value == expected_value diff --git a/tion_btle/__init__.py b/tion_btle/__init__.py index c1f5387..47745b5 100644 --- a/tion_btle/__init__.py +++ b/tion_btle/__init__.py @@ -1,2 +1,4 @@ from .s3 import S3 from .lite import Lite +from .s4 import S4 +from .tion import tion as Tion \ No newline at end of file diff --git a/tion_btle/s4.py b/tion_btle/s4.py index a84bdd0..419ac40 100644 --- a/tion_btle/s4.py +++ b/tion_btle/s4.py @@ -90,14 +90,6 @@ def _packages(self) -> list: bytearray([0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x98, 0x5d]) ] - @property - def _dummy_data(self) -> bytearray: - full_message = bytearray([0x00, 0x2f, 0x00, 0x3a, 0x27, 0x31, 0x32, 0x72, 0x7b, 0x64, 0xd7, 0x31, 0xea, 0x58, - 0x3a, 0x2f, 0x51, 0x00, 0x19, 0x04, 0x0e, 0x10, 0x1b, 0x26, 0x3b, 0x6e, 0x07, 0x00, - 0xfa, 0x4e, 0x07, 0x00, 0x06, 0xff, 0xe5, 0x00, 0xa6, 0xe9, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x06, 0x00, 0x98, 0x5d]) - return full_message[15:-2] - @property def command_getStatus(self) -> bytearray: return bytearray([LiteFamily.SINGLE_PACKET_ID, 0x10, 0x00, self.MAGIC_NUMBER, 0xa1] + From a6cd15dbb34f1f84b731c6561f7168ba59164d4a Mon Sep 17 00:00:00 2001 From: IATkachenko Date: Thu, 7 Jul 2022 19:39:43 +0300 Subject: [PATCH 07/11] ci(test): test module for python 3.9 and 3.10 only --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 04a469e..ccc8e7b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python: [3.6, 3.7, 3.8, 3.9] + python: ['3.9', '3.10'] type: [unit, functional] # Steps represent a sequence of tasks that will be executed as part of the job From ffc4c8bed17c751fec9c1dca3fffe01e9031fafb Mon Sep 17 00:00:00 2001 From: IATkachenko Date: Fri, 8 Jul 2022 19:23:36 +0300 Subject: [PATCH 08/11] ci(tests): don't run functional tests Actually it tests nothing --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ccc8e7b..4c004db 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -16,7 +16,7 @@ jobs: strategy: matrix: python: ['3.9', '3.10'] - type: [unit, functional] + type: [unit] # Steps represent a sequence of tasks that will be executed as part of the job steps: From 6d162a86ddc12f88bb7848a2367dcb85ea15a235 Mon Sep 17 00:00:00 2001 From: Igor Tkachenko Date: Fri, 8 Jul 2022 15:28:48 +0300 Subject: [PATCH 09/11] refactor(instance): convert heating to true property --- tests/unit/tion.py | 50 +++++++++++++++++----------------------------- tion_btle/tion.py | 49 ++++++++++----------------------------------- 2 files changed, 28 insertions(+), 71 deletions(-) diff --git a/tests/unit/tion.py b/tests/unit/tion.py index e928df5..793579c 100644 --- a/tests/unit/tion.py +++ b/tests/unit/tion.py @@ -1,37 +1,23 @@ -import sys -import unittest -import unittest.mock as mock -from unittest.mock import MagicMock -sys.modules['bluepy'] = MagicMock() -sys.modules['bluepy.btle'] = MagicMock() -import tion_btle.tion # noqa: E402 +import pytest +from tion_btle.tion import tion as Tion -class TionTests(unittest.TestCase): - def setUp(self): - self.patch = mock.patch('tion_btle.tion.tion.__init__', return_value=None) - self.patch.start() - self._tion = tion_btle.tion.tion("") - def tearDown(self): - self.patch.stop() +@pytest.mark.parametrize( + "heater, heater_temp, in_temp, out_temp, result", + [ + ["on", 20, -2, 21, "on"], + ["on", 20, 15, 21, "on"], + ["on", 16, 15, 21, "off"], + ["off", 20, 15, 21, "off"], + ]) +def test__detect_heating_state(heater, in_temp, out_temp, heater_temp, result): + """Test heating detection""" - def test___detect_heating_state(self): - # state, in, out, target, heater - _variants = [ - ["on", -2, 21, 20, "on"], - ["on", 15, 21, 20, "on"], - ["off", 15, 21, 16, "on"], - ["off", 15, 21, 20, "off"], - ] + tion = Tion(mac="") + tion.heater = heater + tion._in_temp = in_temp + tion._out_temp = out_temp + tion._heater_temp = heater_temp - for expect, in_temp, out_temp, heater_temp, heater in _variants: - with self.subTest(expect=expect, - in_temp=in_temp, out_temp=out_temp, heater_temp=heater_temp, heater=heater): - # call private __detect_heating_state from tion class - self._tion._tion__detect_heating_state(in_temp, out_temp, heater_temp, heater) - self.assertEqual(self._tion.heating, expect) - - -if __name__ == '__main__': - unittest.main() + assert tion.heating == result diff --git a/tion_btle/tion.py b/tion_btle/tion.py index c913d75..f0b4ecb 100644 --- a/tion_btle/tion.py +++ b/tion_btle/tion.py @@ -131,7 +131,6 @@ def __init__(self, mac: str): self._state: bool = False self._heater: bool = False self._sound: bool = False - self._heating: bool = False self._filter_remain: float = 0.0 self._error_code: int = 0 self.__failed_connects: int = 0 @@ -209,35 +208,16 @@ def __generate_common_json(self) -> dict: "model": self.model, } - def __detect_heating_state(self, - in_temp: int = None, - out_temp: int = None, - heater_temp: int = None, - heater: str = None) -> None: - """ - Tries to guess is heater working right now - :param in_temp: air intake temperature - :param out_temp: ait outtake temperature - :param heater_temp: target temperature for heater - :param heater: heater state - :return: None - """ - if in_temp is None: - in_temp = self.in_temp - if out_temp is None: - out_temp = self.out_temp - if heater_temp is None: - heater_temp = self.heater_temp - if heater is None: - heater = self.heater - - if heater == "off": - self.heating = "off" - else: - if heater_temp - in_temp > 3 and out_temp > in_temp: - self.heating = "on" - else: - self.heating = "off" + @property + def heating(self) -> str: + """Tries to guess is heater working right now.""" + if self.heater == "off": + return "off" + + if self.heater_temp - self.in_temp > 3 and self.out_temp > self.in_temp: + return "on" + + return "off" def get_state_from_breezer(self, keep_connection: bool = False) -> None: """ @@ -257,7 +237,6 @@ def get_state_from_breezer(self, keep_connection: bool = False) -> None: self.__connections_count -= 1 self._decode_response(response) - self.__detect_heating_state() def get(self, keep_connection: bool = False, skip_update: bool = False) -> dict: """ @@ -509,14 +488,6 @@ def sound(self, new_state: str): def filter_remain(self) -> float: return self._filter_remain - @property - def heating(self) -> str: - return self._decode_state(self._heating) - - @heating.setter - def heating(self, new_state: str): - self._heating = self._encode_state(new_state) - @property def mode(self): return self._process_mode(self._mode) From 89a31241dc8f072190000f8f638f38dcf38d3adc Mon Sep 17 00:00:00 2001 From: IATkachenko Date: Fri, 8 Jul 2022 21:14:34 +0300 Subject: [PATCH 10/11] ci: add pytest to requirements_test.txt --- requirements_test.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements_test.txt b/requirements_test.txt index 39f5fcc..ea47d2e 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,2 +1,3 @@ -r requirements.txt +pytest bluepy \ No newline at end of file From 1d873d349c93d6b8681ae9d078a2274e71d2d6db Mon Sep 17 00:00:00 2001 From: IATkachenko Date: Fri, 8 Jul 2022 21:16:19 +0300 Subject: [PATCH 11/11] test: revert migration to bleak --- tests/unit/test_tion.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/unit/test_tion.py b/tests/unit/test_tion.py index 53889d9..726673f 100644 --- a/tests/unit/test_tion.py +++ b/tests/unit/test_tion.py @@ -1,8 +1,8 @@ +import bluepy import time import pytest -import unittest import unittest.mock as mock -from bleak import exc + import tion_btle.tion from tion_btle.tion import tion @@ -34,7 +34,7 @@ def a(self, _succeed_run: int = 0): if self.count - 1 == _succeed_run: return "expected_result" - raise exc.BleakError + raise bluepy.btle.BTLEDisconnectError i = TestRetry() start = time.time()