org.piwik.sdk.dispatcher.DispatcherTest.java Source code

Java tutorial

Introduction

Here is the source code for org.piwik.sdk.dispatcher.DispatcherTest.java

Source

/*
 * Android SDK for Piwik
 *
 * @link https://github.com/piwik/piwik-android-sdk
 * @license https://github.com/piwik/piwik-sdk-android/blob/master/LICENSE BSD-3 Clause
 */
package org.piwik.sdk.dispatcher;

import android.content.Context;

import org.json.JSONArray;
import org.json.JSONObject;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentMatchers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.piwik.sdk.QueryParams;
import org.piwik.sdk.TrackMe;
import org.piwik.sdk.tools.Connectivity;

import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.UUID;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@SuppressWarnings("ALL")
public class DispatcherTest {

    Dispatcher mDispatcher;
    EventCache mEventCache;
    @Mock
    EventDiskCache mEventDiskCache;
    @Mock
    Connectivity mConnectivity;
    @Mock
    Context mContext;
    URL mApiUrl;

    @Before
    public void setup() throws Exception {
        mApiUrl = new URL("http://example.com");
        MockitoAnnotations.initMocks(this);
        when(mConnectivity.isConnected()).thenReturn(true);
        when(mConnectivity.getType()).thenReturn(Connectivity.Type.MOBILE);

        when(mEventDiskCache.isEmpty()).thenReturn(true);
        mEventCache = spy(new EventCache(mEventDiskCache));
        mDispatcher = new Dispatcher(mEventCache, mConnectivity, new PacketFactory(mApiUrl));
    }

    @Test
    public void testClear() {
        mDispatcher.clear();
        verify(mEventCache).clear();
    }

    @Test
    public void testClear_cleanExit() throws InterruptedException {
        final List<Packet> dryRunData = Collections.synchronizedList(new ArrayList<Packet>());
        mDispatcher.setDryRunTarget(dryRunData);
        mDispatcher.submit(getGuestEvent());
        mDispatcher.forceDispatch();
        Thread.sleep(100);
        assertFalse(dryRunData.isEmpty());
        dryRunData.clear();

        when(mConnectivity.isConnected()).thenReturn(false);
        mDispatcher.submit(getGuestEvent());
        assertFalse(mEventCache.isEmpty());
        mDispatcher.clear();
        when(mConnectivity.isConnected()).thenReturn(true);
        assertTrue(mEventCache.isEmpty());
        verify(mEventCache).clear();
        Thread.sleep(100);
        assertTrue(dryRunData.isEmpty());
    }

    @Test
    public void testGetDispatchMode() {
        assertEquals(DispatchMode.ALWAYS, mDispatcher.getDispatchMode());
        mDispatcher.setDispatchMode(DispatchMode.WIFI_ONLY);
        assertEquals(DispatchMode.WIFI_ONLY, mDispatcher.getDispatchMode());
    }

    @Test
    public void testDispatchMode_wifiOnly() throws Exception {
        when(mEventDiskCache.isEmpty()).thenReturn(false);
        when(mConnectivity.getType()).thenReturn(Connectivity.Type.MOBILE);
        mDispatcher.setDispatchMode(DispatchMode.WIFI_ONLY);
        mDispatcher.submit(getGuestEvent());
        mDispatcher.forceDispatch();
        Thread.sleep(50);
        verify(mEventDiskCache, never()).uncache();
        verify(mEventDiskCache).cache(ArgumentMatchers.<Event>anyList());
        when(mConnectivity.getType()).thenReturn(Connectivity.Type.WIFI);
        mDispatcher.forceDispatch();
        Thread.sleep(50);
        verify(mEventDiskCache).uncache();
    }

    @Test
    public void testConnectivityChange() throws Exception {
        when(mEventDiskCache.isEmpty()).thenReturn(false);
        when(mConnectivity.isConnected()).thenReturn(false);
        mDispatcher.submit(getGuestEvent());
        mDispatcher.forceDispatch();
        Thread.sleep(50);
        verify(mEventDiskCache, never()).uncache();
        verify(mEventDiskCache).cache(ArgumentMatchers.<Event>anyList());
        when(mConnectivity.isConnected()).thenReturn(true);
        mDispatcher.forceDispatch();
        Thread.sleep(50);
        verify(mEventDiskCache).uncache();
    }

    @Test
    public void testGetDispatchGzipped() {
        assertFalse(mDispatcher.getDispatchGzipped());
        mDispatcher.setDispatchGzipped(true);
        assertTrue(mDispatcher.getDispatchGzipped());
    }

    @Test
    public void testDispatch_gzip() throws Exception {
        Packet packet = mock(Packet.class);

        URL url = new URL("http://example.com");
        when(packet.getTargetURL()).thenReturn(url);

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("test", "test");
        when(packet.getPostData()).thenReturn(jsonObject);

        HttpURLConnection urlConnection = mock(HttpURLConnection.class);
        when(packet.openConnection()).thenReturn(urlConnection);
        OutputStream outputStream = mock(OutputStream.class);
        when(urlConnection.getOutputStream()).thenReturn(outputStream);

        mDispatcher.setDispatchGzipped(false);
        mDispatcher.dispatch(packet);
        verify(urlConnection, never()).addRequestProperty("Content-Encoding", "gzip");

        mDispatcher.setDispatchGzipped(true);
        mDispatcher.dispatch(packet);
        verify(urlConnection).addRequestProperty("Content-Encoding", "gzip");
    }

    @Test
    public void testDefaultConnectionTimeout() throws Exception {
        assertEquals(Dispatcher.DEFAULT_CONNECTION_TIMEOUT, mDispatcher.getConnectionTimeOut());
    }

    @Test
    public void testSetConnectionTimeout() throws Exception {
        mDispatcher.setConnectionTimeOut(100);
        assertEquals(100, mDispatcher.getConnectionTimeOut());
    }

    @Test
    public void testDefaultDispatchInterval() throws Exception {
        assertEquals(Dispatcher.DEFAULT_DISPATCH_INTERVAL, mDispatcher.getDispatchInterval());
    }

    @Test
    public void testForceDispatchTwice() throws Exception {
        mDispatcher.setDispatchInterval(-1);
        mDispatcher.setConnectionTimeOut(20);
        mDispatcher.submit(getGuestEvent());

        assertTrue(mDispatcher.forceDispatch());
        assertFalse(mDispatcher.forceDispatch());
    }

    @Test
    public void testMultiThreadDispatch() throws Exception {
        List<Packet> dryRunData = Collections.synchronizedList(new ArrayList<Packet>());
        mDispatcher.setDryRunTarget(dryRunData);
        mDispatcher.setDispatchInterval(20);

        final int threadCount = 20;
        final int queryCount = 100;
        final List<String> createdEvents = Collections.synchronizedList(new ArrayList<String>());
        launchTestThreads(mApiUrl, mDispatcher, threadCount, queryCount, createdEvents);

        checkForMIAs(threadCount * queryCount, createdEvents, dryRunData);
    }

    @Test
    public void testForceDispatch() throws Exception {
        List<Packet> dryRunData = Collections.synchronizedList(new ArrayList<Packet>());
        mDispatcher.setDryRunTarget(dryRunData);
        mDispatcher.setDispatchInterval(-1L);

        final int threadCount = 10;
        final int queryCount = 10;
        final List<String> createdEvents = Collections.synchronizedList(new ArrayList<String>());
        launchTestThreads(mApiUrl, mDispatcher, threadCount, queryCount, createdEvents);
        Thread.sleep(500);
        assertEquals(threadCount * queryCount, createdEvents.size());
        assertEquals(0, dryRunData.size());
        mDispatcher.forceDispatch();

        checkForMIAs(threadCount * queryCount, createdEvents, dryRunData);
    }

    @Test
    public void testBatchDispatch() throws Exception {
        List<Packet> dryRunData = Collections.synchronizedList(new ArrayList<Packet>());
        mDispatcher.setDryRunTarget(dryRunData);
        mDispatcher.setDispatchInterval(1500);

        final int threadCount = 5;
        final int queryCount = 5;
        final List<String> createdEvents = Collections.synchronizedList(new ArrayList<String>());
        launchTestThreads(mApiUrl, mDispatcher, threadCount, queryCount, createdEvents);
        Thread.sleep(1000);
        assertEquals(threadCount * queryCount, createdEvents.size());
        assertEquals(0, dryRunData.size());
        Thread.sleep(1000);

        checkForMIAs(threadCount * queryCount, createdEvents, dryRunData);
    }

    @Test
    public void testRandomDispatchIntervals() throws Exception {
        final List<Packet> dryRunData = Collections.synchronizedList(new ArrayList<Packet>());
        mDispatcher.setDryRunTarget(dryRunData);

        final int threadCount = 10;
        final int queryCount = 100;
        final List<String> createdEvents = Collections.synchronizedList(new ArrayList<String>());

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    while (getFlattenedQueries(new ArrayList<>(dryRunData)).size() != threadCount * queryCount)
                        mDispatcher.setDispatchInterval(new Random().nextInt(20 - -1) + -1);
                } catch (Exception e) {
                    e.printStackTrace();
                }

            }
        }).start();

        launchTestThreads(mApiUrl, mDispatcher, threadCount, queryCount, createdEvents);

        checkForMIAs(threadCount * queryCount, createdEvents, dryRunData);
    }

    public static void checkForMIAs(int expectedEvents, List<String> createdEvents, List<Packet> dryRunOutput)
            throws Exception {
        int previousEventCount = 0;
        int previousFlatQueryCount = 0;
        List<String> flattenedQueries;
        while (true) {
            Thread.sleep(500);
            flattenedQueries = getFlattenedQueries(new ArrayList<>(dryRunOutput));
            if (flattenedQueries.size() == expectedEvents) {
                break;
            } else {
                int currentEventCount = createdEvents.size();
                int currentFlatQueryCount = flattenedQueries.size();
                assertNotEquals(previousEventCount, currentEventCount);
                assertNotEquals(previousFlatQueryCount, currentFlatQueryCount);
                previousEventCount = currentEventCount;
                previousFlatQueryCount = currentFlatQueryCount;
            }
        }

        assertEquals(flattenedQueries.size(), expectedEvents);
        assertEquals(createdEvents.size(), expectedEvents);

        // We are done, lets make sure can find all send queries in our dispatched results
        while (!createdEvents.isEmpty()) {
            String query = createdEvents.remove(0);
            assertTrue(flattenedQueries.remove(query));
        }
        assertTrue(createdEvents.isEmpty());
        assertTrue(flattenedQueries.isEmpty());
    }

    public static void launchTestThreads(final URL apiUrl, final Dispatcher dispatcher, int threadCount,
            final int queryCount, final List<String> createdQueries) {
        for (int i = 0; i < threadCount; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        for (int j = 0; j < queryCount; j++) {
                            Thread.sleep(new Random().nextInt(20 - 0) + 0);
                            TrackMe trackMe = new TrackMe()
                                    .set(QueryParams.EVENT_ACTION, UUID.randomUUID().toString())
                                    .set(QueryParams.EVENT_CATEGORY, UUID.randomUUID().toString())
                                    .set(QueryParams.EVENT_NAME, UUID.randomUUID().toString())
                                    .set(QueryParams.EVENT_VALUE, j);
                            dispatcher.submit(trackMe);
                            createdQueries.add(apiUrl.toString() + new Event(trackMe.toMap()).getEncodedQuery());
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                        assertFalse(true);
                    }
                }
            }).start();
        }
    }

    public static List<String> getFlattenedQueries(List<Packet> packets) throws Exception {
        List<String> flattenedQueries = new ArrayList<>();
        for (Packet request : packets) {
            if (request.getPostData() != null) {
                JSONArray batchedRequests = request.getPostData().getJSONArray("requests");
                for (int json = 0; json < batchedRequests.length(); json++) {
                    String unbatchedRequest = request.getTargetURL().toExternalForm()
                            + batchedRequests.get(json).toString();
                    flattenedQueries.add(unbatchedRequest);
                }
            } else {
                flattenedQueries.add(request.getTargetURL().toExternalForm());
            }
        }
        return flattenedQueries;
    }

    public static TrackMe getGuestEvent() {
        TrackMe trackMe = new TrackMe();
        trackMe.set(QueryParams.SESSION_START, 1);
        return trackMe;
    }
}