Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package com.d4rk.androidtutorials.java.notifications.managers;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;

import com.d4rk.androidtutorials.java.notifications.receivers.AppUsageNotificationReceiver;

import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.MockedStatic;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

public class AppUsageNotificationsManagerTest {

@Test
public void scheduleAppUsageCheck_setsRepeatingAlarmForReceiver() {
Context context = mock(Context.class);
AlarmManager alarmManager = mock(AlarmManager.class);
when(context.getSystemService(Context.ALARM_SERVICE)).thenReturn(alarmManager);

PendingIntent pendingIntent = mock(PendingIntent.class);
AtomicReference<Intent> capturedIntent = new AtomicReference<>();

try (MockedStatic<PendingIntent> mockedPendingIntent = mockStatic(PendingIntent.class)) {
mockedPendingIntent.when(() -> PendingIntent.getBroadcast(
eq(context),
eq(0),
any(Intent.class),
eq(PendingIntent.FLAG_IMMUTABLE)
)).thenAnswer(invocation -> {
Intent intent = invocation.getArgument(2);
capturedIntent.set(intent);
return pendingIntent;
});

long beforeCall = System.currentTimeMillis();
AppUsageNotificationsManager manager = new AppUsageNotificationsManager(context);
manager.scheduleAppUsageCheck();
long afterCall = System.currentTimeMillis();

ArgumentCaptor<Long> triggerCaptor = ArgumentCaptor.forClass(Long.class);
verify(alarmManager).setRepeating(
eq(AlarmManager.RTC_WAKEUP),
triggerCaptor.capture(),
eq(TimeUnit.DAYS.toMillis(3)),
eq(pendingIntent)
);

long interval = TimeUnit.DAYS.toMillis(3);
long triggerTime = triggerCaptor.getValue();
long scheduledOrigin = triggerTime - interval;
assertTrue(scheduledOrigin >= beforeCall);
assertTrue(scheduledOrigin <= afterCall);

Intent intent = capturedIntent.get();
assertNotNull("PendingIntent should target the usage receiver", intent);
assertNotNull("PendingIntent should include a component", intent.getComponent());
assertEquals(AppUsageNotificationReceiver.class.getName(), intent.getComponent().getClassName());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.d4rk.androidtutorials.java.notifications.receivers;

import android.content.Context;
import android.content.Intent;

import androidx.work.OneTimeWorkRequest;
import androidx.work.WorkManager;

import com.d4rk.androidtutorials.java.notifications.workers.AppUsageNotificationWorker;

import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.MockedStatic;

import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.verify;

public class AppUsageNotificationReceiverTest {

@Test
public void onReceive_enqueuesAppUsageWorker() {
Context context = mock(Context.class);
WorkManager workManager = mock(WorkManager.class);
try (MockedStatic<WorkManager> mockedWorkManager = mockStatic(WorkManager.class)) {
mockedWorkManager.when(() -> WorkManager.getInstance(context)).thenReturn(workManager);

AppUsageNotificationReceiver receiver = new AppUsageNotificationReceiver();
receiver.onReceive(context, new Intent());

ArgumentCaptor<OneTimeWorkRequest> requestCaptor = ArgumentCaptor.forClass(OneTimeWorkRequest.class);
verify(workManager).enqueue(requestCaptor.capture());

OneTimeWorkRequest request = requestCaptor.getValue();
assertEquals(AppUsageNotificationWorker.class.getName(), request.getWorkSpec().workerClassName);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package com.d4rk.androidtutorials.java.notifications.workers;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.content.SharedPreferences;

import androidx.preference.PreferenceManager;
import androidx.work.ListenableWorker;
import androidx.work.WorkerParameters;
import androidx.core.app.NotificationCompat;

import com.d4rk.androidtutorials.java.R;

import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.MockedConstruction;
import org.mockito.MockedStatic;
import org.mockito.Mockito;

import java.util.concurrent.TimeUnit;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

public class AppUsageNotificationWorkerTest {

@Test
public void doWork_whenLastUsedExceedsThreshold_createsChannelAndNotifies() {
Context context = mock(Context.class);
when(context.getApplicationContext()).thenReturn(context);
NotificationManager notificationManager = mock(NotificationManager.class);
when(context.getSystemService(Context.NOTIFICATION_SERVICE)).thenReturn(notificationManager);

SharedPreferences sharedPreferences = mock(SharedPreferences.class);
SharedPreferences.Editor editor = mock(SharedPreferences.Editor.class);
when(sharedPreferences.edit()).thenReturn(editor);
when(editor.putLong(anyString(), anyLong())).thenReturn(editor);

long threshold = TimeUnit.DAYS.toMillis(3);
long currentTime = System.currentTimeMillis();
when(sharedPreferences.getLong(eq("lastUsed"), anyLong())).thenReturn(currentTime - threshold - 1L);

WorkerParameters parameters = mock(WorkerParameters.class);

try (MockedStatic<PreferenceManager> mockedPreferences = mockStatic(PreferenceManager.class)) {
mockedPreferences.when(() -> PreferenceManager.getDefaultSharedPreferences(context))
.thenReturn(sharedPreferences);

when(context.getString(R.string.app_usage_notifications)).thenReturn("App usage");
when(context.getString(R.string.notification_last_time_used_title)).thenReturn("We miss you");
when(context.getString(R.string.summary_notification_last_time_used)).thenReturn("Come back soon");

Notification notification = mock(Notification.class);

try (MockedConstruction<NotificationCompat.Builder> mockedBuilder =
Mockito.mockConstruction(NotificationCompat.Builder.class, (builderMock, contextData) -> {
when(builderMock.setSmallIcon(anyInt())).thenReturn(builderMock);
when(builderMock.setContentTitle(any(CharSequence.class))).thenReturn(builderMock);
when(builderMock.setContentText(any(CharSequence.class))).thenReturn(builderMock);
when(builderMock.setAutoCancel(anyBoolean())).thenReturn(builderMock);
when(builderMock.build()).thenReturn(notification);
})) {

AppUsageNotificationWorker worker = new AppUsageNotificationWorker(context, parameters);
ListenableWorker.Result result = worker.doWork();

assertEquals(ListenableWorker.Result.success(), result);

ArgumentCaptor<NotificationChannel> channelCaptor = ArgumentCaptor.forClass(NotificationChannel.class);
verify(notificationManager).createNotificationChannel(channelCaptor.capture());
NotificationChannel channel = channelCaptor.getValue();
assertEquals("app_usage_channel", channel.getId());
assertEquals("App usage", channel.getName().toString());

verify(notificationManager).notify(eq(0), eq(notification));

ArgumentCaptor<Long> timestampCaptor = ArgumentCaptor.forClass(Long.class);
verify(editor).putLong(eq("lastUsed"), timestampCaptor.capture());
assertTrue(timestampCaptor.getValue() >= currentTime);
verify(editor).apply();

assertEquals(1, mockedBuilder.constructed().size());
}
}
}

@Test
public void doWork_whenLastUsedWithinThreshold_skipsNotification() {
Context context = mock(Context.class);
when(context.getApplicationContext()).thenReturn(context);
NotificationManager notificationManager = mock(NotificationManager.class);
when(context.getSystemService(Context.NOTIFICATION_SERVICE)).thenReturn(notificationManager);

SharedPreferences sharedPreferences = mock(SharedPreferences.class);
SharedPreferences.Editor editor = mock(SharedPreferences.Editor.class);
when(sharedPreferences.edit()).thenReturn(editor);
when(editor.putLong(anyString(), anyLong())).thenReturn(editor);

long threshold = TimeUnit.DAYS.toMillis(3);
long currentTime = System.currentTimeMillis();
when(sharedPreferences.getLong(eq("lastUsed"), anyLong())).thenReturn(currentTime - threshold + 1L);

WorkerParameters parameters = mock(WorkerParameters.class);

try (MockedStatic<PreferenceManager> mockedPreferences = mockStatic(PreferenceManager.class)) {
mockedPreferences.when(() -> PreferenceManager.getDefaultSharedPreferences(context))
.thenReturn(sharedPreferences);

when(context.getString(R.string.app_usage_notifications)).thenReturn("App usage");
when(context.getString(R.string.notification_last_time_used_title)).thenReturn("We miss you");
when(context.getString(R.string.summary_notification_last_time_used)).thenReturn("Come back soon");

try (MockedConstruction<NotificationCompat.Builder> mockedBuilder =
Mockito.mockConstruction(NotificationCompat.Builder.class)) {
AppUsageNotificationWorker worker = new AppUsageNotificationWorker(context, parameters);
ListenableWorker.Result result = worker.doWork();

assertEquals(ListenableWorker.Result.success(), result);

verify(notificationManager, never()).createNotificationChannel(any(NotificationChannel.class));
verify(notificationManager, never()).notify(anyInt(), any(Notification.class));

verify(editor).putLong(eq("lastUsed"), anyLong());
verify(editor).apply();

assertTrue(mockedBuilder.constructed().isEmpty());
}
}
}
}
Loading