File: test_live_stream.py

package info (click to toggle)
python-logi-circle 0.2.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 300 kB
  • sloc: python: 1,685; xml: 16; sh: 5; makefile: 4
file content (152 lines) | stat: -rw-r--r-- 6,735 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
"""The tests for the Logi API platform."""
import json
import os
from unittest.mock import MagicMock, patch
import aresponses
from tests.test_camera import TestCamera
from logi_circle.const import (API_HOST,
                               ACCESSORIES_ENDPOINT,
                               LIVE_RTSP_ENDPOINT,
                               LIVE_IMAGE_ENDPOINT,
                               ACCEPT_IMAGE_HEADER,
                               DEFAULT_IMAGE_QUALITY,
                               DEFAULT_IMAGE_REFRESH)
from .helpers import async_return, FakeStream
TEMP_IMAGE = 'temp.jpg'


class TestLiveStream(TestCamera):
    """Unit test for the LiveStream class."""

    def cleanup(self):
        """Cleanup any assets downloaded as part of the unit tests."""
        super(TestLiveStream, self).cleanup()
        if os.path.isfile(TEMP_IMAGE):
            os.remove(TEMP_IMAGE)

    def test_get_image(self):
        """Test response handling for get_image method"""
        endpoint = '%s/%s%s' % (ACCESSORIES_ENDPOINT, self.test_camera.id, LIVE_IMAGE_ENDPOINT)

        async def run_test():
            async with aresponses.ResponsesMockServer(loop=self.loop) as arsps:
                arsps.add(API_HOST, endpoint, 'get',
                          aresponses.Response(status=200,
                                              text="Look ma I'm an image",
                                              headers={'content-type': 'image/jpeg'}))
                arsps.add(API_HOST, endpoint, 'get',
                          aresponses.Response(status=200,
                                              text="What a purdy picture",
                                              headers={'content-type': 'image/jpeg'}))

                # I am of course cheating here by returning text instead of an image
                # for image requests. get_image trusts that the Logi API will always
                # return a valid image for these requests so I don't want to overcomplicate
                # the test.

                # Test return of image
                img = await self.test_camera.live_stream.download_jpeg()
                self.assertEqual(img, b"Look ma I'm an image")

                # Test download of image to disk
                await self.test_camera.live_stream.download_jpeg(filename=TEMP_IMAGE)
                with open(TEMP_IMAGE, 'r') as test_file:
                    data = test_file.read()
                    self.assertEqual(data, "What a purdy picture")

        self.loop.run_until_complete(run_test())

    def test_get_image_params(self):
        """Test handling of quality and refresh parameters"""
        endpoint = '%s/%s%s' % (ACCESSORIES_ENDPOINT, self.test_camera.id, LIVE_IMAGE_ENDPOINT)

        # Spy on fetch requests
        self.logi._fetch = MagicMock(
            return_value=async_return(FakeStream()))

        async def run_test():
            # Test defaults
            await self.test_camera.live_stream.download_jpeg()

            self.logi._fetch.assert_called_with(
                headers=ACCEPT_IMAGE_HEADER,
                params={'quality': DEFAULT_IMAGE_QUALITY,
                        'refresh': str(DEFAULT_IMAGE_REFRESH).lower()},
                raw=True,
                url=endpoint)

            # Test quality
            await self.test_camera.live_stream.download_jpeg(quality=55)
            self.logi._fetch.assert_called_with(
                headers=ACCEPT_IMAGE_HEADER,
                params={'quality': 55,
                        'refresh': str(DEFAULT_IMAGE_REFRESH).lower()},
                raw=True,
                url=endpoint)

            await self.test_camera.live_stream.download_jpeg(refresh=True)
            self.logi._fetch.assert_called_with(
                headers=ACCEPT_IMAGE_HEADER,
                params={'quality': DEFAULT_IMAGE_QUALITY,
                        'refresh': 'true'},
                raw=True,
                url=endpoint)

        self.loop.run_until_complete(run_test())

    def test_get_rtsp_url(self):
        """Test retrieval of RTSP URL"""
        endpoint = '%s/%s%s' % (ACCESSORIES_ENDPOINT, self.test_camera.id, LIVE_RTSP_ENDPOINT)
        expected_rtsp_uri = json.loads(self.fixtures['rtsp_uri'])['rtsp_uri'].replace('rtsp:', 'rtsps:')

        async def run_test():
            async with aresponses.ResponsesMockServer(loop=self.loop) as arsps:
                arsps.add(API_HOST, endpoint, 'get',
                          aresponses.Response(status=200,
                                              text=self.fixtures['rtsp_uri'],
                                              headers={'content-type': 'application/json'}))

                rtsp_uri = await self.test_camera.live_stream.get_rtsp_url()
                self.assertEqual(expected_rtsp_uri, rtsp_uri)

        self.loop.run_until_complete(run_test())

    def test_get_download_rtsp(self):
        """Test download of RTSP stream"""
        # pylint: disable=invalid-name
        TEST_RTSP_URL = 'rtsps://woop.woop.com/abc123'
        TEST_DURATION = 915
        TEST_FILENAME = 'test.mp4'
        TEST_FFMPEG_BIN = '/mock/ffmpeg'
        # pylint: enable=invalid-name

        self.test_camera.live_stream.get_rtsp_url = MagicMock(
            return_value=async_return(TEST_RTSP_URL))

        async def run_test():
            with patch('subprocess.check_call') as mock_subprocess:
                self.logi.ffmpeg_path = TEST_FFMPEG_BIN
                await self.test_camera.live_stream.download_rtsp(duration=TEST_DURATION,
                                                                 filename=TEST_FILENAME,
                                                                 blocking=True)

                # Check ffmpeg bin is first argument
                self.assertEqual(mock_subprocess.call_args[0][0][0], TEST_FFMPEG_BIN)

                # Test RTSP URI is somewhere in the call
                self.assertIn(TEST_RTSP_URL, mock_subprocess.call_args[0][0])

                # Test duration is somewhere in the call
                self.assertIn(str(TEST_DURATION), mock_subprocess.call_args[0][0])

                # Test filename is somewhere in the call
                self.assertIn(TEST_FILENAME, mock_subprocess.call_args[0][0])

            # Download should raise if ffmpeg not detected
            self.logi.ffmpeg_path = None
            with self.assertRaises(RuntimeError):
                await self.test_camera.live_stream.download_rtsp(duration=TEST_DURATION,
                                                                 filename=TEST_FILENAME,
                                                                 blocking=True)

        self.loop.run_until_complete(run_test())