com.microsoft.live.unittest.ApiTest.java Source code

Java tutorial

Introduction

Here is the source code for com.microsoft.live.unittest.ApiTest.java

Source

// ------------------------------------------------------------------------------
// Copyright (c) 2014 Microsoft Corporation
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
// ------------------------------------------------------------------------------

package com.microsoft.live.unittest;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.HttpVersion;
import org.apache.http.StatusLine;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.message.BasicStatusLine;
import org.apache.http.protocol.HTTP;
import org.json.JSONException;
import org.json.JSONObject;

import android.os.Build;
import android.test.InstrumentationTestCase;

import com.microsoft.live.LiveConnectClient;
import com.microsoft.live.LiveDownloadOperation;
import com.microsoft.live.LiveOperation;
import com.microsoft.live.LiveOperationException;
import com.microsoft.live.TestUtils;
import com.microsoft.live.constants.ErrorCodes;
import com.microsoft.live.constants.ErrorMessages;
import com.microsoft.live.constants.JsonKeys;
import com.microsoft.live.mock.MockHttpClient;
import com.microsoft.live.mock.MockHttpEntity;
import com.microsoft.live.mock.MockHttpResponse;
import com.microsoft.live.test.util.AssertMessages;
import com.microsoft.live.test.util.AsyncRunnable;

public abstract class ApiTest<OperationType, ListenerType> extends InstrumentationTestCase {

    private static final String INVALID_FORMAT = "!@#098 {} [] This is an invalid formated "
            + "response body asdfkaj{}dfa(*&!@#";

    /**
     * Changes the mockClient to one that checks if the incoming requests contains the
     * Live Library header and then reverts to the original mockClient.
     */
    protected void loadLiveLibraryHeaderChecker() {
        final MockHttpClient currentMockClient = this.mockClient;
        MockHttpClient liveLibraryHeaderCheckerClient = new MockHttpClient() {
            @Override
            public HttpResponse execute(HttpUriRequest request) throws IOException, ClientProtocolException {
                Header header = request.getFirstHeader("X-HTTP-Live-Library");

                assertEquals("android/" + Build.VERSION.RELEASE + "_5.0", header.getValue());

                // load the old mock client back after we check.
                mockClient = currentMockClient;
                return currentMockClient.execute(request);
            }

            @Override
            public HttpResponse getHttpResponse() {
                return currentMockClient.getHttpResponse();
            }

            @Override
            public void setHttpResponse(HttpResponse httpResponse) {
                currentMockClient.setHttpResponse(httpResponse);
            }

            @Override
            public void addHttpResponse(HttpResponse httpResponse) {
                currentMockClient.addHttpResponse(httpResponse);
            }

            @Override
            public void clearHttpResponseQueue() {
                currentMockClient.clearHttpResponseQueue();
            }
        };

        this.mockClient = liveLibraryHeaderCheckerClient;
    }

    /** wait time to retrieve a response from a blocking queue for an async call*/
    protected static int WAIT_TIME_IN_SECS = 10;

    protected static void assertEquals(InputStream is, String response) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        StringBuilder sb = new StringBuilder();

        try {
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } finally {
            // Close silently.
            // Close could throw an exception, and if we don't catch it, it will trump
            // the originally thrown exception.
            // Unfortunately, this means that if there was a problem only with close the reader,
            // it will be ignored. I assume we can safely ignore this case, because if there is
            // a problem closing a stream, there is little we can do to correc this.
            try {
                reader.close();
            } catch (Exception e) {
            }
        }

        assertEquals(response, sb.toString());
    }

    protected BlockingQueue<LiveOperationException> exceptionQueue;
    protected LiveConnectClient liveConnectClient;
    protected MockHttpClient mockClient;
    protected MockHttpEntity mockEntity;
    protected MockHttpResponse mockResponse;
    protected ListenerType queueingListener;
    protected BlockingQueue<OperationType> responseQueue;

    public abstract void testAsyncResponseBodyInvalid() throws Throwable;

    public abstract void testAsyncResponseBodyValid() throws Throwable;

    public abstract void testAsyncResponseBodyValidWithUserState() throws Throwable;

    public abstract void testSyncResponseBodyInvalid() throws Exception;

    public abstract void testSyncResponseBodyValid() throws Exception;

    protected abstract void checkValidResponseBody(OperationType operation) throws Exception;

    protected abstract AsyncRunnable<OperationType, ListenerType> createAsyncRunnable(String requestPath);

    protected abstract AsyncRunnable<OperationType, ListenerType> createAsyncRunnable(String requestPath,
            Object userState);

    protected abstract void loadValidResponseBody() throws Exception;

    protected abstract String getMethod();

    protected void checkOperationMembers(LiveDownloadOperation operation, String method, String path) {
        this.checkOperationMembers(operation, method, path, null);
    }

    protected void checkOperationMembers(LiveDownloadOperation operation, String method, String path,
            Object userState) {
        assertEquals(method, operation.getMethod());
        assertEquals(path, operation.getPath());
        assertEquals(userState, operation.getUserState());
    }

    /**
    * Asserts that the given LiveOperation has the correct method, path, and userState
    *
    * @param operation
    * @param method
    * @param path
    */
    protected void checkOperationMembers(LiveOperation operation, String method, String path) {
        this.checkOperationMembers(operation, method, path, null);
    }

    /**
     * Asserts that the given LiveOperation has the correct method, path, and userState.
     *
     * @param operation
     * @param method
     * @param path
     * @param userState
     */
    protected void checkOperationMembers(LiveOperation operation, String method, String path, Object userState) {
        assertEquals(method, operation.getMethod());
        assertEquals(path, operation.getPath());
        assertEquals(userState, operation.getUserState());
    }

    /**
     * Asserts the response contains the error for an Invalid Path.
     *
     * @param operation
     * @param requestPath
     * @throws JSONException
     */
    protected void checkPathInvalid(LiveOperation operation, String requestPath) throws JSONException {
        JSONObject result = operation.getResult();
        JSONObject error = result.getJSONObject(JsonKeys.ERROR);
        String message = error.getString(JsonKeys.MESSAGE);
        String code = error.getString(JsonKeys.CODE);

        assertEquals(String.format(ErrorMessages.URL_NOT_VALID, requestPath.toLowerCase()), message);
        assertEquals(ErrorCodes.REQUEST_URL_INVALID, code);

        String rawResult = operation.getRawResult();
        assertEquals(result.toString(), rawResult);
    }

    protected void checkResponseBodyInvalid(LiveDownloadOperation operation) throws IOException {
        InputStream is = operation.getStream();
        ApiTest.assertEquals(is, INVALID_FORMAT);
    }

    /**
     * Asserts the LiveOperation's result and raw result members are null.
     *
     * @param operation
     */
    protected void checkResponseBodyInvalid(LiveOperation operation) {
        JSONObject result = operation.getResult();
        assertNull(result);

        String rawResult = operation.getRawResult();
        assertNull(rawResult);
    }

    protected void checkReturnedException(LiveDownloadOperation fromMethod, LiveDownloadOperation fromCallback,
            LiveOperationException exception) throws IOException {
        assertNotNull(exception.getMessage());

        this.checkReturnedOperations(fromMethod, fromCallback);
    }

    /**
      * Asserts the returned exception is not null and the LiveOperations from the method,
      * and the callback listener are the same object. Also, asserts the responseQueue,
      * and exceptionQueue are empty.
      *
      * @param fromMethod
      * @param fromCallback
      * @param exception
      */
    protected void checkReturnedException(LiveOperation fromMethod, LiveOperation fromCallback,
            LiveOperationException exception) {
        assertNotNull(exception.getMessage());

        this.checkReturnedOperations(fromMethod, fromCallback);

        this.checkResponseBodyInvalid(fromMethod);
    }

    /**
      * Asserts the returned LiveOperations from the method, and from the callback listener
      * are the same object. Also, asserts the responseQueue and exceptionQueue are empty.
      *
      * @param fromMethod
      * @param fromCallback
      */
    protected <T> void checkReturnedOperations(T fromMethod, T fromCallback) {
        assertTrue(fromMethod == fromCallback);
        assertTrue(this.responseQueue.isEmpty());
        assertTrue(this.exceptionQueue.isEmpty());
    }

    protected void failNoIllegalArgumentExceptionThrown() {
        this.failNoExceptionThrown(IllegalArgumentException.class);
    }

    protected void failNoLiveOperationExceptionThrown() {
        this.failNoExceptionThrown(LiveOperationException.class);
    }

    protected void failNoNullPointerExceptionThrown() {
        this.failNoExceptionThrown(NullPointerException.class);
    }

    /** Loads an invalid formated body into the HttpClient
     * @throws Exception */
    protected void loadInvalidResponseBody() throws Exception {
        byte[] bytes = INVALID_FORMAT.getBytes();
        this.mockResponse.addHeader(HTTP.CONTENT_LEN, Long.toString(bytes.length));
        this.mockEntity.setInputStream(new ByteArrayInputStream(bytes));
        this.mockClient.setHttpResponse(mockResponse);
    }

    /**
     * Loads an invalid path response into the HttpClient.
     *
     * @param requestPath
     * @throws Exception
     */
    protected void loadPathInvalidResponse(String requestPath) throws Exception {
        JSONObject error = new JSONObject();
        error.put(JsonKeys.CODE, ErrorCodes.REQUEST_URL_INVALID);

        String message = String.format(ErrorMessages.URL_NOT_VALID, requestPath.toLowerCase());
        error.put(JsonKeys.MESSAGE, message);

        JSONObject response = new JSONObject();
        response.put(JsonKeys.ERROR, error);

        byte[] bytes = response.toString().getBytes();
        this.mockResponse.addHeader(HTTP.CONTENT_LEN, Long.toString(bytes.length));
        this.mockEntity.setInputStream(new ByteArrayInputStream(bytes));
        StatusLine status = new BasicStatusLine(HttpVersion.HTTP_1_1, HttpStatus.SC_BAD_REQUEST, "Bad Request");
        this.mockResponse.setStatusLine(status);
        this.mockClient.setHttpResponse(this.mockResponse);
    }

    protected LiveOperationException pollExceptionQueue() throws InterruptedException {
        return this.exceptionQueue.poll(WAIT_TIME_IN_SECS, TimeUnit.SECONDS);
    }

    protected OperationType pollResponseQueue() throws InterruptedException {
        return this.responseQueue.poll(WAIT_TIME_IN_SECS, TimeUnit.SECONDS);
    }

    @Override
    protected void setUp() throws Exception {
        super.setUp();

        // Set up the MockClient
        this.mockEntity = new MockHttpEntity();

        StatusLine statusLine = new BasicStatusLine(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
        this.mockResponse = new MockHttpResponse(this.mockEntity, statusLine);
        this.mockClient = new MockHttpClient(this.mockResponse);
        this.loadLiveLibraryHeaderChecker();
        this.exceptionQueue = new LinkedBlockingQueue<LiveOperationException>();
        this.liveConnectClient = TestUtils.newLiveConnectClient(this.mockClient);
    }

    @Override
    protected void tearDown() throws Exception {
        super.tearDown();

        this.mockEntity = null;
        this.mockClient = null;
        this.responseQueue = null;
        this.exceptionQueue = null;
        this.queueingListener = null;
        this.liveConnectClient = null;
    }

    private void failNoExceptionThrown(Class<?> cl) {
        fail(String.format(AssertMessages.NO_EXCEPTION_THROWN, cl));
    }
}