Пример #1
0
async def test_stream_stopped_while_decoding(hass):
    """Tests that worker quits when stop() is called while decodign."""
    # Add some synchronization so that the test can pause the background
    # worker. When the worker is stopped, the test invokes stop() which
    # will cause the worker thread to exit once it enters the decode
    # loop
    worker_open = threading.Event()
    worker_wake = threading.Event()

    stream = Stream(hass, STREAM_SOURCE, {}, hass.data[DOMAIN][ATTR_SETTINGS])
    stream.add_provider(HLS_PROVIDER)

    py_av = MockPyAv()
    py_av.container.packets = PacketSequence(TEST_SEQUENCE_LENGTH)

    def blocking_open(stream_source, *args, **kwargs):
        # Let test know the thread is running
        worker_open.set()
        # Block worker thread until test wakes up
        worker_wake.wait()
        return py_av.open(stream_source, args, kwargs)

    with patch("av.open", new=blocking_open):
        await stream.start()
        assert worker_open.wait(TIMEOUT)
        # Note: There is a race here where the worker could start as soon
        # as the wake event is sent, completing all decode work.
        worker_wake.set()
        await stream.stop()

    # Stream is still considered available when the worker was still active and asked to stop
    assert stream.available
Пример #2
0
async def async_decode_stream(hass, packets, py_av=None, stream_settings=None):
    """Start a stream worker that decodes incoming stream packets into output segments."""
    stream = Stream(
        hass, STREAM_SOURCE, {}, stream_settings or hass.data[DOMAIN][ATTR_SETTINGS]
    )
    stream.add_provider(HLS_PROVIDER)

    if not py_av:
        py_av = MockPyAv()
    py_av.container.packets = iter(packets)  # Can't be rewound

    with patch("av.open", new=py_av.open), patch(
        "homeassistant.components.stream.core.StreamOutput.put",
        side_effect=py_av.capture_buffer.capture_output_segment,
    ):
        try:
            run_worker(hass, stream, STREAM_SOURCE, stream_settings)
        except StreamEndedError:
            # Tests only use a limited number of packets, then the worker exits as expected. In
            # production, stream ending would be unexpected.
            pass
        finally:
            # Wait for all packets to be flushed even when exceptions are thrown
            await hass.async_block_till_done()

    return py_av.capture_buffer
Пример #3
0
async def test_stream_open_fails(hass):
    """Test failure on stream open."""
    stream = Stream(hass, STREAM_SOURCE, {})
    stream.add_provider(HLS_PROVIDER)
    with patch("av.open") as av_open, pytest.raises(StreamWorkerError):
        av_open.side_effect = av.error.InvalidDataError(-2, "error")
        run_worker(hass, stream, STREAM_SOURCE)
        await hass.async_block_till_done()
        av_open.assert_called_once()
Пример #4
0
async def test_stream_open_fails(hass):
    """Test failure on stream open."""
    stream = Stream(hass, STREAM_SOURCE)
    stream.add_provider(STREAM_OUTPUT_FORMAT)
    with patch("av.open") as av_open:
        av_open.side_effect = av.error.InvalidDataError(-2, "error")
        stream_worker(hass, stream, threading.Event())
        await hass.async_block_till_done()
        av_open.assert_called_once()
Пример #5
0
async def test_stream_open_fails(hass):
    """Test failure on stream open."""
    stream = Stream(hass, STREAM_SOURCE, {})
    stream.add_provider(HLS_PROVIDER)
    with patch("av.open") as av_open:
        av_open.side_effect = av.error.InvalidDataError(-2, "error")
        segment_buffer = SegmentBuffer(hass, stream.outputs)
        stream_worker(STREAM_SOURCE, {}, segment_buffer, threading.Event())
        await hass.async_block_till_done()
        av_open.assert_called_once()
async def test_worker_log(hass, caplog):
    """Test that the worker logs the url without username and password."""
    stream = Stream(hass, "https://*****:*****@foo.bar", {})
    stream.add_provider(HLS_PROVIDER)

    with patch("av.open") as av_open, pytest.raises(StreamWorkerError) as err:
        av_open.side_effect = av.error.InvalidDataError(-2, "error")
        run_worker(hass, stream, "https://*****:*****@foo.bar")
        await hass.async_block_till_done()
    assert str(err.value) == "Error opening stream https://****:****@foo.bar"
    assert "https://*****:*****@foo.bar" not in caplog.text
Пример #7
0
async def test_worker_log(hass, caplog):
    """Test that the worker logs the url without username and password."""
    stream = Stream(hass, "https://*****:*****@foo.bar", {})
    stream.add_provider(HLS_PROVIDER)
    with patch("av.open") as av_open:
        av_open.side_effect = av.error.InvalidDataError(-2, "error")
        segment_buffer = SegmentBuffer(hass, stream.outputs)
        stream_worker("https://*****:*****@foo.bar", {}, segment_buffer,
                      threading.Event())
        await hass.async_block_till_done()
    assert "https://*****:*****@foo.bar" not in caplog.text
    assert "https://****:****@foo.bar" in caplog.text
Пример #8
0
async def async_decode_stream(hass, packets, py_av=None):
    """Start a stream worker that decodes incoming stream packets into output segments."""
    stream = Stream(hass, STREAM_SOURCE)
    stream.add_provider(STREAM_OUTPUT_FORMAT)

    if not py_av:
        py_av = MockPyAv()
    py_av.container.packets = packets

    with patch("av.open", new=py_av.open), patch(
            "homeassistant.components.stream.core.StreamOutput.put",
            side_effect=py_av.capture_buffer.capture_output_segment,
    ):
        stream_worker(hass, stream, threading.Event())
        await hass.async_block_till_done()

    return py_av.capture_buffer
Пример #9
0
async def test_update_stream_source(hass):
    """Tests that the worker is re-invoked when the stream source is updated."""
    worker_open = threading.Event()
    worker_wake = threading.Event()

    stream = Stream(hass, STREAM_SOURCE, {})
    stream.add_provider(HLS_PROVIDER)
    # Note that keepalive is not set here.  The stream is "restarted" even though
    # it is not stopping due to failure.

    py_av = MockPyAv()
    py_av.container.packets = PacketSequence(TEST_SEQUENCE_LENGTH)

    last_stream_source = None

    def blocking_open(stream_source, *args, **kwargs):
        nonlocal last_stream_source
        if not isinstance(stream_source, io.BytesIO):
            last_stream_source = stream_source
        # Let test know the thread is running
        worker_open.set()
        # Block worker thread until test wakes up
        worker_wake.wait()
        return py_av.open(stream_source, args, kwargs)

    with patch("av.open", new=blocking_open), patch(
            "homeassistant.components.stream.worker.SegmentBuffer.check_flush_part",
            side_effect=MockFlushPart.wrapped_check_flush_part,
            autospec=True,
    ):
        stream.start()
        assert worker_open.wait(TIMEOUT)
        assert last_stream_source == STREAM_SOURCE

        # Update the stream source, then the test wakes up the worker and assert
        # that it re-opens the new stream (the test again waits on thread_started)
        worker_open.clear()
        stream.update_source(STREAM_SOURCE + "-updated-source")
        worker_wake.set()
        assert worker_open.wait(TIMEOUT)
        assert last_stream_source == STREAM_SOURCE + "-updated-source"
        worker_wake.set()

        # Cleanup
        stream.stop()
Пример #10
0
async def async_decode_stream(hass, packets, py_av=None):
    """Start a stream worker that decodes incoming stream packets into output segments."""
    stream = Stream(hass, STREAM_SOURCE, {})
    stream.add_provider(HLS_PROVIDER)

    if not py_av:
        py_av = MockPyAv()
    py_av.container.packets = iter(packets)  # Can't be rewound

    with patch("av.open", new=py_av.open), patch(
            "homeassistant.components.stream.core.StreamOutput.put",
            side_effect=py_av.capture_buffer.capture_output_segment,
    ):
        segment_buffer = SegmentBuffer(hass, stream.outputs)
        stream_worker(STREAM_SOURCE, {}, segment_buffer, threading.Event())
        await hass.async_block_till_done()

    return py_av.capture_buffer
Пример #11
0
async def test_update_stream_source(hass):
    """Tests that the worker is re-invoked when the stream source is updated."""
    worker_open = threading.Event()
    worker_wake = threading.Event()

    stream = Stream(hass, STREAM_SOURCE, {})
    stream.add_provider(HLS_PROVIDER)
    # Note that retries are disabled by default in tests, however the stream is "restarted" when
    # the stream source is updated.

    py_av = MockPyAv()
    py_av.container.packets = PacketSequence(TEST_SEQUENCE_LENGTH)

    last_stream_source = None

    def blocking_open(stream_source, *args, **kwargs):
        nonlocal last_stream_source
        if not isinstance(stream_source, io.BytesIO):
            last_stream_source = stream_source
            # Let test know the thread is running
            worker_open.set()
            # Block worker thread until test wakes up
            worker_wake.wait()
        return py_av.open(stream_source, args, kwargs)

    with patch("av.open", new=blocking_open):
        stream.start()
        assert worker_open.wait(TIMEOUT)
        assert last_stream_source == STREAM_SOURCE
        assert stream.available

        # Update the stream source, then the test wakes up the worker and assert
        # that it re-opens the new stream (the test again waits on thread_started)
        worker_open.clear()
        stream.update_source(STREAM_SOURCE + "-updated-source")
        worker_wake.set()
        assert worker_open.wait(TIMEOUT)
        assert last_stream_source == STREAM_SOURCE + "-updated-source"
        worker_wake.set()
        assert stream.available

        # Cleanup
        stream.stop()
Пример #12
0
async def test_update_stream_source(hass):
    """Tests that the worker is re-invoked when the stream source is updated."""
    worker_open = threading.Event()
    worker_wake = threading.Event()

    stream = Stream(hass, STREAM_SOURCE)
    stream.add_provider(STREAM_OUTPUT_FORMAT)
    # Note that keepalive is not set here.  The stream is "restarted" even though
    # it is not stopping due to failure.

    py_av = MockPyAv()
    py_av.container.packets = PacketSequence(TEST_SEQUENCE_LENGTH)

    last_stream_source = None

    def blocking_open(stream_source, *args, **kwargs):
        nonlocal last_stream_source
        if not isinstance(stream_source, io.BytesIO):
            last_stream_source = stream_source
        # Let test know the thread is running
        worker_open.set()
        # Block worker thread until test wakes up
        worker_wake.wait()
        return py_av.open(stream_source, args, kwargs)

    with patch("av.open", new=blocking_open):
        stream.start()
        assert worker_open.wait(TIMEOUT)
        assert last_stream_source == STREAM_SOURCE

        # Update the stream source, then the test wakes up the worker and assert
        # that it re-opens the new stream (the test again waits on thread_started)
        worker_open.clear()
        stream.update_source(STREAM_SOURCE + "-updated-source")
        worker_wake.set()
        assert worker_open.wait(TIMEOUT)
        assert last_stream_source == STREAM_SOURCE + "-updated-source"
        worker_wake.set()

        # Ccleanup
        stream.stop()
Пример #13
0
def preload_stream(hass, stream_source):
    """Preload a stream for use in tests."""
    stream = Stream(hass, stream_source)
    hass.data[DOMAIN][ATTR_STREAMS][stream_source] = stream
    return stream
Пример #14
0
 def add_provider(self, fmt, timeout=OUTPUT_IDLE_TIMEOUT):
     """Add a finished event to Stream.add_provider."""
     provider = Stream.add_provider(self, fmt, timeout)
     provider_ready.set()
     return provider