/*
* Copyright (C) 2014 SCVNGR, Inc. d/b/a LevelUp
*
* 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.scvngr.levelup.core.net;
import android.content.Context;
import android.support.annotation.NonNull;
import android.test.suitebuilder.annotation.SmallTest;
import com.scvngr.levelup.core.net.AbstractRequest.BadRequestException;
import com.scvngr.levelup.core.test.SupportAndroidTestCase;
import com.scvngr.levelup.core.util.LogManager;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* Tests {@link com.scvngr.levelup.core.net.LevelUpConnection}.
*/
public final class LevelUpConnectionTest extends SupportAndroidTestCase {
@Override
protected void setUp() throws Exception {
super.setUp();
LevelUpConnection.setNextInstance(null);
}
/**
* Tests {@link com.scvngr.levelup.core.net.LevelUpConnection#newInstance(android.content.Context)}.
*/
@SmallTest
public void testNewInstance() {
// Make sure it returns a new object every time.
assertNotSame(LevelUpConnection.newInstance(getContext()),
LevelUpConnection.newInstance(getContext()));
final LevelUpConnection connection = new LevelUpConnection(getContext());
assertNotSame(connection, LevelUpConnection.newInstance(getContext()));
// Make sure we return the object we set before calling new instance().
LevelUpConnection.setNextInstance(connection);
assertSame(connection, LevelUpConnection.newInstance(getContext()));
// Subsequent calls should still return the same instance.
assertSame(connection, LevelUpConnection.newInstance(getContext()));
LevelUpConnection.setNextInstance(null);
assertNotSame(connection, LevelUpConnection.newInstance(getContext()));
}
@SmallTest
public void testGetLastRequest() {
final LevelUpConnection connection = new LevelUpConnection(getContext());
LevelUpConnection.setNextInstance(connection);
final LevelUpRequest request =
new LevelUpRequest(getContext(), HttpMethod.GET,
LevelUpRequest.API_VERSION_CODE_V14, getName(), null, null);
connection.setLastRequest(getName(), request);
assertEquals(request, connection.getLastRequest(getName()));
assertEquals(request, connection.getLastRequest(null));
}
/**
* Tests {@link com.scvngr.levelup.core.net.LevelUpConnection#setNextResponse}.
*
* @throws com.scvngr.levelup.core.net.AbstractRequest.BadRequestException
*/
@SmallTest
public void testSetNextResponse_withSpecificUrl() throws BadRequestException {
final LevelUpConnection connection = new LevelUpConnection(getContext());
final LevelUpRequest request =
new LevelUpRequest(getContext(), HttpMethod.GET,
LevelUpRequest.API_VERSION_CODE_V14, "/", null, null);
final LevelUpResponse response = new LevelUpResponse("test", LevelUpStatus.ERROR_SERVER);
LevelUpConnection.setNextInstance(connection);
connection.setNextResponse(request.getUrl(getContext()).toString(), response);
assertSame(connection, LevelUpConnection.newInstance(getContext()));
assertSame(response, connection.send(request));
}
/**
* Tests {@link com.scvngr.levelup.core.net.LevelUpConnection#setNextResponse}.
*
* @throws com.scvngr.levelup.core.net.AbstractRequest.BadRequestException
*/
@SmallTest
public void testSetNextResponse_withNullUrl() throws BadRequestException {
final LevelUpConnection connection = new LevelUpConnection(getContext());
final LevelUpRequest request =
new LevelUpRequest(getContext(), HttpMethod.GET,
LevelUpRequest.API_VERSION_CODE_V14, "/", null, null);
final LevelUpResponse response = new LevelUpResponse("test", LevelUpStatus.ERROR_SERVER);
LevelUpConnection.setNextInstance(connection);
connection.setNextResponse(null, response);
assertSame(connection, LevelUpConnection.newInstance(getContext()));
assertSame(response, connection.send(request));
}
/**
* Tests {@link com.scvngr.levelup.core.net.LevelUpConnection#setNextResponse}.
*
* @throws com.scvngr.levelup.core.net.AbstractRequest.BadRequestException
*/
@SmallTest
public void testSetNextResponse_withUnRecognizedUrl() throws BadRequestException {
final LevelUpConnection connection = new LevelUpConnection(getContext());
final LevelUpRequest request =
new LevelUpRequest(getContext(), HttpMethod.GET,
LevelUpRequest.API_VERSION_CODE_V14, "/", null, null);
final LevelUpResponse response = new LevelUpResponse("test", LevelUpStatus.ERROR_SERVER);
LevelUpConnection.setNextInstance(connection);
connection.setNextResponse("www.example.com", response);
assertSame(connection, LevelUpConnection.newInstance(getContext()));
try {
// Since no URL match was found, we should throw an exception.
connection.send(request);
fail("should throw RuntimeException");
} catch (final RuntimeException e) {
// Expected exception.
}
}
/**
* Tests {@link com.scvngr.levelup.core.net.LevelUpConnection#setNextResponse}.
*
* @throws com.scvngr.levelup.core.net.AbstractRequest.BadRequestException
* @throws InterruptedException
*/
@SmallTest
public void testSetNextResponse_threading() throws BadRequestException, InterruptedException {
final LevelUpConnection connection = new LevelUpConnection(getContext());
final LevelUpRequest request1 =
new LevelUpRequest(getContext(), HttpMethod.GET,
LevelUpRequest.API_VERSION_CODE_V14, "1", null, null);
final LevelUpResponse response1 = new LevelUpResponse("test1", LevelUpStatus.ERROR_SERVER);
final String requestUrl1 = request1.getUrl(getContext()).toString();
// Add the first response to the list.
connection.setNextResponse(requestUrl1, response1);
final LevelUpRequest request2 =
new LevelUpRequest(getContext(), HttpMethod.GET,
LevelUpRequest.API_VERSION_CODE_V14, "2", null, null);
final LevelUpResponse response2 = new LevelUpResponse("test2", LevelUpStatus.ERROR_SERVER);
final String requestUrl2 = request2.getUrl(getContext()).toString();
// Add the second response to the list.
connection.setNextResponse(requestUrl2, response2);
// Create one more request and don't set it right now.
final LevelUpRequest request3 =
new LevelUpRequest(getContext(), HttpMethod.GET,
LevelUpRequest.API_VERSION_CODE_V14, "3", null, null);
final String requestUrl3 = request3.getUrl(getContext()).toString();
LevelUpConnection.setNextInstance(connection);
final CountDownLatch latch1 = new CountDownLatch(1);
final Runnable runnable1 = new Runnable() {
@Override
public void run() {
assertSame(connection, LevelUpConnection.newInstance(getContext()));
assertSame(response1, connection.send(request1));
assertNull(connection.getNextResponse(requestUrl1));
assertNotNull(connection.getNextResponse(requestUrl2));
latch1.countDown();
}
};
new Thread(runnable1).start();
assertTrue(latch1.await(2, TimeUnit.SECONDS));
assertNull(connection.getNextResponse(requestUrl1));
assertNotNull(connection.getNextResponse(requestUrl2));
final CountDownLatch latch2 = new CountDownLatch(1);
final Runnable runnable2 = new Runnable() {
@Override
public void run() {
// Add a new request in a different thread.
final LevelUpResponse response3 =
new LevelUpResponse("test3", LevelUpStatus.ERROR_SERVER);
connection.setNextResponse(requestUrl3, response3);
assertSame(response3, connection.send(request3));
assertNull(connection.getNextResponse(requestUrl1));
assertNotNull(connection.getNextResponse(requestUrl2));
assertNull(connection.getNextResponse(requestUrl3));
latch2.countDown();
}
};
new Thread(runnable2).start();
assertTrue(latch2.await(2, TimeUnit.SECONDS));
assertNull(connection.getNextResponse(requestUrl1));
assertNotNull(connection.getNextResponse(requestUrl2));
assertNull(connection.getNextResponse(requestUrl3));
// Request the last remaining stored request.
assertSame(response2, connection.send(request2));
assertNull(connection.getNextResponse(requestUrl1));
assertNull(connection.getNextResponse(requestUrl2));
assertNull(connection.getNextResponse(requestUrl3));
// Make sure there are no more cached responses.
assertNull(connection.getNextResponse(null));
}
/**
* Tests {@link com.scvngr.levelup.core.net.LevelUpConnection#send}.
*/
@SmallTest
public void testSend_basic() {
final LevelUpConnection connection = LevelUpConnection.newInstance(getContext());
final LevelUpRequest request =
new LevelUpRequest(getContext(), HttpMethod.GET,
LevelUpRequest.API_VERSION_CODE_V14, "user", null, null);
NetworkConnection.setNextResponse(new StreamingResponse());
final LevelUpResponse response = connection.send(request);
assertNotNull(response);
}
/**
* Tests {@link com.scvngr.levelup.core.net.LevelUpConnection#send}.
*
* @throws Exception if {@link com.scvngr.levelup.core.net.LevelUpConnectionTest.MockHttpUrlConnection} throws
*/
@SmallTest
public void testSend_withResponse() throws Exception {
final LevelUpConnection connection = LevelUpConnection.newInstance(getContext());
final LevelUpRequest request =
new LevelUpRequest(getContext(), HttpMethod.GET,
LevelUpRequest.API_VERSION_CODE_V14, "user", null, null);
NetworkConnection.setNextResponse(new StreamingResponse(new MockHttpUrlConnection()));
final LevelUpResponse response = connection.send(request);
assertEquals(LevelUpStatus.OK, response.getStatus());
assertEquals(MockHttpUrlConnection.INPUT_STREAM_STRING, response.getData());
// Make sure that the last request is set.
final AbstractRequest sentRequest =
connection.getLastRequest(request.getUrl(getContext()).toString());
assertNotNull(sentRequest);
assertEquals(request, sentRequest);
}
/**
* Tests {@link com.scvngr.levelup.core.net.LevelUpConnection#setNetworkEnabled(boolean)}.
*/
@SmallTest
public void testSetNetworkEnabled() {
{
// Default should not throw a runtime exception.
NetworkConnection.setNextResponse(new StreamingResponse());
final LevelUpConnection connection = LevelUpConnection.newInstance(getContext());
connection.send(new LevelUpRequest(getContext(), HttpMethod.GET,
LevelUpRequest.API_VERSION_CODE_V14, "user", null, null));
}
{
final LevelUpConnection connection = LevelUpConnection.newInstance(getContext());
LevelUpConnection.setNetworkEnabled(false);
try {
connection
.send(new LevelUpRequest(getContext(), HttpMethod.GET,
LevelUpRequest.API_VERSION_CODE_V14,
"user", null, null));
fail("network connection should throw exception");
} catch (final RuntimeException e) {
// Expected exception
}
}
}
/**
* Tests that requests that throw exceptions are caught properly.
*/
@SmallTest
public void testSendWithRequestThatThrowsException() {
final LevelUpConnection connection = LevelUpConnection.newInstance(getContext());
final TestRequest request = new TestRequest(getContext());
final LevelUpResponse response = connection.send(request);
assertEquals(LevelUpStatus.ERROR_BAD_REQUEST, response.getStatus());
}
/**
* Test impl of {@link com.scvngr.levelup.core.net.LevelUpRequest} that throws an exception in
* {@link com.scvngr.levelup.core.net.LevelUpRequest#getUrlString(android.content.Context)}.
*/
private static final class TestRequest extends LevelUpRequest {
public TestRequest(@NonNull final Context context) {
super(context, HttpMethod.GET, LevelUpRequest.API_VERSION_CODE_V14, "test", null, null);
}
@Override
@NonNull
public String getUrlString(@NonNull final Context context) throws BadRequestException {
throw new BadRequestException("test");
}
}
/**
* Wrapped {@link java.net.HttpURLConnection} to return {@link java.net.HttpURLConnection#HTTP_OK} for
* {@link java.net.HttpURLConnection#getResponseCode()}.
*/
private static class MockHttpUrlConnection extends HttpURLConnection {
public static final String INPUT_STREAM_STRING = "Mock response!";
private final InputStream mStream;
/**
* Constructor.
*
* @throws java.net.MalformedURLException if the URL is malformed
*/
public MockHttpUrlConnection() throws MalformedURLException {
super(new URL("http://www.example.com"));
mStream = new ByteArrayInputStream(INPUT_STREAM_STRING.getBytes());
}
@Override
public int getResponseCode() throws IOException {
return HttpURLConnection.HTTP_OK;
}
@Override
public InputStream getInputStream() throws IOException {
return mStream;
}
@Override
public void disconnect() {
try {
mStream.close();
} catch (final IOException e) {
LogManager.e("error disconnecting", e);
}
}
@Override
public boolean usingProxy() {
return false;
}
@Override
public void connect() throws IOException {
// do nothing
}
}
}