def test_pause(_LOGGER):
    consumer = _consumer.Consumer()
    consumer._can_consume.set()

    assert consumer.pause() is None
    assert not consumer._can_consume.is_set()
    _LOGGER.debug.assert_called_once_with('Pausing consumer')
def test_blocking_consume_two_exceptions():
    policy = mock.create_autospec(base.BasePolicy, instance=True)

    exc1 = NameError('Oh noes.')
    exc2 = ValueError('Something grumble.')
    policy.on_exception.side_effect = OnException(acceptable=exc1)

    response_generator1 = RaisingResponseGenerator(exc1)
    response_generator2 = RaisingResponseGenerator(exc2)
    policy.call_rpc.side_effect = (response_generator1, response_generator2)

    consumer = _consumer.Consumer()
    consumer.resume()
    consumer._consumer_thread = mock.create_autospec(threading.Thread,
                                                     instance=True)

    # Establish that we get responses until we are sent the exiting event.
    assert consumer._blocking_consume(policy) is None
    assert consumer._consumer_thread is None

    # Check mocks.
    assert policy.call_rpc.call_count == 2
    assert response_generator1.next_calls == 1
    assert response_generator2.next_calls == 1
    policy.on_exception.assert_has_calls([mock.call(exc1), mock.call(exc2)])
def test_resume(_LOGGER):
    consumer = _consumer.Consumer()
    consumer._can_consume.clear()

    assert consumer.resume() is None
    assert consumer._can_consume.is_set()
    _LOGGER.debug.assert_called_once_with('Resuming consumer')
def test_blocking_consume_when_exiting(_LOGGER):
    consumer = _consumer.Consumer()
    assert consumer._stopped.is_set() is False
    consumer._stopped.set()

    # Make sure method cleanly exits.
    assert consumer._blocking_consume(None) is None

    _LOGGER.debug.assert_called_once_with('Event signaled consumer exit.')
def test_paused():
    consumer = _consumer.Consumer()
    assert consumer.paused is True

    consumer._can_consume.set()
    assert consumer.paused is False

    consumer._can_consume.clear()
    assert consumer.paused is True
def test_stop_request_generator_response_not_done():
    consumer = _consumer.Consumer()

    response_generator = mock.Mock(spec=('done', ))
    response_generator.done.return_value = False
    stopped = consumer._stop_request_generator(None, response_generator)
    assert stopped is False

    # Check mocks.
    response_generator.done.assert_called_once_with()
def test_blocking_consume():
    policy = mock.Mock(spec=('call_rpc', 'on_response'))
    policy.call_rpc.return_value = iter((mock.sentinel.A, mock.sentinel.B))

    consumer = _consumer.Consumer()
    consumer.resume()

    assert consumer._blocking_consume(policy) is None
    policy.call_rpc.assert_called_once()
    policy.on_response.assert_has_calls(
        [mock.call(mock.sentinel.A), mock.call(mock.sentinel.B)])
def test_blocking_consume():
    policy = mock.create_autospec(base.BasePolicy, instance=True)
    policy.call_rpc.return_value = iter((mock.sentinel.A, mock.sentinel.B))

    consumer = _consumer.Consumer()
    consumer.resume()

    assert consumer._blocking_consume(policy) is None
    policy.call_rpc.assert_called_once()
    policy.on_response.assert_has_calls(
        [mock.call(mock.sentinel.A),
         mock.call(mock.sentinel.B)])
def test_stop_consuming():
    consumer = _consumer.Consumer()
    assert consumer._stopped.is_set() is False
    thread = mock.Mock(spec=threading.Thread)
    consumer._consumer_thread = thread

    assert consumer.stop_consuming() is None

    # Make sure state was updated.
    assert consumer._stopped.is_set() is True
    assert consumer._consumer_thread is None
    # Check mocks.
    thread.join.assert_called_once_with()
def test_start_consuming():
    creds = mock.Mock(spec=credentials.Credentials)
    client = subscriber.Client(credentials=creds)
    policy = client.subscribe('sub_name_e')
    consumer = _consumer.Consumer()
    with mock.patch.object(threading, 'Thread', autospec=True) as Thread:
        consumer.start_consuming(policy)

    assert consumer._stopped.is_set() is False
    Thread.assert_called_once_with(
        name=_consumer._BIDIRECTIONAL_CONSUMER_NAME,
        target=consumer._blocking_consume,
        args=(policy, ),
    )
    assert consumer._consumer_thread is Thread.return_value
def test_stop_request_generator_queue_non_empty():
    # Model scenario tested:
    # - The request generator **is** running
    # - The request queue **is not** empty
    # Expected result:
    # - ``_stop_request_generator()`` can't call ``.close()`` (since
    #   the generator is running) but then returns with ``False`` because
    #   the queue **is not** empty
    consumer = _consumer.Consumer()
    # Attach a "fake" queue to the request generator so the generator can
    # block on an empty queue while the consumer's queue is not empty.
    queue_ = queue.Queue()
    received = queue.Queue()
    request_generator = basic_queue_generator(queue_, received)
    # Make sure the consumer's queue is not empty.
    item1 = 'not-empty'
    consumer._request_queue.put(item1)

    thread = threading.Thread(target=next, args=(request_generator, ))
    thread.start()

    # Make sure the generator is stuck at the blocked ``.get()``
    # in ``thread``.
    while not request_generator.gi_running:
        pass
    assert received.empty()
    assert request_generator.gi_frame is not None

    response_generator = mock.Mock(spec=('done', ))
    response_generator.done.return_value = True
    stopped = consumer._stop_request_generator(request_generator,
                                               response_generator)
    assert stopped is False

    # Make sure the generator is **still** not finished.
    assert request_generator.gi_running
    assert request_generator.gi_frame is not None
    assert consumer._request_queue.get() == item1
    # Allow the generator to exit.
    item2 = 'just-exit'
    queue_.put(item2)
    # Wait until it's actually done.
    while request_generator.gi_running:
        pass
    assert received.get() == item2

    # Check mocks.
    response_generator.done.assert_called_once_with()
Exemple #12
0
    def __init__(self,
                 client,
                 subscription,
                 flow_control=types.FlowControl(),
                 histogram_data=None):
        self._client = client
        self._subscription = subscription
        self._consumer = _consumer.Consumer()
        self._ack_deadline = 10
        self._last_histogram_size = 0
        self._future = None
        self.flow_control = flow_control
        self.histogram = _histogram.Histogram(data=histogram_data)

        # These are for internal flow control tracking.
        # They should not need to be used by subclasses.
        self._bytes = 0
        self._ack_on_resume = set()
def test_stop_request_generator_not_running():
    # Model scenario tested:
    # - The request generator **is not** running
    # - The request queue **is not** empty
    # Expected result:
    # - ``_stop_request_generator()`` successfully calls ``.close()``
    consumer = _consumer.Consumer()
    queue_ = consumer._request_queue
    received = queue.Queue()
    request_generator = basic_queue_generator(queue_, received)

    item1 = 'unblock-please'
    item2 = 'still-here'
    queue_.put(item1)
    queue_.put(item2)
    assert not queue_.empty()
    assert received.empty()
    thread = threading.Thread(target=next, args=(request_generator, ))
    thread.start()

    # Make sure the generator is not stuck at the blocked ``.get()``
    # in the thread.
    while request_generator.gi_running:
        pass
    assert received.get() == item1
    # Make sure it **isn't** done.
    assert request_generator.gi_frame is not None

    response_generator = mock.Mock(spec=('done', ))
    response_generator.done.return_value = True
    stopped = consumer._stop_request_generator(request_generator,
                                               response_generator)
    assert stopped is True

    # Make sure it **is** done.
    assert not request_generator.gi_running
    assert request_generator.gi_frame is None
    assert not queue_.empty()
    assert queue_.get() == item2
    assert queue_.empty()

    # Check mocks.
    response_generator.done.assert_called_once_with()
def test_blocking_consume_on_exception():
    policy = mock.create_autospec(base.BasePolicy, instance=True)
    policy.call_rpc.return_value = iter((mock.sentinel.A, mock.sentinel.B))
    exc = TypeError('Bad things!')
    policy.on_response.side_effect = exc

    consumer = _consumer.Consumer()
    consumer.resume()
    consumer._consumer_thread = mock.Mock(spec=threading.Thread)
    policy.on_exception.side_effect = OnException()

    # Establish that we get responses until we are sent the exiting event.
    consumer._blocking_consume(policy)
    assert consumer._consumer_thread is None

    # Check mocks.
    policy.call_rpc.assert_called_once()
    policy.on_response.assert_called_once_with(mock.sentinel.A)
    policy.on_exception.assert_called_once_with(exc)
Exemple #15
0
    def __init__(self, client, subscription,
                 flow_control=types.FlowControl(), histogram_data=None):
        self._client = client
        self._subscription = subscription
        self._consumer = _consumer.Consumer()
        self._ack_deadline = 10
        self._last_histogram_size = 0
        self._future = None
        self.flow_control = flow_control
        self.histogram = _histogram.Histogram(data=histogram_data)
        """.Histogram: the histogram tracking ack latency."""
        self.leased_messages = {}
        """dict[str, float]: A mapping of ack IDs to the local time when the
            ack ID was initially leased in seconds since the epoch."""

        # These are for internal flow control tracking.
        # They should not need to be used by subclasses.
        self._bytes = 0
        self._ack_on_resume = set()
def test_stop_request_generator_running():
    # Model scenario tested:
    # - The request generator **is** running
    # - The request queue **is** empty
    # Expected result:
    # - ``_stop_request_generator()`` can't call ``.close()`` (since
    #   the generator is running) but then verifies that the queue is
    #   empty and sends ``STOP`` into the queue to successfully stop
    #   the generator
    consumer = _consumer.Consumer()
    queue_ = consumer._request_queue
    received = queue.Queue()
    request_generator = basic_queue_generator(queue_, received)

    thread = threading.Thread(target=next, args=(request_generator, ))
    thread.start()

    # Make sure the generator is stuck at the blocked ``.get()``
    # in the thread.
    while not request_generator.gi_running:
        pass
    assert received.empty()
    assert request_generator.gi_frame is not None

    response_generator = mock.Mock(spec=('done', ))
    response_generator.done.return_value = True
    stopped = consumer._stop_request_generator(request_generator,
                                               response_generator)
    assert stopped is True

    # Make sure it **is** done, though we may have to wait until
    # the generator finishes (it has a few instructions between the
    # ``get()`` and the ``break``).
    while request_generator.gi_running:
        pass
    request_generator.close()
    assert request_generator.gi_frame is None
    assert received.get() == _helper_threads.STOP
    assert queue_.empty()

    # Check mocks.
    response_generator.done.assert_called_once_with()
def test_stop_request_generator_close_failure():
    # Model scenario tested:
    # - The input isn't actually a generator
    # Expected result:
    # - ``_stop_request_generator()`` falls through to the ``LOGGER.error``
    #   case and returns ``False``
    consumer = _consumer.Consumer()

    request_generator = mock.Mock(spec=('close', ))
    request_generator.close.side_effect = TypeError('Really, not a generator')

    response_generator = mock.Mock(spec=('done', ))
    response_generator.done.return_value = True
    stopped = consumer._stop_request_generator(request_generator,
                                               response_generator)
    assert stopped is False

    # Make sure close() was only called once.
    request_generator.close.assert_called_once_with()
    response_generator.done.assert_called_once_with()
def test_blocking_consume_iter_exception_while_paused():
    policy = mock.create_autospec(base.BasePolicy, instance=True)
    exc = TypeError('Bad things!')
    policy.call_rpc.return_value = RaisingResponseGenerator(exc, active=False)

    consumer = _consumer.Consumer()
    # Ensure the consume is paused.
    consumer.pause()
    consumer._consumer_thread = mock.Mock(spec=threading.Thread)
    policy.on_exception.side_effect = OnException()

    # Start the thread. It should not block forever but should notice the rpc
    # is inactive and raise the exception from the stream and then exit
    # because on_exception returns false.
    consumer._blocking_consume(policy)
    assert consumer._consumer_thread is None

    # Check mocks.
    policy.call_rpc.assert_called_once()
    policy.on_exception.assert_called_once_with(exc)
Exemple #19
0
    def __init__(self,
                 client,
                 subscription,
                 flow_control=types.FlowControl(),
                 histogram_data=None):
        """Instantiate the policy.

        Args:
            client (~.pubsub_v1.subscriber.client): The subscriber client used
                to create this instance.
            subscription (str): The name of the subscription. The canonical
                format for this is
                ``projects/{project}/subscriptions/{subscription}``.
            flow_control (~.pubsub_v1.types.FlowControl): The flow control
                settings.
            histogram_data (dict): Optional: A structure to store the histogram
                data for predicting appropriate ack times. If set, this should
                be a dictionary-like object.

                .. note::
                    Additionally, the histogram relies on the assumption
                    that the dictionary will properly sort keys provided
                    that all keys are positive integers. If you are sending
                    your own dictionary class, ensure this assumption holds
                    or you will get strange behavior.
        """
        self._client = client
        self._subscription = subscription
        self._consumer = _consumer.Consumer(self)
        self._ack_deadline = 10
        self._last_histogram_size = 0
        self._future = None
        self.flow_control = flow_control
        self.histogram = _histogram.Histogram(data=histogram_data)

        # These are for internal flow control tracking.
        # They should not need to be used by subclasses.
        self._bytes = 0
        self._ack_on_resume = set()
        self._paused = False
def test_request_generator_thread():
    consumer = _consumer.Consumer()
    creds = mock.Mock(spec=credentials.Credentials)
    client = subscriber.Client(credentials=creds)
    policy = client.subscribe('sub_name_e')
    generator = consumer._request_generator_thread(policy)

    # The first request that comes from the request generator thread
    # should always be the initial request.
    initial_request = next(generator)
    assert initial_request.subscription == 'sub_name_e'
    assert initial_request.stream_ack_deadline_seconds == 10

    # Subsequent requests correspond to items placed in the request queue.
    consumer.send_request(types.StreamingPullRequest(ack_ids=['i']))
    request = next(generator)
    assert request.ack_ids == ['i']

    # The poison pill should stop the loop.
    consumer.send_request(_helper_threads.STOP)
    with pytest.raises(StopIteration):
        next(generator)
def test_send_request():
    consumer = _consumer.Consumer()
    request = types.StreamingPullRequest(subscription='foo')
    with mock.patch.object(queue.Queue, 'put') as put:
        consumer.send_request(request)
        put.assert_called_once_with(request)
def create_consumer():
    creds = mock.Mock(spec=credentials.Credentials)
    client = subscriber.Client(credentials=creds)
    subscription = client.subscribe('sub_name_e')
    return _consumer.Consumer(policy=subscription)