diff --git a/app/build.gradle b/app/build.gradle
index aef0b9bc7e2f2382b2c06ec0229942fe84c3f8f1..35ed7284f4e9e71b6f3821778e90f7ab6fc92cd9 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,3 +1,4 @@
+
plugins {
id 'com.android.application'
id 'kotlin-android'
@@ -113,7 +114,7 @@ android {
lintConfig file('lint.xml')
}
kotlin.sourceSets.all {
- languageSettings.useExperimentalAnnotation("kotlin.RequiresOptIn")
+ languageSettings.optIn("kotlin.RequiresOptIn")
}
}
@@ -156,6 +157,8 @@ dependencies {
testImplementation 'org.mockito:mockito-inline:2.13.0'
testImplementation "androidx.arch.core:core-testing:2.1.0"
+ testImplementation "io.mockk:mockk:1.12.3"
+
// Coil and PhotoView
implementation "io.coil-kt:coil:1.4.0"
implementation 'com.github.Baseflow:PhotoView:2.3.0'
diff --git a/app/src/main/java/foundation/e/apps/api/DownloadManager.kt b/app/src/main/java/foundation/e/apps/api/DownloadManager.kt
index 0c4d099acab55d0444d5e73c4405219b17fc2d9e..e3d2730af88eece8c70cb647f586d220464a1e14 100644
--- a/app/src/main/java/foundation/e/apps/api/DownloadManager.kt
+++ b/app/src/main/java/foundation/e/apps/api/DownloadManager.kt
@@ -19,6 +19,7 @@ package foundation.e.apps.api
import android.app.DownloadManager
import android.net.Uri
+import foundation.e.apps.OpenForTesting
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
@@ -34,6 +35,7 @@ import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
@Singleton
+@OpenForTesting
class DownloadManager @Inject constructor(
private val downloadManager: DownloadManager,
@Named("cacheDir") private val cacheDir: String,
diff --git a/app/src/main/java/foundation/e/apps/api/fdroid/FdroidRepository.kt b/app/src/main/java/foundation/e/apps/api/fdroid/FdroidRepository.kt
index 9990db75b86595170bb8d7559863d9f6260d6406..beba5d5b2c7e633c16652c106a8640ea34b89c6b 100644
--- a/app/src/main/java/foundation/e/apps/api/fdroid/FdroidRepository.kt
+++ b/app/src/main/java/foundation/e/apps/api/fdroid/FdroidRepository.kt
@@ -12,7 +12,7 @@ import javax.inject.Singleton
class FdroidRepository @Inject constructor(
private val fdroidApi: FdroidApiInterface,
private val fdroidDao: FdroidDao,
-) {
+) : IFdroidRepository {
companion object {
const val UNKNOWN = "unknown"
@@ -26,7 +26,7 @@ class FdroidRepository @Inject constructor(
*
* Result may be null.
*/
- private suspend fun getFdroidInfo(packageName: String): FdroidEntity? {
+ override suspend fun getFdroidInfo(packageName: String): FdroidEntity? {
return fdroidDao.getFdroidEntityFromPackageName(packageName)
?: fdroidApi.getFdroidInfoForPackage(packageName).body()?.let {
FdroidEntity(packageName, it.authorName).also {
@@ -35,7 +35,7 @@ class FdroidRepository @Inject constructor(
}
}
- suspend fun getAuthorName(fusedApp: FusedApp): String {
+ override suspend fun getAuthorName(fusedApp: FusedApp): String {
if (fusedApp.author != UNKNOWN || fusedApp.origin != Origin.CLEANAPK) {
return fusedApp.author.ifEmpty { UNKNOWN }
}
@@ -49,14 +49,14 @@ class FdroidRepository @Inject constructor(
return result?.authorName ?: FdroidEntity.DEFAULT_FDROID_AUTHOR_NAME
}
- suspend fun isFdroidApplicationSigned(context: Context, packageName: String, apkFilePath: String, signature: String): Boolean {
+ override suspend fun isFdroidApplicationSigned(context: Context, packageName: String, apkFilePath: String, signature: String): Boolean {
if (isFdroidApplication(packageName)) {
return ApkSignatureManager.verifyFdroidSignature(context, apkFilePath, signature)
}
return false
}
- private suspend fun isFdroidApplication(packageName: String): Boolean {
+ override suspend fun isFdroidApplication(packageName: String): Boolean {
return fdroidApi.getFdroidInfoForPackage(packageName).isSuccessful
}
}
diff --git a/app/src/main/java/foundation/e/apps/api/fdroid/IFdroidRepository.kt b/app/src/main/java/foundation/e/apps/api/fdroid/IFdroidRepository.kt
new file mode 100644
index 0000000000000000000000000000000000000000..2e18be4cf5f98044f53051857cd66c075726b6af
--- /dev/null
+++ b/app/src/main/java/foundation/e/apps/api/fdroid/IFdroidRepository.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright MURENA SAS 2023
+ * Apps Quickly and easily install Android apps onto your device!
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package foundation.e.apps.api.fdroid
+
+import android.content.Context
+import foundation.e.apps.api.fdroid.models.FdroidEntity
+import foundation.e.apps.api.fused.data.FusedApp
+
+interface IFdroidRepository {
+ /**
+ * Get Fdroid entity from DB is present.
+ * If not present then make an API call, store the fetched result and return the result.
+ *
+ * Result may be null.
+ */
+ suspend fun getFdroidInfo(packageName: String): FdroidEntity?
+
+ suspend fun getAuthorName(fusedApp: FusedApp): String
+
+ suspend fun isFdroidApplicationSigned(
+ context: Context,
+ packageName: String,
+ apkFilePath: String,
+ signature: String
+ ): Boolean
+
+ suspend fun isFdroidApplication(packageName: String): Boolean
+}
diff --git a/app/src/main/java/foundation/e/apps/di/RepositoryModule.kt b/app/src/main/java/foundation/e/apps/di/RepositoryModule.kt
index 0508db1ebf6def951c1948674e867034105917dd..f527d7a7ada8f0f62f44329b077fad7f78f4e01c 100644
--- a/app/src/main/java/foundation/e/apps/di/RepositoryModule.kt
+++ b/app/src/main/java/foundation/e/apps/di/RepositoryModule.kt
@@ -6,6 +6,10 @@ import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import foundation.e.apps.api.exodus.repositories.AppPrivacyInfoRepositoryImpl
import foundation.e.apps.api.exodus.repositories.IAppPrivacyInfoRepository
+import foundation.e.apps.api.fdroid.FdroidRepository
+import foundation.e.apps.api.fdroid.IFdroidRepository
+import foundation.e.apps.manager.fused.FusedManagerImpl
+import foundation.e.apps.manager.fused.IFusedManager
import javax.inject.Singleton
@Module
@@ -14,4 +18,12 @@ interface RepositoryModule {
@Singleton
@Binds
fun getRepositoryModule(trackerRepositoryImpl: AppPrivacyInfoRepositoryImpl): IAppPrivacyInfoRepository
+
+ @Singleton
+ @Binds
+ fun getFusedManagerImpl(fusedManagerImpl: FusedManagerImpl): IFusedManager
+
+ @Singleton
+ @Binds
+ fun getFdroidRepository(fusedManagerImpl: FdroidRepository): IFdroidRepository
}
diff --git a/app/src/main/java/foundation/e/apps/manager/fused/FusedManagerImpl.kt b/app/src/main/java/foundation/e/apps/manager/fused/FusedManagerImpl.kt
index a9ce748dfcc260ff0cb726f71896ef47437a4249..d5f8ad0853e1e240052348582b74a695d26b93cb 100644
--- a/app/src/main/java/foundation/e/apps/manager/fused/FusedManagerImpl.kt
+++ b/app/src/main/java/foundation/e/apps/manager/fused/FusedManagerImpl.kt
@@ -57,42 +57,42 @@ class FusedManagerImpl @Inject constructor(
@Named("download") private val downloadNotificationChannel: NotificationChannel,
@Named("update") private val updateNotificationChannel: NotificationChannel,
@ApplicationContext private val context: Context
-) {
+) : IFusedManager {
private val TAG = FusedManagerImpl::class.java.simpleName
@RequiresApi(Build.VERSION_CODES.O)
- fun createNotificationChannels() {
+ override fun createNotificationChannels() {
notificationManager.apply {
createNotificationChannel(downloadNotificationChannel)
createNotificationChannel(updateNotificationChannel)
}
}
- suspend fun addDownload(fusedDownload: FusedDownload) {
+ override suspend fun addDownload(fusedDownload: FusedDownload) {
fusedDownload.status = Status.QUEUED
databaseRepository.addDownload(fusedDownload)
}
- suspend fun getDownloadById(fusedDownload: FusedDownload): FusedDownload? {
+ override suspend fun getDownloadById(fusedDownload: FusedDownload): FusedDownload? {
return databaseRepository.getDownloadById(fusedDownload.id)
}
- suspend fun getDownloadList(): List {
+ override suspend fun getDownloadList(): List {
return databaseRepository.getDownloadList()
}
- fun getDownloadLiveList(): LiveData> {
+ override fun getDownloadLiveList(): LiveData> {
return databaseRepository.getDownloadLiveList()
}
- suspend fun clearInstallationIssue(fusedDownload: FusedDownload) {
+ override suspend fun clearInstallationIssue(fusedDownload: FusedDownload) {
flushOldDownload(fusedDownload.packageName)
databaseRepository.deleteDownload(fusedDownload)
}
@OptIn(DelicateCoroutinesApi::class)
- suspend fun updateDownloadStatus(fusedDownload: FusedDownload, status: Status) {
+ override suspend fun updateDownloadStatus(fusedDownload: FusedDownload, status: Status) {
if (status == Status.INSTALLED) {
fusedDownload.status = status
DownloadManagerBR.downloadedList.clear()
@@ -108,7 +108,7 @@ class FusedManagerImpl @Inject constructor(
private val mutex = Mutex()
- suspend fun downloadApp(fusedDownload: FusedDownload) {
+ override suspend fun downloadApp(fusedDownload: FusedDownload) {
mutex.withLock {
when (fusedDownload.type) {
Type.NATIVE -> downloadNativeApp(fusedDownload)
@@ -117,7 +117,7 @@ class FusedManagerImpl @Inject constructor(
}
}
- suspend fun installApp(fusedDownload: FusedDownload) {
+ override suspend fun installApp(fusedDownload: FusedDownload) {
val list = mutableListOf()
when (fusedDownload.type) {
Type.NATIVE -> {
@@ -146,7 +146,7 @@ class FusedManagerImpl @Inject constructor(
}
@OptIn(DelicateCoroutinesApi::class)
- suspend fun cancelDownload(fusedDownload: FusedDownload) {
+ override suspend fun cancelDownload(fusedDownload: FusedDownload) {
if (fusedDownload.id.isNotBlank()) {
fusedDownload.downloadIdMap.forEach { (key, _) ->
downloadManager.remove(key)
@@ -164,7 +164,7 @@ class FusedManagerImpl @Inject constructor(
}
}
- suspend fun getFusedDownload(downloadId: Long = 0, packageName: String = ""): FusedDownload {
+ override suspend fun getFusedDownload(downloadId: Long, packageName: String): FusedDownload {
val downloadList = getDownloadList()
var fusedDownload = FusedDownload()
downloadList.forEach {
@@ -181,12 +181,12 @@ class FusedManagerImpl @Inject constructor(
return fusedDownload
}
- private fun flushOldDownload(packageName: String) {
+ override fun flushOldDownload(packageName: String) {
val parentPathFile = File("$cacheDir/$packageName")
if (parentPathFile.exists()) parentPathFile.deleteRecursively()
}
- private suspend fun downloadNativeApp(fusedDownload: FusedDownload) {
+ override suspend fun downloadNativeApp(fusedDownload: FusedDownload) {
var count = 0
var parentPath = "$cacheDir/${fusedDownload.packageName}"
@@ -214,7 +214,7 @@ class FusedManagerImpl @Inject constructor(
databaseRepository.updateDownload(fusedDownload)
}
- private fun getGplayInstallationPackagePath(
+ override fun getGplayInstallationPackagePath(
fusedDownload: FusedDownload,
it: String,
parentPath: String,
@@ -228,7 +228,7 @@ class FusedManagerImpl @Inject constructor(
}
}
- private fun createObbFileForDownload(
+ override fun createObbFileForDownload(
fusedDownload: FusedDownload,
url: String
): File {
@@ -239,7 +239,7 @@ class FusedManagerImpl @Inject constructor(
return File(parentPath, obbFile.name)
}
- fun moveOBBFilesToOBBDirectory(fusedDownload: FusedDownload) {
+ override fun moveOBBFilesToOBBDirectory(fusedDownload: FusedDownload) {
fusedDownload.files.forEach {
val parentPath =
context.getExternalFilesDir(null)?.absolutePath + "/Android/obb/" + fusedDownload.packageName
@@ -253,39 +253,39 @@ class FusedManagerImpl @Inject constructor(
}
}
- fun getBaseApkPath(fusedDownload: FusedDownload) =
+ override fun getBaseApkPath(fusedDownload: FusedDownload) =
"$cacheDir/${fusedDownload.packageName}/${fusedDownload.packageName}_1.apk"
- suspend fun installationIssue(fusedDownload: FusedDownload) {
+ override suspend fun installationIssue(fusedDownload: FusedDownload) {
flushOldDownload(fusedDownload.packageName)
fusedDownload.status = Status.INSTALLATION_ISSUE
databaseRepository.updateDownload(fusedDownload)
}
- suspend fun updateAwaiting(fusedDownload: FusedDownload) {
+ override suspend fun updateAwaiting(fusedDownload: FusedDownload) {
fusedDownload.status = Status.AWAITING
databaseRepository.updateDownload(fusedDownload)
}
- suspend fun updateUnavailable(fusedDownload: FusedDownload) {
+ override suspend fun updateUnavailable(fusedDownload: FusedDownload) {
fusedDownload.status = Status.UNAVAILABLE
databaseRepository.updateDownload(fusedDownload)
}
- suspend fun updateFusedDownload(fusedDownload: FusedDownload) {
+ override suspend fun updateFusedDownload(fusedDownload: FusedDownload) {
databaseRepository.updateDownload(fusedDownload)
}
- suspend fun insertFusedDownloadPurchaseNeeded(fusedDownload: FusedDownload) {
+ override suspend fun insertFusedDownloadPurchaseNeeded(fusedDownload: FusedDownload) {
fusedDownload.status = Status.PURCHASE_NEEDED
databaseRepository.addDownload(fusedDownload)
}
- fun isFusedDownloadInstalled(fusedDownload: FusedDownload): Boolean {
+ override fun isFusedDownloadInstalled(fusedDownload: FusedDownload): Boolean {
return pkgManagerModule.isInstalled(fusedDownload.packageName)
}
- fun getFusedDownloadInstallationStatus(fusedApp: FusedDownload): Status {
+ override fun getFusedDownloadInstallationStatus(fusedApp: FusedDownload): Status {
return pkgManagerModule.getPackageStatus(fusedApp.packageName, fusedApp.versionCode)
}
}
diff --git a/app/src/main/java/foundation/e/apps/manager/fused/FusedManagerRepository.kt b/app/src/main/java/foundation/e/apps/manager/fused/FusedManagerRepository.kt
index c9bd1d6dd84c8ea371a627bd17992c525a328157..e59041073a5b38fd081357b0ce3e99e1dba30bca 100644
--- a/app/src/main/java/foundation/e/apps/manager/fused/FusedManagerRepository.kt
+++ b/app/src/main/java/foundation/e/apps/manager/fused/FusedManagerRepository.kt
@@ -5,6 +5,7 @@ import android.os.Build
import androidx.annotation.RequiresApi
import androidx.lifecycle.LiveData
import androidx.lifecycle.asFlow
+import foundation.e.apps.OpenForTesting
import foundation.e.apps.api.fdroid.FdroidRepository
import foundation.e.apps.api.fused.data.FusedApp
import foundation.e.apps.manager.database.fusedDownload.FusedDownload
@@ -16,8 +17,9 @@ import javax.inject.Inject
import javax.inject.Singleton
@Singleton
+@OpenForTesting
class FusedManagerRepository @Inject constructor(
- private val fusedManagerImpl: FusedManagerImpl,
+ private val fusedManagerImpl: IFusedManager,
private val fdroidRepository: FdroidRepository
) {
diff --git a/app/src/main/java/foundation/e/apps/manager/fused/IFusedManager.kt b/app/src/main/java/foundation/e/apps/manager/fused/IFusedManager.kt
new file mode 100644
index 0000000000000000000000000000000000000000..94ab9705e5ed1d8909ebb179a428132d806da9e5
--- /dev/null
+++ b/app/src/main/java/foundation/e/apps/manager/fused/IFusedManager.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright MURENA SAS 2023
+ * Apps Quickly and easily install Android apps onto your device!
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package foundation.e.apps.manager.fused
+
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.lifecycle.LiveData
+import foundation.e.apps.manager.database.fusedDownload.FusedDownload
+import foundation.e.apps.utils.enums.Status
+import kotlinx.coroutines.DelicateCoroutinesApi
+import java.io.File
+
+interface IFusedManager {
+ fun createNotificationChannels()
+
+ suspend fun addDownload(fusedDownload: FusedDownload)
+
+ suspend fun getDownloadById(fusedDownload: FusedDownload): FusedDownload?
+
+ suspend fun getDownloadList(): List
+ fun getDownloadLiveList(): LiveData>
+
+ suspend fun clearInstallationIssue(fusedDownload: FusedDownload)
+
+ suspend fun updateDownloadStatus(fusedDownload: FusedDownload, status: Status)
+
+ suspend fun downloadApp(fusedDownload: FusedDownload)
+
+ suspend fun installApp(fusedDownload: FusedDownload)
+
+ suspend fun cancelDownload(fusedDownload: FusedDownload)
+
+ suspend fun getFusedDownload(downloadId: Long = 0, packageName: String = ""): FusedDownload
+ fun flushOldDownload(packageName: String)
+
+ suspend fun downloadNativeApp(fusedDownload: FusedDownload)
+ fun getGplayInstallationPackagePath(
+ fusedDownload: FusedDownload,
+ it: String,
+ parentPath: String,
+ count: Int
+ ): File
+
+ fun createObbFileForDownload(
+ fusedDownload: FusedDownload,
+ url: String
+ ): File
+
+ fun moveOBBFilesToOBBDirectory(fusedDownload: FusedDownload)
+ fun getBaseApkPath(fusedDownload: FusedDownload): String
+
+ suspend fun installationIssue(fusedDownload: FusedDownload)
+
+ suspend fun updateAwaiting(fusedDownload: FusedDownload)
+
+ suspend fun updateUnavailable(fusedDownload: FusedDownload)
+
+ suspend fun updateFusedDownload(fusedDownload: FusedDownload)
+
+ suspend fun insertFusedDownloadPurchaseNeeded(fusedDownload: FusedDownload)
+ fun isFusedDownloadInstalled(fusedDownload: FusedDownload): Boolean
+ fun getFusedDownloadInstallationStatus(fusedApp: FusedDownload): Status
+}
diff --git a/app/src/main/java/foundation/e/apps/manager/workmanager/AppInstallProcessor.kt b/app/src/main/java/foundation/e/apps/manager/workmanager/AppInstallProcessor.kt
index b0c5b749bbdc1dbc7e45ca59439d21e93e39c6d7..b4bcff8e48170632f60c9609adccfaefc8b6e888 100644
--- a/app/src/main/java/foundation/e/apps/manager/workmanager/AppInstallProcessor.kt
+++ b/app/src/main/java/foundation/e/apps/manager/workmanager/AppInstallProcessor.kt
@@ -19,7 +19,6 @@
package foundation.e.apps.manager.workmanager
import android.content.Context
-import android.util.Log
import dagger.hilt.android.qualifiers.ApplicationContext
import foundation.e.apps.R
import foundation.e.apps.api.DownloadManager
@@ -86,6 +85,12 @@ class AppInstallProcessor @Inject constructor(
return@let
}
+ if (!fusedManagerRepository.validateFusedDownload(fusedDownload)) {
+ fusedManagerRepository.installationIssue(it)
+ Timber.d("!!! installationIssue")
+ return@let
+ }
+
if (fusedDownload.areFilesDownloaded() && !fusedManagerRepository.isFusedDownloadInstalled(
fusedDownload
)
@@ -96,12 +101,6 @@ class AppInstallProcessor @Inject constructor(
runInForeground?.invoke(it.name)
- if (!fusedManagerRepository.validateFusedDownload(fusedDownload)) {
- fusedManagerRepository.installationIssue(it)
- Timber.d("!!! installationIssue")
- return@let
- }
-
startAppInstallationProcess(it)
mutex.lock()
}
@@ -204,7 +203,8 @@ class AppInstallProcessor @Inject constructor(
try {
handleFusedDownloadStatus(download)
} catch (e: Exception) {
- Log.e(TAG, "observeDownload: ", e)
+ Timber.e(TAG, "observeDownload: ", e)
+ fusedManagerRepository.installationIssue(download)
finishInstallation(download)
}
}
diff --git a/app/src/main/java/foundation/e/apps/updates/manager/UpdatesWorker.kt b/app/src/main/java/foundation/e/apps/updates/manager/UpdatesWorker.kt
index 009ecc62e18d2931b653ed500816d99554041be0..49eb09ca2a16f75e58d5c2748fb052601f0e043c 100644
--- a/app/src/main/java/foundation/e/apps/updates/manager/UpdatesWorker.kt
+++ b/app/src/main/java/foundation/e/apps/updates/manager/UpdatesWorker.kt
@@ -14,7 +14,6 @@ import androidx.work.CoroutineWorker
import androidx.work.WorkInfo.State
import androidx.work.WorkManager
import androidx.work.WorkerParameters
-import androidx.work.await
import com.aurora.gplayapi.data.models.AuthData
import com.google.gson.Gson
import dagger.assisted.Assisted
@@ -35,10 +34,12 @@ import foundation.e.apps.utils.enums.User
import foundation.e.apps.utils.eventBus.AppEvent
import foundation.e.apps.utils.eventBus.EventBus
import foundation.e.apps.utils.modules.DataStoreManager
-import kotlinx.coroutines.delay
-import timber.log.Timber
import java.io.ByteArrayOutputStream
import java.net.URL
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.withContext
+import timber.log.Timber
@HiltWorker
class UpdatesWorker @AssistedInject constructor(
@@ -85,7 +86,10 @@ class UpdatesWorker @AssistedInject constructor(
private suspend fun checkManualUpdateRunning(): Boolean {
val workInfos =
- WorkManager.getInstance(context).getWorkInfosByTag(UpdatesWorkManager.USER_TAG).await()
+ withContext(Dispatchers.IO) {
+ WorkManager.getInstance(context).getWorkInfosByTag(UpdatesWorkManager.USER_TAG)
+ .get()
+ }
if (workInfos.isNotEmpty()) {
val workInfo = workInfos[0]
Timber.d("Manual update status: workInfo.state=${workInfo.state}, id=${workInfo.id}")
diff --git a/app/src/test/java/foundation/e/apps/FusedApiRepositoryTest.kt b/app/src/test/java/foundation/e/apps/FusedApiRepositoryTest.kt
index e3842065ca97dcdcf474d4c055e95b3d60a53063..8f03f4ecb2cbfb5682933b8b4ca7f34681f6b170 100644
--- a/app/src/test/java/foundation/e/apps/FusedApiRepositoryTest.kt
+++ b/app/src/test/java/foundation/e/apps/FusedApiRepositoryTest.kt
@@ -30,24 +30,24 @@ import org.mockito.kotlin.any
class FusedApiRepositoryTest {
private lateinit var fusedApiRepository: FusedAPIRepository
@Mock
- private lateinit var fusedApiImple: FusedAPIImpl
+ private lateinit var fusedAPIImpl: FusedAPIImpl
@Before
fun setup() {
MockitoAnnotations.openMocks(this)
- fusedApiRepository = FusedAPIRepository(fusedApiImple)
+ fusedApiRepository = FusedAPIRepository(fusedAPIImpl)
}
@Test
fun isAnyAppUpdated_ReturnsTrue() {
- Mockito.`when`(fusedApiImple.isAnyFusedAppUpdated(any(), any())).thenReturn(true)
+ Mockito.`when`(fusedAPIImpl.isAnyFusedAppUpdated(any(), any())).thenReturn(true)
val isAnyAppUpdated = fusedApiRepository.isAnyFusedAppUpdated(listOf(), listOf())
assertTrue("isAnyAppUpdated", isAnyAppUpdated)
}
@Test
fun isAnyInstallStatusChanged_ReturnsTrue() {
- Mockito.`when`(fusedApiImple.isAnyAppInstallStatusChanged(any())).thenReturn(true)
+ Mockito.`when`(fusedAPIImpl.isAnyAppInstallStatusChanged(any())).thenReturn(true)
val isAnyAppUpdated = fusedApiRepository.isAnyAppInstallStatusChanged(listOf())
assertTrue("isAnyAppUpdated", isAnyAppUpdated)
}
diff --git a/app/src/test/java/foundation/e/apps/fusedManager/FakeFusedManager.kt b/app/src/test/java/foundation/e/apps/fusedManager/FakeFusedManager.kt
new file mode 100644
index 0000000000000000000000000000000000000000..bfe765b2ee2aa8a67fb480c4a7efce12271d481a
--- /dev/null
+++ b/app/src/test/java/foundation/e/apps/fusedManager/FakeFusedManager.kt
@@ -0,0 +1,130 @@
+/*
+ * Copyright MURENA SAS 2023
+ * Apps Quickly and easily install Android apps onto your device!
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package foundation.e.apps.fusedManager
+
+import androidx.lifecycle.LiveData
+import foundation.e.apps.manager.database.fusedDownload.FusedDownload
+import foundation.e.apps.manager.database.fusedDownload.FusedDownloadDAO
+import foundation.e.apps.manager.fused.IFusedManager
+import foundation.e.apps.utils.enums.Status
+import java.io.File
+
+class FakeFusedManager(private val fusedDownloadDAO: FusedDownloadDAO) : IFusedManager {
+ override fun createNotificationChannels() {
+ TODO("Not yet implemented")
+ }
+
+ override suspend fun addDownload(fusedDownload: FusedDownload) {
+ fusedDownload.status = Status.QUEUED
+ fusedDownloadDAO.addDownload(fusedDownload)
+ }
+
+ override suspend fun getDownloadById(fusedDownload: FusedDownload): FusedDownload? {
+ return fusedDownloadDAO.getDownloadById(fusedDownload.id)
+ }
+
+ override suspend fun getDownloadList(): List {
+ TODO("Not yet implemented")
+ }
+
+ override fun getDownloadLiveList(): LiveData> {
+ TODO("Not yet implemented")
+ }
+
+ override suspend fun clearInstallationIssue(fusedDownload: FusedDownload) {
+ TODO("Not yet implemented")
+ }
+
+ override suspend fun updateDownloadStatus(fusedDownload: FusedDownload, status: Status) {
+ TODO("Not yet implemented")
+ }
+
+ override suspend fun downloadApp(fusedDownload: FusedDownload) {
+ TODO("Not yet implemented")
+ }
+
+ override suspend fun installApp(fusedDownload: FusedDownload) {
+ TODO("Not yet implemented")
+ }
+
+ override suspend fun cancelDownload(fusedDownload: FusedDownload) {
+ TODO("Not yet implemented")
+ }
+
+ override suspend fun getFusedDownload(downloadId: Long, packageName: String): FusedDownload {
+ TODO("Not yet implemented")
+ }
+
+ override fun flushOldDownload(packageName: String) {
+ TODO("Not yet implemented")
+ }
+
+ override suspend fun downloadNativeApp(fusedDownload: FusedDownload) {
+ TODO("Not yet implemented")
+ }
+
+ override fun getGplayInstallationPackagePath(
+ fusedDownload: FusedDownload,
+ it: String,
+ parentPath: String,
+ count: Int
+ ): File {
+ TODO("Not yet implemented")
+ }
+
+ override fun createObbFileForDownload(fusedDownload: FusedDownload, url: String): File {
+ TODO("Not yet implemented")
+ }
+
+ override fun moveOBBFilesToOBBDirectory(fusedDownload: FusedDownload) {
+ TODO("Not yet implemented")
+ }
+
+ override fun getBaseApkPath(fusedDownload: FusedDownload): String {
+ return "root/data/apps/${fusedDownload.packageName}/${fusedDownload.packageName}_1.apk"
+ }
+
+ override suspend fun installationIssue(fusedDownload: FusedDownload) {
+ TODO("Not yet implemented")
+ }
+
+ override suspend fun updateAwaiting(fusedDownload: FusedDownload) {
+ TODO("Not yet implemented")
+ }
+
+ override suspend fun updateUnavailable(fusedDownload: FusedDownload) {
+ TODO("Not yet implemented")
+ }
+
+ override suspend fun updateFusedDownload(fusedDownload: FusedDownload) {
+ TODO("Not yet implemented")
+ }
+
+ override suspend fun insertFusedDownloadPurchaseNeeded(fusedDownload: FusedDownload) {
+ TODO("Not yet implemented")
+ }
+
+ override fun isFusedDownloadInstalled(fusedDownload: FusedDownload): Boolean {
+ TODO("Not yet implemented")
+ }
+
+ override fun getFusedDownloadInstallationStatus(fusedApp: FusedDownload): Status {
+ TODO("Not yet implemented")
+ }
+}
diff --git a/app/src/test/java/foundation/e/apps/fusedManager/FusedManagerRepositoryTest.kt b/app/src/test/java/foundation/e/apps/fusedManager/FusedManagerRepositoryTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..801c45a32b255f772b490660002fc747a8b6dd08
--- /dev/null
+++ b/app/src/test/java/foundation/e/apps/fusedManager/FusedManagerRepositoryTest.kt
@@ -0,0 +1,126 @@
+/*
+ * Copyright MURENA SAS 2023
+ * Apps Quickly and easily install Android apps onto your device!
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package foundation.e.apps.fusedManager
+
+import android.app.Application
+import androidx.arch.core.executor.testing.InstantTaskExecutorRule
+import foundation.e.apps.api.fdroid.FdroidRepository
+import foundation.e.apps.installProcessor.FakeFusedDownloadDAO
+import foundation.e.apps.manager.database.fusedDownload.FusedDownload
+import foundation.e.apps.manager.database.fusedDownload.FusedDownloadDAO
+import foundation.e.apps.manager.fused.FusedManagerRepository
+import foundation.e.apps.manager.workmanager.InstallWorkManager
+import foundation.e.apps.util.MainCoroutineRule
+import foundation.e.apps.utils.enums.Status
+import io.mockk.every
+import io.mockk.mockkObject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+class FusedManagerRepositoryTest {
+ @Rule
+ @JvmField
+ val instantExecutorRule = InstantTaskExecutorRule()
+
+ // Sets the main coroutines dispatcher to a TestCoroutineScope for unit testing.
+ @ExperimentalCoroutinesApi
+ @get:Rule
+ var mainCoroutineRule = MainCoroutineRule()
+
+ private lateinit var fusedDownloadDAO: FusedDownloadDAO
+ private lateinit var fakeFusedManager: FakeFusedManager
+
+ @Mock
+ private lateinit var application: Application
+
+ @Mock
+ private lateinit var fdroidRepository: FdroidRepository
+
+ private lateinit var fusedManagerRepository: FusedManagerRepository
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.openMocks(this)
+ InstallWorkManager.context = application
+ fusedDownloadDAO = FakeFusedDownloadDAO()
+ fakeFusedManager = FakeFusedManager(fusedDownloadDAO)
+ fusedManagerRepository = FusedManagerRepository(fakeFusedManager, fdroidRepository)
+ }
+
+ @Test
+ fun addDownload() = runTest {
+ val fusedDownload = initTest()
+
+ val isSuccessful = fusedManagerRepository.addDownload(fusedDownload)
+ assertTrue("addDownload", isSuccessful)
+ assertEquals("addDownload", 1, fusedDownloadDAO.getDownloadList().size)
+ }
+
+ private fun initTest(hasAnyExistingWork: Boolean = false): FusedDownload {
+ mockkObject(InstallWorkManager)
+ every { InstallWorkManager.checkWorkIsAlreadyAvailable(any()) } returns hasAnyExistingWork
+ return createFusedDownload()
+ }
+
+ @Test
+ fun `addDownload when work is already available`() = runTest {
+ val fusedDownload = initTest(true)
+
+ val isSuccessful = fusedManagerRepository.addDownload(fusedDownload)
+ assertFalse("addDownload", isSuccessful)
+ }
+
+ @Test
+ fun `addDownload when fusedDownload already exists`() = runTest {
+ val fusedDownload = initTest()
+ fusedDownloadDAO.addDownload(fusedDownload)
+
+ val isSuccessful = fusedManagerRepository.addDownload(fusedDownload)
+ assertFalse("addDownload", isSuccessful)
+ }
+
+ @Test
+ fun `addDownload when fusedDownload already exists And has installation issue`() = runTest {
+ val fusedDownload = initTest()
+ fusedDownload.status = Status.INSTALLATION_ISSUE
+ fusedDownloadDAO.addDownload(fusedDownload)
+
+ val isSuccessful = fusedManagerRepository.addDownload(fusedDownload)
+ assertTrue("addDownload", isSuccessful)
+ }
+
+ private fun createFusedDownload(
+ packageName: String? = null,
+ downloadUrlList: MutableList? = null
+ ) = FusedDownload(
+ id = "121",
+ status = Status.AWAITING,
+ downloadURLList = downloadUrlList ?: mutableListOf("apk1", "apk2"),
+ packageName = packageName ?: "com.unit.test"
+ )
+}
diff --git a/app/src/test/java/foundation/e/apps/installProcessor/AppInstallProcessorTest.kt b/app/src/test/java/foundation/e/apps/installProcessor/AppInstallProcessorTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..5156a7b343adc4070edb88526092216039dd2971
--- /dev/null
+++ b/app/src/test/java/foundation/e/apps/installProcessor/AppInstallProcessorTest.kt
@@ -0,0 +1,195 @@
+/*
+ * Copyright MURENA SAS 2023
+ * Apps Quickly and easily install Android apps onto your device!
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package foundation.e.apps.installProcessor
+
+import android.app.DownloadManager.Query
+import android.content.Context
+import androidx.arch.core.executor.testing.InstantTaskExecutorRule
+import com.aurora.gplayapi.data.models.AuthData
+import foundation.e.apps.api.DownloadManager
+import foundation.e.apps.api.fdroid.FdroidRepository
+import foundation.e.apps.manager.database.DatabaseRepository
+import foundation.e.apps.manager.database.fusedDownload.FusedDownload
+import foundation.e.apps.manager.fused.IFusedManager
+import foundation.e.apps.manager.workmanager.AppInstallProcessor
+import foundation.e.apps.util.MainCoroutineRule
+import foundation.e.apps.utils.enums.Status
+import foundation.e.apps.utils.modules.DataStoreManager
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+class AppInstallProcessorTest {
+ // Run tasks synchronously
+ @Rule
+ @JvmField
+ val instantExecutorRule = InstantTaskExecutorRule()
+
+ // Sets the main coroutines dispatcher to a TestCoroutineScope for unit testing.
+ @ExperimentalCoroutinesApi
+ @get:Rule
+ var mainCoroutineRule = MainCoroutineRule()
+
+ private lateinit var fakeFusedDownloadDAO: FakeFusedDownloadDAO
+ private lateinit var databaseRepository: DatabaseRepository
+ private lateinit var fakeDownloadManager: DownloadManager
+ private lateinit var fakeFusedManagerRepository: FakeFusedManagerRepository
+
+ @Mock
+ private lateinit var fakeFusedManager: IFusedManager
+
+ @Mock
+ private lateinit var fakeFdroidRepository: FdroidRepository
+
+ @Mock
+ private lateinit var context: Context
+
+ @Mock
+ private lateinit var downloadManager: android.app.DownloadManager
+
+ @Mock
+ private lateinit var query: Query
+
+ @Mock
+ private lateinit var dataStoreManager: DataStoreManager
+
+ private lateinit var appInstallProcessor: AppInstallProcessor
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.openMocks(this)
+ fakeFusedDownloadDAO = FakeFusedDownloadDAO()
+ databaseRepository = DatabaseRepository(fakeFusedDownloadDAO)
+ fakeFusedManagerRepository =
+ FakeFusedManagerRepository(fakeFusedDownloadDAO, fakeFusedManager, fakeFdroidRepository)
+ fakeDownloadManager =
+ FakeDownloadManager(downloadManager, "/home/data/foundation.e.apps/", query)
+
+ appInstallProcessor = AppInstallProcessor(
+ context,
+ databaseRepository,
+ fakeFusedManagerRepository,
+ fakeDownloadManager,
+ dataStoreManager
+ )
+ }
+
+ @Test
+ fun processInstallTest() = runTest {
+ val fusedDownload = initTest()
+
+ val finalFusedDownload = runProcessInstall(fusedDownload)
+ assertTrue("processInstall", finalFusedDownload == null)
+ }
+
+ private suspend fun initTest(
+ packageName: String? = null,
+ downloadUrlList: MutableList? = null
+ ): FusedDownload {
+ val fusedDownload = createFusedDownload(packageName, downloadUrlList)
+ fakeFusedDownloadDAO.addDownload(fusedDownload)
+ Mockito.`when`(dataStoreManager.getAuthData()).thenReturn(AuthData("", ""))
+ return fusedDownload
+ }
+
+ @Test
+ fun `processInstallTest when FusedDownload is already failed`() = runTest {
+ val fusedDownload = initTest()
+ fusedDownload.status = Status.BLOCKED
+
+ val finalFusedDownload = runProcessInstall(fusedDownload)
+ assertEquals("processInstall", Status.BLOCKED, finalFusedDownload?.status)
+ }
+
+ @Test
+ fun `processInstallTest when files are downloaded but not installed`() = runTest {
+ val fusedDownload = initTest()
+ fusedDownload.downloadIdMap = mutableMapOf(Pair(231, true))
+
+ val finalFusedDownload = runProcessInstall(fusedDownload)
+ assertTrue("processInstall", finalFusedDownload == null)
+ }
+
+ @Test
+ fun `processInstallTest when packageName is empty and files are downloaded`() = runTest {
+ val fusedDownload = initTest(packageName = "")
+ fusedDownload.downloadIdMap = mutableMapOf(Pair(231, true))
+
+ val finalFusedDownload = runProcessInstall(fusedDownload)
+ assertEquals("processInstall", Status.INSTALLATION_ISSUE, finalFusedDownload?.status)
+ }
+
+ @Test
+ fun `processInstallTest when downloadUrls are not available`() = runTest {
+ val fusedDownload = initTest(downloadUrlList = mutableListOf())
+
+ val finalFusedDownload = runProcessInstall(fusedDownload)
+ assertEquals("processInstall", Status.INSTALLATION_ISSUE, finalFusedDownload?.status)
+ }
+
+ @Test
+ fun `processInstallTest when exception is occurred`() = runTest {
+ val fusedDownload = initTest()
+ fakeFusedManagerRepository.forceCrash = true
+
+ val finalFusedDownload = runProcessInstall(fusedDownload)
+ assertEquals("processInstall", Status.INSTALLATION_ISSUE, finalFusedDownload?.status)
+ }
+
+ @Test
+ fun `processInstallTest when download is failed`() = runTest {
+ val fusedDownload = initTest()
+ fakeFusedManagerRepository.willDownloadFail = true
+
+ val finalFusedDownload = runProcessInstall(fusedDownload)
+ assertEquals("processInstall", Status.INSTALLATION_ISSUE, finalFusedDownload?.status)
+ }
+
+ @Test
+ fun `processInstallTest when install is failed`() = runTest {
+ val fusedDownload = initTest()
+ fakeFusedManagerRepository.willInstallFail = true
+
+ val finalFusedDownload = runProcessInstall(fusedDownload)
+ assertEquals("processInstall", Status.INSTALLATION_ISSUE, finalFusedDownload?.status)
+ }
+
+ private suspend fun runProcessInstall(fusedDownload: FusedDownload): FusedDownload? {
+ appInstallProcessor.processInstall(fusedDownload.id, false)
+ return fakeFusedDownloadDAO.getDownloadById(fusedDownload.id)
+ }
+
+ private fun createFusedDownload(
+ packageName: String? = null,
+ downloadUrlList: MutableList? = null
+ ) = FusedDownload(
+ id = "121",
+ status = Status.AWAITING,
+ downloadURLList = downloadUrlList ?: mutableListOf("apk1", "apk2"),
+ packageName = packageName ?: "com.unit.test"
+ )
+}
diff --git a/app/src/test/java/foundation/e/apps/installProcessor/FakeDownloadManager.kt b/app/src/test/java/foundation/e/apps/installProcessor/FakeDownloadManager.kt
new file mode 100644
index 0000000000000000000000000000000000000000..58c38f0a950917b59bbf899385fd6a26fdc63d25
--- /dev/null
+++ b/app/src/test/java/foundation/e/apps/installProcessor/FakeDownloadManager.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright MURENA SAS 2023
+ * Apps Quickly and easily install Android apps onto your device!
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package foundation.e.apps.installProcessor
+
+import foundation.e.apps.api.DownloadManager
+
+class FakeDownloadManager(
+ downloadManger: android.app.DownloadManager,
+ cacheDir: String,
+ query: android.app.DownloadManager.Query
+) : DownloadManager(downloadManger, cacheDir, query) {
+
+ override suspend fun checkDownloadProcess(
+ downloadingIds: LongArray,
+ handleFailed: suspend () -> Unit
+ ) {
+ if (downloadingIds.contains(-1)) {
+ handleFailed()
+ }
+ }
+}
diff --git a/app/src/test/java/foundation/e/apps/installProcessor/FakeFusedDownloadDAO.kt b/app/src/test/java/foundation/e/apps/installProcessor/FakeFusedDownloadDAO.kt
new file mode 100644
index 0000000000000000000000000000000000000000..3ec0dcc8c854aa1f22a0116641d13e9ebeef4035
--- /dev/null
+++ b/app/src/test/java/foundation/e/apps/installProcessor/FakeFusedDownloadDAO.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright MURENA SAS 2023
+ * Apps Quickly and easily install Android apps onto your device!
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package foundation.e.apps.installProcessor
+
+import androidx.lifecycle.LiveData
+import foundation.e.apps.manager.database.fusedDownload.FusedDownload
+import foundation.e.apps.manager.database.fusedDownload.FusedDownloadDAO
+
+class FakeFusedDownloadDAO : FusedDownloadDAO {
+ val fusedDownloadList = mutableListOf()
+
+ override suspend fun addDownload(fusedDownload: FusedDownload) {
+ fusedDownloadList.add(fusedDownload)
+ }
+
+ override fun getDownloadLiveList(): LiveData> {
+ TODO("Not yet implemented")
+ }
+
+ override suspend fun getDownloadList(): List {
+ return fusedDownloadList
+ }
+
+ override suspend fun getDownloadById(id: String): FusedDownload? {
+ return fusedDownloadList.find { it.id == id }
+ }
+
+ override fun getDownloadFlowById(id: String): LiveData {
+ TODO("Not yet implemented")
+ }
+
+ override suspend fun updateDownload(fusedDownload: FusedDownload) {
+ fusedDownloadList.replaceAll { if (it.id == fusedDownload.id) fusedDownload else it }
+ }
+
+ override suspend fun deleteDownload(fusedDownload: FusedDownload) {
+ fusedDownloadList.remove(fusedDownload)
+ }
+}
diff --git a/app/src/test/java/foundation/e/apps/installProcessor/FakeFusedManagerRepository.kt b/app/src/test/java/foundation/e/apps/installProcessor/FakeFusedManagerRepository.kt
new file mode 100644
index 0000000000000000000000000000000000000000..04925cf6aff18cb1fa20e1f123f207e43fc85f50
--- /dev/null
+++ b/app/src/test/java/foundation/e/apps/installProcessor/FakeFusedManagerRepository.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright MURENA SAS 2023
+ * Apps Quickly and easily install Android apps onto your device!
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package foundation.e.apps.installProcessor
+
+import foundation.e.apps.api.fdroid.FdroidRepository
+import foundation.e.apps.manager.database.fusedDownload.FusedDownload
+import foundation.e.apps.manager.fused.FusedManagerRepository
+import foundation.e.apps.manager.fused.IFusedManager
+import foundation.e.apps.utils.enums.Status
+import kotlinx.coroutines.delay
+
+class FakeFusedManagerRepository(
+ private val fusedDownloadDAO: FakeFusedDownloadDAO,
+ fusedManager: IFusedManager,
+ fdroidRepository: FdroidRepository,
+) : FusedManagerRepository(fusedManager, fdroidRepository) {
+ var isAppInstalled = false
+ var installationStatus = Status.INSTALLED
+ var willDownloadFail = false
+ var willInstallFail = false
+ var forceCrash = false
+
+ override suspend fun downloadApp(fusedDownload: FusedDownload) {
+ fusedDownload.status = Status.DOWNLOADING
+ fusedDownload.downloadIdMap = mutableMapOf(Pair(341, false), Pair(342, false))
+ fusedDownloadDAO.updateDownload(fusedDownload)
+ delay(10000)
+
+ if (willDownloadFail) {
+ fusedDownload.downloadIdMap.clear()
+ fusedDownload.downloadIdMap = mutableMapOf(Pair(-1, false), Pair(-1, false))
+ fusedDownloadDAO.updateDownload(fusedDownload)
+ return
+ }
+
+ fusedDownload.downloadIdMap.replaceAll { _, _ -> true }
+ fusedDownload.status = Status.DOWNLOADED
+ fusedDownloadDAO.updateDownload(fusedDownload)
+ }
+
+ override suspend fun updateDownloadStatus(fusedDownload: FusedDownload, status: Status) {
+ when (status) {
+ Status.INSTALLING -> {
+ handleStatusInstalling(fusedDownload)
+
+ }
+ Status.INSTALLED -> {
+ if (forceCrash) {
+ throw RuntimeException()
+ }
+
+ fusedDownloadDAO.deleteDownload(fusedDownload)
+ }
+ else -> {
+ fusedDownload.status = status
+ fusedDownloadDAO.updateDownload(fusedDownload)
+ }
+ }
+ }
+
+ private suspend fun handleStatusInstalling(
+ fusedDownload: FusedDownload
+ ) {
+ fusedDownload.status = Status.INSTALLING
+ fusedDownloadDAO.updateDownload(fusedDownload)
+ delay(5000)
+
+ if (willInstallFail) {
+ updateDownloadStatus(fusedDownload, Status.INSTALLATION_ISSUE)
+ } else {
+ updateDownloadStatus(fusedDownload, Status.INSTALLED)
+ }
+ }
+
+ override suspend fun installationIssue(fusedDownload: FusedDownload) {
+ fusedDownload.status = Status.INSTALLATION_ISSUE
+ fusedDownloadDAO.updateDownload(fusedDownload)
+ }
+
+ override fun isFusedDownloadInstalled(fusedDownload: FusedDownload): Boolean {
+ return isAppInstalled
+ }
+
+ override fun getFusedDownloadPackageStatus(fusedDownload: FusedDownload): Status {
+ return installationStatus
+ }
+}