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
1 change: 1 addition & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -106,4 +106,5 @@ dependencies {
testImplementation libs.androidx.core.testing
testImplementation libs.mockito.core
testImplementation libs.mockito.inline
testImplementation libs.robolectric
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.d4rk.androidtutorials.java.data.repository;

import android.content.Context;
import android.content.SharedPreferences;

import androidx.appcompat.app.AppCompatDelegate;
import androidx.preference.PreferenceManager;

import com.d4rk.androidtutorials.java.R;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;

import static org.junit.Assert.*;

@RunWith(RobolectricTestRunner.class)
@Config(sdk = 33)
public class DefaultMainRepositoryApplyThemeSettingsTest {

private static final String[] DARK_MODE_VALUES = {
"MODE_NIGHT_FOLLOW_SYSTEM",
"MODE_NIGHT_NO",
"MODE_NIGHT_YES",
"MODE_NIGHT_AUTO_BATTERY"
};

private DefaultMainRepository createRepository(String prefValue) {
Context context = RuntimeEnvironment.getApplication();
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
prefs.edit().clear().putString(context.getString(R.string.key_theme), prefValue).apply();
return new DefaultMainRepository(context);
}

@Test
public void applyThemeSettingsChangesModeWhenDifferent() {
DefaultMainRepository repo = createRepository("MODE_NIGHT_YES");
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);

assertTrue(repo.applyThemeSettings(DARK_MODE_VALUES));
assertEquals(AppCompatDelegate.MODE_NIGHT_YES, AppCompatDelegate.getDefaultNightMode());
}

@Test
public void applyThemeSettingsNoChangeWhenSame() {
DefaultMainRepository repo = createRepository("MODE_NIGHT_NO");
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);

assertFalse(repo.applyThemeSettings(DARK_MODE_VALUES));
assertEquals(AppCompatDelegate.MODE_NIGHT_NO, AppCompatDelegate.getDefaultNightMode());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.d4rk.androidtutorials.java.data.repository;

import android.content.Context;
import android.content.Intent;
import android.net.Uri;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;

import static org.junit.Assert.*;

@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE, sdk = 33)
public class DefaultMainRepositoryBuildShortcutIntentTest {

private DefaultMainRepository createRepository() {
Context context = RuntimeEnvironment.getApplication();
return new DefaultMainRepository(context);
}

@Test
public void buildShortcutIntentInstalled() {
DefaultMainRepository repo = createRepository();
Intent intent = repo.buildShortcutIntent(true);
assertEquals(Intent.ACTION_MAIN, intent.getAction());
assertTrue(intent.getCategories().contains(Intent.CATEGORY_LAUNCHER));
assertEquals("com.d4rk.androidtutorials", intent.getComponent().getPackageName());
assertEquals("com.d4rk.androidtutorials.MainActivity", intent.getComponent().getClassName());
}

@Test
public void buildShortcutIntentNotInstalled() {
DefaultMainRepository repo = createRepository();
Intent intent = repo.buildShortcutIntent(false);
assertEquals(Intent.ACTION_VIEW, intent.getAction());
assertEquals(Uri.parse("https://play.google.com/store/apps/details?id=com.d4rk.androidtutorials"), intent.getData());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.d4rk.androidtutorials.java.data.source;

import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonObjectRequest;
import com.android.volley.toolbox.JsonRequest;
import com.d4rk.androidtutorials.java.data.model.PromotedApp;

import org.json.JSONObject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;

import java.lang.reflect.Field;
import java.util.List;

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE, sdk = 33)
public class DefaultHomeRemoteDataSourceTest {

@Test
public void fetchPromotedAppsFiltersAndMapsResults() throws Exception {
RequestQueue queue = mock(RequestQueue.class);
ArgumentCaptor<JsonObjectRequest> captor = ArgumentCaptor.forClass(JsonObjectRequest.class);

DefaultHomeRemoteDataSource source = new DefaultHomeRemoteDataSource(queue, "url");

final List<PromotedApp>[] results = new List[2];
final int[] index = {0};
source.fetchPromotedApps(apps -> results[index[0]++] = apps);

verify(queue).add(captor.capture());
JsonObjectRequest request = captor.getValue();

String json = "{\"data\":{\"apps\":[{\"name\":\"Name1\",\"packageName\":\"pkg1\",\"iconLogo\":\"icon1\"},{\"name\":\"Name2\",\"packageName\":\"com.d4rk.androidtutorials.other\",\"iconLogo\":\"icon2\"}]}}";
JSONObject response = new JSONObject(json);

Field listenerField = JsonRequest.class.getDeclaredField("mListener");
listenerField.setAccessible(true);
@SuppressWarnings("unchecked")
com.android.volley.Response.Listener<JSONObject> listener =
(com.android.volley.Response.Listener<JSONObject>) listenerField.get(request);
listener.onResponse(response);

assertEquals(1, results[0].size());
PromotedApp app = results[0].get(0);
assertEquals("Name1", app.name());
assertEquals("pkg1", app.packageName());
assertEquals("icon1", app.iconUrl());

Field errorField = Request.class.getDeclaredField("mErrorListener");
errorField.setAccessible(true);
com.android.volley.Response.ErrorListener errorListener =
(com.android.volley.Response.ErrorListener) errorField.get(request);
errorListener.onErrorResponse(new VolleyError());

assertTrue(results[1].isEmpty());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.d4rk.androidtutorials.java.data.source;

import android.content.res.AssetManager;

import com.d4rk.androidtutorials.java.data.model.QuizQuestion;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE, sdk = 33)
public class DefaultQuizLocalDataSourceTest {

@Test
public void loadQuestionsParsesFile() throws Exception {
AssetManager manager = mock(AssetManager.class);
String json = "[{\"question\":\"Q1\",\"options\":[\"A\",\"B\"],\"answer\":1},{\"question\":\"Q2\",\"options\":[\"C\",\"D\",\"E\"],\"answer\":0}]";
when(manager.open("quiz_questions.json")).thenReturn(
new ByteArrayInputStream(json.getBytes(StandardCharsets.UTF_8)));

DefaultQuizLocalDataSource dataSource = new DefaultQuizLocalDataSource(manager);
List<QuizQuestion> result = dataSource.loadQuestions();

assertEquals(2, result.size());
QuizQuestion first = result.get(0);
assertEquals("Q1", first.question());
assertArrayEquals(new String[]{"A", "B"}, first.options());
assertEquals(1, first.answerIndex());
}

@Test
public void loadQuestionsReturnsEmptyOnError() throws Exception {
AssetManager manager = mock(AssetManager.class);
when(manager.open("quiz_questions.json")).thenThrow(new IOException("missing"));

DefaultQuizLocalDataSource dataSource = new DefaultQuizLocalDataSource(manager);
List<QuizQuestion> result = dataSource.loadQuestions();

assertTrue(result.isEmpty());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.d4rk.androidtutorials.java.notifications.managers;

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

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;

import java.lang.reflect.Field;
import java.util.concurrent.TimeUnit;

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE, sdk = 33)
public class AppUsageNotificationsManagerTest {

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

AppUsageNotificationsManager manager = new AppUsageNotificationsManager(context);

PendingIntent pendingIntent = mock(PendingIntent.class);
Field intentField = AppUsageNotificationsManager.class.getDeclaredField("notificationIntent");
intentField.setAccessible(true);
intentField.set(manager, pendingIntent);

Field alarmField = AppUsageNotificationsManager.class.getDeclaredField("alarmManager");
alarmField.setAccessible(true);
alarmField.set(manager, alarmManager);

long now = System.currentTimeMillis();
manager.scheduleAppUsageCheck();

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

long expected = now + TimeUnit.DAYS.toMillis(3);
assertTrue(Math.abs(triggerCaptor.getValue() - expected) < TimeUnit.MINUTES.toMillis(1));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.d4rk.androidtutorials.java.notifications.managers;

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

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;

import java.lang.reflect.Field;
import java.util.concurrent.TimeUnit;

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE, sdk = 33)
public class QuizReminderManagerTest {

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

QuizReminderManager manager = new QuizReminderManager(context);

PendingIntent pendingIntent = mock(PendingIntent.class);
Field intentField = QuizReminderManager.class.getDeclaredField("reminderIntent");
intentField.setAccessible(true);
intentField.set(manager, pendingIntent);

Field alarmField = QuizReminderManager.class.getDeclaredField("alarmManager");
alarmField.setAccessible(true);
alarmField.set(manager, alarmManager);

long now = System.currentTimeMillis();
manager.scheduleDailyReminder();

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

long expected = now + TimeUnit.DAYS.toMillis(1);
assertTrue(Math.abs(triggerCaptor.getValue() - expected) < TimeUnit.MINUTES.toMillis(1));
}
}
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ playServicesAds = "24.5.0"
codeview = "1.3.9"
hilt = "2.57.1"
room = "2.6.1"
robolectric = "4.12.2"
glide = "4.16.0"

[libraries]
Expand Down Expand Up @@ -71,5 +72,6 @@ hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hilt"
hilt-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hilt" }
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" }
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" }
robolectric = { module = "org.robolectric:robolectric", version.ref = "robolectric" }
glide = { module = "com.github.bumptech.glide:glide", version.ref = "glide" }
glide-compiler = { module = "com.github.bumptech.glide:compiler", version.ref = "glide" }
Loading