core/tests/components/camera/test_uvc.py

291 lines
11 KiB
Python

"""The tests for UVC camera module."""
import socket
import unittest
from unittest import mock
import requests
from uvcclient import camera
from uvcclient import nvr
from homeassistant.setup import setup_component
from homeassistant.components.camera import uvc
from tests.common import get_test_home_assistant
class TestUVCSetup(unittest.TestCase):
"""Test the UVC camera platform."""
def setUp(self):
"""Setup things to be run when tests are started."""
self.hass = get_test_home_assistant()
def tearDown(self):
"""Stop everything that was started."""
self.hass.stop()
@mock.patch('uvcclient.nvr.UVCRemote')
@mock.patch.object(uvc, 'UnifiVideoCamera')
def test_setup_full_config(self, mock_uvc, mock_remote):
""""Test the setup with full configuration."""
config = {
'platform': 'uvc',
'nvr': 'foo',
'password': 'bar',
'port': 123,
'key': 'secret',
}
fake_cameras = [
{'uuid': 'one', 'name': 'Front', 'id': 'id1'},
{'uuid': 'two', 'name': 'Back', 'id': 'id2'},
{'uuid': 'three', 'name': 'Old AirCam', 'id': 'id3'},
]
def fake_get_camera(uuid):
""""Create a fake camera."""
if uuid == 'id3':
return {'model': 'airCam'}
else:
return {'model': 'UVC'}
mock_remote.return_value.index.return_value = fake_cameras
mock_remote.return_value.get_camera.side_effect = fake_get_camera
mock_remote.return_value.server_version = (3, 2, 0)
assert setup_component(self.hass, 'camera', {'camera': config})
self.assertEqual(mock_remote.call_count, 1)
self.assertEqual(
mock_remote.call_args, mock.call('foo', 123, 'secret')
)
mock_uvc.assert_has_calls([
mock.call(mock_remote.return_value, 'id1', 'Front', 'bar'),
mock.call(mock_remote.return_value, 'id2', 'Back', 'bar'),
])
@mock.patch('uvcclient.nvr.UVCRemote')
@mock.patch.object(uvc, 'UnifiVideoCamera')
def test_setup_partial_config(self, mock_uvc, mock_remote):
""""Test the setup with partial configuration."""
config = {
'platform': 'uvc',
'nvr': 'foo',
'key': 'secret',
}
fake_cameras = [
{'uuid': 'one', 'name': 'Front', 'id': 'id1'},
{'uuid': 'two', 'name': 'Back', 'id': 'id2'},
]
mock_remote.return_value.index.return_value = fake_cameras
mock_remote.return_value.get_camera.return_value = {'model': 'UVC'}
mock_remote.return_value.server_version = (3, 2, 0)
assert setup_component(self.hass, 'camera', {'camera': config})
self.assertEqual(mock_remote.call_count, 1)
self.assertEqual(
mock_remote.call_args, mock.call('foo', 7080, 'secret')
)
mock_uvc.assert_has_calls([
mock.call(mock_remote.return_value, 'id1', 'Front', 'ubnt'),
mock.call(mock_remote.return_value, 'id2', 'Back', 'ubnt'),
])
@mock.patch('uvcclient.nvr.UVCRemote')
@mock.patch.object(uvc, 'UnifiVideoCamera')
def test_setup_partial_config_v31x(self, mock_uvc, mock_remote):
"""Test the setup with a v3.1.x server."""
config = {
'platform': 'uvc',
'nvr': 'foo',
'key': 'secret',
}
fake_cameras = [
{'uuid': 'one', 'name': 'Front', 'id': 'id1'},
{'uuid': 'two', 'name': 'Back', 'id': 'id2'},
]
mock_remote.return_value.index.return_value = fake_cameras
mock_remote.return_value.get_camera.return_value = {'model': 'UVC'}
mock_remote.return_value.server_version = (3, 1, 3)
assert setup_component(self.hass, 'camera', {'camera': config})
self.assertEqual(mock_remote.call_count, 1)
self.assertEqual(
mock_remote.call_args, mock.call('foo', 7080, 'secret')
)
mock_uvc.assert_has_calls([
mock.call(mock_remote.return_value, 'one', 'Front', 'ubnt'),
mock.call(mock_remote.return_value, 'two', 'Back', 'ubnt'),
])
@mock.patch.object(uvc, 'UnifiVideoCamera')
def test_setup_incomplete_config(self, mock_uvc):
"""Test the setup with incomplete configuration."""
assert setup_component(
self.hass, 'camera', {'platform': 'uvc', 'nvr': 'foo'})
assert not mock_uvc.called
assert setup_component(
self.hass, 'camera', {'platform': 'uvc', 'key': 'secret'})
assert not mock_uvc.called
assert setup_component(
self.hass, 'camera', {'platform': 'uvc', 'port': 'invalid'})
assert not mock_uvc.called
@mock.patch.object(uvc, 'UnifiVideoCamera')
@mock.patch('uvcclient.nvr.UVCRemote')
def test_setup_nvr_errors(self, mock_remote, mock_uvc):
"""Test for NVR errors."""
errors = [nvr.NotAuthorized, nvr.NvrError,
requests.exceptions.ConnectionError]
config = {
'platform': 'uvc',
'nvr': 'foo',
'key': 'secret',
}
for error in errors:
mock_remote.return_value.index.side_effect = error
assert setup_component(self.hass, 'camera', config)
assert not mock_uvc.called
class TestUVC(unittest.TestCase):
"""Test class for UVC."""
def setup_method(self, method):
""""Setup the mock camera."""
self.nvr = mock.MagicMock()
self.uuid = 'uuid'
self.name = 'name'
self.password = 'seekret'
self.uvc = uvc.UnifiVideoCamera(self.nvr, self.uuid, self.name,
self.password)
self.nvr.get_camera.return_value = {
'model': 'UVC Fake',
'recordingSettings': {
'fullTimeRecordEnabled': True,
},
'host': 'host-a',
'internalHost': 'host-b',
'username': 'admin',
}
self.nvr.server_version = (3, 2, 0)
def test_properties(self):
""""Test the properties."""
self.assertEqual(self.name, self.uvc.name)
self.assertTrue(self.uvc.is_recording)
self.assertEqual('Ubiquiti', self.uvc.brand)
self.assertEqual('UVC Fake', self.uvc.model)
@mock.patch('uvcclient.store.get_info_store')
@mock.patch('uvcclient.camera.UVCCameraClientV320')
def test_login(self, mock_camera, mock_store):
""""Test the login."""
self.uvc._login()
self.assertEqual(mock_camera.call_count, 1)
self.assertEqual(
mock_camera.call_args, mock.call('host-a', 'admin', 'seekret')
)
self.assertEqual(mock_camera.return_value.login.call_count, 1)
self.assertEqual(mock_camera.return_value.login.call_args, mock.call())
@mock.patch('uvcclient.store.get_info_store')
@mock.patch('uvcclient.camera.UVCCameraClient')
def test_login_v31x(self, mock_camera, mock_store):
"""Test login with v3.1.x server."""
self.nvr.server_version = (3, 1, 3)
self.uvc._login()
self.assertEqual(mock_camera.call_count, 1)
self.assertEqual(
mock_camera.call_args, mock.call('host-a', 'admin', 'seekret')
)
self.assertEqual(mock_camera.return_value.login.call_count, 1)
self.assertEqual(mock_camera.return_value.login.call_args, mock.call())
@mock.patch('uvcclient.store.get_info_store')
@mock.patch('uvcclient.camera.UVCCameraClientV320')
def test_login_tries_both_addrs_and_caches(self, mock_camera, mock_store):
""""Test the login tries."""
responses = [0]
def fake_login(*a):
"""Fake login."""
try:
responses.pop(0)
raise socket.error
except IndexError:
pass
mock_store.return_value.get_camera_password.return_value = None
mock_camera.return_value.login.side_effect = fake_login
self.uvc._login()
self.assertEqual(2, mock_camera.call_count)
self.assertEqual('host-b', self.uvc._connect_addr)
mock_camera.reset_mock()
self.uvc._login()
self.assertEqual(mock_camera.call_count, 1)
self.assertEqual(
mock_camera.call_args, mock.call('host-b', 'admin', 'seekret')
)
self.assertEqual(mock_camera.return_value.login.call_count, 1)
self.assertEqual(mock_camera.return_value.login.call_args, mock.call())
@mock.patch('uvcclient.store.get_info_store')
@mock.patch('uvcclient.camera.UVCCameraClientV320')
def test_login_fails_both_properly(self, mock_camera, mock_store):
""""Test if login fails properly."""
mock_camera.return_value.login.side_effect = socket.error
self.assertEqual(None, self.uvc._login())
self.assertEqual(None, self.uvc._connect_addr)
def test_camera_image_tries_login_bails_on_failure(self):
""""Test retrieving failure."""
with mock.patch.object(self.uvc, '_login') as mock_login:
mock_login.return_value = False
self.assertEqual(None, self.uvc.camera_image())
self.assertEqual(mock_login.call_count, 1)
self.assertEqual(mock_login.call_args, mock.call())
def test_camera_image_logged_in(self):
""""Test the login state."""
self.uvc._camera = mock.MagicMock()
self.assertEqual(self.uvc._camera.get_snapshot.return_value,
self.uvc.camera_image())
def test_camera_image_error(self):
""""Test the camera image error."""
self.uvc._camera = mock.MagicMock()
self.uvc._camera.get_snapshot.side_effect = camera.CameraConnectError
self.assertEqual(None, self.uvc.camera_image())
def test_camera_image_reauths(self):
""""Test the re-authentication."""
responses = [0]
def fake_snapshot():
"""Fake snapshot."""
try:
responses.pop()
raise camera.CameraAuthError()
except IndexError:
pass
return 'image'
self.uvc._camera = mock.MagicMock()
self.uvc._camera.get_snapshot.side_effect = fake_snapshot
with mock.patch.object(self.uvc, '_login') as mock_login:
self.assertEqual('image', self.uvc.camera_image())
self.assertEqual(mock_login.call_count, 1)
self.assertEqual(mock_login.call_args, mock.call())
self.assertEqual([], responses)
def test_camera_image_reauths_only_once(self):
""""Test if the re-authentication only happens once."""
self.uvc._camera = mock.MagicMock()
self.uvc._camera.get_snapshot.side_effect = camera.CameraAuthError
with mock.patch.object(self.uvc, '_login') as mock_login:
self.assertRaises(camera.CameraAuthError, self.uvc.camera_image)
self.assertEqual(mock_login.call_count, 1)
self.assertEqual(mock_login.call_args, mock.call())