2016-03-09 09:25:50 +00:00
|
|
|
"""Test Home Assistant util methods."""
|
2017-10-03 03:25:04 +00:00
|
|
|
from unittest.mock import patch, MagicMock
|
2014-12-04 09:14:27 +00:00
|
|
|
from datetime import datetime, timedelta
|
2014-11-23 17:51:16 +00:00
|
|
|
|
2019-04-30 16:20:38 +00:00
|
|
|
import pytest
|
|
|
|
|
2015-09-13 05:56:49 +00:00
|
|
|
from homeassistant import util
|
|
|
|
import homeassistant.util.dt as dt_util
|
2014-11-23 17:51:16 +00:00
|
|
|
|
|
|
|
|
2019-04-30 16:20:38 +00:00
|
|
|
def test_sanitize_filename():
|
|
|
|
"""Test sanitize_filename."""
|
|
|
|
assert util.sanitize_filename("test") == 'test'
|
|
|
|
assert util.sanitize_filename("/test") == 'test'
|
|
|
|
assert util.sanitize_filename("..test") == 'test'
|
|
|
|
assert util.sanitize_filename("\\test") == 'test'
|
|
|
|
assert util.sanitize_filename("\\../test") == 'test'
|
|
|
|
|
|
|
|
|
|
|
|
def test_sanitize_path():
|
|
|
|
"""Test sanitize_path."""
|
|
|
|
assert util.sanitize_path("test/path") == 'test/path'
|
|
|
|
assert util.sanitize_path("~test/path") == 'test/path'
|
|
|
|
assert util.sanitize_path("~/../test/path") == '//test/path'
|
|
|
|
|
|
|
|
|
|
|
|
def test_slugify():
|
|
|
|
"""Test slugify."""
|
|
|
|
assert util.slugify("T-!@#$!#@$!$est") == 't_est'
|
|
|
|
assert util.slugify("Test More") == 'test_more'
|
|
|
|
assert util.slugify("Test_(More)") == 'test_more'
|
|
|
|
assert util.slugify("Tèst_Mörê") == 'test_more'
|
|
|
|
assert util.slugify("B8:27:EB:00:00:00") == 'b8_27_eb_00_00_00'
|
|
|
|
assert util.slugify("test.com") == 'test_com'
|
|
|
|
assert util.slugify("greg_phone - exp_wayp1") == 'greg_phone_exp_wayp1'
|
|
|
|
assert util.slugify("We are, we are, a... Test Calendar") == \
|
|
|
|
'we_are_we_are_a_test_calendar'
|
|
|
|
assert util.slugify("Tèst_äöüß_ÄÖÜ") == 'test_aouss_aou'
|
|
|
|
assert util.slugify("影師嗎") == 'ying_shi_ma'
|
|
|
|
assert util.slugify("けいふぉんと") == 'keihuonto'
|
|
|
|
|
|
|
|
|
|
|
|
def test_repr_helper():
|
|
|
|
"""Test repr_helper."""
|
|
|
|
assert util.repr_helper("A") == 'A'
|
|
|
|
assert util.repr_helper(5) == '5'
|
|
|
|
assert util.repr_helper(True) == 'True'
|
|
|
|
assert util.repr_helper({"test": 1}) == 'test=1'
|
|
|
|
assert util.repr_helper(datetime(1986, 7, 9, 12, 0, 0)) == \
|
|
|
|
'1986-07-09T12:00:00+00:00'
|
|
|
|
|
|
|
|
|
|
|
|
def test_convert():
|
|
|
|
"""Test convert."""
|
|
|
|
assert util.convert("5", int) == 5
|
|
|
|
assert util.convert("5", float) == 5.0
|
|
|
|
assert util.convert("True", bool) is True
|
|
|
|
assert util.convert("NOT A NUMBER", int, 1) == 1
|
|
|
|
assert util.convert(None, int, 1) == 1
|
|
|
|
assert util.convert(object, int, 1) == 1
|
|
|
|
|
|
|
|
|
|
|
|
def test_ensure_unique_string():
|
|
|
|
"""Test ensure_unique_string."""
|
|
|
|
assert util.ensure_unique_string("Beer", ["Beer", "Beer_2"]) == 'Beer_3'
|
|
|
|
assert util.ensure_unique_string("Beer", ["Wine", "Soda"]) == 'Beer'
|
|
|
|
|
|
|
|
|
|
|
|
def test_ordered_enum():
|
|
|
|
"""Test the ordered enum class."""
|
|
|
|
class TestEnum(util.OrderedEnum):
|
|
|
|
"""Test enum that can be ordered."""
|
|
|
|
|
|
|
|
FIRST = 1
|
|
|
|
SECOND = 2
|
|
|
|
THIRD = 3
|
|
|
|
|
|
|
|
assert TestEnum.SECOND >= TestEnum.FIRST
|
|
|
|
assert TestEnum.SECOND >= TestEnum.SECOND
|
|
|
|
assert TestEnum.SECOND < TestEnum.THIRD
|
2014-12-04 09:14:27 +00:00
|
|
|
|
2019-04-30 16:20:38 +00:00
|
|
|
assert TestEnum.SECOND > TestEnum.FIRST
|
|
|
|
assert TestEnum.SECOND <= TestEnum.SECOND
|
|
|
|
assert TestEnum.SECOND <= TestEnum.THIRD
|
2014-12-04 09:14:27 +00:00
|
|
|
|
2019-04-30 16:20:38 +00:00
|
|
|
assert TestEnum.SECOND > TestEnum.FIRST
|
|
|
|
assert TestEnum.SECOND <= TestEnum.SECOND
|
|
|
|
assert TestEnum.SECOND <= TestEnum.THIRD
|
|
|
|
|
|
|
|
assert TestEnum.SECOND >= TestEnum.FIRST
|
|
|
|
assert TestEnum.SECOND >= TestEnum.SECOND
|
|
|
|
assert TestEnum.SECOND < TestEnum.THIRD
|
|
|
|
|
|
|
|
# Python will raise a TypeError if the <, <=, >, >= methods
|
|
|
|
# raise a NotImplemented error.
|
|
|
|
with pytest.raises(TypeError):
|
|
|
|
TestEnum.FIRST < 1
|
|
|
|
|
|
|
|
with pytest.raises(TypeError):
|
|
|
|
TestEnum.FIRST <= 1
|
|
|
|
|
|
|
|
with pytest.raises(TypeError):
|
|
|
|
TestEnum.FIRST > 1
|
|
|
|
|
|
|
|
with pytest.raises(TypeError):
|
|
|
|
TestEnum.FIRST >= 1
|
|
|
|
|
|
|
|
|
|
|
|
def test_throttle():
|
|
|
|
"""Test the add cooldown decorator."""
|
|
|
|
calls1 = []
|
|
|
|
calls2 = []
|
|
|
|
|
|
|
|
@util.Throttle(timedelta(seconds=4))
|
|
|
|
def test_throttle1():
|
|
|
|
calls1.append(1)
|
|
|
|
|
|
|
|
@util.Throttle(timedelta(seconds=4), timedelta(seconds=2))
|
|
|
|
def test_throttle2():
|
|
|
|
calls2.append(1)
|
|
|
|
|
|
|
|
now = dt_util.utcnow()
|
|
|
|
plus3 = now + timedelta(seconds=3)
|
|
|
|
plus5 = plus3 + timedelta(seconds=2)
|
|
|
|
|
|
|
|
# Call first time and ensure methods got called
|
|
|
|
test_throttle1()
|
|
|
|
test_throttle2()
|
|
|
|
|
|
|
|
assert len(calls1) == 1
|
|
|
|
assert len(calls2) == 1
|
|
|
|
|
|
|
|
# Call second time. Methods should not get called
|
|
|
|
test_throttle1()
|
|
|
|
test_throttle2()
|
|
|
|
|
|
|
|
assert len(calls1) == 1
|
|
|
|
assert len(calls2) == 1
|
|
|
|
|
|
|
|
# Call again, overriding throttle, only first one should fire
|
|
|
|
test_throttle1(no_throttle=True)
|
|
|
|
test_throttle2(no_throttle=True)
|
|
|
|
|
|
|
|
assert len(calls1) == 2
|
|
|
|
assert len(calls2) == 1
|
|
|
|
|
|
|
|
with patch('homeassistant.util.utcnow', return_value=plus3):
|
2014-12-05 05:06:45 +00:00
|
|
|
test_throttle1()
|
|
|
|
test_throttle2()
|
|
|
|
|
2019-04-30 16:20:38 +00:00
|
|
|
assert len(calls1) == 2
|
|
|
|
assert len(calls2) == 1
|
2014-12-05 05:06:45 +00:00
|
|
|
|
2019-04-30 16:20:38 +00:00
|
|
|
with patch('homeassistant.util.utcnow', return_value=plus5):
|
|
|
|
test_throttle1()
|
|
|
|
test_throttle2()
|
2014-12-05 05:06:45 +00:00
|
|
|
|
2019-04-30 16:20:38 +00:00
|
|
|
assert len(calls1) == 3
|
|
|
|
assert len(calls2) == 2
|
2014-12-05 05:06:45 +00:00
|
|
|
|
|
|
|
|
2019-04-30 16:20:38 +00:00
|
|
|
def test_throttle_per_instance():
|
|
|
|
"""Test that the throttle method is done per instance of a class."""
|
|
|
|
class Tester:
|
|
|
|
"""A tester class for the throttle."""
|
2014-12-05 05:06:45 +00:00
|
|
|
|
2019-04-30 16:20:38 +00:00
|
|
|
@util.Throttle(timedelta(seconds=1))
|
|
|
|
def hello(self):
|
|
|
|
"""Test the throttle."""
|
|
|
|
return True
|
2014-12-04 09:14:27 +00:00
|
|
|
|
2019-04-30 16:20:38 +00:00
|
|
|
assert Tester().hello()
|
|
|
|
assert Tester().hello()
|
2015-10-09 06:49:55 +00:00
|
|
|
|
2016-03-09 09:25:50 +00:00
|
|
|
|
2019-04-30 16:20:38 +00:00
|
|
|
def test_throttle_on_method():
|
|
|
|
"""Test that throttle works when wrapping a method."""
|
|
|
|
class Tester:
|
|
|
|
"""A tester class for the throttle."""
|
2015-10-09 06:49:55 +00:00
|
|
|
|
2019-04-30 16:20:38 +00:00
|
|
|
def hello(self):
|
|
|
|
"""Test the throttle."""
|
|
|
|
return True
|
2015-10-11 17:42:42 +00:00
|
|
|
|
2019-04-30 16:20:38 +00:00
|
|
|
tester = Tester()
|
|
|
|
throttled = util.Throttle(timedelta(seconds=1))(tester.hello)
|
2016-03-09 09:25:50 +00:00
|
|
|
|
2019-04-30 16:20:38 +00:00
|
|
|
assert throttled()
|
|
|
|
assert throttled() is None
|
2015-10-11 17:42:42 +00:00
|
|
|
|
|
|
|
|
2019-04-30 16:20:38 +00:00
|
|
|
def test_throttle_on_two_method():
|
|
|
|
"""Test that throttle works when wrapping two methods."""
|
|
|
|
class Tester:
|
|
|
|
"""A test class for the throttle."""
|
2016-02-27 22:18:56 +00:00
|
|
|
|
2019-04-30 16:20:38 +00:00
|
|
|
@util.Throttle(timedelta(seconds=1))
|
|
|
|
def hello(self):
|
|
|
|
"""Test the throttle."""
|
|
|
|
return True
|
2016-03-09 09:25:50 +00:00
|
|
|
|
2019-04-30 16:20:38 +00:00
|
|
|
@util.Throttle(timedelta(seconds=1))
|
|
|
|
def goodbye(self):
|
|
|
|
"""Test the throttle."""
|
|
|
|
return True
|
2016-02-27 22:18:56 +00:00
|
|
|
|
2019-04-30 16:20:38 +00:00
|
|
|
tester = Tester()
|
2016-02-27 22:18:56 +00:00
|
|
|
|
2019-04-30 16:20:38 +00:00
|
|
|
assert tester.hello()
|
|
|
|
assert tester.goodbye()
|
2016-02-27 22:18:56 +00:00
|
|
|
|
2017-10-03 03:25:04 +00:00
|
|
|
|
2019-04-30 16:20:38 +00:00
|
|
|
@patch.object(util, 'random')
|
|
|
|
def test_get_random_string(mock_random):
|
|
|
|
"""Test get random string."""
|
|
|
|
results = ['A', 'B', 'C']
|
2017-10-03 03:25:04 +00:00
|
|
|
|
2019-04-30 16:20:38 +00:00
|
|
|
def mock_choice(choices):
|
|
|
|
return results.pop(0)
|
2017-10-03 03:25:04 +00:00
|
|
|
|
2019-04-30 16:20:38 +00:00
|
|
|
generator = MagicMock()
|
|
|
|
generator.choice.side_effect = mock_choice
|
|
|
|
mock_random.SystemRandom.return_value = generator
|
2017-10-03 03:25:04 +00:00
|
|
|
|
2019-04-30 16:20:38 +00:00
|
|
|
assert util.get_random_string(length=3) == 'ABC'
|
2018-03-10 03:38:51 +00:00
|
|
|
|
|
|
|
|
|
|
|
async def test_throttle_async():
|
|
|
|
"""Test Throttle decorator with async method."""
|
|
|
|
@util.Throttle(timedelta(seconds=2))
|
|
|
|
async def test_method():
|
|
|
|
"""Only first call should return a value."""
|
|
|
|
return True
|
|
|
|
|
|
|
|
assert (await test_method()) is True
|
|
|
|
assert (await test_method()) is None
|
2018-03-17 03:27:05 +00:00
|
|
|
|
|
|
|
@util.Throttle(timedelta(seconds=2), timedelta(seconds=0.1))
|
|
|
|
async def test_method2():
|
|
|
|
"""Only first call should return a value."""
|
|
|
|
return True
|
|
|
|
|
|
|
|
assert (await test_method2()) is True
|
|
|
|
assert (await test_method2()) is None
|