core/tests/util/test_init.py

301 lines
9.1 KiB
Python

"""Test Home Assistant util methods."""
import unittest
from unittest.mock import patch, MagicMock
from datetime import datetime, timedelta
from homeassistant import util
import homeassistant.util.dt as dt_util
import pytest
class TestUtil(unittest.TestCase):
"""Test util methods."""
def test_sanitize_filename(self):
"""Test sanitize_filename."""
assert "test" == util.sanitize_filename("test")
assert "test" == util.sanitize_filename("/test")
assert "test" == util.sanitize_filename("..test")
assert "test" == util.sanitize_filename("\\test")
assert "test" == util.sanitize_filename("\\../test")
def test_sanitize_path(self):
"""Test sanitize_path."""
assert "test/path" == util.sanitize_path("test/path")
assert "test/path" == util.sanitize_path("~test/path")
assert "//test/path" == util.sanitize_path("~/../test/path")
def test_slugify(self):
"""Test slugify."""
assert "t_est" == util.slugify("T-!@#$!#@$!$est")
assert "test_more" == util.slugify("Test More")
assert "test_more" == util.slugify("Test_(More)")
assert "test_more" == util.slugify("Tèst_Mörê")
assert "b8_27_eb_00_00_00" == util.slugify("B8:27:EB:00:00:00")
assert "test_com" == util.slugify("test.com")
assert "greg_phone_exp_wayp1" == \
util.slugify("greg_phone - exp_wayp1")
assert "we_are_we_are_a_test_calendar" == \
util.slugify("We are, we are, a... Test Calendar")
assert "test_aouss_aou" == util.slugify("Tèst_äöüß_ÄÖÜ")
assert "ying_shi_ma" == util.slugify("影師嗎")
assert "keihuonto" == util.slugify("けいふぉんと")
def test_repr_helper(self):
"""Test repr_helper."""
assert "A" == util.repr_helper("A")
assert "5" == util.repr_helper(5)
assert "True" == util.repr_helper(True)
assert "test=1" == util.repr_helper({"test": 1})
assert "1986-07-09T12:00:00+00:00" == \
util.repr_helper(datetime(1986, 7, 9, 12, 0, 0))
def test_convert(self):
"""Test convert."""
assert 5 == util.convert("5", int)
assert 5.0 == util.convert("5", float)
assert util.convert("True", bool) is True
assert 1 == util.convert("NOT A NUMBER", int, 1)
assert 1 == util.convert(None, int, 1)
assert 1 == util.convert(object, int, 1)
def test_ensure_unique_string(self):
"""Test ensure_unique_string."""
assert "Beer_3" == \
util.ensure_unique_string("Beer", ["Beer", "Beer_2"])
assert "Beer" == \
util.ensure_unique_string("Beer", ["Wine", "Soda"])
def test_ordered_enum(self):
"""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 not (TestEnum.SECOND >= TestEnum.THIRD)
assert TestEnum.SECOND > TestEnum.FIRST
assert not (TestEnum.SECOND > TestEnum.SECOND)
assert not (TestEnum.SECOND > TestEnum.THIRD)
assert not (TestEnum.SECOND <= TestEnum.FIRST)
assert TestEnum.SECOND <= TestEnum.SECOND
assert TestEnum.SECOND <= TestEnum.THIRD
assert not (TestEnum.SECOND < TestEnum.FIRST)
assert not (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_ordered_set(self):
"""Test ordering of set."""
set1 = util.OrderedSet([1, 2, 3, 4])
set2 = util.OrderedSet([3, 4, 5])
assert 4 == len(set1)
assert 3 == len(set2)
assert 1 in set1
assert 2 in set1
assert 3 in set1
assert 4 in set1
assert 5 not in set1
assert 1 not in set2
assert 2 not in set2
assert 3 in set2
assert 4 in set2
assert 5 in set2
set1.add(5)
assert 5 in set1
set1.discard(5)
assert 5 not in set1
# Try again while key is not in
set1.discard(5)
assert 5 not in set1
assert [1, 2, 3, 4] == list(set1)
assert [4, 3, 2, 1] == list(reversed(set1))
assert 1 == set1.pop(False)
assert [2, 3, 4] == list(set1)
assert 4 == set1.pop()
assert [2, 3] == list(set1)
assert 'OrderedSet()' == str(util.OrderedSet())
assert 'OrderedSet([2, 3])' == str(set1)
assert set1 == util.OrderedSet([2, 3])
assert set1 != util.OrderedSet([3, 2])
assert set1 == set([2, 3])
assert set1 == {3, 2}
assert set1 == [2, 3]
assert set1 == [3, 2]
assert set1 != {2}
set3 = util.OrderedSet(set1)
set3.update(set2)
assert [3, 4, 5, 2] == set3
assert [3, 4, 5, 2] == set1 | set2
assert [3] == set1 & set2
assert [2] == set1 - set2
set1.update([1, 2], [5, 6])
assert [2, 3, 1, 5, 6] == set1
def test_throttle(self):
"""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 1 == len(calls1)
assert 1 == len(calls2)
# Call second time. Methods should not get called
test_throttle1()
test_throttle2()
assert 1 == len(calls1)
assert 1 == len(calls2)
# Call again, overriding throttle, only first one should fire
test_throttle1(no_throttle=True)
test_throttle2(no_throttle=True)
assert 2 == len(calls1)
assert 1 == len(calls2)
with patch('homeassistant.util.utcnow', return_value=plus3):
test_throttle1()
test_throttle2()
assert 2 == len(calls1)
assert 1 == len(calls2)
with patch('homeassistant.util.utcnow', return_value=plus5):
test_throttle1()
test_throttle2()
assert 3 == len(calls1)
assert 2 == len(calls2)
def test_throttle_per_instance(self):
"""Test that the throttle method is done per instance of a class."""
class Tester:
"""A tester class for the throttle."""
@util.Throttle(timedelta(seconds=1))
def hello(self):
"""Test the throttle."""
return True
assert Tester().hello()
assert Tester().hello()
def test_throttle_on_method(self):
"""Test that throttle works when wrapping a method."""
class Tester:
"""A tester class for the throttle."""
def hello(self):
"""Test the throttle."""
return True
tester = Tester()
throttled = util.Throttle(timedelta(seconds=1))(tester.hello)
assert throttled()
assert throttled() is None
def test_throttle_on_two_method(self):
"""Test that throttle works when wrapping two methods."""
class Tester:
"""A test class for the throttle."""
@util.Throttle(timedelta(seconds=1))
def hello(self):
"""Test the throttle."""
return True
@util.Throttle(timedelta(seconds=1))
def goodbye(self):
"""Test the throttle."""
return True
tester = Tester()
assert tester.hello()
assert tester.goodbye()
@patch.object(util, 'random')
def test_get_random_string(self, mock_random):
"""Test get random string."""
results = ['A', 'B', 'C']
def mock_choice(choices):
return results.pop(0)
generator = MagicMock()
generator.choice.side_effect = mock_choice
mock_random.SystemRandom.return_value = generator
assert util.get_random_string(length=3) == 'ABC'
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
@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