Skip to content

Commit

Permalink
tests: add unit test coverage for QueueManager
Browse files Browse the repository at this point in the history
  • Loading branch information
wzieba committed Nov 3, 2023
1 parent 3156144 commit 5fb5f35
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import java.io.FileNotFoundException
import java.io.ObjectInputStream
import java.io.ObjectOutputStream

internal class LocalStorageRepository(private val context: Context) {
internal open class LocalStorageRepository(private val context: Context) {
/**
* Persist an object to storage.
*
Expand All @@ -33,7 +33,7 @@ internal class LocalStorageRepository(private val context: Context) {
persistObject(ArrayList<Map<String, Any>>())
}

val storedQueue: ArrayList<Map<String, Any?>?>
open val storedQueue: ArrayList<Map<String, Any?>?>
/**
* Get the stored event queue from persistent storage.
*
Expand Down Expand Up @@ -65,7 +65,7 @@ internal class LocalStorageRepository(private val context: Context) {
/**
* Delete an event from the stored queue.
*/
fun expelStoredEvent() {
open fun expelStoredEvent() {
val storedQueue = storedQueue
storedQueue.removeAt(0)
}
Expand All @@ -74,7 +74,7 @@ internal class LocalStorageRepository(private val context: Context) {
* Save the event queue to persistent storage.
*/
@Synchronized
fun persistQueue(inMemoryQueue: List<Map<String, Any?>?>) {
open fun persistQueue(inMemoryQueue: List<Map<String, Any?>?>) {
ParselyTracker.PLog("Persisting event queue")
val storedQueue = storedQueue
val hs = HashSet<Map<String, Any?>?>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import java.util.Formatter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.UUID;
Expand All @@ -52,7 +53,7 @@ public class ParselyTracker {
@SuppressWarnings("StringOperationCanBeSimplified")
// private static final String ROOT_URL = "http://10.0.2.2:5001/".intern(); // emulator localhost
private static final String ROOT_URL = "https://p1.parsely.com/".intern();
protected ArrayList<Map<String, Object>> eventQueue;
private final ArrayList<Map<String, Object>> eventQueue;
private boolean isDebug;
private final Context context;
private final Timer timer;
Expand Down Expand Up @@ -97,6 +98,10 @@ protected ParselyTracker(String siteId, int flushInterval, Context c) {
);
}

List<Map<String, Object>> getInMemoryQueue() {
return eventQueue;
}

/**
* Singleton instance accessor. Note: This must be called after {@link #sharedInstance(String, Context)}
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
import androidx.annotation.NonNull;

class QueueManager extends AsyncTask<Void, Void, Void> {
private static final int QUEUE_SIZE_LIMIT = 50;
private static final int STORAGE_SIZE_LIMIT = 100;
static final int QUEUE_SIZE_LIMIT = 50;
static final int STORAGE_SIZE_LIMIT = 100;

@NonNull
private final ParselyTracker parselyTracker;
Expand All @@ -24,10 +24,10 @@ public QueueManager(
@Override
protected Void doInBackground(Void... params) {
// if event queue is too big, push to persisted storage
if (parselyTracker.eventQueue.size() > QUEUE_SIZE_LIMIT) {
if (parselyTracker.getInMemoryQueue().size() > QUEUE_SIZE_LIMIT) {
ParselyTracker.PLog("Queue size exceeded, expelling oldest event to persistent memory");
localStorageRepository.persistQueue(parselyTracker.eventQueue);
parselyTracker.eventQueue.remove(0);
localStorageRepository.persistQueue(parselyTracker.getInMemoryQueue());
parselyTracker.getInMemoryQueue().remove(0);
// if persisted storage is too big, expel one
if (parselyTracker.storedEventsCount() > STORAGE_SIZE_LIMIT) {
localStorageRepository.expelStoredEvent();
Expand Down
109 changes: 109 additions & 0 deletions parsely/src/test/java/com/parsely/parselyandroid/QueueManagerTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package com.parsely.parselyandroid

import androidx.test.core.app.ApplicationProvider
import com.parsely.parselyandroid.QueueManager.QUEUE_SIZE_LIMIT
import com.parsely.parselyandroid.QueueManager.STORAGE_SIZE_LIMIT
import org.assertj.core.api.Assertions.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.LooperMode
import org.robolectric.shadows.ShadowLooper.shadowMainLooper

@RunWith(RobolectricTestRunner::class)
@LooperMode(LooperMode.Mode.PAUSED)
internal class QueueManagerTest {

private lateinit var sut: QueueManager

private val tracker = FakeTracker()
private val repository = FakeLocalRepository()

@Before
fun setUp() {
sut = QueueManager(tracker, repository)
}

@Test
fun `given the queue is smaller than any threshold, when querying flush manager, do nothing`() {
// given
val initialInMemoryQueue = listOf(mapOf("test" to "test"))
tracker.applyFakeQueue(initialInMemoryQueue)

// when
sut.execute().get()
shadowMainLooper().idle();

// then
assertThat(tracker.inMemoryQueue).isEqualTo(initialInMemoryQueue)
assertThat(repository.storedQueue).isEmpty()
}

@Test
fun `given the in-memory queue is above the in-memory limit, when querying flush manager, then save queue to local storage and remove first event`() {
// given
val initialInMemoryQueue = (1..QUEUE_SIZE_LIMIT + 1).map { mapOf("test" to it) }
tracker.applyFakeQueue(initialInMemoryQueue)

// when
sut.execute().get()
shadowMainLooper().idle();

// then
assertThat(repository.storedQueue).isEqualTo(initialInMemoryQueue)
assertThat(tracker.inMemoryQueue).hasSize(QUEUE_SIZE_LIMIT)
}

@Test
fun `given the in-memory queue is above the in-memory limit and stored events queue is above stored-queue limit, when querying flush manager, then expel the last event from local storage`() {
// given
val initialInMemoryQueue = (1..QUEUE_SIZE_LIMIT + 1).map { mapOf("in memory" to it) }
tracker.applyFakeQueue(initialInMemoryQueue)
val initialStoredQueue = (1..STORAGE_SIZE_LIMIT + 1).map { mapOf("storage" to it) }
repository.persistQueue(initialStoredQueue)

// when
sut.execute().get()
shadowMainLooper().idle();

// then
assertThat(repository.wasEventExpelled).isTrue
}

inner class FakeTracker : ParselyTracker(
"siteId", 10, ApplicationProvider.getApplicationContext()
) {

private var fakeQueue: List<Map<String, Any>> = emptyList()

internal override fun getInMemoryQueue(): List<Map<String, Any>> = fakeQueue

fun applyFakeQueue(fakeQueue: List<Map<String, Any>>) {
this.fakeQueue = fakeQueue.toList()
}

override fun storedEventsCount(): Int {
return repository.storedQueue.size
}
}

class FakeLocalRepository :
LocalStorageRepository(ApplicationProvider.getApplicationContext()) {

private var localFileQueue = emptyList<Map<String, Any?>?>()
var wasEventExpelled = false

override fun persistQueue(inMemoryQueue: List<Map<String, Any?>?>) {
this.localFileQueue += inMemoryQueue
}

override val storedQueue: ArrayList<Map<String, Any?>?>
get() = ArrayList(localFileQueue)


override fun expelStoredEvent() {
wasEventExpelled = true
}
}
}

0 comments on commit 5fb5f35

Please sign in to comment.