mirror of
https://github.com/home-assistant/core.git
synced 2025-12-27 14:31:13 +00:00
Component "Image processing" (#5166)
* Init new component for image processing. * Add demo platform * address comments * add unittest v1 for demo * Add unittest for alpr * Add openalpr local test * Add openalpr cloud platform * Add unittest openalpr cloud platform * Update stale docstring * Address paulus comments * Update stale docstring * Add coro to function * Add coro to cloud
This commit is contained in:
1
tests/components/image_processing/__init__.py
Normal file
1
tests/components/image_processing/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""Test 'image_processing' component plaforms."""
|
||||
209
tests/components/image_processing/test_init.py
Normal file
209
tests/components/image_processing/test_init.py
Normal file
@@ -0,0 +1,209 @@
|
||||
"""The tests for the image_processing component."""
|
||||
from unittest.mock import patch, PropertyMock
|
||||
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.const import ATTR_ENTITY_PICTURE
|
||||
from homeassistant.bootstrap import setup_component
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
import homeassistant.components.image_processing as ip
|
||||
|
||||
from tests.common import get_test_home_assistant, assert_setup_component
|
||||
|
||||
|
||||
class TestSetupImageProcessing(object):
|
||||
"""Test class for setup image processing."""
|
||||
|
||||
def setup_method(self):
|
||||
"""Setup things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant()
|
||||
|
||||
def teardown_method(self):
|
||||
"""Stop everything that was started."""
|
||||
self.hass.stop()
|
||||
|
||||
def test_setup_component(self):
|
||||
"""Setup demo platfrom on image_process component."""
|
||||
config = {
|
||||
ip.DOMAIN: {
|
||||
'platform': 'demo'
|
||||
}
|
||||
}
|
||||
|
||||
with assert_setup_component(1, ip.DOMAIN):
|
||||
setup_component(self.hass, ip.DOMAIN, config)
|
||||
|
||||
def test_setup_component_with_service(self):
|
||||
"""Setup demo platfrom on image_process component test service."""
|
||||
config = {
|
||||
ip.DOMAIN: {
|
||||
'platform': 'demo'
|
||||
}
|
||||
}
|
||||
|
||||
with assert_setup_component(1, ip.DOMAIN):
|
||||
setup_component(self.hass, ip.DOMAIN, config)
|
||||
|
||||
assert self.hass.services.has_service(ip.DOMAIN, 'scan')
|
||||
|
||||
|
||||
class TestImageProcessing(object):
|
||||
"""Test class for image processing."""
|
||||
|
||||
def setup_method(self):
|
||||
"""Setup things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant()
|
||||
|
||||
config = {
|
||||
ip.DOMAIN: {
|
||||
'platform': 'demo'
|
||||
},
|
||||
'camera': {
|
||||
'platform': 'demo'
|
||||
},
|
||||
}
|
||||
|
||||
with patch('homeassistant.components.image_processing.demo.'
|
||||
'DemoImageProcessing.should_poll',
|
||||
new_callable=PropertyMock(return_value=False)):
|
||||
setup_component(self.hass, ip.DOMAIN, config)
|
||||
|
||||
state = self.hass.states.get('camera.demo_camera')
|
||||
self.url = "{0}{1}".format(
|
||||
self.hass.config.api.base_url,
|
||||
state.attributes.get(ATTR_ENTITY_PICTURE))
|
||||
|
||||
def teardown_method(self):
|
||||
"""Stop everything that was started."""
|
||||
self.hass.stop()
|
||||
|
||||
@patch('homeassistant.components.camera.demo.DemoCamera.camera_image',
|
||||
autospec=True, return_value=b'Test')
|
||||
@patch('homeassistant.components.image_processing.demo.'
|
||||
'DemoImageProcessing.process_image', autospec=True)
|
||||
def test_get_image_from_camera(self, mock_process, mock_camera):
|
||||
"""Grab a image from camera entity."""
|
||||
self.hass.start()
|
||||
|
||||
ip.scan(self.hass, entity_id='image_processing.demo')
|
||||
self.hass.block_till_done()
|
||||
|
||||
assert mock_camera.called
|
||||
assert mock_process.called
|
||||
|
||||
assert mock_process.call_args[0][1] == b'Test'
|
||||
|
||||
@patch('homeassistant.components.camera.async_get_image',
|
||||
side_effect=HomeAssistantError())
|
||||
@patch('homeassistant.components.image_processing.demo.'
|
||||
'DemoImageProcessing.process_image', autospec=True)
|
||||
def test_get_image_without_exists_camera(self, mock_process, mock_image):
|
||||
"""Try to get image without exists camera."""
|
||||
self.hass.states.remove('camera.demo_camera')
|
||||
|
||||
ip.scan(self.hass, entity_id='image_processing.demo')
|
||||
self.hass.block_till_done()
|
||||
|
||||
assert mock_image.called
|
||||
assert not mock_process.called
|
||||
|
||||
|
||||
class TestImageProcessingAlpr(object):
|
||||
"""Test class for image processing."""
|
||||
|
||||
def setup_method(self):
|
||||
"""Setup things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant()
|
||||
|
||||
config = {
|
||||
ip.DOMAIN: {
|
||||
'platform': 'demo'
|
||||
},
|
||||
'camera': {
|
||||
'platform': 'demo'
|
||||
},
|
||||
}
|
||||
|
||||
with patch('homeassistant.components.image_processing.demo.'
|
||||
'DemoImageProcessingAlpr.should_poll',
|
||||
new_callable=PropertyMock(return_value=False)):
|
||||
setup_component(self.hass, ip.DOMAIN, config)
|
||||
|
||||
state = self.hass.states.get('camera.demo_camera')
|
||||
self.url = "{0}{1}".format(
|
||||
self.hass.config.api.base_url,
|
||||
state.attributes.get(ATTR_ENTITY_PICTURE))
|
||||
|
||||
self.alpr_events = []
|
||||
|
||||
@callback
|
||||
def mock_alpr_event(event):
|
||||
"""Mock event."""
|
||||
self.alpr_events.append(event)
|
||||
|
||||
self.hass.bus.listen('found_plate', mock_alpr_event)
|
||||
|
||||
def teardown_method(self):
|
||||
"""Stop everything that was started."""
|
||||
self.hass.stop()
|
||||
|
||||
def test_alpr_event_single_call(self, aioclient_mock):
|
||||
"""Setup and scan a picture and test plates from event."""
|
||||
aioclient_mock.get(self.url, content=b'image')
|
||||
|
||||
ip.scan(self.hass, entity_id='image_processing.demo_alpr')
|
||||
self.hass.block_till_done()
|
||||
|
||||
state = self.hass.states.get('image_processing.demo_alpr')
|
||||
|
||||
assert len(self.alpr_events) == 4
|
||||
assert state.state == 'AC3829'
|
||||
|
||||
event_data = [event.data for event in self.alpr_events if
|
||||
event.data.get('plate') == 'AC3829']
|
||||
assert len(event_data) == 1
|
||||
assert event_data[0]['plate'] == 'AC3829'
|
||||
assert event_data[0]['confidence'] == 98.3
|
||||
assert event_data[0]['entity_id'] == 'image_processing.demo_alpr'
|
||||
|
||||
def test_alpr_event_double_call(self, aioclient_mock):
|
||||
"""Setup and scan a picture and test plates from event."""
|
||||
aioclient_mock.get(self.url, content=b'image')
|
||||
|
||||
ip.scan(self.hass, entity_id='image_processing.demo_alpr')
|
||||
ip.scan(self.hass, entity_id='image_processing.demo_alpr')
|
||||
self.hass.block_till_done()
|
||||
|
||||
state = self.hass.states.get('image_processing.demo_alpr')
|
||||
|
||||
assert len(self.alpr_events) == 4
|
||||
assert state.state == 'AC3829'
|
||||
|
||||
event_data = [event.data for event in self.alpr_events if
|
||||
event.data.get('plate') == 'AC3829']
|
||||
assert len(event_data) == 1
|
||||
assert event_data[0]['plate'] == 'AC3829'
|
||||
assert event_data[0]['confidence'] == 98.3
|
||||
assert event_data[0]['entity_id'] == 'image_processing.demo_alpr'
|
||||
|
||||
@patch('homeassistant.components.image_processing.demo.'
|
||||
'DemoImageProcessingAlpr.confidence',
|
||||
new_callable=PropertyMock(return_value=95))
|
||||
def test_alpr_event_single_call_confidence(self, confidence_mock,
|
||||
aioclient_mock):
|
||||
"""Setup and scan a picture and test plates from event."""
|
||||
aioclient_mock.get(self.url, content=b'image')
|
||||
|
||||
ip.scan(self.hass, entity_id='image_processing.demo_alpr')
|
||||
self.hass.block_till_done()
|
||||
|
||||
state = self.hass.states.get('image_processing.demo_alpr')
|
||||
|
||||
assert len(self.alpr_events) == 2
|
||||
assert state.state == 'AC3829'
|
||||
|
||||
event_data = [event.data for event in self.alpr_events if
|
||||
event.data.get('plate') == 'AC3829']
|
||||
assert len(event_data) == 1
|
||||
assert event_data[0]['plate'] == 'AC3829'
|
||||
assert event_data[0]['confidence'] == 98.3
|
||||
assert event_data[0]['entity_id'] == 'image_processing.demo_alpr'
|
||||
212
tests/components/image_processing/test_openalpr_cloud.py
Normal file
212
tests/components/image_processing/test_openalpr_cloud.py
Normal file
@@ -0,0 +1,212 @@
|
||||
"""The tests for the openalpr clooud platform."""
|
||||
import asyncio
|
||||
from unittest.mock import patch, PropertyMock
|
||||
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.const import ATTR_ENTITY_PICTURE
|
||||
from homeassistant.bootstrap import setup_component
|
||||
import homeassistant.components.image_processing as ip
|
||||
from homeassistant.components.image_processing.openalpr_cloud import (
|
||||
OPENALPR_API_URL)
|
||||
|
||||
from tests.common import (
|
||||
get_test_home_assistant, assert_setup_component, load_fixture)
|
||||
|
||||
|
||||
class TestOpenAlprCloudlSetup(object):
|
||||
"""Test class for image processing."""
|
||||
|
||||
def setup_method(self):
|
||||
"""Setup things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant()
|
||||
|
||||
def teardown_method(self):
|
||||
"""Stop everything that was started."""
|
||||
self.hass.stop()
|
||||
|
||||
def test_setup_platform(self):
|
||||
"""Setup platform with one entity."""
|
||||
config = {
|
||||
ip.DOMAIN: {
|
||||
'platform': 'openalpr_cloud',
|
||||
'source': {
|
||||
'entity_id': 'camera.demo_camera'
|
||||
},
|
||||
'region': 'eu',
|
||||
'api_key': 'sk_abcxyz123456',
|
||||
},
|
||||
'camera': {
|
||||
'platform': 'demo'
|
||||
},
|
||||
}
|
||||
|
||||
with assert_setup_component(1, ip.DOMAIN):
|
||||
setup_component(self.hass, ip.DOMAIN, config)
|
||||
|
||||
assert self.hass.states.get('image_processing.openalpr_demo_camera')
|
||||
|
||||
def test_setup_platform_name(self):
|
||||
"""Setup platform with one entity and set name."""
|
||||
config = {
|
||||
ip.DOMAIN: {
|
||||
'platform': 'openalpr_cloud',
|
||||
'source': {
|
||||
'entity_id': 'camera.demo_camera',
|
||||
'name': 'test local'
|
||||
},
|
||||
'region': 'eu',
|
||||
'api_key': 'sk_abcxyz123456',
|
||||
},
|
||||
'camera': {
|
||||
'platform': 'demo'
|
||||
},
|
||||
}
|
||||
|
||||
with assert_setup_component(1, ip.DOMAIN):
|
||||
setup_component(self.hass, ip.DOMAIN, config)
|
||||
|
||||
assert self.hass.states.get('image_processing.test_local')
|
||||
|
||||
def test_setup_platform_without_api_key(self):
|
||||
"""Setup platform with one entity without api_key."""
|
||||
config = {
|
||||
ip.DOMAIN: {
|
||||
'platform': 'openalpr_cloud',
|
||||
'source': {
|
||||
'entity_id': 'camera.demo_camera'
|
||||
},
|
||||
'region': 'eu',
|
||||
},
|
||||
'camera': {
|
||||
'platform': 'demo'
|
||||
},
|
||||
}
|
||||
|
||||
with assert_setup_component(0, ip.DOMAIN):
|
||||
setup_component(self.hass, ip.DOMAIN, config)
|
||||
|
||||
def test_setup_platform_without_region(self):
|
||||
"""Setup platform with one entity without region."""
|
||||
config = {
|
||||
ip.DOMAIN: {
|
||||
'platform': 'openalpr_cloud',
|
||||
'source': {
|
||||
'entity_id': 'camera.demo_camera'
|
||||
},
|
||||
'api_key': 'sk_abcxyz123456',
|
||||
},
|
||||
'camera': {
|
||||
'platform': 'demo'
|
||||
},
|
||||
}
|
||||
|
||||
with assert_setup_component(0, ip.DOMAIN):
|
||||
setup_component(self.hass, ip.DOMAIN, config)
|
||||
|
||||
|
||||
class TestOpenAlprCloud(object):
|
||||
"""Test class for image processing."""
|
||||
|
||||
def setup_method(self):
|
||||
"""Setup things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant()
|
||||
|
||||
config = {
|
||||
ip.DOMAIN: {
|
||||
'platform': 'openalpr_cloud',
|
||||
'source': {
|
||||
'entity_id': 'camera.demo_camera',
|
||||
'name': 'test local'
|
||||
},
|
||||
'region': 'eu',
|
||||
'api_key': 'sk_abcxyz123456',
|
||||
},
|
||||
'camera': {
|
||||
'platform': 'demo'
|
||||
},
|
||||
}
|
||||
|
||||
with patch('homeassistant.components.image_processing.openalpr_cloud.'
|
||||
'OpenAlprCloudEntity.should_poll',
|
||||
new_callable=PropertyMock(return_value=False)):
|
||||
setup_component(self.hass, ip.DOMAIN, config)
|
||||
|
||||
state = self.hass.states.get('camera.demo_camera')
|
||||
self.url = "{0}{1}".format(
|
||||
self.hass.config.api.base_url,
|
||||
state.attributes.get(ATTR_ENTITY_PICTURE))
|
||||
|
||||
self.alpr_events = []
|
||||
|
||||
@callback
|
||||
def mock_alpr_event(event):
|
||||
"""Mock event."""
|
||||
self.alpr_events.append(event)
|
||||
|
||||
self.hass.bus.listen('found_plate', mock_alpr_event)
|
||||
|
||||
self.params = {
|
||||
'secret_key': "sk_abcxyz123456",
|
||||
'tasks': "plate",
|
||||
'return_image': 0,
|
||||
'country': 'eu',
|
||||
'image_bytes': "aW1hZ2U="
|
||||
}
|
||||
|
||||
def teardown_method(self):
|
||||
"""Stop everything that was started."""
|
||||
self.hass.stop()
|
||||
|
||||
def test_openalpr_process_image(self, aioclient_mock):
|
||||
"""Setup and scan a picture and test plates from event."""
|
||||
aioclient_mock.get(self.url, content=b'image')
|
||||
aioclient_mock.post(
|
||||
OPENALPR_API_URL, params=self.params,
|
||||
text=load_fixture('alpr_cloud.json'), status=200
|
||||
)
|
||||
|
||||
ip.scan(self.hass, entity_id='image_processing.test_local')
|
||||
self.hass.block_till_done()
|
||||
|
||||
state = self.hass.states.get('image_processing.test_local')
|
||||
|
||||
assert len(aioclient_mock.mock_calls) == 2
|
||||
assert len(self.alpr_events) == 5
|
||||
assert state.attributes.get('vehicles') == 1
|
||||
assert state.state == 'H786P0J'
|
||||
|
||||
event_data = [event.data for event in self.alpr_events if
|
||||
event.data.get('plate') == 'H786P0J']
|
||||
assert len(event_data) == 1
|
||||
assert event_data[0]['plate'] == 'H786P0J'
|
||||
assert event_data[0]['confidence'] == float(90.436699)
|
||||
assert event_data[0]['entity_id'] == \
|
||||
'image_processing.test_local'
|
||||
|
||||
def test_openalpr_process_image_api_error(self, aioclient_mock):
|
||||
"""Setup and scan a picture and test api error."""
|
||||
aioclient_mock.get(self.url, content=b'image')
|
||||
aioclient_mock.post(
|
||||
OPENALPR_API_URL, params=self.params,
|
||||
text="{'error': 'error message'}", status=400
|
||||
)
|
||||
|
||||
ip.scan(self.hass, entity_id='image_processing.test_local')
|
||||
self.hass.block_till_done()
|
||||
|
||||
assert len(aioclient_mock.mock_calls) == 2
|
||||
assert len(self.alpr_events) == 0
|
||||
|
||||
def test_openalpr_process_image_api_timeout(self, aioclient_mock):
|
||||
"""Setup and scan a picture and test api error."""
|
||||
aioclient_mock.get(self.url, content=b'image')
|
||||
aioclient_mock.post(
|
||||
OPENALPR_API_URL, params=self.params,
|
||||
exc=asyncio.TimeoutError()
|
||||
)
|
||||
|
||||
ip.scan(self.hass, entity_id='image_processing.test_local')
|
||||
self.hass.block_till_done()
|
||||
|
||||
assert len(aioclient_mock.mock_calls) == 2
|
||||
assert len(self.alpr_events) == 0
|
||||
165
tests/components/image_processing/test_openalpr_local.py
Normal file
165
tests/components/image_processing/test_openalpr_local.py
Normal file
@@ -0,0 +1,165 @@
|
||||
"""The tests for the openalpr local platform."""
|
||||
import asyncio
|
||||
from unittest.mock import patch, PropertyMock, MagicMock
|
||||
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.const import ATTR_ENTITY_PICTURE
|
||||
from homeassistant.bootstrap import setup_component
|
||||
import homeassistant.components.image_processing as ip
|
||||
|
||||
from tests.common import (
|
||||
get_test_home_assistant, assert_setup_component, load_fixture)
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def mock_async_subprocess():
|
||||
"""Get a Popen mock back."""
|
||||
async_popen = MagicMock()
|
||||
|
||||
@asyncio.coroutine
|
||||
def communicate(input=None):
|
||||
"""Communicate mock."""
|
||||
fixture = bytes(load_fixture('alpr_stdout.txt'), 'utf-8')
|
||||
return (fixture, None)
|
||||
|
||||
async_popen.communicate = communicate
|
||||
return async_popen
|
||||
|
||||
|
||||
class TestOpenAlprLocalSetup(object):
|
||||
"""Test class for image processing."""
|
||||
|
||||
def setup_method(self):
|
||||
"""Setup things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant()
|
||||
|
||||
def teardown_method(self):
|
||||
"""Stop everything that was started."""
|
||||
self.hass.stop()
|
||||
|
||||
def test_setup_platform(self):
|
||||
"""Setup platform with one entity."""
|
||||
config = {
|
||||
ip.DOMAIN: {
|
||||
'platform': 'openalpr_local',
|
||||
'source': {
|
||||
'entity_id': 'camera.demo_camera'
|
||||
},
|
||||
'region': 'eu',
|
||||
},
|
||||
'camera': {
|
||||
'platform': 'demo'
|
||||
},
|
||||
}
|
||||
|
||||
with assert_setup_component(1, ip.DOMAIN):
|
||||
setup_component(self.hass, ip.DOMAIN, config)
|
||||
|
||||
assert self.hass.states.get('image_processing.openalpr_demo_camera')
|
||||
|
||||
def test_setup_platform_name(self):
|
||||
"""Setup platform with one entity and set name."""
|
||||
config = {
|
||||
ip.DOMAIN: {
|
||||
'platform': 'openalpr_local',
|
||||
'source': {
|
||||
'entity_id': 'camera.demo_camera',
|
||||
'name': 'test local'
|
||||
},
|
||||
'region': 'eu',
|
||||
},
|
||||
'camera': {
|
||||
'platform': 'demo'
|
||||
},
|
||||
}
|
||||
|
||||
with assert_setup_component(1, ip.DOMAIN):
|
||||
setup_component(self.hass, ip.DOMAIN, config)
|
||||
|
||||
assert self.hass.states.get('image_processing.test_local')
|
||||
|
||||
def test_setup_platform_without_region(self):
|
||||
"""Setup platform with one entity without region."""
|
||||
config = {
|
||||
ip.DOMAIN: {
|
||||
'platform': 'openalpr_local',
|
||||
'source': {
|
||||
'entity_id': 'camera.demo_camera'
|
||||
},
|
||||
},
|
||||
'camera': {
|
||||
'platform': 'demo'
|
||||
},
|
||||
}
|
||||
|
||||
with assert_setup_component(0, ip.DOMAIN):
|
||||
setup_component(self.hass, ip.DOMAIN, config)
|
||||
|
||||
|
||||
class TestOpenAlprLocal(object):
|
||||
"""Test class for image processing."""
|
||||
|
||||
def setup_method(self):
|
||||
"""Setup things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant()
|
||||
|
||||
config = {
|
||||
ip.DOMAIN: {
|
||||
'platform': 'openalpr_local',
|
||||
'source': {
|
||||
'entity_id': 'camera.demo_camera',
|
||||
'name': 'test local'
|
||||
},
|
||||
'region': 'eu',
|
||||
},
|
||||
'camera': {
|
||||
'platform': 'demo'
|
||||
},
|
||||
}
|
||||
|
||||
with patch('homeassistant.components.image_processing.openalpr_local.'
|
||||
'OpenAlprLocalEntity.should_poll',
|
||||
new_callable=PropertyMock(return_value=False)):
|
||||
setup_component(self.hass, ip.DOMAIN, config)
|
||||
|
||||
state = self.hass.states.get('camera.demo_camera')
|
||||
self.url = "{0}{1}".format(
|
||||
self.hass.config.api.base_url,
|
||||
state.attributes.get(ATTR_ENTITY_PICTURE))
|
||||
|
||||
self.alpr_events = []
|
||||
|
||||
@callback
|
||||
def mock_alpr_event(event):
|
||||
"""Mock event."""
|
||||
self.alpr_events.append(event)
|
||||
|
||||
self.hass.bus.listen('found_plate', mock_alpr_event)
|
||||
|
||||
def teardown_method(self):
|
||||
"""Stop everything that was started."""
|
||||
self.hass.stop()
|
||||
|
||||
@patch('asyncio.create_subprocess_exec',
|
||||
return_value=mock_async_subprocess())
|
||||
def test_openalpr_process_image(self, popen_mock, aioclient_mock):
|
||||
"""Setup and scan a picture and test plates from event."""
|
||||
aioclient_mock.get(self.url, content=b'image')
|
||||
|
||||
ip.scan(self.hass, entity_id='image_processing.test_local')
|
||||
self.hass.block_till_done()
|
||||
|
||||
state = self.hass.states.get('image_processing.test_local')
|
||||
|
||||
assert popen_mock.called
|
||||
assert len(self.alpr_events) == 5
|
||||
assert state.attributes.get('vehicles') == 1
|
||||
assert state.state == 'PE3R2X'
|
||||
|
||||
event_data = [event.data for event in self.alpr_events if
|
||||
event.data.get('plate') == 'PE3R2X']
|
||||
assert len(event_data) == 1
|
||||
assert event_data[0]['plate'] == 'PE3R2X'
|
||||
assert event_data[0]['confidence'] == float(98.9371)
|
||||
assert event_data[0]['entity_id'] == \
|
||||
'image_processing.test_local'
|
||||
Reference in New Issue
Block a user