Skip to content

Commit

Permalink
Merge pull request #366 from Countly/countly_timer
Browse files Browse the repository at this point in the history
Countly timer
  • Loading branch information
turtledreams authored Aug 14, 2024
2 parents f4bbe2b + d6db35a commit 4955c24
Show file tree
Hide file tree
Showing 3 changed files with 159 additions and 0 deletions.
104 changes: 104 additions & 0 deletions sdk/src/androidTest/java/ly/count/android/sdk/CountlyTimerTests.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package ly.count.android.sdk;

import androidx.test.ext.junit.runners.AndroidJUnit4;
import java.util.concurrent.ExecutorService;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;

@RunWith(AndroidJUnit4.class)
public class CountlyTimerTests {

private CountlyTimer countlyTimer;
private ModuleLog mockLog;

@Before
public void setUp() {
countlyTimer = new CountlyTimer();
mockLog = Mockito.mock(ModuleLog.class);
CountlyTimer.TIMER_DELAY_MS = 0;
}

@After
public void tearDown() {
countlyTimer.stopTimer(mockLog);
Assert.assertNull(countlyTimer.timerService);
}

@Test
public void validateInitialValues() {
Assert.assertNull(countlyTimer.timerService);
Assert.assertEquals(0, CountlyTimer.TIMER_DELAY_MS);
}

@Test
public void startTimer_validDelay() {
Runnable mockRunnable = Mockito.mock(Runnable.class);

countlyTimer.startTimer(1, mockRunnable, mockLog);
Mockito.verify(mockLog).i("[CountlyTimer] startTimer, Starting timer timerDelay: [1000 ms]");
}

@Test
public void startTimer_invalidDelay() {
Runnable mockRunnable = Mockito.mock(Runnable.class);

countlyTimer.startTimer(-1, mockRunnable, mockLog);
Mockito.verify(mockLog).i("[CountlyTimer] startTimer, Starting timer timerDelay: [1000 ms]");
}

@Test
public void startTimer() {
Runnable mockRunnable = Mockito.mock(Runnable.class);

countlyTimer.startTimer(99, mockRunnable, mockLog);
Mockito.verify(mockLog).i("[CountlyTimer] startTimer, Starting timer timerDelay: [99000 ms]");
}

@Test
public void startTimer_withTimerDelayMS() {
CountlyTimer.TIMER_DELAY_MS = 500;
Runnable mockRunnable = Mockito.mock(Runnable.class);

countlyTimer.startTimer(1, mockRunnable, mockLog);
Mockito.verify(mockLog).i("[CountlyTimer] startTimer, Starting timer timerDelay: [500 ms]");
}

/**
* Test that the timer is stopped when a new timer is started
* This is to prevent multiple timers from running at the same time
* And it is not reusing the previous timer
*/
@Test
public void startTimer_reuseTimer() {
countlyTimer.stopTimer(mockLog);

Assert.assertNull(countlyTimer.timerService);

Runnable mockRunnable = Mockito.mock(Runnable.class);
countlyTimer.startTimer(1, mockRunnable, mockLog);

Assert.assertNotNull(countlyTimer.timerService);
ExecutorService timerService = countlyTimer.timerService;

countlyTimer.startTimer(2, mockRunnable, mockLog);
Assert.assertNotEquals(timerService, countlyTimer.timerService);
}

@Test
public void stopTimer() {
countlyTimer.startTimer(1, Mockito.mock(Runnable.class), mockLog);
countlyTimer.stopTimer(mockLog);
Mockito.verify(mockLog).i("[CountlyTimer] stopTimer, Stopping timer");
}

@Test
public void stopTimer_nullTimer() {
countlyTimer.stopTimer(mockLog);
Mockito.verify(mockLog, Mockito.never()).i("[CountlyTimer] stopTimer, Stopping timer");
Mockito.verify(mockLog).d("[CountlyTimer] stopTimer, Timer already stopped");
}
}
54 changes: 54 additions & 0 deletions sdk/src/main/java/ly/count/android/sdk/CountlyTimer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package ly.count.android.sdk;

import androidx.annotation.NonNull;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

class CountlyTimer {

ScheduledExecutorService timerService;
protected static int TIMER_DELAY_MS = 0; // for testing purposes

protected void stopTimer(@NonNull ModuleLog L) {
if (timerService != null) {
L.i("[CountlyTimer] stopTimer, Stopping timer");
try {
timerService.shutdown();
if (!timerService.awaitTermination(1, TimeUnit.SECONDS)) {
timerService.shutdownNow();
if (!timerService.awaitTermination(1, TimeUnit.SECONDS)) {
L.e("[CountlyTimer] stopTimer, Global timer must be locked");
}
}
} catch (Exception e) {
L.e("[CountlyTimer] stopTimer, Error while stopping global timer " + e);
}
timerService = null;
} else {
L.d("[CountlyTimer] stopTimer, Timer already stopped");
}
}

protected void startTimer(long timerDelay, @NonNull Runnable runnable, @NonNull ModuleLog L) {
long timerDelayInternal = timerDelay * 1000;

if (timerDelayInternal < UtilsTime.ONE_SECOND_IN_MS) {
timerDelayInternal = UtilsTime.ONE_SECOND_IN_MS;
}

if (TIMER_DELAY_MS > 0) {
timerDelayInternal = TIMER_DELAY_MS;
}

L.i("[CountlyTimer] startTimer, Starting timer timerDelay: [" + timerDelayInternal + " ms]");

if (timerService != null) {
L.d("[CountlyTimer] startTimer, timer was running, stopping it");
stopTimer(L);
}

timerService = Executors.newSingleThreadScheduledExecutor();
timerService.scheduleWithFixedDelay(runnable, 0, timerDelayInternal, TimeUnit.MILLISECONDS);
}
}
1 change: 1 addition & 0 deletions sdk/src/main/java/ly/count/android/sdk/UtilsTime.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.util.List;

public class UtilsTime {
protected static int ONE_SECOND_IN_MS = 1000;

public static class Instant {
public final long timestampMs;
Expand Down

0 comments on commit 4955c24

Please sign in to comment.