Package com.twitter.hbc.httpclient

Source Code of com.twitter.hbc.httpclient.BasicClientTest

/**
* Copyright 2013 Twitter, Inc.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/

package com.twitter.hbc.httpclient;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.twitter.hbc.BasicReconnectionManager;
import com.twitter.hbc.RateTracker;
import com.twitter.hbc.ReconnectionManager;
import com.twitter.hbc.core.HttpConstants;
import com.twitter.hbc.core.HttpHosts;
import com.twitter.hbc.core.endpoint.RawEndpoint;
import com.twitter.hbc.core.event.EventType;
import com.twitter.hbc.core.processor.HosebirdMessageProcessor;
import com.twitter.hbc.httpclient.auth.Authentication;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.conn.ClientConnectionManager;
import org.junit.Before;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

public class BasicClientTest {

  private HttpClient mockClient;
  private HttpResponse mockResponse;
  private HttpEntity mockHttpEntity;
  private StatusLine mockStatusLine;
  private ReconnectionManager mockReconnectionManager;
  private HosebirdMessageProcessor mockProcessor;
  private Authentication mockAuth;
  private RateTracker mockRateTracker;

  private InputStream mockInputStream;

  private ClientConnectionManager mockConnectionManager;

  private final ExecutorService executorService;

  public BasicClientTest() {
    ThreadFactory threadFactory = new ThreadFactoryBuilder()
            .setDaemon(true)
            .setNameFormat("hosebird-client-unit-test-%d")
            .build();
    executorService = Executors.newSingleThreadExecutor(threadFactory);
  }

  @Before
  public void setup() throws Exception {
    mockClient = mock(HttpClient.class);
    mockResponse = mock(HttpResponse.class);
    mockStatusLine = mock(StatusLine.class);

    mockReconnectionManager = mock(BasicReconnectionManager.class);
    mockConnectionManager = mock(ClientConnectionManager.class);
    mockRateTracker = mock(RateTracker.class);

    mockInputStream = mock(InputStream.class);
    mockAuth = mock(Authentication.class);

    mockProcessor = mock(HosebirdMessageProcessor.class);

    mockHttpEntity = mock(HttpEntity.class);

    // set up required mocks to mock out all of the clientbase stuff
    when(mockClient.execute(any(HttpUriRequest.class)))
            .thenReturn(mockResponse);
    when(mockClient.getConnectionManager())
            .thenReturn(mockConnectionManager);

    when(mockResponse.getStatusLine())
            .thenReturn(mockStatusLine);
    when(mockResponse.getEntity())
            .thenReturn(mockHttpEntity);
    when(mockHttpEntity.getContent())
            .thenReturn(mockInputStream);
    when(mockStatusLine.getReasonPhrase())
            .thenReturn("reason");
  }

  // These tests are going to get a little hairy in terms of mocking, but doable.
  // Some of the functionality is already tested in ClientBaseTest, but this tests
  // the overall flow. Worth it?

  @Test
  public void testIOExceptionDuringProcessing() throws Exception {
    ClientBase clientBase = new ClientBase("name",
            mockClient, new HttpHosts("http://hi"), new RawEndpoint("/endpoint", HttpConstants.HTTP_GET), mockAuth,
            mockProcessor, mockReconnectionManager, mockRateTracker
    );
    BasicClient client = new BasicClient(clientBase, executorService);
    final CountDownLatch latch = new CountDownLatch(1);
    when(mockStatusLine.getStatusCode())
            .thenReturn(200);

    doNothing().when(mockProcessor).setup(any(InputStream.class));
    doThrow(new IOException()).
            doThrow(new IOException()).
            doThrow(new IOException()).
            doAnswer(new Answer() {
              @Override
              public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
                latch.countDown();
                return null;
              }
            }).when(mockProcessor).process();

    client.connect();
    latch.await();
    assertFalse(clientBase.isDone());
    verify(mockProcessor, times(4)).setup(any(InputStream.class));
    // throw 3 exceptions, 4th one keeps going
    verify(mockProcessor, atLeast(4)).process();

    client.stop();
    verify(mockConnectionManager, atLeastOnce()).shutdown();
    assertTrue(client.isDone());
    assertEquals(EventType.STOPPED_BY_USER, clientBase.getExitEvent().getEventType());
  }

  @Test
  public void testInterruptedExceptionDuringProcessing() throws Exception {
    ClientBase clientBase = new ClientBase("name",
            mockClient, new HttpHosts("http://hi"), new RawEndpoint("/endpoint", HttpConstants.HTTP_GET), mockAuth,
            mockProcessor, mockReconnectionManager, mockRateTracker
    );

    when(mockStatusLine.getStatusCode())
            .thenReturn(200);

    doThrow(new InterruptedException()).when(mockProcessor).process();

    when(mockClient.getConnectionManager())
            .thenReturn(mockConnectionManager);

    BasicClient client = new BasicClient(clientBase, executorService);

    assertFalse(clientBase.isDone());
    client.connect();
    assertTrue(client.waitForFinish(100));
    assertTrue(client.isDone());
    verify(mockProcessor).setup(any(InputStream.class));
    verify(mockConnectionManager, atLeastOnce()).shutdown();
    assertEquals(EventType.STOPPED_BY_ERROR, client.getExitEvent().getEventType());
    assertTrue(client.getExitEvent().getUnderlyingException() instanceof InterruptedException);
  }

  @Test
  public void testConnectionRetries() throws Exception {
    HttpHosts mockHttpHosts = mock(HttpHosts.class);
    ClientBase clientBase = new ClientBase("name",
            mockClient, mockHttpHosts, new RawEndpoint("/endpoint", HttpConstants.HTTP_GET), mockAuth,
            mockProcessor, mockReconnectionManager, mockRateTracker
    );

    BasicClient client = new BasicClient(clientBase, executorService);
    final CountDownLatch latch = new CountDownLatch(1);
    when(mockHttpHosts.nextHost())
            .thenReturn("http://somehost.com");
    when(mockClient.execute(any(HttpUriRequest.class)))
            .thenReturn(mockResponse)
            .thenReturn(mockResponse)
            .thenThrow(new IOException())
            .thenReturn(mockResponse);
    when(mockStatusLine.getStatusCode())
            .thenReturn(HttpConstants.Codes.UNAUTHORIZED)
            .thenReturn(HttpConstants.Codes.SERVICE_UNAVAILABLE)
            .thenReturn(HttpConstants.Codes.SUCCESS);

    // turn off the client when we start processing
    doAnswer(new Answer() {
      @Override
      public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
        latch.countDown();
        return null;
      }
    }).when(mockProcessor).process();

    // for 401 Unauthorized
    when(mockReconnectionManager.shouldReconnectOn400s()).thenReturn(true);

    /** for shutdown **/
    when(mockClient.getConnectionManager())
            .thenReturn(mockConnectionManager);

    assertFalse(clientBase.isDone());
    client.connect();
    latch.await();
    client.stop();
    assertTrue(client.isDone());

    // exponential backoff twice: once for 401 once for 503
    verify(mockReconnectionManager, times(2)).handleExponentialBackoff();
    // for thrown IOException
    verify(mockReconnectionManager).handleLinearBackoff();
    // for successful connection
    verify(mockReconnectionManager).resetCounts();

    // finally start setting up processor/processing for the last attempt that goes through
    verify(mockProcessor, atLeastOnce()).setup(any(InputStream.class));
    verify(mockProcessor, atLeastOnce()).process();

    assertEquals(EventType.STOPPED_BY_USER, clientBase.getExitEvent().getEventType());
    verify(mockConnectionManager, atLeastOnce()).shutdown();
  }
}
TOP

Related Classes of com.twitter.hbc.httpclient.BasicClientTest

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.