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,62 @@
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 androidx.test.core.app.ApplicationProvider;

import com.d4rk.androidtutorials.java.R;

import org.junit.After;
import org.junit.Test;

import static org.junit.Assert.*;

public class DefaultMainRepositoryApplyThemeSettingsTest {

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

@After
public void reset() {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
}

@Test
public void applyThemeSettings_updatesModeWhenDifferent() {
Context context = ApplicationProvider.getApplicationContext();
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
prefs.edit().clear().commit();

AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
prefs.edit().putString(context.getString(R.string.key_theme), "MODE_NIGHT_YES").commit();

DefaultMainRepository repo = new DefaultMainRepository(context);
boolean changed = repo.applyThemeSettings(darkModeValues);

assertTrue(changed);
assertEquals(AppCompatDelegate.MODE_NIGHT_YES, AppCompatDelegate.getDefaultNightMode());
}

@Test
public void applyThemeSettings_noChangeWhenSame() {
Context context = ApplicationProvider.getApplicationContext();
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
prefs.edit().clear().commit();

AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
prefs.edit().putString(context.getString(R.string.key_theme), "MODE_NIGHT_NO").commit();

DefaultMainRepository repo = new DefaultMainRepository(context);
boolean changed = repo.applyThemeSettings(darkModeValues);

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

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

import androidx.test.core.app.ApplicationProvider;

import org.junit.Test;

import static org.junit.Assert.*;

public class DefaultMainRepositoryBuildShortcutIntentTest {

@Test
public void buildShortcutIntent_installed() {
Context context = ApplicationProvider.getApplicationContext();
DefaultMainRepository repo = new DefaultMainRepository(context);

Intent intent = repo.buildShortcutIntent(true);
assertEquals(Intent.ACTION_MAIN, intent.getAction());
assertTrue(intent.getCategories().contains(Intent.CATEGORY_LAUNCHER));
assertEquals("com.d4rk.androidtutorials.MainActivity", intent.getComponent().getClassName());
}

@Test
public void buildShortcutIntent_notInstalled() {
Context context = ApplicationProvider.getApplicationContext();
DefaultMainRepository repo = new DefaultMainRepository(context);

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,85 @@
package com.d4rk.androidtutorials.java.data.source;

import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
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 java.lang.reflect.Field;
import java.util.List;

import static org.junit.Assert.*;

public class DefaultHomeRemoteDataSourceTest {

private static class FakeRequestQueue extends RequestQueue {
Response.Listener<JSONObject> success;
Response.ErrorListener error;

FakeRequestQueue() {
super(null, null);
}

@Override
public <T> Request<T> add(Request<T> request) {
try {
Field listenerField = JsonRequest.class.getDeclaredField("mListener");
listenerField.setAccessible(true);
success = (Response.Listener<JSONObject>) listenerField.get(request);

Field errorField = Request.class.getDeclaredField("mErrorListener");
errorField.setAccessible(true);
error = (Response.ErrorListener) errorField.get(request);
} catch (Exception e) {
throw new RuntimeException(e);
}
return request;
}

void triggerSuccess(JSONObject obj) {
success.onResponse(obj);
}

void triggerError(VolleyError volleyError) {
error.onErrorResponse(volleyError);
}
}

@Test
public void fetchPromotedApps_parsesAndFilters() throws Exception {
FakeRequestQueue queue = new FakeRequestQueue();
DefaultHomeRemoteDataSource source = new DefaultHomeRemoteDataSource(queue, "url");
final List<PromotedApp>[] result = new List[1];
source.fetchPromotedApps(apps -> result[0] = apps);

JSONObject payload = new JSONObject("{\"data\":{\"apps\":[{\"name\":\"One\",\"packageName\":\"com.example.one\",\"iconLogo\":\"icon1\"},{\"name\":\"Two\",\"packageName\":\"com.d4rk.androidtutorials.other\",\"iconLogo\":\"icon2\"}]}}");

queue.triggerSuccess(payload);

assertNotNull(result[0]);
assertEquals(1, result[0].size());
PromotedApp app = result[0].get(0);
assertEquals("One", app.name());
assertEquals("com.example.one", app.packageName());
assertEquals("icon1", app.iconUrl());
}

@Test
public void fetchPromotedApps_errorReturnsEmpty() {
FakeRequestQueue queue = new FakeRequestQueue();
DefaultHomeRemoteDataSource source = new DefaultHomeRemoteDataSource(queue, "url");
final List<PromotedApp>[] result = new List[1];
source.fetchPromotedApps(apps -> result[0] = apps);

queue.triggerError(new VolleyError("boom"));

assertNotNull(result[0]);
assertTrue(result[0].isEmpty());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
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 java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.AbstractExecutorService;
import java.util.concurrent.TimeUnit;

import static org.junit.Assert.*;

public class DefaultQuizLocalDataSourceTest {

private static class FakeAssetManager extends AssetManager {
private final InputStream stream;
FakeAssetManager(String json) {
this.stream = new ByteArrayInputStream(json.getBytes(StandardCharsets.UTF_8));
}
@Override
public InputStream open(String fileName) throws IOException {
return stream;
}
}

private static class ErrorAssetManager extends AssetManager {
@Override
public InputStream open(String fileName) throws IOException {
throw new IOException("missing");
}
}

private static class ImmediateExecutor extends AbstractExecutorService {
@Override public void shutdown() {}
@Override public List<Runnable> shutdownNow() { return Collections.emptyList(); }
@Override public boolean isShutdown() { return false; }
@Override public boolean isTerminated() { return false; }
@Override public boolean awaitTermination(long timeout, TimeUnit unit) { return true; }
@Override public void execute(Runnable command) { command.run(); }
}

@Test
public void loadQuestions_parsesValidFile() {
String json = "[{\"question\":\"Q1\",\"options\":[\"A\",\"B\"],\"answer\":0},{\"question\":\"Q2\",\"options\":[\"C\",\"D\"],\"answer\":1}]";
DefaultQuizLocalDataSource source = new DefaultQuizLocalDataSource(new FakeAssetManager(json), new ImmediateExecutor());
final List<QuizQuestion>[] result = new List[1];
source.loadQuestions(q -> result[0] = q);

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

@Test
public void loadQuestions_errorReturnsEmptyList() {
DefaultQuizLocalDataSource source = new DefaultQuizLocalDataSource(new ErrorAssetManager(), new ImmediateExecutor());
final List<QuizQuestion>[] result = new List[1];
source.loadQuestions(q -> result[0] = q);
assertNotNull(result[0]);
assertTrue(result[0].isEmpty());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.d4rk.androidtutorials.java.notifications.managers;

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

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

import java.util.concurrent.TimeUnit;

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

public class AppUsageNotificationsManagerTest {

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

try (MockedStatic<PendingIntent> pendingStatic = mockStatic(PendingIntent.class)) {
pendingStatic.when(() -> PendingIntent.getBroadcast(any(Context.class), anyInt(), any(Intent.class), anyInt()))
.thenReturn(pendingIntent);

AppUsageNotificationsManager manager = new AppUsageNotificationsManager(context);
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.SECONDS.toMillis(1));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.d4rk.androidtutorials.java.notifications.managers;

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

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

import java.util.concurrent.TimeUnit;

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

public class QuizReminderManagerTest {

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

try (MockedStatic<PendingIntent> pendingStatic = mockStatic(PendingIntent.class)) {
pendingStatic.when(() -> PendingIntent.getBroadcast(any(Context.class), anyInt(), any(Intent.class), anyInt()))
.thenReturn(pendingIntent);

QuizReminderManager manager = new QuizReminderManager(context);
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.SECONDS.toMillis(1));
}
}
}
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ codeview = "1.3.9"
hilt = "2.57.1"
room = "2.7.2"
glide = "5.0.4"
robolectric = "4.11.1"

[libraries]
aboutlibraries = { module = "com.mikepenz:aboutlibraries", version.ref = "aboutlibraries" }
Expand Down Expand Up @@ -73,3 +74,4 @@ androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" }
glide = { module = "com.github.bumptech.glide:glide", version.ref = "glide" }
glide-compiler = { module = "com.github.bumptech.glide:compiler", version.ref = "glide" }
robolectric = { module = "org.robolectric:robolectric", version.ref = "robolectric" }
Loading