"""Test config validators."""
from datetime import timedelta, datetime, date
import enum
import os
from socket import _GLOBAL_DEFAULT_TIMEOUT
from unittest.mock import Mock, patch
import uuid

import homeassistant
import pytest
import voluptuous as vol

import homeassistant.helpers.config_validation as cv


def test_boolean():
    """Test boolean validation."""
    schema = vol.Schema(cv.boolean)

    for value in ('T', 'negative', 'lock'):
        with pytest.raises(vol.MultipleInvalid):
            schema(value)

    for value in ('true', 'On', '1', 'YES', 'enable', 1, True):
        assert schema(value)

    for value in ('false', 'Off', '0', 'NO', 'disable', 0, False):
        assert not schema(value)


def test_latitude():
    """Test latitude validation."""
    schema = vol.Schema(cv.latitude)

    for value in ('invalid', None, -91, 91, '-91', '91', '123.01A'):
        with pytest.raises(vol.MultipleInvalid):
            schema(value)

    for value in ('-89', 89, '12.34'):
        schema(value)


def test_longitude():
    """Test longitude validation."""
    schema = vol.Schema(cv.longitude)

    for value in ('invalid', None, -181, 181, '-181', '181', '123.01A'):
        with pytest.raises(vol.MultipleInvalid):
            schema(value)

    for value in ('-179', 179, '12.34'):
        schema(value)


def test_port():
    """Test TCP/UDP network port."""
    schema = vol.Schema(cv.port)

    for value in ('invalid', None, -1, 0, 80000, '81000'):
        with pytest.raises(vol.MultipleInvalid):
            schema(value)

    for value in ('1000', 21, 24574):
        schema(value)


def test_isfile():
    """Validate that the value is an existing file."""
    schema = vol.Schema(cv.isfile)

    fake_file = 'this-file-does-not.exist'
    assert not os.path.isfile(fake_file)

    for value in ('invalid', None, -1, 0, 80000, fake_file):
        with pytest.raises(vol.Invalid):
            schema(value)

    # patching methods that allow us to fake a file existing
    # with write access
    with patch('os.path.isfile', Mock(return_value=True)), \
            patch('os.access', Mock(return_value=True)):
        schema('test.txt')


def test_url():
    """Test URL."""
    schema = vol.Schema(cv.url)

    for value in ('invalid', None, 100, 'htp://ha.io', 'http//ha.io',
                  'http://??,**', 'https://??,**'):
        with pytest.raises(vol.MultipleInvalid):
            schema(value)

    for value in ('http://localhost', 'https://localhost/test/index.html',
                  'http://home-assistant.io', 'http://home-assistant.io/test/',
                  'https://community.home-assistant.io/'):
        assert schema(value)


def test_platform_config():
    """Test platform config validation."""
    options = (
        {},
        {'hello': 'world'},
    )
    for value in options:
        with pytest.raises(vol.MultipleInvalid):
            cv.PLATFORM_SCHEMA(value)

    options = (
        {'platform': 'mqtt'},
        {'platform': 'mqtt', 'beer': 'yes'},
    )
    for value in options:
        cv.PLATFORM_SCHEMA_BASE(value)


def test_ensure_list():
    """Test ensure_list."""
    schema = vol.Schema(cv.ensure_list)
    assert [] == schema(None)
    assert [1] == schema(1)
    assert [1] == schema([1])
    assert ['1'] == schema('1')
    assert ['1'] == schema(['1'])
    assert [{'1': '2'}] == schema({'1': '2'})


def test_entity_id():
    """Test entity ID validation."""
    schema = vol.Schema(cv.entity_id)

    with pytest.raises(vol.MultipleInvalid):
        schema('invalid_entity')

    assert schema('sensor.LIGHT') == 'sensor.light'


def test_entity_ids():
    """Test entity ID validation."""
    schema = vol.Schema(cv.entity_ids)

    options = (
        'invalid_entity',
        'sensor.light,sensor_invalid',
        ['invalid_entity'],
        ['sensor.light', 'sensor_invalid'],
        ['sensor.light,sensor_invalid'],
    )
    for value in options:
        with pytest.raises(vol.MultipleInvalid):
            schema(value)

    options = (
        [],
        ['sensor.light'],
        'sensor.light'
    )
    for value in options:
        schema(value)

    assert schema('sensor.LIGHT, light.kitchen ') == [
        'sensor.light', 'light.kitchen'
    ]


def test_entity_domain():
    """Test entity domain validation."""
    schema = vol.Schema(cv.entity_domain('sensor'))

    options = (
        'invalid_entity',
        'cover.demo',
    )

    for value in options:
        with pytest.raises(vol.MultipleInvalid):
            print(value)
            schema(value)

    assert schema('sensor.LIGHT') == 'sensor.light'


def test_entities_domain():
    """Test entities domain validation."""
    schema = vol.Schema(cv.entities_domain('sensor'))

    options = (
        None,
        '',
        'invalid_entity',
        ['sensor.light', 'cover.demo'],
        ['sensor.light', 'sensor_invalid'],
    )

    for value in options:
        with pytest.raises(vol.MultipleInvalid):
            schema(value)

    options = (
        'sensor.light',
        ['SENSOR.light'],
        ['sensor.light', 'sensor.demo']
    )
    for value in options:
        schema(value)

    assert schema('sensor.LIGHT, sensor.demo ') == [
        'sensor.light', 'sensor.demo'
    ]
    assert schema(['sensor.light', 'SENSOR.demo']) == [
        'sensor.light', 'sensor.demo'
    ]


def test_ensure_list_csv():
    """Test ensure_list_csv."""
    schema = vol.Schema(cv.ensure_list_csv)

    options = (
        None,
        12,
        [],
        ['string'],
        'string1,string2'
    )
    for value in options:
        schema(value)

    assert schema('string1, string2 ') == [
        'string1', 'string2'
    ]


def test_event_schema():
    """Test event_schema validation."""
    options = (
        {}, None,
        {
            'event_data': {},
        },
        {
            'event': 'state_changed',
            'event_data': 1,
        },
    )
    for value in options:
        with pytest.raises(vol.MultipleInvalid):
            cv.EVENT_SCHEMA(value)

    options = (
        {'event': 'state_changed'},
        {'event': 'state_changed', 'event_data': {'hello': 'world'}},
    )
    for value in options:
        cv.EVENT_SCHEMA(value)


def test_icon():
    """Test icon validation."""
    schema = vol.Schema(cv.icon)

    for value in (False, 'work'):
        with pytest.raises(vol.MultipleInvalid):
            schema(value)

    schema('mdi:work')
    schema('custom:prefix')


def test_time_period():
    """Test time_period validation."""
    schema = vol.Schema(cv.time_period)

    options = (
        None, '', 'hello:world', '12:', '12:34:56:78',
        {}, {'wrong_key': -10}
    )
    for value in options:
        with pytest.raises(vol.MultipleInvalid):
            schema(value)

    options = (
        '8:20', '23:59', '-8:20', '-23:59:59', '-48:00', {'minutes': 5}, 1, '5'
    )
    for value in options:
        schema(value)

    assert timedelta(seconds=180) == schema('180')
    assert timedelta(hours=23, minutes=59) == schema('23:59')
    assert -1 * timedelta(hours=1, minutes=15) == schema('-1:15')


def test_service():
    """Test service validation."""
    schema = vol.Schema(cv.service)

    with pytest.raises(vol.MultipleInvalid):
        schema('invalid_turn_on')

    schema('homeassistant.turn_on')


def test_service_schema():
    """Test service_schema validation."""
    options = (
        {}, None,
        {
            'service': 'homeassistant.turn_on',
            'service_template': 'homeassistant.turn_on'
        },
        {
            'data': {'entity_id': 'light.kitchen'},
        },
        {
            'service': 'homeassistant.turn_on',
            'data': None
        },
        {
            'service': 'homeassistant.turn_on',
            'data_template': {
                'brightness': '{{ no_end'
            }
        },
    )
    for value in options:
        with pytest.raises(vol.MultipleInvalid):
            cv.SERVICE_SCHEMA(value)

    options = (
        {'service': 'homeassistant.turn_on'},
        {
            'service': 'homeassistant.turn_on',
            'entity_id': 'light.kitchen',
        },
        {
            'service': 'light.turn_on',
            'entity_id': 'all',
        },
        {
            'service': 'homeassistant.turn_on',
            'entity_id': ['light.kitchen', 'light.ceiling'],
        },
    )
    for value in options:
        cv.SERVICE_SCHEMA(value)


def test_slug():
    """Test slug validation."""
    schema = vol.Schema(cv.slug)

    for value in (None, 'hello world'):
        with pytest.raises(vol.MultipleInvalid):
            schema(value)

    for value in (12345, 'hello'):
        schema(value)


def test_string():
    """Test string validation."""
    schema = vol.Schema(cv.string)

    with pytest.raises(vol.Invalid):
        schema(None)

    with pytest.raises(vol.Invalid):
        schema([])

    with pytest.raises(vol.Invalid):
        schema({})

    for value in (True, 1, 'hello'):
        schema(value)


def test_temperature_unit():
    """Test temperature unit validation."""
    schema = vol.Schema(cv.temperature_unit)

    with pytest.raises(vol.MultipleInvalid):
        schema('K')

    schema('C')
    schema('F')


def test_x10_address():
    """Test x10 addr validator."""
    schema = vol.Schema(cv.x10_address)
    with pytest.raises(vol.Invalid):
        schema('Q1')
        schema('q55')
        schema('garbage_addr')

    schema('a1')
    schema('C11')


def test_template():
    """Test template validator."""
    schema = vol.Schema(cv.template)

    for value in (None, '{{ partial_print }', '{% if True %}Hello', ['test']):
        with pytest.raises(vol.Invalid):
            schema(value)

    options = (
        1, 'Hello',
        '{{ beer }}',
        '{% if 1 == 1 %}Hello{% else %}World{% endif %}',
    )
    for value in options:
        schema(value)


def test_template_complex():
    """Test template_complex validator."""
    schema = vol.Schema(cv.template_complex)

    for value in (None, '{{ partial_print }', '{% if True %}Hello'):
        with pytest.raises(vol.MultipleInvalid):
            schema(value)

    options = (
        1, 'Hello',
        '{{ beer }}',
        '{% if 1 == 1 %}Hello{% else %}World{% endif %}',
        {'test': 1, 'test2': '{{ beer }}'},
        ['{{ beer }}', 1]
    )
    for value in options:
        schema(value)

    # ensure the validator didn't mutate the input
    assert options == (
        1, 'Hello',
        '{{ beer }}',
        '{% if 1 == 1 %}Hello{% else %}World{% endif %}',
        {'test': 1, 'test2': '{{ beer }}'},
        ['{{ beer }}', 1]
    )


def test_time_zone():
    """Test time zone validation."""
    schema = vol.Schema(cv.time_zone)

    with pytest.raises(vol.MultipleInvalid):
        schema('America/Do_Not_Exist')

    schema('America/Los_Angeles')
    schema('UTC')


def test_date():
    """Test date validation."""
    schema = vol.Schema(cv.date)

    for value in ['Not a date', '23:42', '2016-11-23T18:59:08']:
        with pytest.raises(vol.Invalid):
            schema(value)

    schema(datetime.now().date())
    schema('2016-11-23')


def test_time():
    """Test date validation."""
    schema = vol.Schema(cv.time)

    for value in ['Not a time', '2016-11-23', '2016-11-23T18:59:08']:
        with pytest.raises(vol.Invalid):
            schema(value)

    schema(datetime.now().time())
    schema('23:42:00')
    schema('23:42')


def test_datetime():
    """Test date time validation."""
    schema = vol.Schema(cv.datetime)
    for value in [date.today(), 'Wrong DateTime', '2016-11-23']:
        with pytest.raises(vol.MultipleInvalid):
            schema(value)

    schema(datetime.now())
    schema('2016-11-23T18:59:08')


@pytest.fixture
def schema():
    """Create a schema used for testing deprecation."""
    return vol.Schema({
        'venus': cv.boolean,
        'mars': cv.boolean,
        'jupiter': cv.boolean
    })


@pytest.fixture
def version(monkeypatch):
    """Patch the version used for testing to 0.5.0."""
    monkeypatch.setattr(homeassistant.const, '__version__', '0.5.0')


def test_deprecated_with_no_optionals(caplog, schema):
    """
    Test deprecation behaves correctly when optional params are None.

    Expected behavior:
        - Outputs the appropriate deprecation warning if key is detected
        - Processes schema without changing any values
        - No warning or difference in output if key is not provided
    """
    deprecated_schema = vol.All(
        cv.deprecated('mars'),
        schema
    )

    test_data = {'mars': True}
    output = deprecated_schema(test_data.copy())
    assert len(caplog.records) == 1
    assert caplog.records[0].name == __name__
    assert ("The 'mars' option (with value 'True') is deprecated, "
            "please remove it from your configuration") in caplog.text
    assert test_data == output

    caplog.clear()
    assert len(caplog.records) == 0

    test_data = {'venus': True}
    output = deprecated_schema(test_data.copy())
    assert len(caplog.records) == 0
    assert test_data == output


def test_deprecated_with_replacement_key(caplog, schema):
    """
    Test deprecation behaves correctly when only a replacement key is provided.

    Expected behavior:
        - Outputs the appropriate deprecation warning if key is detected
        - Processes schema moving the value from key to replacement_key
        - Processes schema changing nothing if only replacement_key provided
        - No warning if only replacement_key provided
        - No warning or difference in output if neither key nor
            replacement_key are provided
    """
    deprecated_schema = vol.All(
        cv.deprecated('mars', replacement_key='jupiter'),
        schema
    )

    test_data = {'mars': True}
    output = deprecated_schema(test_data.copy())
    assert len(caplog.records) == 1
    assert ("The 'mars' option (with value 'True') is deprecated, "
            "please replace it with 'jupiter'") in caplog.text
    assert {'jupiter': True} == output

    caplog.clear()
    assert len(caplog.records) == 0

    test_data = {'jupiter': True}
    output = deprecated_schema(test_data.copy())
    assert len(caplog.records) == 0
    assert test_data == output

    test_data = {'venus': True}
    output = deprecated_schema(test_data.copy())
    assert len(caplog.records) == 0
    assert test_data == output


def test_deprecated_with_invalidation_version(caplog, schema, version):
    """
    Test deprecation behaves correctly with only an invalidation_version.

    Expected behavior:
        - Outputs the appropriate deprecation warning if key is detected
        - Processes schema without changing any values
        - No warning or difference in output if key is not provided
        - Once the invalidation_version is crossed, raises vol.Invalid if key
            is detected
    """
    deprecated_schema = vol.All(
        cv.deprecated('mars', invalidation_version='1.0.0'),
        schema
    )

    message = ("The 'mars' option (with value 'True') is deprecated, "
               "please remove it from your configuration. "
               "This option will become invalid in version 1.0.0")

    test_data = {'mars': True}
    output = deprecated_schema(test_data.copy())
    assert len(caplog.records) == 1
    assert message in caplog.text
    assert test_data == output

    caplog.clear()
    assert len(caplog.records) == 0

    test_data = {'venus': False}
    output = deprecated_schema(test_data.copy())
    assert len(caplog.records) == 0
    assert test_data == output

    invalidated_schema = vol.All(
        cv.deprecated('mars', invalidation_version='0.1.0'),
        schema
    )
    test_data = {'mars': True}
    with pytest.raises(vol.MultipleInvalid) as exc_info:
        invalidated_schema(test_data)
    assert ("The 'mars' option (with value 'True') is deprecated, "
            "please remove it from your configuration. This option will "
            "become invalid in version 0.1.0") == str(exc_info.value)


def test_deprecated_with_replacement_key_and_invalidation_version(
        caplog, schema, version
):
    """
    Test deprecation behaves with a replacement key & invalidation_version.

    Expected behavior:
        - Outputs the appropriate deprecation warning if key is detected
        - Processes schema moving the value from key to replacement_key
        - Processes schema changing nothing if only replacement_key provided
        - No warning if only replacement_key provided
        - No warning or difference in output if neither key nor
            replacement_key are provided
        - Once the invalidation_version is crossed, raises vol.Invalid if key
        is detected
    """
    deprecated_schema = vol.All(
        cv.deprecated(
            'mars', replacement_key='jupiter', invalidation_version='1.0.0'
        ),
        schema
    )

    warning = ("The 'mars' option (with value 'True') is deprecated, "
               "please replace it with 'jupiter'. This option will become "
               "invalid in version 1.0.0")

    test_data = {'mars': True}
    output = deprecated_schema(test_data.copy())
    assert len(caplog.records) == 1
    assert warning in caplog.text
    assert {'jupiter': True} == output

    caplog.clear()
    assert len(caplog.records) == 0

    test_data = {'jupiter': True}
    output = deprecated_schema(test_data.copy())
    assert len(caplog.records) == 0
    assert test_data == output

    test_data = {'venus': True}
    output = deprecated_schema(test_data.copy())
    assert len(caplog.records) == 0
    assert test_data == output

    invalidated_schema = vol.All(
        cv.deprecated(
            'mars', replacement_key='jupiter', invalidation_version='0.1.0'
        ),
        schema
    )
    test_data = {'mars': True}
    with pytest.raises(vol.MultipleInvalid) as exc_info:
        invalidated_schema(test_data)
    assert ("The 'mars' option (with value 'True') is deprecated, "
            "please replace it with 'jupiter'. This option will become "
            "invalid in version 0.1.0") == str(exc_info.value)


def test_deprecated_with_default(caplog, schema):
    """
    Test deprecation behaves correctly with a default value.

    This is likely a scenario that would never occur.

    Expected behavior:
        - Behaves identically as when the default value was not present
    """
    deprecated_schema = vol.All(
        cv.deprecated('mars', default=False),
        schema
    )

    test_data = {'mars': True}
    output = deprecated_schema(test_data.copy())
    assert len(caplog.records) == 1
    assert caplog.records[0].name == __name__
    assert ("The 'mars' option (with value 'True') is deprecated, "
            "please remove it from your configuration") in caplog.text
    assert test_data == output

    caplog.clear()
    assert len(caplog.records) == 0

    test_data = {'venus': True}
    output = deprecated_schema(test_data.copy())
    assert len(caplog.records) == 0
    assert test_data == output


def test_deprecated_with_replacement_key_and_default(caplog, schema):
    """
    Test deprecation with a replacement key and default.

    Expected behavior:
        - Outputs the appropriate deprecation warning if key is detected
        - Processes schema moving the value from key to replacement_key
        - Processes schema changing nothing if only replacement_key provided
        - No warning if only replacement_key provided
        - No warning if neither key nor replacement_key are provided
            - Adds replacement_key with default value in this case
    """
    deprecated_schema = vol.All(
        cv.deprecated('mars', replacement_key='jupiter', default=False),
        schema
    )

    test_data = {'mars': True}
    output = deprecated_schema(test_data.copy())
    assert len(caplog.records) == 1
    assert ("The 'mars' option (with value 'True') is deprecated, "
            "please replace it with 'jupiter'") in caplog.text
    assert {'jupiter': True} == output

    caplog.clear()
    assert len(caplog.records) == 0

    test_data = {'jupiter': True}
    output = deprecated_schema(test_data.copy())
    assert len(caplog.records) == 0
    assert test_data == output

    test_data = {'venus': True}
    output = deprecated_schema(test_data.copy())
    assert len(caplog.records) == 0
    assert {'venus': True, 'jupiter': False} == output

    deprecated_schema_with_default = vol.All(
        vol.Schema({
            'venus': cv.boolean,
            vol.Optional('mars', default=False): cv.boolean,
            vol.Optional('jupiter', default=False): cv.boolean
        }),
        cv.deprecated('mars', replacement_key='jupiter', default=False)
    )

    test_data = {'mars': True}
    output = deprecated_schema_with_default(test_data.copy())
    assert len(caplog.records) == 1
    assert ("The 'mars' option (with value 'True') is deprecated, "
            "please replace it with 'jupiter'") in caplog.text
    assert {'jupiter': True} == output


def test_deprecated_with_replacement_key_invalidation_version_default(
        caplog, schema, version
):
    """
    Test deprecation with a replacement key, invalidation_version & default.

    Expected behavior:
        - Outputs the appropriate deprecation warning if key is detected
        - Processes schema moving the value from key to replacement_key
        - Processes schema changing nothing if only replacement_key provided
        - No warning if only replacement_key provided
        - No warning if neither key nor replacement_key are provided
            - Adds replacement_key with default value in this case
        - Once the invalidation_version is crossed, raises vol.Invalid if key
        is detected
    """
    deprecated_schema = vol.All(
        cv.deprecated(
            'mars', replacement_key='jupiter', invalidation_version='1.0.0',
            default=False
        ),
        schema
    )

    test_data = {'mars': True}
    output = deprecated_schema(test_data.copy())
    assert len(caplog.records) == 1
    assert ("The 'mars' option (with value 'True') is deprecated, "
            "please replace it with 'jupiter'. This option will become "
            "invalid in version 1.0.0") in caplog.text
    assert {'jupiter': True} == output

    caplog.clear()
    assert len(caplog.records) == 0

    test_data = {'jupiter': True}
    output = deprecated_schema(test_data.copy())
    assert len(caplog.records) == 0
    assert test_data == output

    test_data = {'venus': True}
    output = deprecated_schema(test_data.copy())
    assert len(caplog.records) == 0
    assert {'venus': True, 'jupiter': False} == output

    invalidated_schema = vol.All(
        cv.deprecated(
            'mars', replacement_key='jupiter', invalidation_version='0.1.0'
        ),
        schema
    )
    test_data = {'mars': True}
    with pytest.raises(vol.MultipleInvalid) as exc_info:
        invalidated_schema(test_data)
    assert ("The 'mars' option (with value 'True') is deprecated, "
            "please replace it with 'jupiter'. This option will become "
            "invalid in version 0.1.0") == str(exc_info.value)


def test_key_dependency():
    """Test key_dependency validator."""
    schema = vol.Schema(cv.key_dependency('beer', 'soda'))

    options = (
        {'beer': None}
    )
    for value in options:
        with pytest.raises(vol.MultipleInvalid):
            schema(value)

    options = (
        {'beer': None, 'soda': None},
        {'soda': None}, {}
    )
    for value in options:
        schema(value)


def test_has_at_most_one_key():
    """Test has_at_most_one_key validator."""
    schema = vol.Schema(cv.has_at_most_one_key('beer', 'soda'))

    for value in (None, [], {'beer': None, 'soda': None}):
        with pytest.raises(vol.MultipleInvalid):
            schema(value)

    for value in ({}, {'beer': None}, {'soda': None}):
        schema(value)


def test_has_at_least_one_key():
    """Test has_at_least_one_key validator."""
    schema = vol.Schema(cv.has_at_least_one_key('beer', 'soda'))

    for value in (None, [], {}, {'wine': None}):
        with pytest.raises(vol.MultipleInvalid):
            schema(value)

    for value in ({'beer': None}, {'soda': None}):
        schema(value)


def test_enum():
    """Test enum validator."""
    class TestEnum(enum.Enum):
        """Test enum."""

        value1 = "Value 1"
        value2 = "Value 2"

    schema = vol.Schema(cv.enum(TestEnum))

    with pytest.raises(vol.Invalid):
        schema('value3')


def test_socket_timeout():  # pylint: disable=invalid-name
    """Test socket timeout validator."""
    schema = vol.Schema(cv.socket_timeout)

    with pytest.raises(vol.Invalid):
        schema(0.0)

    with pytest.raises(vol.Invalid):
        schema(-1)

    assert _GLOBAL_DEFAULT_TIMEOUT == schema(None)

    assert schema(1) == 1.0


def test_matches_regex():
    """Test matches_regex validator."""
    schema = vol.Schema(cv.matches_regex('.*uiae.*'))

    with pytest.raises(vol.Invalid):
        schema(1.0)

    with pytest.raises(vol.Invalid):
        schema("  nrtd   ")

    test_str = "This is a test including uiae."
    assert (schema(test_str) == test_str)


def test_is_regex():
    """Test the is_regex validator."""
    schema = vol.Schema(cv.is_regex)

    with pytest.raises(vol.Invalid):
        schema("(")

    with pytest.raises(vol.Invalid):
        schema({"a dict": "is not a regex"})

    valid_re = ".*"
    schema(valid_re)


def test_comp_entity_ids():
    """Test config validation for component entity IDs."""
    schema = vol.Schema(cv.comp_entity_ids)

    for valid in ('ALL', 'all', 'AlL', 'light.kitchen', ['light.kitchen'],
                  ['light.kitchen', 'light.ceiling'], []):
        schema(valid)

    for invalid in (['light.kitchen', 'not-entity-id'], '*', ''):
        with pytest.raises(vol.Invalid):
            schema(invalid)


def test_schema_with_slug_keys_allows_old_slugs(caplog):
    """Test schema with slug keys allowing old slugs."""
    schema = cv.schema_with_slug_keys(str)

    with patch.dict(cv.INVALID_SLUGS_FOUND, clear=True):
        for value in ('_world', 'wow__yeah'):
            caplog.clear()
            # Will raise if not allowing old slugs
            schema({value: 'yo'})
            assert "Found invalid slug {}".format(value) in caplog.text

        assert len(cv.INVALID_SLUGS_FOUND) == 2


def test_entity_id_allow_old_validation(caplog):
    """Test schema allowing old entity_ids."""
    schema = vol.Schema(cv.entity_id)

    with patch.dict(cv.INVALID_ENTITY_IDS_FOUND, clear=True):
        for value in ('hello.__world', 'great.wow__yeah'):
            caplog.clear()
            # Will raise if not allowing old entity ID
            schema(value)
            assert "Found invalid entity_id {}".format(value) in caplog.text

        assert len(cv.INVALID_ENTITY_IDS_FOUND) == 2


def test_uuid4_hex(caplog):
    """Test uuid validation."""
    schema = vol.Schema(cv.uuid4_hex)

    for value in ['Not a hex string', '0', 0]:
        with pytest.raises(vol.Invalid):
            schema(value)

    with pytest.raises(vol.Invalid):
        # the 13th char should be 4
        schema('a03d31b22eee1acc9b90eec40be6ed23')

    with pytest.raises(vol.Invalid):
        # the 17th char should be 8-a
        schema('a03d31b22eee4acc7b90eec40be6ed23')

    hex = uuid.uuid4().hex
    assert schema(hex) == hex
    assert schema(hex.upper()) == hex